pax_global_header00006660000000000000000000000064136071721100014510gustar00rootroot0000000000000052 comment=c369d872d527f03c17cd54af03813db6096c7e0b crispy-doom-crispy-doom-5.6.4/000077500000000000000000000000001360717211000162345ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/.github/000077500000000000000000000000001360717211000175745ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/.github/CONTRIBUTING.md000066400000000000000000000105301360717211000220240ustar00rootroot00000000000000Thanks for contributing to Chocolate Doom! Whatever your contribution, whether it's code or just a bug report, it's greatly appreciated. The project is governed by the [Contributor Covenant](http://contributor-covenant.org/version/1/4/) version 1.4. By contributing to the project you agree to abide by its terms. To report violations, please send an email to fraggle@gmail.com. ### Reporting bugs Before reporting a bug, it's worth checking if this really is a bug. Chocolate Doom's mission is to reproduce the Vanilla (DOS) versions of the Doom engine games, bugs and all. Check out the [NOT-BUGS](../NOT-BUGS.md) file for a list of common issues which aren't really bugs at all. You might also try searching [the GitHub issues list](https://github.com/chocolate-doom/chocolate-doom/issues) to see if your bug has already been reported. If you're confident that you've found a real bug (or even if you're not sure!) please go ahead and [file an issue on GitHub](https://github.com/chocolate-doom/chocolate-doom/issues/new). You'll need a GitHub account, but it's pretty easy to sign up. Please try to give as much information as possible: * What version of Chocolate Doom are you using? Check the title bar of the window for the version number. * Chocolate Doom runs on many different operating systems (not just Windows!). Please say which operating system and what version of it you're using. * Please say which game you're playing (Doom 1, Doom 2, Heretic, Hexen, Strife, etc.) and if you're using any fan-made WADs or mods, please say which mods (and where they can be downloaded!). It helps to give the full command line you're using to start the game. * Please mention if you have any special configuration you think may be relevant, too. ### Feature requests Chocolate Doom is always open to new feature requests; however, please be aware that the project is designed around a deliberately limited [philosophy](../PHILOSOPHY.md), and many features common in other source ports will not be accepted. Here are a few common requests which are often rejected: * "High resolution" rendering (greater than 320x200 display). * An option to disable Vanilla limits, such as the visplane rendering limit. * Ability to play "No Rest For The Living", the expansion pack which comes with the XBLA / BFG Edition of Doom. If you're not sure whether your feature is in line with the project philosophy, don't worry - just ask anyway! To make a feature request, [file an issue on GitHub](https://github.com/chocolate-doom/chocolate-doom/issues/new). ### Bug fixes / code submission Thank you for contributing code to Chocolate Doom! Please check the following guidelines before opening a pull request: * All code must be licensed under [the GNU General Public License, version 2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). Please don't reuse code that isn't GPL, or that is GPLv3 licensed. Be aware that by submitting your code to the project, you're agreeing to license it under the GPL. * Please follow the coding style guidelines described in the [HACKING](../HACKING.md) file. * Please don't make unnecessary changes which just change formatting without any actual change to program logic. While being consistent is nice, such changes destroy the ability to use the `git blame` command to see when code was last changed. * The guidelines given above in the "feature requests" section also apply here. New features which aren't in line with the project philosophy are likely to be rejected. If you're not sure, open a feature request first and ask before you start implementing your feature. * Follow the guidelines for [how to write a Git commit message](http://chris.beams.io/posts/git-commit/). In short: the first line should be a short summary; keep to an 80 column limit; use the imperative mood ("fix bug X", rather than "fixed bug X" or "fixing bug X"). If your change fixes a particular subsystem, prefix the summary with that subsystem: eg. "doom: Fix bug X" or "textscreen: Change size of X". * If you're making a change related to a bug, reference the GitHub issue number in the commit message, eg. "This is a partial fix for #646". This will link your commit into the issue comments. If your change is a fix for the bug, put the word "fixes" before the issue number to automatically close the issue once your change is merged. crispy-doom-crispy-doom-5.6.4/.github/ISSUE_TEMPLATE.md000066400000000000000000000006121360717211000223000ustar00rootroot00000000000000 ### Background Version of Crispy Doom: Operating System and version: Game: (Doom/Heretic/Hexen/Strife/other) Any loaded WADs and mods (please include full command line): ### Bug description Observed behavior: Expected behavior: crispy-doom-crispy-doom-5.6.4/.gitignore000066400000000000000000000010641360717211000202250ustar00rootroot00000000000000CMDLINE INSTALL Makefile Makefile.in TAGS aclocal.m4 autom4te.cache autotools bin config.h config.hin config.log config.status configure lib obj rpm.spec stamp-h stamp-h.in stamp-h1 tags \#*\# DOOM*.png HTIC*.png HEXEN*.png STRIFE*.png DOOM*.pcx HTIC*.pcx HEXEN*.pcx STRIFE*.pcx # CMake-specific ignores /.vs /build* /CMakeSettings.json /out # These are the default patterns globally ignored by Subversion: *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo *.rej *~ .#* .*.swp .DS_store # Ignore GNU Global tags and html files GPATH GRTAGS GTAGS /HTML/ crispy-doom-crispy-doom-5.6.4/.gitmodules000066400000000000000000000001441360717211000204100ustar00rootroot00000000000000[submodule "quickcheck"] path = quickcheck url = https://github.com/chocolate-doom/quickcheck.git crispy-doom-crispy-doom-5.6.4/.lvimrc000066400000000000000000000014151360717211000175320ustar00rootroot00000000000000" Local vimrc configuration file. Install the localvimrc.vim vim script. set expandtab set tabstop=8 set softtabstop=4 set shiftwidth=4 " Add all tag files to tags path. let topdir = findfile("configure.ac", ".;") let topdir = substitute(topdir, "configure.ac", "", "") " Check tags file in current dir: set tags+=tags " Add tag files in parent directories: let tagfiles = findfile("tags", ".;", -1) " Add tag files for libraries: call add(tagfiles, topdir . "opl/tags") call add(tagfiles, topdir . "pcsound/tags") call add(tagfiles, topdir . "textscreen/tags") for tagfile in tagfiles " Don't go beyond the project top level when adding parent dirs: if stridx(tagfile, topdir) >= 0 exec "set tags+=" . tagfile endif endfor unlet topdir unlet tagfiles crispy-doom-crispy-doom-5.6.4/.travis.sh000077500000000000000000000007131360717211000201620ustar00rootroot00000000000000#!/bin/sh if [ "$ANALYZE" = "true" ] ; then cppcheck --error-exitcode=1 -j2 -UTESTING -Iopl -Isrc -Isrc/setup opl pcsound src textscreen 2> stderr.txt RET=$? if [ -s stderr.txt ] then cat stderr.txt fi exit $RET else set -e ./autogen.sh --enable-werror make -j4 make install DESTDIR=/tmp/whatever make dist PREFIX=`sed -n '/PROGRAM_PREFIX/p' ${PWD}/config.h | cut -d '"' -f 2` make -j4 -C quickcheck check SOURCE_PORT=$PWD/src/${PREFIX}doom fi crispy-doom-crispy-doom-5.6.4/.travis.yml000066400000000000000000000005561360717211000203530ustar00rootroot00000000000000language: c compiler: gcc sudo: required dist: bionic env: - ANALYZE=false - ANALYZE=true addons: apt: packages: - cppcheck - libpng-dev - libsdl2-dev - libsdl2-mixer-dev - libsdl2-net-dev - libsdl2-image-dev - libsamplerate0-dev script: ./.travis.sh branches: only: - master crispy-doom-crispy-doom-5.6.4/AUTHORS000066400000000000000000000003311360717211000173010ustar00rootroot00000000000000Simon Howard James Haley Samuel Villarreal Fabian Greffrath Jonathan Dowland Alexey Khokholov crispy-doom-crispy-doom-5.6.4/CMakeLists.txt000066400000000000000000000043701360717211000210000ustar00rootroot00000000000000set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") cmake_minimum_required(VERSION 3.7.2) project("Crispy Doom" VERSION 5.6.4 LANGUAGES C) # Autotools variables set(top_srcdir ${CMAKE_CURRENT_SOURCE_DIR}) set(top_builddir ${CMAKE_CURRENT_BINARY_DIR}) # AC_INIT variables set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_TARNAME "crispy-doom") set(PACKAGE_VERSION "${PROJECT_VERSION}") set(PACKAGE_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") set(PACKAGE_BUGREPORT "https://github.com/fabiangreffrath/crispy-doom/issues") string(REGEX REPLACE " Doom$" "" PACKAGE_SHORTNAME "${PACKAGE_NAME}") set(PACKAGE_COPYRIGHT "Copyright (C) 1993-2017") set(PACKAGE_LICENSE "GNU General Public License, version 2") # Any settings that should apply to all targets in this directory and all # subdirectories should go here. Use judiciously. if(MSVC) add_definitions("/D_CRT_SECURE_NO_WARNINGS" "/D_CRT_SECURE_NO_DEPRECATE" "/D_CRT_NONSTDC_NO_DEPRECATE") else() add_compile_options("-Wall" "-Wdeclaration-after-statement" "-Wredundant-decls") endif() find_package(SDL2 2.0.1) find_package(SDL2_mixer 2.0.0) find_package(SDL2_net 2.0.0) # Check for libsamplerate. find_package(samplerate) if(SAMPLERATE_FOUND) set(HAVE_LIBSAMPLERATE TRUE) endif() # Check for libpng. find_package(PNG) if(PNG_FOUND) set(HAVE_LIBPNG TRUE) endif() find_package(m) include(CheckSymbolExists) include(CheckIncludeFile) check_symbol_exists(strcasecmp "strings.h" HAVE_DECL_STRCASECMP) check_symbol_exists(strncasecmp "strings.h" HAVE_DECL_STRNCASECMP) check_include_file("dirent.h" HAVE_DIRENT_H) string(CONCAT WINDOWS_RC_VERSION "${PROJECT_VERSION_MAJOR}, " "${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}, 0") # Without a hyphen. This is used for the bash-completion scripts. string(TOLOWER "${PACKAGE_SHORTNAME}" PROGRAM_SPREFIX) # With a hyphen, used almost everywhere else. set(PROGRAM_PREFIX "${PROGRAM_SPREFIX}-") configure_file(cmake/config.h.cin config.h) configure_file(src/resource.rc.in src/resource.rc) configure_file(src/setup-res.rc.in src/setup-res.rc) configure_file(src/setup/setup-manifest.xml.in src/setup/setup-manifest.xml) foreach(SUBDIR textscreen midiproc opl pcsound src) add_subdirectory("${SUBDIR}") endforeach() crispy-doom-crispy-doom-5.6.4/CODE_OF_CONDUCT.md000066400000000000000000000062311360717211000210350ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fraggle@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ crispy-doom-crispy-doom-5.6.4/COPYING.md000066400000000000000000000427471360717211000177040ustar00rootroot00000000000000### GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ### Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION **0.** This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. **1.** You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. **2.** You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: **a)** You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. **b)** You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. **c)** If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. **3.** You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: **a)** Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, **b)** Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, **c)** Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. **4.** You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. **5.** You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. **6.** Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. **7.** If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. **8.** If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. **9.** The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. **10.** If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. **NO WARRANTY** **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### END OF TERMS AND CONDITIONS ### How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands \`show w' and \`show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than \`show w' and \`show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl.html) instead of this License. crispy-doom-crispy-doom-5.6.4/ChangeLog000066400000000000000000003637251360717211000200260ustar00rootroot00000000000000 ### Changes of Crispy Doom 5.2 from Crispy Doom 5.1 Crispy Doom 5.2 has been released on June 20, 2018. This release introduces many new features and sections in the Crispness menu, including a new "Rendering" section, a "Crosshair" section for crosshair options including health-based coloring and highlighting on target, and a "Demos" section with options for demo recording and playback timers as well as the demo progress bar. Furthermore, there is smooth diminishing lighting, additional gamma correction levels, increased number of sound channels and support for taller textures as well as numerous other improvements and bug fixes. **Features** * Smooth interpolated texture scrolling has been introduced for uncapped rendering and linedef specific 85 (Scroll texture right) is now supported, as suggested by JNechaevsky. * The crosshair may now indicate the player's health (as requested by JNechaevsky) and change its color ("highlight") when pointing on a target. Furthermore, crosshair options have been moved into their own submenu in the Crispness menu. * Intermediate gamma correction levels (`0.5`, `1.5`, `2.5` and `3.5`) have been implemented, on JNechaevsky's request. * Smooth diminishing lighting with 32 light levels instead of 16 may now be enabled in the "Visual" section of the Crispness menu. * The number of sound channels may now be 16 or 32 as an alternative to the default 8, adjustable in the "Audible" section of the Crispness menu. * The "Rendering" Crispness menu section has been introduced, comprising the high-resolution rendering, uncapped framerate and smooth pixel scaling options. * Support for DeePsea tall patches has been added to the main patch drawing functions. Furthermore, tall textures and sprites composed of DeePsea tall patches are now supported, as suggested by JNechaevsky. * Resolution-agnostic and fullscreen-stretching patch drawing functions have been implemented, mostly taken from Doom Retro. * The "Demos" section of the Crispness menu has been introduced, including options for enabling the recording timer, choosing forward or backward counting playback timer and the demo progress bar (which is now 2 px high). * Support for MUSINFO lumps (dynamic music changing) has been added, mostly taken from PrBoom+. **Improvements** * The Fuzz effect used when drawing "invisible" things is now scrolled independent of the rendering framerate and paused when the game is paused, on JNechaevsy's suggestion. * The "Uncapped" framerate setting resulting in 100% CPU usage has been removed from the Crispness menu. It still remains available as a benchmarking measure by manually setting `crispy_uncapped` to `1` in the `crispy-doom.cfg` config file. * CPU usage is now capped by limiting the rendering framerate to 35 if the window is minimized, thanks to JNechaevsky for the idea. * A game version mismatch notification is now thrown when attempting to load a savegame that has been saved with a different IWAD/game version. The engine just did nothing before which was discovered by Zodomaniac. * The "Miscellaneous Sound Fixes" option now includes playing a sound if the menu is activated with a different key than ESC (e.g. on game launch) as Zodomaniac suggested as well as playing the Arch-Vile's fire start sound (DSFLAMST), the absence of which was noticed by JNechaevsky. * The `red2green` color translation table has been toned down a bit removing the "metallic green gloss" from Barons' and Knights' blood splats while leaving the overall appearance (and that of the colored gore pools) intact after JNechaevsky's thorough investigation. * For disabled multi-items in the Crispness menu, "None" is now explicitly printed instead of leaving a blank space after Zodomaniac spotted some inconsistency. * The "Permanent Mouse Look" option has been moved from the "Mouse" menu to the "Tactical" page of the Crispness menu, as Zodomaniac and some Doomworld members suggested. * The "Negative Player Health" feature has been simplified to a choice of on/off for all gamemodes. * The "Extended Savegames" feature is now enabled by default and has been removed from the Crispness menu. Reasons are lack of space in the menu and redundancy as extended savegames are still compatible with Vanilla and Chocolate Doom. * The "Colored Blood and Corpses" and "Fix Spectre and Lost Soul blood" features have been combined into one menu item (i.e. colored blood now includes Spectre and Lost Soul blood fixes) and simplified to an on/off choice. * It is now possible to switch Crispness menu pages using PgUp/PgDn and to wrap around between the first and last menu page, according to JNechaevsky's and mfrancis95's suggestions. * A new and exclusive Crisps background texture for the Crispness menu has been created by JNechaevsky. * The "Mono SFX" option is now a Crispness menu item available in the "Audible" section. The corresponding `-monosfx` command line option has been removed. * Minor wording fixes have been applied to some Crispness menu items, now reading "Squat weapon down on impact" and "Crosshair shape". * Fonts in the finale text screens are now rendered with shadows. * The Player's view height is now immediately updated when his sector moves. * Warnings about mapthings without any skill tag set are now issued. * The "Always Run" and "Use native keyboard mapping" are now disabled by default, like in Chocolate Doom. This is in order not to confuse players installing Crispy for the first time. Zodomaniac requested this upon discovering the impossibility to type in savegame names in Cyrillic keyboard layout when using native keyboard mapping, which was also confirmed by JNechaevsky under different OSes. * Palette changes now don't occur when the help screen or the Crispness menu is active, thanks to mfrancis95 for the contribution. **Bug Fixes** * Fuzz effect going out of bounds noticed by JNechaevsky has been fixed. * A crash that occured when loading a map without any things has been fixed, thanks to mfrancis95 for noticing. * The status bar is now immediately getting redrawn after the help screens have been shown, spotted by JNechaevsky. * The blood fix for Lost Souls not getting toggled in-game immediately like the colored blood for other monsters has been fixed, pointed out by Zodomaniac. * Demos are now played back with interpolation in case of uncapped rendering even if the menu is open, on mfrancis95's report. * Romero's head is not randomly flipped after death anymore, thanks to JNechaevsky for inspiring the flipped corpse feature overhaul. * Color translation dependence on gamma level spotted by JNechaevsky has been fixed. * Weapon sprite moving when looking up/down in the low resolution rendering mode has been fixed. * Planes rotating in the low resolution rendering mode have been fixed. * The game remaining paused after saving while recording a demo has been fixed on Zodomaniac's report. * The bug when music was starting to play while paused and changed volume spotted by JNechaevsky has been fixed. * The in-engine "fixes" replacing WolfSS by Zombiemen for the BFG edition IWAD and removing the TNT MAP31 yellow key erroneous "multiplayer only" flag have been removed because they make demo playback fail as Zodomaniac's investigation has shown. Instead, the PWAD fixes (https://forum.zdoom.org/viewtopic.php?f=19&t=53776 and http://www.teamtnt.com/other/tnt31fix.zip respectively) are recommended. * Crashes when completing IWAD bonus maps (E1M10: Sewers, MAP33: Betray) while recording a demo have been fixed on Zodomaniac's report. * The possibility of falling down into a wall after a savegame is loaded has been fixed. * Zero music volume not muting OPL (and not only OPL) music fully has been fixed. Thanks to JNechaevsky, Zodomaniac and mfrancis95 for bringing this up again and nukeykt for clarifying OPL chip limitations and suggesting to pause music when it is muted. * The MAXBUTTONS limit has been removed (mostly because of the SPECHITS cheat and because of the more-then-one-switch-texture-per-line fix mentioned below). * The glitch of switch textures not changing in some segments has been fixed by means of registering up to three buttons at once for lines with more than one switch texture. Thanks to JNechaevsky for spotting this bug in the Vanilla code and Brad Harding and Jeff Doggett for presenting alternative approaches. * Savegames are now prevented from restoring out-of-range flats. This could happen if a PWAD was loaded in Choco with the -file parameter, a savegame had been saved and then restored in Crispy (which loads PWADs with the -merge parameter). * The brightmap for the COMP2 texture has been fixed in Brad Harding's way. * Taking a "Clean screenshot" could break the menu if the regular screenshot key was unbound. This has been fixed due to JNechaevsky's efforts to track this down. * Taking a screenshot without the 'screen shot' message has been repaired. * A crash when finishing NERVE MAP09 while recording/playing back a demo in case NERVE.WAD is auto-loaded with BFG Edition DOOM 2 IWAD has been fixed on Zodomaniac's report. * A crash when finishing NERVE MAP06 while recording a demo has been fixed, thanks to galileo31dos01 for pointing this out. * The status bar is now backed up with the arms widget background to fix the automap causing pixel replacements, reported by JNechaevsky. * Some consistency fixes have been applied to Chex Quest: the Episode menu is not shown anymore when returning from the Skill menu, the "Colored blood and corpses" and "Randomly mirrored corpses" Crispness menu entries are explicitly disabled, on request by JNechaevsky. * Resurrections of mirrored corpses staying mirrored have been fixed by mfrancis95. * Bullet puffs now don't slip to the edge of a sector anymore when passing through the plane. Bullets hitting the floors and ceilings in direct aiming mode now have their puffs displayed at the actual hit spots, thanks to JNechaevsky for noticing. * Window going out of vertical bounds at startup has been fixed, thanks to JNechaevsky's report. **Known Issues** * Changing the "High Resolution Rendering" option requires a restart of the engine for the change to take effect. This is currently the only option that requires this. Switching this feature with immediate effect is considered a release goal for the next version of Crispy Doom. * If "Vertical Aiming" is set to "direct" and the "Highlight Crosshair on Target" feature is enabled, the crosshair will get highlighted even if the direct shot would miss but if it would hit with autoaiming enabled. * Demo timer shadows the FPS counter if both are enabled. Crispy Doom 5.2 is based on Chocolate Doom 3.0.0 and has merged all changes to the Chocolate Doom master branch up to commit [`5329fb5d`](https://github.com/chocolate-doom/chocolate-doom/commit/5329fb5d75971138b20abf940ed63635bd2861e0) === Changes of Crispy Doom 5.1 from Crispy Doom 5.0 === Crispy Doom 5.1 has been released on January 13, 2018. Crispy Doom 5.1 offers some new features and improvements, most of them requested by JNechaevsky, and is accompanied by a new Music Pack adapted to the SDL2 version and tested by Zodomaniac (if you are experiencing problems with playing music remove crispy-midiproc.exe from the game folder). '''Features''' * Optional Brightmaps for select textures and flats ("walls") and/or pick-up and weapon HUD sprites ("items") have been introduced for all supported IWADs. Thanks to JNechaevsky for this contribution! * Extended automap colors are now optional, the setting is in the new 'Navigational' section of the Crispness menu. * A new setting for squatting down the weapon when falling has been introduced to the 'Tactical' section of the Crispness menu. * A new setting for miscellaneous sound fixes has been introduced to the Audible section of the Crispness menu. * Demo de-syncing cheats are now disallowed during demo recording or playback as Zodomaniac requested. * An option for weapon bobbing with half amplitude during firing has been added. * Combined card and skull keys are now supported in the status bar (if provided by a PWAD). * The VERSION cheat code showing the engine version, build date and SDL version has been introduced. '''Improvements''' * The GPL-licensed P_CreateBlockMap() implementation from MBF has been re-introduced. * The "power up" sound is not played anymore when selecting the berserk fist, according to Paar's request. * Fake contrast logic is now also applied to mid-textured lines. * Demo version mismatch has been made non-fatal for in-game demos, the demo just doesn't play if its version differs from the IWAD version. On deliberate launch of a wrong versioned demo the game still exits with an error message. * The 'A secret is revealed!' message isn't shown anymore when the menu is active. * The current music now restarts if IDMUS00 is entered. * If the D_INTRO music is from a PWAD, and D_INTROA is from a different WAD file, the former is played, as galileo31dos01 pointed out. * The INTERCEPTS limit has been removed. * The inverted palette effect is now disabled when the player dies (e.g. when telefragged while having the Invulnerability power-up). * The yellow bonus palette is now reset and never shown if the player dies. '''Bug Fixes''' * The player's looking direction is restored from savegames only if either keyboard-look or mouse-look is enabled. * Binding of the crispy_smoothscaling config variable has been fixed, reported by Paar. * The missing line not drawn at the bottom of fuzzed sprites has been fixed. * VSYNC is now disabled if software rendering is enforced. * Support for compressed ZDBSP nodes has been brought back, it was accidentally removed in 5.0 by the switch to the SDL2-based configure script. Crispy Doom 5.1 is based on Chocolate Doom 3.0.0 and has merged all changes to the Chocolate Doom master branch, which has meanwhile merged the sdl2-branch, up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/55a1c1c9e5ddc3c4e4e27240860d48bdb5701798 55a1c1c9] === Changes of Crispy Doom 5.0 from Crispy Doom 4.3 === Crispy Doom 5.0 has been released on November 04, 2017. This is the first release based on SDL2, merging Chocolate Doom's SDL2-branch which has yet to see its own release. Besides this major transition, it features the following changes and fixes: * The NUMCMAPS variable is now initialized to 32 to fix the missing "Finished" and "Now Entering" lines on the intermission screens for Doom 1 (bug introduced in Crispy 4.2). * Joystick look up/down (which has been introduced for the three non-Doom games in Choco's SDL2-branch) has been adapted to Doom. * Smooth scaling of the framebuffer content to the game window (which is the only choice in Choco's SDL2-branch) has been made optional with plain nearest interpolation as the other alternative. * The 60FPS and 70FPS rendering modes have been removed in favor of proper VSYNC. * DOS references in the game quit confirmation dialogs have been replaced with the name of the actual operating system. * A new -monosfx command line parameter has been introduced to play all sound effects in mono. Inspired by other Choco derivatives like Russian Doom and Riscos Doom. * Resolutions above 1920x1200 are now properly supported, thanks to SDL2-based hardware scaling. Crispy Doom 5.0 has merged all changes to the Chocolate Doom sdl2-branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/413f4ecd7aae41229cc7b40ea3275970dc18cdb4 413f4ecd] === Changes of Crispy Doom 4.3 from Crispy Doom 4.2 === Crispy Doom 4.3 has been released on September 19, 2017. This quick fix release introduces improvements requested by ZeroMaster010 and is supposed to be the last release based on SDL1. '''Improvements''' * The sector->soundtarget element is now saved in extended savegames. * The -shorttics command line option has been added to force low-resolution turning even when not recording a demo which is useful for demo run practice. * Smoother fake contrasts are disabled again as they disturb pixel perfect alignment for speedrunners. Crispy Doom 4.3 and 4.2 have merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/83a71dd850fb02e571e2b5117f234395f8869a11 83a71dd8]. === Changes of Crispy Doom 4.2 from Crispy Doom 4.1 === Crispy Doom 4.2 has been released on September 15, 2017. The changes since 4.1 are mostly polish of previously introduced features. '''Improvements''' * DOS references have been removed from the game quit confirmation dialogs on Zodomaniac's request inspired by Doom Retro, now they say:
Press Y to quit.
I wouldn't leave if i were you. Your desktop is much worse.
You're trying to say you like your desktop better than me, right? and
Don't go now, there's a dimensional shambler waiting at the command prompt!
Note: When switching to SDL2, the desktop references will be replaced by the platform name detected by SDL2 ;) * "Screen shot" message doesn't get burned into the screenshots anymore when they are taken one after another on Zodomaniac's report. * Map coordinates enabled by the IDMYPOS cheat are now persistent, i.e. they don't automatically disappear anymore after a few seconds. * The (sprframe->rotate == -1) case is handled since it has been made non-fatal. * Interpolation of player missiles for the first tic is suppressed. * Different spellings of similar cheats in other engines are now supported:
Kill all monsters and disable all cube spitters: TNTEM (PrBoom+), KILLEM (MBF), FHHALL (Doom95)
Toggle monsters being deaf and blind until attacked: NOTARGET (PrBoom+), FHSHH (Doom95)
Display framerate: SHOWFPS, IDRATE (PrBoom+) * Higher scaling video modes up to 3200x2400/3200x2000 are supported on Calinou's request. * Crashes with maps with unusual map numbers are prevented. * Music number under- and overflows for both Doom 1 and 2 are prevented. * Negative ammo values are not allowed anymore thanks to the report by Zodomaniac and AnotherLife. * Evil grin is now triggered when IDFA/IDKFA is used which is 1) rather reasonable by itself as ID(K)FA gives weapons and items and 2) prevents the grin triggering when the next random item is picked up that AnotherLife noticed. * The first extra state is explicitly set to 1089 and the first extra mapthing to 150 to remain in sync with Doom Retro. * Weapon sprite is removed from clean screenshots only in maximized screen (no HUD) mode now, it was awkward before when the status bar was rendered but the weapon wasn't. * Calling pspr action pointers from mobj states and vice versa won't crash the game anymore after Zodomaniac's and JNechaevsky's investigation. * Menu messages are always printed with shadows regardless of which menu is the current one. Thanks to JNechaevsly for spotting this. Crispy Doom 4.3 and 4.2 have merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/83a71dd850fb02e571e2b5117f234395f8869a11 83a71dd8]. === Changes of Crispy Doom 4.1 from Crispy Doom 4.0 === Crispy Doom 4.1 has been released on June 14, 2017. This release adds some further polish to the engine both in terms of Vanilla bug fixing and Crispy-specific features. '''Features''' * A "Deathmatch 3.0" mode has been added by AXDOOMER. * Dedicated music tracks for the 4th episode are now supported as lumps following the D_E4Mx name schema. Thanks to Death Egg for the feature request. * If the player is gibbed or crushed, the death animation is drawn in the Health widget of the Crispy HUD, keeping in sync with and applying the color translation of the actual player sprite. Thanks Zodomaniac for the suggestion. * Doomguy's mugshot never changes to evil grin or rampage face in god mode, because they don't have the glowing eyes as Zodomaniac and JNechaevsky pointed out. * Multi-resolution icon files for windows have been added by JNechaevsky and replace the icons formerly contributed by Zodomaniac. '''Improvements''' * Game parameters (i.e. -fast, nomonsters, -respawn) are now re-read from command line if and only if a new game is started or a savegame is loaded. This is not performed if playing a demo is involved. Thanks to Zodomaniac for spotting this. * Savegame names in the Quick Load and Quick Save dialogs are now printed in golden letters, thanks to Zodomaniac for the suggestion. * Menu items are now drawn with a shadow instead of shading the background when the game is paused ot the menu is active, based on an idea by JNechaevsky. * Weapon recoil pitch has been refined with the help of JNechaevsky. * The Weapon Attack Alignment feature now has 4 options (Off, Centered, Horizontal and Bobbing) that have been shaped out in discussion with Im TPhentr, JNechaevsky and Zodomaniac. * The Zombieman's firing frames are now rendered full-bright. Thanks to JNechaevsky for pointing out this inconsistency. * Missing sounds are not fatal anymore. * The "angle" crosshair has been renamed "chevron" and got its vertical offset fixed. Thanks to Woolie Wool for pointing this out. '''Bug Fixes''' * Players who left the game are no longer interpolated. Thanks to 38_ViTa_38 for the bug report. * Line breaks are now dynamically added for lines exceeding the screen width on the intermission screen. Thanks riderr3 for the bug report. * Weapons are not drawn 1 pixel too high anymore when player is idle. this bug has been re-fixed after being reported by JNechaevsky. * Weapons aren't changed anymore upon music change with the IDMUS cheat. * Restoring savegames with flipped levels has been fixed, following the April 1st incident. * Par times are now drawn conditionally on the intermission screen, as suggested by AXDOOMER. * A potential crash that would accur when using the IDBEHOLDS cheat with the Crispy HUD in the Shareware version has been eliminated. * Weapon sound sources have been implemented as requested by JNechaevsky. Sounds from these sources do not interfere with the sounds emitted by the player (e.g. "oof"), except for deliberately preserving the silent BFG trick from Vanilla. * The Ammo widget now switches colors at correct values, after Zodomaniac's report. * The "fast" parameters are now really only applied once, thanks to report by Pete-Lawrence. * Sound clipping in non-DOOM1 MAP08 has been fixed thanks to JNechaevsky and AXDOOMER. * Button states are now saved in extended savegames as suggested by JNechaevsky and Jeff Doggett. * The view cannot be changed anymore when playing back demos, resolving the ambiguity reported by Zodomaniac. * Crispy Doom now supports the SMOOTHED mod, thanks to a missing comma and an extra sprite from DR in the sprnames[] array. Thanks to AnotherLife aka VGA for the patient replies on the forums. Also, A_RandomJump() is now allowed in deaths in the cast sequence and in player weapon states, thanks to the code provided by Brad Harding and Jeff Doggett. * The Inability to load savegames while a netgame demo is playing has been resolved. Thanks to AXDOOMER for reporting this for Memento Mori PWAD where all in-game demos are netgame ones and JNechaevsky for providing the fix in the code. * A bug that has caused Revenants firing to desync in-game demos has been fixed, allowing for multi-hour stress tests by loading a PWAD that provides DEMOx lumps containing entire speed-runs. * A bug has been fixed that caused the demo sequence to crash for The Ultimate Doom and Final Doom if the DEMO4 lump was missing. Crispy Doom 4.1 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/429fa571c476bf8ffb8aa9994e63db0ef79efe23 429fa571]. === Changes of Crispy Doom 4.0 from Crispy Doom 3.5 === Crispy Doom 4.0 has been released on Mar 11, 2017. It is based on Chocolate Doom 2.3.0. This release introduces some new features and numerous improvements and bug fixes. '''Features''' * It is now possible to play demos from savegames, thanks Zodomaniac for the idea. * It is now possible to delete savegames. * Different crosshair types have been introduced: cross, angle and dot. * Additional BOOM and MBF states, sprites and code pointers (supported through BEX format) have been added. * DEHACKED states up to 4000 and 100 additional sprites and mapthings from Doom Retro have been added. '''Improvements''' * Dead players falling off an edge don't make an "oof" sound anymore. * Extended savegames have been improved to fix restoring of "fireflicker" sectors. * Fake contrast has been smoothened. * The Cyberdemon's firing frames are now rendered full-bright. * Sound is now properly clipped in non-Doom1 MAP08. * Status bar face hysteresis has been eliminated. * Toggling "always run" now plays a sound. * 16 sprite rotations are now supported. * Negative player health that can be shown optionally. Thanks to JNechaevsky for suggesting all of the above! * Smooth freelook and weapon recoil has been implemented by AlexMax, thanks for that! * DMENUPIC is now used instead of TITLEPIC for the BFG Edition of DOOM2.WAD. * The screenshot filename limit has been increased according to bzzrak's suggestion. * E2M9 is shown on the E2 intermission screen if it has been completed. * The MAXBUTTONS value is doubled to allow for using the "SPECHITS" cheat on E2M5. '''Code Clean-Up''' * The "Crispness" menu has been disabled in the Setup tool because of its size and functionality being unable to cover all the options. * The "Invert Vertical Axis" option in the Setup tool has been replaced with "Permanent Mouse Look". * DoomBSP's original BLOCKMAP builder is now used instead of MBF's one as Crispy's internal BLOCKMAP builder (with removed limits and support for flipped levels and BLOCKMAP compression). * Linedefs without tags don't apply locally anymore even in singleplayer (they send the wrong signal, maps that have this must be considered broken). '''Bug Fixes''' * In the BFG Edition the correct music track is played when starting "Hell on Earth" after the "NRFTL" episode. Thanks to Zodomaniac for the bug report. * Blood spawned by monsters that are being harmed/killed by crushers has been fixed. Thanks to JNechaevsky for the report. * Flats don't get more distorted the closer they are to the right in Low-Res mode anymore, thanks to JNechaevsky's report. * Attacking Lost Souls flying over the player now don't bump into him (being meters above ;) and deal damage as Zodomaniac reported. * While in menus player view is not changed in singleplayer mode and rotated horizontally only in multiplayer mode as pointed out by Zodomaniac. * Music number overflows at MAP >= 36 are prevented after Zodomaniac's report. * The game doesn't crash anymore on the intermission screen with maps > 33 which was spotted by Zodomaniac. * Game parameters are now re-read from the command line when starting a new game, because an in-game demo could change them in between as Zodomaniac noticed. * When playing demos, NERVE.WAD levels are now skipped only if map <= 9 (the game errored out on demo's MAP10 before). Crispy Doom 4.0 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/9a257cdf78ee4fa7d3de5bbb5be3ca733b06077f 9a257cdf78]. === Changes of Crispy Doom 3.5 from Crispy Doom 3.4 === Crispy Doom 3.5 has been released on Dec 27, 2016. This release introduces some major new features as well as refinements and bug fixes. '''Features''' * Total time for all completed levels is now counted, mostly taken from PrBoom+. * Extended savegames have been implemented that store the name of the WAD the current map has been loaded from. It can be toggled in the Crispness->Tactical menu and is enabled by default. When attempting to load a savegame, it is checked whether the map is restored from the same WAD file and if this check fails, the player is informed and given the choice to cancel or continue loading the savegame. * While at it, total level times, extra kills (i.e. respawned or resurrected monsters), [[doom:Sector_type_17_is_disabled_after_loading_a_saved_game|sectors with fireflicker]], Automap markers and the players' lookdirs are also stored in extended savegames. * Furthermore, [[doom:Moving_platform_heights_not_preserved_in_saved_games|plats in stasis]] are now properly saved and restored. '''Improvements''' * New 128x128 icons by Zodomaniac. * Playing sounds in full length is now optional. Thanks to mason1729 and Danfun64 for the suggestion. * Overflow simulation into the frame table has been dropped, so that e.g. the vbatman.deh patch for Batman Doom is not necessary anymore. * Bullet puffs are now drawn in outdoor areas. Thanks to Ptoing for the idea. * Certain projectiles (e.g. the player's plasma gun and some enemies' weaponry) aren't absorbed by outdoor walls anymore. Instead, missiles are set into the last safe state in their death state sequence. Thanks to plumsinus for the suggestion. * In Chex Quest, the kill stats are not called "kills" anymore, as in this game you don't "kill monsters" but "return Flemoids" back to where they came from. * Weapon sprites may still be rendered centered, but their actual coordinates will be left untouched. * Sprite offsets for mirrored sprites have been fixed. * Brain spitters are now disabled when the TNTEM cheat kicks in, by setting numbraintargets to -1 * Blood and bullet puff sprites are now randomly flipped. '''Bug Fixes''' * Proper music is played if a map is reloaded from a savegame during the intermission screen. Thanks to Zodomaniac for the bug report. * The rendering of the "Loading Disk Icon" has been repaired. * The "Tutti Frutti Effect" is also fixed in Low Resolution mode now. Thanks to Vesperas for noticing. * MBF sky transfers with tag 0 have been fixed. Linedefs 271 & 272 are allowed to use tag 0 to affect the whole level. Thanks to Jeffdoggett for the heads-up and the code example. * The yellow key in TNT MAP31 isn't marked as multi-player only anymore, arranged alongside the fix for the Wolf SS with the BFG-Edition IWADs. Thanks to Andrewj for the suggestion and Jeffdoggett for the code example. * The SMMU swirling flats implementation has been repaired, thanks to Bzzrak for noticing. * A crash is fixed when attempting to play music in a map > 9 (e.g. during demo playback) when NERVE.WAD is loaded. Thanks to nbmrjuhneibkr for the report. Crispy Doom 3.5 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/5d3c0e0a824abd076bacffcfd01a6df5765435fe 5d3c0e0a82]. === Changes of Crispy Doom 3.4 from Crispy Doom 3.3 === Crispy Doom 3.4 has been released on Jun 11, 2016. This is another bug-fix release with only few new features. '''Features''' * Support for for SMMU swirling flats has been added. However, this feature must be explicitely enabled by adding an ANIMATED lump and setting either anim->speed to some value higher than 65535 or by setting anim->numpics to 1 for the flat in question. So, currently no Vanilla flat will make use of it, but I hope that some future MegaWADs will ever adopt this feature. Thanks to plumsinus for the discussion. '''Improvements''' * The music is not changed anymore when changing the map. This preserves the choice expressed by using the IDMUS cheat. Thanks to plumsinus for the feature request. * Crispy Doom now masquerades itself as "Chocolate Doom 2.2.1" in network games towards both clients and servers. This means it is now possible to play mixed network games with either Choco or Crispy as servers and clients. Thanks to AlexMax for requesting this feature. * Sprites are not visible behind closed doors or in sectors with no headroom anymore. Based on a similar fix found in MBF. * It is now possible to bind the "180 turn" key to a mouse button. Thanks to Danfun64 for the feature request. * Inputs are now flipped when recording or playing back demos with -fliplevels. Demos with monsters will still desync, though, because of the way the flipped BLOCKMAP is processed. Thanks to Danfun64 for reporting this issue and to Jon and Linguica for nagging me to fix it. * The GOOBERS cheat now leaves DR-type closed doors intact. * An Archvile fire's floorz and ceilingz values are now updated to prevent it from jumping back and forth between the floor heights of its (faulty) spawn sector and the target's actual sector. Thanks to buvk and vadrig4r for discussing this issue and to Quasar for his excellent analysis in [https://www.doomworld.com/vb/post/1297952 this thread]. * Removed map objects may now finish playing their sounds. When a map object is removed from the map by P_RemoveMobj(), instead of stopping its sounds, its coordinates are transfered to a "sound object" so stereo positioning and distance calculations continue to work even after the corresponding map object has already disappeared. Thanks to Megamur for requesting and jeff-d and kb1 for discussing this feature and especially jeff-d for the original implementation idea. * The "power up" sound is now played when selecting the Berserk fist, but only if it is not playing already. This brings back a feature that was removed more than a year ago, because I considered it "useless" back then. * The "saw up" sound is now played to finish when the Chainsaw is selected. * Empty demo lumps are now ignored. That is, it is now possible to load empty DEMO1.lmp, DEMO2.lmp and DEMO3.lmp files to prevent the game from running any demos during the title screen. * The lumps needed to render the Doom 2 "Which Expansion" menu may now be provided by a PWAD. Thanks to Doomer1999 and Zodomaniac for requesting this feature. * The map names of John Romero's new E1M8B and E1M4B maps as well as the individual maps of the Master Levels for Doom 2 are now shown in the Automap. * If the "Player may walk under/over monsters" feature is enabled, the monsters' melee attacks do now include a height check. That is, it is not possible anymore to get bitten by a monsters several stories beneath or above. Thanks to Zodomaniac for reporting this issue. * Strings are not colorized anymore if they have been Dehacked. Thanks to plumsinus for reporting the issue. * The Automap colors have been improved once again and are now basically PrBoom+'s default colors. Thanks to plumsinus and ptoing for reporting this issue. '''Bug Fixes''' * The incorrect vertical offset for stretched MBF sky-transfer skies has been fixed. Thanks to plumsinus for the bug report. * The game missions pack_nerve and pack_master have been added as valid game mode/mission combinations. This should fix network games with these PWADs. Thanks to Danfun64 for the bug report. * The resolution choice in the setup tool has been adapted to only show resolutions actually supported by Crispy Doom. Thanks to Zodomaniac for the discussion. * The music lump format detection has been fixed. Technically, this means that the MUS format header check has been enabled again. With the variety of music formats supported by Crispy Doom (through SDL_Mixer) it was not sufficient anymore to assume that any file which isn't in MIDI format must be in MUS format, except when the format conversion from MUS to MIDI fails. Thanks to Zodomaniac for the bug report and for providing some lumps in FLAC format which were reported as successfully converted by the mus2mid() function. This will break playback of the D_DM2TTL lump of DECA.WAD which erroneously has its format header read "PWAD" instead of "MUS". * The checks for "FRAME" keys in [CODEPTR] sections in BEX files are now case-insensitive. * A bug in the ordering of targets upon saving and loading a game has been fixed. Thanks to jeff-d for noticing. * The lookdir variable is only changed when processing a tic. Thanks to jmtd for noticing that mouselook up/down still works in single player games even when the game is paused or in a menu. Crispy Doom 3.4 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/010e52f99e74a6f85a808f3c79504d96841e2e11 010e52f99e]. === Changes of Crispy Doom 3.3 from Crispy Doom 3.2 === Crispy Doom 3.3 has been released on Mar 10, 2016. This is mostly a bug-fix release with only few new features. '''The Good, the Bad, the Ugly''' * For the first time, this release of Crispy Doom is accompanied by some kind of a "Music Pack". It contains the fluidsynth library and all the libraries that it depends on, bundled with a freely-available soundfont. In order to use this "Music Pack", download the ZIP archive, extract its content into your Crispy Doom directory, select the "Native MIDI" music device and start Crispy Doom from the crispy-doom-music-pack.bat batch file. This file sets some environment variables that are required to convince the SDL_Mixer library to use fluidsynth with the bundled soundfont for music rendering. The Music Pack is available for download here: : http://www.greffrath.com/~fabian/crispy-doom-music-pack_2.zip * From this release on, Crispy Doom will get released without Crispy Heretic, Hexen and Strife. These three games haven't seen any serious development in the past two years and have only been carried as dead freight. This situation will not change until someone volunteers to take over active maintenance of these ports. * From this release on, the official Win32 binaries will be build on MSYS2. The supplemental shared libraries bundled with the release will also be taken from this distribution. Since ABI-compatibility with the libraries bundled with former releases cannot be guaranteed, it is recommended to install this release into a clean directory and not mix up the libraries with other releases. '''Features''' * A '''variable frame rate''' mode has been added. In addition to 35 fps mode which is the coarse, though original, frame rate on the one hand and completely uncapped frame rate, which burns up CPU cycles and battery, this adds two new rendering modes: ** 60 fps may be ideal for those playing Doom on an LCD screen which tend to refresh with that rate, ** 70 fps which means each tick is rendered twice (with interpolation) which may look much better on CRT screens. : Thanks to plumsinus, Zodomaniac and Danfun64 for testing this feature. * Support for '''ANIMATED and SWITCHES lumps''' has been added. Likewise, support for '''MBF sky transfer''' linedefs has been added. All the code has been blatantly taken from MBF. Thanks to plumsinus for suggesting these features and to Breeder for reporting a char signedness issue and confirming the fix on the Doomworld forums. '''Improvements''' * Space Marine corpses scattered around the maps are now randomly colorized. Thanks to Mr.Unsmiley for this suggestion! * There are now separate options for Colored Blood and Colored Corpses. * The original GREEN and BLUE2 color translation tables from BOOM have been re-introduced and are now used for Colored Blood and Pools of Gore. They keep some shades of red untouched and thus provide for a more balanced coloring. * Map Things are now counted on each map to provide for an additional source of pseudo-randomization (e.g. used for randomly flipping Space Marine corpses) and for more precise reporting of SPECHITS and INTERCEPTS overflows. * The loading of digital music from lumps has been simplified to support OGG, FLAC, MP3 and whatever format SDL_Mixer supports alike. * The "Secret Revealed!" message is always printed in gold, regardless of Colorized HUD texts being enabled or not. Likewise, the Crispness menu is now always rendered colorized. * The -mergedump parameter now reports the number of merged lumps and errors out if called without an additional argument. * The duration of the SSG muzzle flash has been reduced by one tic. While at it, the SSG flash frames have been turned full bright. Both fixes have been imported from MBF. * The weapon sprite is not centered anymore if its firing state's misc1 variable is set. Fix imported from Doom Retro. * The game's skill level is now logged in plain text. * The SPECHITS cheat does not change the direction of doors anymore if they are already moving. * Sound effects are now played when DR type doors are manually closed while still open or re-opened while closing. Thanks to Megamur for the original suggestion. * The flashing HOM indicator is now pulsating. * On the Automap, the map title is now printed in gray letters from the first colon onward. * One-sided walls on the Automap are now drawn in a more desaturated red to better distinguish them from red-keyed linedefs. Thanks to plumsinus for this suggestion! * It is now forbidden to start a New Game or (Quick) Load Game while recording a demo. Thanks again to plumsinus for reporting this issue! * End Game will now quit if recording or playing back a demo, just like MBF. '''Bug Fixes''' * A nasty bug has been fixed that caused multiplayer games to crash as soon as the second player joined the game. Thanks to Zodomaniac for initially reporting this bug and to Danfun64 for helping to track it down. * A graphical glitch caused by lines longer than 32767 units has been fixed. Thanks to entryway of PrBoom+ fame! * The sky offset is now correctly set if sky stretching is switched in-game (i.e. by toggling freelook, mouselook or recoil pitch). Thanks to plumsinus for reporting this bug! * The size of demo lumps recorded with the -longtics option is now calculated correctly. This bug caused the demo progress bar at the bottom of the screen to overflow beyond the frame buffer and eventually crash the game. Thanks again to plumsinus for reporting this bug! * The game is now paused if a message is shown (e.g. Quick Save confirmation) while recording a demo. Thanks to plumsinus for reporting this issue. * Movement of the projected laser crosshair is now smooth even with uncapped frame rate enabled. While at it, the crosshair is now vertically centered when idle, just like the static one. Thanks to Zodomaniac for reporting this issue. * An infinite loop caused by the Demon Speed bug has been fixed. Thanks to Zodomaniac for the bug report and the savegames that helped to reproduce the issue. * Wording in the Crispness menu has been changed from "Missiles" to "Projectiles". Thanks again to Zodomaniac for this suggestion. Crispy Doom 3.3 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/4077f339e7a70fea357b047d8b6fd722b00f283b 4077f339e7]. === Changes of Crispy Doom 3.2 from Crispy Doom 3.1 === Crispy Doom 3.2 has been released on Nov 4, 2015. This is merely a bug-fix release to fix some nasty bugs (missing icons in executables and crispy-*-setup.exe requiring admin rights) in the Windows version of Crispy Doom 3.1. Crispy Doom 3.2 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/2efd8ce2217cfbb81b9b5532228febfe32800392 fe32800392]. === Changes of Crispy Doom 3.1 from Crispy Doom 3.0 === Crispy Doom 3.1 has been released on Nov 3, 2015. This isn't considered a major Crispy Doom release, although it is based upon the Chocolate Doom 2.2.1 point release. '''Features''' * '''Colorized''' Status Bar numbers and HUD texts can now be enabled separately. * If Colorized HUD texts are enabled, the colors of the keycards and skull keys are now emphasized in HUD messages. * Pools of gore left behind from crushing Cacodemons or Hell Nobles are now colorized in their respective blood colors if Colorized Blood is enabled. Thanks to Megamur for this suggestion. * Player messaged in multiplayer games are now colorized, i.e. the "indigo" player writes in grey and the "brown" player writes in gold. * The Status Bar face background is now properly colored when playing back a multiplayer demo that was recorded by another player than Player 1. Thanks to Zodomaniac for the bug report and demo. * Optional '''Look Spring''' has been implemented for both keyboard and mouse look, leaving the former default "Locked View" as another alternative (feature originally requested by user VGA in May 2015). * A '''Direct Vertical Aiming''' mode has been added. Formerly, auto-aiming would still kick in if a monster is aimed at and thus suppress direct aiming. This mode is now optional. * It is now possible to enable '''Translucency''' separately for Missiles and Items. * It is now possible to use the Translucent Crispy HUD even if the Translucency feature is generally disabled. * Tranclucency for Health Potions has been disabled, it just looked too crappy. * To compensate for this, the BFG spray explosions are now rendered translucently. ;) * Translucent rendering in the Low Resolution mode has been fixed. * '''Screenshots''' in PNG format are now a reproduction of the actual screen content, i.e. they are saved with the same dimensions as the rendered game. * The "Clean Screenshot" feature now got its own dedicated key binding. Previously, this feature was either overlooked or triggered accidently. Thanks again to Megamur for this suggestion. * In the course of this, the "Clean Screenshot" feature has actually been fixed. It relied on the engine rendering each frame twice which it doesn't do anymore since Chocolate Doom 2.2.0. * When taking a "Clean Screenshot" the background isn't shaded and neither the Pause picture nor the Menu are drawn anymore. * The system's native qsort() implementation with a comparison function retaining stable sorting is now used for ordering the vissprites[] array. * The default OPL emulation mode is now OPL3 with correctly reversed stereo channels. * Music lumps are now properly checked for being MP3 files. This was somehow forgotten when support for music lumps in OGG and FLAC formats has been implemented in Crispy Doom 2.3. * No nukage damage is applied anymore if the NOCLIP cheat is active. * The used Screen Mode is now always reported at start-up, Thanks to [http://www.flaterco.com/kb/DOOM/ChocolateDOOM.html FlaterCo] for the idea. * Some information is printed to stderr when saving a game. '''Bug Fixes''' * Lost Souls aren't counted as Kills anymore, not even in the separate extrakills counter. * When a vertex would need to move more then 8 map units for fixing a slime trail in P_RemoveSlimeTrails(), it is probably misplaced on purpose so its original coordinate is used instead. This fixes the rendering of [https://www.doomworld.com/vb/doom-editing/74354-stupid-bsp-tricks/ Linguortals]. * Single-patched columns are normally not composited but directly read from the patch lump ignoring their originy value. This fixes an oddly rendered door in E3M5, thanks to ptoing and esselfortium for reporting. * Barrel explosions aren't randomly mirrored anymore. They have very distinct sideways shading on them and that flipping around just looks weird. Thanks to ptoing for reporting. * If a linedef with an untagged DR special is pushed from the wrong side, print a warning instead of crashing or exiting the game. * If the game is paused in Automap mode (but not in Automap Overlay mode) the screen isn't shaded anymore. Thanks to Megamur for this suggestion. * The blinking health indicator has been disregarded as distracting and has thus been disabled. Thanks to Megamur for the report. * Rendering glitches caused by integer overflows in the rw_distance variable have been fixed. This used to cause strange artifacts in e.g. planisf2.wad where sprites have been overridden by segs from far behind. Thanks to Andrey Budko for his help with fixing this. * Also, overflows in R_PointToAngle() for very long distances have been fixed. This should fix even more rendering glitches, e.g. the vertical HOM stripes at the horizon in planisf2.wad. Thanks again to Andrey Budko for helping me to fix this and taking my approach over into PrBoom+. * A bug has been fixed that caused mid-textures with out-of-screen coordinates to be entirely emitted from rendering. Thanks to vesperas for reporting this rendering glitch. * A bug reported by Valgrind as been fixed which caused reading of uninitialized memory from the postcount[] array. Since the values read from this array determine the amount of memory allocated for each column in a texture, this bug was quite a memory hog. '''Code Cleanup''' * The V_DrawPatch() function has been simplified and now uses the same code path for opaque, translucent and colorized patches. * By now, running cppcheck on the Crispy Doom source code doesn't emit any more singnificant warnings than running it over the Chocolate Doom source code. * The Berserk Pack patch is only displayed in the Ammo widget if it is actually available (i.e. not in the Shareware version). * If the DSSECRET lump is available, e.g. from a PWAD, play this instead of DSITMBK when a secret is revealed and the corresponding feature is enabled. Crispy Doom 3.1 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/ff61aa8695321e21b349583b4b43f19084d870c4 9084d870c4]. === Changes of Crispy Doom 3.0 from Crispy Doom 2.3 === Crispy Doom 3.0 has been released on July 14, 2015. This is a major Crispy Doom release, because it is based upon the Chocolate Doom 2.2.0 release. '''Major Features''' * Add optional rendering with '''Uncapped Framerate'''. Thank you very much to AlexMax for this major code contribution! In the course of this, add an FPS counter that can get activated with the new SHOWFPS cheat. * Allow for a '''variable jump height''', either "low" (i.e. 8 units of vertical momentum as in ZDoom) or "high" (i.e. the previous value of 9 units as in Hexen or current PrBoom+). * Support the '''"masterlevels.wad" all-in-one PWAD''' file from the PSN version. Thanks to quakeguy123 for the suggestion. * If "-warp" is used with "-playdemo", '''fast-forward the demo''' up to the requested map. * Fix rendering of '''single-patched transparent textures''' used as an upper or lower texture, or as a mid texture on a single-sided wall. Finally found a fix nearly one year after this has been requested by plumsinus. \o/ '''Other Enhancements''' * Add a third "Crispness" in-game menu page and get rid of some of the lesser used items in the "Cripsness" menu in the crispy-doom-setup tool. * Render missing flats as sky. * Permit binding mouse button to jump. Thanks to Jonathan Dowland for the PR. * Prevent frame buffer overflows in V_DrawPatch(). It should now be impossible to trigger a "Bad V_DrawPatch" error anymore. Also, prevent frame buffer overflows in V_CopyRect(). * Never override savegames during demo playback anymore. * Do not reset "-respawn", "-fast" and "-nomonsters" parameters in G_DoNewGame() anymore. * The NOTARGET cheat makes all monsters forget their current target. Thanks to bradharding for the idea. * In the Automap, initialize the zoomlevel on huge maps so that a 4096 units square map would just fit in. Somehow inspired by Doom Retro. * Make "bad texturecolumn" errors non-fatal. * Colorize the confusing IDBEHOLD power-up menu. Inspired by Doom Retro. * Make the DEH/BEX parser more tolerant. If a DEHACKED file does not contain a valid header signature, print a warning but do not error out. If a DEHACKED file contains BEX sections which contain non-escaped newlines and there is no new valid section marker after the newline, try to proceed parsing with the previous line parser. Fixes loading the completely messed-up Jptr_fix.bex file provided on https://www.doomworld.com/pageofdoom/lostdoom.html. * Differentiate between Weapon Recoil Thrust and Pitch options. * When a linedef is missing a first sidedef (aka. right side), replace with a dummy sidedef instead of crashing. * Allow Chocolate Doom 2.2.0 clients to connect to Crispy Doom servers. '''Bug fixes''' * In the Automap, do not calculate player coordinates for players not in game. Fixes a crash in multiplayer games reported by Marscaleb. * In Automap Overlay mode, draw the Automap beneath the bezel for smaller view sizes. Thanks to Ronald Lasmanowicz for reporting. * Also in Automap Overlay mode, for full view sizes, move the map title line to the bottom and remove the obtrusive map origin line. * Catch overflows in the SlopeDiv() function only during rendering. Fixes the single last remaining demo desync in Choco's statcheck test. * In the Crispy HUD, properly center the Berserk Pack patch in the Ammo widget. * Only interpret the second argument to the "-warp" parameter as "startmap" if it is not yet another parameter (i.e. begins with '-'). * Restrict conditions for recognizing E1M10 and MAP33 and fix Par Times for MAP10 in "commercial" game mode. * The demo progress bar is now better recognizable. * Draw the Berserk Pack in the Ammo widget and blink the Health indicator also in Automap Overlay mode. * Force redrawing the status bar when switching Automap Overlay mode back and forth. * No Rest for the Living was not special-cased if the "nerve.wad" PWAD file name was in upper case. Thanks chungy for the report. * The map name for MAP33 now always defaults to "Betray" if it has not been dehacked to another name. Thanks to quakeguy123 for the suggestion. * The highest and lowest viewing angles can now be reached with the keyboard without needing to press the corresponding button a second time. Deviating from Heretic and Hexen behaviour here. * When resurrecting dead players with the IDDQD cheat, they now face the same direction again as before (not exactly, but closer). * Fix rendering glitches caused by segs that had their vertices moved in order to prevent slime trails. The shorter a segs is, the more affected is its angle by moving its vertices. So re-calculate seg angles after moving vertices in P_RemoveSlimeTrails() and use these angles during rendering only. * Remove the limits on merged PNAMES and TEXTURE1/2 lumps. * Disable auto-loading of PWADs in the "shareware" game mode. '''Code clean-up''' * Reformat R_DrawColumnInCache(), R_GenerateComposite(), R_GenerateLookup() and R_DrawColumn() to closer match their Chocolate Doom pendants. These functions were previously replaced with code from MBF to fix the Medusa and Tutti-Frutti effects, respectively. Now, these fixes have been merged into the Vanilla functions. * Do the slow linear search in R_FlatNumForName() only if the initially returned lump number is not within the "flats" range. * In the Crispness menu, the "Jumping" and "Crosshair" items now offer multiple choices. * Get rid of the player2_t type. '''Other ports''' * Remove MAXVISPLANES, MAXDRAWSEGS, MAXSEGS and MAXVISSPRITES limits for Crispy Heretic. Crispy Doom 3.0 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/b81ba59d6649794cfba735d1ffa0e9458a6ee486 b81ba59d66]. === Changes of Crispy Doom 2.3 from Crispy Doom 2.2 === Crispy Doom 2.3 has been released on March 4, 2015. '''Automap improvements''' * The Automap now has an '''Overlay Mode''' that draws the map directly onto the player view and that can be toggled by pressing 'O'. * The Automap now has a '''Rotate Mode''' that -- as the name suggests -- keeps the player arrow oriented upwards and rotates the entire map instead. It can be toggled by pressing the 'R' key and fits nicely into the Automap Overlay Mode. * If Follow Mode is disabled and Overlay Mode is enabled, instead of panning through the map, it will remain static. * The extra triangle for the player is not drawn anymore if the IDDT cheat is enabled. * If Follow Mode is enabled an actual crosshair is drawn instead of a single point. '''Cheats improvements''' * A new "NOMOMENTUM" cheat has been added -- that is, merely enabled, all the code has already been there before -- that avoids the player from gaining momentum. It is called a "debug aid" in the source code and is pretty useless unless you want to position the player at an exact position. * A "gibs" sound is now played if a dead player is resurrected by the IDDQD cheat. * Players are now given full in-air control in IDCLIP mode. Thanks Linguica for the idea. '''New features''' * The '''secret E1M10 map''' present exclusively in the XBOX variant of The Ultimate Doom is now supported. The map is entered by triggering a secret, erm, "wall" in E1M1 and exits to E1M2. The map name is right in both the Automap and the Intermission screen and the blood splat is drawn in the latter, though only once. To enter the map with a cheat code, type IDCLEV10; to warp there from the command line, type "-warp 1 A" (or any other letter for the map). Thanks to Ronald Lasmanowicz of Wii-Doom for some suggestions regarding the implementation. If you want to try out this feature but do not own the XBOX variant of DOOM.WAD, try the patch posted here: https://www.doomworld.com/vb/wads-mods/71374-patch-ultimate-doom-1-9-xbox-doom/ . * The '''texture files''' (TEXTURE1/2 lumps) and '''patch lookup tables''' (PNAMES lumps) from PWADs do not override those of the IWAD anymore. Instead, up to 8 TEXTURE1/2 lumps and up to 4 PNAMES lumps '''get merged''', so that all textures from all loaded WADs are available. This makes it possible to e.g. run "crispy-doom -iwad freedoom2.wad -file doom1.wad", which loads the ''Freedoom: Phase 2'' IWAD and replaces all textures which are also present in the ''Doom 1 Shareware'' WAD but leaves all others intact. * '''Weapon Pitch''' has been added as a new feature and fits nicely if combined with Weapon Recoil. * The '''static crosshair''' has been added back. It is now drawn as the HU font's '+' character in the center of the screen and is rendered translucent if that is globally enabled. It has become the default, the crosshair rendered into the game world has to get activated by a new dedicated switch. * Support for '''music in OGG or FLAC formats from lumps in PWADs''' has been added. This means, it is possible to run e.g. DoomMetalVol4 by simply calling "crispy-doom -file DoomMetalVol4.wad". Please note that this feature is only available if Music is set to "Native MIDI". * PWAD files and DEHCAKED patches that are located in the config directory (i.e. the same directory that holds the crispy-doom.cfg file) and that are named following the preloadN.{wad,deh,bex} naming scheme (with N=[0..9]) are '''automatically pre-loaded at startup'''. For example, Linux users may call "ln -s /path/to/DoomMetalVol4.wad ~/.crispy-doom/preload0.wad" to automate the call cited in the previous bullet point. * It is now possible to '''dump the result of the "-merge" parameter into a file''' by means of the "-mergedump .wad" parameter. This is meant as a convenient "DEUSF replacement" for Vanilla Doom or other exotic source ports that still do not support the "-merge" feature. It is now possible to e.g create a portable variant of Requiem by calling "crispy-doom -iwad doom2.wad -file requiem.wad reqmus.wad req21fix.wad -mergedump req4all.wad". Please note that no duplicates are removed, so the resulting file gets rather big (about as big as the sum of all input files). * Crispy Doom is now able to (re-)create '''BLOCKMAP lumps''' if they are either too small or too big or if requested by the user by the "-blockmap" parameter. The actual BLOCKMAP creation routine has been taken from Lee Killough's MBF source port. * Crispy Doom is now able to load maps with '''NODES lumps in either compressed or uncompressed ZDBSP format or DeePBSP format''' and/or LINEDEFS and THINGS lumps in Hexen map format. The code is mostly adapted from PrBoom+ and Chocolate Hexen, though especially the ZDBSP nodes loading routine has been heavily modified, condensed and simplified. Please note that although it is now possible to load and explore maps in Hexen format, all interactions with their environment are most probably broken. Let me state that this feature has only been added because of a rejected patch of mine (tsts...) to add support for compressed ZDBSP nodes to PrBoom+. ;) * If running out of zone memory, Crispy Doom now allocates another zone of twice the size instead of crashing with a '''Z_Malloc error''' message. Please note that this turns Crispy Doom into a memory hog quite quickly, but it should only happen in very huge maps anyway and is still better than crashing. '''Rendering improvements''' * Wobbling long walls have been fixed, using e6y's beautiful fixed-point math formula which is also used in PrBoom+. * Visplanes with the same flats now match up far better than before. Fixed using code adapted from PrBoom+, converted to fixed point math (and already merge back into PrBoom+). * Some rendering glitches introduced by the Wiggle Fix (feature introduced in Crispy Doom 2.0) have been fixed, thanks to e6y and PrBoom+. '''Minor improvements''' * Lost Souls and spawned monsters are now counted in an extra variable that is displayed in the Automap stats next to the regular kills count, separated by a '+' sign. They are not added to the regular kills variable anymore which means one less (demo-critical) deviation from Vanilla behavior and one less switch in the Crispness menu. As a collateral damage, the special-casing of the Keen monsters in "-nomonsters" mode had to go as well, but that's to cope with. * In the Crispy HUD, the health indicator now only blinks if below 10%. * Weapon recoil is now applied after trajectories have been calculated. * Warnings are now printed whenever SPECHITS or INTERCEPTS overflows are triggered. * The previously selected savegame in the Load Game menu is now also pre-selected in the Save Game menu and the other way round. * The braintargets limit has been removed. It was previously set to 32 and broke e.g. Speed of Doom's MAP30 which has 40 braintargets. Crispy Doom 2.3 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/622653dde922a486a056b63d37dc6d7d6f26f3a4 7d6f26f3a4]. === Changes of Crispy Doom 2.2 from Crispy Doom 2.1 === Crispy Doom 2.2 has been released on January 2, 2015. '''Laser Pointer improvements''' * The laser spot now points to the center coordinates of the target. * Laser spots are now solid instead of translucent. They became practically invisible too quickly, which was exactly not their purpose. '''Cheats improvements''' * New cheat: IDBEHOLD0 will reset all player powers. * When the IDDQD cheat is activated after a player has died, his body is respawned at the current position. '''Feature review''' * The "center weapon sprite when firing" feature is now optional. * The "colored blood" feature as well as the "fix lost soul and spectre blood" features are now optional. * The "randomly mirrored corpses" feature is now optional. * A second page has been added to the in-game "Crispness" menu to account for the added options. * New optional feature: The "Kills" statistics are fixed, i.e. the "Kills" ratios shown after finishing a map or in the automap stats are adjusted each time a new monster is spawned on the map (e.g. in Nightmare mode, a Lost Soul spawned by a Pain Elemental, a monster resurrected by an Archiville or a monster spawned by a cube spitter). Furthermore, in "-nomonsters" games, Keens are now preserved but don't count towards the "Kills" statistics. * If the "player may walk over/under monsters" feature is enabled, the player is now also able to walk under solid hanging corpses. '''Further improvements''' * In the Crispy HUD, the health indicator now blinks if it is below 10%. Somehow inspired by Doom Retro. * Logging to stderr has been generally improved. Especially, the map slot, the WAD file and the skill are now printed whenever a map is loaded. Furthermore, absurd texture names in error messages have been fixed. * Linedefs with the two-sided flag but without second sidedef are now fixed in a demo-compatible way, i.e. they are rendered as one-sided walls when a mid-texture is set and transparent else. * Missing textures and flats do not lead to crashes anymore. Missing textures are rendered as HOM, i.e. black in Crispy Doom, and missing flats are replaced with the first flat available. In a very limited scope, this feature makes it possible to intermix resources and maps from different Doom missions. * Most Slime Trails are removed when loading a map. This feature has been mostly taken from Lee Killough's implementation in MBF, but has been modified for demo-compatibility to not modify actual vertex coordinates, but instead dummy "pseudovertexes" that are only used in rendering. '''Additional Bug fixes''' * The patch color translations are now cleared after drawing the "Crispness" menu cursor. This could lead to the menu picture or the status bar being rendered too dark. Thanks to fistmarine for the bug report. * Crashes in huge maps that occured due to the Wiggle-Fix (introduced in Crispy Doom 2,0) have been fixed. This has been done by upgrading the entire renderer to use 32-bit integer math. Thanks to kb1, RjY and Quasar for pointers to the relevant code changes! * Crashes caused by the laser spot getting drawn behind the view plane have been fixed. * The laser spot is not able to trigger an intercepts overflow anymore. * A crash in maps with a cube spitter but without spawn spots (e.g. MAP04 in DV.WAD) has been fixed. * A crash during map transitions has been fixed if a PWAD contains a stray MAP33 lump (e.g. INTIME.WAD). MAP33 is now only considered a regular map if the CWILV32 lump is also present. Crispy Doom 2.2 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/80bf45c902f78e3cd0212938ee176a3c6f436997 3c6f436997]. === Changes of Crispy Doom 2.1 from Crispy Doom 2.0 === Crispy Doom 2.1 has been released on November 12, 2014. This is mostly a bug fix release to treat a bug that reproducibly crashed the setup program on Windows systems. Further changes that have accumulated in the short time frame include: '''Color translation improvements''' * Color space translations are now also applied to gray shades. This means that now really '''any''' status bar can be fully colorized. The only exception are the gray drop shadows of the original IWAD status bar numbers, which are intentionally left untouched. '''Menu improvements''' * In menus, numerical values are now always shown next to sliders. * Setting mouse sensitivity to 0 now disables the corresponding axis entirely. * The Crispness menu has got a complete overhaul: ** The in-game "Crispness" menu now has a solid background. There was too much text displayed and the game graphics in the background were too distracting for it to remain legible. ** The menu items are now ordered into "Visual", "Tactical" and "Physical" categories. ** Disabled menu items are now indicated by darker colors. '''More victims of the feature clean-up''' * The "power up" sound is not played anymore when selecting the Berserker fist. This feature was hardly noticable at all and did not justify all the code that it required. Also, speaking about random non-Vanilla featuritis... * Common mapping errors (e.g. missing sidedefs) are not fixed upon level setup anymore. After all, they are errors and should be fixed during mapping and not by the engine. But, more importantly, some mapping errors are demo and network game critical. However, due to the order in which variables are juggled around in p_setup.c, it is impossible to conditionally enable the fixed based on certain variables. Instead, I attempted to implement a separate patch, but failed and thus decided to drop that feature entirely. '''Further improvements''' * The screen wiping speed and amplitude have been adjusted to be closer to Vanilla. This involved removing a work-around that has been introduced in Crispy Doom 1.0. * During demo playback, a thin (2 px) bar indicating demo progress is now printed at the bottom of the screen (similar to PrBoom+). * A warning will be printed when loading a level if it contains unknown linedef types (i.e. line->special > 141), e.g. Boom linedefs or generalized linedef types. Crispy Doom 2.1 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/66b295d461789f204d19b7181b1a11804a666728 804a666728]. === Changes of Crispy Doom 2.0 from Crispy Doom 1.5 === Crispy Doom 2.0 "Back to the Roots" has been released on October 27, 2014. This is a major Crispy Doom release, because it has merged the Chocolate Doom 2.1.0 release. '''"Back to the Roots" campaign''' * For the current release, Crispy Doom has undergone a strict feature revision. Features that are considered demo or netgame critical but are not user-visible enough to justify a dedicated switch, have been removed. This has been done to avoid development two separate feature sets, one for demos and net games and one for regular play, and to generally remain closer to actual Vanilla Doom behaviour. As a rule of thumb, features which affect the BFG Edition support, especially the No Rest For The Living expansion, and features which affect level progression in general have been left intact. Furthermore, using cheats is considered at own risk (they are disabled in netgames and screw up demo recording anyway), so they are no longer restricted to single player games. ** It is not possible anymore to switch between the regular fist and the chainsaw. ** A new level will not automatically start with the chainsaw anymore if the previous one was quit with the fist. ** The "restart level" and "go to next level" keys are considered as shortcuts to the IDCLEV cheat and are thus treated as such, i.e. they are only disabled in net games. ** The monster corpse flipping feature (based on randomization of monster corpse health values) is now unconditionally enabled, it is considered harmless. ** The vertical bobbing of ammo released by killed monsters has been removed, it has been shown to desync demos. ** The ability to gib a monster with the SSG has been removed, it has caused demos to instantly desync whenever it occured. However, that feature is not lost forever, it has already been merged into Doom Retro in the mean time. ** The weapon sprite is now unconditionally centered during shooting, but only horizontally. The vertical position affects the weapon lowering and raising times which in turn affect demo sync. ** The behaviour of the "Run" key when resurrecting a dead player has been switched. If it is held down, the most current savegame is now reloaded. If it is not pressed, the level now starts from scratch, just as in Vanilla Doom. ** Furthermore, for a cleaner diff relative to Chocolate Doom, the code added or changed by Crispy Doom has been extensively documented, commented, restructured and cleaned up where appropriate. '''Support for BEX files and lumps''' * Support for DEHACKED lumps or files in the BEX format (established in Boom and MBF) has been added. The implementation is considered complete and supports the following features: ** BITS mnemonics in regular "Things" sections, ** [CODEPTR] sections, ** [PARS] sections, ** INCLUDE directives for which the following additional rules apply: *** DEHACKED lumps loaded from PWADs may not include files, *** files that have already been included may not include other files, *** files included with the INCLUDE NOTEXT directive will have their regular "Text" sections ignored; ** [STRINGS] sections. Support for the latter has been merged from Chocolate Doom, but is enabled in Crispy Doom without any further restrictions. '''Menu improvements''' * The in-game menu now has a "Crispness" item that resembles the same item from the crispy-setup tool. It allows to enable or disable most of Crispy Doom's features from inside the game without the need to open an external application and restart the game. * The "permanent mouse look" switch has been added to the "Mouse Sensitivity" menu. * During demo recording or net games, it will be impossible to enable certain "Crispness" features for compatibility reasons. These will appear as grayed out in the menu. * Since there are no graphics for the word "Crispness" available in regular Doom resources, the menu item has to be renderd in the HU font. To avoid optical clashes with the other menu entries, these are now rendered in the HU font as well. '''Automap improvements''' * If the "Automap stats" feature is enabled, the current map coordinates will be shown in the upper right corner of the Automap in a human readable form. * Also, if the "Automap stats" feature is enabled and the current map is loaded from a PWAD, the file name of this PWAD and the map are displayed in the bottom left corner of the Automap. '''Cheat improvements''' * The IDMYPOS cheat has been improved to show the current map coordinates in the upper right corner in a human-readable form. This is the same widget that is also shown in the Automap when the "Automap stats" feature is enabled. It is shown twice as long as the text line printed by the original implementation, i.e. long enough to take a decent screenshot. * The SPECHITS cheat now also triggers tag 666/667 sectors. On maps, where specific monsters are expected to trigger that tag, their actions take precedence. On any other map, the tags are triggered as killing all "Keen" monsters would do. * The TNTHOM cheat has been introduced to toggle the flasing HOM detector on and off in-game. * IDMUS0x or IDMUSx0 will not lead to crashes anymore. * IDCLEV00 now reloads the current level and, in Doom 1, IDCLEV0x now warps to map x in the current episode. '''Color translation improvements''' * The former static color translation tables have been replaced by dynamically generated ones based on actual color space translations, thanks Paul Haeberli. Formerly, the color translation tables were identical to the ones found in Boom and MBF and only covered the red range (palette indices 176-191, 44, 45, 47, 67) so that other colors were not translated at all. With the new tables, '''any''' color can be converted to any other color in the Status Bar or the HU font. Due to limitations in the color space translation procedure, though, gray tones will remain gray. Furthermore, due to limitations in the original Doom palette, light blue tones are often mapped to grays. * The translucency filter table has been improved. First, the entire algorithm to calculate that table has been re-implemented from scratch, replacing the former implementation by Lee Killough found in Boom and MBF. Second, for the same reason mentioned before, the algorithm has been tuned by plums to emphasize the blue tones in the results. * The translucency filter initialization routine now properly indicates if the TRANMAP lump has been generated or loaded from a file at startup. '''Transparent Crispy HUD''' * When the display size is expanded beyond the regular Crispy HUD, the HUD is now rendered as translucent. Of course, this only works if transparency is generally enabled. * When the translucent Crispy HUD is enabled, all HU messages will be printed as translucent, too. '''Laser Pointer improvements''' * The static laser pointer introduced in Crispy Doom 1.1 has been removed. Its implementation has always been considered emberrassing, since it was merely four red pixels hard-coded into the center of the frame buffer. * The new laser pointer works like a "real" laser vision spot and is now an actual sprite (i.e. the '+' character of the HU font) that is projected into the game world and shows '''exactly''' where the next shot will hit. This also means that it will lock itself to a monster sprite if the next shot is going to hit that monster. * Also, the ability to change the color of the spot when a monster is targeted has been removed, because laser vision spots really just don't do that. * It is not yet decided if the old static laser spot or the color changing feature will ever return in one form or another. '''Further improvements''' * The per-line "WiggleFix" developed by kb1 and e6y has been applied. In the course of this, Lee Killough's int64 sprtopscreen fix has also been applied. * The number of supported savegames has been raised to 8. * In the cast shown after beating Doom 2, the monsters can now be rotated using the left/right keys. Furthermore, they can get skipped back and forth by using the strafe left/right keys and turned into gibs by pressing the "Run" key. * Colored blood is now enabled on a per-monster basis. That is, if a monster's death sprites have been replaced by a PWAD, this monster's blood will not get colorized anymore. This allows for disabling of colored blood for specific monsters by loading a PWAD that merely needs to include a single lump: BOSSI0 for the Baron of Hell, BOS2I0 for the Hell Knight or HEADG0 for the Cacodemon. Furthermore, Lost souls bleeding Puffs can be disabled by replacing the SKULG0 lump and Spectres bleeding Spectre blood can be disabled by replacing the SARGI0 lump. Colored blood is generally disabled in Chex Quest (where monsters do not bleed, anyway) and Hacx, with the exception of the Thorn Thing in the latter, which now bleeds green blood. * Lost Souls spawned by Pain Elementals now also bleed Puffs. * In Deathmatch games, frags are now colorized in the status bar. Positive frags are shown in green, negative ones in red and zero flags appear golden. * The red palette which is applied when the player is hurt is toned down a bit when the menu is active, so the latter remains legible. '''Further Bug and Compatibility fixes''' * Since Crispy Doom 1.5, PWADs loaded with the "-file" parameter are treated as if they were loaded with the "-merge" parameter. This has lead to issues with PWADs that contain duplicate lumps once inside the "flats" range and once outside. To overcome this issue, lumps returned by the FlatNumForName() function are now restricted forcefully into the "flats" range (as an exception, this rule does not apply to PWADs which have been merged using one of the NWT-style merging parameters). Please note that this may cause graphical glitches in the flats rendering for savegames that are saved in Crispy Doom and afterwards loaded in Chocolate Doom. These can be avoided by loading the PWAD with the "-merge" parameter in Chocolate Doom as well. * The rude extra quit messages containing profanity (feature introduced in Crispy Doom 1.5) have been disabled again. Their reception was mostly negative, and since they did not win the game anything and did not justify another dedicated switch, they had to go. Maybe they were disabled for a reason in the first place... * The checks for the NERVE.WAD PWAD file have been simplified. First, the special-casing for this PWAD is not restricted to the Doom 2 IWAD from the Doom 3 BFG Edition anymore but is now also applied if it is loaded alongside the regular Doom 2 IWAD. Second, the special-casing is not restricted to the PWAD being loaded with the "-file" command line parameter anymore but is now applied independent of the way the PWAD has been loaded. * The check for the SSG resources has been simplified and does not lead to a crash anymore if a PWAD is loaded that contains the SSG sprites, but not its sounds (e.g. Freedoom's leftover.wad). It may not work when "PC Speaker" is selected for sound effects, though. * Text lines that exceed the width of the screen will no longer lead to crashes. Thanks Dragonsbrethren for the bug report. * The fix for the common mapping error which clears the ML_TWOSIDED flag when a linedef is missing a second sidedef has been fixed and is now only applied in single player games. * The hackish code that checked for the "Run" key being pressed in the menus when selecting to quit the game has been removed. Instead, to speed up the exit sequence, the exit sounds are now omitted if it has been chosen to not show the ENDOOM screen. However, it is still possible to instantly quit from in-game by pressing "Run"+F10. * Since Crispy Doom 1.1, the default movement keys were mapped to W, A, S and D and the keys to control the menu have served as a secondary mapping. However, this led to conflicts with specific key setups in which the menu keys were given a different meaning. Therefore, the original key mapping has now been reset and an alternative key set for forward, backward, strafe left and right has been introduced to which the W, A, S, D keys have been mapped. Thanks Average for reporting this issue. * Unpausing in the menus during demo recording does not lead to desyncing anymore. * The original Doom 2 bug which leads to the sky not changing between episodes has been fixed. * The bug which caused flats getting more distorted the closer they are to the right of the screen has been fixed. Thanks to manny for reporting the bug and to bradharding for pointing me to the right code change. * The newly allocated memory regions when raising the static limits are now initialized to please Valgrind. * Holding the ESC key no longer causes the menu to repeatedly flicker on and off. Thanks joe-ilya for reporting the issue. * Upon map initialization, unknown map things (e.g. players 5 to 8 starts) are now ignored and will not lead to crashes anymore. Crispy Doom 2.0 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/76b6d1a239ddda25a8645ac0e94d45884b71fa5a 76b6d1a239]. === Changes of Crispy Doom 1.5 from Crispy Doom 1.4 === Crispy Doom 1.5 "Supernova" has been released on August 18, 2014. '''Gameplay''' * Automap improvements: ** Exit lines are now drawn in white. ** Corpses are now shown as gray rectangles when the IDDT cheat is active. ** The disappearance of map and grid lines when zooming far out in huge maps has been fixed. * A new, unique SPECHITS cheat has been added that triggers (by either using, shooting or crossing) all special lines available in the map '''at once''' -- including lines that require a key to get activated but excluding teleporters and level exits. ''While this cheat may appear completely pointless at first, it has proven useful for debugging purposes and allows map designers to "just see what happens if...". Be warned, however, that using this cheat may leave the map in a completely inconsistent state and may cause several overflows and unpredictable behaviour. Furthermore, please note that doors which can be activated from both sides will not move at all, because they will be opened by one linedef and closed again by the other one in the same gametic.'' * The ID[K]FA cheats now give the backpack to the player. * Optional weapon recoil has been added for all weapons except the fist and the chainsaw, using the recoil thrust values found in MBF and PrBoom+. This feature has to get enabled in ''crispy-doom-setup'', it is disabled by default and only generally available in regular single player games. * The extra quit messages ('''containing profanity!''') found in the source code have been enabled and are now shown with the same probability as the original messages. * The weapon sprite now gets centered upon firing. * When the "Quick Load" button (F9) is pressed before the game has been saved via "Quick Save" (F6), the regular "Load Game" menu is now shown and the selected slot is taken as the "Quick Save" slot. Also, the "Quick Save" slot is now cleared once the game is ended via "End Game" in the "Options" menu. * If the player exits a map with the Berserker fist active and is equiped with the chaisaw, he will start the next map with the chainsaw instead of the normal fist. This feature is only available in regular single player games. * The "power up" sound is now played (but only audible to the consoleplayer) each time the Berserker fist is selected. '''Technical''' * Compatibility improvements: ** The default amount of RAM that is allocated for the game has been doubled to 32 MB, the minimal amount has been quadrupled to 16 MB. ** The "Medusa" effect from multi-patch textures has been fixed by lazily replacing (and slightly adapting) R_DrawColumnInCache(), R_GenerateComposite() and R_GenerateLookup() in src/doom/r_data.c with Lee Killough's implementations from MBF. ** The "Tutti Frutti" effect from short textures has been fixed by (again lazily) replacing R_DrawColumn() in src/doom/r_draw.c with Lee Killough's implementation from MBF. ''However, this fix has not yet been applied to the other column drawing functions, namely R_Draw{Fuzz,Translated,TL}Column[Low]().'' ** The MAXVISSPRITES, MAXVISPLANES and MAXDRAWSEGS limits have been entirely removed (''i.e. the formerly static arrays now get resized dynamically''), using no Boom-derived code at all. ''However, for performance reasons (mostly sprite ordering in R_SortVisSprites()), the MAXVISSPRITES limit is currently capped at 4096 (i.e. 32 x Vanilla MAXVISSPRITES limit).'' ** The MAXSEGS limit has been raised from 32 to (SCREENWIDTH/2+1), i.e. the same value found in MBF. ** The BLOCKMAP limit has been removed, using code from PrBoom+. ** Support for extended nodes has been implemented, again using code from PrBoom+. ** The MAX_ADJOINING_SECTORS limit (''formerly up to 20 adjoining sectors'') has been removed, while keeping the one overflow (''i.e. if h == 21'') that could possibly get emulated intact -- based on code adapted from PrBoom+. ** The Vanilla savegame and demo limits are now unconditionally disabled. ** When entering an unknown special sector, the game will not crash with an error message anymore but merely print a warning. ** The file size limit (96 kB) for MID files inside PWADs has been disabled. ** Common mapping errors (''e.g. missing sidedefs'') are now fixed upon loading the maps, using code adapted from PrBoom+. ** A [[doom:Hall_of_mirrors_effect|HOM]] is fixed if both ceilingplane and floorplane are the same visplane (e.g. both are skies). ** An integer overflow in SlopeDiv() has been fixed that formerly lead to an open subsector and thus a giant slime trail in nuts.wad. : All of the above changes make it possible to properly play (and save, and exit, and record and playback demos in) ''Fraken''maps like e.g. nuts.wad, arcadia.wad and all of NOVA.wad -- gosh, what broken mess. :p * Enemy targets and tracers are now preserved when saving and loading a game. ''This is achieved by enumerating all thinker pointers upon saving a game and storing the corresponding indices in the mobj->target and mobj->tracers fields instead of the actual pointers. When loading a game, after all the thinkers have been restored, the reverse process is applied and all indices in the mobj->target and mobj->tracers fields are replaced by the corresponding current pointers again. This process is completely Vanilla compatible, as Vanilla will ignore the contents of the mobj->target and mobj->tracers fields anyway and overwrite them with NULL when loading a game. Otherwise, when loading a game saved in Vanilla, the contents of the mobj->target and mobj->tracers fields will not match any of the indices expected by Crispy Doom and will thus get overwritten with NULL, just as in Vanilla.'' * Games saved in a map with an active cube spitter will not cause a crash anymore when loaded again. ''When awakened, the cube spitter counts the number of spawn spots and saves them in the numbraintargets variable. However, in Vanilla its value is not recalculated when loading a game. This has been fixed by calling A_BrainAwake() again if numbraintargets == 0.'' * Mouse look improvements: ** Vertical mouse movement has been vastly smoothened by adjusting the scale instead of the mouse delta. ''In the former implementation the vertical mouse delta was scaled down by 1/8, i.e. the lower three bits were discarded. So by moving the mouse really slowly, it was possible to not change lookdir at all when the delta on every tic was less than 8. This has been fixed by moving the division into the rendering and slope calculations and scaling all the constants up by factor 8.'' Thanks to clarry of the doomworld forum for this highly appreciated patch! ** Centering the view with the mouse button assigned to mouse look is now more lenient. Any click with that button that is shorter than 6 tics will now center the view. ** The view is now smoothly centered after teleporting. ** ''The yslope[i] array is not recalculated anymore in R_SetupFrame() for each frame when lookdir changes; instead a lookup table is calculated once in R_ExecuteSetViewSize.'' * All PWADs given as arguments to the "-file" parameter will now get merged as if they were passed to the "-merge" parameter. * It is now possible to scroll through the menus with the same mouse buttons that are assigned for changing to the previous/next weapon -- which are usually mouse wheel down/up, respectively. * If the "Run" key is pressed while the game window is closed (e.g. by clicking on the X button in the upper right corner) or during confirmation of the quit message, the game now exits instantly (in addition to the feature introduced in Crispy Doom 1.4). * The background now slowly fades out when a menu is activated or the game is paused. '''Additional bug fixes''' * The tranmap.dat file will not get saved in the root directory on Windows systems anymore, instead it will now get saved in the same directory as e.g. crispy-doom.cfg. ''This was caused by an extra leading path separator in the tranmap.dat file name string.'' Thanks to plums of the doomworld forums for the bug report. * Visual glitches with transparent sprites caused by palette changes have now been fixed. ''The cached translucency map in the tranmap.dat file is checked for palette changes at startup. However, in the original code derived from MBF, only the first 256 bytes of the palette were compared, whereas the base palette in the PLAYPAL lump has 768 bytes. So, changes in later palette indices went completely unnoticed.'' * The state of the "always run" toggle (introduced in Crispy Doom 1.3) after loading and saving a game has been fixed. ''Enabling "always run" makes the joybspeed variable greater than the size of the array which holds the button states, which caused an out-of-bounds read in the part of the expression that determines whether speed should be true.'' Thanks to clarry of the doomworld forums for the bug report and the patch! * If things are stuck together vertically because of moving sectors, they are now allowed to move further apart. The fix for this bug has been taken from Doom Retro. Crispy Doom 1.5 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/97f1de66497c1071ac138e0c63152267b9d2978f 97f1de6649]. === Changes of Crispy Doom 1.4 from Crispy Doom 1.3 === Crispy Doom 1.4 has been released on June 12, 2014. '''Gameplay''' * Players are now allowed to walk over or underneath shootable objects, i.e. monsters and barrels. This needs to be explicitely enabled in ''crispy-doom-setup'', but is disabled by default and generally only available in single-player games. : Some restrictions still apply to this feature: :# Only the player can walk over or underneath other objects, monsters can not. This prevents multiple monsters from piling up and avoids the need for more complicated measures in the code that would potentially break compatibility. :# It is only allowed to walk over or underneath shootable objects. Most other objects in Doom have arbitrary heights hard-coded into the engine which do not necessarily match the actual appereance in the game. :# Melee attacks across differences heights are entirely unaffected by this feature. * Automap improvements: ** Keyed doors are now drawn in their respective colors. ** Thus, in order to distinguish red-keyed doors from regular walls (which were formerly drawn in red) and yellow-keyed doors from walls with ceiling level changes (which were formerly drawn in yellow), these are now drawn in darker red and yellow colors, respectively. ** Furthermore, teleporters (which were formerly drawn in just that darker red color) are now drawn in green. Additionally, WR teleporters (''linedef type #97'') are now also drawn in green if they are not secret. ** When the IDDT cheats are active: *** Secret sector boundaries are now drawn in purple until they are revealed (''i.e. as long as sector->special == 9''). *** Keys are now shown as crosses in their respective colors. *** The triangle size now represents the actual thing size. *** Countable kills are now shown as red and countable items as yellow triangles. ** Episode and Map are now explicitely shown in the automap if the map title string has been modified by means of dehacked. * HUD improvements: ** When either the God Mode cheat or the Invincibility powerup are active, Health and Armor values on the status bar are now printed in gray. ** When the fist is selected and the Berserk Pack is active, its sprite is now drawn into the previously empty Ammo field in the Crispy HUD. '''Technical''' * Gun flash sprites are now rendered translucent. * Holding down the "Run" key while taking a screen shot now takes the picture without the weapon sprite or any other HUD elements. * Holding down the "Run" key while choosing "Quit Game" now exits instantly. This actually turns Shift+F10 into an emergency "Boss Key". ;) * The presence of MAP33 is not hard-coded to the doom2.wad IWAD file of the BFG Edition anymore. Instead, it is now checked at startup and the level progression (i.e. ''MAP02-(secret exit)->MAP33->MAP03'') is adapted accordingly. * Once a savegame has been rejected to continue from after player death (by holding down the "Run" key during resurrection, feature introduced in Crispy Doom 1.3) it is not considered anymore until loaded or saved again. * The IDCLEVxy cheat now eats key presses, i.e. the second digit of the level number is not interpreted as a weapon selection anymore. * When using the -warp command line parameter for Doom 1, the episode and map numbers do not have to be separated by spaces anymore. * The flashing HOM indicator (introduced in Crispy Doom 1.3) has been turned into a command line option -flashinghom and now flashes even when the NOCLIP cheat is active. Without this option, though, HOMs are still drawn in black but do not flash in red anymore. Crispy Doom 1.4 and Crispy Doom 1.3 have merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/e0331a01741905ac665adce1da0fb04e86db4b05 e0331a0174]. === Changes of Crispy Doom 1.3 from Crispy Doom 1.2 === Crispy Doom 1.3 has been released on May 19, 2014. * Crispy Doom now has its own icon! It is is composed of the [https://www.chocolate-doom.org/wiki/images/7/77/Chocolate-logo.png Chocolate Doom icon] and a [[wikipedia:File:Potato-Chips.jpg|photo]] of potato crisps (Utz-brand, grandma's kettle-cooked style) by [https://commons.wikimedia.org/wiki/User:Evan-Amos Evan-Amos] who kindly released it into the [[wikipedia:Public_domain|public domain]]. '''New features''' * A "quick reverse" key has been added. * A key to toggle "always run" has been added, set to Caps Lock by default. * Keys have been added to "go to the next level" and to "reload the current level", but left unset by default (based on code taken from PrBoom+). * Support to invert the mouse on the vertical axis has been added. * It is now possible to toggle between Fist and Chainsaw even without the Berserk pack applied, not available in demo recording/playback and netgame mode. * Boom's TNTEM and TNTWEAPx cheats as well as PrBoom+'s/ZDoom's NOTARGET cheat have been implemented. * The Automap is now updated while playing. * The Automap markers are now centerd on the map. * Vertical aiming (introduced in Crispy Doom 1.2) is now optional, since it may have some severe undesired effects on gameplay. It can be enabled in ''crispy-doom-setup'', but is disabled by default and in demo recording/playback and netgame mode. * The corpse flipping randomization (introduced in Crispy Doom 1.2) has been improved by improving corpse health randomization. ''The flipping decision is based on the monster health after death. However, since some weapons do only apply damage in multiples of (multiples of) 2, the distribution was uneven. In Crispy Doom 1.3, moster health is now reduced by target->tics & 1, which itself is randomized by target->tics -= P_Random()&3 before. Thus, a more even distribution is achived across all weapons and monster types.'' Furthermore, the Barrel has been added as an exception that should not have its death animation flipped (thanks Doom Retro). * The lethal pellet of a point-blank SSG blast may now get an additional damage boost to achieve an occasional gib chance, disabled in demo recording/playback and netgame mode. ''This only happens if the target is within melee range (i.e. if P_CheckMeleeRange(target) returns true) and if damage >= 10 for the lethal pellet, which roughly corresponds to a 2/3 chance.'' * If the player dies and the game has been loaded or saved before in the current level, that savegame is now reloaded instead of restarting the entire level from scratch. However, since this behaviour may be undesired, it is possible to suppress it by holding the "Run" key during resurrection. * The player view is now centered when the player dies or hits hard ground (the latter only when mouse look is disabled). * Existing demo files with the same file name are now saved from getting overwritten by adding a file name suffix. That is, if you run e.g. crispy-doom -record demo and a file called demo.lmp does already exist, Crispy Doom will now save the new demo as demo-000.lmp, or demo-001.lmp if that does also exist, etc. * Entering menus while recording demos will now pause the game to prevent [[doom:Demo_desyncing_caused_by_menu_access|instant desyncing]]. * A [[doom:HOM|HOM]] has been added that flashes between black and red (but remains black if the ''noclip'' cheat is active). * Chocolate Doom 2.0.0 clients are now allowed to connect to Crispy Doom servers. '''Experimental features''' * The "flipped levels" feature has been ported over (i.e. "stolen") from Strawberry Doom. To try this out, add the -fliplevels parameter to the command line. * The SSG is now also available in Doom 1 if the required resources are made available. To try this out, run the game via e.g. crispy-doom -iwad doom.wad -merge doom2.wad doom.wad and type IDKFA or TNTWEAP9. '''Other bug fixes''' * The tutti-frutti effect which appeared around the weapon sprite under certain circumstances has been fixed. * A crash has been fixed when an Automap marker reaches the border of the map. * A bug that made the status bar and the face visible under certain circumstances when using the ''noclip'' cheat has been fixed. '''Non-Doom Ports''' * Due to popular demand, the non-Doom games that are part of Chocolate Doom have now been added back into the release. However, they have not seen any development since the Crispy Doom 1.0 release and are strictly '''unsupported'''. Feature requests are accepted if accompanied by patches. ;-) Crispy Doom 1.4 and Crispy Doom 1.3 have merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/e0331a01741905ac665adce1da0fb04e86db4b05 e0331a0174]. === Changes of Crispy Doom 1.2 from Crispy Doom 1.1 === Crispy Doom 1.2 has been released on April 10, 2014. It is mostly a bug fix release to fix the jerky mouse look in Crispy Doom 1.1. If you were unsatisfied with the mouse look performance of Crispy Doom 1.1 it is highly recommended to upgrade to Crispy Doom 1.2: * Unjerkify mouse look. ** In Crispy Doom 1.1 vertical mouse movement changed the look variable which decided how the actual player->lookdir variable will be changed in the next game tic. However, the look variable is intended for keyboard use and changes the lookdir in rather coarse steps. The code has been changed to now act directly on the player->lookdir variable which provides for a much smoother mouse look. ** Mouse sensitivity selection is moved into a separate sub-menu of the Options menu. ** Allow to set separate values for horizontal and vertical mouse sensitivity in both the Options menu and in ''crispy-doom-setup''. ** Increase mouse sensitivity thermometer range up to 20. ** Fix a crash in the Options menu when mouse sensitivity exceeds the maximum value. Instead, allow to exceed the thermometer range and print values next to it. ** Fix slopes for bullets and missiles that would not have hit a target anyway. This means that missed shots will now go into the actual vertical direction looked into. However, auto-aim will still work and guide shots to their aim. This feature is only enabled in single-player games (though probably harmless). * Monster death sprites and corpses are now flipped randomly. While the idea is stolen from Doom Retro, the implementation is completely different. The flipping decision is based on the value of thing->health, which is randomized in Doom, since all damage done by weapons is already randomized. Once a monster is dead (i.e. thing->flags & MF_CORPSE), this value remains constant. Although probably harmless, this feature is also only available in single-player games. * Fix different view frames for normal fullscreen mode and Crispy HUD. * Fix a crash when attempting to test settings from ''crispy-doom-setup'', merged from Chocolate Doom. * Fix "fast doors make two closing sounds" and "fast doors reopening with wrong sound" engine bugs. Crispy Doom 1.2 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/a1b066a0eb0d2cf5d17ad68ca833fcdd21f80725 a1b066a0eb]. === Changes of Crispy Doom 1.1 from Crispy Doom 1.0 === Crispy Doom 1.1 has been released on April 5, 2014. '''Visual Enhancements''' * '''Minimal HUD:''' When the view size is increased one step beyond fullscreen, a minimal HUD is displayed which only shows the numbers of the status bar without its background. * '''Colored Patches and Sprites:''' Color translation lookup tables have been ported over from Boom 2.02. They can be used to (optionally) colorize the numbers on the status bar, the HUD font and also the blood of certain monsters. ** '''Colored Status Bar Number:''' The numbers displayed in the status bar or the minimal HUD can get colorized depending on their value. If enabled, health is colored red, yellow, green or blue for values up to 25%, 50%, 100% and above 100% respectively. Ammo is colored red, yellow and green for values up to 25%, 50%, 100% of the ''initial'' maximum ammo, respectively. Excess ammo, that can only be carried when a backpack has been found, gets colored blue. Armor is colored green or blue depending on the type of armor the player currently wears, red means no armor. Colored numbers in the status bar can be enabled in the ''crispy-doom-setup'' tool and are disabled by default. ** '''Colored Blood:''' Blood is now colored depending on the monster class. Spectres and invisible players will now bleed spectre blood, Lost Souls will now bleed Puffs (''since spawning Puffs demands the RNG differently than spawning Blood, this feature is only enabled in single player games''), Cacodemons bleed blue blood and Hell Knights and Barons of Hell bleed green blood. ''This feature required pointing the target field of the blood mobj_t struct to the monster mobj_t struct and furthermore the addition of another field to the vissprite_t struct to keep track of the object that a sprite belongs to. This is considered [https://www.doomworld.com/vb/post/1254800 harmless], though). * '''Translucency:''' Translucency support has been ported over from Boom 2.02. As in Boom, the nececssary color tinting lookup table is created once at startup and then cached on the hard disk for subsequent uses, alternatively it can be supplied as a lump or in a PWAD file. The same objects as in Boom 2.02 have been tagged as translucent in mobjinfo[]. Additionally, rocket and barrel explosions, Lost Soul and Pain Elemental explosions, and the explosions of the Boss Brain (Icon of Sin) have been tagged translucent. Also, if a monster gets resurrected by an Arch Vile from a pool of blood and turns into a "[[doom:Ghost_monster|ghost monster]]" it will rise from the dead as translucent. Translucency can generally be enabled in the ''crispy-doom-setup'' tool and is disabled by default. * '''Shaded Menus:''' The background is shaded when a menu is active or when the game is paused. Furthermore, menu entries that currently make no sense, e.g. ''Save Game'' or ''End Game'' when no game is active, are shaded. Also, empty savegame slots are shaded in the ''Load Game'' menu. '''Enhancements affecting gameplay''' * '''Free Look:''' Free look support has been ported over from Chocolate Hexen. It is now possible to look up and down up to a certain degree using the keyboard or mouse. Using the latter, it is possible to change the viewing angle by pressing a certain key and moving the mouse vertically. Single-clicking that key without moving the mouse will center the view again. It is furthermore possible to activate permanent mouse look. Mouse viewing may feel a bit jerky, because the view angle is only increased in integers. To enable mouse viewing without also enabling vertical mouse movement, the "-novert" command line parameter had to be modified to only apply to vertical ''translational'' movement. Since both free look and permanent mouse look require stretching the sky texture, they need to get enabled in the ''crispy-doom-setup'' tool and are disabled by default. * '''Jumping:''' Jumping support has been ported over from Chocolate Hexen. It needs to get enabled in the ''crispy-doom-setup'' tool and is of course disabled by default. Since jumping involves in-air movement which may cause demos or network games to desync, this feature is entirely disabled when not in a single player game. : ''To achieve the above two features without sacrificing savegame compatibility with Chocolate Doom, a new player2_t struct has been introduced into the code to hold the additional player-speficic variables. This means that viewing angle and jumping state are not saved in savegames.'' * '''Autorun:''' When the autorun feature is active, using the "run" key will result in walking at normal speed. * '''Laser Pointer:''' A red laser pointer dot can be shown in the center of the screen to help for aiming. Optionally, the color of the dot can change from red to yellow if a target is within reach. Both options need to be enabled in the ''crispy-doom-setup'' tool and are disabled by default. * '''Secret Message:''' A centered "A secret is revealed!" message is printed in a golden font and a sound is played when a secret is found. This can be enabled in the ''crispy-doom-setup'' tool and is disabled by default. * '''Automap stats:''' Additional level statistics can be shown in the automap, including the number of kills, items and secrets as well as the level time. This feature needs to be enabled in the ''crispy-doom-setup'' tool and is disabled by default. * '''Ammo Bob:''' Ammo or weapons released by killed enemies will slightly bob vertically (''this feature is only available in a single player game''). * '''Engine Bugs:''' The "Ouch Face" and the "Picked up a Medikit that you really need" messages are shown as intended. '''Technical Enhancements''' * '''Vertical Mouse Movement:''' Separate values for mouse acceleration and threshold can now be applied for the vertical axis in the ''crispy-doom-setup'' tool. * '''PNG Screenshots:''' Screenshots can now be taken in PNG format. This feature has been merged from Chocolate Doom and is now the default in Crispy Doom. Also, a dedicated key can be set for taking screenshots, including the "Print Screen" key. * '''Automatic loading of DEHACKED lumps:''' Chocolate Doom has got the ability to load DEHACKED lumps embedded in PWAD files via the "-dehlump" command line parameter. This feature has been merged into Crispy Doom and is now the default. It can be disabled, though, via the "-nodehlump" or "-nodeh" command line parameters. The latter does additionally disable the special treatment of the chex.wad and hacx.wad IWAD files and the nerve.wad PWAD file. Furthermore, error handling has been made more tolerant for embedded and automatically loaded DEHACKED lumps: If errors are detected, error messages are printed, but the game does not abort. * '''Wolf SS:''' Although all resources of the Wolf SS enemy have been entirely removed from the BFG Edition doom2.wad IWAD file, there is still one present in the included [[doom:MAP33:_Betray_(Xbox_Doom_II)|MAP33]]. Therefore, all Wolf SS monsters are replaced with Former Humans in single player games when the BFG Edition doom2.wad IWAD is in use. * '''No-clip Cheats:''' Both idspispopd and idclip cheats are allowed in both Doom and Doom II. * '''Par Times:''' Par times for Episode 4 of The Ultimate Doom have been taken over from the BFG Edition. Also, the par time for MAP33 of the Doom II BFG Edition now reads "SUCKS!" as in the Xbox Doom II version. Both were added in a manner that does not interfere with statdump's output. * '''Sensible Defaults:''' Some default configuration values have been changed. For example, the ENDOOM screen is not displayed, the vanilla_savegame_limit and vanilla_demo_limit size limits are disabled, autorun is enabled and the game is started in full width (screenblocks = 10) by default. * '''Default Keys:''' The default keys for forward, backward, strafe left and right have been changed to '''W''', '''S''', '''A''' and '''D''', respectively. However, it is still possible to control the game with the cursor keys. '''Bug Fixes''' * The weapon sprite was one pixel too high when the player was idle: https://github.com/fabiangreffrath/crispy-doom/issues/1 . * The "No rule to make target crispy-server.6, needed by all-am" Make error was fixed: https://github.com/fabiangreffrath/crispy-doom/issues/2 . '''Crispy-Doom-Setup''' Crispy Doom now comes with its own dedicated setup tool called ''crispy-doom-setup''. Instead of chocolate-doom-setup's "Compatibility" section it has one called "Crispness" which allows to selectively activate the following features: * Enable translucency * Show colored numbers in status bar * Show level stats in automap * Show secrets revealed message * Show laser pointer * Change laser pointer color on target * Enable jumping [*] * Enable free look [*] * Enable permanent mouse look : The items marked with an asterisk require the setting of additional keys in the "Keyboard" or "Mouse" sections, respectively. '''Non-Doom Ports''' The non-Doom games that are part of Chocolate Doom have fallen behind in terms of development and haven't seen any significant changes since Crispy Doom 1.0. Thus, only Doom is included in the 1.1 release and will be from now on. Crispy Doom 1.1 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/a80aa343a53c3dd3db2434a52d70de72ec4df955 a80aa343a5]. === Changes of Crispy Doom 1.0 from Chocolate Doom 2.0.0 === Crispy Doom 1.0 has been released on March 10, 2014. '''Display Resolution''' * The display resolution in all four games (Doom, Heretic, Hexen and Strife) has been doubled in both horizontal and vertical direction, resulting in a 640x400 native resolution. * Additionally, in Crispy Doom, the ''Graphic Detail: Low'' mode has been modified to halve the display resolution also in the vertical direction (unlike Vanilla and Chocolate Doom, which only halve the resolution in the horizontal direction in this mode). As a result, in Crispy Doom in ''Graphic Resolution: Low'' mode the screen is rendered '''exactly''' identical to Chocolate Doom in ''Graphic Resolution: High'' mode. * An additional ''mode_squash_1p5x'' video mode has been introduced which stretches the screen by factor 1.25 horizontally and by factor 1.5 vertically. This results in a 800x600 display resolution to provide for a intermediate resolution suitable for vintage monitors. Due to the increased display resolution, the supported video modes differ from Chocolate Doom. The following table gives an overview over the available video modes in Crispy Doom 1.0 and Chocolate Doom 2.0.0. A red backgrond color means that a video mode has been disabled in fullscreen mode for quality concerns. Additionally, all video modes that would result in a display resolution exceeding [[wikipedia:WUXGA|WUXGA]] (1920x1200) have been disabled in Crispy Doom. {| class="wikitable" ! Video Mode ! Chocolate Doom 2.0.0 ! Crispy Doom 1.0 |- | mode_scale_1x || style="background-color: green" | 320x200 || style="background-color: green" | 640x400 |- | mode_scale_2x || style="background-color: green" | 640x400 || style="background-color: green" | 1280x800 |- | mode_scale_3x || style="background-color: green" | 960x600 || style="background-color: green" | 1920x1200 |- | mode_scale_4x || style="background-color: green" | 1280x800 || style="background-color: red" | 2560x1600 |- | mode_scale_5x || style="background-color: green" | 1600x1000 || style="background-color: red" | 3200x2000 |- | mode_stretch_1x || style="background-color: red" | 320x240 || style="background-color: green" | 640x480 |- | mode_stretch_2x || style="background-color: green" | 640x480 || style="background-color: green" | 1280x960 |- | mode_stretch_3x || style="background-color: green" | 960x720 || style="background-color: red" | 1920x1440 |- | mode_stretch_4x || style="background-color: green" | 1280x960 || style="background-color: red" | 2560x1920 |- | mode_stretch_5x || style="background-color: green" | 1600x1200 || style="background-color: red" | 3200x2400 |- | mode_squash_1x || style="background-color: red" | 256x200 || style="background-color: red" | 512x400 |- | mode_squash_1p5x || -- || style="background-color: green" | 800x600 |- | mode_squash_2x || style="background-color: red" | 512x400 || style="background-color: green" | 1024x800 |- | mode_squash_3x || style="background-color: green" | 800x600 || style="background-color: green" | 1600x1200 |- | mode_squash_4x || style="background-color: green" | 1024x800 || style="background-color: red" | 2048x1600 |- | mode_squash_5x || style="background-color: green" | 1280x1000 || style="background-color: red" | 2560x2000 |} '''Raised Limits''' The static engine limits in all four games have been raised in line with [http://prboom-plus.sourceforge.net/doom-plus.features.html Doom+]. For the four included games this results in the following limits: {| class="wikitable" ! rowspan="2" | Limit ! colspan="4" | Chocolate 2.0.0 || colspan="4" | Crispy 1.0 |- ! Doom || Heretic || Hexen || Strife || Doom || Heretic || Hexen || Strife |- | MAXVISPLANES || 128 || 128 || 160 || 200 || 128*8 || 128*8 || 160*8 || 200*8 |- | MAXVISSPRITES || 128 || 128 || 192 || 128 || 128*8 || 128*8 || 192*8 || 128*8 |- | MAXDRAWSEGS || 256 || 256 || 256 || 256 || 256*8 || 256*8 || 256*8 || 256*8 |- | MAXPLATS || 30 || 30 || 30 || 30 || 30*256 || 30*256 || 30*256 || 30*256 |- | SAVEGAMESIZE || -- || 196608 || -- || -- || -- || 196608*16 || -- || -- |- | MAXLINEANIMS || 64 || 64 || 64 || 96 || 64*256 || 64*256 || 64*256 || 96*256 |- | MAXOPENINGS || 320*64 || 320*64 || 320*64 || 320*64 || 640*64*4 || 640*64*4 || 640*64*4 || 640*64*4 |} '''Improved Support for the BFG Edition''' Chocolade Doom has no support for ''No Rest for the Living'', because its maps require a limit-removing source port. However, with its raised static limits there is no more reason to not add full support for this expansion to Crispy Doom. If the IWAD loaded with Crispy Doom is recognized as doom2.wad shipped with the Doom 3: BFG Edition, the following changes apply: * The maximum number of levels (NUMCMAPS) is raised to 33, the secret exit in MAP02 leads to MAP33 and the exit there leads back to MAP03. If furthermore the nerve.wad PWAD file can be found and no other PWAD files are loaded, the following additional changes apply: * The nerve.wad PWAD file is automatically loaded at startup, the level name patch lumps (CWILVxx) are renamed to not collide with regular Doom 2 ones. * An additional submenu as added to the New Game menu that allows for selection of either Hell on Earth (i.e. regular Doom 2) of No Rest For The Living. If otherwise the nerve.wad PWAD file is loaded via the "-file nerve.wad" parameter, the following additional changes apply: * The name of the expansion is shown in the window title and the game banner. * The TITLEPIC lump is replaced with the DMENUPIC lump. If either "No Rest For The Living" is selected from the New Game menu or if the nerve.wad PWAD file is loaded via the "-file nerve.wad" parameter, the following changes apply: * The level names in the automap and on the interlevel screen are adjusted. * The level music and par times are adjusted. * The secret exit in MAP04 leads to MAP09 and the exit there leads back to MAP05. * It is not possible to enter levels after MAP09 via ''IDCLEVxx'' cheat. * There is no "''Now entering...''" screen shown after level 8. * The intermission screen after level 6 is replaced by another one after level 8 with adjusted text. * The Doom 2 cast sequence is shown after level 8. Please note that all of the above changes do '''only''' apply for the doom2.wad IWAD file from the BFG Edition; they do '''not''' apply for the regular doom2.wad IWAD file. Consequently, it is still possible to play the nerve.wad PWAD file as a regular PWAD without special treatment if loaded alongside the regular doom2.wad IWAD file. Crispy Doom 1.0 is based on Chocolate Doom 2.0.0 with merged changes from the GIT master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/ecf457ddcbf481b451f37474b057a06e7d843b66 ecf457ddcb]. == Versioning ==
Crispy Doom 4.3 and 4.2 have merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/83a71dd850fb02e571e2b5117f234395f8869a11 83a71dd8].
Crispy Doom 4.1 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/429fa571c476bf8ffb8aa9994e63db0ef79efe23 429fa571].
Crispy Doom 4.0 is based on Chocolate Doom 2.3.0 and has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/9a257cdf78ee4fa7d3de5bbb5be3ca733b06077f 9a257cdf78].
Crispy Doom 3.5 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/5d3c0e0a824abd076bacffcfd01a6df5765435fe 5d3c0e0a82].
Crispy Doom 3.4 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/010e52f99e74a6f85a808f3c79504d96841e2e11 010e52f99e].
Crispy Doom 3.3 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/4077f339e7a70fea357b047d8b6fd722b00f283b 4077f339e7].
Crispy Doom 3.2 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/2efd8ce2217cfbb81b9b5532228febfe32800392 fe32800392].
Crispy Doom 3.1 is based on Chocolate Doom 2.2.1 and has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/ff61aa8695321e21b349583b4b43f19084d870c4 9084d870c4].
Crispy Doom 3.0 is based on Chocolate Doom 2.2.0 and has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/b81ba59d6649794cfba735d1ffa0e9458a6ee486 b81ba59d66].
Crispy Doom 2.3 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/622653dde922a486a056b63d37dc6d7d6f26f3a4 7d6f26f3a4].
Crispy Doom 2.2 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/80bf45c902f78e3cd0212938ee176a3c6f436997 3c6f436997].
Crispy Doom 2.1 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/66b295d461789f204d19b7181b1a11804a666728 804a666728].
Crispy Doom 2.0 is based on Chocolate Doom 2.1.0 and has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/76b6d1a239ddda25a8645ac0e94d45884b71fa5a 76b6d1a239].
Crispy Doom 1.5 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/97f1de66497c1071ac138e0c63152267b9d2978f 97f1de6649].
Crispy Doom 1.3 and Crispy Doom 1.4 both have merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/e0331a01741905ac665adce1da0fb04e86db4b05 e0331a0174].
Crispy Doom 1.2 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/a1b066a0eb0d2cf5d17ad68ca833fcdd21f80725 a1b066a0eb].
Crispy Doom 1.1 has merged all changes to the Chocolate Doom master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/a80aa343a53c3dd3db2434a52d70de72ec4df955 a80aa343a5].
Crispy Doom 1.0 is based on Chocolate Doom 2.0.0 with merged changes from the GIT master branch up to commit [https://github.com/chocolate-doom/chocolate-doom/commit/ecf457ddcbf481b451f37474b057a06e7d843b66 ecf457ddcb].

crispy-doom-crispy-doom-5.6.4/HACKING.md000066400000000000000000000153551360717211000176330ustar00rootroot00000000000000# Coding style guidelines The coding style guidelines for Chocolate Doom are designed to keep the style of the original source code. This maintains consistency throughout the program, and does not require the original code to be changed. Some of these guidelines are stricter than what was done in the original source; follow these when writing new code only: there is no need to change existing code to fit them. You should set tabs to *display* as eight spaces, not four. However, *indentation* should be four spaces. If possible, do not use tab characters at all. There is a utility called “expand†which will remove tab characters. For the reasoning behind this, see: http://www.jwz.org/doc/tabs-vs-spaces.html Please write code to an 80 column limit so that it fits within a standard 80 column terminal. Do not leave trailing whitespace at the end of lines. Functions should be named like this: `AB_FunctionName`. The `AB` prefix denotes the subsystem (`AM_` for automap, `G_` for game, etc). If a function is static, you can omit the prefix and just name it like `FunctionName`. Functions and global variables should always be made static if possible. Put `_t` on the end of types created with typedef. Type names like this should be all lowercase and have the subsystem name at the start. An example of this is `txt_window_t`. When creating structures, always typedef them. Do not use Hungarian notation. Do not use the goto statement. Use C++-style comments, ie. `//` comments, not `/* ... */` comments. I don’t care that this isn’t standard ANSI C. Variables should be named like this: `my_variable_name`, not like this: `MyVariableName`. In pointer variable declarations, place the `*` next to the variable name, not the type. When casting variables from one type to another, put a space after the last closing brace. When using an if, do, while, or for statement, always use the { } braces even when they are not necessary. For example, do this: ```c if (condition) { body; } ``` Not this: ```c if (condition) // NO body; ``` Write code like this: ```c typedef struct { int member1; char *member2; } my_structure_t; void FunctionName(int argument, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7) { int assign_var; assign_var = arg2 + arg3 * arg4 * (arg5 + arg6); if (foo && !bar || baz && qux || !(foo && bar && baz)) { body; } else if (xyz + 4 < abc * 4 + 3) { body; } else { body; } if (very_long_condition_like_this_one_that_forces_a_line_break && other_condition) { body; } switch (argument) { case FIRST: code; break; case SECOND: code; break; default: break; } for (a = 0; a < 10; ++a) { FunctionCall(arg1, arg2, arg3, arg4, arg_split_onto_second_line); } while (a < 10) { loop_body; } do { } while (condition); } ``` ## Editor-specific default settings If you use vim, you can put this into your `.vimrc` (or install the `localvimrc` script): ``` set expandtab set tabstop=8 set softtabstop=4 set shiftwidth=4 ``` Or, if you use Emacs, put this in your `.emacs`: ```lisp (add-hook 'c-mode-hook (lambda () (when (and buffer-file-name (string-match "chocolate-doom" buffer-file-name)) (c-set-style "bsd") (setq indent-tabs-mode nil) (setq tab-width 8) (setq c-basic-offset 4)))) ``` ## Security The C standard library has a number of unsafe functions that should be avoided when writing code for Chocolate Doom. These are: Unsafe function | Safer alternative ------------------|------------------------ `gets()` | `fgets(.., stdin)` `sprintf` | `M_snprintf()` `snprintf` | `M_snprintf()` `vsprintf` | `M_vsnprintf()` `vsnprintf` | `M_vsnprintf()` `strcpy()` | `M_StringCopy()` `strncpy()` | `M_StringCopy()` `strcat()` | `M_StringConcat()` `strncat()` | `M_StringConcat()` `strdup()` | `M_StringDuplicate()` `realloc()` | `I_Realloc()` Lots of the code includes calls to DEH_String() to simulate string replacement by the Dehacked tool. Be careful when using Dehacked replacements of printf format strings. For example, do not do this: ```c printf(DEH_String("foo %s"), s); sprintf(mybuf, DEH_String("bar %s"), t); ``` Instead do this: ```c DEH_printf("foo %s", s); DEH_snprintf(mybuf, sizeof(mybuf), "bar %s", t); ``` This does the format string replacement safely in a way that checks the arguments securely. ## Portability Chocolate Doom is designed to be cross-platform and work on different Operating Systems and processors. Bear this in mind when writing code. Do not use the `long` type (its size differs across platforms; use `int` or `int64_t` depending on which you want). Use Doom’s byte data type for byte data. `int` is assumed to be a 32-bit integer, and `short` is a 16-bit integer. You can also use the ISO C99 data types: `intN_t` and `uintN_t` where N is 8, 16, 32, 64. Be careful with platform dependencies: do not use Windows API functions, for example. Use SDL where possible. Preprocessor `#defines` are set that can be used to identify the OS if necessary: `_WIN32` for Windows and `__MACOSX__` for Mac OS X. Others are set through SDL. Try to avoid this if possible. Be careful of endianness! Doom has `SHORT()` and `LONG()` macros that do endianness conversion. Never assume that integer types have a particular byte ordering. Similarly, never assume that fields inside a structure are aligned in a particular way. This is most relevant when reading or writing data to a file or a network pipe. For signed integers, you shouldn’t assume that `(i >> n)` is the same as `(i / (1 << n))`. However, most processors handle bitshifts of signed integers properly, so it’s not a huge problem. ## GNU GPL and licensing All code submitted to the project must be licensed under the GNU GPLv2 or a compatible license. If you use code that you haven’t 100% written yourself, say so. Add a copyright header to the start of every file. Use this template: ``` // // Copyright(C) YEAR Author's name // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 description goes here* // ``` crispy-doom-crispy-doom-5.6.4/Makefile.am000066400000000000000000000037421360717211000202760ustar00rootroot00000000000000AUX_DIST_GEN = \ $(ac_aux_dir)/install-sh \ $(ac_aux_dir)/missing CMAKE_FILES= \ CMakeLists.txt \ cmake/FindSDL2.cmake \ cmake/FindSDL2_mixer.cmake \ cmake/FindSDL2_net.cmake \ cmake/Findm.cmake \ cmake/Findsamplerate.cmake \ cmake/config.h.cin DOC_FILES= \ COPYING.md \ README.md \ README.Music.md \ NEWS.md \ PHILOSOPHY.md \ ChangeLog EXTRA_DIST= \ $(AUX_DIST_GEN) \ $(CMAKE_FILES) \ $(DOC_FILES) \ NOT-BUGS.md \ README.Strife.md \ .lvimrc \ HACKING.md \ TODO.md \ rpm.spec \ win32/win_opendir.c \ win32/win_opendir.h doomdocsdir = ${docdir}/../${PROGRAM_PREFIX}doom doomdocs_DATA = $(DOC_FILES) NOT-BUGS.md #hereticdocsdir = ${docdir}/../${PROGRAM_PREFIX}heretic #hereticdocs_DATA = $(DOC_FILES) #hexendocsdir = ${docdir}/../${PROGRAM_PREFIX}hexen #hexendocs_DATA = $(DOC_FILES) #strifedocsdir = ${docdir}/../${PROGRAM_PREFIX}strife #strifedocs_DATA = $(DOC_FILES) README.Strife.md MAINTAINERCLEANFILES = $(AUX_DIST_GEN) SUBDIRS=textscreen midiproc opl pcsound data src man DIST_SUBDIRS=pkg $(SUBDIRS) if HAVE_PYTHON INSTALL : man/INSTALL.template man/simplecpp ./man/simplecpp -DDOOM -DHERETIC -DHEXEN -DSTRIFE \ -DLONG_GAME_NAME="@PACKAGE_SHORTNAME@ Doom" \ -DLONG_EXE_NAME="@PROGRAM_PREFIX@doom" \ < man/INSTALL.template > $@ endif crispy-doom-crispy-doom-5.6.4/NEWS.md000066400000000000000000002006701360717211000173370ustar00rootroot00000000000000## 3.1.0 (2019-??-??) ### General * WAD file autoloading was added - WAD and DEH files can be copied into an autoload folder to be automatically included on every game start. * Music pack configuration has been significantly simplified. By simply copying .flac/.ogg music files into a folder they will be automatically detected by filename and used. * Music packs can now be used with OPL as a fallback, and music pack config files can have any name ending in '.cfg'. * Network synchronization now uses a PID controller by default, which makes games more smooth and more stable, especially for Internet play. * UDP hole punching is now used to make servers behind NAT gateways automatically accessible to the Internet. * OPL emulation now uses Nuked OPL3 v1.8 (thanks nukeykt) * The setup tool now uses "Romero Blue" as a background (see the wiki: for more info). * The 0 and 5 keys on the number pad can now be bound independently of any other keyboard key (thanks BlooD2ool). * With aspect ratio correction disabled, the game can scale to any arbitrary size and remove all black borders in full screen mode. (thanks chungy) * The executable's location is now checked when looking for IWADs. * The IWAD files installed by Steam-on-Linux are now detected (thanks chungy). * It's now possible to use `-response` to load response files. * Default savegame name now includes the WAD filename (thanks Fabian). ### Refactorings * CMake project files have been added, replacing the Microsoft Visual Studio and Code::Blocks files. CMake maintains support for multiple IDEs and versions thereof, and reduces developer overhead when updating Chocolate Doom (huge thanks to AlexMax for this work). * Source code has been retrofitted to fix many compiler warnings and add const annotations to many variables (thanks turol). * Several functions have been hardened against incomplete reads and error conditions, and made safer (thanks turol). * Man page generation has been reworked to use autoconf macro substitution, making it eaiser for downstream forks to change the project name (thanks Jon). * We now print a meaningful error message when a savegame cannot be loaded (thanks Zodomaniac, chungy). * There's now a log file feature for the network code to aid in tracking down multiplayer bugs. ### Bug fixes * Fixed an exception thrown by the Windows kernel when debugging with GDB (thanks AXDOOMER). * Loop metadata now works properly with music packs on Windows. * Mouse movement is ignored when the game window isn't active (thanks Julia Nechaevskaya). * A bug was fixed where music would not play after pausing on an intermission screen (thanks Julia Nechaevskaya). * Timeouts when connecting to a network server were fixed (thanks @bradc6). * A long-standing bug where some visplane overflows caused crashes was fixed (thanks Mike Francis). * A multiplayer deadlock bug where clients would stop sending tics after missing tics from the server was fixed. There are both client- and server- side fixes to fix the problem when playing with older versions (thanks MadDog and Mortrixs for help tracking this down). ### Doom * Map33 intermission screen and map33-map35 automap names are emulated (thanks CapnClever). * We now exit gracefully when player starts are missing (thanks Mike Francis). * We now exit gracefully on levels with a boss brain and no boss spitter things (thanks Jason Benaim). * It's now possible to play multiplayer with gameversion=1.2. ### Heretic * P\_FindNextHighestFloor was changed to match vanilla behavior (thanks AXDOOMER). * WAD hash table is now generated for speed (thanks Mike Francis). * HHE level name replacements now apply on the intermission screen (thanks ETTiNGRiNDER). ### Hexen * ACS code has been hardened against potential security vulnerabilities. * WAD hash table is now generated for speed (thanks AXDOOMER). ### Strife * Sehacked replacements of the "empty slot" string now work. * VOICES.WAD is now found in a case-insensitive way (thanks Mike Francis). ## 3.0.0 (2017-12-30) Chocolate Doom 3.0 is a new major revision. The main change is that the codebase has been ported to SDL 2.0. This brings a number of benefits, although there have also been some other minor changes (all listed below). Huge thanks go to the entire Chocolate Doom team for working on the port to SDL2, and to all the testers who have found and reported bugs during its development. ### General * All screen scaling is now performed in hardware, meaning that the game can run in arbitrary window sizes in high quality. It can also scale to very large resolutions sizes without using large amounts of CPU or suffering degraded performance (thanks Fabian). * It is now possible to switch between windowed and full screen modes while the game is running by pressing alt + enter (thanks Jon) * Windows binaries now ship with several previously-optional DLLs. This means it is now possible to take PNG screenshots and to use digital music packs (FLAC/Ogg Vorbis formats). * The game now remembers your preferred monitor and will start on the same monitor you were using the last time you played. Windows appear centered on the screen. * The OS X launcher was tweaked somewhat, and now uses proper path controls for choosing files. FreeDM was added as an IWAD. * Configuration files on Mac OS X and Unix are now stored in locations compliant with the XDG standard (thanks chungy): - On Unix: `~/.local/share/chocolate-doom/` - On OS X: `~/Library/Application Support/chocolate-doom/` * Icons when the game is running are now a higher resolution. * Keyboard input is improved and uses the new SDL input API; on systems with on-screen keyboards, this should activate the on-screen keyboard when it is appropriate. * Menu navigation with the joystick is now much more practical, and it’s possible to bind a joystick axis to look up/down in games which support it (thanks Jon, Wintermute0110). * Several command line options were removed that were judged to be useless: `-grabmouse`, `-novert` and `-nonovert`. The mouse grabbing and novert settings can still be configured in the setup tool. * There is no longer any option in the setup tool to specify a screen resolution, since in full screen mode the game just runs at the desktop resolution without changing screen modes. If necessary, the config file options `fullscreen_width` and `fullscreen_height` can be used to explicitly set a screen resolution. * There is no longer a soft dependency on Zenity on Unix systems; the SDL API is now used to display error dialogs. * Joysticks are identified more precisely using GUID now. * A new parameter, `-savedir` allows users to specify a directory from which to load and save games. (thanks CapnClever) * The midiproc code from Eternity Engine has been imported, improving native MIDI playback on Windows and fixing a long-standing bug with music volume adjustment (thanks AlexMax, Quasar). * VGA “porch†emulation was added (thanks Jon). * The codebase now compiles with OpenWatcom (thanks Stephen Finniss). ### Doom * The GOG install of Doom 3: BFG Edition is now detected (thanks chungy) * A `-shorttics` command line parameter was added that simulates recording a vanilla demo without actually recording a demo. ### Hexen * The CD audio option for music playback has been removed; the CD playback API has been removed from SDL 2.0. However, it is possible to use digital music packs as an alternative. ### Strife * `voices.wad` is now correctly loaded before PWADs (thanks @Catoptromancy) ### libtextscreen * On OS X on machines with retina displays, text screens are rendered using a high detail font. * File selector widgets now look more visually distinctive. * There is now a convenience widget for conditionally hiding widgets. * Font handling was restructured to be based around PNG format fonts which are converted during the build and can be more easily edited. * Handling of code pages was cleaned up, so it is easier to change the code to work with a different code page now. * Lots of the UI code was changed to use UTF-8 strings. * File extensions when using the Zenity file selector are now case insensitive (thanks Jon). ## 2.3.0 (2016-12-29) ### General * Bash completion scripts are included (thanks Fabian) * The OS X launcher now supports the .lmp file format (thanks Jon) * Pitch-shifting from early versions of Doom, Heretic, and Hexen. is now supported (thanks Jon) * Aspect ratio-corrected 1600×1200 PNGs are now written (thanks Jon) * OPL emulation is more accurate (thanks Nuke.YKT) * DMX bugs with GUS cards are now better emulated (thanks Nuke.YKT) * The disk activity floppy disk icon is now shown (thanks Fabian, Jon) * Checksum calculations are fixed on big endian systems, allowing multiplayer games to be played in mixed little/big-endian environments (thanks GhostlyDeath, njankowski) * The NES30, SNES30, and SFC30 gamepads are detected and configured automatically by the Setup tool. The automap can also be configured to a joystick button (thanks Jon) * The vanilla limit of 4046 lumps per WAD is now enforced (thanks Jon, Quasar, Edward-san) * Solidsegs overflow is emulated like in vanilla (thanks Quasar) * Multiple capitalizations are now tried when searching for WAD files, for convenience when running on case sensitive filesystems (thanks Fabian). * A new command line argument, `-strictdemos`, was added, to allow more careful control over demo format extensions. Such extensions are now forbidden in WAD files and warning messages are shown. ### Build systems * There is better compatibility with BSD Make (thanks R.Rebello) * `./configure --with-PACKAGE` checks were repaired to behave logically, rather than disabling the feature (thanks R.Rebello) * Games are now installed to ${bindir} by default, eg. /usr/local/bin, rather than /usr/local/games (thanks chungy) * Visual Studio 2015 is now supported (thanks Azarien) * SDL headers and libraries can now exist in the Microsoft Visual Studio project directory (thanks Quasar) * CodeBlocks projects were repaired by removing non-existent files from the project files (thanks krystalgamer) ### Doom * Chex Quest’s level warp cheat (LEESNYDER##) now behaves more like like the original EXE (thanks Nuke.YKT) * It’s now possible to start multiplayer Chex Quest games. * Freedoom: Phase 1 <= 0.10.1 can now be loaded with mods, with -gameversion older than ultimate (thanks Fabian, chungy) * The IWAD order preference for GOG.com installs matches vanilla Final Doom: doom2, plutonia, tnt, doom (thanks chungy) * There are better safety checks against write failures when saving a game, such as when the directory is read-only (thanks terrorcide) * Versions 1.666, 1.7, and 1.8 are emulated (thanks Nuke.YKT) * Crashes are now handled more gracefully when a linedef references nonexistent sidedefs (thanks Fabian) ### Heretic * Map names were added for Episode 6, fixing a crash after completing a level in this episode (thanks J.Benaim) * Support for unlimited demo/savegames was added (thanks CapnClever) * Demo support is expanded: `-demoextend` allows demos to last longer than a single level; `-shortticfix` adjusts low-resolution turning to match Doom’s handling, and there is now `-maxdemo` and `-longtics` support (thanks CapnClever) ### Hexen * The MRJONES cheat code returns an identical string to vanilla, and enables fully reproducible builds (thanks Fabian) * An issue was fixed where the game crashed while killing the Wraithverge in 64-bit builds (thanks J.Benaim) * Support for unlimited demo/savegames was added (thanks CapnClever) * Mouse buttons for strafe left/right and move backward were added, as well as a “Double click acts as use†mouse option (thanks CapnClever) * Demo support is expanded: `-demoextend` allows demos to last longer than a single level; `-shortticfix` adjusts low-resolution turning to match Doom’s handling, and there is now `-maxdemo` and `-longtics` support (thanks CapnClever) ### Strife * Support was added for automatic loading of the IWAD from the GOG.com release of Strife: Veteran Edition on Windows (thanks chungy) * Jumping can now be bound to a mouse button (thanks Gez) * Gibbing logic was changed to match vanilla behavior (thanks Quasar) * Several constants differences from vanilla were fixed (thanks Nuke.YKT, Quasar) * When using -iwad, voices.wad from the IWAD’s directory is prefered over auto-detected DOS/Steam/GOG.com installs (thanks Quasar) ### libtextscreen * The API for creating and managing tables and columns was simplified. * It’s now possible to cycle through tables with the tab key. * Windows can now have multiple columns. ## 2.2.1 (2015-09-10) Chocolate Doom has not seen a great deal of “stable†patch releases in its history. While the development tree sees major new features and changes, the purpose of this release, and hopefully others to follow like it, is to repair some deficiencies that existed in 2.2.0. ### General * Preferences for the OS X launcher are now stored with a unique name to not conflict with other applications. (thanks Xeriphas1994) * Unix desktop entry files are now brought up to full desktop entry specification compliance. (thanks chungy, Fabian) * Unix AppData entries are now included, allowing software centers to display detailed information about the engines. (thanks chungy) * Partial XDG base directory specification compliance on Unix systems now exist to search for IWAD paths. One benefit is that $HOME/.local/share/games/doom is now a valid location to store and automatically find IWADs. (thanks chungy) ### Build systems * The Microsoft Visual Studio build system was not fully functional in 2.2.0 and has been fixed. (thanks Linguica) * The autoconf build system checks for windres only for Windows toolchains. Some Linux distributions mistakingly include the program in their native toolchains. (thanks Fabian) * A compiler hint for packed structs has been added, which otherwise broke the games when built under recent GCC releases for Windows. (thanks Fabian) ### Doom * The GOG.com releases of The Ultimate Doom, Doom II, and Final Doom are now detected and supported on Windows. (thanks chungy) * An integer overflow was used in spawn angle calculation, undefined C behavior which broke with Clang optimization. (thanks David Majnemer for insight) ### Setup tool * The help URL for the level warp menu now points to the proper wiki page, rather than the multiplayer page. * The manifest has been updated for Windows 10 compatibility. (thanks chungy) ## 2.2.0 (2015-06-09) * The Hexen four level demo IWAD is now supported. Thanks to Fabian Greffrath for his careful investigation and emulation of the demo game’s behavior in developing this. * OPL music playback has been improved in a number of ways to match the behavior of Vanilla Doom’s DMX library much more closely. OPL3 playback is also now supported. Thanks go to Alexey Khokholov for his excellent research into the Vanilla DMX library that enabled these improvements. * New gamepad configurations: - PS4 DualShock 4 (thanks Matt “3nT†Davis). - Xbox One controller on Linux (thanks chungy). - “Super Joy Box 7†USB/PC gameport adapter. * The Doom reload hack has been added back. See the wiki for more context on this: http://doomwiki.org/wiki/Reload_hack * The IWAD file from Strife: Veteran Edition is now detected automatically (thanks chungy). * It’s now possible to build outside of the source directory (thanks Dave Murphy). * MSVC project files were brought up to date (thanks dbrackett16). * M_StringDuplicate() has been added as a safer replacement for strdup() (thanks Quasar). M_StringCopy() now handles short buffers more gracefully. * The netgame discrepancy window is now dismissed by pressing enter to proceed, not escape (thanks AXDOOMER). * A couple of source files that were in the previous release and were GPL3 have been replaced by GPL2 equivalents. Previous releases that included these files should be retroactively considered GPL3. ### Bug fixes * A long-standing bug that could cause every display frame to be rendered twice was fixed (thanks Linguica, Harha, AXDOOMER). * Lots of endianness fixes were integrated that were found by Ronald Lasmanowicz during development of his Wii port of Chocolate Doom, including a fix for a bug that could cause monsters to become partially invisible. * DeHackEd files without a newline character at the EOF are now correctly parsed (thanks Fabian). * An infinite loop that could occur in the weapon cycling code was fixed (thanks raithe, Fabian). * Mouse input triggered by cursor warp was fixed (thanks Super6-4). * Loop tags in substitute music files are ignored if both of the loop tags are equal to zero. This makes us consistent with other source ports that support the tags. * It’s now possible to more conveniently play back demo .lmp files with names that end in the all-caps “.LMP†(thanks Ioan Chera). * Some code that accessed memory after freeing it was fixed. Two new parameters, -zonezero and -zonescan, were added to try to help detect these cases. * Mistaken assumptions about representations of booleans that affected some ARM systems were fixed (thanks floppes). * memcpy() uses on overlapping memory were changed to use memmove(), fixing abort traps on OpenBSD (thanks ryan-sg). * Hyphens in manpages were fixed (thanks chungy, Fabian). * Lots of compiler build warnings were fixed (thanks Fabian). ### Setup tool * The setup tool now has help buttons for its various different screens, which link to articles on the wiki that give more information (thanks to chungy for helping to put the wiki pages together). * A fix was applied for a buffer overrun that could occur if the user had lots of IWAD files installed (thanks Fabian). * A crash related to username lookup was fixed. * It’s now possible to connect via the setup tool to multiplayer servers that are not listening on the default port (thanks AXDOOMER). ### Doom * Sky transitions when emulating the id anthology version of the Final Doom executable were fixed (thanks AXDOOMER, Fabian, chungy). * Structure fields in the stair-building functions were fixed to be deterministic, fixing a desync in mm09-512.lmp (thanks Fabian). ### Hexen * A bug with texture names that had long names was fixed (thanks ETTiNGRiNDER). * Minotaur spawn time is now stored in little endian format, fixing a bug that affected compatibility with Vanilla savegames on big endian systems. * Code that starts ACS scripts is no longer compiler-dependent. ### Strife (all these are thanks to Quasar) * Sound priority was changed, so that the ticking sound that Stalker enemies make while active matches Vanilla behavior (thanks GeoffLedak). * Minor fixes to game behavior to match Vanilla, discovered during development of Strife: Veteran edition. * Behavior of descending stairs was fixed to match Vanilla. * Inventory items beyond the 8-bit range are now allowed in netgames. * Automap behavior better matches Vanilla now. * Multiplayer name changes were fixed. * Sound origin behavior for switches was fixed. * Teleport beacon behavior was fixed. * Default Strife skill level and screen size were changed to match Vanilla. * Bug was fixed where Rowan would not always take Beldin’s ring. * Totally-invisible objects are now displayed correctly, and a Vanilla glitch with Shadow Acolytes is correctly emulated. * The level name for MAP29 (Entity’s Lair) was fixed (thanks chungy). ### libtextscreen * The main loop now exits immediately once all windows are closed (thanks AXDOOMER). * The large font is no longer selected based entirely on screen size. ## 2.1.0 (2014-10-22) Chocolate Doom now supports high-quality substitute music packs that are used in place of the original MIDI music tracks. I’m hoping to put together high-quality recordings of the music for all supported games using the Roland SC-55 synthesizer originally used to compose Doom’s music (thanks twipley and MusicallyInspired). Support for joysticks and gamepads has been significantly improved in this version. Most gamepads should now work; if they don’t, please report a bug. A number of gamepads are now automatically detected and configured automatically; if yours is not, you can help by sending in details. See the following page: http://www.chocolate-doom.org/wiki/index.php/Adding_your_gamepad OPL MIDI playback has been significantly improved, and problems with most tracks should now be resolved. Multi-track MIDI files now play back properly, MIDI tempo meta events are now supported and problems with stuttering when playing certain tracks have been fixed. If you still have problems with OPL playback, let me know. Also of note is that Chocolate Doom now has a document that describes the philosophy of the project and the reasoning behind its design (see PHILOSOPHY distributed with the source). ### Other new features * There is now a -dehlump command line parameter to load Dehacked files contained inside WAD files (thanks Fabian Greffrath). * PNG format screenshots are now supported, and there is a dedicated key binding for taking screenshots without needing to always use -devparm (thanks Fabian Greffrath). The PrintScreen key can be used as a key binding (thanks AXDOOMER). * There is now a config file variable (snd_maxslicetime_ms) to control the sound buffer size, and the default is more precise to reduce sound latency (thanks Holering). * You can now use an external command for music playback (thanks Holering). * All games now detect if you’re tring to play using the wrong type of IWAD (doom.wad with Hexen, etc.) and exit with a helpful error message. A couple of users made this mistake after the 2.0 release introduced support for the new games. * The OS X app now associates with .hhe and .seh files. * There is now a -nodes parameter that automatically starts a netgame when a desired number of players have joined the game. * There is now more extensive documentation about music configuration (README.Music). * On Linux, a GUI pop-up is used when the game quits with an error to show the error message (thanks Willy Barro). * There are now Linux .desktop files for all supported games (thanks Mike Swanson). * The -geometry command line parameter can now be used to specify fullscreen or windowed modes, eg. -geometry 640x480w or -geometry 1024x768f. (thanks Mike Swanson) ### Doom * Minor workarounds were added to allow the BFG Edition IWADs to be used without crashing the game (thanks Fabian Greffrath). * GUS patch files included with the BFG Edition are now automatically detected. * The “no fog on spawn west†Vanilla bug is now correctly emulated (thanks xttl). * Behavior of older versions of Doom back to v1.666 can now be emulated. * The new Freedoom IWAD names are now recognized and supported. * Freedoom’s DEHACKED lump can now be parsed and is automatically loaded when a Freedoom IWAD file is used (thanks Fabian Greffrath). A new command line parameter, -nodeh, can be used to prevent this from being loaded. * Behavior of the M_EPI4 menu item is now correctly emulated based on game version (thanks AXDOOMER). * IDCLEV up to MAP40 is now supported, to match Vanilla (thanks AXDOOMER). * Level warping on the command line (-warp) to episodes higher than 4 is possible, matching Vanilla behavior (thanks plumsinus). * The -cdrom command line parameter writes savegames to the correct directory now, matching Vanilla Doom behavior (thanks AXDOOMER). * The Doom II mission pack to use can now be specified manually on the command line with the -pack parameter (thanks chungy) ### Heretic * Weapon cycling keys for mouse and joystick were fixed (thanks Sander van Dijk). * The -timedemo parameter has been fixed, and -playdemo now handles full paths correctly. * A bug when panning the map was fixed (thanks Chris Fielder). * A savegame bug where plat_t structures were not restored correctly was fixed (thanks romeroyakovlev). * Rebinding of the pause key was fixed (thanks Fabian Greffrath). ### Hexen * Music workarounds have been added so that it is possible to play using the Mac version of the Hexen IWAD file. * Weapon cycling keys for mouse and joystick were fixed (thanks Sander van Dijk). * The -timedemo parameter has been fixed, and -playdemo now handles full paths correctly. * There are now key bindings to allow the artifact keys to be rebound (thanks Fabian Greffrath). * Rebinding of the pause key was fixed (thanks Fabian Greffrath). * Maximum level number was extended to MAP60, allowing multiplayer games using the Deathkings add-on. * The startup screen can now be aborted by pressing escape, like in Vanilla. * Desync when playing back DEMO1 was fixed (thanks alexey.lysiuk). ### Strife * “Show mission†key is configured properly in setup (thanks Sander van Dijk). * Default music volume level now matches Vanilla (thanks AXDOOMER). * Teleport beacon allegiance was fixed to match Vanilla (thanks Quasar). * The stair building code now more closely matches Vanilla (thanks Quasar). * Torpedo weapon changing behavior now matches Vanilla (thanks Quasar). ### Cleanups * The copyright headers at the top of all source files have been vastly simplified. * Unsafe string functions have been eliminated from the codebase. Thanks to Theo de Raadt for calling out Chocolate Doom by name (alongside many other packages) for still using unsafe functions like strcpy: http://marc.info/?l=openbsd-tech&m=138733933417096 * vldoor_e enum values are now namespaced to avoid potential conflicts with POSIX standard functions. ### Bug fixes * WAD and Dehacked checksums are now sent to clients and checked correctly when setting up netgames. * A bug was fixed that caused sound not to work in multiplayer games (thanks to everyone who reported this, and for AXDOOMER and Quasar for help in fixing it). * The “D_DDTBLU disease†bug affecting certain MIDI files has been fixed (thanks plumsinus, Brad Harding and Quasar). * Calculation of the -devparm “ticker†dots was fixed to match Vanilla behavior (thanks _bruce_ and AXDOOMER). * The PC speaker code now supports the full range of sound frequencies (thanks Gez). * Annoying “jumping†behavior when grabbing the mouse cursor was fixed. * The screen is now initialized at the native bit depth by default, to avoid problems with systems that don’t handle 8-bit screenbuffers very well any more. * The --docdir argument to the configure script is now honored (thanks Jan Engelhardt). * Various issues with the build were fixed (thanks Jan Engelhardt and Fabian Greffrath). * Backwards parameters were fixed in the sound code (thanks proteal). * A crash was fixed when running fullscreen with the -2 parameter (thanks Fabian Greffrath). * A crash when using large values of snd_channels was fixed (thanks AXDOOMER). * A resource leak in the BSD PC speaker code was fixed (thanks Edward-san). * Windows resource files were fixed for Windows 7 (thanks Brad Harding). * A hard to trigger crash caused by a realloc() in the WAD code was fixed (thanks Fabian Greffrath for debugging). * A bug has been fixed where Chocolate Doom would stay running in the background on Windows after quitting. SDL_Quit() is called now (thanks johnsirett, Brad Harding, Quasar). * String replacements in dehacked lumps can now be overridden if a subsequent dehacked patch replaces the same string. ### libtextscreen * Clicking on scrollbars now jumps to the correct position (thanks AXDOOMER). * A use-after-free bug has been fixed where a click in a window that causes the window to close could lead to a crash (thanks DuClare). * Characters that are unprintable in the Extended ASCII chart are just ignored when they’re typed, rather than appearing as an upside-down question mark (thanks AXDOOMER). ## 2.0.0 (2013-12-09) This is version 2.0 of Chocolate Doom! This new major version is released to celeberate the 20th anniversary of the first release of Doom in 1993. Happy Birthday Doom! This new version has some major changes compared to the 1.0 series: * The codebase now includes Chocolate Heretic and Chocolate Hexen. These are based on the GPL source code released by Raven Software. * Also included is Chocolate Strife. This was developed through a mammoth four year reverse engineering project conducted by James “Quasar†Haley and Samuel “Kaiser†Villareal. The result is the most accurate reproduction of Strife to date, including full demo and savegame compatibility. See README.Strife for more information. ### Minor features that are nonetheless worth mentioning * Chocolate Doom now includes a -statdump command line option, which emulates the output of the statdump.exe tool. This is used to implement a form of regression testing (statcheck) that directly compares against the Vanilla behavior. * Chocolate Heretic includes HHE patch file support, and I believe is the first Heretic port to include this feature. * GUS “pseudo-emulation†is now supported. This does not fully emulate a GUS, but Doom’s DMXGUS lump can be used to generate a Timidity configuration file that plays music using the GUS patch set. * The setup tool now includes a built-in server browser, for use when selecting a server to join. Version 2.0 of Chocolate Doom has been in development for a long time, and there have been many bugs fixed over this time, too many to list here. Thanks to all the people who have tested it and diligently reported bugs over this time, and to all the people who have tested the beta releases over the past couple of months. Your contributions have been essential and invaluable. ## 1.7.0 (2012-06-09) * Fixed gnome-screensaver desktop file (thanks Rahul Sundaram). * Updated COPYING to current version of GPL2 (thanks Rahul Sundaram). * Running servers now re-resolve the address of the master server occasionally, to adapt to DNS address changes. * Error dialog is no longer shown on OS X when running from the console. * The Makefiles no longer use GNU make extensions, so the package builds on OpenBSD. * There is now an OPL MIDI debug option (-opldev), useful for when developing GENMIDI lumps. * A workaround for SDL mouse lag is now only used on Windows (where it is needed), and not on other systems. This fixes Chocolate Doom on AmigaOS (thanks Timo Sievänen). * UTF-8 usernames are supported, and Windows usernames with non-ASCII characters are now supported (thanks AXDOOMER). ### Compatibility * Palette accuracy is reduced to 6 bits per channel, to more accurately emulate the PC VGA hardware (thanks GhostlyDeath). * Fixed teleport behavior when emulating the alternate Final Doom executable (-gameversion final2) (thanks xttl). ### Bugs fixed * Fixed weapon cycling keys when playing in Shareware Doom and using the IDKFA cheat (thanks AXDOOMER). * Fixed the default mouse buttons in the setup tool (thanks AXDOOMER). * Chat macros now work when vanilla_keyboard_mapping is turned off. * Default chat macros were fixed in the setup tool. * Ping time calculation was fixed for LAN search, and made more accurate for all searches. * Fixed bug with detection of IWAD type by filename (thanks mether). ### libtextscreen * There is now limited UTF-8 text support in the textscreen library, used in the label and input box widgets. * Scroll bar behavior was fixed (thanks AXDOOMER). * Input boxes stop editing and save when they lose their focus, correcting a previous counterintuitive behavior (thanks Twelve). * The numeric keypad now works properly when entering text values (thanks Twelve). ## 1.6.0 (2011-05-17) * The instructions in the INSTALL file are now customized for different platforms, and each binary package contains a version with instructions specific to the platform that it is targetting. This should help to avoid confusion that some users have reported experiencing. * The display settings window in the setup tool has been reorganised to a better arrangement. * It is now possible to load .lmp files (and play back demos) with long filenames (thanks blzut3). * In the setup tool, it is now possible to hold down shift when changing key/mouse/joystick bindings to prevent other bindings to the same key from being cleared (thanks myk). * The joystick menu in the setup tool now has a test button (thanks AXDOOMER). * Specifying the -privateserver option implies -server (thanks Porsche Monty). * The Mac OS X .dmg package now has a background and looks generally more polished. * In Mac OS X, it is now possible to simply double click an IWAD file in the Finder to configure its location within the launcher. * Freedesktop.org desktop files are now installed for Doom and the setup tool, which will appear in the main menu on desktop environments such as Gnome and KDE (thanks Adrián Chaves Fernández). * The Chex Quest dehacked patch (chex.deh) will now be detected if it is in the same directory as the IWAD file. ### Compatibility * Added support for the alternate version of the Final Doom executable included in some later versions of the Id Anthology. This version fixed the demo loop crash that occurred with the “original†Final Doom executable. This executable can be selected on the command line with -gameversion final2. It has been made the default when playing with the Final Doom IWADs (the original behavior can be selected with -gameversion final). (thanks Porsche Monty, Enjay). * Very short sound effects are not played, to better emulate the behavior of DMX in Vanilla Doom (thanks to Quasar for help in investigating this). * The null sector dereference emulation code has been imported from Prboom+ - this fixes a desync with CLNJ-506.LMP (thanks entryway). * The IDMUS cheat doesn’t work when emulating the v1.9 executable (thanks AXDOOMER). ### Bugs fixed * Menu navigation when using joystick/joypad (thanks AXDOOMER). * For configuration file value for shift keys, use scan code for right shift, not left shift (thanks AXDOOMER). * Default joystick buttons for the setup tool now match Vanilla (thanks twipley). * Visual Studio project files work again (thanks GhostlyDeath). * The default sfx/music volume set by the setup tool is now 8 instead of 15, matching the game itself. (thanks AXDOOMER). * Weapon cycling from the shotgun to the chaingun in Doom 1 now works properly (thanks AXDOOMER). * MIDI playback that locked up when using an empty MUS / MIDI file (thanks AXDOOMER). * Default sampling rate used by setup tool changed to 44100Hz, to match the game default (thanks AXDOOMER). * Cheat codes and menu hot keys now work when shift is held down or capslock turned on (thanks AXDOOMER). ### libtextscreen * The background on GUI controls now lights up when hovering over them, so that it is more obvious what you are selecting. * It is now possible to type a “+†in input boxes (thanks AXDOOMER). * It is possible to use the mouse wheel to scroll through scroll panes. * Clicking on scroll bars now moves the scroll handle to a matching location. * Clicking outside a dropdown list popup window now dismisses the window. * Window hotkeys that are an alphabetical letter now work when shift is held down or capslock turned on (thanks AXDOOMER). ## 1.5.0 (2011-01-02) Big changes in this version: * The DOSbox OPL emulator (DBOPL) has been imported to replace the older FMOPL code. The quality of OPL emulation is now therefore much better. * The game can now run in screen modes at any color depth (not just 8-bit modes). This is mainly to work around problems with Windows Vista/7, where 8-bit color modes don’t always work properly. * Multiplayer servers now register themselves with an Internet master server. Use the -search command line parameter to find servers on the Internet to play on. You can also use DoomSeeker (http://skulltag.net/doomseeker/) which supports this functionality. * When running in windowed mode, it is now possible to dynamically resize the window by dragging the window borders. * Names can be specified for servers with the -servername command line parameter. * There are now keyboard, mouse and joystick bindings to cycle through available weapons, making play with joypads or mobile devices (ie. without a proper keyboard) much more practical. * There is now a key binding to change the multiplayer spy key (usually F12). * The setup tool now has a “warp†button on the main menu, like Vanilla setup.exe (thanks Proteh). * Up to 8 mouse buttons are now supported (including the mousewheel). * A new command line parameter has been added (-solo-net) which can be used to simulate being in a single player netgame. * There is now a configuration file parameter to set the OPL I/O port, for cards that don’t use port 0x388. * The Python scripts used for building Chocolate Doom now work with Python 3 (but also continue to work with Python 2) (thanks arin). * There is now a NOT-BUGS file included that lists some common Vanilla Doom bugs/limitations that you might encounter (thanks to Sander van Dijk for feedback). ### Compatibility * The -timer and -avg options now work the same as Vanilla when playing back demos (thanks xttl) * A texture lookup bug was fixed that caused the wrong sky to be displayed in Spooky01.wad (thanks Porsche Monty). * The HacX v1.2 IWAD file is now supported, and can be used standalone without the need for the Doom II IWAD (thanks atyth). * The I_Error function doesn’t display “Error:†before the error message, matching the Vanilla behavior. “Error†has also been removed from the title of the dialog box that appears on Windows when this happens. This is desirable as not all such messages are actually errors (thanks Proteh). * The setup tool now passes through all command line arguments when launching the game (thanks AXDOOMER). * Demo loop behavior (ie. whether to play DEMO4) now depends on the version being emulated. When playing Final Doom the game will exit unexpectedly as it tries to play the fourth demo - this is Vanilla behaviour (thanks AXDOOMER). ### Bugs fixed * A workaround has been a bug in old versions of SDL_mixer (v1.2.8 and earlier) that could cause the game to lock up. Please upgrade to a newer version if you haven’t already. * It is now possible to use OPL emulation at 11025Hz sound sampling rate, due to the new OPL emulator (thanks Porsche Monty). * The span renderer function (used for drawing floors and ceilings) now behaves the same as Vanilla Doom, so screenshots are pixel-perfect identical to Vanilla Doom (thanks Porsche Monty). * The zone memory system now aligns allocated memory to 8-byte boundaries on 64-bit systems, which may fix crashes on systems such as sparc64 (thanks Ryan Freeman and Edd Barrett). * The configure script now checks for libm, fixing compile problems on Fedora Linux (thanks Sander van Dijk). * Sound distortion with certain music files when played back using OPL (eg. Heretic title screen). * Error in Windows when reading response files (thanks Porsche Monty, xttl, Janizdreg). * Windows Vista/7 8-bit color mode issues (the default is now to run in 32-bit color depth on these versions) (thanks to everybody who reported this and helped test the fix). * Screen borders no longer flash when running on widescreen monitors, if you choose a true-color screen mode (thanks exp(x)). * The controller player in a netgame is the first player to join, instead of just being someone who gets lucky. * Command line arguments that take an option now check that an option is provided (thanks Sander van Dijk). * Skill level names in the setup tool are now written the same as they are on the in-game “new game†menu (thanks AXDOOMER). * There is no longer a limit on the lengths of filenames provided to the -record command line parameter (thanks AXDOOMER). * Window title is not lost in setup tool when changing video driver (thanks AXDOOMER). ### libtextscreen * The font used for the textscreen library can be forced by setting the TEXTSCREEN_FONT environment variable to “small†or “normalâ€. * Tables or scroll panes that don’t contain any selectable widgets are now themselves not selectable (thanks Proteh). * The actions displayed at the bottom of windows are now laid out in a more aesthetically pleasing way. ## 1.4.0 (2010-07-10) The biggest change in this version is the addition of OPL emulation. This emulates Vanilla Doom’s MIDI playback when using a Yamaha OPL synthesizer chip, as was found on SoundBlaster compatible cards. A software OPL emulator is included as most modern computers do not have a hardware OPL chip any more. If you do have one, you can configure Chocolate Doom to use it; see README.OPL. The OPL playback feature is not yet perfect or 100% complete, but is judged to be good enough for general use. If you find music that does not play back properly, please report it as a bug. ### Other changes * The REJECT overflow emulation code from PrBoom+ has been imported. This fixes demo desync on some demos, although others will still desync. * Warnings are now generated for invalid dehacked replacements of printf format strings. Some potential buffer overflows are also checked. * The installation instructions (INSTALL file) have been clarified and made more platform-agnostic. * The mouse is no longer warped to the center of the screen when the demo sequence advances. * Key bindings can now be changed for the demo recording quit key (normally ‘q’) and the multiplayer messaging keys (normally ‘t’, ‘g’, ‘i’, ‘b’ and ‘r’). ## 1.3.0 (2010-02-10) * Chocolate Doom now runs on Windows Mobile/Windows CE! * It is possible to rebind most/all of the keys that control the menu, shortcuts, automap and weapon switching. The main reason for this is to support the Windows CE port and other platforms where a full keyboard may not be present. * Chocolate Doom now includes a proper Mac OS X package; it is no longer necessary to compile binaries for this system by hand. The package includes a simple graphical launcher program and can be installed simply by dragging the “Chocolate Doom†icon to the Applications folder. (thanks to Rikard Lang for extensive testing and feedback) * The video mode auto-adjust code will automatically choose windowed mode if no fullscreen video modes are available. * The zone memory size is automatically reduced on systems with a small amount of memory. * The “join game†window in the setup tool now has an option to automatically join a game on the local network. * Chocolate Doom includes some initial hacks for compiling under SDL 1.3. * Recent versions of SDL_mixer include rewritten MIDI code on Mac OS X. If you are using a version of SDL_mixer with the new code, music will now be enabled by default. * Windows Vista and Windows 7 no longer prompt for elevated privileges when running the setup tool (thanks hobbs and MikeRS). * The Windows binaries now have better looking icons (thanks MikeRS). * Magic values specified using the -spechit command line parameter can now be hexadecimal. * DOOMWADDIR/DOOMWADPATH can now specify the complete path to IWAD files, rather than the path to the directory that contains them. * When recording shorttics demos, errors caused by the reduced turning resolution are carried forward, possibly making turning smoother. * The source tarball can now be used to build an RPM package: rpmbuild -tb chocolate-doom-VER.tar.gz ### Compatibility * The A_BossDeath behavior in v1.9 emulation mode was fixed (thanks entryway) * The “loading†disk icon is drawn more like how it is drawn in Vanilla Doom, also fixing a bug with chook3.wad. * Desync on 64-bit systems with ep1-0500.lmp has (at long last) been fixed (thanks exp(x)). * Donut overrun emulation code imported from Prboom+ (thanks entryway). * The correct level name should now be shown in the automap for pl2.wad MAP33 (thanks Janizdreg). * In Chex Quest, the green radiation suit colormap is now used instead of the red colormaps normally used when taking damage or using the berserk pack. This matches Vanilla chex.exe behavior (thanks Fuzztooth). * Impassible glass now displays and works the same as in Vanilla, fixing wads such as OTTAWAU.WAD (thanks Never_Again). ### Bugs fixed * Memory-mapped WAD I/O is disabled by default, as it caused various issues, including a slowdown/crash with Plutonia 2 MAP23. It can be explicitly re-enabled using the “-mmap†command line parameter. * Crash when saving games due to the ~/.chocolate-doom/savegames directory not being created (thanks to everyone who reported this). * Chocolate Doom will now run under Win95/98, as the SetProcessAffinityMask function is looked up dynamically. * Compilation under Linux with older versions of libc will now work (the semantics for sched_setaffinity were different in older versions) * Sound clipping when using libsamplerate was improved (thanks David Flater) * The audio buffer size is now calculated based on the sample rate, so there is not a noticeable delay when using a lower sample rate. * The manpage documentation for the DOOMWADPATH variable was fixed (thanks MikeRS). * Compilation with FEATURE_MULTIPLAYER and FEATURE_SOUND disabled was fixed. * Fixed crash when using the donut special type and the joining linedef is one sided (thanks Alexander Waldmann). * Key settings in a configuration file that are out of range do not cause a crash (thanks entryway). * Fix ear-piercing whistle when playing the MAP05 MIDI music using timidity with EAWPATS (thanks entryway / HackNeyed). ### libtextscreen * There is now a second, small textscreen font, so that the ENDOOM screen and setup tool can be used on low resolution devices (eg. PDAs/embedded devices) * The textscreen library now has a scrollable pane widget. Thanks to LionsPhil for contributing code to scroll up and down using the keyboard. * Doxygen documentation was added for the textscreen library. ## 1.2.1 (2008-12-10) This version just fixes a crash at the intermission screen when playing Doom 1 levels. ## 1.2.0 (2008-12-10) Happy 15th Birthday, Doom! * Chocolate Doom now has an icon that is not based on the proprietary Doom artwork. * There is now memory-mapped WAD I/O support, which should be useful on some embedded systems. * Chex quest emulation support is now included, although an auxiliary dehacked patch is needed (chexdeh.zip in the idgames archive). ### Compatibility * The armor class is always set to 2 when picking up a megasphere (thanks entryway). * The quit screen prompts to quit “to dos†instead of just to quit (thanks MikeRS) * The “dimensional shambler†quit message was fixed. * Fix crash related to A_BFGSpray with NULL target when using dehacked patches - discovered with insaned2.deh (thanks CSonicGo) * NUL characters are stripped from dehacked files, to ensure correct behavior with some dehacked patches (eg. the one with portal.wad). ### Bugs fixed * “Python Image Library†should have been “Python Imaging Library†(thanks exp(x)). * The setup tool should no longer ask for elevated permissions on Windows Vista (this fix possibly may not work). * The application icon is set properly when running under Windows XP with the “Luna†theme. * Fix compilation under Cygwin to detect libraries and headers from the correct environment. * The video code does not try to read SDL events before SDL has been properly initialised - this was causing problems with some older versions of SDL. ## 1.1.1 (2008-04-20) The previous release (v1.1.0) included a bug that broke compilation when libsamplerate support was enabled. The only change in this version is to fix this bug. ## 1.1.0 (2008-04-19) * The video mode code has been radically restructured. The video mode is now chosen by directly specifying the mode to use; the scale factor is then chosen to fit the screen. This is helpful when using widescreen monitors (thanks Linguica) * MSVC build project files (thanks GhostlyDeath and entryway). * Unix manpage improvements; the manpage now lists the environment variables that Chocolate Doom uses. Manpages have been added for chocolate-setup and chocolate-server, from the versions for the Debian Chocolate Doom package (thanks Jon Dowland). * INSTALL file with installation instructions for installing Chocolate Doom on Unix systems. * Support for high quality resampling of sound effects using libsamplerate (thanks David Flater). * A low pass filter is applied when doing sound resampling in an attempt to filter out high frequency noise from the resampling process. * R_Main progress box is not displayed if stdout is a file (produces cleaner output). * Client/server version checking can be disabled to allow different versions of Chocolate Doom to play together, or Chocolate Doom clients to play with Strawberry Doom clients. * Unix manpages are now generated for the Chocolate Doom configuration files. * The BSD PC speaker driver now works on FreeBSD. ### Compatibility * Use the same spechits compatibility value as PrBoom+, for consistency (thanks Lemonzest). * The intercepts overrun code has been refactored to work on big endian machines. * The default startup delay has been set to one second, to allow time for the screen to settle before starting the game (some monitors have a delay before they come back on after changing modes). * If a savegame buffer overrun occurs, the savegame does not get saved and existing savegames are not overwritten (same behaviour as Vanilla). ### Bugs fixed * Desync with STRAIN demos and dehacked Misc values not being set properly (thanks Lemonzest) * Don’t grab the mouse if the mouse is disabled via -nomouse or use_mouse in the configuration file (thanks MikeRS). * Don’t center the mouse on startup if the mouse is disabled (thanks Siggi). * Reset the palette when the window is restored to clear any screen corruption (thanks Catoptromancy). * mus2mid.c should use `MEM_SEEK_SET`, not `SEEK_SET` (thanks Russell) * Fast/Respawn options were not being exchanged when starting netgames (thanks GhostlyDeath). * Letterbox mode is more accurately described as “pillarboxed†or “windowboxed†where appropriate (thanks MikeRS) * Process affinity mask is set to 1 on Windows, to work around a bug in SDL_mixer that can cause crashes on multi-core machines (thanks entryway). * Bugs in the joystick configuration dialog in the setup tool have been fixed. ## 1.0.0 (2007-12-10) This release is dedicated to Dylan “Toke†McIntosh, who was tragically killed in a car crash in 2006. I knew Dylan from IRC and the Doomworld forums for several years, and he had a deep passion for this game. He was also a huge help for me while developing Chocolate Doom, as he helped point out a lot of small quirks in Vanilla Doom that I didn’t know about. His death is a great loss. RIP Toke. This is the first release to reach full feature parity with Vanilla Doom. As a result, I have made this version 1.0.0, so Chocolate Doom is no longer beta! ### Big new features * Multiplayer! This version includes an entirely new multiplayer engine, based on a packet server architecture. I’d like to thank joe, pritch, Meph and myk, and everyone else who has helped test the new code for their support, feedback and help in testing this. The new code still needs more testing, and I’m eager to hear any feedback on this. * A working setup tool. This has the same look and feel as the original setup.exe. I hope people like it! Note that it has some advantages over the original setup.exe - for example, you can use the mouse. ### Other new features * New mus conversion code thanks to Ben Ryves. This converts the Doom .mus format to .mid a lot better. As one example, tnt.wad Map02 is now a lot closer to how Vanilla says. Also, the music on the deca.wad titlescreen now plays! * x3, x4 and x5 display scale (thanks to MikeRS for x5 scale). * Fullscreen “letterbox†mode allows Chocolate Doom to run on machines where 1.6:1 aspect ratio modes are unavailable (320x200/640x400). The game runs in 320x240/640x480 instead, with black borders. The system automatically adjusts to this if closer modes are unavailable. * Aspect ratio correction: you can (also) run at 640x480 without black borders at the top and bottom of the screen. * PC speaker sound effect support. Chocolate Doom can output real PC speaker sounds on Linux, or emulate a PC speaker through the sound card. * Working three-screen mode, as seen in early versions of Doom! To test this out, put three computers on a LAN and type: chocolate-doom -server chocolate-doom -autojoin -left chocolate-doom -autojoin -right * Allow a delay to be specified on startup, to allow the display to settle after changing modes before starting the game. * Allow the full path and filename to be specified when loading demos: It is now possible to type “chocolate-doom -playdemo /tmp/foo.lmp†for example. * Savegames are now stored in separate directories depending on the IWAD: eg. the savegames for Doom II are stored in a different place to those for Doom I, Final Doom, etc. (this does not affect Windows). * New mouse acceleration code works based on a threshold and acceleration. Hopefully this should be closer to what the DOS drivers do. There is a ‘test’ feature in the setup tool to help in configuring this. * New “-nwtmerge†command line option that emulates NWT’s “-merge†option. This allows TiC’s Obituary TC to be played. * The ENDOOM screen no longer closes automatically, you have to click the window to make it go away. * Spechit overrun fixes and improvements. Thanks to entryway for his continued research on this topic (and because I stole your improvements :-). Thanks to Quasar for reporting a bug as well. * Multiple dehacked patches can be specified on the command line, in the same way as with WADs - eg. -deh foo.deh bar.deh baz.deh. * Default zone memory size increased to 16MB; this can be controlled using the -mb command-line option. * It is now possible to record demos of unlimited length (by default, the Vanilla limit still applies, but it can now be disabled). * Autoadjusting the screen mode can now be disabled. * On Windows, the registry is queried to detect installed versions of Doom and automatically locate IWAD files. IWADs installed through Steam are also autodetected. * Added DOOMWADPATH that can be used like PATH to specify multiple locations in which to search for IWAD files. Also, “-iwad†is now enhanced, so that eg. “-iwad doom.wad†will now search all IWAD search paths for “doom.wadâ€. * Improved mouse tracking that should no longer lag. Thanks to entryway for research into this. * The SDL driver can now be specified in the configuration file. The setup tool has an option on Windows to select between DirectX and windib. * Joystick support. * Configuration file option to change the sound sample rate. * More than three mouse buttons are now supported. ### Portability improvements * Chocolate Doom now compiles and runs cleanly on MacOS X. Huge thanks go to Insomniak who kindly gave me an account on his machine so that I could debug this remotely. Big thanks also go to athanatos on the Doomworld forums for his patience in testing various ideas as I tried to get Chocolate Doom up and running on MacOS. * Chocolate Doom now compiles and runs natively on AMD64. * Chocolate Doom now compiles and runs on Solaris/SPARC, including the Sun compiler. Thanks to Mike Spooner for some portability fixes. * Improved audio rate conversion, so that sound should play properly on machines that don’t support low bitrate output. ### Compatibility fixes * Check for IWADs in the same order as Vanilla Doom. * Dehacked code will now not allow string replacements to be longer than those possible through DOS dehacked. * Fix sound effects playing too loud on level 8 (thanks to myk for his continued persistence in getting me to fix this) * Save demos when quitting normally - it is no longer necessary to press ‘q’ to quit and save a demo. * Fix spacing of -devparm mode dots. * Fix sky behavior to be the same as Vanilla Doom - when playing in Doom II, the skies never change from the sky on the first level unless the player loads from a savegame. * Make -nomouse and config file use_mouse work again. * Fix the -nomusic command-line parameter. Make the snd_sfxdevice snd_musicdevice values in the configuration file work, so that it is possible to disable sound, as with Vanilla. * Repeat key presses when the key is held down (this is the Vanilla behavior) - thanks to Mad_Mac for pointing this out. * Don’t print a list of all arguments read from response files - Vanilla doesn’t do this. * Autorun only when joyb_speed >= 10, not >= 4. Thanks to Janizdreg for this. * Emulate a bug in DOS dehacked that can overflow the dehacked frame table and corrupt the weaponinfo table. Note that this means Batman Doom will no longer play properly (identical behavior to Vanilla); vbatman.deh needs to also be applied to fix it. (Thanks grazza) * Allow dehacked 2.3 patches to be loaded. * Add more dehacked string replacements. * Compatibility option to enable or disable native key mappings. This means that people with non-US keyboards can decide whether to use their correct native mapping or behave like Vanilla mapping (which assumes all keyboards are US). * Emulate overflow bug in P_FindNextHighestFloor. Thanks to entryway for the fix for this. * Add -netdemo command line parameter, for playing back netgame demos recorded with a single player. * The numeric keypad now behaves like Vanilla Doom does. * Fix some crashes when loading from savegames. * Add intercepts overrun emulation from PrBoom-plus. Thanks again to entryway for his research on this subject. * Add playeringame overrun emulation. ### Bugs fixed * Fix crash when starting new levels due to the intermission screen being drawn after the WI_ subsystem is shut down (thanks pritch and joe) * Catch failures to initialise sound properly, and fail gracefully. * Fix crasher in 1427uv01.lmp (thanks ultdoomer) * Fix crash in udm1.wad. * Fix crash when loading a savegame with revenant tracer missiles. * Fix crash when loading a savegame when a mancubus was in the middle of firing. * Fix Doom 1 E1-3 intermission screen animations. * Fix loading of dehacked “sound†sections. * Make sure that modified copyright banners always end in a newline - this fixes a bug with av.wad (thanks myk) * Added missing quit message (“are you sure you want to quit this great game?â€). * Fix when playing long sound effects - the death sound in marina.wad now plays properly, for example. * Fix buffer overrun on the quicksave prompt screen that caused a mysterious cycling character to appear. * IDCLEV should not work in net games (thanks Janizdreg) * Stop music playing at the ENDOOM screen. * Fix sound sample rate conversion crash. * Fix “pop†heard at the end of sound effects. * Fix crash when playing long sounds. * Fix bug with -timedemo accuracy over multi-level demos. * Fix bug with the automap always following player 1 in multiplayer mode (thanks Janizdreg). ## 0.1.4 (2006-02-13) * NWT-style merging command line options (allows Mordeth to be played) * Unix manpage (thanks Jon Dowland) * Dehacked improvements/fixes: * Allow changing the names of graphic lumps used in menu, status bar intermission screen, etc. * Allow changing skies, animated flats + textures * Allow changing more startup strings. * Allow text replacements on music + sfx lump names * Fix for plutonia map12 crash. * Fix bug with playing long sfx at odd sample rates. * Big Endian fixes (for MacOS X). Thanks to athanatos for helping find some of these. * Install into /usr/games, rather than /usr/bin (thanks Jon Dowland) ## 0.1.3 (2006-01-20) * Imported the spechit overrun emulation code from prboom-plus. Thanks to Andrey Budko for this. * New show_endoom option in the chocolate-doom.cfg config file allows the ENDOOM screen to be disabled. * Chocolate Doom is now savegame-compatible with Vanilla Doom. * Fixes for big endian machines (thanks locust) * Fixed the behavior of the dehacked maximum health setting. * Fix the “-skill 0†hack to play without any items (thanks to Janizdreg for pointing out that this was nonfunctional) * Fix playing of sounds at odd sample rates (again). Sound effects at any sample rate now play, but only sounds with valid headers. This is the *real* way Vanilla Doom behaves. Thanks to myk for pointing out the incorrect behavior. ## 0.1.2 (2005-10-29) * Silence sounds at odd sample rates (rather than bombing out); this is the way Vanilla Doom behaves. * Handle multiple replacements of the same sprite in a PWAD. * Support specifying a specific version to emulate via the command line (-gameversion) * Fix help screen orderings and skull positions. Behave exactly as the original executables do. ## 0.1.1 (2005-10-18) * Display startup “banners†if they have been modified through dehacked. * Dehacked “Misc†section support. ### Bugs fixed * Doom 1 skies always using Episode 1 sky * Crash when switching applications while running fullscreen * Lost soul bounce logic (do not bounce in Registered/Shareware) * Mouse buttons mapped incorrectly (button 1 is right, 2 is middle) * Music not pausing when game is paused, when using SDL_mixer’s native MIDI playback. * Pink icon on startup (palette should be fully set before anything is loaded) ## 0.1.0 (2005-10-09) * Dehacked support * WAD merging for TCs * ENDOOM display * Fix bug with invalid MUS files causing crashes * Final Doom fixes ## 0.0.4 (2005-09-27) * Application icon and version info included in Windows .exe files * Fixes for non-x86 architectures * Fix uac_dead.wad (platform drop on e1m8 should occur when all bosses die, not just barons) * Fix “loading†icon to work for all graphics modes ## 0.0.3 (2005-09-17) * Mouse acceleration code to emulate the behaviour of old DOS mouse drivers (thanks to Toke for information about this and suggestions) * Lock surfaces properly when we have to (fixes crash under Windows 98) ## 0.0.2 (2005-09-13) * Remove temporary MIDI files generated by sound code. * Fix sound not playing at the right volume * Allow alt-tab away while running in fullscreen under Windows * Add second configuration file (chocolate-doom.cfg) to allow chocolate-doom specific settings. * Fix switches not changing in Ultimate Doom ## 0.0.1 (2005-09-07) First beta release crispy-doom-crispy-doom-5.6.4/NOT-BUGS.md000066400000000000000000000145701360717211000177630ustar00rootroot00000000000000The aim of Chocolate Doom is to behave as closely to Vanilla Doom as possible. As a result, you may experience problems that you would also experience when using Vanilla Doom. These are not “bugs†as Chocolate Doom is behaving as intended. This is not intended to be a comprehensive list of Vanilla Doom bugs. For more information, consult the “engine bugs†page of the Doom Wiki. ## Game exits after title screen with message about game version The game may exit after the title screen is shown, with a message like the following: Demo is from a different game version! (read 2, should be 109) *** You may need to upgrade your version of Doom to v1.9. *** See: https://www.doomworld.com/classicdoom/info/patches.php This appears to be v1.0/v1.1/v1.2. This usually indicates that your IWAD file that you are using to play the game (usually named doom.wad or doom2.wad) is out of date. Chocolate Doom only supports versions 1.666 through 1.9. To fix the problem, you must upgrade your IWAD file, preferably to 1.9. The URL in the message has downloadable upgrade patches that you can use to upgrade. ## Game exits in demo loop when playing Final Doom When playing with the Final Doom IWAD files (tnt.wad, plutonia.wad), if you leave the game at the title screen to play through the demo loop, it will eventually exit with the following error message: W_GetNumForName: demo4 not found! This is the same behavior as the Vanilla executables that were bundled with Final Doom. When Ultimate Doom was developed, a fourth demo was added to the demo loop, and this change was retained in the Final Doom version of the executable. However, the Final Doom IWADs do not include a fourth demo, so the game crashes. An alternate version of Final Doom was included in the Id Anthology boxed set, and this version of the game fixed this bug. However, this version also changes the teleport behavior, so the default is to emulate the most commonly distributed version of the game. To use the alternate version, run with: chocolate-doom -gameversion final2 ## Game exits when accessing the options menu The game may exit with the message “Bad V_DrawPatch†when accessing the options menu, if you have your mouse sensitivity set high. The Doom options menu has a slider that allows the mouse sensitivity to be controlled; however, it has only a very limited range. It is common for experienced players to set a mouse sensitivity that is much higher than what can be set via the options menu. The setup program allows a larger range of values to be set. However, setting very high sensitivity values causes the game to exit when accessing the options menu under Vanilla Doom. Because Chocolate Doom aims to emulate Vanilla Doom as closely as possible, it does the same thing. One solution to the problem is to set a lower mouse sensitivity. Alternatively, all of the settings in the options menu can be controlled through Doom’s key bindings anyway: Option | Key ---------------------------|----- End game | F7 Messages on/off | F8 Graphic detail high/low | F5 Screen size smaller/larger | -/+ Sound volume menu | F4 ## Game exits with “Savegame buffer overrun†when saving the game If you are playing on a particularly large level, it is possible that when you save the game, the game will quit with the message “Savegame buffer overrunâ€. Vanilla Doom has a limited size memory buffer that it uses for saving games. If you are playing on a large level, the buffer may be too small for the entire savegame to fit. Chocolate Doom allows the limit to be disabled: in the setup tool, go to the “compatibility†menu and disable the “Vanilla savegame limit†option. If this error happens to you, your game has not been lost! A file named temp.dsg is saved; rename this to doomsav0.dsg to make it appear in the first slot in the “load game†menu. (On Unix systems, you will need to look in the .chocolate-doom/savegames directory in your home directory) ## Game ends suddenly when recording a demo If you are recording a very long demo, the game may exit suddenly. Vanilla Doom has a limited size memory buffer that it uses to save the demo into. When the buffer is full, the game exits. You can tell if this happens, as the demo file will be around 131,072 bytes in size. You can work around this by using the -maxdemo command line parameter to specify a larger buffer size. Alternatively, the limit can be disabled: in the setup tool, go to the compatibility menu and disable the “Vanilla demo limit†option. ## Game exits with a message about “visplanes†The game may exit with one of these messages: R_FindPlane: no more visplanes R_DrawPlanes: visplane overflow (129) This is known as the “visplane overflow†limit and is one of the most well-known Vanilla Doom engine limits. You should only ever experience this when trying to play an add-on level. The level you are trying to play is too complex; it was most likely designed to work with a limit removing source port. More information can be found here (archived link): https://archive.is/s6h7V ## IDMUS## cheat doesn’t work with shareware/registered Doom IWADs The IDMUS cheat allows the in-game music to be changed. However, in the original v1.9 this cheat didn’t work properly when playing with the Doom 1 (shareware and registered) IWADs. This bug was fixed in the Ultimate Doom and Final Doom executables. Chocolate Doom emulates this bug. When playing with the shareware or registered Doom IWADs, the IDMUS cheat therefore does not work properly. If you are playing with the Ultimate Doom IWAD, the Ultimate Doom executable is emulated by default, so the cheat works properly. ## Some graphics are wrong when playing with BFG Edition IWADs If you are playing using the IWAD files from Doom 3: BFG Edition, you may notice that certain graphics appear strange or wrong. This includes the title screen, and parts of the options menu. The IWAD files in the new BFG Edition have had some significant changes from the IWAD files in older releases. Some of the graphics lumps have been removed or changed, and the Doom 2 secret levels are also censored. Chocolate Doom includes some small workarounds that allow the game to run, but for the best experience, it’s best to get a copy of the classic versions of the IWADs. These are still available to buy from Steam or GOG.com. crispy-doom-crispy-doom-5.6.4/PHILOSOPHY.md000066400000000000000000000233631360717211000202630ustar00rootroot00000000000000Chocolate Doom has been designed around a careful and deliberate philosophy that attempts to recreate the original (“Vanillaâ€) DOS executables for Doom, Heretic, Hexen and Strife. This document describes some of that philosophy and the reasoning behind it. This document is descriptive, not proscriptive. # Vanilla behavior Ideally Chocolate Doom aims to recreate the behavior of the Vanilla binaries, but different aspects of Vanilla behavior are held to varying degrees of importance. It can be imagined as different “tiers†of compatibility: * The game and gameplay itself is of central importance. Here, the Vanilla behavior ought to be maintained as accurately as possible. This includes the look, feel and sound, and things like demo compatibility. * The surrounding aspects of the game that aren’t part of the central gameplay experience can be extended as long as there’s a good reason and Vanilla behavior is respected. * The setup tool is not required to reproduce the behavior of the Vanilla setup tool, even though it reproduces its look and feel. “Vanilla†is defined as: * DOS Doom 1.9 (although there are actually multiple “1.9â€s). * DOS Heretic 1.3. * DOS Hexen 1.1. * DOS Strife 1.31. * DOS Chex Quest. Compatibility with older versions of the DOS binaries is also a secondary goal (though not pre-release versions). Other ports (either official or unofficial) are out of scope: this includes console ports, non-DOS ports, Doom 95 and Doom 3: BFG Edition. # Compatibility Chocolate Doom aims to be compatible with Vanilla Doom in several different ways. Examples are: * Bug compatibility: the aim is to emulate compatibility of the original game down to bugs that were present in the DOS executables. This includes maintaining the limitations of the original engine: for example, the infamous “visplane overflow†bug is intentionally still present, where other source ports have removed it; this allows mappers targeting Vanilla Doom to use Chocolate Doom as a faithful substitute. * Demo compatibility: Doom was one of the first games to develop a ‘speedrunning’ community, and thousands of recordings of Doom gameplay (`.lmp` demo files) exist in the Compet-N archive. Chocolate Doom aims for the highest standard of demo compatibility with Vanilla Doom, a goal that is often complicated by obscure behavior that can be difficult to reproduce. * Configuration file compatibility: Chocolate Doom uses the same configuration file format as Vanilla Doom, such that a user should be able to reuse their existing Vanilla configuration file without making any changes. Extra non-Vanilla options are stored in a separate configuration file. * Savegame file compatibility: Chocolate Doom uses the same savegame file format as Vanilla, such that it should be possible to import and use existing savegame files. # DOS tools Chocolate Doom includes some features that aren’t part of Vanilla Doom but exist for compatibility with DOS tools that interact with it. These are considered part of the Vanilla experience and ought to be treated as such. Some examples are: * The novert setting, which reproduces the functionality of `novert.exe`. * The `-deh` parameter, which loads Dehacked patches like dehacked.exe does under DOS. Chocolate Doom imposes the same limitations that Vanilla Dehacked does. # Exceptions Chocolate Doom differs from Vanilla Doom in a number of ways. In most cases these are subtle, minor differences. Nonetheless they deserve some explanation and justification. Here are some examples of situations where changes are considered acceptable: 1. Vanilla behavior can be broken that is harmful, eg. can damage the player’s computer, or is just outright misleading. For example: - Vanilla uses unbounded sprintf and strcpy (security issue). - Vanilla has crashes that force the user to reboot the machine. - Vanilla has an out of memory message that recommends tweaking CONFIG.SYS. As Chocolate Doom doesn’t run under DOS, reproducing this message would not be helpful. 2. Subtly extending behavior is okay where it’s not clear what the Vanilla behavior is anyway. For example: - Opening the menu releases mouse grab in Chocolate Doom. - Chocolate Hexen’s graphical startup screen runs in a window. 3. Supporting obsolete technology is not a goal: it’s considered acceptable that Chocolate Doom does not support every type of hardware from 1993. However, Chocolate Doom should aim to recreate the functionality in a modern way. Examples of technologies that aren’t supported are: - No support for IPX networks, but TCP/IP is supported instead. - No support for dial-up/serial connections; modern operating systems have features that do that for you. - No MS-DOS version. 4. Changes are acceptable that allow the player to be able play the game. For example: - There are new key bindings for actions that can’t be rebound with Vanilla Doom, because it’s useful for portability to machines that don’t have a full keyboard. - There are additional mouse and joystick button bindings that let you perform actions with them that can only be done with the keyboard in Vanilla Doom. - Chocolate Doom includes some hacks to support the Doom 3: BFG Edition IWAD files. The assumption is that being able to at least play is better than nothing, even if it’s not Vanilla behavior. - Chocolate Strife does not emulate the save bug from Vanilla 1.31, which could corrupt saves when overwriting a slot, if the old slot was not part of the current game’s progression. Vanilla behavior is unexpected and potentially devastating. 5. Adding extra options to Vanilla functionality is acceptable as long as the defaults match Vanilla, it doesn’t change gameplay and there’s a good reason for it. For example: - PNG screenshots are supported because PCX is an obsolete format. - Chocolate Doom has the vanilla_keyboard_mapping option that allows the user to use the native keyboard mapping for their computer, rather than always assuming a US layout. 6. Changing configuration file defaults is acceptable where there’s a very good reason. For example: - Vanilla Doom defaults to no sound or music if a configuration file is not found. Chocolate Doom defaults to having sound effects and music turned on by default, because modern computers support these; there’s no need to configure hardware IRQ settings to get sound working. 7. Things can be changed if they’re really just inconsequential. For example: - The startup messages in Chocolate Doom are not identical to Vanilla Doom and are not necessarily in the same order. - Vanilla Doom has command line options named `-comdev`, `-shdev` and `-regdev` used by id internally for development; these have been removed. 8. Expansions to the vanilla demo formats are allowed, to make recording and playback of vanilla gameplay more convenient, with the following restrictions: - Such expansions are not supported in WAD files (they are not an editing feature for WAD authors to use). - Support for these features can be completely disabled using the `-strictdemos` command line argument. - A warning is shown to the user on the console (stdout) when a demo using one of these features is recorded or played back. A good litmus test of when it’s acceptable to break from Vanilla behavior is to ask the question: “Although this is Vanilla behavior, is there anyone who would want this?†For example, emulating Vanilla bugs like the visplane overflow bug is something that is useful for people making Vanilla format maps. On the other hand, painstakingly emulating Vanilla Doom by starting with no sound or music by default is not helpful to anyone. # Minimalism Chocolate Doom aims to be minimalist and straightforward to configure; in particular, the setup tool should have a sane interface. Part of the inspiration for Chocolate Doom came from Boom’s complicated maze of options menus (and a desire to avoid them). Too many preferences lead to a bad user interface; see Havoc Pennington’s essay on Free Software UI: http://ometer.com/free-software-ui.html Chocolate Doom has some options that are quite obscure and only really of interest to a small number of people. In these cases, the options are hidden away in configuration files and deliberately not exposed in the setup tool. The assumption is that if you care enough about those obscure features, editing a configuration file by hand should not be a huge problem for you. Also inspirational was the README file from Havoc’s Metacity window manager, where the list of features begins: > Boring window manager for the adult in you. Many window managers > are like Marshmallow Froot Loops; Metacity is like Cheerios. I’d like to think that Chocolate Doom’s philosophy towards features is similar. The idea is for a source port that is boring. I find the best software - both proprietary and open source - is software that is “egolessâ€: it does a job well without pretentions about its importance or delusions of grandeur. A couple of other notable examples of software that I feel embody this spirit of design in a beautiful way are Marco Pesenti Gritti’s Epiphany web browser and Evince PDF viewer. # Other philosophical aspects Chocolate Doom aims for maximal portability. That includes running on many different CPUs, different operating systems and different devices (ie. not just a desktop machine with a keyboard and mouse). Chocolate Doom is and will always remain Free Software. It will never include code that is not compatible with the GNU GPL. crispy-doom-crispy-doom-5.6.4/README.Music.md000066400000000000000000000151471360717211000206020ustar00rootroot00000000000000Doom has a memorable and atmospheric soundtrack. Like many games of the era, it is MIDI-based. Chocolate Doom includes a number of different options for music playback, detailed below. # Native MIDI playback Most modern operating systems have some kind of built-in support for MIDI playback; some have very good quality MIDI playback (Mac OS X for example). To use this, choose “Native MIDI†in the sound configuration dialog in the setup tool. # Timidity Timidity is a software-based MIDI synthesizer, and a version of it is included in the SDL2_mixer library used by Chocolate Doom. To use Timidity for MIDI playback, first download a sound font. An example of a good quality sound font is the eawpats font, which can be downloaded from the idgames archive as sounds/eawpats.zip: https://www.doomworld.com/idgames/sounds/eawpats Having installed a sound font, select “Native MIDI†in the sound configuration dialog in the setup tool, and use the “Timidity configuration file†widget below to enter the path to the Timidity configuration file (normally named timidity.cfg). # Gravis Ultrasound (GUS) The Gravis Ultrasound (GUS) was a PC sound card popular in the ’90s, notable for having wavetable synthesis that provided MIDI playback that was superior to most other cards of the era. Chocolate Doom includes a “pseudo-GUS emulation†feature that simulates the GUS (using Timidity, under the hood). To use this feature you need a copy of the GUS patch files that were distributed with the original GUS patches. If you have Doom 3: BFG Edition, these patches are included with its version of classic Doom, and are automatically detected. Otherwise, they can be downloaded from the idgames archive as music/dgguspat.zip: https://www.doomworld.com/idgames/music/dgguspat Having downloaded the patches, select “GUS (emulated)†in the sound configuration dialog in the setup tool, and use the “GUS patch path†widget to enter the path to the directory containing the patch files. For the music not to sound weird, you will almost certainly want to load gusgonna.wad (sounds/gusgonna.zip in the idgames archive) when you run the game, to work around a bug in vanilla Doom's instrument mappings parser: https://www.doomworld.com/idgames/sounds/gusgonna By default a GUS card with 1024KB is simulated; to simulate a 256KB, 512KB or 768KB card instead, change the gus_ram_kb option in chocolate-doom.cfg. # OPL (Soundblaster / Adlib) Most people playing Doom in the ’90s had Soundblaster-compatible sound cards, which used the Yamaha OPL series of chips for FM-based MIDI synthesis. Chocolate Doom includes the ability to emulate these chips for a retro experience. OPL emulation is the default MIDI playback, but can be selected in the setup tool as “OPL (Adlib/SB)â€. Most modern computers do not include an OPL chip any more, as CPUs are fast enough to do decent software MIDI synthesis. However, no software emulator sounds exactly like a real (hardware) OPL chip, and a few cards do have real hardware OPL. If you have such a card, here’s how to configure Chocolate Doom to use it. ## Sound cards with OPL chips If you have an ISA sound card, it almost certainly includes an OPL chip. Modern computers don’t have slots for ISA cards though, so you must be running a pretty old machine. If you have a PCI sound card, you probably don’t have an OPL chip. However, there are some exceptions to this. The following cards are known to include “legacy†OPL support: * C-Media CMI8738 (*) * Forte Media FM801 * Cards based on the Yamaha YMF724 (*) Other cards that apparently have OPL support but have not been tested: * S3 SonicVibes * AZTech PCI 168 (AZT 3328 chipset) * ESS Solo-1 sound cards (ES1938, ES1946, ES1969 chipset) * Conexant Riptide Audio/Modem combo cards * Cards based on the Crystal Semiconductors CS4281 * Cards based on the Avance Logic ALS300 * Cards based on the Avance Logic ALS4000 If you desperately want hardware OPL music, you may be able to find one of these cards for sale cheap on eBay. For the cards listed above with (\*) next to them, OPL support is disabled by default and must be explictly enabled in software. See sections below for operating system-specific instructions on how you may be able to do this. If your machine is not a PC, you don’t have an OPL chip, and you will have to use the software OPL. ## Operating System support If you’re certain that you have a sound card with hardware OPL, you may need to take extra steps to configure your operating system to allow access to it. To do hardware OPL, Chocolate Doom must access the chip directly, which is usually not possible in modern operating systems unless you are running as the superuser (root/Administrator). ### Microsoft Windows On modern Windows systems it is not possible to directly access the OPL chip, even when running as Administrator. Fortunately, it is possible to use the “ioperm.sys†driver developed for Cygwin (look for iopermsys-0.2.1.tar.bz2): http://openwince.sourceforge.net/ioperm/ It is not necessary to have Cygwin installed to use this. Copy the ioperm.sys file into the same directory as the Chocolate Doom executable and it should be automatically loaded. You can confirm that hardware OPL is working by checking for this message in stdout.txt: OPL_Init: Using driver 'Win32'. If you have a C-Media CMI8738, you may need to enable the `FM_EN` flag in Windows Device Manager to access hardware OPL output. See [this](http://www.vogons.org/viewtopic.php?f=46&t=36445) thread on vogons.org for some more information. ### Linux If you are using a system based on the Linux kernel, you can access the OPL chip directly, but you must be running as root. You can confirm that hardware OPL is working, by checking for this message on startup: OPL_Init: Using driver 'Linux'. If you are using one of the PCI cards in the list above with a (*) next to it, you may need to manually enable FM legacy support. Add the following to your /etc/modprobe.conf file to do this: options snd-ymfpci fm_port=0x388 options snd-cmipci fm_port=0x388 ### OpenBSD/NetBSD You must be running as root to access the hardware OPL directly. You can confirm that hardware OPL is working by checking for this message on startup: OPL_Init: Using driver 'OpenBSD'. There is no native OPL backend for FreeBSD yet. Sorry! # Other options If you have some other favorite MIDI playback option that isn’t listed above, you can set a hook to invoke an external command for MIDI playback using the ‘snd_musiccmd’ configuration file option. For example, set: snd_musiccmd "aplaymidi -p 128:0" in your chocolate-doom.cfg file. crispy-doom-crispy-doom-5.6.4/README.Strife.md000066400000000000000000000122251360717211000207500ustar00rootroot00000000000000```````````````````````````````````````````````````````````````````````` Samuel ‘Kaiser’ Villarreal and James ‘Quasar’ Haley Present C H O C O L A T E ______________________________._________________________ / _____/\__ ___/\______ \ \_ _____/\_ _____/ \_____ \ | | | _/ || __) | __)_ / \ | | | | \ || \ | \ /_______ / |____| |____|_ /___|\___ / /_______ / \/ \/ \/ \/ ```````````````````````````````````````````````````````````````````````` ## What is it? Chocolate Strife is the most accurate and complete recreation of Rogue Entertainment’s “Strife: Quest for the Sigil.†It was created through more than four years of reverse engineering effort with the blessings of the original programmers of the game. ## Why? The source code for Strife was lost, which means, unlike the code for all the other commercial DOOM-engine games, it cannot be released. The only access we have to the code is the binary executable file. Tools such as IDA Pro have been employed to disassemble and decompile the executable, which was cross- referenced against the Linux DOOM and DOS Heretic sources and painstakingly combed over multiple times, instruction-by-instruction, to ensure that the resulting Chocolate Doom-based executable is as close as possible to the original. ## Is it Legal? Chocolate Strife was originally reverse-engineered from the DOS Strife binaries. Although reverse engineering is legally a protected activity, this nonetheless left some open questions about its legal status. In 2014, a new commercial release of Strife was published (Strife: Veteran Edition) based on the Chocolate Strife code, and developed by the authors of Chocolate Strife under commercial license. The release of Strife: Veteran Edition, along with its GPL-licensed source code, constitutes tacit approval for the legal status of Chocolate Strife by its current copyright holder. ## Is it Perfect? Almost, but not entirely! That’s where you come in. Help us by reporting any discrepancies you may notice between this executable and the vanilla DOS program! However, do *not* report any glitch that you can replicate in the vanilla EXE as a bug. The point of Chocolate Strife, like Chocolate Doom before it, is to be as bug-compatible with the original game as possible. Also be aware that some glitches are impossible to compatibly recreate, and wherever this is the case, Chocolate Strife has erred on the side of not crashing the program, for example by initializing pointers to NULL rather than using them without setting a value first. ## What are some known problems? The demo version is *not* supported, and there are not any current plans to support it in the future, due to the vast number of differences (the demo version of Strife is based on an earlier version of Rogue’s codebase). The commercial Strife IWAD version 1.1 may run, but also exhibit issues. Like the demo version, there are no current plans to fully support it. Make sure your copy is updated to at least 1.2. Strife: Veteran Edition already includes the required version. ## How do I use it? From the Run box or a command line, issue a command to Chocolate Strife just like you would run Chocolate Doom. Most of the same command line parameters are supported. voices.wad will be read from the same directory as the IWAD, if it can be found. If it is not found, then voices will be disabled just as would happen with the vanilla executable. Do not add voices.wad using -file, as that is redundant and unnecessary. Some new command-line parameters in Chocolate Strife include the following: - -nograph - Disables the graphical introduction sequence. -devparm implies this. - -novoice - Disables voices even if voices.wad can be found. - -work - Enables Rogue’s playtesting mode. Automatic godmode, and pressing the inventory drop key will toggle noclipping. - -flip - Flips player gun graphics. This is buggy, however, because it does not reverse the graphics’ x offsets (this is an accurate emulation of the vanilla engine’s behavior). - -random - Randomizes the timing and location of item respawns in deathmatch, when item respawning is enabled. ## Copyright This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. See the “COPYING†file for the full license text. The source code for this program is available from the same location where you downloaded this package. Aside from Chocolate Doom, portions of the code are derived from the Eternity Engine, Copyright 2011 Team Eternity, as published under the GNU GPL. crispy-doom-crispy-doom-5.6.4/README.md000066400000000000000000000750461360717211000175270ustar00rootroot00000000000000# Crispy Doom [![Crispy Doom Icon](https://www.chocolate-doom.org/wiki/images/b/be/Crispy-doom.png)](https://github.com/fabiangreffrath/crispy-doom) [![Top Language](https://img.shields.io/github/languages/top/fabiangreffrath/crispy-doom.svg?style=flat)](https://github.com/fabiangreffrath/crispy-doom) [![Code Size](https://img.shields.io/github/languages/code-size/fabiangreffrath/crispy-doom.svg?style=flat)](https://github.com/fabiangreffrath/crispy-doom) [![License](https://img.shields.io/github/license/fabiangreffrath/crispy-doom.svg?style=flat&logo=gnu)](https://github.com/fabiangreffrath/crispy-doom/blob/master/COPYING.md) [![Release](https://img.shields.io/github/release/fabiangreffrath/crispy-doom.svg?style=flat)](https://github.com/fabiangreffrath/crispy-doom/releases) [![Release Date](https://img.shields.io/github/release-date/fabiangreffrath/crispy-doom.svg?style=flat)](https://github.com/fabiangreffrath/crispy-doom/releases) [![Downloads](https://img.shields.io/github/downloads/fabiangreffrath/crispy-doom/latest/total.svg?style=flat)](https://github.com/fabiangreffrath/crispy-doom/releases) [![Commits](https://img.shields.io/github/commits-since/fabiangreffrath/crispy-doom/latest.svg?style=flat)](https://github.com/fabiangreffrath/crispy-doom/commits/master) [![Last Commit](https://img.shields.io/github/last-commit/fabiangreffrath/crispy-doom.svg?style=flat)](https://github.com/fabiangreffrath/crispy-doom/commits/master) [![Travis Build Status](https://img.shields.io/travis/com/fabiangreffrath/crispy-doom.svg?style=flat&logo=travis)](https://travis-ci.com/fabiangreffrath/crispy-doom/) Crispy Doom is a limit-removing enhanced-resolution Doom source port based on [Chocolate Doom](https://www.chocolate-doom.org/wiki/index.php/Chocolate_Doom). Its name means that its internal 640x400 resolution looks "crisp" and is also a [slight reference](http://www.mathsisfun.com/recipie.html) to its origin. ## Synopsis Crispy Doom is a friendly fork of [Chocolate Doom](https://www.chocolate-doom.org/wiki/index.php/Chocolate_Doom) that provides a higher display resolution, removes the [static limits](https://doomwiki.org/wiki/Static_limits) of the Doom engine and offers further optional visual, tactical and physical enhancements while remaining entirely config file, savegame, netplay and demo compatible with the original. ## Objectives and features Crispy Doom is a source port that aims to provide a faithful Doom gaming experience while also featuring some user-requested improvements and enhancements. It is forked off of Chocolate Doom to take advantage of its free and open-source code base, portability, accuracy and compatibility with Vanilla Doom. Its core features are: * Enhanced 640x400 display resolution, with the original 320x200 resolution still available in the "High Resolution Rendering: Off" mode. * Uncapped rendering framerate with interpolation and optional vertical synchronization (VSync) with the screen refresh rate. * Intermediate gamma correction levels (0.5, 1.5, 2.5 and 3.5). * Removal of all static engine limits, or at least raising of the less crucial ones. * Full support for the "Doom Classic" WADs shipped with the "Doom 3: BFG Edition", especially the "No Rest For The Living" episode shipped in the NERVE.WAD file. * Support for all versions of John Romero's Episode 5: Sigil for Ultimate Doom. Furthermore, the following optional user-visible and audible features are available: * Jumping. * Free vertical looking, including mouse look and vertical aiming. * Aiming support by a crosshair that may get directly rendered into the game world. * A new minimal Crispy HUD, displaying only the status bar numbers. * Clean Screenshot feature, enabling to take screenshots without HUD elements and even without status bar numbers and weapon sprites at higher screen sizes. * Colorized status bar numbers, HUD texts and blood sprites for certain monsters. * Translucency for certain sprites and status bar elements in the Crispy HUD. * Randomly mirrored death animations and corpse sprites. * Command line options to allow for playing with flipped player weapon sprites and/or entirely flipped level geometry. * Players may walk over or under monsters and hanging corpses. * Centered Weapons when firing, weapon recoil thrust and pitch. * Reports whenever a secret is revealed. * Level statistics and extended coloring in the Automap. * Playing sounds in full length, and misc. other sound fixes. * Demo recording and/or playback timers and progress bar. * Demo continue and take-over features, handing controls over to the player when demo playback is finished or interrupted. Most of these features are disabled by default and need to get enabled either in the in-game "Crispness" menu, in the crispy-doom-setup tool or as command line parameters. They are implemented in a way that preserves demo-compatibility with Vanilla Doom and network game compatibility with Chocolate Doom. Furthermore, Crispy Doom's savegames and config files are compatible, though not identical (see the [Compatibility section in the Wiki](https://github.com/fabiangreffrath/crispy-doom/wiki/Compatibility)), to Vanilla Doom's. Crispy Doom strives for maximum compatibility with all "limit-removing Vanilla" maps -- but not Boom or ZDoom maps. More specifically, Crispy Doom supports some select advanced features such as [ANIMATED](https://doomwiki.org/wiki/ANIMATED) and [SWITCHES](https://doomwiki.org/wiki/SWITCHES) lumps, MBF sky transfers, SMMU swirling flats and [MUSINFO](https://doomwiki.org/wiki/MUSINFO) -- but neither generalized linedef and sector types nor DECORATE and MAPINFO. Many additional less user-visible features have been implemented, e.g. fixed engine limitations and crashes, fixed rendering bugs, fixed harmless game logic bugs, full support for DEHACKED files and lumps in BEX format, additional and improved cheat codes, an improved Automap, and many more! Due to the extra DEHACKED states added from [MBF](https://doomwiki.org/wiki/MBF), Crispy Doom supports [enhancer](https://www.doomworld.com/forum/topic/84859-black-ops-smooth-weapons-dehacked-mod) [mods](https://www.doomworld.com/forum/topic/85991-smoothed-smooth-monsters-for-doom-retro-and-crispy-doom) that can make the gameplay even more pleasing to the eyes. For a detailed list of features and changes please refer to the release notes below. ### New controls (with default bindings) * Move Forward (alt.) W * Move Backward (alt.) S * Strafe Left (alt.) A * Strafe Right (alt.) D * Jump (bindable to joystick and mouse buttons as well) / (as in Hexen and Strife) * Quick Reverse (bindable to mouse buttons as well) * Mouse Look (bindable to mouse buttons or permanent) * Look up (bindable to joystick axes as well) PgDn (as in Heretic) * Look down (bindable to joystick axes as well) Del (as in Heretic) * Center view End (as in Heretic) * Toggle always run * Toggle vertical mouse movement (new in 5.4) * Delete savegame Del * Go to next level * Reload current level * Save a clean screenshot * Toggle Automap overlay mode O * Toggle Automap rotate mode R * Resurrect from savegame (single player mode only) "Run" + "Use" ### New command line parameters * `-dm3` specifies the Deathmatch 3.0 rules (weapons stay, items respawn) for netgames (since 4.1). * `-episode 1` launches Hell on Earth and `-episode 2` launches No Rest for the Living episode if the Doom 2 IWAD shipped with the Doom 3: BFG Edition is used. * `-warp 1a` warps to the secret level E1M10: Sewers of XBox Doom IWAD (since 2.3). * `-mergedump ` merges the PWAD file(s) given on the command line with the IWAD file and writes the resulting data into the `` given as argument. May be considered as a replacement for the `DEUSF.EXE` tool (since 2.3). * `-blockmap` forces a (re-)building of the BLOCKMAP lumps for loaded maps (since 2.3). * `-playdemo demoname -warp N` plays back fast-forward up to the requested map (since 3.0). * `-loadgame N -record demoname` and `-loadgame N -playdemo demoname` allow to record and play demos starting from a savegame instead of the level start (since 4.0). * `-playdemo demoname1 -record demoname2` plays back fast-forward until the end of demoname1 and continues recording as demoname2 (new in 5.5). * `-fliplevels` loads mirrored versions of the maps (this was the default on April 1st up to version 5.0). * `-flipweapons` flips the player's weapons (new in 5.3). ### New cheat codes * `TNTWEAP` followed by a weapon number gives or removes this weapon (8 = Chainsaw, 9 = SSG). `TNTWEAP0` takes away all weapons and ammo except for the pistol and 50 bullets. Try to load Doom 1 with `DOOM2.WAD` as a PWAD and type `TNTWEAP9` to play the SSG in Doom 1. * `TNTEM`, `KILLEM` or `FHHALL` kill all monsters on the current map (and disables all cube spitters). * `SPECHITS` triggers all [Linedef actions](https://doomwiki.org/wiki/Linedef_type) on a map at once, no matter if they are enabled by pushing, walking over or shooting or whether they require a key or not. It also triggers all boss monster and Commander Keen actions if possible. * `NOTARGET` or `FHSHH` toggle deaf and blind monsters that do not act until attacked. * `TNTHOM` toggles the flashing [HOM](https://doomwiki.org/wiki/Hall_of_mirrors_effect) indicator (disabled by default). * `SHOWFPS` or `IDRATE` toggle printing the FPS in the upper right corner. * `NOMOMENTUM` toggles a debug aid for pixel-perfect positioning on a map (not recommended to use in-game). * `GOOBERS` triggers an easter egg, i.e. an "homage to an old friend". ;-) * `IDBEHOLD0` disables all currently active power-ups (since 2.2). * `IDCLEV00` restarts the current level (since 2.0). * `IDMUS00` restarts the current music (new in 5.1). * `VERSION` shows the engine version, build date and SDL version (new in 5.1). * `SKILL` shows the current skill level (new in 5.5.2). ## Download Binaries for Windows XP / Vista / 7 / 8.1 / 10 (32-bit binaries compatible with both x86 and x64 editions) are available here: https://github.com/fabiangreffrath/crispy-doom/releases/download/crispy-doom-5.6.4/crispy-doom-5.6.4-win32.zip Daily builds of Crispy Doom can be found here: http://latest.chocolate-doom.org/ Crispy Doom can play nearly all variants of Doom. If you don't own any, you may download the [Shareware version of Doom](http://cdn.debian.net/debian/pool/non-free/d/doom-wad-shareware/doom-wad-shareware_1.9.fixed.orig.tar.gz), extract it and copy the DOOM1.WAD file into your Crispy Doom directory. Alternatively, you may want to play Crispy Doom with [Freedoom](https://www.chocolate-doom.org/wiki/index.php/Freedoom) and a MegaWAD. ### Sources [![Open Hub](https://www.openhub.net/p/crispy-doom/widgets/project_thin_badge?style=flat&format=gif)](https://www.openhub.net/p/crispy-doom) The Crispy Doom source code is available at GitHub: https://github.com/fabiangreffrath/crispy-doom. It can be [downloaded in either ZIP or TAR.GZ format](https://github.com/fabiangreffrath/crispy-doom/releases) or cloned via ``` git clone https://github.com/fabiangreffrath/crispy-doom.git ``` Brief instructions to set up a build system on Windows can be found [in the Crispy Doom Wiki](https://github.com/fabiangreffrath/crispy-doom/wiki/Building-on-Windows). A much more detailed guide is provided [in the Chocolate Doom Wiki](https://www.chocolate-doom.org/wiki/index.php/Building_Chocolate_Doom_on_Windows), but applies to Crispy Doom as well for most parts. Compilation on Debian systems (Debian 10 "buster" or later) should be as simple as ``` sudo apt install build-essential automake git sudo apt build-dep crispy-doom ``` to install the prerequisites and then ``` cd crispy-doom autoreconf -fiv ./configure make ``` After successful compilation the resulting binaries can be found in the `src/` directory. ## News ### Crispy Doom 5.6.4 Crispy Doom 5.6.4 has been released on January 14, 2020. This release addresses the community feedback received after 5.6.3. **Features** * Toggling full screen using Enter on num. keyboard is now possible (inherited from Chocolate Doom). * `IDCLEV` is now allowed during demo playback and warps to the requested map (starting a new demo). * If a new game is started (current level is reloaded/next level is loaded on pressing the respective key) during demo recording, a new demo is started. Thanks to Looper from Doomworld forums for the feature request. * 'Intermediate Crispy HUD' without the status bar but with the face and its background in place has been introduced. **Improvements** * `leveltime` is now shown in the demo timer widget during recording instead of the accumulated demo time, thanks to Looper from Doomworld forums for the input. * Windows binaries being 32-bit has been clarified, thanks to RetroDoomKid for the remark. * Demo timer widget is now reset when restarting a demo during recording. * `gamemap` is now set to `startmap` when restarting a demo during recording. * Blood splats and projectile puffs are now drawn as small squares in the Automap. * Level/demo restart key description has been adjusted to become self-explanatory. * Static demo file name suffix counter has been added. This avoids checks for an increasing number of file names being available by just remembering the latest given suffix number. Thanks to Looper for the suggestion. * Demos are now restarted from the map they were started, thanks to Looper for the input. * OSX: Freedoom single player IWADs have been added to launcher (inherited from Chocolate Doom). * The smooth chainsaw idle animation has been brought back. * Weapon bobbing has been reworked and made adjustable. * Disallowing the vertical mouse movement now disables controlling the menus with the mouse, thanks to bryc for requesting this. * TNTWEAP0 now removes the berserk strength, all weapons and ammo except for pistol and 50 bullets. * Early exit from the tally screen after ExM8 is now forced, which enables demos to progress to the end of game sequence. * TNTWEAP2 now removes the pistol. * Some clipping optimizations taken from JNechaevsky's Russian Doom (and there from MBF respectively) have been implemented. * Savegame name is automatically overridden on saving if it already starts with a map identifier, proposed by zebzorb. * Status bar optimizations, including numbers to be only redrawn if necessary, on JNechaevsky's suggestion. * In automap overlay mode the automap is now drawn on top of everything as JNechaevsky suggested, not beneath the bezel for decreased screen sizes. **Bug Fixes** * Missing prototype for `calloc()` in `r_data.c` causing memory corruption on 64bit in Windows/MSVC builds has been fixed, thanks to zx64 for spotting this. * Crash when the flag for the berserk pack sprite patch memory zone is changed has been fixed, thanks to IsBebs for the bug report and Zodomaniac, JNechaevsky and turol for helping with the analysis. * Zombie player crash on SELFDEAD has been fixed, thanks to tpoppins for the report and turol for the analysis. * `"doomstat.h"` is now included instead of `` in `doom/r_swirl.c`, fixing compilation with MSVC2017, and packed attribute for structs when compiling with MSVC has been fixed. Thanks to drfrag666 for reporting and confirming the fix. * Shadowed menu and text drawing has been removed, as it is bugged in wipe screens. * The par time for MAP33 is now determined correctly (inherited from Chocolate Doom). * Subsequent calls to `A_BrainAwake()` to reset the `braintargeton` variable are now allowed. This fixes demo sync for maps with more than one brain, e.g. PL2. * Player viewheight in NOMOMENTUM mode has been fixed on Zodomaniac's report. * The revenant sync bug (with homing or non-homing missiles) with New Game demos has hopefully been fixed. * No statdump output is generated now for ExM8, and updating the Archvile fire's `floorz` and `ceilingz` values has been reverted, which fixes demo desyncs that fraggle discovered. Thanks a lot! * Fuzz effect animation remaining static in one case has been fixed, this happened if the number of pixels to apply the fuzz effect to was an integer multiple of FUZZTABLE. Thanks to JNechaevsky for the suggestion! * Status bar face expression staying across level changes has been removed, thanks to JNechaevsky for pointing this out. * Automap panning in flippedlevels mode has been fixed, thanks to JNechaevsky for reporting. * Self-repeating states in `P_LatestSafeState()` are now handled. * Max-sized background buffer is now allocated for the bezel. This fixes a crash when the game is started with `crispy->hires == 0` and `scaledviewwidth != SCREENWIDTH` and then `crispy->hires` is switched to `1`. * Switching to the fist after typing a cheat expecting two parameters has been fixed. This affects IDMUSx1 and IDCLEVx1, thanks to maxmanium for pointing this out. * Automap marker coordinate for flipped levels has been fixed. * Loss of grid lines near the automap boundary has been fixed, spotted by JNechaevsky. * Overlayed automap blinking one tic on screen borders has been fixed, noticed by JNechaevsky. Crispy Doom 5.6.4 is based on Chocolate Doom 3.0.0 and has merged all changes to the Chocolate Doom master branch up to commit [`f81b5c7b`](https://github.com/chocolate-doom/chocolate-doom/commit/f81b5c7b3e7c24364fce681ad3d7ba18119a867b). ### Crispy Doom 5.6.3 Crispy Doom 5.6.3 has been released on October 04, 2019. This release addresses the community feedback received after 5.6.2 release and brings support for the updated Episode 5: Sigil v1.2/v1.21. **Features** * Automap overlay and rotate modes are now stored as config variables, suggested by JNechaevsky. * Versions 1.2 and 1.21 of Episode 5: Sigil are now supported. **Improvements** * Par times provided by Sigil 1.21 have been coded in, their introduction noticed by JNechaevsky. * Par times for Episode 4: Thy Flesh Consumed and Episode 5: Sigil can now be provided in BEX format. * A workaround has been implemented for missing textures in SWITCHES lumps: if one texture is missing, the whole pair is disabled. Thanks to Aurelius for reporting this issue with the OTEX 1.0 texture pack in the Doomworld forum. **Bug Fixes** * Sigil's DEHACKED patch is no longer loaded when auto-loading the WAD, as this would break any episode-finishing demo for Doom 1. * Status bar background appearing at low framerates with Crispy HUD and automap overlay on when holding TAB key has been fixed, spotted by JNechaevsky and confirmed by Zodomaniac. * Configuration not being saved when exiting the game while recording a demo has been fixed, reported by Zodomaniac. Now configuration is always saved on exit. * Player weapon sound source is now set properly when loading a savegame, thanks to maxmanium from the Doomworld forum for bringing attention to this. **Known Issues** * [No music and high-pitched sound effects](https://github.com/fabiangreffrath/crispy-doom/issues/454) occur with SDL2.dll v2.0.10 and SDL2_mixer.dll v2.0.4 on Windows in case of 5.1 speaker configuration, according to investigation by StasBFG. [An unofficial DLL pack fixing this and providing fluidsynth soundfont support](https://github.com/fabiangreffrath/crispy-doom/files/3616050/crispy-doom-DLL-fix-pack.zip) is provided by Zodomaniac. Crispy Doom 5.6.3 is based on Chocolate Doom 3.0.0 and has merged all changes to the Chocolate Doom master branch up to commit [`ee9fc21f`](https://github.com/chocolate-doom/chocolate-doom/commit/ee9fc21fd6b7e50706fa093b9ccabd6dd56b02db). ### Crispy Doom 5.6.2 Crispy Doom 5.6.2 has been released on September 13, 2019. The primary aim of this release is to fix the music-related bugs that surfaced in 5.6.1 and previous releases. **Bug Fixes** * Pulled midiproc-related bug fixes from Chocolate Doom. * Use inherited handles to communicate with midiproc to prevent libraries that print error messages to standard streams from disrupting communication with the subprocess. Thanks to Zodomaniac for noticing this years ago when playing with the Memento Mori music PWAD, to Fabian Greffrath for spotting where this bug lurks and to AlexMax for finally fixing it! * Call `UnregisterSong()` where appropriate and do not unset `midi_server_registered` in `StopSong()`. This fixes the same song being played over and over again despite level changes when using MP3/OGG/FLAC music PWADs, pointed out by Zodomaniac. * Clean screenshots are now saved without demo progress bar after Zodomaniac spotted that it gets burned into them. * Screenshots are now saved without alpha channel, they were transparent before on MacOS as JamesDunne reported. **Other Games** * Heretic's `BLOCKMAP` limit has been removed. Thanks to Jeff Green for the contribution. Crispy Doom 5.6.2 is based on Chocolate Doom 3.0.0 and has merged all changes to the Chocolate Doom master branch up to commit [`ee9fc21f`](https://github.com/chocolate-doom/chocolate-doom/commit/ee9fc21fd6b7e50706fa093b9ccabd6dd56b02db). ### Crispy Doom 5.6.1 Crispy Doom 5.6.1 has been released on August 23, 2019. It is dedicated to hotfixing the bugs reported by the community after the 5.6 release. **Bug Fixes** * The `IDBEHOLD0` cheat not cancelling the player's invisibility has been fixed, thanks to maxmanium for being watchful. * The crash when a door that is actually a platform is opened again while going down has now actually been fixed, thanks to maxmanium for pointing this out at the Doomworld forums and Zodomaniac for the confirmation. * The door-closing sound playing even when the door is already closed has been fixed, thanks to Worm from the Doomworld forums for the heads-up. This especially affects repeatable walkover triggers. * SIGIL.wad is no longer auto-loaded anymore if another PWAD already modifies the texture files. This fixes the buttons in REKKR being rendered incorrectly, thanks to IsBebs for the report. **Regressions** * The "Show Player Coords: Always" setting is now disabled to prevent cheating while speedrunning. Thanks to ZeroMaster010 for the repeated suggestions at the Doomworld forums. Crispy Doom 5.6.1 is based on Chocolate Doom 3.0.0 and has merged all changes to the Chocolate Doom master branch up to commit [`b9d4c04c`](https://github.com/chocolate-doom/chocolate-doom/commit/b9d4c04c840321f5ec70787d8afb1256766aaa01). ### Crispy Doom 5.6 Crispy Doom 5.6 has been released on August 1, 2019. This release features support for the new Ultimate Doom Episode 5: Sigil by John Romero (with its MP3 soundtrack by Buckethead) and the Doom Metal Vol. 5 metal soundtrack mod for all IWADs. **Features** * SIGIL.wad and SIGIL_SHREDS.wad are auto-loaded with Ultimate Doom IWAD when available, suggested by buvk. The Sigil art screen is only used when finishing episode 5. If you want to replace DMENUPIC and other art by Sigil's, load it manually. * Support for alternative music tracks for Final Doom has been implemented as introduced in DoomMetalVol5.wad, music replacement tables provided by Zodomaniac. **Improvements** * Joystick jump button can now be assigned, contributed by Jeff Green. * Item position in Crispness menu is now remembered as well as in the rest of Doom menu, fixing the non-Doominess spotted by JNechaevsky. * Ambiguity in music backend name `Native MIDI` pointed out by pmjdebruijn has been eliminated, now it reads `MIDI/MP3/OGG/FLAC`. * Automap colors for different things (visible with IDDT) have been figured out by Zodomaniac: orange for projectiles, including Lost Souls, and dark gold for shootable things like barrels. * Extra Arch-Vile fire spawn sound is only played if available, which makes Capellan's SpecBoss.wad work with Doom 1 as IWAD. * Optional secret counting in the "secret revealed" message has been introduced, suggested by Ledmeister. * Green brightmap is applied to barrels according to JNechaevsky's idea. * Colors for HUD digits have been improved on artistic advice by JNechaevsky. * Zooming and moving Automap with the mouse wheel has been implemented, thanks to JNechaevsky for the suggestion and testing. * Tally screen is displayed after ExM8, requested by Sector 147 and tested by JNechaevsky. * Weapon pickup message is printed when using the `TNTWEAPx` cheat, requested by Zodomaniac. **Bug Fixes** * Support for SMMU swirling flats has been repaired. * Playing with 32 sound channels is now actually enabled, thanks to seed and SiFi270 for pointing this out and providing examples. * More crashes with maps without map title graphics lump are prevented. * Level transitions back from MAP33 when playing Doom 2 extensions (e.g. NERVE) have been fixed, thanks to buvk for reporting. * Playing up to three sounds from lines with more than one switch texture has been fixed, squashing the button spamming sound bug reported by Looper in the forums. * A crash when a door that is actually a platform is closed manually has been fixed, spotted by glyphic from the forums. * An off-by-one typo in the par time drawing decision has been fixed. * The SSG reloading sounds being breakable have been fixed, reported by JNechaevsky. * Flat lumps are prevented from being mistaken as patches, at least when composing textures. This fixes a crash when loading any map with Sunder.wad (and who knows where else) spotted by JNechaevsky. If the flat lump name is unambiguous, though, then the one found is used, as Brad Harding pointed out. This fixes WOS.wad. * The ammo type is reset in `P_CheckAmmo()` when a weapon is removed (by the `TNTWEAPx` cheat) after Zodomaniac's report, so that even the chainsaw which consumes no ammo is removed properly. **Regressions** * Crispy's own WAD autoload mechanism has been replaced by Choco's one, autoloading files from the `doom-all` subdirectory of the config directory. Crispy Doom 5.6 is based on Chocolate Doom 3.0.0 and has merged all changes to the Chocolate Doom master branch up to commit [`485b939b`](https://github.com/chocolate-doom/chocolate-doom/commit/485b939b9b01e00ab47cd34a9de4a4e901d96a33). ## Documentation * **[Changelog](https://github.com/fabiangreffrath/crispy-doom/wiki/Changelog)** * **[Compatibility](https://github.com/fabiangreffrath/crispy-doom/wiki/Compatibility)** * **[Crispness](https://github.com/fabiangreffrath/crispy-doom/wiki/Crispness)** * **[FAQ](https://github.com/fabiangreffrath/crispy-doom/wiki/FAQ)** ## Versioning Crispy Doom's major version number is increased whenever a new Chocolate Doom (pre-)release got merged into its code base. The minor version number is increased for intermediate releases that do only contain Crispy-specific changes or unreleased changes to the Chocolate Doom code base. The micro or patch version is reserved for post-release hotfixes, it remained unused until the 5.5 release. ## Contact The canonical homepage for Crispy Doom is https://github.com/fabiangreffrath/crispy-doom Crispy Doom is maintained by [Fabian Greffrath](mailto:fabian@greffXremovethisXrath.com). Please report any bugs, glitches or crashes that you encounter to the GitHub [Issue Tracker](https://github.com/fabiangreffrath/crispy-doom/issues). ## Acknowledgement Although I have played the thought of hacking on Chocolate Doom's renderer for quite some time already, it was Brad Harding's [Doom Retro](https://www.chocolate-doom.org/wiki/index.php/Doom_Retro) that provided the incentive to finally do it. However, his fork aims at a different direction and I did not take a single line of code from it. Lee Killough's [MBF](https://doomwiki.org/wiki/WinMBF) was studied and used to debug the code, especially in the form of Team Eternity's [WinMBF](https://doomwiki.org/wiki/WinMBF) source port, which made it easier to compile and run on my machine. And of course there is fraggle's [Chocolate Doom](https://www.chocolate-doom.org/wiki/index.php/Chocolate_Doom) with its exceptionally clean and legible source code. Please let me take this opportunity to appreciate all these authors for their work! Also, thanks to plums of the [Doomworld forums](https://www.doomworld.com/vb/) for beta testing, "release manager" Zodomaniac and "art director" JNechaevsky for the continuous flow of support and inspiration during the post-3.x development cycle and (last but not the least) [Cacodemon9000](http://www.moddb.com/members/cacodemon9000) for his [Infested Outpost](http://www.moddb.com/games/doom-ii/addons/infested-outpost) map that helped to track down quite a few bugs! Furthermore, thanks to VGA for his aid with adding support for his two mods: [PerK & NightFright's Black Ops smooth weapons add-on converted to DEHACKED](https://www.doomworld.com/forum/topic/84859-black-ops-smooth-weapons-dehacked-mod) and [Gifty's Smooth Doom smooth monster animations converted to DEHACKED](https://www.doomworld.com/forum/topic/85991-smoothed-smooth-monsters-for-doom-retro-and-crispy-doom) that can make the gameplay even more pleasing to the eyes. ## Legalese Doom is © 1993-1996 Id Software, Inc.; Boom 2.02 is © 1999 id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman; PrBoom+ is © 1999 id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman, © 1999-2000 Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, © 2005-2006 Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko; Chocolate Doom is © 1993-1996 Id Software, Inc., © 2005 Simon Howard; Chocolate Hexen is © 1993-1996 Id Software, Inc., © 1993-2008 Raven Software, © 2008 Simon Howard; Strawberry Doom is © 1993-1996 Id Software, Inc., © 2005 Simon Howard, © 2008-2010 GhostlyDeath; Crispy Doom is additionally © 2014-2019 Fabian Greffrath; all of the above are released under the [GPL-2+](https://www.gnu.org/licenses/gpl-2.0.html). SDL 2.0, SDL_mixer 2.0 and SDL_net 2.0 are © 1997-2016 Sam Lantinga and are released under the [zlib license](http://www.gzip.org/zlib/zlib_license.html). Secret Rabbit Code (libsamplerate) is © 2002-2011 Erik de Castro Lopo and is released under the [GPL-2+](http://www.gnu.org/licenses/gpl-2.0.html). Libpng is © 1998-2014 Glenn Randers-Pehrson, © 1996-1997 Andreas Dilger, © 1995-1996 Guy Eric Schalnat, Group 42, Inc. and is released under the [libpng license](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt). Zlib is © 1995-2013 Jean-loup Gailly and Mark Adler and is released under the [zlib license](http://www.zlib.net/zlib_license.html). The Crispy Doom icon (as shown at the top of this page) is composed of the [Chocolate Doom icon](https://www.chocolate-doom.org/wiki/images/7/77/Chocolate-logo.png) and a [photo](https://en.wikipedia.org/wiki/File:Potato-Chips.jpg) of potato crisps (Utz-brand, grandma's kettle-cooked style) by [Evan-Amos](https://commons.wikimedia.org/wiki/User:Evan-Amos) who kindly released it into the [public domain](https://en.wikipedia.org/wiki/Public_domain). The current high-resolution version of this icon has been contributed by JNechaevsky (formerly by Zodomaniac). crispy-doom-crispy-doom-5.6.4/TODO.md000066400000000000000000000032301360717211000173210ustar00rootroot00000000000000This is Chocolate Doom’s “to do†list. Note that this is kind of an arbitrary and unstructured wish list of features and improvements. The bug tracker (http://chocolate-doom.org/bugs) has more feature requests. * Multiplayer: - Use UPnP to automatically configure port forwarding for NATed networks. - Multiplayer options and configuration file (server name, etc) * Improve multiplayer startup: - Select an IWAD automatically from the server’s game type rather than all players having to specify -iwad. - Send list of WADs to load instead of all clients having to specify -file. - Same applies to dehacked patches and wad merging parameters. * Portability improvements: - Test on and fix for architectures where `((-2) >> 1) != -1` - Use size-specific types (eg. `int32_t` instead of int) - Don’t make structure packing assumptions when loading levels. - Port to every OS and architecture under the sun - Port to Emscripten and release a web-based version. * Video capture mode - Real-time recording of gameplay - Batch conversion of demos into videos * Heretic/Hexen/Strife: - Merge r_draw.c to common version and delete duplicates - Heretic v1.2 emulation (if possible) - Hexen v1.0 emulation (if possible/necessary) - Strife v1.1 emulation (for demo IWAD support) - Screensaver mode Crazy pie in the sky ideas: * Automatic WAD installer - download and run TCs from a list automatically (automating the current “instructions on wiki†system). * Textscreen interface to the Compet-N database: menu driven system to automatically download and play speedruns. * DWANGO-like interface for finding players and setting up games. crispy-doom-crispy-doom-5.6.4/autogen.sh000077500000000000000000000000531360717211000202330ustar00rootroot00000000000000#!/bin/sh autoreconf -fi ./configure "$@" crispy-doom-crispy-doom-5.6.4/cmake/000077500000000000000000000000001360717211000173145ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/cmake/FindSDL2.cmake000066400000000000000000000126571360717211000216360ustar00rootroot00000000000000# FindSDL2.cmake # # Copyright (c) 2018, Alex Mayfield # 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. # # Currently works with the following generators: # - Unix Makefiles (Linux, MSYS2) # - Ninja (Linux, MSYS2) # - Visual Studio # Cache variable that allows you to point CMake at a directory containing # an extracted development library. set(SDL2_DIR "${SDL2_DIR}" CACHE PATH "Location of SDL2 library directory") # Use pkg-config to find library locations in *NIX environments. find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(PC_SDL2 QUIET sdl2) endif() # Find the include directory. find_path(SDL2_INCLUDE_DIR "SDL_version.h" HINTS "${SDL2_DIR}/include" ${PC_SDL2_INCLUDE_DIRS}) # Find the version. Taken and modified from CMake's FindSDL.cmake. 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 "${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() # Find the SDL2 and SDL2main libraries if(CMAKE_SIZEOF_VOID_P STREQUAL 8) find_library(SDL2_LIBRARY "SDL2" HINTS "${SDL2_DIR}/lib/x64" ${PC_SDL2_LIBRARY_DIRS}) find_library(SDL2_MAIN_LIBRARY "SDL2main" HINTS "${SDL2_DIR}/lib/x64" ${PC_SDL2_LIBRARY_DIRS}) else() find_library(SDL2_LIBRARY "SDL2" HINTS "${SDL2_DIR}/lib/x86" ${PC_SDL2_LIBRARY_DIRS}) find_library(SDL2_MAIN_LIBRARY "SDL2main" HINTS "${SDL2_DIR}/lib/x86" ${PC_SDL2_LIBRARY_DIRS}) endif() set(SDL2_LIBRARIES "${SDL2_MAIN_LIBRARY}" "${SDL2_LIBRARY}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SDL2 FOUND_VAR SDL2_FOUND REQUIRED_VARS SDL2_INCLUDE_DIR SDL2_LIBRARIES VERSION_VAR SDL2_VERSION ) if(SDL2_FOUND) # SDL2 imported target. add_library(SDL2::SDL2 UNKNOWN IMPORTED) set_target_properties(SDL2::SDL2 PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_SDL2_CFLAGS_OTHER}" INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" IMPORTED_LOCATION "${SDL2_LIBRARY}") # SDL2main imported target. if(MINGW) # Gross hack to get mingw32 first in the linker order. add_library(SDL2::_SDL2main_detail UNKNOWN IMPORTED) set_target_properties(SDL2::_SDL2main_detail PROPERTIES IMPORTED_LOCATION "${SDL2_MAIN_LIBRARY}") # Ensure that SDL2main comes before SDL2 in the linker order. CMake # isn't smart enough to keep proper ordering for indirect dependencies # so we have to spell it out here. target_link_libraries(SDL2::_SDL2main_detail INTERFACE SDL2::SDL2) add_library(SDL2::SDL2main INTERFACE IMPORTED) set_target_properties(SDL2::SDL2main PROPERTIES IMPORTED_LIBNAME mingw32) target_link_libraries(SDL2::SDL2main INTERFACE SDL2::_SDL2main_detail) else() add_library(SDL2::SDL2main UNKNOWN IMPORTED) set_target_properties(SDL2::SDL2main PROPERTIES IMPORTED_LOCATION "${SDL2_MAIN_LIBRARY}") endif() endif() crispy-doom-crispy-doom-5.6.4/cmake/FindSDL2_mixer.cmake000066400000000000000000000107771360717211000230430ustar00rootroot00000000000000# FindSDL2_mixer.cmake # # Copyright (c) 2018, Alex Mayfield # 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. # # Currently works with the following generators: # - Unix Makefiles (Linux, MSYS2) # - Ninja (Linux, MSYS2) # - Visual Studio # Cache variable that allows you to point CMake at a directory containing # an extracted development library. set(SDL2_MIXER_DIR "${SDL2_MIXER_DIR}" CACHE PATH "Location of SDL2_mixer library directory") # Use pkg-config to find library locations in *NIX environments. find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(PC_SDL2_MIXER QUIET SDL2_mixer) endif() # Find the include directory. find_path(SDL2_MIXER_INCLUDE_DIR "SDL_mixer.h" HINTS "${SDL2_MIXER_DIR}/include" ${PC_SDL2_MIXER_INCLUDE_DIRS}) # Find the version. Taken and modified from CMake's FindSDL.cmake. 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 "${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() # Find the library. if(CMAKE_SIZEOF_VOID_P STREQUAL 8) find_library(SDL2_MIXER_LIBRARY "SDL2_mixer" HINTS "${SDL2_MIXER_DIR}/lib/x64" ${PC_SDL2_MIXER_LIBRARY_DIRS}) else() find_library(SDL2_MIXER_LIBRARY "SDL2_mixer" HINTS "${SDL2_MIXER_DIR}/lib/x86" ${PC_SDL2_MIXER_LIBRARY_DIRS}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SDL2_mixer FOUND_VAR SDL2_MIXER_FOUND REQUIRED_VARS SDL2_MIXER_INCLUDE_DIR SDL2_MIXER_LIBRARY VERSION_VAR SDL2_MIXER_VERSION ) if(SDL2_MIXER_FOUND) # Imported target. add_library(SDL2::mixer UNKNOWN IMPORTED) set_target_properties(SDL2::mixer PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_SDL2_MIXER_CFLAGS_OTHER}" INTERFACE_INCLUDE_DIRECTORIES "${SDL2_MIXER_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES SDL2::SDL2 IMPORTED_LOCATION "${SDL2_MIXER_LIBRARY}") endif() crispy-doom-crispy-doom-5.6.4/cmake/FindSDL2_net.cmake000066400000000000000000000106011360717211000224670ustar00rootroot00000000000000# FindSDL2_net.cmake # # Copyright (c) 2018, Alex Mayfield # 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. # # Currently works with the following generators: # - Unix Makefiles (Linux, MSYS2) # - Ninja (Linux, MSYS2) # - Visual Studio # Cache variable that allows you to point CMake at a directory containing # an extracted development library. set(SDL2_NET_DIR "${SDL2_NET_DIR}" CACHE PATH "Location of SDL2_net library directory") # Use pkg-config to find library locations in *NIX environments. find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(PC_SDL2_NET QUIET SDL2_net) endif() # Find the include directory. find_path(SDL2_NET_INCLUDE_DIR "SDL_net.h" HINTS "${SDL2_NET_DIR}/include" ${PC_SDL2_NET_INCLUDE_DIRS}) # Find the version. Taken and modified from CMake's FindSDL.cmake. 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 "${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() # Find the library. if(CMAKE_SIZEOF_VOID_P STREQUAL 8) find_library(SDL2_NET_LIBRARY "SDL2_net" HINTS "${SDL2_NET_DIR}/lib/x64" ${PC_SDL2_NET_LIBRARY_DIRS}) else() find_library(SDL2_NET_LIBRARY "SDL2_net" HINTS "${SDL2_NET_DIR}/lib/x86" ${PC_SDL2_NET_LIBRARY_DIRS}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SDL2_net FOUND_VAR SDL2_NET_FOUND REQUIRED_VARS SDL2_NET_INCLUDE_DIR SDL2_NET_LIBRARY VERSION_VAR SDL2_NET_VERSION ) if(SDL2_NET_FOUND) # Imported target. add_library(SDL2::net UNKNOWN IMPORTED) set_target_properties(SDL2::net PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_SDL2_NET_CFLAGS_OTHER}" INTERFACE_INCLUDE_DIRECTORIES "${SDL2_NET_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES SDL2::SDL2 IMPORTED_LOCATION "${SDL2_NET_LIBRARY}") endif() crispy-doom-crispy-doom-5.6.4/cmake/Findm.cmake000066400000000000000000000034771360717211000213660ustar00rootroot00000000000000# Findm.cmake # # Copyright (c) 2018, Alex Mayfield # 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. # # Finds libm, so we can link against it for math functions. If libm doesn't # exist, linking against the m target will have no effect. find_library(M_LIBRARY m) add_library(m INTERFACE) if(M_LIBRARY) target_link_libraries(m INTERFACE "${M_LIBRARY}") endif() crispy-doom-crispy-doom-5.6.4/cmake/Findsamplerate.cmake000066400000000000000000000055541360717211000232650ustar00rootroot00000000000000# Findsamplerate.cmake # # Copyright (c) 2018, Alex Mayfield # 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. # # Currently works with the following generators: # - Unix Makefiles (Linux, MSYS2) # - Ninja (Linux, MSYS2) # - Visual Studio # Use pkg-config to find library locations in *NIX environments. find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(PC_SAMPLERATE QUIET samplerate) endif() # Find the include directory. find_path(SAMPLERATE_INCLUDE_DIR "samplerate.h" HINTS ${PC_SAMPLERATE_INCLUDE_DIRS}) # Find the version. I don't know if there is a correct way to find this on # Windows - the config.h in the tarball is wrong for 0.1.19. if(PC_SAMPLERATE_VERSION) set(SAMPLERATE_VERSION "${PC_SAMPLERATE_VERSION}") endif() # Find the library. find_library(SAMPLERATE_LIBRARY "samplerate" HINTS ${PC_SAMPLERATE_LIBRARY_DIRS}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(samplerate FOUND_VAR SAMPLERATE_FOUND REQUIRED_VARS SAMPLERATE_INCLUDE_DIR SAMPLERATE_LIBRARY VERSION_VAR SAMPLERATE_VERSION ) if(SAMPLERATE_FOUND) # Imported target. add_library(samplerate::samplerate UNKNOWN IMPORTED) set_target_properties(samplerate::samplerate PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_SAMPLERATE_CFLAGS_OTHER}" INTERFACE_INCLUDE_DIRECTORIES "${SAMPLERATE_INCLUDE_DIR}" IMPORTED_LOCATION "${SAMPLERATE_LIBRARY}") endif() crispy-doom-crispy-doom-5.6.4/cmake/config.h.cin000066400000000000000000000006111360717211000215000ustar00rootroot00000000000000#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@" #cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@" #cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@" #cmakedefine PACKAGE_STRING "@PACKAGE_STRING@" #cmakedefine PROGRAM_PREFIX "@PROGRAM_PREFIX@" #cmakedefine HAVE_LIBSAMPLERATE #cmakedefine HAVE_LIBPNG #cmakedefine HAVE_DIRENT_H #cmakedefine01 HAVE_DECL_STRCASECMP #cmakedefine01 HAVE_DECL_STRNCASECMP crispy-doom-crispy-doom-5.6.4/configure.ac000066400000000000000000000134611360717211000205270ustar00rootroot00000000000000AC_INIT(Crispy Doom, 5.6.4, fabian@greffrath.com, crispy-doom) PACKAGE_SHORTNAME=${PACKAGE_NAME% Doom} PACKAGE_SHORTDESC="Limit-removing enhanced-resolution Doom source port" PACKAGE_COPYRIGHT="Copyright (C) 1993-2020" PACKAGE_LICENSE="GNU General Public License, version 2" PACKAGE_MAINTAINER="Fabian Greffrath" PACKAGE_URL="http://fabiangreffrath.github.io/crispy-doom" PACKAGE_RDNS="io.github.fabiangreffrath" PACKAGE_ISSUES="https://github.com/fabiangreffrath/crispy-doom/issues" AC_CONFIG_AUX_DIR(autotools) AC_CANONICAL_HOST orig_CFLAGS="$CFLAGS" AM_PROG_AR AC_PROG_CC AC_PROG_RANLIB AC_CHECK_PROG(HAVE_PYTHON, python, true, false) OPT_LEVEL=2 # If this is gcc, we have some options we'd like to turn on. Turn on # optimisation and debugging symbols. if test "$GCC" = "yes" then WARNINGS="-Wall -Wdeclaration-after-statement -Wredundant-decls" CFLAGS="-O$OPT_LEVEL -g $WARNINGS $orig_CFLAGS" fi PKG_CHECK_MODULES(SDL, [sdl2 >= 2.0.1]) PKG_CHECK_MODULES(SDLMIXER, [SDL2_mixer >= 2.0.0]) PKG_CHECK_MODULES(SDLNET, [SDL2_net >= 2.0.0]) # [Crispy] Check for zlib. AC_ARG_WITH([zlib], AS_HELP_STRING([--without-zlib], [Build without zlib @<:@default=check@:>@]), [], [ [with_zlib=check] ]) AS_IF([test "x$with_zlib" != xno], [ PKG_CHECK_MODULES(LIBZ, zlib, [ AC_DEFINE([HAVE_LIBZ], [1], [zlib installed]) ], [ AS_IF([test "x$with_zlib" != xcheck], [AC_MSG_FAILURE( [--with-zlib was given, but test for zlib failed]) ]) ]) ]) # Check for libsamplerate. AC_ARG_WITH([libsamplerate], AS_HELP_STRING([--without-libsamplerate], [Build without libsamplerate @<:@default=check@:>@]), [], [ [with_libsamplerate=check] ]) AS_IF([test "x$with_libsamplerate" != xno], [ PKG_CHECK_MODULES(SAMPLERATE, samplerate >= 0.1.8, [ AC_DEFINE([HAVE_LIBSAMPLERATE], [1], [libsamplerate installed]) ], [ AS_IF([test "x$with_libsamplerate" != xcheck], [AC_MSG_FAILURE( [--with-libsamplerate was given, but test for libsamplerate failed]) ]) ]) ]) # Check for libpng. AC_ARG_WITH([libpng], AS_HELP_STRING([--without-libpng], [Build without libpng @<:@default=check@:>@]), [], [ [with_libpng=check] ]) AS_IF([test "x$with_libpng" != xno], [ PKG_CHECK_MODULES(PNG, libpng >= 1.2.50, [ AC_DEFINE([HAVE_LIBPNG], [1], [libpng installed]) ], [ AS_IF([test "x$with_libpng" != xcheck], [AC_MSG_FAILURE( [--with-libpng was given, but test for libpng failed]) ]) ]) ]) # [crispy] true-color rendering as a compile-time option AC_ARG_ENABLE([truecolor], AS_HELP_STRING([--enable-truecolor], [true-color rendering (experimental) @<:@default=no@:>@]), AC_DEFINE([CRISPY_TRUECOLOR], [1], [true-color rendering]) ) # TODO: We currently link everything against libraries that don't need it. # Use the specific library CFLAGS/LIBS variables instead of setting them here. CFLAGS="$CFLAGS $SDL_CFLAGS ${SAMPLERATE_CFLAGS:-} ${PNG_CFLAGS:-} ${LIBZ_CFLAGS:-}" LDFLAGS="$LDFLAGS $SDL_LIBS ${SAMPLERATE_LIBS:-} ${PNG_LIBS:-} ${LIBZ_LIBS:-}" AC_CHECK_LIB(m, log) # [crispy] use stdlib's qsort() function for sorting the vissprites[] array AC_CHECK_FUNCS(qsort) AC_CHECK_HEADERS([dirent.h linux/kd.h dev/isa/spkrio.h dev/speaker/speaker.h]) AC_CHECK_FUNCS(mmap ioperm) AC_CHECK_DECLS([strcasecmp, strncasecmp], [], [], [[#include ]]) # OpenBSD I/O i386 library for I/O port access. # (64 bit has the same thing with a different name!) AC_CHECK_LIB(i386, i386_iopl) AC_CHECK_LIB(amd64, amd64_iopl) case "$host" in *-*-mingw* | *-*-cygwin* | *-*-msvc* ) AC_CHECK_TOOL(WINDRES, windres, ) ;; *) WINDRES= ;; esac AC_CHECK_TOOL(OBJDUMP, objdump, ) AC_CHECK_TOOL(STRIP, strip, ) AC_ARG_ENABLE([werror], AS_HELP_STRING([--enable-werror], [Treat warnings as errors])) AS_IF([test "x$enable_werror" = "xyes"], [ CFLAGS="$CFLAGS -Werror" ]) AM_CONDITIONAL(HAVE_WINDRES, test "$WINDRES" != "") AM_CONDITIONAL(HAVE_PYTHON, $HAVE_PYTHON) dnl Automake v1.8.0 is required, please upgrade! AM_INIT_AUTOMAKE([1.8.0 foreign]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) WINDOWS_RC_VERSION=`echo $PACKAGE_VERSION | sed 's/-.*//; s/\./, /g; s/$/, 0/'` dnl Without a hyphen. This is used for the bash-completion scripts. PROGRAM_SPREFIX=$(echo $PACKAGE_SHORTNAME | tr A-Z a-z) dnl With a hyphen, used almost everywhere else. PROGRAM_PREFIX=${PROGRAM_SPREFIX}- AC_SUBST(PROGRAM_PREFIX) AC_DEFINE_UNQUOTED(PROGRAM_PREFIX, "$PROGRAM_PREFIX", Change this when you create your awesome forked version) AC_SUBST(PROGRAM_SPREFIX) AM_CONFIG_HEADER(config.h:config.hin) AC_SUBST(WINDOWS_RC_VERSION) AC_SUBST(SDLMIXER_CFLAGS) AC_SUBST(SDLMIXER_LIBS) AC_SUBST(SDLNET_CFLAGS) AC_SUBST(SDLNET_LIBS) AC_SUBST(ac_aux_dir) AC_SUBST(PACKAGE_SHORTNAME) AC_SUBST(PACKAGE_SHORTDESC) AC_SUBST(PACKAGE_COPYRIGHT) AC_SUBST(PACKAGE_LICENSE) AC_SUBST(PACKAGE_MAINTAINER) AC_SUBST(PACKAGE_URL) AC_SUBST(PACKAGE_RDNS) AC_SUBST(PACKAGE_ISSUES) dnl Shut up the datarootdir warnings. AC_DEFUN([AC_DATAROOTDIR_CHECKED]) AC_OUTPUT([ Makefile man/Makefile man/bash-completion/Makefile man/bash-completion/doom.template man/bash-completion/heretic.template man/bash-completion/hexen.template man/bash-completion/strife.template midiproc/Makefile opl/Makefile opl/examples/Makefile pcsound/Makefile pkg/Makefile pkg/config.make pkg/osx/Info.plist rpm.spec data/Makefile src/Makefile src/Doom.metainfo.xml src/Doom.desktop src/Doom_Screensaver.desktop src/doom/Makefile src/Heretic.metainfo.xml src/Heretic.desktop src/heretic/Makefile src/Hexen.metainfo.xml src/Hexen.desktop src/hexen/Makefile src/resource.rc src/setup-res.rc src/setup/Makefile src/setup/Setup.desktop src/setup/setup-manifest.xml src/Strife.metainfo.xml src/Strife.desktop src/strife/Makefile textscreen/Makefile textscreen/examples/Makefile textscreen/fonts/Makefile ]) crispy-doom-crispy-doom-5.6.4/data/000077500000000000000000000000001360717211000171455ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/data/.gitignore000066400000000000000000000000541360717211000211340ustar00rootroot00000000000000Makefile.in Makefile *-doom.png *-setup.png crispy-doom-crispy-doom-5.6.4/data/Makefile.am000066400000000000000000000010451360717211000212010ustar00rootroot00000000000000 EXTRA_DIST= \ README \ doom.ico \ doom8.ico \ doom.png \ setup.ico \ setup8.ico \ setup.png \ convert-icon iconsdir = $(prefix)/share/icons/hicolor/128x128/apps icons_DATA = @PROGRAM_PREFIX@doom.png \ @PROGRAM_PREFIX@setup.png @PROGRAM_PREFIX@doom.png : doom.png cp $(top_srcdir)/data/doom.png $@ @PROGRAM_PREFIX@setup.png : setup.png cp $(top_srcdir)/data/setup.png $@ CLEANFILES = $(icons_DATA) crispy-doom-crispy-doom-5.6.4/data/README000066400000000000000000000013231360717211000200240ustar00rootroot00000000000000The Chocolate Doom icon is based on an image by Chris Metcalf (http://www.chrismetcalf.org/) which is copyrighted to him: http://www.flickr.com/photos/laffy4k/448920776/ Chris has kindly agreed that the Chocolate Doom icon may be used under the GNU GPL, so the copyright status of the icon is the same as that of the rest of the project. The "foo8.ico" files are 8-bit depth only, while the "foo.ico" files contain full 32-bit versions, scaled to different sizes and with proper alpha masks (as well as the 8-bit versions). The 8-bit versions are used when setting the icon within SDL, as SDL under Windows displays full color icons in a very poor quality. The full-color versions are used in the resource files. crispy-doom-crispy-doom-5.6.4/data/convert-icon000077500000000000000000000037601360717211000215070ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright(C) 2005-2014 Simon Howard # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # # Converts images into C structures to be inserted in programs # import sys import os import re try: import Image except ImportError: try: from PIL import Image except ImportError: print("WARNING: Could not update %s. " "Please install the Python Imaging library or Pillow." % sys.argv[2]) sys.exit(0) def convert_image(filename, output_filename): im = Image.open(filename) 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 const unsigned int %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%02x%02x%02x, " % val) elements_on_line += 1 if elements_on_line >= 6: elements_on_line = 0 outfile.write("\n") outfile.write(" ") outfile.write("\n") outfile.write("};\n") convert_image(sys.argv[1], sys.argv[2]) crispy-doom-crispy-doom-5.6.4/data/doom.ico000066400000000000000000003300661360717211000206070ustar00rootroot00000000000000€€ (†@@ (B®00 ¨%ÖJ(( h~p  ¨æŠ ˆ Ž› ¸¥ hΫ(€  Øóÿ–ÓïÿŒÎîÿÑïÿ¡Úóÿ¨Üóÿ¨Þôÿ¯ßõÿ¢ÙòÿšÕòÿ¢×ïÿ£×ïÿ¢ÖðÿŸÖîÿ¦Øïÿ¡×îÿ‡Ìêÿ™Óîÿ­ÜñÿŸØïÿÎëÿŒÔíÿ®Ýñÿ¥Öìÿ Öîÿ˜Ñìÿ©Úðÿ®ÛîÿžÐêÿ£Øïÿ«Üñÿ‡Çäÿy³ÿ n±ÿ q¶ÿt·ÿ}»ÿ*…ÁÿG”ÇÿNšÊÿRžÌÿH˜Ëÿ6’ÈÿˆÂÿˆÃÿv¶ÝÿŸËêÿˆÁãÿg°Øÿ_¬Öÿƒ»Þÿ†½áÿq·Ýÿ€ÀâÿÅåÿƒÀáÿc±ØÿB¡Îÿ>žÎÿ@žÎÿWª×ÿg³Ýÿ]®ÛÿQ©×ÿkµßÿx¹âÿy¼ãÿr¸áÿR¬Üÿf¶áÿs¼åÿp»ãÿo»åÿ`´áÿ[±ßÿT°Þÿs¾åÿÂåÿÇéÿ„Ãçÿ{Âæÿw¾æÿjºåÿl¹åÿe¶ãÿh¸ãÿ/ŒÌÿ}Äÿ…Èÿ9‘Íÿ)‹ÈÿA–Íÿ7Ëÿ+ˆÅÿ s»ÿj³ÿ|¼ÿ+‡Ãÿ|¹ÿq²ÿ s±ÿl­ÿ2Ãÿi¯×ÿy¶Ùÿ‡¿ßÿÂàÿ”Äàÿ˜Çáÿ‡ÂàÿÆâÿšÇâÿ’Ãßÿ…ÀÝÿ†½Þÿ}½Ûÿ½Üÿˆ¿Þÿˆ¾ÜÿŒÁßÿ}¼Üÿv¸Ùÿw¸ÙÿmµØÿrµØÿx¸Øÿi°Óÿb«Òÿ‡Ðïÿ”Ðîÿ¦×ðÿ›Óïÿ‘Ñïÿ¡Ùõÿ¦Üõÿ·ãøÿ¦Úôÿ£Ùòÿ¤Øðÿ¨Øðÿ«Ôïÿ«Øðÿ©Úñÿ¤Úñÿ”Ñïÿ¢×ðÿ«ÜðÿÌéÿpÃäÿËéÿ«Ûñÿ¦Öíÿ•Ìêÿ Óíÿ¤Öïÿ±Ûñÿ±Øíÿ¨×ðÿ‚Âãÿwµÿe­ÿe®ÿ`®ÿd²ÿyºÿ;ŽÇÿT™ËÿO™ËÿO›ËÿF—Ëÿ-ÇÿŠÅÿ(ŒÆÿ‚¼ãÿ¡ÏìÿÄåÿ~¹ßÿX¨Ôÿ‡¼âÿ–Äæÿ‘Åæÿ|¼ßÿƒÀâÿ‹ÃäÿnµÜÿXªØÿp¶ßÿ^¯Üÿn¶àÿk¶àÿR«ÚÿP©ÙÿZ¬Úÿe³ÞÿrºáÿZ¯ßÿQ«Üÿ[²ßÿk¹äÿu½åÿs½äÿ^´àÿZ±àÿO­Þÿd¸ãÿ}ÁæÿÇéÿ„ÅçÿÂçÿz¿åÿj¸äÿq»åÿp»æÿ`´àÿ)‰Èÿ~Åÿ%…Çÿ1ˆÇÿ)ˆÉÿÅÿ s½ÿ%~Åÿoºÿk¶ÿ t¸ÿ$‚¾ÿ€¼ÿu¶ÿp´ÿk®ÿHÉÿm±Õÿj±Öÿ„¼ÞÿÀáÿ‘ÂáÿŽÂàÿ‚½Ýÿ‹Áàÿ‘ÂàÿŒÁÞÿ…¾Ýÿ‡¿Þÿ¿Üÿ‚¾ÜÿƒºÛÿÂßÿ‘ÄÞÿ}¼Üÿx¸Ûÿw¹Úÿi²×ÿk±Ôÿo³Ôÿm²Õÿo²ÕÿyÄéÿ…Çëÿ¦Öðÿ¯ÚðÿœÑðÿ”Òñÿ™Öôÿ¨Ûöÿ²Þôÿ«Ûóÿ§×òÿ³Üôÿ»Þõÿ»áôÿ¶áôÿ™Ôñÿ–ÓðÿªÜóÿ´Þòÿ|ÄæÿqÄèÿ Õïÿ¢Óïÿ¦Øíÿ¥Öïÿ§Öïÿ¨Ôíÿ¦Ôíÿ´ÛòÿÀßÿk­ÿZ©ÿd¬ÿ`®ÿb±ÿZ°ÿ t¹ÿ*…ÂÿDÈÿH”ÉÿC“Éÿ>”Ëÿ$ŒÆÿŒÆÿ-ŽÉÿƒ»âÿ˜ÊëÿžÌìÿÈéÿj¯ÙÿT§Ôÿƒ½âÿ€¼áÿy¸Þÿs·Ýÿiµßÿf²Ýÿ€»ãÿg°Ûÿl´Ýÿ‡Àåÿ€½äÿ~¾äÿq¸âÿ\¯Üÿq¸âÿ~¾æÿ`²áÿT­Ýÿ[°Ýÿeµáÿ`³âÿ`³áÿX²ßÿ`´àÿqºäÿ„Áæÿ~¿äÿ€Ãäÿ…Ãçÿ{Àåÿ~¿åÿr¼æÿk»çÿX®Þÿ!ˆÈÿ‚Äÿ$†Éÿ€Æÿ&†Çÿ>’Íÿ/‰Éÿ-Æÿ?ŒËÿ1„Äÿ!{Àÿw»ÿ r¶ÿ t´ÿnµÿq·ÿ tµÿP¢Îÿh®Õÿg®Ôÿ‚¼ÛÿŽÂáÿ†¾àÿ}¼Ýÿ‡¿ßÿ‡Àßÿ‘ÃáÿÃßÿ†½Þÿƒ¼Üÿ¹ÚÿºÛÿºÜÿ·Úÿƒ¼Üÿ{¼Üÿx»Ûÿh³×ÿd²×ÿj²Õÿi±Öÿr³×ÿl²Öÿ‡ÇêÿžÑðÿ¥Òñÿ®Ùñÿ¯Øòÿ˜Ñïÿ|Æëÿ›Óòÿ¬Ûôÿ Ôòÿ³Þõÿ¸Þóÿ©Øòÿ¨Ûôÿ•Òïÿ†ËìÿˆÎíÿ‹Ðîÿ•ÓíÿÎìÿ¡Õïÿ²Ýòÿ·àóÿ¬Ùïÿ¥Ùðÿ¬Ùðÿª×ïÿ¢Ôïÿl­Õÿ\¢ÿQ¢ÿW¤ÿV¨ÿb¯ÿ pµÿ`°ÿbµÿ wºÿ}Àÿ*†Âÿ&ˆÄÿ(ŒÇÿ(Çÿ#ÇÿˆÄÿq³ßÿ“Æêÿ¢ÐðÿÊêÿ~¹ßÿS¥ÔÿJ£Òÿd­Ùÿ}ºáÿw¹áÿc²ÞÿÃèÿwµÞÿ ‹ÆÿJ Òÿ‘ÃçÿŽÄçÿŽÁçÿŒÄçÿ‹ÆèÿŽÅèÿ‘ÈëÿqºäÿP«ÞÿX°ßÿnºäÿv¾çÿÂçÿw¾åÿt¼åÿ}¾äÿ~Áåÿ~Àæÿ‹Æçÿw¼âÿ|¾åÿ•ÈëÿŠÅéÿr¿èÿ=šÐÿl¹ÿ~Äÿ1ŽÍÿ ‰Êÿ#†Éÿ"„Çÿ"‚ÅÿF”Îÿ3ŠÈÿ-†Æÿ%‚Âÿz¼ÿxºÿl²ÿh°ÿk±ÿ~»ÿ\¨Òÿ|¶Ùÿ{µØÿj°Õÿp·Ùÿ`²×ÿ|¼Ýÿ‹Ááÿz¼ÛÿƒÀÞÿƒ¾Þÿ†¾Þÿ…¾Þÿ‚»Üÿ}ºÛÿ‚¼Ýÿ»ÛÿŽÂáÿ‡Áàÿ}»ÛÿnµØÿf±Öÿl²Öÿj±Ôÿe­Ôÿg°ÖÿÏïÿ¬Õñÿ§Õñÿ¨Öóÿ¶Üóÿ«Ùòÿ{Äêÿ‰Êîÿ™ÐðÿžÖòÿ¥Úôÿ²ÞõÿŸÔóÿÓòÿ†Çéÿ‹Ëìÿ‡Ìíÿ}Èëÿ{ÇêÿÕñÿ°Ûðÿ­Ùðÿ°Ýòÿ£Øïÿ¤×ðÿ¨ØïÿšÕðÿ^¦ÐÿM–ÿB—ÿLŸÿV£ÿY§ÿc«ÿ n®ÿ o°ÿ\­ÿn¶ÿ w»ÿ z¾ÿ€Âÿ…Äÿ"ŠÅÿˆÃÿ{½ÿ>—Íÿ„¿äÿˆÂæÿÇéÿ’Åçÿ‡½ãÿM¥ÕÿG£Óÿ€¼âÿ‰Àäÿe²ßÿ†Âæÿ~ºáÿi­Ûÿ~¸áÿ’ÄèÿÅèÿ‚½äÿ†¿äÿ‹Âæÿ‰Âæÿ„Äæÿu½åÿ\±àÿd·âÿv¿åÿÂçÿ|ÁäÿŽÇéÿÈéÿ‚ÂçÿÃæÿŽÇéÿ“Èêÿl¸ãÿÀæÿŽÄèÿ|ÀæÿK Õÿg³ÿ^°ÿgµÿq¹ÿ _¯ÿl¹ÿh¸ÿ j·ÿ;‘ÍÿF•Íÿ/Çÿ1Éÿ%‡Äÿ1ŠÆÿ{ºÿk¯ÿl¯ÿ/ŒÁÿw³Ùÿ‚¸Úÿ‡¹Ûÿq±ÖÿQ©Òÿg²×ÿz¹Ûÿs¸Ûÿs¸Úÿt¸Ûÿw»ÚÿxºÚÿv¹Ùÿ¼Ýÿ‚¼Ýÿr´×ÿv·Ùÿ…¿ßÿ€¾Ýÿx¸ÙÿoµØÿj±Öÿn±Õÿc®Óÿb­Ôÿl²ÖÿŸÓïÿ§Óðÿ¨Óòÿ§Ôóÿ¨Õñÿ¨Øòÿ¢Öòÿ¥Öòÿ™ÖòÿÓðÿ’ÒñÿÖóÿ•Ôòÿ¥Üôÿ‡Ëêÿ’Ðïÿ«Ùñÿ¤Õðÿ¢×ðÿ¬Ûñÿ°×îÿ³Ûïÿ£ÕíÿÉéÿ‘Éêÿ˜ÐíÿJ‰ºÿ>Žÿ7ŽÿFšÿOžÿW£ÿW£ÿ[¢ÿ e¢ÿj¢ÿ f£ÿa¨ÿo³ÿp¹ÿ}¿ÿ |¾ÿ }¿ÿ„Âÿ z¼ÿ?—Ïÿ‡ÀåÿŽÃæÿ¾ãÿˆÄçÿŠÃçÿP¦Ôÿ?ÐÿY¦×ÿm²Ýÿeµßÿ‰Äèÿ˜Éëÿ˜ÈêÿˆÀäÿ‡Àäÿ‘Åéÿ†Áåÿ†ÃæÿÅçÿŽÃèÿÀåÿ{Àçÿf·ãÿU®ÝÿW¯Þÿt¼äÿy¾äÿ‚Âæÿ‰Åçÿ{¾äÿŒÈêÿ†Çèÿ~Áåÿkºãÿdºãÿz¼äÿ]¬Úÿo·ÿ\­ÿZ­ÿX«ÿS¨ÿL¤ÿYªÿR¥ÿHŸÿz¼ÿ „¾ÿ…Àÿ*‹Äÿ+ŽÆÿ†Âÿ€¿ÿ½ÿ€¼ÿY¥Ïÿy³Öÿz³×ÿ†¹Ûÿn²Öÿ`®Ôÿy¸Øÿt¶×ÿnµÚÿr¸Ùÿo·ÙÿiµÙÿb³Öÿi³Øÿo¶Øÿt·Ùÿt·Úÿt·Ùÿy»Ûÿt¸Úÿr¶Õÿq´Öÿe¯Õÿ_­Óÿb®Óÿg°Ôÿn³×ÿžÒðÿªÕñÿšÌìÿ•Êíÿ±Úóÿ©Öñÿ¨Ùòÿ·ÞôÿÔðÿ‰Ððÿ™Øóÿ£ÚóÿšÓïÿ­Ýôÿ˜Òïÿ“Ñîÿ±Ûñÿ¶Úñÿ±Ûïÿ¬Ùðÿ¼àóÿ±ÚïÿÏëÿ‰ÃäÿŒÍìÿT“Àÿ%zÿ1‹ÿ:’ÿD—ÿO¡ÿ]¦ÿYžÿW•ÿO†ÿP‚ÿR‡ÿY’ÿZÿX¤ÿc®ÿo¸ÿ|¿ÿƒÀÿs¸ÿ)„Âÿ}»ãÿ§Ïîÿ¦ÐíÿÆéÿj·àÿ<šÎÿHŸÒÿN¦×ÿT«Ûÿ‚Áæÿ‹ÄèÿŒÄæÿÃçÿŽÄèÿ’ÅéÿÆéÿ“ÉëÿÄèÿÆèÿ“ÇêÿˆÇèÿÈëÿ|Áæÿjºåÿx½äÿƒÁåÿ~Âæÿ…ÄçÿŽÆêÿ‰Äçÿ€Ãæÿ„Æêÿ~ÂçÿvÀæÿtÃéÿ‰ÅéÿN¢Öÿ)‰Éÿz¾ÿe°ÿa®ÿ\«ÿYªÿR£ÿU¢ÿQ ÿU¥ÿ`ªÿh­ÿi­ÿo²ÿ|·ÿ|¹ÿ}»ÿ†¾ÿc¬Òÿq°Óÿz´Öÿt´×ÿ`­Òÿm³Õÿq´×ÿnµ×ÿm´×ÿoµ×ÿd±ÖÿR­ÓÿV¯ÖÿY¯ÔÿZ°Öÿm¶Úÿl³×ÿj³Ùÿc±Öÿg²Õÿr´×ÿk±Ôÿ_¬Òÿ\¬Óÿc¬Óÿo³Ôÿf°Õÿ—Ððÿ¤Ñîÿ”Æëÿ„ÀéÿžÎïÿ­Ùóÿ Ôòÿ¸ßõÿ´Úóÿ²Ûóÿ´ßôÿªÚôÿ¯Ùòÿ¤Ôðÿ©Öðÿ¦Ôïÿ¦ÔïÿµÜòÿªÖïÿ«Ôîÿ±Øîÿ¬Öìÿ˜Êéÿo²×ÿB…¹ÿ>Šÿ.†ÿ7ŽÿG—ÿE›ÿMŸÿU ÿV˜ÿUŒÿ6‡°ÿXÿ3[ÿ:gÿ:xÿ1}ÿ[Ÿÿv²ÿx¶ÿx¸ÿo³ÿZ¥ÿOŸÒÿ£Îîÿ¬ÒðÿžÏîÿy¼ãÿG¥×ÿ[®Ûÿk¶àÿƒÃçÿ”Èëÿ˜Èêÿ—Èêÿ—ÉêÿšËëÿ Ïíÿ›Ììÿ—Ëìÿ–Êêÿ™Ìîÿ—ÎîÿšÑïÿÐïÿÉëÿ‚Åèÿ¿ãÿ~¿ãÿƒÆæÿˆÅèÿ”Ëìÿ—Ììÿ~ÂåÿŠÇéÿ—ÎîÿŽÌíÿ§Üõÿ‘Éêÿ>—ÎÿB¡ÖÿFÑÿ!¾ÿg¯ÿa­ÿd­ÿ_¨ÿU¡ÿQžÿV¡ÿW¡ÿNœÿJ˜ÿSÿa¤ÿ`¡ÿb£ÿ(…»ÿd­ÒÿY¤Ìÿi°ÓÿY¬Ñÿ_¬Òÿg®Ôÿ[«Óÿd°Õÿl³Õÿe¯ÔÿV«ÑÿP§ÐÿQªÒÿN«ÓÿV®ÕÿZ­ÔÿV«Óÿ]®Õÿ_®Óÿb®Óÿ_¬Òÿ`­Ñÿ[¬Òÿe®Óÿl²Õÿm²Öÿg¯ÖÿˆÈêÿŸÒñÿ¬Õðÿ¨Ôñÿ±Úòÿ²Ùñÿ¦Ôðÿ¤Õñÿ²Üòÿ¡Ñîÿ¥ÒñÿŸÒïÿÐïÿ©Õïÿ´Úïÿ®Ôíÿ§Óîÿ£Óîÿ§Õíÿ¥Òêÿ¬Ôëÿ£Îèÿf­Õÿ'j©ÿvÿ%}ÿ7Žÿ<’ÿE˜ÿLŸÿR¤ÿ\¢ÿY•ÿO€ÿeÀáÿqÐðÿF‘¯ÿ@aÿ%LÿKnÿ#fŠÿ&nšÿm£ÿ l¨ÿ l¬ÿVžÿ t´ÿ‚Áæÿ£Ðîÿ¢Ðïÿ˜ÊëÿQ«ÛÿT¬Ýÿ…ÂèÿÅéÿ’Èêÿ™ÌìÿžÍìÿŸÎíÿ›ÍìÿŸÐîÿÌìÿ›ÍëÿžÐíÿ§Õðÿ—ÏïÿžÒðÿ¥Õðÿ‘Ëìÿ“ÍìÿŠÊêÿ‰Éêÿ˜ÎîÿÌìÿ‹ÇèÿÈêÿ†ÈèÿˆÈêÿÎíÿŸÚõÿ’Óîÿ'j¬ÿK›ÿ%†Áÿ7›Íÿ7”Ëÿ}¼ÿf¯ÿc«ÿc¨ÿX£ÿR ÿU¡ÿNœÿM›ÿOžÿL™ÿLÿU‹ÿUÿq«ÿe«ÏÿY©ÏÿS§Ìÿe®Ðÿf­ÑÿT§ÎÿY©Ñÿc¯Óÿl³Õÿd°ÔÿY¬Òÿe±Óÿe¯ÔÿU«ÐÿW«ÓÿT©ÐÿS¨Ñÿ[¬Óÿ]­Òÿb¯Ôÿf¯ÔÿX«Ñÿ\¬Ñÿc®Óÿe°Ôÿg±Õÿh±Öÿ†Æéÿ˜ÑïÿŸÓïÿ¥×ñÿ¯Ùòÿ²Ùñÿ©Öðÿ«Ôðÿ¬ÖðÿªÓïÿ§ÑíÿšÌëÿ¦ÓïÿžÐìÿ£Òíÿ¬ÕíÿžÏêÿŠÅåÿ“ÉçÿžÍêÿ˜ÆæÿO™Êÿ n±ÿc®ÿ:ÿ!}ÿ2Œÿ@—ÿMÿR¡ÿ\ªÿ g©ÿa˜ÿP|ÿ`¹ÙÿuÖöÿxÚúÿoÍìÿL•±ÿ#Tkÿ=UÿKjÿ%Z€ÿMÿN‹ÿP“ÿP–ÿAŽÀÿŽÄçÿŸÍíÿŒÅèÿI¨Úÿq¼äÿ–Èëÿ˜Ëìÿ•ÉìÿÎîÿ™ËíÿÆéÿ“Éëÿ˜Ñïÿ•ÍîÿÑïÿ©Øðÿ­Úòÿ™Ðïÿ¤ÕñÿšÑîÿˆÉéÿËëÿÊëÿ–ÐïÿŸÒðÿšÐïÿ˜Ïîÿ‚Çêÿc¾çÿiÃéÿ~Íïÿa¯Úÿa¨ÿG™ÿMœÿOžÿq³ÿˆÂÿ…¿ÿ~»ÿe­ÿa¨ÿ^¦ÿX£ÿOžÿLšÿQÿT ÿIÿSŠÿWÿYÿ cÿD•Âÿa«Îÿ}¶×ÿy´Öÿp²Ôÿq²Ôÿj±Õÿ]­Óÿ^¯Ôÿd±Õÿb±Ôÿi²Õÿb¯ÓÿY¨ÑÿR§ÐÿWªÐÿ]­ÑÿY¬ÒÿY­ÓÿY¬ÒÿX«ÑÿJ£ÍÿP¦ÍÿXªÐÿa®Óÿf±Õÿg´Øÿ™ÑñÿœÔòÿ–Îîÿ’Êëÿ—Ëìÿ§ÑðÿžÎîÿ§Óîÿ§Ôíÿ¡ÐíÿœËëÿ¢Ðìÿ©ÒìÿŸÎêÿŸÏëÿ˜Ëéÿ™Ëéÿˆ¿àÿo´Úÿ?~²ÿr´ÿrºÿ|¾ÿ[¤ÿ!zÿ*…ÿ7‘ÿJ›ÿR¡ÿS£ÿ[©ÿ iªÿ`—ÿS|ÿa¹ÙÿtÔôÿjÍðÿkÏòÿxÛúÿpÏîÿN˜³ÿEaÿ?ÿ!Lÿ5hÿB|ÿG…ÿ R’ÿ^¢Êÿ’Åãÿz¼âÿG¨Ùÿc¶áÿŒÅéÿƒÆéÿ‹Æéÿ Ððÿ¥Ôðÿ™ÏîÿÊìÿ—Óðÿ™Óòÿ¥Øóÿ³Þòÿ­Üñÿ©Ùñÿ§Øñÿ—Ïîÿ‡Éëÿ’ÎîÿŽÍìÿ˜Óïÿ‰Ëíÿ€ÈëÿvÅéÿa½çÿpÃëÿ‘Ôñÿ]±ÜÿX¤ÿR¢ÿ\¨ÿZ§ÿZ¦ÿZ£ÿ_¤ÿq³ÿ¼ÿx¶ÿaªÿ^¨ÿ[¤ÿTŸÿTÿV¡ÿP˜ÿR‰ÿVÿXÿXÿs¬ÿF›Æÿo±Óÿ~¹ÙÿtµÖÿt´×ÿr³Ôÿn±Ôÿe±×ÿ_°ÖÿZ®ÔÿY¯Óÿ_¯Óÿa¯Óÿ[¬ÒÿP¨ÐÿL¦ÎÿP§ÏÿB£ËÿJ¦Îÿ]®ÓÿYªÒÿG ÉÿH¤ÌÿX¬Òÿo´ÕÿoµÕÿmµÙÿ3ÌÿY©Ûÿ‰ÈìÿŸÓñÿšËìÿ“Åéÿ”ÆéÿŸËìÿžÌëÿžÎêÿ¡Ðëÿ­Õîÿ¹Ùíÿ¬Õëÿ˜ÊèÿÆæÿÇåÿƒ¿âÿf±Ùÿ<‰½ÿ&…Âÿ„Âÿz¹ÿ,„ÿ!ÿ1Šÿ;•ÿP¡ÿX¥ÿZ¨ÿ]ªÿk¬ÿjÿW}ÿb»ÛÿuÕôÿaÈðÿO¿ðÿ\ÆðÿlÏòÿwÚúÿrÑðÿM–³ÿGdÿ@ÿ$Nÿ4fÿ;tÿI…ÿKŽºÿl¯ÓÿR¦ÓÿH¨Øÿ`·âÿyÃéÿˆÁèÿ›Ìíÿ¢Õòÿ“Ííÿ”Ñîÿ ×ñÿ£Øñÿ“Ñïÿ§ÙòÿªÛóÿ¨ÚóÿŠÌëÿvÂæÿyÆéÿƒÈéÿˆÉëÿyÄéÿc¼æÿh¾çÿc½çÿO¹æÿyËïÿŠÍîÿv³ÿb©ÿbªÿc«ÿaªÿd¬ÿ]¥ÿOšÿN’ÿ t³ÿ|·ÿh¬ÿ^§ÿX¡ÿW ÿUžÿM™ÿPŽÿT‹ÿWÿYÿ^›ÿ7Åÿl±Õÿv·×ÿpµ×ÿi²Õÿo³Õÿj¯Ôÿc­Óÿh´×ÿd³×ÿ^¯Õÿ\¯Õÿ^¯Õÿ\¯Õÿ_¯ÓÿUªÑÿH¥ÎÿV©ÐÿT¨ÎÿN§ÍÿU«ÒÿXªÐÿUªÑÿY­Óÿo¶Øÿr¶Øÿl´ÖÿkµÙÿkµÿe²ÿ7Ëÿ‚Åéÿ Óïÿ§ÓîÿªÕíÿµØîÿ¨Ôîÿ§ÔíÿŸÏìÿ–Êêÿ¯×íÿŸÎéÿŽÄäÿ˜ËéÿƒÂãÿ=†¹ÿKŠÿD–ÊÿL¦Öÿ‰ÅÿPœÿwÿ(†ÿ3ŒÿA™ÿW§ÿ]©ÿa­ÿi°ÿt±ÿ"nžÿ.d…ÿf¾ÝÿtÔôÿbÉðÿN¿ðÿN¿ðÿPÀðÿ[ÅðÿkÏòÿxÛúÿsÓòÿPš¸ÿIfÿ>ÿ&Oÿ4eÿ?wÿ$s¢ÿ0Œ»ÿ5™ËÿOªÙÿs¿åÿ‘Êëÿ›ÏïÿŒËíÿšÔïÿ­Ûóÿ©Üóÿ±àôÿ“Óðÿ®Þôÿ¬ÜôÿšÓðÿ„ÇèÿxÃæÿtÄçÿ†ÉêÿÍíÿ{Äèÿ|Åêÿ ÓðÿˆËíÿsÉïÿjÀèÿ3Çÿ n¯ÿi¬ÿi­ÿ o¯ÿg¬ÿf«ÿZ£ÿZ¢ÿFˆÿP“ÿi®ÿe«ÿY¢ÿQÿP›ÿM–ÿMŒÿTŠÿVÿYÿYÿ n¨ÿH¡Íÿt¹Øÿl³Öÿn³Öÿh±Õÿh±Õÿh¯Ôÿ^­Óÿ`²×ÿa²×ÿa´×ÿc³×ÿ_°Öÿ]¯ÕÿV­ÔÿN©ÑÿG¦ÐÿUªÒÿT¨ÐÿJ¥ÎÿI§ÎÿW«ÒÿV­ÓÿX¯Õÿh¶Ùÿq¹ÚÿoµØÿr¹Üÿ„Âÿw½ÿa°ÿ `¯ÿ-{½ÿˆÃèÿÎíÿ¨Öîÿ£Ðìÿ®Öïÿ¨Ôíÿ•ÈæÿŒÂãÿ‰¿âÿŠÅæÿl¬ÔÿP˜Åÿ.{ÿ +~ÿTªÙÿM«Úÿ}»ÿ%€ÿ}ÿ1ÿ@–ÿO ÿX¥ÿ`¬ÿf°ÿ{½ÿ$¸ÿ5x¥ÿ;k‰ÿe¾ÝÿtÔôÿaÉðÿN¿ðÿPÀðÿPÀðÿN¿ðÿPÀðÿZÅðÿjÎñÿwÛúÿtÕôÿRž»ÿ Nkÿ"Bÿ!JÿV~ÿm™ÿ'¯ÿ=“ÃÿW©Óÿ|ÀâÿƒÇèÿÏíÿ¦Ûóÿ±Þóÿ¦Ûòÿ³áõÿœÙòÿ¦Üóÿ Øòÿ‘Ðîÿ‰Ëëÿ~Æçÿ‡Ìëÿ‰Íìÿ€ÈêÿsÃéÿ–ÒðÿžÔñÿÍðÿh¿éÿ&ŠÂÿu´ÿy¶ÿu²ÿt³ÿu´ÿr²ÿi¬ÿ g«ÿ o°ÿc¦ÿ?€ÿKÿUŸÿO˜ÿIÿIŽÿKŠÿRŠÿVÿZÿ\’ÿ^–ÿ%‡¼ÿY­Õÿ_°Ôÿb°Óÿd°Õÿ`¯Õÿ\°Õÿa²×ÿ_±Öÿ]¯×ÿR¬ÕÿQ­ÕÿX­Õÿ_±ÖÿY¯ÖÿI§ÑÿD¦ÐÿK§ÐÿK¦ÎÿM§ÏÿQ§ÐÿN¨ÏÿRªÑÿSªÔÿO«Óÿ_³Øÿn¸Ûÿw¾Þÿn¼ßÿ,ŽÊÿ$ˆÅÿ|¿ÿg±ÿV©ÿl³ÿ-‚¿ÿB“ÈÿB‘ÅÿW Ïÿ]¦Ôÿf±Úÿa¦Òÿ4†¿ÿ%`¢ÿ(xÿpÿpÿ4‰ÿS®ÜÿT³ßÿf«ÿxÿ-‰ÿ@–ÿU¤ÿ[¨ÿb­ÿr·ÿ(„Âÿ,‹Åÿ7Œ¿ÿIƒªÿ.f…ÿd¿ÝÿtÔóÿaÈðÿN¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿN¿ðÿPÀðÿYÅðÿiÎñÿwÚúÿuÖõÿU£Àÿ!Qnÿ=WÿLkÿa‡ÿ'užÿ=‹´ÿT¤ÊÿqºÙÿ Ôëÿ£Ùðÿ§Üóÿ Úóÿ¯â÷ÿ•Õñÿ”ÓðÿŽÎíÿˆÌëÿ„Êëÿ~Çéÿ|ÇêÿÉìÿzÉìÿzÊìÿ–×óÿ…ËîÿQ«Ùÿ0Åÿ‚½ÿ'ˆÁÿ-ˆÁÿz¶ÿy¶ÿ}¸ÿ~¹ÿ%ƒ¾ÿ0ŠÁÿ#…½ÿ„»ÿ T™ÿAÿ@ÿJ•ÿL“ÿR—ÿU›ÿR—ÿW”ÿ[•ÿ^”ÿdÿ0Âÿa¯Õÿ^®ÕÿW¬ÓÿX¬ÔÿY®ÔÿQ®ÔÿU®ÖÿY®×ÿY®ÔÿJ«ÓÿF¨ÓÿH©ÓÿIªÓÿC§ÐÿH¨Ñÿ@¤ÏÿB¤ÐÿM©ÑÿI§ÐÿA¤ÎÿF¥ÏÿK©Ñÿg·Ûÿh¶Ýÿa´ÚÿmºÝÿe¸Þÿ_·Þÿ(Éÿ*‹Çÿ1ŽÉÿ{»ÿq¶ÿl´ÿ`«ÿ^ªÿc­ÿ p¶ÿ#¿ÿE’Çÿ@ŽÄÿu·ÿ1…ÿpÿxÿ%€ÿ R¨ÿW¬Ýÿ]½åÿ"Wžÿ%…ÿB—ÿLÿU¤ÿd®ÿ{»ÿ1ÈÿG—ÌÿD•ËÿZ›ÇÿM†ªÿ!_ÿd½ÜÿuÔóÿaÈðÿN¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿN¿ðÿOÀðÿYÄðÿhÍñÿvÙùÿu×öÿ[«Çÿ-fÿ ?Yÿ-Xpÿ?tÿQŽªÿc¢¿ÿ|¸Ñÿ’Èáÿ¢Öëÿ ×ïÿ˜Õðÿ™Õðÿ‰ËìÿˆÍëÿŽÎíÿ|ÉëÿpÄêÿyÇìÿnÇíÿ„Îïÿ}ÎðÿzËîÿH¢Óÿ9Åÿ:’Èÿ7’Çÿ.Åÿ4Çÿ)…¿ÿ%ƒ½ÿ+…¾ÿ+…¾ÿ6ÁÿD”Çÿ<‘Åÿ)ŠÂÿ$‚¼ÿE‹¾ÿb¨ÿ[¤ÿ^¤ÿe«ÿf¬ÿ m¯ÿl­ÿS›ÿ[–ÿk£ÿ8•ÇÿY¬ÓÿY­ÔÿY¬ÔÿT«ÓÿU­ÔÿPªÒÿU¯ÕÿQ­ÕÿP¬ÕÿL«ÔÿF¨ÒÿD¦Ñÿ@¥ÏÿD¦ÑÿC¦Ðÿ>£Îÿ;¢Íÿ<¡Ìÿ@¢Íÿ4œÉÿG¦Ðÿt¾ßÿÂâÿˆÉçÿg·Ýÿ]³Úÿc¹Þÿ`¶ßÿ#ŒÆÿ-‹ÇÿI›Ðÿ*…Àÿ}½ÿ(‚Àÿ{¼ÿf®ÿm³ÿ{¹ÿ"ƒ¾ÿE’ÇÿG—Èÿv¶ÿNÿ&}ÿ"~ÿEœÿe´ÿI¥×ÿa½äÿY¡ÿ9ÿE˜ÿO¡ÿc®ÿ{»ÿ"…Âÿ2‹ÆÿK˜ËÿO›ÌÿI˜Äÿ+tŸÿ9fÿc¹ÛÿuÕôÿ`ÈðÿN¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿN¿ðÿOÀðÿXÄðÿhÌñÿu×øÿu×öÿ]¯Ëÿ>r‡ÿ)Oaÿ5^rÿ@y“ÿ`˜®ÿ®ÃÿоÔÿ‹ÅÞÿ{ÃáÿˆÌéÿÈéÿƒÊëÿŽÎîÿÐðÿtÈíÿfÀéÿ\¾êÿÍñÿcÀìÿM¨ÖÿB›ÌÿHžÎÿKÎÿD˜Éÿ8‘Çÿ4Åÿ4Åÿ2ŒÂÿ7Ãÿ.ˆÀÿ7ÅÿA—ÉÿQ¡ÏÿLœËÿ;–Éÿ_°ÚÿvÁäÿqÀæÿk¹àÿL¢ÒÿV¦Õÿ<ŽÅÿ%~ºÿf¯ÿXšÿj¡ÿ>žËÿW­×ÿV¬Õÿ_±ØÿZ°ÙÿM«ÔÿS®ÕÿY¯ØÿO«ÔÿR«ÕÿMªÔÿJ©ÔÿI©ÒÿC¥ÐÿE¦Ðÿ=¤Îÿ-œÊÿ2Éÿ-™Æÿ1šÇÿOªÔÿÈäÿœÔëÿ¡Ôëÿ¤ÚïÿƒÇåÿP¬Öÿ_·Þÿr¿åÿ;˜ÏÿHŸÐÿFœÍÿ#ƒÀÿ*†ÃÿVžÎÿAÅÿj¯ÿq²ÿy¸ÿ*„¿ÿ8ÄÿI™Êÿ-†¾ÿ`«ÿD”ÿ%‚ÿM¥ÿ\®ÿAœÒÿnÃèÿ4‚¼ÿ<’ÿOŸÿZ¨ÿs¹ÿ¿ÿ)‡Ãÿ<ÈÿI—ËÿH™Ìÿ-‰¼ÿ6sÿ"Yÿe½ÞÿuÕôÿ`ÈðÿN¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿN¿ðÿO¿ðÿXÃðÿgÌñÿt×÷ÿu×÷ÿa³Ïÿ=v‹ÿ$Qcÿ.]qÿW“ÿ^“ªÿVœ¹ÿT¦Èÿj¸Øÿn¾âÿ‚ÉèÿÒðÿœÛõÿgÀéÿM¬ÝÿG¥ÙÿN©ØÿF£Õÿ<Ðÿ@ŸÏÿF¡ÐÿL ÏÿMžÎÿIšÊÿF™ÉÿD—Èÿ2ŠÀÿ9Åÿ?”Èÿ;’ÇÿF›ÊÿP¡ÏÿT©ÖÿjÄëÿŒÚôÿßùÿ“äûÿœæýÿãúÿžáùÿ“ØóÿÙóÿs²ÛÿoªÿrªÿE¢Ïÿ]°ØÿR«ÕÿV®×ÿKªÓÿIªÓÿI«ÕÿHªÕÿOªÔÿNªÕÿH©ÔÿG¨Óÿ@¤Ðÿ9¡Îÿ8¡Íÿ8¢Íÿ'˜Èÿ/›Éÿ/šÉÿ? Íÿ‚Êæÿ|Çåÿ’Ðéÿ¥Ûíÿ­ßòÿÄæÿZ°Ùÿ\µÝÿl¾åÿDœÑÿP¢Ñÿ6”Êÿ¾ÿ€ÀÿRžÏÿRœÌÿ o±ÿm±ÿ"|¹ÿ)ƒ½ÿ4ŒÃÿ&‹Âÿ|ºÿh®ÿ@‘ÿ!ÿO¤ÿa±ÿB›Óÿd»äÿRª×ÿG—ÿHŸÿq·ÿ}¾ÿ%ƒÁÿ+ˆÃÿ.ŠÅÿ6’Éÿ9šÍÿ]›ÿbÿ *^ÿf¿ßÿtÕôÿ`ÈðÿN¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿO¿ðÿO¿ðÿWÃðÿfËðÿsÖ÷ÿvØ÷ÿ`¶Òÿ;wÿ2Veÿ8]oÿBwÿPŽ©ÿR›»ÿT¨Êÿl¹Úÿ„Êèÿ†Íëÿ@ ÓÿC ÓÿM¦×ÿG£Ôÿ<žÐÿ>¡Ôÿ9¡Óÿ>¢Ñÿ@žÐÿH ÐÿP£ÐÿT¢ÐÿQ¡ÎÿOžÌÿF˜ÉÿD•ÈÿB—ÈÿNËÿW¤ÑÿqÅêÿ‘Û÷ÿ¤æüÿœäüÿ™ãüÿèüÿ¥êýÿ¦çûÿ¯æúÿ°æüÿšÛõÿ‚ÏîÿrÃçÿdµßÿZ¬ÖÿO¨ÓÿS®ÖÿN«ÔÿI©Óÿ@¦ÒÿDªÔÿN¬ÖÿK«ÕÿD©ÓÿE©Óÿ=¥Ñÿ7¡Íÿ7ŸÍÿ1ŸÎÿ0Éÿ2ŸÌÿ/œËÿ[¶ÛÿˆÐéÿŒÍëÿŸÙíÿ£Üîÿ§Ýñÿ†Éèÿf¸àÿk¼äÿoÀäÿT£Òÿ_¨ÔÿE˜Êÿ †ÀÿB•Ëÿ^¦ÑÿNšËÿs´ÿj®ÿw¶ÿ-‚½ÿ1ˆÁÿ}ºÿrµÿPžÿ$ÿ ‚ÿM¡ÿh±ÿ8“Îÿf»åÿW´ßÿWŸÿR¢ÿv»ÿ|½ÿ"‚Àÿ †Áÿ!‰Âÿ ÈÿUžÿrÿ hÿ )^ÿfÀàÿtÕôÿ`ÈðÿN¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿO¿ðÿN¿ðÿVÃðÿeËðÿsÕöÿvØøÿd¹ÕÿC}’ÿ6XfÿAapÿG{‘ÿV’¬ÿ[Ÿ½ÿ[§Éÿ;—Âÿˆ½ÿ*•Èÿ)•Ëÿ#˜Îÿ9£ÔÿB¦Õÿ9¢Óÿ@£ÕÿJ¦×ÿD¤ÓÿF£ÑÿK¢ÐÿO¤ÑÿX¨ÒÿR£ÏÿG›ËÿN¡ÎÿP¡Íÿb´Þÿ€Ôôÿ¥æûÿ£çûÿ¢éüÿŸçûÿ™åúÿ¥êýÿ°ìýÿ²ëýÿ°êüÿ©åúÿÙõÿŠØöÿŒØôÿ}Æèÿ^°ÙÿM§Óÿ[®ØÿX¯Øÿ?¦ÐÿE©ÓÿX²ÚÿJªÕÿ=£Ðÿ<¤Ïÿ6£Ïÿ3¡Ïÿ0ŸÍÿ —Éÿ•Çÿ+™ÉÿB¦ÐÿsÄäÿƒÍéÿ£ÚîÿªÝðÿ£Ûðÿ«ßñÿ Õíÿt¾âÿo¾âÿm¾ãÿX¥ÑÿV£Ðÿ>–Èÿ+ŠÄÿb¨Õÿu°×ÿb¥Ðÿ|¹ÿr²ÿvµÿy¸ÿ%†¾ÿ9Äÿl®ÿ-†ÿ!‚ÿ%†ÿ=–ÿ i±ÿ4–Ïÿj½åÿtÆëÿ8„¿ÿ[«ÿ x¼ÿ€¿ÿ¾ÿÀÿ…Âÿw¹ÿ>ÿsÿ#jÿ ,aÿgÂáÿtÕóÿ`ÈðÿN¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿPÀðÿOÀðÿN¿ðÿVÃðÿdÊðÿrÕöÿvØùÿg¼ÙÿP…˜ÿ9[hÿ2Ykÿ2k…ÿ6x›ÿ1‚ªÿ0‰¶ÿ1Àÿ Äÿ”Çÿ(›Íÿ6ŸÐÿ9£Óÿ<¤ÓÿX¯Úÿ@¤ÔÿA¤ÕÿP§×ÿI£ÒÿA ÏÿH£ÐÿD ÏÿL¥ÒÿM§ÕÿÏïÿâúÿ¨èüÿ¥èüÿ«íýÿ–ãûÿ èüÿ«ìýÿµíýÿ¯ìýÿ¦êüÿçüÿ”Þ÷ÿ¬äúÿ¢ãøÿ›àøÿ‘Ùóÿ{Ãåÿd±ØÿX®ØÿE©ÓÿK¬ÖÿE«Öÿ>¦Òÿ?¥Ïÿ7¢Îÿ(œËÿ˜Éÿ'›Ëÿ,šÊÿ2œËÿF¨ÑÿwÆåÿ‡Ñëÿ”Õëÿ§Ýðÿ¦Ûïÿ¡Úîÿ¥Üðÿ¤×îÿn¹ßÿh¼áÿrÁäÿX¡ÎÿS¡ÏÿKžÌÿ,‹ÂÿW£Òÿ·Ûÿi¬Ôÿ!ºÿm±ÿy¸ÿ|¼ÿ$…ÀÿS¢Ðÿ%t±ÿ"ÿ'…ÿ,‹ÿC›ÿc°ÿ%ŒÉÿf¸âÿwÇìÿc²Ýÿe¯ÿu»ÿ¾ÿ$‰Ãÿ€¾ÿo¶ÿr·ÿJ—ÿtÿ"kÿ .bÿhÃãÿtÕóÿ_ÇïÿM½îÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿO¾íÿN½íÿM½íÿTÀîÿcÈïÿrÔõÿvÙúÿf¾ÛÿB–ÿ*Qcÿ'Peÿ4jƒÿC€žÿKްÿL—½ÿ=˜Äÿ+–Æÿ.›Êÿ=£ÓÿD§×ÿJªÙÿX±ÜÿX¯Ûÿc³ÞÿZ°ÛÿV®Úÿ[°ÛÿY®ÚÿW­Ùÿ`¶âÿ“Ý÷ÿªéüÿ±ëýÿ©êüÿŸäúÿ”áùÿ¬êýÿ»ïýÿ¯ìüÿ³ìýÿ½ïýÿ´ïüÿªëüÿ¹êýÿ½ëýÿ±éüÿªçûÿ˜ßøÿ‡Îëÿr¹ÞÿQ©Õÿ=¤Ñÿ4£Ðÿ>¦Òÿ;¢Îÿ2ŸËÿ"–ÇÿÄÿ)™ÉÿB¥Òÿe¼àÿÖíÿ¦Ýñÿ¢Ûïÿ Ûðÿ˜Øîÿ£Ûïÿ§Ûíÿ ØíÿˆËèÿr¼ÞÿwÀáÿvÂäÿ\¤ÐÿV¢ÎÿJ›ÊÿKžÍÿg¬Ôÿw²Öÿd¬Óÿ{¸ÿg°ÿv¶ÿ#|»ÿ5ÄÿP ÎÿDÀÿ*„ÿ)‡ÿ1ŽÿP£ÿb¯ÿ…ÃÿO¬ÛÿZ¸äÿ_¿çÿ#„Áÿo¶ÿ z¼ÿ |¼ÿc²ÿj³ÿ {»ÿSŸÿ3‡ÿ.tÿ >nÿhÅäÿtÔóÿ]ÄìÿK·çÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿM¹èÿK¸èÿJ¸èÿQ¼éÿaÆìÿqÓõÿxÚúÿiÁÞÿH…›ÿ-Tfÿ.TfÿBn…ÿN„ŸÿJޝÿC”»ÿDšÆÿ?žËÿE¥Óÿ]±Üÿrºâÿs»âÿmºàÿi¶ßÿb²Ýÿ`³Ûÿg¶Ýÿl¶àÿƒÌïÿ©åûÿ§äûÿ°ëýÿ¢èûÿ’Þøÿ–à÷ÿ¯ëüÿ´íýÿ¯êüÿ¦éüÿ°ìüÿ½ðýÿºðýÿ¶îýÿ·ìýÿ·ìýÿ²êýÿ¦ãùÿªçûÿ¸êûÿ™Ôðÿn¾âÿe»àÿR®ØÿNªÖÿL«ÖÿMªÕÿgºßÿÐëÿ¥Þñÿ¡Ýòÿªâôÿ´äõÿ¬àòÿ›Úðÿ—×íÿ«Ýïÿ¥Øíÿ•ÑìÿzÁáÿu½ÞÿƒÅäÿ{Ââÿc«Ôÿ]¥Ðÿ`§Ôÿa«Õÿj¯Öÿl®ÔÿaªÑÿu¶ÿl´ÿ/‰ÂÿY¡ÐÿQ¡ÎÿH›Ëÿ-¹ÿ*†ÿ+Œÿ3“ÿGžÿe°ÿ …Âÿ:ŸÓÿdºäÿT·åÿV°Þÿ~¾ÿm´ÿK£ÿI¤ÿq¸ÿ}»ÿ_§ÿJ™ÿF†ÿ KvÿiÆåÿtÔóÿ\ÁéÿH²áÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿJ´ãÿI³âÿH³âÿO·äÿ_ÃéÿpÒôÿwÛûÿiÃáÿK‰Ÿÿ:Zhÿ;Wfÿ?mƒÿIœÿL®ÿL™½ÿP¢Éÿm³×ÿy¼Ýÿt¼àÿq¼áÿn¸àÿi¶ßÿdµÞÿpºßÿx¿åÿ•Úõÿ¬çûÿ¶éüÿ¯éûÿ¡åúÿÚôÿÚõÿ äùÿªéüÿ¬éýÿªêüÿªëýÿ©éüÿ±éüÿµíüÿ²íýÿ²ëýÿ²êýÿºëüÿºëýÿµëþÿ²ëüÿ¬èûÿ·êûÿ¬ãøÿŸßõÿ¤âöÿ±æùÿ»ìûÿ¾ìùÿ¶è÷ÿµå÷ÿ¹ç÷ÿ«àòÿ ÜñÿžÛñÿ™Øîÿ Öìÿ…Åäÿx¶Úÿj´Øÿq¹Üÿ|Ááÿo»Þÿe«Ôÿa©ÒÿcªÔÿeªÔÿdªÓÿZ¨ÑÿS¡Íÿp³ÿj±ÿ#…¾ÿ<“ÆÿC™Éÿ9“Æÿy¶ÿ-‰ÿ.Žÿ9•ÿI¡ÿc°ÿ‚Àÿ:™Ñÿo»äÿi¿éÿ‡Íïÿq¹àÿN ÿ:›ÿV¬ÿw»ÿ x·ÿc«ÿ]¢ÿSŠÿ QyÿiÆåÿtÕôÿZ½åÿF­ÜÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿH¯ÝÿG®ÝÿF­ÜÿL²ßÿ]ÀçÿoÑóÿwÛüÿkÆãÿQޤÿ7[jÿ0Scÿ>kÿG€œÿW“°ÿm¥Âÿu±Îÿq¶×ÿj·Üÿl¹ßÿj¹áÿl»áÿp»áÿb¶áÿ†Òñÿ¦åùÿ›à÷ÿ˜Þöÿ âøÿ§æúÿ“Þöÿ®æûÿ¨åúÿ¤åúÿ´ëýÿ·ìýÿ«éýÿ²êüÿ½íüÿ³êýÿ©éüÿ­ëýÿ°ëýÿ²ëýÿ«éýÿ©çûÿ°çûÿÂìüÿÅïýÿ¿íüÿ½ëûÿ·êûÿµéûÿ²éùÿ¬ã÷ÿ¥ßõÿ§àôÿ²áòÿ§ÞñÿÙïÿÊçÿ†Êåÿ~Ãáÿ†ÃâÿnµØÿc²Öÿm·Ùÿq¹ÜÿW£ÏÿY§Òÿ]©Òÿ\¦ÑÿU¤ÏÿZ§ÒÿMžÊÿ p²ÿj°ÿs²ÿ€¼ÿB—Éÿ2ŽÃÿp°ÿ4ÿ5“ÿ=˜ÿO¤ÿb®ÿ~Àÿ4“ÍÿZ«Ûÿc»æÿxÇëÿ‰Ñðÿ?ƒ¾ÿ9™ÿ_²ÿy¼ÿp¶ÿl±ÿr¬ÿc“ÿ\€ÿkÇæÿtÔôÿYºâÿC¨ÖÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿE©×ÿC¨×ÿJ­Úÿ[½äÿnÐòÿwÛüÿlÈæÿO§ÿ7[lÿ8VdÿIo‚ÿZˆÿc˜²ÿm§Âÿv±Ïÿw·Ùÿt¹Ýÿg¶ßÿ\³ÝÿP°Þÿ„Ïñÿ¢â÷ÿŸá÷ÿ˜ÞõÿŠÖòÿ¡äùÿ®çûÿ®èüÿŸâøÿ ãøÿ¦åûÿ®êýÿµëýÿµêüÿºéýÿ´ëýÿ©èüÿ¨éüÿ¥éüÿžæüÿ›æüÿ¦éüÿ°ëüÿ°éüÿµêýÿºëüÿ³éûÿ±éûÿ¨çùÿ«å÷ÿ«â÷ÿ«âõÿºèöÿ¶ãôÿ¤Ùðÿ‘Îêÿ’Íçÿ—Ìçÿ”Ìæÿ˜Íæÿ}½ÜÿZ­Óÿa¯Ôÿn¸ÚÿCšÊÿS£ÍÿP£ÎÿDšÉÿU¥ÐÿaªÔÿD˜Éÿk°ÿl°ÿq²ÿ/‰Áÿ6Åÿ}¸ÿ `¦ÿ4ÿ?™ÿJ ÿU¦ÿe°ÿ}¿ÿ"ŠÆÿ3˜ÏÿZ±ßÿnÂêÿoÈíÿ^±Þÿ"l²ÿg³ÿy½ÿmµÿ w¹ÿ&¶ÿ!k™ÿ%c…ÿkÈçÿtÕôÿW·ßÿ@¢ÐÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿD¥ÒÿB¤ÒÿA¢ÑÿG¨ÔÿY¹àÿmÎðÿwÜüÿnÊéÿU•¬ÿ=`oÿ=WeÿMpÿa‹Ÿÿqµÿ}­Çÿ¶Òÿc©ÒÿX«Ùÿ[µáÿ•Õñÿ—ÚóÿœàöÿŠÔñÿxËîÿ™Ýõÿ·ëýÿ°èüÿœà÷ÿ¦ãøÿ§åùÿ©çúÿ¯éýÿ®èüÿ±èüÿ®éüÿªéüÿ¢æüÿ™äúÿ™ãúÿ”âûÿ‘ßúÿáùÿ›äûÿ©çûÿ­éüÿ°éüÿ¦åúÿ¥ä÷ÿ¬ãöÿ©áöÿ¯ãöÿ°áôÿ£ÛñÿÕìÿŸÔêÿ§ÔéÿªÔêÿ¥Òèÿ£Ñçÿn¶ØÿS¨Ïÿe¯Òÿh²Õÿ>šËÿ>™ÊÿIžÌÿ>–ÈÿJŸÍÿ[§Ñÿ¹ÿa¬ÿo±ÿx·ÿ6ŽÃÿ/‰Àÿ~¹ÿY£ÿA˜ÿC›ÿN¥ÿX¨ÿb¯ÿu»ÿ!„Ãÿ ŒÈÿ/™ÑÿH¬Þÿa¾èÿmÆîÿ€Èêÿ,ˆÄÿh³ÿI›ÿ_«ÿ y²ÿ f–ÿb…ÿlÈèÿtÕôÿU´Üÿ>ËÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿA ÍÿB ÍÿB ÍÿAŸÌÿ?ËÿD¢ÏÿVµÜÿlÍïÿwÜüÿoÌëÿU˜¯ÿBdrÿEZgÿWt‚ÿhŽ ÿqµÿl¤Áÿa¦Ìÿa°×ÿ}Çæÿ„ËêÿÔïÿŽÕðÿŽÖñÿ«äøÿ±çúÿªäùÿ©âøÿ¡á÷ÿžã÷ÿ¨åúÿ¢ãùÿ¼ëûÿ§äùÿªæúÿ¦æùÿ—âøÿ“àøÿžãùÿŠÚ÷ÿvÑóÿ…Øöÿ›äúÿŸåúÿ¬çûÿ¹èûÿ©åùÿ¬äøÿ¨áöÿ¥ßôÿ°âôÿ¬ßòÿÖîÿ£Õëÿ¥Óêÿ¦Ôéÿ©ÕéÿªÔéÿÆäÿ[ªÒÿY¨ÌÿYªÏÿg®Ñÿ4˜Éÿ"Ãÿ7•Èÿ?—ÊÿS£ÐÿKžËÿq´ÿa­ÿr³ÿz¹ÿ(‰Àÿx¶ÿn°ÿHšÿ8–ÿDžÿO¥ÿ_­ÿf°ÿg´ÿ r¼ÿ ~ÂÿˆÇÿT«ÜÿyÂèÿc½èÿyÈîÿ†ÌíÿM›Íÿ.ƒÿ&€ÿJ“ÿQŠÿYÿkÉèÿtÕôÿS±Øÿ<˜Åÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ?›Èÿ>šÇÿ<˜ÅÿAÉÿT±ØÿkËíÿwÜýÿoÎíÿZœ³ÿEhuÿF\fÿSqÿY…œÿJЬÿ_¡Áÿ…¾Öÿ…ÅàÿƒÇæÿ“Òîÿ”Øòÿ©â÷ÿ¬âöÿ¤ßõÿ Üóÿ«áöÿ£Þöÿ™Ûôÿ‘Ùòÿ¥àöÿœÝõÿà÷ÿ™ßöÿ–Üõÿ†×ôÿŽÙôÿ}ÐñÿiÊðÿzÒóÿ†Øõÿ™Þ÷ÿ™ßöÿŸàöÿ›Þôÿ–ÜõÿŸÞõÿ¥ßóÿ©ÞôÿžÙñÿ¤Öîÿ¤Óëÿ¦Ôêÿ¦ÕéÿªÖèÿ¢Ñçÿ‚Áàÿh±Ôÿ^¨Ìÿ_©Ëÿ^©Ìÿ1“Çÿ-‘ÆÿFœËÿFšÊÿF›Ëÿ*ŠÀÿ_ªÿ\©ÿq²ÿ»ÿ€»ÿk°ÿGÿ9–ÿ?šÿH ÿS¦ÿa¯ÿh±ÿg³ÿi¶ÿr»ÿƒÄÿ+–Ïÿi·ãÿe¹äÿd¼æÿhÀéÿvÈìÿV¨ÕÿP™ÿ)|ÿ"kÿ–Éÿ.Æÿ s±ÿ^¨ÿY§ÿ`«ÿb­ÿ[©ÿZ¦ÿD˜ÿGÿM ÿV¦ÿ[¨ÿ`¬ÿh²ÿe°ÿh´ÿi¶ÿt¾ÿ‚ÅÿŒÊÿ ”Ïÿ=¤ØÿH«ÝÿL®àÿR³áÿ]µâÿV¬Õÿ2v ÿR{ÿlÉéÿtÖõÿN¦Íÿ5ˆ´ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8Œ·ÿ8‹¶ÿ5ˆ´ÿ9¸ÿL¤ËÿgÅçÿxÝýÿtÔôÿY£¾ÿ6fyÿ3Rbÿ@gxÿS…™ÿm²ÿ…³ÇÿŠºÓÿÆÞÿŸÓèÿ–Ñëÿ†Ëëÿ~ÈéÿÓîÿƒÌëÿ‰Ðíÿ†Ñîÿ“×ñÿ—Øñÿ•Õñÿ|ÊîÿxÉíÿqÇíÿgÃêÿrÇíÿtÈíÿyÈêÿvÇêÿ„Ðíÿ–Õîÿ’Ðíÿ„ËçÿƒÈæÿŠÈæÿ„ÅãÿÉåÿ™Ìåÿ•Êãÿ™Êâÿ¥Òåÿ™ÌãÿŠÁÝÿ>ºÿx­ÿ±ÿ@™Ìÿ:—Ìÿ9–Êÿ@—Íÿ%ˆÁÿh­ÿc¬ÿc«ÿ[©ÿd¯ÿB‡ÃÿW—Ëÿ0o±ÿ LŸÿF›ÿQ£ÿ[¨ÿ]­ÿi±ÿh²ÿk³ÿj¶ÿlºÿ~Ãÿ„Æÿ †Èÿ’Îÿ3¢Øÿ2£ÙÿFªÝÿAªÚÿ?žÊÿ0„ªÿ/w–ÿnËêÿtÕõÿL¢Éÿ3ƒ®ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡±ÿ6‡²ÿ6‡²ÿ5†±ÿ3ƒ®ÿ6‡²ÿIŸÆÿeÂäÿwÛüÿsÖõÿ^©Âÿ5g{ÿ)M^ÿIixÿbˆšÿs²ÿ®Äÿ…¹Òÿ‹ÂÝÿ„Âáÿ‚ÄãÿÍëÿ‡Íìÿ‚Ëëÿ…ÎíÿËêÿˆÏëÿ†Ïíÿ€Ëëÿ{ÊìÿiÃëÿuÆêÿ„Îíÿ‹Ðíÿ‚ËêÿyÇèÿzÄåÿtÂåÿ„ÉçÿÍçÿÈçÿÂáÿÆâÿ•ÈãÿœÍäÿ›ÍäÿšÊáÿ–Æàÿ‘Åàÿ—ÇÞÿ1ˆ·ÿj¤ÿp¦ÿ4“ÉÿB˜ËÿCšÍÿ9•Ìÿ oµÿ_­ÿc­ÿg¯ÿQ¢ÿj´ÿ™Ìëÿ—Æèÿƒ¾åÿ_¨Øÿ"l°ÿLŸÿY¨ÿ]¬ÿd°ÿk´ÿl´ÿm¶ÿnºÿt½ÿw¿ÿzÀÿ ‚Åÿ#’Îÿ3ŸÖÿ7¤ÙÿA¨Ùÿ2˜Êÿ*€ªÿ/u”ÿmËëÿtÕõÿJŸÅÿ1~¨ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ4‚¬ÿ3«ÿ0~¨ÿ3«ÿFšÁÿc¿âÿvÛûÿt×÷ÿ\©ÄÿAoÿD[eÿPkxÿY‚–ÿd”«ÿm¢¼ÿ‡·Ñÿ˜ÅßÿŠÅãÿzÃäÿ‡ÈéÿwÃæÿ{ÇéÿÑíÿŒÑíÿŠÏëÿwÇèÿzÇéÿƒÌìÿ’ÑíÿšÓîÿ—ÑîÿÍêÿ„Æåÿt¾âÿ|ÁâÿÁâÿt»ÞÿÅãÿ«Öêÿ™ÌãÿšÌäÿšÊâÿ¢Íâÿ™ÉàÿŽÃÞÿ‰¿Ùÿ@”¿ÿ|®ÿ ~°ÿ}»ßÿŸÑíÿŒÄåÿ1ŽÄÿa¬ÿT¥ÿU¦ÿP¢ÿ:—ÿX¦ÿj§Öÿ£Ðïÿ…Âçÿo·áÿg«Õÿ!k¯ÿT¥ÿY§ÿ[¬ÿ_®ÿb®ÿi´ÿl¸ÿmºÿq½ÿ‡Êÿ(’Îÿ2šÒÿD¥ÚÿR¬ÞÿS«ÛÿU£Îÿ5€©ÿ9v•ÿnÌëÿtÕõÿH›Áÿ/x£ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ1}§ÿ2}§ÿ1|¦ÿ.x£ÿ0{¦ÿC”»ÿa¼ÞÿvÚûÿuØùÿ_®ÈÿCrƒÿ:UbÿRhuÿe…—ÿo›°ÿ~®Æÿ‚¶Õÿ{½Ûÿ„ÆäÿÅåÿ€Èèÿ†ÍêÿŽÌêÿ‰Ëéÿ€ÇçÿŒÍêÿÌéÿŒËèÿ“Îéÿ‘ÏìÿŽÌêÿ‚Äåÿu¿àÿn»Þÿ}Ááÿ€ÂáÿžÏæÿžÐåÿ…Âßÿt¹×ÿ™ËáÿÅÝÿˆÅÜÿŽÄÜÿÀÛÿb§ÉÿY£ÅÿPžÅÿ¹äõÿ¯àôÿ¯ÝòÿŸÕïÿv¹áÿ[ŸÐÿj£Òÿ-m²ÿB—ÿ?„¿ÿx·àÿÃéÿŒÈêÿ…Ãèÿr¹ãÿg°ÛÿY§ÿtºÿ@Ëÿ9–Óÿ/“Ñÿ"Ðÿ0•ÐÿG£Øÿmµâÿc¶äÿy¿çÿc³áÿ`µãÿ_´áÿHªÚÿbªÒÿH‰­ÿB}šÿoÍìÿtÕõÿF—½ÿ,sÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/w¡ÿ,tÿ-v ÿ@¶ÿ_¸ÛÿuÙúÿuÚúÿa±ÌÿNxˆÿ=WdÿJiyÿaŠŸÿt¦Àÿu²ÐÿÁÞÿ‚Ããÿ‚Âäÿ‡Ææÿ„Åæÿ|Áãÿ|Âäÿ‹Èçÿ‹Ççÿ‰Æçÿ‘Ìéÿ„Èçÿ†Çæÿ‚Äãÿ}Áàÿw¾ßÿ|Ààÿ”ËäÿŸÎçÿÌãÿÆàÿÅßÿŠÂÞÿ}¼Ùÿq·ÕÿŠÀÝÿÀÝÿn¬Íÿh¨Èÿt®Íÿ¨Úðÿ¤Ûðÿ“Òïÿ˜Ôðÿ—Õòÿ‘Óðÿ™Ùóÿ…ÊéÿV¥ÔÿP Òÿa°Ûÿh´ßÿu½åÿ‚ÇëÿuÂëÿe¹çÿIŸØÿUªàÿsÁîÿf¹çÿC¨àÿC¥Ûÿw¿çÿ’Íîÿ™Ïðÿ†Æêÿn½çÿc´âÿ`¶ãÿa·ãÿr½åÿ}·ØÿuŸ¸ÿX‰ŸÿnÍìÿsÕõÿD’¸ÿ)n—ÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-sœÿ-r›ÿ*n˜ÿ+p™ÿ=‰°ÿ\µ×ÿtØùÿvÛûÿ`³ÏÿM€“ÿGk}ÿS‡ ÿl§Âÿp³Ôÿu¸Ûÿz½áÿ‡Ãæÿ¿àÿn·Ýÿq¹ÝÿÁáÿ„ÂâÿˆÅåÿƒÃãÿ{ÀàÿŠÆåÿ{¾ßÿ~¿àÿÁàÿÀßÿŠÄáÿŸÍåÿ¦ÑæÿžËâÿ§Íãÿ–ÇßÿÄÞÿ‘ÃÜÿ’ÃÜÿÀÛÿn¬Ìÿj¨Éÿ}³Ñÿ¦×ïÿ¡ÚòÿŒÒîÿ‘Öðÿ‘Òïÿ‡ÊìÿˆÌíÿ•ÔñÿÑðÿs¿æÿ\±àÿ]´ãÿ€Èîÿ…Æêÿr½èÿZ³çÿe¸êÿu¾ëÿ]°åÿU­áÿ<£ÛÿvÀéÿž×òÿÔòÿ—ÒñÿŒÌíÿtÂêÿtÃêÿŠÊîÿ€ÅéÿzÃçÿzºÙÿx¢¸ÿ` ÿnÍíÿsÕõÿBŽ´ÿ'i‘ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+l•ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ+n–ÿ*m–ÿ'j“ÿ(k“ÿ:ƒªÿZ±ÔÿsÕöÿsÖ÷ÿg»ØÿX‰ ÿgš´ÿhªÌÿo´Ùÿq¹Üÿe³Ùÿi´Ûÿp·Ûÿm´Úÿ}ºÛÿ}¼Üÿ†ÃâÿŽÃãÿÀàÿ‡ÄâÿÄãÿÅåÿ–Éæÿ†Àáÿ{»Ûÿ‘ÅâÿœËâÿŸËãÿ§Ñãÿ›ÉÞÿ‘ÁÜÿœÅÞÿšÆÜÿ{µÓÿd¤Èÿw®Íÿo¬Ìÿ­Üðÿ¬Ýòÿ˜Ùñÿ“Öñÿ—Ôðÿ“Ññÿ‰Îîÿ‡Îïÿ{ÈïÿŒÐðÿ}ÊîÿvÅíÿmÁíÿ~Äïÿv¿ëÿd¶çÿ`³çÿcµæÿ`³åÿE§àÿQ¯âÿƒÍîÿ•ÔðÿŠÎîÿŠÎîÿ‘Ñîÿ‰ÍíÿsÄéÿƒÊíÿŠËëÿƒÅéÿqµ×ÿfš³ÿX‹ŸÿoÎíÿsÕõÿ?аÿ$dŒÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(k“ÿ$r›ÿ)hÿ)fŽÿ)hÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ(i‘ÿ)i‘ÿ(h‘ÿ%dÿ$a‹ÿC´ÿrÔóÿrÖ÷ÿXަÿ^ލÿg§Çÿk²Öÿr´Øÿa®Óÿ}¹Úÿq´Øÿf¯Õÿe®ÔÿvµÙÿƒ¼ÝÿqµØÿlµÙÿ¾ßÿƒÀáÿ•Éçÿ‰Æäÿ€ÁáÿÅäÿ‹Ãàÿ”Çáÿ§Óçÿ¡ÏåÿÁÝÿ”ÄÞÿšÅÝÿ”ÂÛÿf¨Éÿf¤Ãÿn©ÈÿiªÇÿ²Ýòÿ®Þòÿœ×òÿ™Õòÿ’Óðÿ—Õðÿ‰ÎïÿpÄíÿkÂìÿhÂìÿhÂíÿa»éÿ[¶éÿn½ìÿgºèÿf¸êÿh¶çÿ[³åÿM«âÿD¨àÿuÅìÿ~Ëîÿ‡Îïÿ“ÔðÿÒïÿ…ÍíÿŠÎíÿƒÈëÿj¿èÿ‚Êíÿ—Ðíÿx¹Øÿb˜³ÿVŠ ÿoÎîÿsÕõÿ>†«ÿ"^‡ÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&c‹ÿ$j‘ÿ“¼ÿ‹´ÿ vžÿ%fÿ'aˆÿ'bŠÿ&d‹ÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ&dŒÿ#_†ÿ7}£ÿsÔôÿqÑñÿO…žÿR…¢ÿ`¢Äÿh­Óÿ^«Óÿ[©Ñÿe¬Òÿh­Ñÿ`©ÐÿfªÑÿx²×ÿ„¼Üÿm¯Ôÿb¬Ôÿ‚½ßÿ“ÇãÿÇäÿ€ÀßÿÇæÿ‘Éåÿ‘Èäÿ…¿ÞÿÇãÿ›Êáÿ›Èàÿ”ÂÜÿ”ÁÛÿˆ¹Öÿ\¢ÄÿU›¼ÿZŸÀÿb¥Ãÿ«Úðÿ±áôÿ­ÞõÿŸØôÿØôÿ–×òÿ…Îîÿ}ËîÿuÅìÿ\½éÿW¹çÿXµçÿL­ãÿU°æÿM®âÿM®ãÿY³æÿV²åÿS®äÿd½ëÿÍîÿ…Ñðÿ„ÑîÿÓðÿ’Óðÿ‰ÏïÿÉíÿ‡Êìÿ…Éìÿ}ÈíÿŠÊéÿw¸Öÿfš³ÿ\¡ÿnÎîÿtÕõÿ;¦ÿ Yÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$^…ÿ"eŒÿ’¼ÿ˜Âÿ—Áÿ¸ÿ{£ÿ"fŽÿ$[ƒÿ$\ƒÿ$^†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ$_†ÿ Zÿ9~£ÿsÕõÿpÑñÿOšÿQƒ ÿ\¡ÃÿM¡ÌÿS¤ÌÿZ¦ÏÿbªÏÿPŸÊÿLŸËÿS£Ìÿ`¦Íÿ{µ×ÿo°Ôÿf­Óÿo®Ñÿ~¹Úÿ‰Ãàÿ|¿Þÿ‡ÄâÿÈçÿÉæÿœÎæÿÄáÿÃàÿÃÞÿ}·×ÿu³Ñÿ]£ÇÿD“¼ÿH”»ÿf¤Åÿ\ž¿ÿ¦×ïÿ¨Üóÿœ×óÿÐðÿ“Óòÿ€Ëðÿ…ÎðÿƒÌîÿyÇíÿjÁìÿV¶èÿR²æÿQ­âÿM«àÿL¬ãÿC©Þÿ4¢Ûÿ6£Ýÿ]¹èÿ€ÏïÿŒÒïÿ‡Óïÿ‹×ðÿ’×ñÿšÖóÿœÕòÿ–ÓðÿŠÍíÿ{Åêÿ~Éìÿ~ÅæÿsµÕÿiš²ÿ`£ÿoÏîÿsÕõÿ9}¡ÿT{ÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Xÿ aˆÿ’¼ÿ–Àÿ&—Àÿ˜Áÿ˜Âÿ“½ÿªÿiÿ"Y€ÿ"V|ÿ"Yÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"ZÿT{ÿ9}¡ÿsÕõÿpÐðÿL€—ÿQƒ ÿJ—¿ÿN¢Ëÿf­ÑÿY¤ÌÿX¢ËÿMŸÉÿOŸËÿA˜ÅÿIœÆÿ_§ÍÿZ¤Ëÿ^¨ÍÿJ¼ÿh®Òÿu·Ùÿ„¾Þÿ‰ÃáÿƒÀàÿ…Ãáÿ—Éæÿ–ÇâÿuµØÿY¥Ïÿh«ÍÿE”Àÿ<Ž»ÿO™¿ÿI•¼ÿa¢ÁÿZ›¿ÿªÝôÿ Úôÿ™ØóÿzÆëÿpÅíÿyÉïÿƒÍñÿˆÏòÿ†ÍðÿvÅîÿa¹êÿb·èÿj¹èÿj¸çÿZ°ãÿH¨ßÿ=¤Ûÿ1ŸÚÿ\¾ëÿÓòÿŽÔñÿ‰Òðÿ†ÎîÿÑïÿ”Òñÿ“Òïÿ–Óñÿ”Ñïÿ‹ÎíÿËìÿwÄçÿ¼Ùÿo³ÿYŒ¢ÿpÏïÿsÕõÿ6xÿOuÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿSyÿ]„ÿ“¼ÿ–Àÿ9˜¿ÿC™¿ÿ4—¾ÿ!—¿ÿ˜Ãÿ—Áÿˆ±ÿn–ÿYÿ Qwÿ SyÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿU{ÿOuÿ8{ ÿtÖöÿoÎïÿH~–ÿS„¡ÿM–½ÿMžÉÿMŸÉÿB™ÅÿC–ÃÿGšÆÿLÆÿ@”Ãÿ8Áÿ6Áÿ1Ž¿ÿGšÆÿP¡ÍÿP¤Ïÿ_«ÒÿyµØÿ~¹Ûÿ»Üÿ}½Ýÿl´Øÿ_«ÒÿQ£ÍÿE™Èÿ[£ÌÿN’ÁÿI‘½ÿoªÌÿT™¾ÿ[¾ÿ\Ÿ¿ÿ¢ÙóÿœÙõÿ›ÙôÿwÄëÿ`»èÿkÁìÿoÁìÿf¼ëÿrÂîÿuÀëÿb·æÿe·çÿ_³äÿb²äÿg´äÿ^°âÿF¨Ýÿ<¨ßÿkÈïÿ™Øóÿ›Øõÿ˜ÙôÿˆÓïÿ„ÐîÿŽÓðÿ”Ôðÿ“ÔïÿˆÎíÿŠÏîÿ€ÊìÿyÅèÿs¸×ÿW’¯ÿM‡¡ÿpÐðÿsÕõÿ4s˜ÿJpÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿNtÿZ€ÿ“½ÿ–Àÿ6•¼ÿC–»ÿC–»ÿB–»ÿ8•¼ÿ'•½ÿ˜Áÿ™Ãÿޏÿvžÿ[‚ÿLrÿLrÿOuÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿPvÿIpÿ8zžÿt×öÿoÎíÿF{”ÿF~žÿG“ºÿA•Äÿ8Áÿ?”Áÿ7’¿ÿA•Âÿ:¿ÿ)ˆ½ÿ„¹ÿ!„¹ÿ„ºÿ,оÿC•ÃÿMžËÿ]©ÒÿV£ÎÿA•ÈÿR¢ÌÿKžÍÿ,ŽÅÿ8“Æÿ:“ÆÿFšÈÿ9¿ÿC¼ÿ\›Äÿ[›ÃÿW˜½ÿT—ºÿU˜ºÿ°ßôÿ¨Ýõÿ‘Õñÿ‘ÕñÿrÂëÿT±ãÿJ«âÿO¯ãÿ{Âìÿs¼èÿV°äÿP°åÿO®âÿR¬âÿN«áÿO­áÿ>¤ÝÿS¶åÿ|Ññÿ’×òÿšÚóÿ•×óÿ‡ÒðÿÐïÿˆÑïÿ”ÖðÿŠÐíÿ†Îíÿ|ÉíÿnÄìÿkÂèÿU¬ÔÿBЬÿG†ŸÿqÑðÿsÖõÿ2o“ÿEjÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿInÿV|ÿ“½ÿ–Àÿ6“¹ÿA“¸ÿ?“¸ÿ?“¸ÿ@“¸ÿB“¸ÿ<“¸ÿ-“»ÿ–¿ÿ™Äÿ”¿ÿ~§ÿ`‡ÿJpÿFkÿInÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿKpÿDiÿ8yœÿuØ÷ÿoÍìÿ?vÿ8y›ÿH“ºÿL›Æÿ;‘Âÿ7ŽÀÿ'‡ºÿ'…ºÿ‚¹ÿ ƒ¸ÿ#„ºÿŠÀÿQ©Ñÿa§ÏÿV¡ÊÿH™ÇÿL™Çÿ'…½ÿ#…½ÿ<”ÄÿG™ÈÿM›ÊÿPÉÿC–ÇÿM›ÇÿF•ÃÿS˜Ãÿ<‡¹ÿ7‡¶ÿV™ÀÿYš¾ÿUš½ÿ§ÝóÿœÙôÿ“Öòÿ™ÖôÿŒÎðÿi¹èÿiºèÿk»ëÿh¹èÿm½êÿ[µåÿ\¶æÿ[¶çÿR®ãÿFªàÿE¨ßÿF¨áÿC³äÿsÍðÿ“ÚóÿÚñÿ…Óñÿ‡Òñÿ„ÑñÿxÍïÿzÌðÿ}ÊíÿÊìÿÇêÿwÃëÿ`½æÿN«ÓÿAЬÿH‡¡ÿqÑñÿsÕõÿ/jÿ@eÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿChÿSxÿ”¾ÿ–Àÿ5¶ÿ@´ÿ>´ÿ>´ÿ>´ÿ=´ÿ>´ÿ@´ÿ>´ÿ2‘·ÿ!”¼ÿ™Ãÿ˜Ãÿ‡°ÿhÿLqÿAeÿCgÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿFkÿ?cÿ8x›ÿuØøÿnÌëÿ?tŽÿ8w™ÿ8еÿC•Ãÿ;Àÿ-‡ºÿ~´ÿ€·ÿ·ÿy±ÿ ˆ½ÿC¨Öÿn·Ùÿ|ºØÿ`¬Òÿ5‘Áÿ&·ÿ{´ÿ‚¸ÿ)‡¼ÿ5Áÿ<“Âÿ?”ÂÿQÇÿ_£Éÿe¦ËÿQ›Ãÿ(³ÿ2…³ÿB¸ÿE¹ÿIŽ·ÿªß÷ÿ¤Ýøÿ¤Þ÷ÿØöÿ•Òóÿ—Ñòÿ†Èîÿ{Áìÿs¾ëÿi¼éÿa¶èÿe¹èÿ\³æÿQ­ãÿN¬áÿN«áÿ@¨ÝÿG´æÿoËñÿ”Ûõÿ“Üõÿ‘ÛôÿÖñÿŽÓðÿ‡ÐîÿzËïÿbÃîÿ[¾êÿU»çÿV¹çÿR·âÿMªÒÿN¯ÿHˆ¢ÿqÑòÿsÕõÿ-e‰ÿ:_ÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿ>bÿPuÿ”¾ÿ–Àÿ4޳ÿ=Œ°ÿ<Œ°ÿ<Œ°ÿ<Œ°ÿ<Œ°ÿ<Œ°ÿ<Œ°ÿ<Œ°ÿ>Œ°ÿ>Œ°ÿ6²ÿ'‘¸ÿ—ÀÿšÅÿ¸ÿr™ÿOtÿ@dÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿAeÿ9^ÿ9xšÿuÙùÿnËëÿ-jˆÿ'n—ÿ9‡´ÿ)…»ÿ,†ºÿ ~µÿ}³ÿz°ÿs®ÿhªÿ"‰¼ÿuÂãÿl³Õÿo²Óÿx³ÔÿO™Ãÿu°ÿl«ÿ{³ÿ ¶ÿ&‚¹ÿ2‰½ÿ,ˆ½ÿJ˜ÄÿTÄÿH•¾ÿ1…²ÿ*€¯ÿ"z®ÿ-²ÿ0…²ÿ,€¯ÿ±á÷ÿ¬ßøÿ¤Ûøÿ¥ÞøÿØöÿ¡ØøÿÏóÿ}ÄîÿvÁìÿ^¸èÿf¸èÿiºèÿ[±ãÿX°äÿW°äÿK¬áÿ5¦ÝÿG³äÿrÑôÿ…Õóÿ‘Ûôÿ‰×óÿ‚Óñÿ|ÐñÿrÊïÿxÊïÿkÇïÿZÀëÿ[ÀëÿU½èÿM¶âÿH¨ÑÿAŽ­ÿC†¢ÿpÏñÿrÒôÿ+`ƒÿ6Zÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ9]ÿLrÿ“¿ÿ”Àÿ3‹°ÿ;‰¬ÿ:‰­ÿ:‰­ÿ:‰­ÿ:‰­ÿ:‰­ÿ:‰­ÿ:‰­ÿ:‰­ÿ;‰­ÿ<‰­ÿ=‰¬ÿ9Š®ÿ,´ÿ”¾ÿÉÿ¨ÿ9]ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ<`ÿ3Xÿ9wšÿvÚúÿmÊéÿ&f†ÿ(o–ÿ#{¬ÿ{±ÿy±ÿs®ÿu¯ÿ r¬ÿq¬ÿlªÿ4–Æÿ{Ææÿy¼Úÿe¬Îÿq®Ðÿd¥Êÿ/…¸ÿc¥ÿg§ÿu°ÿg¥ÿq­ÿ)µÿ7ˆ¹ÿ,²ÿ!x©ÿ)}­ÿ-€¯ÿ0„²ÿ-°ÿ#°ÿ&´ÿ­áøÿ­àøÿŸØöÿŸÚ÷ÿ„ÍòÿˆÌòÿ’Ñòÿf¹éÿo½ëÿ^´çÿf¸èÿfºçÿ]µæÿ`¶çÿS¯äÿ@ªßÿ<©Þÿ9¨ßÿoËñÿˆÖôÿ‚Öôÿ~ÔóÿtÑóÿsÏòÿhËðÿdÈðÿtËðÿhÇïÿdÄîÿjÇïÿRºåÿG¨ÒÿB‹­ÿA…£ÿoÌñÿoÎóÿ(Z~ÿ1Tÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ4VÿInÿ‘Áÿ’Àÿ3ˆ­ÿ;†¨ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ:†©ÿ>…¨ÿ%¶ÿ—Æÿy¤ÿ3Wÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ7[ÿ .Rÿ9v™ÿvÙúÿjÈçÿ.f„ÿ(k”ÿp¥ÿz±ÿo«ÿnªÿ o©ÿk©ÿi§ÿg§ÿ:—Åÿ~Æäÿz»Úÿd­ÐÿY¢ÇÿZ¢ÆÿG“¿ÿ*€´ÿb¢ÿN•ÿ9‚ÿLÿ h§ÿsªÿy«ÿ!|«ÿ/ƒ±ÿ%{®ÿ,²ÿz¯ÿ p¬ÿ*†ºÿ§Ý÷ÿ©àúÿ­âùÿ£ÜùÿÑóÿyÆïÿ~ÆîÿxÁìÿÆîÿk»êÿ^µéÿdºèÿjºèÿX²æÿE®ãÿ=ªàÿ=«ßÿ;©àÿnÌñÿÛôÿÚõÿ‚ÖôÿvÑòÿxÑñÿpÎòÿgÉðÿ^ÄíÿYÄíÿbÄîÿeÅïÿfÂëÿ]±ØÿH¯ÿEˆ¤ÿmÈñÿmÉñÿ%Uyÿ ,Nÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ.QÿEkÿÁÿÀÿ3…ªÿ9ƒ¥ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ8ƒ¦ÿ:‚¥ÿ#‹¶ÿ–Çÿržÿ.Pÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ2Uÿ )Lÿ8u˜ÿuÖúÿhÃæÿ[|ÿ]‹ÿi¢ÿp«ÿf¥ÿd¤ÿd¢ÿ\ ÿXžÿW›ÿJ¢ÌÿyÄäÿ¾Ýÿr³ÓÿR Æÿ]£ÆÿF”¾ÿ%€´ÿx¯ÿ[šÿM’ÿZ™ÿp¦ÿs©ÿy­ÿ#}®ÿ!}¯ÿ'°ÿ!{®ÿ'‚¶ÿH˜ÂÿZ¢Çÿ›Ûõÿ ÜùÿÝ÷ÿ”Òòÿ‰ÐòÿzÈðÿŠÌðÿËïÿƒÆïÿvÃîÿeºëÿe»ëÿk½ìÿY´èÿH®ãÿ?«áÿ:«àÿ8©ßÿjÈïÿ„Øõÿ×õÿ†ÚõÿzÓóÿlÏóÿpÏóÿuÏóÿ^Èðÿ^ÇðÿiÉòÿ[ÆðÿjÇíÿh¸ÙÿS–²ÿQ§ÿkÄðÿkÄðÿ"Nsÿ 'Iÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ )KÿBhÿÂÿÁÿ2‚§ÿ7€¡ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ6€¢ÿ8¡ÿ!‰¶ÿ”Èÿk˜ÿ (Jÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ -Oÿ$Fÿ8s—ÿsÓúÿe¾ãÿQxÿT†ÿ[˜ÿ\ÿS™ÿM”ÿN”ÿR—ÿL‘ÿ ^œÿn¼ßÿŽÌçÿw»Úÿl±ÒÿhªÌÿPÃÿ9Ž»ÿ ²ÿ|°ÿy¯ÿ k¨ÿe¡ÿp§ÿ p¨ÿq©ÿ {®ÿ'„´ÿ1‹ºÿ+‡·ÿH™Âÿr¯Ïÿn­ÏÿžÞøÿ¨àùÿ¦àøÿ¨Þùÿ›Ú÷ÿÒòÿ€ËñÿƒÌñÿƒÈïÿ}ÄðÿxÅïÿnÀîÿe¾ìÿ^·éÿO²äÿV´åÿ2¥Ýÿ& ÛÿsÊðÿŒÛöÿ„Ùöÿ†Ùõÿ†ÙöÿÖõÿqÓôÿmÐôÿ]ÉòÿaÌóÿuÑõÿqÒõÿuÏñÿt¿Ýÿ^µÿT¨ÿh¿ïÿiÀîÿInÿ"Cÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ $Eÿ?fÿÄÿ‹Áÿ1¤ÿ6}žÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ5}Ÿÿ6|žÿ ˆ·ÿ’Êÿd‘ÿ #Dÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ (Jÿ@ÿ8q—ÿrÏúÿc¹áÿ HsÿGÿ?ˆÿ(}ÿ"ƒÿ;ÿL“ÿAŠÿ8ÿP§Ëÿ‡Ïìÿ”Îçÿr¹Øÿd­ÎÿS Æÿ;’¾ÿ9Žºÿ,†µÿ(´ÿ|²ÿ s¬ÿc¥ÿg¥ÿn§ÿn¦ÿ ³ÿU£Èÿd¨ÉÿY¥ÈÿY¦Èÿe¬Ìÿf«Ìÿ¡ßøÿªâùÿœÚöÿÝ÷ÿ›Û÷ÿÓõÿ~ÉòÿÎôÿ‰Ïôÿ„ÌòÿÆðÿqÁîÿ[·êÿc»êÿH¯ãÿ3¦ßÿ3¦Þÿ ›Ùÿ^Áëÿ–áøÿÞùÿßúÿ…Üøÿ|×÷ÿÙøÿ‹ÝøÿuÓöÿeÐöÿ}Ùùÿ‰ÜûÿÖôÿvÄàÿk£·ÿV©ÿg»îÿg»íÿChÿ>ÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ #Eÿ?ÿ œËÿ‡Ôðÿ—Ôìÿ‚Çãÿj·Ùÿ]¬ÒÿP¥ÎÿN¢ÉÿC˜Äÿ<–Ãÿ,нÿ#ƒ·ÿ´ÿµÿ€µÿ}´ÿ~´ÿB›Æÿi´Öÿ…¿Üÿ‹ÀÛÿ‚»Øÿy¸Ôÿo³Òÿ]«Îÿ¤ØõÿŒÊîÿ’ÑóÿÕõÿœÜúÿ—Øøÿ†ÑóÿtÈñÿxÅïÿÏòÿŒÏòÿ{Åïÿg»ìÿS³çÿ7¨ßÿ%ŸÜÿ™Ùÿ4¬âÿ‚Ýùÿ—æûÿ™çüÿ¡êüÿ¦ìüÿ’çüÿ‹äüÿ†ãûÿåüÿ‘ãüÿŽäüÿ‘æüÿ“ã÷ÿƒÎâÿsª¸ÿRެÿa¯ìÿa­èÿ1Wÿ-ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ-ÿ 3\ÿˆÉÿ‚Àÿ.s–ÿ0qÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0q‘ÿ0qÿ/q’ÿºÿŒÌÿ Dqÿ ,ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ4ÿ )ÿ7j—ÿk¿øÿ[¦Øÿ)h‡ÿ1ƒ¨ÿ:—ÂÿF Îÿ7˜Éÿ&ÆÿB¤Óÿ…Ñîÿ˜Öïÿž×íÿÅâÿ`²Õÿh²ÖÿR¤ÎÿIŸÉÿD›ÄÿCšÅÿ4’Áÿƒ·ÿ|³ÿ }´ÿ‚µÿx°ÿ{²ÿE Éÿz¼ÚÿÁÜÿ•ÂÚÿŽÀØÿv´Óÿ{·Õÿa¬Îÿ˜ÒñÿÏñÿ¬Üøÿ“Óóÿ‡Îñÿ~Éïÿl¾ìÿ}ÈïÿzÅîÿÊñÿ™Ôõÿ|Èðÿ^ÆñÿcÈñÿT¼èÿ;­âÿ-¥Þÿw×øÿ éýÿ¢êüÿžëüÿ’èüÿ—êüÿçüÿ–éüÿ›éüÿ”çüÿ–çüÿ“æüÿˆåüÿ‰á÷ÿ‰Ðâÿq©·ÿW¬ÿ_«ëÿ^©æÿ-Rÿ )ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ )ÿ1[ÿ†ÊÿÀÿ,p‘ÿ.nŒÿ.nÿ.nÿ.nÿ.nÿ.nÿ.nÿ.nÿ.nÿ.nÿ.nÿ.nÿ.nŒÿ-oÿ€¼ÿ‰Ìÿ Èÿz½ÚÿˆÀÜÿ‹ÀÛÿ‰¿Úÿv¶Ôÿb¯Ðÿ^®Ïÿi±Ñÿ©Üõÿ«Ý÷ÿ“×ôÿ{ÌðÿrÄïÿtÇñÿ Üøÿ§Ü÷ÿœÓóÿ™ÓóÿžÙ÷ÿ›Õôÿ‹ÐóÿtÔùÿoÕúÿÜûÿ™æüÿ£ëüÿ ìüÿ¡îýÿšêüÿ–çüÿ‹äüÿ†åüÿˆçüÿ‘çüÿ–éüÿšêüÿŸëüÿ–éüÿàöÿËáÿ`¤µÿPŠ­ÿ[ŸêÿX›âÿ'Nÿ *ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ (ÿ 4aÿ€Ìÿz¾ÿ)f‡ÿ*e‚ÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*eƒÿ*dÿ(hŠÿ|ÁÿÊÿ-Wÿ )ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ 'ÿ9k ÿc®ôÿR•Ïÿ0lŠÿG±ÿY±×ÿ×ðÿ›Þóÿ ßóÿ¥Þñÿ Üñÿ¢Ûïÿ‰ËçÿnºÝÿl¸Úÿk´×ÿO¥Íÿ?™Æÿ5”Ãÿ*‰¾ÿ(‹¾ÿ,ŽÀÿ‡ºÿƒ¸ÿ…¸ÿ…ºÿS¨Íÿ‡ÂÝÿÂÛÿ†¿ÙÿnµÔÿj°Ñÿ]¬ÎÿS©Íÿ`®Ñÿ¯Þõÿ¬Üöÿ¤Ù÷ÿƒÏðÿT·êÿn¾îÿÇïÿ‘ÎðÿŸÖõÿ³ãúÿ¦àùÿ©ßùÿ™ÖõÿŒÔöÿzÖúÿ„Úûÿ›ãüÿ¦èüÿŸäüÿáûÿ‰àüÿŠßüÿ‚Þüÿ{Þûÿ‰ßüÿ‡áüÿ‰áûÿ”åüÿ˜æüÿŒãüÿŠÝöÿzÇáÿ]¡µÿO‡­ÿY›éÿV—àÿ&Lÿ *ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ (ÿ 5dÿÍÿw¾ÿ(cƒÿ)b~ÿ)bÿ)bÿ)bÿ)bÿ)bÿ)bÿ)bÿ)bÿ)bÿ)bÿ)bÿ)a}ÿ'e‰ÿzÃÿ}Èÿ(Rÿ *ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ 'ÿ9k£ÿa©óÿQ‘Íÿ6pŒÿRš»ÿxÁâÿ—Öðÿ‘×ðÿ©àòÿªÝðÿ˜ØîÿœØïÿ‹ËæÿpºÜÿf²Öÿf°ÖÿO£Îÿ?™Çÿ(Œ¾ÿ1Ž¿ÿ<–Äÿ5“Âÿ&‹¼ÿ#нÿ‡¼ÿ¶ÿN¦Îÿ¿ÜÿˆÁÝÿz¹Öÿl²Ôÿf®ÏÿV¦ËÿM¤ËÿS©ÏÿÖóÿ¢×óÿ¨Ûöÿž×ôÿÊîÿY·êÿp»êÿ’ÍïÿšÔóÿšÔôÿ™ØõÿžÝøÿžÚöÿŽÏðÿxÊòÿˆ×öÿ“Üøÿ˜ßùÿ’Üøÿ‹ÜúÿÚúÿ‰Ýûÿ‰ÝûÿzÛûÿƒÛûÿ†ÝûÿŠÞüÿ‡Ýûÿ‹Þûÿáüÿ‹Ûöÿ{Åàÿe ´ÿP„­ÿV—èÿT’ßÿ $Kÿ *ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ (ÿ 5fÿ~Îÿu½ÿ'`ÿ(_{ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(_|ÿ(^yÿ%cˆÿyÅÿyÆÿ$Nÿ *ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ (ÿ:k¥ÿ^¥òÿPŽÌÿCx‘ÿ[£Ãÿp¶Ýÿ±ãöÿœÜòÿ±ãôÿ´ßòÿ•Óìÿ–Òîÿ…Ççÿq»Ýÿe±×ÿ]¬Ôÿ]¬ÓÿHŸËÿ+¿ÿ(ŒÀÿ,ŽÂÿ(Œ¾ÿˆ»ÿ!нÿ†ºÿ €¶ÿK£Îÿ¾ÜÿˆÀÜÿw·Öÿs´Óÿ~·Õÿl®ÏÿK Èÿ4”Äÿ˜Óóÿ§Úõÿ˜ÕñÿŸ×ôÿ”Ññÿ„Íïÿe¿ëÿvÇðÿ‰ÌñÿÍïÿ‹Ìîÿ™×ôÿ“Õôÿ‘Ðòÿg¾êÿ\¾íÿ…ÔöÿŒ×öÿ™Üøÿ”Ûøÿ~ÔõÿŠÚøÿ‰Úùÿ„×ùÿ€×ùÿƒÙúÿ†Øúÿ‰ÚúÿÛúÿŠÙùÿ{ÑóÿuÂÞÿ`›±ÿF«ÿU“èÿQÝÿ "Iÿ +ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ (ÿ 6hÿ|Ïÿs¼ÿ&\{ÿ&[wÿ&[xÿ&[xÿ&[xÿ&[xÿ&[xÿ&[xÿ&[xÿ&[xÿ&[xÿ&[xÿ&[xÿ&Zuÿ$aˆÿxÇÿuÃÿ!Hÿ +ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ (ÿ:j¨ÿ]¡ñÿOŠÊÿ;w’ÿ[¦Äÿ™Ôèÿ£âôÿ¤áóÿ¾ê÷ÿµãóÿ—Ôíÿ‹Îëÿ†Èçÿl·Ûÿ\¯ÖÿW¬ÔÿN¥ÏÿIŸËÿ<–Åÿ,ŽÁÿ"Š¿ÿ0’Âÿ)¿ÿ‚¹ÿ|·ÿ €¸ÿO¨Ðÿ¿Üÿƒ¾Ûÿw¸ÖÿuµÔÿx´Ôÿc«ÎÿV§ËÿR£Ìÿ›ÔóÿŸÕôÿš×ôÿ’Óñÿ‘Ïðÿœ×óÿƒÎðÿS¹éÿ\¿ìÿ‹Ïñÿ‡ËðÿªÛ÷ÿÑñÿtÂìÿa¹çÿZ´åÿh¿ìÿ‹Óóÿ’×ôÿƒÐòÿ|Ïòÿ’Øöÿ“ÙöÿˆÑòÿyÌñÿ{ÏôÿÓöÿÓõÿ}ÐöÿqÊôÿsÈîÿm¸ÙÿM‘¬ÿD{ªÿTçÿP‰Üÿ !Hÿ +ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ 'ÿ 7kÿ yÐÿ p»ÿ%Zxÿ%Ytÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Yuÿ%Xqÿ$_ˆÿwÊÿqÀÿDÿ ,ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ )ÿ;j«ÿ[œðÿI…Çÿ>|–ÿy´Êÿ–Ôçÿ×ïÿ£áôÿºéøÿ§ÝòÿŠÏìÿ€ÈèÿÄåÿu»Ýÿc±×ÿb²Øÿ^¬Ôÿ>˜Èÿ2‘Ãÿ3‘Âÿ'ŒÀÿ5’Âÿ.Àÿ…ºÿ|µÿ‡»ÿa°Ôÿ†ÀÞÿ‡¾Ýÿz¸×ÿk²Ôÿ]¬Ðÿe±Ôÿ‚¾Ûÿ¼ÙÿÏõÿ¥Ûøÿ–ÖóÿŽÏñÿ¬Ý÷ÿ®Þ÷ÿ×ôÿ˜ÖóÿkÄîÿ‚ÍñÿŠÏñÿ”Õôÿ”Òñÿ|ÇîÿU°âÿQ­àÿV¯âÿa·èÿzÉðÿ{Êñÿ…ÎñÿÍñÿÒòÿ›ÔôÿŒÌðÿ}ÇîÿkÁíÿa½ìÿc¼ìÿ=ªâÿ0¤Úÿ*“Åÿ*yžÿ3o£ÿTŒçÿM…Úÿ Gÿ +ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ 'ÿ 8lÿ!xÑÿ nºÿ#Wtÿ#Vpÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Vqÿ#Umÿ"^ˆÿuÌÿn¾ÿ?ÿ-ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ )ÿ;i­ÿY—ïÿFÅÿMƒ™ÿƒ¼Îÿ˜Ôèÿ¡ßôÿ±åõÿ¬ßñÿš×íÿËêÿ‡ËéÿŽÊèÿ¿ßÿb±×ÿWªÒÿJ¢Ìÿ9˜ÇÿL¡ËÿIœÉÿ9”Åÿ0‘Äÿ2’Ãÿ‰½ÿƒ¸ÿ(Àÿh¶Ùÿ„ÁÝÿ|ºÙÿm´Õÿ[­Òÿc²Òÿ~½ÚÿŠÁÜÿ†½ÙÿZ¹ëÿ“Óöÿ”ÓóÿŽÑóÿœÚöÿ‘Òóÿ‚Êïÿ™ÔòÿŽÒñÿÎðÿƒËðÿuÇîÿƒËðÿi»èÿA¦ÜÿA¥ÛÿK¨Þÿ[¯ßÿR®àÿi»éÿn¾ëÿxÅïÿŠËïÿ‹ÊðÿŒÌðÿ~Çîÿf¼éÿP²åÿ6¦Ýÿ$œØÿÏÿ}ºÿf“ÿ'f ÿS‡çÿKØÿDÿ*ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ&ÿ 5nÿ#vÑÿ l¹ÿ"Toÿ"Slÿ"Smÿ"Smÿ"Smÿ"Smÿ"Smÿ"Smÿ"Smÿ"Smÿ"Smÿ"Smÿ"Smÿ"Qiÿ!\‰ÿ!tÍÿi»ÿ:ÿ,ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ /ÿ'ÿ8f®ÿV‘îÿI€Åÿ[Œÿ†½Îÿ›×êÿ°åöÿ²åöÿ³ãòÿ¢Øíÿj½ãÿo½áÿpºÞÿk´Úÿf²ÙÿW©ÒÿI ÌÿBŸÌÿM¢ÍÿHœÊÿ6–Æÿ7•Åÿ/‘Äÿ(Áÿ(¿ÿ Ž¿ÿk·ØÿŠÄàÿq¸×ÿ\®Óÿb²Õÿx»ØÿŒÃÜÿ’ÂÙÿŠ¿ÙÿÅÿX·êÿ™Óõÿ–Öôÿ‚ËðÿvÄíÿmÃîÿuÅíÿ”Öóÿ—Øôÿ„ÎðÿY»éÿNµèÿd·éÿm»éÿ[³äÿN«Ýÿ]«ÝÿYªÜÿM§Ûÿ=£ÜÿB¦ßÿn»çÿn¼èÿ\³ãÿM¬àÿD¥Ûÿ+›Ôÿ(›Õÿ*œ×ÿ-—Òÿ~¸ÿ\Žÿ#f¡ÿWèÿS‰Õÿ7?Nÿ400ÿ435ÿ435ÿ535ÿ535ÿ535ÿ645ÿ745ÿ745ÿ745ÿ745ÿ966ÿ:66ÿ966ÿ966ÿ<3-ÿ/Nuÿ rÒÿ!i¸ÿ!Plÿ!Oiÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Ojÿ!Meÿ![‹ÿ rÏÿ$i¸ÿ;;=ÿ<74ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ<86ÿ=96ÿ>96ÿ>96ÿ>95ÿ=97ÿN}¼ÿV–êÿO…Åÿ_Žÿ·Ëÿ†Èáÿ“Òëÿ¬ßòÿ´âôÿ¤ØïÿrÁäÿu¾áÿk¶Ûÿw»ÞÿnµÛÿR¦ÒÿV¨ÕÿK¡Îÿ]ªÓÿQ£ÎÿN¡ÌÿEÉÿ3•ÄÿCžÊÿ5˜Æÿˆ½ÿ_¯ÕÿÁßÿm·Ùÿ\³×ÿw½ÛÿŠÄÞÿ‡ÀÛÿŒÁÚÿvµÓÿÄÿ*˜Òÿl¿ìÿvÈòÿ‚ÉóÿŽÎòÿuÃìÿÇíÿ„ÊïÿšÕòÿËïÿkÁëÿPµæÿD®ãÿm¿íÿxÀëÿf¸åÿ]±áÿVªÜÿEŸÒÿBÒÿJ Öÿ,“Ïÿ"Ìÿ2˜Ñÿ,˜ÓÿE¤×ÿf¶âÿa´äÿP®ßÿX®ÛÿOœÆÿgÿ&g¡ÿ] ìÿZœâÿˆy\ÿ”n7ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ˜q5ÿ`o{ÿmÒÿ!f¶ÿLgÿLfÿLgÿLgÿLgÿLgÿLgÿLgÿLgÿLgÿLgÿLgÿLgÿJaÿ YÿoÐÿ0n´ÿŽpAÿ“p;ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’p=ÿ’q=ÿ’q=ÿ“o:ÿtHÿ_™ÖÿXžìÿTŠÄÿaŒœÿ„´Êÿ£Îãÿ¨Øíÿ«Ûïÿ°ÜïÿÐéÿd¾åÿ„Ææÿn¶Üÿ^­×ÿO¥ÐÿN¦ÐÿK¡ÏÿGŸËÿV¨Óÿ^¬Óÿ]¨ÑÿEŸËÿJ¤ÍÿG£Îÿ;Ëÿ%‘ÂÿN§Îÿ{Àßÿi¹ÛÿgºÚÿp¼Ùÿw¼Ùÿ|½×ÿƒ¾Øÿo³Òÿ)–Ðÿ9›ÒÿI£ØÿI­âÿZ¸ìÿzÇñÿ‰ÌïÿŒÎïÿ–Ðòÿ‘Ìðÿ‰ËïÿŠÎîÿ†Èíÿ\³ãÿN±äÿzÅìÿ^·åÿ8¦Ûÿ-Õÿ;žÓÿ:Òÿ>šÏÿ€¿ÿÁÿEÒÿd®Þÿ‘Çëÿ—Ìíÿ–Îîÿ{Àèÿk¶áÿp®ÏÿW‡¡ÿ?uªÿX›ëÿX—àÿ€pVÿ‰g5ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿh1ÿYi|ÿkÔÿ!d´ÿJcÿJbÿJcÿJcÿJcÿJcÿJcÿJcÿJcÿJcÿJcÿJcÿJcÿH]ÿ XÿmÒÿ3j¯ÿ†h<ÿˆh9ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿ‡h:ÿˆg7ÿ„kEÿ[•ÖÿV›ìÿS†ÁÿaŒ›ÿ¿Ñÿºßíÿ¶áôÿ«Üðÿ±ÞïÿÊèÿ@©Úÿlºàÿy¿áÿmµÛÿQ¨ÒÿJ¤ÐÿGŸÍÿT§ÑÿQ§ÒÿQ¦ÑÿZ©ÓÿO¤ÏÿCŸËÿBžÊÿ5™Èÿ3—ÅÿH¢ÎÿzÁàÿmºÛÿg¹Ûÿl¸Ùÿk¹×ÿÅÜÿŠÂÙÿ`¯Ïÿ;œÑÿ8™ÐÿG¢Ôÿ?žÑÿ8œÒÿI¯äÿËòÿŒÎñÿ‰Îñÿ‰Ìñÿ‚ÈíÿuÄíÿ~Çîÿl¼éÿg·åÿd·äÿe·äÿZ°ßÿ8ŸÓÿ3™Îÿ7œÏÿŒÅÿ~¼ÿP Óÿm¯ÚÿrµÞÿ–Êìÿ˜Éìÿ–Êíÿ›Îîÿ”Çèÿ}±Ïÿh‘¤ÿS€±ÿU—êÿW”Þÿ~lPÿ…d3ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿŠe/ÿVg|ÿjÕÿ"a³ÿG_ÿG^ÿG_ÿG_ÿG_ÿG_ÿG_ÿG_ÿG_ÿG_ÿG_ÿG_ÿG_ÿEYÿW’ÿ kÔÿ6h«ÿ„e7ÿ…e7ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e8ÿ„e7ÿ„e7ÿ„d7ÿ„d7ÿ„d7ÿƒd7ÿ„c4ÿ€hDÿY“ØÿU˜ëÿS„Àÿi ÿ’¾Îÿ¨×èÿ¯ßñÿªÛïÿ§ÜñÿmÁæÿV©×ÿ]¯Øÿ_´Üÿc³ÜÿL¥ÑÿC ÎÿK¤Ïÿ`­×ÿXªÔÿM¥ÏÿM¥ÐÿP¨ÑÿS¦Ñÿ[«ÔÿD¡Íÿ5•Åÿ)¾ÿc¶ÛÿtÀáÿq¿Þÿm¹Ùÿp¸ØÿÀÛÿˆÁÝÿq¶Ôÿf¯Üÿa¬Ùÿ_¬ØÿG¡Ñÿ)‘Çÿ(–ÏÿJ°æÿxÅðÿxÄðÿqÄðÿc¼íÿ\·éÿ^·èÿ\¶çÿt½éÿ‚Âèÿj¹äÿX¯ÝÿH¤Öÿh¯ÛÿU¢Ñÿ.Æÿ'ŠÄÿu²Ýÿ‹¾ãÿ‡¾ãÿŠÁåÿ“ÇéÿÍëÿÍëÿ“Äæÿ‚°Ïÿ]Š ÿQ~°ÿT•êÿW‘Ûÿ|hKÿ‚a2ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿb6ÿ‡b-ÿSd~ÿhÖÿ"_±ÿC[ÿC[ÿC\ÿC\ÿC\ÿC\ÿC\ÿC\ÿC\ÿC\ÿC\ÿC\ÿC\ÿAUÿV”ÿiÕÿ9f¦ÿƒb2ÿb5ÿb6ÿb5ÿb5ÿb5ÿb5ÿb5ÿ€b5ÿ€b5ÿ€b5ÿ€b5ÿ€b5ÿ€b5ÿ€b5ÿb5ÿb5ÿb6ÿ‚a1ÿ|fDÿXØÿT•ëÿTƒ¿ÿs– ÿ™¾Íÿ¤Óçÿ¬Þðÿ©ÙïÿtÅèÿd»åÿˆÆåÿz¾áÿg´Üÿ\­ØÿW¬ØÿO¤ÑÿS§ÓÿN¥ÑÿZ¬Õÿa¯ÕÿlµÚÿT©ÒÿP¤ÌÿY¨ÑÿFžÊÿ>œÈÿ)Áÿ9ŸÌÿf¼âÿpÀàÿwÁÞÿ‚ÁÝÿuºÙÿyºØÿw¸ÖÿsµÝÿh°Ûÿa¬ØÿDžÐÿ@›Ðÿ*“Éÿ5šÐÿH«âÿ\·ëÿj½íÿX·êÿfºëÿhºèÿR¯ãÿR«àÿe¸äÿ^³àÿRªÙÿD ÓÿHŸÒÿLÏÿ)‡Ãÿ8‘Éÿx³Üÿ—Ãäÿ…ºàÿv¸ÞÿŠÁåÿ˜ÈêÿšÉêÿ”Ãäÿz«ËÿN€šÿK{®ÿT“ëÿVŽØÿzdFÿ^0ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ~_4ÿ„_*ÿQbÿf×ÿ"\°ÿ?Wÿ@Xÿ@Yÿ@Yÿ@Yÿ@Yÿ@Yÿ@Yÿ@Yÿ@Yÿ@Yÿ@Yÿ@Yÿ>Sÿ U˜ÿ h×ÿÿ.Cÿ.Cÿ.Cÿ.Cÿ.Cÿ.Cÿ.Cÿ.Cÿ.Cÿ.Cÿ.Cÿ.Bÿ.Cÿ#Q¯ÿ$\ÛÿKRvÿoJÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿkK'ÿlI!ÿfTFÿM€ÜÿL‚æÿKq¯ÿS€•ÿg¡¿ÿŒÃáÿšÐìÿ‘ÉèÿŒÆæÿ¿áÿrºÞÿu¼ÝÿpºßÿR©×ÿR¨Õÿ^¯ÖÿfµÛÿeµÚÿ€Ââÿz¾ßÿ_¬ÕÿP¦Ðÿo±×ÿq±ÕÿC¡Îÿ(”Çÿ6˜Èÿ!Àÿ†ºÿ5“Âÿ[³Ùÿm½ßÿzÄàÿŠËãÿ„Æáÿ]­ÚÿT©×ÿ`±Ûÿp·ÞÿV¨Óÿ9›Íÿ\©Õÿg­Õÿ_­×ÿW£ÏÿR¢ÎÿP¢ÏÿA—Èÿ9’Åÿ*ŠÂÿ6ŽÃÿ:Äÿ0‹Ãÿ!½ÿG˜ÈÿV¨Òÿ[¨Óÿ^©Óÿa¬Öÿk°Øÿl±Ùÿp²Úÿh°Øÿ[¬ÕÿN¥Ðÿ:•Ãÿ7†°ÿ5m‰ÿ?o³ÿMƒêÿQu¼ÿhI'ÿhH$ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿhI&ÿkHÿBQ‰ÿ&ZÜÿ!I ÿ):ÿ+@ÿ+@ÿ+@ÿ+@ÿ+@ÿ+@ÿ+@ÿ+@ÿ+@ÿ+@ÿ+@ÿ+>ÿ,Bÿ$P´ÿ%ZÙÿLOnÿlGÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿhH%ÿiFÿcRGÿL~ÜÿK€åÿHn¬ÿV‚•ÿh¦Åÿ~¼Ûÿ…Àâÿz¼ßÿx»ÛÿsºÝÿsºßÿp¸ßÿp¹àÿZ¯ÙÿQ¨Ôÿr´ÚÿpµÙÿX¬Öÿ[°×ÿ^°ÖÿS¨ÓÿC¡ÌÿqµÙÿ…¼ßÿN¤ÐÿŽÃÿ'“Äÿ.‘Ãÿ-Áÿ'‹ÀÿR¨Ñÿ„ÊæÿŠÈãÿÌäÿ”ËåÿO§×ÿX«Ùÿh³Üÿy»àÿU¨ÔÿAžÎÿT©Óÿ[ªÖÿK¤ÑÿK ÎÿW¦ÑÿX¥ÑÿG™Èÿ1Ãÿ*ŒÂÿ9’Æÿ8Åÿ'…Àÿ/ŠÂÿX¤ÐÿR¥Òÿ^©Ôÿ`¬Öÿ_©Óÿ_ªÓÿi¯×ÿe­Õÿb«ÖÿV§ÔÿE¡Îÿ0Àÿ+¬ÿ[ÿ7h³ÿMëÿPq¸ÿeE#ÿeF"ÿeF$ÿeF$ÿeF$ÿeF$ÿeF$ÿeF$ÿeF$ÿeF$ÿeF$ÿeF$ÿeF#ÿeF#ÿeF#ÿeF#ÿhEÿ@NŒÿ'XÝÿ!Fÿ&6ÿ(<ÿ(<ÿ(<ÿ(<ÿ(<ÿ(<ÿ(<ÿ(<ÿ(<ÿ(<ÿ(<ÿ':ÿ*Aÿ%O¸ÿ'WÙÿMLfÿhDÿeE#ÿeE#ÿeE#ÿeE#ÿeE#ÿeE#ÿeE#ÿeE#ÿeE#ÿeE#ÿdE#ÿdE#ÿdE#ÿdE#ÿdE#ÿdE#ÿdE#ÿeCÿ_OGÿK|ÝÿK}äÿAj©ÿ5tÿU›¾ÿt´Ôÿj±Öÿc®Õÿj³×ÿd°Õÿ\¬ÕÿT«Ùÿk·ßÿc²ÜÿlµÛÿ{¶Ùÿs·Ùÿ[¬ÕÿP§ÑÿR¨ÑÿQ¨ÒÿD¡ÌÿC¤ÏÿM¦Ðÿ?œÊÿ> ÍÿC£ÎÿC¡Ëÿ=™Æÿ ˆ»ÿ'ÀÿyÄâÿÍåÿŒÍãÿ–ÏæÿO©Øÿ\®Ùÿd±Üÿe±ÙÿI¢ÑÿCžÏÿN¤ÔÿW¨ÕÿM£Ñÿ=›Ëÿ:–ÉÿGÍÿC˜Êÿ<”Éÿ2Çÿ0Âÿ&…¾ÿ|¹ÿF™Ìÿe¬ÖÿX¨ÓÿYªÓÿb«Öÿ]©ÓÿS¦Ñÿ\©ÓÿS¥ÏÿP£Ðÿ:˜Êÿ,’Çÿ‚ºÿ hÿBoÿ2`±ÿM€ëÿOn³ÿbBÿbB!ÿbB"ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿbB!ÿeAÿ?Mÿ(UÝÿ Cšÿ #2ÿ%9ÿ%9ÿ%9ÿ%9ÿ%9ÿ%9ÿ%9ÿ%9ÿ%9ÿ%9ÿ%9ÿ $6ÿ'@ÿ'O½ÿ'V×ÿNH^ÿdAÿbB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿaB!ÿb@ÿ\MGÿJzÞÿK{äÿ2a¤ÿ;vÿaž½ÿn¯Òÿi­ÓÿX¥Îÿ_«Óÿi°×ÿJ£Ñÿ4¡Õÿb´Þÿr¸àÿo¹áÿv»àÿm¶Ýÿ_¯ÖÿS¨Òÿa®Ôÿz¸Øÿk³Öÿ;Éÿ0˜ÇÿL¥ÐÿL¤ÍÿBÉÿN£ÍÿAÊÿ,Âÿ„ºÿU¬ÓÿÎæÿ‰ÊâÿŽÍåÿH¥Öÿu¸ßÿn´Ýÿa°ÙÿE¡ÑÿM¦ÓÿW©×ÿQ§ÓÿH¡ÐÿP£Ñÿ9˜Éÿ6–ÈÿG›Ìÿ:”Éÿ3Åÿ2ŒÃÿ ƒ½ÿ!„¾ÿW¤Ñÿ^©ÔÿX©ÓÿW¨ÒÿP¤ÑÿM¤ÒÿK£ÐÿL¤ÒÿE Ïÿ8—ÊÿˆÃÿ ‰Äÿw³ÿn¢ÿ"Z~ÿÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿ_?ÿa>ÿ=Kÿ*TÞÿ @—ÿ .ÿ "5ÿ "5ÿ "5ÿ "5ÿ "5ÿ "5ÿ "5ÿ "5ÿ "5ÿ "5ÿ "5ÿ !2ÿ%@ÿ(NÂÿ(T×ÿNEUÿa>ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ^?ÿ_=ÿYKHÿIxÞÿHwâÿ ÒÿB Òÿ?ŸÑÿ-“Éÿ ~¿ÿ!‰Åÿ;’Ãÿ9„¯ÿ2eƒÿ>i¼ÿHyêÿLf©ÿ]:ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ\<ÿ];ÿ ÎÿB¢Òÿ9œÎÿ>ŸÍÿS©ÔÿS©ÕÿV¯ÙÿV­Øÿ[®×ÿb³Ùÿa¯Öÿ_®Õÿm³Öÿz¹Úÿt·ÚÿS§ÐÿDœÊÿ*޾ÿ(ŽÁÿ$Âÿ2‘Ãÿ#ŠÀÿ{ÀÞÿœÒçÿŠÈãÿ4—ÎÿC¡ÓÿG¡Óÿ@ÐÿO¦ØÿJ¤ÔÿCžÐÿ;šÌÿ@›ÍÿR ÏÿMžÏÿL¢ÏÿEœÏÿ5“Éÿ2Æÿ(‰Ãÿ+ŠÂÿDœÌÿE Òÿ7›Ðÿ?ŸÐÿD¡Ôÿ@¡Óÿ> Óÿ< Òÿ6ÑÿL¢Ôÿ!ŒÅÿ6—ÎÿG ÓÿLœÉÿ?„¯ÿ*aÿ9f¼ÿGwêÿKb£ÿZ7ÿY9ÿY9ÿY9ÿY9ÿY9ÿY9ÿY9ÿY9ÿY9ÿY9ÿY9ÿX9ÿX9ÿX9ÿX9ÿZ8ÿ:F“ÿ,Qßÿ:ÿ&ÿ .ÿ .ÿ .ÿ .ÿ .ÿ .ÿ .ÿ .ÿ .ÿ .ÿ .ÿ )ÿ#Cÿ*LÈÿ+OÔÿN>EÿZ8ÿX9ÿX9ÿX9ÿX9ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿX8ÿY5ÿTFIÿFtàÿGràÿ4^ÿ3s‘ÿ@ŒµÿH›Æÿ;—Èÿ]¨Ïÿ`ªÑÿKŸÍÿG¢ÏÿO¥Ðÿ7•Æÿ8šÊÿ6˜Éÿ8™Éÿ\¯ÖÿwºÝÿ}»Ýÿ‚½ÝÿŽÂàÿ{¹Úÿ†Áßÿ‚¾Þÿs¶Ùÿc¬ÓÿFžÉÿ-‘Ãÿ1“Åÿ-Åÿ!‹Ãÿ^¤ÿB†¹ÿ ÙíÿŒÊåÿBœÒÿO¥ÖÿI¡Òÿ@ÑÿO¦ÖÿF¢Óÿ2–Ëÿ2•Íÿ>˜Ëÿ?šÏÿQ¥ÕÿS§Óÿ7–Ëÿ.Æÿ"‡Âÿ#†Àÿ'ŠÄÿF ÒÿA ÒÿB¡ÒÿA¡ÓÿC¤ÖÿQ©ÙÿO¨ÙÿJ¦ÕÿK¦Öÿ+•Íÿ<™ÎÿZªØÿ]­Ùÿ[¤Ïÿ>„®ÿ%bƒÿ?iÁÿEtéÿJ^ÿW4ÿV6ÿV6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿU6ÿW5ÿ9D–ÿ,Oàÿ7ÿ"ÿ+ÿ+ÿ+ÿ+ÿ+ÿ+ÿ+ÿ+ÿ+ÿ+ÿ+ÿ%ÿ!Fÿ+KÌÿ,MÐÿL:>ÿW4ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿU5ÿV2ÿQDIÿEráÿHpàÿ R•ÿb‡ÿ.¯ÿ0¾ÿC›ÉÿY¥ÎÿJÊÿP¤Îÿ[©ÑÿS¥ÍÿF›ÇÿIžËÿEœÈÿH Êÿg°Öÿj°×ÿj³Øÿw¹Úÿ†Áàÿ„½Ýÿ{»Üÿ‰Åäÿ€½Ýÿtµ×ÿ|¹ÙÿM¡Íÿ*‘Äÿt±ÿ`¢ÿQ™ÿW¢ÿZªÔÿ„Ëåÿ.“Ëÿ<ÓÿL ÓÿLžÓÿS¥ÕÿR§×ÿZ«ÚÿDžÒÿBœÐÿ<›Ïÿ4˜ÌÿS ÐÿJ›Íÿ/Åÿ ‚¾ÿ „Àÿ(’ÉÿO¦ÖÿG¤ÓÿJ¥ÕÿH¤ÖÿVªÙÿ^°Üÿ\®ÚÿH¤ÖÿXªÙÿC ÔÿU§Øÿd°Üÿ[ªØÿe§ÐÿN޲ÿ(e…ÿ@hÂÿDqèÿIY—ÿS0ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿR3ÿT2ÿ8B˜ÿ-Máÿ4‰ÿÿ'ÿ'ÿ'ÿ'ÿ'ÿ'ÿ'ÿ'ÿ'ÿ'ÿ'ÿ!ÿ Hÿ-JÐÿ.JÍÿL66ÿS1ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿR2ÿS/ÿNAJÿDoáÿFmßÿ"S•ÿd‰ÿ x©ÿ„¸ÿBšÈÿP ÌÿP¡Ëÿc­Ôÿc¬ÓÿW¥ÎÿE›ÆÿN ËÿMŸÉÿP¢ÊÿQ£Íÿ»ÜÿtµØÿqµ×ÿ½ÞÿžÌæÿÄãÿf±×ÿmµØÿf¯ÓÿZ¨ÒÿAžËÿ†¼ÿ^¡ÿXœÿXšÿ[¥ÿ!½ÿxÆãÿ/”Ìÿ9˜Ðÿ:–Îÿ:–ÏÿLŸÕÿN¦×ÿQ¦×ÿBŸÒÿI¡ÔÿQ¥Õÿ;™Ïÿ9–Íÿ6“Ëÿ.Çÿ~¼ÿ&ŽÆÿ:œÐÿR¦Öÿ5™ÑÿF¢Óÿb­Úÿ^¬ÙÿM§ØÿS«Úÿh³Ýÿf²Ýÿ_¯Úÿd®Úÿ^­ÚÿW«×ÿP Ëÿ7…­ÿ.hˆÿ>fÃÿCnèÿHT‘ÿP- ÿO0ÿO0ÿO0ÿO0ÿO0ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿP.ÿ7@šÿ/Kâÿ1†ÿÿ#ÿ#ÿ#ÿ#ÿ#ÿ#ÿ#ÿ#ÿ#ÿ#ÿ#ÿÿKÿ.JÔÿ.HÉÿJ3.ÿP.ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿO/ÿN/ÿN/ÿN/ÿN/ÿN/ÿN/ÿO, ÿK?KÿCmâÿDjÝÿ$T‘ÿdˆÿw©ÿ+‡»ÿ=–ÆÿE›Èÿ\§ÑÿV¤ÎÿY§ÏÿZ¦ÍÿFšÈÿ9”Ãÿ`©Ïÿg­ÒÿM¢Ëÿp´Øÿm²Õÿ`­Óÿ€¿ßÿŠÄâÿz¼Ýÿj²×ÿs¶ÙÿT©ÐÿL§Ñÿ0•Çÿb¤ÿ[žÿWšÿS–ÿh­ÿ }¾ÿe»ßÿ,Ëÿ ˆÆÿ†Çÿ7–Ðÿ6˜ÑÿV§Ùÿ@Òÿ=žÒÿE¡Ôÿ?›Ñÿ=–Íÿ6•Íÿ.‘Èÿ„¿ÿ…Áÿ/•Ëÿ.—ÎÿL¦ÖÿZªØÿ6›ÐÿP¦ÕÿJ¤Ôÿ>ŸÓÿNªÙÿj¶ßÿc±Üÿ_°Üÿ[®Ûÿ[®ÜÿY­Ùÿ^§ÐÿFŒ²ÿ8l‹ÿ=dÅÿBlèÿFP‹ÿM) ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿM+ ÿ6?ÿ/Iâÿ.‚ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿ Gÿ/HÙÿ0EÆÿH.'ÿM+ÿK,ÿK,ÿK,ÿK,ÿK,ÿK,ÿK,ÿK,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿL,ÿK,ÿK,ÿL)ÿH=MÿBkâÿBgÙÿ$V†ÿcŒÿ%z¬ÿD•Âÿ7“Åÿ>—ÇÿGËÿQ¢ÌÿHžÈÿJžÊÿF›Çÿ9”ÂÿS£ÌÿZ¥ÎÿJ ËÿV¨Ïÿ\©Ñÿc­Òÿa­Ôÿk²Öÿk´ØÿmµÙÿo¶ÙÿG£Ñÿ.’Çÿz¶ÿ\Ÿÿ^¡ÿUšÿ`Ÿÿ ‚ÀÿŠÅÿaºÞÿÅÿ‡Éÿ2“Ïÿ8—Ñÿ2–ÏÿH Óÿ/–Ïÿ3šÑÿN¥×ÿ<›Ïÿ9–Íÿ3”Ìÿ+‘Èÿ„Àÿ0’ËÿP©ØÿO©ØÿV«ÙÿY­Úÿ4™ÎÿDŸÐÿ=œÑÿR¨×ÿI¥×ÿP¨ØÿM¦Øÿ[®ÚÿV«Ùÿ[­ÚÿS©×ÿJžÌÿJ޳ÿ4kŠÿ=bÆÿAjçÿDL„ÿI&ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿJ( ÿ5<Ÿÿ1Hãÿ*~ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿ Eÿ 2•ÿ.Gàÿ0DÌÿF+ÿI( ÿI)ÿI)ÿI)ÿI)ÿI)ÿI)ÿH)ÿH)ÿH)ÿH)ÿH)ÿH)ÿH)ÿH)ÿH)ÿH)ÿH)ÿH%ÿF;NÿAiâÿAe×ÿQƒÿ \ˆÿ2‚°ÿG•Ãÿ/ŒÀÿ,ŽÃÿCšÉÿPžÊÿ4ŽÀÿ2¾ÿ6‘Áÿ3Àÿ:•ÄÿAšÇÿ@šÈÿP¤Ëÿ\«Ðÿn²Õÿ]ªÑÿq·ÙÿpµÙÿb¯Õÿ[®Öÿ(ŒÂÿe¨ÿb¦ÿ\ ÿ\ ÿQ˜ÿ {µÿ•Ïÿ&›Ïÿeºßÿ"‹Ëÿ‰Êÿ6–ÒÿAšÒÿ/”Ïÿ7˜Óÿ9™Ñÿ5—ÐÿE ÕÿHŸÒÿ4•Íÿ,‘Éÿ'Æÿ)‹Åÿ>Ðÿ^°Üÿ^¯ÛÿY­ÙÿJ¦Öÿ;ŸÑÿ5–Îÿ4˜ÏÿJ¤ÕÿD£ÕÿM©ÙÿC¦×ÿS­ÚÿZ®Üÿ^®ÛÿY©ØÿX¤Îÿ?‰¯ÿ(eˆÿ<`Èÿ@gçÿCH~ÿF"ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF&ÿF& ÿG% ÿ5: ÿ2Fäÿ'zÿÿ ÿ ÿ ÿ ÿ ÿÿ ÿ ÿ Cÿ)€ÿ(;½ÿ/Eáÿ2DÖÿ4<¥ÿ<2`ÿE&ÿE&ÿE&ÿE&ÿE&ÿE&ÿE&ÿE&ÿE&ÿE&ÿE&ÿE%ÿE%ÿE%ÿE%ÿE%ÿE%ÿE%ÿE%ÿE"ÿC:Oÿ@fâÿ@cÖÿ,^ÿ_‹ÿsªÿ‚»ÿƒ»ÿ…»ÿ:’ÅÿN›Éÿ7ŽÁÿ4Àÿ:•ÃÿKŸÊÿT¤ÌÿT¥Îÿ=–ÇÿP¢Êÿh°Óÿl±Õÿqµ×ÿ]¬Õÿ[®ÖÿI£Ðÿ*ŒÂÿc¨ÿ\£ÿ_¥ÿ_£ÿXžÿ_¢ÿ—Îÿ!Óÿ9¤Òÿ+Ðÿ„Éÿ‹Ìÿ*’Îÿ0”Íÿ*’Ïÿ+’Ïÿ=™Òÿ.•Ïÿ4—ÎÿFÒÿ9™Ïÿ3•Ìÿ.’Êÿ2–ÌÿW°Üÿcµàÿj¶ßÿc³ÝÿL©Öÿ8Òÿ>ŸÔÿQ©Øÿ>¡ÔÿD¥ÖÿLªÙÿU¬ÚÿD§ØÿO«ÛÿB¢Õÿ7›Ðÿ:˜Æÿ4„«ÿ%b‡ÿ:^Êÿ?eæÿACwÿCÿC# ÿC# ÿC# ÿC# ÿC# ÿC# ÿC# ÿC# ÿC# ÿC# ÿB# ÿB# ÿB# ÿB# ÿB# ÿC" ÿ59¢ÿ2Eäÿ$wÿ ÿÿÿÿ ÿÿ @ÿ&}ÿ(8»ÿ0Càÿ3DÚÿ5;«ÿ:0hÿ?',ÿC" ÿD!ÿB" ÿB# ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿBÿA7Qÿ>dáÿ>_Ôÿ[{œÿn±ÿ?Џÿx³ÿ¹ÿ%†¾ÿ,Âÿ3ŽÁÿ)ŒÀÿ8•ÄÿCœÉÿ`«ÒÿR£ÍÿM¡Ëÿe«Ñÿƒ»Ûÿƒ»Üÿ~¼Üÿ_­Öÿ7˜Êÿ…Àÿ r³ÿ_¨ÿ[¤ÿXŸÿWÿR™ÿWšÿ"‘Åÿ,¥Úÿ6¥Öÿ/ŸÑÿ–Ìÿ‰Ìÿ*•Ñÿ>žÕÿ8™ÒÿFžÕÿ8™Ðÿ<›ÓÿDÔÿJ¢ÓÿBžÒÿB›Ïÿ<—ÍÿF™Ìÿ9—Íÿ\³àÿjºãÿg¹âÿb¶ÞÿY±ÜÿI¨×ÿQ¬Ûÿc´Þÿ= ÕÿK¨ÙÿH§Øÿ<£ÓÿD¦×ÿ=¢Öÿ: Óÿ< Óÿ4–Æÿ-©ÿ+d‰ÿ;\Ëÿ>bæÿ??qÿ@ÿ@ ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ@ ÿ@ ÿ@ ÿ@ ÿ@ ÿ@ÿ39¤ÿ2Eäÿ$sÿÿ ÿÿ >ÿ%{ÿ(8¹ÿ0Càÿ3DÛÿ4;­ÿ8/iÿ<%,ÿ?ÿ@ÿ@ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ? ÿ?ÿ?5Rÿ=aáÿ<\Óÿp‡¡ÿ¤¾Âÿ¬Ðáÿj¯×ÿ¼ÿºÿ!ƒ½ÿ…»ÿ%ŠÀÿ0’Ãÿ=—ÈÿHŸÊÿH ËÿIŸËÿ]ªÓÿz¸Úÿk°ÖÿGŸÏÿ ŠÃÿo±ÿb«ÿe¬ÿc«ÿ`¨ÿ]§ÿV ÿYžÿ0‘ÃÿH´àÿF°Þÿ>«Øÿ'žÏÿ2žÏÿ6˜Òÿ6šÔÿS¤ØÿFŸÕÿP¥ÙÿO¢Öÿ<šÒÿEŸÔÿBÒÿ@Ïÿ9—Îÿ0“Êÿ8—Íÿ2Çÿ8šÒÿh¾æÿtÁçÿq¾äÿ`·ßÿT²ÝÿV°ÞÿA¤ÕÿA¤ÕÿH¦ÙÿD¥×ÿ3ŸÓÿM¨Ùÿ9¤Öÿ7¢Õÿ:ŸÒÿ4”Åÿ.€§ÿ]…ÿ8YÌÿ=`åÿ=;jÿ<ÿ< ÿ= ÿ= ÿ= ÿ= ÿ= ÿ= ÿ= ÿ< ÿ< ÿ< ÿ< ÿ<ÿ<ÿ<ÿ<ÿ38¦ÿ3Eãÿ$tÿ 0ÿ%yÿ'8¸ÿ0Càÿ3DÜÿ4:®ÿ6.jÿ:#+ÿ<ÿ=ÿ=ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<ÿ<3Tÿ<_áÿ;ZÒÿh„žÿ¼ÃÿºÜèÿ·àòÿ‰Èçÿ@–Èÿs³ÿw¶ÿ u¶ÿ u´ÿv¶ÿw¶ÿs²ÿx¶ÿl°ÿv¶ÿo°ÿ\¤ÿ[¤ÿk¯ÿk°ÿg¯ÿd­ÿ`ªÿ]¦ÿ[¢ÿ<žÌÿ]ÄëÿX»åÿJ³Þÿ;©Øÿ, Òÿ%šÐÿ@ŸÖÿ9šÒÿEŸÕÿf¯Ýÿ`¬ÙÿF ÓÿL¢ÕÿK¢ÓÿIŸÒÿS¡Ñÿ^©ÖÿZ§Ôÿ9˜Ëÿ.Èÿ4ŽÈÿB¢Öÿi¾èÿzÃèÿn¿åÿi¼äÿ`¸ãÿO°ÜÿL®ÛÿE©Øÿ@¥×ÿLªÚÿW²ÞÿM°ÝÿL°ÝÿP¬ÙÿA›Èÿ3„©ÿ&b‰ÿ9WÎÿ<]äÿ:6cÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ28¨ÿ0BÚÿ->Ìÿ/BÜÿ3DÝÿ3:°ÿ5,lÿ7 +ÿ9ÿ:ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ:1Vÿ;\âÿ;XÑÿgƒžÿŒ³¾ÿ²Øæÿ¿æôÿ´áóÿœÏëÿJ™Ëÿ6Âÿ$…¼ÿ~¸ÿ i­ÿUžÿ]¤ÿX¥ÿT¢ÿW¤ÿW¡ÿZ£ÿZ£ÿc¬ÿi®ÿf®ÿe«ÿ[¤ÿ o¯ÿ7¡ÑÿdÄìÿjÄêÿe¿åÿTµÞÿH¯Ûÿ3¤Õÿ™Ðÿ=¢ØÿR¨ÛÿT©Ùÿ_®Üÿ_ª×ÿ[©×ÿ^©ÙÿV¨ÕÿCšÏÿ]¥ÓÿU§ÔÿW§ÖÿO Ñÿ5‘Èÿ8Æÿ€½ÿ8œÓÿuÁèÿ†ËëÿÈéÿÇéÿsÄèÿnÀæÿ[ºãÿeºäÿe¼åÿ[¹äÿf¼åÿf½æÿk¼äÿS©ÐÿD±ÿ:m’ÿ9UÎÿ:Zâÿ81\ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ1:´ÿ0BÚÿ29­ÿ3,nÿ5,ÿ6ÿ7ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ6ÿ80Wÿ:Záÿ:VÐÿn‰¡ÿ¡¿Æÿ¶Úèÿºãóÿ¶àñÿ±ÝñÿpµÞÿ>—ÊÿCœÊÿ=˜Ëÿ6•Ëÿ"„½ÿdªÿ\¨ÿ^§ÿc«ÿ[¥ÿ^¨ÿaªÿV¦ÿR£ÿW¦ÿj¬ÿ(‘ÅÿN¹äÿ~ÑñÿrÈëÿuÇéÿwÈêÿY¸áÿF­Ûÿ5¦ÕÿšÐÿF©ÝÿK¨ÜÿW«ÛÿXªØÿe¯Ûÿf¯ÛÿV¨ÖÿQ£ÕÿT¤Ôÿ[§ÕÿI Ðÿ4–ÌÿF›Íÿ?•Êÿ&‹Ãÿ~¹ÿ‚¼ÿ_­ÚÿyÅêÿ”ÖòÿÚóÿŽÕïÿŠÒñÿšÚõÿžØóÿ„ÎîÿeÂéÿlÂêÿoÄëÿg¿èÿe³×ÿ\›´ÿCq–ÿ8SÏÿ9Wáÿ5-Uÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ2%Uÿ20ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ2ÿ2ÿ2ÿ2ÿ2ÿ2ÿ2ÿ2ÿ2ÿ2ÿ2 ÿ5,Uÿ8Wáÿ:TÐÿ…š®ÿ¹ÍÑÿµÚçÿÄèõÿÆêöÿ·ßñÿÐëÿX§Ôÿ6•Êÿ?—ÉÿHÍÿDŸÏÿ l±ÿV¤ÿY§ÿ\ªÿZ©ÿZ©ÿT¦ÿ[¦ÿ%}ºÿW¦ÒÿyÅçÿ{Ñòÿ|Ðòÿ{ÍíÿvÈêÿxÉëÿuÈêÿY¸áÿL®Üÿ<©×ÿ–Îÿd³áÿ[°ÝÿK¨Ûÿ_¯Üÿf°Üÿg®ÛÿZ©×ÿFŸÑÿN£ÔÿHžÒÿGžÐÿ3“Êÿ4•Éÿ?—Êÿ1Æÿ0ŽÃÿ.ŠÀÿ*ˆÀÿ+ŽÄÿM¨×ÿ`®Úÿm¿éÿi¿èÿzÃåÿ™Úôÿ—Ûõÿ”ØôÿŠÒðÿ‡Îîÿ{Çêÿc¶Úÿc ¶ÿHr˜ÿ6QÐÿ8Ußÿ3(Nÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0 ÿ41kÿ6Sßÿ9RÑÿ“ª¾ÿ»ÓÚÿÏæðÿÈéöÿÁäöÿÁâòÿ»âñÿ’ÆçÿIŸÏÿDœÍÿX¤ÐÿQ¢Ðÿ7–Éÿv¶ÿe«ÿ_«ÿ]©ÿaªÿ!~¼ÿc¸Ýÿ‹Õïÿ¢àôÿ àöÿ‘ØóÿÎîÿ|Ëëÿ{ÊìÿuÆêÿnÄéÿg¿æÿF¬Üÿ3¥Öÿ#œÑÿcµàÿ\±ßÿc°Ýÿa¯ÝÿZ­Ûÿ[«ÙÿU¥Õÿ[©ÖÿN£ÒÿDžÏÿ8—Ìÿ;•Éÿ;—ËÿB™Êÿ0Äÿ-ŒÂÿ9ŽÃÿ/‰¿ÿ»ÿ yµÿk­ÿzºÿs³ÿe§ÿ€»ÿV®ÛÿˆÐïÿ˜Úôÿ¢ß÷ÿ•Ôïÿ}¿Ûÿj£·ÿMt™ÿ5MÐÿ7RÞÿ2%Hÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ1ÿ3+^ÿ4@§ÿ1IÒÿ0HÔÿF_Ðÿ§ÁÒÿÈÞçÿÆãñÿ´Üïÿ°ÚïÿÉèõÿÇèôÿ ÑëÿN¢ÑÿAšÍÿT¤ÓÿV¥ÓÿG ÑÿH£Ðÿ/Åÿj°ÿ3ÆÿX±ÚÿsÅèÿ•Ôðÿ Úñÿ™Ûóÿ‹ÔðÿÌîÿ‚ÍíÿƒÐîÿyÇêÿnÁèÿ[¸äÿV·ãÿD­Üÿ9¦Øÿ0¡Öÿn¹áÿ_´ßÿ`±ÝÿP¨ÙÿU«ÚÿZ«Úÿ\ªÙÿ[¨ÖÿDžÎÿ:—ÎÿC›Íÿ:˜Êÿ6–Êÿ<–Éÿ:•Èÿ;“Æÿ9’Äÿ:‘Äÿ*‰Áÿ„½ÿs³ÿh®ÿa§ÿf¨ÿn¯ÿ‡¿ÿEžÏÿS©Õÿd¸âÿzÈìÿ‚Äßÿp§ºÿQwÿ5JÑÿ6OÝÿ2#Cÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0 ÿ1ÿ2$Jÿ39”ÿ2FÍÿ0Jàÿ8QØÿUlÊÿ~˜ÉÿŸÂÒÿ°Õâÿ½ÞìÿÍçóÿÇäòÿ¹Ýðÿ¿âòÿ³áóÿ¦ØíÿT£Ðÿ8“ÊÿW£Óÿh«×ÿS§Ôÿ;›Îÿ/•Êÿ„½ÿQ®Úÿm¼ãÿ‚Æçÿ¡×ïÿ¦ÚñÿÌìÿyÉëÿ‡Ðîÿ}ÊëÿqÃéÿk¿çÿuÃéÿZ·åÿM³àÿD¯Üÿ?ªÛÿ* Öÿg¸ãÿbµáÿ]±ÞÿZ­Üÿg°Üÿ[©ØÿU«ØÿW©×ÿO¤ÔÿL¢ÓÿH¡ÐÿO£ÐÿLŸÎÿJ ÏÿIžÍÿBšÊÿB›Éÿ@˜Éÿ1Äÿ$‹Àÿwµÿk°ÿb¨ÿc©ÿ€»ÿ4”ÇÿI¡ÏÿN¦ÐÿK¢ÐÿK¦Ôÿg°ÕÿEŠ©ÿ2`“ÿ5HÒÿ5LÛÿ2!=ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ17ÿ22ÿ2AÀÿ/FÝÿ4LÛÿG`Íÿh…Åÿ“®Æÿ²ÎÒÿ´Ùßÿ¼àéÿ¾âïÿÇçôÿÓëöÿÖì÷ÿÊåóÿ¾áðÿ¶Ýòÿ¬Ûïÿ‹ÂäÿDšÎÿK ÐÿM¡Ðÿd¯ÙÿJ£ÓÿM¨ÔÿM©ÕÿD£ÔÿW®Ùÿj¸àÿu¾ãÿqÁåÿvÇéÿÒíÿ…ÌìÿsÂèÿp¿æÿn¾æÿ€Èëÿl¿èÿb»æÿU³áÿ2¤Öÿ'žÕÿS±ßÿ[´àÿfµàÿq·àÿc±Ýÿ[­Ûÿf²Üÿd±Ûÿf±ÚÿQ¨ÕÿG£ÒÿF¡ÏÿN¤ÐÿN¤ÑÿBŸÎÿ:–Èÿ4•Èÿ8–Éÿ/‘Æÿ)ŽÂÿ€ºÿ u³ÿY£ÿe«ÿˆ¼ÿ9˜ÉÿP¦Ñÿ[¬Öÿ]¬×ÿA›Éÿ.…·ÿ,wšÿ%Wÿ3FÓÿ3JÚÿ17ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ1%ÿ1,lÿ2<±ÿ/CÙÿ1FÞÿ=VÑÿXxÆÿ£Æÿ¢ÄÌÿ¥Ï×ÿ¶Ýåÿ®Ýíÿ¯âòÿÃëøÿÕïúÿÐìøÿÃåõÿÄäôÿÌèôÿÇãòÿ¿áñÿ½áñÿ¶Üïÿl±ÙÿN¢Òÿh²Üÿ^®ÙÿO©Öÿ`±Úÿt»ßÿc³Ýÿa³ßÿm¹àÿq¾áÿvÄçÿ•Ôðÿ”Óðÿs¿æÿn½æÿxÃèÿ‚ËëÿŠÏíÿzÇëÿjÀèÿY³àÿ@¨Ùÿ0¡ÖÿS®ÝÿS¯Þÿn¹âÿ}¼áÿj¶àÿj³Þÿk°Ûÿt´Üÿj°ÚÿZ«ØÿZ­Ùÿb®ØÿZªÕÿV¨ÔÿQ¥ÒÿV¨ÑÿG Îÿ*’Æÿ+‘Åÿˆ¿ÿ }¹ÿg¬ÿY¤ÿ q³ÿ)ÄÿFžÌÿ\ªÕÿW«Õÿ\«ÖÿQ Ìÿ:Џÿ-w™ÿ(VŽÿ2CÓÿ2GØÿ13ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0ÿ1&Vÿ17 ÿ/@Ñÿ/CÞÿ6NÔÿPnÉÿi•Åÿ}¶Êÿ—ÊÕÿ­Ùãÿ¯Ýìÿ­ßñÿ³ãöÿµçùÿºêúÿ¯åøÿËïüÿÔðüÿÈèõÿÅæõÿÍèõÿÓê÷ÿ×íøÿÍêöÿ¿ãóÿ’ÅäÿKÐÿl²Üÿe¯Ùÿh´Üÿn¶Ýÿ]±ÛÿL¬Ùÿ[²Üÿ„Éêÿ‹ÌìÿÎíÿÍìÿ|Ãçÿt¾äÿÄèÿtÂèÿ€ÊëÿˆÎíÿ†Íîÿc»åÿ]·ãÿS´àÿ2ŸÖÿ€Âåÿh·áÿX¯ßÿr¹âÿc³Þÿc³ÞÿqµÞÿ†¾áÿ…¼âÿ]¯Ûÿ]¯Ùÿf±Ûÿ`­×ÿ_¬Õÿ\§Óÿ`­ÕÿK£Ðÿ:˜Éÿ*’Åÿ‡¿ÿj­ÿ[¥ÿh­ÿ{¸ÿ5–ÉÿK£ÑÿS¨ÓÿQ§ÒÿW¨ÓÿMÊÿ;Œ¹ÿ$u˜ÿPŽÿ2AÔÿ1CÔÿ0/ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0 ÿ0Cÿ01ÿ/<Çÿ-?Þÿ2F×ÿC_ËÿZ„Äÿl¥Åÿw½ÐÿŒÐßÿÖéÿžÝñÿ±å÷ÿ°æùÿºêúÿ¹êüÿµéúÿ°äøÿ•Öòÿ°áöÿºç÷ÿÍîùÿÂè÷ÿËêøÿÊçöÿÕìùÿÊèõÿ²Ýðÿ{¸ÞÿIœÏÿr²Ûÿ†¼àÿ‚½àÿ{¼ÞÿV­ÙÿX°Üÿ`´Ýÿp¼âÿ„ÄéÿÁçÿ~Âåÿiºâÿb¸áÿi»âÿÇèÿ{Æèÿ‚ÉêÿŠÎëÿ{ÇçÿnÀçÿZ³ßÿF§×ÿ|Àæÿ]³áÿW±Þÿj¸âÿi·àÿY±ÝÿN­Ûÿ¾ãÿ‚½àÿc³Üÿd²Ûÿl´Üÿp´Úÿh°Øÿj°Öÿ_¬ÔÿR©Óÿ4—Êÿ&ÅÿŠÀÿb©ÿ^§ÿo±ÿƒ½ÿ>›ÌÿD ÎÿY¨ÔÿT¥ÒÿN¢Ïÿ@˜Çÿ1¼ÿ u˜ÿP‘ÿ1?Ôÿ0@Ñÿ0*ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ01ÿ0+yÿ/8ºÿ-=Ûÿ/AÚÿ:SËÿQwÃÿ`™Âÿu´Ìÿ€ÄØÿ~Äàÿ‚ÌëÿØóÿ™à÷ÿ¡äùÿªèûÿÆñüÿÀðýÿ¿îýÿÈñüÿ´åøÿŒÎíÿ‘Óîÿ¦Üóÿ²âõÿÂèöÿÃç÷ÿÉèöÿÈçôÿ½âòÿµßîÿžÌçÿd«×ÿb©×ÿj±Ýÿd³Ýÿj¸Þÿw»ßÿ‡Ããÿx¾áÿ^²Þÿg¶ãÿvÀæÿyÁåÿrºàÿk¸Ýÿe¸ßÿp¿äÿq¾åÿ}Âåÿq¾âÿwÂæÿ„Çèÿq¼áÿc¶Þÿo½åÿj¸ãÿj¹ãÿh¸âÿ`µáÿf¶àÿf·àÿg¶àÿyºßÿs¸Ýÿw¼Ýÿq·Ûÿe³Úÿ^®Øÿj²Úÿl²ÚÿV§Óÿ8˜Ëÿ ŽÄÿ€ºÿ]¦ÿaªÿt´ÿ#ŒÂÿ?žÎÿX©Óÿg­Öÿc«ÕÿV¤Ðÿ?šÈÿ0޼ÿs—ÿN“ÿ1?Õÿ0?Ïÿ0&ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0 ÿ0'dÿ06«ÿ0=Öÿ/?Ýÿ4KÏÿFhÁÿ\Ž¿ÿp®Æÿ~ÁÓÿzÃÝÿÉçÿ~Ëíÿ…Ðñÿ‚ÐôÿyÐöÿ’Ýùÿ¡åüÿ©éûÿÈóýÿ¿ïýÿºëüÿ°äúÿ§ßõÿ®àôÿ Ûñÿ•Òïÿ›ØñÿÂéøÿ¾æõÿ¼ãóÿÄãòÿ»ßïÿ¯Úïÿ¶ÛîÿˆÂãÿM¢ÕÿG¡ÔÿJ¦×ÿj¶àÿ“Éçÿ~Âäÿd·àÿh¹àÿgºåÿb·ãÿ}Áçÿ‹ÆçÿŒÉçÿ€Âäÿr¼ßÿu¾àÿ|Ãäÿ‡Èéÿ‚Ççÿ‚Ææÿn¹Ýÿu½âÿp½æÿe¹âÿ]µáÿi¹âÿeµáÿa³ßÿl¸áÿR¬ÜÿJ©Ùÿj¶ßÿ|¾ßÿr·Ýÿc±Úÿ]¯×ÿpµÛÿ`®×ÿAÍÿ;šÍÿ"‘Æÿ n±ÿa©ÿl°ÿ z¸ÿ1—ÇÿB¡ÏÿW¨ÓÿX¤Ñÿ`©ÔÿL¡ÏÿAžÍÿ)Œ»ÿt–ÿ"P–ÿ1?Õÿ0?Ìÿ0"ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0"Oÿ02šÿ1<Îÿ0?Þÿ/EÓÿ1YÂÿ>yºÿSš¾ÿc®Çÿ{¾Öÿ‚ÅâÿˆÎëÿvÉîÿsËóÿzÎóÿ}Íóÿ}Ïôÿ|ÎôÿÐóÿ™àùÿ¬êüÿ«éûÿ¯çûÿ©âøÿ—Õñÿ§ßöÿ³âöÿ®àôÿ©Ýòÿ¢Ûðÿ©ßôÿ²âõÿÁçöÿÆäóÿÂâóÿ®×îÿµÚïÿƒ¾ãÿ:›Ñÿ7šÐÿAŸÐÿT¯Ûÿg¸àÿ‚Æäÿ€Åäÿ†Éåÿo¼ãÿQ®àÿkºäÿ•Îíÿ•Îîÿ”Ëëÿ‘Éèÿ…Ççÿ…ÇæÿÉçÿˆÈäÿ‚Ãáÿl¹Þÿm¹Þÿr¾åÿq¾åÿdºãÿt¾äÿp»áÿbµàÿ_µßÿfµàÿV®ÜÿR®Ûÿx»àÿ¾âÿp·Þÿd°Úÿb­ØÿU¨ÒÿAžÏÿ0‘Éÿn¯ÿ_§ÿm¯ÿo²ÿ‚¾ÿ=œÍÿK£ÐÿS¦ÓÿN£ÐÿS£ÐÿG¢ÓÿD¢Òÿ8¾ÿt•ÿ!O˜ÿ1?Öÿ0?Êÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0>ÿ0.†ÿ0:Ãÿ0?Þÿ1CØÿ,OÅÿ(g¶ÿ-„´ÿ;™¼ÿP¨Ëÿ]±Øÿn¿ãÿÉéÿ‡Ììÿ…ÍïÿwÊðÿtÉðÿÑõÿÍñÿuÉñÿtËñÿuËðÿ”Ùõÿ¥âøÿ¦âøÿ¦ß÷ÿ¦Þöÿ§Þõÿªßõÿ¬ßôÿ®àõÿ¯àõÿ²áõÿ«àôÿ›Øóÿ§Ûòÿ¸áòÿ³ÝðÿžÎëÿ›Ìêÿ€¿ãÿR¨Øÿ3›Ðÿ@£ÕÿJ¨ÚÿL¬ÚÿqÀâÿŒÉçÿ¤×ìÿ~ÂãÿF£ÜÿZ±âÿÃçÿËêÿ¢ÔðÿŸÓîÿu½áÿsºÛÿ…ÅãÿŒÈæÿ‘Éåÿ|¾àÿm·ÛÿsÁæÿ~Äçÿe¹äÿs¿æÿ†Äåÿs¼áÿf¸áÿu½ãÿl¸áÿS®ÛÿP«Ùÿd´ßÿs¸Þÿe¯Úÿ]«Öÿ\ªÕÿ4•ÈÿY¡ÿW£ÿb¬ÿl±ÿ x¶ÿ"ŒÄÿ>œÍÿ]¬ÖÿY©ÔÿEžÎÿO¦ÒÿJ¨Ùÿ>¢Òÿ=“Àÿ1z˜ÿ&P›ÿ0?×ÿ0>Èÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0,ÿ0*sÿ/7µÿ-<Ùÿ.?Ûÿ.IÈÿ*\·ÿ.x°ÿ'‰³ÿ%•½ÿ:¤ÎÿE©ÖÿT³ßÿd½æÿd¿éÿwÈíÿ‡ÐðÿÓòÿŠÑòÿŠÔóÿ€Ðôÿ‚Òöÿ€ÎóÿxËñÿwÊñÿŸßùÿ®äøÿ¨ß÷ÿ„Éìÿ„ÉìÿžÙóÿ«áøÿ«âøÿ¬Þõÿ«Úñÿ¯ÞôÿœÖñÿŠÍìÿšÒîÿœÑìÿˆÆèÿ‡Äæÿ‚Àãÿv½ßÿK¦ÖÿB¡ÕÿQ¨ØÿV¬ÛÿJ«Ùÿe¸ßÿ†Èåÿ›Òëÿ¼àÿM¤Úÿ`²ãÿm½åÿËìÿžÓðÿ“ÌëÿƒÂáÿ¿Þÿ€ÀÞÿ~¿Þÿs¸Ùÿs·Úÿx¼Üÿ\¶ãÿcºåÿn¾åÿi½æÿÉéÿŠÅçÿ…Äåÿn¹àÿd´Þÿ^±Üÿe´ÝÿgµÝÿg²Ûÿk²Ùÿh¯Úÿ]«Öÿ?ŸÍÿfªÿ`¨ÿi®ÿl±ÿ„¾ÿ3•ÈÿL£Ðÿc¬×ÿY¥ÒÿG¡ÐÿJ§ØÿL©ÙÿA¢Ñÿ8’¿ÿ-y—ÿ&Pžÿ1?×ÿ0>Åÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0ÿ0%^ÿ04¦ÿ.<Óÿ->Ýÿ4JÑÿGhÃÿUоÿN›»ÿ8’·ÿ޾ÿ1›Îÿ%œÔÿ:¬ÝÿOµäÿS¶æÿXºèÿqÇîÿÌíÿ‡ÎïÿÒñÿ„Ìïÿ|ÌòÿsËóÿwËòÿÎóÿ{Íòÿ~ÍñÿÍñÿ“Õôÿ¦Þ÷ÿm³ßÿÂèÿ§Þöÿž×ôÿˆÌîÿ¡Øóÿ¯Þôÿ¤ÕñÿžÔïÿˆÊëÿ°Üñÿ’Ëêÿi·áÿŒÅçÿŽÆçÿt»àÿO§Öÿ>¡×ÿY­Üÿj³Þÿ=¤ÕÿF­Øÿp¾áÿ‹Èåÿj±Ùÿ8˜Òÿ7¡ÚÿS¯ßÿ‡Äçÿ•ÊéÿŠÇæÿ‹ÆãÿÉåÿÈåÿw¶Öÿt¶×ÿx¶×ÿz»Úÿc¸ãÿ]·ãÿrÁçÿk¿åÿm¾åÿyÀæÿ…Ãåÿt¼áÿm¸áÿq¸ßÿn·ßÿY°Ûÿ_¯Úÿh±Ùÿc­Øÿ`°Ùÿ.‰¿ÿ[¦ÿcªÿi¯ÿm´ÿ…Àÿ=›ÍÿP¨ÓÿR¦ÒÿR¨ÕÿC¦Õÿ7£×ÿC§ÚÿC£Óÿ@”Àÿ,y–ÿ&O ÿ1?Øÿ0=Ãÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0!Jÿ01“ÿ/;Ëÿ.>Þÿ1EÕÿ7ZÅÿ^†ÂÿsªÄÿ‰ÁÎÿÊÚÿsÅãÿS±Üÿ%–Ïÿ*žÔÿ0¤Ûÿ3«àÿC³âÿTºçÿX¼éÿhÂìÿÌîÿÑðÿŒÍïÿ•Ôòÿ†Îðÿ|Ïòÿ‹Óõÿ…ÏñÿrÇñÿ{Ëðÿ{Çïÿ„ËíÿÏîÿ ×óÿ¨Ýöÿ ØóÿÔðÿœÒîÿÃèÿÄèÿ~Âçÿ‹ÊëÿŠÉëÿƒÅéÿy¾äÿf³àÿ‘Æèÿ˜Êèÿ†Áäÿk³Üÿ= ×ÿ^°ßÿ|»áÿeµÞÿq½àÿw¿àÿ~Ãâÿq»ÞÿBžÑÿ0›ÒÿP¦ÙÿŽÇèÿ…Ààÿ”Çäÿ’ËåÿŒÊæÿ…Ãáÿ‚¾Üÿ‰Ãàÿ•Èãÿ~¼ÞÿÅçÿp¿æÿg½æÿfºæÿxÁèÿ‡Èéÿ‰ÆçÿÂäÿm¸áÿ|½âÿd¯Úÿ7–Ïÿd°Ûÿm²Úÿg²ÛÿW¬Øÿ ^¤ÿ]©ÿf­ÿk±ÿw·ÿ%ŒÄÿ;šÎÿB¢ÑÿJ¥ÓÿD¤ÕÿA¦×ÿ<¥ÖÿB¨ØÿD¤Õÿ<•Âÿ+z–ÿ(N¢ÿ1?Ùÿ0<Àÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ08ÿ0-ÿ0:¿ÿ0>Üÿ/AÙÿ6RÉÿ=p¾ÿJ‘½ÿh±Æÿ†ÇØÿ‡Ìãÿ¡Úîÿµâôÿ…ÒðÿsÍñÿAªÚÿ™Óÿ1£Ûÿ5«áÿB³ãÿL·æÿ[¾êÿkÅîÿÒñÿÒñÿšØõÿ›ÙõÿˆÎñÿwÊñÿxËòÿ†Ñóÿ‚ÌñÿzÊïÿyÉðÿ€ÌðÿÈìÿÆìÿ‡Éíÿ†Êíÿ›Ñðÿ¡Òïÿ—Ììÿn·ãÿk·âÿ‚Ãçÿ–Ííÿ”Íëÿ‹Æçÿu¹áÿ‘Åèÿ–Ééÿ‡Âãÿ|»ßÿK¤ØÿJ¨Üÿq¸àÿ|½àÿ‹Äãÿ|¾Þÿ„ÆãÿˆÈæÿa±Ùÿ8—Êÿx»âÿÄåÿ–Èåÿ¦Ñèÿ’Êåÿ~Ááÿ™Ìåÿ‘Çáÿy¶ØÿºÚÿ†ÀÞÿo¿èÿ}Äçÿfºäÿq½æÿyÃçÿ‡Ééÿ‡Åèÿ…Äçÿy¾ãÿw»âÿAšÑÿLžÒÿj³ÜÿpµÝÿ`´Ýÿ%l©ÿK›ÿd®ÿl°ÿs¶ÿ{¼ÿ&Åÿ?šÎÿ?¡ÑÿRªÖÿX®ÛÿG§ÙÿT­ÜÿZ°ÝÿM©×ÿ:–Ãÿ0}˜ÿ+P¦ÿ1?Úÿ0;»ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0&ÿ0(lÿ07°ÿ1>Øÿ0@Üÿ0JÌÿ1b½ÿ9¶ÿE›¼ÿM¬Êÿ`¹ÚÿÑéÿŒÕîÿŒÙñÿ–Üõÿ­èûÿ âùÿÔôÿ]Áëÿ[²Ýÿ;¦Ùÿ/¦Ýÿ4­ãÿJ·æÿY½ëÿqÇðÿ„Îðÿ”×ôÿ•Øôÿ”Óòÿ{ÇîÿcÀíÿ^ºêÿkÀíÿtÇðÿzÉîÿmÃíÿxÈîÿ…Êíÿ‡ÉìÿƒÇêÿ~ÄçÿyÁèÿp¼äÿÃæÿk¶âÿhµâÿÀåÿ‹Åçÿ•ÉìÿœËìÿ—Êéÿ‰ÁäÿŠÀåÿ–Éçÿ™Éæÿa­Üÿb¯Þÿ†Àäÿ‹Ããÿ”Éçÿ‘ÈåÿŒÈæÿ“Ïêÿ~Àáÿ@“ÂÿS§ÐÿŠÇèÿËèÿªÐçÿ¤Ðåÿ‡ÃàÿˆÇâÿ”Çàÿ•ÄÝÿŽÁÜÿÂßÿ]¸åÿi¼åÿ`¹äÿ\·äÿq½æÿu¾åÿ…Ççÿ|Âæÿd³ßÿN¦×ÿ`®Üÿi³ÞÿZ­ÛÿY®Ûÿ2“Çÿ7†ÿW¤ÿg¯ÿk²ÿzºÿ„¿ÿ<˜ÊÿS¥ÓÿL¦Õÿ]°ÜÿW®ÜÿS­ÛÿY±Ýÿ_·âÿ_²ÜÿJœÅÿ;šÿ0Q©ÿ0?Úÿ0:·ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0ÿ0$Wÿ04 ÿ0<Ñÿ0?Þÿ-EÑÿ+W¾ÿ.rµÿ0‹µÿ9œ¿ÿG¨ÍÿT²Ûÿ^½åÿnÇëÿ„ÒòÿŒÙóÿ›âøÿ©èûÿ¨çúÿŸäùÿ•Þ÷ÿ‚Ö÷ÿfÅëÿ0 ×ÿ$ Ùÿ)¤ÜÿGµäÿX½ëÿmÆïÿ{Êðÿ…ÏòÿÕóÿ—ÕóÿƒËðÿ_¾ìÿe¿ìÿk¿ëÿb»êÿoÃìÿuÄìÿvÅíÿÈìÿ†ÈìÿyÁèÿv¿çÿw¿æÿp¼åÿt½ãÿw½âÿu¼âÿv»áÿ‹Ãæÿ”ÈéÿœÌëÿ“Çêÿ’Éçÿ’ÇçÿžÏêÿ¥Ñêÿj±ßÿi²ßÿŠÂåÿŽÂæÿŠÁãÿÇåÿ–Ìçÿ’Íçÿw¸Ûÿ>ºÿ4Œ¸ÿX¯Ùÿ{ÁäÿŒÅãÿœÊãÿžÉâÿ†Âàÿ–ÊâÿŠÁÛÿ‡ÁÞÿv¼Ûÿi¼æÿe¹ãÿX³ßÿJ­Þÿm»äÿr¼ãÿiºäÿaµàÿY±Ýÿh·Þÿ‚ÀâÿiµÝÿ@ŸÒÿ'’Èÿ _¥ÿ?’ÿa¬ÿi¯ÿrµÿyºÿ*‘ÅÿO£Ñÿ_¬×ÿW¬ÙÿHªÛÿP¬Ýÿg¶àÿg·âÿd»æÿe¶ßÿU¤Èÿ?ƒ›ÿ2P«ÿ0?Ûÿ09³ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0 ÿ0Dÿ00ÿ0;Èÿ/>Þÿ/CÖÿ.RÃÿ.jµÿ'±ÿ/“¹ÿ7Æÿ;¤Ñÿ@«ÚÿJ²àÿgÂëÿvËðÿpÉðÿ}ÑòÿŸâøÿ åûÿ§çûÿ¼îýÿºîüÿ¼îýÿ¬çûÿ–àúÿoÄèÿ!œÕÿ(£Úÿ0§ÝÿL¶çÿgÄîÿÍñÿˆÐòÿŽÔóÿŒÐòÿkÁíÿU·èÿnÂîÿpÂìÿc½éÿuÅìÿ„Êîÿs¿ìÿ~Æìÿr¿çÿrÁéÿyÂæÿƒÄæÿÆéÿ‘Èèÿ”ÉéÿÅèÿÄçÿ‡Áæÿ–Êêÿ™ËêÿŽÆèÿ•Êêÿ›Îêÿ Ñëÿ›Ìèÿ[©Üÿh²àÿ‹Ãåÿ¼ßÿi²ÚÿkµÜÿ¤Ñçÿ£Òèÿn±×ÿ;‹¶ÿ9‹²ÿB™ÁÿJ«ØÿY²ÜÿƒÀâÿ’ÈãÿˆÀßÿ”Æàÿ’ÇÞÿ•Èßÿ—Èáÿc¸ãÿ[·âÿb·âÿ\´ßÿs»ãÿ‚Âåÿb¶Þÿy¼àÿo¹àÿo¸ßÿh´Ýÿ@ Ñÿ#Èÿ‚¾ÿ]¦ÿH—ÿf¯ÿp´ÿq¶ÿ ~½ÿ@œÎÿ]ª×ÿ_­ÚÿM©ÙÿC§ÚÿO¬Ýÿb·ãÿj¾çÿb¼æÿf·àÿX¥ÉÿF†ÿ5P®ÿ/?Ûÿ08¯ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ02ÿ0,zÿ09»ÿ/>Üÿ/AÚÿ/LÈÿ/d·ÿ4~³ÿ6‘·ÿ@žÂÿ@¢Íÿ6£Õÿ:¨Úÿ@®àÿB²ãÿI¸æÿgÄîÿdÅïÿvÍñÿŒØôÿ˜àùÿŒÛöÿžãùÿ»íýÿºïüÿÅóýÿÊóýÿ¯ëýÿ¡åûÿH­Üÿ%Ÿ×ÿ-¥Úÿ/¨ÝÿTºéÿuÉòÿÍñÿ„ÎñÿŠÐòÿjÂíÿR¶çÿ[ºèÿlÁëÿsÄìÿ„ÇìÿyÃêÿk½èÿo¿éÿÅìÿ|ÄéÿˆÊìÿ—Îíÿ ÓîÿžÏíÿšÌëÿŸÍîÿÌìÿ‹ÃæÿŒÁçÿ“Äèÿ—ÈéÿžÎìÿ›Îëÿ”Éëÿ’ÈçÿZªÜÿSªßÿ¿äÿ“Ççÿ¼áÿ‚Ááÿ°Øëÿ´Ûíÿl­Óÿ:‹¶ÿF•ºÿNš¼ÿH Çÿ9 ÔÿX¬Ùÿu¼ÝÿÅàÿ™Èàÿ›Ëàÿ¢ÏáÿŸËàÿ_·áÿW²Þÿjºãÿv¼ãÿŽÇçÿ—Ééÿ}¿âÿ‘Çæÿ‡Äæÿbµàÿ0šÍÿÅÿ‹Äÿs¸ÿY¦ÿU¡ÿj²ÿu·ÿ {»ÿ ¿ÿ=œÏÿ[«ØÿI§×ÿW­Üÿ_²ßÿcµáÿeºåÿbºçÿb¾çÿg¹áÿ`¨ËÿL‰ ÿ2O°ÿ/?Ûÿ07ªÿ0ÿ0ÿ0ÿ0 ÿ0ÿ0!ÿ0'fÿ06«ÿ1>×ÿ0@Ýÿ/HÍÿ3]¼ÿ2v²ÿ@µÿJžÀÿL¤ÍÿO©ÖÿP­ÜÿJ­ÞÿC®àÿG²âÿ@²äÿ>´åÿPºèÿaÂìÿkÉïÿ{ÏðÿŠ×ôÿÛ÷ÿâøÿ¥èûÿŸçüÿ¥êüÿ¦êüÿ¼ðýÿ¾ñýÿ—àúÿxÐòÿG°àÿ%Ÿ×ÿ+¤Ûÿ4©ÞÿV·çÿ|ÉñÿsÅîÿkÁìÿV¹éÿYºéÿh¼éÿuÂìÿyÅìÿo¿éÿ`¸åÿzÂéÿr¾çÿ~ÃêÿŽËëÿŠÍëÿÓðÿ¡ÓðÿªÕðÿ Ííÿ«Óíÿ£Òíÿ›Ííÿ£Îëÿ§Íêÿ¡Êêÿ£ÏéÿŽÃçÿŽÂçÿ’Ææÿf®ÞÿJ¤ÝÿU®ßÿx¾åÿ}Áåÿ¤Óëÿ­Øíÿ¦Ôëÿ^¢Ëÿ,ƒ¯ÿ?·ÿM™¹ÿW½ÿJœÃÿH¢Ïÿ^±Úÿ…Äâÿ—Ìâÿ—ÊãÿšËãÿŸÏâÿk½äÿa¹âÿs½äÿz¾âÿÃåÿŒÈéÿ|ÂåÿÈéÿi´Ýÿ!Èÿ5™Ìÿ=™Íÿ‹Äÿb¬ÿY§ÿj±ÿt·ÿyºÿƒ¾ÿ&‘Çÿ=žÑÿR¨ØÿJ¨Ùÿeµßÿh¸ãÿk¼åÿp¾æÿtÁèÿpÃëÿeºãÿcªÌÿX¢ÿ9R´ÿ.?Üÿ06¥ÿ0ÿ0 ÿ0ÿ0"Qÿ02šÿ0<Ïÿ0?Þÿ/DÒÿ+T¿ÿ.o³ÿ7ˆ±ÿB˜½ÿJ¡ÈÿX¨Ôÿ]°ÝÿX²ßÿV³âÿ\¸äÿXµãÿE±ãÿG´äÿD³äÿDµäÿR¼èÿrÈïÿpËïÿ“Ûöÿ’Ü÷ÿšâúÿ¥èýÿ¥èüÿ¡æüÿ®êüÿ­ëüÿ²íýÿ½ðüÿ¦çüÿ”âüÿÑóÿ5¥Ûÿ'¢Úÿ>«ÝÿL°àÿWµäÿOµäÿX¹çÿS¶åÿa»èÿvÂìÿn¾éÿh¾èÿV´äÿYµãÿzÃèÿ„ÄéÿzÁæÿ}ÂæÿŽÍìÿ™Ñðÿ«Ùòÿ«Õñÿ«Òïÿ®×ïÿ—Ìêÿ˜Éëÿ¢Ìêÿ¨Íëÿ¥ËêÿÊêÿ~¹ãÿ‡¿äÿ“ÅæÿBŸØÿ>¡ÜÿZ¯ßÿs»äÿl¹ãÿ‚ÂåÿÇæÿ›Íçÿ6ˆ¹ÿ |«ÿ:бÿK”·ÿU›¹ÿj¥Àÿg¦ÅÿR¥Êÿ^­Ôÿy¿ßÿˆÅáÿˆÅáÿÇãÿ€Ãçÿi»äÿzÁæÿ…Äçÿs½ãÿp¼ãÿc´ÞÿY«Øÿ<›Îÿ1–ËÿM¤ÒÿJ Ðÿ.’Åÿcªÿk±ÿv·ÿ z¸ÿ}¼ÿ ‹Åÿ@œÎÿ@¢ÓÿG¨×ÿ[°Ýÿu½åÿn¿çÿlÁçÿlÀèÿdÁëÿwÉìÿÇåÿºÒÿnŸ¬ÿ>V»ÿ.>Úÿ06¥ÿ0<ÿ0.†ÿ0:Äÿ0>Þÿ/B×ÿ0PÅÿ/hµÿ*¯ÿ,޵ÿ/˜Áÿ? ÌÿD¥ÕÿM­Üÿ]´áÿS´âÿUµãÿ\¸åÿ[¶åÿN³âÿB±ãÿIµäÿQ¸çÿM¸åÿV½èÿiÆïÿÐñÿ‘ÙõÿŸâúÿ˜Þùÿ•àùÿ¡äûÿ§èüÿµíýÿ´íýÿ°îüÿ±ìýÿ¹ïýÿ®éüÿŸãúÿzÎïÿ7§ÚÿA¬àÿ]µãÿV³ãÿX¸æÿ`¼èÿQ²äÿ_¹èÿf»èÿe»æÿaºäÿi¾æÿo¾çÿq½åÿŽËêÿ–ÍëÿŽÊêÿÌìÿœÑðÿ¥×ñÿ±Ýòÿ½Þòÿ¯Õîÿ™Ëëÿ–Çèÿ™ÇèÿŸÉêÿ¤Ìêÿ›Çéÿƒ»âÿºâÿŒ½âÿAœ×ÿC¥Þÿu¹åÿy¼åÿs½åÿ‡Ååÿ™Ïêÿv·Ûÿt®ÿyªÿ,¬ÿ?‹²ÿN”·ÿp¤Ãÿk¥Áÿm§ÂÿXŸ¾ÿVŸÀÿ`§ÇÿY¤ÉÿR ÈÿzÅçÿc¸ãÿO©×ÿG¥ÖÿO«×ÿE¦ÓÿD¢ÑÿH¢ÐÿJ¢ÑÿAžÐÿ>Ïÿ:›Ïÿ!‡Áÿd­ÿv·ÿ‚½ÿ‡Àÿ$ÅÿJ ÐÿN¦ÓÿC¦×ÿU®Ýÿs½ãÿÃæÿxÃèÿxÅêÿnÄìÿzÊíÿŒÑîÿŽÐêÿ…ÃÚÿv®¼ÿ@ZÃÿ-;Òÿ1>Ïÿ1>Øÿ/@Ûÿ.KÉÿ2bºÿ9~´ÿE“¸ÿGœÀÿ@žÊÿ< Ñÿ0¡Öÿ;§ØÿM®ÞÿQ±áÿe¹æÿ[µäÿR³ãÿV´åÿP²ãÿb»èÿP¶ãÿE³ãÿH³åÿT¹æÿgÃìÿtÈïÿ…ÑòÿØôÿ˜Þøÿ¢äúÿ“Þøÿœãúÿ«éýÿ¸íýÿ²ìüÿ³íüÿ¹ñýÿÌöýÿÏõýÿÁðýÿ§äúÿZ¸çÿ9§ÜÿV´ãÿ_¸åÿU´åÿZ·æÿO°ãÿR²ãÿl¿êÿm½èÿa¸äÿ|ÆêÿÄèÿ{ÁåÿËëÿ˜ÏîÿŽÉêÿ‡Èéÿ‘Ìëÿ”Íìÿ˜Îîÿ¤ÔïÿŸÍëÿ…¾åÿÄçÿÀåÿŒ¿äÿ‡ºãÿнäÿœÇéÿœÈèÿƒ¸ßÿ\¦Þÿc¯äÿ½çÿv»åÿl¹äÿ”Ìêÿ–Íêÿ5Çÿz·ÿ2ˆ¿ÿ r­ÿ!{¬ÿ@‰±ÿ[˜¹ÿU–¶ÿM³ÿ>НÿG‘¯ÿ?ˆ­ÿ@¦ÿC¥ÿoÀçÿL©×ÿA Ðÿ?¢Òÿ@£ÒÿF¤ÒÿK¦ÓÿI£ÑÿI¢Ðÿ?žÎÿI Ñÿ3™Ìÿv¸ÿc«ÿƒÀÿ$Åÿ&Çÿ<šÍÿS¨ÔÿV­ÙÿO®Úÿh¹âÿ|Âæÿ|Åèÿ|ÆêÿŽÍïÿÕòÿœØóÿ˜ÛóÿÙñÿ“ÔçÿÄÔÿE\Íÿ.8Ñÿ+EÊÿ"U¼ÿ+s²ÿ7Œ´ÿDšÀÿP¥Íÿ^«ØÿSªÙÿJªÜÿ6¨Úÿ=ªÝÿG¯àÿK°âÿO±ãÿ[µäÿU´âÿ[·ãÿU³äÿO±âÿP´áÿOµäÿJ³ãÿ_¼èÿb¿êÿeÁìÿzËñÿ‹ÔóÿˆÓóÿ×öÿžâúÿ¤èüÿ¥èüÿ¤èüÿ¡çüÿ§êüÿ¼ðýÿ¾ðýÿÈõýÿÓúþÿ×úýÿÆóþÿƒÑòÿG®ÞÿJ¯ßÿL®ßÿK°áÿS´äÿU´åÿR±ãÿi¼çÿp¿èÿg½åÿ{Äèÿ†Éêÿ…Èçÿ†ÉèÿËëÿÈéÿËêÿŸÒïÿšÍéÿˆÄçÿŽÆèÿÆéÿ‘Ççÿ˜ÇéÿžÈéÿ–Ãèÿ‰»äÿ›ÆèÿÀãÿe©ØÿG”Ìÿ:—×ÿf°æÿs¸æÿm¸åÿt¾åÿ…Ééÿo·ßÿ3‘Æÿ6“ÆÿF˜ÇÿD“Âÿ%~²ÿkŸÿ!r¡ÿ6}¦ÿ;‚ªÿ8¨ÿLˆªÿZˆ¦ÿ]… ÿC|žÿN§Öÿ>žÍÿA¤ÔÿQ«ØÿRªÖÿZ«×ÿ^¬ÖÿG¤ÐÿA ÑÿV©ÖÿX§Óÿ4˜Ìÿ o³ÿnµÿ‰Âÿ)’Çÿ%‘Èÿ3šÏÿL©ØÿQ­Üÿ^´àÿcºäÿlÀçÿrÄêÿ‡Íîÿ¤Úôÿ§Þõÿ¤àöÿ³èûÿ¾îüÿšÛïÿ1—ÇÿaÄÿe¾ÿ u²ÿ!Œ¸ÿ=žÇÿB¤ÒÿS¬Üÿa´áÿd·âÿV²âÿF­àÿ<­àÿR³äÿQ²ãÿL±âÿVµãÿN³áÿQ´âÿ_·äÿV´ãÿR²ãÿM±ãÿV·æÿ[ºçÿnÀêÿoÃëÿpÃëÿwÈîÿ‡ÐñÿÕóÿšÜ÷ÿ¡áúÿ“ßùÿ¦çüÿ³ëüÿ­éüÿ±ìýÿ¾òýÿÊõýÿË÷ýÿÔùýÿÍ÷ýÿÃòþÿ•ÛøÿU´äÿS°áÿO¯âÿH°ßÿV¶âÿZ¶ãÿW²áÿP°àÿi¿çÿ€ÈêÿŒËëÿ–ÏîÿŠÊêÿÇèÿ‰ÈéÿŠÆèÿŒÇéÿœÐíÿ¢ÓìÿÈéÿ•Ìêÿ–ÉéÿÄçÿ›Èéÿ¢Ééÿ¾æÿo²ßÿo±ßÿk­Üÿ[¡ÓÿYœÏÿ=šØÿO§áÿ\¯ãÿh¹æÿƒÆéÿ~Âçÿ]ª×ÿBšÌÿGžËÿIÇÿX ÆÿH’½ÿ h£ÿ d›ÿ?wœÿ[‡¥ÿi£ÿoŒ¢ÿX…žÿ3wžÿ3¨ÿUªÖÿN§ÔÿD¦ÓÿK¨ÖÿY­ØÿZªÖÿd®ØÿSªÔÿN§ÕÿJ¡ÑÿL¢Òÿ-”Èÿ_¨ÿi±ÿ*‘Éÿ8™ÎÿD¡Ñÿ<¡ÒÿF¨ÙÿF­Üÿ^·âÿ[¸äÿ_¾éÿ~Ëíÿ—Öðÿœßõÿ¥çûÿºñÿÿ¶ëüÿˆÐðÿ0œÕÿ†Êÿ…Æÿ‡Åÿ ‰Çÿ%›Óÿ=«ÝÿM±àÿ`¹åÿk½çÿYµäÿ[·åÿ`¸åÿF±áÿU¶äÿY¶åÿQ´ãÿc¹åÿV´ãÿO³âÿ]¶åÿ[¶äÿQ±ãÿI¯âÿB¯âÿY¹çÿsÂëÿ|ÈíÿqÆìÿdÁìÿÍðÿÔñÿ‘Öôÿ’Øõÿ‘Øöÿ¢âúÿ¹ìýÿ±êýÿªéüÿ®éüÿ¿óýÿÈõýÿÒøýÿÙúýÿÖ÷þÿ Û÷ÿN¯âÿ[³äÿa¸åÿR³âÿT²âÿV³âÿW³áÿ[·ãÿxÅêÿ‹Íìÿ˜ÑïÿÓðÿÍìÿÄçÿ€Äåÿ‹Æèÿ–ÌëÿÊéÿ’Íêÿ”ÊéÿÈèÿÇèÿ‡¾äÿœÉëÿ˜Åçÿ‡»ãÿk°ÝÿV¤ÙÿP ÕÿU™ÎÿK•Îÿ1šÙÿ>¥Þÿ[²åÿa¸æÿ|Åêÿ{¸ßÿl¯ÕÿV¥ÏÿIžËÿ=•ÁÿA’½ÿ*²ÿm¥ÿiŸÿ]‚žÿv¡ÿlŒ¡ÿWŸÿg—ÿj›ÿ;…®ÿZ¬ØÿW«ØÿR©ÕÿT¬Øÿe²Úÿ\­ÖÿZ«×ÿ\¬ØÿSªÕÿF¢Òÿ3—Ìÿo­ÿ[¦ÿ{ºÿ6šÎÿI£ÓÿE£×ÿS«ÜÿW±ÝÿW´àÿaºåÿvÃêÿƒÎïÿ’Øòÿ¡âøÿ£èýÿ™àøÿsÆëÿ2¡ÝÿÔÿ‘Ôÿ”Öÿ•Ôÿ•Ôÿ “Õÿ$¢ÝÿH³äÿ\¸åÿf¼èÿ`ºçÿT·äÿf½çÿX¶åÿI²ãÿTµåÿV¶åÿ`¹æÿl¾èÿZ·åÿW¶åÿ[¶äÿG°àÿS³ãÿK¯âÿP³äÿa¼éÿkÁìÿkÂìÿoÂëÿkÂìÿ€ÌïÿÔòÿŽÔóÿ—Úõÿ•Úöÿ˜Ü÷ÿ©æûÿ­èüÿºëüÿÂîýÿ¿ðüÿÉôýÿÉóýÿÁïýÿ²èüÿ{ÈðÿL®àÿX´ãÿ`·äÿa¸åÿ[´ãÿ^µâÿtÀèÿuÃéÿŽÎîÿ˜Ôñÿ£Úòÿ¤Øðÿ˜Ðîÿ—Ïîÿ›ÏìÿŽÈçÿ•Îìÿ¢Ôíÿ˜Ìêÿ‘Éèÿ™Íéÿ˜Êèÿ‡¿äÿ‘ÂæÿŽÀäÿ€¸àÿm°ÛÿN Ôÿ=—Ñÿ+zÀÿ€Èÿ0ÜÿU±äÿnºçÿu¿èÿ|½ãÿs´Ùÿ|¹Üÿ‚¹ÛÿW¥Íÿ@–ÁÿB•¾ÿ/ˆ³ÿu©ÿkŸÿRžÿ_„ ÿQ€žÿ h•ÿY“ÿo¡ÿ'~«ÿP¥ÔÿN§ÔÿZ¬×ÿ]­ØÿU©ÔÿU©Õÿ\®Øÿ[®ØÿQ¨ÖÿD¤Õÿ…ºÿT—ÿb«ÿ}½ÿCŸÒÿH£Õÿ=¤ÕÿP­Ýÿi¹ãÿl¿èÿtÅìÿÕòÿœßöÿ±èûÿ¯ìþÿd¿èÿ•Öÿ’Ôÿ–ØÿœÜÿžÝÿžÜÿžÜÿÜÿšÛÿ$¡ÝÿQ·æÿxÄìÿyÃêÿlÀçÿd½èÿk¿éÿc»çÿO´äÿ\¸æÿ^ºçÿ_ºæÿ_»çÿe¼èÿj½èÿ_¸åÿN²ãÿY·åÿK°äÿ_ºèÿkÀêÿpÂëÿ|ÇìÿxÇìÿtÆíÿÓñÿzÈîÿqÄîÿ€Íðÿ¦àùÿ‘Öôÿ–Ü÷ÿ­çüÿ·êýÿ¼ìýÿºëýÿÇïýÿ¹ìýÿ´ëþÿ¥àøÿYµåÿX´ãÿ[´ãÿ_µãÿh¸æÿq¼åÿ^³áÿe¹äÿo¾çÿÐðÿ¨Üôÿ­Þóÿ«Ùñÿ£Õîÿ›Ñïÿ²Úðÿ¨ÕìÿÁäÿ’Ëéÿ¢ÒìÿžÏéÿ£Ðêÿ¥Ïêÿ“ÄæÿŠÀãÿˆ¼âÿy¶Ýÿc©ØÿG›Ðÿ9‰Æÿ4yºÿ†ÏÿLªâÿo»çÿ‚ÃìÿÀçÿ¼ÞÿqµØÿt·ØÿƒºÜÿh¬ÐÿNœÅÿOžÃÿG—¾ÿ=¶ÿ/y¤ÿBxœÿAwœÿc”ÿ a—ÿk ÿv§ÿx¨ÿ6œÍÿ8œÍÿA¡ÓÿXª×ÿ\«×ÿV©ÕÿY«ÖÿZ®ÙÿT¨Øÿ7Ðÿr®ÿ]£ÿi²ÿ {Àÿ?£ÖÿG§ÛÿQ­Ýÿ^¶áÿqÀéÿËíÿ—Úôÿ©æûÿ¬ëþÿ¢åûÿV¹åÿ‘Ôÿ™Ùÿ œÜÿ% Þÿ"¡ßÿ(£áÿ.¥áÿ,¤àÿ!¡Þÿ  Þÿ(¥ßÿ[¼èÿÇìÿqÃêÿuÂëÿxÃëÿf½éÿc¼èÿc¼çÿ\¸æÿ_»æÿ`ºçÿ\ºçÿZ¹çÿd»çÿbºçÿh»èÿS¶æÿR¶äÿh¿èÿxÅìÿkÂêÿ€ÊíÿƒÊîÿyÇíÿ‹ÐðÿÌïÿtÇîÿyÈîÿÕôÿ£ÝõÿŽÖõÿªäüÿ³èüÿ´ëýÿ³êüÿ¹èüÿ¬äüÿ¯æýÿÈïÿR®áÿj»çÿxÁèÿiºåÿzÀçÿ€Äèÿr¾çÿr¼ãÿt¾äÿzÁçÿ•Ðîÿ¢Öñÿ›Ðîÿ•Íëÿ‘ËëÿŸÔîÿ©Öîÿ‰Æçÿ•ËéÿÎèÿ–Ëèÿ˜Êéÿ¡Íéÿ’Äåÿw¸Þÿi³Ýÿe¯Ûÿ`ªÖÿOÏÿ9~·ÿ!^œÿB Üÿk¸éÿ~¿éÿ}¼æÿ‡¾ãÿŒÃäÿw¸Üÿ€¼Üÿ{·Øÿu²Ôÿg¨Êÿc¥ÅÿF—½ÿD’¼ÿ2€«ÿ+q™ÿgšÿbœÿ iŸÿu§ÿuªÿ}°ÿ8žÎÿ0™Ëÿ&—ÍÿTªØÿ]®Úÿ^¬ØÿT§ÔÿTª×ÿJ§×ÿ…¾ÿn¬ÿl­ÿf±ÿ,”ÐÿI©ÛÿT°Þÿ`¸äÿwÁéÿ”Ôñÿ­ãøÿ¼ïÿÿ¯èýÿuÈíÿ-ŸÛÿ—ØÿÜÿ'¢Þÿ(¤àÿ2ªãÿ9­ãÿ7«ãÿ3«ãÿ-©ãÿ1©âÿ7«âÿ,¨âÿP·çÿrÅìÿrÆêÿsÅëÿmÁêÿ[¹çÿS·æÿQ¸æÿT·æÿkÀéÿb»çÿ^¹çÿ\¹çÿYºæÿe½çÿ~Åêÿf¼çÿW¶åÿY¹çÿwÅíÿlÂìÿkÂìÿ~ÉîÿŒÏïÿ€ÊïÿyÈîÿsÈïÿvÈïÿvÈðÿ¥Þ÷ÿžÚöÿ“Öôÿ¤àùÿ àúÿ—Ûøÿ´çüÿºèüÿ¤Úøÿk»èÿU²ãÿq¾çÿ†Åêÿ‡Åêÿ‡Äéÿ€ÃçÿÆçÿÊèÿˆÈçÿv¾ãÿ~ÂæÿŠÈéÿ“ÍîÿˆÇçÿ‡ÉéÿÊëÿÊéÿËéÿÈéÿ•Êèÿ’Ççÿ™Êéÿ¢Îéÿ ËçÿˆÀãÿm´ÝÿvµÝÿk­×ÿM—Êÿ,d¢ÿ/o¦ÿb¸êÿ|Àêÿ‘Çìÿ—ÈéÿˆÁãÿt¸Ýÿt·Ùÿ½ßÿ‚¼Ýÿj°Òÿk­Îÿq¬ÌÿRœÀÿA‘ºÿ)€¯ÿk¢ÿg¢ÿuªÿw­ÿu©ÿ °ÿ‚³ÿ+–Êÿ-—Éÿ,™ÍÿJ§ÔÿW¬×ÿZ«ØÿY©ØÿUªÚÿ.–Ëÿu°ÿu²ÿt±ÿh±ÿ?¤Ùÿk»äÿwÀçÿvÄéÿˆÔóÿ¯èýÿÂðÿÿ”×óÿ7¡Üÿ–×ÿ'Ûÿ*¢ßÿ.¦áÿ6«ãÿ>±çÿF´èÿM´çÿGµéÿEµçÿ>²çÿ@³èÿ:²èÿ1¯çÿJ¶èÿtÅìÿÉìÿhÁêÿc¿êÿtÃêÿtÄêÿ_»çÿa¼çÿnÁêÿc»çÿc¼éÿe¼éÿX¸æÿmÁéÿsÁèÿg¾éÿe»èÿU·çÿf¿êÿvÅíÿwÇîÿzÈïÿ†Íðÿ|ÆîÿŒÍîÿ†ÎðÿƒËðÿxÈïÿ†ÏòÿvÇïÿ‰Ññÿ‹Ññÿ‡Ïñÿ•Öõÿ­áúÿ¸çüÿ“ÏóÿM­ãÿJ®àÿa·ãÿ|Àçÿ’Êìÿ”Ëìÿ‡ÇéÿŒËêÿ‘ËëÿŠÅçÿ†ÅçÿÄåÿzÁäÿƒÇèÿÊêÿŽÌêÿ›Òðÿ™Îìÿ‡ÅçÿŽÊêÿ¥Òëÿ©ÑêÿËéÿ¡Ïêÿ§Ðéÿ˜ÉèÿÃäÿ„»àÿ_¨×ÿ:ˆÁÿ$Z•ÿAÆÿf¸ëÿ€¿éÿ…Ãæÿ}¿áÿn¸ÝÿsºÞÿÄáÿ~»Üÿx¸Úÿm±Òÿp°Ðÿs®Ïÿa¤Åÿ?’»ÿrªÿd£ÿw¬ÿ~¯ÿ ±ÿ}°ÿ-‰¸ÿ3¼ÿ7›Îÿ.˜Êÿ)–ÊÿH¦Õÿ\«×ÿ]­×ÿ`­×ÿG¥ÖÿˆÀÿ x±ÿ {³ÿw³ÿs¸ÿI¬ÞÿoÃêÿ|Êîÿ“Ùôÿ•Þùÿ^¹çÿE©Ýÿ”Öÿ“Ùÿ,¢Þÿ5¨áÿF³çÿFµéÿJ·êÿG¸ìÿJ¹ìÿJ¹ìÿK¹íÿIºëÿC¸êÿG¹êÿ>¶ìÿ:¶ìÿ^¾ìÿrÅìÿwÈîÿ]¿êÿgÂëÿzÇëÿ†Êíÿ|Æéÿg¿çÿnÃëÿuÄëÿ|ÆìÿyÅêÿf¾éÿb½éÿ|Äëÿ|ÅìÿnÁêÿe¾êÿkÂìÿkÁëÿuÆíÿ|Çîÿ{Çîÿ}Èîÿ”Ôòÿ‰ÏðÿËïÿfÂîÿoÃíÿiÁëÿ˜Öóÿ ×óÿ•Òñÿ{Äìÿ“Ñóÿ ×÷ÿdµæÿB©ßÿW°ãÿx¿çÿ‡ÆçÿƒÅçÿ‚ÅéÿŒÉêÿ‘ÉìÿŽÉêÿ†ÅåÿŒÇèÿÊéÿ|Ãæÿ†Æèÿ—Éëÿ–ÍíÿÌìÿ¤ÓïÿŸÎëÿ‹Èéÿ§Òíÿ­Óëÿ¡Îéÿ Ïêÿ¡Îèÿ‰Âäÿu¹áÿf²ÜÿU¦ÓÿBÂÿD’Èÿ]³èÿ~¾êÿŸÐïÿ”Èèÿ~¿áÿw½ßÿ€Âãÿ•Éçÿ{¹Ýÿg°×ÿ]ªÓÿq°Òÿo®Ïÿ`£Æÿ0Џÿ o¨ÿu¬ÿ&ƒµÿ$„¶ÿ2йÿ:¼ÿ0޽ÿ*Œ¼ÿ(@€ B“Òðÿ—ÒïÿØóÿ­àöÿ¤Úóÿ¥×ðÿªØðÿ¤ÙðÿœÕïÿžÖîÿƒÎêÿ¦ÙîÿÒíÿ«Ùðÿ±Þóÿp´Úÿ m°ÿi³ÿ%ƒÀÿRšËÿN›Ìÿ"ŠÄÿR¤ÔÿšÊéÿk±Úÿ„¼áÿ„Àãÿ…Áãÿ^®ØÿU©Öÿd²ÝÿW­Úÿg³Þÿl¶áÿ[±ßÿp»åÿe·âÿV±ßÿv¿åÿŠÆèÿyÀæÿn»æÿe¶ãÿ#‡Éÿ'‡Éÿ0ŒÊÿ'„Æÿp¹ÿ|¼ÿy¸ÿo±ÿNŸÌÿ€¼Ýÿ•ÅâÿÂàÿ’ÄáÿŒÂßÿ‚¾Ýÿƒ½ÜÿŒÀÞÿ|¼Üÿp¶Ùÿp´Öÿl±ÕÿŒÊíÿ¬×ñÿÔñÿ–Óòÿ«Úôÿ²Üôÿ³ÝôÿšÔðÿ”Óðÿ‘Ðìÿ–Òîÿ­ÛðÿªÙðÿ®Ýôÿl§Ñÿ[§ÿ^¬ÿc²ÿt¼ÿ5ŒÆÿ5Éÿ‹ÆÿO¢Ôÿ¡Îîÿ‡¾äÿb­Ùÿz¹ßÿt¹áÿs¶àÿ[©ØÿˆÀæÿ½äÿ{½äÿt»åÿV¯ßÿmºäÿmºäÿq»äÿ€Áæÿ‚Âæÿ…Âçÿ{Áèÿ/Íÿ‚Æÿ#‡Éÿ)‡Éÿ7‹Ëÿ+ƒÄÿy»ÿm³ÿ t¶ÿb«Óÿx¶ÙÿxºÜÿ€½Þÿ‡Àßÿ‰Àßÿ„½Ýÿ€»Üÿ†¾Þÿ¾Ýÿj³×ÿj±Õÿl±Öÿ£ÓñÿªÖòÿªØòÿ•Ðïÿ—Óòÿ¡ØóÿÖòÿÍíÿ“ÐîÿœÔïÿ´Üñÿ¦Øðÿ¡×ñÿRÁÿA•ÿO ÿ]¦ÿ f¥ÿf¬ÿu»ÿÂÿƒÂÿ,ŽÉÿŒÃçÿ‘Æéÿc¯ÚÿT¦Öÿq·áÿ…Àæÿ}¸áÿÃçÿŠÂçÿÅèÿ~Áçÿ_³áÿp»äÿ…Äçÿ‡ÄèÿˆÆèÿy¿åÿ~ÂèÿQ¡Õÿ^¯ÿ`±ÿ^°ÿ`°ÿ*…Ãÿ)ŒÆÿ&ŠÅÿ xºÿ*‰Àÿ€·ÙÿyµÙÿe±Õÿv¸Úÿr¸Úÿn·Úÿu¸Úÿx¹Úÿz»ÜÿzºÛÿl³Öÿd®Ôÿi°Öÿ Òðÿ™Ëíÿ§Õòÿ¯Ûôÿ¤Øòÿ¢Ùôÿ¥Øòÿ¢Õðÿ±Úòÿ²Ûðÿ·ÝñÿÆåÿF·ÿ.…ÿ@–ÿS¡ÿ[™ÿc‘ÿCsÿDˆÿ k°ÿ}¾ÿu¹ÿ„¼ãÿ¥Òðÿ_°ÜÿP§Øÿ{¾åÿ•Éêÿ˜Êëÿ—Êëÿ”Éêÿ”Êìÿ”ÌíÿÃèÿz¾äÿ…ÅçÿÈéÿ‡ÆèÿŒÌíÿ‰Êìÿ?—Ðÿ Àÿ_¬ÿY§ÿQŸÿY¤ÿ]¦ÿ h«ÿo­ÿD™Çÿo±Ôÿf¯Ôÿf°Õÿl³Öÿd°ÕÿS¬ÓÿW®Õÿb±×ÿc±Öÿh±Õÿc¯Óÿd¯Ôÿk±Õÿ™Ñïÿ¥Ôñÿ®Øòÿª×ñÿ­Öñÿ£Òïÿ£Óïÿ¬ÖîÿŸÐìÿŸÎéÿ†¼ßÿ*|¹ÿ(€ÿ6ÿMŸÿZ¥ÿ^‘ÿd½ÝÿP»ÿ&ZzÿRuÿW‹ÿR—ÿ:†¾ÿ§Õñÿq»äÿu¼åÿ˜ËìÿœÍíÿ™ÌìÿšÏîÿ£Ôïÿ£Õðÿ¡Ôðÿ’Íìÿ‘Íìÿ˜ÏîÿŒÉëÿ|Èëÿ…Éëÿ7{¹ÿ ]¨ÿ(‹Äÿ|»ÿe«ÿY£ÿPÿMœÿN‘ÿTŒÿ2„µÿg°Óÿm±Óÿc­Óÿc°Ôÿa°Ôÿb¯ÓÿVªÒÿU©ÑÿY¬Òÿ]­ÓÿS¨Ïÿa®Óÿh²×ÿj²àÿ—Ðïÿ£Óðÿ£Ïíÿ¤Ñíÿ¥Ñìÿ­ÕìÿÏëÿÉèÿUœÉÿ€¿ÿY¤ÿ%ÿC˜ÿW¦ÿcªÿc‘ÿgÁàÿkÖüÿjÎñÿSžºÿ Opÿ*Xÿ9sÿN»ÿa±Ûÿe¹ãÿ‹ÈìÿŸÐðÿ•Ïîÿ›Õñÿ¦Ùòÿ¬ÜóÿÍíÿ„Éëÿ‹Ìíÿ{ÅëÿjÂëÿzÊîÿ#}ºÿT£ÿ[§ÿW¡ÿ m­ÿ r´ÿ\¥ÿTŸÿQ—ÿTŒÿZ“ÿ?‘Áÿx¸Ùÿtµ×ÿm±Õÿd²×ÿ^±Õÿa°ÕÿX«ÒÿP§ÏÿO§ÏÿV«ÑÿP¨Ïÿi³ÖÿnµØÿ s»ÿ1…Åÿy´Þÿ¦Óîÿ¨ÔíÿŸÏëÿ˜Êçÿ„¾áÿBx¯ÿ1q­ÿ3˜Îÿ,†ÿ/‹ÿP¢ÿc¯ÿuµÿ4sšÿkÃáÿYÆóÿOÀñÿ_ÎùÿkÏòÿSž»ÿ Qrÿ,Yÿh”ÿ8—Çÿt½äÿ“Ðïÿ¦Ûóÿ¬Þõÿ£ÛóÿžÖñÿÆèÿ‚ÉêÿÇëÿÐïÿvÇíÿ6–Ëÿ n¯ÿ p±ÿ l¯ÿc¨ÿP“ÿVÿP™ÿK’ÿQ‹ÿWÿn¥ÿ]®Õÿj´×ÿd±Õÿb°Öÿ]°×ÿ[±Öÿ\°ÖÿMªÒÿN¨ÑÿP¨ÐÿN¨ÐÿU­Õÿk·ÚÿqºÝÿ(ŒÈÿt¹ÿh²ÿyºÿ0†ÁÿR Ðÿ@Åÿ<Šÿrÿ5|½ÿ<ŒÄÿ(…ÿMŸÿi°ÿ)†ÄÿE•Çÿ>{ÿfÁàÿ[ÇóÿN¿ðÿN¿ðÿPÁñÿ_ÎùÿkÑôÿU¢¿ÿ-i…ÿZzÿD…¦ÿ|»×ÿ¤Ùïÿ¥Ýõÿ–Õñÿ‹ÎíÿÉëÿ{ÊíÿÐñÿwÄèÿA™Íÿ(ˆÁÿ&„¿ÿ €ºÿ*…¾ÿ4Äÿ x³ÿ[ ÿVžÿ\¡ÿ\ŸÿU“ÿ~³ÿ]°×ÿY­ÕÿU­ÕÿW¯ÖÿP¬ÕÿG©ÓÿG¨ÒÿA¦ÐÿA¤Îÿ?¡ÌÿW®Õÿv¿àÿg·Üÿd¹ßÿ8–Íÿ4Çÿ,‡Ãÿt¶ÿn²ÿ3‹Ãÿ0ŠÁÿA’ÿ0ÿ,ŠÊÿH•Êÿ@”ÿa­ÿƒÁÿE—ÍÿF—ÆÿM~ÿe½Þÿ[ÈóÿN¿ðÿPÀðÿPÀðÿN¿ðÿPÁñÿ^ÍùÿjÏóÿ^­ÈÿCzÿ?p…ÿj™­ÿpµÒÿvÃåÿÑðÿ‚ÍðÿZ¶äÿ^¶ãÿB ÒÿFœÍÿGšËÿ=”Çÿ7ŽÄÿ:ÅÿJœËÿU«Øÿ{Êëÿ|ÌíÿtÂæÿg²Üÿ*z´ÿ(†¹ÿX®×ÿU®ÖÿL«ÕÿP¬ÖÿM«ÕÿF¨Óÿ?¤Ïÿ5 Ìÿ+™ÇÿN«ÓÿŒÎèÿ§Ûïÿm¼àÿc¹áÿS¤Óÿ/ŽÆÿI™Íÿ2ˆÀÿ q³ÿ.ˆÀÿ½ÿD–ÿ4ÿ&„Åÿ\²Ýÿ V£ÿ uºÿ%‡Ãÿ,‘ÈÿWœÿ'fÿhÁáÿ[ÈóÿO¿ðÿPÀðÿPÀðÿPÀðÿPÀðÿN¿ðÿPÁñÿ[Ë÷ÿhÏôÿ]°ËÿG~“ÿ@oƒÿN¨ÿk³ÒÿL¨Ôÿ1™Ïÿ5žÑÿ<¢ÔÿB£ÔÿI£ÒÿP£ÐÿLžÌÿE™ÉÿT¦Óÿ‹×õÿ¦ëÿÿ ëÿÿ®îÿÿ·îÿÿ”ÙõÿxÇêÿbµÜÿQªÕÿF©ÓÿJ¬ÖÿH©Ôÿ<¥Ðÿ1 Îÿ%˜Éÿ2žÌÿvÅäÿ Ùîÿªßñÿ‚Ççÿj¼ãÿX£Ðÿ;”Èÿk­ÖÿC”Æÿ p³ÿ$„¿ÿ-ºÿ'„ÿ3ÿ~ÀÿnÂèÿ)‚Àÿ vºÿ…Âÿ xºÿ,‚ÿ +jÿiÄãÿZÇòÿO¾ïÿP¿ïÿP¿ïÿP¿ïÿP¿ïÿP¿ïÿP¿ïÿN¾ïÿO¿ðÿZÉõÿgÎôÿdµÐÿJ‚—ÿ0c|ÿ.s–ÿ8¼ÿ-šËÿ6¡ÒÿHªÙÿQ­ÚÿU¬ÙÿQ©ÖÿO§ÕÿrÃèÿªêþÿ¥éüÿ çüÿ³îýÿ²íýÿªêüÿ±êýÿ£âùÿÉéÿ\±Ùÿ?¦Óÿ9¢Ïÿ(šÉÿ#˜ÉÿC¨Òÿ|ÈåÿšÙïÿ¥Üðÿ¨Ýñÿ†Èæÿq¿âÿ^¦ÑÿW£Ðÿp²×ÿ>‘Äÿs¶ÿC—Êÿ@Âÿ,‰ÿ<—ÿvºÿWµáÿP®Ýÿ t¸ÿ_®ÿr·ÿL™ÿ C|ÿjÆäÿWÀëÿJ¶åÿL·æÿL·æÿL·æÿL·æÿL·æÿL·æÿL·æÿL·æÿJ¶æÿK¶æÿUÀíÿeËñÿaµÒÿJƒ˜ÿDl€ÿB|™ÿD—¾ÿZ®×ÿv¾âÿmºâÿe¶ßÿnºâÿšÜöÿ²ëýÿ›âùÿœâùÿ°ìýÿ®ëýÿ¶îýÿ¸îþÿµìþÿ´ëýÿªâøÿŒÑíÿ|ÈçÿzÆæÿ–Õîÿ«âõÿ³åöÿ£Ýòÿ›ØîÿÉæÿt¼ÝÿzÁáÿ_¨ÒÿbªÔÿa«Ôÿ/ˆÀÿ q´ÿ=–Èÿ(€ºÿ3ÿBœÿ r¸ÿK¥Øÿ}ÍïÿN“ÊÿE¡ÿw»ÿ j¬ÿaÿkÆåÿS·âÿE«ÚÿG­ÛÿG­ÛÿG­ÛÿG­ÛÿG­ÛÿG­ÛÿG­ÛÿG­ÛÿG­ÛÿG¬ÛÿF¬ÚÿF¬ÛÿQ¸äÿdÈîÿe¸ÔÿN‡›ÿ@kÿW…›ÿoªÆÿt¹Üÿnºâÿc¸âÿ•Úõÿžâ÷ÿ™ß÷ÿ§åûÿ¥åúÿ®êýÿ³ëýÿ·ëýÿ¬êýÿ«êýÿ¨êýÿ¹îþÿÂðÿÿ»îþÿµëûÿ®å÷ÿ²ãõÿžØïÿŽÍçÿŒÇãÿlµØÿj¶ÙÿEÌÿHÌÿV¥Ñÿ|ºÿo²ÿ.Ãÿm¯ÿ=—ÿP¤ÿ n¶ÿ%ÉÿS±àÿoÂéÿ6Èÿ`­ÿ j¬ÿ!p˜ÿlÈæÿO¯ÚÿA ÎÿC¢ÐÿC¢ÐÿC¢ÐÿC¢ÐÿC¢ÐÿC¢ÐÿC¢ÐÿC¢ÐÿC¢ÐÿC¢ÐÿC£ÐÿC¢ÐÿA¡ÏÿA¡ÏÿL®ÚÿaÄéÿfº×ÿSŒ ÿOrƒÿjŒ ÿp§Æÿ`±Ûÿ‹Òïÿ‘Øóÿ—Ûõÿ²éüÿ¥ãøÿ¥åúÿ¯èûÿ¯èüÿ¢æúÿ˜ãúÿŠÜøÿ“áùÿ¬éüÿ®èûÿ§ã÷ÿ®ãöÿªÞòÿ Ôëÿ©ÕêÿžÏçÿa®Óÿb®Òÿ1”ÈÿAšËÿ@—Éÿe­ÿ x·ÿ|¹ÿMŸÿA›ÿYªÿh³ÿr¼ÿ(“Îÿj¾èÿvÊîÿGÄÿ >Œÿ HÿkÈçÿK¨Ñÿ<—Ãÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ>™Åÿ=—Äÿ<–ÄÿG¤Ðÿ]¾äÿf»Ùÿ[’¥ÿCm€ÿ>w•ÿy±Êÿ‚Æãÿ•Øñÿ¡Þõÿ™Øòÿ›Üõÿ›Ûôÿ™Ýõÿ”Ýõÿ†ÕóÿrËðÿvÏòÿŒÙõÿšÞõÿžÝôÿœÙñÿÕïÿ©Öìÿ«Øêÿ–Êäÿg­ÐÿV£ÇÿD›ÍÿHÎÿ!ƒ½ÿY§ÿi°ÿaªÿ>•ÿM¡ÿ^¬ÿg²ÿj¶ÿ }Âÿ*™ÒÿJ®ßÿ^¾éÿP Îÿ V†ÿiÅåÿGŸÈÿ8Œ¸ÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ:ºÿ8¹ÿ7‹¸ÿBšÅÿZ¹ßÿ_¸ØÿG‹¤ÿCo‚ÿQ—ÿz°Èÿ•Íåÿ–ÓîÿÑïÿÔðÿŒÓðÿ’ØòÿˆÒðÿpÇíÿkÆíÿtÉíÿ…Ñîÿ–ÕïÿŠÍêÿËèÿÐèÿŸÐæÿ£Ðæÿf©Íÿ$²ÿBšÍÿ<–Ëÿj±ÿZ¨ÿ_¬ÿi¥ÕÿHŠÄÿ V¤ÿX¨ÿd¯ÿh³ÿo»ÿ{Âÿ‘Îÿ;¦Ûÿ@¤Óÿ3„§ÿlÈçÿC–¿ÿ3‚¬ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ5…¯ÿ4ƒ­ÿ2¬ÿ>ºÿY³Ùÿd¼ÚÿP’©ÿPv…ÿd‡˜ÿ{¬ÄÿŠÃàÿ‡Êéÿ€Éêÿ†Îìÿ‰ÐíÿwÈêÿƒÍìÿÐíÿ€Çèÿ{Ääÿ‚ÄãÿÈäÿ–ËãÿœÌâÿ›Ëâÿa§Ëÿ uªÿžÒíÿ€Àãÿ;‰Åÿ$l³ÿ]¨ÿ‡¿åÿŒËìÿR›Ïÿc®ÿ!{Àÿ~Ãÿ"†Èÿ?šÓÿJ¦ÚÿP­ßÿS§ÔÿF‡ªÿlÉèÿ>޵ÿ.x¡ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1z¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ1{¤ÿ/x¢ÿ-v ÿ8…¯ÿT¬Òÿd¼ÛÿY™®ÿQu…ÿfŠžÿz³Ðÿ€Ããÿ†Èèÿ†Èèÿ…ÈçÿŽËéÿÍêÿ†Çæÿu½ßÿ‚Ãâÿ ÏæÿÅßÿ‹ÄÞÿ‰ÂÜÿ{µÔÿ^¤Æÿ©ÜòÿšÙòÿ—ÖòÿÎíÿkµÞÿfµáÿÆìÿn½êÿ]¯ãÿaµçÿR¯âÿÎïÿ”ÐðÿuÁéÿtÁéÿxºÝÿj›²ÿmÉèÿ:…¬ÿ)m–ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ-n–ÿ-o—ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ+o—ÿ(k•ÿ2z£ÿP¥Ëÿc»ÚÿZœ´ÿ`•®ÿr´Öÿx¼àÿs¹ÞÿwºÝÿ†Ââÿ„Ããÿ„ÃãÿˆÃãÿ†Âàÿ›Ëäÿ¤Îäÿ”ÅÞÿ•ÄÝÿz³Ñÿt®Ìÿ­ÝòÿšÙòÿ–Õñÿ…ÍðÿyÊðÿkÂìÿm¾ìÿj»êÿe·éÿN«âÿqÂêÿ”ÔñÿÑïÿÉìÿƒÊîÿ‚Ààÿa™±ÿmÉèÿ6|¢ÿ$b‹ÿ'fÿ'fÿ'fÿ'fÿ'fÿ'fŽÿ&j’ÿ}¦ÿ$m–ÿ(d‹ÿ(dŒÿ(fŽÿ'gÿ'fÿ'fÿ'fÿ'fÿ'fÿ'fÿ'fÿ'fÿ'fÿ(gÿ(gÿ&dÿ"^‡ÿ8€¦ÿoÐðÿY‘«ÿe§Ëÿc¯Öÿk°Õÿe¬Óÿ}¸ÚÿrµÙÿ‡ÁáÿŽÇåÿŠÅäÿÆâÿÍäÿšÇàÿÀÚÿa£Äÿe¦Åÿ¬ÜòÿžØóÿ‘Óòÿ…ÎðÿmÃíÿV¶èÿR¯åÿL­âÿFªàÿc¼éÿ‡ÒðÿÔðÿ•Ôñÿ‹Íîÿ€Éíÿ~¾Þÿf›²ÿmÉèÿ1s™ÿ Y€ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#\‚ÿ!b‰ÿ•¿ÿ•¾ÿƒ«ÿk“ÿ"[‚ÿ$Xÿ#\‚ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ#]„ÿ Y€ÿ0q—ÿlÉèÿS‰¥ÿSžÄÿZ¨ÏÿV£ÌÿNŸÉÿa§Îÿb¨Ïÿj¬Ñÿ‚ÀßÿŒÆäÿ”ÊæÿŠÁßÿu³ÕÿY ÆÿM—¾ÿ]ŸÀÿ¦ÜõÿˆÏðÿkÁìÿuÅïÿxÅîÿa·èÿc´æÿW®âÿ;¦Ýÿ{Ìïÿ—ØóÿŠÒïÿ•Õñÿ”Óïÿ„Íîÿv½Þÿ^˜±ÿlÉèÿ-jÿOuÿSyÿSyÿSyÿSyÿSyÿQwÿY€ÿ’»ÿ:™Àÿ6™Áÿ"—¿ÿˆ±ÿm•ÿV}ÿNtÿPvÿSyÿSyÿSyÿSyÿSyÿSyÿSyÿSyÿSyÿNuÿ.l‘ÿmÉèÿM†¢ÿI–¿ÿD™ÆÿA–Ãÿ;’Âÿ(‰¼ÿ3ÀÿMžÊÿc«Óÿf¬Ôÿ^«ÓÿJžÌÿE—ÆÿM“Àÿ\œÂÿY›½ÿ¥Üõÿ”Öóÿp¿ëÿ]µçÿo¼éÿ[µæÿT°äÿLªàÿI¯ãÿ…Ôòÿ•Úóÿ‡Óñÿ…Ñðÿ‚ÌíÿwÇíÿ[³ÛÿI¯ÿlÉèÿ)a…ÿDjÿInÿInÿInÿInÿInÿGlÿRwÿ‘»ÿ9“¹ÿB‘¶ÿ@’·ÿ8”ºÿ(–¾ÿ¶ÿs›ÿUzÿFjÿHmÿInÿInÿInÿInÿInÿInÿInÿDiÿ,fŠÿmÉèÿ?~œÿB‘¼ÿ7ŽÀÿ#„¹ÿ}¶ÿ)Ãÿb®ÔÿQŸÊÿ(…»ÿ'ˆ½ÿ>•ÆÿG™ÇÿW ÈÿA޼ÿ?Œ¸ÿO”»ÿªàøÿ¢Ü÷ÿšÕõÿ„Çïÿj¼êÿe¸èÿZ²åÿL«áÿB®âÿ}ÓóÿÛôÿ…ÓòÿyÌïÿeÄíÿZ¾êÿL®ØÿI’°ÿkÇçÿ%W{ÿ:^ÿ?cÿ?cÿ?cÿ?cÿ?cÿ=`ÿJoÿ»ÿ6³ÿ<‹¯ÿ<‹¯ÿ=‹¯ÿ>‹¯ÿ:²ÿ,“¹ÿ’»ÿršÿ?cÿ>bÿ?cÿ?cÿ?cÿ?cÿ?cÿ?cÿ9]ÿ*`„ÿlÉèÿ1t—ÿ&€²ÿ~¶ÿx°ÿo¬ÿN§Ñÿz¼Úÿb¦Ëÿu¯ÿp¬ÿv°ÿ5мÿ?¹ÿ-°ÿ)€°ÿ&±ÿ«àøÿ¢Üøÿ‰ÏóÿÆîÿl¼ëÿd¸éÿa¶çÿF¬âÿ=¬áÿyÑòÿ†ØõÿuÑòÿkËñÿdÆïÿbÆðÿV³ÜÿI’²ÿhÀåÿ Mpÿ0Sÿ5Xÿ5Xÿ5Xÿ5Xÿ5Xÿ2UÿBgÿ¼ÿ5ˆ¬ÿ:…§ÿ9…¨ÿ9…¨ÿ9…¨ÿ:…¨ÿ=ƒ¦ÿ0´ÿ…²ÿ6Zÿ4Wÿ5Xÿ5Xÿ5Xÿ5Xÿ5Xÿ5Xÿ .Rÿ(Z}ÿlÇèÿ$iÿq©ÿmªÿe¥ÿ`¢ÿ]®Õÿx»Ùÿ]¥Èÿ6Šºÿ _ŸÿN‘ÿp¨ÿy¬ÿ%}¯ÿ$~²ÿ9޽ÿ¢ßøÿ¢ÝøÿŒÑóÿ†ËñÿÆðÿm¿íÿbºêÿI¯ãÿ7©àÿwÏòÿ‰Û÷ÿ|ÕõÿpÐôÿ`ÊñÿkÎõÿnÂãÿZš¶ÿd·ãÿBfÿ &Hÿ +Mÿ +Mÿ +Mÿ +Mÿ +Mÿ (Iÿ :_ÿмÿ3‚¦ÿ7 ÿ6¡ÿ6¡ÿ6¡ÿ6¡ÿ8~Ÿÿ,ˆ°ÿyªÿ *Lÿ *Lÿ +Mÿ +Mÿ +Mÿ +Mÿ +Mÿ +Mÿ$Fÿ%SwÿgÀçÿ QÿA‰ÿ;ŠÿDŽÿf¢ÿ„Éæÿt¸×ÿPÅÿ0‰·ÿ}±ÿ k§ÿ l¦ÿx­ÿB–ÀÿMÄÿk®Îÿ¤Þøÿ Ü÷ÿ”×öÿ€Ìóÿ‹ÏóÿyÆðÿ^¹ëÿ<ªáÿœÚÿvÎñÿ˜åûÿƒÛùÿ†ÜùÿyØùÿ…Ýüÿ‚Ðéÿe¡¹ÿ`¯áÿ6[ÿ=ÿ!Bÿ!Bÿ!Bÿ!Bÿ!Bÿ>ÿ 2Wÿ…½ÿ1|Ÿÿ4y™ÿ3yšÿ3yšÿ3yšÿ3yšÿ5x˜ÿ*„¯ÿq¥ÿ?ÿ Bÿ!Bÿ!Bÿ!Bÿ!Bÿ!Bÿ!Bÿ:ÿ"Lqÿb¸æÿ +lÿ Aÿ3ÿ$lÿ^£ÍÿÑêÿa®ÑÿIœÆÿ2‹ºÿ.†¶ÿ}³ÿx°ÿ/޽ÿw¹×ÿx·Óÿi°ÏÿšÔóÿ”ÔôÿšÚøÿ~ÍóÿˆÍóÿÉñÿ_¹êÿ.£ÝÿžÜÿ†Ûøÿ¢ëýÿ”æüÿŽäüÿ“åüÿåþÿŒ×ëÿe¢ºÿ[¦Þÿ+Oÿ2ÿ7ÿ7ÿ7ÿ7ÿ7ÿ2ÿ*Pÿ‚½ÿ/v˜ÿ1s’ÿ0s“ÿ0s“ÿ0s“ÿ0s“ÿ1rÿ(€®ÿgžÿ2ÿ7ÿ7ÿ7ÿ7ÿ7ÿ7ÿ7ÿ/ÿ Dkÿ`°ãÿ%k”ÿ,‚±ÿ ³ÿGœÈÿ—×ðÿwÀßÿYªÑÿJŸÈÿ9“Âÿ¶ÿ~´ÿ}³ÿYªÏÿÃÝÿƒ»×ÿj±ÑÿÎñÿ’Òôÿ‡ÍñÿÈðÿ‘Ñôÿ„ÎóÿfËôÿZÃïÿmÏóÿªîþÿŸìüÿ–êüÿ—êüÿ”èüÿ“éþÿŠØêÿe¡ºÿXžÜÿ $Hÿ +ÿ0ÿ0ÿ0ÿ0ÿ0ÿ +ÿ%Lÿ~¾ÿ-o‘ÿ.l‹ÿ.lŒÿ.lŒÿ.lŒÿ.lŒÿ/kˆÿ&}­ÿ^—ÿ *ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ'ÿ@iÿ^©àÿ9~žÿ@žËÿi¿ãÿ¤àõÿ˜Õíÿp»ÝÿXªÑÿJ Éÿ@™Æÿ·ÿ}³ÿ†¹ÿq¶Öÿ‹ÀÜÿs¶Ôÿo³ÒÿŸÙõÿ„ÏòÿzÉñÿ×öÿ¢Ùöÿ—Öõÿ{Óøÿ{Ýýÿ ëþÿ«ðýÿ¡îýÿçüÿŽèüÿ–éüÿšëþÿ‡Õêÿ]ºÿU–Ùÿ !Fÿ +ÿ0ÿ0ÿ0ÿ0ÿ0ÿ +ÿ%Mÿ{¿ÿ*iŠÿ+g„ÿ+g…ÿ+g…ÿ+g…ÿ+g…ÿ,fÿ%y­ÿW’ÿ )ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ'ÿ@lÿZ Ýÿ;¡ÿf¹Üÿ âöÿ¬âôÿÐêÿk¸Ûÿ[¬Òÿ=˜Æÿ,Àÿ!‡¼ÿ¶ÿ3—Äÿ†ÁÝÿƒ½Ùÿd¯Ñÿ^®ÐÿªÛõÿšÖôÿh¿íÿ„Èïÿ¢Øõÿ£Ýøÿ“Õõÿ€Ô÷ÿœãûÿ•áûÿ‡ÞûÿÞüÿ†ßüÿâüÿ”åþÿ„Òéÿ\˜¹ÿPŽ×ÿ Eÿ +ÿ0ÿ0ÿ0ÿ0ÿ0ÿ *ÿ&Oÿw¿ÿ(cƒÿ(a}ÿ(a~ÿ(a~ÿ(a~ÿ(a~ÿ)`zÿ$t­ÿPÿ (ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ 'ÿ@nÿV™ÛÿHŠ«ÿ‹Îëÿªâõÿ§ÜñÿÏêÿm·Ûÿ[«Óÿ8•Äÿ.ŽÀÿ*Ž¿ÿ„ºÿ/•Äÿ‡ÁÝÿx¸Öÿh®ÏÿG Êÿ ×õÿ×óÿ“ÒñÿqÅîÿ|Èïÿ™ÕóÿÐñÿc»êÿÎóÿÙöÿˆ×÷ÿŒØ÷ÿÓöÿ…Öøÿ‚ÔúÿsÃãÿSµÿM†Öÿ Dÿ ,ÿ0ÿ0ÿ0ÿ0ÿ0ÿ *ÿ&Rÿ s¿ÿ&]|ÿ&[vÿ&[wÿ&[wÿ&[wÿ&[wÿ&Zsÿ#q¯ÿJ‡ÿ 'ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ &ÿ@qÿQ‘ÙÿX–²ÿ˜Ùíÿ²ç÷ÿ¢Úñÿ†Ééÿk·ÛÿX«Óÿ>˜Çÿ,ŽÁÿ,Àÿ ~·ÿ8™Æÿ†ÁÞÿv¶Öÿi°Òÿh°Óÿ…Ëóÿš×õÿžØõÿŽÑòÿ€Íðÿ‡ÏòÿzÆíÿL«ßÿW¯âÿm¿êÿzÈðÿ“Ñóÿ„Êðÿ^ºéÿ;ªâÿÇÿ%q¦ÿL~ÔÿAÿ ,ÿ 0ÿ 0ÿ 0ÿ 0ÿ 0ÿ*ÿ$Tÿ!p¿ÿ#Vuÿ#Toÿ#Tpÿ#Tpÿ#Tpÿ#Tpÿ#Slÿ#n°ÿ Bÿ&ÿ 0ÿ 0ÿ 0ÿ 0ÿ 0ÿ 0ÿ 0ÿ&ÿÿ{Z.ÿzZ1ÿzZ1ÿzZ1ÿzZ1ÿzZ1ÿ~Z*ÿc]^ÿ \½ÿ=Vÿ•Èÿ2ŽÄÿ.‰Âÿ=”Çÿ^«Õÿb¬Öÿi¯×ÿi¯ØÿQ§Óÿ.‡²ÿ0j¤ÿPwÈÿfJ,ÿgG"ÿgH%ÿgH%ÿgH%ÿgG%ÿgG%ÿjFÿUM^ÿ"N¹ÿ*?ÿ*=ÿ*>ÿ*>ÿ*>ÿ)<ÿ,Fÿ%R¿ÿYLNÿiFÿfG%ÿfG%ÿfG%ÿfG%ÿfG%ÿfG%ÿfG%ÿgDÿXh“ÿDvÍÿS¨ÿz»Üÿr·Ûÿi³ÙÿfµÞÿd²ÛÿqµÚÿ[®ÖÿW«ÓÿVªÒÿT¨Òÿ0™Èÿ4•Åÿ3•ÅÿÇãÿ”Îåÿ[®Ûÿh³ÜÿK¤ÓÿS¨ÕÿI¡ÐÿA›Ëÿ@—Êÿ1Äÿ%…¿ÿS¢Ðÿ[ªÔÿW§ÓÿR¦ÒÿI Ïÿ(Çÿs¦ÿ&^ ÿOqÄÿ`C%ÿa@ÿaA!ÿaA!ÿaA!ÿaA!ÿaA!ÿd@ÿPH^ÿ"I·ÿ $7ÿ $6ÿ $7ÿ $7ÿ $7ÿ #4ÿ'Bÿ(OÀÿVEEÿb@ÿ`A!ÿ`A!ÿ`A!ÿ`A!ÿ`A!ÿ`A ÿ`A ÿa>ÿSc”ÿ>pÊÿV§ÿo±Õÿa¬ÔÿU©ÔÿJªÙÿl¶ßÿm·ÞÿY¬Öÿc¯ÖÿQ§ÐÿJ¥ÏÿT§Ïÿ;™Çÿ+ÀÿnºÛÿ•ÐæÿJ£ÕÿR§ÖÿM§ÖÿF¡ÑÿL¡ÐÿN¡Ðÿ?˜Ëÿ*ŠÃÿ6’ÇÿCŸÑÿE¢ÒÿD¢Óÿ? Óÿ1–Ìÿ+‘Ëÿ<‹´ÿ7jªÿKj¾ÿ[<ÿ[:ÿZ;ÿZ;ÿZ;ÿZ;ÿZ;ÿ]:ÿLC_ÿ#Eµÿ /ÿ /ÿ 0ÿ 0ÿ 0ÿ -ÿ #Aÿ*LÁÿS>;ÿ[9ÿZ:ÿZ:ÿZ:ÿZ:ÿZ:ÿZ:ÿZ:ÿ[7ÿP^”ÿ=lÇÿG…¤ÿP£Íÿ\©ÑÿM¤Ðÿ@ŸÎÿ?žÍÿRªÕÿiµÛÿs¸Ûÿt·Úÿ|»ÜÿV¦Ïÿ.‘Ãÿ#‰Àÿ?Âÿ•Ïçÿ>œÒÿI ÓÿO¥ÕÿAžÑÿ?›ÏÿK¡Òÿ9”Êÿ „Àÿ9˜ÍÿF£ÔÿK¦×ÿT«ÚÿL§×ÿE Ôÿ_®ÜÿN•ºÿ3i­ÿIc¸ÿT5ÿT4ÿT5ÿT5ÿT5ÿT5ÿT5ÿV3ÿG>`ÿ#@²ÿ'ÿ(ÿ)ÿ)ÿ)ÿ$ÿ @ÿ-HÁÿO82ÿU3ÿT4ÿT4ÿT4ÿT4ÿT4ÿT4ÿT4ÿU1 ÿMZ•ÿ2dÂÿm–ÿ6“ÄÿT£Îÿ\©ÒÿP¡ËÿFœÈÿV¦Îÿt¶Úÿ½ÝÿŽÄâÿx»Üÿl³Öÿ6•Æÿc¥ÿUžÿ[«Õÿ*Ëÿ3“ÎÿK¢ÖÿH£ÕÿF Óÿ;˜Îÿ)ŒÆÿ!‰ÄÿB¡ÔÿI£ÕÿS§×ÿQ©Ùÿd²Ýÿa°Üÿ]¯ÜÿK–ºÿ9k±ÿE\³ÿN.ÿN.ÿN.ÿN.ÿN.ÿN.ÿN.ÿP- ÿD9aÿ#<°ÿÿ!ÿ"ÿ"ÿÿ ÿ <ÿ.FÈÿK1)ÿO-ÿN.ÿN.ÿN.ÿN.ÿN.ÿN.ÿN.ÿN+ ÿIU—ÿ0a»ÿl–ÿ8’ÄÿKŸÌÿV¤ÎÿHœÈÿIÈÿW¦Îÿa¬Òÿk³×ÿ{¼Ýÿo¶ÚÿG¤Ðÿt±ÿT˜ÿc§ÿ;¡Òÿ†Éÿ1”Ðÿ7˜Ñÿ7šÒÿCžÓÿ3•Íÿ#‹ÅÿE¡ÓÿZ®ÛÿE£Ôÿ:›ÐÿH¤ÖÿN©ÙÿX®ÛÿZ­ÛÿI•¹ÿ7i³ÿCV®ÿH' ÿG(ÿG(ÿG(ÿG(ÿG(ÿG(ÿI&ÿ@4bÿ#6®ÿ ÿ ÿÿ ÿ3ÿ"iÿ$5¡ÿ6<žÿF)ÿG' ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG$ÿEP—ÿ0a¸ÿl—ÿ&‡¾ÿ4’ÄÿB–Åÿ7’ÂÿGÈÿHžÊÿ]ªÐÿn´×ÿe²×ÿI¡Ïÿo°ÿ[¡ÿZžÿŽÆÿ:§ÖÿÍÿ6™Ñÿ6—Ñÿ:šÒÿAÒÿ8—Íÿ7–Íÿ`´àÿi¸áÿI¨×ÿI¦×ÿF¤×ÿG§×ÿD¦Øÿ>¡Õÿ2‹²ÿ1d´ÿ@O¨ÿB ÿB" ÿB" ÿB" ÿB" ÿB" ÿB" ÿCÿ=0cÿ"2¨ÿÿ -ÿeÿ!/–ÿ06ÿ;3tÿ@)6ÿA! ÿA! ÿA! ÿA! ÿA! ÿA! ÿA! ÿA! ÿA! ÿA! ÿAÿ–ËÿˆÍêÿàöÿŠÕòÿ|ËìÿlÂéÿQ´àÿ/¢Õÿe·âÿ\¯ÝÿZ¬ÚÿYªØÿH¡ÒÿBÎÿB›Íÿ?˜Éÿ=•Çÿ#‡¿ÿm¯ÿ]¤ÿ€ºÿR¨Ôÿf¸àÿe¨Àÿ;_¿ÿ34‹ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ/ÿ-3ÿ.+zÿ:K·ÿ\xÔÿŠ©×ÿ³ÖäÿËèôÿÈåóÿ¾ãóÿŠÅäÿIÏÿ[©ÖÿB¡ÑÿA¢ÒÿsÀåÿŒÎìÿ…Ïíÿ~ÊëÿsÂéÿnÀèÿQ´áÿ2¤×ÿY²àÿm¸áÿh³Þÿl²Üÿ`®ÙÿR¨ÕÿT§ÓÿK¢Ïÿ;™Ëÿ'Äÿ s³ÿdªÿ2“ÆÿX«ÕÿQ¤Ñÿ0€¥ÿ+Qºÿ30†ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ.#ÿ-"fÿ3=©ÿGgÐÿj–Ûÿ›Äßÿ²ßèÿ¸êõÿÆîûÿÏìùÿÊèõÿÎèõÿ¿âóÿg®Ùÿa®Ùÿg´Üÿ]²Üÿh¸àÿ‚ÇéÿˆÌìÿuÀçÿ}Çêÿ†Îîÿd¼åÿ<¦Ùÿmºãÿeµáÿc´àÿt¸àÿv¹ßÿe²Ûÿg°Øÿd®ÖÿE¡Ïÿ‹Áÿb©ÿ t´ÿBžÎÿYªÕÿO¢Îÿ+€¥ÿ&L»ÿ2+€ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ.Sÿ/1˜ÿšÌÿm²ÿ)Èÿ;žÑÿL¬Úÿ]·ãÿqÄêÿœÚôÿµêüÿ¦áøÿC¦Øÿ rÃÿˆÄÿBªÓÿ`¸âÿ_¸åÿK²äÿP´äÿW¶äÿTµãÿZµäÿM±ãÿS¶æÿqÃìÿpÅíÿˆÒòÿ˜Ü÷ÿŸâúÿ°ëýÿ³íýÿÈöýÿÙüþÿ²çûÿU³äÿQ²âÿU´âÿZµãÿ{Æêÿ–Ñîÿ‰Êêÿ‡Æèÿ—Íìÿ–ÌêÿŽÇèÿ–Æéÿ•Ãçÿl°ßÿY Ôÿ>–ÒÿO«ãÿuÀéÿr¶ÝÿJŸÌÿJ›Åÿw¬ÿ*oœÿl£ÿL|œÿ/y£ÿS©ÖÿX¬×ÿ\­×ÿ]®ÙÿN©×ÿw³ÿk°ÿ>ŸÒÿL©Úÿ]¶âÿwÇìÿ¡áøÿ’ÛöÿP¶åÿ"œÛÿ–Øÿš×ÿŸÜÿ]»éÿmÀéÿd½èÿY¸æÿX·æÿb»çÿaºçÿV¶äÿN²äÿ^ºèÿtÅìÿuÆíÿ„Îðÿ‡Ñòÿ™Û÷ÿ¨åûÿ¼íýÿÄñþÿÄòÿÿŒÒóÿU±ãÿc·åÿc¶ãÿl½çÿ–ÓñÿªÛòÿÓïÿÑìÿ”Ìéÿ›Ïêÿ›ÌéÿÂæÿ‚»áÿY¦×ÿ0ƒÃÿ.’Ôÿk»éÿ~¿åÿz¹Úÿo±ÔÿGšÂÿ1‡´ÿÈÿ =‘ÿG›ÿr·ÿ7ŒÂÿH‹­ÿgËïÿM¿ñÿQÃôÿ_Îúÿ`¿âÿ=‚¢ÿ"c„ÿ?€¢ÿ‚¾Úÿ©ÞôÿÙôÿŒÎíÿ}ÉëÿÎïÿvÁæÿ9”Éÿ»ÿ~¹ÿ(…¾ÿv±ÿZŸÿXžÿZ›ÿV’ÿ=—Äÿ_±×ÿW®ÔÿV®ÖÿLªÓÿI©ÒÿA¤ÎÿA¢Ìÿ\°ÕÿmºÞÿf¸Þÿ>™Îÿ-ŠÄÿz¹ÿ p³ÿ2ŽÄÿN›ÿ:”ÿEžÔÿV¢ÿg°ÿ1Èÿ=‰¾ÿ)aÿiÍðÿOÀñÿPÀðÿN¿ðÿQÃôÿ^Îúÿ`ÁäÿQ˜²ÿI}‘ÿ_“©ÿk³ÓÿˆÏïÿbºåÿQ®Þÿ@ ÑÿEœÌÿD˜Êÿ;‘ÅÿB•Çÿ]±ÛÿƒÑïÿ‚Ðïÿ}ÄèÿAÂÿFŸÌÿS¬ÕÿKªÔÿN«ÕÿF§Òÿ:¢Îÿ)™ÇÿH©Òÿ–Óêÿ“Ðëÿc¸ßÿO ÎÿE˜ËÿI–Èÿs´ÿ(ˆÀÿ C•ÿ:”ÿI¦Øÿ1†Áÿ vºÿ‹ÅÿG“ÿF€ÿkÑóÿOÀðÿPÀðÿPÀðÿPÀðÿN¿ðÿPÂóÿZÊøÿ_ÂçÿU›µÿJ”ÿC‚Ÿÿ0Œºÿ,šÎÿ9£ÕÿG§×ÿO§ÔÿM£ÐÿP¦ÓÿÙõÿ¨íÿÿªíÿÿ¸ñÿÿ¦çüÿ’×òÿl»àÿJ©Ôÿ>¥Ñÿ/Ëÿ!—Èÿ;£Ïÿ€Êçÿ§ÞðÿžÖíÿq¾ãÿZ¤ÐÿbªÔÿNšÉÿx¸ÿD—Éÿ=‘ÿ<—ÿ6˜ÐÿW³àÿ q¶ÿkµÿM™ÿ [‘ÿjÏñÿK·çÿL¸çÿL¸çÿL¸çÿL¸çÿL¸çÿK·çÿK¹éÿVÂïÿ^¿ãÿQšµÿF{‘ÿ>{˜ÿA–¿ÿdµÝÿlºâÿ`³ÝÿyÄèÿ­éýÿœãùÿ¦çúÿ°ìýÿµîýÿ¸îþÿ°éûÿ–ÖðÿvÄåÿk¾àÿ€Éçÿ¤Þòÿ¨àòÿšÖìÿƒÄâÿv¾àÿZ¦ÑÿaªÓÿ9Äÿw¶ÿ6‘ÄÿB—ÿGžÿ#‡ÅÿlÃéÿGŽÉÿ[­ÿl®ÿ/§ÿgÈëÿE©×ÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿFªØÿD©ØÿE«ÚÿQ·äÿ]»ßÿZ¡»ÿK~’ÿ\ˆžÿr«Éÿl·ßÿxÇìÿáøÿšßöÿ¨åúÿ©çûÿµëýÿ°êýÿ§êüÿ§éýÿ¹îþÿ¼îþÿµêûÿ´æöÿ£Ùïÿ–Îçÿ~¾Ýÿe²Öÿ9—ÉÿJŸÌÿ{¹ÿw·ÿt´ÿCšÿV§ÿ r¹ÿ1™ÑÿrÅìÿD˜ÎÿL—ÿ't¡ÿfÅçÿ>›Éÿ@Êÿ@Êÿ@Êÿ@Êÿ@Êÿ@Êÿ@Êÿ@Êÿ@Êÿ>›Éÿ?ÊÿJªÖÿZ·Ûÿ[£¼ÿ^‰šÿR‚œÿf©Éÿ‹ÐìÿÞ÷ÿ¥à÷ÿžÞöÿ¤á÷ÿ¡âøÿÜöÿzÒóÿŽÜ÷ÿ¤ãøÿ£àõÿ¤Üòÿ¤Õìÿ¬×êÿ¾Üÿ[¨Ìÿ=—Êÿ9”Çÿc¬ÿl±ÿS¢ÿCšÿ_¬ÿg³ÿ zÀÿ8¡×ÿ[ºæÿCÂÿ0oÿa¿âÿ8¹ÿ:»ÿ:»ÿ:»ÿ:»ÿ:»ÿ:»ÿ:»ÿ:»ÿ:»ÿ:»ÿ:»ÿ8Žºÿ8ŽºÿCÈÿV±ÕÿOºÿI‚˜ÿW‡ÿµÌÿ—ÑëÿÓðÿŽÓðÿÖñÿ‰ÒðÿmÆíÿpÈîÿ„Ðîÿ“Ôîÿ‹Ìéÿ›Ðèÿ£Òçÿ‹ÁÜÿ2Š·ÿY¨Õÿ-‡ÂÿU¥ÿ i±ÿq«Øÿ(q´ÿV¦ÿf±ÿm¹ÿ Äÿ0ÖÿA¤ÔÿE™ºÿ^¸Ûÿ1ªÿ4‚­ÿ4‚­ÿ4‚­ÿ4‚­ÿ4‚­ÿ4‚¬ÿ4‚­ÿ4‚­ÿ4‚­ÿ4‚­ÿ4‚­ÿ4‚­ÿ4‚­ÿ2€«ÿ1ªÿ?’¼ÿR©ÍÿV£¾ÿYŠœÿe‹žÿ~®Èÿ…Çæÿ‚ÊêÿŠÏìÿ|ÉêÿŒÎìÿ…ÉèÿyÁâÿŠÆãÿ”Éãÿ–Éáÿˆ¿Úÿ-‡¶ÿ§Úñÿ…Æèÿe¦ÕÿM’Èÿ„Æêÿn¸ãÿ?‘Îÿ>œ×ÿU©Ûÿq¹äÿc¶äÿc¯Øÿb¥ÁÿY²Õÿ+qšÿ.užÿ.užÿ.užÿ.užÿ.užÿ.s›ÿ.sœÿ.užÿ.užÿ.užÿ.užÿ.užÿ.užÿ.užÿ.užÿ,sœÿ*r›ÿ7ƒ¬ÿN¢ÆÿY£¾ÿY‹ ÿm¥Áÿ}Àâÿ}ÀãÿÁâÿŒÇæÿ†Åäÿ~¿àÿ‘ÇâÿœËâÿÄÝÿˆ½Ùÿq­Ìÿ§ÛñÿšØòÿÔòÿwÉïÿg¼éÿl¼ëÿc·éÿ[µæÿ’ÔñÿÑïÿ€Éíÿ‚Àáÿk«ÄÿV¬Ïÿ%c‹ÿ(gÿ(gÿ(gÿ(gÿ'gÿ"w ÿ#n–ÿ'dŒÿ(eŒÿ(gÿ(gÿ(gÿ(gÿ(gÿ(gÿ(gÿ(gÿ&eÿ#b‹ÿ1wŸÿc½Ýÿ]˜´ÿf­Óÿh°Öÿl°Õÿ{¸Úÿ‚¾ÞÿŽÇäÿÆãÿžÌäÿœÈßÿz³Ñÿd¤Äÿ¨ÛòÿŽÒñÿ„ÍðÿlÂìÿX³æÿR®âÿEªàÿzÉîÿÔðÿ”ÓðÿˆÍîÿ~¿ßÿk¬ÅÿS¦ÉÿU|ÿ"Zÿ"Zÿ"Zÿ"Zÿ"Xÿ‰²ÿ"–¿ÿ©ÿgÿ Xÿ"V|ÿ"Y€ÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ"Zÿ!Y€ÿ!Zÿ^³ÓÿT’¯ÿT¡ÉÿT¢ËÿLÇÿQžÉÿ]¥Íÿ½ÝÿˆÃâÿ~»Üÿd¨ÎÿSšÁÿYœ¾ÿ¢ÛôÿzÇíÿc¹éÿm½êÿ\³åÿT¬áÿN²ãÿ‘ØóÿŒÔðÿÒïÿ‡Îïÿk¹Þÿ\§ÄÿQ¡ÂÿGlÿMrÿMrÿMrÿMrÿKqÿ„­ÿ:˜¾ÿ<—½ÿ*”¼ÿ„­ÿhÿPvÿInÿMsÿMrÿMrÿMrÿMrÿLqÿPvÿ^²ÑÿJ‹ªÿB”Âÿ2¾ÿ$…ºÿ1‘ÂÿNžÉÿ@•ÆÿDšÉÿF›ÊÿJ—ÅÿJ‘½ÿT˜¼ÿ¨ÞöÿšÖõÿÅîÿhºéÿ^µæÿLªáÿNµåÿŠÙôÿ‡ÕòÿuËïÿeÃìÿO¯ÙÿS¥ÅÿO™¼ÿ9]ÿ@dÿ@dÿ@dÿ?cÿ?cÿªÿ7µÿ>‹¯ÿ>Œ°ÿ;³ÿ.’¸ÿ‰±ÿ`‡ÿ=aÿ@dÿ@dÿ@dÿ@dÿ>bÿDhÿ]°Ïÿ6~¢ÿ%ƒ·ÿy±ÿ w±ÿe³×ÿgªÎÿv°ÿq­ÿ2‡ºÿAºÿ+€°ÿ+±ÿ©àøÿ˜Ööÿ…Êðÿo½ëÿd¸éÿJ®âÿG±ãÿƒÖôÿyÓóÿhÊðÿ`Æðÿ[·ßÿX¦ÈÿH³ÿ +Nÿ2Vÿ2Vÿ2Vÿ1Uÿ1Uÿ{¨ÿ4ˆ®ÿ9ƒ¥ÿ8ƒ¦ÿ8ƒ¦ÿ;‚¥ÿ2Ž´ÿržÿ.Pÿ2Vÿ2Vÿ2Vÿ2Vÿ0Sÿ9\ÿZªÌÿl—ÿf¨ÿ]¡ÿm©ÿw¾Ýÿd©Ëÿ-„µÿYšÿ f¢ÿy¬ÿ'€±ÿ?‘¾ÿ£ßøÿœÚöÿ„ÍòÿƒÉñÿk¿íÿF®ãÿ5§ßÿ‡Ùöÿ…ÛøÿvÔõÿoÓ÷ÿzÌéÿg­Ìÿ@~©ÿ>ÿ %Gÿ %Gÿ %Gÿ $Fÿ$Gÿu§ÿ2€¥ÿ5{œÿ4{ÿ4{ÿ5z›ÿ.…­ÿc’ÿ Aÿ %Gÿ %Gÿ %Gÿ %Gÿ#Eÿ-OÿT¢Êÿ 8tÿPÿ"iÿL‘¿ÿƒÈäÿPŸÆÿ0ˆ·ÿ|±ÿoªÿ,‰¸ÿe­Íÿj¯Ïÿ›Õôÿ™ØöÿƒÏóÿ‡ÌòÿrÂîÿ5¦àÿ.§ßÿ—äûÿ–æûÿŽãüÿäýÿ‹Ùíÿj®Ïÿ8oŸÿ.ÿ8ÿ8ÿ8ÿ7ÿ9ÿn¥ÿ/yÿ1s’ÿ1s“ÿ1s“ÿ2r‘ÿ+©ÿWˆÿ0ÿ8ÿ8ÿ8ÿ8ÿ5ÿ BÿQ˜Çÿ$a‘ÿd‘ÿ2{©ÿ‹Íéÿl¸ÚÿN Éÿ5¾ÿ´ÿ|³ÿ[ªÎÿÁÛÿp³Òÿ’Ñòÿ‰Ïòÿ†Ìòÿ•ÓôÿxÍóÿ`ÉòÿƒÚ÷ÿ«ðýÿ˜ëüÿ“éüÿ•êþÿÛíÿgªÎÿ3c˜ÿ&ÿ0ÿ0ÿ0ÿ/ÿ2ÿi¤ÿ-p“ÿ-k‰ÿ-kŠÿ-kŠÿ.j‡ÿ)y¦ÿ L€ÿ 'ÿ0ÿ0ÿ0ÿ0ÿ-ÿ;ÿOÄÿ<‰¬ÿ[¹àÿŸá÷ÿ‘Ñêÿb²ÖÿJ Êÿ3‘Áÿ}´ÿ‡ºÿy¹Øÿ»Øÿk²Òÿ¥ÛõÿzÉðÿ…Ëñÿ¥Úöÿ–×öÿÙúÿ¡éýÿšçüÿˆãüÿŠäüÿ˜éÿÿŒÙíÿ`£Íÿ0\•ÿ&ÿ0ÿ0ÿ0ÿ.ÿ3ÿf¥ÿ)hŠÿ)c€ÿ)cÿ)cÿ*b}ÿ&r£ÿ F|ÿ 'ÿ0ÿ0ÿ0ÿ0ÿ-ÿ<ÿJ‡ÃÿK‘´ÿ’×ðÿ¬âôÿ‹Ìçÿe²×ÿ?™Çÿ+¿ÿ†ºÿ*‘Áÿ…¿Üÿs´ÓÿS¦Ìÿ¢Ùõÿ–ÓòÿyÆîÿƒÌðÿšÖôÿpÂìÿƒÐóÿ‹Ø÷ÿŠÙøÿ‚Õ÷ÿ…ØûÿuÇæÿX—Éÿ+S’ÿ&ÿ0ÿ0ÿ0ÿ .ÿ4ÿb¦ÿ'`ÿ&[vÿ&[wÿ&[wÿ&Ysÿ%lŸÿ >xÿ'ÿ0ÿ0ÿ0ÿ0ÿ -ÿ=ÿEÂÿb¢Àÿ¦âôÿ¨Þòÿ‚Ææÿb±×ÿBœÉÿ,ŽÀÿ†»ÿ*‘Áÿ¾Ûÿp³Óÿc­ÑÿsÁìÿœÙöÿŠÏñÿƒÍðÿzÈîÿW±ãÿY¯áÿh»èÿ€Æíÿp¿êÿF®ãÿ‘Éÿ,yºÿ5V‘ÿ'ÿ2ÿ2ÿ2ÿ0ÿ7ÿ`§ÿ#Wxÿ"Smÿ"Snÿ"Snÿ"Qiÿ"eœÿ@uÿ)ÿ2ÿ2ÿ2ÿ2ÿ/ÿ$AÿG~Äÿt­Äÿ¬ãôÿ™Öîÿu½áÿ`­ÕÿIŸÌÿBšÈÿ)Áÿ=œÈÿw¼Ûÿm¶×ÿ‰ÀÚÿ2™Òÿf»éÿ€Éðÿ‘ÑñÿnÁëÿe¹èÿW¯àÿ;Óÿ-‘ÌÿQ¦Ùÿt¾èÿ]ªÒÿ@„¿ÿj›ÿ„b1ÿe:ÿe:ÿe:ÿƒe8ÿfAÿ/f¨ÿMmÿJbÿKdÿKdÿI^ÿ[šÿ]jvÿˆe2ÿ‚e:ÿ‚e:ÿ‚e:ÿ‚e:ÿƒd5ÿ}lRÿTËÿ­Ãÿ´áòÿ…ÊéÿfµÜÿQ¦ÑÿO¤ÐÿU§ÐÿF Ìÿ>žÊÿlºÜÿs¼Úÿ}¼Öÿ[ª×ÿ7™ÍÿM¬ÞÿoÁîÿcºêÿd·æÿY°Þÿ@›Ïÿ@•Êÿ‰¾âÿ›Ìíÿ‘¿ÛÿUŽÅÿi{“ÿ†`+ÿƒc6ÿƒc6ÿƒc6ÿ„c4ÿ€c=ÿ/a¨ÿEdÿBYÿC[ÿC[ÿATÿUšÿbfoÿ‡b-ÿ‚b5ÿ‚b5ÿ‚b5ÿ‚b5ÿƒa0ÿ{iPÿU‰ËÿŒ²Åÿ£ÛðÿtÁåÿi¶ÝÿT©ÕÿWªÔÿ^®ÖÿY©Óÿ2”ÄÿO­ÕÿÄàÿw¹Öÿb±ÚÿW§ÔÿFžÎÿ]¬Úÿ_­ÜÿB Öÿ-•Íÿ‚ÁÿWŸÍÿ{¶Ûÿw¶Ýÿr«ÌÿL…ÁÿerŠÿ{V%ÿxY0ÿxY0ÿxY0ÿyY.ÿuZ9ÿ.Z§ÿ<[ÿ9Pÿ:Rÿ:Rÿ8KÿQœÿ_]fÿ|X'ÿwX/ÿxX/ÿxX/ÿwX/ÿxV*ÿq`MÿSƒÊÿ€®Äÿ‡Ëéÿ|Ääÿp¼áÿ`°Úÿh²Øÿb°ÖÿM¤Ïÿ9—Æÿ=ÌÿxÂâÿk¸Ùÿb±Üÿ_­ØÿW§Óÿ\¨Òÿk­ÔÿPœËÿy¸ÿ%‚½ÿb©Ôÿ_ªÕÿT¦Óÿ]¡ÃÿK¿ÿ`iƒÿrMÿpP*ÿpP*ÿpP*ÿqP(ÿlR5ÿ,T¦ÿ4Pÿ1Fÿ2Hÿ2Hÿ0BÿM ÿ\V]ÿsO"ÿoP*ÿoP*ÿoP*ÿoP*ÿpN$ÿjYJÿM}ÈÿhŸ»ÿŠÉçÿ‡Èæÿm¹ßÿ]¯Øÿv»Ýÿ`­ÕÿO¤ÐÿG Íÿ7–Åÿc¶ÛÿtÀáÿZ­ÚÿZ¬ÖÿS¦ÓÿT¦ÑÿKËÿ6Äÿ)…¿ÿNžÍÿd®×ÿi¯×ÿ]¬×ÿ6·ÿ6q¸ÿ]a|ÿiEÿgH%ÿgH%ÿgH%ÿhH"ÿeJ1ÿ,N¥ÿ,Gÿ)=ÿ*?ÿ*?ÿ(9ÿ I¢ÿZMRÿiFÿgG$ÿgG$ÿgG$ÿgG$ÿhEÿbQGÿGuÅÿ]–µÿ~¿àÿn¶Ûÿd³Üÿh²Ùÿf³ØÿY«ÓÿW©Òÿ6›Êÿ*ÁÿX­ÔÿÎæÿY¬ÙÿY«×ÿO¦ÔÿJ¡ÏÿCšËÿ0Äÿ1ÄÿT¥ÒÿR¦ÒÿL£Ñÿ2•Ëÿ{®ÿ0i¶ÿXYuÿ`=ÿ_@ÿ_@ÿ_?ÿ`?ÿ]B.ÿ+G¤ÿ #=ÿ !3ÿ "5ÿ "5ÿ !2ÿ#E¤ÿVEGÿ`>ÿ^?ÿ^?ÿ^?ÿ^?ÿ_=ÿ[JDÿBoÄÿX’±ÿe®ÔÿU¨ÓÿK§Öÿf³Üÿ_°Ùÿ`®ÕÿU¨ÐÿT§Ïÿ2“Ãÿ?›Éÿ’ÎåÿCŸÒÿM¤ÕÿD ÑÿFžÏÿHžÏÿ)‰Âÿ8–ËÿD¢ÓÿJ¦Öÿ@ Óÿ>ÒÿI•½ÿ8m»ÿQNlÿX4ÿW7ÿW7ÿW7ÿW7ÿU:*ÿ)A£ÿ 2ÿ *ÿ ,ÿ +ÿ*ÿ%@£ÿR<:ÿW6ÿV6ÿV6ÿV6ÿV6ÿW4ÿTCBÿ—ÅÿN¢Ìÿd®Òÿj³×ÿHžÍÿq±ÿSšÿy¶ÿ0£Ôÿ1—ÑÿG Õÿ@œÒÿEžÒÿ<—ÌÿN§Øÿl½äÿS®ÜÿD¥×ÿA¤Öÿ@¦Ùÿ2¹ÿ1a¼ÿ?2Rÿ>ÿ> ÿ> ÿ> ÿ>ÿ>" ÿ)7¯ÿ]ÿ ({ÿ0.|ÿ;)Mÿ?"ÿ?ÿ=ÿ= ÿ= ÿ= ÿ= ÿ= ÿ=ÿ9(9ÿOmÆÿ˜ÁÏÿR£Ñÿ€ºÿ$ƒ»ÿ/…¼ÿAÂÿz·ÿb«ÿV¢ÿ"}¹ÿL´ßÿ0¢ÓÿI¦Úÿ`¬ÚÿV¦ÖÿR£ÓÿHžÏÿ*ŒÄÿT«Ùÿ‚ËìÿxÈëÿrÄéÿfÀéÿT¥Æÿ9b¿ÿ6'Hÿ5ÿ5ÿ5ÿ5ÿ5ÿ4ÿ0-{ÿ4%Qÿ7ÿ7ÿ6ÿ5ÿ5ÿ5ÿ5ÿ5ÿ5ÿ5ÿ5ÿ4ÿ/.ÿLhÈÿ·ÖÛÿºãöÿS¢Ðÿ-‹Âÿc«ÿR¢ÿU£ÿ w·ÿH¤ÓÿsÊìÿjÂçÿ1¢Õÿ`²àÿ[­ÛÿX¨ÖÿEžÐÿ:—Ëÿ4Åÿ.‹Áÿ+Äÿ1ŠÁÿVªÖÿ…ÏðÿxºÑÿBcÂÿ0@ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ/ ÿ, ÿ+;ÿ4;’ÿp‹ÕÿÇãêÿÆçöÿˆÄäÿJžÐÿ<–Êÿy¹ÿV¦Õÿ“Öðÿ×óÿxÈëÿ^ºäÿ5¥×ÿ_´àÿd²Ýÿa®ÚÿR¦ÕÿK¢ÐÿGžÍÿ:–Èÿ~¹ÿ^¥ÿ(ŠÀÿW¬×ÿJ—ºÿ2T½ÿ1;ÿ0 ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ. ÿ,-ÿ1+nÿEY³ÿo×ÿÆçÿ¿åðÿÎêöÿÊæôÿ®Øîÿ]¨ÖÿZ¬ØÿR­Øÿo½ãÿ‡Ììÿ}ÆéÿzÅéÿnÁèÿ;§Ùÿg·áÿfµàÿnµßÿp¶Ýÿd°Ùÿa­Õÿ?ŸÍÿx¶ÿk¯ÿDŸÎÿYªÕÿ1†­ÿ'J¹ÿ17ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0 ÿ/ÿ. ^ÿ8DžÿPxÐÿnªäÿ“×îÿ»îøÿ¿ðüÿ©àöÿºåöÿÍê÷ÿÇæôÿx¸Ýÿj²Ûÿn¸Þÿlºâÿ~Ãçÿp¼âÿyÃæÿ}Çéÿ`¶àÿl»äÿe¶áÿ`³ßÿhµÝÿq·Ýÿe±Ùÿ:˜Êÿj®ÿ|ºÿQ¦ÒÿV§Ôÿ,ˆ±ÿ'G¸ÿ13ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ/ ÿ, ÿ-Iÿ.5ÿ3\¼ÿRØÿy½äÿÐíÿÒóÿŒÙøÿ°êüÿ¬âøÿ¨Ýôÿ§Ýòÿ·ãôÿ½àñÿ‚¿ãÿ>žÓÿmºáÿ}Ãåÿi¹ãÿŽÊêÿ‚ÄäÿƒÅäÿx¾àÿi¼åÿwÀæÿv½âÿa³Þÿh´Ýÿe±Úÿy·ÿc«ÿ/’ÇÿU¨ÔÿF¦Øÿ7µÿ+G¸ÿ0/ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ0ÿ.8ÿ0.{ÿE[ºÿ=zÌÿ%ÍÿB¯ÚÿgÄèÿŠÒñÿ…Ñóÿ}ÎóÿÏòÿ˜Öóÿ—Óðÿ¥Ûôÿ¢×ñÿ”Ðíÿ‹ÆèÿwºàÿI¥×ÿX±ÜÿÄãÿP©Ûÿ„Åéÿ‘Éæÿ‚ÀÞÿz»Ûÿl½æÿrÀçÿ„ÅçÿkµÞÿ^®ÚÿX£Ñÿ bªÿqµÿ;šÍÿN©×ÿH«Üÿ;·ÿ,G¸ÿ0+ÿ0ÿ0ÿ0ÿ0 ÿ0 ÿ/)ÿ.%jÿ0F¦ÿ°äÿvÅëÿe½èÿY¸æÿ`ºçÿ`ºçÿY¶åÿ^ºçÿvÅìÿ€Ëîÿ}Ëïÿ–Øõÿªäúÿ»ìýÿ¯åûÿg»çÿm»æÿp½åÿ~ÅéÿŸÕðÿœÑíÿ–Íêÿ–ÌéÿœËèÿ„½âÿ^¥Óÿ,}¼ÿ`±äÿƒÀåÿ}ºÛÿi¬ÏÿF•¼ÿ1w¢ÿh›ÿt¨ÿ,˜ËÿM§ÔÿY«Øÿ„¼ÿt´ÿ^µâÿ‡ÐñÿvÆíÿ8¨àÿ-¦áÿ>²çÿEµéÿ<²èÿI·éÿqÅìÿmÂêÿh¿éÿk¿éÿg¾éÿpÀéÿc½éÿsÅíÿƒÊïÿ€ËïÿyÉïÿ‘ÓòÿœØõÿ„Éðÿ_µãÿŠÇéÿŽÉêÿ‡ÅçÿƒÅçÿËëÿ–ÍëÿœÍêÿ£Ïéÿ‰ÃäÿTÍÿK“ÉÿŒÈìÿ€¿áÿ~¼Þÿp±ÓÿUÂÿsªÿ ~²ÿ+‰¸ÿ((P @“ÐîÿžÖñÿ¤Úóÿ«Ùñÿ¥Øðÿ˜ÓîÿÑìÿ¦Øïÿ±ßóÿe¦Ñÿd¯ÿz½ÿH—Ëÿ-Èÿ¼âÿy·Þÿ¼áÿq¶Ýÿ^¬Ùÿlµßÿm·àÿ`³ßÿi¸ãÿe¶âÿƒÂæÿ~ÂçÿTªÜÿ!†Èÿ/‹Êÿ#~Âÿw¸ÿ t´ÿe¬ÓÿŠÁßÿŒÂßÿŠÀÞÿƒ¼Üÿƒ¾Ýÿo¶Øÿl±Õÿ¡Ðïÿ¥Ôñÿ™Óñÿ£Ùóÿ™Óðÿ˜Òïÿ®Ûñÿ¨ÚñÿS‹¿ÿI›ÿZ¢ÿ^¡ÿv¼ÿƒÄÿo³ßÿ‡ÁåÿW¨×ÿyºãÿ‚¼ãÿŽÃçÿˆÃçÿg·âÿt¼ãÿ…ÄçÿƒÄçÿ|Áçÿy¾ÿa±ÿkµÿ'„Áÿ|¼ÿ(‡Àÿw´×ÿm´×ÿu¸Úÿo¶Ùÿv·Úÿy¹Úÿk²Õÿf¯Ôÿ Óðÿ©Õñÿ«Øòÿ£Õðÿ¦Õïÿ¬Øðÿ›Îéÿ=zµÿ/ˆÿKœÿn¡ÿ1z ÿS„ÿ[˜ÿ8…¾ÿ–Îïÿg´àÿ’ÇêÿšËìÿšÍìÿÐîÿÉëÿŠÇéÿ‹Éêÿ‡ÊìÿL“Éÿ{¼ÿg¯ÿS ÿRŸÿXšÿ1†¶ÿm²Ôÿf¯Óÿc°ÔÿW¬ÒÿX¬Òÿ^­Óÿ\«Ñÿf¯Ôÿ`ªÚÿ™Ëìÿ­Öðÿ«Ôîÿ¦Õîÿt¯Õÿ,†Àÿ5ÿ=•ÿ[¥ÿ8…­ÿnÕøÿZ´Ôÿ0lŽÿ >oÿ>}©ÿ`³Þÿ“ÍðÿÔñÿ¥Ùòÿ¡Öðÿ‡Éêÿ‰ËìÿyÉîÿ>—ËÿW£ÿ\¤ÿg©ÿW¡ÿL“ÿQŠÿ8‹¹ÿt·Øÿi±Õÿ_±Õÿ]®ÓÿP§ÏÿP§ÏÿT©Ðÿmµ×ÿ|¿ÿ'}¾ÿN™Ìÿg­×ÿ@z´ÿE‘ÿ0|»ÿ2Œÿ c®ÿ,„½ÿJ²ÿ^ÈòÿPÄöÿ]Ë÷ÿX²Õÿ/r”ÿ'm‘ÿc£Âÿ¢Øïÿ¡ÛöÿŒÎîÿ}ÉìÿËíÿQ¦Õÿ~ºÿ{¸ÿ"~¹ÿd¤ÿ WžÿX™ÿ[–ÿL£Íÿ]°ÖÿX®ÕÿO«ÔÿJ©ÒÿB£ÎÿK§Ïÿh·Ûÿg¸Ýÿ<—Íÿ.ˆÂÿq³ÿ%…¿ÿ V ÿGÿ?‘ÉÿV¤ÿ$ˆÅÿ6¸ÿ/h—ÿaËóÿN¿ðÿOÀñÿRÅöÿ\ËøÿZ·ÙÿO“¬ÿV‰ÿb¤Áÿ{ÅçÿS²áÿE§×ÿ@›ÍÿE™Êÿ>’ÅÿIÍÿyÈêÿ‰Õòÿ†ËëÿP ÍÿQ©ÓÿJ©ÓÿIªÔÿC§Ñÿ1žËÿ*™ÈÿtÁáÿÖíÿhºáÿNŸÍÿZ¤Ðÿ)ƒ½ÿ&†¿ÿM›ÿD›ÿQ¬Ûÿ~¾ÿÁÿJ–ÿ&XŽÿbÍôÿM¾îÿO¾îÿO¾îÿN¾îÿPÂóÿXÇôÿYµØÿS˜°ÿ>|—ÿ1‚ªÿ3šËÿI«ÛÿX®ÛÿR§Ôÿq¿ãÿ¨ëþÿ§ëþÿ·ñÿÿ¯íÿÿœÜôÿr¿âÿJ«Õÿ7¡Îÿ:£ÏÿoÀàÿŸÛðÿ ×íÿwÁãÿ^§Ñÿe¬Óÿ$¼ÿ6‘ÅÿQŸÿFÿF§ØÿP¦×ÿ\¬ÿ`¦ÿ/y¥ÿ]ÃêÿG°ßÿI±ßÿI±ßÿI±ßÿI±ßÿH°ßÿI´ãÿR»èÿX²ÕÿU•­ÿF€šÿY•´ÿt¸Ûÿh¹ãÿÖóÿ£äúÿ¡ãùÿ¬éüÿ·íýÿµíþÿ¯ëýÿ¥á÷ÿžÜóÿ«ãõÿµæöÿ×îÿ…Äâÿn¸ÛÿDœËÿLžÌÿv¶ÿ€ºÿKÿT¦ÿ…ÄÿdºåÿB–ÎÿY ÿ4„¬ÿX¸ßÿ?žÌÿB¡ÎÿB¡ÎÿB¡ÎÿB¡ÎÿB¡ÎÿA¡Îÿ@ ÍÿA¢ÑÿL®ÚÿX°ÒÿW–­ÿcŽ¡ÿ\–¶ÿ|Ââÿ•Ûõÿ¨äùÿ¢á÷ÿ«åùÿ¡äùÿ‹Ü÷ÿ“ßùÿ®èûÿ©ã÷ÿ§Üñÿ¦ÕëÿÆáÿ^«Ïÿ;—Êÿ)…¿ÿe­ÿ c«ÿBšÿ^¬ÿiµÿ"ÍÿW·åÿ?ŠÀÿ4x¥ÿS®Ôÿ7¹ÿ:‘¼ÿ:‘¼ÿ:‘¼ÿ:‘¼ÿ:‘¼ÿ:‘¼ÿ:‘¼ÿ:‘¼ÿ9»ÿ9‘¾ÿCžÉÿUªÍÿL”¯ÿP…œÿn¤»ÿ•Íåÿ”Öòÿ‘Õñÿ×ñÿzÌîÿmÇîÿ†Ñðÿ“Ôîÿ‘Îéÿ¤Óéÿ—Éáÿ@“½ÿe°Ùÿ"{»ÿT¤ÿažÐÿ?…Áÿ\ªÿk¶ÿ yÀÿ+™ÓÿF¥ÕÿMž¿ÿL¡Çÿ0|§ÿ3«ÿ3«ÿ3«ÿ3€ªÿ3€ªÿ3«ÿ3«ÿ3«ÿ3«ÿ3«ÿ1©ÿ1€«ÿ>‘ºÿLŸÂÿW›µÿaŽ ÿu¡¸ÿÃâÿ†ÌëÿƒËêÿ‡Ìëÿ‡ÊéÿzÁáÿÈãÿ”ÈáÿÅÝÿC”½ÿ¨ÝòÿˆÉêÿh¬Ùÿs»äÿxÀêÿO¡ÚÿO©Þÿ~ÃèÿuÁéÿoµÜÿg«ÆÿD•»ÿ(k•ÿ,q™ÿ,q™ÿ,q™ÿ,qšÿ+n—ÿ,m–ÿ,p™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ,q™ÿ*n—ÿ)n˜ÿ6‚«ÿH–¹ÿZœ¶ÿj£¿ÿw¼ßÿw»Þÿ…Ááÿ†Äãÿ„Ââÿ–ÉâÿšÉáÿŒ¿Úÿs®Ìÿ§Ûòÿ•Öòÿ€Îñÿa¼êÿ\´çÿU±åÿxÈìÿ–×ñÿ‰Îïÿ€Ààÿl¯Èÿ?Нÿ [ƒÿ%aˆÿ%aˆÿ%_‡ÿ"hÿ…¯ÿsœÿ"b‰ÿ$\ƒÿ%_†ÿ%aˆÿ%aˆÿ%aˆÿ%aˆÿ%aˆÿ#^†ÿ[„ÿU¨Êÿ[™·ÿ^©Ðÿ\§ÎÿeªÐÿp±ÕÿŠÄâÿ’ÉäÿŽÂÞÿn¬Íÿ[žÀÿŸÙóÿtÄìÿqÁìÿa·çÿQ«àÿP²ãÿÖòÿŽÓðÿÑðÿu¼ßÿc¬Èÿ:£ÿJpÿQwÿQwÿOtÿ\‚ÿ&–¾ÿ5˜¾ÿ"вÿp˜ÿXÿMrÿPvÿQwÿQwÿQwÿQwÿMsÿRŸÀÿO‘²ÿC—Äÿ7Àÿ/¿ÿJ›ÇÿX¤Îÿ[¨ÒÿOŸËÿK”ÀÿV™½ÿ¦ÝöÿÎñÿo½êÿ`µæÿL«áÿVºçÿŽÚôÿ€ÐðÿmÆîÿS±ÛÿU©Èÿ6s–ÿ:^ÿAeÿAeÿ>bÿPuÿ&·ÿ@°ÿ=޳ÿ6‘¶ÿ&Œ³ÿršÿDiÿ@dÿAeÿAeÿAeÿ=aÿQœ¼ÿ9…ªÿ#ƒ¸ÿ s®ÿA›Éÿh­Ðÿw±ÿx²ÿ?½ÿ4…³ÿ.‚²ÿ¦ßøÿ‘Òóÿ{ÄîÿgºêÿM¯ãÿMµåÿ…ØôÿrÏòÿaÇñÿ]ºáÿ[©Ëÿ.cˆÿ )Lÿ1Tÿ1Tÿ.PÿCiÿ%ˆ³ÿ:‚¥ÿ8‚¥ÿ9‚¥ÿ<„¦ÿ!‹¶ÿ=bÿ/Pÿ1Tÿ1Tÿ1Tÿ -PÿM–¸ÿh™ÿVœÿVšÿ`«Òÿi¯Ïÿ'~±ÿ]ÿp§ÿ'±ÿB“¿ÿ¡Üöÿ•Öõÿ…ÌòÿxÄïÿAªáÿ7©àÿ‘àùÿƒÛøÿ|Ùúÿ€Ñìÿg­Îÿ$Pyÿ9ÿ Bÿ Bÿ=ÿ 5]ÿ%€®ÿ5y˜ÿ3xšÿ3xšÿ4z›ÿ}­ÿ*Nÿ?ÿ Bÿ Bÿ Bÿ?ÿG‹µÿ?zÿ!Wÿ+h ÿ‚ÉåÿS£Êÿ0‰¸ÿz°ÿ~³ÿi¯Ðÿp²Ñÿ“ÑòÿÓôÿˆÍòÿËòÿJ¶èÿ_Äíÿ¤íýÿ”èüÿ•éþÿÜîÿfªÎÿ@lÿ )ÿ2ÿ2ÿ-ÿ*Sÿ%y©ÿ0oŒÿ.oŽÿ.oŽÿ/qÿr¦ÿ=ÿ0ÿ2ÿ2ÿ1ÿ/ÿD€¯ÿ5„®ÿF¢ÌÿŠÎéÿwÀßÿN¢Ëÿ0¿ÿ {±ÿ=™Äÿ‹ÁÜÿr´ÓÿšÖôÿzÇðÿšÓôÿ•Öõÿ}Øùÿ¢ëýÿœêüÿ‹æüÿ–êÿÿŽÜïÿ`¡Íÿ9gÿ &ÿ/ÿ/ÿ )ÿ(Sÿ$r¥ÿ+d€ÿ*eƒÿ*eƒÿ+h…ÿi ÿ8ÿ-ÿ/ÿ/ÿ.ÿ.ÿ@x®ÿN™½ÿ™Ýóÿ¥Ýñÿm¸ÛÿGžÊÿ)Œ¾ÿ·ÿX©Îÿƒ½Ùÿ\«Îÿ¥ÚõÿˆÍïÿ~Èïÿ™ÕóÿzÈîÿ†Óôÿ‹Úøÿ‡Ùùÿ†ÚûÿxÊèÿU”Éÿ1eÿ'ÿ /ÿ /ÿ)ÿ&Vÿ#i ÿ'Ztÿ&[xÿ&[wÿ'_|ÿ_œÿ5ÿ .ÿ /ÿ /ÿ /ÿ /ÿ:o®ÿk­Ìÿ®æ÷ÿ˜Ôíÿm·ÜÿEÊÿ+Àÿƒ¹ÿ[ªÐÿ{¹×ÿa¬Ïÿo¾êÿ—Öõÿ†Îðÿ~Êïÿ[´äÿY¯áÿg¹çÿp¾èÿQ²äÿ%•Ëÿ0z¼ÿ1Chÿ +ÿ &3ÿ &3ÿ"#-ÿ 9\ÿ!b›ÿ"Qhÿ"Rmÿ"Rlÿ!Usÿ!\™ÿ"'6ÿ"&2ÿ"&3ÿ"&3ÿ"&2ÿ"'6ÿDu²ÿ|¶Ïÿ®ãôÿƒÈçÿe²ØÿK¡ÍÿB›Èÿ(Áÿ^®Óÿq¹Ùÿ…¾Ùÿ=ÓÿY³âÿ„ËñÿxÆîÿh»èÿW¯Þÿ1•ÌÿBšÐÿ€Àèÿx¶ØÿK‰Ãÿzwoÿ‹g2ÿ‰i9ÿ‰i9ÿŽi3ÿpkcÿW•ÿF\ÿHbÿH`ÿIjÿ9d˜ÿŒj9ÿŠi8ÿ‰i9ÿ‰i9ÿŠh7ÿ‡kAÿ[оÿŠ·Îÿ¦Üñÿo¼áÿX«ÕÿO¥ÐÿW©ÒÿDŸËÿN¨Ñÿt¿Þÿ|¼×ÿ_­Øÿ<šÍÿS¬Ýÿ`´åÿS­ßÿD£Öÿ0Æÿp®×ÿŽÂåÿ†¸ÕÿQ‰Ãÿqkeÿ[*ÿ}]2ÿ}]2ÿ]+ÿf`aÿOÿVÿ=Tÿ@bÿ:]“ÿ€^/ÿ|]2ÿ|]2ÿ|]2ÿ}\/ÿz_<ÿYƒ½ÿŒ¸ÏÿˆÍëÿs½âÿ^°Úÿ^®×ÿa¯ÖÿJ¡Íÿ0–Æÿq¾àÿu»Ùÿc±Ûÿ^ªÖÿV¦Ñÿi­ÕÿOŸÎÿ{ºÿ0ˆÁÿh­Õÿ\©Ôÿ`£ÆÿM‚¿ÿi`\ÿtQ$ÿrS,ÿrS,ÿvR$ÿ^X_ÿFˆÿ3Dÿ5Kÿ4Iÿ8[ÿ=WŽÿvR'ÿrR+ÿrR+ÿrR+ÿrQ(ÿoU6ÿQ|¼ÿp©ÈÿˆÊèÿ}Âäÿa±Úÿo·Ûÿ^¬ÔÿL£Ïÿ<˜ÇÿV¬Õÿq¿àÿ]¯ÚÿT¨ÔÿU§ÓÿO¡Íÿ=“Åÿ)…¾ÿU£Ïÿf¯×ÿa®×ÿ=’»ÿ;s¸ÿcVTÿiGÿhI%ÿhH%ÿkGÿWO^ÿ>‚ÿ(8ÿ+@ÿ*<ÿ0Vÿ>OˆÿkHÿgH%ÿhH%ÿhH%ÿhG"ÿfK2ÿJsºÿcžÀÿ~Àáÿk¶Ýÿe²Ûÿi³ÙÿY«ÓÿN¥Ïÿ,“ÄÿF¡ÌÿŒÌåÿX«ØÿR¨ÕÿK£ÑÿGÌÿ1ŽÄÿ5‘ÆÿR¦ÓÿM¥Òÿ:›Îÿ"€³ÿ5jµÿ\LLÿ_<ÿ^>ÿ^>ÿa=ÿPF\ÿ5{ÿ ,ÿ !4ÿ 0ÿ (Sÿ>Hÿ`>ÿ]>ÿ]>ÿ]>ÿ]=ÿ]B-ÿEl¹ÿV•ºÿ`¬ÔÿK¥ÓÿW¬Øÿ`°Ùÿc°Öÿ_­ÓÿDžÉÿ0‘Ãÿ…Äàÿ?œÐÿL£ÕÿEŸÑÿFÏÿ(‰Ãÿ9™ÍÿK¥ÖÿN¨×ÿJ¤×ÿNšÃÿ[ÿ,sÿ ÿ)ÿÿ Jÿ=@|ÿV3ÿT4ÿT4ÿS4ÿS3ÿT8(ÿÿ? ÿ@ ÿ@ ÿ? ÿ? ÿ?ÿ=#ÿJf»ÿm¨Æÿ-ŒÃÿ$†½ÿ9ÂÿRœÊÿ6Âÿf­ÿW ÿ/“Çÿ3¦ÖÿK¦Ùÿ]ªÙÿQ£ÔÿL Ñÿ1Èÿ^±ÝÿxÇéÿmÀçÿfÀéÿS§Ëÿ8\·ÿ6&ÿ6ÿ6ÿ5ÿ6ÿ4"Aÿ/,zÿ4 8ÿ8ÿ7ÿ6ÿ5ÿ5ÿ5ÿ5ÿ5ÿ5ÿ/ÿG^½ÿ¼Ûäÿ“Ëèÿ3Åÿq³ÿV£ÿY¥ÿ"|ºÿU±ÛÿpÇëÿ7¦Öÿ`²ßÿ]¬ÚÿP£Óÿ=™Ìÿ6’Æÿ0Âÿ&‡Àÿ4ŽÄÿqÂçÿw»×ÿ>Z¸ÿ/ÿ0ÿ0ÿ0ÿ0ÿ0ÿ1ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ/ ÿ, ÿ+7ÿ8<†ÿuÔÿÈäîÿ¿äôÿd®ØÿD›Íÿ!‚¾ÿ_­Ùÿ’ÖñÿƒÐðÿi¿çÿ:¨Ùÿbµáÿf²Ýÿc®ÚÿS§ÔÿQ¤Ðÿ7—Èÿn°ÿs³ÿO§ÓÿD”¼ÿ/J²ÿ1ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ. ÿ+(ÿ2-lÿIa«ÿw Ýÿ¥Òðÿ¼èóÿËê÷ÿÎéöÿŒÄãÿ^«×ÿ^²Ûÿs¿äÿƒÈêÿuÂçÿzÇêÿK®Ýÿj¹âÿb´àÿk¶Þÿm¶Üÿf±Øÿ:šËÿh­ÿ*ŒÃÿ[«Öÿ3‹¶ÿ(B¯ÿ1ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ. ÿ/ÿ/!Tÿ7G•ÿTÏÿn²éÿ‚Òòÿ«íûÿ¾ðþÿ¦Þõÿ¯áôÿËêöÿ©ÓëÿZ©×ÿm¸ßÿr½ãÿw¿åÿ|Âäÿ€Æçÿs¾âÿl½åÿs½ãÿdµßÿiµÞÿe±Úÿ!|¹ÿl°ÿCžÍÿQ©Öÿ6½ÿ+B¯ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ. ÿ-?ÿ8=ˆÿ.^»ÿ.ˆÒÿY¶ãÿ†Òîÿ„ÔôÿzÍôÿ”ØõÿØóÿ§Ýôÿ¥ÚòÿžÔîÿ‘ÈçÿN¦×ÿW°Ûÿ{Áâÿdµâÿ’ËêÿƒÂàÿ{½Ýÿk½åÿzÂçÿz¾ãÿb°Üÿ[¦Óÿcªÿ|ºÿL¥ÓÿJ«Ûÿ=”Âÿ-B¯ÿ0ÿ0ÿ0ÿ0 ÿ/ ÿ//ÿ..vÿ5Y´ÿ\™Úÿ”ÒñÿcÁåÿ-¨ÛÿV½êÿŽÓòÿ†ÏñÿuÈðÿ}Éîÿ‰ÊíÿˆÈêÿ{¿æÿ‰ÅèÿŠÃåÿo¶ßÿuºàÿ~ÁáÿWªÖÿ”ÉæÿÈâÿ„¾Üÿa¸ãÿs½äÿu½ãÿU¬Ùÿq³ÿ`ªÿ1“ÉÿT«Úÿ`¸äÿS Êÿ0A¬ÿ0ÿ/ ÿ/!ÿ/&eÿ2K¢ÿ5wÇÿ6šÚÿS»åÿ„Ùóÿªëýÿ´íþÿX»åÿ7ªàÿÎòÿqÄîÿh¾êÿuÂëÿzÃéÿŠÇéÿŽÅèÿ–ÈéÿœÌêÿ~½ãÿz»áÿ™ÌæÿSžÂÿZ«ÑÿŠÅâÿ›Ìâÿk»ãÿt»áÿ`°Úÿ2–Ëÿi¯ÿ|»ÿD¢Óÿg·âÿpÄëÿq²Óÿ8A±ÿ-Yÿ1?ÿ/i¿ÿ=ÖÿT¯ÞÿS¸âÿE¶äÿaÃíÿ‘Ûöÿ¢æûÿ·ðýÿªèûÿOµãÿS´äÿ[¹èÿg¼èÿq¿æÿŠÉêÿ¢Ôðÿ¦ÑîÿœÈéÿšÆçÿk²àÿm¸äÿ†Ââÿ1†´ÿM•¹ÿa¨Êÿu²ÐÿT­ÙÿL§ÔÿM¥Óÿ3Çÿzºÿ9ÏÿW±Þÿ„Ëìÿ¦âøÿyÀâÿbÅÿ3‚ÒÿT«ÝÿH²ÞÿNµâÿX¶åÿP²ãÿX¹æÿtÇíÿ“Ùöÿ£åûÿ³íüÿÕûÿÿÜôÿI­ßÿS²âÿpÀèÿŠÊêÿÉéÿ–Íëÿ“Èèÿ’Áçÿw²ÞÿP¡Øÿmºèÿe°Ùÿ3¾ÿ*y§ÿS†£ÿ=| ÿK¥ÔÿZ¬×ÿV¬×ÿx¶ÿ$ˆÄÿX±ßÿ†Ïïÿ‹ÖòÿM´äÿœÛÿšÖÿO¸äÿnÂêÿY¸çÿ]¸æÿ]¸åÿR³ãÿh¿éÿ{ÈíÿƒÎðÿ›Ý÷ÿ´êüÿÅñþÿ‰Ïðÿ^´ãÿg¹äÿŠÌìÿ Ôïÿ•Ìêÿ—Ìéÿ˜Éèÿ€»áÿBÇÿ>—Óÿy¿çÿx·ÚÿV¢Èÿ4ªÿ2o™ÿo¡ÿ0šÌÿW«×ÿ>œÍÿp°ÿL¨ÚÿˆÑñÿnÃëÿ3§àÿ0¨ãÿ@²èÿ8±èÿZ¼êÿrÄëÿi¿èÿh¾èÿg¾èÿi¾éÿjÀëÿÉîÿÊïÿÌðÿ•Öôÿœ×öÿd¸æÿ‚ÃèÿŠÈéÿ…Åæÿ‹Èéÿ”ÌëÿšÌêÿ¡Îéÿ{ºàÿA‹Àÿr´áÿ†Ãäÿ{»Ýÿo¯Ðÿ1…´ÿtªÿ&…¶ÿ( @ €˜Òïÿ Øóÿ¨Úòÿ¤×ñÿ“Òîÿž×ïÿ¯ßóÿa¡Ïÿd¯ÿ+†Äÿ1‘Éÿo´Þÿ~ºàÿzºàÿc¯Úÿnµßÿm·áÿdµáÿj¸ãÿÁæÿzÀçÿ3‘Ïÿ&…Çÿ)„Åÿ u¸ÿ2ŒÂÿ„¾ÞÿŠÂàÿ‡¿Þÿ†¾Ýÿw¹Úÿk±Õÿ¥Óñÿ¤Õòÿ¡×óÿžÕñÿ¨ÙñÿªØïÿM„»ÿD˜ÿ `žÿZ—ÿp³ÿY¦Øÿ…Ãèÿf²ÝÿŒÃçÿ“ÆéÿŒÆéÿu½åÿ‰ÅèÿËìÿc®Üÿ k¶ÿ]«ÿo´ÿr³ÿPžÊÿo´×ÿl´×ÿe³×ÿoµØÿk³Öÿe¯Ôÿ‰Åéÿ°Ùóÿ®Øñÿ­ÙòÿÄäÿ7€ºÿ-ˆÿNœÿ=¹ÿT§Èÿ%hÿSˆÿb¥Ïÿ~ÄëÿŸÑñÿ¢ÖñÿŸÔðÿËëÿ‰Íîÿb±Ýÿh®ÿ l¯ÿ]¦ÿJ–ÿTÿSŸÇÿo´×ÿb°ÕÿY¬ÒÿS©ÐÿT©Ðÿf±Õÿ"ÂÿP™Íÿs´ÛÿZ‘Ãÿ+iªÿZ¥ÿF›ÿ!y¶ÿQ Âÿ_ÐüÿZÅïÿG˜½ÿ)n“ÿF°ÿ“Êãÿ¦Þ÷ÿÐðÿËîÿk»ãÿ#ƒ¿ÿs³ÿm¬ÿ \¡ÿT—ÿ eÿZ¬Ôÿ_±ÖÿU®ÕÿL©ÒÿC£Íÿ]±Öÿj¹Ýÿ9”Êÿ"~¼ÿ}»ÿ V¡ÿ[¨ÿ+zºÿu¹ÿ,x²ÿ?€­ÿ[ÌøÿNÀòÿUÉùÿZÆñÿR©ÊÿX”«ÿcŸ¸ÿh´ÙÿH¬Ýÿ@¡ÓÿCšÌÿ?•Çÿa³Ûÿ‹Öòÿ‡Íìÿ\¬ÕÿV®×ÿF¨Óÿ>¤Ðÿ(™ÈÿJ«ÓÿœÖíÿuÁäÿ[¥ÑÿNœËÿ%…ÀÿV¢ÿX¨ÿMªÚÿv»ÿN™ÿ7z«ÿZÉôÿMºêÿM»êÿM»ëÿPÀñÿT¾êÿUªÊÿE‰¦ÿ7ƒ¦ÿFžÉÿc¶àÿ^²Ýÿ—Üöÿ¨ìþÿµñÿÿ·ñÿÿ¢ßöÿwÄåÿYµÛÿj¾ßÿ™Øîÿœ×íÿ{ÂâÿV¦Ðÿ=“Æÿ‚½ÿX¥ÿW¨ÿP¯ÝÿA‘Íÿ[¢ÿ?”»ÿRµàÿD¦ÔÿE¨ÕÿE¨ÕÿE¨ÕÿD¨ÖÿF¬ÛÿP±ÛÿU¥ÅÿV’ªÿj›´ÿj¯Óÿ“Úöÿ£äûÿ©æûÿ²êüÿ¦èüÿ£æüÿµìýÿ¶éúÿ«Þòÿ“Ëåÿj´×ÿ:—Éÿu¶ÿ i¯ÿHÿ[«ÿzÀÿO²âÿ6‚ºÿ>‹µÿI¤Íÿ:‘¾ÿ<”¿ÿ<”¿ÿ<”¿ÿ<”Àÿ<“¿ÿ:’¿ÿ=—ÄÿF¡ÊÿT ¿ÿOªÿdžµÿŽÆÝÿšÙôÿ–Ùôÿ‹ÖòÿrËðÿ‰Õòÿ•ÖïÿÒêÿ¢ÑçÿPžÅÿe°ÚÿqµÿD†ÃÿN’Éÿi³ÿyÀÿ.—ÒÿJ§ÕÿU¦Èÿ>·ÿ1}§ÿ3€ªÿ3€ªÿ3~¨ÿ3~¨ÿ3€ªÿ3€ªÿ3©ÿ1~¨ÿ2­ÿ>¸ÿJ–¶ÿ]—­ÿq ·ÿ€Áßÿ…Ëëÿ„Êêÿ†ÉéÿÂâÿ”Éãÿ–ÈàÿVŸÅÿ¨Ýóÿ‹Ííÿo½çÿqÀëÿV­âÿvÃéÿˆÌîÿx»ßÿh°Ìÿ3|£ÿ(i‘ÿ*l”ÿ*k“ÿ'r›ÿ&n—ÿ)gÿ*i’ÿ*k”ÿ*l”ÿ*k”ÿ(i’ÿ)l•ÿ3|¤ÿTž¼ÿg¥ÃÿoµÛÿy¹ÛÿƒÀàÿŒÆãÿÌäÿ‘ÂÛÿp¬Ëÿœ×òÿ}Êïÿi¿ìÿQ­âÿ]¹çÿÖòÿ“Ôñÿ~Ááÿd®Êÿ+jÿT{ÿ!X~ÿ!Tzÿy¡ÿ'“»ÿ!y¡ÿaˆÿSzÿ!U|ÿ!Xÿ!X~ÿ W~ÿOwÿJ•·ÿV¾ÿLžÉÿB—ÄÿT¡Ëÿt·Úÿs·Úÿ\¡ÊÿW›¾ÿ ÚõÿyÄíÿe¸èÿM¬áÿdÁêÿŽØóÿzÌðÿ\µÞÿV¨Èÿ%X|ÿ@eÿDhÿ?cÿn–ÿ;–»ÿ;’¶ÿ.³ÿy ÿQwÿBfÿDhÿDhÿ>cÿHŽ®ÿ>µÿ¸ÿ%‰½ÿ\§Ïÿ"}µÿ0‡ºÿ;Œ¹ÿ5†´ÿ¤ÞøÿŠÎòÿq¿ìÿM¯äÿX¼èÿƒØöÿhÌóÿ`¾äÿY¥ÈÿBgÿ +Nÿ/Rÿ *Lÿcÿ5‰®ÿ9£ÿ;‚¤ÿ2Ž´ÿZƒÿ*Lÿ/Rÿ/Rÿ *LÿB„§ÿ^•ÿCŠÿA‘Áÿm´Ôÿ x­ÿd¡ÿ%±ÿE–ÀÿÙöÿŒÒôÿÊòÿC­ãÿR»èÿ˜æüÿ‡àüÿ‡Øðÿ`¢Çÿ+Pÿ8ÿ<ÿ5ÿV„ÿ1}¢ÿ3v•ÿ3u•ÿ.§ÿ Doÿ5ÿ<ÿ<ÿ6ÿ;tŸÿTˆÿ Yÿy½ÞÿX©Ïÿ,‰¹ÿz±ÿ_©Îÿz¸Õÿ‘Òóÿ‡Íòÿ“ÓôÿnÎõÿ“äúÿœíýÿ•ìÿÿ‘àòÿZ™ÅÿCÿ +ÿ/ÿ (ÿN€ÿ-r•ÿ,j‡ÿ-h†ÿ)tžÿ 6cÿ (ÿ/ÿ/ÿ )ÿ6h˜ÿO£Êÿ”Ýöÿ‡ÊæÿM¢Ìÿ$‰¼ÿ‰»ÿ~»Ùÿl²ÒÿŸØõÿ‚Êðÿ–Ôôÿ„ÐóÿÚ÷ÿÞûÿ‰Ýüÿ|ÏìÿN‰ÀÿBÿ+ÿ /ÿ)ÿIÿ(e‡ÿ']yÿ(]wÿ&j–ÿ,^ÿ(ÿ /ÿ /ÿ)ÿ/_˜ÿx»Ùÿ®æøÿ}ÃâÿHŸÌÿ$нÿ+‘Áÿ|»Ùÿb­Ðÿp¿êÿÓôÿ„Îñÿc¹çÿY°áÿd¶äÿ`¸çÿ9ŸÒÿ9y·ÿ69Gÿ1.1ÿ105ÿ4./ÿ(T„ÿ Wzÿ!Pjÿ!Phÿ!^ÿ.C^ÿ4..ÿ215ÿ215ÿ3./ÿDm ÿŒÄÜÿ Ûòÿk·ÝÿO£Ïÿ>™Çÿ;›ÈÿqºÚÿ~¼ØÿE¡ÔÿY±áÿuÄîÿd¹èÿM©Úÿ5’Êÿ~»ãÿ‡½ÞÿWнÿ…mJÿ‹h4ÿ‰h8ÿj3ÿJd†ÿGkÿD\ÿCYÿRˆÿsj]ÿŽi2ÿŠh8ÿŠh7ÿŠg4ÿc„ªÿ”ÃÜÿ‰Îìÿ_°ÚÿT¨Óÿ[«Ôÿ>œÊÿc¶Úÿ|½Ùÿ_®ÙÿM¢Ñÿ`­ÚÿR¥Öÿ!†ÄÿI˜ËÿuµÜÿo¬ÐÿU‚¸ÿs[>ÿxV,ÿwW/ÿ{X+ÿBX‚ÿ:^ÿ7Nÿ7JÿJƒÿh[SÿyW)ÿvW/ÿvW.ÿvV,ÿ[y¦ÿ~·×ÿ„Êéÿi·Þÿh³Úÿ]­Õÿ?›ÉÿO¨Òÿr¾Þÿ^¯ÚÿX©ÕÿX¥ÐÿD–Çÿ*†¾ÿ]¨Óÿb®ØÿC—ÃÿEs®ÿiN5ÿjI$ÿjJ'ÿnK#ÿ=M€ÿ.Pÿ+@ÿ*;ÿA~ÿaOHÿkJ!ÿiJ&ÿiJ%ÿjI%ÿPm£ÿl¨Îÿ|Àãÿf´Üÿj´ÚÿZ«Ôÿ@žÌÿ9šÈÿ€ÆãÿVªØÿN¦ÔÿJ Ïÿ3Åÿ<–ÉÿR¨ÕÿD¡Òÿ*‡»ÿ=f¨ÿ^A+ÿ]=ÿ]>ÿ`?ÿ8C|ÿ #Aÿ 2ÿ ,ÿ8yÿYC>ÿ^<ÿ]=ÿ\=ÿ]=ÿHd ÿS—ÂÿXªÔÿO¨Õÿb±Øÿi³Øÿ\¬Òÿ-Âÿpµ×ÿ8—ÏÿF¡ÔÿBÐÿ+Æÿ?ÐÿN§×ÿT«ÛÿR ËÿAc§ÿQ3 ÿQ0ÿQ1ÿS2ÿ39yÿ1ÿÿÿ2ÿP61ÿQ0ÿQ1ÿQ1ÿS1ÿ:Wšÿ%}³ÿR¤ÏÿHÈÿ^«Ñÿ„Àßÿh³Øÿ j©ÿ'‚»ÿ'Íÿ<›Óÿ9˜ÎÿAžÒÿW®ÛÿB¡ÔÿLªÛÿBšÈÿ:Y¢ÿE%ÿE$ ÿE%ÿG% ÿ.0zÿ9ÿJÿ %aÿ6-YÿD%ÿD$ ÿD$ ÿD$ ÿD#ÿDZÿ=¼ÿ)‹Áÿ<”ÄÿZ¦ÏÿLŸÍÿn¯ÿ f¨ÿ+ÐÿK¥ØÿT¦ÖÿJŸÑÿ;—Ìÿc¶àÿk¿åÿa¼æÿN¦Ñÿ8Qÿ8 ÿ7ÿ7ÿ8ÿ0*iÿ+$Zÿ6 1ÿ:ÿ9ÿ7ÿ7ÿ7ÿ7ÿ/ ÿIW¡ÿ±×çÿW§Ôÿ}¹ÿ bªÿb«ÿ(ƒ¿ÿaºâÿ?¬Úÿ`²ßÿ\«ÙÿFžÐÿ8”Çÿ1Äÿ'…¿ÿZ¯Úÿq¹Ûÿ:Lšÿ/ ÿ0ÿ0ÿ0ÿ1ÿ2ÿ0ÿ0ÿ0ÿ0ÿ. ÿ, ÿ-.ÿ=>uÿ}—ÐÿÏêôÿ ÒëÿKŸÐÿ+‰Ãÿh´ÝÿÖòÿxÈíÿ@«Ûÿe¶áÿi´Þÿd°ÙÿZªÔÿ0“Æÿi­ÿCÍÿA“Ãÿ,=‘ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ/ ÿ0 ÿ.ÿ4-_ÿIc©ÿs¨àÿ¯àúÿ¹ëøÿÇé÷ÿÄäóÿo´Üÿg¶ÞÿwÂæÿ{Ãçÿ|Æéÿ_·áÿm¼äÿh¸áÿk·Þÿj´Ûÿ#‚½ÿy·ÿTªÕÿ9‘Äÿ*9Žÿ1 ÿ0ÿ0ÿ0ÿ0 ÿ/ ÿ, ÿ- Kÿ*DŽÿ?}Èÿs¹ëÿÓöÿŒÝùÿ¬æúÿ¨Þöÿ«ßôÿ°Ûñÿi³ÝÿZ°Üÿq¼ãÿ|Áçÿ‡Æåÿ|¿àÿl¾æÿ|Âæÿiµßÿ[§Ôÿg­ÿ*ÅÿP­Úÿ=–Ëÿ.8Œÿ0 ÿ0ÿ0 ÿ. ÿ.9ÿ0<~ÿP{Áÿ~ºêÿ?­áÿD·åÿŠÔóÿ„ÏóÿyÉñÿ‰ÌîÿËìÿ†ÆéÿŒÆèÿu¹áÿl·ßÿqºÞÿo·ßÿ—Ëæÿƒ¿Üÿh»äÿ|Áæÿ_²Ýÿv·ÿi¯ÿD¡Òÿ]·ãÿV¤Ôÿ16„ÿ.ÿ.)ÿ00jÿ9_¬ÿ7ŠÓÿF±çÿ‚Ùöÿ±ñÿÿ˜ß÷ÿ9¬àÿpÆîÿoÃíÿl¿ëÿyÃêÿ‹Éêÿ’Çéÿ›Ëêÿ‰Ãæÿy»ãÿŠÁßÿLœÂÿz½Ýÿ—Ëäÿe·àÿ`±ÛÿAžÐÿ t¶ÿ$Åÿ\²ÞÿƒÍïÿ€¾àÿ1D¨ÿ3L–ÿ9~ÊÿA¢ÞÿY¹åÿO¹åÿ[¿ëÿÚ÷ÿ§èýÿÁôÿÿ×òÿJ°áÿY·æÿn¾çÿ…ÆéÿÑîÿ ÎìÿšÆèÿs³àÿk·åÿf¯×ÿ/ƒ±ÿS–¸ÿbŸ¾ÿO¨ÕÿW¬×ÿ8’Çÿ€¾ÿQ­Üÿ„Îîÿ‰ÔòÿG­àÿ‹ÒÿX¸æÿ[½çÿV¹æÿX¶åÿU¶åÿsÅìÿ‰Òòÿ¤ãúÿÄôÿÿµéúÿY´ãÿ_·ãÿÏîÿ–Îìÿ•Ìêÿ•Çèÿu²Ýÿ?”Ðÿk¸æÿk±×ÿ6‹¹ÿ’ÿ#v¨ÿmžÿ)q§ÿs¶ßÿˆÆëÿžÏîÿ”ÍìÿˆÈêÿÄèÿ2„Áÿ_ªÿU ÿl¦ÿg­Ñÿg±Õÿ]®Ôÿ^­Óÿc®Óÿ=’Ëÿq±Úÿo¥Ðÿ6w´ÿC˜ÿeªÿRªÏÿ\Åíÿ<Œ´ÿ5}¤ÿt°Ïÿ¤Ùòÿ‘Óòÿ~Èìÿ:•Êÿk®ÿh©ÿS™ÿn£ÿa°ÖÿZ¯ÕÿI¦ÏÿM§Ïÿg¶Úÿ<“Éÿ!½ÿ\¥ÿc­ÿ{½ÿo®ÿBŒºÿUÊùÿUÉùÿS¾éÿS§Èÿa¡¹ÿW¤Çÿ?¡ÓÿDŸÒÿJ ÏÿÌìÿ‘Õðÿs¾áÿZ±Ùÿ<£Ïÿ4 Íÿ€Èäÿ„Éèÿ^¨Òÿ4Äÿ`©ÿk´ÿE£×ÿX¢ÿ=½ÿO¹æÿI±àÿI²âÿJµæÿM±ÛÿKœ¾ÿI‘°ÿc¥Åÿt¿ãÿ¤éÿÿ°îÿÿºñÿÿ áøÿŽÔïÿžÛñÿ£Ùïÿz¿Þÿ7“Æÿp³ÿT£ÿ[«ÿ4›Ôÿ4‰Áÿ?‘»ÿBœÇÿ=–Âÿ=–Âÿ=–Âÿ<–Ãÿ>›ÈÿFžÆÿSš¸ÿY™¶ÿ€»Òÿœ×ïÿ›Ýöÿ„ÕóÿÙõÿžÙñÿ¤Ôêÿe«Îÿh°Úÿ9„ÁÿW›Ðÿ {¿ÿ3•ÑÿU­ÙÿS¡Äÿ3¨ÿ0z¤ÿ1{¥ÿ0x¢ÿ0x¢ÿ0{¤ÿ/z¥ÿ0~©ÿ8…­ÿJ®ÿf¶ÿ|¼Ûÿ‚Çéÿ…Èçÿ‡ÅãÿœÌãÿl¬Íÿ¢Ûóÿ}Êïÿb¹éÿe»èÿ“Õòÿ†Ææÿ\¢Àÿ$bŠÿ%`‡ÿ$b‰ÿ"}¥ÿ#q™ÿ"_†ÿ$]„ÿ$aˆÿ#_‡ÿ ^‡ÿBŠ­ÿ_£Äÿ]¨Ñÿg­ÓÿˆÃáÿ„½Ûÿg¦Çÿ˜Öóÿo¾ëÿP¬âÿlÄëÿŠÕóÿj¾äÿM—¸ÿGlÿEiÿHmÿ(ˆ¯ÿ8’·ÿ(€§ÿbˆÿEjÿFkÿ?eÿ:x™ÿ=‘ºÿ‚¹ÿKÈÿ4нÿ:½ÿ:‰µÿ Ü÷ÿÈðÿP¯äÿ]¿êÿ}×÷ÿiÈìÿL²ÿ *Mÿ )Kÿ .Rÿ$w ÿ:£ÿ:…§ÿ w¡ÿ +Oÿ *Lÿ$Fÿ0g‹ÿ PŠÿe ÿi³Õÿv¬ÿu«ÿL›Âÿ’ÓóÿÐôÿbÀíÿyÕóÿ™ìÿÿ’åùÿM…«ÿ2ÿ1ÿ;ÿ!i•ÿ1qÿ1s’ÿbŽÿ3ÿ3ÿ *ÿ'R|ÿ;…²ÿxºÙÿ`¯Ôÿ„¸ÿAšÅÿ}»×ÿ•ÔóÿŒÏòÿ‰ÒôÿÞùÿŽâýÿ€Öôÿ=s¥ÿ,ÿ,ÿ9ÿ]Œÿ)`{ÿ*c€ÿR„ÿ-ÿ /ÿ$ÿGyÿƒÈåÿ ÝôÿR¥Ïÿ†»ÿU§Íÿo³Ôÿiºæÿ†Îòÿm¿êÿW®ßÿ_²âÿT®ÞÿBr ÿI>4ÿI?3ÿGDBÿ%X…ÿNgÿPmÿ.W~ÿL?4ÿI@6ÿJ;-ÿKf…ÿ”Ëæÿ„ÉèÿR¦Ñÿ=šÈÿTªÑÿ}½ÙÿN¤Ôÿ[¯Þÿ_²áÿ3”Ìÿ^¦Ôÿ‚ºßÿb‚¢ÿ†b1ÿ‡c1ÿeBÿ)O|ÿÿ .Lÿ?KlÿoM$ÿkL'ÿlHÿXgƒÿp°Øÿr½áÿh´ÚÿV©Òÿ8˜Èÿo¼ÝÿO¦ÕÿK¢Ñÿ5‘Æÿ?šÌÿP¨×ÿ7’ÈÿD^Œÿ]9ÿ\;ÿZ?1ÿ1gÿ'ÿ8ÿ:=dÿ^;ÿZ:ÿ\7ÿIW~ÿK—ÈÿR¨Óÿ\­Õÿs¸ÛÿB›ÉÿL›Èÿ.’Íÿ?œÒÿ7–ÌÿH¤ÕÿM¨ÙÿL¢ÓÿAU†ÿK' ÿK) ÿJ/*ÿ#_ÿ&ÿTÿ;0OÿL) ÿJ*ÿK&ÿ?Lyÿ*„½ÿ>˜ÆÿZ¨Ïÿg°Öÿs°ÿ}¹ÿD ÖÿJ Òÿ?šÎÿ_³Þÿ^¸ãÿPªÚÿ8H~ÿ9ÿ9ÿ9ÿ'%eÿ+:ÿ8#ÿ:ÿ8ÿ8ÿ1 ÿEH|ÿŠÁãÿ4‘Åÿs´ÿo²ÿ8‘ÇÿC­Ûÿb²ÞÿX¨ÖÿAšËÿ,‹Âÿ4‘Æÿd²Ýÿ7>xÿ. ÿ/ÿ/ÿ2ÿ2ÿ0 ÿ/ ÿ,ÿ.)ÿFHtÿƒŸÎÿÐìøÿ|¼ßÿ7‘Èÿl¸ßÿ†ÐðÿQ´áÿi¸âÿiµÞÿg³Úÿ‚¼ÿ'ˆÀÿC—Íÿ*1pÿ0 ÿ0ÿ0ÿ0 ÿ-ÿ*ÿ//YÿLk§ÿk¬ãÿ¡ãÿÿ¸íüÿ¿æ÷ÿšÌèÿ`±ÜÿvÀæÿÄçÿt¿ãÿp¿æÿs¼ãÿ]¨Õÿp²ÿ>žÐÿBœÔÿ-0kÿ0 ÿ/ ÿ- ÿ-$HÿF]“ÿdšÓÿ6¤âÿzÑõÿ…Õöÿ„Ïòÿ‘ÏîÿŽËëÿ†ÂåÿiµÞÿo¹ßÿ…ÃäÿŠÃàÿn¼äÿn¹áÿ"½ÿw·ÿY²ßÿb®ßÿ4.pÿ-.ÿ2C{ÿ?y»ÿ>¢áÿyÔùÿ´ôÿÿtÌîÿYºçÿkÀëÿq¿éÿ‹Éêÿ—Ééÿ•Çèÿ|½åÿp±ÔÿV¢ÇÿŠÂÝÿZ¯ÚÿN¥Óÿ~¼ÿA¢Óÿ‡Ïïÿq½åÿ)nÂÿK™ÖÿL³èÿVºèÿY¼èÿ‡Óóÿ­êüÿÅôþÿ`ºæÿ\·äÿˆÊêÿ—Íëÿ™Ééÿo®Üÿb±âÿY§Ñÿ6€ªÿEƒ¦ÿI¥Óÿ<˜Êÿ)ŒÅÿzÉíÿY»èÿ0©âÿ9³æÿnÆíÿe¾éÿa»çÿlÀêÿ€Ëïÿ”×õÿ Ü÷ÿr¾çÿÄèÿ”ÍìÿšÎêÿ‘ÆåÿM—Ìÿs¸âÿw·Øÿ6…±ÿu¨ÿ(( ¡Öòÿ¥Øóÿ¢Øòÿ¦ÚðÿW“Æÿb§ÿ%ƒ¿ÿwºâÿu¸àÿx¸áÿsºãÿt½äÿÄèÿ,†Çÿw½ÿ~»ÿp²×ÿ½Üÿ{ºÛÿl²ÖÿŠÃçÿª×ñÿ˜ËçÿC}¸ÿA”ÿ7¸ÿ(z©ÿA‚¯ÿ{»áÿ§Øôÿ˜ÑïÿˆËìÿC”Êÿ_¨ÿH•ÿp§ÿl³Öÿ]¯ÔÿS©Ðÿ_®Óÿ.‰Äÿ?Åÿ'fªÿ_ªÿk¬ÿQ§ÑÿYËøÿE¥ÎÿR½ÿ‚ºÒÿuÀäÿR®Ýÿ(†¿ÿ=ÄÿG’ÃÿA”ÂÿS­Õÿ9¡ÎÿO¬ÓÿwÁâÿT¢Ïÿ%»ÿS£ÿ:—Ðÿ b§ÿ?“ÂÿP¿îÿN½íÿNºèÿNªÐÿF•¶ÿH—½ÿb¯Öÿ”Üøÿ¶õÿÿ­èüÿÍêÿoÂâÿ–Õìÿ‡Èåÿ6‘Åÿk°ÿP¢ÿ%‹Êÿ2‰Áÿ@•¿ÿAÈÿ>™Åÿ>™Åÿ>›ÉÿBžÉÿL›½ÿYœ¹ÿw¶ÏÿžØïÿžàøÿŒÚöÿŸßöÿ§Øîÿp³Óÿh°ÙÿM–ÍÿA‘Ìÿ6“Ïÿ]±ÜÿQžÁÿ.y¢ÿ0y¢ÿ/x¢ÿ.užÿ/v ÿ.y¤ÿ1~§ÿ>…§ÿ_›¶ÿy»Úÿ€Åæÿ†ÆåÿšÍåÿu±ÐÿœØôÿoÂíÿ[·çÿÔóÿ‡ÊéÿQ—·ÿV}ÿ V|ÿ!vžÿ(¨ÿ dŒÿ W~ÿ XÿS{ÿ9|ŸÿU ÄÿJÊÿf­Ôÿs´Øÿ\žÃÿ™×õÿjºéÿQ³äÿ~ÔôÿfÃêÿCˆªÿ5Xÿ4Wÿi‘ÿ<´ÿ2Нÿ]„ÿ4Xÿ3Vÿ/fˆÿl¡ÿ,ƒ¸ÿC•Áÿs¬ÿ6Š·ÿ—Öõÿ~ÉñÿV¼êÿãûÿŠàùÿEzŸÿ3ÿ4ÿS}ÿ3zšÿ3|žÿNwÿ3ÿ2ÿ"Lsÿ)lžÿg«ÏÿA™Åÿ ƒµÿm±Ñÿ“ÓóÿŒÐòÿ‡Ööÿ”åþÿƒÚ÷ÿ5f–ÿ'ÿ)ÿFuÿ+fƒÿ*hˆÿ 9hÿ'ÿ%ÿ>lÿ†ËçÿÐëÿ0‘Âÿ<™Åÿt¶Öÿh¹åÿ~Éðÿ_µåÿY®ßÿa³âÿJp”ÿSB0ÿUD3ÿ4UtÿMjÿQsÿ?RgÿVD0ÿTA-ÿSdxÿ”Ìèÿr½áÿHŸÌÿH¢Íÿx¼ÙÿS§Õÿ^­Ûÿ>™ÏÿK›Íÿu³Ûÿdyÿ[*ÿ…^.ÿCTmÿ7Rÿ<_ÿ][^ÿ„^*ÿZ&ÿlqzÿ¿ãÿn»áÿ`®×ÿBžÌÿh·ÚÿY«×ÿN¡Îÿ3ÃÿR¦Óÿ?˜ÌÿK`€ÿfAÿgD!ÿ6@cÿ $9ÿ *LÿOFPÿfCÿd?ÿTZqÿ`§Ôÿa²Ûÿe²ØÿH¡Ìÿ\¬Óÿ9˜Ðÿ=šÏÿ:™ÍÿK¨ØÿGŸÔÿDSxÿP, ÿS1ÿ+.Zÿ(ÿ RÿE4?ÿP.ÿP+ ÿAHhÿ0‰ÃÿHŸËÿo¶ÙÿD•Åÿ|¶ÿBžÕÿB›ÏÿL¥Õÿ^·áÿR¬Þÿ:Doÿ:ÿ<ÿ,$Tÿ$>ÿ4)ÿ:ÿ9ÿ2 ÿCAgÿs³Ýÿ,ˆ¿ÿ"w¶ÿ*ƒ¾ÿ;¤Ôÿc²ÞÿU§Ôÿ:–Èÿ*‹ÂÿYªÚÿ58gÿ. ÿ/ÿ1ÿ2ÿ0ÿ.ÿ.)ÿIMsÿ‰¤ÍÿÊéøÿd­×ÿM Ñÿ„Ììÿ]ºäÿl»ãÿp¹àÿDšËÿ€¼ÿB˜Ñÿ+-_ÿ0 ÿ/ ÿ/ÿ/ÿ/1Wÿ;h¡ÿo¯ãÿ‘Ýÿÿ°éüÿ±Þóÿ}¾âÿg¸àÿÃçÿÂâÿsÀçÿj¶ßÿw·ÿ3•ËÿQ¥Þÿ0(Wÿ- ÿ1'Dÿ1Tÿ\ŸÕÿŒÖùÿN½ìÿ|ÏóÿtÅîÿ†ÈëÿÇéÿ„Áåÿx»ßÿm³ÖÿÇâÿc´ÞÿBœÎÿ}¼ÿi¼ãÿw½æÿ1W¥ÿ>zµÿK¨ãÿP»íÿ~Õöÿ¹òÿÿ˜ÜôÿP²ãÿtÁèÿ”ÍëÿœÉêÿs³áÿkµßÿ?·ÿX•¶ÿK¦Ôÿ-ÃÿPªÙÿoÅíÿ<®äÿ>²çÿjÆïÿa½èÿf½éÿËïÿÜ÷ÿ˜Öóÿp¼åÿËëÿ›Ïëÿ‡¾áÿSžÓÿ{½âÿI“»ÿs£ÿ(  @¦ØóÿªÝõÿ£ØðÿRÂÿf¨ÿ?Äÿ}½äÿ}½äÿx½åÿ‚ÅéÿF˜Ðÿlµÿ*„½ÿ{ºÚÿyºÚÿl²Öÿj¯Ûÿ…»ßÿBºÿO›ÿG¡Ëÿ8ºÿW›Áÿ›Ïéÿ’Óòÿ]­Ûÿn°ÿWÿ.³ÿ^±ÖÿI¦Ïÿa±Öÿ<”Èÿi­ÿ m´ÿm®ÿJ¥ÓÿUÉøÿM´Þÿ]«ËÿQ¡ÅÿFËÿf·ßÿ–ÚóÿƒËéÿ[µÛÿe»Ýÿ‡Êçÿ7Åÿ ^¨ÿt»ÿ,‡Áÿ@˜ÄÿB¡ÎÿAŸÎÿ@ ÎÿCšÂÿS›»ÿr´Ðÿ£Þòÿ£åûÿ”Üöÿ©Þóÿ}¼Úÿi±ÚÿNšÒÿ=—ÑÿdµÞÿL—»ÿ+rœÿ-w¡ÿ+uŸÿ,q›ÿ.w¡ÿ4{¡ÿV•³ÿw»Ûÿ|Âäÿ•Ìæÿz´Òÿ•Õôÿ]·èÿyÌðÿÌìÿA„¦ÿDjÿa‡ÿ.‹±ÿ$o•ÿQwÿGmÿ0m‘ÿ;¼ÿE™ÆÿRÉÿI“¾ÿ—Õõÿ_¹éÿqÏòÿÚ÷ÿ8m‘ÿ8ÿAgÿ6„§ÿ1‚§ÿ :`ÿ;ÿ LpÿgžÿT¡Êÿ{±ÿI˜Áÿ“ÓôÿƒÏóÿàûÿŠàüÿ+V‚ÿÿ+Vÿ+j‹ÿ'g‹ÿ Jÿ"ÿ7bÿ‚Åãÿi·Úÿ*Ž¿ÿo´ÔÿkºåÿrÁëÿT¬Ýÿe´åÿRkÿ^D'ÿHR[ÿNoÿ PtÿQOMÿ]E*ÿZdnÿËêÿa±ÚÿDŸËÿm·ØÿXª×ÿL Ðÿ?–Êÿ`©ØÿblvÿTÿ\TSÿ4Sÿ;^ÿnWBÿ{Rÿjhjÿu¸àÿj·ÞÿM¤Ïÿ_²×ÿG Óÿ;–ÊÿE ÑÿCÔÿJPeÿ\4 ÿD9Gÿ9ÿ'SÿT;0ÿY3 ÿIJ[ÿ@”Êÿ_¯Öÿ_«Òÿ6ŽÁÿ?œÓÿ@›ÏÿU¯ÜÿS«ßÿ=>Yÿ>ÿ4#7ÿDÿ0+ÿ=ÿ7 ÿB;UÿY¤Õÿ/ˆ¾ÿ0…¾ÿ/•Êÿc²ÞÿQ¥Òÿ,ÃÿLŸÕÿ4/Nÿ. ÿ0ÿ1 ÿ-ÿ0#ÿGNoÿªËÿ»áõÿP¡Ñÿo»âÿiÀçÿr¿æÿa­Øÿ½ÿ>—Òÿ-#?ÿ.ÿ-ÿ<;WÿDq¢ÿX§ÝÿˆØÿÿžßùÿÒíÿt»áÿv¾ãÿˆÆäÿm»âÿ1Åÿ3“Êÿlµâÿ2=uÿ7V„ÿAËÿoÊöÿªñÿÿiÇíÿh¾éÿˆÈêÿ•Çéÿz»äÿZ¤Ìÿk¨ÇÿJ¤Óÿ/Æÿj¿çÿOµæÿC«æÿcÃñÿaÀìÿ|Ëðÿ©ãúÿ‚ÊíÿzÂçÿœÐìÿ¸ßÿd­Üÿ\¤Êÿ&v¤ÿcrispy-doom-crispy-doom-5.6.4/data/doom.png000066400000000000000000000705101360717211000206140ustar00rootroot00000000000000‰PNG  IHDR€€Ã>aËgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“tIMEá 7 £ãzÃpLIDATxÚÔý×ÓfYvÞ‰ýÖ6ǼösùefU–é®ntƒ‡Ä’n¤]H#EH3Œ‰‰‘.t­;üº” ‰ÒÅÄHr"8CrÐèÙhSÝe»ªÒ~öuÇl³t±Oft]¦ ̉ȪʬÏåÙk/û<Ï’¯ÿŸþŽÆ4 šhׯ`ý)ˆ†Ü?åâá÷ÙÜ\@©°¼ñ»ÀòÞ—pÍŠju†s–íGßåpõáâMˆ7øù¶šágKŒ5cQ‹  dTˆÅ¤ÈÐ|ê3ÄxÒð1 ¸cÄ´¤Ô‘Çkœ­@*0Í o@2¦:GPrÜA0®BŒkªò÷2;;EcÆÚ?Cñ(™úøË`rèˆÝÕìÖ5l>ø· ›w0Õ»x©ElÁ“ÉÄ4bÄ"ÆÆ[Œm°v±S­‰!ãÛœ£R‘c‡H&+ÄÝSRIèoé¶WÜ>½Â ¼òÚ=êõ븣WÃÀØ_‘Sõ5ÖŸÒmŸ2v—ÄГݚ”2Ýá†Ýãïpóð‡XÛR/— Ö/°¾Bt@4bÿWÿÃÓ?T¬5¸j…±AMr<°ß\¡)¢IÑÖçw±Í ªŽ40Ö²¸÷›¬^ÿ{Ô'¯1n¯ˆý-š#9t䨑SDQP!©M¤œÐДÐIq@ñ˜úÌ r@s,š{r‰hD¤"‡=¤=šÊAŠ_ *hÜ¢q$§-b<ÖµˆÄXÄ·¨‰ˆ*‹mN°4 ŒøÙšjqŽkÖˆ a÷˜¬#b ƶ¸j…˜Í#˜bØš2š#š{TÖºbè&ã\CŠ¡†1àZT4‚ (¨‰ýžýÕo¿ùŒªŽŽ²‚j$çHŠ9%²4ä@ È*¨bèãžq{Ÿۃq(ŠÆ‘0ì0Æ—÷óŸþO^ÿCPŒ&êÙ]Œ­KJ#ð¾&ô;Û=yTlRÚå ë ¥¤HNl…¸†æø%–/ý¶Y2nž»4 h䨣È#iìÑБÃGR†¦>ÁTG( ÁAòxM®!÷bëéEТ€Á¸%9îÉq‡‡kN ¨fÄ5ˆm2šHÚ#¿~}2~K5?ÆÕ3Œ\s„Xš‰ÃM#¶9Æúeñ<9u ¨ ‘Ë«87'åšPÕT¼PTS1\Ä¡1»+Âá š)gRÙ^íÙ÷/=8ÆÕs¬[aÜÄ‘ÃH¬­q„Б²’ô‡KRè‰IÇ@FŒæL = Å‹üoþÇþЯ0~…bÈ0Æ VÁ¸Ìöú@wÓSy©WGHVrŽd GrØ£°UC{òóû¿NJ‘îâ=rê!kñ$q¦€f%§D {r8 bñË—±Õ9š#h¹hBŒñ(RTµ„ Í MùwÐp$LµÂØ%bª#Dš:L5‰hNˆxÜüâÐŽj~†©˜z>}\ ù€qŽ4ìwˆ­%× "äqK÷FœóÔ‹;øæÄ‚D,¶Y!~Aì·ˆ«ÁÖ€N`IÖÐ_¢±/—" 䨓ÃÈæê–;ÇPµór>¡ÆÏÉq@ÅSBU†Ž;b…¦L×Ev±!†D8l )–И"‚ÅUí=Ä(Ƶˆ8RŽå3•Œ Tõ’ÙÑ‚ËG7Ô¢ !0JÌ7Œ¶¸Ã¶¤pß®pÍÎþÆÿãçܾý¯ã#=ÆYÄŒ€ZܼF¬ñ䨣i,.-W,b*W¼K¸.Þ!öhèÉÆ•ÿŸ#ʈÁtÌ ŠT5¶=&‡Æ{Ĺrã\ I<М½Fý.q¼!‡]ùÞ)«HÃã*l5£^ÜÅÍQS#1ƒ«4‚_’C¦\1Ž,‚àQ-oÂUG¨™Ì.p–s°§sïÐ,Zªc‡«OˆydR<µ&Œ™Ç=ñðŒ¤͉qGÅTç¨;c¸y3l‰0kÉ1aÿÿ‹ßùCƒjDŒbý…ò‚&·•SÀ!ñ0P/*šåã c*²r*.^cFÓHÎc<³³,_þ[øåˤ¾c<ÜÇžBH1’S,®·Z’³"!%Ö;å‡rðšAbÛ’ÈÅU%¥=¨ïAœO‹h@5Cã°íº¸èz D¬ó%Ö[¦.·Üð5˜zJ\!;úí#D1ˆh<àê%Æ9ÄÏK-ÅSiè GrˆÃWµ˜jŽÆ¡ü"CÉÃc*ÀÂóðö83’Œ ÔuÆzÆ"¦\‚ÆxT•q÷ˆþpI #ÝöŠÃÕS†ÃæŸÃ´w°³cÒØ1î®pUMÕ.°ÿ»ÿÙ—ÿ0§r‡ SŒcjbꦃ¬ŸS7™ýÅ5¤D³š—ÊÀXRÜ#’cjrÚCÜ¥B¬ÅÍVÌÏ¿ÌüÁß¡Z¿Fw{Éþæ Ý¡ccñ(~QÜ£ ˜ª$Yi„œJU’öˆ­ šú)ñÉyDL±äÑ<`l…ñkL5舭–%s·c!cë¦>*UŠöa:|_¼@UÜ7á@ù‚CŒ!Ç=hMäx@S±•’»ˆ*)ý 1ì5åÿÇ¡ä- ¤P‚?Ã|k }ËP]/‰7ÊnHìú¤Ä”A-)Ž¥LL»’<FŒ™O$‚ä„’ÀBBSƒ1’±~^„ˆb«ª¾AÔÿÔ Zßø’ÌÅŽþâMv—ï¢:åIb°bU„ŒkN1Ɠî$‡9£SkLb Æ5äÈ¡+Ì”öyZˆOµ¼T RØ×xFÚõLsŒŠ©ghJìž½G;PGèìoÓ®=¡njœt˜ø 'ckÔcÆÙjÉlqŠ`ÉqçHq(µ%¶„´” ÖS¯Ö®· û=ŠÁV–œãæh®qfÀˆÇØ9ÈòHÌOÉý‡ˆo‘úˆ ÛmÇÅMÀÇÌRÛU,'Ï”nѳ¿¹¸wÀžÎ©gBcG*kñf‡­2¾>‰Á¹Ò”\‰E0“›»Iã%YgTö¨ÄÕ°u‹Tí”}ïJ.ã—`K¨Ð<"®Ü`â㎰ùó’Ç-~vkH#b-iìc±Öï”F¨3rÎøæãj4ô4¬8\»7# °†X™Jæ@=¾^²zéoR/î’oßFQÄÍ€Më´'3äÝo°»ø×öln’«cV÷zy%‘Â@Š;ð<^b¢¢•àPq-‹³30-)l!Gâ°' =Ýæ!qÜággd©qí–ŒaÜ`‹…Ôm GÚõ¨bv°F-‚H$%£ØÔ!9rKß%6›žy̬Þìàÿò þÞRø;¸áýÈ~ݱ»£lÎn%Ìf†Y+4i n5B(Í뵤qz(Íã!¤°Á8:CdQ§Ø#F± ñÇ9Dˆ é {RÃáÙÙß¼5Á“SÀZÁXO6#¶š•wªlRƒHUò Íäœ@À˜ ë[ŒoÁÏAZ=h Ç„Úò3‹X4öŒÝšÆ×ŒÃ3¡j׈­Pc‰¡ÇV Ö¯þ.¾ÇvwÌîág'øö˜ÙòHñx)ÜbDñUÉRÄÅpæ“멆_b]‹­w4vŽ«ç\ýèˆÝš-:îUú[ ª™jÃØß`¼ÇØ»øª"õ·× ÖMeŸC¨0º[ÆÛÈa³Å‡þ›[øç×ðr ÿÞþÑ ~ë Ü¿€ÐvëÄþ®°;ií,2oMí±6asÀø©_0%a9î#¨ó ¾´‰å˜X_—ƒ0Rš9y(¡ÄxÔÔÐ_’‡ †ëG\¿ó¯Éã ¦>&§ÎÏQ ¨Œ-U­ä⸠nþ2:ˆz‹‹i–ÅÐŒ‡Éi Æ€c,jKÛZsœ:“ã|1ncÈq$öÏpós4g$'rŠÌÏ>ÏìøUÂö)®]âš¶®P3âM¦bK»žÑ¬Ï±Õª4ΤÂåqOŽCé•;‹Ø#†œX_Q¯^æô•ßáöé÷é»'ˆô´Kǰl®a%Je@ºžAÓÌÎËçëŽÔoÁW˜äqÞ’EK>– 9Ò¸'æÒpRÎ! ðAÿŸkø|¿¿„?X ¿¶w?ƒ~6°?M\o¹=ïh× ³ÆÒT–Ê VFŠÅJDµô4l3'.±õªtîÄ`}=…º±”av‰¨‡[ú«¸ùðÏý5vÊ'êzF½z­äKi(]JÈñ€ñs0 b*Òx(F˜6XWP•âe4@EøãZT‡RåjÉà<®Y‚fT·xퟔ˜©0¶Â/_ÂT ÒØ±¾û‡ñ€óÖ¬7>dæ…ÙêŒúøsHsŒÁ¢bÁxœƒ8Kι?”oæ—Äþ)9 h¿fyþ[X÷‡Û¨W\Dº[e¾„4&4—5#dçŽHqK7%û6GHUO7MÑTòŒ,RêhaÊ…§G`Èð½=|ïÿ ø³âþÞ^ÝDN>R‹[öwnÙßsØcK»¨i+¥¶•wT•Ç:S\žØ©ì S©BÃŽ,;»Æ ¶)³‡þ ªpõÁwnßÇXƒuíê%šÕKˆ¯Á,°D”p¸@äêjl}2õ):Œ±QrØ`tF"€³/f2&rÎÄajJ°c*L;+mâÐ" ÒÁ •QÒ80ì/qõÒ®Sa4²EÇŒµBÖRÆå4”®à(àÏ=ÓŸm|u_ÛÂ{ÿx-üîA9¿ü~`¿ÙŸwlÏÕqÅl¦ÌZhkO ¸F¦&L h±¾f2•—tغ'Ü N3ûâîrI»_Š¡ ‚üòÕÌ·@ƈ#iGÝÎyõ7?ÏÅÒo¶X…Ðw„nƒ®î‡Ž×Õ2ÆUWÜ]¼%!(-†ÉôÇ®üWaû _ßÂ7vðvÂoÏᯔ¿³€û·Â~©ìÏO_ÚSŸ–G-‹˜-çTZâºË’¶(S͈I!ö¥‰#–œ¬ghO_AU'£—‚v2¦$¾b bȘ2°Kì쌴»Â¦–8†°Ifˆ÷eŒ-¾€Q¼’ˆ¥ÝìKƒK,Ö5䔨ÚÆÎ¨š5¡»"[šIñ) ¹›jÒŒ± ±»$§7»‡OŠ{RÜÈX½Â’ÅRÏ眼²æúƒ‘~Û#ÀØu„pÀT âfˆi1Ö“Æ@LÃÞÐò2P¬È¯ÎücPà*Â}ÿòîÿD ú·Ž{WŽø¾r8íÙ<¸åðŠ£9ó,çÌZCÛ:œ*Î(šÊ<Â8_ ÍÔ¸Êcê 0oÈ![l½(^7Gòj­¯±í¸9UXЦ„e6 Ý dCJB5oñ³3Œ_‘â¼{q‹k[L³ÆÏN±„aGÎ_Ïñ€˜šD,ÓոÈâ 4µb™okÄ6§[ÊÕ8µ(…牉µ-¾9b÷ˆ@½rn„Ð)¾„nKÕ¬AWÏÑœAjbÚD4ü8øwxþ?k øp€{øÿ^•ô?XÁ,…/í«‡·„ïuô/ï¹øü@õà”Åé’Ö mÝRYpÎ#’1&âæ+Œ«Á´w!Œ ˆ© |;9ö¥ô®_"g-¹s,15SË~±5Ö;\}‡jù9rÜ’ú TO‚f…©Ö„a iìˆq X_ãŒ`5•Œ`­ÇaÛÒùJ¶ZOXõ-9T«/“ՠݣÒ&7¨A„”””:2ƒñø¶Áú)(qL„®#/±¿ES’JÑ,!‚S^Ò¯2ø˜Æ0hi?¯ƒÿçÔ‚þ÷Wð÷÷#¯^?Á¿uÁx2cóÚ·¯­hïŸ2[ÏiüȬñÔó£’ ª`TJßH ¸ ëgäý5ãÕ{`®9ÂT Tì4 šÀ$jÉÙ1Ü>âpù¦:ÁU±eb8. pÕ-Ðîu5¦^¡’ ‡§¤ÏþÁOƒÏüó+ø7𠆿·‚´üæî‚õWäUCÿÊ9‡7Îñ¯œ1;³Ìæ´•ÃÛ€÷Ö â[üò.¶¹ Ò2ŽäÉŠ)gâ[\³!0bœÁ‡XK[¬±øÙÆÕ˜BA"˜âþ­«‘<`ëö^2w—V®{t¸Â‰ÑRê¥yO œzÄ6e“K)cG΀ñˆBŠ{ÂPb[ÈÖµçñóL¿ û‘qØá‡=©žãª¢ÓÉci{J©¯Ê_/ðCTx¯‡÷:å?»€/Ì„?8ÊüÃ}ÇnÞgþƒˆÇs6¯ÝgóÅ/P½ú í²b±:¦qï#UsL–Mr.©˜*a<”÷®‘ØïénÞG,e‚©#ñ°ÃÖKl½FÅ""Œ»+ÄWøÊÓ,îbÜ)ÃíÉ1U¿ø¹s8`!„‘±»"ÎU§äH±/h^iÉ1£iƒf!ž m”  Y3šb °¡P»rN„a$[AVHÀƑˬ[æóÍúU¶ï=$<»„Mùsÿ—Nƒþš=S úO·ÊŸîàÿúXù› áGþî~Ë-î»ï1žpý¹W¸ý™½ò€Å²¢=µ»Â¹ëæ† ²˜Ô“ÎÓ·9\¼ Fèv—T³qìIy€Ã3D¾=Ƹ‹Es"tO±Ín¶$õ4 ÄC­—äa[ð4îÈ©Ãeq¨¥÷{0¡1´@™ÂØÁx(e`.p±L{æLéŠ)Äq,ÙmˆhVÄ€ŠÒmoiæG´«;TUÃ|¹¦^³ «aÎáY1Yñæy_ð¿cÏ”/ÜFø××ÊÝ(wká÷ÖÂ?>øÝcÎ=A¿õ]†—^bøµ7h¾øË{w˜Ï”ªqyÜcœ“H±ãæáŸû]á(V+ÂpCì7„qK·yÊùç~‡zvDµ8£^?ÀXG:|TÆÑ¾º&™G4Hà 1nIñ0S;\™;'P‡ñˉï&%¹3ëj²&ÒØ¡©´E,1ìI)!R“ÂPX)ãUa~r‡j†~Çòô.÷>ÿ·8yí7ñ³#¬kºùòšR™ ˜åü_YøÙC ÊþDùgÏ൙ðŽ ÿèdÏolßbþî»Äoßeóþ‡ø×ÎpiDÃÉãOÐlÉãcÈýqËİ!Çji¹ó¹¼ôÆçYž¹@Χ>«jåÈ9 Öár„0\bÝëבØ_#R¬Ô;qÞ2‘8¬•ŠóW¾ÀúÞçï ‡K4Ë>Üñ‚8 © _\5CõÈÄ`é·†ý–¡Û£²ÆCí ÄúÓ‘…U>_¾X ÿÑ©ãí!ó•½ákËÛƒ°ÍòïÜÊ$}>!†ápCÎ'L<;†Û'\?|‹ÃvS8…cÇájÃîú€µ ¹ÐMR’òb–ÒÌÀøBH »?úæ÷¹üà#ü§Ü{ýK7+ê$i,îP µÞÅ¡X•Øšqûp’S1¤q[ÂANäÜû@ŠCi6ˆ¥U¬î½ŽøÅ”nHaYešhŸ~¦rº-ц± SâP<€1T^1nš ÈÏ?}^%ü¯+¦¼þ‹§WüÑÕ-÷xÅï®—ü““†ÿøÄòÖhøã½åkåѰËå{ɯØT‹`&Q[cê¶ìŸ|‡‡ßý—\¾÷g äˆb©ç-õüˆ”Ö:b?âê†0 n7h±ÍôM’BLVvO7¼yý-b0¼üÆ«…¿˜ºi¨7‚´¸qû×®{IN›"11s5ä€_âŽTøj`qö€¬Ž”fOÚwÄ”@*rÊ„~O<Ü"â±uä0®Æ73HįÑþ@»‰Ø (;T[ ÀØ2#ÿå^¾þÌï‘ç#f°"XS¾Ö&&¾q»ã››ÿôá3Þ˜cøõ‚ÿdÝð9~8Z¾Ú9þMïy/ZöÙ`>þ„*d£`"¶Ï¸zïßÐç-òí÷±V±~†«š ‚6`\‹©êùRìÊDÕµ˜zNÓ,©'ÔË;Ô«·è®ž°»¾áp½'%%Ä‚¸wo㇠[ÎîÌÉÒ jѰʼn[¡)bM nVúË®F%#x\]``9…Âæ‰F7O™W åÏ5e!öqìñ³£ÂŒUÈqõ D*ü|©õ˜p·OÛ£V#…Õãì'¾}ųh‰³Bùspó]N|c»å[Ûÿ¯ÇŽ/Ì[~÷hÉß^ÍùOŽjþ—8~ÿª«ùÿí[ú_Æ~‰Qµ¹x”ÍÃïpóô /XŽ5bJ—ÎUw0õãኘž¡»)0pcè®ß'Œ·Ô‹3Œñ˜æ„ù/2?}ÕþŠíÅCž¾ýzˆ@R!ÅÀß}HóÛKªv…T§ØJq¡ßb«y™îÉX=õ²(rY‹q5±ß8ŒId»-a_g«ÂÍ·eÌ9.1Õ¢(Œ™(R/ R—‰"¹L´Ru3Äõä`+xo°ö“wƒ@”lŠ«µVp/BÊŸ7)EÙçÄ7¶;¾µÝ±öŽÏÏ[þwŽøN×¼R%þxly”Ü/ä,èÇðš)´nS°~w™÷¸öuê£t¼Â-Ã7)ŒiÌ¥ÝÎ×XWU22)jE {ªö´h#¸~~ $ž½ûaÈ„Iˆì°I<}Ôs÷¾â¨Y¾ô»8¿¸G CwE½8Ç8‡«¾ VŠÌJ‘eAš3Ýþ,8g©÷ÛbÄà‡FRØâ›%~v^€Œ ÌLA¬ÇV5ªÏ øÄ”¬¸ä¿¼ü¼WoDÉFÀ(΂³æå“ü¬ŸÙåÌ×7;œ~ÿl…5Z*ì/ ~±Ïzn$jµ¥ñ%ZtŒK¤Ð¡d|Ýx—+<Ízu2 Ï"1L"V¶"ÇÖU“w0ÆL¢W`«¢°¾û*Î{nž>æâÃë1¸|h«L»¼`ñ’â¤r4ítÂäæhÂÕ+Ò¸+˺!§k4;¬i¨êcúÝaèpþp„ÝCLµÀUk £q‡˜Šœsiþd€ˆhŽe¨v%®‰C¦À:[ú*òSoð—63…5à&ðqâ‰Ñò¹Qc‹ˆÚ¿œµô—š&…dŒàÔÐfè¤ÀîÃá’jy4åLGdÍ8_´Ãþ#¦\¸Š¨#Íüœ¬Š¦žÙê%¬ŸÇ-¡ÛàN½fvæÀ›Ë[ú}ßw»ÄÍÓ"*%X¶òˆ]AñMÁ¹[Oöä0 È’0Í{t[ú€s[ü쩱¤ñ²hÑ4 4+aÿq3Rƒñ+\½,¹…Ÿa«‘œ·¨5/rcí'O»&zš<€µ‚û˜0£¬EIR°~µLþ¸ð‹Œ‹`´((=¯ sîw†ñ3b¨fk@Ðñ€ðíŠ0lHÁb­+9UÁV [žÐß ëPõøÙž³÷xüÎc4gR†aŸQsŠâªö¸äšÈy(‡ºÍ ¨¢f†5–º="Kìw“š˜EUC4“HÄé²EÐv}&[ÆÃ˜ŽfrÐÔ‘†]i9+ø)äÍÓO𒙚-F±NpÖ|, q1H’±F‹Qf÷ã²ô<¿Ì×7 ØçÜ?}!Há›%¾ö)9Wq¸-É8¡|¬Æ•ЧjOHÃ1…ì*Æ1ì/ ´«—ˆãPx‰Õ’±¿¡Y,XŸÍÙ\è7dÎéÛ¬q)ç&Qâ½£Æaë%)í[ì’3jÖÍÈ®ˆ8¦0±„˜ðÞ·o+H–”¶ˆf²&È Ûœ±¿~Ÿœ\½&ÄB¢Ìj‰9"¶}Q”ºðgŸŸwŒþPÊ «¸qkÁZó >÷¤êdàŸ{kyá—~mþ2æZ<@.9PÏŽiVP-–ÔËŠãrXDîoȹ›”LvS»\uŽñõѤ¤VTHrÜb}M 8ˆý Ýö!ãî1b+ŽïÝgyràé»Ï8ý¿Ç/ÿ>9 8_/¨/¡š‹Ü›5®]cS‘g‹18X d<KJ•¢‰“BÀ7KRƒs °E/ÛbXÆïâ*voãêc¢?›ÆË¬EŒàýä-A'Pœ)9€þÜàç?Y :+’@Á¤ÂïÿDÏÏ~šÉ/†_ÆÖ¸ÙŠfy†Ÿœ XCagûš<îpêÊ€ñTÍQQ"‘Hì¢8Œ« 4/gÄͱ¾-:̱`7BŒ%/áÚ#îýZËK¿ý;3yÀÙªÅ5ëBè´†a÷¤ˆ)bIFI¡GÄ3v—EÂÎ1R˜¥9%â°¥»}B=¿CuôEžK²å¬ˆ_–ÌAÊX˜ùöÙûìö–p¨HËöÇ!ÀYrþt ÆL9“üòQAEH’«4Œýð“¶ ‚Ú€sëL½¤]αڑÂk<¦=&h‡·#M{n‰q"–œ{ÄÔ ‡+T'ºZéwÏp¾)<Åz‰K ×ÝBž”O¤¡]Þ£Y•²ßXœ8G®0¶)e–4He‹n؉O&E'¤éF•"âÃÐmpÍ Õ"¼äÜšœãÄ¡«Ð8P™Ä‡-~È¤ë »«ž¡:BìÎ ÞYRú40…¡pßìs¾Ã/Û[T°¥ À(µã~™2ð—3Œ-9€ÉÆþ’œV¤dDŠÄ[bÐ4büo+¡ß]âfÇX«ˆk'ã0X7ÃN8¤"Ë:-C¶,œÕa©}Q&·‚³ƒüÜÚ]ÿÒ?(çœíO{€ü1ê@¥t%#Š¥qŠIîçÀ/—“ü<($SS8ó€äëN±(FfçÐqÍ1Vg`ÛÂÈ2uéÃà F‹:y¸ÃW5R¯ý‘ˆ5Eû9ÅD=U3cqö ÌLiÎ9ãçE (hô¥aC™Ý›¾räÜMäÂ<ÑÆjRÈ©¨UWº-Ãá–vuTD*EÇ5…2e*Ô 56ÏpÕ€oš£E‡›R²Yg0b>q+@yÑ ´SÂõË~=…bR  ¶ZBÀg1P¦ÌÔ &a}Mµ8¥j¨›9µ?&ÇŽDF4 ºDr*ÝÍaKÜÌŠÅ4sT¥ˆQah—o`«ûgßfým0sròdUœkX¿Qš|1]d2)0ÆMQ M®^ANä¼+I…)’.®ZQ/ï“CGè@‰³Y?N!¨ˆÒO€ý¬ ^x¬9~„˜ u®]¬Je•qÜ“ã¾|ì„ÚF™D·j\½ šß!…@wûc39+©ß”„Û.Á4„ñ†"Íâ.õ¬%öט:adANœh!f*öÏÕä)·Æ~‹1J;¿ nIй$š7Œ\3î.0nFwõ#¬s´ësŒX¢î ý ¹/›.ÂcõüuR\õ”<0u휵µ?s\¿ÜËÿI€)áÖY!é/_è~’0å: ¨ô/¯&™-Æ.¦x»ªYÊ8l ¾€n­· µ-qØÇ-ªŠ8bP­ðó#\µ"—ŒýÍa"󀌭Vä4 bñõŒf±Â93­Xò¸AÃWÍOÉTäx …jö2¡¿!tÏpÍ)Ä¡0L\SÄ óHÌZø}¹ðüSÉa$wŽMÖBYTS_¡èåjÊ:“¬˜zAå–¸*`«RžGÁM­WùEþçž¾ø§"dû<L³ýx·×ˆüD¨Ó|B?fÄÿbl…YE€žfq‚wŽœz4¤’Å;‹ÐPY_-å>\ám„á@è÷e´.”½CqS(éÕ ÛÜ!'ÁVÆþ¶l‚QSªhÜ’Æ—ðt×à›£¢T=aýs±¾)—Gœm_/„-6^ƒÙR™Yeðx舻[ÆÝ†fý €=r_$S08Ó Õ²Œ'%-Cxះ›í“ÀŸÛtùù¯þymò<0†’|̾BñЍl.¨Z>ÖHá牨 6MJEäÁ¹#ß´TÞ3ö·€ )Lï>‚°«0¶%å±@ï&Ü“8‡õE&&„c Õ4kã1ÆàÑÐcWË¢kÂØáB÷ë2ˆcGŽ—ô»§Øjư{Š«æx7Ç`Hã%¯i\DˆÞQ¹–bÈÄYÏ~»'u× ûKfGw©gwËú“Ôã\…qqØR‰ÊôfÅÈ4½+ÜAù8oû'Þr†}cKe?æU N! ²©4§>¡üä“_”%ë0õ1~vŠkTv6¤±ÇUU‘tën0$¢fÆþº¼3S¤êŒ_ dœ÷ ãf’ú]0ž1î.ÊŒA¡ª*f«sÜü.Æ·ÄqO”2.ÅŽG$BwK·„a[ÊÁk‡?¢¶-23t›á°§=¾OªèWˆ[el£ŒCG:\cŽîRµ b¨\ã«¢ÂYÕK†qÄíär†™’8ƒIÓ~ʵf–>3Às Ô&c­!c>Q^ò³¦¥öy¢É jfˆk°>bö·zº!¡$ÈDr:ú}Ñ6Y‰ãˆ0blCÕQåÄ8H}Wº·Ú‘óÀÉÝßÂÏî`êS .b`f©W¸ªy…Ëæ® 7®(GhÇzÖÒÎe9QØ€™Mªö7[㈚ª$"Þ!^ãXÓ x =ȈØÎÏŠeÊ@]ïJŒ~‘´•n Dû)¸2aË8×¹‚û¸%ý¤P;¡9õÏ}¯ÿó}¥Ÿùø) ÄJÙ'hqU»·w©ýÅwÑ`P©@jЄó+rTLUtœ4mÉRÖÌ)«âT,®²ÄfĦ‘#Î/©Úˆ–!œ±¤˜ˆ)`M…Ó0mð"!†pCã…ÓÓ3Ú“_›6iD"1„",TÍñ‹ps®Šf@Êr¢Ã-Ö;R(-H{¬¬[c]ñUÙlë+|³ÄÄmÑpÅŒsŸ¢ì’çS¨³ûÜÔ€#ãÔ Ø_xÐ1*è'ŒO): ÓøÛš²¢FU&ÅPŠmOH‡[ðwIHqœhw™~wY¤d'¡(%#¶%;bŒ¥ß‚b«–œÚÕÑ$\Y3Tšªú˜œ×]}ÕO6yd>[²>:f~ï·À¯Jû1n…‹Gìn Š%vErŒ¢E?.§FRÁünÞAµ$8õüUD\¡…ƒ1‘TM³/3òÉ|ºÆK¹ “Sé~L¡N#aÅ›„·ò‚Xñ±Ÿàf-Aä':"Z–Hh(Y¿XpõÉ—éÆo°{öĹ”už¾¿%…¾¨„7eFoÜ–Þ‹Ä”Ê+ÄIJ8yÜ’¬J J(Ë1¤"®pi¸ÄUÇxëhÍlEµ|€Ú9`J—N2Æ‚Ú"o†-’;ªÕk ‡§äáF;¬mѬ4ó{„Þí£?EtÀ=@Ã9¦nJº{ÊØmЩ»fì®{ËgeåÖËóVðÇä…åIªU 8Í8„ñ/ðçëŠ-³¬ ’Iq(¡Zb eÍŒcš#¬ü×¼nÆØïËêëȱ@ÄÊœs¢yÅ1îËrаGLf±¸Ÿß)Ü -•X¶”äL [\óåyÃñÝW©f Œ£¶-ø¦°Í©¿&uÄýˆÕâ•â¨ðÍ+ =}·"uí1'÷Éq€xMuEìÓ¬ïc4Æ1Yº«›¾[À#æ¼0„?‘ÈOý·Ê”µOeàÇD…‘á°Ô‘ðÆ`Ô}2ø‰OqJ6eü]ÚÔJèô› ̬žÖÍL}D{ô êOéw7E\;3ñúa8P5KÄØ]|NÚA ÂXÄ!;-*;„šv€/9™³Çœ¾ôë´g_'ä~Co@׃É%c [D_“"Äñ1ã°fˆxê¦Åä-Æ Õý¿ÄqG7ıcÿô›ˆÐÌ0ÂþÉ3âîm>GÜöýÀöëÿ%ÃuüqøçÊŸþ½˜Ÿ&w%…p¯O@g˜VðüÒa F±ý-ñ{ÿŒqü„à9†Z6Ny:[s÷^1€/hlŸ@µÂTgeµ\}Dp R襚aÝœ~÷¤h/Œ`'ÑÎ@N‰ñ°ÁÕ-)Œ¤¡Ã×e ]Vƒ†=¦Y`«59—lÆ´¸×ÿö?¦=ÿÍR;çˆ[ÜÁŒ=iØbꣲ rì@ÁÏïÔ ›g®ß%§„5 T`]¦™ßÇØWËü >¦ ò39l ‡gÄnƸÂx¸BÇKD;|Svk‘}OÿΟžm1Ÿ„—2Œf…~þd*…;]~ÎàÉlîHo}q?yiúâ ÃÖ »/ÿ=ää%ˆ-m«ŠzyD³<-î< yÍ%é6Ö•A` c¿GsO )âxûbYÕ0nɪˆu…vfg(1*ÒÝÕbëS” ßãÚóß¶§¤Ü‘BÆÚvvT¶[§ž´û[Ÿ€m°Í9)>E|wUäÑ/IÞú•Bw²Miìø¦¸›q‡±_78{JÝÑß¾‡©ÌÈ,fl7C¼Áø¢_ûi›e7³:«8k>™<÷¼“φ+ö\bÑ€ø¢ç›ú]‰Ï9¡qT«iQf¤Z“³ÇeOض1¨@ †çºÅ“¶³BöÄ8ÐÌO˸8GÄ´ˆ[‘ÆÜ]#âË¢mM8õ‹Â×›€ ápq©B¸y«(_6÷ЬXLN—eV]ÏQ;RÍWÔǯ ¾-Û.sÀ6Ç ÁT¶eûêìôr¢YÑF%,õ!ú¬+¥‘3Ó¿OyєҘ⬕×ÈJnÉóRò³S°V^—¿«5¤aW3¦ÜÈ[‹¯OKi>ÌÉ}‡ÆX–Y Øª¡Æº²ÌZs&ÇCyÿ©¨€æaKð q¨²Áº¶$—¹ÀþÂá?+ý‘B?q¾ì ³3RÚ»§¤ýÜì>ƯQ|Qù·Ú\ÜEÓ[Cur㫲Ïr¸*Š˜(Öðn†¶%Ö’Û¹ßQ?;&ø€8Á8A>üuBŒ¿·eýñ¸€²)eûÇ…–ÿeEËÂ爇a÷ò+¸º@Á‘¢Ì.n‰©„¬e¡¶j DZŒ×}¤@ rŽ˜²³%ÇJÖ8Ü–ÍîRfÿØVfŒÃ5ýá-$Ž8lY<ìÜ‚väá‚xûØ#˜ßÅԳɵƒqŽœ,„‘z¾ÆÍ_ÆøeY·ª£#ÖË îÒuÉÆ˜~¬©°²Ânë*’oÊpæÅÆŒOsݲêt{KˆÓü˜eücTÑgxþ$˜ Þ‘îÉû¬ÖgRjÛ£±äÖµ¤a(+f¥.V']êå8t¤öZ6™ä&Fe£kAK–9‚xÒ°#u—8S­ ê$ÈãSâæ]¬?.KŸ¬}\E‡âZÇ Þele0hŠ4iDSF´@ÇодÅÿx”pî«`sY#o¦"Ÿ¼üc tñDKè>AXcJ¬vàŸ¡ŠyYC+M›0ºã¥l³ v6­t±:]RD0¦bì6¤Ð£šËÒìªA&Ñir*‡œÇPDÊbÍ<öÈý8¡:º‡+ûr9m øÙ9bÕñË`f¤¡'ì®0Þ–X7X{‹Ä¬I`SÑÍ7kHc‘°H2i È8 RD#ˆc8Xóãøï?› ëPœ•ï ¬qÅZÅ–àyðqBŠ#Ý~G·}Äêì¾^ƒö˜2]›(…Tk½'JÙ@ª"¸jFèän@Œ)Z1€ëZÔ”…jÊZ{bÙ¦jœSc\[äâÃîírIe޵äp îop«E"HY•’Ç Îl튰T½¦8Šøø‰…˜É‹xÿÙJE”`0Îî_ l7{šGoròê—qÕý2 ÌÓá'¶·¨˜õ-Ö•)®«*â°) 9b@ãˆñ….W¤¸ÇâËÊÛæäº,¨4l‹·Oa Øö¨,zrs4ÁxûÛœ`­+óüa‹Ä[ÔnÈÆòØ“Ó%v6@ut‰6À¦.[" M5ð|}ªuÅȦà³ Sh>¹0fš!XýL @µx¨R¸\ q¬Ø…¬%ÖçL‘qM‘j ãªlSψ[–é¡q5È ¦¹ƒŽWh<@À(ãæýi‹z…­ˆÃ–:T`Ø?&ìž‘BÏ/ÿ>UuRöÅÈ´1L<¤aš]§‰îå I>ÑB1SbclÉügà&N½š‚áû¤9€1fÚ@åùLõbž—Ö—æ›fˆÃ– †Ço}‹Ýõ%'¾DsüFÕ›Š0ȇÆÍÐS¤“RFÄi,ÞB!dAöÛk–ýU!þLœcj0 Ö-IiÀYß–vb…ñW“Ó‚Xyò°ueÜhRbç‰Ã%ÃþQ!†-Woý1§õ»¸7q §d/ÝBêÊv+SO“1©K¬×ͤX•ÊgpÞàœ|bCȹ”üTðñaæFrð6J]œ5Ÿ¸;ù“O1Ω °y$'%'ƒR‘†=×?úa÷Œ»_ʸöN!ƒHÙ¸Z:]yŽ!'â¸GÄQ6˜fœm ý–4tŒ»g´«Øê• IãJO!·¸’ˆæëÏP\UZW”?šã6nÉiœÚ»±uŠÎ™ÃÓwX¼üŒÅêuÀ@Ø”ø¯€©À´è¸›úâ ÍSi˜º)é*kU*o©>…'ÈìÄ ¡0„?ÓÈ/ÄBÛ˜égúôðÂ@]Å<ÜÜp=(éšuCkGÆ!“?¼â° |îwÿR´°«Ûçå : ä4‡±GŒ”YKÎXçSPC©¿)‚Ô’!õ` 9v gŒÅO\ô”Í{HR·¡°NHž¤É#jri6¤‘ãñ¨†Í3æq,ØÙt@㮸~Ó”Ï{ˆ»R9¸Ä ¨+wg0Ùâ+‹ÿ”`&T0RTB"ß¼Ð2J] •7ŸI“ª@ <ùá-ï¼sàÑÞ©…Õ<³;À+"üƒ×®øƒÒðÿà÷ñõçk¢!&ŒVW„6BR²´˜z†q¸Æƒ˜–ýå#ªÙ ü¼¬ÿI¦Õ~˜ 'fÀ5³Â.ÐlÑT•9³3@*KS‚,¤Ða«1î±iDÌ)qÜcü¬I5NØuASBòµMYK—v˜úCO4LKªr,.Q ¾2E+è^·œãdN"ƒ ?!ï*ò"TÕgS¢¦\ ð%(ʨ å¨λ̣+åµ¹ð7_¬Œ¼õµòk¿û»ÔË5®Z!ÃXv»¢Õ”1e %¹‹±/»¤ÐqH{êåšjqSù²D3h±¸²¥È]à ª»"i*Í‚ÆrÛUGTcšBÜüe†Ý‡Så¨l¢ Nø9¦5ñ{È×eÁ¢”ŽÄž˜1$rØñ(Wðû%ØO¬L Ð>Ù$¿è©ºªê³‘2·yªŠ­àjËâT8j¡„7®ÀŒÂòLðk£põøC>zëÛ¼þ뿇oטn‹ñ-jŠtéIáe„cw˸-K£LUÚïÛëKüâ¦/B€«æeÜŸSÂêX†}‡Æ=yØ‚™£¹0z4õ¤aCiå:È{4Ukë* (xõ¯°‡¸ÍnšÉ'rÚuæ¤!‘†2W4ù>mÐ)óWQ̤bÓÏtþŇ/ü8™¬ª> rXÎò"0ÞRÍ*ªÊräÿ@H=¤Â¨§j…”?ø£?æôüef'5Æ8¬kñ­b$†§E¬3ŽäQDïg džÄõG?À×-šíúþìÁ‰™—†Fðj ©/ʱ'OÄ‚4Ð0€u¨(9•™–jF1Åý‡šÂÖT\;Ôr8”JPc‘¢›–Jç¡#{rÈ“d¼ÁWÏ“ÀOT;ÁŸ³ƒl¡P|̯ôÜ(U]<€~,Žá/2€<ÍÄã-n¶ Ž‰Ù,ÑVEg1'-䜠¤X¸·Oß§ž­Êò'±ÄaSD£ü‘+Ò¸ãúÑ–~;RÍ ®q[€·ŠabÈè$IOθÙN\…f%÷{„„¦ã‰ûwŠ AØO+ㄺ²7Hæ@@5 2-€M •4‰B(h. 2Æ£ñPN‰%ô„¾ÀÑs.1Q2xÿ)s-™?&°äߥ[f_CU¥³O›¤\ø LP·kj<¾º¤®F†:E §1ÊxؼØèžb÷b 7 š„ÃÕ–°ØÞÀe¹0UÁfdÀÙ9 W3v;.ßûóõ\ÁªÆ‚'3מ‚–e®9"Ç¡t¬´¼˜œRéõ‹EL ±Ã7ë¢u"vŒEê\ÇIòÍ@ړ¾ԯ)Yú y£b'œüsðÉs€ž‹2k‘\ù˜h#o÷ÔÕ¾ä*ÿ\êîÓ=6OÚRºrFÖªö‘-ÕlN±@¹|7 xÕC‘ìC=;eØß2nŸ±ú>Þî¾~ÆêΖºmhVwpÍ’†¡/Š ®*{X!Î3ƈ#‡R·EÃH eª‡9¦PºƒF ¥’–b»È—jRŒ›O™WdGÖ¶ ‹â®,žÊErV³JjŒ-jF‘àS€£x¥© PùË<@ÙcnÌ€·[¼Ýbdd·‡‡„wß/´5ý òÀœ‹9ªÀJ‘}¨ç§4Ôewëx 剙29†ý-1tÄAŠzX¶„îŠq÷¬x¨Ùëf뮞•Ë3p ãpà°½$=i8B®]SÆ¥œ äØ#jHãŽáæC4'ÌóѼ±$ Å¿šâs*ÀCY.•Åb²” —]´Ð“D Z#Þ`SBã€/¹&‡=ª¾ƒ€wöS…UpVa KÖ>eþ|ƒIÙãÌ-ÎìÉ9q}-|øÐòð±°ßeï?ýáÃTÊóï-øvÕã u{—útíoðõ­bu€Ù?{·ü"â[†Ýt¸¤jqU¯f¸j>m6·¸ªFª¾]ã}Ãæé»h»ÄÏÖU‡Cª²þMä<º§Äþ¢4i¬§^Ž#0NÐ"3É“))lÁ¬ ³X)aÁzrØM×q‰T-LŽÀa*'gˆé€Ž¬ÏïgñŸ. kó‹ oLyÑþüG ;œ¹EèGåá¥áÇž‹+aË'|Vÿü±ùÇ+qDJØ3jK£§²Ä<âëY™©d–uxb®¾Ëò^‹Êšz|½b}gEwó¨'X?+ÂÆL—«¨¸A¯Èdªæ5%d; {„€ÏxûˆßVò+|4ë‹™BñkZ¬øÆ#fN &yŒuPÏHC…KCQn%SÍNð¾Áy‡­jšÕÌ7-¡H„þuB¿#ôbß1;9¥ÛHaÏìäqwYú7Fp1d¬/"ξ=sŠŽ›â"RBcGŽ{r>C$Åb=BÆÂk3Ž3yÜ€_"Iý\ëÇ#YÊv°Ô“Æí Isœ¼>õy#èSäŽ._ô aDsÇàæÆñô¢æöÖcù ê3vóÑ“sþ‰0©uiÑ#²ÎâÚì8õrWþÙcÛUs\Z¸Æ²»ÚrrÿŒj¶ÂW ¬÷å]ZK³¸OJ‰îö!ýö É4pؒLJÝ#rJ¸f††ˆÃVˆ+{€¬˜‚%·Ž4l^l—Êy_¸lÙ!©Ã»Š¤Š³ó:õ±0·qkS ^¹HÎ*=rè'¢ ×"f(ËK&y(ÀŠÀ”–JKÉ醾7\]×\^y-#Õ¯ÈÍÿÅPFݪ¥ö0R´|³Bä@~.ékb"ÕìqKÚÅyËç꥚ùßÎÐÔ!Æãý¼ô\ ¹§^ÞÇ/îNŒ¡%‡Í3›g +Žd3ÎX_УaWÖ—æiʧb‡¤ëæ`’"ˆñ+LH.a!ÇvôWi«¶Y ¨±¨¦¢l+4Œèp…iN°n†‘[È©@ž¦ä>m x€” ›áêºâvãƒy>¹õ§{„© ü àœ`² ¦¢š-¦=»¸ƒ¦oŽðõŒ1uˆ@»,ú Æ@Ô€µu!Å*Äý ¦j1~IñRcÛSluLRÇœ¬÷8ÕX:€2¡~JÑÛœÀxÀøyÜà䀘ÄžÔßL(Ôã-±Fwõóû¿‰±óBU-ÉLдÇ6'dñÄî¶ÌŒvüØ>M€©íûáGKúÞ‘RiWþ¯âÈúQ-H££ãù¤,Ôu=ÉçWXï±fFŒ;|sŠÆžK'ÏÚ²,ÒUEATŒ¢q_T>\M {Ä4Eɤ^àÚÕ$å{S¤Moµ¨’™f û—】@S½žSÙ0-`Üu–ÃíSâák íú%)—5çb*Œ_׈›ׄýÛÞ+ ¤aOîo ÌZr,.ck\e1f|1Õ*I ý”Ø@CݤþW}ìL¼ Ë»G¼ñk/óò+g¨ÂGO„$-¼›ö8ÁˆCò¬ÅHøå¤,êñÕ]r.{E<9Ä´hL„a‡oOÁV¤ÁVÓåí[ö@·óc4^bm \‘ Í Lma±E'x{ÁÕûoBŽ4«5¾j†€oŠhA0Û'¨Û ³“;0B ×ìÿ9Cµ¼‡1 iHcOöˆ\í@» §±x Àgáþz<ýVþìî1ŸûÂKÜp bxûý[¾õ½[®n/ÍYÌz—Û'QþÙ×yúÖ»¼ö{¿Ík¿ýÍââ”GBÙoßÿo¿Âòä˜õÝ—¨ëHÎ{4wì®/¨—whV¯al(´ä˜S¦ˆÃö)c:¦»¼ ôÕ“ðY/þÊ_ïgçǼöÆ}ÎïŸUøÁ{·|õÛ·üèÃ-ëåŒ×^>çåûG¬––Ùr†·¦tú¡²Ù.-lëižYv3— ¡Ö;RŠä\òë×’x@ÃŽ¤eÍh÷¤Ôá«“RŽká'¥Ðá.ßþ7ÌÖwvW|ô§ÿO¾÷§ ›UKèá.Þz‡{oü&«{G„îãþ’Íõ ïí+tÛ-c·eóô}ìÄís•`­ÁZƒóó“#Ü|«[êÙ¶^€–‘fì¯Hãómä“|Fðð·‡^þí¼ãìüˆW>wŸ³»ÇĤ|÷í[þøOøðÑÀryÊ>÷÷ï,¸sºb=s4ÕHU9 c #Z4ñBI̋ˆ³srNÓöõ ‡áy¸ÅÌfkH¹# [šÅË`šK÷6Œ;BqÏ~ð/J–/Š!ákCòŠJĵ…ß?_yV÷^eìö„®cûì ïýßróÁ;øº,g4N0•¼h» c3H¤Ûð±¥YÝÅzE9 ¦&õû©]O³ö)Tö¿C@Q_9Nïóòk÷89[3ÆÌ·¿÷Œ¯~û’'W†Å|ί}þ„³£9'ë§§wX.×Tæ€ÑPĬÒa’/7?gÐ4@ eØ_–Q¼æ²ßaÚ"b«¨%…¶øå]rsĬ>BÕ`ëÒŠÏ¡´û×?âöÉA\Ér:X–õÌÎΆy(9›o…0ìxç«ÿœzqÌÓwÞãæGoº_OƒQÄI‘0)0ÂÈlæ¸J0.S-V4«åR˜“Hà[0cJO¯ÓÙR~z5†_ù¹Н*Žïsÿ•»Ÿ¬èÆÄ7¾{Éý›÷xüôÀÉÙ}¾ôù»œŸqr4gµ¨™7o"ÎÜR5MÁVhDüœ¾^`C|£¡ŠûÛ ÏÝ’S~Qûψ#ø«;xp•çøìˆ»/Ÿ³:^rè"_ûÖ#¾ú‡<»Š¬W3~ã×ïrïî=îܹËrV3ŸWÌæ ,„-䀫 9!h1à];h•tx6@9m!D ¾©h9úÙñkrÌİ/—ÎUd¦˜+cg±H;'íÖϨççX7-ŒÈÓÚWƒâüŒªY³OÑ(Ô+¡Z8PÇæ*Òïu6•?Oºü|ISdÙÚam ^ܧ^žáë%¾]UœkéºÈãÝòæWß%Veæ%w¨ü§P ýUùóNeå9:;æìÞ‹õ‚Ý~ä_}å-¾ò'o³98NNïðåÏqïü„õ²âätÁꨥ-Ê*[‰åv»9ÖùRMšÊEä)ƒ„‚½¨òahº2 ’0Æ_a«˜‘‡H[v8ØìüÅ*Ý8¤”iLÕ.˜Ÿ¼Z´ŠSÜw[\s†ºãšÕ¾¾ ÛŒmÀ· àhº óŒ[¹ðª™0iJ¿x[ƃmjÚ“¬_þ=Œ¬/Z7*žÇï~À7þÙ×ùèí't‡‘êåÓŸêþµòÓ黪b}zÄÉÝ3f‹Û}ä¾þ”¯}ã!O?efvüú_ä•Ïý NŽ—,çBmGf‹9Í ªÖ£¡'t|=/(lJ—Lj›ˆ,ãjRìÈa )!Ö ©/T}c?+< [•^€1¨³@Q/³‚"’˜U ¡¤vh·% {4GêÙ²˜ó8œ† {Œ©qí³ã´Çn&(wªÅõ*sü¹—ïfÂF)[KôéW|[Q-,®i˜Ÿ|¾Œ–u‡kŽˆÉñáŸ}“ýÿoé·;Ö÷¡®„¼’²ç9À_¥üÔ¯=Ëã#ŽîœÒÌZnwù'OùúwnØìáduÊßú›w8ª÷Ü9?ãìå3f£vPÏÎq•-óñ¨«±õk›–ͪ#¶š£ÙN3‚ˆ¥Æè•#õŠÒaë¦Z"~]¸”âË %÷¸ªÆTsPG=);RJE’7ç¢æ<Äáðë=­?FÄà¬aÚÆ5¤qDs(’åša³AóĶñKÚ£c/¨×7Ä> GâXx¡ÕzF½:·†vu„uÉ7تeè#?ú·ÿ%×?ú.ë£ÄK¯ü†Ç[å ójüvý•ÿ4—v¾bqtÄêô˜ªm¸ÙŒ|å+ùúwoÙw–Ó“¿ù`ÍÙªaYÌíŒùܲš'|åË`“UĬód«˜œ ‹Ú*•Y š0Õ 9l”ˆ¦-¸E0™ÜGL½ÀÍN ÍNæˆÌÉy‡jÁiJÖÅîI†"öYÚñÖÏÈá€HÀƒ± i°Í¢ìÛË`|‹u Ë;¯Ñ_=AÓH =G\U3ˆ`kËxˆH !ê4Ì\ÝâjG³MsRÈ ÆbÝ|Š˜ªE\ùBˆÖzyÌl}—nP»ÉMVEOXL¦j˜Ÿ|žz¶ÀT Ô4|øÝoñäÛ?äø%ƒ_)˜±ŒaKè-AåÇ}ûï8˜n¼õÍrÍ|½ÆVW=ò•wùæw.èGáÎÉ’Ï=Xsv4ãxÕ²œyfuÄ™T¸ˆÙBꑸǭŽQ\³ [S°ùy  qŠX†HU¸Ál+¶*8 ïb*ó DÙOUB@Ü I¥TÌ´C`^²ÿtKoŠ—È%•vq5ǹªlW÷gë²ÜQ(chȹ+ÊÞÆ¡"ÌÖçEæÕylÕà›!ÜbRÆ8!¥$Îãê†zyRtLÍí“'<úî÷ÈAÐdÊ2^Ã-}§Œ~UPIÿ.=ÀO|µXÓ.WWñäòÀ¿ùöøÖwŸÒw#gG oÜo9=]prrÌrÞÒú„7]QA-ˆ=Bò0é*94ˆq¸ö¤Üüþ¶>+ü|r9<±E­_„:'¾¿ˆÁöE§í*É ~bÑçª+Zæ6fâ_ä<¢aK¦*Tû¦!í“sšÔäHã~Zã×`ÅAFS,7ÎM9Ñ;6®EÃç4³ó‚ô‡mö¤,Eðc,Šˆ`¬ÇxA´#v×ùè»_cuz‹vB”3sÄè®”1fâ\_´R­ýÕåÏ+²j¥ÂÏWÔó%Çãg{þä[?âOßÜ‚r¶r¼ñҜӅe=³Àr)Ô­ÃNTšy"Ί›Q5e·1uIÖhܺ날ªƒC^¬´-m°±”y”p¨â‹¶°8”HNŒE*Ç=b,âf?+ÔâϾÿ”œàüdÍùiËÑ,±¨Fæu¤ö™ÆE*nqê\²ur*Ì&QŒö„”÷·T‹ÓrË}nj˜™ºhÿ™ªÐë‰ …7!L™¶92Õ@o¦ÚºæpõÝåÉñÀp¸)(_É †vu—öô ªÅIYé[µÓmS:ˆ¶žS½Œoné7BJcƒÓ4B43Ü<Áû9¾='Š+è__úòš ATóöHCœèâ0Š¥ºjF=?ÃÏŽ¹}öŒÛSµ[­±uƒ­¶ŒÛiŒô¸y‡\•kŠZØgf«^2äzmæd,<Úð•ûß}ó9FÎOÎOœ¬*Vm¢6;œ±Ôn5–<Þ5”¦™mÉi$瀈#iGBØ_v4ë~QÔÏ‘ ëçhî(E¿ L]B@ê] Xȇò®ûö·×ì¯/énD¯ÐÜO‰d"¥žÔo¹}ôu\{D½¸Ëòô5æw¿ˆoÎ'{·EBCY.ÕÌ©â’nsAN‡01P!‡CA÷ˆ'Ò8”Ð`ëÂGËK¦»~J®ñ- ·C£€÷…¤à+R <|ó-ÆýzV8…)¬µ´Ç+ºíŽØ‡'c’š<@‘Qÿ ”&g« ZbªI ï?Üñ'zÅ›o_@ì¹{ä9YÔ¬ÛȼºaîŸv8‰øªTD"qRY3.Êö/3'NÓ¸ÒrÍ[ãÀ|s…õ-R&cê–<Œ“VfÑx]˜Ò}¸Œj!èÄaÏîâ»Û"Á“Òã+DËŠ>Láà,Y ª=ýöãþšíõG¿ô7¨æ'¿ÄÖG ÑXÂÞìœþö ¤‘g4Mõ£%…=ýÍGxÍ„Ã-¡ïH¡Çú¶:BÄ1ì?ÂW§ :%hæLr0eçí~ÅGo¾ eqªhJX;ít²™±<ß`>ÜÈSÖUÀgwð5T L='&x÷ƒ _ýæCÞz÷5Î[îžÝádY1o í·ÐV3¬ø’‹Ô+Ä4h‘lIÜM‘ÌI©èÈú9âØc¤¤Ü>ùWWO’:&2])çÔ€Öä”Iã5Ú¿OÎp¸Ý°½üˆ±¿ÅÚ%’RÀâpÕŠ¬{$@<¶^`rƒæDw¤°#åÍã—~ƒjž1nŽØ¶0¸­'åcÝ4QŒ8oKï8íIcGwõ¶^MÛ8Nk`…Ççõ|IŽÕybwÙ=.3‰L8³L¿è®w¸,ÄQ±uœöOüöºæ×þþ9£{È×ÿèÑ Oý™|]n|HðÖ»Wüñ×?àݼ³<¸Ss÷tÁéÚ±^8Öǧ,×g¾Èå[_”1¾.YuìË‹¢*‡’p™²ˆ!ÇŒe?ŸæL [ÍØÝ옭n°ÕÕüRœ„°ÜD½ïP­…°ÛpØ\3îo ]G?ô¤PÖSÚ—3ILÇ8–…ÜÞƒzr,ò7q•!å„Çx¸¢Û<ÅÏN‰‡+l{ŒX‡É‘x¸@Ç]ÙZf.…CTôsŒi[‘’2n‹¼³¯Œ­pÍ1ã`иA,ÓvȱPjÄ:ž¼ûˆq?`ç°½ÌW€¥eùáãQ^ùRÏ;ïm¸Üð‰Ñº"àšÉÕ·ŒÞyûºüû´MÃç^{;'-Gm`=SÖG'´µ§žÓ´…X)/žÃõcâP?#‰!„¾”U¦BÅ“³”𘠜}_ø½ÌøÝgE<éã²ïEÀµP/0~Æ2?|ó)_ýúcÞûðšyÛðÆ«çÜ{ùçç¯pt´`VƒgG]Ï XcѤ¤|ÀµK梈¸ùKÄýS®..Øo÷H–¢ÂFÒx(ëÛ³BÎ$âxÀù#ê2»Ûûë§,ïìiljŒÃÈáê ˆgwù”ýí%»'—\n.©*ÂØQOÔ{LKŒ{0#ž”z wÉM€›†çËrÊûÅ–í!Æۇßcuÿ×q8òH.±ê4`ý p8ƒCs`Ü?c<\·ätAèGr*ùª†€´vª1$áú"³Û•þÕ“‡ÊêØ–€šˆÑÒßbe J·Ì°øj©t­=bqj8=a½™êá_ÂÄ€kºOû>ñýï<â¾òÞ{ï!룻|îÞ÷îpçÎ §w^b}|‡Ùb…3ŠÑ#@I}‘\S„6e'BsŒ0 ®Á¯îqÚž’Þù»ë›Â‚ÎCɇúCÁ𛉊d<•¸¹$7Œ}äænÞÿ¯ Ã%˜ŒŸDØ–áörGpB­J)Ö”â$¸Y‘sÑc.,¢ÒþÍ9N@ň)Þ¹* 5u‚ˆ¥Û>&¥‘åY‡óKœ¯¶O§å“–0lʾ€zÆýîꆰOÌNÏX¾zz}‚Š¡Yžà«ß4ô‡‘0Üÿõ%ûþ#›Í6òð‘ç·4ûvÞsØM›?ªÆCbûtC3³£[׌²Cò²ôÉ  ˜R7×KLÕ°ï"ßþÚ{üWÿÕw¸¸>°l¯Ý±Ü= œßîÜ«Yß9âèî«Ôu]´ó4`«Ó¢Ì]ÏIaœÈ0j KF¤)»{jùçoÌpþ7ßgØ]aL¡V÷‡›)û÷ÓÔtDcOŠ! |ðæ†îæC$õ®¦”I®‰$ Ù¢‚ÂËãËÊØ©¼D˜@œоhÜCre+ÉÅÄ5áeÊòÈÝÕ;ÿÿºÎ¥Ç²ó*ÃÏwÛ×sNºwµ»Ým·ØIìR)ŠP@€@bSþ3&Œòk2ŒÈıBBˆQŒØÆvÚ6¶Ói÷¥ª«ê\÷ÞßÁÚÝ$†5©ªsöÞk¯o­÷}^êÉCÖ¤ìÅ0‚—ØÙ~½À¯7l/W(5eÿÖ3ÜúÆQÌŽÑe+¹@¶&%ß^Rû+Ïsók¯|ûsÝ»ÃO¿÷cú‰>£ÔÀl?PLa+ªæÑÉœXà×Kš½9¾˜:9!|ÿ6ö¼ø¥C®íOÙß?`÷ð„ÙÑ3”UƒËkŒnÆŽ^‘q(S ]…²±_†,™z¦’qkè‚5®9áðéÄd¶Ã§ïü ‹Ó¤¬£,+) mZõ¿0N³^yNÏšJ1™(†½¿…h ;kÈõÏôô[±}a”n䑃¤})‹è”…Á¬ xü9Ò@VÚPnœ©8´)É* ÇqýHCÅ•5ÙÎÐyÀnÎÎ k®¼ð5®¼ô-Êër‡èe*LÜ ú%%œ+)g'ò…mÝÜevxȰ]óÑÛŸ°Z¬˜L-õ¤æø–ã“·<ýV ²L+1d2éüCï!LÿÇ1PýŸ ßH©w«UÏ[oÜæµ¾ËGüšZyÅá¼â¹ë»7ìî”Lw÷™ì_¥l°ã&ú-F" H‰ä{l=ÃXGžœ$A5¦mgbЉ¿>ŵWI”T“7^ú:wÞ{›ûŸ¼‡ïÄ鬭" ž¡ß èôf3ÍÑh4ºuÆjø…]àe 9qñð»W´Ç'<žŒÉÔP–m9u£°f*èå¬eˆ¢œ@‚!'6 )gRêÆpĕ¤LIÂ5{ôÜ+Lo²óÔ‹Ó+DŸ‰›•”+[KÉÑÈñ£Ú#Û•3fŒ‹-›]¾ð»ßä©çnrö«ñ«ÏÉ1påVÃég úEÆÑ ø!ƒQ""‰ ¥:Rv ›GâO|bé5ã…Ÿ \ÅbÑñæO?àõ×ßå“Ûw) œÔŠZspÐòÒ«ßàäÆ5L8¥0žfº‡s%Ù°[ΠÚO1®Aé~<æÐ]´˜~I +yÒòXfs&öIÃ#Áè¤KªéÓÜøíïpñðý⎶¤?|¢ŠòÔ\}ºÄ•ŽíÅ çÄšsÆDÐlÓQ;‚œ–—[¶çTóSN~cœUVXA79’ÃJ6|jDõ0öêq^¶’ÀÑ Œ7T»)sÄÞ|õ/eí›21È{§hvä½h ѬE/l¶#í{‹qÏ™àz\=£ Xyt\S8ÃÕç ý[ xIHó(#Vzü°!ù‚œ’„šØ 5­P¶ärÑñÖ·ùákïðé‡wqÁsuªÙk5m»{;¼òí?ääÙ—Qñ‚þü>e{…br e„i¨Œþ±²¤¡ÛyÔh7~QŽ@Â5Ç(S‘†%ÚVd2®š‘‡9v’ÞÝgRÍ>7¾ú*oßýüjvZŽh*£´¢ÙÑÎu[â‡(èYçé‹ÊBªû*=N7QlºÀÃÏïb§{LŸÃU;¨lD«aŒ  R/SÄ¥2ãdýx³ÙQÄ5F(S@Œ Ì2 SìË‘°Á‹Ý[Ùgœ …0èd…í¯…i/;æ-Ƙ¢ìF<ìW¸ú/ýÞñÝ–·ƒÛÿDÝÀt_±:…ÂCØŽñA؀ꅔ"Ì!›ŠÌÅÅšŸÿüC~ôú{œ~~ÁÑ•†çO&ÌCÍ€K&ºËžÃëÏòìK_§™£•"Å ”ïb‹²› Œ™¸Sȼ>g#ÚûÑi¶µue*ôÌô$Ã*rˆ!Êü¢”è]”<õå§™_‘7¿÷÷\ܹÍõ¯<Åä`‡8$–§¿fûèB¦…@ ™8È.H‹eTcŸ—ƬÄvºC;?ÔÓ­?•›Èa‰Rí*0%9 Bo,¦Ô£ 1eˆÊd¥EÓÁ’yœ ¥´Ö–R6¤Õ#|XãÚCLÝ =«Ü“9~ÙJùˆ=)q«<Ùn)ñýù^xÆ¡Í7^Á¸šöà˜÷ò ºÓs‘a«tÆèBÁ;o¾Ëíðé'°9ñt Õ£ÃùÀþ^‹3&tU9®~áK4ó›S“â’asŸà·˜bF¿¹OÑ&¬Þ'”c…ÑFÞ9¡RÒY%Øø\‰aEðQ¢™‹…Å8aë¦~)ŒìÝ–Éü„ßù³¿àÞ¾Fá">*–矱9» ö#X0(r°(£ÀUŠÐgRWAÓª‘jª˜Í+ŒU¤¨(ê)­QÙˆ+(õ’ª6ÆF“#iïUѺ*–“T¶'ÜÞ‘ÚžÃ8e,Æ’ã±"<,ÈnÀš ¦ž‰3ÅŒø7FŽç¨b"Ýd0匰=‡ÑõÙ6Y‡Êíj‚ϸªaçè/SNæüìï¾Oô°ÑèœÎ¸B>hÊžOß~²ææ~¦)4­†2gÚfCzŠªF¹’!wÌn1?ù"J—Ä4HfŽ_Òo7è>†-¡;§Þ{c'‰JÂ!LÊ·# )‰˜ÅH‚­Pié"âÄRE#«”èñl1A©,¥Pgˆ½0gðd¥È~…rG.å”{Hkùt¦& [P¸WÖ¨\óâ«ß$ =¿üщ«Í(VÄNuŠvjÁ¨0PdOÓj ­pM9%Akƒ+*Ú½LÙ2lOÉq‹6Wa«;„~‰R»h»‡ß<ÄLk©d¢_ƒRSÂztµ•¨IiƒÊž¸=Å÷ ”¶¸æ˜¬ ´³¤Ha-‰©DÙ`k"A~g5ayvÎêÁ}Ò D1=–}W‹×#&Íþ]l[£tÀºVܼ~Kôk‘pû qØ`ëcQ_ÙR ÅÊíYB!Ædc)y4nlÚݨ,g„RB«5"@^Z;lŽIòéT¬›kHaͰø c[L1A»ñ™™†Ü_’F&ß‘Ãg¤Ô ×\E»@ÂV­4|,šW¾óì]™ó‹|±wÔ2(2%˜J£U$m#ÕŽÆÕk4¦¨¤¡q–”=*H0²<S´¤” ^ª¶¨e bkâö.)z†í†Õ®Þ¥˜ì 5,D’Öh-h3‚«·Ø¢Å”;hÛÉO` Y[bè0¦ÅºŠ¡_`Š=”™ÐîÕ/~•_m~ʰÚP–Sµô OY{)æb~xU4xŽVàû FOé‡%Ûå¥5®ýˆjzMä^#®'‡ ÙØrþ†ì*Ìø³Ñµ¼q†Õ(òËÙ÷äÜ‘‡R'YEÊbCwŠ«2ÊÔdW@èIþBRŪ)ºØ‘Žiɺ۠U¦ž4,%£Î‰ú7ø%FL¹+»rÇ»ÕáêCn¼ü-\5å½þq­*)\«iq.㪚¢ÜÅ” Ê$ŒÕ"¬HÓ–ÌŽ¾DQÍqÕ ï=ýòÛóÛdÀÕÇr°+Ù˜eƒ.¹¸ûÕü{Í7äiªñÂGÀ`tÃε’‚è!ÇvìÎFdn ÆÓek09%lQ Šók·ÂÓ~NÙ6”³ÒÑ’’S@VÛà»5„Ä:|¿ Æ@¿YËèßF†å=üö>ÚUä,çý¢=&§­`åÌ>®1¦ø´ìR"ç€+¦$2ÚTä¬Ð1£0”Ò  ò¸¢6óW_ÿnÎ]N?\bTÀVSL9“Çs¾¿”rž2¦;m©¦W±ÍÊ:´uB׊=9I~ÁôøŠv¡_Ò¯â7hæÏãª]´säì1Eƒ)v~ð¾‡-$“ìå·¾ƒï.Æ-ÞØ,+¹ÐÖaŠ‘Åo 0Ä8œ£uDÛã¦@”ÁX·§¶h)¦»t‹ R·ù·iÈiMV ›3rLl§ Ûs0à&’Ëœ:RÊø˜hö¯R´ûhcpÅ£Ky ¢R­‹'R´œZiÉlV2·±ååÜØ£‰Ã8'9Ùdíä—A›òe ©?…¼Ÿš)ÉÚ¢—Î7t2~̨NR?R$¥$FÆØÒ¸fŽ«¦B²‹uY§ C"ýeóë/òÜ·þœr÷Û³;u([¼ M`|ßka¥”Ç#Ú†”)ö Ý™¼¿É¿¦ßÜ“okR „°¥Û>`qöëůé7§²ÒU’ˆnËÆÍÄ­SîѪ5J¤%dQiA°i-¥_›qQ‚ŸÒx%‘o[WaƼ_EÀ•%JkʽëÔGÏ * º'Ž7ªH1eâˆÖ › TJ’ö¥4âÚ€«ÑÖ¡­E»RVÑZ¯€*aŒ•ï\bZÆ`%ºS`Œ¬˜“ßðߥ¿µ jH„%tEXtdate:create2017-10-09T16:55:12+02:00üÔ†%tEXtdate:modify2017-10-09T16:55:12+02:00Il:IEND®B`‚crispy-doom-crispy-doom-5.6.4/data/doom8.ico000066400000000000000000000102761360717211000206750ustar00rootroot00000000000000  ¨( @ ˜Òïÿ Øóÿ¨Úòÿ¤×ñÿ“Òîÿž×ïÿ¯ßóÿa¡Ïÿe¯ÿ,…Âÿ1Çÿo´Þÿ~ºàÿzºàÿc¯Úÿnµßÿm·áÿdµáÿj¸ãÿÁæÿzÀçÿ3‘Ïÿ&…Çÿ)„Åÿ u¸ÿ2ŒÂÿ„¾ÞÿŠÂàÿ‡¿Þÿ†¾Ýÿw¹Úÿk±Õÿ¥Óñÿ¤Õòÿ¡×óÿžÕñÿ¨ÙñÿªØïÿMƒºÿC”ÿ]Ÿÿb¦ÿ v·ÿX¤Óÿ…Áæÿf±ÝÿŒÃçÿ“ÆéÿŒÆéÿu½åÿ‰ÅèÿËìÿc®Üÿ k¶ÿ]«ÿo´ÿr³ÿPžÊÿo´×ÿl´×ÿe³×ÿoµØÿk³Öÿe¯Ôÿ‰Åéÿ°Ùóÿ®Øñÿ­ÙòÿÄäÿ7€ºÿ,‡ÿM”ÿF—¼ÿM¾ÿT‚ÿVÿd¦Ïÿ|¿åÿžÏîÿ¢ÕñÿŸÔðÿËëÿ‰Íîÿb±Ýÿh®ÿ l¯ÿ]¦ÿJ–ÿTÿSŸÇÿo´×ÿb°ÕÿY¬ÒÿS©ÐÿT©Ðÿf±Õÿ"ÂÿP™Íÿs´ÛÿZ‘Ãÿ+iªÿZ¥ÿE™ÿ"r§ÿN˜·ÿWÃðÿX¹ÞÿI˜»ÿ]‰ÿI—½ÿ“Éâÿ¢ØðÿŒÎíÿËíÿk»ãÿ#ƒ¿ÿs³ÿm¬ÿ \¡ÿT—ÿ eÿZ¬Ôÿ_±ÖÿU®ÕÿL©ÒÿC£Íÿ]±Öÿj¹Ýÿ9”Êÿ"~¼ÿ}»ÿ V¡ÿ[¨ÿ+zºÿt¸ÿ,r£ÿD‰«ÿSÂðÿPÀðÿRÁðÿZ¼âÿU©ÈÿQ¨ÿh¦Âÿd²ÖÿD¥Öÿ?ŸÑÿCšËÿ?•Çÿa³Ûÿ‹Öòÿ‡Íìÿ\¬ÕÿV®×ÿF¨Óÿ>¤Ðÿ(™ÈÿJ«ÓÿœÖíÿuÁäÿ[¥ÑÿNœËÿ%…ÀÿV¢ÿX¨ÿMªÚÿvºÿJŠÿF¯ÿM¶äÿK´ãÿK´ãÿK´ãÿLµãÿW·ÞÿYªÉÿH‡¢ÿ@Š®ÿEžÈÿ^°Úÿ]°Úÿ—Üõÿ¨ìþÿµñÿÿ·ñÿÿ¢ßöÿwÄåÿYµÛÿj¾ßÿ™Øîÿœ×íÿ{ÂâÿV¦Ðÿ=“Æÿ‚½ÿX¥ÿW¨ÿP¯ÝÿAËÿS‘ÿO»ÿE£ÏÿB ÍÿB ÍÿB ÍÿB ÍÿB ÍÿC¡ÎÿR­ÓÿY©ÇÿW¦ÿs¢»ÿk°ÒÿŽÔîÿ¡áøÿ©æúÿ²êüÿ¦èüÿ£æüÿµìýÿ¶éúÿ«Þòÿ“Ëåÿj´×ÿ:—Éÿu¶ÿ i¯ÿHÿ[«ÿzÀÿO²áÿ3u¥ÿL—·ÿ<¼ÿ8Œ¸ÿ8Œ¸ÿ8Œ¸ÿ8Œ¸ÿ8Œ¸ÿ8Œ¸ÿ8Œ¸ÿ9¹ÿJ ÆÿT¥ÄÿC…¢ÿcž·ÿ‹ÃÙÿ”Òëÿ•Øòÿ‹ÖòÿrËðÿ‰Õòÿ•ÖïÿÒêÿ¢ÑçÿPžÅÿe°ÚÿqµÿD†ÃÿN’Éÿi³ÿyÀÿ-•ÑÿC•¼ÿSž»ÿ3~¦ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ/x¡ÿ0y¢ÿB‘¶ÿT¢ÁÿY’©ÿt£»ÿ}¾Üÿ…Êêÿ„Êêÿ†ÉéÿÂâÿ”Éãÿ–ÈàÿVŸÅÿ¨Ýóÿ‹Ííÿo½çÿqÀëÿV­âÿvÃéÿ‡Êìÿh¤Ãÿ\£½ÿ+k‘ÿ&d‹ÿ&d‹ÿ&d‹ÿ$j’ÿ&d‹ÿ&d‹ÿ&d‹ÿ&d‹ÿ&d‹ÿ&d‹ÿ&d‹ÿ&d‹ÿ:¥ÿ^¬Éÿdž»ÿm´Ùÿy¸ÛÿƒÀàÿŒÆãÿÌäÿ‘ÂÛÿp¬Ëÿœ×òÿ}Êïÿi¿ìÿQ­âÿ]¹çÿÖòÿ’Óïÿn©ÄÿY¡¼ÿ"W}ÿOuÿOuÿOuÿ~§ÿ"ˆ°ÿvÿTzÿOuÿOuÿOuÿOuÿOuÿOuÿX¥ÃÿJ†¦ÿKÈÿB—ÄÿT¡Ëÿt·Úÿs·Úÿ\¡ÊÿW›¾ÿ ÚõÿyÄíÿe¸èÿM¬áÿdÁêÿŽØóÿyËîÿPžÁÿRœ¹ÿ>bÿ;_ÿ;_ÿ;_ÿv ÿ:‰¬ÿ6Š®ÿ%†­ÿwŸÿGlÿ;_ÿ;_ÿ;_ÿ;_ÿV¢Àÿ1wœÿ€·ÿ%‰½ÿ\§Ïÿ"}µÿ0‡ºÿ;Œ¹ÿ5†´ÿ¤ÞøÿŠÎòÿq¿ìÿM¯äÿX¼èÿƒØöÿgÊñÿR£ÅÿR•ºÿ 'Iÿ 'Iÿ 'Iÿ 'Iÿkšÿ5|žÿ5|žÿ5|žÿ-€§ÿNwÿ 'Iÿ 'Iÿ 'Iÿ 'IÿK¶ÿ I}ÿCŠÿA‘Áÿm´Ôÿ x­ÿd¡ÿ%±ÿE–ÀÿÙöÿŒÒôÿÊòÿC­ãÿR»èÿ˜æüÿ†Þúÿr·ÍÿPŽ»ÿ3ÿ3ÿ3ÿ3ÿb—ÿ/pÿ/pÿ/pÿ)vŸÿ :dÿ3ÿ3ÿ3ÿ3ÿC®ÿBsÿ!YŒÿy½ÞÿX©Ïÿ,‰¹ÿz±ÿ_©Îÿz¸Õÿ‘Òóÿ‡Íòÿ“ÓôÿnÎõÿ“äúÿœíýÿ”êýÿz½ÎÿL‚¸ÿ0ÿ0ÿ0ÿ0ÿ[•ÿ*d‚ÿ*d‚ÿ*d‚ÿ%l˜ÿ 1]ÿ0ÿ0ÿ0ÿ0ÿC|²ÿBŒ­ÿ”Ûôÿ‡ÊæÿM¢Ìÿ$‰¼ÿ‰»ÿ~»Ùÿl²ÒÿŸØõÿ‚Êðÿ–Ôôÿ„ÐóÿÚ÷ÿÞûÿˆÛúÿi¯ÉÿCuµÿ0ÿ0ÿ0ÿ0ÿS“ÿ$Xsÿ$Xsÿ$Xsÿ!a‘ÿ)Vÿ0ÿ0ÿ0ÿ0ÿ@p¯ÿf¢»ÿ­ãõÿ}ÃâÿHŸÌÿ$нÿ+‘Áÿ|»Ùÿb­Ðÿp¿êÿÓôÿ„Îñÿc¹çÿY°áÿd¶äÿ`¶åÿ1…±ÿAq·ÿ2ÿ2ÿ2ÿ2ÿR”ÿKeÿKeÿKeÿ XŽÿ/Qÿ2ÿ2ÿ2ÿ2ÿDp²ÿz«¼ÿžØïÿk·ÝÿO£Ïÿ>™Çÿ;›ÈÿqºÚÿ~¼ØÿE¡ÔÿY±áÿuÄîÿd¹èÿM©Úÿ5’Êÿ}¹áÿp›¶ÿQ€Åÿ|]3ÿ|]3ÿ|]3ÿ|]3ÿ<^•ÿ?Wÿ?Wÿ?Wÿ%S‹ÿm^Mÿ|]3ÿ|]3ÿ|]3ÿ|]3ÿP~Äÿ‰¬ºÿ†Êéÿ_°ÚÿT¨Óÿ[«Ôÿ>œÊÿc¶Úÿ|½Ùÿ_®ÙÿM¢Ñÿ`­ÚÿR¥Öÿ!†ÄÿI˜Ëÿt³ÚÿZ‹©ÿLs¼ÿpQ+ÿpQ+ÿpQ+ÿpP*ÿ9U—ÿ2Hÿ2Hÿ2Hÿ%J‡ÿgR>ÿoP*ÿoP*ÿoP*ÿoP*ÿLtÃÿs ´ÿÆæÿi·Þÿh³Úÿ]­Õÿ?›ÉÿO¨Òÿr¾Þÿ^¯ÚÿX©ÕÿX¥ÐÿD–Çÿ*†¾ÿ]¨Óÿa¬Öÿ8zžÿFi¼ÿcD"ÿcD"ÿcD"ÿcD"ÿ8Kÿ&:ÿ&:ÿ&:ÿ%CŠÿ_D/ÿcC"ÿcC"ÿbC"ÿbC"ÿFj¿ÿ^ªÿz½àÿf´Üÿj´ÚÿZ«Ôÿ@žÌÿ9šÈÿ€ÆãÿVªØÿN¦ÔÿJ Ïÿ3Åÿ<–ÉÿR§ÕÿD Ðÿ%o™ÿA^¶ÿV7ÿV7ÿV7ÿV7ÿ5AŒÿ ,ÿ ,ÿ ,ÿ&;ŒÿT7 ÿV6ÿV6ÿV6ÿV6ÿ@^»ÿG ÿW§ÑÿO¨Õÿb±Øÿi³Øÿ\¬Òÿ-Âÿpµ×ÿ8—ÏÿF¡ÔÿBÐÿ+Æÿ?ÐÿN§×ÿS©ØÿF„¨ÿ=V³ÿJ*ÿJ*ÿJ*ÿJ*ÿ27‰ÿÿÿÿ'3ŽÿJ*ÿI)ÿI)ÿI)ÿI)ÿ9U´ÿ k”ÿQ£ÍÿHÈÿ^«Ñÿ„Àßÿh³Øÿ j©ÿ'‚»ÿ'Íÿ<›Óÿ9˜ÎÿAžÒÿW®ÛÿB¢ÔÿL¨Øÿ9ƒ©ÿ5Lžÿ= ÿ= ÿ= ÿ= ÿ/2ÿ &ÿ `ÿ&)pÿ1)[ÿ= ÿ= ÿ= ÿ<ÿ<ÿ9Q¦ÿ8|¢ÿ'ˆ¾ÿ<”ÄÿZ¦ÏÿLŸÍÿn¯ÿ f¨ÿ+ÐÿK¥ØÿT¦ÖÿJŸÑÿ;—Ìÿc¶àÿk¿åÿ`¹ãÿD²ÿ2Ežÿ0ÿ0ÿ0ÿ0ÿ/+wÿ-#Xÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ>Qªÿž¾ÌÿT¤Ñÿ}¹ÿ bªÿb«ÿ(ƒ¿ÿaºâÿ?¬Úÿ`²ßÿ\«ÙÿFžÐÿ8”Çÿ1Äÿ'…¿ÿX¬×ÿc ¼ÿ1@ ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0 ÿ2'Rÿ7<ƒÿas©ÿ½ÕßÿŸÐêÿKŸÐÿ+‰Ãÿh´ÝÿÖòÿxÈíÿ@«Ûÿe¶áÿi´Þÿd°ÙÿZªÔÿ0“Æÿi­ÿBšÊÿ9€¦ÿ*6—ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ/>ÿ02{ÿ9Hÿ^„¦ÿ“»Èÿ£ÏàÿÁâðÿÄäóÿo´Üÿg¶ÞÿwÂæÿ{Ãçÿ|Æéÿ_·áÿm¼äÿh¸áÿk·Þÿj´Ûÿ#‚½ÿy·ÿT§Óÿ2€§ÿ*7›ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0ÿ0+ÿ--rÿ(9ˆÿ/^•ÿb›¶ÿl±Ðÿ~Ççÿ§àõÿ¨Ýõÿ«ßôÿ°Ûñÿi³ÝÿZ°Üÿq¼ãÿ|Áçÿ‡Æåÿ|¿àÿl¾æÿ|Âæÿiµßÿ[§Ôÿg­ÿ*ÅÿOª×ÿ7†­ÿ+6–ÿ0ÿ0ÿ0ÿ0ÿ-(eÿ)6…ÿ9Z”ÿgš²ÿ5¹ÿ=¢Ðÿ„ÌìÿƒÎòÿxÉðÿ‰ÌîÿËìÿ†ÆéÿŒÆèÿu¹áÿl·ßÿqºÞÿo·ßÿ—Ëæÿƒ¿Üÿh»äÿ|Áæÿ_²Ýÿv·ÿi¯ÿD¡Òÿ[³àÿO”·ÿ.9œÿ0 ÿ.$Vÿ*3€ÿ&EŒÿ*sŸÿ>•¼ÿt¾Ûÿªæöÿ—Ýöÿ9«ßÿpÆîÿoÃíÿl¿ëÿyÃêÿ‹Éêÿ’Çéÿ›Ëêÿ‰Ãæÿy»ãÿŠÁßÿLœÂÿz½Ýÿ—Ëäÿe·àÿ`±ÛÿAžÐÿ t¶ÿ$Åÿ\²ÝÿÊíÿ|·Îÿ1Eºÿ'@Žÿ(f˜ÿ7ˆ±ÿP ÉÿL¬Úÿ[¼éÿÚöÿ§èýÿÁôÿÿ×òÿJ°áÿY·æÿn¾çÿ…ÆéÿÑîÿ ÎìÿšÆèÿs³àÿk·åÿf¯×ÿ/ƒ±ÿS–¸ÿbŸ¾ÿO¨ÕÿW¬×ÿ8’Çÿ€¾ÿQ­Üÿ„Îîÿ‰ÓòÿE©Øÿ‚ºÿNŸÇÿW®ÙÿTµãÿX¶äÿUµåÿsÅìÿ‰Òòÿ¤ãúÿÄôÿÿµéúÿY´ãÿ_·ãÿÏîÿ–Îìÿ•Ìêÿ•Çèÿu²Ýÿ?”Ðÿk¸æÿk±×ÿ6‹¹ÿžÎÿ@žÎÿWª×ÿg³Ýÿ]®ÛÿQ©×ÿkµßÿx¹âÿy¼ãÿr¸áÿR¬Üÿf¶áÿs¼åÿp»ãÿo»åÿ`´áÿ[±ßÿT°Þÿs¾åÿÂåÿÇéÿ„Ãçÿ{Âæÿw¾æÿjºåÿl¹åÿe¶ãÿh¸ãÿ/ŒÌÿ}Äÿ…Èÿ9‘Íÿ)‹ÈÿA–Íÿ7Ëÿ+ˆÅÿ s»ÿj³ÿ|¼ÿ+‡Ãÿ|¹ÿq²ÿ s±ÿl­ÿ2Ãÿi¯×ÿy¶Ùÿ‡¿ßÿÂàÿ”Äàÿ˜Çáÿ‡ÂàÿÆâÿšÇâÿ’Ãßÿ…ÀÝÿ†½Þÿ}½Ûÿ½Üÿˆ¿Þÿˆ¾ÜÿŒÁßÿ}¼Üÿv¸Ùÿw¸ÙÿmµØÿrµØÿx¸Øÿi°Óÿb«Òÿ‡Ðïÿ”Ðîÿ¦×ðÿ›Óïÿ‘Ñïÿ¡Ùõÿ¦Üõÿ·ãøÿ¦Úôÿ£Ùòÿ¤Øðÿ¨Øðÿ«Ôïÿ«Øðÿ©Úñÿ¤Úñÿ”Ñïÿ¢×ðÿ«ÜðÿÌéÿpÃäÿËéÿ«Ûñÿ¦Öíÿ•Ìêÿ Óíÿ¤Öïÿ±Ûñÿ±Øíÿ¨×ðÿ‚Âãÿwµÿe­ÿe®ÿ`®ÿd²ÿyºÿ;ŽÇÿT™ËÿO™ËÿO›ËÿF—Ëÿ-ÇÿŠÅÿ(ŒÆÿ‚¼ãÿ¡ÏìÿÄåÿ~¹ßÿX¨Ôÿ‡¼âÿ–Äæÿ‘Åæÿ|¼ßÿƒÀâÿ‹ÃäÿnµÜÿXªØÿp¶ßÿ^¯Üÿn¶àÿk¶àÿR«ÚÿP©ÙÿZ¬Úÿe³ÞÿrºáÿZ¯ßÿQ«Üÿ[²ßÿk¹äÿu½åÿs½äÿ^´àÿZ±àÿO­Þÿd¸ãÿ}ÁæÿÇéÿ„ÅçÿÂçÿz¿åÿj¸äÿq»åÿp»æÿ`´àÿ)‰Èÿ~Åÿ%…Çÿ1ˆÇÿ)ˆÉÿÅÿ s½ÿ%~Åÿoºÿk¶ÿ t¸ÿ$‚¾ÿ€¼ÿu¶ÿp´ÿk®ÿHÉÿm±Õÿj±Öÿ„¼ÞÿÀáÿ‘ÂáÿŽÂàÿ‚½Ýÿ‹Áàÿ‘ÂàÿŒÁÞÿ…¾Ýÿ‡¿Þÿ¿Üÿ‚¾ÜÿƒºÛÿÂßÿ‘ÄÞÿ}¼Üÿx¸Ûÿw¹Úÿi²×ÿk±Ôÿo³Ôÿm²Õÿo²ÕÿyÄéÿ…Çëÿ¦Öðÿ¯ÚðÿœÑðÿ”Òñÿ™Öôÿ¨Ûöÿ²Þôÿ«Ûóÿ§×òÿ³Üôÿ»Þõÿ»áôÿ¶áôÿ™Ôñÿ–ÓðÿªÜóÿ´Þòÿ|ÄæÿqÄèÿ Õïÿ¢Óïÿ¦Øíÿ¥Öïÿ§Öïÿ¨Ôíÿ¦Ôíÿ´ÛòÿÀßÿk­ÿZ©ÿd¬ÿ`®ÿb±ÿZ°ÿ t¹ÿ*…ÂÿDÈÿH”ÉÿC“Éÿ>”Ëÿ$ŒÆÿŒÆÿ-ŽÉÿƒ»âÿ˜ÊëÿžÌìÿÈéÿj¯ÙÿT§Ôÿƒ½âÿ€¼áÿy¸Þÿs·Ýÿiµßÿf²Ýÿ€»ãÿg°Ûÿl´Ýÿ‡Àåÿ€½äÿ~¾äÿq¸âÿ\¯Üÿq¸âÿ~¾æÿ`²áÿT­Ýÿ[°Ýÿeµáÿ`³âÿ`³áÿX²ßÿ`´àÿqºäÿ„Áæÿ~¿äÿ€Ãäÿ…Ãçÿ{Àåÿ~¿åÿr¼æÿk»çÿX®Þÿ!ˆÈÿ‚Äÿ$†Éÿ€Æÿ&†Çÿ>’Íÿ/‰Éÿ-Æÿ?ŒËÿ1„Äÿ!{Àÿw»ÿ r¶ÿ t´ÿnµÿq·ÿ tµÿP¢Îÿh®Õÿg®Ôÿ‚¼ÛÿŽÂáÿ†¾àÿ}¼Ýÿ‡¿ßÿ‡Àßÿ‘ÃáÿÃßÿ†½Þÿƒ¼Üÿ¹ÚÿºÛÿºÜÿ·Úÿƒ¼Üÿ{¼Üÿx»Ûÿh³×ÿd²×ÿj²Õÿi±Öÿr³×ÿl²Öÿ‡ÇêÿžÑðÿ¥Òñÿ®Ùñÿ¯Øòÿ˜Ñïÿ|Æëÿ›Óòÿ¬Ûôÿ Ôòÿ³Þõÿ¸Þóÿ©Øòÿ¨Ûôÿ•Òïÿ†ËìÿˆÎíÿ‹Ðîÿ•ÓíÿÎìÿ¡Õïÿ²Ýòÿ·àóÿ¬Ùïÿ¥Ùðÿ¬Ùðÿª×ïÿ¢Ôïÿl­Õÿ\¢ÿQ¢ÿW¥ÿV©ÿc±ÿ q¶ÿa²ÿbµÿ wºÿ}Àÿ*†Âÿ&ˆÄÿ(ŒÇÿ(Çÿ#ÇÿˆÄÿq³ßÿ“Æêÿ¢ÐðÿÊêÿ~¹ßÿS¥ÔÿJ£Òÿd­Ùÿ}ºáÿw¹áÿc²ÞÿÃèÿwµÞÿ ‹ÆÿJ Òÿ‘ÃçÿŽÄçÿŽÁçÿŒÄçÿ‹ÆèÿŽÅèÿ‘ÈëÿqºäÿP«ÞÿX°ßÿnºäÿv¾çÿÂçÿw¾åÿt¼åÿ}¾äÿ~Áåÿ~Àæÿ‹Æçÿw¼âÿ|¾åÿ•ÈëÿŠÅéÿr¿èÿ=šÐÿl¹ÿ~Äÿ1ŽÍÿ ‰Êÿ#†Éÿ"„Çÿ"‚ÅÿF”Îÿ3ŠÈÿ-†Æÿ%‚Âÿz¼ÿxºÿl²ÿh°ÿk±ÿ~»ÿ\¨Òÿ|¶Ùÿ{µØÿj°Õÿp·Ùÿ`²×ÿ|¼Ýÿ‹Ááÿz¼ÛÿƒÀÞÿƒ¾Þÿ†¾Þÿ…¾Þÿ‚»Üÿ}ºÛÿ‚¼Ýÿ»ÛÿŽÂáÿ‡Áàÿ}»ÛÿnµØÿf±Öÿl²Öÿj±Ôÿe­Ôÿg°ÖÿÏïÿ¬Õñÿ§Õñÿ¨Öóÿ¶Üóÿ«Ùòÿ{Äêÿ‰Êîÿ™ÐðÿžÖòÿ¥Úôÿ²ÞõÿŸÔóÿÓòÿ†Çéÿ‹Ëìÿ‡Ìíÿ}Èëÿ{ÇêÿÕñÿ°Ûðÿ­Ùðÿ°Ýòÿ£Øïÿ¤×ðÿ¨ØïÿšÕðÿ^¦ÐÿM–ÿB—ÿLŸÿW¤ÿ[ªÿf°ÿ t·ÿu¹ÿ`´ÿq»ÿ y½ÿ z¿ÿ€Âÿ„Äÿ"ŠÅÿˆÃÿ{½ÿ>—Íÿ„¿äÿˆÂæÿÇéÿ’Åçÿ‡½ãÿM¥ÕÿG£Óÿ€¼âÿ‰Àäÿe²ßÿ†Âæÿ~ºáÿi­Ûÿ~¸áÿ’ÄèÿÅèÿ‚½äÿ†¿äÿ‹Âæÿ‰Âæÿ„Äæÿu½åÿ\±àÿd·âÿv¿åÿÂçÿ|ÁäÿŽÇéÿÈéÿ‚ÂçÿÃæÿŽÇéÿ“Èêÿl¸ãÿÀæÿŽÄèÿ|ÀæÿK Õÿg³ÿ^°ÿgµÿq¹ÿ _¯ÿl¹ÿh¸ÿ j·ÿ;‘ÍÿF•Íÿ/Çÿ1Éÿ%‡Äÿ1ŠÆÿ{ºÿk¯ÿl¯ÿ/ŒÁÿw³Ùÿ‚¸Úÿ‡¹Ûÿq±ÖÿQ©Òÿg²×ÿz¹Ûÿs¸Ûÿs¸Úÿt¸Ûÿw»ÚÿxºÚÿv¹Ùÿ¼Ýÿ‚¼Ýÿr´×ÿv·Ùÿ…¿ßÿ€¾Ýÿx¸ÙÿoµØÿj±Öÿn±Õÿc®Óÿb­Ôÿl²ÖÿŸÓïÿ§Óðÿ¨Óòÿ§Ôóÿ¨Õñÿ¨Øòÿ¢Öòÿ¥Öòÿ™ÖòÿÓðÿ’ÒñÿÖóÿ•Ôòÿ¥Üôÿ‡Ëêÿ’Ðïÿ«Ùñÿ¤Õðÿ¢×ðÿ¬Ûñÿ°×îÿ³Ûïÿ£ÕíÿÉéÿ‘Éêÿ˜ÐíÿJ‰ºÿ>Žÿ7ŽÿFšÿOŸÿX¥ÿ[«ÿd±ÿ s¹ÿ{»ÿ u»ÿlºÿv¿ÿt¿ÿ~Áÿ |¿ÿ }¿ÿ„Âÿ z¼ÿ?—Ïÿ‡ÀåÿŽÃæÿ¾ãÿˆÄçÿŠÃçÿP¦Ôÿ?ÐÿY¦×ÿm²Ýÿeµßÿ‰Äèÿ˜Éëÿ˜ÈêÿˆÀäÿ‡Àäÿ‘Åéÿ†Áåÿ†ÃæÿÅçÿŽÃèÿÀåÿ{Àçÿf·ãÿU®ÝÿW¯Þÿt¼äÿy¾äÿ‚Âæÿ‰Åçÿ{¾äÿŒÈêÿ†Çèÿ~Áåÿkºãÿdºãÿz¼äÿ]¬Úÿo·ÿ\­ÿZ­ÿX«ÿS¨ÿL¤ÿYªÿR¥ÿHŸÿz¼ÿ „¾ÿ…Àÿ*‹Äÿ+ŽÆÿ†Âÿ€¿ÿ½ÿ€¼ÿY¥Ïÿy³Öÿz³×ÿ†¹Ûÿn²Öÿ`®Ôÿy¸Øÿt¶×ÿnµÚÿr¸Ùÿo·ÙÿiµÙÿb³Öÿi³Øÿo¶Øÿt·Ùÿt·Úÿt·Ùÿy»Ûÿt¸Úÿr¶Õÿq´Öÿe¯Õÿ_­Óÿb®Óÿg°Ôÿn³×ÿžÒðÿªÕñÿšÌìÿ•Êíÿ±Úóÿ©Öñÿ¨Ùòÿ·ÞôÿÔðÿ‰Ððÿ™Øóÿ£ÚóÿšÓïÿ­Ýôÿ˜Òïÿ“Ñîÿ±Ûñÿ¶Úñÿ±Ûïÿ¬Ùðÿ¼àóÿ±ÚïÿÏëÿ‰ÃäÿŒÍìÿT“Àÿ%zÿ1‹ÿ:’ÿD—ÿO¡ÿ_«ÿa¬ÿg±ÿo¸ÿ u»ÿpºÿs½ÿl»ÿa¶ÿi¹ÿs¾ÿ~Âÿ„Áÿs¸ÿ)„Âÿ}»ãÿ§Ïîÿ¦ÐíÿÆéÿj·àÿ<šÎÿHŸÒÿN¦×ÿT«Ûÿ‚Áæÿ‹ÄèÿŒÄæÿÃçÿŽÄèÿ’ÅéÿÆéÿ“ÉëÿÄèÿÆèÿ“ÇêÿˆÇèÿÈëÿ|Áæÿjºåÿx½äÿƒÁåÿ~Âæÿ…ÄçÿŽÆêÿ‰Äçÿ€Ãæÿ„Æêÿ~ÂçÿvÀæÿtÃéÿ‰ÅéÿN¢Öÿ)‰Éÿz¾ÿe°ÿa®ÿ\«ÿYªÿR£ÿU¢ÿQ ÿU¥ÿ`ªÿh­ÿi­ÿo²ÿ|·ÿ|¹ÿ}»ÿ†¾ÿc¬Òÿq°Óÿz´Öÿt´×ÿ`­Òÿm³Õÿq´×ÿnµ×ÿm´×ÿoµ×ÿd±ÖÿR­ÓÿV¯ÖÿY¯ÔÿZ°Öÿm¶Úÿl³×ÿj³Ùÿc±Öÿg²Õÿr´×ÿk±Ôÿ_¬Òÿ\¬Óÿc¬Óÿo³Ôÿf°Õÿ—Ððÿ¤Ñîÿ”Æëÿ„ÀéÿžÎïÿ­Ùóÿ Ôòÿ¸ßõÿ´Úóÿ²Ûóÿ´ßôÿªÚôÿ¯Ùòÿ¤Ôðÿ©Öðÿ¦Ôïÿ¦ÔïÿµÜòÿªÖïÿ«Ôîÿ±Øîÿ¬Öìÿ˜Êéÿo²×ÿB…¹ÿ>Šÿ.†ÿ7ŽÿG—ÿE›ÿM ÿY§ÿd®ÿj³ÿt¹ÿ rºÿpºÿnºÿV²ÿA¥ÿm¾ÿ…Çÿ€Âÿ|¾ÿq¶ÿZ¦ÿOŸÒÿ£Îíÿ¬ÒïÿžÏîÿy¼ãÿG¥×ÿ[®Ûÿk¶àÿƒÃçÿ”Èëÿ˜Èêÿ—Èêÿ—ÉêÿšËëÿ Ïíÿ›Ììÿ—Ëìÿ–Êêÿ™Ìîÿ—ÎîÿšÑïÿÐïÿÉëÿ‚Åèÿ¿ãÿ~¿ãÿƒÆæÿˆÅèÿ”Ëìÿ—Ììÿ~ÂåÿŠÇéÿ—ÎîÿŽÌíÿ§Üõÿ‘Éêÿ>—ÎÿB¡ÖÿFÑÿ!¾ÿg¯ÿa­ÿd­ÿ_¨ÿU¡ÿQžÿV¡ÿW¡ÿNœÿJ˜ÿSÿa¤ÿ`¡ÿb£ÿ(…»ÿd­ÒÿY¤Ìÿi°ÓÿY¬Ñÿ_¬Òÿg®Ôÿ[«Óÿd°Õÿl³Õÿe¯ÔÿV«ÑÿP§ÐÿQªÒÿN«ÓÿV®ÕÿZ­ÔÿV«Óÿ]®Õÿ_®Óÿb®Óÿ_¬Òÿ`­Ñÿ[¬Òÿe®Óÿl²Õÿm²Öÿg¯ÖÿˆÈêÿŸÒñÿ¬Õðÿ¨Ôñÿ±Úòÿ²Ùñÿ¦Ôðÿ¤Õñÿ²Üòÿ¡Ñîÿ¥ÒñÿŸÒïÿÐïÿ©Õïÿ´Úïÿ®Ôíÿ§Óîÿ£Óîÿ§Õíÿ¤Ðèÿ©ÑçÿŸÈâÿb¦Ìÿ%e ÿoÿ#vÿ4‡ÿ:ŒÿC”ÿKÿR¤ÿa«ÿ k±ÿo¶ÿ}¼ÿ t»ÿs¼ÿ^³ÿe¹ÿ+‘Íÿ5˜Îÿ2’Ëÿ%„Äÿy½ÿ t·ÿZ¤ÿ!v·ÿ‚Ãçÿ£Ðîÿ¢Ïîÿ˜ÊëÿQ«ÛÿT¬Ýÿ…ÂèÿÅéÿ’Èêÿ™ÌìÿžÍìÿŸÎíÿ›ÍìÿŸÐîÿÌìÿ›ÍëÿžÐíÿ§Õðÿ—ÏïÿžÒðÿ¥Õðÿ‘Ëìÿ“ÍìÿŠÊêÿ‰Éêÿ˜ÎîÿÌìÿ‹ÇèÿÈêÿ†ÈèÿˆÈêÿÎíÿŸÚõÿ’Óîÿ'j¬ÿK›ÿ%†Áÿ7›Íÿ7”Ëÿ}¼ÿf¯ÿc«ÿc¨ÿX£ÿR ÿU¡ÿNœÿM›ÿOžÿL™ÿLÿU‹ÿUÿq«ÿe«ÏÿY©ÏÿS§Ìÿe®Ðÿf­ÑÿT§ÎÿY©Ñÿc¯Óÿl³Õÿd°ÔÿY¬Òÿe±Óÿe¯ÔÿU«ÐÿW«ÓÿT©ÐÿS¨Ñÿ[¬Óÿ]­Òÿb¯Ôÿf¯ÔÿX«Ñÿ\¬Ñÿc®Óÿe°Ôÿg±Õÿh±Öÿ†Æéÿ˜ÑïÿŸÓïÿ¥×ñÿ¯Ùòÿ²Ùñÿ©Öðÿ«Ôðÿ¬ÖðÿªÓïÿ§ÑíÿšÌëÿ¦ÓïÿžÐìÿ£Òíÿ¬ÕíÿžÏêÿ‰Ääÿ‘Æãÿ˜Æâÿ޹×ÿG‰µÿ^˜ÿR‘ÿ0tÿfÿ*uÿ7‚ÿEÿM–ÿY¤ÿ k°ÿw¸ÿw¹ÿ{¼ÿ}Àÿ3ÊÿKœÔÿ2–Ðÿ)”Íÿ<›Ñÿ3‘Êÿ8†¿ÿdªÿ]¨ÿZ¥ÿV¡ÿC“Çÿ‘Èëÿ ÎîÿÅèÿI¨Úÿq¼äÿ–Èëÿ˜Ëìÿ•ÉìÿÎîÿ™ËíÿÆéÿ“Éëÿ˜Ñïÿ•ÍîÿÑïÿ©Øðÿ­Úòÿ™Ðïÿ¤ÕñÿšÑîÿˆÉéÿËëÿÊëÿ–ÐïÿŸÒðÿšÐïÿ˜Ïîÿ‚Çêÿc¾çÿiÃéÿ~Íïÿa¯Úÿa¨ÿG™ÿMœÿOžÿq³ÿˆÂÿ…¿ÿ~»ÿe­ÿa¨ÿ^¦ÿX£ÿOžÿLšÿQÿT ÿIÿSŠÿWÿYÿ cÿD•Âÿa«Îÿ}¶×ÿy´Öÿp²Ôÿq²Ôÿj±Õÿ]­Óÿ^¯Ôÿd±Õÿb±Ôÿi²Õÿb¯ÓÿY¨ÑÿR§ÐÿWªÐÿ]­ÑÿY¬ÒÿY­ÓÿY¬ÒÿX«ÑÿJ£ÍÿP¦ÍÿXªÐÿa®Óÿf±Õÿg´Øÿ™ÑñÿœÔòÿ–Îîÿ’Êëÿ—Ëìÿ§ÑðÿžÎîÿ§Óîÿ§Ôíÿ¡ÐíÿœËëÿ¢Ðìÿ©ÒìÿŸÎêÿŸÏëÿ˜Ëéÿ˜Êèÿ…»Üÿi«Ïÿ9q ÿ^•ÿWŽÿW…ÿÿ\Œÿ$s§ÿ@ŽÀÿNÏÿg±ÝÿN¡Ðÿf¦ÿCÿM™ÿM–ÿL—ÿQ›ÿSÿSŸÿSžÿP›ÿV¡ÿSžÿV¡ÿ+‹Æÿ6ŸÓÿ9¥ÛÿS±áÿvÂéÿ’Ëìÿ›ÏïÿŒËíÿšÔîÿ­Ûóÿ©Üóÿ±àôÿ“Óðÿ®Þôÿ¬ÜôÿšÓðÿ„ÇèÿxÃæÿtÄçÿ†ÉêÿÍíÿ{Äèÿ|Åêÿ ÓðÿˆËíÿsÉïÿjÀèÿ3Çÿ n¯ÿi¬ÿi­ÿ o¯ÿg¬ÿf«ÿZ£ÿZ¢ÿFˆÿP“ÿi®ÿe«ÿY¢ÿQÿP›ÿM–ÿMŒÿTŠÿVÿYÿYÿ n¨ÿH¡Íÿt¹Øÿl³Öÿn³Öÿh±Õÿh±Õÿh¯Ôÿ^­Óÿ`²×ÿa²×ÿa´×ÿc³×ÿ_°Öÿ]¯ÕÿV­ÔÿN©ÑÿG¦ÐÿUªÒÿT¨ÐÿJ¥ÎÿI§ÎÿW«ÒÿV­ÓÿX¯Õÿh¶Ùÿq¹ÚÿoµØÿr¹Üÿ„Âÿw½ÿa°ÿ `¯ÿ-{½ÿˆÃèÿÎíÿ¨Öîÿ£Ðìÿ®Öïÿ¨Ôíÿ•ÈæÿŒÂãÿˆ¾áÿ‡Ááÿf¢ÈÿEƒªÿ"[ÿ ÿÿVWVÿŒŽÿÀÂÁÿ×ÙØÿàâáÿàâáÿÙÛÚÿÅÇÆÿ˜›™ÿBCBÿÿ $4ÿ2n–ÿJŠ´ÿMžÊÿH“ÃÿR™ÿ?ÿI•ÿJ–ÿK—ÿM˜ÿO™ÿN™ÿTÿV ÿSžÿX£ÿY¤ÿPÿ"ˆÆÿ"•Ñÿ1Ÿ×ÿF¨Ýÿ^¶äÿÈìÿ…ËìÿŽÑïÿ§Ûóÿ±Þóÿ¦Ûòÿ³áõÿœÙòÿ¦Üóÿ Øòÿ‘Ðîÿ‰Ëëÿ~Æçÿ‡Ìëÿ‰Íìÿ€ÈêÿsÃéÿ–ÒðÿžÔñÿÍðÿh¿éÿ&ŠÂÿu´ÿy¶ÿu²ÿt³ÿu´ÿr²ÿi¬ÿ g«ÿ o°ÿc¦ÿ?€ÿKÿUŸÿO˜ÿIÿIŽÿKŠÿRŠÿVÿZÿ\’ÿ^–ÿ%‡¼ÿY­Õÿ_°Ôÿb°Óÿd°Õÿ`¯Õÿ\°Õÿa²×ÿ_±Öÿ]¯×ÿR¬ÕÿQ­ÕÿX­Õÿ_±ÖÿY¯ÖÿI§ÑÿD¦ÐÿK§ÐÿK¦ÎÿM§ÏÿQ§ÐÿN¨ÏÿRªÑÿSªÔÿO«Óÿ_³Øÿn¸Ûÿw¾Þÿn¼ßÿ,ŽÊÿ$ˆÅÿ|¿ÿg±ÿV©ÿl³ÿ-‚¿ÿB“ÈÿB‘ÅÿW Ïÿ]¦Ôÿf±Úÿa¦Òÿ3„½ÿ#\›ÿ#jÿUÿÿÿz|{ÿ˜œšÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÃÅÄÿ`b`ÿÿ*7ÿ2t™ÿ&q£ÿ7ƒÿ9‰ÿAŽÿD’ÿL˜ÿK˜ÿL˜ÿSœÿO™ÿRÿTÿVŸÿW¢ÿU ÿU¡ÿ1’Ïÿ1Øÿ%™Ôÿ4ŸØÿL¬ßÿ_»çÿ{Éëÿ¨Ýõÿ¦Ýõÿ¨Þõÿ Úóÿ¯â÷ÿ•Õñÿ”ÓðÿŽÎíÿˆÌëÿ„Êëÿ~Çéÿ|ÇêÿÉìÿzÉìÿzÊìÿ–×óÿ…ËîÿQ«Ùÿ0Åÿ‚½ÿ'ˆÁÿ-ˆÁÿz¶ÿy¶ÿ}¸ÿ~¹ÿ%ƒ¾ÿ0ŠÁÿ#…½ÿ„»ÿ T™ÿAÿ@ÿJ•ÿL“ÿR—ÿU›ÿR—ÿW”ÿ[•ÿ^”ÿdÿ0Âÿa¯Õÿ^®ÕÿW¬ÓÿX¬ÔÿY®ÔÿQ®ÔÿU®ÖÿY®×ÿY®ÔÿJ«ÓÿF¨ÓÿH©ÓÿIªÓÿC§ÐÿH¨Ñÿ@¤ÏÿB¤ÐÿM©ÑÿI§ÐÿA¤ÎÿF¥ÏÿK©Ñÿg·Ûÿh¶Ýÿa´ÚÿmºÝÿe¸Þÿ_·Þÿ(Éÿ*‹Çÿ1ŽÉÿ{»ÿq¶ÿl´ÿ`«ÿ^ªÿc­ÿ p¶ÿ#¿ÿE’Çÿ@ŽÄÿr³ÿ-zÿZÿ ÿÿ‚†„ÿ•š˜ÿàâáÿÞàßÿÄÇÆÿ°³²ÿ°³²ÿÅÇÆÿßáàÿàâáÿàâáÿàâáÿÓÕÔÿghgÿÿ '6ÿ1kÿ/wÿ;ˆÿ>ÿF•ÿK˜ÿJ™ÿMšÿU¡ÿT¡ÿOšÿTŸÿTŸÿU¢ÿUŸÿX¢ÿ3”Ðÿ<¡Úÿ!—Öÿ_¶åÿeºåÿpÂéÿ{ÉìÿŽÒïÿŸÙôÿ©àõÿ¤Üóÿ™ÖòÿšÖñÿ‰ËìÿˆÍëÿŽÎíÿ|ÉëÿpÄêÿyÇìÿnÇíÿ„Îïÿ}ÎðÿzËîÿH¢Óÿ9Åÿ:’Èÿ7’Çÿ.Åÿ4Çÿ)…¿ÿ%ƒ½ÿ+…¾ÿ+…¾ÿ6ÁÿD”Çÿ<‘Åÿ)ŠÂÿ$‚¼ÿE‹¾ÿb¨ÿ[¤ÿ^¤ÿe«ÿf¬ÿ m¯ÿl­ÿS›ÿ[–ÿk£ÿ8•ÇÿY¬ÓÿY­ÔÿY¬ÔÿT«ÓÿU­ÔÿPªÒÿU¯ÕÿQ­ÕÿP¬ÕÿL«ÔÿF¨ÒÿD¦Ñÿ@¥ÏÿD¦ÑÿC¦Ðÿ>£Îÿ;¢Íÿ<¡Ìÿ@¢Íÿ4œÉÿG¦Ðÿt¾ßÿÂâÿˆÉçÿg·Ýÿ]³Úÿc¹Þÿ`¶ßÿ#ŒÆÿ-‹ÇÿI›Ðÿ*…Àÿ}½ÿ(‚Àÿ{¼ÿf®ÿm³ÿ{¹ÿ"ƒ¾ÿE’ÇÿG–Çÿq®ÿDˆÿ=ÿÿnpnÿ•“ÿàááÿÍÏÎÿ’—•ÿ‡ŒŠÿ“‘ÿ‘•”ÿŽ’ÿ”˜–ÿÏÑÐÿàâáÿàâáÿàâáÿÔÕÕÿ^_^ÿÿ)ÿ-iÿ4{ÿ>ŒÿF”ÿA•ÿIÿPŸÿRŸÿOœÿQÿU¡ÿU¡ÿSŸÿS ÿ `©ÿ9˜ÐÿO«Þÿ5£Ûÿbºçÿb½çÿlÃêÿhÄíÿ…ÑðÿŸØóÿžÚóÿ—Öñÿ€Ììÿ‹Ðîÿ€ÉëÿƒÊëÿŽÎîÿÐðÿtÈíÿfÀéÿ\¾êÿÍñÿcÀìÿM¨ÖÿB›ÌÿHžÎÿKÎÿD˜Éÿ8‘Çÿ4Åÿ4Åÿ2ŒÂÿ7Ãÿ.ˆÀÿ7ÅÿA—ÉÿQ¡ÏÿLœËÿ;–Éÿ_°ÚÿvÁäÿqÀæÿk¹àÿL¢ÒÿV¦Õÿ<ŽÅÿ%~ºÿf¯ÿXšÿj¡ÿ>žËÿW­×ÿV¬Õÿ_±ØÿZ°ÙÿM«ÔÿS®ÕÿY¯ØÿO«ÔÿR«ÕÿMªÔÿJ©ÔÿI©ÒÿC¥ÐÿE¦Ðÿ=¤Îÿ-œÊÿ2Éÿ-™Æÿ1šÇÿOªÔÿÈäÿœÔëÿ¡Ôëÿ¤ÚïÿƒÇåÿP¬Öÿ_·Þÿr¿åÿ;˜ÏÿHŸÐÿFœÍÿ#ƒÀÿ*†ÃÿVžÎÿAÅÿj¯ÿq²ÿy¸ÿ*„¿ÿ8ÄÿH—Èÿ*}±ÿN‹ÿ ÿ675ÿ”’ÿÌÎÎÿÛÝÜÿ‘ÿ”’ÿsvtÿ:;:ÿ&&&ÿBDBÿ|}ÿ”’ÿÏÑÐÿàâáÿàâáÿàâáÿÑÓÒÿTUTÿÿ.ÿ,lÿ7€ÿ?ÿE•ÿSŸÿS¡ÿQ ÿPÿPžÿR ÿW£ÿU£ÿNÿ,…ÂÿY­ÛÿB Ôÿ/šÐÿWµåÿ{Êðÿ`ÂëÿU¾éÿaÃìÿÒïÿƒÍìÿkÂèÿ`¿çÿtÈëÿtÇìÿ…Îîÿ‘ÔòÿœÛõÿh¿éÿM¬ÝÿG¥ÙÿN©ØÿF£Õÿ<Ðÿ@ŸÏÿF¡ÐÿL ÏÿMžÎÿIšÊÿF™ÉÿD—Èÿ2ŠÀÿ9Åÿ?”Èÿ;’ÇÿF›ÊÿP¡ÏÿT©ÖÿjÄëÿŒÚôÿßùÿ“äûÿœæýÿãúÿžáùÿ“ØóÿÙóÿs²ÛÿoªÿrªÿE¢Ïÿ]°ØÿR«ÕÿV®×ÿKªÓÿIªÓÿI«ÕÿHªÕÿOªÔÿNªÕÿH©ÔÿG¨Óÿ@¤Ðÿ9¡Îÿ8¡Íÿ8¢Íÿ'˜Èÿ/›Éÿ/šÉÿ? Íÿ‚Êæÿ|Çåÿ’Ðéÿ¥Ûíÿ­ßòÿÄæÿZ°Ùÿ\µÝÿl¾åÿDœÑÿP¢Ñÿ6”Êÿ¾ÿ€ÀÿRžÏÿRœÌÿ o±ÿm±ÿ"|¹ÿ)ƒ½ÿ4ŒÃÿ%‰¿ÿq©ÿLÿÿqtrÿ“˜–ÿàâáÿ¹¼»ÿ‘ÿ^`^ÿ ÿ ÿ$;ÿÿ ÿpsqÿ ¥£ÿàâáÿàâáÿàâáÿàâáÿÍÏÎÿKLKÿÿ3ÿ8wÿCŒÿK™ÿLÿOžÿO ÿP¡ÿP¡ÿU¢ÿS¡ÿR ÿV¢ÿT¥Öÿs»äÿ^¯ÝÿI¤Öÿ7žÓÿlÃìÿoÉñÿZÀêÿJºæÿwÉíÿxÇìÿlÄìÿpÆëÿfÂëÿ_ÂêÿwËîÿ‹ÕóÿŠÒñÿA¡ÔÿC ÔÿM¦ÖÿG£Ôÿ<žÐÿ>¡Ôÿ9¡Óÿ>¢Ñÿ@žÐÿH ÐÿP£ÐÿT¢ÐÿQ¡ÎÿOžÌÿF˜ÉÿD•ÈÿB—ÈÿNËÿW¤ÑÿqÅêÿ‘Û÷ÿ¤æüÿœäüÿ™ãüÿèüÿ¥êýÿ¦çûÿ¯æúÿ°æüÿšÛõÿ‚ÏîÿrÃçÿdµßÿZ¬ÖÿO¨ÓÿS®ÖÿN«ÔÿI©Óÿ@¦ÒÿDªÔÿN¬ÖÿK«ÕÿD©ÓÿE©Óÿ=¥Ñÿ7¡Íÿ7ŸÍÿ1ŸÎÿ0Éÿ2ŸÌÿ/œËÿ[¶ÛÿˆÐéÿŒÍëÿŸÙíÿ£Üîÿ§Ýñÿ†Éèÿf¸àÿk¼äÿoÀäÿT£Òÿ_¨ÔÿE˜Êÿ †ÀÿB•Ëÿ^¦ÑÿNšËÿs´ÿj®ÿw¶ÿ-‚½ÿ1ˆÁÿz¶ÿf¢ÿ*Rÿÿ‡‹‰ÿ¦ª©ÿàâáÿ¦ª©ÿƒ‡…ÿÿ3ÿE‰ÿj¨ÿgÿ &8ÿÿ‘ÿàâáÿàâáÿàâáÿàâáÿàâáÿÉËËÿCDCÿÿ:ÿ?yÿKÿPšÿU ÿS£ÿR¢ÿT£ÿT£ÿY¥ÿX¥ÿ+†Áÿ]²Ýÿs½ãÿr½ãÿc²ÝÿB Ôÿ3Ôÿ:¥Úÿ_¿ëÿ^ÂëÿpÉïÿdÅíÿ~ÍïÿŠÐïÿwÍðÿ|ÏòÿtÉïÿjÂéÿ@¥ÔÿÇÿ*™Ìÿ(–Íÿ#™Ïÿ9¤ÔÿB¦Õÿ9¢Óÿ@£ÕÿJ¦×ÿD¤ÓÿF£ÑÿK¢ÐÿO¤ÑÿX¨ÒÿR£ÏÿG›ËÿN¡ÎÿP¡Íÿb´Þÿ€Ôôÿ¥æûÿ£çûÿ¢éüÿŸçûÿ™åúÿ¥êýÿ°ìýÿ²ëýÿ°êüÿ©åúÿÙõÿŠØöÿŒØôÿ}Æèÿ^°ÙÿM§Óÿ[®ØÿX¯Øÿ?¦ÐÿE©ÓÿX²ÚÿJªÕÿ=£Ðÿ<¤Ïÿ6£Ïÿ3¡Ïÿ0ŸÍÿ —Éÿ•Çÿ+™ÉÿB¦ÐÿsÄäÿƒÍéÿ£ÚîÿªÝðÿ£Ûðÿ«ßñÿ Õíÿt¾âÿo¾âÿm¾ãÿX¥ÑÿV£Ðÿ>–Èÿ+ŠÄÿb¨Õÿu°×ÿb¥Ðÿ|¹ÿr²ÿvµÿy¸ÿ%†¾ÿ8Š¿ÿ`›ÿ4ÿÿ‘•”ÿ®±°ÿàâáÿ«¯®ÿnonÿÿ(]†ÿMÿk¨ÿjžÿNsÿÿ~€ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÄÆÅÿ=>=ÿÿ"AÿDÿP”ÿX¡ÿ^§ÿc©ÿb«ÿ`«ÿ_ªÿt¶ÿH¥×ÿ]±Ýÿr¾ãÿb¶àÿL¬ØÿI§Øÿ4ŸÔÿÉÿ3¢ØÿpÆîÿbÂìÿuÎðÿ’ÙôÿžÞøÿƒÑñÿiÁéÿS³ßÿL«Ûÿ?¥Øÿ9 Ôÿ7žÓÿ"—Îÿ˜Íÿ(Ïÿ6ŸÑÿ9£Óÿ<¤ÓÿX®Úÿ@¤ÔÿA¤ÕÿP§×ÿI£ÒÿA ÏÿH£ÐÿD ÏÿL¥ÒÿM§ÕÿÏïÿâúÿ¨èüÿ¥èüÿ«íýÿ–ãûÿ èüÿ«ìýÿµíýÿ¯ìýÿ¦êüÿçüÿ”Þ÷ÿ¬äúÿ¢ãøÿ›àøÿ‘Ùóÿ{Ãåÿd±ØÿX®ØÿE©ÓÿK¬ÖÿE«Öÿ>¦Òÿ?¥Ïÿ7¢Îÿ(œËÿ˜Éÿ'›Ëÿ,šÊÿ2œËÿF¨ÑÿwÆåÿ‡Ñëÿ”Õëÿ§Ýðÿ¦Ûïÿ¡Úîÿ¥Üðÿ¤×îÿn¹ßÿh¼áÿrÁäÿX¡ÎÿS¡ÏÿKžÌÿ,‹ÂÿW£Òÿ·Ûÿi¬Ôÿ!ºÿm±ÿy¸ÿ|¼ÿ$…ÀÿQŸÌÿ!hŸÿ +ÿ%&%ÿ‘–”ÿ¨«ªÿàâáÿÅÇÆÿmonÿÿ=m‡ÿJ€ÿ[’ÿ[ˆÿIgÿÿ˜š™ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¾À¿ÿ787ÿÿ(KÿG„ÿ[œÿd¨ÿg­ÿl°ÿk°ÿh®ÿ*ŽÉÿY¯Ýÿf¸àÿ_¶àÿEªÙÿB©Úÿ7£Õÿ*žÒÿ!™ÏÿËÿWµâÿiÃëÿuËïÿ…ÒòÿvÉëÿ\ºâÿVµàÿ`·áÿY´àÿX´ßÿ^¶áÿ^´àÿY±ÞÿC§Øÿ.žÑÿ/ŸÏÿ>¥ÕÿD¨×ÿJªÙÿX±ÜÿX¯Ûÿc³ÞÿZ°ÛÿV®Úÿ[°ÛÿY®ÚÿW­Ùÿ`¶âÿ“Ý÷ÿªéüÿ±ëýÿ©êüÿŸäúÿ”áùÿ¬êýÿ»ïýÿ¯ìüÿ³ìýÿ½ïýÿ´ïüÿªëüÿ¹êýÿ½ëýÿ±éüÿªçûÿ˜ßøÿ‡Îëÿr¹ÞÿQ©Õÿ=¤Ñÿ4£Ðÿ>¦Òÿ;¢Îÿ2ŸËÿ"–ÇÿÄÿ)™ÉÿB¥Òÿe¼àÿÖíÿ¦Ýñÿ¢Ûïÿ Ûðÿ˜Øîÿ£Ûïÿ§Ûíÿ ØíÿˆËèÿr¼ÞÿwÀáÿvÂäÿ\¤ÐÿV¢ÎÿJ›ÊÿKžÍÿg¬Ôÿw²Öÿd¬Óÿ{¸ÿg°ÿv¶ÿ#|»ÿ5ÄÿOËÿ>°ÿ=ÿÿ”’ÿ•š˜ÿàâáÿàâáÿ™›šÿÿ(0ÿJlÿBlÿCfÿ"ÿÿÒÔÔÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿ¸»ºÿ000ÿÿ3WÿVŒÿd ÿ p®ÿ uµÿq³ÿ u¶ÿ8žÑÿZ±Þÿm½ãÿb¸àÿJ­ÛÿJ®ÛÿB«Øÿ5¢Òÿ.ŸÑÿÊÿ ŠÇÿJ¯ÝÿnÅëÿa»çÿW·âÿY¶áÿb»ãÿsÀåÿh¼åÿb»äÿf½åÿq½åÿo½åÿ^µàÿO®ÜÿKªÚÿC¨×ÿGªÙÿ^³ßÿrºãÿr»âÿmºàÿi¶ßÿb²Ýÿ`³Ûÿg¶Ýÿl¶àÿƒÌïÿ©åûÿ§äûÿ°ëýÿ¢èûÿ’Þøÿ–à÷ÿ¯ëüÿ´íýÿ¯êüÿ¦éüÿ°ìüÿ½ðýÿºðýÿ¶îýÿ·ìýÿ·ìýÿ²êýÿ¦ãùÿªçûÿ¸êûÿ™Ôðÿn¾âÿe»àÿR®ØÿNªÖÿL«ÖÿMªÕÿgºßÿÐëÿ¥Þñÿ¡Ýòÿªâôÿ´äõÿ¬àòÿ›Úðÿ—×íÿ«Ýïÿ¥Øíÿ•ÑìÿzÁáÿu½ÞÿƒÅäÿ{Ââÿc«Ôÿ]¥Ðÿ`§Ôÿa«Õÿj¯Öÿl®ÔÿaªÑÿu¶ÿl´ÿ/‰ÂÿY¡ÐÿQ¡ÎÿGšÉÿ*y®ÿXÿ ÿƒÿ…ŠˆÿÓÔÔÿàâáÿÝÞÝÿ|~}ÿÿ ÿ ÿÿ'''ÿ¾¿¾ÿâããÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿ²´´ÿ*+*ÿ ÿCgÿc•ÿt«ÿ|µÿz¸ÿ)‹ÅÿA¢ÔÿV°ÝÿzÂçÿg¼âÿG®ÛÿL¯ÛÿS²ÞÿQ°Üÿ;¦Ôÿ&›Ïÿ‘Ëÿ‘ÌÿP³àÿ\»æÿl¿èÿrÁèÿsÂçÿ}Äèÿ}ÄèÿtÀæÿwÂçÿ„ÇçÿÅæÿm½ãÿi»áÿb¶áÿZ´àÿY³ßÿs½äÿ}Áãÿu¿âÿq½âÿn¸ßÿi¶ßÿdµÞÿpºßÿx¿åÿ•Úõÿ¬çûÿ¶éüÿ¯éûÿ¡åúÿÚôÿÚõÿ äùÿªéüÿ¬éýÿªêüÿªëýÿ©éüÿ±éüÿµíüÿ²íýÿ²ëýÿ²êýÿºëüÿºëýÿµëþÿ²ëüÿ¬èûÿ·êûÿ¬ãøÿŸßõÿ¤âöÿ±æùÿ»ìûÿ¾ìùÿ¶è÷ÿµå÷ÿ¹ç÷ÿ«àòÿ ÜñÿžÛñÿ™Øîÿ Öìÿ…Åäÿx¶Úÿj´Øÿq¹Üÿ|Ááÿo»Þÿe«Ôÿa©ÒÿcªÔÿeªÔÿdªÓÿZ¨ÑÿS¡Íÿp³ÿj±ÿ#…¾ÿ<“ÆÿC™Éÿ9’Åÿu°ÿ(yÿÿ\^]ÿŠÿ¢¦¤ÿàâáÿàâáÿãääÿÊËÊÿ•••ÿŠŠŠÿ±±±ÿÜÜÜÿãäãÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáââÿ¬¯­ÿ%%$ÿ)ÿTwÿpŸÿw­ÿ¸ÿN£Óÿg³ÝÿjºãÿxÅèÿh¾ãÿUµßÿJ°ÝÿT·àÿ_ºâÿK®Úÿ;¥Õÿ.žÑÿ#˜Îÿ:£×ÿ|ÇëÿŽÍîÿŒËìÿŠËëÿÍìÿÎìÿÍìÿ‘Ìëÿ‡ÉéÿˆÉéÿ|Ææÿk¾ãÿk½ãÿgºãÿp¾äÿÄæÿ‚ÄåÿxÁäÿm¼ãÿm»áÿjºâÿl¼áÿo»áÿbµáÿ†Òñÿ¦åùÿ›à÷ÿ˜Þöÿ âøÿ§æúÿ“Þöÿ®æûÿ¨åúÿ¤åúÿ´ëýÿ·ìýÿ«éýÿ²êüÿ½íüÿ³êýÿ©éüÿ­ëýÿ°ëýÿ²ëýÿ«éýÿ©çûÿ°çûÿÂìüÿÅïýÿ¿íüÿ½ëûÿ·êûÿµéûÿ²éùÿ¬ã÷ÿ¥ßõÿ§àôÿ²áòÿ§ÞñÿÙïÿÊçÿ†Êåÿ~Ãáÿ†ÃâÿnµØÿc²Öÿm·Ùÿq¹ÜÿW£ÏÿY§Òÿ]©Òÿ\¦ÑÿU¤ÏÿZ§ÒÿMžÊÿ p²ÿj°ÿs²ÿ€¼ÿB—Éÿ2ŽÃÿn­ÿ0„ÿ7ÿÿ‰Œ‹ÿ…Šˆÿº½¼ÿàâáÿàâáÿáââÿãääÿäåäÿãääÿáââÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáââÿ¦©§ÿ ÿ *ÿYÿ k™ÿ{¯ÿGžÎÿt½àÿ~Åèÿ›ÙóÿsÄèÿc¹ãÿvÂæÿpÁåÿl½åÿT³ÝÿA©ÖÿB§×ÿ/Ñÿ$•Ëÿ]·ãÿ–×òÿšÔñÿ•Òðÿ ÖðÿœÓðÿ•Òîÿ•ÑîÿŠËëÿŠËêÿƒÈèÿ}Äçÿ|ÄçÿÆèÿƒÇçÿ„ÈçÿÇçÿÆçÿ„Äçÿ~Âçÿx¿äÿh¸áÿ\´ÞÿO°Þÿ„Ïñÿ¢â÷ÿŸá÷ÿ˜ÞõÿŠÖòÿ¡äùÿ®çûÿ®èüÿŸâøÿ ãøÿ¦åûÿ®êýÿµëýÿµêüÿºéýÿ´ëýÿ©èüÿ¨éüÿ¥éüÿžæüÿ›æüÿ¦éüÿ°ëüÿ°éüÿµêýÿºëüÿ³éûÿ±éûÿ¨çùÿ«å÷ÿ«â÷ÿ«âõÿºèöÿ¶ãôÿ¤Ùðÿ‘Îêÿ’Íçÿ—Ìçÿ”Ìæÿ˜Íæÿ}½ÜÿZ­Óÿa¯Ôÿn¸ÚÿCšÊÿS£ÍÿP£ÎÿDšÉÿU¥ÐÿaªÔÿD˜Éÿk°ÿl°ÿq²ÿ/‰Áÿ6Åÿ}¸ÿ _¥ÿ2‰ÿ7‡ÿÿCDCÿ”’ÿ…Šˆÿ»¾½ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿŸ¢¡ÿÿ*ÿ \ƒÿu¢ÿA”ÂÿY®ØÿŽÎìÿ‘Òîÿ‚ÌëÿwÃæÿˆËêÿ‚Èèÿi½ãÿL­Øÿ=¥ÒÿC¨Öÿ6 Óÿ)™Îÿ'˜Íÿ}Èêÿ¨Þõÿ Úôÿ¤Úòÿ¡Øðÿ–Òïÿ”ÐîÿŽÏìÿ‰Íêÿ†Ìêÿ‹ÎêÿŠÍëÿ‹Ììÿ‰Ìêÿ‰Ëêÿ‰ËêÿÌìÿ”Îíÿ–ÎíÿÊëÿjµàÿ[°àÿ\·äÿ•Öòÿ—ÚóÿœàöÿŠÔñÿxËîÿ™Ýõÿ·ëýÿ°èüÿœà÷ÿ¦ãøÿ§åùÿ©çúÿ¯éýÿ®èüÿ±èüÿ®éüÿªéüÿ¢æüÿ™äúÿ™ãúÿ”âûÿ‘ßúÿáùÿ›äûÿ©çûÿ­éüÿ°éüÿ¦åúÿ¥ä÷ÿ¬ãöÿ©áöÿ¯ãöÿ°áôÿ£ÛñÿÕìÿŸÔêÿ§ÔéÿªÔêÿ¥Òèÿ£Ñçÿn¶ØÿS¨Ïÿe¯Òÿh²Õÿ>šËÿ>™ÊÿIžÌÿ>–ÈÿJŸÍÿ[§Ñÿ¹ÿa¬ÿo±ÿx·ÿ6ŽÃÿ/‰Àÿ~¹ÿY£ÿ@–ÿ?’ÿ&QÿÿY[Yÿ”’ÿ…Šˆÿ¸»¹ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßáàÿ˜›šÿÿ !-ÿgŒÿ0ƒ®ÿX©ÐÿƒÇåÿ‚ÊçÿŠÎêÿ‡ÌêÿwÈçÿrÁåÿo¾âÿP­ÚÿQ®ÚÿO«ØÿLªÚÿ/›Îÿ3›ÏÿF¦ÖÿxÆêÿŽÒðÿÛôÿ¤Ýöÿœ×òÿœÕòÿÒîÿ}Éêÿ{ÈìÿxÉéÿ‚Êéÿ€Èêÿ}Ééÿ‰Îìÿ–ÑîÿÓðÿœÔîÿœÔïÿ•ÎîÿÅèÿl»åÿg»æÿÍíÿ†ÎíÿÕðÿŽÕðÿŽÖñÿ«äøÿ±çúÿªäùÿ©âøÿ¡á÷ÿžã÷ÿ¨åúÿ¢ãùÿ¼ëûÿ§äùÿªæúÿ¦æùÿ—âøÿ“àøÿžãùÿŠÚ÷ÿvÑóÿ…Øöÿ›äúÿŸåúÿ¬çûÿ¹èûÿ©åùÿ¬äøÿ¨áöÿ¥ßôÿ°âôÿ¬ßòÿÖîÿ£Õëÿ¥Óêÿ¦Ôéÿ©ÕéÿªÔéÿÆäÿ[ªÒÿY¨ÌÿYªÏÿg®Ñÿ4˜Éÿ"Ãÿ7•Èÿ?—ÊÿS£ÐÿKžËÿq´ÿa­ÿr³ÿz¹ÿ(‰Àÿx¶ÿn°ÿHšÿ8•ÿC›ÿJ›ÿ&EÿÿWYWÿ”’ÿ…Šˆÿ²µ´ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÞàßÿ”’ÿÿ #/ÿ*q”ÿJ–ºÿv»Ùÿ‚Èäÿ‰Ìèÿ‰Ïéÿ}ÈèÿvÂäÿl¿áÿp¼âÿb·ßÿU­ÙÿP«×ÿA¤ÒÿG¥ÓÿA Ïÿ:žÑÿW¯Úÿi»áÿ{Èéÿ›ØõÿzÃêÿ4œ×ÿ;ž×ÿAÖÿNµäÿnÈíÿ…ÎìÿÐîÿ‘Òïÿ˜Õòÿ¡Øóÿ£Ùòÿ¢ÚòÿœÔòÿˆÊìÿb·ãÿqÁéÿ–ÖñÿŽÓïÿ‡Íîÿ•Öòÿ•Ùóÿ©ã÷ÿ¬âöÿ£Þõÿ Üóÿ«áöÿ£Þöÿ™Ûôÿ‘Ùòÿ¥àöÿœÝõÿà÷ÿ™ßöÿ–Üõÿ†×ôÿŽÙôÿ}ÐñÿiÊðÿzÒóÿ†Øõÿ™Þ÷ÿ™ßöÿŸàöÿ›Þôÿ–ÜõÿŸÞõÿ¥ßóÿ©ÞôÿžÙñÿ¤Öîÿ¤Óëÿ¦Ôêÿ¦ÕéÿªÖèÿ¢Ñçÿ‚Áàÿh±Ôÿ^¨Ìÿ_©Ëÿ^©Ìÿ1“Çÿ-‘ÆÿFœËÿFšÊÿF›Ëÿ*ŠÀÿ_ªÿ\©ÿq²ÿ»ÿ€»ÿk°ÿGÿ9–ÿ?šÿHŸÿQ¢ÿ[¤ÿ*GÿÿOQPÿ“’ÿ…Šˆÿ­°¯ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÝÞÞÿ‰ŠŠÿÿ&2ÿCŸÿk¨Äÿ{½ÙÿÅäÿ€Éçÿ{ÇåÿnÁâÿ\´ÜÿyÀâÿmºàÿS¯ÙÿOªÖÿR­ØÿQª×ÿG¥ÒÿG¦ÔÿQª×ÿE¡Ñÿ:žÎÿC¤Õÿ1˜Íÿ&Çÿ!ŽÆÿ‰Åÿ8ž×ÿV¹çÿƒÐðÿ˜Öòÿ¢Ùôÿ•Óðÿ‰Ïïÿ€Ëíÿ|Çëÿg»ãÿQ®Üÿ*šÑÿb»äÿ–Ôñÿ˜ÕñÿÒïÿzÈéÿ†Ïìÿ‹ÓðÿžÝóÿ“Õðÿ€ÈêÿÒðÿšÙòÿ•Øòÿ”×òÿšØóÿÖñÿŽ×òÿšÝöÿŽØòÿÑðÿ}ÎïÿtËïÿjÇîÿkÇïÿrËñÿ|ÑòÿÓòÿ‹Öòÿ—Ûôÿ¤Þöÿ—×ñÿÓíÿ”ÒîÿŽÏìÿœÒìÿ¨Öíÿ¨Õêÿ¬Øëÿª×éÿ›ÍåÿÅàÿ„¾ÜÿX¡ÉÿJšÁÿPŸÄÿ;•ÊÿU¢ÏÿKÌÿ;–Ëÿ8•Éÿ{¸ÿW¥ÿS¥ÿh¯ÿ u¶ÿ u¶ÿ b§ÿ9ÿAšÿKŸÿU§ÿW¨ÿ^©ÿ_¥ÿ-MÿÿHIHÿ’‘ÿ…Šˆÿ¨«ªÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÛÝÜÿƒÿ ÿ,6ÿ_’¦ÿh©Åÿs¼ØÿoÀãÿ…ÍèÿuÂåÿa´ÝÿyÁâÿq¼àÿ`³Ûÿ^²ÛÿY¯ÙÿX¯×ÿOªÕÿPªÔÿV­×ÿV¬ØÿQ¨ÕÿD¤Òÿ:œÎÿ2™Êÿ-˜Ëÿ,™Íÿ9Íÿ8›Îÿ=¡Óÿ>¢Òÿ:ŸÎÿ%“Çÿ!’Èÿ1›Ïÿ*˜ÍÿŒÆÿÉÿ'™Ðÿ]¹åÿyÇéÿqÂçÿ€Ëíÿ{Çéÿ‡Îìÿ–×ñÿžÙñÿ›×ðÿ™Õñÿ“Óïÿ‘Ôîÿ™ÙòÿšÚòÿ™×ñÿŽÕðÿ“Øòÿ×òÿŽØóÿØòÿ‰ÓðÿqÇîÿpÇîÿhÅîÿqÊïÿpÊïÿxÌïÿ‡Óïÿ“Ùóÿ£Ûòÿ•ÔîÿˆÏêÿ•Òìÿ‘Îêÿ›Òëÿ§Õëÿ¦Ôéÿ¥ÖëÿœÎåÿŸÍãÿ¦Îäÿ‡ÀÜÿJ—Áÿ6‹·ÿB“¼ÿ9˜ËÿU¤ÑÿKÎÿ>–Éÿ.Æÿ s±ÿ^¨ÿY§ÿ`«ÿb­ÿ[©ÿZ¦ÿD˜ÿGÿM ÿV¦ÿ[¨ÿ`«ÿf¯ÿ_¦ÿ0SÿÿABAÿŽ’ÿ…Šˆÿ£§¥ÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÙÛÚÿy|zÿ ÿ!19ÿX§ÿr°Èÿz¾Ùÿ|Ãâÿ‡Ìèÿ|Ãåÿu¾áÿg·Ýÿe¶Üÿg·ÜÿW°ÙÿJªÕÿT¬×ÿQªÕÿN¨ÓÿSªÖÿXªÖÿZ­ØÿRªÕÿN¦ÓÿE£Ñÿ@ŸÐÿ/–Ìÿ&•Éÿ3›Îÿ)•ÊÿÇÿ"•Éÿ%•Éÿ&”Èÿ&”Êÿ(—Ìÿ1žÒÿGªÛÿi¾èÿg¾æÿzÆéÿ|Èêÿ€Ììÿ’Óðÿ¢ÛóÿÔðÿ›Õïÿ¥Ûòÿ˜Õïÿ‡Ììÿ~ÈéÿÒîÿƒËëÿ‰Ðíÿ†Ñîÿ“×ñÿ—Øñÿ•Õñÿ|ÊîÿxÉíÿqÇíÿgÃêÿrÇíÿtÈíÿyÈêÿvÇêÿ„Ðíÿ–Õîÿ’Ðíÿ„ËçÿƒÈæÿŠÈæÿ„ÅãÿÉåÿ™Ìåÿ•Êãÿ™Êâÿ¥Òåÿ™ÌãÿŠÁÝÿ>ºÿx­ÿ±ÿ@™Ìÿ:—Ìÿ9–Êÿ@—Íÿ%ˆÁÿh­ÿc¬ÿc«ÿ[©ÿd¯ÿB‡ÃÿW—Ëÿ0o±ÿ LŸÿF›ÿQ£ÿ[¨ÿ]­ÿi°ÿf¯ÿe©ÿ5[ÿÿ;<;ÿŒŽÿ†‹‰ÿŸ£¡ÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ×ÙØÿoqpÿ ÿ"4;ÿ[“ªÿt²Çÿ¾Ùÿq¼Þÿn¾àÿtÀàÿg·Ûÿe´Ûÿk¹ÞÿdµÜÿT¬ÖÿJ§ÓÿP©ÔÿR©ÓÿF¥ÒÿW¬×ÿX«ÖÿT©ÕÿWªÖÿ[¬ØÿK¥Òÿ6œÎÿ>¡ÒÿP©Øÿ: Òÿ*˜Íÿ)™Ìÿ2›Îÿ3™Íÿ4Ñÿ>¡Õÿ7ŸÒÿ<£Öÿ_ºäÿ_¼åÿ~Éêÿb»ãÿa¸âÿ‹Ïìÿ˜ÔðÿœÕñÿœÖðÿ—Óïÿ–ÑîÿŠÊêÿ…ÇèÿŽÎíÿˆÎìÿ‚Ìëÿ…ÎíÿËêÿˆÏëÿ†Ïíÿ€Ëëÿ{ÊìÿiÃëÿuÆêÿ„Îíÿ‹Ðíÿ‚ËêÿyÇèÿzÄåÿtÂåÿ„ÉçÿÍçÿÈçÿÂáÿÆâÿ•ÈãÿœÍäÿ›ÍäÿšÊáÿ–Æàÿ‘Åàÿ—ÇÞÿ1ˆ·ÿj¤ÿp¦ÿ4“ÉÿB˜ËÿCšÍÿ9•Ìÿ oµÿ_­ÿc­ÿg¯ÿQ¢ÿj´ÿ™Ìëÿ—Æèÿƒ¾åÿ_¨Øÿ"l°ÿLŸÿY¨ÿ]¬ÿd°ÿk³ÿj°ÿg­ÿÌÿA ÍÿY¬Öÿ`°Øÿe·Üÿe¶ÞÿdµÝÿkºßÿ‚Ææÿh·ÝÿMª×ÿI¨ØÿH¦ÖÿRªÚÿI¤Ôÿ8žÑÿ8ŸÐÿ)—ÌÿŠÄÿŠÃÿ…ÁÿK«ÙÿoÂçÿwÀäÿm½âÿ‡Éèÿ“Ïêÿ“Ìêÿ‘Ëéÿ†Çåÿ„ÅäÿÁäÿ€ÀáÿÀáÿo·Üÿq·Ûÿq¹Ûÿe³Ùÿi´Ûÿp·Ûÿm´Úÿ}ºÛÿ}¼Üÿ†ÃâÿŽÃãÿÀàÿ‡ÄâÿÄãÿÅåÿ–Éæÿ†Àáÿ{»Ûÿ‘ÅâÿœËâÿŸËãÿ§Ñãÿ›ÉÞÿ‘ÁÜÿœÅÞÿšÆÜÿ{µÓÿd¤Èÿw®Íÿo¬Ìÿ­Üðÿ¬Ýòÿ˜Ùñÿ“Öñÿ—Ôðÿ“Ññÿ‰Îîÿ‡Îïÿ{ÈïÿŒÐðÿ}ÊîÿvÅíÿmÁíÿ~Äïÿv¿ëÿd¶çÿ`³çÿcµæÿ`³åÿE§àÿQ¯âÿƒÍîÿ•ÔðÿŠÎîÿ‰ÍíÿÏëÿ„Æåÿe¬Íÿ-FQÿÿz~|ÿŠÿ‘ÿÍÏÎÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÃÅÄÿ@A@ÿÿ.LZÿP¬ÿZ¤Äÿ^¯ÓÿmºÝÿq»Ýÿc·ÛÿgºÞÿhºÝÿV®ØÿY°Ùÿ^´ÛÿX²Ùÿd·Ýÿh¸ÞÿkºßÿdµÝÿy¿áÿl¹ßÿ^²ÛÿT­ÙÿD¥Ôÿ@¢Ôÿ=ŸÑÿ6œÐÿ3™Îÿ4™Íÿ‹ÄÿŠÄÿƒÀÿ-“Êÿk¾åÿ}ÂäÿxÃäÿÇåÿƒÇæÿŠÊèÿŒÉçÿ„ÅåÿÉèÿŠÅåÿ~¾ßÿ{»ÝÿpµÙÿk´Ùÿr´Øÿa®Óÿ}¹Úÿq´Øÿf¯Õÿe®ÔÿvµÙÿƒ¼ÝÿqµØÿlµÙÿ¾ßÿƒÀáÿ•Éçÿ‰Æäÿ€ÁáÿÅäÿ‹Ãàÿ”Çáÿ§Óçÿ¡ÏåÿÁÝÿ”ÄÞÿšÅÝÿ”ÂÛÿf¨Éÿf¤Ãÿn©ÈÿiªÇÿ²Ýòÿ®Þòÿœ×òÿ™Õòÿ’Óðÿ—Õðÿ‰ÎïÿpÄíÿkÂìÿhÂìÿhÂíÿa»éÿ[¶éÿn½ìÿgºèÿf¸êÿh¶çÿ[³åÿM«âÿD¨àÿuÅìÿ~Ëîÿ‡Îïÿ“ÔðÿÒïÿ…ÍíÿˆÌêÿ~Áãÿa¯Ôÿ-FRÿÿvxwÿ‹ŽÿŠÿÊÌËÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¾À¿ÿ998ÿÿ3SaÿX”¯ÿ_§ÇÿhµØÿe¸Üÿq¾àÿm½ßÿc·Üÿj»àÿw¿ãÿyÂàÿ€Åäÿt¿áÿa·Úÿh·Þÿh·Ýÿpºßÿi¶Ýÿj·ßÿ_±ÜÿX®ÚÿB£ÓÿE¤ÔÿJ¥ÕÿA Òÿ5™Íÿ/•Ëÿ+“Çÿ'Èÿ„¿ÿA¢Òÿeºãÿ{ÆçÿÆäÿ{ÅåÿƒÇçÿ‚ÄäÿxÀàÿƒÅãÿ‡Âáÿs¸Üÿl²Ùÿi±Öÿi¯Öÿ^«Óÿ[©Ñÿe¬Òÿh­Ñÿ`©ÐÿfªÑÿx²×ÿ„¼Üÿm¯Ôÿb¬Ôÿ‚½ßÿ“ÇãÿÇäÿ€ÀßÿÇæÿ‘Éåÿ‘Èäÿ…¿ÞÿÇãÿ›Êáÿ›Èàÿ”ÂÜÿ”ÁÛÿˆ¹Öÿ\¢ÄÿU›¼ÿZŸÀÿb¥Ãÿ«Úðÿ±áôÿ­ÞõÿŸØôÿØôÿ–×òÿ…Îîÿ}ËîÿuÅìÿ\½éÿW¹çÿXµçÿL­ãÿU°æÿM®âÿM®ãÿY³æÿV²åÿS®äÿd½ëÿÍîÿ…Ñðÿ„ÑîÿÓðÿ’Óðÿ‰ÏïÿÉíÿ†ÈêÿÃåÿr·Ùÿ1HSÿÿnqoÿ‘ÿˆ‹ÿÆÈÇÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ·º¹ÿ343ÿÿ9\mÿI­ÿ_ªÊÿk¶Øÿo»Ýÿl¼Ýÿe¹ÞÿiºÞÿr½àÿ|Âáÿ{Áàÿi¶Üÿm¹Ýÿl»ßÿc¶Üÿe·Þÿ[¯ÚÿY¯Úÿ_²ÝÿdµÝÿO¨ÖÿL§ÖÿQ§ÖÿJ¤ÔÿB ÑÿH ÒÿDÏÿ0“Éÿ„ÀÿŠÃÿe¸àÿ|ÃæÿtÂåÿo¿ãÿÆäÿˆÈåÿ‹Èåÿv¼Ýÿs¸Ûÿq²Ùÿk¯Öÿe°ÖÿN£ÏÿR£ÌÿZ¦ÏÿbªÏÿPŸÊÿLŸËÿS£Ìÿ`¦Íÿ{µ×ÿo°Ôÿf­Óÿo®Ñÿ~¹Úÿ‰Ãàÿ|¿Þÿ‡ÄâÿÈçÿÉæÿœÎæÿÄáÿÃàÿÃÞÿ}·×ÿu³Ñÿ]£ÇÿD“¼ÿH”»ÿf¤Åÿ\ž¿ÿ¦×ïÿ¨Üóÿœ×óÿÐðÿ“Óòÿ€Ëðÿ…ÎðÿƒÌîÿyÇíÿjÁìÿV¶èÿR²æÿQ­âÿM«àÿL¬ãÿC©Þÿ4¢Ûÿ6£Ýÿ]¹èÿ€ÏïÿŒÒïÿ‡Óïÿ‹×ðÿ’×ñÿšÖóÿœÕòÿ–ÓðÿŠÍíÿzÃèÿyÃäÿv·Øÿ-GTÿ ÿhkiÿŽ’ÿ‡ŒŠÿÂÄÃÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿ±´³ÿ./-ÿÿ4`sÿW–²ÿa§Éÿi´×ÿg¸Üÿg»ßÿn»ßÿu¾ßÿzÀáÿl¹Ýÿe´Ûÿ…Åäÿ†Çæÿq½àÿu¿áÿd´Ýÿ_±ÛÿY¬ÚÿP¨ÕÿY­Ùÿh³Üÿ_­ÙÿS¨Öÿ\¬Øÿ\«ÖÿP¢Òÿ9–Ìÿ†Àÿp³ÿA›Ìÿ‡Éçÿn½âÿtÂåÿ’ÎéÿŒÇæÿˆÆäÿ¿ßÿs·Ûÿo³×ÿj­ÕÿQ¥ÐÿO¤Îÿf­ÑÿY¤ÌÿX¢ËÿMŸÉÿOŸËÿA˜ÅÿIœÆÿ_§ÍÿZ¤Ëÿ^¨ÍÿJ¼ÿh®Òÿu·Ùÿ„¾Þÿ‰ÃáÿƒÀàÿ…Ãáÿ—Éæÿ–ÇâÿuµØÿY¥Ïÿh«ÍÿE”Àÿ<Ž»ÿO™¿ÿI•¼ÿa¢ÁÿZ›¿ÿªÝôÿ Úôÿ™ØóÿzÆëÿpÅíÿyÉïÿƒÍñÿˆÏòÿ†ÍðÿvÅîÿa¹êÿb·èÿj¹èÿj¸çÿZ°ãÿH¨ßÿ=¤Ûÿ1ŸÚÿ\¾ëÿÓòÿŽÔñÿ‰Òðÿ†ÎîÿÑïÿ”Òñÿ“Òïÿ–Óñÿ”Ñïÿ‹Îíÿ€ÉêÿvÂåÿ¿Üÿ5LWÿ ÿbdcÿŽ“‘ÿ†‹‰ÿ½À¿ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿ«­­ÿ(('ÿ%,ÿ=jÿT”³ÿR¢Æÿ[¯Óÿd·Ûÿk»àÿvÀáÿi·ÝÿˆÉçÿ}Ããÿk¹ÞÿyÁâÿu¿áÿg¶Ýÿ[²ÛÿY¯ÙÿV«ÖÿT©ÖÿX«Øÿe´ÜÿN§Ôÿ?žÏÿS§Öÿl²ÛÿT¦Òÿ4’Êÿ|»ÿ_ªÿs³ÿl¼ßÿwÀâÿŒÌêÿÑìÿ’Êæÿ‚Âáÿ|¼Üÿ`®Õÿd®Öÿm¯ÔÿU£ÎÿN ÌÿMŸÉÿB™ÅÿC–ÃÿGšÆÿLÆÿ@”Ãÿ8Áÿ6Áÿ1Ž¿ÿGšÆÿP¡ÍÿP¤Ïÿ_«ÒÿyµØÿ~¹Ûÿ»Üÿ}½Ýÿl´Øÿ_«ÒÿQ£ÍÿE™Èÿ[£ÌÿN’ÁÿI‘½ÿoªÌÿT™¾ÿ[¾ÿ\Ÿ¿ÿ¢ÙóÿœÙõÿ›ÙôÿwÄëÿ`»èÿkÁìÿoÁìÿf¼ëÿrÂîÿuÀëÿb·æÿe·çÿ_³äÿb²äÿg´äÿ^°âÿF¨Ýÿ<¨ßÿkÈïÿ™Øóÿ›Øõÿ˜ÙôÿˆÓïÿ„ÐîÿŽÓðÿ”Ôðÿ“ÔïÿˆÎíÿŠÏîÿ€ÊëÿzÅêÿ{Ãæÿl¶Ùÿ*HWÿÿ[\[ÿ“‘ÿ…Šˆÿ¸»ºÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¦¨¦ÿ##"ÿ(/ÿDuŒÿR”³ÿR¢Åÿgµ×ÿh¹ÛÿaµÛÿi¸ÜÿwÀàÿyÂâÿf¹Þÿg·Ûÿp»ÞÿhºÝÿ\³Ûÿ\¯ÙÿrºÞÿm¶Ýÿm¶Ýÿf±ÙÿU¨ÔÿJ¡ÑÿR¥ÔÿX©ÔÿU§Óÿ:•Éÿl±ÿc¬ÿ]¨ÿ4“ÆÿzÅåÿ‹Éèÿ‹ÈæÿŽÇåÿ€½Þÿs¹Ùÿe®Óÿd¬Ôÿ\§ÏÿMŸËÿA–Æÿ7Áÿ?”Áÿ7’¿ÿA•Âÿ:¿ÿ)ˆ½ÿ„¹ÿ!„¹ÿ„ºÿ,оÿC•ÃÿMžËÿ]©ÒÿV£ÎÿA•ÈÿR¢ÌÿKžÍÿ,ŽÅÿ8“Æÿ:“ÆÿFšÈÿ9¿ÿC¼ÿ\›Äÿ[›ÃÿW˜½ÿT—ºÿU˜ºÿ°ßôÿ¨Ýõÿ‘Õñÿ‘ÕñÿrÂëÿT±ãÿJ«âÿO¯ãÿ{Âìÿs¼èÿV°äÿP°åÿO®âÿR¬âÿN«áÿO­áÿ>¤ÝÿS¶åÿ|Ññÿ’×òÿšÚóÿ•×óÿ‡ÒðÿÐïÿˆÑïÿ”ÖðÿŠÐíÿ†Îíÿ|ÉíÿmÄìÿlÄìÿ\»çÿX¶âÿ\±Øÿ)L[ÿÿTUTÿ”’ÿ…Šˆÿ³¶µÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßáàÿž¡ ÿÿ+2ÿ^‹žÿg ºÿu¶Ñÿ…Ãàÿ~Ááÿ‡Çæÿ~Æäÿ}Ââÿo¹Ýÿu½àÿs½ßÿ\³Úÿ`³Üÿu½ßÿz½àÿz¾áÿj´Üÿs·Ýÿm±ÙÿQ£ÑÿGŸÏÿBœÎÿKžÏÿF–Ëÿ-„¾ÿh°ÿb©ÿ1ÄÿwÁâÿ…Ááÿ‹ÅãÿŒÃãÿ}ºÝÿ`®×ÿj¯Ôÿ]§ÐÿIžÊÿNŸÊÿLÈÿ;‘Áÿ7ŽÀÿ'‡ºÿ'…ºÿ‚¹ÿ ƒ¸ÿ#„ºÿŠÀÿQ©Ñÿa§ÏÿV¡ÊÿH™ÇÿL™Çÿ'…½ÿ#…½ÿ<”ÄÿG™ÈÿM›ÊÿPÉÿC–ÇÿM›ÇÿF•ÃÿS˜Ãÿ<‡¹ÿ7‡¶ÿV™ÀÿYš¾ÿUš½ÿ§ÝóÿœÙôÿ“Öòÿ™ÖôÿŒÎðÿi¹èÿiºèÿk»ëÿh¹èÿm½êÿ[µåÿ\¶æÿ[¶çÿR®ãÿFªàÿE¨ßÿF¨áÿC³äÿsÍðÿ“ÚóÿÚñÿ…Óñÿ‡Òñÿ„ÑñÿxÍïÿzÌðÿ}ÊíÿÊìÿÇêÿwÃêÿaÀêÿU¼èÿY¹æÿa»äÿZ²Ùÿ.QaÿÿLMLÿŽ“‘ÿ…Šˆÿ®±°ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÞßßÿ—š˜ÿÿ"/5ÿh£ÿp¥¿ÿv´Óÿ„ÂÞÿu¾Þÿ`´ÚÿtÀàÿ‹ÉäÿƒÄãÿz¿áÿiµÛÿ^¯Úÿr¹Üÿx»ÞÿjµÛÿp·Üÿy¹Üÿo²ÚÿGœÎÿ?—Ìÿ1“Éÿ+ŒÄÿ¾ÿ*„¾ÿo³ÿh¯ÿ=–ÊÿÁãÿºÜÿ~½Ýÿ“ÅäÿvµÙÿd¬Õÿ\¨ÒÿV£ÍÿJœÈÿ=–ÄÿD—Åÿ:¿ÿ-‡ºÿ~´ÿ€·ÿ·ÿy±ÿ ˆ½ÿC¨Öÿn·Ùÿ|ºØÿ`¬Òÿ5‘Áÿ&·ÿ{´ÿ‚¸ÿ)‡¼ÿ5Áÿ<“Âÿ?”ÂÿQÇÿ_£Éÿe¦ËÿQ›Ãÿ(³ÿ2…³ÿB¸ÿE¹ÿIŽ·ÿªß÷ÿ¤Ýøÿ¤Þ÷ÿØöÿ•Òóÿ—Ñòÿ†Èîÿ{Áìÿs¾ëÿi¼éÿa¶èÿe¹èÿ\³æÿQ­ãÿN¬áÿN«áÿ@¨ÝÿG´æÿoËñÿ”Ûõÿ“Üõÿ‘ÛôÿÖñÿŽÓðÿ‡ÐîÿzËïÿbÃîÿ[¾êÿU»çÿV¸æÿSºæÿU»çÿiÃìÿcÀéÿ[»ãÿa¶Ûÿ7YiÿÿDEDÿŽ’ÿ…Šˆÿ¨¬«ÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÝÞÞÿ“’ÿÿ!/5ÿNƒœÿ`ž¹ÿ†ÀÙÿ}ÀÞÿt½ÝÿyÂàÿ}Âàÿx¿àÿ‹Çäÿ‡ÅäÿiµÛÿq¹Þÿ`°Ùÿh³Úÿ¼ÝÿtµÛÿf¯ØÿHœÎÿ1Éÿ†Áÿu¶ÿpµÿl²ÿsµÿk±ÿ¼ÿh³Ûÿ€½àÿq¶Ùÿn´Úÿe®Öÿo®ÕÿP¢Íÿ;•Äÿ3Âÿ>“Ãÿ)†¼ÿ+…ºÿ ~µÿ}³ÿz°ÿs®ÿhªÿ"‰¼ÿuÂãÿl³Õÿo²Óÿx³ÔÿO™Ãÿu°ÿl«ÿ{³ÿ ¶ÿ&‚¹ÿ2‰½ÿ,ˆ½ÿJ˜ÄÿTÄÿH•¾ÿ1…²ÿ*€¯ÿ"z®ÿ-²ÿ0…²ÿ,€¯ÿ±á÷ÿ¬ßøÿ¤Ûøÿ¥ÞøÿØöÿ¡ØøÿÏóÿ}ÄîÿvÁìÿ^¸èÿf¸èÿiºèÿ[±ãÿX°äÿW°äÿK¬áÿ5¦ÝÿG³äÿrÑôÿ…Õóÿ‘Ûôÿ‰×óÿ‚Óñÿ|ÐñÿrÊïÿxÊïÿkÇïÿZÀëÿ[ÀëÿT½èÿN¹åÿPºæÿWÀéÿbÀêÿ`¿êÿZºåÿrºÞÿ9aqÿÿ=>=ÿ‹Žÿ†‹‰ÿ¤§¦ÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÜÝÝÿ‰‹Šÿÿ!/5ÿl•§ÿ…³Èÿˆ¿Ùÿ€Âßÿ{¿ßÿy¿ßÿ}¿àÿÂâÿy¾àÿ}¿âÿ€ÂâÿrºÝÿw¹Ýÿ†¾àÿ~ºßÿh²ÚÿI›Ìÿ|ºÿ v¸ÿ v·ÿ v·ÿrµÿ t·ÿp³ÿi®ÿ?Îÿ`²Ùÿ_°×ÿ]­Ôÿ[ªÒÿJžÊÿOŸÉÿ0ŽÂÿ4Áÿ&…ºÿ|³ÿx±ÿs®ÿu¯ÿ r¬ÿq¬ÿlªÿ4–Æÿ{Ææÿy¼Úÿe¬Îÿq®Ðÿd¥Êÿ/…¸ÿc¥ÿg§ÿu°ÿg¥ÿq­ÿ)µÿ7ˆ¹ÿ,²ÿ!x©ÿ)}­ÿ-€¯ÿ0„²ÿ-°ÿ#°ÿ&´ÿ­áøÿ­àøÿŸØöÿŸÚ÷ÿ„ÍòÿˆÌòÿ’Ñòÿf¹éÿo½ëÿ^´çÿf¸èÿfºçÿ]µæÿ`¶çÿS¯äÿ@ªßÿ<©Þÿ9¨ßÿoËñÿˆÖôÿ‚Öôÿ~ÔóÿtÑóÿsÏòÿhËðÿdÈðÿtËðÿhÇïÿdÄîÿjÇîÿR¼èÿO¹èÿX½êÿ]ÀìÿtÊïÿsÈíÿtÄèÿl½áÿ:h{ÿÿ897ÿŠŽÿ†‹‰ÿ ¤¢ÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÚÜÛÿƒƒÿÿ%39ÿg“¦ÿy®Åÿv·Õÿx¾àÿ‹ÆåÿŒÇæÿ‚Äãÿu¹ßÿx»âÿw»Þÿn¶ÜÿnµÜÿ„½àÿ€ºßÿk­Öÿ&ƒ¾ÿ~½ÿ„¾ÿ½ÿ w·ÿ x·ÿ|¹ÿq³ÿg­ÿGžÏÿe³Úÿ`­Öÿg¬ÔÿHœÊÿ4Âÿ0Š¿ÿ9ŽÀÿ6‹¾ÿy²ÿ{³ÿnªÿnªÿ o©ÿk©ÿi§ÿg§ÿ:—Åÿ~Æäÿz»Úÿd­ÐÿY¢ÇÿZ¢ÆÿG“¿ÿ*€´ÿb¢ÿN•ÿ9‚ÿLÿ h§ÿsªÿy«ÿ!|«ÿ/ƒ±ÿ%{®ÿ,²ÿz¯ÿ p¬ÿ*†ºÿ§Ý÷ÿ©àúÿ­âùÿ£ÜùÿÑóÿyÆïÿ~ÆîÿxÁìÿÆîÿk»êÿ^µéÿdºèÿjºèÿX²æÿE®ãÿ=ªàÿ=«ßÿ;©àÿnÌñÿÛôÿÚõÿ‚ÖôÿvÑòÿxÑñÿpÎòÿgÉðÿ^ÄíÿYÄíÿbÄîÿdÅïÿgÅîÿfÃîÿaÂíÿhÇïÿ|Ððÿ~Ññÿ{ÎïÿmÅêÿl¿âÿ=q‡ÿÿ000ÿˆŒ‹ÿ‡ŒŠÿœ žÿÜÝÜÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿØÚÙÿy|zÿ ÿ$29ÿn—¬ÿ‚°Éÿˆ½Ùÿ•Éåÿ•ÍêÿÉèÿ€¿âÿn·Þÿy¼àÿrµÜÿQ§Ôÿg¯×ÿY¤Ñÿ/ŠÂÿ&‡Áÿ,‹Äÿ1ŽÇÿ „¿ÿ~»ÿz¹ÿ{¹ÿp´ÿ p´ÿ+ˆÀÿR¤ÏÿY§Óÿ5‘Æÿ&‰¿ÿ%†½ÿ$»ÿ!µÿx²ÿp®ÿq¬ÿf¥ÿd¤ÿd¢ÿ\ ÿXžÿW›ÿJ¢ÌÿyÄäÿ¾Ýÿr³ÓÿR Æÿ]£ÆÿF”¾ÿ%€´ÿx¯ÿ[šÿM’ÿZ™ÿp¦ÿs©ÿy­ÿ#}®ÿ!}¯ÿ'°ÿ!{®ÿ'‚¶ÿH˜ÂÿZ¢Çÿ›Ûõÿ ÜùÿÝ÷ÿ”Òòÿ‰ÐòÿzÈðÿŠÌðÿËïÿƒÆïÿvÃîÿeºëÿe»ëÿk½ìÿY´èÿH®ãÿ?«áÿ:«àÿ8©ßÿjÈïÿ„Øõÿ×õÿ†ÚõÿzÓóÿlÏóÿpÏóÿuÏóÿ^Èðÿ^ÇðÿiÉòÿ[ÅïÿlËñÿsËñÿqËòÿ}ÒóÿÓóÿ€Òñÿ‚ÒòÿØóÿ”Öïÿ{ÆäÿO–ÿÿ*+*ÿ†Šˆÿ‡ŒŠÿ˜œ›ÿÙÛÚÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÖ××ÿqtrÿ ÿ*6<ÿv›®ÿжËÿ”ÆÞÿ™ÌäÿžÐìÿÈçÿo¸Þÿ{¾ßÿm²ÚÿC™ÌÿPœÍÿ#‚¿ÿ~¼ÿ:‘Èÿ6‘Çÿ ŠÃÿ!‰Âÿ‡Áÿƒ¾ÿ¿ÿ y¸ÿp²ÿg®ÿy¸ÿ(Ãÿ}ºÿ|·ÿ|·ÿ r±ÿ q°ÿkªÿa¢ÿ\ŸÿS™ÿM”ÿN”ÿR—ÿL‘ÿ ^œÿn¼ßÿŽÌçÿw»Úÿl±ÒÿhªÌÿPÃÿ9Ž»ÿ ²ÿ|°ÿy¯ÿ k¨ÿe¡ÿp§ÿ p¨ÿq©ÿ {®ÿ'„´ÿ1‹ºÿ+‡·ÿH™Âÿr¯Ïÿn­ÏÿžÞøÿ¨àùÿ¦àøÿ¨Þùÿ›Ú÷ÿÒòÿ€ËñÿƒÌñÿƒÈïÿ}ÄðÿxÅïÿnÀîÿe¾ìÿ^·éÿO²äÿV´åÿ2¥Ýÿ& ÛÿsÊðÿŒÛöÿ„Ùöÿ†Ùõÿ†ÙöÿÖõÿqÓôÿmÐôÿ]ÉòÿaÌóÿuÑõÿpÑôÿwÓõÿÔõÿ}ÔöÿƒÖõÿ†×õÿƒÔôÿ‹×ôÿ‘Ü÷ÿƒÓòÿƒÑîÿˆÎèÿ`”§ÿ'>Fÿ$%$ÿƒ‡…ÿˆ‹ÿ•š˜ÿרØÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÔÕÕÿikjÿÿ,9?ÿ} ¯ÿ‘ºÌÿŸËÞÿ Ñéÿ~¾áÿd´Þÿm¸àÿT¢Òÿ8‹Åÿ)ƒ¾ÿ~¼ÿ&‰Äÿ2‘Éÿ*ŽÇÿ*Èÿ0”Êÿ(ŽÆÿƒ¿ÿ»ÿ‡¿ÿ½ÿt·ÿn²ÿj°ÿf®ÿk®ÿn¯ÿl­ÿdªÿ[¡ÿD‘ÿ)~ÿ"ƒÿ;ÿL“ÿAŠÿ8ÿP§Ëÿ‡Ïìÿ”Îçÿr¹Øÿd­ÎÿS Æÿ;’¾ÿ9Žºÿ,†µÿ(´ÿ|²ÿ s¬ÿc¥ÿg¥ÿn§ÿn¦ÿ ³ÿU£Èÿd¨ÉÿY¥ÈÿY¦Èÿe¬Ìÿf«Ìÿ¡ßøÿªâùÿœÚöÿÝ÷ÿ›Û÷ÿÓõÿ~ÉòÿÎôÿ‰Ïôÿ„ÌòÿÆðÿqÁîÿ[·êÿc»êÿH¯ãÿ3¦ßÿ3¦Þÿ ›Ùÿ^Áëÿ–áøÿÞùÿßúÿ…Üøÿ|×÷ÿÙøÿ‹ÝøÿuÓöÿeÐöÿ}Ùùÿ‰ÜúÿÚùÿ‚ÙøÿÝøÿ‹Û÷ÿ‹ÜøÿÖõÿ“Þøÿ–Þøÿ”Ý÷ÿŽ×õÿyÏîÿzÇæÿ_ ¹ÿ*GRÿÿƒÿ‰ŽŒÿ’–•ÿÔÖÕÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÑÓÒÿ_a`ÿÿ0’ÿwÿEÿ ÿ0ÿ ÿ 8ÿ&zÿH•¿ÿˆÒìÿÐéÿp»Üÿd±Õÿ[ªÎÿ[§ÌÿJžÇÿ;“Àÿ,‡·ÿ,…µÿ2ˆ·ÿ%ƒµÿ{°ÿ|´ÿƒ¶ÿ%…¹ÿN¥Ëÿs¸ÕÿºØÿy·ÕÿyµÔÿm°Ïÿe¯Ïÿ¨Üöÿ‹Íðÿ†Íñÿ¡Üùÿ¥ÜøÿŸÛøÿŠÔôÿ{Ëòÿ’Óõÿ’Ñóÿ|ÉñÿtÂíÿj¼îÿY¶èÿB®âÿ.£ÝÿšÚÿ “×ÿlÊðÿ¤ìýÿ¥éüÿèüÿãúÿãûÿŒáüÿãûÿéüÿŽâûÿãûÿ‹áûÿ”äûÿšçüÿŠáûÿ„Ýûÿ•âûÿ›åûÿœæüÿ—âûÿ“áúÿ•áùÿ¦èúÿ–âùÿ•Þöÿ˜ÙðÿŠÍãÿ2LVÿÿwzxÿ‹Žÿ‘ÿÎÐÏÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÊÌËÿOPOÿÿ9GÿEƒ¥ÿ4‰·ÿƒ¹ÿ(ŽÆÿ3“Éÿ7˜ÌÿDžÑÿ:šÏÿA ÐÿN¦ÔÿI¤Òÿ=œÍÿ3“Èÿ-Åÿ7•Éÿ6•Èÿ4“Æÿ9—Éÿ3•Èÿ=šËÿ2–Èÿ!ÅÿÂÿ!‹Âÿ…½ÿ…½ÿ"„¾ÿxµÿ%ƒ´ÿ…¿ÿ}·ÿx¯ÿ>œËÿ‡Ôðÿ—Ôìÿ‚Çãÿj·Ùÿ]¬ÒÿP¥ÎÿN¢ÉÿC˜Äÿ<–Ãÿ,нÿ#ƒ·ÿ´ÿµÿ€µÿ}´ÿ~´ÿB›Æÿi´Öÿ…¿Üÿ‹ÀÛÿ‚»Øÿy¸Ôÿo³Òÿ]«Îÿ¤ØõÿŒÊîÿ’ÑóÿÕõÿœÜúÿ—Øøÿ†ÑóÿtÈñÿxÅïÿÏòÿŒÏòÿ{Åïÿg»ìÿS³çÿ7¨ßÿ%ŸÜÿ™Ùÿ4¬âÿ‚Ýùÿ—æûÿ™çüÿ¡êüÿ¦ìüÿ’çüÿ‹äüÿ†ãûÿåüÿ‘ãüÿŽäüÿ‘åûÿ–çüÿ‘åüÿ›çüÿ„ÞûÿŽãûÿ’âüÿ–åüÿšçüÿ¦éüÿ§êüÿªëüÿžçüÿšãúÿ¡åùÿ‘ÛòÿŒÎäÿ8PXÿÿqsrÿŒ‘ÿŠÿËÍÌÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÆÈÇÿHJHÿÿ4Hÿ$r›ÿ,…´ÿ@™ÈÿQ¥ÓÿS¦Õÿ?žÐÿE¡ÒÿC¡Òÿ? Ðÿ@ŸÐÿ>Ïÿ9šËÿ7˜Éÿ>›Ìÿ/”Çÿ8—Ëÿ8™Ëÿ5˜Ëÿ@žÏÿ>œÎÿ1˜Êÿ+–Éÿ:ÍÿA¡Îÿ? Ïÿ=¢Ïÿ>¡ÎÿG¡Ïÿ7˜Éÿ&ÆÿB¤Óÿ…Ñîÿ˜Öïÿž×íÿÅâÿ`²Õÿh²ÖÿR¤ÎÿIŸÉÿD›ÄÿCšÅÿ4’Áÿƒ·ÿ|³ÿ }´ÿ‚µÿx°ÿ{²ÿE Éÿz¼ÚÿÁÜÿ•ÂÚÿŽÀØÿv´Óÿ{·Õÿa¬Îÿ˜ÒñÿÏñÿ¬Üøÿ“Óóÿ‡Îñÿ~Éïÿl¾ìÿ}ÈïÿzÅîÿÊñÿ™Ôõÿ|Èðÿ^ÆñÿcÈñÿT¼èÿ;­âÿ-¥Þÿw×øÿ éýÿ¢êüÿžëüÿ’èüÿ—êüÿçüÿ–éüÿ›éüÿ”çüÿ–çüÿ“æüÿ‡äüÿ‹æüÿšèüÿ™æûÿåüÿ–çüÿ•äüÿèüÿšæüÿ èüÿ¨ìüÿ­îüÿªíüÿ«ìüÿ¢éüÿ®êúÿ¬çõÿ–Òæÿ9QYÿ ÿjmkÿŽ’ÿˆ‹ÿÇÉÈÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÁÂÂÿBCBÿÿ>Qÿ1|¡ÿ7¸ÿ>˜ÈÿBŸÏÿF¢ÒÿP¨ÖÿY¬ØÿXª×ÿE£ÓÿK¤ÓÿQ¦ÓÿH¢ÐÿJ£ÑÿD Ïÿ>›Îÿ;žÏÿC£ÓÿG¨ÔÿB¦ÔÿJªÕÿJ¨ÔÿV©ÓÿR¨ÒÿH£ÎÿC¢Ïÿ> Ïÿ<Ìÿ9šÉÿY¯×ÿ„Ïìÿ«âôÿœÖíÿšÕîÿÄãÿd²×ÿf±ÕÿU§ÎÿJ ÊÿMŸÉÿOŸÉÿCšÆÿ€µÿ }´ÿ }²ÿ |°ÿ |²ÿ‚´ÿ^®Ðÿ½Ùÿƒ½Ùÿ‡¼Úÿ~º×ÿe°Ðÿ|·Öÿr³ÒÿÍîÿxÆîÿÑôÿ‰Íñÿ–ÔóÿŠÍðÿi»ëÿ¢Ù÷ÿ¤Ûøÿ–Öõÿ…ÍñÿxÌôÿiÎõÿkÑøÿxØûÿuÖúÿ‚Ùùÿ‰áüÿ·ðüÿªíüÿ§ïýÿ–éüÿéüÿ›êüÿçüÿ¡íüÿ“èüÿçüÿ•çüÿ–éüÿ’åüÿ•åüÿŽäûÿŒäüÿ—æüÿœèüÿžèüÿŸæüÿ—äüÿœåüÿ£êüÿŸëüÿ¥ìüÿ«íüÿ¦èüÿ£æúÿ¥äõÿ¤Úêÿ@U[ÿ ÿegfÿŽ“‘ÿ‡ŒŠÿÃÅÄÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿº½¼ÿ==<ÿÿEXÿ=ƒ§ÿE•¿ÿ>›ÊÿUª×ÿe±Úÿ[­ÚÿY«×ÿN¥ÒÿV©Õÿd¯ÚÿL£ÒÿK¥Ñÿ>¢ÒÿI¦ÓÿQªÕÿQ«Öÿc´Üÿa¯ÙÿVªÕÿU«ÕÿZªÓÿP£ÍÿP¦ÏÿD¤ÏÿN¨Òÿ<ŸÏÿoÃåÿ¡áõÿ¬äöÿ¨ÝòÿŽÑêÿˆÌèÿp½àÿm¸ÛÿX«ÒÿQ¦ÐÿDÉÿGžÇÿGÉÿ5’Ãÿ„¹ÿ¶ÿ~³ÿ‚¶ÿƒµÿ+Áÿq·×ÿ€¾Úÿ‚½Ûÿƒ¼Ùÿw¸Õÿ^­Ðÿm³Òÿo³Ñÿ¬Ýõÿ}ÍðÿÎóÿ‹ÍñÿˆÐóÿ{ÉñÿŒÎóÿ«Þøÿ›Ôóÿ™ÖõÿšÙôÿŠÎñÿ}ÑöÿkÓùÿrÖùÿzÛúÿ’åüÿœéüÿ³òýÿ°ñýÿ®óüÿ©òýÿêüÿ‘èüÿŒçüÿ’èüÿèüÿ’çüÿ—éüÿ•èüÿ“åüÿŽãüÿ‰áüÿäüÿ–æüÿ“åüÿŽàüÿœçüÿ¦êüÿœæüÿ›æüÿ–ãüÿ™æüÿ¢èüÿœäüÿžâúÿžâøÿ®çöÿ´âëÿDX]ÿ ÿ]_^ÿ”’ÿ†‹‰ÿ¾ÁÀÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿµ·¶ÿ787ÿÿ*Qdÿ=…©ÿ?”¾ÿK ÍÿH£ÒÿU«Øÿ`°Úÿ¾âÿy¼ßÿh°Ùÿ\¬ØÿL§ÓÿZ¯ØÿX¯ØÿTªÕÿh´Ùÿs¸ÛÿxºÜÿu¸Üÿk´ØÿTªÓÿYªÔÿL§ÑÿO©ÒÿU¬Õÿa¹ßÿŽÚòÿ§äõÿ°ãõÿ¦Üïÿœ×íÿ‚Èåÿh¶Ûÿe³×ÿY­Òÿ]«ÒÿGžÊÿ9–Åÿ0‘Âÿ ‰½ÿ"†½ÿ‚¶ÿ~µÿ„·ÿ‡»ÿ>Èÿz½ÚÿˆÀÜÿ‹ÀÛÿ‰¿Úÿv¶Ôÿb¯Ðÿ^®Ïÿi±Ñÿ©Üõÿ«Ý÷ÿ“×ôÿ{ÌðÿrÄïÿtÇñÿ Üøÿ§Ü÷ÿœÓóÿ™ÓóÿžÙ÷ÿ›Õôÿ‹ÐóÿtÔùÿoÕúÿÜûÿ™æüÿ£ëüÿ ìüÿ¡îýÿšêüÿ–çüÿ‹äüÿ†åüÿˆçüÿ‘çüÿ–éüÿšêüÿŸëüÿ–èüÿ“æüÿŽãüÿ‚ßûÿ‰ãüÿ’äüÿâüÿáüÿ“áüÿŽßûÿ“âûÿ˜âûÿ›äüÿšãûÿ•àúÿ›âûÿ—ßùÿ…ÔõÿÔóÿ™Øñÿ•Ïçÿ?U`ÿÿVXVÿ”’ÿ…Šˆÿ¹¼»ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿ®±°ÿ00/ÿÿ%Skÿ;†«ÿNšÁÿHŸÌÿ[¬Öÿi´Üÿk²ÛÿZ¬×ÿ\­×ÿ\±Ùÿ`±ÚÿkµÛÿd´Üÿd´Úÿu¹Ûÿz¹Ûÿt¹Ûÿv¹Ûÿa­Õÿ]°ÙÿV­×ÿI¨ÔÿUª×ÿ^»ãÿ×ñÿ›Ýòÿ ßóÿ¥Þñÿ Üñÿ¢Ûïÿ‰ËçÿnºÝÿl¸Úÿk´×ÿO¥Íÿ?™Æÿ5”Ãÿ*‰¾ÿ(‹¾ÿ,ŽÀÿ‡ºÿƒ¸ÿ…¸ÿ…ºÿS¨Íÿ‡ÂÝÿÂÛÿ†¿ÙÿnµÔÿj°Ñÿ]¬ÎÿS©Íÿ`®Ñÿ¯Þõÿ¬Üöÿ¤Ù÷ÿƒÏðÿT·êÿn¾îÿÇïÿ‘ÎðÿŸÖõÿ³ãúÿ¦àùÿ©ßùÿ™ÖõÿŒÔöÿzÖúÿ„Úûÿ›ãüÿ¦èüÿŸäüÿáûÿ‰àüÿŠßüÿ‚Þüÿ{Þûÿ‰ßüÿ‡áüÿ‰áûÿ”åüÿ˜æüÿŒãüÿâüÿˆßüÿÛûÿ‚Üûÿ|Øúÿy×úÿ‹Üûÿ‹ÛùÿŠÚùÿ€ÕøÿÙøÿ”Úøÿ‹×öÿ„ÒôÿrÊðÿkÆðÿpÄíÿd¾ëÿh¿éÿ|Çèÿf¯Ôÿ3LÿÿOPOÿ“‘ÿ…Šˆÿ³·µÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿ¨««ÿ*+*ÿ&ÿ)]vÿJ®ÿ[ Åÿ^ªÑÿZ©ÒÿWªÖÿh´Úÿy¼Ýÿl¶ÜÿkµÛÿn·Ýÿt»ßÿz½ßÿ|»Þÿo´Ùÿ]¯×ÿZ­ÕÿJ¤ÏÿW¯ØÿY±ÛÿS®Øÿc¹áÿÌîÿ—×ñÿ‘×ðÿ©àòÿªÝðÿ˜ØîÿœØïÿ‹ËæÿpºÜÿf²Öÿf°ÖÿO£Îÿ?™Çÿ(Œ¾ÿ1Ž¿ÿ<–Äÿ5“Âÿ&‹¼ÿ#нÿ‡¼ÿ¶ÿN¦Îÿ¿ÜÿˆÁÝÿz¹Öÿl²Ôÿf®ÏÿV¦ËÿM¤ËÿS©ÏÿÖóÿ¢×óÿ¨Ûöÿž×ôÿÊîÿY·êÿp»êÿ’ÍïÿšÔóÿšÔôÿ™ØõÿžÝøÿžÚöÿŽÏðÿxÊòÿˆ×öÿ“Üøÿ˜ßùÿ’Üøÿ‹ÜúÿÚúÿ‰Ýûÿ‰ÝûÿzÛûÿƒÛûÿ†ÝûÿŠÞüÿ‡Ýûÿ‹ÞûÿàüÿŽàûÿŠÜûÿŒÛûÿ~ÖùÿpÐ÷ÿfÌôÿrÏöÿwÏõÿlËóÿyÐõÿ~ÎóÿwÊðÿqÅíÿkÄîÿc¿íÿX¸éÿH¯áÿE­ßÿ[µäÿj½æÿ)Åÿq¬ÿ4PÿÿGIGÿ“’ÿ…Šˆÿ®±°ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ£¦¤ÿ%&%ÿ#,ÿ6j„ÿP°ÿTžÅÿN¥Îÿ]°Øÿp·Ûÿr¸Ýÿq¸ÝÿvºÝÿq¹Üÿ¿ßÿw½Þÿt¼ßÿN«×ÿN¬Öÿo¹ÞÿiµÜÿY¯Ùÿg¹ßÿgºáÿmÄêÿvÁéÿ±ä÷ÿ›Üòÿ±ãôÿ´ßòÿ•Óìÿ–Òîÿ…Ççÿq»Ýÿe±×ÿ]¬Ôÿ]¬ÓÿHŸËÿ+¿ÿ(ŒÀÿ,ŽÂÿ(Œ¾ÿˆ»ÿ!нÿ†ºÿ €¶ÿK£Îÿ¾ÜÿˆÀÜÿw·Öÿs´Óÿ~·Õÿl®ÏÿK Èÿ4”Äÿ˜Óóÿ§Úõÿ˜ÕñÿŸ×ôÿ”Ññÿ„Íïÿe¿ëÿvÇðÿ‰ÌñÿÍïÿ‹Ìîÿ™×ôÿ“Õôÿ‘Ðòÿg¾êÿ\¾íÿ…ÔöÿŒ×öÿ™Üøÿ”Ûøÿ~ÔõÿŠÚøÿ‰Úùÿ„×ùÿ€×ùÿƒÙúÿ†Øúÿ‰ÚúÿÛúÿŠÙùÿ~Öùÿ„Ùùÿ„ÖøÿoÎôÿmËóÿjÉòÿhÇòÿ`Ãïÿ_ÀîÿkÂîÿhÃïÿ`½êÿM³ãÿJ°áÿN±äÿG¯âÿF®áÿBªÞÿD©Ýÿ<§Ûÿ €¾ÿq¯ÿj¨ÿ4SÿÿABAÿŽ’ÿ…Šˆÿ©­«ÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßààÿž žÿ ÿ'/ÿBvÿY–´ÿb¥Èÿd­Òÿk³Ùÿm¶Üÿh´Ùÿc³Úÿl¶Üÿu»Ýÿ^±ÚÿY±ÙÿP«Øÿe´ÝÿvºÞÿt¸Üÿw¼àÿg¹àÿ\·áÿlÅêÿ¡ßõÿ£âôÿ£áóÿ¾ê÷ÿµãóÿ—Ôíÿ‹Îëÿ†Èçÿl·Ûÿ\¯ÖÿW¬ÔÿN¥ÏÿIŸËÿ<–Åÿ,ŽÁÿ"Š¿ÿ0’Âÿ)¿ÿ‚¹ÿ|·ÿ €¸ÿO¨Ðÿ¿Üÿƒ¾Ûÿw¸ÖÿuµÔÿx´Ôÿc«ÎÿV§ËÿR£Ìÿ›ÔóÿŸÕôÿš×ôÿ’Óñÿ‘Ïðÿœ×óÿƒÎðÿS¹éÿ\¿ìÿ‹Ïñÿ‡ËðÿªÛ÷ÿÑñÿtÂìÿa¹çÿZ´åÿh¿ìÿ‹Óóÿ’×ôÿƒÐòÿ|Ïòÿ’Øöÿ“ÙöÿˆÑòÿyÌñÿ{ÏôÿÓöÿÓõÿ}ÐöÿqÊôÿvÍôÿzÏóÿlÉòÿiÅðÿUºêÿT¹èÿP·æÿU·çÿM°äÿO°âÿO±âÿK¯âÿ>¬Þÿ8¦ÛÿC©ÜÿC©ÞÿDªÞÿ.Ÿ×ÿÏÿ/¢Øÿ8£Úÿ>¢Öÿ/™Ìÿ†»ÿ>]ÿÿ9:9ÿŒŽÿ†‹‰ÿ¥©§ÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÞßßÿ˜š™ÿÿ'/ÿM~—ÿb˜µÿs­Íÿn²Öÿn·Ûÿm¸Üÿq¹Þÿo¸Þÿo·ÛÿX­ÖÿT­×ÿp»àÿv½àÿt»àÿj¸Þÿp»àÿY´Ýÿ^¼æÿÖðÿžßóÿØïÿ£áóÿºéøÿ§ÝòÿŠÏìÿ€ÈèÿÄåÿu»Ýÿc±×ÿb²Øÿ^¬Ôÿ>˜Èÿ2‘Ãÿ3‘Âÿ'ŒÀÿ5’Âÿ.Àÿ…ºÿ|µÿ‡»ÿa°Ôÿ†ÀÞÿ‡¾Ýÿz¸×ÿk²Ôÿ]¬Ðÿe±Ôÿ‚¾Ûÿ¼ÙÿÏõÿ¥Ûøÿ–ÖóÿŽÏñÿ¬Ý÷ÿ®Þ÷ÿ×ôÿ˜ÖóÿkÄîÿ‚ÍñÿŠÏñÿ”Õôÿ”Òñÿ|ÇîÿU°âÿQ­àÿV¯âÿa·èÿzÉðÿ{Êñÿ…ÎñÿÍñÿÒòÿ›ÔôÿŒÌðÿ}ÇîÿkÁíÿa½ìÿc¼ìÿ=ªâÿ2¨àÿ/¥Þÿ=©àÿ9§Ýÿ0£Üÿ+ Ùÿ šÕÿ4¢Ùÿ0 Øÿ"™Ôÿ"˜Ñÿ#—Òÿ—Ñÿ˜Ðÿ.œÕÿ)œÕÿŒÊÿ%•Ïÿ`¼èÿkÅíÿlÈðÿ{ÌñÿvÆíÿf½æÿDÌÿ"Wrÿÿ343ÿŠŽŒÿ†‹‰ÿ¡¥£ÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÝÞÞÿ‘“’ÿÿ*2ÿW‚šÿv£½ÿmªËÿ½Úÿ…Âàÿ¿àÿy½ÞÿsºÝÿd³ÝÿeµÝÿs½áÿo½âÿj»âÿb¶âÿk¼äÿV´ßÿsÇëÿßõÿ Þôÿ¡ßôÿ±åõÿ¬ßñÿš×íÿËêÿ‡ËéÿŽÊèÿ¿ßÿb±×ÿWªÒÿJ¢Ìÿ9˜ÇÿL¡ËÿIœÉÿ9”Åÿ0‘Äÿ2’Ãÿ‰½ÿƒ¸ÿ(Àÿh¶Ùÿ„ÁÝÿ|ºÙÿm´Õÿ[­Òÿc²Òÿ~½ÚÿŠÁÜÿ†½ÙÿZ¹ëÿ“Óöÿ”ÓóÿŽÑóÿœÚöÿ‘Òóÿ‚Êïÿ™ÔòÿŽÒñÿÎðÿƒËðÿuÇîÿƒËðÿi»èÿA¦ÜÿA¥ÛÿK¨Þÿ[¯ßÿR®àÿi»éÿn¾ëÿxÅïÿŠËïÿ‹ÊðÿŒÌðÿ~Çîÿf¼éÿP²åÿ6¦Þÿ$œØÿ’ÔÿÑÿ‘Óÿ‘ÒÿÒÿ•ÒÿÎÿÎÿÐÿ‘Ïÿ ‹Ìÿ‡Êÿ ‡ËÿËÿ ‡ÇÿˆÇÿ,˜ÏÿmÂéÿŒÕóÿ„Õóÿšà÷ÿ¥âùÿ£âùÿ•Üõÿ…Ïîÿl»áÿ;rÿÿ---ÿ‰ŒŠÿ‡ŒŠÿœ ŸÿÜÝÜÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÚÜÛÿŠŽŒÿÿ+3ÿ\‡ÿ`›¹ÿ|´Ñÿw¸Úÿm¹Üÿo»Þÿ„ÁãÿƒÃäÿyÂãÿe»âÿi¼äÿlÀæÿtÂçÿvÄéÿe¿çÿˆÔñÿŸàôÿ¢âõÿ°åöÿ²åöÿ³ãòÿ¢Øíÿj½ãÿo½áÿpºÞÿk´Úÿf²ÙÿW©ÒÿI ÌÿBŸÌÿM¢ÍÿHœÊÿ6–Æÿ7•Åÿ/‘Äÿ(Áÿ(¿ÿ Ž¿ÿk·ØÿŠÄàÿq¸×ÿ\®Óÿb²Õÿx»ØÿŒÃÜÿ’ÂÙÿŠ¿ÙÿÅÿX·êÿ™Óõÿ–Öôÿ‚ËðÿvÄíÿmÃîÿuÅíÿ”Öóÿ—Øôÿ„ÎðÿY»éÿNµèÿd·éÿm»éÿ[³äÿN«Ýÿ]«ÝÿYªÜÿM§Ûÿ=£ÜÿB¦ßÿn»çÿn¼èÿ\³ãÿM¬àÿD¥Ûÿ+›Ôÿ(›Õÿ*œ×ÿ.›×ÿŽÐÿ…Íÿ‰Íÿ†ÌÿŽÍÿÌÿ‰Éÿ ‡Ëÿ „Çÿ~Åÿ{Åÿ €Âÿ ƒÁÿw´ÿ7™ÌÿwÊïÿ”Ý÷ÿŸãøÿžà÷ÿ¦â÷ÿžÜõÿ£ãùÿŸá÷ÿ”ÚôÿÓîÿrÀãÿ2x—ÿ-;ÿ&&%ÿ†Šˆÿˆ‹ÿ™›ÿÚÛÛÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿØÚÙÿ‚„ƒÿÿ,4ÿQ„žÿh¢¾ÿy´Ôÿ}½Ýÿ|Ààÿy¿âÿ€Ääÿ}ÅçÿsÂèÿvÆèÿ‚ÉêÿÐíÿp¾äÿlÄêÿ‘Øñÿ•×ïÿŒÑëÿ“Òìÿ¬Þñÿ´âôÿ¤ØïÿrÁäÿu¾áÿk¶Ûÿw»ÞÿnµÛÿR¦ÒÿV¨ÕÿK¡Îÿ]ªÓÿQ£ÎÿN¡ÌÿEÉÿ3•ÄÿCžÊÿ5˜Æÿˆ½ÿ_¯ÕÿÁßÿm·Ùÿ\³×ÿw½ÛÿŠÄÞÿ‡ÀÛÿŒÁÚÿvµÓÿÄÿ*˜Òÿl¿ìÿvÈòÿ‚ÉóÿŽÎòÿuÃìÿÇíÿ„ÊïÿšÕòÿËïÿkÁëÿPµæÿD®ãÿm¿íÿxÀëÿf¸åÿ]±áÿVªÜÿEŸÒÿBÒÿJ Öÿ,“Ïÿ"Ìÿ2˜Ñÿ,˜ÓÿE¤×ÿf¶âÿa´äÿP®ßÿ[³áÿZ±àÿ*–ÑÿyÂÿuÀÿ„Çÿ ‡ÉÿƒÇÿ{Äÿp¼ÿq¶ÿq´ÿq°ÿq±ÿ ¼ÿR¶çÿ•Ýøÿ¯ëüÿ¬êûÿ®éýÿ»èûÿ«àöÿ‰Ôñÿ‹Øóÿœßöÿ—ÚóÿÓïÿx¿ãÿ>†§ÿ3Dÿ ÿ‚†„ÿˆ‹ÿ•š˜ÿ×ÙØÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÖØ×ÿ|}}ÿ ÿ.6ÿfަÿ|©Äÿq°ÑÿeµÜÿ{ÂäÿˆÊêÿ‹Íìÿ‡ÍíÿtÉìÿ Üôÿ•ÒïÿC¥Öÿ†Ðïÿ”ÕïÿšÓíÿª×íÿ¨Øíÿ«Ûïÿ°ÜïÿÐéÿd¾åÿ„Ææÿn¶Üÿ^­×ÿO¥ÐÿN¦ÐÿK¡ÏÿGŸËÿV¨Óÿ^¬Óÿ]¨ÑÿEŸËÿJ¤ÍÿG£Îÿ;Ëÿ%‘ÂÿN§Îÿ{Àßÿi¹ÛÿgºÚÿp¼Ùÿw¼Ùÿ|½×ÿƒ¾Øÿo³Òÿ)–Ðÿ9›ÒÿI£ØÿI­âÿZ¸ìÿzÇñÿ‰ÌïÿŒÎïÿ–Ðòÿ‘Ìðÿ‰ËïÿŠÎîÿ†Èíÿ\³ãÿN±äÿzÅìÿ^·åÿ8¦Ûÿ-Õÿ;žÓÿ:Òÿ>šÏÿ€¿ÿÁÿEÒÿd®Þÿ‘Çëÿ—Ìíÿ–Îîÿ{Àèÿn¼çÿ€Æëÿ}ÀéÿP¨Üÿ-’Íÿ w½ÿkµÿm·ÿmµÿp³ÿo¯ÿr°ÿn¯ÿx´ÿ/žÒÿxÌñÿ¬çûÿ¢äøÿ¡äùÿžã÷ÿ§åùÿ¤ÜöÿšÙóÿƒÑïÿžß÷ÿ“Øôÿ‘ØóÿÙòÿ|Âãÿ(~ªÿ5Gÿÿ~€ÿ‰ŽŒÿ’–•ÿÕ×ÖÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÔÕÔÿrusÿ ÿ!08ÿb¦ÿm¨Ãÿ{½ÚÿˆËçÿœØñÿ«â÷ÿ¨á÷ÿ€Ìëÿj¿ãÿ’ÊÿM®Ùÿ•Óîÿ‘Òìÿ§ßóÿÂé÷ÿ·áôÿ«Üïÿ±ÞïÿÊèÿ@©Úÿl¹ßÿy¾àÿmµÛÿQ¨ÒÿJ¤ÐÿGŸÍÿT§ÑÿQ§ÒÿQ¦ÑÿZ©ÓÿO¤ÏÿCŸËÿBžÊÿ5™Èÿ3—ÅÿH¢ÎÿzÁàÿmºÛÿg¹Ûÿl¸Ùÿk¹×ÿÅÜÿŠÂÙÿ`¯Ïÿ;œÑÿ8™ÐÿG¢Ôÿ?žÑÿ8œÒÿI¯äÿËòÿŒÎñÿ‰Îñÿ‰Ìñÿ‚ÈíÿuÄíÿ~Çîÿl¼éÿg·åÿd·äÿe·äÿZ°ßÿ8ŸÓÿ3™Îÿ7œÏÿŒÅÿ~¼ÿP Óÿm¯ÚÿrµÞÿ–Êìÿ˜Éìÿ–ÊíÿšÎîÿ™ÍïÿŽÉëÿ’Îíÿ“Îïÿ‹ÈëÿZªÙÿ#¿ÿo±ÿn­ÿo®ÿr±ÿt²ÿo®ÿ(•ÌÿcÄïÿßùÿžÞ÷ÿ˜Üõÿ¨â÷ÿÜôÿ Üóÿ¥ÛóÿÛôÿÔòÿšÚôÿŽÔñÿ}Ïïÿ˜Ûóÿ„ËìÿG¥Óÿo¥ÿ/Cÿÿz|{ÿŠÿ”’ÿÒÔÓÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÑÓÒÿjlkÿ ÿ%5<ÿs¯ÿo±Ëÿ}ÃÞÿ‰ÎéÿsÃåÿ;¦Õÿ4 Ñÿ+—ÌÿÇÿ{Èèÿ¥ÜóÿšÕñÿ«Ýñÿ¯àñÿ®ßðÿ¨Øëÿ£×ëÿiºÞÿR¡ÍÿX¥ÌÿZ«Ñÿ_¬ÓÿJ¡ÌÿBžÌÿK£Îÿ`­×ÿXªÔÿM¥ÏÿM¥ÐÿP¨ÑÿS¦Ñÿ[«ÔÿD¡Íÿ5•Åÿ)¾ÿc¶ÛÿtÀáÿq¿Þÿm¹Ùÿp¸ØÿÀÛÿˆÁÝÿq¶Ôÿf¯Üÿa¬Ùÿ_¬ØÿG¡Ñÿ)‘Çÿ(–ÏÿJ°æÿxÅðÿxÄðÿqÄðÿc¼íÿ\·éÿ^·èÿ\¶çÿt½éÿ‚Âèÿj¹äÿX¯ÝÿH¤Öÿh¯ÛÿU¢Ñÿ.Æÿ'ŠÄÿu²Ýÿ‹¾ãÿ‡¾ãÿŠÁåÿ“ÇéÿÍëÿÍëÿ—Êíÿ“ÈëÿƒÄéÿÊíÿšÏðÿŒÆêÿc®Ýÿ:”Ìÿ€½ÿt²ÿo®ÿ y·ÿ4¢×ÿE³åÿU½ëÿmÉðÿÚôÿ¡àöÿ°ä÷ÿ§ßöÿ³ã÷ÿµãöÿšÚóÿŠÖòÿ¥Þõÿ“Ôðÿ‰Ñðÿ’Óïÿ…Ììÿk¹áÿ.”Çÿg¢ÿ%<ÿÿuwwÿŒÿ‘ÿÏÑÐÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÏÐÐÿacbÿÿ*:AÿSŽªÿ:·ÿ8™Çÿ8 ÏÿE¦Õÿ?¢Òÿ,šÌÿJ­Úÿ•Õïÿ­àòÿ¥Úíÿ¬×èÿ£Òæÿ¢ÒâÿœÈÜÿh°ÏÿW¢Æÿs§ÁÿfŸ¼ÿW˜¹ÿP—½ÿPŸÇÿLÈÿR¤ÐÿN¤ÐÿZ¬Õÿa¯ÕÿlµÚÿT©ÒÿP¤ÌÿY¨ÑÿFžÊÿ>œÈÿ)Áÿ9ŸÌÿf¼âÿpÀàÿwÁÞÿ‚ÁÝÿuºÙÿyºØÿw¸ÖÿsµÝÿh°Ûÿa¬ØÿDžÐÿ@›Ðÿ*“Éÿ5šÐÿH«âÿ\·ëÿj½íÿX·êÿfºëÿhºèÿR¯ãÿR«àÿe¸äÿ^³àÿRªÙÿD ÓÿHŸÒÿLÏÿ)‡Ãÿ8‘Éÿx³Üÿ—Ãäÿ…ºàÿv¸ÞÿŠÁåÿ˜Èêÿ™Éêÿ™ÉëÿŠÄçÿo¸áÿ~¿åÿ†Âèÿ•Ëíÿ‡Äçÿv¶àÿNžÔÿ)ŠÈÿ‡ÄÿQ±áÿV¼ëÿY¼ëÿyÍñÿƒÔóÿ„ÔñÿØòÿ¥àõÿ°ãøÿ²åøÿªáùÿœÞöÿzÎñÿŠÓõÿ¡Úóÿ—Öñÿ’ÏîÿvÃèÿu¿æÿ\®Ûÿ'ŒÂÿažÿ$=ÿÿmpnÿ‘ÿŠÿÌÎÍÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿËÍÌÿ[][ÿÿ0>ÿ2|ŸÿPÁÿp¶Öÿq»ßÿ.‡¼ÿ$„ºÿm½áÿŽÊäÿ›Ëßÿ¥ÊÚÿšÅÖÿ¼Íÿ‘¸Çÿu¨¼ÿH¬ÿU‰¢ÿUƒ™ÿN|”ÿ@jÿN†¤ÿHޱÿ]¢Æÿ\§ÐÿU«ÔÿZ­Ôÿb¯Öÿb°Øÿ\¬Ôÿ_¬Ôÿ\«ÕÿH Ëÿ3“Ãÿ%Áÿ(ÂÿXµÝÿg¼áÿ‰Éåÿ“Ëäÿ~¾Úÿc°ÐÿlµÕÿQªÖÿH¦ÓÿW©ÖÿEŸÏÿ8™Îÿ)‘ÆÿCŸÎÿI ÐÿP¨ÜÿQ«áÿA¨àÿM¬âÿR²åÿ@¢Ùÿ1•ÒÿR«ÞÿN¨Ûÿ6œÑÿ9šÐÿ7–Íÿ8‘Êÿ*‡ÂÿC“Èÿm°Ùÿ‘Ãäÿ‰»Þÿ¹ßÿ‹Àåÿ¿äÿÁåÿ‹Áæÿ»àÿmµÝÿt¸ßÿy»áÿ{½åÿlµâÿu»ãÿr¸âÿP¤ØÿG©ÝÿxËòÿrÊíÿW¾èÿsËðÿˆÓñÿ˜ÙóÿœÚôÿ‰Õïÿ‘ÙóÿÝõÿØóÿØòÿ‰ÒðÿÕñÿ±àöÿ£ØòÿÉëÿuÃèÿj¹âÿ]¯Ýÿ1–Íÿl®ÿc ÿ(@ÿ ÿhjiÿŽ“‘ÿˆ‹ÿÈÊÉÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÇÊÉÿRTSÿÿ%;Fÿ]‘ªÿm«Åÿ=€®ÿ?ƒÿ@‘¼ÿˆ¼Ôÿˆ²Çÿ‹®¿ÿŠ©¹ÿzž¯ÿu™¦ÿ`Ššÿ:kÿ.LZÿ-3ÿ ÿÿ ÿ ÿ:JÿEŠ®ÿVŸÅÿd«Òÿm°Öÿe±Øÿg³Øÿ[®ÕÿY¬ÖÿM¤Ïÿ7–Çÿ)Àÿ#¿ÿˆ»ÿN«Öÿk½âÿ‚ÅäÿŽÈãÿƒÃÞÿk¶Öÿo¶×ÿY¯ÙÿSªØÿe°Úÿ^¬×ÿW¨Õÿ=™ÌÿK¡ÐÿO¢Ðÿ[¨Õÿg­Øÿx³ÜÿcªÖÿO¥ÖÿN¨ÜÿE¤Úÿ: Ôÿ(™Òÿ+–Ïÿ*‘Êÿ ‰Çÿx¹ÿ,„ÁÿWŸÌÿm¯Õÿ„¸Ýÿ|µÚÿs±ØÿxµÜÿ»àÿp±Úÿr´Üÿo²Ûÿm¶Þÿw»áÿ|½âÿh³ÞÿW«ÛÿV¬Ûÿ`²ÞÿZ­Ýÿc»æÿƒÓòÿ×ôÿ}Ïðÿ‚ÐñÿŠÔðÿŸÝóÿžÚòÿš×òÿ’Ôñÿ‘ÖñÿÙòÿ•Úôÿ{ÊìÿÊëÿžÚôÿžØòÿ‹ÐîÿËìÿjºãÿkµßÿI Òÿ rµÿc§ÿd¢ÿ*Bÿ ÿ`baÿ“‘ÿ‡ŒŠÿÃÆÅÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÃÅÄÿMMMÿÿ.BKÿ9q‘ÿ7pÿ(m–ÿk´ÿyœ­ÿu‘ ÿk‡•ÿPjuÿ3FOÿ#ÿÿÿÿ"""ÿGHGÿjljÿ~€ÿPQPÿ ÿ4eÿQ•¶ÿ_¦ÊÿsµÙÿs·Ùÿ]¯×ÿF¢Ïÿ@žÉÿZ«ÒÿAŸËÿ*‘Äÿ0”Æÿ/’Âÿ2–ÇÿT´Þÿm½áÿ‰Éãÿ{Âàÿ]³×ÿU®ÔÿwºàÿP¤Óÿf¯Ùÿk²Üÿt³Ûÿb«Öÿ?œÍÿl³Úÿa¬ÕÿV¨Óÿr²Ùÿz²Úÿn®×ÿT¢ÑÿC›Íÿ6•Ëÿ †Ãÿ¿ÿÀÿqºÿ o´ÿ@ŽÅÿo®Ôÿu±×ÿi¬Ôÿ\©Òÿ[§ÑÿfªÔÿbªÔÿY§Òÿr´ÛÿsµÝÿm¶Þÿuºßÿ{»Þÿr¶ÞÿY¬ÚÿTªÚÿT¬ÚÿY®ÝÿnÁêÿ€Ñòÿ”Üõÿ“Úóÿ¡ßôÿ¤ßöÿ«á÷ÿ¨Ýôÿ”ÔðÿžÚôÿ•×òÿ~ÑñÿrÉëÿqÄèÿwÅêÿwÆéÿoÂçÿŠËëÿ~ÃèÿZ±ÝÿW«Úÿ/”Ëÿw¸ÿj°ÿfªÿe¤ÿ,CÿÿY[Zÿ”’ÿ†‹‰ÿ¿ÂÀÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¾Á¿ÿDEDÿÿ/ÿ8_ÿ?=ÿ‹ÿ†‹‰ÿª®¬ÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàááÿµ¸·ÿ½À¿ÿÓÕÔÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÜÞÝÿ~€ÿ ÿ=KÿK«ÿLšÁÿAÇÿq´Øÿ…¼ßÿN¤ÐÿŽÃÿ'“Äÿ.‘Ãÿ-Áÿ'‹ÀÿR¨Ñÿ„ÊæÿŠÈãÿÌäÿ”ËåÿO§×ÿX«Ùÿh³Üÿy»àÿU¨ÔÿAžÎÿT©Óÿ[ªÖÿK¤ÑÿK ÎÿW¦ÑÿX¥ÑÿG™Èÿ1Ãÿ*ŒÂÿ9’Æÿ8Åÿ'…Àÿ/ŠÂÿX¤ÐÿR¥Òÿ^©Ôÿ`¬Öÿ_©Óÿ_ªÓÿi¯×ÿe­Õÿb«ÖÿV¦ÔÿE¡Îÿ2–Éÿ2•Êÿ*ŠÃÿ+‹Åÿ4“ÉÿE›ÌÿEœÍÿ5’Èÿ2™ÑÿlÆñÿ¦æúÿ©æúÿ¸èûÿºèúÿ¼ëûÿµêüÿ§åùÿ¤à÷ÿ¡Þ÷ÿ—ÜõÿÕòÿŒÑîÿ’ÓñÿœÔñÿÆéÿu¾äÿ`°Üÿ[°Ýÿj²ÝÿX¨×ÿ=šÐÿ+Èÿy¼ÿf²ÿc¯ÿ t³ÿ u´ÿ u´ÿy·ÿ s³ÿ¸ÿ Yqÿÿ787ÿ‰ŽŒÿ†‹‰ÿ¦©¨ÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÐÒÒÿGHGÿÿ7pŒÿE޲ÿ@˜ÀÿB¡ÌÿM¦Ðÿ?œÊÿ> ÍÿC£ÎÿC¡Ëÿ=™Æÿ ˆ»ÿ'ÀÿyÄâÿÍåÿŒÍãÿ–ÏæÿO©Øÿ\®Ùÿd±Üÿe±ÙÿI¢ÑÿCžÏÿN¤ÔÿW¨ÕÿM£Ñÿ=›Ëÿ:–ÉÿGÍÿC˜Êÿ<”Éÿ2Çÿ0Âÿ&…¾ÿ|¹ÿF™Ìÿe¬ÖÿX¨ÓÿYªÓÿb«Öÿ]©ÓÿS¦Ñÿ\©ÓÿS¥ÏÿP£Ðÿ:˜Êÿ,’ÇÿˆÂÿ{¹ÿf­ÿ_ªÿ|»ÿƒ¾ÿ‡Âÿ…ÂÿH®âÿ‚Õ÷ÿ®êüÿ¼ëþÿ¸êûÿ¶êûÿ®çúÿ¤â÷ÿ¥ã÷ÿœßøÿ˜Þ÷ÿŒ×ôÿ Ýöÿ˜Õñÿ‘ÒðÿˆÊìÿÄçÿx¿æÿp¹âÿ\¯ÜÿO¥×ÿJ Òÿ;˜Íÿ/ŒÆÿ {½ÿh±ÿ`­ÿp²ÿo±ÿn²ÿp³ÿ¼ÿE§ÖÿW®×ÿ9m†ÿÿ00/ÿˆŒ‹ÿ‡ŒŠÿ¡¥£ÿÞßÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ³´´ÿÿ*4ÿ\‹£ÿ_Ÿ¿ÿ9—Âÿ0—ÅÿL¥ÐÿL¤ÍÿBÉÿN£ÍÿAÊÿ,Âÿ„ºÿU¬ÓÿÎæÿ‰ÊâÿŽÍåÿH¥Öÿu¸ßÿn´Ýÿa°ÙÿE¡ÑÿM¦ÓÿW©×ÿQ§ÓÿH¡ÐÿP£Ñÿ9˜Éÿ6–ÈÿG›Ìÿ:”Éÿ3Åÿ2ŒÃÿ ƒ½ÿ!„¾ÿW¤Ñÿ^©ÔÿX©ÓÿW¨ÒÿP¤ÑÿM¤ÒÿK£ÐÿL¤ÒÿE Ïÿ8—ÊÿˆÃÿ‰Äÿ|¼ÿƒ¿ÿ6‹Åÿ-…¿ÿ(½ÿtµÿ^¬ÿx¿ÿS»îÿ–Þûÿ´ëýÿ´çúÿ±æùÿ´éûÿ«æúÿ«æúÿ©äùÿ•Þõÿ˜Ýõÿ˜Ýöÿ¢ß÷ÿ¡ÛöÿÑïÿ†ÉêÿƒÂåÿu¼âÿg¶ßÿQ¦Öÿ+•Ìÿ+”Êÿ=–Íÿ-ŠÃÿ vºÿb°ÿ`­ÿp²ÿ r³ÿ tµÿ|ºÿ?¦×ÿbºåÿe·àÿ<ˆ¿ÿBnÿÿ**)ÿ†‰ˆÿˆ‹ÿœ ŸÿÜÞÝÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàááÿ‚ƒ‚ÿÿ'ETÿJŒ¬ÿD”¼ÿS¤ËÿYªÒÿh±ÓÿV§Îÿ2’Âÿ/’Ãÿ4’Ãÿ=“ÃÿF›ÉÿÍæÿ–ÍäÿËåÿ@ŸÓÿc¯Ûÿ^­ÙÿP§ÖÿW¬ÚÿI¦ÔÿS©ÔÿP¦ÓÿS¦ÔÿN¡ÏÿQ£ÐÿJžÍÿLœËÿ9“Çÿ*ŠÂÿ,ŠÂÿ(ˆÁÿ?—ÊÿT¥Òÿ.•ÌÿDŸÑÿT¦ÔÿO¥Óÿ?žÑÿ> ÒÿB Òÿ?ŸÑÿ-“Éÿ ~¾ÿ!‰Åÿ=™ÌÿEÐÿN›ÍÿE”ÊÿH•Ëÿ"~½ÿ\¨ÿ{ÂÿK·ïÿƒÕ÷ÿ®êüÿ°éûÿ´êûÿ»ëüÿ¹ëüÿ¢äúÿŸäùÿ¤âøÿÞöÿ—ÜôÿšÙóÿ™×óÿÕòÿ”ÎîÿŒÇêÿi¶Þÿn¹äÿg±Üÿ1“Êÿ„Áÿ)Çÿ ƒÀÿm´ÿ`¯ÿg¯ÿo±ÿo±ÿ„¾ÿW³Þÿb½åÿlÃêÿ'|¸ÿ>ŽÿDÿ2kÿ-ÿ###ÿ‚†„ÿˆ‹ÿ™›ÿÚÜÛÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿרØÿABAÿ ÿC|—ÿ^š¸ÿs¯Îÿsµ×ÿS§ÐÿDœÊÿ*޾ÿ(ŽÁÿ$Âÿ2‘Ãÿ#ŠÀÿ{ÀÞÿœÒçÿŠÈãÿ4—ÎÿC¡ÓÿG¡Óÿ@ÐÿO¦ØÿJ¤ÔÿCžÐÿ;šÌÿ@›ÍÿR ÏÿMžÏÿL¢ÏÿEœÏÿ5“Éÿ2Æÿ(‰Ãÿ+ŠÂÿDœÌÿE Òÿ7›Ðÿ?ŸÐÿD¡Ôÿ@¡Óÿ> Óÿ< Òÿ6ÑÿL¢Ôÿ!ŒÅÿ6—ÎÿG¡ÔÿO¤ÓÿLŸÑÿ@”Êÿ7ÇÿD˜Ìÿ‚Áÿvºÿ…ÆÿE­çÿnÈòÿ«êüÿ©èûÿ¶îýÿ¼ïüÿ³êûÿžàøÿ—Þöÿ¤à÷ÿ•Úôÿ’Øóÿ•ÚóÿžÙôÿ‘ÏíÿˆÈêÿ€Ãçÿd´ßÿb´ßÿ]­Ûÿ4”Ìÿ€¿ÿ |¾ÿ sºÿf°ÿ[«ÿ`«ÿe¬ÿp²ÿ]²ÝÿyÅèÿtÂéÿJ¬ÚÿU ÿG“ÿH”ÿCÿ8uÿ,Aÿÿ‚ÿ‰ŽŒÿ•š˜ÿØÚÙÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ»½¼ÿÿ/7ÿg”«ÿtªÆÿo°Ñÿb«ÑÿFžÉÿ-‘Ãÿ1“Åÿ-Åÿ!‹Ãÿ^¤ÿB†¹ÿ ÙíÿŒÊåÿBœÒÿO¥ÖÿI¡Òÿ@ÑÿO¦ÖÿF¢Óÿ2–Ëÿ2•Íÿ>˜Ëÿ?šÏÿQ¥ÕÿS§Óÿ7–Ëÿ.Æÿ"‡Âÿ#†Àÿ'ŠÄÿF ÒÿA ÒÿB¡ÒÿA¡ÓÿC¤ÖÿQ©ÙÿO¨ÙÿJ¦ÕÿK¦Öÿ+•Íÿ<™ÎÿZªØÿ]­Úÿ`­ÚÿJžÐÿ=šÎÿKŸÑÿ3Èÿ!…Áÿ"‚Àÿ#ŽËÿ,§ãÿfÅïÿ¦äùÿ±éûÿ§êüÿ§éüÿ›âùÿ¡áøÿ©á÷ÿžÜôÿ˜Ùóÿ˜Öòÿ‹ÐîÿŒÏíÿ„ÊëÿÆèÿq»áÿZ°Þÿ]¯ÜÿV©Öÿ)‘ÈÿƒÁÿuºÿl³ÿd¯ÿR¥ÿ^ªÿd«ÿ2ÆÿvÃèÿ~ÅèÿX¶áÿz¸ÿD”ÿL™ÿJ˜ÿG•ÿ hªÿ€¯ÿ 0Bÿÿ|~}ÿŠÿ’–•ÿÖØ×ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿâããÿ…‡†ÿÿ6Q`ÿp¡»ÿv¯Íÿr±Óÿ|¸ØÿM¡Íÿ*‘Äÿt±ÿ`¢ÿQ™ÿW¢ÿZªÔÿ„Ëåÿ.“Ëÿ<ÓÿL ÓÿLžÓÿS¥ÕÿR§×ÿZ«ÚÿDžÒÿBœÐÿ<›Ïÿ4˜ÌÿS ÐÿJ›Íÿ/Åÿ ‚¾ÿ „Àÿ(’ÉÿO¦ÖÿG¤ÓÿJ¥ÕÿH¤ÖÿVªÙÿ^°Üÿ\®ÚÿH¤ÖÿXªÙÿC ÔÿU§Øÿd°Üÿ[«Ùÿl°Üÿ\©ÖÿBŸÑÿR¥ÖÿFŸÑÿ;˜Îÿ6“Êÿ)”Îÿ7«ãÿlÇîÿ›áøÿ¬êûÿß÷ÿÜ÷ÿœß÷ÿŽØóÿ¤áöÿšÚóÿ’Öñÿ~Ííÿ…ÍíÿˆÊëÿ†Éìÿ|Äæÿu½äÿd±ÞÿP¦×ÿC¡Óÿ2”Êÿ€¿ÿn¶ÿl³ÿ`¬ÿN¢ÿ`ªÿh¬ÿ@›Íÿ€ÇêÿqÀçÿ9¡ÔÿU¡ÿG•ÿF•ÿN›ÿ¾ÿ˜ÐÿŒÃÿ|«ÿ(:ÿÿ‚…„ÿ…Šˆÿ¹¼ºÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿרØÿ½À¿ÿÜÝÜÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÜÝÜÿ@A?ÿÿIšÿ^œºÿa¦ÈÿY¦ÐÿAžËÿ†¼ÿ^¡ÿXœÿXšÿ[¥ÿ!½ÿxÆãÿ/”Ìÿ9˜Ðÿ:–Îÿ:–ÏÿLŸÕÿN¦×ÿQ¦×ÿBŸÒÿI¡ÔÿQ¥Õÿ;™Ïÿ9–Íÿ6“Ëÿ.Çÿ~¼ÿ&ŽÆÿ:œÐÿR¦Öÿ5™ÑÿF¢Óÿb­Úÿ^¬ÙÿM§ØÿS«Úÿh³Ýÿf²Ýÿ_¯Úÿd®Úÿ^­ÚÿW¬ØÿU©×ÿB¡ÓÿG¤ÔÿW¨×ÿaªØÿO£Õÿ=Ñÿ3™ÒÿD°åÿzÍòÿ‹×óÿ‰Ùõÿ‘Úóÿ‹Õóÿ•ÙóÿÖðÿ‡Ðïÿ‹ÑíÿŽÏíÿ€ÈêÿmÀèÿsÁèÿÅéÿo¼åÿa´àÿ_°ÛÿF¡Óÿ7œÏÿ5”Ëÿy¼ÿj³ÿk±ÿO¢ÿDšÿU£ÿcªÿ%ŠÃÿ[µàÿN¯ÞÿÂÿK›ÿC’ÿUžÿ#“ËÿšÒÿÌÿ †Áÿ u«ÿGkÿ ÿ‚€ÿ…ŠˆÿÍÏÎÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßààÿÊÌËÿ°³²ÿ•š˜ÿ…Šˆÿ…Šˆÿ› žÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿÃÄÃÿÿ/9ÿY¨ÿL˜¼ÿJ¢Êÿ0”Æÿb¤ÿ[žÿWšÿS–ÿh­ÿ }¾ÿe»ßÿ,Ëÿ ˆÆÿ†Çÿ7–Ðÿ6˜ÑÿV§Ùÿ@Òÿ=žÒÿE¡Ôÿ?›Ñÿ=–Íÿ6•Íÿ.‘Èÿ„¿ÿ…Áÿ/•Ëÿ.—ÎÿL¦ÖÿZªØÿ6›ÐÿP¦ÕÿJ¤Ôÿ>ŸÓÿNªÙÿj¶ßÿc±Üÿ_°Üÿ[®Ûÿ[®ÛÿZ®Úÿd±ÞÿUªÙÿX«ÙÿQ¨×ÿ[¦ÕÿH ÓÿE ÔÿA Öÿ@¬ãÿ‚Îñÿ‰ÔòÿÓðÿ–Ûôÿ†Óñÿ‚ÐîÿÌëÿmÀçÿ}Æèÿ†Êêÿ“Îìÿk¼ãÿK®Þÿf»åÿhºâÿV¯Üÿi´ÞÿF¢Óÿ6šÍÿ)ŽÇÿu¹ÿn´ÿd¬ÿAÿ?—ÿFšÿU£ÿi°ÿ8 Óÿ6ŸÒÿb±ÿB‘ÿ b¨ÿ"™Ðÿ—Ðÿ ‹Êÿ„Æÿ~½ÿn§ÿ2OÿÿŒÿˆ‹ÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿרØÿ½À¿ÿ¢¦¤ÿŠÿ…Šˆÿ‹Žÿ”“ÿ’ÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿâããÿŒŽÿÿ6Xiÿ:†¬ÿ+ˆ¹ÿx²ÿ\žÿ^¡ÿUšÿ`Ÿÿ ‚ÀÿŠÅÿaºÞÿÅÿ‡Éÿ2“Ïÿ8—Ñÿ2–ÏÿH Óÿ/–Ïÿ3šÑÿN¥×ÿ<›Ïÿ9–Íÿ3”Ìÿ+‘Èÿ„Àÿ0’ËÿP©ØÿO©ØÿV«ÙÿY­Úÿ4™ÎÿDŸÐÿ=œÑÿR¨×ÿI¥×ÿP¨ØÿM¦Øÿ[®ÚÿV«Ùÿ[­ÚÿTªØÿO©Ùÿ[®ÜÿRªØÿT©ØÿV¨ÖÿO¤ÖÿZ¨Ùÿ>šÒÿ)ÙÿjÃíÿ‡ÑñÿvÍîÿyÏîÿzÍíÿrÇêÿxÅéÿoÁæÿrÁçÿ}ÅéÿÇéÿ{ÃèÿX²ÞÿO­Ýÿ`´àÿW­Ûÿ]«ÚÿE¡Óÿ.”Ìÿ‰Ãÿr·ÿe±ÿBÿ5“ÿ7ŽÿB˜ÿNœÿMœÿ!Àÿ'Éÿ^¬ÿ|¼ÿ%ŸÕÿ,žÔÿ ŠËÿ‚ÇÿÁÿtµÿaœÿ&ÿ/0/ÿ”’ÿšžœÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÛÝÜÿ°³²ÿ•š˜ÿ…Šˆÿˆ‹ÿ“‘ÿ‹ŽÿwzxÿSTTÿ%%%ÿ+,*ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßààÿDDCÿ$ÿfÿX’ÿ]žÿ[žÿ\ ÿQ˜ÿ {µÿ•Ïÿ&›Ïÿeºßÿ"‹Ëÿ‰Êÿ6–ÒÿAšÒÿ/”Ïÿ7˜Óÿ9™Ñÿ5—ÐÿE ÕÿHŸÒÿ4•Íÿ,‘Éÿ'Æÿ)‹Åÿ>Ðÿ^°Üÿ^¯ÛÿY­ÙÿJ¦Öÿ;ŸÑÿ5–Îÿ4˜ÏÿJ¤ÕÿD£ÕÿM©ÙÿC¦×ÿS­ÚÿZ®Üÿ^®ÛÿZªÚÿ_°ÜÿL¨ØÿA¤ÖÿE£ÖÿG¢ÓÿU§×ÿO¡ÔÿEœÓÿ-œÔÿO¹æÿ|ÎñÿƒÑðÿtËëÿrÇêÿtÅèÿ‡Éëÿ„Åæÿr¾äÿ‚ÈéÿƒÇçÿt¿æÿd¸âÿJ©ÙÿD¨ØÿQªÙÿW§×ÿ@œÐÿ+‘Êÿ¾ÿf±ÿL¡ÿ0ÿ?‹ÿC†ÿ@‘ÿ@•ÿM™ÿ a«ÿ‡Åÿ•Ïÿ+¡×ÿ4 ÔÿËÿzÂÿxÂÿy½ÿg«ÿX“ÿ ÿPRQÿ‹Žÿ®±°ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¾ÁÀÿ‡ŒŠÿ•“ÿ„‚ÿhjiÿ<=<ÿÿÿ ÿ ,>ÿ%2ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáââÿÉËÊÿÿ/ÿH€ÿV•ÿ\žÿXÿ_¢ÿ—Îÿ!Óÿ9¤Òÿ+Ðÿ„Éÿ‹Ìÿ*’Îÿ0”Íÿ*’Ïÿ+’Ïÿ=™Òÿ.•Ïÿ4—ÎÿFÒÿ9™Ïÿ3•Ìÿ.’Êÿ2–ÌÿW°Üÿcµàÿj¶ßÿc³ÝÿL©Öÿ8Òÿ>ŸÔÿQ©Øÿ>¡ÔÿD¥ÖÿLªÙÿU¬ÚÿD§ØÿO«ÛÿB¡Õÿ7œÒÿ?¢Ôÿ@¤Õÿ;ŸÔÿ:ŸÔÿ6›ÐÿCÒÿNžÓÿ>•Íÿ3‘Ëÿ=¨ÚÿoÇíÿsÊïÿxÊîÿuÆêÿzÆéÿŠÉêÿ”Êêÿ…ÅçÿˆÉèÿ‹Ééÿiºâÿ\µáÿS¯Üÿ@¢×ÿK¥×ÿE¡Ôÿ=™Ðÿ!ˆÄÿ y»ÿ\ªÿ?–ÿ2ˆÿJˆÿB‚ÿ?Šÿa©ÿ$•Íÿ. Óÿ-ŸÓÿ2¡Õÿ5¢Õÿ‰ÈÿzÁÿv¾ÿp»ÿiµÿ_¤ÿNˆÿÿmonÿ†‹‰ÿÂÄÃÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ£¦¥ÿ•“ÿ999ÿ ÿÿ$*ÿ&Snÿd•ÿv¬ÿ"{¯ÿbˆÿ+;ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿãäãÿ”•”ÿÿ0VÿJ…ÿM‘ÿV˜ÿ"‘Åÿ,¥Úÿ6¥Öÿ/ŸÑÿ–Ìÿ‰Ìÿ*•Ñÿ>žÕÿ8™ÒÿFžÕÿ8™Ðÿ<›ÓÿDÔÿJ¢ÓÿBžÒÿB›Ïÿ<—ÍÿF™Ìÿ9—Íÿ\³àÿjºãÿg¹âÿb¶ÞÿY±ÜÿI¨×ÿQ¬Ûÿc´Þÿ= ÕÿK¨ÙÿH§Øÿ<£ÓÿD¦×ÿ=¢Öÿ9 Óÿ<¡Óÿ7 Ôÿ9¡ÓÿD¢ÖÿB Ôÿ1™Ïÿ(’Ëÿ(Éÿ0“Ìÿ ŠÇÿ}Âÿ/šÔÿA©ÞÿN´âÿj¾èÿtÃèÿ‰ÊéÿÐïÿ“ËëÿŽÍìÿÈèÿ‰Åçÿv¼ãÿZ®Ýÿ?ŸÓÿQ¦Öÿ>šÐÿ(Èÿ{¼ÿc¯ÿ=—ÿ(ˆÿ3ŒÿK“ÿu¬ÿ1•Èÿ3¤Ùÿ9¤Øÿ8¡Õÿ5 Ôÿ1Óÿ ~Áÿr¼ÿp¼ÿpºÿlµÿf°ÿZŸÿ«Øÿ'žÏÿ2žÏÿ6˜Òÿ6šÔÿS¤ØÿFŸÕÿP¥ÙÿO¢Öÿ<šÒÿEŸÔÿBÒÿ@Ïÿ9—Îÿ0“Êÿ8—Íÿ2Çÿ8šÒÿh¾æÿtÁçÿq¾äÿ`·ßÿT²ÝÿV°ÞÿA¤ÕÿA¤ÕÿH¦ÙÿD¥×ÿ3ŸÓÿM¨Ùÿ9¤Öÿ7¢Õÿ; Ôÿ8ŸÔÿ;¡Óÿ-™Ïÿ,™Ïÿ(“ËÿŒÇÿ €ÂÿƒÁÿ†Æÿ ‰Êÿ‚ÅÿÅÿ“Ðÿo½æÿxÃéÿÆèÿÊêÿÆèÿ‚ÅçÿÆèÿ‘Çêÿs¸àÿQªÙÿE¥×ÿJ£Öÿ?›Ðÿ~¿ÿj´ÿN¥ÿ-ÿF™ÿ9ˆÁÿQ§ØÿW´âÿX²àÿ.–Ñÿ#ŒÊÿD§Ûÿ?§Úÿ(“Íÿ ‰Èÿqºÿc²ÿd³ÿd²ÿ]ªÿM•ÿ)Nÿÿ‰Œ‹ÿ“‘ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÈÊÉÿ†‹‰ÿpsqÿÿ‘µÂÿªØçÿÄëøÿÊîûÿ¹âõÿ‰Ççÿ@•Çÿr±ÿ r®ÿT‚ÿ$7ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáââÿÏÏÏÿÿ$@ÿ7ºÿ[ÀæÿX»åÿJ³Þÿ;©Øÿ, Òÿ%šÐÿ@ŸÖÿ9šÒÿEŸÕÿf¯Ýÿ`¬ÙÿF ÓÿL¢ÕÿK¢ÓÿIŸÒÿS¡Ñÿ^©ÖÿZ§Ôÿ9˜Ëÿ.Èÿ4ŽÈÿB¢Öÿi¾èÿzÃèÿn¿åÿi¼äÿ`¸ãÿO°ÜÿL®ÛÿE©Øÿ@¥×ÿLªÚÿW²ÞÿM°ÝÿL°ÝÿQ­ÛÿF§×ÿA§×ÿ:£Õÿ-ŸÕÿ1Óÿ6ŸÒÿ,›Òÿ%˜Ïÿ"–Îÿ“ÍÿÎÿŒËÿƒÇÿ)–Ðÿb¸äÿlÁéÿƒÇèÿŠÉéÿ‰Æçÿ‡ÂåÿÀäÿr·ßÿM¥×ÿ6œÓÿG¢Óÿ<™Îÿ}¾ÿ&ƒÂÿ&xºÿ3„ÂÿO«ÛÿN°áÿ,–ÐÿÄÿx¿ÿ q»ÿ9 Õÿ_¾êÿnÃìÿPµåÿ,—×ÿ!ˆËÿ'Êÿx½ÿ b°ÿS£ÿJÿ1ÿ(('ÿ‘•”ÿ£§¥ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¬°®ÿ‘ÿGHGÿÿÅÔÿ­Ûëÿ°àóÿÀéøÿÁè÷ÿ³àòÿœÏëÿJ˜Êÿ5‹Àÿ"´ÿZ„ÿ 5ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿãääÿ€€€ÿ ÿ]µÚÿhÁæÿe¿åÿTµÞÿH¯Ûÿ3¤Õÿ™Ðÿ=¢ØÿR¨ÛÿT©Ùÿ_®Üÿ_ª×ÿ[©×ÿ^©ÙÿV¨ÕÿCšÏÿ]¥ÓÿU§ÔÿW§ÖÿO Ñÿ5‘Èÿ8Æÿ€½ÿ8œÓÿuÁèÿ†ËëÿÈéÿÇéÿsÄèÿnÀæÿ[ºãÿeºäÿe¼åÿ[¹äÿf¼åÿf½åÿl¾çÿY¶áÿVµãÿa¸äÿb¹äÿ\¶âÿ^·âÿZ³àÿK«ÛÿI©Úÿ=¤×ÿ7¡Ôÿ(šÑÿ’Íÿ‹ÉÿŒÌÿ8¢ÙÿX³âÿb·ãÿ|¿çÿ|Àåÿx½âÿl³ÞÿUªØÿK¤×ÿR£Ôÿ3ËÿŠÈÿE£ØÿV­Ýÿ;¤Úÿ.—ÐÿÂÿo¸ÿa±ÿ_¯ÿ-ŒÈÿ„Îòÿ‰ÔóÿšÜøÿ’Øöÿ€Éîÿc²åÿY´èÿZ¹êÿN¬ßÿr±ÿjŸÿ ÿHJIÿŒ‘ÿ·º¹ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ•“ÿŽ’ÿÿM]cÿ¤ËÛÿ´ßîÿÇëøÿÃêùÿ¼åõÿµàñÿ±ÝñÿpµÞÿ>–ÉÿBšÈÿ:‘Âÿ&j’ÿ (:ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿáâáÿÄÆÆÿÿ6Zhÿl¾ßÿtÅæÿwÈêÿY¸áÿF­Ûÿ5¦ÕÿšÐÿF©ÝÿK¨ÜÿW«ÛÿXªØÿe¯Ûÿf¯ÛÿV¨ÖÿQ£ÕÿT¤Ôÿ[§ÕÿI Ðÿ4–ÌÿF›Íÿ?•Êÿ&‹Ãÿ~¹ÿ‚¼ÿ_­ÚÿyÅêÿ”ÖòÿÚóÿŽÕïÿŠÒñÿšÚõÿžØóÿ„ÎîÿeÂéÿlÂêÿoÄëÿgÁêÿmÃéÿuÃèÿrÃêÿpÁêÿnÁéÿ\¸åÿ_¹äÿcºåÿ]¶ãÿ[´ßÿQ­ÜÿG§Úÿ3ŸÔÿ,™ÏÿÉÿ„ÈÿÎÿB¥Ûÿl¹äÿh¸ãÿl¶áÿf³ÞÿTªÚÿL¡ÕÿKžÒÿÄÿŒËÿ$‘Ïÿ$ÌÿƒÄÿ t¼ÿ]°ÿW­ÿg¶ÿw½ÿC Öÿ—ÐñÿŸÝõÿ—Üöÿ­æúÿºíýÿ¾îýÿ áùÿ…Óõÿd¾íÿ3˜Ïÿj¢ÿÿghhÿ‡ŒŠÿÌÎÍÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÑÒÒÿ…Šˆÿ}~ÿ ÿ‹¤ÿ³ÕáÿÐí÷ÿÜôýÿÁèõÿÅéöÿÆéöÿ·ßñÿÐëÿX§Ôÿ6”Éÿ>•ÇÿE–Äÿ1r”ÿ!6ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿàâáÿàâáÿáâáÿÄÆÆÿÿ+HTÿq½ÚÿsÃäÿwÇéÿuÈêÿY¸áÿL®Üÿ<©×ÿ–Îÿd³áÿ[°ÝÿK¨Ûÿ_¯Üÿf°Üÿg®ÛÿZ©×ÿFŸÑÿN£ÔÿHžÒÿGžÐÿ3“Êÿ4•Éÿ?—Êÿ1Æÿ0ŽÃÿ.ŠÀÿ*ˆÀÿ+ŽÄÿM¨×ÿ`®Úÿm¿éÿi¿èÿzÃåÿ™Úôÿ—Ûõÿ”ØôÿŠÒðÿ†Îîÿ}ÉìÿkÆîÿËìÿ}ÊíÿoÅìÿpÅëÿsÃéÿg¾êÿh¿éÿqÁéÿh»åÿ^µâÿ[²àÿP«ÚÿH¦Ùÿ6ÒÿŽËÿ …ÇÿÆÿ"ÍÿA¤ØÿK¨ÙÿE¤ØÿN¤ÖÿDÒÿ"Êÿu¼ÿl»ÿxÁÿr¼ÿ]³ÿZ¯ÿh¶ÿ}Áÿ yÁÿ }ÂÿJ©ÛÿˆÊíÿ§Üõÿ™Úôÿ—Üôÿ Ýõÿ¶åùÿ±éüÿ°êýÿ·è÷ÿ“Êßÿh¢ÿÿz|{ÿ‡ŒŠÿÞßßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿµ¸·ÿ‰ŽŒÿ^`_ÿÿ ¶¿ÿ¹ØãÿÑíöÿÒîùÿØðûÿÉêøÿÁäõÿÁâòÿ»âñÿ’ÆçÿIŸÏÿD›ÌÿW¢ÎÿN›Çÿ'kÿ$8ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿœ ŸÿÝÞÞÿáâáÿÄÆÆÿÿ2JTÿu½ÚÿxÅäÿzÈêÿuÆêÿnÄéÿg¿æÿF¬Üÿ3¥Öÿ#œÑÿcµàÿ\±ßÿc°Ýÿa¯ÝÿZ­Ûÿ[«ÙÿU¥Õÿ[©ÖÿN£ÒÿDžÏÿ8—Ìÿ;•Éÿ;—ËÿB™Êÿ0Äÿ-ŒÂÿ9ŽÃÿ/‰¿ÿ»ÿ yµÿk­ÿzºÿs³ÿe§ÿ€»ÿV®ÛÿˆÐïÿ˜Úôÿ¡Þ÷ÿ—×òÿˆÐïÿˆÐïÿ‡ÏïÿÍîÿ~ÍîÿÌîÿwÉìÿqÅëÿÈëÿtÂêÿyÂéÿeºäÿN¯ÞÿJ«ÜÿSªÛÿ5œÓÿ‘ÌÿŒÉÿ€Åÿ ‚ÇÿƒÈÿÎÿ/—Ðÿ%‘Ëÿ„Åÿ s½ÿ^²ÿE¤ÿL§ÿX®ÿkºÿ$‹Êÿ>•Ïÿo½ÿ‚ÆÿH¨Ûÿu¿èÿ€ÈìÿŒÐïÿ’Øòÿ•Ùóÿ Üöÿ ßøÿ°çûÿÍíõÿ±ÖÝÿc{}ÿÿ‡Šˆÿ˜œ›ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿšžœÿ•“ÿ./.ÿ$)+ÿ—«²ÿ±Ì×ÿÆäîÿÔìøÿÊèöÿµÝïÿ°ÚïÿÉèõÿÇèôÿ ÑëÿN¢ÑÿAšÍÿT£ÒÿU£ÑÿD™Èÿ3u•ÿ,<ÿ()(ÿ‰ŒŠÿ‡ŒŠÿ…Šˆÿ¡ ÿÂÄÃÿÿ0ISÿu»Úÿ~Çæÿ‚ÎìÿyÇêÿnÁèÿ[¸äÿV·ãÿD­Üÿ9¦Øÿ0¡Öÿn¹áÿ_´ßÿ`±ÝÿP¨ÙÿU«ÚÿZ«Úÿ\ªÙÿ[¨ÖÿDžÎÿ:—ÎÿC›Íÿ:˜Êÿ6–Êÿ<–Éÿ:•Èÿ;“Æÿ9’Äÿ:‘Äÿ*‰Áÿ„½ÿs³ÿh®ÿa§ÿf¨ÿn¯ÿ‡¿ÿEžÏÿS©Õÿd¸áÿ|ËðÿŽÖôÿÖôÿ”Úõÿ•Øõÿ”Úöÿ–Û÷ÿ•ÙôÿÚóÿÙóÿ˜Öðÿ˜ÔðÿŽÌìÿn¿æÿg¼æÿr»äÿh¶àÿK¥Øÿ=Òÿ(’ÌÿÃÿn½ÿj½ÿxÃÿÆÿ zÁÿi¹ÿS¬ÿ;žÿQªÿ^´ÿ\´ÿ p¿ÿ‚ÈÿËÿ rÀÿ2—Óÿg¹åÿnÀèÿ~Èìÿ‡ÍîÿÔòÿ•Ùõÿ‹Öõÿ¨áúÿÁåòÿ®Ï×ÿ?=ÿÿHV[ÿ”²¼ÿ¨ÌÙÿ»ÜêÿÍæòÿÇäòÿ¹Ýïÿ¿âòÿ³áóÿ¦ØíÿT£Ðÿ8“ÊÿW£ÓÿhªÖÿR¥Òÿ8”Åÿ"j‘ÿ):ÿ()(ÿ‰ŒŠÿˆ‹ÿŒÿÿ,FQÿo¸ØÿƒÉçÿ|ÈéÿqÃéÿk¿çÿuÃéÿZ·åÿM³àÿD¯Üÿ?ªÛÿ* Öÿg¸ãÿbµáÿ]±ÞÿZ­Üÿg°Üÿ[©ØÿU«ØÿW©×ÿO¤ÔÿL¢ÓÿH¡ÐÿO£ÐÿLŸÎÿJ ÏÿIžÍÿBšÊÿB›Éÿ@˜Éÿ1Äÿ$‹Àÿwµÿk°ÿb¨ÿc©ÿ€»ÿ4”ÇÿI¡ÏÿN¦ÐÿK¢ÐÿL¨×ÿqÁêÿY²àÿX±àÿZ´ãÿW°àÿ[³àÿqÁéÿÖòÿŸß÷ÿ•Úôÿ˜Úòÿ•ÖñÿÉëÿxÆêÿxÃèÿr»åÿ`²ÞÿU¬ÛÿR¦Øÿ3šÒÿ v¾ÿV¯ÿY´ÿbºÿZµÿT±ÿS­ÿR°ÿ]µÿ h¼ÿ‚Åÿ0—ÑÿP®ÞÿpÃêÿ%…Éÿ1”Ðÿi¹åÿfºæÿqÀéÿwÅëÿÉìÿËîÿwÉíÿ•Øôÿ¬ÝðÿœÆÕÿ!ÿ@A@ÿŽ’ÿÁÃÂÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ«®­ÿ…Šˆÿ’ÿ>>>ÿÿKY^ÿ•²½ÿ±ÍÙÿÉàëÿÓèóÿÉäòÿ¾áðÿ¶Ýòÿ¬Ûïÿ‹ÂäÿDšÎÿK ÐÿM¡Ðÿd®ØÿI¡ÑÿJ¡Ëÿ7zšÿ5Eÿ()(ÿƒ‡…ÿÿ(EQÿm·×ÿ‰Ëæÿ„ÊêÿsÂèÿp¿æÿn¾æÿ€Èëÿl¿èÿb»æÿU³áÿ2¤Öÿ'žÕÿS±ßÿ[´àÿfµàÿq·àÿc±Ýÿ[­Ûÿf²Üÿd±Ûÿf±ÚÿQ¨ÕÿG£ÒÿF¡ÏÿN¤ÐÿN¤ÑÿBŸÎÿ:–Èÿ4•Èÿ8–Éÿ/‘Æÿ)ŽÂÿ€ºÿ u³ÿY£ÿe«ÿˆ¼ÿ9˜ÉÿP¦Ñÿ[¬Öÿ]¬×ÿBÌÿ3’Éÿ<œÏÿ7›Ïÿ-—ÎÿÊÿ †ÇÿŒÉÿ#Íÿ0™ÓÿmÀéÿ‡Ïðÿo¿éÿ}Èíÿ~Êíÿ…Ëîÿn½çÿ[³àÿC¦ÙÿS«Þÿ0•Ðÿa´ÿU®ÿX¯ÿR­ÿQ­ÿZµÿh¼ÿ)Íÿ;ŸÕÿ1œÔÿK«ÛÿmÀçÿ€ÊíÿÐïÿ1ŒÊÿŒÌÿP¬ßÿ^µâÿn¿éÿuÃêÿÉìÿ{ÊíÿoÅëÿ‹ÑïÿšÖíÿŠÂ×ÿÿ`a`ÿ‰ŽŒÿÕ×Öÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¯²±ÿ…Šˆÿ’ÿ>>>ÿÿQZ^ÿŸ´½ÿ«ÈÖÿºØèÿÉäñÿÆâñÿ¿áñÿ½áñÿ¶Üïÿl±ÙÿN¢Òÿh²Üÿ^®ÙÿO¨Õÿ_¯×ÿo´ÖÿJ†¥ÿ"?Nÿÿ*HUÿo¸Ùÿ‘Îéÿ“Ñîÿs¿æÿn½æÿxÃèÿ‚ËëÿŠÏíÿzÇëÿjÀèÿY³àÿ@¨Ùÿ0¡ÖÿS®ÝÿS¯Þÿn¹âÿ}¼áÿj¶àÿj³Þÿk°Ûÿt´Üÿj°ÚÿZ«ØÿZ­Ùÿb®ØÿZªÕÿV¨ÔÿQ¥ÒÿV¨ÑÿG Îÿ*’Æÿ+‘Åÿˆ¿ÿ }¹ÿg¬ÿY¤ÿ q³ÿ)ÄÿFžÌÿ\ªÕÿW«Õÿ[«ÖÿS£Ïÿ?™Ëÿ>œÐÿ9œÏÿ,™Ðÿ$”ÎÿÊÿ‰Éÿ …Éÿ}Ãÿ ÆÿˆÈÿ~Æÿ}Äÿ&‰ÌÿQ§Ýÿ>—Òÿ-’Ñÿ‰ÌÿJ¨ÛÿtÂÿm¾ÿ€ÈÿƒÉÿ…Êÿ&“Òÿ3 Úÿ?­áÿ]¼æÿpÃêÿ|Èëÿ•ÓðÿŒÌëÿ–Õñÿ{Ãêÿ'‹Éÿ!ÎÿA¦ÜÿN­Þÿd»æÿsÁêÿuÄìÿwÈîÿqÈîÿ‹Ôòÿ¡Ùïÿ‰¾Óÿÿx{yÿ’ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ°³²ÿ…Šˆÿ’ÿ>>>ÿÿPZ^ÿ˜±ºÿ¬ÉÖÿÃÜéÿÐæóÿÖì÷ÿÍêöÿ¿ãóÿ’ÅäÿKÐÿl²Üÿe¯Ùÿh´ÜÿnµÜÿ\°ÙÿK©ÕÿY­Öÿs°ÍÿˆÇæÿËéÿŒËêÿ|Ãçÿt¾äÿÄèÿtÂèÿ€ÊëÿˆÎíÿ†Íîÿc»åÿ]·ãÿS´àÿ2ŸÖÿ€Âåÿh·áÿX¯ßÿr¹âÿc³Þÿc³ÞÿqµÞÿ†¾áÿ…¼âÿ]¯Ûÿ]¯Ùÿf±Ûÿ`­×ÿ_¬Õÿ\§Óÿ`­ÕÿK£Ðÿ:˜Éÿ*’Åÿ‡¿ÿj­ÿ[¥ÿh­ÿ{¸ÿ5–ÉÿK£ÑÿS¨ÓÿQ§ÒÿW¨ÓÿO ÎÿBœÎÿ1›Ðÿ$•Ìÿ(—Ïÿ'—Îÿ(•Îÿ‡Çÿ †ÈÿƒÆÿ„Æÿ~ÅÿtÂÿsÃÿvÇÿ„Îÿ!“Õÿ(˜Øÿ‘Õÿ,šØÿ•×ÿžÝÿ1¨àÿN·åÿkÄëÿ{Êîÿ}ÌïÿpÈíÿrÊìÿ„Ñïÿ€ÍîÿY¸æÿ<©ÞÿDªÞÿ2Öÿ‡Ãÿ0žÔÿ<¦ÚÿB§ÛÿP´áÿd½çÿj¾èÿ]¼çÿ]¾éÿcÂìÿ†Ðîÿ•Ðçÿ7KSÿ,-,ÿ“’ÿáãâÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ°³²ÿ…Šˆÿ’ÿ>>>ÿÿLY]ÿ”±¼ÿ²ÍÙÿÀÛéÿÒèõÿÉçôÿ²Ýðÿ{¸ÞÿIœÏÿr²Ûÿ†¼àÿ‚½àÿ{¼ÞÿV­ÙÿX°Üÿ`´Ýÿp¼âÿ„ÄéÿÁçÿ~Âåÿiºâÿb¸áÿi»âÿÇèÿ{Æèÿ‚ÉêÿŠÎëÿ{ÇçÿnÀçÿZ³ßÿF§×ÿ|Àæÿ]³áÿW±Þÿj¸âÿi·àÿY±ÝÿN­Ûÿ¾ãÿ‚½àÿc³Üÿd²Ûÿl´Üÿp´Úÿh°Øÿj°Öÿ_¬ÔÿR©Óÿ4—Êÿ&ÅÿŠÀÿb©ÿ^§ÿo±ÿƒ½ÿ>›ÌÿD ÎÿY¨ÔÿT¥ÒÿN¢ÎÿAšËÿ7œÑÿ-›Ñÿ"–Îÿ’Ìÿ(—Ïÿ+–Ïÿ ‰Êÿ‡Íÿ€ÍÿyÌÿÏÿ!”Øÿ0¦ãÿB°èÿN°æÿ@¢áÿ_ºéÿjÀìÿZ¶çÿA¯çÿ>²çÿjÃìÿ|ÌðÿoËïÿdÃìÿC²æÿL¶æÿjÅìÿ`ÃíÿZ½êÿQ·æÿ/žØÿ$›×ÿ+¤ÜÿP¸åÿSµæÿR¹åÿG¬ÝÿM¯ßÿO´âÿXµãÿQ³äÿD²áÿQ¶åÿY¾èÿyÇéÿbš²ÿ+DNÿÿ€‚ÿáââÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ°³²ÿ…Šˆÿ’ÿ>>>ÿÿIW\ÿ”°¼ÿ°Ë×ÿ¾ÛèÿºÞîÿ´ÞíÿžÌçÿd«×ÿb©×ÿj±Ýÿd³Ýÿj¸Þÿw»ßÿ‡Ããÿx¾áÿ^²Þÿg¶ãÿvÀæÿyÁåÿrºàÿk¸Ýÿe¸ßÿp¿äÿq¾åÿ}Âåÿq¾âÿwÂæÿ„Çèÿq¼áÿc¶Þÿo½åÿj¸ãÿj¹ãÿh¸âÿ`µáÿf¶àÿf·àÿg¶àÿyºßÿs¸Ýÿw¼Ýÿq·Ûÿe³Úÿ^®Øÿj²Úÿl²ÚÿV§Óÿ8˜Ëÿ ŽÄÿ€ºÿ]¦ÿaªÿt´ÿ#ŒÂÿ?žÎÿX©Óÿg­Öÿc«ÕÿV£ÐÿAžÌÿ6ŸÒÿ+›Ñÿ”Îÿ”Ïÿ–Öÿ–×ÿ‰Öÿ•Ûÿ+žáÿFªåÿHµéÿoÄðÿT´èÿ5£áÿ‹×ÿ Ûÿ&£ãÿ=±èÿmÇðÿvÌðÿdÆîÿ]ÅðÿZÅïÿLºéÿE±ãÿ^Âíÿ‡Õôÿ–ÜøÿƒÕöÿX·èÿ&ŠËÿ €Äÿ3©ÞÿP·æÿoÆíÿyËîÿdÃêÿC«ÝÿL®àÿD­ßÿ9¨Úÿ7§Ûÿ=®ßÿR·äÿY¹åÿlÂèÿ{Ååÿ{¿Üÿ2LXÿÿSUSÿÓÔÔÿáââÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ°³²ÿ…Šˆÿ’ÿ>>>ÿÿGV\ÿ­¹ÿ«ÇÔÿ±Ôãÿ¬×ëÿµÚíÿˆÂãÿM¢ÕÿG¡ÔÿJ¦×ÿj¶àÿ“Éçÿ~Âäÿd·àÿh¹àÿgºåÿb·ãÿ}Áçÿ‹ÆçÿŒÉçÿ€Âäÿr¼ßÿu¾àÿ|Ãäÿ‡Èéÿ‚Ççÿ‚Ææÿn¹Ýÿu½âÿp½æÿe¹âÿ]µáÿi¹âÿeµáÿa³ßÿl¸áÿR¬ÜÿJ©Ùÿj¶ßÿ|¾ßÿr·Ýÿc±Úÿ]¯×ÿpµÛÿ`®×ÿAÍÿ;šÍÿ"‘Æÿ n±ÿa©ÿl°ÿ z¸ÿ1—ÇÿB¡ÏÿW¨ÓÿX¤Ñÿ`©ÔÿL¡ÏÿC¢Òÿ.Òÿ.œÓÿ$˜Ñÿ˜Óÿ4¨ãÿB±éÿ3ªèÿD²ëÿ:­éÿPºîÿV¹êÿH¯æÿ0¢áÿ œÝÿ*¢àÿ“ØÿÖÿ‹Óÿ.žÝÿE°ãÿD³çÿU½íÿvÐñÿ˜Ý÷ÿ¦éýÿ¬ñÿÿ¨èýÿ…Îðÿ@ ×ÿ‰ÉÿÎÿ.¥ÛÿW¹çÿoÇîÿwÊîÿuÊìÿeÂëÿF«ÝÿCªÝÿB«Þÿ+£Ùÿ&¢Ùÿ9­àÿK¶ãÿ[¼çÿhÂëÿpÄêÿ„ÊéÿƒÅãÿ?`nÿÿ''&ÿ±³²ÿâäãÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ´·¶ÿ…Šˆÿ’ÿ>>>ÿÿHV\ÿ—­¹ÿªÆÕÿ¥Ìâÿ²×ëÿ‚½âÿ:›Ñÿ7šÐÿAŸÐÿT¯Ûÿg¸àÿ‚Æäÿ€Åäÿ†Éåÿo¼ãÿQ®àÿkºäÿ•Îíÿ•Îîÿ”Ëëÿ‘Éèÿ…Ççÿ…ÇæÿÉçÿˆÈäÿ‚Ãáÿl¹Þÿm¹Þÿr¾åÿq¾åÿdºãÿt¾äÿp»áÿbµàÿ_µßÿfµàÿV®ÜÿR®Ûÿx»àÿ¾âÿp·Þÿd°Úÿb­ØÿU¨ÒÿAžÏÿ0‘Éÿn¯ÿ_§ÿm¯ÿo²ÿ‚¾ÿ=œÍÿK£ÐÿS¦ÓÿN£ÐÿR£ÐÿG¢ÒÿE¥×ÿ>¡Õÿ+žÓÿ)›Òÿ–Óÿ$›Ôÿ%Úÿ&žÛÿ“ÕÿÓÿˆÐÿ †Îÿ ‡Íÿ…Îÿ†Îÿ‡Òÿ†Òÿ„Îÿ’×ÿP¸êÿhÉóÿ{Ô÷ÿ™áùÿ¯êûÿ¼óÿÿ›ÞõÿK¤Õÿ9œÔÿ…Èÿ …Èÿ –Òÿ(¡ÛÿE²ãÿhÄëÿuÊîÿmÈîÿZÁéÿ?²äÿB¨Ûÿ?¨Øÿ;¨Ûÿ.¦Úÿ+¥Ûÿ9¯àÿE²âÿY¹æÿc¾éÿsÇíÿ„Ííÿ†ËëÿÆçÿY—´ÿ(ESÿ ÿ{|{ÿÚÜÛÿáâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ…Šˆÿ’ÿ>>>ÿÿFU\ÿŠ«¹ÿŒ·Ðÿ”Ãàÿ~½àÿR¨Øÿ3›Ðÿ@£ÕÿJ¨ÚÿL¬ÚÿqÀâÿŒÉçÿ¤×ìÿ~ÂãÿF£ÜÿZ±âÿÃçÿËêÿ¢ÔðÿŸÓîÿu½áÿsºÛÿ…ÅãÿŒÈæÿ‘Éåÿ|¾àÿm·ÛÿsÁæÿ~Äçÿe¹äÿs¿æÿ†Äåÿs¼áÿf¸áÿu½ãÿl¸áÿS®ÛÿP«Ùÿd´ßÿs¸Þÿe¯Úÿ]«Öÿ\ªÕÿ4•ÈÿY¡ÿW£ÿb¬ÿl±ÿ x¶ÿ"ŒÄÿ>œÍÿ]¬ÖÿY©ÔÿEžÎÿO¦ÒÿJ¨Ùÿ@¦×ÿE¦ØÿE¦Øÿ5¡Õÿ$›Ôÿ—Óÿ”Ñÿ“ÓÿÑÿ‘ÓÿŽÓÿ ŽÓÿ ŒÐÿÔÿ“×ÿ•Úÿ˜ÚÿœÜÿLºìÿ{Ðôÿ˜àùÿ¨èýÿ½ñýÿÄôÿÿ‘ÖôÿŽÍÿxÁÿˆÉÿ•Ðÿ!™Ôÿ3¥ÛÿB°áÿV½çÿaÀêÿiÅíÿwËïÿcÂìÿQ¹åÿ?£×ÿ9£Ùÿ:¦Úÿ0¥Ûÿ+§Ûÿ?±áÿI²áÿW¸æÿf¿éÿdÀêÿwÈíÿ†ÏïÿÑïÿ†Êêÿ€Åâÿ2R`ÿÿ;;;ÿÃÄÃÿâããÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ…Šˆÿ’ÿ>>>ÿÿ7P^ÿn ¼ÿx±Ñÿt¹ÛÿK¦ÖÿB¡ÕÿQ¨ØÿV¬ÛÿJ«Ùÿe¸ßÿ†Èåÿ›Òëÿ¼àÿM¤Úÿ`²ãÿm½åÿËìÿžÓðÿ“ÌëÿƒÂáÿ¿Þÿ€ÀÞÿ~¿Þÿs¸Ùÿs·Úÿx¼Üÿ\¶ãÿcºåÿn¾åÿi½æÿÉéÿŠÅçÿ…Äåÿn¹àÿd´Þÿ^±Üÿe´ÝÿgµÝÿg²Ûÿk²Ùÿh¯Úÿ]«Öÿ?ŸÍÿfªÿ`¨ÿi®ÿl±ÿ„¾ÿ3•ÈÿL£Ðÿc¬×ÿY¥ÒÿG¡ÐÿJ§×ÿL©ØÿC§×ÿ@¥ØÿA¦Ùÿ2£×ÿ/¡×ÿ( ×ÿ*Ÿ×ÿšÖÿ"›Øÿ˜Ùÿ–Øÿ’Öÿ–×ÿ+¡Ûÿ.£àÿ>­äÿEµéÿ\ÄðÿÕ÷ÿ¢æûÿ¨ëÿÿºñÿÿ¯æûÿrÁêÿŠÊÿÇÿÎÿ“Ñÿ–Ôÿ-¥Ûÿ?®áÿJ¶ãÿ[Âéÿ_ÄíÿÏðÿ€Ïðÿ}ÎïÿxÌîÿ_½çÿA¥Øÿ"™Óÿ4¤Úÿ%¢Ûÿ;¯áÿO¶æÿS·æÿX»èÿqÇîÿÌíÿ‡ÎïÿÒñÿƒÊíÿzÈíÿmÁçÿL€™ÿ1:ÿÿ“”“ÿàááÿáâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ…Šˆÿ’ÿ>>>ÿÿA\lÿ±ÏÿpµÙÿO§Öÿ>¡×ÿY­Üÿj³Þÿ=¤ÕÿF­Øÿp¾áÿ‹Èåÿj±Ùÿ8˜Òÿ7¡ÚÿS¯ßÿ‡Äçÿ•ÊéÿŠÇæÿ‹ÆãÿÉåÿÈåÿw¶Öÿt¶×ÿx¶×ÿz»Úÿc¸ãÿ]·ãÿrÁçÿk¿åÿm¾åÿyÀæÿ…Ãåÿt¼áÿm¸áÿq¸ßÿn·ßÿY°Ûÿ_¯Úÿh±Ùÿc­Øÿ`°Ùÿ.‰¿ÿ[¦ÿcªÿi¯ÿm´ÿ…Àÿ=›ÍÿP¨ÓÿR¦ÒÿR¨ÕÿC¦Õÿ6£×ÿC§ÚÿE¨ÚÿI©Úÿ@§Úÿ5¥Ùÿ>¨Ûÿ@©Ýÿ;¨Üÿ)£Ûÿ'£Ýÿ)¢Þÿ ŸÞÿ% Ýÿ:©áÿ@¯åÿR¸éÿnÅïÿuÏóÿ×÷ÿœåüÿ¤ãøÿ\ºæÿK®àÿ%•ÒÿŠÉÿ‘Ïÿ"˜Óÿ#œÕÿ& Øÿ4¦Ýÿ=«ßÿNµäÿ]¾éÿgÅíÿfÅîÿ×ôÿ•ÚõÿžÝõÿžÞõÿ|ÒóÿW¸åÿ&™Óÿ*ŸÖÿ/¥Üÿ3«àÿC³áÿTºæÿX¼éÿhÂìÿÌîÿÑðÿŒÍïÿ•Ôòÿ…Íïÿ{Íïÿ†Ììÿ{¿Þÿ*IYÿÿOPNÿÏÐÐÿâãâÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ…Šˆÿ’ÿ>>>ÿÿ„¯ÉÿºÜÿk²Ûÿ= ×ÿ^°ßÿ|»áÿeµÞÿq½àÿw¿àÿ~Ãâÿq»ÞÿBžÑÿ0›ÒÿP¦ÙÿŽÇèÿ…Ààÿ”Çäÿ’ËåÿŒÊæÿ…Ãáÿ‚¾Üÿ‰Ãàÿ•Èãÿ~¼ÞÿÅçÿp¿æÿg½æÿfºæÿxÁèÿ‡Èéÿ‰ÆçÿÂäÿm¸áÿ|½âÿd¯Úÿ7–Ïÿd°Ûÿm²Úÿg²ÛÿW¬Øÿ ^¤ÿ]©ÿf­ÿk±ÿw·ÿ%ŒÄÿ;šÎÿB¢ÑÿJ¥ÓÿD¤ÕÿA¦×ÿ<¥ÖÿB¨ØÿG©ÜÿDªÝÿ>©Ûÿ?©Ûÿ@­ßÿ8ªßÿ@­áÿ8¬âÿ;­ãÿA¯åÿ?±çÿ:°çÿIµêÿZ»ìÿqÉðÿ‹×ôÿ‰Ö÷ÿ–âýÿ}Ïñÿ+•Ðÿ ‡Çÿ‘Îÿ–Ñÿ šÔÿ#Öÿ9¥Ùÿ5¥Úÿ3¥ÛÿC¬àÿTµåÿ^½éÿaÁëÿcÄíÿzÏïÿ•Üöÿ‘Ûõÿ©åùÿºèúÿ‡ÔóÿsÎñÿAªÛÿ™Òÿ1£Úÿ5«áÿB³ãÿL·æÿ[¾êÿkÅîÿÒñÿÒñÿšØõÿ›ÙõÿˆÎñÿwÊñÿwÉðÿƒÌíÿ{ÁäÿCn‚ÿ 'ÿÿ§¨¨ÿãäãÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿº½¼ÿ†‹‰ÿŠŽŒÿÿvž·ÿƒ½Ýÿ|»ßÿK¤ØÿJ¨Üÿq¸àÿ|½àÿ‹Äãÿ|¾Þÿ„ÆãÿˆÈæÿa±Ùÿ8—Êÿx»âÿÄåÿ–Èåÿ¦Ñèÿ’Êåÿ~Ááÿ™Ìåÿ‘Çáÿy¶ØÿºÚÿ†ÀÞÿo¿èÿ}Äçÿfºäÿq½æÿyÃçÿ‡Ééÿ‡Åèÿ…Äçÿy¾ãÿw»âÿAšÑÿLžÒÿj³ÜÿpµÝÿ`´Ýÿ%l©ÿK›ÿd®ÿl°ÿs¶ÿ{¼ÿ&Åÿ?šÎÿ?¡ÑÿRªÖÿX®ÛÿG§ÙÿT­ÜÿZ°ÝÿP®ÞÿC¬àÿE®ßÿK²âÿH³ãÿJ³äÿLµçÿIµèÿG¶éÿQ¹éÿS¼ìÿV½íÿW¾ïÿkÉóÿÖöÿÓöÿf¿ëÿN¬Üÿ …ÉÿÎÿ-œÔÿ.ŸÖÿ/£×ÿ,¢Ùÿ'¡Ùÿ,¥Úÿ:¨ÝÿN°áÿG±ãÿN·äÿTºçÿXÀêÿhÈïÿ˜Üöÿ‘ÛõÿÜõÿ—Þöÿ­èüÿ âùÿÔôÿ]Àëÿ[²Ýÿ;¦Ùÿ/¦Ýÿ4­ãÿJ·æÿY½ëÿqÇðÿ„Îðÿ”×ôÿ•Øôÿ”Óòÿ{ÇîÿcÀíÿ^ºêÿk¿ìÿrÄìÿuÁåÿd³Ùÿ+HVÿÿhhhÿØÚÙÿáââÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿª­¬ÿÿ1DQÿ´×ÿ”Æãÿ™Éæÿa­Üÿb¯Þÿ†Àäÿ‹Ããÿ”Éçÿ‘ÈåÿŒÈæÿ“Ïêÿ~Àáÿ@“ÂÿS§ÐÿŠÇèÿËèÿªÐçÿ¤Ðåÿ‡ÃàÿˆÇâÿ”Çàÿ•ÄÝÿŽÁÜÿÂßÿ]¸åÿi¼åÿ`¹äÿ\·äÿq½æÿu¾åÿ…Ççÿ|Âæÿd³ßÿN¦×ÿ`®Üÿi³ÞÿZ­ÛÿY®Ûÿ2“Çÿ7†ÿW¤ÿg¯ÿk²ÿzºÿ„¿ÿ<˜ÊÿS¥ÓÿL¦Õÿ]°ÜÿW®ÜÿS­ÛÿY±Ýÿ_¶âÿc¸ãÿU´ãÿUµãÿV¹çÿT¹çÿ_¾ìÿ`ÀìÿW¾ìÿX½îÿZ¿ïÿgÄðÿXÅôÿQ¾îÿcÁíÿV±æÿX·æÿŠËÿ‡Éÿ Îÿ ›Ôÿ1¡Øÿ9¦Úÿ=¨Üÿ2§Ûÿ2¦Üÿ-¦Ýÿ:ªßÿ=­áÿB±âÿO·åÿZ½éÿbÄíÿpËðÿ…ÔôÿÚôÿ›âøÿ©çûÿ§çúÿŸäùÿ•Þ÷ÿ‚Ö÷ÿfÅëÿ0 ×ÿ$ Ùÿ)¤ÜÿGµäÿX½ëÿmÆïÿ{Êðÿ…ÏòÿÕóÿ—ÕóÿƒËðÿ_¾ìÿe¿ìÿk¿ëÿb»êÿnÁêÿr¿æÿo¹ßÿ=^oÿÿ+,+ÿ»¼»ÿãääÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿÃÅÄÿÿ3EQÿ‡¹ÕÿÁàÿÍèÿ¥Ñêÿj±ßÿi²ßÿŠÂåÿŽÂæÿŠÁãÿÇåÿ–Ìçÿ’Íçÿw¸Ûÿ>ºÿ4Œ¸ÿX¯Ùÿ{ÁäÿŒÅãÿœÊãÿžÉâÿ†Âàÿ–ÊâÿŠÁÛÿ‡ÁÞÿv¼Ûÿi¼æÿe¹ãÿX³ßÿJ­Þÿm»äÿr¼ãÿiºäÿaµàÿY±Ýÿh·Þÿ‚ÀâÿiµÝÿ@ŸÒÿ'’Èÿ _¥ÿ?’ÿa¬ÿi¯ÿrµÿyºÿ*‘ÅÿO£Ñÿ_¬×ÿW¬ÙÿHªÛÿP¬Ýÿg¶àÿg·âÿd»æÿi½çÿb½èÿ\ºçÿ]½éÿ_¾ìÿ`ÁîÿaÃîÿdÄðÿ`ÅðÿaÄñÿYÃñÿ(Ñÿ(p¬ÿKŸÕÿ,šÔÿ•Ñÿ!—Ñÿ(žÔÿ3 ×ÿ)¡×ÿ6¦ÚÿB¬Üÿ@¬Ýÿ8¨Ûÿ2§Üÿ9«Þÿ>­ßÿ?¯àÿC³äÿL¶åÿhÄîÿvÌñÿpÊðÿ}Ñòÿžâøÿ åûÿ§çûÿ¼îýÿºîüÿ¼îýÿ¬çûÿ–àúÿoÄèÿ!œÕÿ(£Úÿ0§ÝÿL¶çÿgÄîÿÍñÿˆÐòÿŽÔóÿŒÐòÿkÁíÿU·èÿnÂîÿpÂìÿc½éÿuÅìÿƒÉíÿq¼èÿy½âÿ\šºÿ'CQÿ ÿƒ‚ÿßààÿáââÿàâáÿàâáÿáâáÿÃÅÅÿÿ5FQÿ‚¶ÕÿÄãÿšÌèÿ Ñëÿ›Ìèÿ[©Üÿh²àÿ‹Ãåÿ¼ßÿi²ÚÿkµÜÿ¤Ñçÿ£Òèÿn±×ÿ;‹¶ÿ9‹²ÿB™ÁÿJ«ØÿY²ÜÿƒÀâÿ’ÈãÿˆÀßÿ”Æàÿ’ÇÞÿ•Èßÿ—Èáÿc¸ãÿ[·âÿb·âÿ\´ßÿs»ãÿ‚Âåÿb¶Þÿy¼àÿo¹àÿo¸ßÿh´Ýÿ@ Ñÿ#Èÿ‚¾ÿ]¦ÿH—ÿf¯ÿp´ÿq¶ÿ ~½ÿ@œÎÿ]ª×ÿ_­ÚÿM©ÙÿC§ÚÿO¬Ýÿb·ãÿj¾çÿb¼æÿj¾éÿf¿éÿfÀëÿqÄìÿsÇïÿnÆïÿqÈðÿkÈòÿKÀñÿ;»óÿ(Óÿ![§ÿ {Àÿ'—Ñÿ%šÒÿ˜Òÿ%žÕÿ6£Ùÿ7¥Úÿ:§Ûÿ=ªÚÿC¬ÞÿA«ÞÿI¯àÿF®ßÿ9«ßÿ<¬àÿA±ãÿB³äÿI¸æÿgÄîÿdÅïÿvÍñÿŒØôÿ˜àùÿŒÛöÿžãùÿ»íýÿºïüÿÅóýÿÊóýÿ¯ëýÿ¡åûÿH­Üÿ%Ÿ×ÿ-¥Úÿ/¨ÝÿTºéÿuÉòÿÍñÿ„ÎñÿŠÐòÿjÂíÿR¶çÿ[ºèÿlÁëÿsÄìÿ„ÇìÿyÃêÿk¼çÿn¾çÿ|Àæÿt·Ùÿ8Taÿÿ>>>ÿÉËÊÿâããÿáâáÿÅÇÇÿÿ0COÿ‡´Õÿ’ÂâÿÌêÿ›Îëÿ”Éëÿ’ÈçÿZªÜÿSªßÿ¿äÿ“Ççÿ¼áÿ‚Ááÿ°Øëÿ´Ûíÿl­Óÿ:‹¶ÿF•ºÿNš¼ÿH Çÿ9 ÔÿX¬Ùÿu¼ÝÿÅàÿ™Èàÿ›Ëàÿ¢ÏáÿŸËàÿ_·áÿW²Þÿjºãÿv¼ãÿŽÇçÿ—Ééÿ}¿âÿ‘Çæÿ‡Äæÿbµàÿ0šÍÿÅÿ‹Äÿs¸ÿY¦ÿU¡ÿj²ÿu·ÿ {»ÿ ¿ÿ=œÏÿ[«ØÿI§×ÿW­Üÿ_²ßÿcµáÿeºåÿbºçÿb¾çÿkÁêÿqÄìÿnÅðÿnÅïÿqÈïÿnÉñÿbÆòÿC°åÿ6ªäÿÓÿ*Çÿ6™Óÿ —Òÿ,ŸÖÿ:£Øÿ&Öÿ.£ÙÿD©Üÿ?©ÛÿO®ßÿU²áÿT²âÿT³âÿR³ãÿK°áÿC¯áÿG²âÿ@²äÿ>´äÿPºèÿaÂìÿkÉïÿ{ÏðÿŠ×ôÿÛ÷ÿâøÿ¥èûÿŸçüÿ¥êüÿ¦êüÿ¼ðýÿ¾ñýÿ—àúÿxÐòÿG°àÿ%Ÿ×ÿ+¤Ûÿ4©ÞÿV·çÿ|ÉñÿsÅîÿkÁìÿV¹éÿYºéÿh¼éÿuÂìÿyÅìÿo¿éÿ`¸åÿzÂéÿr¾çÿ~Âéÿ‹Çæÿ„Ãàÿl‘¤ÿ/=Fÿÿ™›šÿÇÉÈÿÿ7HTÿ–¾Ùÿ¢Çãÿ Èèÿ£ÏéÿŽÃçÿŽÂçÿ’Ææÿf®ÞÿJ¤ÝÿU®ßÿx¾åÿ}Áåÿ¤Óëÿ­Øíÿ¦Ôëÿ^¢Ëÿ,ƒ¯ÿ?·ÿM™¹ÿW½ÿJœÃÿH¢Ïÿ^±Úÿ…Äâÿ—Ìâÿ—ÊãÿšËãÿŸÏâÿk½äÿa¹âÿs½äÿz¾âÿÃåÿŒÈéÿ|ÂåÿÈéÿi´Ýÿ!Èÿ5™Ìÿ=™Íÿ‹Äÿb¬ÿY§ÿj±ÿt·ÿyºÿƒ¾ÿ&‘Çÿ=žÑÿR¨ØÿJ¨Ùÿeµßÿh¸ãÿk¼åÿp¾æÿtÁèÿpÄëÿiÂìÿsÆîÿ~ÌïÿÍïÿÏñÿƒÑðÿcÆðÿ%šÚÿ{¾ÿ!q§ÿ)™Óÿ'ž×ÿ'œ×ÿ/¡Öÿ/¡Øÿ7¦ÚÿE«ÜÿN®àÿS°àÿ^³ãÿ`·æÿZ¶ãÿW´äÿ\¹äÿWµãÿE±ãÿG´äÿD³äÿDµäÿR¼èÿrÈïÿpËïÿ“Ûöÿ’Ü÷ÿšâúÿ¥èýÿ¥èüÿ¡æüÿ®êüÿ­ëüÿ²íýÿ½ðüÿ¦çüÿ”âüÿÑóÿ5¥Ûÿ'¢Úÿ>«ÝÿL°àÿWµäÿOµäÿX¹çÿS¶åÿa»èÿvÂìÿn¾éÿh¾èÿV´äÿYµãÿzÃèÿ„ÄéÿzÁæÿ}ÁåÿŒËéÿ”Êèÿ ËâÿDU`ÿÿÿ8LWÿ޼ÜÿÆãÿ§Ëéÿ¥ËêÿÊêÿ~¹ãÿ‡¿äÿ“ÅæÿBŸØÿ>¡ÜÿZ¯ßÿs»äÿl¹ãÿ‚ÂåÿÇæÿ›Íçÿ6ˆ¹ÿ |«ÿ:бÿK”·ÿU›¹ÿj¥Àÿg¦ÅÿR¥Êÿ^­Ôÿy¿ßÿˆÅáÿˆÅáÿÇãÿ€Ãçÿi»äÿzÁæÿ…Äçÿs½ãÿp¼ãÿc´ÞÿY«Øÿ<›Îÿ1–ËÿM¤ÒÿJ Ðÿ.’Åÿcªÿk±ÿv·ÿ z¸ÿ}¼ÿ ‹Åÿ@œÎÿ@¢ÓÿG¨×ÿ[°Ýÿu½åÿn¿çÿlÁçÿlÀèÿdÁëÿwÉíÿ†Ïîÿ’Öñÿ—Ùóÿ’Øóÿ‹ÕòÿmÈïÿ6€Áÿ*ƒÊÿ3”Íÿ7¦Øÿ<¨Ûÿ:¦Úÿ8¦Ùÿ5¦Ùÿ6¦Ûÿ6¨ÜÿD¬ÝÿG¬ßÿN°áÿ^¶äÿT´âÿUµãÿ\¸äÿ[¶äÿN²âÿB±ãÿIµäÿQ¸çÿM¸åÿV½èÿiÆïÿÐñÿ‘ÙõÿŸâúÿ˜Þùÿ•àùÿ¡äûÿ§èüÿµíýÿ´íýÿ°îüÿ±ìýÿ¹ïýÿ®éüÿŸãúÿzÎïÿ7§ÚÿA¬àÿ]µãÿV³ãÿX¸æÿ`¼èÿQ²äÿ_¹èÿf»èÿe»æÿaºäÿi¾æÿo¾çÿq½åÿŽËêÿ–ÍëÿŽÊêÿÌìÿ›Ïîÿ¢Ôíÿ¬×ëÿ¬ÊÜÿ‘±Æÿ•Ååÿ“Ããÿ˜ÅæÿŸÉêÿ¤Ìêÿ›Çéÿƒ»âÿºâÿŒ½âÿAœ×ÿC¥Þÿu¹åÿy¼åÿs½åÿ‡Ååÿ™Ïêÿv·Ûÿt®ÿyªÿ,¬ÿ?‹²ÿN”·ÿp¤Ãÿk¥Áÿm§ÂÿXŸ¾ÿVŸÀÿ`§ÇÿY¤ÉÿR ÈÿzÅçÿc¸ãÿO©×ÿG¥ÖÿO«×ÿE¦ÓÿD¢ÑÿH¢ÐÿJ¢ÑÿAžÐÿ>Ïÿ:›Ïÿ!‡Áÿd­ÿv·ÿ‚½ÿ‡Àÿ$ÅÿJ ÐÿN¦ÓÿC¦×ÿU®Ýÿs½ãÿÃæÿxÃèÿxÅêÿnÄìÿyÊíÿÒîÿ’×ñÿ”Úóÿ–Ýöÿ‘Úöÿ„Öùÿ\Àïÿ2ŒËÿ,˜Óÿ.¥ÙÿG­ßÿJ­ßÿT¯áÿR¯ßÿFªÝÿ?¨Üÿ1¦Üÿ;©ÜÿN¯ßÿQ±âÿe¹åÿ[µäÿR³ãÿV´åÿP²ãÿb»èÿP¶ãÿE³ãÿH³åÿT¹æÿgÃìÿtÈïÿ…ÑòÿØôÿ˜Þøÿ¢äúÿ“Þøÿœãúÿ«éýÿ¸íýÿ²ìüÿ³íüÿ¹ñýÿÌöýÿÏõýÿÁðýÿ§äúÿZ¸çÿ9§ÜÿV´ãÿ_¸åÿU´åÿZ·æÿO°ãÿR²ãÿl¿êÿm½èÿa¸äÿ|ÆêÿÄèÿ{ÁåÿËëÿ˜ÏîÿŽÉêÿ‡Èéÿ‘Ìëÿ”Íìÿ˜Îîÿ£ÓîÿžÌêÿ„½äÿÄçÿÀåÿŒ¿äÿ‡ºãÿнäÿœÇéÿœÈèÿƒ¸ßÿ\¦Þÿc¯äÿ½çÿv»åÿl¹äÿ”Ìêÿ–Íêÿ5Çÿz·ÿ2ˆ¿ÿ r­ÿ!{¬ÿ@‰±ÿ[˜¹ÿU–¶ÿM³ÿ>НÿG‘¯ÿ?ˆ­ÿ@¦ÿC¥ÿoÀçÿL©×ÿA Ðÿ?¢Òÿ@£ÒÿF¤ÒÿK¦ÓÿI£ÑÿI¢Ðÿ?žÎÿI Ñÿ3™Ìÿv¸ÿc«ÿƒÀÿ$Åÿ&Çÿ<šÍÿS¨ÔÿV­ÙÿO®Úÿh¹âÿ|Âæÿ|Åèÿ|ÆêÿŽÍïÿÕòÿœØóÿ™Ûôÿ ß÷ÿžäúÿ—äüÿ…ÕõÿS·çÿ˜ÖÿšÚÿ4§ÜÿF®ÞÿO®áÿX³âÿc¶åÿV°áÿK­àÿ6©Üÿ=ªÞÿG¯ßÿK°âÿO±ãÿ[´äÿU´âÿ[·ãÿU³äÿO±âÿP´áÿOµäÿJ³ãÿ_¼èÿb¿êÿeÁìÿzËñÿ‹ÔóÿˆÓóÿ×öÿžâúÿ¤èüÿ¥èüÿ¤èüÿ¡çüÿ§êüÿ¼ðýÿ¾ðýÿÈõýÿÓúþÿ×úýÿÆóþÿƒÑòÿG®ÞÿJ¯ßÿL®ßÿK°áÿS´äÿU´åÿR±ãÿi¼çÿp¿èÿg½åÿ{Äèÿ†Éêÿ…Èçÿ†ÉèÿËëÿÈéÿËêÿŸÒïÿšÍéÿˆÄçÿŽÆèÿÆéÿ‘Ççÿ˜ÇéÿžÈéÿ–Ãèÿ‰»äÿ›ÆèÿÀãÿe©ØÿG”Ìÿ:—×ÿf°æÿs¸æÿm¸åÿt¾åÿ…Ééÿo·ßÿ3‘Æÿ6“ÆÿF˜ÇÿD“Âÿ%~²ÿkŸÿ!r¡ÿ6}¦ÿ;‚ªÿ8¨ÿLˆªÿZˆ¦ÿ]… ÿC|žÿN§Öÿ>žÍÿA¤ÔÿQ«ØÿRªÖÿZ«×ÿ^¬ÖÿG¤ÐÿA ÑÿV©ÖÿX§Óÿ4˜Ìÿ o³ÿnµÿ‰Âÿ)’Çÿ%‘Èÿ3šÏÿL©ØÿQ­Üÿ^´àÿcºäÿlÀçÿrÄêÿ‡Íîÿ¤Úôÿ§Þõÿ¤àöÿ³èûÿÁñÿÿ¢äúÿ8¤ÚÿÑÿ‰ÐÿÓÿ) ÚÿE­àÿG¯áÿW³äÿd¸åÿe¸äÿV²ãÿF®àÿ<­àÿR³äÿQ²ãÿL±âÿVµãÿN³áÿQ´âÿ_·äÿV´ãÿR²ãÿM±ãÿV·æÿ[ºçÿnÀêÿoÃëÿpÃëÿwÈîÿ‡ÐñÿÕóÿšÜ÷ÿ¡áúÿ“ßùÿ¦çüÿ³ëüÿ­éüÿ±ìýÿ¾òýÿÊõýÿË÷ýÿÔùýÿÍ÷ýÿÃòþÿ•ÛøÿU´äÿS°áÿO¯âÿH°ßÿV¶âÿZ¶ãÿW²áÿP°àÿi¿çÿ€ÈêÿŒËëÿ–ÏîÿŠÊêÿÇèÿ‰ÈéÿŠÆèÿŒÇéÿœÐíÿ¢ÓìÿÈéÿ•Ìêÿ–ÉéÿÄçÿ›Èéÿ¢Ééÿ¾æÿo²ßÿo±ßÿk­Üÿ[¡ÓÿYœÏÿ=šØÿO§áÿ\¯ãÿh¹æÿƒÆéÿ~Âçÿ]ª×ÿBšÌÿGžËÿIÇÿX ÆÿH’½ÿ h£ÿ d›ÿ?wœÿ[‡¥ÿi£ÿoŒ¢ÿX…žÿ3wžÿ3¨ÿUªÖÿN§ÔÿD¦ÓÿK¨ÖÿY­ØÿZªÖÿd®ØÿSªÔÿN§ÕÿJ¡ÑÿL¢Òÿ-”Èÿ_¨ÿi±ÿ*‘Éÿ8™ÎÿD¡Ñÿ<¡ÒÿF¨ÙÿF­Üÿ^·âÿ[¸äÿ_¾éÿ~Ëíÿ—Öðÿœßõÿ¥çûÿºñÿÿ¶ëüÿ‰Ñòÿ0ŸÙÿ‹ÑÿŠÒÿÔÿÔÿ'¢Ýÿ?°ãÿM²ãÿ`¹æÿk½çÿYµäÿ[·åÿ`¸åÿF±áÿU¶äÿY¶åÿQ´ãÿc¹åÿV´ãÿO³âÿ]¶åÿ[¶äÿQ±ãÿI¯âÿB¯âÿY¹çÿsÂëÿ|ÈíÿqÆìÿdÁìÿÍðÿÔñÿ‘Öôÿ’Øõÿ‘Øöÿ¢âúÿ¹ìýÿ±êýÿªéüÿ®éüÿ¿óýÿÈõýÿÒøýÿÙúýÿÖ÷þÿ Û÷ÿN¯âÿ[³äÿa¸åÿR³âÿT²âÿV³âÿW³áÿ[·ãÿxÅêÿ‹Íìÿ˜ÑïÿÓðÿÍìÿÄçÿ€Äåÿ‹Æèÿ–ÌëÿÊéÿ’Íêÿ”ÊéÿÈèÿÇèÿ‡¾äÿœÉëÿ˜Åçÿ‡»ãÿk°ÝÿV¤ÙÿP ÕÿU™ÎÿK•Îÿ1šÙÿ>¥Þÿ[²åÿa¸æÿ|Åêÿ{¸ßÿl¯ÕÿV¥ÏÿIžËÿ=•ÁÿA’½ÿ*²ÿm¥ÿiŸÿ]‚žÿv¡ÿlŒ¡ÿWŸÿg—ÿj›ÿ;…®ÿZ¬ØÿW«ØÿR©ÕÿT¬Øÿe²Úÿ\­ÖÿZ«×ÿ\¬ØÿSªÕÿF¢Òÿ3—Ìÿo­ÿ[¦ÿ{ºÿ6šÎÿI£ÓÿE£×ÿS«ÜÿW±ÝÿW´àÿaºåÿvÃêÿƒÎïÿ’Øòÿ¡âøÿ£èýÿ™àøÿsÆëÿ2¡ÝÿÔÿ’Õÿ•Øÿ–×ÿ–×ÿ ”×ÿ$£ßÿH³äÿ\¸åÿf¼èÿ`ºæÿT¶äÿf½çÿX¶åÿI²ãÿTµåÿV¶åÿ`¹æÿl¾èÿZ·åÿW¶åÿ[¶äÿG°àÿS³ãÿK¯âÿP³äÿa¼éÿkÁìÿkÂìÿoÂëÿkÂìÿ€ÌïÿÔòÿŽÔóÿ—Úõÿ•Úöÿ˜Ü÷ÿ©æûÿ­èüÿºëüÿÂîýÿ¿ðüÿÉôýÿÉóýÿÁïýÿ²èüÿ{ÈðÿL®àÿX´ãÿ`·äÿa¸åÿ[´ãÿ^µâÿtÀèÿuÃéÿŽÎîÿ˜Ôñÿ£Úòÿ¤Øðÿ˜Ðîÿ—Ïîÿ›ÏìÿŽÈçÿ•Îìÿ¢Ôíÿ˜Ìêÿ‘Éèÿ™Íéÿ˜Êèÿ‡¿äÿ‘ÂæÿŽÀäÿ€¸àÿm°ÛÿN Ôÿ=—Ñÿ+zÀÿ€Èÿ0ÜÿU±äÿnºçÿu¿èÿ|½ãÿs´Ùÿ|¹Üÿ‚¹ÛÿW¥Íÿ@–ÁÿB•¾ÿ/ˆ³ÿu©ÿkŸÿRžÿ_„ ÿQ€žÿ h•ÿY“ÿo¡ÿ'~«ÿP¥ÔÿN§ÔÿZ¬×ÿ]­ØÿU©ÔÿU©Õÿ\®Øÿ[®ØÿQ¨ÖÿD¤Õÿ…ºÿT—ÿb«ÿ}½ÿCŸÒÿH£Õÿ=¤ÕÿP­Ýÿi¹ãÿl¿èÿtÅìÿÕòÿœßöÿ±èûÿ¯ìþÿd¿èÿ•Öÿ’Ôÿ–ØÿœÜÿžÝÿžÜÿžÜÿÛÿšÛÿ$¡ÝÿQ·æÿxÄìÿyÃêÿlÀçÿd½èÿk¿éÿc»çÿO´äÿ\¸æÿ^ºçÿ_ºæÿ_»çÿe¼èÿj½èÿ_¸åÿN²ãÿY·åÿK°äÿ_ºèÿkÀêÿpÂëÿ|ÇìÿxÇìÿtÆíÿÓñÿzÈîÿqÄîÿ€Íðÿ¦àùÿ‘Öôÿ–Ü÷ÿ­çüÿ·êýÿ¼ìýÿºëýÿÇïýÿ¹ìýÿ´ëþÿ¥àøÿYµåÿX´ãÿ[´ãÿ_µãÿh¸æÿq¼åÿ^³áÿe¹äÿo¾çÿÐðÿ¨Üôÿ­Þóÿ«Ùñÿ£Õîÿ›Ñïÿ²Úðÿ¨ÕìÿÁäÿ’Ëéÿ¢ÒìÿžÏéÿ£Ðêÿ¥Ïêÿ“ÄæÿŠÀãÿˆ¼âÿy¶Ýÿc©ØÿG›Ðÿ9‰Æÿ4yºÿ†ÏÿLªâÿo»çÿ‚ÃìÿÀçÿ¼ÞÿqµØÿt·ØÿƒºÜÿh¬ÐÿNœÅÿOžÃÿG—¾ÿ=¶ÿ/y¤ÿBxœÿAwœÿc”ÿ a—ÿk ÿv§ÿx¨ÿ6œÍÿ8œÍÿA¡ÓÿXª×ÿ\«×ÿV©ÕÿY«ÖÿZ®ÙÿT¨Øÿ7Ðÿr®ÿ]£ÿi²ÿ {Àÿ?£ÖÿG§ÛÿQ­Ýÿ^¶áÿqÀéÿËíÿ—Úôÿ©æûÿ¬ëþÿ¢åûÿV¹åÿ‘Ôÿ™Ùÿ œÜÿ% Þÿ"¡ßÿ(£áÿ.¥áÿ,¤àÿ!¡Þÿ  Þÿ(¥ßÿ[¼èÿÇìÿqÃêÿuÂëÿxÃëÿf½éÿc¼èÿc¼çÿ\¸æÿ_»æÿ`ºçÿ\ºçÿZ¹çÿd»çÿbºçÿh»èÿS¶æÿR¶äÿh¿èÿxÅìÿkÂêÿ€ÊíÿƒÊîÿyÇíÿ‹ÐðÿÌïÿtÇîÿyÈîÿÕôÿ£ÝõÿŽÖõÿªäüÿ³èüÿ´ëýÿ³êüÿ¹èüÿ¬äüÿ¯æýÿÈïÿR®áÿj»çÿxÁèÿiºåÿzÀçÿ€Äèÿr¾çÿr¼ãÿt¾äÿzÁçÿ•Ðîÿ¢Öñÿ›Ðîÿ•Íëÿ‘ËëÿŸÔîÿ©Öîÿ‰Æçÿ•ËéÿÎèÿ–Ëèÿ˜Êéÿ¡Íéÿ’Äåÿw¸Þÿi³Ýÿe¯Ûÿ`ªÖÿOÏÿ9~·ÿ!^œÿB Üÿk¸éÿ~¿éÿ}¼æÿ‡¾ãÿŒÃäÿw¸Üÿ€¼Üÿ{·Øÿu²Ôÿg¨Êÿc¥ÅÿF—½ÿD’¼ÿ2€«ÿ+q™ÿgšÿbœÿ iŸÿu§ÿuªÿ}°ÿ8žÎÿ0™Ëÿ&—ÍÿTªØÿ]®Úÿ^¬ØÿT§ÔÿTª×ÿJ§×ÿ…¾ÿn¬ÿl­ÿf±ÿ,”ÐÿI©ÛÿT°Þÿ`¸äÿwÁéÿ”Ôñÿ­ãøÿ¼ïÿÿ¯èýÿuÈíÿ-ŸÛÿ—ØÿÜÿ'¢Þÿ(¤àÿ2ªãÿ9­ãÿ7«ãÿ3«ãÿ-©ãÿ1©âÿ7«âÿ,¨âÿP·çÿrÅìÿrÆêÿsÅëÿmÁêÿ[¹çÿS·æÿQ¸æÿT·æÿkÀéÿb»çÿ^¹çÿ\¹çÿYºæÿe½çÿ~Åêÿf¼çÿW¶åÿY¹çÿwÅíÿlÂìÿkÂìÿ~ÉîÿŒÏïÿ€ÊïÿyÈîÿsÈïÿvÈïÿvÈðÿ¥Þ÷ÿžÚöÿ“Öôÿ¤àùÿ àúÿ—Ûøÿ´çüÿºèüÿ¤Úøÿk»èÿU²ãÿq¾çÿ†Åêÿ‡Åêÿ‡Äéÿ€ÃçÿÆçÿÊèÿˆÈçÿv¾ãÿ~ÂæÿŠÈéÿ“ÍîÿˆÇçÿ‡ÉéÿÊëÿÊéÿËéÿÈéÿ•Êèÿ’Ççÿ™Êéÿ¢Îéÿ ËçÿˆÀãÿm´ÝÿvµÝÿk­×ÿM—Êÿ,d¢ÿ/o¦ÿb¸êÿ|Àêÿ‘Çìÿ—ÈéÿˆÁãÿt¸Ýÿt·Ùÿ½ßÿ‚¼Ýÿj°Òÿk­Îÿq¬ÌÿRœÀÿA‘ºÿ)€¯ÿk¢ÿg¢ÿuªÿw­ÿu©ÿ °ÿ‚³ÿ+–Êÿ-—Éÿ,™ÍÿJ§ÔÿW¬×ÿZ«ØÿY©ØÿUªÚÿ.–Ëÿu°ÿu²ÿt±ÿh±ÿ?¤Ùÿk»äÿwÀçÿvÄéÿˆÔóÿ¯èýÿÂðÿÿ”×óÿ7¡Üÿ–×ÿ'Ûÿ*¢ßÿ.¦áÿ6«ãÿ>±çÿF´èÿM´çÿGµéÿEµçÿ>²çÿ@³èÿ:²èÿ1¯çÿJ¶èÿtÅìÿÉìÿhÁêÿc¿êÿtÃêÿtÄêÿ_»çÿa¼çÿnÁêÿc»çÿc¼éÿe¼éÿX¸æÿmÁéÿsÁèÿg¾éÿe»èÿU·çÿf¿êÿvÅíÿwÇîÿzÈïÿ†Íðÿ|ÆîÿŒÍîÿ†ÎðÿƒËðÿxÈïÿ†ÏòÿvÇïÿ‰Ññÿ‹Ññÿ‡Ïñÿ•Öõÿ­áúÿ¸çüÿ“ÏóÿM­ãÿJ®àÿa·ãÿ|Àçÿ’Êìÿ”Ëìÿ‡ÇéÿŒËêÿ‘ËëÿŠÅçÿ†ÅçÿÄåÿzÁäÿƒÇèÿÊêÿŽÌêÿ›Òðÿ™Îìÿ‡ÅçÿŽÊêÿ¥Òëÿ©ÑêÿËéÿ¡Ïêÿ§Ðéÿ˜ÉèÿÃäÿ„»àÿ_¨×ÿ:ˆÁÿ$Z•ÿAÆÿf¸ëÿ€¿éÿ…Ãæÿ}¿áÿn¸ÝÿsºÞÿÄáÿ~»Üÿx¸Úÿm±Òÿp°Ðÿs®Ïÿa¤Åÿ?’»ÿrªÿd£ÿw¬ÿ~¯ÿ ±ÿ}°ÿ-‰¸ÿ3¼ÿ7›Îÿ.˜Êÿ)–ÊÿH¦Õÿ\«×ÿ]­×ÿ`­×ÿG¥ÖÿˆÀÿ x±ÿ {³ÿw³ÿs¸ÿI¬ÞÿoÃêÿ|Êîÿ“Ùôÿ•Þùÿ^¹çÿE©Ýÿ”Öÿ“Ùÿ,¢Þÿ5¨áÿF³çÿFµéÿJ·êÿG¸ìÿJ¹ìÿJ¹ìÿK¹íÿIºëÿC¸êÿG¹êÿ>¶ìÿ:¶ìÿ^¾ìÿrÅìÿwÈîÿ]¿êÿgÂëÿzÇëÿ†Êíÿ|Æéÿg¿çÿnÃëÿuÄëÿ|ÆìÿyÅêÿf¾éÿb½éÿ|Äëÿ|ÅìÿnÁêÿe¾êÿkÂìÿkÁëÿuÆíÿ|Çîÿ{Çîÿ}Èîÿ”Ôòÿ‰ÏðÿËïÿfÂîÿoÃíÿiÁëÿ˜Öóÿ ×óÿ•Òñÿ{Äìÿ“Ñóÿ ×÷ÿdµæÿB©ßÿW°ãÿx¿çÿ‡ÆçÿƒÅçÿ‚ÅéÿŒÉêÿ‘ÉìÿŽÉêÿ†ÅåÿŒÇèÿÊéÿ|Ãæÿ†Æèÿ—Éëÿ–ÍíÿÌìÿ¤ÓïÿŸÎëÿ‹Èéÿ§Òíÿ­Óëÿ¡Îéÿ Ïêÿ¡Îèÿ‰Âäÿu¹áÿf²ÜÿU¦ÓÿBÂÿD’Èÿ]³èÿ~¾êÿŸÐïÿ”Èèÿ~¿áÿw½ßÿ€Âãÿ•Éçÿ{¹Ýÿg°×ÿ]ªÓÿq°Òÿo®Ïÿ`£Æÿ0Џÿ o¨ÿu¬ÿ&ƒµÿ$„¶ÿ2йÿ:¼ÿ0޽ÿ*Œ¼ÿ(@€ B“Òðÿ—ÒïÿØóÿ­àöÿ¤Úóÿ¥×ðÿªØðÿ¤ÙðÿœÕïÿžÖîÿƒÎêÿ¦ÙîÿÒíÿ«Ùðÿ±Þóÿp´Úÿ m°ÿi³ÿ%ƒÀÿRšËÿN›Ìÿ"ŠÄÿR¤ÔÿšÊéÿk±Úÿ„¼áÿ„Àãÿ…Áãÿ^®ØÿU©Öÿd²ÝÿW­Úÿg³Þÿl¶áÿ[±ßÿp»åÿe·âÿV±ßÿv¿åÿŠÆèÿyÀæÿn»æÿe¶ãÿ#‡Éÿ'‡Éÿ0ŒÊÿ'„Æÿp¹ÿ|¼ÿy¸ÿo±ÿNŸÌÿ€¼Ýÿ•ÅâÿÂàÿ’ÄáÿŒÂßÿ‚¾Ýÿƒ½ÜÿŒÀÞÿ|¼Üÿp¶Ùÿp´Öÿl±ÕÿŒÊíÿ¬×ñÿÔñÿ–Óòÿ«Úôÿ²Üôÿ³ÝôÿšÔðÿ”Óðÿ‘Ðìÿ–Òîÿ­ÛðÿªÙðÿ®Ýôÿl§Ñÿ[§ÿ^¬ÿc²ÿt»ÿ5ŒÆÿ5Èÿ‹ÆÿO¢Ôÿ¡Îîÿ‡¾äÿb­Ùÿz¹ßÿt¹áÿs¶àÿ[©ØÿˆÀæÿ½äÿ{½äÿt»åÿV¯ßÿmºäÿmºäÿq»äÿ€Áæÿ‚Âæÿ…Âçÿ{Áèÿ/Íÿ‚Æÿ#‡Éÿ)‡Éÿ7‹Ëÿ+ƒÄÿy»ÿm³ÿ t¶ÿb«Óÿx¶ÙÿxºÜÿ€½Þÿ‡Àßÿ‰Àßÿ„½Ýÿ€»Üÿ†¾Þÿ¾Ýÿj³×ÿj±Õÿl±Öÿ£ÓñÿªÖòÿªØòÿ•Ðïÿ—Óòÿ¡ØóÿÖòÿÍíÿ“ÐîÿœÔïÿ´Üñÿ¦Øðÿ¡×ñÿRÁÿA”ÿO¡ÿb¯ÿ t¹ÿnºÿw¿ÿ~Áÿ‚Áÿ,ŽÉÿŒÃçÿ‘Æéÿc¯ÚÿT¦Öÿq·áÿ…Àæÿ}¸áÿÃçÿŠÂçÿÅèÿ~Áçÿ_³áÿp»äÿ…Äçÿ‡ÄèÿˆÆèÿy¿åÿ~ÂèÿQ¡Õÿ^¯ÿ`±ÿ^°ÿ`°ÿ*…Ãÿ)ŒÆÿ&ŠÅÿ xºÿ*‰Àÿ€·ÙÿyµÙÿe±Õÿv¸Úÿr¸Úÿn·Úÿu¸Úÿx¹Úÿz»ÜÿzºÛÿl³Öÿd®Ôÿi°Öÿ Òðÿ™Ëíÿ§Õòÿ¯Ûôÿ¤Øòÿ¢Ùôÿ¥Øòÿ¢Õðÿ±Úòÿ²Ûðÿ·ÝñÿŽÅäÿE€¶ÿ.„ÿ?•ÿU¥ÿf±ÿ tºÿmºÿ]´ÿuÀÿ€Áÿu¹ÿ„»âÿ¥Òïÿ_¯ÜÿP§Øÿ{¾åÿ•Éêÿ˜Êëÿ—Êëÿ”Éêÿ”Êìÿ”ÌíÿÃèÿz¾äÿ…ÅçÿÈéÿ‡ÆèÿŒÌíÿ‰Êìÿ?—Ðÿ Àÿ_¬ÿY§ÿQŸÿY¤ÿ]¦ÿ h«ÿo­ÿD™Çÿo±Ôÿf¯Ôÿf°Õÿl³Öÿd°ÕÿS¬ÓÿW®Õÿb±×ÿc±Öÿh±Õÿc¯Óÿd¯Ôÿk±Õÿ™Ñïÿ¥Ôñÿ®Øòÿª×ñÿ­Öñÿ£Òïÿ£Óïÿ¬ÖîÿŸÐìÿ™Æàÿs¡Àÿ _Žÿ[ÿ)mÿ@…ÿZ¢ÿs·ÿz½ÿ&Åÿ+‰Êÿ4“Ìÿxºÿ[¦ÿ;‰Áÿ¨Õòÿqºäÿu¼åÿ˜ËìÿœÍíÿ™ÌìÿšÏîÿ£Ôïÿ£Õðÿ¡Ôðÿ’Íìÿ‘Íìÿ˜ÏîÿŒÉëÿ|Èëÿ…Éëÿ7{¹ÿ ]¨ÿ(‹Äÿ|»ÿe«ÿY£ÿPÿMœÿN‘ÿTŒÿ2„µÿg°Óÿm±Óÿc­Óÿc°Ôÿa°Ôÿb¯ÓÿVªÒÿU©ÑÿY¬Òÿ]­ÓÿS¨Ïÿa®Óÿh²×ÿj²àÿ—Ðïÿ£Óðÿ£Ïíÿ¤Ñíÿ¥Ñìÿ­ÕìÿÏëÿˆ¿Üÿ1Zuÿ!ÿÿÿÿ ÿ2Uÿv±ÿ6’Êÿj´Þÿ.~¸ÿ X¡ÿTžÿRŸÿTŸÿTšÍÿd¶áÿe¸ãÿŠÇëÿŸÐïÿ•Ïîÿ›Õñÿ¦Ùòÿ¬ÜóÿÍíÿ„Éëÿ‹Ìíÿ{ÅëÿjÂëÿzÊîÿ#}ºÿT£ÿ[§ÿW¡ÿ m­ÿ r´ÿ\¥ÿTŸÿQ—ÿTŒÿZ“ÿ?‘Áÿx¸Ùÿtµ×ÿm±Õÿd²×ÿ^±Õÿa°ÕÿX«ÒÿP§ÏÿO§ÏÿV«ÑÿP¨Ïÿi³ÖÿnµØÿ s»ÿ1…Åÿy´Þÿ¦Óîÿ¨ÔíÿŸÏëÿ˜Êçÿ~µÖÿ3Jÿ ÿ444ÿ•—–ÿ²´³ÿœžÿEGFÿÿ6HÿOšÇÿ iªÿE’ÿK–ÿP›ÿTžÿSžÿV¢ÿ*‘Ìÿ>§ÜÿvÁéÿ“Ðïÿ¦Úòÿ¬Þôÿ£ÛóÿžÖñÿÆèÿ‚ÉêÿÇëÿÐïÿvÇíÿ6–Ëÿ n¯ÿ p±ÿ l¯ÿc¨ÿP“ÿVÿP™ÿK’ÿQ‹ÿWÿn¥ÿ]®Õÿj´×ÿd±Õÿb°Öÿ]°×ÿ[±Öÿ\°ÖÿMªÒÿN¨ÑÿP¨ÐÿN¨ÐÿU­Õÿk·ÚÿqºÝÿ(ŒÈÿt¹ÿh²ÿyºÿ0†ÁÿR Ðÿ@ŽÄÿ"NÿÿTVUÿÆÈÇÿÏÑÐÿÑÓÒÿàâáÿÚÜÛÿwyxÿÿ9ÿ5ƒÿH–ÿMšÿRÿSÿU¡ÿX£ÿ+’Ðÿ:¤ÜÿaºåÿŒÑðÿªßöÿ¦Ýõÿ–Ôðÿ‹ÍìÿÉëÿ{ÊíÿÐñÿwÄèÿA™Íÿ(ˆÁÿ&„¿ÿ €ºÿ*…¾ÿ4Äÿ x³ÿ[ ÿVžÿ\¡ÿ\ŸÿU“ÿ~³ÿ]°×ÿY­ÕÿU­ÕÿW¯ÖÿP¬ÕÿG©ÓÿG¨ÒÿA¦ÐÿA¤Îÿ?¡ÌÿW®Õÿv¿àÿg·Üÿd¹ßÿ8–Íÿ4Çÿ,‡Ãÿt¶ÿn²ÿ3‹Ãÿ/†¼ÿ ÿABBÿ¾ÁÀÿ¡¥¤ÿuywÿhkjÿŸ£¡ÿÝÞÞÿÞßßÿtvtÿÿ9ÿBÿOŸÿQŸÿRŸÿQŸÿd¬ÿD¢ÖÿK­àÿoÆîÿlÇíÿ’Ôðÿ{ËìÿyÈìÿÑðÿ‚ÍïÿZ¶äÿ^¶ãÿB ÒÿFœÍÿGšËÿ=”Çÿ7ŽÄÿ:ÅÿJœËÿU«Øÿ{Êëÿ|ÌíÿtÂæÿg²Üÿ*z´ÿ(†¹ÿX®×ÿU®ÖÿL«ÕÿP¬ÖÿM«ÕÿF¨Óÿ?¤Ïÿ5 Ìÿ+™ÇÿN«ÓÿŒÎèÿ§Ûïÿm¼àÿc¹áÿS¤Óÿ/ŽÆÿI™Íÿ2ˆÀÿ q³ÿ.ˆÀÿq¦ÿÿŒŽÿÊÌËÿgiiÿÿ ÿ...ÿ¿ÁÀÿàâáÿÝÞÞÿjlkÿÿ"CÿOšÿR¢ÿT¢ÿT¢ÿ:ŽÆÿp¼äÿL§ØÿO²âÿZÀëÿoÈíÿxËîÿrÊïÿxËîÿL¬Úÿ/™Ïÿ5Ñÿ<¢ÓÿB£ÔÿI£ÒÿP£ÐÿLžÌÿE™ÉÿT¦Óÿ‹×õÿ¦ëÿÿ ëÿÿ®îÿÿ·îÿÿ”ÙõÿxÇêÿbµÜÿQªÕÿF©ÓÿJ¬ÖÿH©Ôÿ<¥Ðÿ1 Îÿ%˜Éÿ2žÌÿvÅäÿ Ùîÿªßñÿ‚Ççÿj¼ãÿX£Ðÿ;”Èÿk­ÖÿC”Æÿ p³ÿ$„¿ÿX€ÿÿŸ£¢ÿÎÐÏÿACBÿÿ:\ÿÿ½¾¾ÿàâáÿàâáÿÚÜÛÿ_a_ÿÿ+Nÿ`¥ÿc¬ÿt·ÿ[±Ýÿb¸àÿE©Øÿ•Íÿ?ªÜÿsËðÿˆÔòÿjÂèÿV³àÿHªÚÿA¦×ÿ.ŸÒÿ6¡ÒÿH©ØÿP­ÚÿU¬ÙÿQ©ÖÿO§ÕÿrÃèÿªêþÿ¥éüÿ çüÿ³îýÿ²íýÿªêüÿ±êýÿ£âùÿÉéÿ\±Ùÿ?¦Óÿ9¢Ïÿ(šÉÿ#˜ÉÿC¨Òÿ|ÈåÿšÙïÿ¥Üðÿ¨Ýñÿ†Èæÿq¿âÿ^¦ÑÿW£Ðÿp²×ÿ>‘Äÿs¶ÿC—Êÿ4t ÿ ÿŽÿÞßßÿ“•“ÿÿÿUUUÿàáàÿàâáÿàâáÿàâáÿØÙÙÿVWWÿ ÿ =]ÿ t°ÿ,‘ÉÿgºãÿXµßÿF­Ûÿ;¦Õÿ’ËÿG®Ýÿh¿èÿh¼äÿtÁæÿqÁçÿ{Ãçÿb·àÿO®Ûÿ]´ßÿv¾ãÿl¹àÿeµÞÿnºâÿšÜöÿ²ëýÿ›âùÿœâùÿ°ìýÿ®ëýÿ¶îýÿ¸îþÿµìþÿ´ëýÿªâøÿŒÑíÿ|ÈçÿzÆæÿ–Õîÿ«âõÿ³åöÿ£Ýòÿ›ØîÿÉæÿt¼ÝÿzÁáÿ_¨ÒÿbªÔÿa«Ôÿ/ˆÀÿ q´ÿ=–Èÿ(€ºÿ ÿgihÿ±µ³ÿáââÿÒÓÓÿÌÍÌÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÕ×ÖÿKLLÿÿJkÿV¥ÐÿÉêÿi¿äÿd»ãÿZ¶àÿ6£Óÿ*›ÑÿzÆêÿ™Ôñÿ˜Òîÿ“ÏíÿŒÌëÿ~ÆçÿvÂæÿÅæÿÅæÿyÀåÿn»âÿb·áÿ•Úõÿžá÷ÿ™ß÷ÿ§åûÿ¥åúÿ®êýÿ³ëýÿ·ëýÿ¬êýÿ«êýÿ¨êýÿ¹îþÿÂðÿÿ»îþÿµëûÿ®å÷ÿ²ãõÿžØïÿŽÍçÿŒÇãÿlµØÿj¶ÙÿEÌÿHÌÿV¥Ñÿ|ºÿo²ÿ.Ãÿm¯ÿGÿÿ€„ƒÿ¹¼»ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÒÔÓÿBBBÿÿ3m‰ÿ‡Êåÿ…ÍëÿÈéÿ`·ßÿIªØÿ3ÑÿJªØÿ’Óðÿ¤Üõÿž×óÿ†ËìÿÊëÿ‡Íìÿ‰Íìÿ–ÑîÿŸÔðÿ‡ÇêÿfºåÿŒÓðÿ×òÿ–Ûôÿ²éüÿ¥ãøÿ¥åúÿ¯èûÿ¯èüÿ¢æúÿ˜ãúÿŠÜøÿ“áùÿ¬éüÿ®èûÿ§ã÷ÿ®ãöÿªÞòÿ Ôëÿ©ÕêÿžÏçÿa®Óÿb®Òÿ1”ÈÿAšËÿ@—Éÿe­ÿ x·ÿ|¹ÿMŸÿA›ÿ%Fÿÿ‚ÿ´·¶ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÏÐÏÿ787ÿ (ÿ\¥ÿƒÈäÿuÅåÿl¼áÿa´ÝÿNª×ÿE¤ÒÿL©Öÿa¶Þÿ`µàÿ1˜Ðÿ;¡×ÿqÆëÿ’Òïÿ‘ÑïÿÏîÿn½åÿ[µáÿÓðÿ‰Ïîÿ–Ùòÿ Ýôÿ˜×òÿ›Üôÿ›Ûôÿ™Ýõÿ”Ýõÿ†ÕóÿrËðÿvÏòÿŒÙõÿšÞõÿžÝôÿœÙñÿÕïÿ©Öìÿ«Øêÿ–Êäÿg­ÐÿV£ÇÿD›ÍÿHÎÿ!ƒ½ÿY§ÿi°ÿaªÿ>•ÿM¡ÿ\¨ÿ+Jÿÿ|}ÿ¯²±ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÊÌËÿ-.-ÿ,2ÿcŸ·ÿzÃâÿs¾âÿj¸Þÿ^³ÛÿR¬×ÿP©ÕÿO§ÔÿF£Ñÿ:Îÿ1™Ìÿ8žÑÿ7žÐÿ*˜Ëÿ+˜Íÿ"•ÌÿK®ÝÿxÆêÿ{Èêÿ’ÓïÿØñÿ—ÕðÿŒÑîÿÓïÿŒÓðÿ’ØòÿˆÒðÿpÇíÿkÆíÿtÉíÿ…Ñîÿ–ÕïÿŠÍêÿËèÿÐèÿŸÐæÿ£Ðæÿf©Íÿ$²ÿBšÍÿ<–Ëÿj±ÿZ¨ÿ_¬ÿi¥ÕÿHŠÄÿ V¤ÿX¨ÿd¯ÿ,Kÿÿx|zÿ©­«ÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÆÈÇÿ$$$ÿ.4ÿl©Âÿl¹Ûÿf¶Ûÿa³ÛÿP©ÔÿX¬Öÿ[®ØÿU«ÖÿX­ØÿD£ÒÿA¢Óÿ6Ðÿ3›Íÿ> Òÿ; ÔÿL®ÝÿtÅêÿuÄçÿ’Òîÿ™Óðÿ“Ïìÿ“Îíÿ‰Ìëÿ€Éêÿ†Îìÿ‰ÐíÿwÈêÿƒÍìÿÐíÿ€Çèÿ{Ääÿ‚ÄãÿÈäÿ–ËãÿœÌâÿ›Ëâÿa§Ëÿ uªÿžÒíÿ€Àãÿ;‰Åÿ$l³ÿ]¨ÿ‡¿åÿŒËìÿR›Ïÿc®ÿ!{Àÿ~Ãÿ9Vÿÿtwuÿ¦©¨ÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÁÃÂÿÿ07ÿ`£Àÿc³Ùÿ`±ÙÿV«Öÿ]®×ÿj´Úÿ^°Ùÿ]±ÚÿR¬ØÿT¬ÙÿZ¯ÜÿY®ÛÿU¬Üÿ=£Õÿ:¢ÕÿrÄêÿqÂçÿ‡Îëÿ’Ïìÿ—Ïëÿ“Íëÿ‡ÇèÿÅåÿ…Èèÿ†Èèÿ…ÈçÿŽËéÿÍêÿ†Çæÿu½ßÿ‚Ãâÿ ÏæÿÅßÿ‹ÄÞÿ‰ÂÜÿ{µÔÿ^¤Æÿ©ÜòÿšÙòÿ—ÖòÿÎíÿkµÞÿfµáÿÆìÿn½êÿ]¯ãÿaµçÿR¯âÿÎïÿA[iÿ ÿorpÿ¢¥¤ÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¼¾½ÿÿ08ÿc¦Åÿf´Ùÿb´ÛÿV¬ÖÿF¡Ïÿ]°Øÿf¶Ýÿi¸ßÿnºáÿU­ÚÿT¬ÛÿF¥×ÿ0›Ïÿ!ÇÿO«Ùÿi¾åÿ}Ææÿ•ÏëÿÊèÿ„Ååÿ}Àáÿv»Þÿx»ßÿs¹ÞÿwºÝÿ†Ââÿ„Ããÿ„ÃãÿˆÃãÿ†Âàÿ›Ëäÿ¤Îäÿ”ÅÞÿ•ÄÝÿz³Ñÿt®Ìÿ­ÝòÿšÙòÿ–Õñÿ…ÍðÿyÊðÿkÂìÿm¾ìÿj»êÿe·éÿN«âÿqÂêÿ”ÔñÿÑïÿ:[jÿÿhljÿž¢ ÿÜÝÜÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿµ·¶ÿÿ1:ÿ]¦Çÿm»ÝÿiºÞÿbµÜÿm»Þÿi¹Þÿh¸Þÿoºàÿ]²ÜÿI§Öÿ@¡Òÿ7›Ïÿ!ŽÆÿ‹Äÿc·àÿÆçÿ„Ççÿ…Æåÿ…ÄãÿxºÝÿk²Øÿc®Ôÿk°Õÿe¬Óÿ}¸ÚÿrµÙÿ‡ÁáÿŽÇåÿŠÅäÿÆâÿÍäÿšÇàÿÀÚÿa£Äÿe¦Åÿ¬ÜòÿžØóÿ‘Óòÿ…ÎðÿmÃíÿV¶èÿR¯åÿL­âÿFªàÿc¼éÿ‡ÒðÿÔðÿ•Ôñÿ‹Íîÿ<^oÿÿbddÿ›ŸžÿÚÜÛÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ­¯¯ÿ  ÿ2<ÿbªËÿg¹Ýÿr¾áÿzÀáÿs½ßÿt¾áÿf¶Ýÿ\°ÛÿV¬ÙÿW«ÙÿP§ÖÿP¤Ôÿ!‰Ãÿ3”Éÿ{ÅæÿÆæÿŠÈåÿx¼Ýÿm±×ÿX¨ÑÿY¦ÎÿV£ÌÿNŸÉÿa§Îÿb¨Ïÿj¬Ñÿ‚ÀßÿŒÆäÿ”ÊæÿŠÁßÿu³ÕÿY ÆÿM—¾ÿ]ŸÀÿ¦ÜõÿˆÏðÿkÁìÿuÅïÿxÅîÿa·èÿc´æÿW®âÿ;¦Ýÿ{Ìïÿ—ØóÿŠÒïÿ•Õñÿ”ÓïÿƒÌíÿ>bsÿÿ\^\ÿ˜œšÿØÙÙÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¥§¦ÿ ÿ4?ÿ[¨Êÿm»ÞÿwÀáÿt¿áÿq½àÿc¶Ýÿ`²Ûÿe²Üÿ]­ØÿP¥Ôÿ_¬×ÿ$„Àÿf­ÿa²Ùÿ“Îëÿ‹ÆäÿnµÙÿc«ÓÿN ËÿC˜ÅÿA–Ãÿ;’Âÿ(‰¼ÿ3ÀÿMžÊÿc«Óÿf¬Ôÿ^«ÓÿJžÌÿE—ÆÿM“Àÿ\œÂÿY›½ÿ¥Üõÿ”Öóÿp¿ëÿ]µçÿo¼éÿ[µæÿT°äÿLªàÿI¯ãÿ…Ôòÿ•Úóÿ‡Óñÿ…Ñðÿ‚ÌíÿwÆìÿa¿êÿ0awÿÿTVUÿ•™—ÿÕ×Öÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿáâáÿœžœÿÿ'•ÆÿG™ÇÿW ÈÿA޼ÿ?Œ¸ÿO”»ÿªàøÿ¢Ü÷ÿšÕõÿ„Çïÿj¼êÿe¸èÿZ²åÿL«áÿB®âÿ}ÓóÿÛôÿ…ÓòÿyÌïÿeÄíÿY½èÿQºçÿ^Àêÿ6hÿÿLNMÿ’–•ÿÓÔÔÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàááÿ’”“ÿÿ,@Iÿy¸Ôÿy¿àÿ‚Ãâÿ{¾áÿp¹Ýÿ{»Þÿx¸Ýÿ4‘Èÿ yºÿrµÿn²ÿ5‘Çÿr¹Ýÿh±×ÿT£Íÿ:“Äÿ)ˆ¼ÿ}´ÿx°ÿo¬ÿN§Ñÿz¼Úÿb¦Ëÿu¯ÿp¬ÿv°ÿ5мÿ?¹ÿ-°ÿ)€°ÿ&±ÿ«àøÿ¢Üøÿ‰ÏóÿÆîÿl¼ëÿd¸éÿa¶çÿF¬âÿ=¬áÿyÑòÿ†ØõÿuÑòÿkËñÿdÆïÿbÄîÿ[ÀëÿaÂíÿwÌðÿEuŠÿÿEFFÿ“‘ÿÐÒÑÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàááÿ‰ŠŠÿÿ1EOÿ„¼ÙÿÈçÿ{½áÿtºÞÿo´Ûÿ[¥Ñÿ&†Áÿƒ¿ÿ{ºÿsµÿ}»ÿZ«ÕÿBšÊÿ,‰¿ÿ'„ºÿw±ÿl©ÿe¥ÿ`¢ÿ]®Õÿx»Ùÿ]¥Èÿ6Šºÿ _ŸÿN‘ÿp¨ÿy¬ÿ%}¯ÿ$~²ÿ9޽ÿ¢ßøÿ¢ÝøÿŒÑóÿ†ËñÿÆðÿm¿íÿbºêÿI¯ãÿ7©àÿwÏòÿ‰Û÷ÿ|ÕõÿpÐôÿ`ÊñÿjÍóÿuÏôÿ}ÒôÿƒÕôÿ‹ØõÿU„—ÿÿ=>=ÿŒŽÿÌÎÍÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßààÿ}€ÿÿTÿ2ÃÿAžÑÿC ÒÿG£Óÿ<›Íÿ7–Éÿ7–Éÿ8˜Ëÿ8šÌÿ)”Èÿ-’Æÿ-‹Âÿ.‡¸ÿ²ÿGœÈÿ—×ðÿwÀßÿYªÑÿJŸÈÿ9“Âÿ¶ÿ~´ÿ}³ÿYªÏÿÃÝÿƒ»×ÿj±ÑÿÎñÿ’Òôÿ‡ÍñÿÈðÿ‘Ñôÿ„ÎóÿfËôÿZÃïÿmÏóÿªîþÿŸìüÿ–êüÿ—êüÿ”èüÿ’çüÿ“æüÿ’åüÿ–çüÿèüÿ£éýÿ§ìýÿ§ìýÿ‰½ËÿAY_ÿ'('ÿƒ‡…ÿ¿ÂÀÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÚÜÛÿ_a`ÿÿ!J`ÿGžËÿY¬ÙÿS©ÖÿU©ÖÿK¤ÒÿB¡ÑÿI¦ÔÿU­ØÿS«ÖÿU©ÓÿK¨ÑÿB¤Òÿh¾âÿ¤àõÿ˜Õíÿp»ÝÿXªÑÿJ Éÿ@™Æÿ·ÿ}³ÿ†¹ÿq¶Öÿ‹ÀÜÿs¶Ôÿo³ÒÿŸÙõÿ„ÏòÿzÉñÿ×öÿ¢Ùöÿ—Öõÿ{Óøÿ{Ýýÿ ëþÿ«ðýÿ¡îýÿçüÿŽèüÿ–éüÿ™êüÿäüÿ‰ãüÿ’åýÿ–äüÿ™äüÿšåüÿæüÿ™âûÿˆÅÛÿJahÿ ! ÿƒÿº½¼ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿØÙÙÿUVUÿÿ&TkÿR¥Ïÿk´Üÿi³Ûÿ[®Øÿ^±Úÿi´ÚÿyºÜÿj³ÙÿV¬ÕÿN¨Óÿi¾âÿ áõÿ¬âôÿÐêÿk¸Ûÿ[¬Òÿ=˜Æÿ,Àÿ!‡¼ÿ¶ÿ3—Äÿ†ÁÝÿƒ½Ùÿd¯Ñÿ^®ÐÿªÛõÿšÖôÿh¿íÿ„Èïÿ¢Øõÿ£Ýøÿ“Õõÿ€Ô÷ÿœãûÿ•áûÿ‡ÞûÿÞüÿ†ßüÿâüÿ“ãüÿáýÿ„ÛüÿwÖùÿÖùÿÔ÷ÿ‡Ôöÿ|ÎòÿhÃîÿZ¹èÿg·ßÿ˜Çÿ,ŽÁÿ,Àÿ ~·ÿ8™Æÿ†ÁÞÿv¶Öÿi°Òÿh°Óÿ…Ëóÿš×õÿžØõÿŽÑòÿ€Íðÿ‡ÏòÿzÆíÿL«ßÿW¯âÿm¿êÿzÈðÿ“Ñóÿ„Êðÿ^ºéÿ;©áÿ›Ùÿ#œÙÿ#›×ÿ#™Ôÿ˜Ôÿ‘Ðÿ‘ÏÿÌÿ8¢×ÿrÈíÿŠÖõÿ‡Òòÿ'M_ÿÿwzyÿ«®­ÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÎÐÏÿ787ÿ!'ÿT‚˜ÿy¹Úÿw¼ßÿp»àÿm½ãÿo¾åÿh½æÿ‰Õñÿ¤áõÿ²äõÿÐëÿyÁãÿp·ÜÿQ§ÑÿFžËÿAšÈÿ1“Äÿˆ½ÿJ¤Íÿ‚ÀÝÿc²Ôÿy»ÙÿÂÜÿ.—Ôÿ‚ÌóÿƒÌóÿ{Çîÿ”ÓòÿuÇîÿU¶çÿe¹èÿZ¯áÿL¥ÚÿG¤ÚÿJ¥ÚÿF¥ÚÿKªÝÿB¨Üÿ8¢Úÿ ‡Ìÿ€Æÿ …Èÿ}Äÿw¼ÿt¶ÿ%’Éÿ•Ü÷ÿ«éüÿ¬äøÿ›ß÷ÿ›ß÷ÿ3Wgÿ ÿrutÿ¦ª¨ÿßààÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÉËËÿ///ÿ(/ÿ`©ÿw¼Þÿ‹ÌëÿƒÍîÿ†ÌìÿmÀçÿ–×ðÿ¢Øîÿ²ßòÿ…Ëéÿpºàÿd°×ÿO¤ÑÿR¥ÐÿW¦ÐÿDžÊÿ7™ÈÿB Ìÿu½Ýÿj¹Úÿ…ÂÛÿ~ºÖÿ6™ÑÿE£ØÿW´åÿˆÎòÿÏñÿ‚Êïÿm¾êÿj¼èÿY´âÿ;ŸÔÿ1•Ìÿ"ˆÄÿ`«Úÿ“Éìÿ’Êíÿ‰Èíÿ|ÁèÿGžÔÿ t¹ÿl°ÿl¬ÿ {¸ÿgÁêÿ¨æûÿ¥äøÿ§ßöÿ•Øóÿ”Ùôÿ–Ûõÿ*Qdÿÿmonÿ¢¥¤ÿÞßßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÄÆÅÿ&''ÿ -4ÿl§¿ÿ†Êçÿh¿äÿ9£ÔÿxÅæÿ§Üòÿ¶àñÿ¤Óçÿ]¦Éÿ[žÀÿT¤ÊÿG ÎÿU¨ÓÿU©ÒÿP¦ÐÿE Íÿ:šÇÿlºÝÿm»Üÿ{¾Úÿz»×ÿf¯ÛÿN£Òÿ+”ËÿP¯âÿkÀîÿ_ºìÿ]¶çÿh·åÿ^²àÿP¥Öÿ9“ÉÿQŸÑÿÁäÿŒÃæÿŸÍìÿ•Éëÿ†Åéÿ•Ëîÿg¯Üÿ%†Âÿ‰ÃÿH²ãÿsÍòÿ”Ûôÿ©áöÿ¯âøÿ’Ùôÿ—Øôÿ’ÓðÿxÄèÿA[ÿÿfihÿž¢ ÿÝÞÝÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¾À¿ÿ!!!ÿ)2ÿL™½ÿ;–Æÿ:•ÃÿŠ»Ïÿ޲Àÿƒ¨µÿP|ÿ#8Cÿ ÿ(M`ÿQ ÇÿY¬Õÿb°×ÿ]­ÔÿQ¤Ïÿ+‘ÂÿF¨Óÿ|ÅäÿƒÁÝÿr¶ÖÿX­ØÿX©ÖÿAœÎÿEŸÏÿY«Üÿ^¯ßÿM¨Üÿ@¡Ùÿ8žÔÿ+‘Ëÿ'…Áÿa¦Òÿ†»Þÿ}¸Þÿ„¼áÿz¹ßÿr¸ßÿx»ãÿj¶áÿ^¯ÞÿlÂëÿzÏòÿ„ÒñÿšÚóÿ–Øòÿ–Ûôÿ†Òðÿ•Õñÿ—ÔðÿyÂèÿM¥×ÿ4Tÿÿ_baÿ›ŸžÿÛÜÜÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¹»ºÿÿ*2ÿNwÿ>_oÿ&*ÿ ÿÿÿÿFGFÿÿ8EÿfªÎÿg³ÙÿS©ÒÿI¡Íÿ)Âÿ3˜Èÿm½âÿ†Æâÿd´×ÿi´Ýÿi²Üÿf®ØÿU¦Óÿ_ªÔÿt²Øÿg«ÔÿFšÌÿ½ÿq·ÿ.†Àÿi¬Õÿ`ªÔÿY§ÒÿZ§Óÿt¶Ýÿu¹Þÿr·Þÿ]®ÛÿQªÚÿqÇíÿ˜Ýõÿ¥àõÿ­áöÿ ÛõÿÐðÿwÉìÿvÆëÿ{Åéÿl¹ãÿ@ Òÿ w¹ÿ6XÿÿWZXÿ™›ÿÙÚÚÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ²´³ÿÿÿ ÿ/0/ÿQTRÿknmÿ„‚ÿ–›™ÿ¦©¨ÿuywÿÿ7Yiÿ_ªÐÿG¡ÎÿN¤ÏÿT¥ÏÿCšÈÿP«ÖÿxÂâÿc¸ÝÿZ®ÛÿY¬ÙÿS§ÔÿX¨ÔÿX§Òÿ[§Ñÿa§ÐÿF˜Èÿ~ºÿ"€»ÿY¥Ñÿc®Øÿc­×ÿ]ªÕÿP¥Òÿ`¬Õÿf¯Øÿc¯Ùÿd¯Úÿ:šÒÿmÅìÿ³èúÿ¯ä÷ÿ¬äøÿ•Ûõÿ‡ÑñÿŽÐïÿoÁèÿbµàÿ\®Ûÿ7œÏÿ„Âÿh°ÿ BbÿÿPRPÿ•š˜ÿÖØ×ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ­¯®ÿvzxÿŠŽŒÿœ Ÿÿ¯²±ÿÃÆÅÿØÙÙÿàâáÿàâáÿÚÛÛÿHIHÿÿ[žÀÿZ§ÑÿT§Òÿ0˜Èÿ%¿ÿM¨ÑÿyÄâÿ~ÅâÿV«Ùÿi´ÜÿM¤ÒÿY©ÕÿU¦ÒÿQ¢Ïÿ>•Èÿ2ŽÄÿ.‰Âÿ=”Çÿ^«Õÿb¬Öÿi¯×ÿi¯×ÿP¦Òÿ5—Êÿ7’ÈÿEÎÿB›ÍÿL¬ßÿáùÿ¶èúÿ´çùÿ¦äùÿ˜Ûõÿ”ÖóÿÑðÿs¿æÿ]°Ýÿd°Üÿ2–Ìÿ t¹ÿi°ÿ w¶ÿCfÿÿHJIÿ“—•ÿÓÕÕÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÐÒÑÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÈÉÉÿÿ5AÿQ¡ÆÿT¨Òÿ0™Èÿ4•Åÿ3•ÅÿÇãÿ”Îåÿ[®Ûÿh³ÜÿK¤ÓÿS¨ÕÿI¡ÐÿA›Ëÿ@—Êÿ1Äÿ%…¿ÿS¢Ðÿ[ªÔÿW§ÓÿR¦ÒÿI Îÿ'ŽÆÿƒ¿ÿy¹ÿ{ºÿy½ÿlÆðÿ¹íÿÿ·êûÿ®çúÿ âøÿ˜ÝöÿœÛõÿ‘ÐîÿÂæÿb±Ýÿ>Ñÿ4’Êÿ n¶ÿh°ÿo²ÿ‡Ãÿ6nˆÿÿ?A@ÿ”’ÿÐÒÑÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ›žÿÿ)UjÿI¢ÌÿT§Ïÿ;™Çÿ+ÀÿnºÛÿ•ÐæÿJ£ÕÿR§ÖÿM§ÖÿF¡ÑÿL¡ÐÿN¡Ðÿ?˜Ëÿ*ŠÃÿ6’ÇÿCŸÑÿE¢ÒÿD¢Óÿ? Óÿ1–Ìÿ+ÉÿFÐÿG˜Íÿ,…Àÿ t¼ÿbÁðÿ¯ëýÿ¹íýÿ¬çûÿŸáøÿ™ÜõÿšÚôÿ”Ïïÿw¾äÿb±Ýÿ#Çÿ~¿ÿd°ÿc¬ÿ&ˆÂÿnÄéÿ<‘Éÿ*^ÿ!ÿ9;9ÿŒÿÍÏÎÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßààÿVXWÿÿp©ÆÿV¦Ïÿ.‘Ãÿ#‰Àÿ?Âÿ•Ïçÿ>œÒÿI ÓÿO¥ÕÿAžÑÿ?›ÏÿK¡Òÿ9”Êÿ „Àÿ9˜ÍÿF£ÔÿK¦×ÿT«ÚÿL§×ÿE Óÿ_­ÚÿY©×ÿGŸÒÿ:•Ëÿ%ŽÊÿP¸éÿ¦æûÿ æûÿàøÿ ÞõÿÔñÿŠÏîÿ„Èêÿj·áÿQ¨×ÿ!ŠÅÿo¶ÿY©ÿaªÿ[®Úÿf¼äÿ W¡ÿF–ÿ Sÿ)7ÿ111ÿˆ‹ÿÕ×ÖÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿßààÿØÙÙÿàâáÿàâáÿàâáÿàâáÿÏÑÐÿÿ'‘ÿ f­ÿ„Äÿ$•ÍÿÎÿ{Âÿ.Kÿ$%$ÿ›žÿàâáÿàâáÿàâáÿàâáÿØÙÙÿ™›ÿ}€ÿ`baÿ;<;ÿÿtwvÿ‘ÿÑÒÒÿàâáÿàâáÿàâáÿáââÿ[][ÿÿS“ÿZžÿŽÆÿ:§ÖÿÍÿ6™Ñÿ6—Ñÿ:šÒÿAÒÿ8—Íÿ7–Íÿ`´àÿi¸áÿI¨×ÿI¦×ÿF¤×ÿG§×ÿD¦×ÿ= Ôÿ<¡Ôÿ;ŸÔÿ4˜Ïÿ4’Ìÿ%‘ÍÿL±âÿjÁéÿ†Éëÿ’Ëëÿ‹Êêÿt¾åÿL©ÚÿG¢Õÿ#‹ÇÿV§ÿ1‹ÿY—ÿ"{¸ÿ+—Ïÿ.žÓÿ‰Èÿp¼ÿk·ÿÿFGFÿ«®­ÿàâáÿàâáÿàâáÿàâáÿ¼¿¾ÿ`caÿÿ ÿ '6ÿ 9Sÿ ÿtwvÿ‘ÿÑÒÒÿàâáÿàâáÿàâáÿÔÖÕÿÿ!=ÿ'ŒÁÿ:¬Üÿ+Ïÿ:›ÔÿQ¥ØÿS¥×ÿG ÔÿJ ÒÿK Ñÿ7”ËÿF£ÖÿqÁèÿi¼ãÿT±ÝÿE¨ÙÿB¦ØÿH«ÛÿD¨Ùÿ?¤Öÿ5ŸÓÿ+˜ÏÿÊÿÊÿ †Èÿ-šÓÿrÁèÿ‹ËëÿŒÇéÿ€¾äÿK¦Øÿ@Òÿ~¿ÿc¯ÿ5„Áÿ7šÒÿ)ÍÿD¨ÜÿO°àÿ…Éÿs¼ÿ_¯ÿ ÿ\_^ÿ¾ÁÀÿàâáÿàâáÿàâáÿàâáÿ¤§¦ÿ566ÿ9FKÿÉïûÿ­ÝòÿMœÍÿ 8Rÿ ÿtwvÿ‘ÿÑÒÒÿàâáÿàâáÿáâáÿ§©¨ÿ ÿeÄêÿK²Ýÿ(ŸÒÿI§ÛÿZ«Ûÿd®ÚÿU§ÖÿT¤ÓÿP¤ÓÿA˜Ìÿ …ÀÿH¢Ôÿ‡ÍîÿŠÑðÿ€ÎïÿÌíÿiÁèÿjÀèÿd½æÿf½çÿ`¹åÿX´áÿO®Ýÿ<£×ÿ “Îÿ’ÎÿE§Ûÿt½åÿo¹áÿT¨Ùÿ4“Ìÿ$’Îÿ4œÓÿÃÿc³ÿÃÿÓóÿžàùÿ•ÕôÿzÉðÿ?šÒÿÿrvuÿÓÕÔÿàâáÿàâáÿàâáÿÞßÞÿ‰‹ÿ ÿ‹¦¯ÿÉìùÿÆëøÿ ÒëÿGÎÿCYÿ ÿtwvÿ‘ÿÑÒÒÿàâáÿÛÝÜÿÿ8^mÿwÉëÿVµßÿ'ŸÓÿ_³àÿ[®Üÿa®ÛÿV§ÖÿJ¡Òÿ9—Ìÿ:–Êÿ0ŽÃÿ.‰Àÿ-Åÿ=™ÍÿEšÌÿg·ÞÿŒÓñÿÔòÿ…Ññÿ…Ððÿ~ÍïÿvÇíÿxÄêÿiºäÿP­Üÿ3›ÒÿˆÈÿŽÌÿ2™Óÿ9œÓÿ€Äÿa´ÿ[¯ÿ j¸ÿvÀÿ*“Ðÿ‹Íïÿ˜Úôÿªäùÿ´êýÿ“ºÇÿÿŠŽŒÿàâáÿàâáÿàâáÿàâáÿÆÈÇÿmpoÿÿµÐÙÿÏéõÿ¿ãôÿÅæõÿp¶ÜÿIžÏÿ"G]ÿ ÿtwvÿ‘ÿÌÎÍÿÿ8Vbÿ|ËìÿlÂéÿQ´àÿ/¢Õÿe·âÿ\¯ÝÿZ¬ÚÿYªØÿH¡ÒÿBÎÿB›Íÿ?˜Éÿ=•Çÿ#‡¿ÿm¯ÿ]¤ÿ€ºÿR¨Ôÿf¸àÿwÆëÿtÄêÿrÃêÿ‡ÏïÿœÚõÿ™×òÿ{Èëÿt¿æÿRªÚÿ*“Íÿg¸ÿh»ÿa¶ÿJ¨ÿY±ÿ€Æÿ<œÖÿ,Ïÿfºæÿ}Èìÿ†Ïðÿ”Øõÿdz‚ÿÿ¤¨¦ÿàâáÿàâáÿàâáÿàâáÿ¼¿¾ÿ}€~ÿÿ2;>ÿ±ËÖÿÄàîÿ¾ãóÿŠÅäÿIÏÿ[©ÖÿG]ÿ ÿsvtÿÿ6T`ÿ~ÊëÿsÂéÿnÀèÿQ´áÿ2¤×ÿY²àÿm¸áÿh³Þÿl²Üÿ`®ÙÿR¨ÕÿT§ÓÿK¢Ïÿ;™Ëÿ'Äÿ s³ÿdªÿ2“ÆÿXªÔÿP¤Ñÿ<™Íÿ1™ÐÿËÿŒËÿ1›ÔÿE¥ÙÿI£Úÿ[¯àÿ? Øÿ2”Ñÿj»ÿm½ÿyÅÿ5˜ÕÿX²àÿtÃèÿ€Èëÿ+ÎÿK«ÞÿnÀéÿwÇìÿÎïÿ->Dÿ;<;ÿµ¸¶ÿàâáÿàâáÿàâáÿàâáÿàâáÿµ¸·ÿ}€~ÿÿ3;>ÿ®ÇÓÿÊãðÿ¿âóÿg®Ùÿa®Ùÿg´Üÿ*Pbÿÿ:YhÿˆÌìÿuÀçÿ}Çêÿ†Îîÿd¼åÿ<¦Ùÿmºãÿeµáÿc´àÿt¸àÿv¹ßÿe²Ûÿg°Øÿd®ÖÿE¡Ïÿ‹Áÿb©ÿ t´ÿBžÎÿYªÔÿO¢Ïÿ6›Ïÿ#–Îÿ#”Îÿ †Êÿ~Éÿ ÌÿŽÔÿ+˜ÚÿA§àÿ;§àÿ>¯äÿbÂëÿaÂìÿmÊïÿ{ÏòÿL¯âÿ3¢Úÿ=§ÚÿG®ßÿUµâÿ[¹æÿW»èÿZ‘©ÿÿswuÿáââÿàâáÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ}€~ÿÿ2:=ÿ³ÊÕÿ¼ßîÿx·Ýÿj¯Ûÿtºßÿm¸ßÿhµÝÿ{Áèÿw¿äÿjºáÿvÂæÿÇèÿyÄçÿ`µßÿl»äÿe¸âÿcµáÿ`´ßÿhµÞÿx»Þÿf³Úÿh±ÙÿE¢Ðÿ¼ÿbªÿ„¾ÿM¥ÑÿaªÔÿJ¢Ðÿ.Òÿ—Ñÿ'ŸÝÿ$žàÿ8§åÿO³èÿ9¨ãÿ•Ûÿ&œÞÿR¸éÿ]ÃîÿwÐóÿƒÖõÿˆÔóÿHªÞÿ‘ÐÿN·åÿuÌïÿU¸äÿB«Ýÿ2¦ÛÿA±áÿa¿éÿ<`pÿ ÿLNMÿØÙÙÿàâáÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ}€~ÿÿ09=ÿ£ÂÐÿ–ÇãÿBžÓÿQªÙÿÃåÿyÁäÿc·ãÿ~ÃçÿŽÉèÿ~Ãäÿ‚Ææÿ†Çåÿs¼àÿq¿æÿn½åÿu½ãÿg¸áÿY±Ýÿl·ßÿmµÝÿ^­×ÿ-‰Âÿbªÿo²ÿ.“ÈÿV¨ÔÿP£ÑÿH¦×ÿ<£×ÿ(œÔÿ!œØÿ™Øÿ‘ÕÿŒÑÿ Óÿ Öÿ!›ÜÿrÌóÿ§éýÿ­çûÿKªÚÿ ‘Îÿ”ÑÿA±âÿnÇîÿjÇíÿF¯àÿ9¤Ùÿ-¦ÛÿA±áÿ]»èÿwÈíÿV‚–ÿ*2ÿ ÿ½¿¾ÿáââÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ}€~ÿÿ.=Fÿ|¶×ÿD£ÔÿG¥ØÿY³Þÿ•Ïéÿe²ßÿd·äÿšÑïÿÈèÿ€ÁàÿƒÁàÿuºÜÿe»åÿm¿çÿÄèÿ|Àäÿi¶ßÿa²Ýÿg±Ûÿe²Ûÿx¶ÿ^©ÿz¹ÿD ÏÿW¨ÔÿC¤ÕÿD¨ÚÿC§Úÿ6¥Ùÿ/¢Ùÿ#žÙÿ›ÛÿœÛÿ;ªâÿZ¿íÿ†Ûùÿ’ÚõÿvÆêÿ2ÕÿŒÍÿ›Õÿ@®áÿ[¿éÿuÍðÿ‘Øôÿ|Îïÿ8¥Úÿ' ÙÿA±ãÿW»èÿvÈîÿÑðÿ‡Îíÿ6XhÿÿˆŠ‰ÿÞßÞÿàâáÿàâáÿàâáÿàâáÿ¶¹¸ÿ}€~ÿÿY€•ÿO¨Ùÿ^¯Þÿ\³Üÿ~ÃâÿWªØÿG¦ÛÿŒÆçÿ‘ÉåÿŒÇãÿ~¼Ûÿ€½ÜÿsÀçÿj½æÿ~Åèÿ‡ÆçÿtºáÿQ¤ÖÿjµÞÿOœËÿ\¦ÿk±ÿƒÀÿB ÑÿO©×ÿF©ÙÿL¬ÜÿE¬ÞÿE¯àÿF²ãÿC²æÿI¶éÿP½ïÿqÌôÿÑôÿW·äÿ#•Ðÿ–Ñÿ!œÖÿ4¦ÜÿC­àÿT¸æÿdÄíÿ‹Øôÿ âøÿ¥äúÿbÂêÿ3£Øÿ5«áÿS»éÿ{Ëðÿ—×ôÿŽÒòÿoÅïÿªÜÿ5¨Üÿ7ªßÿB±âÿV¼éÿnÉðÿŒÙõÿ¢åûÿ³ìüÿ¥æüÿhÃêÿ#ŸØÿE´åÿuÊðÿÔôÿƒÌñÿ`½ëÿi¿ìÿ_ž¿ÿ0M[ÿ ÿ ¡ ÿáâáÿàâáÿÛÝÜÿÿQ^ÿžÌëÿ˜Ëêÿu¸âÿ\®àÿ~¿äÿ¡Òëÿˆ¾Üÿ:‹´ÿJ›¾ÿK¤Ðÿy½ßÿ›Íäÿ£Ðäÿo¾åÿw¿åÿ|Áåÿt»áÿF¡Óÿ9œÏÿy¸ÿf®ÿzºÿ%ÆÿF¤Õÿa´àÿn¾æÿj¿éÿuÈíÿˆÑñÿØõÿc¹æÿ&ŠËÿ.•Ìÿ2¥Ûÿ3¤Ùÿ<©ÜÿP°áÿ]¶äÿZ·äÿX¶äÿH³äÿHµåÿ_Áëÿ†ÕóÿšàùÿŸåüÿ«éýÿ²îýÿ¶ïþÿšàúÿD°àÿD¬ßÿZ¸æÿU·æÿf¼éÿi½èÿcºåÿÃèÿŠÉéÿe¢ÿ.:Aÿÿ?Taÿ¡ÊêÿžÉéÿÁåÿk±àÿRªßÿu½åÿ•ÍëÿU Êÿ {ªÿN”¶ÿj¦Âÿ]§Éÿk²Òÿr³Õÿe¹áÿI§ÖÿI¨ÕÿG£ÒÿE Ðÿ@žÐÿu·ÿ~¼ÿ(ÇÿM¥ÔÿV°ÜÿyÁæÿ€ÈìÿˆÏïÿÝõÿŸãùÿvÌòÿ0Ùÿ4¡×ÿP²ãÿX²âÿ@ªÞÿ=«ÞÿO±âÿ[¶äÿV´äÿUµäÿN¶åÿWºèÿoÆîÿ‹ÕôÿšßùÿŸåûÿ«êýÿ³îýÿÅôýÿÓúÿÿ‘ØôÿH®ßÿS³ãÿS´åÿ^·æÿj¾çÿ}Äèÿ‡Èéÿ‘Ëëÿ“Íìÿ˜Îìÿ”Æäÿ’Åèÿ•ÃçÿÀæÿнâÿY¤Ùÿi³åÿx¿çÿÃæÿ3Åÿ/ˆ¼ÿ)}­ÿB†­ÿEŠ®ÿN‹ªÿF¤ÿM§ÕÿF¦ÔÿY¬×ÿV©ÕÿN¦Ôÿ>šÌÿm²ÿ)Èÿ;žÑÿL¬Úÿ]·ãÿqÄêÿœÚôÿµëüÿ§âùÿG¯âÿ‹Òÿ˜ØÿG±ãÿa¸æÿ^·åÿJ²âÿP³äÿW¶äÿTµãÿZµäÿM±ãÿS¶æÿqÃìÿpÅíÿˆÒòÿ˜Ü÷ÿŸâúÿ°ëýÿ³íýÿÈöýÿÙüþÿ²çûÿU³äÿQ²âÿU´âÿZµãÿ{Æêÿ–Ñîÿ‰Êêÿ‡Æèÿ—Íìÿ–ÌêÿŽÇèÿ–Æéÿ•Ãçÿl°ßÿY Ôÿ>–ÒÿO«ãÿuÀéÿr¶ÝÿJŸÌÿJ›Åÿw¬ÿ*oœÿl£ÿL|œÿ/y£ÿS©ÖÿX¬×ÿ\­×ÿ]®ÙÿN©×ÿw³ÿk°ÿ>ŸÒÿL©Úÿ]¶âÿwÇìÿ¡áøÿ’ÛöÿP¶åÿ"œÛÿ•Øÿ—ØÿžÜÿ\ºèÿm¿éÿd¼èÿY¸æÿX·æÿb»çÿaºçÿV¶äÿN²äÿ^ºèÿtÅìÿuÆíÿ„Îðÿ‡Ñòÿ™Û÷ÿ¨åûÿ¼íýÿÄñþÿÄòÿÿŒÒóÿU±ãÿc·åÿc¶ãÿl½çÿ–ÓñÿªÛòÿÓïÿÑìÿ”Ìéÿ›Ïêÿ›ÌéÿÂæÿ‚»áÿY¦×ÿ0ƒÃÿ.’Ôÿk»éÿ~¿åÿz¹Úÿo±ÔÿGšÂÿ1‡´ÿ™Îÿ-ŠÄÿz¹ÿ p³ÿ2Âÿÿ^a`ÿ¾ÁÀÿjlkÿ[^\ÿ»¾½ÿßààÿ{}|ÿÿ$PÿOŸÿPžÿNÿ%x¸ÿMªÜÿa½èÿqÉîÿ‚ÍíÿvÈìÿ‰Ñðÿa¸ãÿQ®Ýÿ@ ÑÿEœÌÿD˜Êÿ;‘ÅÿB•Çÿ]±ÛÿƒÑïÿ‚Ðïÿ}ÄèÿAÂÿFŸÌÿS¬ÕÿKªÔÿN«ÕÿF§Òÿ:¢Îÿ)™ÇÿH©Òÿ–Óêÿ“Ðëÿc¸ßÿO ÎÿE˜ËÿI–Èÿs´ÿ!pžÿ ÿ®±°ÿ˜œ›ÿÿ ÿmooÿàâáÿßààÿoqqÿÿ0\ÿY¦ÿaªÿS§Öÿ_´Þÿ3ŸÔÿP·åÿ{ÏñÿvÊíÿ\¸ãÿ6žÒÿ+šÏÿ7¡ÓÿG§ÖÿO§ÔÿM¢ÐÿP¦ÓÿÙõÿ¨íÿÿªíÿÿ¸ñÿÿ¦çüÿ’×òÿl»àÿJ©Ôÿ>¥Ñÿ/Ëÿ!—Èÿ;£Ïÿ€Êçÿ§ÞðÿžÖíÿq¾ãÿZ¤ÐÿbªÔÿNšÉÿx¸ÿ8|¤ÿ ÿ¤§¦ÿÃÅÄÿÿÿ§©¨ÿàâáÿàâáÿÝßÞÿegfÿÿFmÿ!‡Âÿd¹âÿN°Üÿ6£Ôÿ&šÑÿb½æÿl¿æÿk¼äÿq¿åÿ_¶ßÿJ«Ùÿe·ßÿj¸àÿ`²ÜÿxÄèÿ­éýÿœãùÿ¦çúÿ°ìýÿµîýÿ¸îþÿ°éûÿ–ÖðÿvÄåÿk¾àÿ€Éçÿ¤Þòÿ¨àòÿšÖìÿƒÄâÿv¾àÿZ¦ÑÿaªÓÿ9Äÿw¶ÿ5ÂÿÿiljÿÊÍÌÿÚÛÛÿÖ××ÿáââÿàâáÿàâáÿàâáÿÜÝÝÿZ[Zÿ ÿ/lŒÿÊêÿoÁåÿa¹áÿ1ŸÑÿB§Øÿ–ÔñÿžÖñÿ•ÑíÿˆÌêÿ|ÅçÿƒÇçÿ„ÆèÿoºâÿvÅéÿœß÷ÿšßöÿ¨åúÿ©çûÿµëýÿ°êýÿ§êüÿ§éýÿ¹îþÿ¼îþÿµêûÿ´æöÿ£Ùïÿ–Îçÿ~¾Ýÿe²Öÿ9—ÉÿJŸÌÿ{¹ÿw·ÿt´ÿCÿÿ}ÿÉËÊÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÙÛÚÿNNNÿ$/ÿb•«ÿ„Ìéÿk¼áÿS®Úÿ<¡Òÿa¶Þÿ†Ëëÿm¼ãÿX²ßÿÌíÿ˜ÕñÿŸÖòÿwÀçÿuÄéÿŽÔðÿœÜôÿ¤ßõÿžÞõÿ¤á÷ÿ¡âøÿÜöÿzÒóÿŽÜ÷ÿ¤ãøÿ£àõÿ¤Üòÿ¤Õìÿ¬×êÿ¾Üÿ[¨Ìÿ=—Êÿ9”Çÿc¬ÿl±ÿS¢ÿB˜ÿ$Bÿÿy|{ÿÅÇÇÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ×ÙØÿACAÿ#7?ÿe¡¸ÿs¿âÿh·ÝÿU®ØÿJ¦ÓÿL¦Ôÿ<Ïÿ.–ÌÿC¦ÕÿF§Õÿ<¢Óÿ0Ñÿf½åÿËëÿ“ÓïÿšÕðÿÑîÿŽÓïÿÖñÿ‰ÒðÿmÆíÿpÈîÿ„Ðîÿ“Ôîÿ‹Ìéÿ›Ðèÿ£Òçÿ‹ÁÜÿ2Š·ÿY¨Õÿ-‡ÂÿU¥ÿ i±ÿq«Øÿ(q´ÿU£ÿ(Fÿ ÿrvtÿÁÃÂÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÔÖÕÿ788ÿ,BKÿa¢¾ÿdµÛÿW­×ÿW¬Öÿ_°ØÿY­ØÿJ¦Ôÿ?¡Ñÿ8Ðÿ@¡Òÿ9 Ôÿ^¹äÿwÅéÿ‘Ñíÿ–Ñíÿ”ÎìÿˆÊêÿ‚ÊéÿŠÏìÿ|ÉêÿŒÎìÿ…ÉèÿyÁâÿŠÆãÿ”Éãÿ–Éáÿˆ¿Úÿ-‡¶ÿ§Úñÿ…Æèÿe¦ÕÿM’Èÿ„Æêÿn¸ãÿ?‘Îÿ=šÔÿ#EYÿÿmpnÿ½¿¾ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÐÒÑÿ/0/ÿ(ALÿbªÌÿ`±ÙÿW«ÕÿV©Óÿ`±Úÿa³Üÿb³ÝÿZ®ÜÿRªÚÿ0›ÏÿF¦Öÿk¿æÿƒÊéÿ•ÎêÿŒÉçÿÃäÿ}Àâÿ}ÀãÿÁâÿŒÇæÿ†Åäÿ~¿àÿ‘ÇâÿœËâÿÄÝÿˆ½Ùÿq­Ìÿ§ÛñÿšØòÿÔòÿwÉïÿg¼éÿl¼ëÿc·éÿ[µæÿ‘Òïÿ=Yfÿÿfihÿ¸»ºÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÌÍÍÿ(((ÿ&AMÿh¶Ùÿf¸Ýÿ^²Ùÿi¹Ýÿj¹Þÿl¹ßÿR«ÙÿC¢Óÿ2˜ÌÿŠÃÿS­Úÿ€Ççÿ‡ÈçÿƒÃãÿv¹Üÿh°Öÿh¯Õÿl°Õÿ{¸Úÿ‚¾ÞÿŽÇäÿÆãÿžÌäÿœÈßÿz³Ñÿd¤Äÿ¨ÛòÿŽÒñÿ„ÍðÿlÂìÿX³æÿR®âÿEªàÿzÉîÿÔðÿ“Òïÿ=[jÿÿ_a`ÿ³¶µÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÆÈÇÿ!!!ÿ%BNÿc´Øÿq½àÿu¾àÿs½àÿdµÝÿY®ÙÿW«ØÿU¨Öÿ=˜Ìÿ!…ÀÿuÀãÿŠÊçÿ|½Þÿf®ÕÿU¤ÎÿT¡ÊÿLÇÿQžÉÿ]¥Íÿ½ÝÿˆÃâÿ~»Üÿd¨ÎÿSšÁÿYœ¾ÿ¢ÛôÿzÇíÿc¹éÿm½êÿ\³åÿT¬áÿN²ãÿ‘ØóÿŒÔðÿÒïÿ†Ììÿ6]pÿÿWYXÿ®±°ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¿ÁÀÿÿ*FRÿr»Üÿu¿áÿs½ßÿdµÜÿm¶Ýÿh²ÙÿP¤Òÿ?–Êÿh®ÿ\ªÕÿ’ÉæÿqµÙÿX¥ÎÿC—Åÿ1Œ½ÿ$…ºÿ1‘ÂÿNžÉÿ@•ÆÿDšÉÿF›ÊÿJ—ÅÿJ‘½ÿT˜¼ÿ¨ÞöÿšÖõÿÅîÿhºéÿ^µæÿLªáÿNµåÿŠÙôÿ‡ÕòÿuËïÿdÁëÿTºçÿ/`vÿÿOQPÿª¬«ÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿº¼»ÿÿ3LVÿx¿ÞÿÁâÿv¼ßÿuºÞÿz¹Ýÿ2Æÿ w¸ÿm²ÿEšËÿy»Ýÿ[§Ðÿ<”Äÿ&…ºÿy±ÿ w±ÿe³×ÿgªÎÿv°ÿq­ÿ2‡ºÿAºÿ+€°ÿ+±ÿ©àøÿ˜Ööÿ…Êðÿo½ëÿd¸éÿJ®âÿG±ãÿƒÖôÿyÓóÿhÊðÿ_Åîÿ_ÂíÿkÇîÿCpƒÿÿGIHÿ¥¨§ÿÞàßÿàâáÿàâáÿàâáÿàâáÿàâáÿ³µµÿÿ9O[ÿ‘Éæÿ}¿âÿl³ÚÿPžÎÿ%‡Áÿ½ÿu¶ÿ€¼ÿA™Êÿ&†½ÿ|µÿh«ÿ\ ÿm©ÿw¾Ýÿd©Ëÿ-„µÿYšÿ f¢ÿy¬ÿ'€±ÿ?‘¾ÿ£ßøÿœÚöÿ„ÍòÿƒÉñÿk¿íÿF®ãÿ5§ßÿ‡Ùöÿ…ÛøÿvÔõÿnÑöÿ€×÷ÿˆÙ÷ÿ‹ÚöÿT€ÿ!%ÿ?@@ÿ ¤¢ÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿàâáÿ­¯®ÿ ÿDW`ÿn¶Ýÿ2ŽÆÿ"…Àÿ6–Ëÿ4“Êÿ!ˆÁÿ}ºÿl±ÿe¬ÿ:ŠÿQÿ"hÿL‘¿ÿƒÈäÿPŸÆÿ0ˆ·ÿ|±ÿoªÿ,‰¸ÿe­Íÿj¯Ïÿ›Õôÿ™ØöÿƒÏóÿ‡ÌòÿrÂîÿ5¦àÿ.§ßÿ—äûÿ–æûÿŽãûÿŽâûÿ’åüÿ‘âüÿ–ãûÿžåûÿd’Ÿÿ,1ÿ799ÿ›žÿÜÝÜÿàâáÿàâáÿàâáÿàâáÿàâáÿ£¥¥ÿÿ@Vÿ2”Êÿ?ÐÿD¡Ñÿ<™Ëÿ6•Èÿ6—Êÿ)’Æÿ$‰Áÿ%s¯ÿe’ÿ2{©ÿ‹Íéÿl¸ÚÿN Éÿ5¾ÿ´ÿ|³ÿ[ªÎÿÁÛÿp³Òÿ’Ñòÿ‰Ïòÿ†Ìòÿ•ÓôÿxÍóÿ`ÉòÿƒÚ÷ÿ«ðýÿ˜ëüÿ“éüÿ”èüÿ“æüÿ‘åüÿšçüÿ èýÿ¥ëýÿu¤±ÿ*9>ÿ121ÿ•™—ÿÙÛÚÿàâáÿàâáÿàâáÿàâáÿàâáÿšœœÿÿ!H]ÿT©×ÿZ¬×ÿS¨ÔÿF¤ÑÿS«Öÿ_°ÙÿV«ÔÿI¨Óÿ\¹áÿŸáöÿ‘Ñêÿb²ÖÿJ Êÿ3‘Áÿ}´ÿ‡ºÿy¹Øÿ»Øÿk²Òÿ¥ÛõÿzÉðÿ…Ëñÿ¥Úöÿ–×öÿÙúÿ¡éýÿšçüÿˆãüÿŠäüÿ—çüÿ’åýÿ†ßüÿ‡ÝûÿÝúÿ‘Ýùÿ‰×÷ÿ]›·ÿ(>Hÿ)+*ÿ•“ÿרØÿàâáÿàâáÿàâáÿàâáÿáâáÿ’‘ÿÿ'Nbÿb°Ùÿi´Ûÿj¶Üÿr¹Üÿj´ÙÿZ®×ÿ[³Ýÿ“×ñÿ¬âóÿ‹Ìçÿe²×ÿ?™Çÿ+¿ÿ†ºÿ*‘Áÿ…¿Üÿs´ÓÿS¦Ìÿ¢Ùõÿ–ÓòÿyÆîÿƒÌðÿšÖôÿpÂìÿƒÐóÿ‹Ø÷ÿŠÙøÿ‚Õ÷ÿ„Öùÿ|Ó÷ÿpÌóÿ\Àíÿ]½ëÿY¹çÿJ°âÿ:¥Ýÿ*„²ÿ.Bÿ#$#ÿŠÿÔÖÕÿàâáÿàâáÿàâáÿàâáÿàááÿ…‡‡ÿÿ4Wiÿq¸Üÿq¹Ýÿa²ÚÿgµÜÿe¶ÞÿvÈêÿ§âõÿ¨Ýòÿ‚Ææÿb±×ÿBœÉÿ,ŽÀÿ†»ÿ*‘Áÿ¾Ûÿp³Óÿc­ÑÿsÁìÿœÙöÿŠÏñÿƒÍðÿzÈîÿW±ãÿY¯áÿh»èÿ€Æíÿp¿êÿF­áÿ"œÙÿ’Óÿ”Ñÿ’ÑÿŠËÿˆÆÿ?§ÙÿÑñÿ„ÄÝÿ(ERÿÿ†Š‰ÿÑÒÒÿàâáÿàâáÿàâáÿàâáÿßààÿ{||ÿÿ@bsÿv¼ßÿs½âÿwÃçÿnÀçÿŒÖðÿ¬âôÿ˜Õíÿu½áÿ`­ÕÿIŸÌÿBšÈÿ)Áÿ=œÈÿw¼Ûÿm¶×ÿ‰ÀÚÿ2™Òÿf»éÿ€Éðÿ‘ÑñÿnÁëÿe¹èÿW¯àÿ;Óÿ-‘ÌÿQ¦Ùÿs¼åÿg¸äÿ@ ×ÿ€Ãÿp·ÿk®ÿ‡Áÿ“Ûöÿ°éûÿ¡Ýõÿ”Öîÿ/KVÿÿ…ƒÿÍÏÎÿàâáÿàâáÿàâáÿàâáÿÞßÞÿprqÿÿInÿŠÏíÿlÁæÿg»âÿ¢Úñÿ³ßñÿ…ÉèÿfµÜÿQ¦ÑÿO¤ÐÿU§ÐÿF Ìÿ>žÊÿlºÜÿs¼Úÿ}¼Öÿ[ª×ÿ7™ÍÿM¬ÞÿoÁîÿcºêÿd·æÿY°Þÿ@›Ïÿ@•Êÿ‰¾âÿšÊëÿ˜ËìÿÉëÿu¶áÿ)†ÁÿˆÃÿW»èÿ“Ûöÿ¨áöÿ¡Ýõÿ“×óÿÑìÿ?Pÿÿ|~ÿÊÌËÿàâáÿàâáÿàâáÿàâáÿÜÞÝÿeffÿÿ8lƒÿ0•Éÿ‚Èçÿ©Òâÿ]~‹ÿ-5ÿ$ÿ0`yÿWªÔÿ^®ÖÿY©Óÿ2”ÄÿO­ÕÿÄàÿw¹Öÿb±ÚÿW§ÔÿFžÎÿ]¬Úÿ_­ÜÿB Öÿ-•Íÿ‚ÁÿWŸÍÿ{¶Ûÿv´Ûÿx·Þÿv¹ßÿr¹áÿ]¯ÞÿpÅìÿ…ÔòÿšÙòÿšÙóÿˆÔñÿ‡ÏîÿÐîÿe´Ýÿ.Gÿ ÿwzxÿÆÈÇÿàâáÿàâáÿàâáÿàâáÿÚÛÛÿ\]\ÿ ÿ  ÿ ÿÿÿ=>=ÿLOMÿ ÿ9bwÿb°ÖÿM¤Ïÿ9—Æÿ=ÌÿxÂâÿk¸Ùÿb±Üÿ_­ØÿW§Óÿ\¨Òÿk­ÔÿPœËÿy¸ÿ%‚½ÿb©Ôÿ_ªÕÿR¥Ñÿe®ÖÿqµÛÿg²ÛÿJ£ÖÿsÈìÿ¬ä÷ÿ¯ã÷ÿ”Øôÿ‚ÍîÿuÄêÿl»äÿRªÙÿƒÀÿ+Gÿ ÿorqÿÂÄÃÿàâáÿàâáÿàâáÿàâáÿ×ÙØÿ^_^ÿJKJÿdgfÿ‰‹ÿ©¬«ÿ»½¼ÿÏÑÐÿSUTÿÿU™»ÿO¤ÐÿG Íÿ7–Åÿc¶ÛÿtÀáÿZ­ÚÿZ¬ÖÿS¦ÓÿT¦ÑÿKËÿ6Äÿ)…¿ÿNžÍÿd®×ÿi¯×ÿ]ªÕÿ<šËÿ=–ÊÿGÎÿH¦Øÿ˜Ü÷ÿ¸èúÿ©äøÿ”Ùõÿ”ÔñÿyÃèÿa²ÝÿN¥Õÿ}¾ÿi¯ÿ2Mÿÿhkiÿ¾À¿ÿàâáÿàâáÿàâáÿàâáÿÔÖÕÿÃÅÄÿØÙÙÿàâáÿàâáÿàâáÿàâáÿØÚÙÿ&'&ÿ!?MÿV¨Ðÿ6›Êÿ*ÁÿX­ÔÿÎæÿY¬ÙÿY«×ÿO¦ÔÿJ¡ÏÿCšËÿ0Äÿ1ÄÿT¥ÒÿR¦ÒÿL£Ñÿ2”Éÿ#ˆÃÿ)‚¾ÿs¶ÿS²ãÿ¸îÿÿ¶êûÿ¢âøÿšÝöÿ™ÖòÿƒÄèÿY­Úÿ2”Ëÿs¸ÿf®ÿ€¼ÿ#Laÿÿaecÿ¸»ºÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ¾À¿ÿÿ)QdÿT§Ïÿ2“Ãÿ?›Éÿ’ÎåÿCŸÒÿM¤ÕÿD ÑÿFžÏÿHžÏÿ)‰Âÿ8–ËÿD¢ÓÿJ¦Öÿ@ Óÿ>œÐÿR¤ÔÿEšÎÿ!‚ÁÿG­áÿªêüÿ«èûÿ à÷ÿ”×òÿ’Ñïÿx¿åÿP§Öÿ¿ÿb®ÿ h¯ÿf»âÿ5‡ÀÿBÿÿZ\[ÿ´·¶ÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿ…†…ÿ ÿY–´ÿ/‘Ãÿhªÿa§Ðÿ-‘ËÿA›ÒÿI£ÕÿAÑÿ7”Ëÿ"‡Ãÿ@ŸÒÿL¥ÕÿR©Øÿ]®Ûÿ_®Ûÿ[¬ÚÿS§ÖÿDžÒÿO²ãÿÛöÿ‹×òÿ†Ïíÿ‚Êëÿo¾æÿeµßÿ@žÑÿ xºÿM¡ÿ \§ÿ@ŸÒÿ ]¦ÿzºÿD`ÿÿ˜››ÿàâáÿàâáÿàâáÿàâáÿÜÝÜÿÃÆÅÿ¬¯®ÿœ ŸÿרØÿàâáÿàâáÿÝÞÞÿ9:9ÿ>Mÿs®ÿP—ÿ*ÆÿˆÊÿ3•Ðÿ6˜Ñÿ?›Ñÿ+ÈÿBŸÒÿZ®Úÿ?ŸÒÿD¢ÔÿOªÙÿT«ÙÿN¨ØÿH¥ÖÿJŸÓÿD¥ÚÿxÍïÿzÊëÿÄçÿ€ÅèÿY²ÞÿP©Ùÿ0”ËÿX§ÿ5‰ÿNšÿ¾ÿ ËÿˆÊÿ6Xÿ!""ÿ®²°ÿàâáÿàâáÿàâáÿ³¶µÿkomÿHJIÿÿOQPÿ•™—ÿרØÿàâáÿàâáÿÅÇÆÿÿ*Mÿy¶ÿ0£Ôÿ1—ÑÿG Õÿ@œÒÿEžÒÿ<—ÌÿN§Øÿl½äÿS®ÜÿD¥×ÿA¤Öÿ?¥Öÿ: Óÿ3›Ñÿ%Êÿ‰Çÿ"–Ñÿd¹äÿ“ÎìÿÊêÿf´ßÿA Ôÿ{½ÿP¢ÿ'v´ÿ,‘Êÿ=§Úÿ‰Êÿgµÿ%ÿ@A@ÿÀÃÂÿàâáÿàâáÿàâáÿ‚…„ÿÿ%)ÿ'NdÿÿOQPÿ•™—ÿרØÿàâáÿáâáÿŽÿ ÿL´ßÿ0¢ÓÿI¦Úÿ`¬ÚÿV¦ÖÿR£ÓÿHžÏÿ*ŒÄÿT«Ùÿ‚ËìÿxÈëÿrÄéÿe¾çÿ_¹ãÿZ¶âÿP°ÞÿE¨Úÿ,™Ñÿ ”ÏÿE§Ûÿv½äÿe²Þÿ9˜Ïÿ$Êÿ+Êÿ s½ÿ0ÌÿÖôÿƒÌîÿc´äÿÿX[YÿÔÖÕÿàâáÿàâáÿÏÑÐÿVXXÿÿËîúÿ¸áóÿ-XrÿÿOQPÿ–š™ÿÙÛÚÿáâáÿ>@?ÿ#>IÿjÂçÿ1¢Õÿ`²àÿ[­ÛÿX¨ÖÿEžÐÿ:—Ëÿ4Åÿ.‹Áÿ+Äÿ1ŠÁÿVªÖÿƒÍíÿˆÒñÿ‡Ññÿ‚Îðÿ‰ÎïÿuÂèÿN«Ûÿ%’ÍÿŒËÿ)ÍÿÄÿV®ÿ]²ÿuÀÿ:›ÕÿÒñÿ ßöÿ´êüÿÿx{zÿàâáÿàâáÿàâáÿ´·¶ÿ454ÿ:BFÿËèôÿÅæõÿˆÄäÿ(VrÿÿOQPÿ™›ÿ>@?ÿ&:BÿX”®ÿ^ºäÿ5¥×ÿ_´àÿd²Ýÿa®ÚÿR¦ÕÿK¢ÐÿGžÍÿ:–Èÿ~¹ÿ^¥ÿ(ŠÀÿWªÕÿV¬ÙÿJ©ÙÿB¦Øÿdºãÿq¿æÿlºåÿX­Ýÿ&ŠÊÿ^´ÿd¹ÿp¾ÿ;•Ðÿcµâÿ8˜Óÿeºæÿ{ÈìÿˆÉåÿÿŸ¡¡ÿàâáÿàâáÿàâáÿÉÌËÿx{zÿ ÿR]bÿÈãñÿ®Øîÿ]¨Öÿ1^vÿÿÿ&9Bÿ[‘ªÿzÅéÿnÁèÿ;§Ùÿg·áÿfµàÿnµßÿp¶Ýÿd°Ùÿa­Õÿ?ŸÍÿx¶ÿk¯ÿDŸÍÿY¨Òÿ9™Ìÿ"”ÍÿŠÊÿ€ÉÿˆÏÿ*•×ÿ;¡Üÿ7¡Üÿ?©ßÿV·åÿnËïÿnÅëÿG¬Þÿ>¥ÙÿO±àÿ^¹åÿdÀéÿÿbdbÿáââÿàâáÿàâáÿàâáÿÉËÊÿx{zÿ ÿQ]bÿÅãñÿx¸Ýÿj²Ûÿj±ÕÿžÓÿmºáÿ}Ãåÿi¹ãÿŽÊêÿ‚ÄäÿƒÅäÿx¾àÿi¼åÿwÀæÿv½âÿa³Þÿh´Ýÿe±Úÿy·ÿc«ÿ/’ÇÿU¨ÔÿF¥ÕÿB¦Øÿ/ Öÿ!œ×ÿ”Öÿ‘Ôÿ%žÝÿOºêÿÛøÿÔñÿ0œÔÿÏÿ;¬ßÿkÈíÿzÍïÿK¯Þÿ( ØÿD²ãÿeÀêÿ_¨ÿ#6?ÿÿÀÁÁÿáâáÿàâáÿàâáÿÉËÊÿx{zÿ ÿ\®ÿI¥×ÿX±ÜÿÄãÿP©Ûÿ„Åéÿ‘Éæÿ‚ÀÞÿz»Ûÿl½æÿrÀçÿ„ÅçÿkµÞÿ^®ÚÿX£Ñÿ bªÿqµÿ;šÍÿN¨×ÿH©ÚÿF«ÝÿC­ßÿA¯âÿA²æÿNºìÿxÍòÿiÃëÿ8¢×ÿ—Òÿ%žÖÿA¬àÿY½éÿÒòÿ¥ãùÿƒÓòÿ6¦Ûÿ7¬àÿeÃìÿ’Ôòÿ…Ììÿ,L[ÿÿ‹‹‹ÿßààÿàâáÿàâáÿÌÎÍÿ,-,ÿ;R`ÿd±Þÿ¾âÿ‡ÆäÿV©Ôÿ}¼àÿžÎæÿŒÅàÿ…¿Üÿa¸ãÿg¹ãÿu¾äÿk·ßÿPªØÿj­ÿ\§ÿ†ÁÿR§ÕÿS®Üÿc¸ãÿ^ºæÿ_¾ëÿdÆñÿRºíÿ>™Óÿ:¥Úÿ–Ñÿ/¡×ÿ=ªÝÿ8ªÞÿD²ãÿaÂìÿ…Õôÿ¦æúÿ¶íýÿ†Óñÿ*£ÙÿQ¹èÿ‹ÓóÿwÇïÿb»êÿ9_rÿ ÿCDCÿÌÎÍÿáâáÿ>@@ÿ*7@ÿ«Ãÿt·âÿ€½âÿ™Íèÿd¨ËÿHžÆÿv½àÿ”Èâÿ—Èàÿhºãÿ|Àåÿ€ÁäÿX­Ùÿ%Æÿ`ªÿrµÿ-”ÊÿX­Üÿd·âÿh¿èÿwÈíÿÐòÿP±äÿ%ŽÌÿ&“Ïÿ+ ÖÿDªÝÿV²âÿUµãÿG±âÿD´äÿjÇîÿÛöÿ¡åûÿ²íýÿ·ïþÿgÄëÿ3¦Üÿd½éÿ]»éÿi¾êÿi½çÿ`™¹ÿ1GSÿÿ+,+ÿ+8Aÿu”ªÿÄæÿ[­Þÿo¹ãÿšÎêÿG”½ÿB³ÿ[¦ÊÿvºÚÿŒÄßÿe·àÿR«ØÿN§ÔÿE¡Ñÿ,Åÿ u¶ÿ(ÆÿK§Öÿjºâÿ€Èëÿ‘Õòÿ á÷ÿlÅìÿ,–ÓÿD¨ÛÿN¯àÿ;©ÝÿL°àÿYµãÿVµäÿN´äÿWºèÿ{Ìðÿ—Þøÿ åûÿ­ëüÿ¿òýÿÅóþÿb¾æÿM¯àÿT´äÿcºæÿsÁçÿˆÈéÿÉéÿ}§¿ÿH_mÿ„¯Îÿ”Âçÿ¸àÿY§Þÿv¾èÿsºàÿ-‰¾ÿ/‚°ÿI‹°ÿO±ÿL‹¬ÿO¨ÖÿO¨ÕÿWªÖÿN§Ôÿz¹ÿ †ÂÿC£ÔÿT±ßÿwÇëÿ¡ßöÿ™ÛõÿH°âÿÓÿ-¦Þÿ`ºæÿ]·åÿN³ãÿX¶äÿY¶äÿR²âÿQ´äÿnÂëÿzÊîÿ’×ôÿ¡âúÿ°êýÿÄóýÿÖûÿÿ~ÉîÿM¯áÿV³âÿnÀçÿ˜ÓïÿÌëÿÉéÿ—ÍêÿÆçÿ–Äçÿr²ßÿH—Ðÿ@žÚÿo½èÿn´ÚÿKŸÉÿ'€²ÿ8uÿUœÿ)sžÿE¢ÒÿVªÖÿ]®Ùÿ=šÌÿf«ÿ4—Ïÿ]´áÿŽÓñÿ›Þ÷ÿR¸æÿ›ÚÿœÜÿÝÿ>¯âÿuÄêÿe½èÿY¸æÿ`ºçÿ`ºçÿY¶åÿ^ºçÿvÅìÿ€Ëîÿ}Ëïÿ–Øõÿªäúÿ»ìýÿ¯åûÿg»çÿm»æÿp½åÿ~ÅéÿŸÕðÿœÑíÿ–Íêÿ–ÌéÿœËèÿ„½âÿ^¥Óÿ,}¼ÿ`±äÿƒÀåÿ}ºÛÿi¬ÏÿF•¼ÿ1w¢ÿh›ÿt¨ÿ,˜ËÿM§ÔÿY«Øÿ„¼ÿt´ÿ^µâÿ‡ÐñÿvÆíÿ8¨àÿ-¦áÿ>²çÿEµéÿ<²èÿI·éÿqÅìÿmÂêÿh¿éÿk¿éÿg¾éÿpÀéÿc½éÿsÅíÿƒÊïÿ€ËïÿyÉïÿ‘ÓòÿœØõÿ„Éðÿ_µãÿŠÇéÿŽÉêÿ‡ÅçÿƒÅçÿËëÿ–ÍëÿœÍêÿ£Ïéÿ‰ÃäÿTÍÿK“ÉÿŒÈìÿ€¿áÿ~¼Þÿp±ÓÿUÂÿsªÿ ~²ÿ+‰¸ÿ((P @“ÐîÿžÖñÿ¤Úóÿ«Ùñÿ¥Øðÿ˜ÓîÿÑìÿ¦Øïÿ±ßóÿe¦Ñÿc­ÿy»ÿH—Êÿ-Èÿ¼âÿy·Þÿ¼áÿq¶Ýÿ^¬Ùÿlµßÿm·àÿ`³ßÿi¸ãÿe¶âÿƒÂæÿ~ÂçÿTªÜÿ!†Èÿ/‹Êÿ#~Âÿw¸ÿ t´ÿe¬ÓÿŠÁßÿŒÂßÿŠÀÞÿƒ¼Üÿƒ¾Ýÿo¶Øÿl±Õÿ¡Ðïÿ¥Ôñÿ™Óñÿ£Ùóÿ™Óðÿ˜Òïÿ®Ûñÿ¨ÚñÿS‹¿ÿI›ÿc°ÿn¹ÿw¾ÿ‚Âÿo³Þÿ‡ÁåÿW¨×ÿyºãÿ‚¼ãÿŽÃçÿˆÃçÿg·âÿt¼ãÿ…ÄçÿƒÄçÿ|Áçÿy¾ÿa±ÿkµÿ'„Áÿ|¼ÿ(‡Àÿw´×ÿm´×ÿu¸Úÿo¶Ùÿv·Úÿy¹Úÿk²Õÿf¯Ôÿ Óðÿ©Õñÿ«Øòÿ£Õðÿ¦Õïÿ¬Øðÿ—Éäÿ$Hkÿ [ÿMžÿ oµÿyÀÿ}Äÿt¹ÿ:ˆÂÿ–Ìíÿf³ßÿ’ÇéÿšËìÿšÍìÿÐîÿÉëÿŠÇéÿ‹Éêÿ‡ÊìÿL“Éÿ{¼ÿg¯ÿS ÿRŸÿXšÿ1†¶ÿm²Ôÿf¯Óÿc°ÔÿW¬ÒÿX¬Òÿ^­Óÿ\«Ñÿf¯Ôÿ`ªÚÿ™Ëìÿ­Öðÿ«Ôîÿ¦Õîÿ2L]ÿÿÿÿ ÿJiÿLœÎÿi«ÿSÿSŸÿH•Êÿb·ãÿ’ËìÿÒïÿ¥Ùòÿ¡Öðÿ‡Éêÿ‰ËìÿyÉîÿ>—ËÿW£ÿ\¤ÿg©ÿW¡ÿL“ÿQŠÿ8‹¹ÿt·Øÿi±Õÿ_±Õÿ]®ÓÿP§ÏÿP§ÏÿT©Ðÿmµ×ÿ|¿ÿ'}¾ÿN™Ìÿg­×ÿ8Rÿÿ{~ÿÒÓÓÿÒÔÓÿuwvÿÿ(PÿC“ÿP›ÿP›ÿb¬ÿ9¤ÛÿxÅëÿ§ÝôÿŸØòÿ‹Íìÿ}ÉìÿËíÿQ¦Õÿ~ºÿ{¸ÿ"~¹ÿd¤ÿ WžÿX™ÿ[–ÿL£Íÿ]°ÖÿX®ÕÿO«ÔÿJ©ÒÿB£ÎÿK§Ïÿh·Ûÿg¸Ýÿ<—Íÿ.ˆÂÿq³ÿ%ƒ½ÿ ÿosqÿŸ£¡ÿCEEÿÿàááÿy{yÿÿ,[ÿPŸÿNÿ0ƒÀÿR¯ßÿcÂêÿ~ÍîÿyÊíÿ~ËíÿQ¯ÝÿD¥Öÿ@›ÍÿE™Êÿ>’ÅÿIÍÿyÈêÿ‰Õòÿ†ËëÿP ÍÿQ©ÓÿJ©ÓÿIªÔÿC§Ñÿ1žËÿ*™ÈÿtÁáÿÖíÿhºáÿNŸÍÿZ¤Ðÿ)ƒ½ÿj—ÿ ÿµ¸·ÿ_b`ÿ ÿ011ÿàâáÿÞàßÿlnnÿ ÿ¤ÕÿrÃèÿŠÐîÿ›×ñÿ’ÓïÿÔðÿ×ñÿzÌîÿmÇîÿ†Ñðÿ“Ôîÿ‘Îéÿ¤Óéÿ—Éáÿ@“½ÿe°Ùÿ"{»ÿT¤ÿažÐÿ?…ÁÿG„ÿ"9ÿ+,,ÿŸ¢¡ÿÞàßÿàâáÿàâáÿàâáÿàâáÿÖØ×ÿ@A@ÿ+BLÿ[Ÿ¾ÿ]°ØÿV«Õÿ^¯ØÿU¬×ÿE£Óÿ? Ñÿ@¡Òÿ:¡Ôÿg½æÿ‚Êëÿ—Òíÿ“Íëÿ…Èéÿ†ËêÿƒËêÿ‡Ìëÿ‡ÊéÿzÁáÿÈãÿ”ÈáÿÅÝÿC”½ÿ¨ÝòÿˆÉêÿh¬Ùÿs»äÿxÀêÿO¡ÚÿB¹ÿ*BNÿ'('ÿ™œ›ÿÝÞÞÿàâáÿàâáÿàâáÿàâáÿÓÕÔÿ787ÿ&?Kÿ\¤Çÿ\¯ØÿUªÔÿb´Ûÿh·Þÿ\°ÜÿL¨Øÿ*–ËÿB¤ÕÿvÄçÿÍéÿŠÇæÿ}Àáÿv»Þÿw»Þÿ…Ááÿ†Äãÿ„Ââÿ–ÉâÿšÉáÿŒ¿Úÿs®Ìÿ§Ûòÿ•Öòÿ€Îñÿa¼êÿ\´çÿU±åÿxÈìÿ„½Ôÿ.EQÿ!""ÿ”˜—ÿÜÝÜÿàâáÿàâáÿàâáÿàâáÿÐÑÑÿ/0/ÿ%@Mÿf¶Ùÿl»Þÿp¼ßÿnºßÿ[°ÚÿK¦ÕÿBŸÐÿ$ŒÄÿ_²Ûÿ…Éçÿ‚Ââÿl²Øÿ^©Ñÿ\¦ÎÿeªÐÿp±ÕÿŠÄâÿ’ÉäÿŽÂÞÿn¬Íÿ[žÀÿŸÙóÿtÄìÿqÁìÿa·çÿQ«àÿP²ãÿÖòÿŽÓðÿ†ÂÞÿ+FRÿÿŽÿÙÛÚÿàâáÿàâáÿàâáÿàâáÿÌÎÍÿ$%%ÿ'COÿp¹Üÿv¿áÿj¹Ýÿb²Úÿc°ÙÿX©Õÿ*†Áÿ/ŠÂÿÌèÿzºÜÿZ¦ÏÿC˜Åÿ6¿ÿ/¿ÿJ›ÇÿX¤Îÿ[¨ÒÿOŸËÿK”ÀÿV™½ÿ¦ÝöÿÎñÿo½êÿ`µæÿL«áÿVºçÿŽÚôÿ€ÐðÿlÅìÿW·ãÿ!ETÿÿ…‰ˆÿ×ÙØÿàâáÿàâáÿàâáÿàâáÿÅÇÆÿÿ1ITÿy¾Ýÿx½ßÿtºÞÿx¸Ýÿ2Æÿ t¶ÿ~»ÿwºÝÿb«Óÿ<•Äÿ#„¹ÿ s®ÿA›Éÿh­Ðÿw±ÿx²ÿ?½ÿ4…³ÿ.‚²ÿ¦ßøÿ‘Òóÿ{ÄîÿgºêÿM¯ãÿMµåÿ…ØôÿrÏòÿ`ÆïÿaÄîÿmÆêÿ/N[ÿÿ|€ÿÔÖÕÿàâáÿàâáÿàâáÿàâáÿÁÃÂÿÿ:NXÿ‹Åâÿm´ÛÿJ›Ìÿ%‡Âÿ¼ÿ v¶ÿ/Ãÿ#ƒ¼ÿp­ÿWœÿU™ÿ`«Òÿi¯Ïÿ'~±ÿ]ÿp§ÿ'±ÿB“¿ÿ¡Üöÿ•Öõÿ…ÌòÿxÄïÿAªáÿ7©àÿ‘àùÿƒÛøÿz×øÿ†ÜùÿÝøÿÛôÿ8VaÿÿvzxÿÒÓÓÿàâáÿàâáÿàâáÿàâáÿº¼»ÿÿ0JXÿ6“Èÿ'ŠÄÿ;™Íÿ1‘Çÿ$‰Âÿy¸ÿk°ÿ>ˆÿ!Wÿ+hŸÿ‚ÉåÿS£Êÿ0‰¸ÿz°ÿ~³ÿi¯Ðÿp²Ñÿ“ÑòÿÓôÿˆÍòÿËòÿJ¶èÿ_Äíÿ¤íýÿ”èüÿ“çüÿ“æüÿ’åüÿ›çüÿ¡æøÿC_gÿ ÿorqÿÏÑÐÿàâáÿàâáÿàâáÿàâáÿ³µ´ÿ ÿ@TÿK¤ÒÿM¥ÔÿBÍÿ>ÎÿF¤Ñÿ@ŸÍÿ:˜ÇÿF¡ËÿŠÎéÿwÀßÿN¢Ëÿ0¿ÿ {±ÿ=™Äÿ‹ÁÜÿr´ÓÿšÖôÿzÇðÿšÓôÿ•Öõÿ}Øùÿ¢ëýÿœêüÿ‹æüÿ•èýÿ“æýÿ‹âüÿ‘âüÿ˜âüÿ—ßøÿ@_lÿÿfihÿËÍÌÿàâáÿàâáÿàâáÿàâáÿ«­¬ÿÿ#GZÿe°Ùÿa±Ùÿe´Úÿo·Ûÿ]®ÖÿX±Úÿ˜Ûòÿ¤Ýñÿm¸ÛÿGžÊÿ)Œ¾ÿ·ÿX©Îÿƒ½Ùÿ\«Îÿ¥ÚõÿˆÍïÿ~Èïÿ™ÕóÿzÈîÿ†Óôÿ‹Úøÿ‡Ùùÿ†ØùÿÔ÷ÿoÍóÿ`Ãïÿc¿ëÿU¶æÿ@©ÝÿJcÿÿ]`_ÿÈÊÉÿàâáÿàâáÿàâáÿàâáÿ£¥¤ÿÿ/O`ÿq¸Ûÿk¶Üÿc³ÚÿeµÝÿzÊëÿ­äõÿ˜Óíÿm·ÜÿEÊÿ+Àÿƒ¹ÿ[ªÐÿ{¹×ÿa¬Ïÿo¾êÿ—Öõÿ†Îðÿ~Êïÿ[´äÿY¯áÿg¹çÿp¾èÿQ°ãÿ,ŸÚÿÑÿŽÎÿ‰Ëÿ ‚ÃÿB¨Ùÿ‰ÕòÿFjxÿÿWYXÿÃÆÅÿàâáÿàâáÿàâáÿàâáÿ—™˜ÿÿ9Xhÿy¾áÿzÄçÿrÂçÿ‘Öðÿ®áòÿƒÈæÿe²ØÿK¡ÍÿB›Èÿ(Áÿ^®Óÿq¹Ùÿ…¾Ùÿ=ÓÿY³âÿ„ËñÿxÆîÿh»èÿW¯Þÿ1•ÌÿBšÐÿ¿æÿ€Ãéÿ^¯ßÿ„Äÿj®ÿ„Àÿ‹Öôÿ±èúÿžÜôÿPsÿ ÿLNMÿ¾ÁÀÿàâáÿàâáÿàâáÿàâáÿ‹Œÿÿ=bsÿaºáÿdºàÿ«Þñÿ£×ìÿ\œ½ÿWªÓÿO¥ÐÿW©ÒÿDŸËÿN¨Ñÿt¿Þÿ|¼×ÿ_­Øÿ<šÍÿS¬Ýÿ`´åÿS­ßÿD£Öÿ0Æÿp®×ÿÀãÿŒÂåÿ†Ãçÿn´ßÿBŸÒÿdÂëÿ×óÿ¢Ýôÿ‘×òÿ–ÖòÿCo…ÿÿFGFÿ¹¼»ÿàâáÿàâáÿàâáÿàâáÿƒ‚ÿÿF_ÿ&7?ÿÿÿ ÿ%ÿ7f~ÿa¯ÖÿJ¡Íÿ0–Æÿq¾àÿu»Ùÿc±Ûÿ^ªÖÿV¦Ñÿi­ÕÿOŸÎÿ{ºÿ0ˆÁÿh­Õÿ[§Òÿf®×ÿs·Ýÿ`®Úÿ^¶ãÿšÞöÿªáöÿ“×óÿ|ËíÿwÄéÿg¶áÿTzÿ ÿ=@?ÿ³¶µÿàâáÿàâáÿàâáÿàááÿuxwÿÿ9;:ÿWZYÿ†‰‡ÿ«¯®ÿNPOÿ#ÿS™»ÿL£Ïÿ<˜ÇÿV¬Õÿq¿àÿ]¯ÚÿT¨ÔÿU§ÓÿO¡Íÿ=“Åÿ)…¾ÿU£Ïÿf¯×ÿ`¬ÕÿBÌÿCšËÿEÏÿh¾æÿ´èúÿ­äøÿ•ÙôÿÐïÿh¸âÿVªØÿƒÁÿIwÿ)ÿ687ÿ­±¯ÿàâáÿàâáÿàâáÿßààÿ¹¼»ÿÈËÊÿÝÞÝÿàâáÿàâáÿÝÞÝÿ'((ÿ =LÿM£Íÿ,“ÄÿF¡ÌÿŒÌåÿX«ØÿR¨ÕÿK£ÑÿGÌÿ1ŽÄÿ5‘ÆÿR¦ÓÿM¥Òÿ:šÌÿ(ŒÅÿ*ƒ¾ÿ|¿ÿ‹×öÿ½îýÿ¥äøÿ›Ýõÿ•Ñïÿqºâÿ:šÎÿu¹ÿh®ÿ)r˜ÿ)8ÿ-.-ÿ§ª©ÿàááÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿàâáÿÇÉÈÿ ÿ-QcÿDžÉÿ0‘Ãÿ…Äàÿ?œÐÿL£ÕÿEŸÑÿFÏÿ(‰Ãÿ9™ÍÿK¥ÖÿN¨×ÿI¢ÔÿU§ÖÿHœÏÿ(ŽËÿzÏòÿ§çúÿ›ÝõÿŽÒðÿ…Èëÿb²Ýÿ!ŠÅÿ^¬ÿt¶ÿY¯ÜÿC€ÿ$:ÿ'('ÿ­±¯ÿàâáÿàâáÿàâáÿàâáÿàâáÿÚÛÛÿÜÝÜÿàâáÿàâáÿ•–•ÿ ÿ;¥ÿd¦ÿ<Áÿ#‹Êÿ;™ÑÿBŸÓÿ4”Ëÿ-ÈÿK¥ÕÿE¡ÓÿSªÙÿ]®ÛÿX¬ÚÿS¨×ÿF ÕÿmÅìÿ‡Ôñÿ~ÈéÿzÄçÿ^µàÿN¨×ÿ{¼ÿ?•ÿ U ÿÀÿ}¾ÿ d“ÿ ÿ¯²±ÿàâáÿàâáÿØÙÙÿ³¶µÿ‰ŒŠÿ]a_ÿ—›™ÿÜÝÜÿàâáÿßààÿJKJÿ*?ÿWœÿ#’Èÿ)’Ïÿ<šÒÿ?œÒÿ:—ÌÿK¥Öÿe¸áÿG¦ÖÿB£ÕÿC¥Öÿ=¡Ôÿ5œÑÿ+Êÿ,šÓÿ^¹äÿÌëÿ‰ÇèÿU­Ûÿ3–Íÿ Z¨ÿWžÿ!¼ÿ+›Óÿ ~Ãÿ&Eÿ+,,ÿÂÄÃÿàâáÿàâáÿ³¶µÿ"""ÿÿ ÿ243ÿ—›™ÿÜÝÜÿàâáÿÍÏÎÿÿ(}ªÿ3¦ÖÿK¦Ùÿ]ªÙÿQ£ÔÿL Ñÿ1Èÿ^±ÝÿxÇéÿmÀçÿe½æÿ\·âÿT²ßÿGªÛÿ0œÓÿ “ÎÿJ©Ûÿx¾åÿZ¬Úÿ(Êÿ'‡ÆÿyÀÿ<šÓÿˆÒòÿh¸äÿ *ÿCFEÿÖØ×ÿàâáÿàâáÿ†‰‡ÿÿ¼Ùãÿnš°ÿ,<ÿ243ÿ—›™ÿÜÝÜÿÙÚÚÿÿg·Øÿ7¦Öÿ`²ßÿ]¬ÚÿP£Óÿ=™Ìÿ6’Æÿ0Âÿ&‡Àÿ4ŽÄÿo¿äÿ„Îïÿ‚Îðÿ…ÏðÿŠÏîÿd¸âÿ6œÒÿ!‹ÊÿƒÆÿcµÿY°ÿxÁÿE¡Øÿ“Õòÿ¦ãùÿÿhjiÿàâáÿàâáÿØÚÙÿUWVÿÿ¨ÀËÿ½âòÿL†¦ÿ/?ÿ243ÿ–™˜ÿÿ1O[ÿi¾æÿ:¨Ùÿbµáÿf²Ýÿc®ÚÿS§ÔÿQ¤Ðÿ7—Èÿn°ÿs³ÿO¥ÑÿM¥Óÿ8ŸÓÿ/šÒÿC¤ØÿP¨ÜÿS«Ýÿ!ˆËÿrÀÿ(„ÊÿN¥Ùÿ^´áÿ7šÔÿf¼æÿyÈíÿÿ‘‘ÿàâáÿàâáÿàâáÿ¨«ªÿ,.-ÿCLQÿ©¿ÊÿŒÄãÿHƒ¦ÿ;Iÿÿ2LYÿuÁæÿzÇêÿK®Ýÿj¹âÿb´àÿk¶Þÿm¶Üÿf±Øÿ:šËÿh­ÿ*ŒÃÿ[©Óÿ9œÎÿ•Ðÿ‘Ôÿ’Öÿ#–Úÿ'™ÛÿD°åÿnÌñÿÓóÿ[ºæÿ:¦ÜÿT·ãÿI®ßÿE°àÿ-Q`ÿÿ¸º¹ÿàâáÿàâáÿàâáÿ¨«ªÿ,.-ÿCMQÿ‹®ÁÿZ©×ÿm¸ßÿo·Üÿw¿åÿ|Âäÿ€Æçÿs¾âÿl½åÿs½ãÿdµßÿiµÞÿe±Úÿ!|¹ÿl°ÿCžÍÿP¦Óÿ>£Öÿ'ÕÿšÙÿ•×ÿ•Øÿ/¦áÿ…Ööÿ‰Óðÿ6 Öÿ'›Õÿ]¿éÿoÈíÿ8¥Ùÿ1§Üÿ\»æÿ4Q^ÿÿŠŒŒÿàáàÿàâáÿàâáÿ¨«ªÿ,.-ÿ4HSÿN¦×ÿW°Ûÿ{Áâÿdµâÿ’ËêÿƒÂàÿ{½Ýÿk½åÿzÂçÿz¾ãÿb°Üÿ[¦Óÿcªÿ|ºÿL¥ÓÿI©ØÿGªÜÿ@¬Þÿ<­áÿ=°æÿaÁîÿnÇíÿDªÛÿ$šÔÿ)ŸØÿN¶åÿxÎñÿ¡áøÿdÀèÿ-£ÚÿU»éÿŽÒòÿ@ctÿ ÿNNNÿÓÔÔÿàâáÿàâáÿ¨«ªÿÿl°Øÿuºàÿ~ÁáÿWªÖÿ”ÉæÿÈâÿ„¾Üÿa¸ãÿs½äÿu½ãÿU¬Ùÿq³ÿ`ªÿ1“ÉÿT«Ùÿ^µáÿa»çÿdÁìÿ]Ãñÿ> Ùÿ8žÕÿ'œÕÿ4£Øÿ=ªÝÿ=­àÿV¼éÿ‚Ôóÿ¨èûÿ´ìýÿX»åÿ7ªàÿÎòÿqÄîÿLŒ¬ÿ"8Dÿÿ¯°°ÿÚÛÚÿÿ—Óÿy¿çÿx·ÚÿV¢Èÿ4ªÿ2o™ÿo¡ÿ0šÌÿW«×ÿ>œÍÿp°ÿL¨ÚÿˆÑñÿnÃëÿ3§àÿ0¨ãÿ@²çÿ8°çÿZ¼êÿrÄëÿi¿èÿh¾èÿg¾èÿi¾éÿjÀëÿÉîÿÊïÿÌðÿ•Öôÿœ×öÿd¸æÿ‚ÃèÿŠÈéÿ…Åæÿ‹Èéÿ”ÌëÿšÌêÿ¡Îéÿ{ºàÿA‹Àÿr´áÿ†Ãäÿ{»Ýÿo¯Ðÿ1…´ÿtªÿ&…¶ÿ( @ €˜Òïÿ Øóÿ¨Úòÿ¤×ñÿ“Òîÿž×ïÿ¯ßóÿa¡Ïÿe¯ÿ,…Âÿ1Çÿo´Þÿ~ºàÿzºàÿc¯Úÿnµßÿm·áÿdµáÿj¸ãÿÁæÿzÀçÿ3‘Ïÿ&…Çÿ)„Åÿ u¸ÿ2ŒÂÿ„¾ÞÿŠÂàÿ‡¿Þÿ†¾Ýÿw¹Úÿk±Õÿ¥Óñÿ¤Õòÿ¡×óÿžÕñÿ¨ÙñÿªØïÿMƒºÿE™ÿh²ÿo¼ÿ|ÁÿY¥Õÿ…Áæÿf±ÝÿŒÃçÿ“ÆéÿŒÆéÿu½åÿ‰ÅèÿËìÿc®Üÿ k¶ÿ]«ÿo´ÿr³ÿPžÊÿo´×ÿl´×ÿe³×ÿoµØÿk³Öÿe¯Ôÿ‰Åéÿ°Ùóÿ®Øñÿ­Ùòÿ=Ucÿÿÿ ÿGhÿ4ŠÇÿg­ÿg¬ÿj°Üÿ}ÁçÿžÏîÿ¢ÕñÿŸÔðÿËëÿ‰Íîÿb±Ýÿh®ÿ l¯ÿ]¦ÿJ–ÿTÿSŸÇÿo´×ÿb°ÕÿY¬ÒÿS©ÐÿT©Ðÿf±Õÿ"ÂÿP™Íÿs´Ûÿ%¤Ðÿ(™ÈÿJ«ÓÿœÖíÿuÁäÿ[¥ÑÿNœËÿp¡ÿÿ¶¹¸ÿIIIÿ---ÿ×ÙØÿàááÿqrrÿÿKrÿ^µßÿ9¤ÖÿC­ÝÿuÇìÿc¸âÿP­ÚÿK«Ùÿ`³Ýÿ]°Úÿ—Üõÿ¨ìþÿµñÿÿ·ñÿÿ¢ßöÿwÄåÿYµÛÿj¾ßÿ™Øîÿœ×íÿ{ÂâÿV¦Ðÿ=“Æÿ¼ÿ ÿpsrÿÕÖÖÿÜÝÝÿàâáÿàâáÿÞàßÿfhhÿ %ÿS…šÿc»áÿ6 ÒÿrÀåÿšÔðÿÈêÿ„Êëÿ’ÎíÿvÁçÿ‘Øóÿ¡áøÿ©æúÿ²êüÿ¦èüÿ£æüÿµìýÿ¶éúÿ«Þòÿ“Ëåÿj´×ÿ:—Éÿu¶ÿ i¯ÿ!HÿÿvxwÿÖØ×ÿàâáÿàâáÿàâáÿÝÞÞÿ[\\ÿ,2ÿWާÿ[°ÚÿNªÕÿY¯Úÿ>¡ÓÿZ³Þÿ^´ÝÿU±Þÿ€ËìÿšØñÿ˜×ñÿ•Øòÿ‹ÖòÿrËðÿ‰Õòÿ•ÖïÿÒêÿ¢ÑçÿPžÅÿe°ÚÿqµÿD†ÃÿM‘Çÿ.NÿÿknlÿÓÕÔÿàâáÿàâáÿàâáÿÛÜÜÿOOOÿ!5>ÿMŒ©ÿV«ÕÿY¬ÖÿQ©ÕÿC£Óÿ= Óÿ=¤ÖÿqÃèÿÐíÿ“Îìÿ„Èèÿ…Êêÿ„Êêÿ†ÉéÿÂâÿ”Éãÿ–ÈàÿVŸÅÿ¨Ýóÿ‹Ííÿo½çÿqÀëÿU¬àÿ7ZlÿÿegfÿÐÒÑÿàâáÿàâáÿàâáÿØÚÙÿFGFÿ";FÿP•´ÿ_²Ùÿk¹ÞÿdµÞÿK§×ÿ+”ËÿM«Øÿ†Êéÿ‰ÇæÿvºÝÿm´Ùÿy¸ÛÿƒÀàÿŒÆãÿÌäÿ‘ÂÛÿp¬Ëÿœ×òÿ}Êïÿi¿ìÿQ­âÿ]¹çÿÕñÿGhuÿ ÿZ\[ÿÌÎÎÿàâáÿàâáÿàâáÿÕÖÖÿ=>>ÿ#>Jÿe¨Æÿr½àÿb´ÜÿZ­ØÿN¤Óÿ&‡Áÿx¿âÿƒÂáÿ^©ÑÿKÈÿB—ÄÿT¡Ëÿt·Úÿs·Úÿ\¡ÊÿW›¾ÿ ÚõÿyÄíÿe¸èÿM¬áÿdÁêÿŽØóÿyËîÿ4f|ÿ ÿPRQÿÈËÊÿàâáÿàâáÿàâáÿÑÓÒÿ454ÿ,COÿu»Ûÿs»ßÿu¸Ýÿ5‘Çÿ q´ÿ^ªÔÿo´Øÿ>—Æÿ€·ÿ%‰½ÿ\§Ïÿ"}µÿ0‡ºÿ;Œ¹ÿ5†´ÿ¤ÞøÿŠÎòÿq¿ìÿM¯äÿX¼èÿƒØöÿgÊñÿdÆïÿEwŒÿ$ÿFHGÿÄÇÆÿàâáÿàâáÿàâáÿÎÏÎÿ()(ÿ8JSÿs¶ÛÿGšÌÿ%ˆÂÿ|ºÿ!ƒ¾ÿ!»ÿ ]žÿCŠÿA‘Áÿm´Ôÿ x­ÿd¡ÿ%±ÿE–ÀÿÙöÿŒÒôÿÊòÿC­ãÿR»èÿ˜æüÿ†ÞúÿŒàûÿ’âûÿažÿ*/ÿ>A?ÿ¿ÂÁÿàâáÿàâáÿàâáÿÉËÊÿ ! ÿ9Kÿ0Æÿ@Ïÿ3”Èÿ#‹Ãÿ|»ÿS‘ÿ!YŒÿy½ÞÿX©Ïÿ,‰¹ÿz±ÿ_©Îÿz¸Õÿ‘Òóÿ‡Íòÿ“ÓôÿnÎõÿ“äúÿœíýÿ”êýÿ–èýÿ’æýÿçýÿoŸ¬ÿ&5:ÿ353ÿ¹¼»ÿàâáÿàâáÿàâáÿÃÅÄÿÿ?Pÿ[ªÔÿS©Õÿ_°ØÿX­ÕÿS±Úÿ”Ûôÿ‡ÊæÿM¢Ìÿ$‰¼ÿ‰»ÿ~»Ùÿl²ÒÿŸØõÿ‚Êðÿ–Ôôÿ„ÐóÿÚ÷ÿÞûÿˆÛúÿ×øÿoÎôÿkÈñÿfÂìÿ>аÿ1Bÿ,.-ÿ²´³ÿàâáÿàâáÿàâáÿ¾À¿ÿÿ)ESÿp·Úÿi¶Ûÿb´ÜÿÍíÿ­ãõÿ}ÃâÿHŸÌÿ$нÿ+‘Áÿ|»Ùÿb­Ðÿp¿êÿÓôÿ„Îñÿc¹çÿY°áÿd¶äÿ`¶åÿ>¨ßÿ’Òÿ‡Éÿ~ÁÿC¨Ùÿu±Èÿ*DOÿ'('ÿ«­¬ÿàâáÿàâáÿàâáÿ·¹¸ÿÿ2LYÿyÃåÿpÁçÿœÛñÿžØïÿk·ÝÿO£Ïÿ>™Çÿ;›ÈÿqºÚÿ~¼ØÿE¡ÔÿY±áÿuÄîÿd¹èÿM©Úÿ5’Êÿ}¹áÿ‘Èëÿm¶âÿ%…Âÿ‡Âÿ‚Ññÿ®æùÿŒÃÙÿ*DOÿ"##ÿ¤¨¦ÿàááÿàâáÿàâáÿ¯²°ÿ ÿ&IYÿEy’ÿ;JPÿ!ÿ0<ÿ:u“ÿ[«Ôÿ>œÊÿc¶Úÿ|½Ùÿ_®ÙÿM¢Ñÿ`­ÚÿR¥Öÿ!†ÄÿI˜Ëÿt³Úÿv¶Ýÿ|½âÿ`°ÞÿvÊîÿžÝõÿ”Øóÿ„Ïïÿv¹Úÿ /Cÿÿœ žÿßáàÿàâáÿàâáÿ§¨¨ÿÿÿ243ÿVYWÿ=?>ÿ-7ÿQ—¹ÿ?›ÉÿO¨Òÿr¾Þÿ^¯ÚÿX©ÕÿX¥ÐÿD–Çÿ*†¾ÿ]¨Óÿa¬ÖÿI ÏÿLŸÎÿL¦×ÿ›Üöÿ²çùÿ•Øôÿ}Çëÿ_±Ýÿ$‡Àÿ'@ÿÿ”˜–ÿÞàßÿàâáÿàâáÿ¹»ºÿ¼¾½ÿÏÑÐÿßáàÿàááÿ,,,ÿ ¡Õÿ ‡Èÿr¾ÿaµÿÄÿT«ÝÿœÜöÿ!ÿ_a`ÿàâáÿàâáÿ¡¤¢ÿÿZejÿ‹µÌÿ6Fÿ'('ÿgjiÿ ÿ:btÿ@«Ûÿe¶áÿi´Þÿd°ÙÿZªÔÿ0“Æÿi­ÿBšÊÿH¢Ñÿ&–Ïÿ"”Òÿ;ŸÚÿE¦Þÿ)’Óÿ8—Õÿ\´âÿU±àÿD§Ûÿ_ºæÿ*BMÿ9;:ÿàâáÿàâáÿרØÿvxwÿÿUciÿa¿ÿ-Paÿ ÿ=`rÿ|Æéÿ_·áÿm¼äÿh¸áÿk·Þÿj´Ûÿ#‚½ÿy·ÿT§Óÿ?¡Óÿ šÕÿ—Ùÿ–Ùÿ#œÞÿyÑôÿ{ÏðÿAªÝÿG±áÿ^½çÿ2¤Úÿ;‰«ÿ&£ÔÿAœÍÿJŸÎÿÌìÿ‘Õðÿs¾áÿZ±Ùÿ<£Ïÿ4 Íÿ€Èäÿ„Éèÿ^¨Òÿ1†¶ÿÿ©¬«ÿÿÕ×Öÿàááÿxyyÿÿ?p‡ÿ;¥Öÿd»äÿ‚Èêÿk¼ãÿxÀåÿxÂæÿ¢åúÿ°íþÿºñÿÿ áøÿŽÔïÿžÛñÿ£Ùïÿz¿Þÿ7“Æÿp²ÿ;ÿ.0/ÿ½¿¾ÿàâáÿàâáÿàááÿkmmÿ!&ÿ@tÿS¬Øÿb´ÝÿW°Üÿh¹àÿeºãÿÔðÿÚóÿšÜõÿ„ÕóÿÙõÿžÙñÿ¤Ôêÿe«Îÿh°Úÿ9„ÁÿH­ÿ )@ÿ()(ÿ¶¸¸ÿàâáÿàâáÿÞàßÿabaÿ%+ÿ?{˜ÿU«ÕÿP©Öÿ?¡Óÿ>¤ÖÿÉêÿ’Íëÿ‚Ææÿ€Æçÿ…Èçÿ‡ÅãÿœÌãÿl¬Íÿ¢Ûóÿ}Êïÿb¹éÿY¤Ìÿ1GRÿ"##ÿ°³²ÿàâáÿàâáÿÜÞÝÿTUTÿ/9ÿQ¨ÿk¹ßÿT¬Øÿ6—ÌÿZ­ØÿŠÈåÿh°Öÿ[¦Ïÿg­ÓÿˆÃáÿ„½Ûÿg¦Çÿ˜Öóÿo¾ëÿP¬âÿlÄëÿÅàÿ%DRÿÿ¨ª©ÿàâáÿàâáÿÚÜÛÿIJIÿ&;Dÿ`›¸ÿp¶Üÿ.‹Äÿ.ŠÂÿv¹Üÿ<–Åÿ·ÿKÈÿ4нÿ:½ÿ:‰µÿ Ü÷ÿÈðÿP¯äÿ]¿êÿ|ÖõÿgÆìÿ-LXÿÿŸ¢¡ÿàâáÿàâáÿØÙÙÿ?@?ÿ,AKÿ>ˆ´ÿ$ˆÂÿ¼ÿ}ºÿJÿe ÿi³Õÿv¬ÿu«ÿL›Âÿ’ÓóÿÐôÿbÀíÿyÕóÿ˜êýÿ“çýÿ•âøÿ™Íÿ%…Âÿ|Êíÿ¯êüÿ—×óÿi·àÿxºÿ>\ÿÿhkiÿרØÿàâáÿàâáÿàâáÿàâáÿÙÛÚÿÿ>PÿLšÇÿ.’Íÿ?œÒÿ7–ÌÿH¤ÕÿL¦×ÿS©ØÿBÒÿ`»åÿŽÔðÿzÃèÿD¡ÔÿS¡ÿq³ÿ:Zÿ ÿÅÇÆÿàâáÿ½¿¾ÿ„†…ÿšžœÿÞàßÿ¼¾½ÿÿw¯ÿD ÖÿJ Òÿ?šÎÿ_³Þÿ\¶àÿV³àÿJªÚÿ4žÔÿV°Þÿn¹áÿ*‹Èÿg±ÿG£ÕÿAUÿ+,,ÿ×ÙØÿÞßÞÿ?AAÿ ÿÿ—š™ÿàááÿÿ0|œÿb²ÞÿX¨ÖÿAšËÿ,‹Âÿ2ŽÄÿl½ãÿf¼åÿh»äÿH¦Ùÿ„Çÿr¾ÿ'†Éÿ[±àÿ"05ÿOQQÿàâáÿÑÓÒÿ233ÿ›ÍÿH©Ùÿ5§Ýÿ3§áÿK´æÿZºäÿ3£Ùÿ_ÀéÿyÌîÿ3§Ýÿ/P^ÿÿŒŽÿàâáÿ¾À¿ÿ ÿV“µÿo¹ßÿ…ÃäÿŠÃàÿn¼äÿn¹áÿ"½ÿw·ÿW¯ÜÿpÃêÿfÁìÿ;¢Úÿ9£Øÿ=¨Ûÿ@°âÿzÐòÿ²íþÿtÊíÿY¹çÿ3\qÿÿQRQÿ#$$ÿ6GTÿ{»âÿp±ÔÿV¢ÇÿŠÂÝÿZ¯ÚÿN¥Óÿ~¼ÿA¢Óÿ†ÍíÿzËîÿ9§ÝÿK¬ÞÿM±áÿV´äÿY¹æÿ†Òòÿ­êüÿÅôþÿ`ºæÿ\·äÿc“ªÿ/?HÿSnÿlª×ÿb±âÿY§Ñÿ6€ªÿEƒ¦ÿI¥Óÿ<˜Êÿ)ŒÅÿzÉíÿY»éÿ/¨âÿ7­ãÿmÂêÿe½èÿaºçÿlÀêÿ€Ëïÿ”×õÿ Ü÷ÿr¾çÿÄèÿ”ÍìÿšÎêÿ‘ÆåÿM—Ìÿs¸âÿw·Øÿ6…±ÿu¨ÿ(( ¡Öòÿ¥Øóÿ¢Øòÿ£ÕëÿG{¥ÿh²ÿ*‹Èÿx¹àÿu·ßÿx¸áÿsºãÿt½äÿÄèÿ,†Çÿw½ÿ~»ÿp²×ÿ½Üÿ{ºÛÿl²ÖÿŠÃçÿ©Öðÿ=R]ÿÿ ÿ /Gÿf«ÿ>‹Ãÿ~Áçÿ¥Ôðÿ˜ÐîÿˆËìÿC”Êÿ_¨ÿH•ÿp§ÿl³Öÿ]¯ÔÿS©Ðÿ_®Óÿ.‰Äÿ"Olÿÿ žÿ¹»ºÿ*++ÿ5ÿ `¦ÿ\·äÿ’ÖóÿtÄèÿP«Ùÿ(…¾ÿ=ÄÿG’ÃÿA”ÂÿS­Õÿ9¡ÎÿO¬ÓÿwÁâÿT¢Ïÿÿbcbÿ|~|ÿ»¼»ÿËÌËÿ$$$ÿ8KÿH«Øÿ`¼åÿn¿åÿZ²ÝÿhµÝÿ’Ùôÿµôÿÿ­éüÿÍêÿoÂâÿ–Õìÿ‡Èåÿ6‘Åÿ 9_ÿÿ¤¨¦ÿàâáÿàâáÿÆÇÆÿÿ*HUÿTªÕÿg·ßÿd¸àÿh¹âÿ…Îîÿ ÜõÿßöÿŒÚ÷ÿŸßöÿ§Øîÿp³Óÿh°ÙÿL”Êÿ6LÿÿœŸžÿàâáÿàâáÿÀÂÁÿÿ#DSÿTªÔÿO©×ÿ7Òÿf»âÿ‘ÎëÿÄåÿÃåÿ†ÆåÿšÍåÿu±ÐÿœØôÿoÂíÿZ´ãÿ6Q]ÿÿ”—•ÿàááÿàâáÿº¼»ÿ ÿ-KZÿi·ÜÿL£Óÿ<˜Ëÿ„Äãÿ[©ÑÿIœÇÿf­Ôÿs´Øÿ\žÃÿ™×õÿjºéÿQ³äÿ|Ïîÿ)O`ÿ ÿ‹ŒÿÞàßÿàâáÿ³µ´ÿ ÿ3N\ÿC˜Ëÿ}»ÿB–Èÿj¦ÿ,ƒ·ÿC•Áÿs¬ÿ6Š·ÿ—Öõÿ~ÉñÿV¼êÿŒáúÿˆßùÿ?akÿÿ‚ÿÝÞÞÿàâáÿ«­¬ÿÿBYÿ7–Éÿ%‡Áÿ#i¡ÿhªÎÿA™Åÿ ƒµÿm±Ñÿ“ÓóÿŒÐòÿ‡Ööÿ“ãüÿƒÛùÿtÐóÿ5\lÿÿtxvÿÜÝÜÿàâáÿ¢¤£ÿÿ1Sdÿd´Ûÿ‰ÔðÿŒÍéÿ0‘Âÿ<™Åÿt¶Öÿh¹åÿ~Éðÿ_µåÿX­Ýÿg¸æÿ0˜ÓÿÂÿ8_pÿÿlonÿÙÛÚÿàâáÿ–˜—ÿÿ0Sbÿ(6<ÿ3Tdÿ@¶ÿH¢Íÿx¼ÙÿS§Õÿ^­Ûÿ>™ÏÿJšËÿºßÿj²Üÿd¹âÿŸÞõÿJm|ÿÿadcÿרØÿàâáÿ‹ÿ ! ÿFHFÿ)*)ÿ!;Iÿ@šÇÿh·ÚÿY«×ÿN¡Îÿ3ÃÿR¤ÒÿCœÍÿ4‘Éÿ˜Úôÿªåùÿ~ÆéÿNoÿÿVWWÿÔÖÕÿáâáÿÓÕÔÿàááÿàááÿ ÿ;Jÿ[«Ñÿ9˜Ðÿ=šÏÿ:™ÍÿK¦ÖÿL¤Õÿ=šÐÿÐðÿ’Õñÿd·àÿ g®ÿGoÿ ÿx{zÿàâáÿÔÖÕÿµ·¶ÿÍÏÎÿÔÕÔÿ ÿNsÿBžÕÿB›ÏÿL¥Õÿ]´ßÿW³ßÿI©ÙÿA§Ùÿn»ãÿ?šÐÿf°ÿBœÑÿÿ“–•ÿàâáÿjlkÿÿLNMÿÍÏÎÿ[^]ÿ'2ÿc²ÞÿU§Ôÿ:–Èÿ)‰Àÿ`³ÜÿZµáÿU¯ßÿ0–Òÿ!€Æÿ-ŒÌÿ_¶ãÿÿ¸»ºÿßáàÿ^`^ÿÿ&ÿ/0/ÿ*0ÿ©ßÿG®ßÿ4¥ÛÿhÆìÿ‘ÚôÿI³áÿ.L[ÿÿ²´³ÿ{}}ÿ ÿx»ßÿm³ÖÿÇâÿc´ÞÿBœÎÿ}¼ÿg¹áÿƒÎðÿL°âÿ>¤ÙÿJ®ßÿP´äÿ|Ïñÿ¸ðÿÿ˜ÜôÿP±âÿ1Qbÿÿÿ9Xoÿkµßÿ?·ÿX•¶ÿK¦Ôÿ-ÃÿPªÙÿoÅíÿ<®äÿ<®ãÿkÀêÿaºçÿe½èÿËïÿÜ÷ÿ˜Öóÿp¼åÿËëÿ›Ïëÿ‡¾áÿSžÓÿ{½âÿI“»ÿs£ÿ(  @¦Øóÿ©Üôÿb‚‘ÿ(ÿDoÿE›Ðÿ~½âÿ}»âÿx¾åÿ‚ÅéÿF˜Ðÿlµÿ*„½ÿ{ºÚÿyºÚÿl²Öÿj¯Ûÿ6L[ÿÿbdcÿ ÿ,RÿU¥Öÿ¢ØóÿÐïÿ\¬Ùÿn°ÿWÿ.³ÿ^±ÖÿI¦Ïÿa±Öÿ<“Çÿÿ”–•ÿ¦¨§ÿŠ‹Šÿ ÿ#VrÿmÅëÿe»ãÿJ¤Óÿe´Üÿ•ÙòÿƒËéÿ[µÛÿe»Ýÿ‡Êçÿ7Åÿ ÿƒƒÿÞàßÿàâáÿ€ÿ ÿ2e}ÿh¸àÿh¹áÿ}Éëÿ§ãùÿ£äúÿ”Üöÿ©Þóÿ}¼Úÿi±Úÿ$Gaÿÿ~€ÿÝÞÞÿàááÿtutÿÿ6j„ÿG¥ÖÿM«ÙÿŒÌêÿ}Áãÿ{Àâÿ•Ìæÿz´Òÿ•Õôÿ]¶çÿ9`qÿÿrsrÿÜÝÜÿÞàßÿikiÿ"(ÿI~™ÿ.Åÿd®Öÿ>”ÃÿE—ÅÿRÉÿI“¾ÿ—Õõÿ_¹éÿoÍðÿ@oÿ ÿgigÿÙÛÚÿÝÞÞÿY[Zÿ(3ÿi“ÿ!‚¼ÿbÿU¡Éÿ{±ÿI˜Áÿ“ÓôÿƒÏóÿŒÞùÿˆßúÿEvˆÿÿ\]\ÿÖØ×ÿÜÝÜÿNPOÿ4?ÿJ®ÿ‚ÉçÿhµØÿ*Ž¿ÿo´ÔÿkºåÿrÁëÿTªÜÿl¹äÿ3˜Òÿ$bÿ$(ÿQSRÿÔÕÕÿÙÚÚÿEEEÿ ÿÿ%ETÿ>¸ÿm·ØÿXª×ÿL Ðÿ>”Èÿf¬Öÿ[ªØÿ•ØóÿdŽŸÿ!,ÿCEDÿÐÒÑÿרØÿ~~ÿž¡ ÿ)*)ÿ9Hÿ^¯ÔÿG Óÿ;–ÊÿEŸÐÿDžÐÿCžÒÿ¡âùÿ†Ííÿ_ÿ 2ÿ:;:ÿ×ÙØÿàâáÿÒÔÔÿÞßßÿÿ8Mÿ?œÓÿ@›ÏÿT­ÚÿX²ÞÿI©Úÿ_·áÿY¬Ûÿj²ÿIfÿ ! ÿÙÚÚÿ–˜—ÿ ÿ›žœÿ»¼»ÿÿc²ÞÿQ¥Òÿ+ŠÁÿP¨ÕÿN®ßÿA¤Úÿ0Ïÿ1ÏÿC~œÿÿáâáÿ’‘ÿÿÿÿ+N^ÿr¿æÿa­Øÿ~ºÿB¢Óÿ)ŸÚÿ8§áÿS·äÿWºæÿT¶áÿ#DSÿÿÙÚÚÿ€ÿ ÿq·ÚÿˆÆäÿm»âÿ1Åÿ1ÇÿwÇìÿP³äÿ?§ÛÿA«ÞÿpÊïÿªéûÿe½äÿ%DTÿ ÿÿ9WkÿZ¤Ìÿk¨ÇÿJ¤Óÿ/Æÿi¾çÿP¸èÿE°ãÿc»çÿ`ºçÿ|Êïÿ©ãúÿ‚ÊíÿyÀåÿƒ¯Æÿz°Õÿd¬Ûÿ\¤Êÿ&v¤ÿcrispy-doom-crispy-doom-5.6.4/data/setup.png000066400000000000000000001056131360717211000210210ustar00rootroot00000000000000‰PNG  IHDR€€Ã>aËgAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDÿÿÿ ½§“tIMEá 7 £ãzÀIDATxÚ¼ýçÓæÙy߉}Nú¥;=÷“:§iLèI˜ ‚`¥-iW+ÉÒZÖªìòÊei]ª}a—«üÂïø?Ø*¯E$%Q$%’¸"µ"%"Ò$Ò`0˜œº§ÓïôK'ùÅùÍ+‰ ºö®B¡Ð=yú¾Î¹Î¾A|ígŸÎwˆè)g—Pf„$¡½ÏáíWYž€ëÉP\öÇ™œ}]Lɦ»h­X}ð õñmºÃ×ÀbF»¨¬ÂT¤’H©"‚Pˆb´ Ò·@€âä»iðÝ}„,@ϲÄû†ÐŸ U"iˆÁûS™í#ˆ·†Ð!uFD u K/)QÕѵDWÛ"†H Ÿ?² Ø×’U{(]°¼ùUºåÛÈl†_Fä[BD„Ê‘ç{¤P)ñý© ”š!@@f3œµ·DûD‘\ƒÁ­ïãÛ#l» Y³¸Œ$réÊYòÙUôÖezÛÑ·ÇߢLŽ2;4«ûôÍζ=Ãû@SŸ²¾ûmNo¿R%ùd‚T Ìe2DìÑ¡þîµó3Q”’èlŠT@D‚àj6Ëc¢wD‰ÌöÏ ŠmbÔø¾F*ÅøìãL¯þùöúÕ1®]ƒ#؆à‚wD"D ¢ÇOt–è=18¼ëˆd¾ ²‚`‰ÁŠZB{„ˆ!2‚Ý€ß} ¤0cbD·"ºžàWiPºD‰ aJð¢á1‚T¨b!(Ñ"è1ÕŒl¼.fˆ(°ë»„Ø#¤DªM² †d:ØÑbpÄУG)º h]àMCJÐ%1¾!A‘ˆr\»as|È[¯å‚­­‚!FGï‚÷Q¼¡ñ@ˆ‚%Î6Ø~C¿:¦_o@j"‘èzl·FJ“¾Ÿ¿ÿ7®þ Ddôäդʅ÷=H0&ǶkêÕ†ÐG””“ ¥Áû¼#x *Cè‚b~žÉùÇPÅ„~yל}G –àšt(Bï[¢m¶&¸ 8‹Ì·‘Ù@C„þß@h!x„ʇ/ÚBt " ‘zBp‚[#¤FÛzb ] T }ðÀ!…ÂÌ®‡_‘æè¼B*.¶Ê@ ¸nEô=ª˜£Ì$ež` ±‡h‰Bž`W¡Ðù­Gø`!zb„}ÊBA£OBÅ5ÇØúÑ{|xÛ³:Þ°i;Î_œ£óJO‘zBlµ5Je¡±¶Á‡ˆ÷ÐÖGxÛâ|¤ï-®ëAb8Û!e‘ÿþ¯^ü) &ßBš)Iˆ)e:ÁQ u`uRÓœ¶d„èɧ[ˆ Á¢×ì†-*+(·Ï3:wïÍá»ßBˆ)“¸ Ñ[bˆïñvC°5Q(Ìä*Û'1Ý ¢GH Â) 1¦'#Eúoßí)à‘Ù©J"™m!„&ú™@8bðaУ=„. 6d£]d>Fæ£áŸË!ÔH­ñ݆~}ˆP ý Bú¾ß€ïÑÚ÷0Å6B"„BS„ãÚBç r @á»¶="º6] ß\K°=Ëã{sÈÊQŠm4#‚ëˆBá¼'ÆH×5X×àÚc¢4cí œõØz…õ.=Þ!Pè¬<‹©K„ÐøàÒ& Q¢€,ŸPm9ºsJ®ÁEudÄ…Sz•Òa°+¼=‹)§èbÝGÿ2ÒŒX¼õ9l¿DŠ©Bö€bJóÑ¡¤!¸–èû”Ò|ݽk:!͘"":]zë¥êˆ:>¡ÊTȹ†#Þo Š”=pCá©Ñc€Ð#¤F•³”¢ó àPÚ¤·^iyºåZ‚ÉAæCá ¡[Ó®î „@H8¢«Ðù©5ÂŒR -R¦Š¶à®ÃuKtV"³Ñué?ð=¡«‘2|ø¼Ø ZöøºFŠŽ<(c@*„L—À9”†#ýúm}„³=Íê˜úø>]½Ñ5d¹‡ªæø¾¡_£³œ¬£Sà:ˆ‘Œ ‰9έÀ["†l´Ï™+-·Wkºeƒm× $:¯ðÍY¬ˆA ¥&Æ»DwŒˆAaÊ=Þa{~•Ù•OQß•ÃW~›öðÛDצMg„(¾EØe œ*‰±Rž"FŸ¾\ Ø5Aô©¦j„23¢KÝDp‚/fŽ0ÅP?¸Tì‘(D:hÒŒf‚Àà hAOÓµ4%äS¤™‘Ï÷_ED‰ âÝfÈb[ßCjƒPšHR‚kpÝŠAŠ2æn R¦üçZ†‡6^út1S"*Éäü.øްõBfˆ¼ÄTû8khÚ%]}ˆï–Äèˆ^z‹m–Do@Èjé'H‘Ñp¡AfsÔÿðß<ø3}·B ª †¡¢×/‰®Cg#Ñ­éÖ§HÐeªp}¿BH‰Î&H™#”Aª ¡Jˆ P5%FEð=R—óËT{—iWG¸®!„²˜!Ì Uº Bl‹·'©x1„á–ÇT… 9üºƒ”!†'ôð”.QùšBÐùˆã÷¿ˆkމAû "FÚ,Y%ð¹¤oO‘Æ ÕL–áÛVD¥‡¶O§qlß!ú; K‚IéWPÕ6{þ:Ço~•»wܼ¹r© Ëbšé)Rgið†!FEô©Ê4‹.½ÛÁ@Hïmp„Dm@˜4&s‚«Q&Gšò£ÛC—²4D™C{DèéNîpòöç ý)2ŸümFÄh‰Q"U**U6&x‡ˆ×/‰] ö5..@(T>"ö=½Ð¬–Ç|õåwyãæ!Ÿxd›‡.Ÿ%+ ¤ÎÓÔT)ˆa(. BJ‚ëqíz´O <Á;F»PÍ/cW÷Ñå]ŒQyF”=F2V”³Šb¶Ê¦ip&2Ôÿío?ö3Á¶H‘ ™m¥¹»mf„2y^Ð7‡ôÍ14kiÖ¦m@*™ž ßÎ`Šmbì?ÖÄÂC舱Ò,ÈЂOu‡ RX„ˆLg’*®é–TAŒõPЄ~™Þj¡ ¾Hz÷£Z=›&ÑïªK7@æ(™FÂ1ÆR}*e "Èa¼ 'ÀoîÒ¿Çé­—èÖw‘B¢´¦¨v)gWÓ3  Ä>ÕÈÔ›2µt!ÍW‚oSÆÑu4½àÍwîò[ÿáÛ„Ñe}êGù½ÏýšŽ­Ùdh_Â($)Èaz¤ÉÅ6RW˜Ù%tµ‹ŠnsJ½9Åä*/ÐÚ…FbÃx2£Ú¹†Aé*µ«Ù-…DhE¡­!¢™àÚûß}‡03&ûO¢ôÛÔ‹›äÓÍ"2š€ïÓ›®Œ! ´Þ»Áž¦ê[n!²<ˆ€ Q€PC(rÂh÷¡»Íxjøào¿¼bk¶Î„[ b›zg²t³C^£¤A@*"£KµHi¸%„´tb((Ó)#Ú5AhTu6µfªH-g{áøæ+t‹÷J¢tF9=O1=09È1 ‰["D Q#ÈQù6B•[ƒP8çhš#–5|íÕ^¹eùÑŸú¯yþ…ç1F3™Œù_þ4m]óÔkLg#JU¡Š¼Ãy›.Š©Ð“ÅÛ.ñË;»#²y5%+&H“¡$ŒÔš‘l0Õytµ ²"WÃÍ´R>ÈT ¹‘M‰n…Q ×ã]‹Ð¦Ú§DDÐ`»߃ՂLF|ßÑmŽÐù¢œ!d†Œ±(%† ÚâBô H”‚(ez²m„žsþ±†Àë|p/°[\w‡,®·Ä¸@ɤC™1Á¤ÞÃ;›žŒhÂ"¨ÿûÿáñŸ*GŠˆÒRAjT¹Ô9ÁZìæ„`;¤P(““wÑ…àæëGäYÀdÃ;ž¥Û u‰RУò ñ¾9‘¡ß ÕnšzJŸoˆÎ ²[ûÎ\Úb¼½ƒÉ¦³³l­ Ý¡“:A †6ÒЧ‚QÊ4'PÙÐ-úTGd"¡tê|KèO õ=üê&냷X¼÷5DhÐJ‘OÏ`ò*Ÿ"†y‡PT™Z9_ðíŒÁ…ÄZKç‹:ðµïò￱ä±Oü5þ7ãËÖÖŒ~bŒìíí³wî2¿ûÙ1±a¾5BR§ÙŠkŽ€Êg(] ²1ºÜJ%Lhp݆õé]´pœ=wÉ΃èÑUD±—Öç¡Fèo[„kðý1Ze[DUເÂäöK d‰.·zÂêÎ+ ÅÖ9tnؽb¸ôĆÕ{o}Jʾ¸f…­ÑJ¡ ¶rÊÝLÄÆ€ˆ-"Ô @)gëTäáAD¤*®C Ö9º¬(w.B„¬Übur—Ó÷¾‚m;¢èQ1 tZc‹è:‚’1¶q!X‚©s¤™€ˆø¾&zÐÅœà=®> >z—õ²iÐY‰ÉLZ“‹4˜B3FHIt1µÈJ¬E˜¡Çô^Q×+îX~ï«,âEþæ÷÷øØÇ>6tß þ‡Ÿ<=ô ÏÿØ_à³ÿî71Zðñ‡/"µFË豈à-BKT^&L…O +“i £m]dºÿÒìB6&D—&•Jb@ˆœRçè¨ÇD·FJ‘ziŠt"„à ®Aª’|vŽöèm¢ß% Ô=wƒï¬ïáìzpD¤v-wÀà]®FKh7eÐB#Dƒ8ס¥€Ø§)¦~^Ê™®ÒL^¡E†Ÿa|æf—o0¿|ƒÛ/}–fy“WH‘ ¤Ò(5Æ»ž0Ù¥*„ÈSñè,¶¾‡I…¢žbŽ]â›S–7éå%*73IuC° Bp(Y¥½Q6BŠ9±' ººgµÙðò'üÁK+®=þçù›ÿÕ_f2ãƒçû„øÎw^a½Y²}þc|é[oððõKdí’|s|?8B‰€s5:Ï=ô«»ºg¶uÈÑ# àVˆØ!õvê"êãÔ‚+ƒöõ]ˆUL Â¥AL¬‰²H[¯¾EåîùÅ“údØ»~‰û¯¾JðiíÚ€ë{‚h=J}°Èˆa‰Œz†˜À1¶X™!QxßÝ="¦…Fç·xߜÔBŽŸ¹ÆµŸºÎâ±›ÖïÒnî#UD÷i$J)D D,Fx„œ¤Þ½=!æÛáû5ÄÁ.ØÝ¡[ÜEªT‰#bÚõË4‚ÁƒÝ ÍÛnP:#Ai\ÈÙÔ-wïñÙ¯ÞæÝÓmþâ_ÿû<õÔLJ:èûÿ•W¾Í«¯}‡Í¦æÝ7^æSîâcºñ±?!ú²<—ÚÙö>1®‡í'ôí2ZQc7)&géû% •ø~‰·Ž¬!UAèNÑ„¡rB²8“?!Mø‚oÓ(v˜º…(ÓðFF¢Œwf—ßõÄ>€×7hçÈÊ9®o‰Þéœ !†1,f‚w`[|ìÓêÖH4Â'dMð«W¨jŽÙcëCL^M÷ÈèÙeöûi„™Ðoj–ïþ!·_ú-š“Ûè²Di…7k²¸ƒ”’(=(Ð"ǵ§ÃÞÃ}O»¸Ãæä€Jˆ¡AÈ1J–H#f• É©4®[\B߬ëo¼Êï~ù6³‹?Âßýßÿ7ìííâýøSÿ·¿ým^{ýU6›†?üÒçy`»æÚ…ôD»Æmî"cLßWwJ·yY–ÓëH½…%Ë2L!G‘Oñ¡#¸ˆ )3l·Lø ¡èE–» sB{ f”`KÝiZ¼¨4n ý‚h#:Ÿ£Tëk¼OKßwØN:P2ua}½ +sL1&[©ûê#Nḭ°IÛ½@lMpõ× i­Ëвá°ýÕ78¥PË÷J£²1“ó kÉfPh²é»Oü%Šù9ÞýÂ?f}ðY5# EÜlˆ"#’`J<´ÇH]á»úú”ÕÑ1„ެœiK¥Â‡V%èŠàZ¤ê@åø(°:§9]x¾ðõ›|ãÉÿ…¿Ã§>õ£h­ÿÄà{ïùö+/óú믱Ù4|ù‹Ÿçü,òÄëLF9FzÄ0g HœkQ”)0ÅYt¹‹.¶ÈŠ1Ú!¯Æ¨ñ&+Py‰µÐÔ§)s!Èrƒëý0ù”è€0ôÖB{D°ë_r6-‚˜Éå4 êW@@ y9âòãpxóír…Š`Û&­"§gp]G ]¢Âº-õÎOÓ›j;¤.1F ˆØvMpà!ªôìX‹£F¥q?!žbËVÐÄþ]m#Çåã rõÏý_¸óÍoŽiŽÞÇÖ'À7émÍAå)ËÄ–`HE‘@Ê‚1M;‘>áö"=Ua}Ä6+êõ ï}pÌï|å8z˜ÿãßû[\¹| ü¶Ðûð#„À9Ç·¿ý2¯¿‘‚ÿ•/}žs;šçŸ~Œ+—öØ›h¦Uý=‚ÔôvÂ#©Æ ò1R˜F1Kãf3'‰ ¥ ð5Á{²rŠTY1Ã6Çø~…&¼«ÓÞ?4iR¸æˆà{tu–( Þmðn“ cù…§Š|4bûÒŒ“›=íªE}Ó`mÌ „®²D*ƒï-Ñõà× Ñå-RÆ£ &¥÷uëÑ[t5Ç‹ ßÖ¸Í!®?EHM¿9%x‰ëõÁ-¬íÐæ}²ò]ôü¦º@9sý'ÿ:¡ólî¿ÇÁk_d}øNËûèl†Ò9¨™ÁŒ3ºöd!q.¦KÅGcX¡4!@p‘Þ9Öµåů¾Ï¿]óÌý5þÂ_ø/(Ëâû¾õßü—_þo¼ùúü/rv;ãÏ=ÉÕ+—9¿?gœ9м ½Œ«O!H¼d£Sí"ÍïjÂú¸º,‘Å Sm#„ÂvkBp˜|„„Ìñ8¼ëÓS+":sZ"à†q©C;H•Úˆ]µ€ ‚C©SlÑõ„€|ª©O¶‰˜Òa›Y1!ÐùˆÈÛoÐhˆ=Áu„èY/Ü#Ö'˜bŽ.¶Ð¾@<ÅÖez]ÂRÑÝBKG¾s=Þ#ßy‘ *<~ýíá¸Þ‘OwɦW)¦Ûœ{ì¬oϹ÷Æ·9¹y¥ 1Z²ÉE¤.†sL‹šØ‘@”:a "x4.æôMÇÃ5ÿöóosÜïñ¿û;ÿ7y˜Hü¾·þ{ƒÿ­—_âÍ7Þ`S7|åK_àÜÞˆž~˜kWÎsîìÓ‘!W6ÁÌ•Fe3dÈQF£ó=²É5‚[áÛC¢<Sd6Ãv]zAûç6Êäh)Pѧ'R*ÓäËw¨l6`ÕWß“M!DIlî@OÚ“ƒxñ¾‘vðBLY L·×{lÓ&×.…¼}Út"È¡CèècQâüµ:£Cp¯§]Äü ålŒ.¦Œ.Èæ ÇÖbx2Ð ðl‰ÎF˜|ž(M‚•kzL× ¾ñøïñÀ£?Éßú+…Ùlú'þÃà[kSðß|ƒºnSðw2>ñì ®_½Ä¹sç˜M'dÚCt'w©n"³mtfÊ•¢¯pU‰ÍQç¨|J[ßLJ‰Eg% 1ö§(©º@Ɉïè(œ³@N k%2Û%Ä 1TÈ!Dˆxo šW(™ö× È™*‹¸Nàú€íZ¼k1å° "ÊÞ< ± 0` bø ðAE#ˆ®c}ë]Ä-ÖŠ|2B¶éšc‚«‘ù4‘Er…^/0“ó˜ÑYT6ÃÌç[PQî¡3Kpï“çĨé6 _{™ÉÖœ¬#d†U .ÓäLÊ)ʈÑ%Z¦¼òæ->óùžyá/ò×þÚ_Ekýÿ¥o}“·Þz3ÿÅ/qn·äG^xŠ®œåÂù Ì&Sªñ ZlÝÐÞ¢_ߣغŒ*çÃ…ëé×·ÒæZgè|ŠÊçi#"’0*› DH<3ÂuKbwLhŽèë#tòZB¶»DU"BOôï!ž¦6ÐõD! ¾O7Ù;B„ ÅRmeDßÓ¯YÙa» ºÜBçB /5>€”£„Æõ›V·„T+3BJ‚ª=ÚetöífEg—ø®§šîqòÎ=lsJ>] Ô-¤®ÈGùxD6a¦Ð…C˜Šl÷IôÖ tVОÞf³øìzE^ÌP¦Bg[h3FÄS„pÈj¯whWÞ|ïñÖ9¦³Ööh­ÿôÁ雼õöwƒaÂ<ÿq®\ÜçìþÓ録£´&z…2%º˜¦Œ¥¯o#µL(i$¶["Â5á…F–/"¶ :T^"U–À©ÍQÚ*Æ ±;F S«ç-„ ÞÚÌTE‚0‡Hp-Î5„Àºï6Ø.>„*!4(]"µÁŒíÚÒmzúné6ø|„Ϊ¡Â^'FŒÒ Å4!†D É&èªb\ÎØ¾òf²[Ÿ¸=2§\Ò×Ç) ÊŠâÌ‹—œÞY‚Œ‰+d䓌éÕ«[[€'ŸíSm_‚Ñ.ºšÁ7(3fì#¡+Žà}š{„@ß;Š<£Þ¬Ù¬7¬×ʲúS¿ç›/}“·ß~‹¦nùÊ‹_‚ÿ×®^àìî”ÙtB‘eDßaëÔ«»vCsúÞ°¹¶{ìf…0c¼ÓÏ7×|ý¾ÅSìðÐÕ9“ùyL5Ç­î Œ©|øA Ø%%ÖöôÍphí‚Å»–à7 J‚ D¿$wmÚžE€fBÎÄ 2&ÜŸ÷ÄЂÇv=¡s¨´ÇwÑõx×¥B+ÂhqùD†ÐS\o‘Z‘Ï v:Šl¶ÛšÚ²ºÿ&¦Òä#Eyöã½;Œv/QN÷ˆ]žÌ0£=¤Ñ ¤)UJ¯Ò ˜l„Ós¢V”£)ms®ïY,NÙÛÛû¾ ïûn~Û´¼ø¥9¿WòÂs7¸vy‡óg¶ÙšïQäEš|ú|G°kê{oQ¾RЬ0唾m集º½Ã;·üÁk9£³ç3Ÿ›?¿ZðÄ£é|¬!úÑw¸ºEåB·"ذD·&ø„&Æ.Íþ] Ò¦”Éö ôujC‚‹^/„´ÅŠ\ß'r¢uÄSÛ,"ÍjA1Ú¢œî‘e£ÉŒÑÖ.¦Úb$®¤µmüns£YƒÄAô×õØf‘x€¡Àöºõ!¶]ÐoŽé÷Á5Œwfd¹Ávžî´§?„Õa¤š[B°tõúÅŠæð>2xÚzC9{0­¤MÊßAçEBâD‹*· j™+F“!ôtmÇééi—|ŸÛ_×57o¾O‘/¿øe¶'šçž~”®œçüÙ¶¦y¦À7»AjÒã]ÃéíoáÚ5Ùxe&tí)›õ)‹å’o¿q›¯>Àµ'Œ½½‹þýKßÀ‹{†ÐÑ÷ W)Í61(BB‹4 ®Þ÷ Úzɺ¼úî†ožçáOülooѶmÚ">÷ÿæ3¿„0'<ý°b<*(Š1Âv‰ "DBw5!8@£ñ1½¿ý1BϲBAí›ÒH¡Q¦"úSB ³#ñ®EJͶ™í=‰Î·MF@$š)£ó #Á6ôí*‘!üЍ'éÿÇm†u­—Ø?QØtPúï{¬õ„¾§oïÑ-oãÛ5„H19C9?Ð%Ñ{ªÝ=t«5Õ–`²/ðNОzºœÞ:¦˜× %Á9SmSÌ/‚h*›#•C°÷aõru›‘ìY­VÔõ†¦©™Ngl&0ư··ÏÑñÛ»»DÓqE5*È‹ ¥$BŒEÜvxÛ|ƒÝ,ð®K\/è»–ãÃC^zå_=<Ïßø/ÙÞža­åâÅK<ýÔ3L&ò<ç_þêÏ!åh› …IXJQm%èx½HÀ·Aûæ6¢À{‹TYâ/Ҁ̫aTªjLè;‚kèêSL6fÿcŸ`¶Žbëlùú<*£Á6¸úNbû ™¦ŽB¦"%†´­‹>1p¿Æ{‡ïB¿¡oOp}‹­‰ý†b4§Ú¾N1Oé;‰w=¶]@€|zÛÔ˜‹Î&ø (æ§´«ŽæhCûþšl,(fÛv(]´ ®]l!uBâ7÷q«»4‡ï³xóeN^½ƒ8jX²Eß÷,W+¦ÓÙ÷­ö÷÷yãÍ×ÙÝÝãäô©T*ê(­é›£TékE¬—t'79ýàØú¾;eµ©ùú×ßáÎóØÿ¥ïÿÂEž{öyF£Þ{yäaþúßú?ó/õgþO=z™Ñ•áÝq¢ÕIƒ·´í›ÄiLb½‡ÄUb”ª|1z„íZ:¿B™ F7ìœ9Ët{ULé›%ÄÁÕiv`—@žvÿ¡öñ!4®;8œ ïúMÒ °5Þuø~…kçPJ3:÷8Åì"2› Š÷ÂjÀøƒÌJ²É‚½O>½€*&ŒÚ#ºzÍzt—ætE·ñlŽ{ÖG‡„þ€Ð´ÈbŠ[ܦ]ÐÝEÆ Ïj±àÎ[ïѶ–J×Ü;=À9ÏéÉ /\ücƒcdkk‹Q5b¾½Åß~‡åÉ=ú-A·–È8CŠ˜žQ›˜Á뻯RŸÞÆ¡èûžÕú”ï¼sÌ×O/ñØOü¶··°ÖráÂEž{îF£ÑGèÃCð7þÛÿžÏüÊ?`>=áÁËc´*Prà+ÆT ¥ÑÁíŽPz‚23מ „$ ˆÔèÛ´?ÆáúˆJdì_ú³³ ŒÁÖGDß',{wšÄ \Gô© tVã¯ñ¶I°jß]MrÈ ‰*†OdœÅ{G°5åÖ5fŸ$«fˆ|/­±½‘¢G¨D¸ ý)¨2+Øœ¶tÍúæ.¦±{ùú€æè>¶ƒúð&÷Þø¶/<€2cºåVwÞ£^¬ðB±9^³>Z°:mØ´ÓA×`­ãôô$­Ò?$-þg>y^°³³ÃéâáÞý#._˜ã]‹k<¦˜¤×÷t‹{œÜ~“õrÁfsÊrYóú;K¾qz…G?õ¿ þóÿQð?üxïy衇ØÙ»Ìëßú·\Ú~’\Έ„Y%¾K€RНík¼ö…ßàÆä.·.Ž=v - úfXêõ Jt¿º.guDðåÎ×À&$²g°Di¢È0YÇx÷"!jBßà½ÅÛ×®‘*ÇŒÎÒ-n¥JX{™ DEðñ£–O&Ÿ‡bZ « Bétú#fg¤œÌ1£)&Sœ»JäO¡Âá]$¸©JeôÞ'½)(¦;¸ÞRÌ6è£Èñû§,¾Fq¦bïúg/n1›JÌhŠíëtpÂu+–¬„$¸ˆ‘ÂCÙ¶+ÖËíΜÍfCY–ðÇw„loïPäÛ;Û¼óþ;-f<‹¨¬%6 Ž^ÿ,w_ýlâHšœ>*>hßÚœåÆ§þâGÁ?þÂ’ö¿÷#¥äèè„ñ ÿ/Šú&ä’·ÿèe2:.=°Ÿ óiÍEêïþ9ó31BŒiÆo“Š9Þ/ Þ"eAôí0®AFL5æŠà¶]àêÓ4¨‰ ¬Ý„½WfŒ v£'©Sè,,½#4$:›£‹]ˆ ¥ ó‹—ØyàF;Ñ£=PÈ,ióx1гS7b7· ýqB»v8!ÒÖOÈ.qVд‚ÖXï.¸ÿæÝrC1©"‡TŠ|:*IˆÈCÀzh=¬qö»{{lÏ·ÙÞÞþ¾… ÖšÃú¶åwoòÞ»ï²:ºÏêÞ›Ü~éßp÷ÏÑõ ^dtÞ;’üáí]|æüçŸ{ñxüÇv1Â/ÿ“ŸçÏý f0)Ò°>^å–Üôi~ã#¡_£…ž½K°']ì ¥s¢ :O0°à-Á[¼³8ÛSŸÞg4Ï@¤_>¸¶Áõ-¦ÚJ0ðÁmÒ!f4#z‡Ôß/ ¢.Qª"8‰)׌æ;dåQ`7iÙ,":¼_ì™ Q¤ŸÙ%¢¦4‰†(qÖbŠ ]®(F-£ ØuÄ6‚¨"ß[±:®™ïì_)(FŠ|´Eµ{¥íé!Z%Ř"@ÙG¦…ãää€"'§'ßw ôaZÞß?í[·xæ¹ç89>åoÝçóxY¿ÇnáÙŸ9¦U`ÕOyé`ŸŸù©(øJ)¾øÅ/ñ‡Ÿý5®î¶G0A‘ p–›ß¹M‘MÈÊ)"ÛAemÛ*¥ížèÓ 'Ÿ$E.¥:ǵk)=A:úf…Ýd¸júæ^™Š®>Bfã¤0&s I½HÒ·bâ 'º¥Ç ¯Ž@„Ôj“cí)FFòê,è1lR+#ô=1¬&O ˆIè)(w-Zg³ë»ÄoÐ¥ ¦‘k¦û×@³+ «À”)³3ÛH£¹õò{Œöv/o“Mfà7¸¶#º@9†²ÌFžÛ‹ûxXœž~ßЇŸ .²Ùl88<`4ª8svçoPoŽøÎý[´÷oáÉyüùŸbþaðÏÿƒ/¥äÞÝûüƯþ"×/\›#¯ïRŠ˜UÔKÏý;-gÎE49“óÏ¢Íø,Î6tÍ1ùx?aгñÀw©] Y‹ÐIÑ*šÍiWjE>>‹P%R¨üˆoW˜b‚©ö1ºahƒ”ã7š1j‚ß\Ö’b¼®vÓ8–iæéŸW.í*\ zœÔ¿dž”3B‹ï|”±¤©è09:«PZ¢+Áô"p3¢|â6æL÷ ¶/í Š·¾±âäÀqõñ´4Kb×QU™Máö1m×±ÞlhÛ†Édú}Rs¤ª*žzêi¬íY.WpppÀéé [[®\»J×õ!È3óÝà?ÿýƒàœã×þů ›xèã—87žR‰ vqÀÉí“ô, 8ºk)³@99d|>¢E¦)Ê=Ò+§æ¨GçS|¿&¢PyAð'Ä Q² Ëç´ëcl× ÍÐØõmd6Fg³ô¦»5BfiW.dÚ$’‡ª'¸ ®[ èÔI¢C …w«ÄVÖsD´ ͪG@@© •Mð®Gé‚@ªò}·FÞºŠ%®_ŒÅö‰T*ñµA„S@6Ä&’O ˜ ¶/Ÿg²w`7l¿ÉÁÝ#Þoi6÷ùØÓû”£(‰“û¤×ã¬W+&ãŠåjÅd2åOó1&cww—ÝÝ]üØC4MÃÉÉ1÷îsttÈj•:‹sò}ƒ¯”âóŸûßú£ßçÌÙÛ2«ã|Š­4›“í&fí9½ŸD¥ •„šBaŠ*P×m¶#R„G{зľC¨ÛZ´^aªD>F …﫸CÄnî"t…oO‘fŠÎ'ˆ8Hœ…kg¸v÷=FÔ—0ˆYy UÌðÝ!("OôÓjg˜H¶JïB¢S @EEp§(S&fLÓ¢2™Øã@1K_hV%J[9;C¹uè6l]:&}MßwlŽ-¯ù™0Û«ÀxÈuôbŒV ù»\,¸pþŸê|˜>LÝãñ˜ñxÌÅ‹—Òàgµäôô”3gÎþ‰Á—RòÁ·ùW¿ñ+ÙP‰ÔI<É6K€?èÖ'tõ1ø€Ipmp=¦Ü%ÆHè[„š%´’ H"¦ØÅ¹ÀÛ¯#Ðcò¤­ÃÄðÍ$>x”¥"F¢ivá[¤3Þ¹ÄÞÕ{¬OÞCèêÀ[ßX0¿X0»8áö‰ãë÷ÎðÈ'þKvv·™N'ìïŸùSÿ; Y–±»»ÇîîÞò{ÿ¹Oß[~ý×þ4w¸þØU.^<Ï|š‘I‡VI²¶™íŽX×´KbÄÎõ§QÅ í]Ðz5)QjTžR­ ÔçQZ”®:‰8zÛPXç1F¥´¯2ð ïWˆ1ɼ¨b—ÍÉ{ßaŠB§¾ÙYß,“¤Š»O¿YbÔmvPªHÛ0á@O E‹o!®Ót2t ßþƒõÈï÷‘Rò…/ü/ýÑðÉÇv9»¿Í¸4áˆvÉúäýú.BeÌÏžc²]sÿv>ö{|Š`;´ÉÇdãóÄðn“HÖ£ËÊ' çÛ58KÀPxïá"¼µ˜b‚oOÑz *éï…~•–4)»èŒõá[˜bN6>ƒÔ9B—È ‰\/X¬(³Œ¬È1£íÔÇcH*«Ä@Ô”Ã4S›”Ó}~ЦBA ‚¨rô0jˆ" 2MVí#Lƒëïl€ d.ñ®ãÎëÈya)Çf{Ùà ‹ã×ð1â"Ü]Á›õ9þÑÿš3göØÞÞáùç^`6›ýÿ-øÚ”’[·>à3¿þË\Ùi97ŒMC®+´ŠôÝ‚Ã:‡Œi£©Ë-Î>TrþãψVY‰.f‰Ð©$Ýú^SDáeLTba蛣$¡FƒTKžØ´ÝŠfq|´G¶õ J²…f’*‘¡G¨|Lˆžå½w:G™mœí ÎÒµ5RDB¨‰*Cæ³A*V ò~qɈ(‚_$)[‘*ýà’´ói:©tïV¸ö)#Åä,BŒña)ްuG°QH„lNîsó¥ÏsæúÃLv¯òÀ³Ï"‹-Þ{ó}Þ|õ·å<ù•9{{û<÷ìóL&“ÿÕƒ/„ m;~ã× ¿z—‡Ÿ>ÇÎÔ`ü}BSãCŽÊ'DïÑÍBO’Ü+('g)¦»©í— -´ÆvÇHU¤6…ï;D¦’nPŸL$ ë)³8ü0ÛÕØf‰.¦Ä˜„—´ž¥öÎwi§à:2Y¦k¿B‡@»YRß½G°‚É^I1UŒ·ö‘JÓlFí)jr) î6’0( ¡8BßU–j‘ᣤ"ª“WTÓËXÛaÛJ)¤VH ‰À Ðì¨]Ïýw^#ƒ*¶™]9G¿2ܺ{•§žûIæó)çΞã™gŸcTþWþ‡Ÿ/~é‹|ýÅßá…‡gœ9³ÇÖ\bÜM\}Š»Än‰³ådo×tëc\·`ëÜŤ5äk”Ôh‚ïFa²q’ñ]w’&I¶™ºHøòÎ-p¶Kãb3ƨ  ]¡«9JÅô¾CBë e ¤NR'Jl_!uF>Ú¦œöH@ç%ªHmTèÖ,NO˜,® T’‰(„¡¤&Ø5Aä5¨€· ? …N-¬mðxl¿BH¼]ú B¥¬”t#B‰Äü‘Iݳ^×ÜyçÔìßz×ñ…7·xâ?Ít:ú|Q–åüÿx[øÃ!7oÞä7ãW¹4︰;eªOÐÎQ•%JÍqÝ!J&ígï<Ö¶dEÅx÷bÑË4œÓÒŒ +tDg|¾M{zYa2MÍ@. m,4~“^­Ô¶YÑÕ ÊéVbEH‰ïŸe"G†ezd®Ég£ ÕY‹KÌr¢[Ó.î#ß&Û~d:)<1¦ yïšX %Ñ;lsTÃ}G°K”*“~‘Ï@ ‚[£2‰.nãÓhZ™tPÍ„®Ùp|ç„·_¼²¾ÁãÏÿ£QÉ•+WyêãO“çùŸ:€)]·¼õÖ›h­ÙšÏ™ŒÇäyRê:b ?ó™ÏП¼É£Oïsvo‹éHkA5{“ÏØ|“ÍÉK Go1¢uÁtÿzò¹¤ŒH0úeR õ:Ÿ&ưF™dRÒÐÙ”|rŽ`lc°}G¿:&í–ä¶Gˆ!Ai“”ÇDB $Þ5ƒJ§@S´ªïN¨dNá[PUnáꌣ›¯b`{þJgD2|·A˜AD>é÷ÇèñEÁ bO© ô6ñ}¿••®pÝ-¨=®èR¢ k[–Màß–Ü17xꓞ¢È¸þÀÇxâ‰'ɲ캽Î9^yåÛ¼ùÖ›@D)EžL&æ[sæóùÀ(1Æ|7SDþ“õr_|‘¯¿ø{<÷Ð.W®^çÌnEÁˆ‘¾]¤‚[M@ØþgÅø yUâÚdî‘bLð5ZĈ‚È€ýÓyÂɈúv…”‘rtôïBâÈ ˆaIÏ ýú©+šã÷QZSÎö‘BábRáŒHmrº°¤2䣳h]%¥Q[£tž8‘¦ >h‰¡ÃnA% B”9JŠ&T6%´oáÚc”ÙBgU?0©whV‡l:xJ UB+mP¦Ç9ˆ²ÀFͪñ|å-ÉüÇxæ“?E‘zða{ìq´Ö?PðcŒ|å+_áŸþã_@JÁþÙólïì0™LX¯×Ü»w!ÆdŒF#¶f[Ìçó„ Éó<¹‹ Ÿwß}—ßúŸþç·ׯ^âÌþ>UÖã× µäšã#Òwâ;„P˜¼¢OÑZ ‚Ð/‰vÎF;2‚«ñ¶#«.`ÛSls€.vÀu‰a¢‹t[C ‘D •¤XlOh4ËAdœ s…„÷‹!!|cˆ¨|”d[LNˆi5œôØ-Ä8s–éù_±?ŵ™$hE$ª ‚‚(jŒÌvé×ï²:‘\b*¶ˆí’¬˜%:»]F'tëè›MÊ~ZÒYDzqüá{GÕ'yî¹O’eš7åÆ#&dÔøníë_çÓÿßÀÝ׿ÄÎNIzžwEª¤¨&lmï²³³ËÖÖŒº®9>>F¼›(pEQ0L™Ï·™Ï·QJòÛ¿ýÛ4Çoóì“û̧èaØj· ø•MQÅÁ T¶¤oÉ &ÊôtFGt+|ß =†æä&¦ØJJÕÖ?øeÒ—CŸ0ÉxÁ®PîäŠL6ˆÉ0Ô©ÜzA¿^RÌ.¢L™T»ühY@6ùH 7º)lÒålÄ7'z´VH5Cæ[I„@ˆaBð‚àÜ€\J:>f¼‹ëÅY$v®kPÆ}A Ю7´M¤÷`cdy´á·+6óOòô3/`ŒæñǞࡇÒòü—^z‰_üôÏbúÛÜ8cWŽ+O졪3¬jÏÉ¢áäô]^¿õ*“O˜nm³½³Çöö6ãɈÕrÅí;·ÑZÓ6/ýÑçyâJɹŒ­ JÑy>á#¬m‘*mX]ßâm‡)%šžh[Ôt’t m‡í´mî¡tZѺ¾!¸#Úõ}TVÑ­ï'¦¬!‘øþáN(´K‚HF“éï œ ¸ªe³Úà›ºÍÕÖòêL²?ñiU+u†ëÖI®%¸4'B êÚ}¢hÒmx‹·mzÿl“¶rºBæó´»’à”[æ3ò|›nuèT¯pÝxv%¦ÜÅ»7iûˆ‚E/øòÍ òâOóäÇŸÁÍ“O>Åõ®_¬ß¶P^ùÎwøôÏýCŒ½Ãþèý>w_þ&;òˆ‹×oàÌ›:P7 ëzÍr¹d±j8:¼ÃÍW_çõ^ ³£ÉœùÎ;»»¼ÿΛœ-xàì.{ó‚IiÈ‹J™¡Û,©ß1}}@¿>LK¸˜ÆËÕt=:ƒ4%®ß`­Åû€ö®Ij×Ab›¡_a»BI´€‰ ±{Ÿ\%ƒf¹ÆÖÊù9|¾E[# ¨, ŠHß5øú¹u†¬ãl†9&K*œY>œÂ~M°‹A|L¹…JÞfD×Ó®oÝ)åx†“ \{H>:“ØD¾4!nRÁ©«¤lB¯2Gfsb·Ïæ#í`ÛÓõ>ÂqøÒû3Êë‘Çž|’,3<õñ§¹zõÚxHUúo¼Á§ÿÑÿH\¼Ê Ï\ãKöæg8Ú2¼÷ͯQ~ìmö¯^¢µ­Sô.ÒÔKš¾g½<æôÞ»¬6že9]Rßû€wïHæ£À“TìoL+C®x}°HU•[dÁÓw5¾mÒô66„б}æILµ‡Ìw€<‰É12Ÿ¢³âÁ%ç.Oè¤fUI9§Í]‚¬Ò@& ²r]Íp®'Ê,>ŒFío‚¯Q²@`´ z„ªÐ&áí“.ظA™¥JtVÑë‚nyÌúà]6ÇïbƒÐ’j\"å`b…%ÚS„aª½„f’ ¥Æ·xðiVC‹ÒS|ߤɠ;b³Ù°l‡«À—oï0}ä/óðäyÆ3O?Ç¥K—~¨à¿óî»üü§ÿíÁ·xöÁŒ³ã{Õˆé”3ÏÜ`V®‘ö€‰¼Çöþu¼œâÈéû9]×ÒõçØ\œ³¼û:›>£íÏàlŠ–ªŒsÇÎöœ2H6ÉéEfi%Q(t¦pEò=Á9´™•ˆˆP&i9ó%3t´ƒƒ!¡³§F°³³K¹ýÐà¤aÂá¬MÂBÙ3Þ=‚î8i¸.™Õ ”Ñx›FÑmPR t’d‘&)vÊ`Ñ:G޶VqŽ­—lÞfq÷ –GïRoNñ®g³:âüµ˜no§Á çPÓËÈl‚PÁYš(K|X!ò1Âìâºu"Ÿè ••´«cŽî¿ÁN¸{*øúáYvÿË\ðcEÁ³Ï>Ï…ó~àbOÁ­[·øÅŸÿ4íéM~òÇžçÊ®c¦)8Å/H•±sfŒëWøõMfgD'8ëqa ïz|ðt³’ºê¨W'ôqNß5D»BÑ“k…ŽkdÐZÊAí´$tkœsiÞBDe%!t”Ó-L>%Êœ¾[EÓÎò9!Dtsüb¨|„ =£jÂlkÎèì“`¦ƒ´+ §hWƒHæÞ6D×$É1’}W ƒ¤%ÝfE}ú61:LQ’.§Ñm臩[€èЦ ãÇœ~ðmî¿õu–Ç÷±~•Š4õ†·¿ó2ç®^çÊ£ÈfדÜ|°ˆÐ¥`èSW€€ “s –@‹Ì*lX½ÏýÛ÷9ì&|mu†ý'~œ+W¯PU#žîyΜ9ûCÿÎÝ»üü§?ÍÁÍoñcÏ>ÀÇ®ãüNd{þ0UYÚ#V÷^F9G–÷^a|öÑ$…:3PTÄ)óŒéä)Öw¾Îâþ]bePÊ Èè›ãôôÅ"9Œ‹ëO±,R;,†i¦X—TÙÇÛ×AO1âlCÄ*©¾>FûîÍ1JSŽ3ŠjJ6¹HT#@"D‘lYD•l—j„ÐM¯ÐÕ÷ Ýdl†•k¤ÅÖVw¾…ˆjë"Ñî#ó"½ýëûD_£òýºgsð:˃wYÝ{Ÿf½B ŽeA$å8€à·ï’—/s±Ú§:÷Ô0TrI¿OBëu®­§Ã˜xF’¦n8><ä ßå‹·sÎÝø.^:Ïd<á¹ç_`owï‡ þÁÁ¿ø ¿À{¯}…{ú WÎm±3ÌÆcªjBYÍÑ;W˜}ŒúäÚÕ!Gï|‘õÁäãs ™¡ŠTðÆH43ªÙ.aõjtT‰m§T—´mRÁÌ@ór5Îm’ôžÝ d`<>‡íá\—4žPDá@WIËÑ®Ðíém&ûó3—ɪ1RˆªLN²mQž=Á7‡¸Í=p ÙøRÊ d˜âˆ–¶YŽ<7Èís×;¡=>Ƶw)fçÑãí2)o.<ëû¯±ºý&Þõd•!/ Á|è›@Ÿ’N&è×áï0ß‹|ë"fz„HèŸXÔ’ØoP™! Ð+\ß³>ú€ƒÛ¯óÚ­ ¿ÿÚ˜ |ŠsçÎ0›mñüs/°½ýƒïò…óOÿé?åo}O>q–¯ã™&E´§¸FÐG‘Ì"qHáÙºðqʭ뜼÷Ep+d±…mÖô}ê6’B©Då[TóKD³C»>%¸ä€Ôˆè±]MVL*Áî܇¤x‰í“8Dô£HH‚|`f›T“i5gçü ÊÝG@ B»Ä÷ Dì t9’Š˜]!DÄdûx®¿Kßm€ ! yQ"à ©Ù¹O" Ÿ×/q}Ãæþ7ƒ1A`7¸æ˜¼"±…ò¢N°0Ó£K(lò,öJ•¢k7Ü¿s›|þ.••ÓA”Éx"mÇ–¾mÙßæÞÍWøÆwùoL¹òøO°··3ìòŸg6Ûú¡‚¿X,ø•_þe^þ£ÏòÜ#»\»0å̶f6‚ª,у›í[¤ðˆÐ$ÓÊå!BŒæWíª¥vÍÞCˆ ™í"•&æ[X=ÆÛˆdÕ.Jh×÷¢o5ˆvZ‚÷ôõ—xÛ㻓O“¨G”D»AcT6#1ø5•è«OÿåþãD)!8ôxÙ·øn…Ì·’dŸNŸ‘Ó-¨OÞ!xŸ4û3P:PŒÎ!Õå´?ÈçˆD4'ض>À5ýæ¶>ÆuLjØU#ºCWxkQiDE&Ñ;@‚ÎÉ3=}‡;¯tì]ÿF;—* Êíž ß×´«V‡÷89>àK_¿Ëï¿ZpãÙŸf{{ögÚå !X¯×üóþÏùê—gÞå¡Îra7g{¾Ea"Âo’w¶•È6±»I3 Õ"d†Òg×ß¡G3L¥Óx[˜$ŒA’ëM VÐW!y#õí†Zl/ñÖâúEò:ð=]¿"Ä4KQù¡*" ÎEDsJˆ •ïÉ0å]î?‰*wð¡ÁÛ€RSTµ•Ü­}‹_€Ê·A¨b‹à#Æ;LsóÜK­ú“‚_×5¿þë¿ÎçþÝgxæ¡-¼¼Ë¥sûÌ*KYŒÑrbHN¡Ñ®/…*k­Mj¥Úú:?BWÛà%1l’2y€#Ï Á ƒ¡­—øÕ]„DÎvH>Ô-´#ØnƒsÅh'ɡ%BO vIhN´NOÑ££ã}x|["u†oïaOßLN[ÅÙ4Ã7s‚?"údóUO6š’Ï/!L™œ³ƒEó€R¬’ûjµ q‚§˜n¥t(ÑÕxÛãÚSBÉt ï“8BL²!x¿N޲Dåø }{ÂÑ[Ÿ#ï…ÁCÓ¬Yœ,¸wï”ï܉¼²~Œ'ä/0•\¸x‘gž~öÞåü¶mùÍßüMþýoÿO\ÍxøÊœóû[£H™ ¤[â}@ ™/bvóž\™$ÇGò –1yÚæݬPÕÖ`«0ùNjÍ»¡mˆ.]ÔÞäH|“̬cW§ïß'ÐЭ°¦ÀõY(]üËû³õ!¦J°¹dÿ© Ñ[”ªð~ƒkîã7÷ÐÕ9¤™ ¢LBÒ Ægˆ~Ê!Û>›LŒtNÔ’`‘Ê"ˆ(cÁèÄD BŒŠXŽ!4 5ÁñÝ ¦È$¤=Dôd2Dð¶¥o Á;$j0¶ µCVîàCϽw_ãôdAÛÁªv¼|¯à¶z§>ùÓ”eþCíò¿7ø}ßóÛ¿ýÛüÏŸùe»œqãÚ6öK¶ÇL¬Ð"™Mzï„^æH•¥5vìé×wQù<}Ÿ± ’H®ÎvØÕI5Ÿ%Û9‘”Ù…ž 3AˆÉP;FKp}Z¯›á-Þ. Á!C"ñ|¸gñÝ*)™[@U(QÑw'´õ›×£QÉxXë1Þ® Ý!nqԌΠójHí µ&x¶'ÍУ H3Iv«Ñ!c2T<øZ ©HRœa°eË!ð>Ùý Ÿ,Ø¥CÊ‘­Aª®LŒ!7d•˜®«é»ub"es¢)1‡Küñ‚ƾú~ÎÝü“<û‰?Gž®?p'žøø¼Ëÿ0øÖZ~÷w—ýëÿ„ínxøòYÎïVlU\,1rŒ”Õwͪ•NŽÞºDT»ýê}lwL¦òıì6è¼DˆœèÍÝ·]|*)£ˆ щÁð²Äw]R/yÚ°j IP½à€ŽÔ^““Iv`d‘]“¶[Àß­ñÍZf3DðjB·|eæÉôI)¤Ê5*vI¨_bt@e©"‰Aã{¢ˆ˜ cÄ–8°sùp¥­F %†–†@¤Cf:éòÇäÞc* ƒë*FÆ·×Ỻaü~r —^M_x–ÍÛïòåw£Ÿàùù ŒQ<ôÐÃ<þèãhc~VÒõýýßÿ,¿öÏ>͵ùŠW§\ÚŸ°·U0ª Jpލ“E<.Ùê%ãÇf°ž5ˆüÑžÒ5 \·$+ |ç±ÁSFó¶>™nj²tQ4q¸¤”}³ÄÛ–ðó(ƒ¿Á*§ýGPÿ¤Ê"¡ÃÙdŒ!µ Û:‹N~¹žà—à{LµPšl~d…ïZìúiTz‹Ü¥“ŸÌÒ“Hv !g ex1h àT¨„ò 1Mx€MàÓ…’ÃÄ¢X¾ óá÷÷Ò ¤Y%„,©[¯ ¾~ÿ'Óë<ûܧ0Fqã‘G¹qãÑý!0x1x>ÿ…/ð+ÿäg¹8Yðè•1—öK¶§’\®0j “…·‰×(dD ºDøš@²¤‘¦Lü ø‘ìr‚kh¼@ªÈ¸š¡² Ä™¶kD‘.Š Ùò9‘¾·(:«°MMhºs-ÁY tI”ɰ3Êdk«ª]ªG¤.“\¼]¿•.©¡ªäuç6§èé8-D²J ý-—ˆØ$a©Ð!BÁ ŠÝd¯b|:}½nºMo?.U·2~˜[IfÊyÒ&=Î#²*H‡.ý³Þ%±fYCJƒÖ^Oh{Íáñ~óÞä¤x–§n<†1šÇ{œ‡|8­À]>$LÄ—¿ò~éþ!çF žzxŸ »’í©bœEŒ“¡m£ÃÛ®v÷K´ijÊTž¼”¥ÔÄàQ&ǵ›´À ‚@Ïj±Æ…ט_zAIšÞ×ßãÞ’ô“”)Q:mqu–áºe2äpIŠ_š,ìëc¼Û 0Éò¶ØqBŒ]²ÚU%º_݇ޢʭdô¤“ˆC¿¸*¶QJ§}~·B¸Q- ¤”„¾%ø#TÕA¶—Œ‘…»z9øªlÕ’vÒ‹iù®g¯,dHÀ¥"•öC&`à!à¬`³iøàÎ ¿òŸãþ²äÑÇO»ü'>ÎõëûwùßûùÚ׿Î?þ¹ÿÛòž¸:åü¶dwš1.EQ`òIr4·-Ýáë(ePªÄµ+‚ð„þ~²–W9t‡H¦xq®IO_PX›n.¶&ÏêÞ¦¨˜šYz…À»:ɺg9!‚ˆﺤ▓½PÔ«û’Ër kýæ„$T$‹úH+=IÛC©sÛÈbØ]nžŒôË÷õ •oáºÁ&ÇÏns»>ÀÛ–½G>E–m'¿18† “B§Ô`§ïfxFÉ<*´@LχÌ:‡³a0—艢À†ÀryÌ­[÷ùÕßz‰E;å±ÇÆÃSO=õb—ÿ½Ÿ—¾õ-~ñýÆö-ž|p‹óÛŠ­ÊR…ÑjpQñ)¸ƒÎ¡÷/:|è¼ÉÄR®Ñ& ¸|’ÖE…RIÚ†èñnI»o~“õÉÛ¦˜_O°z™a»šP¯‘º"Z‹Y2ÖŠ ³Þ÷)[D°A lV'LÚãDü8RæIQOð¾C+S¦qb{Œ4Ï ~ŒË ¡[&—PUÒo×\wD·¹“EìŠã7_d'Ÿ Æg.áPìùø&¹[Éœä@E–C°‰úeÛÔ$ ØôˆäŽ÷ƒ™¤#HCWße½î¹ywÉ¿ø×/± {<|ãú°Ë–K—.ÿÐBðï|‡Ÿûÿ߈ÓoòØõ1g¦=óR0«Æ”Õ%$Þ.q]ÖÉÎ&Dbh)ý𑟒¤ÀÛ.±”ƒÇûcbph3•%D1ØÓÐR'ï»>àÌÃ]î%O!~Q$,fŒ1 =ë7¡Ó÷'Z•Øv…ïúõåô"*ÛJúÊÞ uš)ø~Ú$ÓÎРÌ.= o“° *æé­êßã]=¼Ý†õý·_8`<½ ¨„°ƒÉs$½ç²$öëáí÷)ÈRƒ]2?&= ¢õÀ"™JèÁ5xé­e½ixýÍøµó:zrë\¡, ž}æ9.\¸øg"\¼ùæ›ü£Ÿý‡¨öŸxö—öKæÙíîjOëÖ(ê`kD–u„®ÁÛ4ˆ‘*‰i8’oq ,Þ5ø.õímXâz—äñ¼¦ÞX|™SÈŽ¾ „[ÇÔk˵g‘í$wrÓ׫¤¸­±ë¾Çu}Ò¯‘"íZB@iƒqÓ=Eé¤FŠo“¥®kÒ:^J•¤×H. 1lÀ/ñÍÐçA .ÛÂeÒéIœ*9¤4I·<`äú4Èð5Ñ­Sê—Eús} n:= ¼ÄóKuGOôMÒ¯Ša*bp¸õ]œt]`³©yí~õ·_c¼û0W®^¢ª*ž{öΞýÁwùßüwß}—ŸûG? í?þcÏsýÒû³ŒIi¿Esø&íéÝäØi&(iÈo1ñ¥Ù¢[b›tžär½m!DÚþ -Î"ô«–®±4k8½í¹óºà[Ç×sÁdX×pI~ôÊ1?þß<ö£ŸÂä#´Éqºç‘1Cj—ÔS}$ˆ™“Å\w‚”!K6GwȪ)˜Q²ÿñ6ù1„2C Ù¡‹ ß׃~ "ú,홵s&zA ³ú1ÎmP¾GÈ\¿Aš*I£KHâ(ˆÞ#BMTE²¥ókd¾ƒ¤M"‘Q&чإ/Ë%H3B¥³ïéÚ%mÙlZ^}÷_ûw°}þq.\8Çx<æùç>ÁÞÞ¾ËÿÞàߺu‹ŸÿôÏÑ-oó©çoð±ë—¸°?e,H¿&ß»ŒV†å¯¤´ëð¢Ehƒ‚d{§2¤H©ôxiÙ¶ÉT{½Ânz¼ $_)!ˆÈ<‡ý&pû8re$xâª@‰ž7¿ò=û,ùd†Î¦ˆ®'Æ ®['­&ïq> IÅs-Ѽoj¿!ŸÌ;3Ié-†T…Nºm&éˆqq ~ ¢K·=Æž]BôèÝúV2$Ê·’I;Ho£ß@8!ôË´È Á³IÏ‚(vA”Ù?×'¹ïk¬ót¾dÕ4|çíc>óûwØ¿ügÎì1›ÎxþùØÞÞù3ÿî€æY܇O=÷ ׯžãÜÞ”­Ù„Bè—ïãí‚ñÎ9ˆ’õámºvEèµÂ„H =B´Ûàû–¾Ý¤€)¶ÆnÖxëÛˆÌ@I, +¡ØŽì.ßÑ &»3Û ŽïÞâƒ7_âêç0å Ù¬¦$Ê. _Äô÷¶'Ø5}³ _%Ó(™¥ñûêä3>@æö£'@g£´îÞ£bŸÄšÛ†è6„nrD ‰Ñ}‹ï–¤Q®†°!º¤j8}É)<ú~˜<1ØÑ'%Ïtšáà ~“R½I>BBD„©†÷?BèQâCA½9b±¨ùê·oó?¿xÄ•‡Ÿc{{Îö|›çž­b—ÿ½Á?<<ä~þç¹ýî·ù‰O<Ì—÷8·[±5RdÒ¡„ÀäS|¿B¨œ|&°ý†l²ËæàMl³@›ˆgO }“–8Q!QôëlÛl¤˜Í)fsºõQº`QcÛ…C—ŽéYm”€¬xoyý‹/²³j;GJÒ%¦xçpö~ët=¡OÞ!0¦‘p¶óœ|ð:&OŠêåì,fwŽ-ä¤DJ‰ ëÛ>Ýbïj¢í@i¢ˆù(‘)ýHM”‰)ý@Î Oµuê£KRtBß>éZD¾öå¾ÇvG´]`±\ó•—îòo¿Úòà?ÂÖÖ”ý½=ž{î…?/_ÁÉÉ ÿø‘·¿óe~ì¹øØ¥-ÎïTÌòH®AÑã{‹ëÖÁÖ•ÎÈ'3”IÞëûob»´î²Â•·÷×v2f¯0=÷JelŽoѵ ‚óèl‚ Ùµè¼%„ä·|LäñNšS÷ß#¯¦ÉüI(\·LžJfŒÇø~ÍÉíª'It¡“rªÉˆHú®ÃÙ@ôo{]m¡…Î’‹W»Aà“‘£™ ¤ÁmÞF•n³4€ Ø4ßbØô–‹Á5Ë{¢ðƒ(DLÕï ‹.Íœèê¤ .¶Mí”18”(ñœ´ãtÕðâKGüþ7<õcL&çΞãÙAýÏüårÉ?û¥_âÕo}‰}î:^ÛçìnŸh0¢—œË‚oñ®A©O)&çPÙœl´Î&½ý%úÍ1BK‚óÉ4Ò‚L€ëã³T³¼LÎ<Š^Ý¡[ãŒÅƒ¯Íâfª‰èÒÓ)¸@)#}½üÈÑÝ»&a “ÿÑ êãvÓ±:…‘É´Cf ›­*‚í:§oÖ½ûuF³=tòïs O&%ºÜIþ}í]l\‡PbèSE2Q „BÈ\ƒ)fH­Ò¶IÓs “1dŒ ¼Ý¤þÕÛZÊ(cÒõ«OÒ›·YÔ–¿~—Ï¿*¸ñÔRU/^ú¡wùßüõzͯþê¯ðÍ?ü,ÏÝØç¡«g¸|á ó±&ÓD‡ë—i«'UÒöG ”"Æd¢-¤B‡ÉÕöy–·°ëõ`Ÿ Î#T¬s"R "Uj‹Év2êŠÐ­î ¤ ÍéÛEBHY— \¦<‚;$iW’¼Ú¡Û,èWlђ3Ww™î­ÈË‚bº‡.&X×ÑumRÑB)"S„6ôΡ 6õí2m·i«çºšàà ÍR€MÓA)ÀûTÓ¢GHIô1éù *GMˆeB¹u2ž 2¥Ç dÇ*唽ëi­aÙtüî‹ïñÍ÷4=õyžqåÊUž~êiò¼ø3¿i~í×~ÿÃoóÂyàœfwÜ3 ŒlÁ5­Ós\Z¨ èÖ÷“ím·J›6™ š ¥£ùyÚÍ íòèlê~E"ÂŽwöí>€ÃÙ *x„l‘zF·¹‹Ë*DßRŒ÷ÒàCLƒ¹Ð#\ Øä¦Öm8ÛàºA7(lsL¿>ÀäU”)¨fSt^!d“]Ð}WS¯Žð}‹ïjTV&á/Ð>ø$¤àZD”ø~Mwz‹<2MRá£M[<RZñ x IæRA(dé °møÅDOB³}„‘hƒpœhZÝOFÓEN6–ßùâ[¼|{“Ïþynxàë<ùCîò¿7ø]×ñ¯þտ⳿û^xü7>vž {;³‚LE„o ý"R³)Ñ÷øn…mNÓ³§¶PÅ6ÑÖD·I£X•!„DçS )Ðù˜~½! Msr“"£Käã]|Tè|Š1Sšõ½4+ÈFcEßm¶¦˜Á¶MÚ´§Iâ.+pY Á±9x =yaJºõ=bwDVŽ‘Ê¡³“Uèl„ÐyÒÿÉrD6Æ”3Œ)XÞ‡XN’Ð7Š51hðÆš:ls×&“!eÈg×ðÖúZ$ÓM âí ä41‹#éYPf˜}GPDV"\‡ ¨SöU’.q–ÆyNVßyñ¯îòÔóÏ¥]þÀË7?Ô.ÿ»Áïûžßú­ßâwþõ¯òñë\›³¿™Ï4ãq‰ULˆyª‡ìú~ÚÜÅ@ 5RÊ4X QŒpmý‘éBÚ¥dfÈFS´¬ïáû¤,žtùÚ˜$¡Jgx“æRí[¢šBÀÛ“Wi§â`ÖapNp|û&gK¢˜ál‹É§Ìö¦4§7O6ÞNЍB§iŸM<o“+¬É·²b›(Ó“­£Ý °ièwý’l~×'F‹2¥:7æ.Ùe9Ã×+´) ÎÒ¢Í%²‡Ê „–™§þ¾_à\MÛ)ú›ßF›ºÚ&Ï+L1&M’8T5ßÓ¯ï ‹*‘†w]‡'PÎÎ'Ügƒ„/h¡²¤élš •©ÕºüH:vK”Œ„JiGÓ¯ðýijC‡«W¨QÉy†I ò g¤7H¥!¯ð]†ö]b ȪmŒ)ÐF£²œbz¹§ÊǶW±íÛ.qmCµ½C³ªñvCµ}·>Jó)ÐΔI"ΦœƒÜ!öË”"¼O>nC5Á:¼ Ã-ð@1ÁŸ‚ „~™`Q2 ò4 DHÜÿè[\»ÄzϦ‰Ü¹wŸùÙ·YsŽÇŸ¼Vš'žü8û3îò!:>÷¹Ïó+¿ôó\۱ܸ:çòù{[ÓI†Ö-SE‡•j·9$¸ž||!%®=MÎgÞ#c‡On*Q Í%B™¤>âRiLYRnuì]”bz–`W¸öU©t’h+¦id-RFЕÍQý0 MMƒ*ÇdÅ|X)+ÖÇ+¶Ïí’USL6Nâ!eçb|ï=Íâ6íê^P¯}M½¾Cð]TDëШ,¡m„B ™°äJã»åGîR!l’ IÐß`t†­FdåÄ€k:äâ.z&Ú¤ÖEj"-Ñui&ÖKêºåö¡å_þÞ[tÙen<üÆhžúøÓ\»öÀŸ)ð~^üò—ù¥üs\ÞvJ3zצÁ–ëH³ C–åØútð:Jƒ«Ð-“p¥³„Ð$y›3g™Ÿ»’`òjðfîIìZbìS†Ѝ<¦ÜÂÖGx»BÉôô¸Æ"pdÕBO(Çûi-:²óÛ£¦¬ÒdUŒ¥ùÎ!´ä“s˜ñ™14¡^P/@H”ÐxÐR%¸5v Á&Ç.dšç»á»d¨¬ ¼HDi¦È`!= ÁÕD»¦=¾M™QÅd0_VÉÿ78¼ÐtmÃzqŸ›‡žýÅû„Ñuzà&Ëxæ™g¹ügØåïç«_ýÿäç–ó³–§ÙçâÙ ûóŒÙ¸DkR:A±BDª˜‚GÈÛ-Á ¡rT¦€˜&š¶IæXœÐ]rM ¶K—ê©éþ%L–!¤Àiù¼ÃûTè-Sè%!ddÕ¼K’|BiÔxè;„0˜b “Wô¾I&Z“¤Ï %¸hQ*O®kÜ&™mK31"G•;¨lŽÐ÷‰Q­ŒAÇèÒPô€MÁ÷>!z‹mèët3ú5Jvˆ`r ®Å·§ µ@…køÿµöf¿šeçyßoM{øæïÌçÔØ]]=sê"›¶(+r ÉüÎE®‚Aà_ÈEreEp”P¤HGrlÅÉ1‚a(%’‘”š¤º;ì­î®îª®áŒß¼‡5äâÝU¡ĆM €ª‹ªsê[{¯µÞ÷}žß³9ûˆþáóhӗظ‰d4õŠÅòœ÷îÖ|ã>dtð,]¿Bž¼ôâÏ7Ëø¥”6Ï?ú ¶²s>ýÔ—÷rvƆ~©pVnǾÞ}K)êÕJ]Hvb [b”“m>Èð+FoD¿Æ×‹Î¨âI±!†@ŠAPºÉ[(ÇLž@çÛÄÑFúÚÈ¡”F›‚@Kt¥t]ÛH¼íï—¸b›ä+¢—Nž1 ­Áf]ŠN$¿Ê‡Í í ¥ "•°å¨Cù^úWç¸2‘Q¹h±±5Ŷ«×c„iÚöIÖ°ž=À¯O1ZSŽd„¸–§Sgh7įÏQ¶O»>§]-0åA§Wo¨VsægǼýÁ1¿ÿ'§l]ú—¯Q–=^~éeÿJÿÍ7ßäë_ý2}Ê‹Ï_æÊa_@Š¥Âĵèæ)(ázøzÁzv‚Òš¼·ƒqy—ˆ+ílí2pÖ3B³€ ôt—ÐV„Ö Û¸~^åÐN3Ü=¸LLÞ£³Úõ:nA$ê–*Yp­QÚ‘BJ 8ZYT\‚1h•£ÜP=™Ãeûĸ0–rÄ n©äm½Ä•Û`2™>š¬{y+”‘è²?%ùSŒÉdh„”A×VF8Á«Ù g·Þ†è)Fc\VR×-®°h£iuÆâþ]òrNokí9«{oBÂôöñÁ2;Ÿñ“wîð/¾wÊþã/sp¸×Íò?ËîîÞ_Éâ¿ûî»|õ7þ{t}›>s•+ö·úŒ%™n!–R×ûJ@ 뾩IÉ`]c3´ÉD¸¬én_Ÿƒo°nDTÑI¨x¬Òø%ÚlV`² »×?ƒÍ2¢¯‰þÓÅÞZgZhÖeø!Éùm|o£ ¦·5¡^€5ØÌI˜µ‘–°J­s|3—̦Na ‰bsø„$‰q I‘—cêjEðY9š¬'2îÜÑ®NYÜ}ŸÙý{$ >~ã‡í~J˜¬‡Ë ñò—9)DH m5™Éˆf$q[wlcÉf–Xã¬XѢ܌wJâšÔ. )J)Û¬aƒË¶¤O „vƒ=ýË?§7Þ§^žqçõoqÿÍשçR2´UK;ƒ“wßãàÆóŒ&´›cšÕ)óó nýàOÙ,4›ó·0V‰JÌ*BÒÔÞ:ÎøñæYžyé—™NÇlmmñòKŸe2ùwŸåÿìâß¹s‡¯}õ+lÎ~Êç?s™Ç¯îr°3`5˜Ø¢$h K55«sHkLVPN.š%íæ¢ó.*B#yÅ"ºP¤ºÁØRP8¡Œ¨³!‰ZL!:‘9„Ql2œ›¢ó!ðð.„Éc +'"¡GI_Wú¡u+ ú’EÁ£B.æ ŒÁš>1ŠRKÊëœ%Ö3t¯×‘P7´õ‚bp ´C'a/¤(ÝÛ¶YÒ†{üÎwå–¯š€Ë5Á%’òØRüýý‘ctp•f³¢ÝlXßçÖ_åâ£÷p¹Ât‹®3%·Ì¨Zϛǚ׫'ùä+ÿ>£Q_|ù/½Ìè_“®õo³ø÷ïßçë_ÿ‹“¿ä /<Îã×¶9Ø2,"NG9s½H¨µRD ¾AE%&ÕhWÇ´›“NŽ–‹‹$0þQâY Ñ7·Q‹T;tè•èÉzcòÁ¦ÜAhSI¾‚Ͱ½\Ô i³§ˆËGÒ5:´r„T¡1+o¾<Öà[‰ÇuCŧ(¬dM×9ìA2„vƒª¸á>±˜ÐË'¤¤1¹´âc+íþõù‡Ìîÿ”•;@ kÉô3ŽÞÎu½$ÖÒs¥¢­—¼÷ýo“¦|—v³Á媳~%°ŠÖC› ªá­†wÔ‹¼ð‹‹^¯ààà—^|‰~ðW²ø''§|ík_ãÞû?æ /qýÊ€££“A—eH ñÈ›¯„`¦4->‰¶>#…ªVˆÂ9J¿9—ÌA7¤iÎñÍœD’ó7iŒQR~¡»™@¢]czû„vNh–˜­Ç ”PeŠž#¾Y’ê ¬Ë±Y)0-Ûïh¨â®&6X£)¬“•j¼oºáäé¬]bCÄû5íò¾$¬š¥ ÄJbVP¯æ¬gwd`”¸Þ.¡©ðÍ[–˜2§Y5¤L¦áœïü!(æÇ À8%[>Ò2!Q·°i¯ß³|\þŸþü¯P—/]æ…^¤,{%‹qqÁoý_ç½7þ˜_|ù:7.¸|´Ítà(³ˆu[Äz%ç;J,Z)H¿"5RòÅ$GS@T]ï´ 5É7„¶¢mfx·–FV»’û^‚¯=h½ÄeÆŽ0¦©{Ldý]©åm!ó„6È.›÷‰ÍJB8S‹²9©i§‘ì#!bÖÇÔÃZ/ÇOÇrt½Ê%)¥]JX›SÝÅëÈE+ƒ*ûÄVÁâ ãzäý=Œí#bûª‘̬³òH^‘ÙÀB²ÌÏ<Õª%—rZzÓbT4 ¬šÈî:î¿À‹¯ü ²Ì>òåÅ¿û,ÿg±Xðû·yý{ßä•OíóÄõŽöûLšÒÔ¸¬ T3šÕ [N»äÓ¹´P“B›’è±^›Läo¡&´k|½Æû bƒV¢o0ªÀå9)b+w…äÉ2‘²7›à[z“\ïH´ºG6¨àØ“LYñ˜¼GfÇBSï˜ÊyŠ ZÑ^äq-%¦QÈÔVfº&€àYc°2k L ¦/3•ðMM¥J ’•ú[W1®À¿Â/Øb‡dK´-(F\~B4S€+ ÀRlæôÐ, ò³šR¡ ¬bäÕ»9[_äåÏýu¬ÕÜxüŸüä§®YþÏ.þjµâw~çŸòêÿù ^¸‘qóʈË{v¶ÆôzÖ guz¨bºH +A³(Q*_I´Õ„¦"Æ g>¶ÄjMŠ !ÂxkÄèàÅöS˜|¥K¹ð…u'Šm¨—§4«cªÙG 3òþ¡€/â†XÏ¥dLÕ£v3Çå}´+ìÔýIR2^~ClrtM •Xõµø%0™ÔøFD5É@hà2+ÐR­$%†’Ü’6 ÚzEŠž¼7”Ì:,Vê Õ®Ð:Ç–zÓË”Ó{Ô”;(²Á„|™>¶äôýH3K`À›DEâ/NKÖ¿ÌK/c47o>ÉóÏ}âçšåÿìâo6~÷w—ÿëûgýòçÐZ=òåÿ<³üŸ]üº®ù½ßû}¾óÏ‹ç*;pnçìL%qÓl=J…¢^Ü•¬‚ÐÑã+a!ƒ]&—ž%ìa‹ZçDßRlßÀ· õ\J*‘L >âCgÌ4$8 ’”bAh ÑWÄúBÊ®¬ á;‚ÆZG4 ƒ”€&‘ét³-b;ïfž’‹¬s´ŽÄÊ£óÀ¤tªR}b\’’è4UL"ÅSê¥<$ö™P:ǸÎÒ¦ZŒ–v´E˜b€êÊíJŒ-î^£:»O ¾­H¾Áf9µRḛ̀^y‚†Íìàe>ûÒ/`Œæ¹çžç©'Ÿþ¹gù¿m[¾ùÍÿ…ÿõ_çñÑ1W· »Ã1Ûã!enɲ›e„vA¨6èlÖIÉ“ÚjÞEÚ.!Ô(“˜\ùý§%³@YB8U bCWÙH:hÚ ¨$o{ ‰VhÝüiºÔ‹Sðs‰¦ÍGh—Ëld3#VKbÑÇå}”²ø r2 ä2‚ÙK5Æ–¢B†G)Š<ß=’ë|ʂ!Ñ¢b/…vhbGb±RÑ4 A©®Ã+—`SLpå„°¼‹¶=ŒÑXWl‰TŒíw !ÎJ”•¥ðí‚|8¥ï“_´4¡æbyÏ^åÅ—ÿΞ}æ9ž~ê™NB%´‡Î•”:Ê¿Åâ{ïùö·¿ÍïÿO_áññ97sv†-£"ÑïåäýmR³Æ”šå¿™£C+A ¬Ë­¤¢ÅP“RbzøÅð€ÐÔÝyY@”Þ¿pŽ%ð*†(~Æ®|JàŠ©µMðÒÓW¨!5kR³Ä Æ(•°Å€h´hóc q–¡T&\„¢p€ˆm•É$Õ+¶RbveI?"¦[”í¡‚”ƒ©c-j×—Û˜› Ù%¢%!A[6ëcm:aÚ\ m5š‚7BöÖ–¤½ñž`^­$k©¬Ïº™q{i˜~ö3LÆ#yòɧ0ưÙT¼ñúë|ðÞOñMÅÎÞÏ}â3]:êÒÇþÍ_1Dþàÿoüã_çjyŸ›‡‡[–q¡(¬ÇjCØ,iñd$ëÛ Òæ>Úe(•w@kwF_Qöɇ‡„ºó3QZFÔ6HÖa§Å ÍBˆ(,•‰‰VìêR”^;Æ õ‹Ž«dI¡Ai‹-·äͯŽ1ùŽøó‰²xÊ­' ÎÎﯔ{äö%µ²“râ›L©+Iæ6ºó_ÄØÚ‘ e5¶(Ë{Ä( É,¡Yu1~F,DRðR7aÊ©$²cmKR»ÂÚEo¤#1p 6mâTøäã7°Öpóæ“dYÆÅÅ_ûêoðÚ÷¿ÅÀŠ^°õPLŸä?úÿsþÆ¿÷ËÿÆ£!¥ÄýË?æŸü毱ßåúÔ2ÉCÛ£°šÔT,ß—ñ4»˜¬'ýûzERŠÔz)Ѫ éòm$3§nªû¼)#)qÐ&R7ü1`z¤ÍBÊŶîIa“`UR tŒµo{d…qkËe–ä—´›sQPek!‡aIÔ]çÃt°FÊ< 1{('Dz]G²†FàPѯäx²=™2ª ¬–]Eb³îMÖß!FG=Àf¹€pÙ›$_a}SKÆl»ƒÂàQíã,í<°¾¸‡¯jòÑ;Ü7bÀ÷§ G#F£Û[Û4MÃw¾ó~ô½oóüõ‚l½€~K4w/Þà·¾ü_stù2O?ýÔ¿ö8øþ÷À?úï~•þì'\9„©K tÀ„Hª¡-‹p›þtiß®‰¾% ”‡¶ÝÐVKa­Ï!Âèèz“#bÛÐ6r4™m=„ZºoÊ€ªºpé |='zOLœÙÉ`ò-9*’,~ô Q%tªhC¤YÍÈÛò–» l×0Ó¹°ÿtöH‰„ß„¢»i'Û›-¡¹èØJ9ë³Yžþ”è×Ôë QùªISŽö)·o ¶$Ò7+%iÕÒAT`ò>ù䮘QÍ!4ÒÁÔ›B'I‘úâ>Îõqå^YQÿ:#gx¬ õÝÛfqò€ª 0,±Ö0ŽpÎñáGòƯs´Speg@¸óÃ]CMŸþ@óê÷ø³?ûO>yóÿ÷ÜõÕWùêûp'¯q}¶ m"O´ÙดB¨f ú“ðHx".š–Øˆv?¶k´vl?öYz£lÑÇŒhêõâ!HMO”73­ÄŸ¸9¥©Î„Æå$“HÕ&È›«M)=‡Ø¢”%¤ ÅêtC1á{ò~« ãú¤(l ±Ø=йì.¡B¥ q-´‘ê‚ÕìœÕù)›Ù‡øæŒ«î"¡"T fwˆ-'äƒ}†Û×èïßÄ{]È"µ(k1EŸÌÙÌOˆa-¹«)b»–³N9B³&4iV&?ZlÑ)ÇŸ?H–¹9ÇôˆÁwr|LŠ‘~ia~ÆH×l uH”N³XÌišúÿãòQJñÚë¯ó•ø«ÄÛ¯òøvb§§çà‚ ¼…Ø&)oTâüþ;çh×%'M½9‘푆М2œ^f0ÞG© P’|H¿·'Ú†ù}ªå9¾®ðÍßT¢ÍoΉÈF™\ŒIâæYŸ ’Bé>¾›ÆIË5¢M†÷5ýùÆ•¨Ð—ĺ¸îX™’h–ü9)lU…²‘”Ä ãëË“c–3Að„0—{M’ˆ>´F'Ö•&¥Šjq—fuÎâüÓ£gÉú[h7Ää‰Öëâù\ojv_xÅ*ÇêºúQXwÕÅ\Š´ëmµ!´Æ 0Ù¥-Õê.Îm˜î+ÂÉ‚MUѶrQiۆݽ}Þù¨at|—£žtÞ ÒxMÈ÷ØÚÚÆû@žÿ«oþ[o½Å—í°~÷{ܘFv{ŠI å“„;&‘B)Ê(fwçô&s4Ú•6¯bÚ€_ÝÁhÇxç †»WHlÐnLhV$Î ¦†XSô%d²­×œß_³Z<ètú¥ÁºQ7ÆmPÑšµ ]’î¢é,¸±mñM…±2™Ý¿ƒÍ4…Í;¤Næ_l Z)ç’†”K6BsNªn#¬gs§whªÆH/!„ƒÅf#bZ¡Â”Ãät,H1à›¥ä'ÇŠ[¦GÏ‘õ#ÚöQ¦·q„X¡í&Š›š™ôŽÃŠÐlØœ}„ɇ‚ô÷M;èæÔ–¢? è×L¶=ýÓsܽÇòñÇðÞ3 ÙÝÛáãýçø¿ÿü6~˜HýÀ"Ö¼ö`‹ËϼÂѥå5¬µæÝwßå×í¿áäµ?æ‰Q`·PL¬¢HBˆkåC3ŽBÙDj¡|üæûl]z‚¬øÍÍæ¥Z¶¯|—|;Ã7st¾G¢$T3’Z¢&µ˜b›^ÿÙðä‘\]›cKð&EàSu°ÊdëÆ}$Õ’Ï—b$´“õX^,é.0ÙY ÁK_ÁÚÎz¿!¥ • ]ÎYÏÏiV3Ú͆ª®­XÎCXÉC×Éô„I8É=øÉo3Mˆ­,ÍúŒÍü®·_ŸaÊ©˜Q¢Ç¯OHÍR³Åü'ûðKtH×v½$\1¥Y_°>»Órl–có>¶Ü¦®Î9?k¹³4\~ì;;;ìîìr÷ã;L¶¶a|ÄG댗#NÍ ®<÷Ež{þyžîy&“É£Åÿàƒ[üÃ_ýUnÿà;<>ôìбS”Vª ¬C‚tt¦ÜuCQø&÷ ù ÀoN ÍãÝ+£=‚¥³ÍǘÞP¦»E¯¥­ª ”KS$bUÓ‘N…î¡PøˆÉ‚ÊIäÄhðM+¢‘¶¢ZœÊÑI’ÚÜ(”V´Õƒôð›'Vnßw ^Q­—Ü}û-~ô¿ÿs>~ÿ òÒb³RêymdZH’7+UªŠ˜Ä‚/[¢&4¡†Dˆ>PŒd×Qbô1ÚÒÌïQ-îKÆ uØzõE×Ûåh6sü½·;©³ŒS•<ÆôZº™ã× ³„ÅÙOøÉoptxÈK/½Ìg^x‘×^û n>õ$ݸA‘,slmMyæég9<<’¥Öܾ}‡¯|ù×¹óãïr½lØ.4£z8+±P"Wì¬æéaÙNŒrߺÅh{Èp:%)ƒ±Íú\ºš®À 1Å6(…)¶ðÕ)Zçò0DðmDù5¶ÒßJ¨TcûGøÕÎNNX-V""IQÒ8š51 Ùœ Iá›5ÖåhïI6²œ-Y?`¸»¢LÑÊÓÔ ë³û ËÓ¬f§,ïŸr:?%Ÿd´Í†¼³Þ£s¬2x¿’äTå¤é„€·QFÙhy¢ uFÒCê9³ßdtø K¬[‚õ$q¬ë«±¤ØÒ¬ŽiÖç´ë1œÚ$È}5µ-ª¿|jZ,šzQ L›¯ÿÁw˜L§ †Cž{ö9¾ø‹¿ÄÝ»s1»@¡˜L&Òë‰à¡”ë7¿úeNßÿ3^ùÔ˜x7P´ ýL=bKëògVIà”aÄw*YÈ4›ŠÔlEôR&%åPÎ’\´ØÖµ+¥mª­$y³$ ´ ±{S5ʸÑÛå6á½7Yž_tÝÌ–äk”²ÔÕZ4üZÚÂh'÷•ù)¾¾ ©<§pqëiëSÐçRÇЄ¨™.i­"O _·„(ÖÐ)‘P™4ÏŒ´¦I±S,yáÐJ£M†ÍFR Y…R†Íâ!4 w6X7ĺŒzñ@>ehë¹äø¶¢Y­Ùœ]Ю½í†WÈÇ[$¥)†[¸,ÇÕº¡®k®<Ûg¶¹C{Ѱ9û˜?ú½oж-1Dž}öYžxâæ£s^ZÁòûï¾û.¿óO~›{oÿ1/ÞÜáÊîˆâjû¯ßFµµˆ.R× B„¶3¡<\ô‡3•:i ² Ögï“ ÷åÖ_Œ°Å¶xþ…¼Œ-·h×'ؼOh›Î “‘4DQª u˜—lxÄÞöö\||‹zyÖI[ªõEwûwÝÔ´!y¡Š´mÍGoÏÙ\ÜF… ñj*™äjO‚QBIÓŠ¶ñ’¢J«Gå%ŠNÄ©ª©Ò£Æo~ÿÛÜ»{‡ÏáùÔ'?ÉÞÞywÝoššãã^ýáùî·þgÂñùĵœËS¸´·ËÖ䀫W¯ðÞŸ¿Îúômt·å˯GpqÝ-~T<|ñ‚OTÕ ›²r(>üb‚í_B©B¼øå.˜\º~ˆU[»eB=Ç7I2õL!íV_ +h\ïÝ«‘ÁhÌ­7¾Ïüä1)|'ËŠJB›–uKŠã4«eËÉi#‰ŸEã¡n”.¦R©[ÿDÝ@½Û¶‡Ò=© ’*‰±ˆŽI¶F-´Ue2tlðÍR²•“ÄåÐ&')á.Ö«3Q eCTÒ$;B§»>=…¤9xú%>ñEòñyBt†2&lD­#Îåä£C|=Ç®g\yrL:¥©×üÅ> ߆N~Äïÿ¯ñÝ«^~œéÖçg§Ü~ÿ].>~‹muÊÍ}ÅÑÐ1É ì€Â Ùºv…Á(ãý¿Áñw»‰f§Ö-¼îÚï1&¢Râ–- ”룋&³Ør„íï¢ÌDî-¾Â7kLÖ (b$¶5¶a¬#ù–%A5Ä mGbŠ žvu‚ëÉ)#®}âsÜ~óuîð&mUm±2H½‘ëd£‘foG~Þj•°Z~>B%DýG_MŠ\?`z0§¿Øå©®k(á)V§Ñf(èå¤eûSN ÞtYÂ=bJÄXɧ§ 1œh-} ¡i±Æb÷žø4ƒÝëŒ/=C6< ´‰°^ÊveKÙr4R~[$ÛC§„ -ªÙ0ÝÙç…Wþ‡W/ñÞ;ÉÐ>`7[p÷äñãIÝXŒ…\{¦}Ï“#ÅN_±S&¶²–•º}r\–ÑNyö _à§ùkœ}tK˜„JlQSÜ“XÛ 1Qއ<ýÊ/1=ºL»¹‡J¦wt³¥56ã›)œ`\O@ ¢÷  :ëcêÑ/åMKÝ6›¡>&6g‚щ3ŠáU®½ð7¹8¾G=¿-€-ÕýLÝ奭 ,àèjŽË›‹%®ë`¦”0t›(Aê@N‹Ù†ÍÅ9ŤÆäƒŽš:›¾ÿ'Iº—(™ð©ÕCw7P.Ìø‘ºé&(Rꌻ1RÀüWÿåßûRï1´=™RWŒÐF¡\.bÅî¡ëY§°Át%‰5Ø\,ÈÂ’¡]³54Œm‹{vLàp8kö†Š­B1é+F#CfÁ~…B1ؾLo¼Ood±¶‘NZ'¿h œÎ2…6PŽ'|âWþCönþŠ@»¾KÖß#ÞJ—Ɉ¨¸žÛZÒkb]Îõ;~uÂØžPO’’²¶Ør‚±=tG-O1¢”Åõ¶p¹ãÁOoÑ.ù€[EôÒ¬Èz†í]CoP}¤Yvxº–@’*‡ íŠ"FAÖËqÅÃð~8ŠVÒÑ#ù. 4 ꥳô¥Øt¢"†¥\]R¢©Ý]'.º40ÿÅßý¥/W ºôn]Œ…1c´Ø Œ‘³Hˆ5ÆtsgÝ­—Í ƒIDATëÐNÄZyúã]{†ƒëÏ“9EZ}L_{l›pbZʯQO1ÈÃ=ƒs$ipÄZúÛa½ lî’—‘­ÃÆ;Êé*^`t÷ŒÓ\ñs>ù9²b,ÈÕ\¹‹+ö1v ;eˆmCh—®ÅЙär¡Q¤jÞMÝ¢LÝÎÖ•EÛ!Ê”(3­_{A kz“=ú;‡ÌŽ/XžÌð­¬ääpÈö¥ YY|bs± ©8¢—6Ch(VK™º<-h4£CQg›ž´é“8‰TòÄXÉëìQ¹HJBtMQî¶ÀšB ÝšbÛý±¶)„^f•6\¤Kù&H.¸~Ê–„°Áè.ñ+6bU2%É{¬-1#f!Ñ›ì1˜n±½·ÇÛúî¿õ£F…"3Gp)A£ð놆%Î%ŒÐnΙ¯O9‰:yt}Ûk06‘é%“­Œ¦VÌN+¶.]çÒ3¿ òk¥$¯G»<Æf;$7xÞ„²˜¬cþ$#Ú{Ðq 6ï´u-Êè.˜éQ:‡/ð¥FKôÎr.=w•É•gøóoü3.nÿ”+Ï_b°3&4‘ÅÉ6gÒ-D.ª¡‘Íèá=EwÏ‹ÝÐŽéO!ÖT«[;ô ”2"&5yçLn:  FB”ÜedÜ­µ%a°$æÈ…R®æïÿ§çK‰œ¸žã×§òM¬A¥‡I]È$)ÏQF)dº„C(­0ùPL Úb²‚,ÏèFLvGLúT³9a½Á%½(V(c’ĪªØ±yD› u¢>¯8yg†I´M$Ú Á[®}êól]}ceÒV-nS/î€)Äëo-ÚÒ·Obá~žD ¨ä\ žô0S+D¢¯Ä:U×xïen¢è$”ílØm ¢¯)‡»l_¹FoÐ0œ Hä,Žï³:9&Ô±»k(yó“t0S´–¶€ÁHÑ+`P(¶öì^&…DVŒ„²–R§#ˆÒTVú±"ù¤ݱ…$ªd|ÝG‰"}S¤®ÌÌ…ß”V„‡É5X3À”#™›ÿÖž’Â9ªË±I¾Áä#üæR@—2m²•Ú•x•ÈÊ>Óý+líP &üé?ý&Ñ{üZ£KÈTB[ù R€äk´³¤:SS-#ÞÏ©ç+tÙÃä¶5<~ƒñáS(b#™9í‚z³F×ßlðÕ9åÖMŒˆœ+**+óˆ¡C j…BãéF­õ©™.¥ð-èÆ½ì¨©¥YŸB¬ð›ûhÕ§ìXÜ{—ÕÅŒÅé1¡‰r/Zé^ê™[‚žÊÃh3ƒíghä~cM#‰ åÆÕ#43´ÍѺÿ¨¥Œ.Åš®fT©rù÷ܸ»$GB# ¶@Ŧ£‹ˆ\^e=DÑ5il6 {lE™jaÎÐv*›F ´*|3“*!ÔWò¿3%¾ÙHƒÂe¸¼D¥’g>ÿ×ñMÍ;ÿÇ–k´VD­LREa ¸Î¨Ñ¶ØžÆ–U<¬ÝS—XÐßÚ‘èùÍ )l„ËSìa‹ÛøzRS´Ý›Ö°Ä˜’Hƒ¨R“áýªsµå¨ˆqJ-asB[ÏQÚâz" ×Γ'ú•$¦ˆ1‘šßµ˜,NÏY>¸Ol”Ì-ºmß•âõQ³}mŠí—(-©!J ‚?´+‘p·kB³Æ–û(eÅ·=1,QnH Ñ%³눱E#)c(×)‹ÀáùÑ*CuÈŸ‡ñW-F'TVbÜ“÷$Üj)£ÁôsF{Ï’\1¢m[êÅm6ç?FG¹/ÃS¿”}#ÚfÆÅÇߣ˜\c«÷ J«®ãAÀ`tƟʽè!•–¡QuÚ!ss0†([Ê}#Fl–¡²“Ë7ðÍœ“·ÿŒ¼ß#÷V1)RRÛ£­Và#¯hë9!xêõJZÿ6Ð,îÑnî£]AJRïgý}RÜ Á˜1mXuÖ6±Ž§”é—<.IÝH¡C a(¥EÙ¨Íßû»ŸûRJA¼ìÆ’šFyl1Ää#IîRšD3§MlÎ{@@k‡)vÑnB3¿M¨ïÉÃä[i"ňJ‰E0Þ¿ÁäÒ5§ï“š LÑuA|$5‰l+'_&]Á”SÙlN +±¬»1ÃO“õ'BÏžÍü#b¬èo=ËGÄD^MÄ7s¬ëcË1YoŒq…8Â_ŸÑ,>øu „êDh^ÚJ-íׄvoΉ±ÍXuR m2ÀCíLÈF‹ÕÍ)0%®¿+}€Íc{ø¶´¼odÊg4†ˆ±Š˜4åôc-)5d½]²bc‡" Uc2´HYCWáÄG½s‰ÇÎ_ÐÃ¥b×Q¡{æïÿgÿÁ—”í¡lAòK”ªDÙjKt>”­'y‰8‰Ø¶â½‹ÝåÈ×]‡ê!^}A"`Ëm1RTËn~/¶„¦7è1>8d=»O[Ïd; ƒö)†GØÞÊ:iDAg–ü‚áþÓdý-šzA½<¦]? 7¹‰+¦hçdÖõ0Ùß®iV÷°™b’Z9âV·i«‹‡Å·”~ÍRÚ:LÖ±øM&†Ðšs´buOÚKÜžvجO6œRÍ/ˆÕ\äߦGŠ+B³¤YŸ’Bd3?¡Ùœƒ7Ø‘\æXc¢ ‘ÞöYm .b´d>•ꮦ—…÷Ò,zD<ÓØ|ˆr®»£Å€-ަ¤t¡h“ï Œ!Ö'VâS39Ibhåæë«œÔ€ª$õ#ÑÏù¼Jãz\1’UX¬ó¢«*LWÆ-PV1¹ò O|ñïO÷°½Œl”á†e3|+HèÎ{-Ì¢Á/E¥iÓT§r~“ðíŠz}O¶x+i^Þo¨6˜Ÿ¾Íj~‡z}"#ÝÈdóÆÄ­“ouѪ%JgÄ$dQié¶i-[¿îšc]þ±JA.^QäÛÖ˜.ïWáqy. ª­+”{¡ º&tªu)&B‡Öóë TŒ’ö¥\ĵWJÎZ¹iQÎÈ Âc¬|æC‚=”è L†12bŽíšÿÅí‚2qßj%tEXtdate:create2017-10-09T16:55:12+02:00üÔ†%tEXtdate:modify2017-10-09T16:55:12+02:00Il:IEND®B`‚crispy-doom-crispy-doom-5.6.4/data/setup8.ico000066400000000000000000000102761360717211000210770ustar00rootroot00000000000000  ¨( @ ˜Òïÿ Øóÿ¨Úòÿ¤×ñÿ“Òîÿž×ïÿ¯ßóÿa¡Ïÿe¯ÿ,…Âÿ1Çÿo´Þÿ~ºàÿzºàÿc¯Úÿnµßÿm·áÿdµáÿj¸ãÿÁæÿzÀçÿ3‘Ïÿ&…Çÿ)„Åÿ u¸ÿ2ŒÂÿ„¾ÞÿŠÂàÿ‡¿Þÿ†¾Ýÿw¹Úÿk±Õÿ¥Óñÿ¤Õòÿ¡×óÿžÕñÿ¨ÙñÿªØïÿMƒºÿE™ÿh²ÿo¼ÿ|ÁÿY¥Õÿ…Áæÿf±ÝÿŒÃçÿ“ÆéÿŒÆéÿu½åÿ‰ÅèÿËìÿc®Üÿ k¶ÿ]«ÿo´ÿr³ÿPžÊÿo´×ÿl´×ÿe³×ÿoµØÿk³Öÿe¯Ôÿ‰Åéÿ°Ùóÿ®Øñÿ­Ùòÿ=Ucÿÿÿ ÿGhÿ4ŠÇÿg­ÿg¬ÿj°Üÿ}ÁçÿžÏîÿ¢ÕñÿŸÔðÿËëÿ‰Íîÿb±Ýÿh®ÿ l¯ÿ]¦ÿJ–ÿTÿSŸÇÿo´×ÿb°ÕÿY¬ÒÿS©ÐÿT©Ðÿf±Õÿ"ÂÿP™Íÿs´Ûÿ%¤Ðÿ(™ÈÿJ«ÓÿœÖíÿuÁäÿ[¥ÑÿNœËÿp¡ÿÿ¶¹¸ÿIIIÿ---ÿ×ÙØÿàááÿqrrÿÿKrÿ^µßÿ9¤ÖÿC­ÝÿuÇìÿc¸âÿP­ÚÿK«Ùÿ`³Ýÿ]°Úÿ—Üõÿ¨ìþÿµñÿÿ·ñÿÿ¢ßöÿwÄåÿYµÛÿj¾ßÿ™Øîÿœ×íÿ{ÂâÿV¦Ðÿ=“Æÿ¼ÿ ÿpsrÿÕÖÖÿÜÝÝÿàâáÿàâáÿÞàßÿfhhÿ %ÿS…šÿc»áÿ6 ÒÿrÀåÿšÔðÿÈêÿ„Êëÿ’ÎíÿvÁçÿ‘Øóÿ¡áøÿ©æúÿ²êüÿ¦èüÿ£æüÿµìýÿ¶éúÿ«Þòÿ“Ëåÿj´×ÿ:—Éÿu¶ÿ i¯ÿ!HÿÿvxwÿÖØ×ÿàâáÿàâáÿàâáÿÝÞÞÿ[\\ÿ,2ÿWާÿ[°ÚÿNªÕÿY¯Úÿ>¡ÓÿZ³Þÿ^´ÝÿU±Þÿ€ËìÿšØñÿ˜×ñÿ•Øòÿ‹ÖòÿrËðÿ‰Õòÿ•ÖïÿÒêÿ¢ÑçÿPžÅÿe°ÚÿqµÿD†ÃÿM‘Çÿ.NÿÿknlÿÓÕÔÿàâáÿàâáÿàâáÿÛÜÜÿOOOÿ!5>ÿMŒ©ÿV«ÕÿY¬ÖÿQ©ÕÿC£Óÿ= Óÿ=¤ÖÿqÃèÿÐíÿ“Îìÿ„Èèÿ…Êêÿ„Êêÿ†ÉéÿÂâÿ”Éãÿ–ÈàÿVŸÅÿ¨Ýóÿ‹Ííÿo½çÿqÀëÿU¬àÿ7ZlÿÿegfÿÐÒÑÿàâáÿàâáÿàâáÿØÚÙÿFGFÿ";FÿP•´ÿ_²Ùÿk¹ÞÿdµÞÿK§×ÿ+”ËÿM«Øÿ†Êéÿ‰ÇæÿvºÝÿm´Ùÿy¸ÛÿƒÀàÿŒÆãÿÌäÿ‘ÂÛÿp¬Ëÿœ×òÿ}Êïÿi¿ìÿQ­âÿ]¹çÿÕñÿGhuÿ ÿZ\[ÿÌÎÎÿàâáÿàâáÿàâáÿÕÖÖÿ=>>ÿ#>Jÿe¨Æÿr½àÿb´ÜÿZ­ØÿN¤Óÿ&‡Áÿx¿âÿƒÂáÿ^©ÑÿKÈÿB—ÄÿT¡Ëÿt·Úÿs·Úÿ\¡ÊÿW›¾ÿ ÚõÿyÄíÿe¸èÿM¬áÿdÁêÿŽØóÿyËîÿ4f|ÿ ÿPRQÿÈËÊÿàâáÿàâáÿàâáÿÑÓÒÿ454ÿ,COÿu»Ûÿs»ßÿu¸Ýÿ5‘Çÿ q´ÿ^ªÔÿo´Øÿ>—Æÿ€·ÿ%‰½ÿ\§Ïÿ"}µÿ0‡ºÿ;Œ¹ÿ5†´ÿ¤ÞøÿŠÎòÿq¿ìÿM¯äÿX¼èÿƒØöÿgÊñÿdÆïÿEwŒÿ$ÿFHGÿÄÇÆÿàâáÿàâáÿàâáÿÎÏÎÿ()(ÿ8JSÿs¶ÛÿGšÌÿ%ˆÂÿ|ºÿ!ƒ¾ÿ!»ÿ ]žÿCŠÿA‘Áÿm´Ôÿ x­ÿd¡ÿ%±ÿE–ÀÿÙöÿŒÒôÿÊòÿC­ãÿR»èÿ˜æüÿ†ÞúÿŒàûÿ’âûÿažÿ*/ÿ>A?ÿ¿ÂÁÿàâáÿàâáÿàâáÿÉËÊÿ ! ÿ9Kÿ0Æÿ@Ïÿ3”Èÿ#‹Ãÿ|»ÿS‘ÿ!YŒÿy½ÞÿX©Ïÿ,‰¹ÿz±ÿ_©Îÿz¸Õÿ‘Òóÿ‡Íòÿ“ÓôÿnÎõÿ“äúÿœíýÿ”êýÿ–èýÿ’æýÿçýÿoŸ¬ÿ&5:ÿ353ÿ¹¼»ÿàâáÿàâáÿàâáÿÃÅÄÿÿ?Pÿ[ªÔÿS©Õÿ_°ØÿX­ÕÿS±Úÿ”Ûôÿ‡ÊæÿM¢Ìÿ$‰¼ÿ‰»ÿ~»Ùÿl²ÒÿŸØõÿ‚Êðÿ–Ôôÿ„ÐóÿÚ÷ÿÞûÿˆÛúÿ×øÿoÎôÿkÈñÿfÂìÿ>аÿ1Bÿ,.-ÿ²´³ÿàâáÿàâáÿàâáÿ¾À¿ÿÿ)ESÿp·Úÿi¶Ûÿb´ÜÿÍíÿ­ãõÿ}ÃâÿHŸÌÿ$нÿ+‘Áÿ|»Ùÿb­Ðÿp¿êÿÓôÿ„Îñÿc¹çÿY°áÿd¶äÿ`¶åÿ>¨ßÿ’Òÿ‡Éÿ~ÁÿC¨Ùÿu±Èÿ*DOÿ'('ÿ«­¬ÿàâáÿàâáÿàâáÿ·¹¸ÿÿ2LYÿyÃåÿpÁçÿœÛñÿžØïÿk·ÝÿO£Ïÿ>™Çÿ;›ÈÿqºÚÿ~¼ØÿE¡ÔÿY±áÿuÄîÿd¹èÿM©Úÿ5’Êÿ}¹áÿ‘Èëÿm¶âÿ%…Âÿ‡Âÿ‚Ññÿ®æùÿŒÃÙÿ*DOÿ"##ÿ¤¨¦ÿàááÿàâáÿàâáÿ¯²°ÿ ÿ&IYÿEy’ÿ;JPÿ!ÿ0<ÿ:u“ÿ[«Ôÿ>œÊÿc¶Úÿ|½Ùÿ_®ÙÿM¢Ñÿ`­ÚÿR¥Öÿ!†ÄÿI˜Ëÿt³Úÿv¶Ýÿ|½âÿ`°ÞÿvÊîÿžÝõÿ”Øóÿ„Ïïÿv¹Úÿ /Cÿÿœ žÿßáàÿàâáÿàâáÿ§¨¨ÿÿÿ243ÿVYWÿ=?>ÿ-7ÿQ—¹ÿ?›ÉÿO¨Òÿr¾Þÿ^¯ÚÿX©ÕÿX¥ÐÿD–Çÿ*†¾ÿ]¨Óÿa¬ÖÿI ÏÿLŸÎÿL¦×ÿ›Üöÿ²çùÿ•Øôÿ}Çëÿ_±Ýÿ$‡Àÿ'@ÿÿ”˜–ÿÞàßÿàâáÿàâáÿ¹»ºÿ¼¾½ÿÏÑÐÿßáàÿàááÿ,,,ÿ ¡Õÿ ‡Èÿr¾ÿaµÿÄÿT«ÝÿœÜöÿ!ÿ_a`ÿàâáÿàâáÿ¡¤¢ÿÿZejÿ‹µÌÿ6Fÿ'('ÿgjiÿ ÿ:btÿ@«Ûÿe¶áÿi´Þÿd°ÙÿZªÔÿ0“Æÿi­ÿBšÊÿH¢Ñÿ&–Ïÿ"”Òÿ;ŸÚÿE¦Þÿ)’Óÿ8—Õÿ\´âÿU±àÿD§Ûÿ_ºæÿ*BMÿ9;:ÿàâáÿàâáÿרØÿvxwÿÿUciÿa¿ÿ-Paÿ ÿ=`rÿ|Æéÿ_·áÿm¼äÿh¸áÿk·Þÿj´Ûÿ#‚½ÿy·ÿT§Óÿ?¡Óÿ šÕÿ—Ùÿ–Ùÿ#œÞÿyÑôÿ{ÏðÿAªÝÿG±áÿ^½çÿ2¤Úÿ;‰«ÿ& doom_se.lzh #endif The resulting file is an LHA archive file, and it can be extracted using an LHA archive tool (there is one available for almost every operating system). #endif # Running the game #if __MACOSX__ Once you have an IWAD file, you can specify its location within the graphical launcher program. Click the “Configure...†button, and then click “Set...†for each IWAD to choose its location. From the main launcher dialog you can then choose which game you want to play and click the “Launch†button to start the game. If you are an advanced user and like to run Doom from the command line, you can use the “Command Prompt...†menu item to open a Terminal window. The DOOMWADPATH environment variable is preconfigured to point to the locations of the IWAD files set within the launcher. You can launch the game with a specific IWAD file by typing, for example: LONG_EXE_NAME -iwad tnt.wad #else LONG_GAME_NAME needs to know where to find your IWAD file. To do this, do one of the following: #if _WIN32 * Within Explorer, simply place the IWAD file in the same folder as the LONG_GAME_NAME files, and double-click `LONG_EXE_NAME.exe`. * Run LONG_GAME_NAME from the command prompt with the `-iwad` command line parameter to specify the IWAD file to use, eg. LONG_EXE_NAME -iwad c:\games\DEFAULT_IWAD * Set the environment variable DOOMWADDIR to the location of a directory containing your IWAD files. * If you have multiple IWADs in different directories, set the environment variable DOOMWADPATH to be a semicolon-separated list of directories to search (similar to the PATH environment variable). #else * Run LONG_GAME_NAME from the Unix console with the `-iwad` command line parameter to specify the IWAD file to use, eg. LONG_EXE_NAME -iwad /root/DEFAULT_IWAD * Put the file into one of the following directories: $HOME/.local/share/games/doom /usr/share/games/doom /usr/local/share/games/doom * Set the environment variable DOOMWADDIR to specify the path to a directory containing your IWAD files. * If you have multiple IWADs in different directories, set the environment variable DOOMWADPATH to be a colon-separated list of directories to search (similar to the Unix PATH environment variable). #endif #endif #if DOOM # Playing with Freedoom Freedoom is an open content project to create a Doom engine-based game that is entirely free software. The website can be found here: https://freedoom.github.io/ Check out the [Chocolate Doom wiki’s page on Freedoom](https://www.chocolate-doom.org/wiki/index.php/Freedoom) for more information. # Playing with Chex Quest Chex Quest is a game based on Doom with some minor modifications that was distributed with boxes of Chex cereal in 1997. It is possible to play Chex Quest using LONG_GAME_NAME. To do this, the following files are needed: * The IWAD file “chex.wadâ€, from the Chex Quest CD. * The dehacked patch “chex.dehâ€, which can be found here: https://www.doomworld.com/idgames/utils/exe_edit/patches/chexdeh (utils/exe_edit/patches/chexdeh.zip in your nearest /idgames mirror) Copy these files into a directory together and use the `-iwad` command line parameter to specify the Chex Quest IWAD file: LONG_EXE_NAME -iwad chex.wad #endif # Installing upgrades #if DOOM LONG_GAME_NAME requires a version 1.9 IWAD file. Generally, if you install a recent version of Doom you should have a version 1.9 IWAD. #elif HERETIC LONG_GAME_NAME requires a version 1.2 (Shareware) or version 1.3 (Shadow of the Serpent Riders) IWAD file. Generally, if you install a recent version of Heretic you should have a version 1.2 or 1.3 IWAD. #elif HEXEN LONG_GAME_NAME requires a version 1.1 IWAD file. Generally, if you install a recent version of Hexen you should have a version 1.1 IWAD. #elif STRIFE LONG_GAME_NAME requires a version 1.2 IWAD file. Generally, if you install a recent version of Strife you should have a version 1.2 IWAD. Please note that Strife version 1.3 does not update the IWAD, if your version.txt file says “STRIFE(TM) VERSION 1.3â€, you are still good. #endif However, if you are installing from a very old CD version or from floppy disks, you might find you have an older version. The most obvious symptom of an out of date IWAD file is that the game will exit at the title screen before the demo starts, with the message “Demo is from a different game version!â€. If this happens, your IWAD file is out of date and you need to upgrade. Upgrade patches are available that will update your game to the latest version, the following sites have the patches: #if DOOM * http://www.doom2.net/doom2/utils.html * http://www.gamers.org/pub/idgames/idstuff/doom * http://www.gamers.org/pub/idgames/idstuff/doom2 #elif HERETIC * http://www.gamers.org/pub/idgames/idstuff/heretic #elif HEXEN * http://www.gamers.org/pub/idgames/idstuff/hexen #elif STRIFE * http://www.gamers.org/pub/idgames/roguestuff #endif Please see the [Doom Wiki’s page on game patches](https://doomwiki.org/wiki/Game_patch) for more information. #if _WIN32 As the patches are binary patches that run as DOS executables, on recent 64-bit versions of Windows you will need to use a DOS emulator (such as DOSBox) to run them. #else As the patches are binary patches that run as DOS executables, you will need to use a DOS emulator (such as DOSBox) to run them. #endif # Music support LONG_GAME_NAME includes OPL emulation code that accurately reproduces the way that the in-game music sounded under DOS when using an Adlib/Soundblaster card. This is, however, not to everyone’s taste. LONG_GAME_NAME includes a number of different options for better quality MIDI playback; see the file README.Music for more details of how to set these up. #if !PRECOMPILED When compiling from source, be sure to compile and install Timidity before installing SDL2_mixer. #endif crispy-doom-crispy-doom-5.6.4/man/Makefile.am000066400000000000000000000213361360717211000210500ustar00rootroot00000000000000SUBDIRS = bash-completion MANPAGE_GEN_FILES = environ.man \ iwad_paths.man \ doom.template \ heretic.template \ hexen.template \ strife.template \ docgen \ default.cfg.template \ extra.cfg.template doomdocsdir = ${docdir}/../${PROGRAM_PREFIX}doom hereticdocsdir = ${docdir}/../${PROGRAM_PREFIX}heretic hexendocsdir = ${docdir}/../${PROGRAM_PREFIX}hexen strifedocsdir = ${docdir}/../${PROGRAM_PREFIX}strife if HAVE_PYTHON GENERATED_MAN_PAGES = \ @PROGRAM_PREFIX@doom.6 \ default.cfg.5 \ @PROGRAM_PREFIX@doom.cfg.5 # @PROGRAM_PREFIX@heretic.6 \ # heretic.cfg.5 \ # @PROGRAM_PREFIX@heretic.cfg.5 \ # @PROGRAM_PREFIX@hexen.6 \ # hexen.cfg.5 \ # @PROGRAM_PREFIX@hexen.cfg.5 \ # @PROGRAM_PREFIX@strife.6 \ # strife.cfg.5 \ # @PROGRAM_PREFIX@strife.cfg.5 \ # @PROGRAM_PREFIX@server.6 SETUP_MAN_PAGES = \ @PROGRAM_PREFIX@doom-setup.6 # @PROGRAM_PREFIX@heretic-setup.6 \ # @PROGRAM_PREFIX@hexen-setup.6 \ # @PROGRAM_PREFIX@strife-setup.6 man_MANS = $(GENERATED_MAN_PAGES) \ $(SETUP_MAN_PAGES) doomdocs_DATA = INSTALL.doom CMDLINE.doom #hereticdocs_DATA = INSTALL.heretic CMDLINE.heretic #hexendocs_DATA = INSTALL.hexen CMDLINE.hexen #strifedocs_DATA = INSTALL.strife CMDLINE.strife if HAVE_WINDRES WIN32=-D_WIN32 doomdocs_DATA += CMDLINE.doom.md #hereticdocs_DATA += CMDLINE.heretic.md #hexendocs_DATA += CMDLINE.hexen.md #strifedocs_DATA += CMDLINE.strife.md endif CLEANFILES = $(GENERATED_MAN_PAGES) $(SETUP_MAN_PAGES) \ $(doomdocs_DATA) $(hereticdocs_DATA) \ $(hexendocs_DATA) $(strifedocs_DATA) DOCGEN = $(srcdir)/docgen DOCGEN_COMMON_ARGS = -n "@PROGRAM_SPREFIX@" -s "@PACKAGE_NAME@" -z "@PACKAGE_SHORTNAME@" @PROGRAM_PREFIX@doom.6: $(top_srcdir)/src $(MANPAGE_GEN_FILES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g doom -m $(srcdir)/doom.template \ $(top_srcdir)/src $(top_srcdir)/src/doom > $@ default.cfg.5: $(top_srcdir)/src $(srcdir)/default.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g doom -m $(srcdir)/default.cfg.template \ -c default $(top_srcdir)/src/m_config.c > $@ @PROGRAM_PREFIX@doom.cfg.5: $(top_srcdir)/src extra.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g doom -m $(srcdir)/extra.cfg.template \ -c extended $(top_srcdir)/src/m_config.c > $@ CMDLINE.doom : CMDLINE.template $(top_srcdir)/src $(top_srcdir)/src/doom $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -p $(srcdir)/CMDLINE.template \ $(top_srcdir)/src/ $(top_srcdir)/src/doom/ > $@ CMDLINE.doom.md : CMDLINE.template.md $(top_srcdir)/src $(top_srcdir)/src/doom $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -M $(srcdir)/CMDLINE.template.md \ $(top_srcdir)/src/ $(top_srcdir)/src/doom/ > $@ CMDLINE.doom.wikitext : $(top_srcdir)/src $(top_srcdir)/src/doom $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -w $(top_srcdir)/src/ $(top_srcdir)/src/doom/ > $@ INSTALL.doom: INSTALL.template $(srcdir)/simplecpp -DDOOM $(WIN32) \ -DLONG_GAME_NAME="@PACKAGE_SHORTNAME@ Doom" \ -DLONG_EXE_NAME="@PROGRAM_PREFIX@doom" \ -DPRECOMPILED < $(srcdir)/INSTALL.template > $@ @PROGRAM_PREFIX@heretic.6: $(top_srcdir)/src $(MANPAGE_GEN_FILES) heretic.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g heretic -m $(srcdir)/heretic.template \ $(top_srcdir)/src $(top_srcdir)/src/heretic > $@ heretic.cfg.5: $(top_srcdir)/src $(srcdir)/default.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g heretic -m $(srcdir)/default.cfg.template \ -c default $(top_srcdir)/src/m_config.c > $@ @PROGRAM_PREFIX@heretic.cfg.5: $(top_srcdir)/src extra.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g heretic -m $(srcdir)/extra.cfg.template \ -c extended $(top_srcdir)/src/m_config.c > $@ CMDLINE.heretic : CMDLINE.template $(top_srcdir)/src $(top_srcdir)/src/heretic $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -p $(srcdir)/CMDLINE.template \ $(top_srcdir)/src/ $(top_srcdir)/src/heretic/ > $@ CMDLINE.heretic.md : CMDLINE.template.md $(top_srcdir)/src $(top_srcdir)/src/heretic $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -M $(srcdir)/CMDLINE.template.md \ $(top_srcdir)/src/ $(top_srcdir)/src/heretic/ > $@ INSTALL.heretic: INSTALL.template $(srcdir)/simplecpp -DHERETIC $(WIN32) \ -DLONG_GAME_NAME="@PACKAGE_SHORTNAME@ Heretic" \ -DLONG_EXE_NAME="@PROGRAM_PREFIX@heretic" \ -DPRECOMPILED < $(srcdir)/INSTALL.template > $@ @PROGRAM_PREFIX@hexen.6: $(top_srcdir)/src $(MANPAGE_GEN_FILES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g hexen -m $(srcdir)/hexen.template \ $(top_srcdir)/src $(top_srcdir)/src/hexen > $@ hexen.cfg.5: $(top_srcdir)/src default.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g hexen -m $(srcdir)/default.cfg.template \ -c default $(top_srcdir)/src/m_config.c > $@ @PROGRAM_PREFIX@hexen.cfg.5: $(top_srcdir)/src extra.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g hexen -m $(srcdir)/extra.cfg.template \ -c extended $(top_srcdir)/src/m_config.c > $@ CMDLINE.hexen : CMDLINE.template $(top_srcdir)/src $(top_srcdir)/src/hexen $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -p $(srcdir)/CMDLINE.template \ $(top_srcdir)/src/ $(top_srcdir)/src/hexen/ > $@ CMDLINE.hexen.md : CMDLINE.template.md $(top_srcdir)/src $(top_srcdir)/src/hexen $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -M $(srcdir)/CMDLINE.template.md \ $(top_srcdir)/src/ $(top_srcdir)/src/hexen/ > $@ INSTALL.hexen: INSTALL.template $(srcdir)/simplecpp -DHEXEN $(WIN32) \ -DLONG_GAME_NAME="@PACKAGE_SHORTNAME@ Hexen" \ -DLONG_EXE_NAME="@PROGRAM_PREFIX@hexen" \ -DPRECOMPILED < $(srcdir)/INSTALL.template > $@ @PROGRAM_PREFIX@strife.6: $(top_srcdir)/src $(MANPAGE_GEN_FILES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g strife -m $(srcdir)/strife.template \ $(top_srcdir)/src $(top_srcdir)/src/strife > $@ @PROGRAM_PREFIX@server.6: $(top_srcdir)/src $(MANPAGE_GEN_FILES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g server -m $(srcdir)/server.template \ $(top_srcdir)/src > $@ $(SETUP_MAN_PAGES): $(top_srcdir)/src $(MANPAGE_GEN_FILES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g $(patsubst @PROGRAM_PREFIX@%-setup.6,%,$@) \ -m $(srcdir)/setup.template \ $(top_srcdir)/src > $@ strife.cfg.5: $(top_srcdir)/src default.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g strife -m $(srcdir)/default.cfg.template \ -c default $(top_srcdir)/src/m_config.c > $@ @PROGRAM_PREFIX@strife.cfg.5: $(top_srcdir)/src extra.cfg.template $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g strife -m $(srcdir)/extra.cfg.template \ -c extended $(top_srcdir)/src/m_config.c > $@ CMDLINE.strife : CMDLINE.template $(top_srcdir)/src $(top_srcdir)/src/strife $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -p $(srcdir)/CMDLINE.template \ $(top_srcdir)/src/ $(top_srcdir)/src/strife/ > $@ CMDLINE.strife.md : CMDLINE.template.md $(top_srcdir)/src $(top_srcdir)/src/strife $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -M $(srcdir)/CMDLINE.template.md \ $(top_srcdir)/src/ $(top_srcdir)/src/strife/ > $@ INSTALL.strife: INSTALL.template $(srcdir)/simplecpp -DSTRIFE $(WIN32) \ -DLONG_EXE_NAME="@PROGRAM_PREFIX@strife" \ -DLONG_GAME_NAME="@PACKAGE_SHORTNAME@ Strife" \ -DPRECOMPILED < $(srcdir)/INSTALL.template > $@ INSTALL: INSTALL.template $(srcdir)//simplecpp -DDOOM -DHERETIC -DHEXEN -DSTRIFE \ -DLONG_GAME_NAME="@PACKAGE_SHORTNAME@ Doom" \ -DLONG_EXE_NAME="@PROGRAM_PREFIX@doom" \ -DPRECOMPILED < $(srcdir)/INSTALL.template > $@ endif EXTRA_DIST = $(man_MANS) $(MANPAGE_GEN_FILES) \ wikipages \ CMDLINE.template \ CMDLINE.template.md \ INSTALL.template \ simplecpp crispy-doom-crispy-doom-5.6.4/man/bash-completion/000077500000000000000000000000001360717211000220735ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/man/bash-completion/.gitignore000066400000000000000000000000511360717211000240570ustar00rootroot00000000000000*doom *heretic *hexen *strife *.template crispy-doom-crispy-doom-5.6.4/man/bash-completion/Makefile.am000066400000000000000000000031201360717211000241230ustar00rootroot00000000000000bashcompletiondir=@datadir@/bash-completion/completions BASH_COMPLETION_TEMPLATES = \ doom.template \ heretic.template \ hexen.template \ strife.template if HAVE_PYTHON BASH_COMPLETION_SCRIPTLETS = \ @PROGRAM_PREFIX@doom # @PROGRAM_PREFIX@heretic \ # @PROGRAM_PREFIX@hexen \ # @PROGRAM_PREFIX@strife bashcompletion_DATA = $(BASH_COMPLETION_SCRIPTLETS) CLEANFILES = $(BASH_COMPLETION_SCRIPTLETS) DOCGEN = $(top_srcdir)/man/docgen DOCGEN_COMMON_ARGS = -n "@PROGRAM_SPREFIX@" -s "@PACKAGE_NAME@" -z "@PACKAGE_SHORTNAME@" @PROGRAM_PREFIX@doom: $(top_srcdir)/src $(DOCGEN) $(BASH_COMPLETION_TEMPLATES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g doom -b doom.template \ $(top_srcdir)/src $(top_srcdir)/src/doom > $@ @PROGRAM_PREFIX@heretic: $(top_srcdir)/src $(DOCGEN) $(BASH_COMPLETION_TEMPLATES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g heretic -b heretic.template \ $(top_srcdir)/src $(top_srcdir)/src/heretic > $@ @PROGRAM_PREFIX@hexen: $(top_srcdir)/src $(DOCGEN) $(BASH_COMPLETION_TEMPLATES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g hexen -b hexen.template \ $(top_srcdir)/src $(top_srcdir)/src/hexen > $@ @PROGRAM_PREFIX@strife: $(top_srcdir)/src $(DOCGEN) $(BASH_COMPLETION_TEMPLATES) $(DOCGEN) $(DOCGEN_COMMON_ARGS) \ -g strife -b strife.template \ $(top_srcdir)/src $(top_srcdir)/src/strife > $@ EXTRA_DIST = \ $(BASH_COMPLETION_TEMPLATES) \ $(BASH_COMPLETION_SCRIPTLETS) else EXTRA_DIST = \ $(BASH_COMPLETION_TEMPLATES) endif crispy-doom-crispy-doom-5.6.4/man/bash-completion/doom.template.in000066400000000000000000000024231360717211000251740ustar00rootroot00000000000000# bash completion for @PACKAGE_SHORTNAME@ Doom -*- shell-script -*- _@PROGRAM_SPREFIX@_doom() { 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 -config|-extraconfig) _filedir cfg ;; -file|-iwad|-aa|-af|-as|-merge|-nwtmerge) _filedir wad ;; -playdemo|-timedemo) _filedir lmp ;; -deh) _filedir '@(bex|deh)' ;; esac case $prev in -pack) COMPREPLY=(doom2 tnt plutonia) ;; -gameversion) COMPREPLY=(1.666 1.7 1.8 1.9 ultimate final final2 hacx chex) ;; -setmem) COMPREPLY=(dos622 dos71 dosbox) ;; esac if [[ $cur == -* ]]; then COMPREPLY=( $( compgen -W '@content' -- "$cur" ) ) fi } && complete -F _@PROGRAM_SPREFIX@_doom @PROGRAM_PREFIX@doom # ex: ts=4 sw=4 et filetype=sh crispy-doom-crispy-doom-5.6.4/man/bash-completion/heretic.template.in000066400000000000000000000022371360717211000256640ustar00rootroot00000000000000# bash completion for @PACKAGE_SHORTNAME@ Heretic -*- shell-script -*- _@PROGRAM_SPREFIX@_heretic() { 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 -config|-extraconfig) _filedir cfg ;; -file|-iwad|-aa|-af|-as|-merge|-nwtmerge) _filedir wad ;; -playdemo|-timedemo) _filedir lmp ;; -deh) _filedir hhe ;; esac case $prev in -hhever) COMPREPLY=(1.0 1.2 1.3) ;; -setmem) COMPREPLY=(dos622 dos71 dosbox) ;; esac if [[ $cur == -* ]]; then COMPREPLY=( $( compgen -W '@content' -- "$cur" ) ) fi } && complete -F _@PROGRAM_SPREFIX@_heretic @PROGRAM_PREFIX@heretic # ex: ts=4 sw=4 et filetype=sh crispy-doom-crispy-doom-5.6.4/man/bash-completion/hexen.template.in000066400000000000000000000020371360717211000253460ustar00rootroot00000000000000# bash completion for @PACKAGE_SHORTNAME@ Hexen -*- shell-script -*- _@PROGRAM_SPREFIX@_hexen() { 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 -config|-extraconfig) _filedir cfg ;; -file|-iwad|-aa|-af|-as|-merge|-nwtmerge) _filedir wad ;; -playdemo|-timedemo) _filedir lmp ;; esac case $prev in -setmem) COMPREPLY=(dos622 dos71 dosbox) ;; esac if [[ $cur == -* ]]; then COMPREPLY=( $( compgen -W '@content' -- "$cur" ) ) fi } && complete -F _@PROGRAM_SPREFIX@_hexen @PROGRAM_PREFIX@hexen # ex: ts=4 sw=4 et filetype=sh crispy-doom-crispy-doom-5.6.4/man/bash-completion/strife.template.in000066400000000000000000000022361360717211000255340ustar00rootroot00000000000000# bash completion for @PACKAGE_SHORTNAME@ Strife -*- shell-script -*- _@PROGRAM_SPREFIX@_strife() { 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 -config|-extraconfig) _filedir cfg ;; -file|-iwad|-aa|-af|-as|-merge|-nwtmerge) _filedir wad ;; -playdemo|-timedemo) _filedir lmp ;; -deh) _filedir seh ;; esac case $prev in -gameversion) COMPREPLY=(1.2 1.31) ;; -setmem) COMPREPLY=(dos622 dos71 dosbox) ;; esac if [[ $cur == -* ]]; then COMPREPLY=( $( compgen -W '@content' -- "$cur" ) ) fi } && complete -F _@PROGRAM_SPREFIX@_strife @PROGRAM_PREFIX@strife # ex: ts=4 sw=4 et filetype=sh crispy-doom-crispy-doom-5.6.4/man/default.cfg.template000066400000000000000000000037221360717211000227320ustar00rootroot00000000000000.TH @CFGFILE@ 5 .SH NAME @CFGFILE@ \- @PACKAGE_SHORTNAME@ @GAME_UPPER@ configuration file .SH DESCRIPTION .PP \fI@CFGFILE@\fR is the configuration file for \fB@PROGRAM_SPREFIX@\-@GAME@\fR(6). The configuration options stored in the file are the same as those stored in the original DOS Vanilla @GAME_UPPER@. Extra @PACKAGE_SHORTNAME@ @GAME_UPPER@-specific options are stored in a separate configuration file, \fB@PROGRAM_SPREFIX@\-@GAME@.cfg\fR. .PP \fI@CFGFILE@\fR is normally stored in the user's home directory, as \fI~/.local/share/@PROGRAM_SPREFIX@\-@GAME@/@CFGFILE@\fR. The path can be overridden using the \fBXDG_DATA_HOME\fR environment variable (see the XDG Base Directory Specification). .PP The \fB@PROGRAM_SPREFIX@\-@GAME@\-setup\fR(6) tool provides a simple to use front-end for editing \fI@CFGFILE@\fR. .br .SH FILE FORMAT The file is a plain-text file, consisting of a list of configuration options and their values, separated by whitespace. Each option is stored on a separate line. Options have different types; an option may have either an integer, floating point or string value. If the option is of a string type, the value is surrounded by quotes ("). .PP For example: .RS .PP integer_value 1 .br integer_value2 1 .br floating_point_value 4.2 .br string_value "hello world" .RE .PP Invalid lines or comments in the file will be ignored, but it is advisable not to put them in the file; the file is rewritten from scratch every time the game exits, so any invalid lines or comments will be lost. .PP Some options are used for keyboard key bindings; these are stored as integer values containing the keyboard scan code of the key to be bound to. Boolean values are also stored as integers, with a value of zero usually indicating "false" and a non-zero value indicating "true". @content .SH SEE ALSO \fB@PROGRAM_SPREFIX@\-@GAME@\fR(6), \fB@PROGRAM_SPREFIX@\-@GAME@.cfg\fR(5), \fB@PROGRAM_SPREFIX@\-@GAME@\-setup\fR(6) crispy-doom-crispy-doom-5.6.4/man/docgen000077500000000000000000000373371360717211000202110ustar00rootroot00000000000000#!/usr/bin/env python # # Chocolate Doom self-documentation tool. This works similar to javadoc # or doxygen, but documents command line parameters and configuration # file values, generating documentation in Unix manpage, wikitext and # plain text forms. # # Comments are read from the source code in the following form: # # //! # // @arg # // @category Category # // @platform # // # // Long description of the parameter # // # # something_involving = M_CheckParm("-param"); # # For configuration file values: # # //! @begin_config_file myconfig # # //! # // Description of the configuration file value. # // # # CONFIG_VARIABLE_INT(my_variable, c_variable), # import io import sys import os import re import glob import getopt TEXT_WRAP_WIDTH = 78 INCLUDE_STATEMENT_RE = re.compile("@include\s+(\S+)") # Use appropriate stdout function for Python 2 or 3 def stdout(buf): if sys.version_info.major < 3: sys.stdout.write(buf) else: sys.stdout.buffer.write(buf) # Find the maximum width of a list of parameters (for plain text output) def parameter_list_width(params): w = 0 for p in params: pw = len(p.name) + 5 if p.args: pw += len(p.args) if pw > w: w = pw return w class ConfigFile: def __init__(self, filename): self.filename = filename self.variables = [] def add_variable(self, variable): self.variables.append(variable) def manpage_output(self): result = ".SH CONFIGURATION VARIABLES\n" for v in self.variables: result += ".TP\n" result += v.manpage_output() return result def plaintext_output(self): result = "" w = parameter_list_width(self.variables) for p in self.variables: result += p.plaintext_output(w) result = result.rstrip() + "\n" return result class Category: def __init__(self, description): self.description = description self.params = [] def add_param(self, param): self.params.append(param) # Plain text output def plaintext_output(self): result = "=== %s ===\n\n" % self.description self.params.sort() w = parameter_list_width(self.params) for p in self.params: if p.should_show(): result += p.plaintext_output(w) result = result.rstrip() + "\n" return result def markdown_output(self): result = "## %s\n\n| Parameter | Description |\n| - | - |\n" % self.description self.params.sort() for p in self.params: if p.should_show(): result += p.markdown_output() result = result.rstrip() + "\n" return result def completion_output(self): result = "" self.params.sort() for p in self.params: if p.should_show(): result += p.completion_output(0) result = result.rstrip() return result def manpage_output(self): result = ".SH " + self.description.upper() + "\n" self.params.sort() for p in self.params: if p.should_show(): result += ".TP\n" result += p.manpage_output() return result def wiki_output(self): result = "=== %s ===\n" % self.description self.params.sort() for p in self.params: if p.should_show(): result += "; " + p.wiki_output() + "\n" # Escape special HTML characters result = result.replace("&", "&") result = result.replace("<", "<") result = result.replace(">", ">") return result categories = ( (None, Category("General options")), ("game", Category("Game start options")), ("video", Category("Display options")), ("net", Category("Networking options")), ("mod", Category("Dehacked and WAD merging")), ("demo", Category("Demo options")), ("compat", Category("Compatibility")), ("obscure", Category("Obscure and less-used options")), ) wikipages = [] config_files = {} # Show options that are in Vanilla Doom? Or only new options? show_vanilla_options = True class Parameter: def __lt__(self, other): return self.name < other.name def __init__(self): self.text = "" self.name = "" self.args = None self.platform = None self.category = None self.vanilla_option = False self.games = None def should_show(self): return not self.vanilla_option or show_vanilla_options def add_text(self, text): if len(text) <= 0: pass elif text[0] == "@": match = re.match('@(\S+)\s*(.*)', text) if not match: raise "Malformed option line: %s" % text option_type = match.group(1) data = match.group(2) if option_type == "arg": self.args = data elif option_type == "platform": self.platform = data elif option_type == "category": self.category = data elif option_type == "vanilla": self.vanilla_option = True elif option_type == "game": self.games = re.split(r'\s+', data.strip()) else: raise "Unknown option type '%s'" % option_type else: self.text += text + " " def _games_only_text(self, pattern="(%s only)"): if not match_game and self.games: games_list = ", ".join(map(str.capitalize, self.games)) return " " + (pattern % games_list) else: return "" def manpage_output(self): result = self.name if self.args: result += " " + self.args result = '\\fB' + result + '\\fR' result += "\n" if self.platform: result += "[%s only] " % self.platform escaped = re.sub('\\\\', '\\\\\\\\', self.text) result += escaped + self._games_only_text() + "\n" return result def wiki_output(self): result = self.name if self.args: result += " " + self.args result += ": " result += add_wiki_links(self.text) if self.platform: result += "'''(%s only)'''" % self.platform result += self._games_only_text("'''(%s only)'''") return result def markdown_output(self): if self.args: name = "%s %s" % (self.name, self.args) else: name = "%s" % self.name name = name.replace("|", "\\|") text = self.text if self.platform: text += " (%s only)" % self.platform text = text.replace("|", "\\|") result = "| %s | %s |\n" % (name, text) # html escape result = result.replace("<", "<") result = result.replace(">", ">") return result def plaintext_output(self, indent): # Build the first line, with the argument on start = " " + self.name if self.args: start += " " + self.args # pad up to the plaintext width start += " " * (indent - len(start)) # Build the description text description = self.text if self.platform: description += " (%s only)" % self.platform description += self._games_only_text() # Build the complete text for the argument # Split the description into words and add a word at a time result = "" words = [word for word in re.split('\s+', description) if word] maxlen = TEXT_WRAP_WIDTH - indent outlines = [[]] for word in words: linelen = sum(len(w) + 1 for w in outlines[-1]) if linelen + len(word) > maxlen: outlines.append([]) outlines[-1].append(word) linesep = "\n" + " " * indent return (start + linesep.join(" ".join(line) for line in outlines) + "\n\n") def completion_output(self, w): result = self.name + " " return result # Read list of wiki pages def read_wikipages(): f = io.open("wikipages", encoding='UTF-8') try: for line in f: line = line.rstrip() line = re.sub('\#.*$', '', line) if not re.match(r'^\s*$', line): wikipages.append(line) finally: f.close() # Add wiki page links def add_wiki_links(text): def replace_name(m): linktext = m.group(1) if linktext == pagename: return '[[%s]]' % pagename else: return '[[%s|%s]]' % (pagename, linktext) for pagename in wikipages: text = re.sub(r'\b(%s)\b' % re.escape(pagename), replace_name, text, count=0, flags=re.IGNORECASE) return text def add_parameter(param, line, config_file): # If we're only targeting a particular game, check this is one of # the ones we're targeting. if match_game and param.games and match_game not in param.games: return # Is this documenting a command line parameter? match = re.search('(M_CheckParm(WithArgs)|M_ParmExists)?\s*\(\s*"(.*?)"', line) if match: param.name = match.group(3) category = dict(categories)[param.category] category.add_param(param) return # Documenting a configuration file variable? match = re.search('CONFIG_VARIABLE_\S+\s*\(\s*(\S+?)\),', line) if match: param.name = match.group(1) config_file.add_variable(param) return raise Exception(param.text) def process_file(filename): current_config_file = None f = io.open(filename, encoding='UTF-8') try: param = None waiting_for_checkparm = False for line in f: line = line.rstrip() # Ignore empty lines if re.match('\s*$', line): continue # Currently reading a doc comment? if param: # End of doc comment if not re.match('\s*//', line): waiting_for_checkparm = True # The first non-empty line after the documentation comment # ends must contain the thing being documented. if waiting_for_checkparm: add_parameter(param, line, current_config_file) param = None else: # More documentation text munged_line = re.sub('\s*\/\/\s*', '', line, 1) munged_line = re.sub('\s*$', '', munged_line) param.add_text(munged_line) # Check for start of a doc comment if re.search("//!", line): match = re.search("@begin_config_file\s*(\S+)", line) if match: # Beginning a configuration file tagname = match.group(1) current_config_file = ConfigFile(tagname) config_files[tagname] = current_config_file else: # Start of a normal comment param = Parameter() waiting_for_checkparm = False finally: f.close() def process_files(path): # Process all C source files. if os.path.isdir(path): files = glob.glob(path + "/*.c") for filename in files: process_file(filename) else: # Special case to allow a single file to be specified as a target process_file(path) def print_template(template_file, substs, content): f = io.open(template_file, encoding='UTF-8') try: for line in f: match = INCLUDE_STATEMENT_RE.search(line) if match: filename = match.group(1) filename = os.path.join(os.path.dirname(template_file), filename) print_template(filename, substs, content) else: line = line.replace("@content", content) for k,v in substs.items(): line = line.replace(k,v) stdout(line.rstrip().encode('UTF-8') + b'\n') finally: f.close() def manpage_output(targets, substs, template_file): content = "" for t in targets: content += t.manpage_output() + "\n" content = content.replace("-", "\\-") print_template(template_file, substs, content) def wiki_output(targets, _, template): read_wikipages() for t in targets: stdout(t.wiki_output().encode('UTF-8') + b'\n') def markdown_output(targets, substs, template_file): content = "" for t in targets: content += t.markdown_output() + "\n" print_template(template_file, substs, content) def plaintext_output(targets, substs, template_file): content = "" for t in targets: content += t.plaintext_output() + "\n" print_template(template_file, substs, content) def completion_output(targets, substs, template_file): content = "" for t in targets: content += t.completion_output() + "\n" print_template(template_file, substs, content) def usage(): print("Usage: %s [-V] [-c tag] [-g game] -n program_name -s package_name [ -z shortname ] ( -M | -m | -w | -p ) ..." \ % sys.argv[0]) print(" -c : Provide documentation for the specified configuration file") print(" (matches the given tag name in the source file)") print(" -s : Package name, e.g. Chocolate Doom (for substitution)") print(" -z : Package short-name, e.g. Chocolate (for substitution)") print(" -n : Program name, e.g. chocolate (for substitution)") print(" -M : Markdown output") print(" -m : Manpage output") print(" -w : Wikitext output") print(" -p : Plaintext output") print(" -b : Bash-Completion output") print(" -V : Don't show Vanilla Doom options") print(" -g : Only document options for specified game.") sys.exit(0) # Parse command line opts, args = getopt.getopt(sys.argv[1:], "n:s:z:M:m:wp:b:c:g:V") output_function = None template = None doc_config_file = None match_game = None substs = {} for opt in opts: if opt[0] == "-n": substs["@PROGRAM_SPREFIX@"] = opt[1] if opt[0] == "-s": substs["@PACKAGE_NAME@"] = opt[1] if opt[0] == "-z": substs["@PACKAGE_SHORTNAME@"] = opt[1] if opt[0] == "-m": output_function = manpage_output template = opt[1] elif opt[0] == "-M": output_function = markdown_output template = opt[1] elif opt[0] == "-w": output_function = wiki_output elif opt[0] == "-p": output_function = plaintext_output template = opt[1] elif opt[0] == "-b": output_function = completion_output template = opt[1] elif opt[0] == "-V": show_vanilla_options = False elif opt[0] == "-c": doc_config_file = opt[1] elif opt[0] == "-g": match_game = opt[1] substs["@GAME@"] = opt[1] substs["@GAME_UPPER@"] = opt[1].title() if "doom" == opt[1]: substs["@CFGFILE@"] = "default.cfg" else: substs["@CFGFILE@"] = opt[1] + ".cfg" if output_function == None or len(args) < 1: usage() else: # Process specified files for path in args: process_files(path) # Build a list of things to document if doc_config_file: documentation_targets = [config_files[doc_config_file]] else: documentation_targets = [c for _, c in categories] # Generate the output output_function(documentation_targets, substs, template) crispy-doom-crispy-doom-5.6.4/man/doom.template000066400000000000000000000027441360717211000215110ustar00rootroot00000000000000.TH @PROGRAM_SPREFIX@\-doom 6 .SH NAME @PROGRAM_SPREFIX@\-doom \- historically compatible Doom engine .SH SYNOPSIS .B @PROGRAM_SPREFIX@\-doom [\fIOPTIONS\fR] .SH DESCRIPTION .PP @PACKAGE_SHORTNAME@ Doom is a port of Id Software's 1993 game "Doom" that is designed to behave as similar to the original DOS version of Doom as is possible. .br @content .SH IWAD SEARCH PATHS @include iwad_paths.man .SH ENVIRONMENT This section describes environment variables that control @PACKAGE_NAME@'s behavior. @include environ.man .SH FILES .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/default.cfg\fR The main configuration file for @PACKAGE_NAME@. See \fBdefault.cfg\fR(5). .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/@PROGRAM_SPREFIX@\-doom.cfg\fR Extra configuration values that are specific to @PACKAGE_NAME@ and not present in Vanilla Doom. See \fB@PROGRAM_SPREFIX@\-doom.cfg\fR(5). .SH SEE ALSO \fB@PROGRAM_SPREFIX@\-server\fR(6), \fB@PROGRAM_SPREFIX@\-setup\fR(6), \fB@PROGRAM_SPREFIX@\-heretic\fR(6), \fB@PROGRAM_SPREFIX@\-hexen\fR(6), \fB@PROGRAM_SPREFIX@\-strife\fR(6) .SH AUTHOR Chocolate Doom is written and maintained by Simon Howard. It is based on the LinuxDoom source code, released by Id Software. .SH COPYRIGHT Copyright \(co id Software Inc. Copyright \(co 2005-2016 Simon Howard. .br This is free software. You may redistribute copies of it under the terms of the GNU General Public License . There is NO WARRANTY, to the extent permitted by law. crispy-doom-crispy-doom-5.6.4/man/environ.man000066400000000000000000000017031360717211000211650ustar00rootroot00000000000000.TP \fBDOOMWADDIR\fR, \fBDOOMWADPATH\fR See the section, \fBIWAD SEARCH PATHS\fR above. .TP \fBPCSOUND_DRIVER\fR When running in PC speaker sound effect mode, this environment variable specifies a PC speaker driver to use for sound effect playback. Valid options are "Linux" for the Linux console mode driver, "BSD" for the NetBSD/OpenBSD PC speaker driver, and "SDL" for SDL-based emulated PC speaker playback (using the digital output). .TP \fBOPL_DRIVER\fR When using OPL MIDI playback, this environment variable specifies an OPL backend driver to use. Valid options are "SDL" for an SDL-based software emulated OPL chip, "Linux" for the Linux hardware OPL driver, and "OpenBSD" for the OpenBSD/NetBSD hardware OPL driver. Generally speaking, a real hardware OPL chip sounds better than software emulation; however, modern machines do not often include one. If present, it may still require extra work to set up and elevated security privileges to access. crispy-doom-crispy-doom-5.6.4/man/extra.cfg.template000066400000000000000000000022011360717211000224200ustar00rootroot00000000000000.TH @PROGRAM_SPREFIX@\-doom.cfg 5 .SH NAME @PROGRAM_SPREFIX@\-doom.cfg \- @PACKAGE_NAME@ configuration file .SH DESCRIPTION .PP \fI@PROGRAM_SPREFIX@\-doom.cfg\fR is a configuration file for \fB@PROGRAM_SPREFIX@\-doom\fR(6). This file acts as an auxiliary configuration file; the main configuration options are stored in \fBdefault.cfg\fR, which contains the same configuration options as Vanilla Doom (for compatibility). \fI@PROGRAM_SPREFIX@\-doom.cfg\fR contains configuration options that are specific to @PACKAGE_NAME@ only. .PP \fI@PROGRAM_SPREFIX@\-doom.cfg\fR is normally stored in the user's home directory, as \fI~/.local/share/@PROGRAM_SPREFIX@\-doom/@PROGRAM_SPREFIX@\-doom.cfg\fR. The path can be overridden using the \fBXDG_DATA_HOME\fR environment variable (see the XDG Base Directory Specification). .PP The \fB@PROGRAM_SPREFIX@\-setup\fR(6) tool provides a simple to use front-end for editing \fI@PROGRAM_SPREFIX@\-doom.cfg\fR. .SH FILE FORMAT .PP The file format is the same as that used for \fBdefault.cfg\fR(5). .br @content .SH SEE ALSO \fB@PROGRAM_SPREFIX@\-doom\fR(6), \fBdefault.cfg\fR(5), \fB@PROGRAM_SPREFIX@\-setup\fR(6) crispy-doom-crispy-doom-5.6.4/man/heretic.template000066400000000000000000000031041360717211000221650ustar00rootroot00000000000000.TH @PROGRAM_SPREFIX@\-heretic 6 .SH NAME @PROGRAM_SPREFIX@\-heretic \- historically compatible Heretic engine .SH SYNOPSIS .B @PROGRAM_SPREFIX@\-heretic [\fIOPTIONS\fR] .SH DESCRIPTION .PP @PACKAGE_SHORTNAME@ Heretic is a port of Raven Software's 1994 game "Heretic" that aims to behave as similar to the original DOS version of Heretic as possible. .br @content .SH IWAD SEARCH PATHS @include iwad_paths.man .SH ENVIRONMENT This section describes environment variables that control @PACKAGE_SHORTNAME@ Heretic's behavior. @include environ.man .SH FILES .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/heretic.cfg\fR The main configuration file for @PACKAGE_SHORTNAME@ Heretic. See \fBheretic.cfg\fR(5). .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/@PROGRAM_SPREFIX@\-heretic.cfg\fR Extra configuration values that are specific to @PACKAGE_SHORTNAME@ Heretic and not present in Vanilla Heretic. See \fB@PROGRAM_SPREFIX@\-heretic.cfg\fR(5). .SH SEE ALSO \fB@PROGRAM_SPREFIX@\-doom\fR(6), \fB@PROGRAM_SPREFIX@\-hexen\fR(6), \fB@PROGRAM_SPREFIX@\-server\fR(6), \fB@PROGRAM_SPREFIX@\-setup\fR(6) .SH AUTHOR Chocolate Heretic is part of the Chocolate Doom project, written and maintained by Simon Howard. It is based on the Heretic source code, released by Raven Software. .SH COPYRIGHT Copyright \(co id Software Inc. Copyright \(co Raven Software Inc. Copyright \(co 2005-2013 Simon Howard. .br This is free software. You may redistribute copies of it under the terms of the GNU General Public License . There is NO WARRANTY, to the extent permitted by law. crispy-doom-crispy-doom-5.6.4/man/hexen.template000066400000000000000000000030451360717211000216550ustar00rootroot00000000000000.TH @PROGRAM_SPREFIX@\-hexen 6 .SH NAME @PROGRAM_SPREFIX@\-hexen \- historically compatible Hexen engine .SH SYNOPSIS .B @PROGRAM_SPREFIX@\-hexen [\fIOPTIONS\fR] .SH DESCRIPTION .PP @PACKAGE_SHORTNAME@ Hexen is a port of Raven Software's 1995 game "Hexen" that aims to behave as similar to the original DOS version of Hexen as possible. .br @content .SH IWAD SEARCH PATHS @include iwad_paths.man .SH ENVIRONMENT This section describes environment variables that control @PACKAGE_SHORTNAME@ Hexen's behavior. @include environ.man .SH FILES .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/hexen.cfg\fR The main configuration file for @PACKAGE_SHORTNAME@ Hexen. See \fBhexen.cfg\fR(5). .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/@PROGRAM_SPREFIX@\-hexen.cfg\fR Extra configuration values that are specific to @PACKAGE_SHORTNAME@ Hexen and not present in Vanilla Hexen. See \fB@PROGRAM_SPREFIX@\-hexen.cfg\fR(5). .SH SEE ALSO \fB@PROGRAM_SPREFIX@\-doom\fR(6), \fB@PROGRAM_SPREFIX@\-heretic\fR(6), \fB@PROGRAM_SPREFIX@\-server\fR(6), \fB@PROGRAM_SPREFIX@\-setup\fR(6) .SH AUTHOR Chocolate Hexen is part of the Chocolate Doom project, written and maintained by Simon Howard. It is based on the Hexen source code, released by Raven Software. .SH COPYRIGHT Copyright \(co id Software Inc. Copyright \(co Raven Software Inc. Copyright \(co 2005-2013 Simon Howard. .br This is free software. You may redistribute copies of it under the terms of the GNU General Public License . There is NO WARRANTY, to the extent permitted by law. crispy-doom-crispy-doom-5.6.4/man/iwad_paths.man000066400000000000000000000041441360717211000216320ustar00rootroot00000000000000To play, an IWAD file is needed. This is a large file containing all of the levels, graphics, sound effects, music and other material that make up the game. IWAD files are named according to the game; the standard names are: .TP \fBdoom.wad, doom1.wad, doom2.wad, tnt.wad, plutonia.wad\fR Doom, Doom II, Final Doom .TP \fBheretic.wad, heretic1.wad, hexen.wad, strife1.wad\fR Heretic, Hexen and Strife (commercial Doom engine games). .TP \fBhacx.wad, chex.wad\fR Hacx and Chex Quest - more obscure games based on the Doom engine. .TP \fBfreedm.wad, freedoom1.wad, freedoom2.wad\fR The Freedoom open content IWAD files. .LP The following directory paths are searched in order to find an IWAD: .TP \fBCurrent working directory\fR Any IWAD files found in the current working directory will be used in preference to IWADs found in any other directories. .TP \fBDOOMWADDIR\fR This environment variable can be set to contain a path to a single directory in which to look for IWAD files. This environment variable is supported by most Doom source ports. .TP \fBDOOMWADPATH\fR This environment variable, if set, can contain a colon-separated list of directories in which to look for IWAD files, or alternatively full paths to specific IWAD files. .TP \fB$HOME/.local/share/games/doom\fR Writeable directory in the user's home directory. The path can be overridden using the \fBXDG_DATA_HOME\fR environment variable (see the XDG Base Directory Specification). .TP \fB/usr/local/share/games/doom, /usr/share/games/doom\fR System-wide locations that can be accessed by all users. The path \fB/usr/share/games/doom\fR is a standard path that is supported by most Doom source ports. These paths can be overridden using the \fBXDG_DATA_DIRS\fR environment variable (see the XDG Base Directory Specification). .LP The above can be overridden on a one-time basis by using the \fB\-iwad\fR command line parameter to provide the path to an IWAD file to use. This parameter can also be used to specify the name of a particular IWAD to use from one of the above paths. For example, '\fB-iwad doom.wad\fR' will search the above paths for the file \fBdoom.wad\fR to use. crispy-doom-crispy-doom-5.6.4/man/server.template000066400000000000000000000032011360717211000220460ustar00rootroot00000000000000.TH @PROGRAM_SPREFIX@\-server 6 .SH NAME @PROGRAM_SPREFIX@\-server \- dedicated server for @PROGRAM_SPREFIX@\-doom .SH SYNOPSIS .B @PROGRAM_SPREFIX@\-server [OPTIONS] .SH DESCRIPTION .PP @PACKAGE_NAME@ is a modern doom engine designed to behave as similar to the original doom game as is possible. .PP .B @PROGRAM_SPREFIX@\-server is a dedicated server for @PACKAGE_SHORTNAME@. It is equivalent to running .B @PROGRAM_SPREFIX@\-doom with the "\-dedicated" option. .PP Game options are not specified to the server, which merely acts to retransmit data between players in the game; parameters for the game should be specified by the first player to connect to the server, who is designated the controlling player. .br .SH OPTIONS .TP \fB-ignoreversion\fR Ignore version mismatches between the server and the client. Using this option may cause game desyncs to occur, or differences in protocol may mean the netgame will simply not function at all. .TP \fB-port \fR Use the specified UDP port for communications, instead of the default (2342). .TP \fB-privateserver\fR Don't register with the global master server. .TP \fB-servername \fR Specify a name for the server. .SH SEE ALSO \fB@PROGRAM_SPREFIX@-doom\fR(6), \fB@PROGRAM_SPREFIX@-setup\fR(6) .SH AUTHOR Chocolate Doom is written and maintained by Simon Howard. .PP This manual was written by Jonathan Dowland. .SH COPYRIGHT Copyright \(co id Software Inc. Copyright \(co 2005-8 Simon Howard. .br This is free software. You may redistribute copies of it under the terms of the GNU General Public License . There is NO WARRANTY, to the extent permitted by law. crispy-doom-crispy-doom-5.6.4/man/setup.template000066400000000000000000000025731360717211000217130ustar00rootroot00000000000000.TH @PROGRAM_SPREFIX@\-@GAME@-setup 6 .SH NAME @PROGRAM_SPREFIX@\-@GAME@-setup \- configuration tool for @PROGRAM_SPREFIX@\-@GAME@ .SH SYNOPSIS .B @PROGRAM_SPREFIX@\-@GAME@-setup [OPTIONS] .SH DESCRIPTION .PP @PACKAGE_SHORTNAME@ @GAME_UPPER@ is a modern @GAME_UPPER@ engine designed to behave as similar to the original @GAME_UPPER@ game as is possible. .PP .B @PROGRAM_SPREFIX@\-@GAME@-setup is a tool for configuring @PACKAGE_SHORTNAME@ @GAME_UPPER@. It provides a menu\-based interface for the display, joystick, keyboard, mouse, sound and compatibility settings. .PP .B @PROGRAM_SPREFIX@\-@GAME@-setup can also be used to start and join network games. .PP .SH OPTIONS .TP \fB-config \fR Load configuration from the specified file, instead of @CFGFILE@. .TP \fB-extraconfig \fR Load extra configuration from the specified file, instead of @PROGRAM_SPREFIX@\-@GAME@.cfg. .SH SEE ALSO \fB@PROGRAM_SPREFIX@\-@GAME@\fR(6), \fBdefault.cfg\fR(5), \fB@PROGRAM_SPREFIX@\-@GAME@.cfg\fR(5) .SH AUTHOR Chocolate Doom is written and maintained by Simon Howard. .PP This manual was written by Jonathan Dowland. .SH COPYRIGHT Copyright \(co id Software Inc. Copyright \(co 2005-8 Simon Howard. .br This is free software. You may redistribute copies of it under the terms of the GNU General Public License . There is NO WARRANTY, to the extent permitted by law. crispy-doom-crispy-doom-5.6.4/man/simplecpp000077500000000000000000000127171360717211000207410ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 # Contributors to the Freedoom project. 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 freedoom project 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 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. # # # simple cpp-style preprocessor # # Understands most features of the C preprocessor, including: # #if .. #elif .. #else .. #endif # - with expressions # #ifdef # #define # #include # import collections import io import sys import re debug = False defines = collections.defaultdict(lambda: False) command_re = re.compile(r"\#(\w+)(\s+(.*))?") include_re = re.compile(r"\s*\"(.*)\"\s*") define_re = re.compile(r"\s*(\S+)\s*(.*?)\s*$") def debug_msg(message): if debug: sys.stderr.write(message) # Parse command line options def parse_cmdline(): for arg in sys.argv[1:]: if not arg.startswith("-D"): continue name = arg[2:] if '=' in name: name, value = name.split('=', 1) else: value = True defines[name] = value def parse_stream(stream): result = read_block(stream, False) if result is not None: raise Exception("Mismatched #if in '%s'" % stream.name) def parse_file(filename): f = io.open(filename, encoding='UTF-8') try: parse_stream(f) finally: f.close() # Evaluate an expression using Python's eval() function. def eval_expr(expr): expr = expr.replace("||", " or ") \ .replace("&&", " and ") \ .replace("!", "not ") code = compile(expr, "", "eval") result = eval(code, {}, defines) return result # #include def cmd_include(arg): # Extract the filename match = include_re.match(arg) if not match: raise Exception("Invalid 'include' command") filename = match.group(1) # Open the file and process it parse_file(filename) # #define def cmd_define(arg): match = define_re.match(arg) name = match.group(1) value = match.group(2) if value == '': value = True defines[name] = value # #undef def cmd_undef(arg): if arg in defines: del defines[arg] # #ifdef/#ifndef def cmd_ifdef(arg, command, stream, ignore): # Get the define name debug_msg("%s %s >\n" % (command, arg)) # Should we ignore the contents of this block? sub_ignore = not eval_expr(arg) if "n" in command: sub_ignore = not sub_ignore # Parse the block result, newarg = read_block(stream, ignore or sub_ignore) debug_msg("%s %s < (%s)\n" % (command, arg, result)) # There may be a second "else" block to parse: if result == "else": debug_msg("%s %s else >\n" % (command, arg)) result, arg = read_block(stream, ignore or (not sub_ignore)) debug_msg("%s %s else < (%s)\n" % (command, arg, result)) if result == "elif": debug_msg("%s %s elif %s>\n" % (command, arg, newarg)) cmd_ifdef(newarg, "if", stream, ignore or (not sub_ignore)) result = "endif" # Should end in an endif: if result != "endif": raise Exception("'if' block did not end in an 'endif'") commands = { "include" : cmd_include, "define" : cmd_define, "undef" : cmd_undef, "if" : cmd_ifdef, "ifdef" : cmd_ifdef, "ifn" : cmd_ifdef, "ifndef" : cmd_ifdef, } # Recursive block reading function # if 'ignore' argument is 1, contents are ignored def read_block(stream, ignore): for line in stream: # Remove newline line = line[0:-1] # Check if this line has a command match = command_re.match(line) if match: command = match.group(1) arg = match.group(3) if command in ("else", "elif", "endif"): return (command, arg) elif command not in commands: raise Exception("Unknown command: '%s'" % \ command) # Get the callback function. func = commands[command] # Invoke the callback function. #ifdef commands # are a special case and need extra arguments. # Other commands are only executed if we are not # ignoring this block. if func == cmd_ifdef: cmd_ifdef(arg, command=command, stream=stream, ignore=ignore) elif not ignore: func(arg) else: if not ignore: for key, value in defines.items(): if isinstance(value, str): line = line.replace(key, value) print(line) parse_cmdline() parse_stream(sys.stdin) crispy-doom-crispy-doom-5.6.4/man/strife.template000066400000000000000000000073111360717211000220420ustar00rootroot00000000000000.TH @PROGRAM_SPREFIX@\-strife 6 .SH NAME @PROGRAM_SPREFIX@\-strife \- historically compatible Strife engine .SH SYNOPSIS .B @PROGRAM_SPREFIX@\-strife [\fIOPTIONS\fR] .SH DESCRIPTION .PP @PACKAGE_SHORTNAME@ Strife is an accurate and complete recreation of Rogue Entertainment's "Strife: Quest for the Sigil". It was created through more than two years of reverse engineering effort with the blessings of the original programmers of the game (see the section HISTORY below). @content .SH IWAD SEARCH PATHS @include iwad_paths.man .SH ENVIRONMENT This section describes environment variables that control @PACKAGE_SHORTNAME@ Strife's behavior. @include environ.man .SH FILES .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/strife.cfg\fR The main configuration file for @PACKAGE_SHORTNAME@ Strife. See \fBstrife.cfg\fR(5). .TP \fB$HOME/.local/share/@PROGRAM_SPREFIX@\-doom/@PROGRAM_SPREFIX@\-strife.cfg\fR Extra configuration values that are specific to @PACKAGE_SHORTNAME@ Strife and not present in Vanilla Strife. See \fB@PROGRAM_SPREFIX@\-strife.cfg\fR(5). .SH SEE ALSO \fB@PROGRAM_SPREFIX@\-doom\fR(6), \fB@PROGRAM_SPREFIX@\-server\fR(6), \fB@PROGRAM_SPREFIX@\-setup\fR(6) .SH HISTORY The source code for Strife was lost, which means, unlike the code for all the other commercial DOOM-engine games, it cannot be released. The only access we have to the code is the binary executable file. Reverse engineering tools were employed to disassemble and decompile the executables, which were cross- referenced against the Linux DOOM and DOS Heretic sources and painstakingly combed over multiple times, instruction-by-instruction, to ensure that the resulting Chocolate-Doom-based executable is as close as possible to the original. .SH LEGALITY Reverse engineering is a protected activity so long as the original code is not used directly in the product. Due to the vast amount of information lost through the process of compilation, and the need to refactor large portions of code in order to eliminate non-portable idioms or to adapt them properly to Chocolate Doom's framework, the resulting code behaves the same, but is not the *same* code. In addition, James Monroe and John Carmack have both stated that they have no objections to the project. Because they are the original authors of the code, and neither Rogue nor their publisher, Velocity, Inc., exist any longer as legal entities, this is effectively legal permission. .SH BUGS @PACKAGE_SHORTNAME@ Strife is almost, but not entirely perfect, in recreating the behavior of Vanilla Strife. Help us by reporting any discrepancies you might notice between this executable and the vanilla DOS program. However, do *not* report any glitch that you can replicate in the vanilla EXE as a bug. The point of @PACKAGE_SHORTNAME@ Strife, like Chocolate Doom before it, is to be as bug-compatible with the original game as possible. Also be aware that some glitches are impossible to compatibly recreate, and wherever this is the case, @PACKAGE_SHORTNAME@ Strife has erred on the side of not crashing the program, for example by initializing pointers to NULL rather than using them without setting a value first. .SH AUTHORS Chocolate Strife is part of the Chocolate Doom project. It was reverse engineered from the DOS versions of Strife by James Haley and Samuel Villarreal. Chocolate Doom was written and maintained by Simon Howard, and is based on the LinuxDoom source code released by Id Software. .SH COPYRIGHT Copyright \(co id Software Inc. Copyright \(co 2005-2013 Simon Howard, James Haley, Samuel Villarreal. .br This is free software. You may redistribute copies of it under the terms of the GNU General Public License . There is NO WARRANTY, to the extent permitted by law. crispy-doom-crispy-doom-5.6.4/man/wikipages000066400000000000000000000002241360717211000207130ustar00rootroot00000000000000# This is a list of wiki pages to automatically link to when generating # wikitext output. Dehacked Doom 1.91 Merging Multiplayer Three screen mode crispy-doom-crispy-doom-5.6.4/midiproc/000077500000000000000000000000001360717211000200425ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/midiproc/.gitignore000066400000000000000000000000531360717211000220300ustar00rootroot00000000000000Makefile.in Makefile *.exe .deps tags TAGS crispy-doom-crispy-doom-5.6.4/midiproc/CMakeLists.txt000066400000000000000000000005121360717211000226000ustar00rootroot00000000000000if(WIN32) add_executable("${PROGRAM_PREFIX}midiproc" WIN32 buffer.c buffer.h main.c proto.h) target_include_directories("${PROGRAM_PREFIX}midiproc" PRIVATE "../src/" "${CMAKE_CURRENT_BINARY_DIR}/../") target_link_libraries("${PROGRAM_PREFIX}midiproc" SDL2::SDL2main SDL2::mixer) endif() crispy-doom-crispy-doom-5.6.4/midiproc/Makefile.am000066400000000000000000000004061360717211000220760ustar00rootroot00000000000000 AM_CFLAGS=-I$(top_srcdir)/src @SDLMIXER_CFLAGS@ EXTRA_DIST=CMakeLists.txt if HAVE_WINDRES noinst_PROGRAMS = @PROGRAM_PREFIX@midiproc @PROGRAM_PREFIX@midiproc_LDADD = @SDLMIXER_LIBS@ @PROGRAM_PREFIX@midiproc_SOURCES = buffer.c buffer.h main.c proto.h endif crispy-doom-crispy-doom-5.6.4/midiproc/buffer.c000066400000000000000000000115021360717211000214560ustar00rootroot00000000000000// // Copyright(C) 2017 Alex Mayfield // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // A simple buffer and reader implementation. // #ifdef _WIN32 #include "buffer.h" #include // // Create a new buffer. // buffer_t *NewBuffer() { buffer_t *buf = malloc(sizeof(buffer_t)); buf->buffer_end = buf->buffer + BUFFER_SIZE; Buffer_Clear(buf); return buf; } // // Free a buffer. // void DeleteBuffer(buffer_t *buf) { free(buf); } // // Return the data in the buffer. // int Buffer_Data(buffer_t *buf, byte **data) { *data = buf->data; return buf->data_len; } // // Push data onto the end of the buffer. // boolean Buffer_Push(buffer_t *buf, const void *data, int len) { ptrdiff_t space_begin, space_end; if (len <= 0) { // Do nothing, successfully. return true; } space_begin = buf->data - buf->buffer; space_end = buf->buffer_end - buf->data_end; if (len > space_end) { if (len > space_begin + space_end) { // Don't overflow the buffer. return false; } // Move our data to the front of the buffer. memmove(buf->buffer, buf->data, buf->data_len); buf->data = buf->buffer; buf->data_end = buf->buffer + buf->data_len; } // Append to the buffer. memcpy(buf->data_end, data, len); buf->data_len += len; buf->data_end = buf->data + buf->data_len; return true; } // // Shift len bytes off of the front of the buffer. // void Buffer_Shift(buffer_t *buf, int len) { ptrdiff_t max_shift; if (len <= 0) { // Do nothing. return; } max_shift = buf->data_end - buf->data; if (len >= max_shift) { // If the operation would clear the buffer, just zero everything. Buffer_Clear(buf); } else { buf->data += len; buf->data_len -= len; } } // // Clear the buffer. // void Buffer_Clear(buffer_t *buf) { buf->data = buf->buffer; buf->data_end = buf->buffer; buf->data_len = 0; } // // Create a new buffer reader. // // WARNING: This reader will invalidate if the underlying buffer changes. // Use it, then delete it before you touch the underlying buffer again. // buffer_reader_t *NewReader(buffer_t* buffer) { buffer_reader_t *reader = malloc(sizeof(buffer_reader_t)); reader->buffer = buffer; reader->pos = buffer->data; return reader; } // // Delete a buffer reader. // void DeleteReader(buffer_reader_t *reader) { free(reader); } // // Count the number of bytes read thus far. // int Reader_BytesRead(buffer_reader_t *reader) { return reader->pos - reader->buffer->data; } // // Read an unsigned byte from a buffer. // boolean Reader_ReadInt8(buffer_reader_t *reader, uint8_t *out) { byte *data, *data_end; int len = Buffer_Data(reader->buffer, &data); data_end = data + len; if (data_end - reader->pos < 1) { return false; } *out = (uint8_t)*reader->pos; reader->pos += 1; return true; } // // Read an unsigned short from a buffer. // boolean Reader_ReadInt16(buffer_reader_t *reader, uint16_t *out) { byte *data, *data_end, *dp; int len = Buffer_Data(reader->buffer, &data); data_end = data + len; dp = reader->pos; if (data_end - reader->pos < 2) { return false; } *out = (uint16_t)((dp[0] << 8) | dp[1]); reader->pos += 2; return true; } // // Read an unsigned int from a buffer. // boolean Reader_ReadInt32(buffer_reader_t *reader, uint32_t *out) { byte *data, *data_end, *dp; int len = Buffer_Data(reader->buffer, &data); data_end = data + len; dp = reader->pos; if (data_end - reader->pos < 4) { return false; } *out = (uint32_t)((dp[0] << 24) | (dp[1] << 16) | (dp[2] << 8) | dp[3]); reader->pos += 4; return true; } // // Read a string from a buffer. // char *Reader_ReadString(buffer_reader_t *reader) { byte *data, *data_start, *data_end, *dp; int len = Buffer_Data(reader->buffer, &data); data_start = reader->pos; data_end = data + len; dp = reader->pos; while (dp < data_end && *dp != '\0') { dp++; } if (dp >= data_end) { // Didn't see a null terminator, not a complete string. return NULL; } reader->pos = dp + 1; return (char*)data_start; } #endif // #ifdef _WIN32 crispy-doom-crispy-doom-5.6.4/midiproc/buffer.h000066400000000000000000000032571360717211000214730ustar00rootroot00000000000000// // Copyright(C) 2017 Alex Mayfield // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // A simple buffer and reader implementation. // #ifndef __BUFFER__ #define __BUFFER__ #include "../src/doomtype.h" #define BUFFER_SIZE 1024 typedef struct { byte buffer[BUFFER_SIZE]; // Buffer. byte *buffer_end; // End of Buffer. byte *data; // Start of actual data. byte *data_end; // End of actual data. int data_len; // Length of actual data. } buffer_t; typedef struct { buffer_t *buffer; byte *pos; } buffer_reader_t; buffer_t *NewBuffer(); void DeleteBuffer(buffer_t* buf); int Buffer_Data(buffer_t *buf, byte **data); boolean Buffer_Push(buffer_t *buf, const void *data, int len); void Buffer_Shift(buffer_t *buf, int len); void Buffer_Clear(buffer_t *buf); buffer_reader_t *NewReader(buffer_t* buffer); void DeleteReader(buffer_reader_t *reader); int Reader_BytesRead(buffer_reader_t *reader); boolean Reader_ReadInt8(buffer_reader_t *reader, uint8_t *out); boolean Reader_ReadInt16(buffer_reader_t *reader, uint16_t *out); boolean Reader_ReadInt32(buffer_reader_t *reader, uint32_t *out); char *Reader_ReadString(buffer_reader_t *reader); #endif crispy-doom-crispy-doom-5.6.4/midiproc/main.c000066400000000000000000000235111360717211000211340ustar00rootroot00000000000000// // Copyright(C) 2012 James Haley // Copyright(C) 2017 Alex Mayfield // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // // Win32/SDL_mixer MIDI Server // // Uses pipes to communicate with Doom. This allows this separate process to // have its own independent volume control even under Windows Vista and up's // broken, stupid, completely useless mixer model that can't assign separate // volumes to different devices for the same process. // // Seriously, how did they screw up something so fundamental? // #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #include #include "SDL.h" #include "SDL_mixer.h" #include "buffer.h" #include "proto.h" #include "config.h" #include "doomtype.h" static HANDLE midi_process_in; // Standard In. static HANDLE midi_process_out; // Standard Out. // Sound sample rate to use for digital output (Hz) static int snd_samplerate = 0; // Currently playing music track. static Mix_Music *music = NULL; //============================================================================= // // Private functions // // // Write an unsigned integer into a simple CHAR buffer. // static boolean WriteInt16(CHAR *out, size_t osize, unsigned int in) { if (osize < 2) { return false; } out[0] = (in >> 8) & 0xff; out[1] = in & 0xff; return true; } // // Cleanly close our in-use pipes. // static void FreePipes(void) { if (midi_process_in != NULL) { CloseHandle(midi_process_in); midi_process_in = NULL; } if (midi_process_out != NULL) { CloseHandle(midi_process_out); midi_process_out = NULL; } } // // Unregisters the currently playing song. This is never called from the // protocol, we simply do this before playing a new song. // static void UnregisterSong() { if (music == NULL) { return; } Mix_FreeMusic(music); music = NULL; } // // Cleanly shut down SDL. // static void ShutdownSDL(void) { UnregisterSong(); Mix_CloseAudio(); SDL_Quit(); } //============================================================================= // // SDL_mixer Interface // static boolean RegisterSong(const char *filename) { music = Mix_LoadMUS(filename); // Remove the temporary MIDI file remove(filename); if (music == NULL) { return false; } return true; } static void SetVolume(int vol) { Mix_VolumeMusic(vol); } static void PlaySong(int loops) { Mix_PlayMusic(music, loops); // [AM] BUG: In my testing, setting the volume of a MIDI track while there // is no song playing appears to be a no-op. This can happen when // you're mixing midiproc with vanilla SDL_Mixer, such as when you // are alternating between a digital music pack (in the parent // process) and MIDI (in this process). // // To work around this bug, we set the volume to itself after the MIDI // has started playing. Mix_VolumeMusic(Mix_VolumeMusic(-1)); } static void StopSong() { Mix_HaltMusic(); } //============================================================================= // // Pipe Server Interface // static boolean MidiPipe_RegisterSong(buffer_reader_t *reader) { char *filename = Reader_ReadString(reader); if (filename == NULL) { return false; } return RegisterSong(filename); } static boolean MidiPipe_UnregisterSong(buffer_reader_t *reader) { UnregisterSong(); return true; } boolean MidiPipe_SetVolume(buffer_reader_t *reader) { int vol; boolean ok = Reader_ReadInt32(reader, (uint32_t*)&vol); if (!ok) { return false; } SetVolume(vol); return true; } boolean MidiPipe_PlaySong(buffer_reader_t *reader) { int loops; boolean ok = Reader_ReadInt32(reader, (uint32_t*)&loops); if (!ok) { return false; } PlaySong(loops); return true; } boolean MidiPipe_StopSong() { StopSong(); return true; } boolean MidiPipe_Shutdown() { exit(EXIT_SUCCESS); } //============================================================================= // // Server Implementation // // // Parses a command and directs to the proper read function. // boolean ParseCommand(buffer_reader_t *reader, uint16_t command) { switch (command) { case MIDIPIPE_PACKET_TYPE_REGISTER_SONG: return MidiPipe_RegisterSong(reader); case MIDIPIPE_PACKET_TYPE_UNREGISTER_SONG: return MidiPipe_UnregisterSong(reader); case MIDIPIPE_PACKET_TYPE_SET_VOLUME: return MidiPipe_SetVolume(reader); case MIDIPIPE_PACKET_TYPE_PLAY_SONG: return MidiPipe_PlaySong(reader); case MIDIPIPE_PACKET_TYPE_STOP_SONG: return MidiPipe_StopSong(); case MIDIPIPE_PACKET_TYPE_SHUTDOWN: return MidiPipe_Shutdown(); default: return false; } } // // Server packet parser // boolean ParseMessage(buffer_t *buf) { CHAR buffer[2]; DWORD bytes_written; int bytes_read; uint16_t command; buffer_reader_t *reader = NewReader(buf); // Attempt to read a command out of the buffer. if (!Reader_ReadInt16(reader, &command)) { goto fail; } // Attempt to parse a complete message. if (!ParseCommand(reader, command)) { goto fail; } // We parsed a complete message! We can now safely shift // the prior message off the front of the buffer. bytes_read = Reader_BytesRead(reader); DeleteReader(reader); Buffer_Shift(buf, bytes_read); // Send acknowledgement back that the command has completed. if (!WriteInt16(buffer, sizeof(buffer), MIDIPIPE_PACKET_TYPE_ACK)) { goto fail; } WriteFile(midi_process_out, buffer, sizeof(buffer), &bytes_written, NULL); return true; fail: // We did not read a complete packet. Delete our reader and try again // with more data. DeleteReader(reader); return false; } // // The main pipe "listening" loop // boolean ListenForever() { BOOL wok = FALSE; CHAR pipe_buffer[8192]; DWORD pipe_buffer_read = 0; boolean ok = false; buffer_t *buffer = NewBuffer(); for (;;) { // Wait until we see some data on the pipe. wok = PeekNamedPipe(midi_process_in, NULL, 0, NULL, &pipe_buffer_read, NULL); if (!wok) { break; } else if (pipe_buffer_read == 0) { SDL_Delay(1); continue; } // Read data off the pipe and add it to the buffer. wok = ReadFile(midi_process_in, pipe_buffer, sizeof(pipe_buffer), &pipe_buffer_read, NULL); if (!wok) { break; } ok = Buffer_Push(buffer, pipe_buffer, pipe_buffer_read); if (!ok) { break; } do { // Read messages off the buffer until we can't anymore. ok = ParseMessage(buffer); } while (ok); } return false; } //============================================================================= // // Main Program // // // InitSDL // // Start up SDL and SDL_mixer. // boolean InitSDL() { if (SDL_Init(SDL_INIT_AUDIO) == -1) { return false; } if (Mix_OpenAudio(snd_samplerate, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { return false; } atexit(ShutdownSDL); return true; } // // InitPipes // // Ensure that we can communicate. // void InitPipes(HANDLE in, HANDLE out) { midi_process_in = in; midi_process_out = out; atexit(FreePipes); } // // main // // Application entry point. // int main(int argc, char *argv[]) { HANDLE in, out; // Make sure we're not launching this process by itself. if (argc < 5) { MessageBox(NULL, TEXT("This program is tasked with playing Native ") TEXT("MIDI music, and is intended to be launched by ") TEXT(PACKAGE_NAME) TEXT("."), TEXT(PACKAGE_STRING), MB_OK | MB_ICONASTERISK); return EXIT_FAILURE; } // Make sure our Choccolate Doom and midiproc version are lined up. if (strcmp(PACKAGE_STRING, argv[1]) != 0) { char message[1024]; _snprintf(message, sizeof(message), "It appears that the version of %s and %smidiproc are out " "of sync. Please reinstall %s.\r\n\r\n" "Server Version: %s\r\nClient Version: %s", PACKAGE_NAME, PROGRAM_PREFIX, PACKAGE_NAME, PACKAGE_STRING, argv[1]); message[sizeof(message) - 1] = '\0'; MessageBox(NULL, TEXT(message), TEXT(PACKAGE_STRING), MB_OK | MB_ICONASTERISK); return EXIT_FAILURE; } // Parse out the sample rate - if we can't, default to 44100. snd_samplerate = strtol(argv[2], NULL, 10); if (snd_samplerate == LONG_MAX || snd_samplerate == LONG_MIN || snd_samplerate == 0) { snd_samplerate = 44100; } // Parse out our handle ids. in = (HANDLE) strtol(argv[3], NULL, 10); if (in == 0) { return EXIT_FAILURE; } out = (HANDLE) strtol(argv[4], NULL, 10); if (out == 0) { return EXIT_FAILURE; } InitPipes(in, out); if (!InitSDL()) { return EXIT_FAILURE; } if (!ListenForever()) { return EXIT_FAILURE; } return EXIT_SUCCESS; } #endif // #ifdef _WIN32 crispy-doom-crispy-doom-5.6.4/midiproc/proto.h000077500000000000000000000020141360717211000213560ustar00rootroot00000000000000// // Copyright(C) 2017 Alex Mayfield // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // Headers for all types of midipipe messages. // #ifndef __PROTO__ #define __PROTO__ typedef enum { MIDIPIPE_PACKET_TYPE_REGISTER_SONG, MIDIPIPE_PACKET_TYPE__DEPRECATED_1, MIDIPIPE_PACKET_TYPE_SET_VOLUME, MIDIPIPE_PACKET_TYPE_PLAY_SONG, MIDIPIPE_PACKET_TYPE_STOP_SONG, MIDIPIPE_PACKET_TYPE_SHUTDOWN, MIDIPIPE_PACKET_TYPE_UNREGISTER_SONG, MIDIPIPE_PACKET_TYPE_ACK, } net_midipipe_packet_type_t; #endif crispy-doom-crispy-doom-5.6.4/opl/000077500000000000000000000000001360717211000170265ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/opl/.gitignore000066400000000000000000000000631360717211000210150ustar00rootroot00000000000000Makefile.in Makefile .deps libopl.a *.rc tags TAGS crispy-doom-crispy-doom-5.6.4/opl/CMakeLists.txt000066400000000000000000000010031360717211000215600ustar00rootroot00000000000000add_library(opl STATIC opl_internal.h opl.c opl.h opl_linux.c opl_obsd.c opl_queue.c opl_queue.h opl_sdl.c opl_timer.c opl_timer.h opl_win32.c ioperm_sys.c ioperm_sys.h opl3.c opl3.h) target_include_directories(opl INTERFACE "." PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../") target_link_libraries(opl SDL2::mixer) crispy-doom-crispy-doom-5.6.4/opl/Makefile.am000066400000000000000000000012261360717211000210630ustar00rootroot00000000000000 AM_CFLAGS=@SDLMIXER_CFLAGS@ EXTRA_DIST=CMakeLists.txt SUBDIRS = . examples noinst_LIBRARIES=libopl.a libopl_a_SOURCES = \ opl_internal.h \ opl.c opl.h \ opl_linux.c \ opl_obsd.c \ opl_queue.c opl_queue.h \ opl_sdl.c \ opl_timer.c opl_timer.h \ opl_win32.c \ ioperm_sys.c ioperm_sys.h \ opl3.c opl3.h crispy-doom-crispy-doom-5.6.4/opl/examples/000077500000000000000000000000001360717211000206445ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/opl/examples/.gitignore000066400000000000000000000000631360717211000226330ustar00rootroot00000000000000Makefile.in Makefile .deps droplay *.exe tags TAGS crispy-doom-crispy-doom-5.6.4/opl/examples/Makefile.am000066400000000000000000000002311360717211000226740ustar00rootroot00000000000000 AM_CFLAGS = -I$(top_srcdir)/opl noinst_PROGRAMS=droplay droplay_LDADD = ../libopl.a @LDFLAGS@ @SDL_LIBS@ @SDLMIXER_LIBS@ droplay_SOURCES = droplay.c crispy-doom-crispy-doom-5.6.4/opl/examples/droplay.c000066400000000000000000000076051360717211000224720ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // Demonstration program for OPL library to play back DRO // format files. // #include #include #include #include "SDL.h" #include "opl.h" #define HEADER_STRING "DBRAWOPL" #define ADLIB_PORT 0x388 void WriteReg(unsigned int reg, unsigned int val) { int i; // This was recorded from an OPL2, but we are probably playing // back on an OPL3, so we need to enable the original OPL2 // channels. Doom does this already, but other games don't. if ((reg & 0xf0) == OPL_REGS_FEEDBACK) { val |= 0x30; } OPL_WritePort(OPL_REGISTER_PORT, reg); for (i=0; i<6; ++i) { OPL_ReadPort(OPL_REGISTER_PORT); } OPL_WritePort(OPL_DATA_PORT, val); for (i=0; i<35; ++i) { OPL_ReadPort(OPL_REGISTER_PORT); } } void ClearAllRegs(void) { int i; for (i=0; i<=0xff; ++i) { WriteReg(i, 0x00); } } void Init(void) { if (SDL_Init(SDL_INIT_TIMER) < 0) { fprintf(stderr, "Unable to initialise SDL timer\n"); exit(-1); } if (!OPL_Init(ADLIB_PORT)) { fprintf(stderr, "Unable to initialise OPL layer\n"); exit(-1); } } void Shutdown(void) { OPL_Shutdown(); } struct timer_data { int running; FILE *fstream; }; void TimerCallback(void *data) { struct timer_data *timer_data = data; int delay; if (!timer_data->running) { return; } // Read data until we must make a delay. for (;;) { int reg, val; // End of file? if (feof(timer_data->fstream)) { timer_data->running = 0; return; } reg = fgetc(timer_data->fstream); val = fgetc(timer_data->fstream); // Register value of 0 or 1 indicates a delay. if (reg == 0x00) { delay = val; break; } else if (reg == 0x01) { val |= (fgetc(timer_data->fstream) << 8); delay = val; break; } else { WriteReg(reg, val); } } // Schedule the next timer callback. OPL_SetCallback(delay * OPL_MS, TimerCallback, timer_data); } void PlayFile(char *filename) { struct timer_data timer_data; int running; char buf[8]; timer_data.fstream = fopen(filename, "rb"); if (timer_data.fstream == NULL) { fprintf(stderr, "Failed to open %s\n", filename); exit(-1); } if (fread(buf, 1, 8, timer_data.fstream) < 8) { fprintf(stderr, "failed to read raw OPL header\n"); exit(-1); } if (strncmp(buf, HEADER_STRING, 8) != 0) { fprintf(stderr, "Raw OPL header not found\n"); exit(-1); } fseek(timer_data.fstream, 28, SEEK_SET); timer_data.running = 1; // Start callback loop sequence. OPL_SetCallback(0, TimerCallback, &timer_data); // Sleep until the playback finishes. do { OPL_Lock(); running = timer_data.running; OPL_Unlock(); SDL_Delay(100 * OPL_MS); } while (running); fclose(timer_data.fstream); } int main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: %s \n", argv[0]); exit(-1); } Init(); PlayFile(argv[1]); ClearAllRegs(); Shutdown(); return 0; } crispy-doom-crispy-doom-5.6.4/opl/ioperm_sys.c000066400000000000000000000225301360717211000213650ustar00rootroot00000000000000// // Copyright(C) 2002, 2003 Marcel Telka // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // Interface to the ioperm.sys driver, based on code from the // Cygwin ioperm library. // #ifdef _WIN32 #include #define WIN32_LEAN_AND_MEAN #include #include #include #include "ioperm_sys.h" #define IOPERM_FILE L"\\\\.\\ioperm" #define IOCTL_IOPERM \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA00, METHOD_BUFFERED, FILE_ANY_ACCESS) struct ioperm_data { unsigned long from; unsigned long num; int turn_on; }; // Function pointers for advapi32.dll. This DLL does not exist on // Windows 9x, so they are dynamically loaded from the DLL at runtime. // haleyjd 09/09/10: Moved calling conventions into ()'s static SC_HANDLE (WINAPI *MyOpenSCManagerW)(wchar_t *lpMachineName, wchar_t *lpDatabaseName, DWORD dwDesiredAccess) = NULL; static SC_HANDLE (WINAPI *MyCreateServiceW)(SC_HANDLE hSCManager, wchar_t *lpServiceName, wchar_t *lpDisplayName, DWORD dwDesiredAccess, DWORD dwServiceType, DWORD dwStartType, DWORD dwErrorControl, wchar_t *lpBinaryPathName, wchar_t *lpLoadOrderGroup, LPDWORD lpdwTagId, wchar_t *lpDependencies, wchar_t *lpServiceStartName, wchar_t *lpPassword); static SC_HANDLE (WINAPI *MyOpenServiceW)(SC_HANDLE hSCManager, wchar_t *lpServiceName, DWORD dwDesiredAccess); static BOOL (WINAPI *MyStartServiceW)(SC_HANDLE hService, DWORD dwNumServiceArgs, wchar_t **lpServiceArgVectors); static BOOL (WINAPI *MyControlService)(SC_HANDLE hService, DWORD dwControl, LPSERVICE_STATUS lpServiceStatus); static BOOL (WINAPI *MyCloseServiceHandle)(SC_HANDLE hSCObject); static BOOL (WINAPI *MyDeleteService)(SC_HANDLE hService); static struct { char *name; void **fn; } dll_functions[] = { { "OpenSCManagerW", (void **) &MyOpenSCManagerW }, { "CreateServiceW", (void **) &MyCreateServiceW }, { "OpenServiceW", (void **) &MyOpenServiceW }, { "StartServiceW", (void **) &MyStartServiceW }, { "ControlService", (void **) &MyControlService }, { "CloseServiceHandle", (void **) &MyCloseServiceHandle }, { "DeleteService", (void **) &MyDeleteService }, }; // Globals static SC_HANDLE scm = NULL; static SC_HANDLE svc = NULL; static int service_was_created = 0; static int service_was_started = 0; static int LoadLibraryPointers(void) { HMODULE dll; int i; // Already loaded? if (MyOpenSCManagerW != NULL) { return 1; } dll = LoadLibraryW(L"advapi32.dll"); if (dll == NULL) { fprintf(stderr, "LoadLibraryPointers: Failed to open advapi32.dll\n"); return 0; } for (i = 0; i < sizeof(dll_functions) / sizeof(*dll_functions); ++i) { *dll_functions[i].fn = GetProcAddress(dll, dll_functions[i].name); if (*dll_functions[i].fn == NULL) { fprintf(stderr, "LoadLibraryPointers: Failed to get address " "for '%s'\n", dll_functions[i].name); return 0; } } return 1; } int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on) { HANDLE h; struct ioperm_data ioperm_data; DWORD BytesReturned; BOOL r; h = CreateFileW(IOPERM_FILE, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { errno = ENODEV; return -1; } ioperm_data.from = from; ioperm_data.num = num; ioperm_data.turn_on = turn_on; r = DeviceIoControl(h, IOCTL_IOPERM, &ioperm_data, sizeof ioperm_data, NULL, 0, &BytesReturned, NULL); if (!r) { errno = EPERM; } CloseHandle(h); return r != 0; } // Load ioperm.sys driver. // Returns 1 for success, 0 for failure. // Remember to call IOperm_UninstallDriver to uninstall the driver later. int IOperm_InstallDriver(void) { wchar_t driver_path[MAX_PATH]; int error; int result = 1; if (!LoadLibraryPointers()) { return 0; } scm = MyOpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (scm == NULL) { error = GetLastError(); fprintf(stderr, "IOperm_InstallDriver: OpenSCManager failed (%i).\n", error); return 0; } // Get the full path to the driver file. GetFullPathNameW(L"ioperm.sys", MAX_PATH, driver_path, NULL); // Create the service. svc = MyCreateServiceW(scm, L"ioperm", L"ioperm support for Cygwin driver", SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, driver_path, NULL, NULL, NULL, NULL, NULL); if (svc == NULL) { error = GetLastError(); if (error != ERROR_SERVICE_EXISTS) { fprintf(stderr, "IOperm_InstallDriver: Failed to create service (%i).\n", error); } else { svc = MyOpenServiceW(scm, L"ioperm", SERVICE_ALL_ACCESS); if (svc == NULL) { error = GetLastError(); fprintf(stderr, "IOperm_InstallDriver: Failed to open service (%i).\n", error); } } if (svc == NULL) { MyCloseServiceHandle(scm); return 0; } } else { service_was_created = 1; } // Start the service. If the service already existed, it might have // already been running as well. if (!MyStartServiceW(svc, 0, NULL)) { error = GetLastError(); if (error != ERROR_SERVICE_ALREADY_RUNNING) { fprintf(stderr, "IOperm_InstallDriver: Failed to start service (%i).\n", error); result = 0; } else { printf("IOperm_InstallDriver: ioperm driver already running.\n"); } } else { printf("IOperm_InstallDriver: ioperm driver installed.\n"); service_was_started = 1; } // If we failed to start the driver running, we need to clean up // before finishing. if (result == 0) { IOperm_UninstallDriver(); } return result; } int IOperm_UninstallDriver(void) { SERVICE_STATUS stat; int result = 1; int error; // If we started the service, stop it. if (service_was_started) { if (!MyControlService(svc, SERVICE_CONTROL_STOP, &stat)) { error = GetLastError(); if (error == ERROR_SERVICE_NOT_ACTIVE) { fprintf(stderr, "IOperm_UninstallDriver: Service not active? (%i)\n", error); } else { fprintf(stderr, "IOperm_UninstallDriver: Failed to stop service (%i).\n", error); result = 0; } } } // If we created the service, delete it. if (service_was_created) { if (!MyDeleteService(svc)) { error = GetLastError(); fprintf(stderr, "IOperm_UninstallDriver: DeleteService failed (%i).\n", error); result = 0; } else if (service_was_started) { printf("IOperm_UnInstallDriver: ioperm driver uninstalled.\n"); } } // Close handles. if (svc != NULL) { MyCloseServiceHandle(svc); svc = NULL; } if (scm != NULL) { MyCloseServiceHandle(scm); scm = NULL; } service_was_created = 0; service_was_started = 0; return result; } #endif /* #ifndef _WIN32 */ crispy-doom-crispy-doom-5.6.4/opl/ioperm_sys.h000066400000000000000000000016401360717211000213710ustar00rootroot00000000000000// // Copyright(C) 2002, 2003 Marcel Telka // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // Interface to the ioperm.sys driver, based on code from the // Cygwin ioperm library. // #ifndef IOPERM_SYS_H #define IOPERM_SYS_H int IOperm_EnablePortRange(unsigned int from, unsigned int num, int turn_on); int IOperm_InstallDriver(void); int IOperm_UninstallDriver(void); #endif /* #ifndef IOPERM_SYS_H */ crispy-doom-crispy-doom-5.6.4/opl/opl.c000066400000000000000000000255141360717211000177730ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL interface. // #include "config.h" #include #include #include "SDL.h" #include "opl.h" #include "opl_internal.h" //#define OPL_DEBUG_TRACE #if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM) extern opl_driver_t opl_linux_driver; #endif #if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64) extern opl_driver_t opl_openbsd_driver; #endif #ifdef _WIN32 extern opl_driver_t opl_win32_driver; #endif extern opl_driver_t opl_sdl_driver; static opl_driver_t *drivers[] = { #if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM) &opl_linux_driver, #endif #if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64) &opl_openbsd_driver, #endif #ifdef _WIN32 &opl_win32_driver, #endif &opl_sdl_driver, NULL }; static opl_driver_t *driver = NULL; static int init_stage_reg_writes = 1; unsigned int opl_sample_rate = 22050; // // Init/shutdown code. // // Initialize the specified driver and detect an OPL chip. Returns // true if an OPL is detected. static opl_init_result_t InitDriver(opl_driver_t *_driver, unsigned int port_base) { opl_init_result_t result1, result2; // Initialize the driver. if (!_driver->init_func(port_base)) { return OPL_INIT_NONE; } // The driver was initialized okay, so we now have somewhere // to write to. It doesn't mean there's an OPL chip there, // though. Perform the detection sequence to make sure. // (it's done twice, like how Doom does it). driver = _driver; init_stage_reg_writes = 1; result1 = OPL_Detect(); result2 = OPL_Detect(); if (result1 == OPL_INIT_NONE || result2 == OPL_INIT_NONE) { printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name); _driver->shutdown_func(); driver = NULL; return OPL_INIT_NONE; } init_stage_reg_writes = 0; printf("OPL_Init: Using driver '%s'.\n", driver->name); return result2; } // Find a driver automatically by trying each in the list. static opl_init_result_t AutoSelectDriver(unsigned int port_base) { int i; opl_init_result_t result; for (i=0; drivers[i] != NULL; ++i) { result = InitDriver(drivers[i], port_base); if (result != OPL_INIT_NONE) { return result; } } printf("OPL_Init: Failed to find a working driver.\n"); return OPL_INIT_NONE; } // Initialize the OPL library. Return value indicates type of OPL chip // detected, if any. opl_init_result_t OPL_Init(unsigned int port_base) { char *driver_name; int i; int result; driver_name = getenv("OPL_DRIVER"); if (driver_name != NULL) { // Search the list until we find the driver with this name. for (i=0; drivers[i] != NULL; ++i) { if (!strcmp(driver_name, drivers[i]->name)) { result = InitDriver(drivers[i], port_base); if (result) { return result; } else { printf("OPL_Init: Failed to initialize " "driver: '%s'.\n", driver_name); return OPL_INIT_NONE; } } } printf("OPL_Init: unknown driver: '%s'.\n", driver_name); return OPL_INIT_NONE; } else { return AutoSelectDriver(port_base); } } // Shut down the OPL library. void OPL_Shutdown(void) { if (driver != NULL) { driver->shutdown_func(); driver = NULL; } } // Set the sample rate used for software OPL emulation. void OPL_SetSampleRate(unsigned int rate) { opl_sample_rate = rate; } void OPL_WritePort(opl_port_t port, unsigned int value) { if (driver != NULL) { #ifdef OPL_DEBUG_TRACE printf("OPL_write: %i, %x\n", port, value); fflush(stdout); #endif driver->write_port_func(port, value); } } unsigned int OPL_ReadPort(opl_port_t port) { if (driver != NULL) { unsigned int result; #ifdef OPL_DEBUG_TRACE printf("OPL_read: %i...\n", port); fflush(stdout); #endif result = driver->read_port_func(port); #ifdef OPL_DEBUG_TRACE printf("OPL_read: %i -> %x\n", port, result); fflush(stdout); #endif return result; } else { return 0; } } // // 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; if (reg & 0x100) { OPL_WritePort(OPL_REGISTER_PORT_OPL3, reg); } else { 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(); } } // Detect the presence of an OPL chip opl_init_result_t OPL_Detect(void) { int result1, result2; int i; // Reset both timers: OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); // Enable interrupts: OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); // Read status result1 = OPL_ReadStatus(); // Set timer: OPL_WriteRegister(OPL_REG_TIMER1, 0xff); // Start timer 1: OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x21); // Wait for 80 microseconds // This is how Doom does it: for (i=0; i<200; ++i) { OPL_ReadStatus(); } OPL_Delay(1 * OPL_MS); // Read status result2 = OPL_ReadStatus(); // Reset both timers: OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60); // Enable interrupts: OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80); if ((result1 & 0xe0) == 0x00 && (result2 & 0xe0) == 0xc0) { result1 = OPL_ReadPort(OPL_REGISTER_PORT); result2 = OPL_ReadPort(OPL_REGISTER_PORT_OPL3); if (result1 == 0x00) { return OPL_INIT_OPL3; } else { return OPL_INIT_OPL2; } } else { return OPL_INIT_NONE; } } // Initialize registers on startup void OPL_InitRegisters(int opl3) { 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); if (opl3) { OPL_WriteRegister(OPL_REG_NEW, 0x01); // Initialize level registers for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) { OPL_WriteRegister(r | 0x100, 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 | 0x100, 0x00); } // More registers ... for (r=1; r < OPL_REGS_LEVEL; ++r) { OPL_WriteRegister(r | 0x100, 0x00); } } // Keyboard split point on (?) OPL_WriteRegister(OPL_REG_FM_MODE, 0x40); if (opl3) { OPL_WriteRegister(OPL_REG_NEW, 0x01); } } // // Timer functions. // void OPL_SetCallback(uint64_t us, opl_callback_t callback, void *data) { if (driver != NULL) { driver->set_callback_func(us, callback, data); } } void OPL_ClearCallbacks(void) { if (driver != NULL) { driver->clear_callbacks_func(); } } void OPL_Lock(void) { if (driver != NULL) { driver->lock_func(); } } void OPL_Unlock(void) { if (driver != NULL) { driver->unlock_func(); } } typedef struct { int finished; SDL_mutex *mutex; SDL_cond *cond; } delay_data_t; static void DelayCallback(void *_delay_data) { delay_data_t *delay_data = _delay_data; SDL_LockMutex(delay_data->mutex); delay_data->finished = 1; SDL_CondSignal(delay_data->cond); SDL_UnlockMutex(delay_data->mutex); } void OPL_Delay(uint64_t us) { delay_data_t delay_data; if (driver == NULL) { return; } // Create a callback that will signal this thread after the // specified time. delay_data.finished = 0; delay_data.mutex = SDL_CreateMutex(); delay_data.cond = SDL_CreateCond(); OPL_SetCallback(us, DelayCallback, &delay_data); // Wait until the callback is invoked. SDL_LockMutex(delay_data.mutex); while (!delay_data.finished) { SDL_CondWait(delay_data.cond, delay_data.mutex); } SDL_UnlockMutex(delay_data.mutex); // Clean up. SDL_DestroyMutex(delay_data.mutex); SDL_DestroyCond(delay_data.cond); } void OPL_SetPaused(int paused) { if (driver != NULL) { driver->set_paused_func(paused); } } void OPL_AdjustCallbacks(float value) { if (driver != NULL) { driver->adjust_callbacks_func(value); } } crispy-doom-crispy-doom-5.6.4/opl/opl.h000066400000000000000000000065451360717211000200030ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL interface. // #ifndef OPL_OPL_H #define OPL_OPL_H #include typedef void (*opl_callback_t)(void *data); // Result from OPL_Init(), indicating what type of OPL chip was detected, // if any. typedef enum { OPL_INIT_NONE, OPL_INIT_OPL2, OPL_INIT_OPL3, } opl_init_result_t; typedef enum { OPL_REGISTER_PORT = 0, OPL_DATA_PORT = 1, OPL_REGISTER_PORT_OPL3 = 2 } 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 #define OPL_REG_NEW 0x105 // 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 // Times #define OPL_SECOND ((uint64_t) 1000 * 1000) #define OPL_MS ((uint64_t) 1000) #define OPL_US ((uint64_t) 1) // // Low-level functions. // // Initialize the OPL subsystem. opl_init_result_t OPL_Init(unsigned int port_base); // Shut down the OPL subsystem. void OPL_Shutdown(void); // Set the sample rate used for software emulation. void OPL_SetSampleRate(unsigned int rate); // 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. opl_init_result_t OPL_Detect(void); // Initialize all registers, performed on startup. void OPL_InitRegisters(int opl3); // // Timer callback functions. // // Set a timer callback. After the specified number of microseconds // have elapsed, the callback will be invoked. void OPL_SetCallback(uint64_t us, opl_callback_t callback, void *data); // Adjust callback times by the specified factor. For example, a value of // 0.5 will halve all remaining times. void OPL_AdjustCallbacks(float factor); // Clear all OPL callbacks that have been set. void OPL_ClearCallbacks(void); // Begin critical section, during which, OPL callbacks will not be // invoked. void OPL_Lock(void); // End critical section. void OPL_Unlock(void); // Block until the specified number of microseconds have elapsed. void OPL_Delay(uint64_t us); // Pause the OPL callbacks. void OPL_SetPaused(int paused); #endif crispy-doom-crispy-doom-5.6.4/opl/opl3.c000066400000000000000000001130311360717211000200460ustar00rootroot00000000000000// // Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Nuked OPL3 emulator. // Thanks: // MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): // Feedback and Rhythm part calculation information. // forums.submarine.org.uk(carbon14, opl3): // Tremolo and phase generator calculation information. // OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): // OPL2 ROMs. // siliconpr0n.org(John McMaster, digshadow): // YMF262 and VRC VII decaps and die shots. // // version: 1.8 // #include #include #include #include "opl3.h" #define RSM_FRAC 10 // Channel types enum { ch_2op = 0, ch_4op = 1, ch_4op2 = 2, ch_drum = 3 }; // Envelope key types enum { egk_norm = 0x01, egk_drum = 0x02 }; // // logsin table // static const Bit16u logsinrom[256] = { 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 }; // // exp table // static const Bit16u exprom[256] = { 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 }; // // freq mult table multiplied by 2 // // 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 // static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; // // ksl table // static const Bit8u kslrom[16] = { 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 }; static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; // // envelope generator constants // static const Bit8u eg_incstep[4][4] = { { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 1, 1, 0 } }; // // address decoding // static const Bit8s ad_slot[0x20] = { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static const Bit8u ch_slot[18] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; // // Envelope generator // typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); typedef void(*envelope_genfunc)(opl3_slot *slott); static Bit16s OPL3_EnvelopeCalcExp(Bit32u level) { if (level > 0x1fff) { level = 0x1fff; } return (exprom[level & 0xff] << 1) >> (level >> 8); } static Bit16s OPL3_EnvelopeCalcSin0(Bit16u phase, Bit16u envelope) { Bit16u out = 0; Bit16u neg = 0; phase &= 0x3ff; if (phase & 0x200) { neg = 0xffff; } if (phase & 0x100) { out = logsinrom[(phase & 0xff) ^ 0xff]; } else { out = logsinrom[phase & 0xff]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; } static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope) { Bit16u out = 0; phase &= 0x3ff; if (phase & 0x200) { out = 0x1000; } else if (phase & 0x100) { out = logsinrom[(phase & 0xff) ^ 0xff]; } else { out = logsinrom[phase & 0xff]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } static Bit16s OPL3_EnvelopeCalcSin2(Bit16u phase, Bit16u envelope) { Bit16u out = 0; phase &= 0x3ff; if (phase & 0x100) { out = logsinrom[(phase & 0xff) ^ 0xff]; } else { out = logsinrom[phase & 0xff]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope) { Bit16u out = 0; phase &= 0x3ff; if (phase & 0x100) { out = 0x1000; } else { out = logsinrom[phase & 0xff]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope) { Bit16u out = 0; Bit16u neg = 0; phase &= 0x3ff; if ((phase & 0x300) == 0x100) { neg = 0xffff; } if (phase & 0x200) { out = 0x1000; } else if (phase & 0x80) { out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; } else { out = logsinrom[(phase << 1) & 0xff]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; } static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope) { Bit16u out = 0; phase &= 0x3ff; if (phase & 0x200) { out = 0x1000; } else if (phase & 0x80) { out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; } else { out = logsinrom[(phase << 1) & 0xff]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } static Bit16s OPL3_EnvelopeCalcSin6(Bit16u phase, Bit16u envelope) { Bit16u neg = 0; phase &= 0x3ff; if (phase & 0x200) { neg = 0xffff; } return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; } static Bit16s OPL3_EnvelopeCalcSin7(Bit16u phase, Bit16u envelope) { Bit16u out = 0; Bit16u neg = 0; phase &= 0x3ff; if (phase & 0x200) { neg = 0xffff; phase = (phase & 0x1ff) ^ 0x1ff; } out = phase << 3; return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; } static const envelope_sinfunc envelope_sin[8] = { OPL3_EnvelopeCalcSin0, OPL3_EnvelopeCalcSin1, OPL3_EnvelopeCalcSin2, OPL3_EnvelopeCalcSin3, OPL3_EnvelopeCalcSin4, OPL3_EnvelopeCalcSin5, OPL3_EnvelopeCalcSin6, OPL3_EnvelopeCalcSin7 }; enum envelope_gen_num { envelope_gen_num_attack = 0, envelope_gen_num_decay = 1, envelope_gen_num_sustain = 2, envelope_gen_num_release = 3 }; static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) { Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) - ((0x08 - slot->channel->block) << 5); if (ksl < 0) { ksl = 0; } slot->eg_ksl = (Bit8u)ksl; } static void OPL3_EnvelopeCalc(opl3_slot *slot) { Bit8u nonzero; Bit8u rate; Bit8u rate_hi; Bit8u rate_lo; Bit8u reg_rate = 0; Bit8u ks; Bit8u eg_shift, shift; Bit16u eg_rout; Bit16s eg_inc; Bit8u eg_off; Bit8u reset = 0; slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; if (slot->key && slot->eg_gen == envelope_gen_num_release) { reset = 1; reg_rate = slot->reg_ar; } else { switch (slot->eg_gen) { case envelope_gen_num_attack: reg_rate = slot->reg_ar; break; case envelope_gen_num_decay: reg_rate = slot->reg_dr; break; case envelope_gen_num_sustain: if (!slot->reg_type) { reg_rate = slot->reg_rr; } break; case envelope_gen_num_release: reg_rate = slot->reg_rr; break; } } slot->pg_reset = reset; ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); nonzero = (reg_rate != 0); rate = ks + (reg_rate << 2); rate_hi = rate >> 2; rate_lo = rate & 0x03; if (rate_hi & 0x10) { rate_hi = 0x0f; } eg_shift = rate_hi + slot->chip->eg_add; shift = 0; if (nonzero) { if (rate_hi < 12) { if (slot->chip->eg_state) { switch (eg_shift) { case 12: shift = 1; break; case 13: shift = (rate_lo >> 1) & 0x01; break; case 14: shift = rate_lo & 0x01; break; default: break; } } } else { shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03]; if (shift & 0x04) { shift = 0x03; } if (!shift) { shift = slot->chip->eg_state; } } } eg_rout = slot->eg_rout; eg_inc = 0; eg_off = 0; // Instant attack if (reset && rate_hi == 0x0f) { eg_rout = 0x00; } // Envelope off if ((slot->eg_rout & 0x1f8) == 0x1f8) { eg_off = 1; } if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) { eg_rout = 0x1ff; } switch (slot->eg_gen) { case envelope_gen_num_attack: if (!slot->eg_rout) { slot->eg_gen = envelope_gen_num_decay; } else if (slot->key && shift > 0 && rate_hi != 0x0f) { eg_inc = ((~slot->eg_rout) << shift) >> 4; } break; case envelope_gen_num_decay: if ((slot->eg_rout >> 4) == slot->reg_sl) { slot->eg_gen = envelope_gen_num_sustain; } else if (!eg_off && !reset && shift > 0) { eg_inc = 1 << (shift - 1); } break; case envelope_gen_num_sustain: case envelope_gen_num_release: if (!eg_off && !reset && shift > 0) { eg_inc = 1 << (shift - 1); } break; } slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; // Key off if (reset) { slot->eg_gen = envelope_gen_num_attack; } if (!slot->key) { slot->eg_gen = envelope_gen_num_release; } } static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type) { slot->key |= type; } static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type) { slot->key &= ~type; } // // Phase Generator // static void OPL3_PhaseGenerate(opl3_slot *slot) { opl3_chip *chip; Bit16u f_num; Bit32u basefreq; Bit8u rm_xor, n_bit; Bit32u noise; Bit16u phase; chip = slot->chip; f_num = slot->channel->f_num; if (slot->reg_vib) { Bit8s range; Bit8u vibpos; range = (f_num >> 7) & 7; vibpos = slot->chip->vibpos; if (!(vibpos & 3)) { range = 0; } else if (vibpos & 1) { range >>= 1; } range >>= slot->chip->vibshift; if (vibpos & 4) { range = -range; } f_num += range; } basefreq = (f_num << slot->channel->block) >> 1; phase = (Bit16u)(slot->pg_phase >> 9); if (slot->pg_reset) { slot->pg_phase = 0; } slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; // Rhythm mode noise = chip->noise; slot->pg_phase_out = phase; if (slot->slot_num == 13) // hh { chip->rm_hh_bit2 = (phase >> 2) & 1; chip->rm_hh_bit3 = (phase >> 3) & 1; chip->rm_hh_bit7 = (phase >> 7) & 1; chip->rm_hh_bit8 = (phase >> 8) & 1; } if (slot->slot_num == 17 && (chip->rhy & 0x20)) // tc { chip->rm_tc_bit3 = (phase >> 3) & 1; chip->rm_tc_bit5 = (phase >> 5) & 1; } if (chip->rhy & 0x20) { rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); switch (slot->slot_num) { case 13: // hh slot->pg_phase_out = rm_xor << 9; if (rm_xor ^ (noise & 1)) { slot->pg_phase_out |= 0xd0; } else { slot->pg_phase_out |= 0x34; } break; case 16: // sd slot->pg_phase_out = (chip->rm_hh_bit8 << 9) | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); break; case 17: // tc slot->pg_phase_out = (rm_xor << 9) | 0x80; break; default: break; } } n_bit = ((noise >> 14) ^ noise) & 0x01; chip->noise = (noise >> 1) | (n_bit << 22); } // // Slot // static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) { if ((data >> 7) & 0x01) { slot->trem = &slot->chip->tremolo; } else { slot->trem = (Bit8u*)&slot->chip->zeromod; } slot->reg_vib = (data >> 6) & 0x01; slot->reg_type = (data >> 5) & 0x01; slot->reg_ksr = (data >> 4) & 0x01; slot->reg_mult = data & 0x0f; } static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data) { slot->reg_ksl = (data >> 6) & 0x03; slot->reg_tl = data & 0x3f; OPL3_EnvelopeUpdateKSL(slot); } static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data) { slot->reg_ar = (data >> 4) & 0x0f; slot->reg_dr = data & 0x0f; } static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) { slot->reg_sl = (data >> 4) & 0x0f; if (slot->reg_sl == 0x0f) { slot->reg_sl = 0x1f; } slot->reg_rr = data & 0x0f; } static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) { slot->reg_wf = data & 0x07; if (slot->chip->newm == 0x00) { slot->reg_wf &= 0x03; } } static void OPL3_SlotGenerate(opl3_slot *slot) { slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); } static void OPL3_SlotCalcFB(opl3_slot *slot) { if (slot->channel->fb != 0x00) { slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); } else { slot->fbmod = 0; } slot->prout = slot->out; } // // Channel // static void OPL3_ChannelSetupAlg(opl3_channel *channel); static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) { opl3_channel *channel6; opl3_channel *channel7; opl3_channel *channel8; Bit8u chnum; chip->rhy = data & 0x3f; if (chip->rhy & 0x20) { channel6 = &chip->channel[6]; channel7 = &chip->channel[7]; channel8 = &chip->channel[8]; channel6->out[0] = &channel6->slots[1]->out; channel6->out[1] = &channel6->slots[1]->out; channel6->out[2] = &chip->zeromod; channel6->out[3] = &chip->zeromod; channel7->out[0] = &channel7->slots[0]->out; channel7->out[1] = &channel7->slots[0]->out; channel7->out[2] = &channel7->slots[1]->out; channel7->out[3] = &channel7->slots[1]->out; channel8->out[0] = &channel8->slots[0]->out; channel8->out[1] = &channel8->slots[0]->out; channel8->out[2] = &channel8->slots[1]->out; channel8->out[3] = &channel8->slots[1]->out; for (chnum = 6; chnum < 9; chnum++) { chip->channel[chnum].chtype = ch_drum; } OPL3_ChannelSetupAlg(channel6); OPL3_ChannelSetupAlg(channel7); OPL3_ChannelSetupAlg(channel8); //hh if (chip->rhy & 0x01) { OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum); } else { OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum); } //tc if (chip->rhy & 0x02) { OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum); } else { OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum); } //tom if (chip->rhy & 0x04) { OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum); } else { OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum); } //sd if (chip->rhy & 0x08) { OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum); } else { OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum); } //bd if (chip->rhy & 0x10) { OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum); OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum); } else { OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum); OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum); } } else { for (chnum = 6; chnum < 9; chnum++) { chip->channel[chnum].chtype = ch_2op; OPL3_ChannelSetupAlg(&chip->channel[chnum]); OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum); OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum); } } } static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) { if (channel->chip->newm && channel->chtype == ch_4op2) { return; } channel->f_num = (channel->f_num & 0x300) | data; channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); OPL3_EnvelopeUpdateKSL(channel->slots[0]); OPL3_EnvelopeUpdateKSL(channel->slots[1]); if (channel->chip->newm && channel->chtype == ch_4op) { channel->pair->f_num = channel->f_num; channel->pair->ksv = channel->ksv; OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); } } static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) { if (channel->chip->newm && channel->chtype == ch_4op2) { return; } channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); channel->block = (data >> 2) & 0x07; channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); OPL3_EnvelopeUpdateKSL(channel->slots[0]); OPL3_EnvelopeUpdateKSL(channel->slots[1]); if (channel->chip->newm && channel->chtype == ch_4op) { channel->pair->f_num = channel->f_num; channel->pair->block = channel->block; channel->pair->ksv = channel->ksv; OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); } } static void OPL3_ChannelSetupAlg(opl3_channel *channel) { if (channel->chtype == ch_drum) { if (channel->ch_num == 7 || channel->ch_num == 8) { channel->slots[0]->mod = &channel->chip->zeromod; channel->slots[1]->mod = &channel->chip->zeromod; return; } switch (channel->alg & 0x01) { case 0x00: channel->slots[0]->mod = &channel->slots[0]->fbmod; channel->slots[1]->mod = &channel->slots[0]->out; break; case 0x01: channel->slots[0]->mod = &channel->slots[0]->fbmod; channel->slots[1]->mod = &channel->chip->zeromod; break; } return; } if (channel->alg & 0x08) { return; } if (channel->alg & 0x04) { channel->pair->out[0] = &channel->chip->zeromod; channel->pair->out[1] = &channel->chip->zeromod; channel->pair->out[2] = &channel->chip->zeromod; channel->pair->out[3] = &channel->chip->zeromod; switch (channel->alg & 0x03) { case 0x00: channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; channel->slots[0]->mod = &channel->pair->slots[1]->out; channel->slots[1]->mod = &channel->slots[0]->out; channel->out[0] = &channel->slots[1]->out; channel->out[1] = &channel->chip->zeromod; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x01: channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; channel->slots[0]->mod = &channel->chip->zeromod; channel->slots[1]->mod = &channel->slots[0]->out; channel->out[0] = &channel->pair->slots[1]->out; channel->out[1] = &channel->slots[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x02: channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; channel->pair->slots[1]->mod = &channel->chip->zeromod; channel->slots[0]->mod = &channel->pair->slots[1]->out; channel->slots[1]->mod = &channel->slots[0]->out; channel->out[0] = &channel->pair->slots[0]->out; channel->out[1] = &channel->slots[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x03: channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; channel->pair->slots[1]->mod = &channel->chip->zeromod; channel->slots[0]->mod = &channel->pair->slots[1]->out; channel->slots[1]->mod = &channel->chip->zeromod; channel->out[0] = &channel->pair->slots[0]->out; channel->out[1] = &channel->slots[0]->out; channel->out[2] = &channel->slots[1]->out; channel->out[3] = &channel->chip->zeromod; break; } } else { switch (channel->alg & 0x01) { case 0x00: channel->slots[0]->mod = &channel->slots[0]->fbmod; channel->slots[1]->mod = &channel->slots[0]->out; channel->out[0] = &channel->slots[1]->out; channel->out[1] = &channel->chip->zeromod; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x01: channel->slots[0]->mod = &channel->slots[0]->fbmod; channel->slots[1]->mod = &channel->chip->zeromod; channel->out[0] = &channel->slots[0]->out; channel->out[1] = &channel->slots[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; } } } static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) { channel->fb = (data & 0x0e) >> 1; channel->con = data & 0x01; channel->alg = channel->con; if (channel->chip->newm) { if (channel->chtype == ch_4op) { channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); channel->alg = 0x08; OPL3_ChannelSetupAlg(channel->pair); } else if (channel->chtype == ch_4op2) { channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); channel->pair->alg = 0x08; OPL3_ChannelSetupAlg(channel); } else { OPL3_ChannelSetupAlg(channel); } } else { OPL3_ChannelSetupAlg(channel); } if (channel->chip->newm) { channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; } else { channel->cha = channel->chb = (Bit16u)~0; } } static void OPL3_ChannelKeyOn(opl3_channel *channel) { if (channel->chip->newm) { if (channel->chtype == ch_4op) { OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm); OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm); } else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); } } else { OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); } } static void OPL3_ChannelKeyOff(opl3_channel *channel) { if (channel->chip->newm) { if (channel->chtype == ch_4op) { OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm); OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm); } else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); } } else { OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); } } static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) { Bit8u bit; Bit8u chnum; for (bit = 0; bit < 6; bit++) { chnum = bit; if (bit >= 3) { chnum += 9 - 3; } if ((data >> bit) & 0x01) { chip->channel[chnum].chtype = ch_4op; chip->channel[chnum + 3].chtype = ch_4op2; } else { chip->channel[chnum].chtype = ch_2op; chip->channel[chnum + 3].chtype = ch_2op; } } } static Bit16s OPL3_ClipSample(Bit32s sample) { if (sample > 32767) { sample = 32767; } else if (sample < -32768) { sample = -32768; } return (Bit16s)sample; } void OPL3_Generate(opl3_chip *chip, Bit16s *buf) { Bit8u ii; Bit8u jj; Bit16s accm; Bit8u shift = 0; buf[1] = OPL3_ClipSample(chip->mixbuff[1]); for (ii = 0; ii < 15; ii++) { OPL3_SlotCalcFB(&chip->slot[ii]); OPL3_EnvelopeCalc(&chip->slot[ii]); OPL3_PhaseGenerate(&chip->slot[ii]); OPL3_SlotGenerate(&chip->slot[ii]); } chip->mixbuff[0] = 0; for (ii = 0; ii < 18; ii++) { accm = 0; for (jj = 0; jj < 4; jj++) { accm += *chip->channel[ii].out[jj]; } chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); } for (ii = 15; ii < 18; ii++) { OPL3_SlotCalcFB(&chip->slot[ii]); OPL3_EnvelopeCalc(&chip->slot[ii]); OPL3_PhaseGenerate(&chip->slot[ii]); OPL3_SlotGenerate(&chip->slot[ii]); } buf[0] = OPL3_ClipSample(chip->mixbuff[0]); for (ii = 18; ii < 33; ii++) { OPL3_SlotCalcFB(&chip->slot[ii]); OPL3_EnvelopeCalc(&chip->slot[ii]); OPL3_PhaseGenerate(&chip->slot[ii]); OPL3_SlotGenerate(&chip->slot[ii]); } chip->mixbuff[1] = 0; for (ii = 0; ii < 18; ii++) { accm = 0; for (jj = 0; jj < 4; jj++) { accm += *chip->channel[ii].out[jj]; } chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); } for (ii = 33; ii < 36; ii++) { OPL3_SlotCalcFB(&chip->slot[ii]); OPL3_EnvelopeCalc(&chip->slot[ii]); OPL3_PhaseGenerate(&chip->slot[ii]); OPL3_SlotGenerate(&chip->slot[ii]); } if ((chip->timer & 0x3f) == 0x3f) { chip->tremolopos = (chip->tremolopos + 1) % 210; } if (chip->tremolopos < 105) { chip->tremolo = chip->tremolopos >> chip->tremoloshift; } else { chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; } if ((chip->timer & 0x3ff) == 0x3ff) { chip->vibpos = (chip->vibpos + 1) & 7; } chip->timer++; chip->eg_add = 0; if (chip->eg_timer) { while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) { shift++; } if (shift > 12) { chip->eg_add = 0; } else { chip->eg_add = shift + 1; } } if (chip->eg_timerrem || chip->eg_state) { if (chip->eg_timer == 0xfffffffff) { chip->eg_timer = 0; chip->eg_timerrem = 1; } else { chip->eg_timer++; chip->eg_timerrem = 0; } } chip->eg_state ^= 1; while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) { if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200)) { break; } chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, chip->writebuf[chip->writebuf_cur].data); chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; } chip->writebuf_samplecnt++; } void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf) { while (chip->samplecnt >= chip->rateratio) { chip->oldsamples[0] = chip->samples[0]; chip->oldsamples[1] = chip->samples[1]; OPL3_Generate(chip, chip->samples); chip->samplecnt -= chip->rateratio; } buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + chip->samples[0] * chip->samplecnt) / chip->rateratio); buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + chip->samples[1] * chip->samplecnt) / chip->rateratio); chip->samplecnt += 1 << RSM_FRAC; } void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) { Bit8u slotnum; Bit8u channum; memset(chip, 0, sizeof(opl3_chip)); for (slotnum = 0; slotnum < 36; slotnum++) { chip->slot[slotnum].chip = chip; chip->slot[slotnum].mod = &chip->zeromod; chip->slot[slotnum].eg_rout = 0x1ff; chip->slot[slotnum].eg_out = 0x1ff; chip->slot[slotnum].eg_gen = envelope_gen_num_release; chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod; chip->slot[slotnum].slot_num = slotnum; } for (channum = 0; channum < 18; channum++) { chip->channel[channum].slots[0] = &chip->slot[ch_slot[channum]]; chip->channel[channum].slots[1] = &chip->slot[ch_slot[channum] + 3]; chip->slot[ch_slot[channum]].channel = &chip->channel[channum]; chip->slot[ch_slot[channum] + 3].channel = &chip->channel[channum]; if ((channum % 9) < 3) { chip->channel[channum].pair = &chip->channel[channum + 3]; } else if ((channum % 9) < 6) { chip->channel[channum].pair = &chip->channel[channum - 3]; } chip->channel[channum].chip = chip; chip->channel[channum].out[0] = &chip->zeromod; chip->channel[channum].out[1] = &chip->zeromod; chip->channel[channum].out[2] = &chip->zeromod; chip->channel[channum].out[3] = &chip->zeromod; chip->channel[channum].chtype = ch_2op; chip->channel[channum].cha = 0xffff; chip->channel[channum].chb = 0xffff; chip->channel[channum].ch_num = channum; OPL3_ChannelSetupAlg(&chip->channel[channum]); } chip->noise = 1; chip->rateratio = (samplerate << RSM_FRAC) / 49716; chip->tremoloshift = 4; chip->vibshift = 1; } void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) { Bit8u high = (reg >> 8) & 0x01; Bit8u regm = reg & 0xff; switch (regm & 0xf0) { case 0x00: if (high) { switch (regm & 0x0f) { case 0x04: OPL3_ChannelSet4Op(chip, v); break; case 0x05: chip->newm = v & 0x01; break; } } else { switch (regm & 0x0f) { case 0x08: chip->nts = (v >> 6) & 0x01; break; } } break; case 0x20: case 0x30: if (ad_slot[regm & 0x1f] >= 0) { OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x40: case 0x50: if (ad_slot[regm & 0x1f] >= 0) { OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x60: case 0x70: if (ad_slot[regm & 0x1f] >= 0) { OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x80: case 0x90: if (ad_slot[regm & 0x1f] >= 0) { OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0xe0: case 0xf0: if (ad_slot[regm & 0x1f] >= 0) { OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0xa0: if ((regm & 0x0f) < 9) { OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); } break; case 0xb0: if (regm == 0xbd && !high) { chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; chip->vibshift = ((v >> 6) & 0x01) ^ 1; OPL3_ChannelUpdateRhythm(chip, v); } else if ((regm & 0x0f) < 9) { OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); if (v & 0x20) { OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); } else { OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); } } break; case 0xc0: if ((regm & 0x0f) < 9) { OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); } break; } } void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) { Bit64u time1, time2; if (chip->writebuf[chip->writebuf_last].reg & 0x200) { OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, chip->writebuf[chip->writebuf_last].data); chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; } chip->writebuf[chip->writebuf_last].reg = reg | 0x200; chip->writebuf[chip->writebuf_last].data = v; time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; time2 = chip->writebuf_samplecnt; if (time1 < time2) { time1 = time2; } chip->writebuf[chip->writebuf_last].time = time1; chip->writebuf_lasttime = time1; chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; } void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) { Bit32u i; for(i = 0; i < numsamples; i++) { OPL3_GenerateResampled(chip, sndptr); sndptr += 2; } } crispy-doom-crispy-doom-5.6.4/opl/opl3.h000066400000000000000000000072051360717211000200600ustar00rootroot00000000000000// // Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Nuked OPL3 emulator. // Thanks: // MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): // Feedback and Rhythm part calculation information. // forums.submarine.org.uk(carbon14, opl3): // Tremolo and phase generator calculation information. // OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): // OPL2 ROMs. // siliconpr0n.org(John McMaster, digshadow): // YMF262 and VRC VII decaps and die shots. // // version: 1.8 // #ifndef OPL_OPL3_H #define OPL_OPL3_H #include #define OPL_WRITEBUF_SIZE 1024 #define OPL_WRITEBUF_DELAY 2 typedef uintptr_t Bitu; typedef intptr_t Bits; typedef uint64_t Bit64u; typedef int64_t Bit64s; typedef uint32_t Bit32u; typedef int32_t Bit32s; typedef uint16_t Bit16u; typedef int16_t Bit16s; typedef uint8_t Bit8u; typedef int8_t Bit8s; typedef struct _opl3_slot opl3_slot; typedef struct _opl3_channel opl3_channel; typedef struct _opl3_chip opl3_chip; struct _opl3_slot { opl3_channel *channel; opl3_chip *chip; Bit16s out; Bit16s fbmod; Bit16s *mod; Bit16s prout; Bit16s eg_rout; Bit16s eg_out; Bit8u eg_inc; Bit8u eg_gen; Bit8u eg_rate; Bit8u eg_ksl; Bit8u *trem; Bit8u reg_vib; Bit8u reg_type; Bit8u reg_ksr; Bit8u reg_mult; Bit8u reg_ksl; Bit8u reg_tl; Bit8u reg_ar; Bit8u reg_dr; Bit8u reg_sl; Bit8u reg_rr; Bit8u reg_wf; Bit8u key; Bit32u pg_reset; Bit32u pg_phase; Bit16u pg_phase_out; Bit8u slot_num; }; struct _opl3_channel { opl3_slot *slots[2]; opl3_channel *pair; opl3_chip *chip; Bit16s *out[4]; Bit8u chtype; Bit16u f_num; Bit8u block; Bit8u fb; Bit8u con; Bit8u alg; Bit8u ksv; Bit16u cha, chb; Bit8u ch_num; }; typedef struct _opl3_writebuf { Bit64u time; Bit16u reg; Bit8u data; } opl3_writebuf; struct _opl3_chip { opl3_channel channel[18]; opl3_slot slot[36]; Bit16u timer; Bit64u eg_timer; Bit8u eg_timerrem; Bit8u eg_state; Bit8u eg_add; Bit8u newm; Bit8u nts; Bit8u rhy; Bit8u vibpos; Bit8u vibshift; Bit8u tremolo; Bit8u tremolopos; Bit8u tremoloshift; Bit32u noise; Bit16s zeromod; Bit32s mixbuff[2]; Bit8u rm_hh_bit2; Bit8u rm_hh_bit3; Bit8u rm_hh_bit7; Bit8u rm_hh_bit8; Bit8u rm_tc_bit3; Bit8u rm_tc_bit5; //OPL3L Bit32s rateratio; Bit32s samplecnt; Bit16s oldsamples[2]; Bit16s samples[2]; Bit64u writebuf_samplecnt; Bit32u writebuf_cur; Bit32u writebuf_last; Bit64u writebuf_lasttime; opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; }; void OPL3_Generate(opl3_chip *chip, Bit16s *buf); void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf); void OPL3_Reset(opl3_chip *chip, Bit32u samplerate); void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); #endif crispy-doom-crispy-doom-5.6.4/opl/opl_internal.h000066400000000000000000000035231360717211000216700ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL internal interface. // #ifndef OPL_INTERNAL_H #define OPL_INTERNAL_H #include "opl.h" typedef int (*opl_init_func)(unsigned int port_base); typedef void (*opl_shutdown_func)(void); typedef unsigned int (*opl_read_port_func)(opl_port_t port); typedef void (*opl_write_port_func)(opl_port_t port, unsigned int value); typedef void (*opl_set_callback_func)(uint64_t us, opl_callback_t callback, void *data); typedef void (*opl_clear_callbacks_func)(void); typedef void (*opl_lock_func)(void); typedef void (*opl_unlock_func)(void); typedef void (*opl_set_paused_func)(int paused); typedef void (*opl_adjust_callbacks_func)(float value); typedef struct { const char *name; opl_init_func init_func; opl_shutdown_func shutdown_func; opl_read_port_func read_port_func; opl_write_port_func write_port_func; opl_set_callback_func set_callback_func; opl_clear_callbacks_func clear_callbacks_func; opl_lock_func lock_func; opl_unlock_func unlock_func; opl_set_paused_func set_paused_func; opl_adjust_callbacks_func adjust_callbacks_func; } opl_driver_t; // Sample rate to use when doing software emulation. extern unsigned int opl_sample_rate; #endif /* #ifndef OPL_INTERNAL_H */ crispy-doom-crispy-doom-5.6.4/opl/opl_linux.c000066400000000000000000000044501360717211000212060ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL Linux interface. // #include "config.h" #if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM) #include #include #include #include #include #include "opl.h" #include "opl_internal.h" #include "opl_timer.h" static unsigned int opl_port_base; static int OPL_Linux_Init(unsigned int port_base) { // Try to get permissions: if (ioperm(port_base, 2, 1) < 0) { fprintf(stderr, "Failed to get I/O port permissions for 0x%x: %s\n", port_base, strerror(errno)); if (errno == EPERM) { fprintf(stderr, "\tYou may need to run the program as root in order\n" "\tto acquire I/O port permissions for OPL MIDI playback.\n"); } return 0; } opl_port_base = port_base; // Start callback thread if (!OPL_Timer_StartThread()) { ioperm(port_base, 2, 0); return 0; } return 1; } static void OPL_Linux_Shutdown(void) { // Stop callback thread OPL_Timer_StopThread(); // Release permissions ioperm(opl_port_base, 2, 0); } static unsigned int OPL_Linux_PortRead(opl_port_t port) { return inb(opl_port_base + port); } static void OPL_Linux_PortWrite(opl_port_t port, unsigned int value) { outb(value, opl_port_base + port); } opl_driver_t opl_linux_driver = { "Linux", OPL_Linux_Init, OPL_Linux_Shutdown, OPL_Linux_PortRead, OPL_Linux_PortWrite, OPL_Timer_SetCallback, OPL_Timer_ClearCallbacks, OPL_Timer_Lock, OPL_Timer_Unlock, OPL_Timer_SetPaused, OPL_Timer_AdjustCallbacks, }; #endif /* #if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM) */ crispy-doom-crispy-doom-5.6.4/opl/opl_obsd.c000066400000000000000000000047441360717211000210040ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL OpenBSD interface (also NetBSD) // #include "config.h" // OpenBSD has a i386_iopl on i386 and amd64_iopl on x86_64, // even though they do the same thing. Take care of this // here, and map set_iopl to point to the appropriate name. #if defined(HAVE_LIBI386) #include #include #include #define set_iopl i386_iopl #elif defined(HAVE_LIBAMD64) #include #include #include #define set_iopl amd64_iopl #else #define NO_OBSD_DRIVER #endif // If the above succeeded, proceed with the rest. #ifndef NO_OBSD_DRIVER #include #include #include #include #include "opl.h" #include "opl_internal.h" #include "opl_timer.h" static unsigned int opl_port_base; static int OPL_OpenBSD_Init(unsigned int port_base) { // Try to get permissions: if (set_iopl(3) < 0) { fprintf(stderr, "Failed to get raise I/O privilege level: " "check that you are running as root.\n"); return 0; } opl_port_base = port_base; // Start callback thread if (!OPL_Timer_StartThread()) { set_iopl(0); return 0; } return 1; } static void OPL_OpenBSD_Shutdown(void) { // Stop callback thread OPL_Timer_StopThread(); // Release I/O port permissions: set_iopl(0); } static unsigned int OPL_OpenBSD_PortRead(opl_port_t port) { return inb(opl_port_base + port); } static void OPL_OpenBSD_PortWrite(opl_port_t port, unsigned int value) { outb(opl_port_base + port, value); } opl_driver_t opl_openbsd_driver = { "OpenBSD", OPL_OpenBSD_Init, OPL_OpenBSD_Shutdown, OPL_OpenBSD_PortRead, OPL_OpenBSD_PortWrite, OPL_Timer_SetCallback, OPL_Timer_ClearCallbacks, OPL_Timer_Lock, OPL_Timer_Unlock, OPL_Timer_SetPaused, OPL_Timer_AdjustCallbacks, }; #endif /* #ifndef NO_OBSD_DRIVER */ crispy-doom-crispy-doom-5.6.4/opl/opl_queue.c000066400000000000000000000141231360717211000211710ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // Queue of waiting callbacks, stored in a binary min heap, so that we // can always get the first callback. // #include #include #include #include "opl_queue.h" #define MAX_OPL_QUEUE 64 typedef struct { opl_callback_t callback; void *data; uint64_t time; } opl_queue_entry_t; struct opl_callback_queue_s { opl_queue_entry_t entries[MAX_OPL_QUEUE]; unsigned int num_entries; }; opl_callback_queue_t *OPL_Queue_Create(void) { opl_callback_queue_t *queue; queue = 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, uint64_t time) { int entry_id; int parent_id; if (queue->num_entries >= MAX_OPL_QUEUE) { fprintf(stderr, "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; } uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue) { if (queue->num_entries > 0) { return queue->entries[0].time; } else { return 0; } } void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue, uint64_t time, float factor) { int64_t offset; int i; for (i = 0; i < queue->num_entries; ++i) { offset = queue->entries[i].time - time; queue->entries[i].time = time + (uint64_t) (offset / factor); } } #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 crispy-doom-crispy-doom-5.6.4/opl/opl_queue.h000066400000000000000000000025351360717211000212020ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // 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, uint64_t time); int OPL_Queue_Pop(opl_callback_queue_t *queue, opl_callback_t *callback, void **data); uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue); void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue, uint64_t time, float factor); #endif /* #ifndef OPL_QUEUE_H */ crispy-doom-crispy-doom-5.6.4/opl/opl_sdl.c000066400000000000000000000301541360717211000206310ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL SDL interface. // #include "config.h" #include #include #include #include #include "SDL.h" #include "SDL_mixer.h" #include "opl3.h" #include "opl.h" #include "opl_internal.h" #include "opl_queue.h" #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. uint64_t expire_time; // Calculated time that timer will expire. } opl_timer_t; // When the callback mutex is locked using OPL_Lock, callback functions // are not invoked. static SDL_mutex *callback_mutex = NULL; // Queue of callbacks waiting to be invoked. static opl_callback_queue_t *callback_queue; // Mutex used to control access to the callback queue. static SDL_mutex *callback_queue_mutex = NULL; // Current time, in us since startup: static uint64_t current_time; // If non-zero, playback is currently paused. static int opl_sdl_paused; // Time offset (in us) due to the fact that callbacks // were previously paused. static uint64_t pause_offset; // OPL software emulator structure. static opl3_chip opl_chip; static int opl_opl3mode; // Temporary mixing buffer used by the mixing callback. static uint8_t *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 }; // SDL parameters. static int sdl_was_initialized = 0; static int mixing_freq, mixing_channels; static Uint16 mixing_format; static int SDLIsInitialized(void) { int freq, channels; Uint16 format; return Mix_QuerySpec(&freq, &format, &channels); } // Advance time by the specified number of samples, invoking any // callback functions as appropriate. static void AdvanceTime(unsigned int nsamples) { opl_callback_t callback; void *callback_data; uint64_t us; SDL_LockMutex(callback_queue_mutex); // Advance time. us = ((uint64_t) nsamples * OPL_SECOND) / mixing_freq; current_time += us; if (opl_sdl_paused) { pause_offset += us; } // Are there callbacks to invoke now? Keep invoking them // until there are no 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; } // The mutex stuff here is a bit complicated. We must // hold callback_mutex when we invoke the callback (so that // the control thread can use OPL_Lock() to prevent callbacks // from being invoked), but we must not be holding // callback_queue_mutex, as the callback must be able to // call OPL_SetCallback to schedule new callbacks. SDL_UnlockMutex(callback_queue_mutex); SDL_LockMutex(callback_mutex); callback(callback_data); SDL_UnlockMutex(callback_mutex); SDL_LockMutex(callback_queue_mutex); } SDL_UnlockMutex(callback_queue_mutex); } // Call the OPL emulator code to fill the specified buffer. static void FillBuffer(uint8_t *buffer, unsigned int nsamples) { // This seems like a reasonable assumption. mix_buffer is // 1 second long, which should always be much longer than the // SDL mix buffer. assert(nsamples < mixing_freq); // OPL output is generated into temporary buffer and then mixed // (to avoid overflows etc.) OPL3_GenerateStream(&opl_chip, (Bit16s *) mix_buffer, nsamples); SDL_MixAudioFormat(buffer, mix_buffer, AUDIO_S16SYS, nsamples * 4, SDL_MIX_MAXVOLUME); } // Callback function to fill a new sound buffer: static void OPL_Mix_Callback(void *udata, Uint8 *buffer, int len) { unsigned int filled, buffer_samples; // Repeatedly call the OPL emulator update function until the buffer is // full. filled = 0; buffer_samples = len / 4; while (filled < buffer_samples) { uint64_t next_callback_time; uint64_t nsamples; SDL_LockMutex(callback_queue_mutex); // 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_sdl_paused || OPL_Queue_IsEmpty(callback_queue)) { nsamples = buffer_samples - filled; } else { next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset; nsamples = (next_callback_time - current_time) * mixing_freq; nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND; if (nsamples > buffer_samples - filled) { nsamples = buffer_samples - filled; } } SDL_UnlockMutex(callback_queue_mutex); // Add emulator output to buffer. FillBuffer(buffer + filled * 4, nsamples); filled += nsamples; // Invoke callbacks for this point in time. AdvanceTime(nsamples); } } static void OPL_SDL_Shutdown(void) { Mix_HookMusic(NULL, NULL); if (sdl_was_initialized) { Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); OPL_Queue_Destroy(callback_queue); free(mix_buffer); sdl_was_initialized = 0; } /* if (opl_chip != NULL) { OPLDestroy(opl_chip); opl_chip = NULL; } */ if (callback_mutex != NULL) { SDL_DestroyMutex(callback_mutex); callback_mutex = NULL; } if (callback_queue_mutex != NULL) { SDL_DestroyMutex(callback_queue_mutex); callback_queue_mutex = NULL; } } static unsigned int GetSliceSize(void) { int limit; int n; limit = (opl_sample_rate * MAX_SOUND_SLICE_TIME) / 1000; // 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 int OPL_SDL_Init(unsigned int port_base) { // Check if SDL_mixer has been opened already // If not, we must initialize it now if (!SDLIsInitialized()) { if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); return 0; } if (Mix_OpenAudio(opl_sample_rate, AUDIO_S16SYS, 2, GetSliceSize()) < 0) { fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); return 0; } SDL_PauseAudio(0); // When this module shuts down, it has the responsibility to // shut down SDL. sdl_was_initialized = 1; } else { sdl_was_initialized = 0; } opl_sdl_paused = 0; pause_offset = 0; // Queue structure of callbacks to invoke. callback_queue = OPL_Queue_Create(); current_time = 0; // Get the mixer frequency, format and number of channels. Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels); // Only supports AUDIO_S16SYS if (mixing_format != AUDIO_S16SYS || mixing_channels != 2) { fprintf(stderr, "OPL_SDL only supports native signed 16-bit LSB, " "stereo format!\n"); OPL_SDL_Shutdown(); return 0; } // Mix buffer: four bytes per sample (16 bits * 2 channels): mix_buffer = malloc(mixing_freq * 4); // Create the emulator structure: OPL3_Reset(&opl_chip, mixing_freq); opl_opl3mode = 0; callback_mutex = SDL_CreateMutex(); callback_queue_mutex = SDL_CreateMutex(); // Set postmix that adds the OPL music. This is deliberately done // as a postmix and not using Mix_HookMusic() as the latter disables // normal SDL_mixer music mixing. Mix_SetPostMix(OPL_Mix_Callback, NULL); return 1; } static unsigned int OPL_SDL_PortRead(opl_port_t port) { unsigned int result = 0; if (port == OPL_REGISTER_PORT_OPL3) { return 0xff; } 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; } 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 + ((uint64_t) tics * OPL_SECOND) / 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; case OPL_REG_NEW: opl_opl3mode = value & 0x01; default: OPL3_WriteRegBuffered(&opl_chip, reg_num, value); break; } } static void OPL_SDL_PortWrite(opl_port_t port, unsigned int value) { if (port == OPL_REGISTER_PORT) { register_num = value; } else if (port == OPL_REGISTER_PORT_OPL3) { register_num = value | 0x100; } else if (port == OPL_DATA_PORT) { WriteRegister(register_num, value); } } static void OPL_SDL_SetCallback(uint64_t us, opl_callback_t callback, void *data) { SDL_LockMutex(callback_queue_mutex); OPL_Queue_Push(callback_queue, callback, data, current_time - pause_offset + us); SDL_UnlockMutex(callback_queue_mutex); } static void OPL_SDL_ClearCallbacks(void) { SDL_LockMutex(callback_queue_mutex); OPL_Queue_Clear(callback_queue); SDL_UnlockMutex(callback_queue_mutex); } static void OPL_SDL_Lock(void) { SDL_LockMutex(callback_mutex); } static void OPL_SDL_Unlock(void) { SDL_UnlockMutex(callback_mutex); } static void OPL_SDL_SetPaused(int paused) { opl_sdl_paused = paused; } static void OPL_SDL_AdjustCallbacks(float factor) { SDL_LockMutex(callback_queue_mutex); OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor); SDL_UnlockMutex(callback_queue_mutex); } opl_driver_t opl_sdl_driver = { "SDL", OPL_SDL_Init, OPL_SDL_Shutdown, OPL_SDL_PortRead, OPL_SDL_PortWrite, OPL_SDL_SetCallback, OPL_SDL_ClearCallbacks, OPL_SDL_Lock, OPL_SDL_Unlock, OPL_SDL_SetPaused, OPL_SDL_AdjustCallbacks, }; crispy-doom-crispy-doom-5.6.4/opl/opl_timer.c000066400000000000000000000141361360717211000211710ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL timer thread. // Once started using OPL_Timer_StartThread, the thread sleeps, // waking up to invoke callbacks set using OPL_Timer_SetCallback. // #include "SDL.h" #include "opl_timer.h" #include "opl_queue.h" typedef enum { THREAD_STATE_STOPPED, THREAD_STATE_RUNNING, THREAD_STATE_STOPPING, } thread_state_t; static SDL_Thread *timer_thread = NULL; static thread_state_t timer_thread_state; static uint64_t current_time; // If non-zero, callbacks are currently paused. static int opl_timer_paused; // Offset in microseconds to adjust time due to the fact that playback // was paused. static uint64_t pause_offset = 0; // Queue of callbacks waiting to be invoked. // The callback queue mutex is held while the callback queue structure // or current_time is being accessed. static opl_callback_queue_t *callback_queue; static SDL_mutex *callback_queue_mutex; // The timer mutex is held while timer callback functions are being // invoked, so that the calling code can prevent clashes. static SDL_mutex *timer_mutex; // Returns true if there is a callback at the head of the queue ready // to be invoked. Otherwise, next_time is set to the time when the // timer thread must wake up again to check. static int CallbackWaiting(uint64_t *next_time) { // If paused, just wait in 50ms increments until unpaused. // Update pause_offset so after we unpause, the callback // times will be right. if (opl_timer_paused) { *next_time = current_time + 50 * OPL_MS; pause_offset += 50 * OPL_MS; return 0; } // If there are no queued callbacks, sleep for 50ms at a time // until a callback is added. if (OPL_Queue_IsEmpty(callback_queue)) { *next_time = current_time + 50 * OPL_MS; return 0; } // Read the time of the first callback in the queue. // If the time for the callback has not yet arrived, // we must sleep until the callback time. *next_time = OPL_Queue_Peek(callback_queue) + pause_offset; return *next_time <= current_time; } static uint64_t GetNextTime(void) { opl_callback_t callback; void *callback_data; uint64_t next_time; int have_callback; // Keep running through callbacks until there are none ready to // run. When we run out of callbacks, next_time will be set. do { SDL_LockMutex(callback_queue_mutex); // Check if the callback at the head of the list is ready to // be invoked. If so, pop from the head of the queue. have_callback = CallbackWaiting(&next_time); if (have_callback) { OPL_Queue_Pop(callback_queue, &callback, &callback_data); } SDL_UnlockMutex(callback_queue_mutex); // Now invoke the callback, if we have one. // The timer mutex is held while the callback is invoked. if (have_callback) { SDL_LockMutex(timer_mutex); callback(callback_data); SDL_UnlockMutex(timer_mutex); } } while (have_callback); return next_time; } static int ThreadFunction(void *unused) { uint64_t next_time; uint64_t now; // Keep running until OPL_Timer_StopThread is called. while (timer_thread_state == THREAD_STATE_RUNNING) { // Get the next time that we must sleep until, and // wait until that time. next_time = GetNextTime(); now = SDL_GetTicks() * OPL_MS; if (next_time > now) { SDL_Delay((next_time - now) / OPL_MS); } // Update the current time. SDL_LockMutex(callback_queue_mutex); current_time = next_time; SDL_UnlockMutex(callback_queue_mutex); } timer_thread_state = THREAD_STATE_STOPPED; return 0; } static void InitResources(void) { callback_queue = OPL_Queue_Create(); timer_mutex = SDL_CreateMutex(); callback_queue_mutex = SDL_CreateMutex(); } static void FreeResources(void) { OPL_Queue_Destroy(callback_queue); SDL_DestroyMutex(callback_queue_mutex); SDL_DestroyMutex(timer_mutex); } int OPL_Timer_StartThread(void) { InitResources(); timer_thread_state = THREAD_STATE_RUNNING; current_time = SDL_GetTicks(); opl_timer_paused = 0; pause_offset = 0; timer_thread = SDL_CreateThread(ThreadFunction, "OPL timer thread", NULL); if (timer_thread == NULL) { timer_thread_state = THREAD_STATE_STOPPED; FreeResources(); return 0; } return 1; } void OPL_Timer_StopThread(void) { timer_thread_state = THREAD_STATE_STOPPING; while (timer_thread_state != THREAD_STATE_STOPPED) { SDL_Delay(1); } FreeResources(); } void OPL_Timer_SetCallback(uint64_t us, opl_callback_t callback, void *data) { SDL_LockMutex(callback_queue_mutex); OPL_Queue_Push(callback_queue, callback, data, current_time + us - pause_offset); SDL_UnlockMutex(callback_queue_mutex); } void OPL_Timer_ClearCallbacks(void) { SDL_LockMutex(callback_queue_mutex); OPL_Queue_Clear(callback_queue); SDL_UnlockMutex(callback_queue_mutex); } void OPL_Timer_AdjustCallbacks(float factor) { SDL_LockMutex(callback_queue_mutex); OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor); SDL_UnlockMutex(callback_queue_mutex); } void OPL_Timer_Lock(void) { SDL_LockMutex(timer_mutex); } void OPL_Timer_Unlock(void) { SDL_UnlockMutex(timer_mutex); } void OPL_Timer_SetPaused(int paused) { SDL_LockMutex(callback_queue_mutex); opl_timer_paused = paused; SDL_UnlockMutex(callback_queue_mutex); } crispy-doom-crispy-doom-5.6.4/opl/opl_timer.h000066400000000000000000000017611360717211000211760ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL timer thread. // #ifndef OPL_TIMER_H #define OPL_TIMER_H #include "opl.h" int OPL_Timer_StartThread(void); void OPL_Timer_StopThread(void); void OPL_Timer_SetCallback(uint64_t us, opl_callback_t callback, void *data); void OPL_Timer_ClearCallbacks(void); void OPL_Timer_Lock(void); void OPL_Timer_Unlock(void); void OPL_Timer_SetPaused(int paused); void OPL_Timer_AdjustCallbacks(float factor); #endif /* #ifndef OPL_TIMER_H */ crispy-doom-crispy-doom-5.6.4/opl/opl_win32.c000066400000000000000000000072521360717211000210140ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // OPL Win32 native interface. // #include "config.h" #ifdef _WIN32 #include #define WIN32_LEAN_AND_MEAN #include #include "opl.h" #include "opl_internal.h" #include "opl_timer.h" #include "ioperm_sys.h" static unsigned int opl_port_base; // MingW? #if defined(__GNUC__) && defined(__i386__) static unsigned int OPL_Win32_PortRead(opl_port_t port) { unsigned char result; __asm__ volatile ( "movl %1, %%edx\n" "inb %%dx, %%al\n" "movb %%al, %0" : "=m" (result) : "r" (opl_port_base + port) : "edx", "al", "memory" ); return result; } static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value) { __asm__ volatile ( "movl %0, %%edx\n" "movb %1, %%al\n" "outb %%al, %%dx" : : "r" (opl_port_base + port), "r" ((unsigned char) value) : "edx", "al" ); } // haleyjd 20110417: MSVC version #elif defined(_MSC_VER) && defined(_M_IX86) static unsigned int OPL_Win32_PortRead(opl_port_t port) { unsigned char result; opl_port_t dst_port = opl_port_base + port; __asm { mov edx, dword ptr [dst_port] in al, dx mov byte ptr [result], al } return result; } static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value) { opl_port_t dst_port = opl_port_base + port; __asm { mov edx, dword ptr [dst_port] mov al, byte ptr [value] out dx, al } } #else // Not x86, or don't know how to do port R/W on this compiler. #define NO_PORT_RW static unsigned int OPL_Win32_PortRead(opl_port_t port) { return 0; } static void OPL_Win32_PortWrite(opl_port_t port, unsigned int value) { } #endif static int OPL_Win32_Init(unsigned int port_base) { #ifndef NO_PORT_RW OSVERSIONINFO version_info; opl_port_base = port_base; // Check the OS version. memset(&version_info, 0, sizeof(version_info)); version_info.dwOSVersionInfoSize = sizeof(version_info); GetVersionEx(&version_info); // On NT-based systems, we must acquire I/O port permissions // using the ioperm.sys driver. if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) { // Install driver. if (!IOperm_InstallDriver()) { return 0; } // Open port range. if (!IOperm_EnablePortRange(opl_port_base, 2, 1)) { IOperm_UninstallDriver(); return 0; } } // Start callback thread if (!OPL_Timer_StartThread()) { IOperm_UninstallDriver(); return 0; } return 1; #endif return 0; } static void OPL_Win32_Shutdown(void) { // Stop callback thread OPL_Timer_StopThread(); // Unload IOperm library. IOperm_UninstallDriver(); } opl_driver_t opl_win32_driver = { "Win32", OPL_Win32_Init, OPL_Win32_Shutdown, OPL_Win32_PortRead, OPL_Win32_PortWrite, OPL_Timer_SetCallback, OPL_Timer_ClearCallbacks, OPL_Timer_Lock, OPL_Timer_Unlock, OPL_Timer_SetPaused, OPL_Timer_AdjustCallbacks, }; #endif /* #ifdef _WIN32 */ crispy-doom-crispy-doom-5.6.4/pcsound/000077500000000000000000000000001360717211000177075ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/pcsound/.gitignore000066400000000000000000000000701360717211000216740ustar00rootroot00000000000000Makefile.in Makefile .deps libpcsound.a *.rc tags TAGS crispy-doom-crispy-doom-5.6.4/pcsound/CMakeLists.txt000066400000000000000000000006321360717211000224500ustar00rootroot00000000000000add_library(pcsound STATIC pcsound.c pcsound.h pcsound_bsd.c pcsound_sdl.c pcsound_linux.c pcsound_win32.c pcsound_internal.h) target_include_directories(pcsound INTERFACE "." PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../") target_link_libraries(pcsound SDL2::mixer) crispy-doom-crispy-doom-5.6.4/pcsound/Makefile.am000066400000000000000000000007001360717211000217400ustar00rootroot00000000000000 AM_CFLAGS=@SDLMIXER_CFLAGS@ EXTRA_DIST=CMakeLists.txt noinst_LIBRARIES=libpcsound.a libpcsound_a_SOURCES = \ pcsound.c pcsound.h \ pcsound_bsd.c \ pcsound_sdl.c \ pcsound_linux.c \ pcsound_win32.c \ pcsound_internal.h crispy-doom-crispy-doom-5.6.4/pcsound/pcsound.c000066400000000000000000000056221360717211000215330ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // PC speaker interface. // #include #include #include #include "config.h" #include "pcsound.h" #include "pcsound_internal.h" #ifdef HAVE_DEV_ISA_SPKRIO_H #define HAVE_BSD_SPEAKER #endif #ifdef HAVE_DEV_SPEAKER_SPEAKER_H #define HAVE_BSD_SPEAKER #endif #ifdef _WIN32 extern pcsound_driver_t pcsound_win32_driver; #endif #ifdef HAVE_BSD_SPEAKER extern pcsound_driver_t pcsound_bsd_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 HAVE_BSD_SPEAKER &pcsound_bsd_driver, #endif #ifdef _WIN32 &pcsound_win32_driver, #endif &pcsound_sdl_driver, NULL, }; static pcsound_driver_t *pcsound_driver = NULL; int pcsound_sample_rate; void PCSound_SetSampleRate(int rate) { pcsound_sample_rate = rate; } 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 (!strcmp(drivers[i]->name, driver_name)) { // Found the driver! if (drivers[i]->init_func(callback_func)) { pcsound_driver = drivers[i]; } else { printf("Failed to initialize 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) { printf("Using PC sound driver: %s\n", pcsound_driver->name); return 1; } else { printf("Failed to find a working PC sound driver.\n"); return 0; } } void PCSound_Shutdown(void) { pcsound_driver->shutdown_func(); pcsound_driver = NULL; } crispy-doom-crispy-doom-5.6.4/pcsound/pcsound.h000066400000000000000000000021641360717211000215360ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // PC speaker interface. // #ifndef PCSOUND_H #define PCSOUND_H typedef void (*pcsound_callback_func)(int *duration, int *frequency); // Initialise the PC speaker subsystem. The given function is called // periodically to request more sound data to play. int PCSound_Init(pcsound_callback_func callback_func); // Shut down the PC speaker subsystem. void PCSound_Shutdown(void); // Set the preferred output sample rate when emulating a PC speaker. // This must be called before PCSound_Init. void PCSound_SetSampleRate(int rate); #endif /* #ifndef PCSOUND_H */ crispy-doom-crispy-doom-5.6.4/pcsound/pcsound_bsd.c000066400000000000000000000160731360717211000223650ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // PC speaker driver for [Open]BSD // (Should be NetBSD as well, but untested). // #include "config.h" // OpenBSD/NetBSD: #ifdef HAVE_DEV_ISA_SPKRIO_H #define HAVE_BSD_SPEAKER #include #endif // FreeBSD #ifdef HAVE_DEV_SPEAKER_SPEAKER_H #define HAVE_BSD_SPEAKER #include #endif #ifdef HAVE_BSD_SPEAKER #include #include #include #include #include #include #include #include #include #include #include #include #include "SDL.h" #include "SDL_thread.h" #include "pcsound.h" #include "pcsound_internal.h" #define SPEAKER_DEVICE "/dev/speaker" // // This driver is far more complicated than it should be, because // OpenBSD has sucky support for threads. Because multithreading // is done in userspace, invoking the ioctl to make the speaker // beep will lock all threads until the beep has completed. // // Thus, to get the beeping to occur in real-time, we must invoke // the ioctl in a separate process. To do this, a separate // sound server is forked that listens on a socket for tones to // play. When a tone is received, a reply is sent back to the // main process and the tone played. // // Meanwhile, back in the main process, there is a sound thread // that runs, invoking the pcsound callback function to get // more tones. This blocks on the sound server socket, waiting // for replies. In this way, when the sound server finishes // playing a tone, the next one is sent. // // This driver is a bit less accurate than the others, because // we can only specify sound durations in 1/100ths of a second, // as opposed to the normal millisecond durations. static pcsound_callback_func callback; static int sound_server_pid; static int sleep_adjust = 0; static int sound_thread_running; static SDL_Thread *sound_thread_handle; static int sound_server_pipe[2]; // Play a sound, checking how long the system call takes to complete // and autoadjusting for drift. static void AdjustedBeep(int speaker_handle, int ms, int freq) { unsigned int start_time; unsigned int end_time; unsigned int actual_time; tone_t tone; // Adjust based on previous error to keep the tempo right if (sleep_adjust > ms) { sleep_adjust -= ms; return; } else { ms -= sleep_adjust; } // Invoke the system call and time how long it takes start_time = SDL_GetTicks(); tone.duration = ms / 10; // in 100ths of a second tone.frequency = freq; // Always a positive duration if (tone.duration < 1) { tone.duration = 1; } if (ioctl(speaker_handle, SPKRTONE, &tone) != 0) { perror("ioctl"); return; } end_time = SDL_GetTicks(); if (end_time > start_time) { actual_time = end_time - start_time; } else { actual_time = ms; } if (actual_time < ms) { actual_time = ms; } // Save sleep_adjust for next time sleep_adjust = actual_time - ms; } static void SoundServer(int speaker_handle) { tone_t tone; int result; // Run in a loop, invoking the callback for (;;) { result = read(sound_server_pipe[1], &tone, sizeof(tone_t)); if (result < 0) { perror("read"); return; } // Send back a response, so the main process knows to send another write(sound_server_pipe[1], &tone, sizeof(tone_t)); // Beep! (blocks until complete) AdjustedBeep(speaker_handle, tone.duration, tone.frequency); } } // Start up the sound server. Returns non-zero if successful. static int StartSoundServer(void) { int result; int speaker_handle; // Try to open the speaker device speaker_handle = open(SPEAKER_DEVICE, O_WRONLY); if (speaker_handle == -1) { // Don't have permissions for the console device? fprintf(stderr, "StartSoundServer: Failed to open '%s': %s\n", SPEAKER_DEVICE, strerror(errno)); return 0; } // Create a pipe for communications if (socketpair(AF_UNIX, SOCK_STREAM, 0, sound_server_pipe) < 0) { perror("socketpair"); close(speaker_handle); return 0; } // Start a separate process to generate PC speaker output // We can't use the SDL threading functions because OpenBSD's // threading sucks :-( result = fork(); if (result < 0) { fprintf(stderr, "Failed to fork sound server!\n"); close(speaker_handle); return 0; } else if (result == 0) { // This is the child (sound server) SoundServer(speaker_handle); close(speaker_handle); exit(0); } else { // This is the parent sound_server_pid = result; close(speaker_handle); } return 1; } static void StopSoundServer(void) { int status; kill(sound_server_pid, SIGINT); waitpid(sound_server_pid, &status, 0); } static int SoundThread(void *unused) { tone_t tone; int duration; int frequency; while (sound_thread_running) { // Get the next frequency to play callback(&duration, &frequency); //printf("dur: %i, freq: %i\n", duration, frequency); // Build up a tone structure and send to the sound server tone.frequency = frequency; tone.duration = duration; if (write(sound_server_pipe[0], &tone, sizeof(tone_t)) < 0) { perror("write"); break; } // Wait until the sound server responds before sending another if (read(sound_server_pipe[0], &tone, sizeof(tone_t)) < 0) { perror("read"); break; } } return 0; } static int PCSound_BSD_Init(pcsound_callback_func callback_func) { callback = callback_func; if (!StartSoundServer()) { fprintf(stderr, "PCSound_BSD_Init: Failed to start sound server.\n"); return 0; } sound_thread_running = 1; sound_thread_handle = SDL_CreateThread(SoundThread, "PC speaker thread", NULL); return 1; } static void PCSound_BSD_Shutdown(void) { // Stop the sound thread sound_thread_running = 0; SDL_WaitThread(sound_thread_handle, NULL); // Stop the sound server StopSoundServer(); } pcsound_driver_t pcsound_bsd_driver = { "BSD", PCSound_BSD_Init, PCSound_BSD_Shutdown, }; #endif /* #ifdef HAVE_BSD_SPEAKER */ crispy-doom-crispy-doom-5.6.4/pcsound/pcsound_internal.h000066400000000000000000000020641360717211000234310ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // PC speaker interface. // #ifndef PCSOUND_INTERNAL_H #define PCSOUND_INTERNAL_H #include "pcsound.h" #define PCSOUND_8253_FREQUENCY 1193280 typedef struct pcsound_driver_s pcsound_driver_t; typedef int (*pcsound_init_func)(pcsound_callback_func callback); typedef void (*pcsound_shutdown_func)(void); struct pcsound_driver_s { const char *name; pcsound_init_func init_func; pcsound_shutdown_func shutdown_func; }; extern int pcsound_sample_rate; #endif /* #ifndef PCSOUND_INTERNAL_H */ crispy-doom-crispy-doom-5.6.4/pcsound/pcsound_linux.c000066400000000000000000000064721360717211000227560ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // PC speaker 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" #include "pcsound_internal.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 sleep_adjust = 0; static void AdjustedSleep(unsigned int ms) { unsigned int start_time; unsigned int end_time; unsigned int actual_time; // Adjust based on previous error to keep the tempo right if (sleep_adjust > ms) { sleep_adjust -= ms; return; } else { ms -= sleep_adjust; } // Do the sleep and record how long it takes start_time = SDL_GetTicks(); SDL_Delay(ms); end_time = SDL_GetTicks(); if (end_time > start_time) { actual_time = end_time - start_time; } else { actual_time = ms; } if (actual_time < ms) { actual_time = ms; } // Save sleep_adjust for next time sleep_adjust = actual_time - ms; } 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); AdjustedSleep(duration); } 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? fprintf(stderr, "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, "PC speaker thread", 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 */ crispy-doom-crispy-doom-5.6.4/pcsound/pcsound_sdl.c000066400000000000000000000132301360717211000223670ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // PC speaker interface. // #include #include #include "SDL.h" #include "SDL_mixer.h" #include "pcsound.h" #include "pcsound_internal.h" #define MAX_SOUND_SLICE_TIME 70 /* ms */ #define SQUARE_WAVE_AMP 0x2000 // If true, we initialized SDL and have the responsibility to shut it // down static int sdl_was_initialized = 0; // Callback function to invoke when we want new sound data 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; // Mixer function that does the PC speaker emulation static void PCSound_Mix_Callback(void *udata, Uint8 *stream, int len) { Sint16 *leftptr; Sint16 *rightptr; Sint16 this_value; int oldfreq; int i; int nsamples; // 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 limit) { return (1 << n); } } // Should never happen? return 1024; } static int PCSound_SDL_Init(pcsound_callback_func callback_func) { int slicesize; // Check if SDL_mixer has been opened already // If not, we must initialize it now if (!SDLIsInitialized()) { if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); return 0; } slicesize = GetSliceSize(); if (Mix_OpenAudio(pcsound_sample_rate, AUDIO_S16SYS, 2, slicesize) < 0) { fprintf(stderr, "Error initializing SDL_mixer: %s\n", Mix_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); return 0; } SDL_PauseAudio(0); // When this module shuts down, it has the responsibility to // shut down SDL. sdl_was_initialized = 1; } // Get the mixer frequency, format and number of channels. Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels); // Only supports AUDIO_S16SYS if (mixing_format != AUDIO_S16SYS || mixing_channels != 2) { fprintf(stderr, "PCSound_SDL only supports native signed 16-bit LSB, " "stereo format!\n"); PCSound_SDL_Shutdown(); return 0; } callback = callback_func; current_freq = 0; current_remaining = 0; Mix_SetPostMix(PCSound_Mix_Callback, NULL); return 1; } pcsound_driver_t pcsound_sdl_driver = { "SDL", PCSound_SDL_Init, PCSound_SDL_Shutdown, }; crispy-doom-crispy-doom-5.6.4/pcsound/pcsound_win32.c000066400000000000000000000042301360717211000225470ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // PC speaker interface. // #ifdef _WIN32 #include "SDL.h" #include "SDL_thread.h" #include #include "pcsound.h" #include "pcsound_internal.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) { return 0; } // Start a thread to play sound. callback = callback_func; sound_thread_running = 1; sound_thread_handle = SDL_CreateThread(SoundThread, "PC speaker thread", 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 */ crispy-doom-crispy-doom-5.6.4/pkg/000077500000000000000000000000001360717211000170155ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/pkg/.gitignore000066400000000000000000000000411360717211000210000ustar00rootroot00000000000000Makefile Makefile.in config.make crispy-doom-crispy-doom-5.6.4/pkg/Makefile.am000066400000000000000000000033021360717211000210470ustar00rootroot00000000000000 OSX_FILES= \ osx/Resources/128x128.png \ osx/Resources/app.icns \ osx/Resources/app.png \ osx/Resources/wadfile.icns \ osx/Resources/wadfile.png \ osx/Resources/launcher.nib/designable.nib \ osx/Resources/launcher.nib/keyedobjects.nib \ osx/disk/dir.DS_Store \ osx/disk/background.png \ osx/GNUmakefile \ osx/Info.plist.in \ osx/PkgInfo \ osx/cp-with-libs \ osx/dmgfix \ osx/main.m \ osx/AppController.m osx/AppController.h \ osx/Execute.m osx/Execute.h \ osx/IWADController.m osx/IWADController.h \ osx/LauncherManager.m osx/LauncherManager.h WIN32_FILES= \ win32/GNUmakefile \ win32/cp-with-libs \ win32/README EXTRA_DIST=$(OSX_FILES) $(WIN32_FILES) crispy-doom-crispy-doom-5.6.4/pkg/config.make.in000066400000000000000000000012471360717211000215320ustar00rootroot00000000000000# Shared file included by the makefiles used to build packages. # This contains various information needed by the makefiles, # and is autogenerated by configure to include various # necessary details. # Tools needed: CC = @CC@ OBJDUMP = @OBJDUMP@ STRIP = @STRIP@ LDFLAGS = @LDFLAGS@ # Package name and version number: PROGRAM_PREFIX = @PROGRAM_PREFIX@ PACKAGE = @PACKAGE@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ # Documentation files to distribute with packages. DOC_FILES = README.md \ README.Music.md \ COPYING.md \ NEWS.md crispy-doom-crispy-doom-5.6.4/pkg/osx/000077500000000000000000000000001360717211000176265ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/pkg/osx/.gitignore000066400000000000000000000000521360717211000216130ustar00rootroot00000000000000Info.plist launcher *.o *.d *.dmg staging crispy-doom-crispy-doom-5.6.4/pkg/osx/AppController.h000066400000000000000000000021751360717211000225700ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 LAUNCHER_APPCONTROLLER_H #define LAUNCHER_APPCONTROLLER_H #include #include "LauncherManager.h" @interface AppController : NSObject { LauncherManager *launcherManager; BOOL filesAdded; } + (void)initialize; - (id)init; - (void)dealloc; - (void)awakeFromNib; - (void)applicationDidFinishLaunching:(NSNotification *)aNotif; - (BOOL)applicationShouldTerminate:(id)sender; - (void)applicationWillTerminate:(NSNotification *)aNotif; - (BOOL)application:(NSApplication *)application openFile:(NSString *)fileName; - (void)showPrefPanel:(id)sender; @end #endif crispy-doom-crispy-doom-5.6.4/pkg/osx/AppController.m000066400000000000000000000070261360717211000225750ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "AppController.h" #include "config.h" @implementation AppController + (void)initialize { NSMutableDictionary *defaults = [NSMutableDictionary dictionary]; /* * Register your app's defaults here by adding objects to the * dictionary, eg * * [defaults setObject:anObject forKey:keyForThatObject]; * */ [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (id)init { if ((self = [super init])) { } self->filesAdded = NO; return self; } - (void)dealloc { [super dealloc]; } - (void)awakeFromNib { [[NSApp mainMenu] setTitle:@PACKAGE_NAME]; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotif { // Uncomment if your application is Renaissance-based // [NSBundle loadGSMarkupNamed:@"Main" owner:self]; } - (BOOL)applicationShouldTerminate:(id)sender { return YES; } - (void)applicationWillTerminate:(NSNotification *)aNotif { } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)_ { return YES; } - (BOOL) application:(NSApplication *) application openFile:(NSString *) fileName { NSString *extension; // This may be an IWAD. If so, add it to the IWAD configuration; // don't add it like a PWAD. if ([self->launcherManager addIWADPath: fileName]) { return YES; } // If this is the first file added, clear out the existing // command line. This allows us to select multiple files // in the finder and open them all together (for TCs, etc). if (!self->filesAdded) { [self->launcherManager clearCommandLine]; } // Add file with appropriate command line option based on extension: extension = [fileName pathExtension]; if (![extension caseInsensitiveCompare: @"wad"]) { [self->launcherManager addFileToCommandLine: fileName forArgument: @"-merge"]; } else if (![extension caseInsensitiveCompare: @"lmp"]) { [self->launcherManager addFileToCommandLine: fileName forArgument: @"-playdemo"]; } else if (![extension caseInsensitiveCompare: @"deh"]) { [self->launcherManager addFileToCommandLine: fileName forArgument: @"-deh"]; [self->launcherManager selectGameByName: "doom"]; } else if (![extension caseInsensitiveCompare: @"hhe"]) { [self->launcherManager addFileToCommandLine: fileName forArgument: @"-deh"]; [self->launcherManager selectGameByName: "heretic"]; } else if (![extension caseInsensitiveCompare: @"seh"]) { [self->launcherManager addFileToCommandLine: fileName forArgument: @"-deh"]; [self->launcherManager selectGameByName: "strife"]; } else { return NO; } self->filesAdded = YES; return YES; } - (void)showPrefPanel:(id)sender { } @end crispy-doom-crispy-doom-5.6.4/pkg/osx/Execute.h000066400000000000000000000015431360717211000214040ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 LAUNCHER_EXECUTE_H #define LAUNCHER_EXECUTE_H void SetProgramLocation(const char *path); void ExecuteProgram(const char *executable, const char *iwad, const char *args); void OpenTerminalWindow(const char *doomwadpath); void OpenDocumentation(const char *filename); #endif /* #ifndef LAUNCHER_EXECUTE_H */ crispy-doom-crispy-doom-5.6.4/pkg/osx/Execute.m000066400000000000000000000131231360717211000214060ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include #include #include #include #include #include "config.h" #define RESPONSE_FILE "/tmp/launcher.rsp" #define TEMP_SCRIPT "/tmp/tempscript.sh" #define WINDOW_TITLE PACKAGE_STRING " command prompt" static char *executable_path; // Called on startup to save the location of the launcher program // (within a package, other executables should be in the same directory) void SetProgramLocation(const char *path) { char *p; executable_path = strdup(path); p = strrchr(executable_path, '/'); *p = '\0'; } // Write out the response file containing command line arguments. static void WriteResponseFile(const char *iwad, const char *args) { FILE *fstream; fstream = fopen(RESPONSE_FILE, "w"); if (iwad != NULL) { fprintf(fstream, "-iwad \"%s\"", iwad); } if (args != NULL) { fprintf(fstream, "%s", args); } fclose(fstream); } static void DoExec(const char *executable, const char *iwad, const char *args) { char *argv[3]; asprintf(&argv[0], "%s/%s", executable_path, executable); if (iwad != NULL || args != NULL) { WriteResponseFile(iwad, args); argv[1] = "@" RESPONSE_FILE; argv[2] = NULL; } else { argv[1] = NULL; } execv(argv[0], argv); exit(-1); } // Execute the specified executable contained in the same directory // as the launcher, with the specified arguments. void ExecuteProgram(const char *executable, const char *iwad, const char *args) { pid_t childpid; char *homedir; childpid = fork(); if (childpid == 0) { signal(SIGCHLD, SIG_DFL); // Change directory to home dir before launch, so that any demos // are saved somewhere sensible. homedir = getenv("HOME"); if (homedir != NULL) { chdir(homedir); } DoExec(executable, iwad, args); } else { signal(SIGCHLD, SIG_IGN); } } // Write a sequence of commands that will display the specified message // via shell commands. static void WriteMessage(FILE *script, char *msg) { char *p; fprintf(script, "echo \""); for (p=msg; *p != '\0'; ++p) { // Start new line? if (*p == '\n') { fprintf(script, "\"\necho \""); continue; } // Escaped character? if (*p == '\\' || *p == '\"') { fprintf(script, "\\"); } fprintf(script, "%c", *p); } fprintf(script, "\"\n"); } // Open a terminal window with the PATH set appropriately, and DOOMWADPATH // set to the specified value. void OpenTerminalWindow(const char *doomwadpath) { FILE *stream; // Generate a shell script that sets the PATH to include the location // where the Doom binaries are, and DOOMWADPATH to include the // IWAD files that have been configured in the launcher interface. // The script then deletes itself and starts a shell. stream = fopen(TEMP_SCRIPT, "w"); fprintf(stream, "#!/bin/sh\n"); //fprintf(stream, "set -x\n"); fprintf(stream, "PATH=\"%s:$PATH\"\n", executable_path); // MANPATH is set to point to the directory within the bundle that // contains the Unix manpages. However, the bundle name or path to // it can contain a space, and OS X doesn't like this! As a // workaround, create a symlink in /tmp to point to the real directory, // and put *this* in MANPATH. fprintf(stream, "rm -f \"/tmp/%s.man\"\n", PACKAGE_TARNAME); fprintf(stream, "ln -s \"%s/man\" \"/tmp/%s.man\"\n", executable_path, PACKAGE_TARNAME); fprintf(stream, "MANPATH=\"/tmp/%s.man:$(manpath)\"\n", PACKAGE_TARNAME); fprintf(stream, "export MANPATH\n"); fprintf(stream, "DOOMWADPATH=\"%s\"\n", doomwadpath); fprintf(stream, "export DOOMWADPATH\n"); fprintf(stream, "rm -f \"%s\"\n", TEMP_SCRIPT); // Window title to something more interesting than "tempscript": fprintf(stream, "echo -en \"\\033]0;%s\\a\"\n", WINDOW_TITLE); // Display a useful message: fprintf(stream, "clear\n"); WriteMessage(stream, "\n" "This command line has the PATH variable configured so that you may\n" "launch the game with whatever parameters you desire.\n" "\n" "For example:\n" "\n" " " PACKAGE_TARNAME " -iwad doom2.wad -file sid.wad -warp 1\n" "\n" "Type 'exit' to exit.\n"); fprintf(stream, "exec $SHELL\n"); fprintf(stream, "\n"); fclose(stream); chmod(TEMP_SCRIPT, 0755); // Tell the terminal to open a window to run the script. [[NSWorkspace sharedWorkspace] openFile: @TEMP_SCRIPT withApplication: @"Terminal"]; } void OpenDocumentation(const char *filename) { NSString *path; path = [NSString stringWithFormat: @"%s/Documentation/%s", executable_path, filename]; [[NSWorkspace sharedWorkspace] openFile: path]; } crispy-doom-crispy-doom-5.6.4/pkg/osx/GNUmakefile000066400000000000000000000063321360717211000217040ustar00rootroot00000000000000# Makefile for building the OS X launcher program and DMG package. include ../config.make DOC_FILES += README.Strife.md NOT-BUGS.md export MACOSX_DEPLOYMENT_TARGET=10.7 STAGING_DIR=staging DMG=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).dmg TOPLEVEL=../.. TOPLEVEL_DOCS=$(patsubst %,../../%,$(DOC_FILES)) # DMG file containing package: $(DMG) : tmp.dmg rm -f $@ ./dmgfix "$(realpath tmp.dmg)" "$(PACKAGE_STRING)" "$(PACKAGE_NAME).app" hdiutil convert -format UDBZ -o $@ tmp.dmg rm -f tmp.dmg tmp.dmg : $(STAGING_DIR) rm -f $@ hdiutil makehybrid -hfs -hfs-volume-name "$(PACKAGE_STRING)" \ -hfs-openfolder $(STAGING_DIR) $(STAGING_DIR) \ -o tmp.dmg # Staging dir build for package: APP_DIR=$(STAGING_DIR)/$(PACKAGE_NAME).app APP_TOP_DIR=$(APP_DIR)/Contents APP_BIN_DIR=$(APP_DIR)/Contents/MacOS SRC_INFO_PLIST=Info.plist APP_DOC_DIR=$(APP_BIN_DIR)/Documentation APP_DOC_RELDIR=$(patsubst $(STAGING_DIR)/%,%,$(APP_DOC_DIR)) $(STAGING_DIR): launcher $(TOPLEVEL_DOCS) rm -rf $(STAGING_DIR) mkdir $(STAGING_DIR) mkdir -p "$(APP_TOP_DIR)" cp -R Resources "$(APP_TOP_DIR)" cp PkgInfo "$(APP_TOP_DIR)" cp $(SRC_INFO_PLIST) "$(APP_TOP_DIR)" mkdir -p "$(APP_BIN_DIR)" mkdir -p "$(APP_DOC_DIR)" cp $(TOPLEVEL_DOCS) "$(APP_DOC_DIR)" mv "$(APP_DOC_DIR)/README.md" "$(APP_DOC_DIR)/README" mv "$(APP_DOC_DIR)/COPYING.md" "$(APP_DOC_DIR)/COPYING" ln -s "$(APP_DOC_RELDIR)/COPYING" "$(STAGING_DIR)/Software License" ln -s "$(APP_DOC_RELDIR)/README" "$(STAGING_DIR)/README" ln -s /Applications "$(STAGING_DIR)" cp launcher "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/launcher" ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)doom "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)doom" ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)heretic "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)heretic" ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)hexen "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)hexen" ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)strife "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)strife" ./cp-with-libs $(TOPLEVEL)/src/$(PROGRAM_PREFIX)setup "$(APP_BIN_DIR)" $(STRIP) "$(APP_BIN_DIR)/$(PROGRAM_PREFIX)setup" $(TOPLEVEL)/man/simplecpp -DPRECOMPILED -D__MACOSX__ \ -DDOOM -DHERETIC -DHEXEN -DSTRIFE \ < $(TOPLEVEL)/man/INSTALL.template \ > "$(APP_DOC_DIR)/INSTALL" mkdir -p "$(APP_BIN_DIR)/man/man5" "$(APP_BIN_DIR)/man/man6" cp $(TOPLEVEL)/man/*.5 "$(APP_BIN_DIR)/man/man5" cp $(TOPLEVEL)/man/*.6 "$(APP_BIN_DIR)/man/man6" for game in doom heretic hexen strife; do \ cp $(TOPLEVEL)/man/CMDLINE.$$game \ "$(APP_DOC_DIR)/CMDLINE-$$game"; \ done cp disk/dir.DS_Store $(STAGING_DIR)/.DS_Store cp disk/background.png $(STAGING_DIR)/background.png clean : launcher_clean rm -f $(DMG) rm -rf $(STAGING_DIR) # Launcher build: CFLAGS = -Wall -I$(TOPLEVEL) LDFLAGS = -framework Cocoa LAUNCHER_OBJS= \ AppController.o \ Execute.o \ IWADController.o \ LauncherManager.o \ main.o launcher : $(LAUNCHER_OBJS) $(CC) $(LDFLAGS) $(LAUNCHER_OBJS) -o $@ %.o : %.m $(CC) -c $(CFLAGS) $^ -o $@ launcher_clean : rm -f $(LAUNCHER_OBJS) launcher crispy-doom-crispy-doom-5.6.4/pkg/osx/IWADController.h000066400000000000000000000025641360717211000225760ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 LAUNCHER_IWADCONTROLLER_H #define LAUNCHER_IWADCONTROLLER_H #include #include @interface IWADController : NSObject { id iwadSelector; id configWindow; id chex; id doom1; id doom2; id plutonia; id tnt; id freedoom1; id freedoom2; id freedm; id heretic; id hexen; id strife; } - (void) closeConfigWindow: (id)sender; - (void) openConfigWindow: (id)sender; - (NSString *) getIWADLocation; - (NSString *) autoloadPath; - (void) awakeFromNib; - (BOOL) setDropdownList; - (void) setDropdownSelection; - (void) saveConfig; - (char *) doomWadPath; - (void) setEnvironment; - (const char *) getGameName; - (BOOL) addIWADPath: (NSString *) path; - (BOOL) selectGameByName: (const char *) name; @end #endif /* #ifndef LAUNCHER_IWADCONTROLLER_H */ crispy-doom-crispy-doom-5.6.4/pkg/osx/IWADController.m000066400000000000000000000244071360717211000226030ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include "config.h" #include "IWADController.h" typedef enum { IWAD_DOOM1, IWAD_DOOM2, IWAD_TNT, IWAD_PLUTONIA, IWAD_CHEX, IWAD_HERETIC, IWAD_HEXEN, IWAD_STRIFE, IWAD_FREEDOOM1, IWAD_FREEDOOM2, IWAD_FREEDM, NUM_IWAD_TYPES } IWAD; static NSString *IWADLabels[NUM_IWAD_TYPES] = { @"Doom", @"Doom II: Hell on Earth", @"Final Doom: TNT: Evilution", @"Final Doom: Plutonia Experiment", @"Chex Quest", @"Heretic", @"Hexen", @"Strife", @"Freedoom: Phase 1", @"Freedoom: Phase 2", @"FreeDM", }; static NSString *IWADFilenames[NUM_IWAD_TYPES + 1] = { @"doom.wad", @"doom2.wad", @"tnt.wad", @"plutonia.wad", @"chex.wad", @"heretic.wad", @"hexen.wad", @"strife.wad", @"freedoom1.wad", @"freedoom2.wad", @"freedm.wad", @"undefined" }; @implementation IWADController - (void) getIWADList: (NSPathControl **) iwadList { iwadList[IWAD_DOOM1] = self->doom1; iwadList[IWAD_DOOM2] = self->doom2; iwadList[IWAD_TNT] = self->tnt; iwadList[IWAD_PLUTONIA] = self->plutonia; iwadList[IWAD_CHEX] = self->chex; iwadList[IWAD_HERETIC] = self->heretic; iwadList[IWAD_HEXEN] = self->hexen; iwadList[IWAD_STRIFE] = self->strife; iwadList[IWAD_FREEDOOM1] = self->freedoom1; iwadList[IWAD_FREEDOOM2] = self->freedoom2; iwadList[IWAD_FREEDM] = self->freedm; } - (IWAD) getSelectedIWAD { unsigned int i; for (i=0; iiwadSelector titleOfSelectedItem] == IWADLabels[i]) { return i; } } return NUM_IWAD_TYPES; } // Get the location of the selected IWAD. - (NSString *) getIWADLocation { IWAD selectedIWAD; NSPathControl *iwadList[NUM_IWAD_TYPES]; selectedIWAD = [self getSelectedIWAD]; if (selectedIWAD == NUM_IWAD_TYPES) { return nil; } else { [self getIWADList: iwadList]; return [[iwadList[selectedIWAD] URL] path]; } } static const char *NameForIWAD(IWAD iwad) { switch (iwad) { case IWAD_HERETIC: return "heretic"; case IWAD_HEXEN: return "hexen"; case IWAD_STRIFE: return "strife"; default: return "doom"; } } // Get the name used for the executable for the selected IWAD. - (const char *) getGameName { return NameForIWAD([self getSelectedIWAD]); } - (void) setIWADConfig { NSPathControl *iwadList[NUM_IWAD_TYPES]; NSUserDefaults *defaults; NSString *key; NSString *value; unsigned int i; [self getIWADList: iwadList]; // Load IWAD filename paths defaults = [NSUserDefaults standardUserDefaults]; for (i=0; iiwadSelector selectItemWithTitle:IWADLabels[i]]; break; } } } // Set the dropdown list to include an entry for each IWAD that has // been configured. Returns true if at least one IWAD is configured. - (BOOL) setDropdownList { NSPathControl *iwadList[NUM_IWAD_TYPES]; BOOL have_wads; id location; unsigned int i; unsigned int enabled_wads; // Build the new list. [self getIWADList: iwadList]; [self->iwadSelector removeAllItems]; enabled_wads = 0; for (i=0; i 0) { [self->iwadSelector addItemWithTitle: IWADLabels[i]]; ++enabled_wads; } } // Enable/disable the dropdown depending on whether there // were any configured IWADs. have_wads = enabled_wads > 0; [self->iwadSelector setEnabled: have_wads]; // Restore the old selection. [self setDropdownSelection]; return have_wads; } - (void) saveConfig { NSPathControl *iwadList[NUM_IWAD_TYPES]; IWAD selectedIWAD; NSUserDefaults *defaults; NSString *key; NSString *value; unsigned int i; [self getIWADList: iwadList]; // Store all IWAD locations to user defaults. defaults = [NSUserDefaults standardUserDefaults]; for (i=0; iconfigWindow isVisible]) { [self->configWindow makeKeyAndOrderFront: sender]; } } // Callback method invoked when the close button is clicked. - (void) closeConfigWindow: (id)sender { [self->configWindow orderOut: sender]; [self saveConfig]; [self setDropdownList]; } - (void) awakeFromNib { [self->configWindow center]; // Set configuration for all IWADs from configuration file. [self setIWADConfig]; // Populate the dropdown IWAD list. if ([self setDropdownList]) { [self setDropdownSelection]; } } // Generate a value to set for the DOOMWADPATH environment variable // that contains each of the configured IWAD files. - (char *) doomWadPath { NSPathControl *iwadList[NUM_IWAD_TYPES]; NSString *location; unsigned int i; BOOL first; char *env; size_t env_len; [self getIWADList: iwadList]; // Calculate length of environment string. env_len = 0; for (i=0; i 0) { env_len += [location length] + 1; } } // Build string. env = malloc(env_len); strlcpy(env, "", env_len); first = YES; for (i=0; i 0) { if (!first) { strlcat(env, ":", env_len); } strlcat(env, [location UTF8String], env_len); first = NO; } } return env; } - (NSString *) autoloadPath { NSArray *array = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES); if ([array count] == 0) { return nil; } IWAD selectedIWAD = [self getSelectedIWAD]; if (selectedIWAD == NUM_IWAD_TYPES) { return nil; } NSString *base = [array objectAtIndex:0]; return [NSString pathWithComponents: [NSArray arrayWithObjects: base, @PACKAGE_TARNAME, @"autoload", IWADFilenames[selectedIWAD], nil]]; } // Set the DOOMWADPATH environment variable to contain the path to each // of the configured IWAD files. - (void) setEnvironment { char *doomwadpath; char *env; // Get the value for the path. doomwadpath = [self doomWadPath]; asprintf(&env, "DOOMWADPATH=%s", doomwadpath); free(doomwadpath); // Load into environment: putenv(env); //free(env); } // Examine a path to a WAD and determine whether it is an IWAD file. // If so, it is added to the IWAD configuration, and true is returned. - (BOOL) addIWADPath: (NSString *) path { NSPathControl *iwadList[NUM_IWAD_TYPES]; NSArray *pathComponents; NSString *filename; unsigned int i; [self getIWADList: iwadList]; // Find an IWAD file that matches the filename in the path that we // have been given. pathComponents = [path pathComponents]; filename = [pathComponents objectAtIndex: [pathComponents count] - 1]; for (i = 0; i < NUM_IWAD_TYPES; ++i) { if ([filename caseInsensitiveCompare: IWADFilenames[i]] == 0) { // Configure this IWAD. [iwadList[i] setURL: [NSURL fileURLWithPath: path]]; // Rebuild dropdown list and select the new IWAD. [self setDropdownList]; [self->iwadSelector selectItemWithTitle:IWADLabels[i]]; return YES; } } // No IWAD found with this name. return NO; } - (BOOL) selectGameByName: (const char *) name { NSPathControl *iwadList[NUM_IWAD_TYPES]; NSString *location; const char *name2; int i; // Already selected an IWAD of the desired type? Just return // success. if (!strcmp(name, [self getGameName])) { return YES; } // Search through the configured IWADs and try to select the // desired game. [self getIWADList: iwadList]; for (i = 0; i < NUM_IWAD_TYPES; ++i) { location = [[iwadList[i] URL] path]; name2 = NameForIWAD(i); if (!strcmp(name, name2) && location != nil && [location length] > 0) { [self->iwadSelector selectItemWithTitle:IWADLabels[i]]; return YES; } } // User hasn't configured any WAD(s) for the desired game type. return NO; } @end crispy-doom-crispy-doom-5.6.4/pkg/osx/Info.plist.in000066400000000000000000000101061360717211000222010ustar00rootroot00000000000000 CFBundleIdentifier org.chocolate-doom.launcher CFBundleDevelopmentRegion English CFBundleDisplayName @PACKAGE_NAME@ CFBundleExecutable launcher CFBundleGetInfoString @PACKAGE_STRING@ CFBundleIconFile app.icns CFBundleInfoDictionaryVersion 6.0 CFBundleName @PACKAGE_NAME@ CFBundlePackageType APPL CFBundleShortVersionString @PACKAGE_VERSION@ CFBundleVersion @PACKAGE_VERSION@ NSHighResolutionCapable true NSPrincipalClass NSApplication NSMainNibFile launcher NSHumanReadableCopyright Copyright (C) 1993-2015, id Software and Raven Software, Simon Howard, James Haley, Samuel Villarreal and other contributors. Licensed under the GNU GPL v2. CFBundleDocumentTypes CFBundleTypeName Doom WAD file CFBundleTypeIconFile wadfile.icns CFBundleTypeRole Viewer CFBundleTypeExtensions wad CFBundleTypeName Doom demo recording CFBundleTypeIconFile wadfile.icns CFBundleTypeRole Viewer CFBundleTypeExtensions lmp CFBundleTypeName Doom Dehacked patch CFBundleTypeIconFile wadfile.icns CFBundleTypeRole Viewer CFBundleTypeExtensions deh CFBundleTypeName Heretic HHE patch CFBundleTypeIconFile wadfile.icns CFBundleTypeRole Viewer CFBundleTypeExtensions hhe CFBundleTypeName Strife Sehacked patch CFBundleTypeIconFile wadfile.icns CFBundleTypeRole Viewer CFBundleTypeExtensions seh crispy-doom-crispy-doom-5.6.4/pkg/osx/LauncherManager.h000066400000000000000000000026261360717211000230410ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 LAUNCHER_LAUNCHERMANAGER_H #define LAUNCHER_LAUNCHERMANAGER_H #include #include #include "IWADController.h" @interface LauncherManager : NSObject { IWADController *iwadController; id launcherWindow; id launchButton; id commandLineArguments; } - (void) launch: (id)sender; - (void) runSetup: (id)sender; - (void) awakeFromNib; - (void) clearCommandLine; - (BOOL) addIWADPath: (NSString *) path; - (void) addFileToCommandLine: (NSString *) fileName forArgument: (NSString *) args; - (BOOL) selectGameByName: (const char *) name; - (void) openTerminal: (id) sender; - (void) openREADME: (id) sender; - (void) openINSTALL: (id) sender; - (void) openCMDLINE: (id) sender; - (void) openCOPYING: (id) sender; - (void) openDocumentation: (id) sender; @end #endif /* #ifndef LAUNCHER_LAUNCHERMANAGER_H */ crispy-doom-crispy-doom-5.6.4/pkg/osx/LauncherManager.m000066400000000000000000000232441360717211000230450ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "Execute.h" #include "LauncherManager.h" #include "config.h" @implementation LauncherManager // Save configuration. Invoked when we launch the game or quit. - (void) saveConfig { NSUserDefaults *defaults; // Save IWAD configuration and selected IWAD. [self->iwadController saveConfig]; // Save command line arguments. defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:[self->commandLineArguments stringValue] forKey:@"command_line_args"]; } // Load configuration, invoked on startup. - (void) setConfig { NSUserDefaults *defaults; NSString *args; defaults = [NSUserDefaults standardUserDefaults]; args = [defaults stringForKey:@"command_line_args"]; if (args != nil) { [self->commandLineArguments setStringValue:args]; } } // Get the next command line argument from the command line. // The position counter used to iterate over arguments is in 'pos'. // The index of the argument that was found is saved in arg_pos. static NSString *GetNextArgument(NSString *commandLine, int *pos, int *arg_pos) { NSRange arg_range; // Skip past any whitespace while (*pos < [commandLine length] && isspace([commandLine characterAtIndex: *pos])) { ++*pos; } if (*pos >= [commandLine length]) { *arg_pos = *pos; return nil; } // We are at the start of the argument. This may be a quoted // string argument, or a "normal" one. if ([commandLine characterAtIndex: *pos] == '\"') { // Quoted string, skip past first quote ++*pos; // Save start position: *arg_pos = *pos; while (*pos < [commandLine length] && [commandLine characterAtIndex: *pos] != '\"') { ++*pos; } // Unexpected end of string? if (*pos >= [commandLine length]) { return nil; } arg_range = NSMakeRange(*arg_pos, *pos - *arg_pos); // Skip past last quote ++*pos; } else { // Normal argument // Save position: *arg_pos = *pos; // Read until end: while (*pos < [commandLine length] && !isspace([commandLine characterAtIndex: *pos])) { ++*pos; } arg_range = NSMakeRange(*arg_pos, *pos - *arg_pos); } return [commandLine substringWithRange: arg_range]; } // Given the specified command line argument, find the index // to insert the new file within the command line. Returns -1 if the // argument is not already within the arguments string. static int GetFileInsertIndex(NSString *commandLine, NSString *needle) { NSString *arg; int arg_pos; int pos; pos = 0; // Find the command line parameter we are searching // for (-merge, -deh, etc) for (;;) { arg = GetNextArgument(commandLine, &pos, &arg_pos); // Searched to end of string and never found? if (arg == nil) { return -1; } if (![arg caseInsensitiveCompare: needle]) { break; } } // Now skip over existing files. For example, if we // have -file foo.wad bar.wad, the new file should be appended // to the end of the list. for (;;) { arg = GetNextArgument(commandLine, &pos, &arg_pos); // If we search to the end of the string now, it is fine; // the new string should be added to the end of the command // line. Otherwise, if we find an argument that begins // with '-', it is a new command line parameter and the end // of the list. if (arg == nil || [arg characterAtIndex: 0] == '-') { break; } } // arg_pos should now contain the offset to insert the new filename. return arg_pos; } // Given the specified string, append a filename, quoted if necessary. static NSString *AppendQuotedFilename(NSString *str, NSString *fileName) { int i; // Search the filename for spaces, and quote if necessary. for (i=0; i<[fileName length]; ++i) { if (isspace([fileName characterAtIndex: i])) { str = [str stringByAppendingString: @" \""]; str = [str stringByAppendingString: fileName]; str = [str stringByAppendingString: @"\" "]; return str; } } str = [str stringByAppendingString: @" "]; str = [str stringByAppendingString: fileName]; return str; } // Clear out the existing command line options. // Invoked before the first file is added. - (void) clearCommandLine { [self->commandLineArguments setStringValue: @""]; } // Add a file to the command line to load with the game. - (void) addFileToCommandLine: (NSString *) fileName forArgument: (NSString *) arg { NSString *commandLine; int insert_pos; // Get the current command line commandLine = [self->commandLineArguments stringValue]; // Find the location to insert the new filename: insert_pos = GetFileInsertIndex(commandLine, arg); // If position < 0, we should add the new argument and filename // to the end. Otherwise, append the new filename to the existing // list of files. if (insert_pos < 0) { commandLine = [commandLine stringByAppendingString: @" "]; commandLine = [commandLine stringByAppendingString: arg]; commandLine = AppendQuotedFilename(commandLine, fileName); } else { NSString *start; NSString *end; // Divide existing command line in half: start = [commandLine substringToIndex: insert_pos]; end = [commandLine substringFromIndex: insert_pos]; // Construct new command line: commandLine = AppendQuotedFilename(start, fileName); commandLine = [commandLine stringByAppendingString: @" "]; commandLine = [commandLine stringByAppendingString: end]; } [self->commandLineArguments setStringValue: commandLine]; } - (void) launch: (id)sender { NSString *iwad; NSString *args; char *executable_name; const char *game_name; [self saveConfig]; iwad = [self->iwadController getIWADLocation]; args = [self->commandLineArguments stringValue]; if (iwad == nil) { NSRunAlertPanel(@"No IWAD selected", @"You have not selected an IWAD (game) file.\n\n" "You must configure and select a valid IWAD file " "in order to launch the game.", @"OK", nil, nil); return; } game_name = [self->iwadController getGameName]; asprintf(&executable_name, "%s%s", PROGRAM_PREFIX, game_name); ExecuteProgram(executable_name, [iwad UTF8String], [args UTF8String]); [NSApp terminate:sender]; } // Invoked when the "Setup Tool" button is clicked, to run the setup tool: - (void) runSetup: (id)sender { const char *game_name; char *arg; [self saveConfig]; [self->iwadController setEnvironment]; // Provide the -game command line parameter to select the game // to configure, based on the game selected in the dropdown. game_name = [self->iwadController getGameName]; asprintf(&arg, "-game %s", game_name); ExecuteProgram(PROGRAM_PREFIX "setup", NULL, arg); free(arg); } // Invoked when the "Terminal" option is selected from the menu, to open // a terminal window. - (void) openTerminal: (id) sender { char *doomwadpath; [self saveConfig]; doomwadpath = [self->iwadController doomWadPath]; OpenTerminalWindow(doomwadpath); free(doomwadpath); } - (void) openREADME: (id) sender { OpenDocumentation("README"); } - (void) openINSTALL: (id) sender { OpenDocumentation("INSTALL"); } - (void) openCMDLINE: (id) sender { const char *game_name; char filename[32]; // We need to open the appropriate doc file for the currently // selected game. game_name = [self->iwadController getGameName]; snprintf(filename, sizeof(filename), "CMDLINE-%s", game_name); OpenDocumentation(filename); } - (void) openCOPYING: (id) sender { OpenDocumentation("COPYING"); } - (void) openDocumentation: (id) sender { OpenDocumentation(""); } - (void) openAutoload: (id) sender { NSFileManager *fm = [NSFileManager defaultManager]; NSString *path = [self->iwadController autoloadPath]; if (path == nil) { return; } if (![fm fileExistsAtPath:path]) { [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; } [[NSWorkspace sharedWorkspace] openFile:path withApplication:@"Finder"]; } - (void) awakeFromNib { [self->launcherWindow setTitle: @PACKAGE_NAME " Launcher"]; [self->launcherWindow center]; [self->launcherWindow setDefaultButtonCell: [self->launchButton cell]]; [self setConfig]; } - (BOOL) addIWADPath: (NSString *) path { return [self->iwadController addIWADPath: path]; } - (BOOL) selectGameByName: (const char *) name { return [self->iwadController selectGameByName: name]; } @end crispy-doom-crispy-doom-5.6.4/pkg/osx/PkgInfo000066400000000000000000000000111360717211000210760ustar00rootroot00000000000000APPL???? crispy-doom-crispy-doom-5.6.4/pkg/osx/Resources/000077500000000000000000000000001360717211000216005ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/pkg/osx/Resources/128x128.png000066400000000000000000000547651360717211000232640ustar00rootroot00000000000000‰PNG  IHDR€€Ã>aËsRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEØ" ÂãÊ] IDATxÚì½wÝ×uçù¹÷^ίshd @0gQ%Š(É”4–lË–-Û3¶Êå-—ÓŽ¦Æ;³³;a=ã²µckVekå¤lI&•H‰$ bÝ@çøò{¿÷Ëwÿx¯ÁV³4hŠÒ¬õªnuW£ÑÝïžïýžs¾çœûüh¾ÄUþ]ñã×ë²Ñ?Ì¿çÕ‚àÇฆ—þ#fx±f­‚Ze`µAƒÿ?â °ò7ÈîçrÕ—@x0\íã_?X{ê% u—¾ês¹ŽñW/u ëÇ`øq«¿rÚõî2»Ëè.­û} Ö¬µ@/³Ô:`ù±›ø!1ÀZãk«Œb@´»"]PÈUð¿»V@H)U†«Áᯘà2€øg ñCú}rñ#]ƒ'º+)¥L ôäs|×ÛöÆt‘ø‹¿ûêñÙ¥r³ oÖxåëÞ:Ë¿ ƒ\ŽþÿÒ~«(ßXeø4 @ñ—~æƒwýùŸ|ò7ßùÞGºa÷Ö;nÞ”¸yb¾\žš_–@H­,!DJJ™ì~=¾ŠE"k܉¾&ÀüqøC2¾Ö5ÌŠñS]ãçü¦þž¡¯}ú~g`ûÞXàÚTÆx.V«úžû6¦‹Håâ)FÇÆ©[K !ÛúsÜýŽAÏî›QAB µn+R3ºN๸Í*ÑL=§1w¿ÝD¡°›àÂìòÙ¥ryñ/ÿö‹ÿøÏ{ (wPï²Áj¬ Å:ïér†ÿŸ&³Ð_G ­øebÑHï;ïÜûÁâÀæHxŒy’¹J Ç °½€FÛÃñ.þùÿÍÐ@ñx©u¤ÝŒ14Fvì&ðæ'Æ¡K:ßÃÐþ»Qa@,ׇKËöi©âÀ^§QÞ›p+[¿ó©縞\¥1¬5xp0‹uÉË O?Ò@Ð_§Ó¿+ ²»†{DÜj¶4v§Ye±TcºbQn:dbnRn:øÍ\ÅB×$^Ri9xAˆ¡Ir‰§ •B—‚lÜ$3Øv~”l*Aqh„Þ=·à·[Øõ…í7pÛýoù‘ì¯|ôwÿó'-«­ÖI¹ ä*W¶V®VW¡~d ½ŽÔ¿:ðËtÓ½ÞŸäÍoyË;bijª,Nœcl®ÌK“ú‡yóÃïaïÞëIÅLÒqƒ„®È%#4l—FÛ£éøØ~ÀrÃahh˜GÞ÷~zz{I÷ ÒÒØzŠò “ÇžâÂéc8Ë“Lœ>¶ÊXÁ4¹†ÅÌUi¦¹JÉ\ ñ# ¾½® °žÒ§¯ùgoß;²ï÷Üpwuâ((lÞÅìóçØ¼uÿÍÍȎ݆(¡çЮWAJæ&ÎóÔ׿Àù©9.NϳsÛÿê×þz¶îÃi” !$¾Ýbqì%>ÿ™O±Tm‘Žä“¦žbhô%îØš{ÃÎáÞCç¦W Ck_+__Ä®6¾¾Šü+ˆOá¢kÐ@Æ—ëlX¬+Ô¤¬"÷¡ï~0èf<™*ðÜ?Ëû?ü³lß³3%°-ÏF)…fD0Ìša²óÆ»ÙsØÍš&‘šNà;¸Í ¾Ó¦4~‚ÓGž¡Q¯ñð{?Èw¾óœò,–㓉›˜lºîFýI|äÿËgÿÀjÛáe‚·• P¬r,*eâ¿íßúð¿>3ùì¢ã…«àt—Ýý¸:Å\«>Š6´×˜îWwŒU_|•ØSz¹ïÆ{~æÑwÝ[زG.œ=‚ãÜòÀû(öô‚D’YßE…¨ÊÅ“©¡TH»2©ë¨ÀG…!veÀm^³†O2¼{?™b~}‰»z?Q\ZÕšPôò·îchó–”U[/œœeýŠãj6»”ÂjB¤{×ð{t°ø®Óhª4[aä‘uTGÉúý ?t· ý€(ßXEù«_z€¾Ÿ}߃÷ß~çÝýíê"ý{ï ¸e*ðPa€KÍô GbøN›ÀnјcæèwRý# ^«N}öý´tCT>Í… "éaðÄS‡0í*îÒE¬Å Üf•]Û¶ì \»vdtrvßký™M½o›ñdú¿ÎÂK  !ß”37¿g ×¶x4v¤ÖªÚa¨­Á #¬ìÛzL þg`±FÞÕWz‰5§¾' ~ñ¿ÿ—õ÷ß~¿Ñœ/4—çÈoÞC4]ÀL¤‰æzi—ç™xîqT’,пïÅ!z·ícøæû¸é~” PaˆfF0â)t3NÜ öÙ2†&éÏDéÜD$•'›Œ¥ÊK fV¥qj•”2ìk™¯W_ÞœZWáù&\Ÿñ{’7<Ü—Û[÷ƒæhËvV¥ŽÚUtƒ×%>xµ¸œñWJº=@ïO¿û­÷}òÿüÄÇ2¹‚yòñ¿Fϳý†;°Ê Ì~žd¡ϪÑXœá‰o=Nsy†­»±êÜV'’¯Œ¿Ô)ö¸f<Ó(86R7@…Ý*á ™1j³cXÍß;3G [¶l!’Ì`—çqe4ÍÔ%‰ØvŽŸ›À šÈv…¸`-N3Ò›Þü½S3/5ÚN« ‚°ûž/²~r¸çžZ¨e¿Zyå&͹ð…2L»pgFϾ³/{û=ùÔðXË©,tÒÆ0Â<>Ðþ ~ÅøÑÕþ>™ˆüÖ¯üÜÛ>ñk{ôC|ô¡c‰C_ùKö¾ázú™xî1–ç&ÙtýmÔç&8òÄ×8ôÄãh^‹scxìë_å‹ÿðuúcª6תaWæéÙsz4Žg5ðÚ ôH͈`•fq›×&p,„Sgr¡ÂwOÏrè…c¼ôüSÄ5Ÿ¸!(¤AĺÉPO†/ç9²‰RÊZ»J¡gpÇõ‘-=©üן;}B)安â@æƒCÅ»ZJÏ~¥rù’àhþ¾¾B¼9g¼§?çu©xæHµU¶‚P^b®áuÀå¤Ýäji÷w~þ½¾ë–mèëë-¨0dôÉ/pý›ß‹¦i”&ϲT*3¸i+ÿøÅ¿ã“õ9Ûá®ýÛÉ'"¥8°s3ŨâñçOs~ô O>Æb¹ÎÝ¿-EÈŽè#5ÖâN½Lj`;±\/žU§1}ލ 9·P£í8^Àó§/"<‹Á\ #Gê&B¬ò_æ8™˜A.a PKa`›zûççÏL-ÎuÅV‚ê¹ÃFÏ}©|åónÂWÊ7„ö@>ºí}ƒ…Û ¦!W›µ@}_€¨qùÖø×Ü-h¯Âï_®¦ßû‘Ù°Údƒ×THÒ^EÐg¬©è倞7Ürýÿë¿þk™t*mÆSØÕE<«a\|îT«òÙ,…¸†¦ëò¶m@‹g»À…‹“¼pnŽF£ÉB­E*"‘¿üž7‘ßrÛwîA‹D±«K(F,I,ÛK$] zñçû4ó£G‘Ñ(©LÝoÓ“Špn¾N¥\¢Ö¨––ˆÇ㨠@HB>ËÛÞö/žçó?…Õj²½?‹$$pmzs©Ìd©99>Wžëƒ+q@ò'† ·†Â(|¾tm›¾äÁ—ËpÞ†[RZúí½é›ßTÌlµÝêTÛ øþFí2jâk"$i×h|}®ŸбhdàÓÿù¿UNF…*ðqeâ¹^^|êÛÔ›¹Tœ¾ÁM,-Ì1?9N$‘áâB•“GS©5h»妋aè,T-ðžwÜå·j"ð” |ÃO>F±˜HhaÆ3xn›ã'ÎPn9„O£íЗŠrÝpžƒEzSQz 9¶_3…‘½¤û·Ï`Æí Ó¼tî"çf–81¹Ì©é2š¤c…d„lÂd¶Òf©ach’tܤìDþ^Бð ©(Ç&+,7m"†Æ–bо\ ¥R7Ú¶›©ãOO$0b)tM²÷ž‡PJÑ\˜Ož[>Zª6ªÝL@¢ïÈ_ŸFþrñ£Ó‡ÀI«] ù¦Bdø=ù;¶'c‰«ÍJûe!I[Ç–šmЯ õ®œþÕé^QÑ÷Ñ÷¾õÞßøØO=Jà Ϫ¸kÓ®—Hgs$tÅø±gq”A¹ZaxhRi€b*ÊæÁ~™<™bÊm“ß~å9š‹“<óÕ¿!žLúGF'™­XÔÚ.…d”Í…5ËckO”\2ÆøBÛóÌÅIE ê6~’‰›4Ú¹„Éù…¶çÓ°}f+gg« å Q^œ£TZf`Ë-Žcµ8qâ1ö¼}ê,ŽÕblf¨.“ki7„ºþƒoê©ðû3ð÷ËŠËØƒù‡î.¦oÿ»™å¯~òüÜÓ¾R+£p«Q¸L뙺Ä:MÑUþ¿ú‘·?øÆ{*f“=2p…Û¬>*ô±ë%œVƒáþ^FOŸ`±Úâät™Lœ¸©cj’žþ>v¼ü–ëp›’ÛºA$] ±4McvŒ‹‡¾N>ÍèÔ<ççë4m¯Óñ«I2qƒí}ib±Ã}>yžñÅ&Crn®ÎŽþ4šÔÛ.‰ˆN¨¶¢kx`¹áP³<j6ŽßiøM¥3ŒÍ.áMNr÷þ%ó'Ÿ¥§Ñ"‘Lá¶j ]w3û—§9»5¾®]vŠ[¬ éþ#×CÒ˜Ÿà[÷)ÒQMÅTb•Bš/PRHÑÉç_ÏW àËKŠo”}~fH?;œ¸åOîÜsàÅrãK?÷ô™?Y§)5\¯ um ×@ò'|ã][úw;ŽâáÛ-|»…g5Hlã⩉&’ônÛG}vŒ¹ñ³8"•L_ûs•3å¶bh‚á|) ÜrØÕŸF×$m7 j¹L•Z”[.MÇ'Ñqý˜¡£ÒqvnÝÂÔb…ñÅ:c 0±ÜD—‚jÛ'¢kô¤£(3Wm“ŽxAH*j¢Pdã&7íÚL¢0Àüò)*M›¡bš\O?®&™;M¼8DÏžÛ8òåÿï´Y°û6oÝEcVÛvV$a”Ðä—ôƒþx2àó !ÿûuºyKOúÝÀgxyv%n ®®×á’¿ô·?ZÚªÙµešóqåN§0ÈK‡ŸæÄèE¼ïvæŽÓИœgb©FÌЉ™A¨˜)·Î'˜­ZSQ®ÌÐhûHÐêŠ8š˜ºd[oSרµ]°c47踓þ|бñq–6®â!Bª–‹ç‡´ŸÉR Ëñ©µ]¦Ë-6ìÈ0Wk3œO2¸s?vm™RÝ"Sìçà÷rèÙgi-MÑ›Ž¸6Ǿò)ÆÏž¤/—Âq]rq£°só`ï±³ãÍ•} P¡Ôº”—4âõëÂt$µŽàóƒË¢ ‡ëpk^ MˆD ÔÊÜ‚¶†á¯Èkã€È‡Þñ¦½…˜^ÇuÛG›œe0GJÁø|3s5¶ö$ÙÚ“ÂÔ%M»3‘ã!R ÊM‡ TÄMb*J"¢³Ü茄ÇL½#À8>-§…¼ ¤'—Æõ=Ò1ƒá|‚rË&1˜XnÒŸ‰Ñ´=”R˜º¤/Ãö É(ç§—Ø9a¾fsòì(ò"õ¦…¦ r‹SÌÎ-áNÍ’8?ÊP_«Y§˜ÏÓn5‰š&" ¨Ïžç©fž:>:»²O+.À0´5VHð•âÿ87Á»ú‹üþ¦$GÛÿ½dò¢­¯‚Œº"–®ôóÑ@—B_‡ú7$©5*’ ¥ÑÌmÚMväzÜF™H®ŸæüBO‘Œš¸~/Scg835O½í2±Üd©aãù!£ó521“Ÿ¸m„þlŒRÃÆÔ5âšN!é|´]M É ˆ›š”X]}?ŸŒQ,èïïãÓ_~‚RÓ¦Œ0”O‘Œè¼x±„”‚|2B.a²o(GˆbϦ>Tàòâ…%Êçš¶GËñ¨¶::A_&J£ÑÀræªmü¥&¾Óq![û2zˆ }‰s§ùâ3§û~piÆÏ‡Ph]—Èd‚Ýo:Qoñ½R›³)~jS?8ç9KçÏ«QN9ŸÖ{Å]wR!´MWް‹«`­ñÀ›Y,yAÖ¦NKøX‹“Ì—ë{ã¸AȳO?E!à  ˜Œ*…ç‡Ý€N£ÒrùÒ‘I~ãáý˜šÄñC”Û püŽŸo¹AwÌÛ$ ®ßÉ×MM’M˜¤t|ž|î‹õ6“¥ 5»c¤„Á@.Žåú dãÌ–-l?àÀæ<¹„åGʵ¨´\ ‰3‹ªåŽ4ÚAØÀÐ5–6 Sg©n#¥`¶Ü$¯ƒÄ²E’‰8å¦]Y•W«‚•@†âªGVɰs…@‡«`‚¦ IDAT ŽÔšÜ‘Kóáá>þxÐç)ËäSõç]ýU¸€!º”Úú®Êá*!Á}qtròôéS3›r±M2ô^›x:Gˆ Nà…ðìè,sU‹½CY4­Íû¡"T,ÍV-þúÙ |øžäSqæ+Mj–C"bа="º$jhT[m/Äö|‚@QLÇé¢EbüåwN0¹ÜDA†”›>õöÃù^ Ø;”A—‚;wöàaÒ|O?ñF­'…ML]R· \?À B? f¹T,—Ó3UBɨΞÁ …d¦È%ªÈé)œ¯Ü°+«–©Kt]C ÄÕÙZ @€.Ú*ñP­Îázƒ{ó~r¸?pùv;Ê¿/§/ÿóÖû)@SB—Bn´(×)„¼<æì(¥ÚóË•‰…™I*‹3H#B!“dtj‘ÑÙ2®ÛI¨BtN¼!P(‚@uŠçRòÜøß:9KOÿ{¶m"ŸŒÐrüK7Ð=¦&)¦xëÛ¹ï¦=”¬€?ûúŽN”°Ü!:ï'Õ B…íäâ&#En=°—žbÃ0È¥âTçp\D<ÆöKu›zÛÅrª-—sóu¾sfž¯›æÂr“F7Ð,7]¤S“š Ñ¶É$bÆúøߺJX ¥^f­³´+,4 !ÁкöýKHø^µÆÇOžãK‹KÜŸlÕµK °¯Xú+—Ò$BhR\éä‹0€ß}£6Ðúã¯<û•ûÁ»÷/.-f¬™*=…_~ö ñˆFiqž›vmæùÓdã&A¨:¨R5$¹D Ëõ‘RcÛ`4HQîØÕO¥Ùæä±#ô‹TÊ%²Q“ÚÌBjDâIâ‰$®Ýf~a‘¨¡ñ ÞL"j°\®1:[¢ÑvÑDG‡0uDDg¾ÖÉ6øAˆ©é,µüÅÏžÿÓñ™…±î¾@4_tƒ]©oʦ~…ïE'¢è\Ãè† šD™®¬ .ZžçÕ]¬~])õžrˇ¡âð…eòÉ(~Rµ\z² >´wÞLŒ¨i0THј§]™ï^7IïD‹&šS_F¥xhë2ïÒu²½ƒÄr}ø¶Esá"µ¥i¬ò<íFTˆçy4,§£*Åèl…±ÅI¢¦ÆÄr“¡\œlœûoØJgÄ Cg 'ÏÛÒ1bé<š¡o`ˆéòsÌ”[ä’Q 3Â;Šlàè¹i¾sô' 3 1Ck}é…‰ÿ§ÞlÍwµõ Ûë†à¡ C¢m$j×d‡tˆ+¸ +Ú†sA¥ikcq-Pë°€· ÖWŸ?wx¡:¨ß¶­ø ðÜäM»G´r¹ÌÄR›÷íäáÛv‰'Àirêì9ª‡q“Ԩ͜ë4tFD3=¤¶‘ØF¼8Hs~‚t2‰Õ¨Q›£>3†Ô$S/= JaÛ6®„!–Œš¤S ÆfKèšäÆ-úsq’±(/Œ-`FL¢™Bg,Üsl 3‘ÆŒ' =ø¸®ÃÁÍyö¤È¤’$ò½${7ãÙw&ÓTÚþâ¿ûì“Ï*¥f€9`¨vqec½å Mv\€®]]°YÍÚÕ ã´uΨºÜ/’¡ щ.ç`Ö‚ÀíÁYj¸KwÜç‰dÁ¾aÏŽÄüÌ$_ùÆ“üôÞCD*|׿ðÓ§ £YfççÑžþISà4kxžG*¢©ÑXœÅ¹iÆç–Yn8TZ.›òqvfB°T³ê- 5Ëri´]L]ÒßÛƒïÚDtÙ—š¥†M6ãí÷ÞŒO£Eãè‘¡çâj:n³Šßrm‹j©žT*…`Õ«˜é…JçŽð–·õhùÊ4ðš\ÀZ¬¾ß÷BÜ­=Éb Žú.{¯ÛÃ/ÿÂÏa&Ò©áÛ-n¾ý.¬ÊÿñÙã|ú»çØ¿)ǯ 3¨Z.õ ¤¢‹õ6MÛ'ÕÑ5Á|­M_&†©kèšäÄ…9òÉå–ƒç‡SQìV ߨe°'3hu™‘D ¡éh²{u¬(ß#CBßÇ·[nßóq<›–íbH‚Ó¶õ)¤&‰'R”ÑxœéÙLßáÞÝ}»¿úüèmÇóW1¡ßÝÈUW î °hWwÕš†’Ðü+1€@É.¨k 5„tíÍ1ëô—¯ Ñý—ÞÿÐA“@Ž3RLÒôË£G@…œ:q¢Ó{f”¶rj¦37ˆè<}n‘ƒ›óœ™«Ñ5’ÑÎÄŽ¡Ib†N*fàù{‡òìÜ2ÈäÌB^š,“OF(dxn§Øãx!¦mÑ\œB7#hf'•†IèûèÑ8Uê•h4›8^ˆßÍí•‚ÉR“-=ij-?Ë5š¶ÇìÂ2{÷‘ïÙ† _Ü<Ü[Hž›š¿ìu° R`èò .@­ˆ 0˜:hh«©îÏïVÿ©.`½;˜ÓŒÝwÛÁ½Ò ;†Š<ø!zrÔæ.ÜÊö½û)ÍNâø!ë$"QÃÅr}ª‹ngúÇÔq¼€0TDL\"ÂÍyl¯#Ë.Õ-šg/òä™y³qîÜÙK_6N¹Ù¦Ùö@Ði2u}4­ |Bß!ðUtÓ$ðl\«‰ë8Øn§æ .â‚¶ B¾szŽ[wô#U§Õ¬a{}ñ›z2H©‰À÷ë `¼¬)¥:A൸€®t%áèpm ÐÍÖ˜ÿrî@]©-|½[>#ï|ó;2š×ûì‰ã¼áÝB# Ù»¢2{ç¾D©ÖD!MÛÇr|ü@!ܾ½‡]ýâ¦NÅrQ¡b¡ÞÆ BþæÐ8 µŽÀvë¶"ùn¥pïP–˜©u/qýNÑh…à‚ $ Úv'8·ºãf¡RˆŽ:áºâ‡!A¨¯3Sn¡IÁ [òüÑ‹lïK1‰QLǰŸÙRƒ=#âØÿŽÿõ¯¾wÒóýu§pBPB[a€ Jê(ÉUÙÑL] •~m.@“ùÚ0À÷Ýù—L&¿õñ=ªÕgD~höò4~«Jà´Q¾ƒ.³KŽ^\¢?ë oÄ „ì„ËAØBÌÔèI§¹¸Ô¤fyf¥ÎÇÉÄMNNW: !¹š…*b¦†ã"ìTÒü Äõ]"z§áÄõC?¤ét2ˆÑ©Kø¡Âö|”‚ºí±Ü°IF ¾yrŽ-Å$½ÿzÆæ*DuèËÄ™)5éIE™š]×0M=æù¾±^wRJ­(Ú\€0ô+++»ÿj)Ð:BЫŠÖ€¿øó?wkBú…çž–îz8-|«ÆÜè1êÕ*;ÞÁþÍEL .,5¨Z.MÛG HEuú2q„€ˆÞ1æ–b‚m½IünÇ®&:')fè¤cCÃñ|*-Çï4„¡"ŸìÔ,'ÀCj¡‹`{aÇ:]€(×'Ð$^·*©(Qm¹l*$¸kg/sµ6ñˆÎHŽé…Ç'K4máB’t"B1›Œß²cpçÇǧ×cÙíÐ7 ØÈNxUB©04…¶j. ¢Km£Õ@ý éÂê&QÍiT¤ßªqÇ›¢:=Š@¸^(xê¥óÄ£&„~gêWïôòeâ&óU‹dÔà‘{®glbš KËB¬lBçT‡ŠNhÂèö†º¤éø—ØC×dg¢7ì”s6r¿ÛªÏï4™¦F J)¤\XnRnºdâ&‹u›7ìÛÌRcŠo»@ÌÐqƒÏï´—yA€ã ’±(:Î+/xa·+xÃ.@èÝ4P\9±\› @D M²Á!Qý*%ÃK’¢Š$M/ Ê“çÂd*MЪsn¾Îìc‡Q@&F¹étÚǃNcÈÛúÈçr\˜œAÝüI)BÅ%髨Ý‚KQŠÖMic¦Ž”‚如 Tš$nj—f ü $bhH® !`¦lqj¦Ê­ÛŠlíIñÌùEþë?e{_ Û 8¤¢ɨÑí+ôyÓ¾AÇá {÷}óøÄ7Ö¬PJÑu{‹¯V³BïWg_( ]m\Za)1^f€kn¹\wPhÍœµÜå®Õ ÙjшI,Ú™µþÂ2A’OF±ŸéJ‹½iö fyß[ïæøéÓL,7)¦"ÝÞ TЍ¡u•LêR^ØI²ð‚€ÕºF*LC# ;¥\·{âM] IÙa„.c$¢:Ë ›†íqýp–\Œ¨©qßž~Ò1ƒRÓù¾!)ß>5ÇõÃÙNO¢í‘4Ä)¥ß}(å÷ \*”ZqWÒÔªA,¥)´nÀÕÀêe`©gfâÕ땆ý¯<ñÜ©Ÿ{ð¶¶Ç„Ì”›L•ìݶ‰m}™û °u=#lë˰gdˆÐ³ùö±‹Œ-ÖéI1œ_…d·kˆ. tº‰V(¥áú¡¡@“’¨!/uû!šÄкѿ€lÂÄr|ž<³(ü°ãfš¶O¹é7uvd,¤Uç>ãËŒ-vÇÕ“‘Nu0‘à£SχaØîª€~wÏB@…¬0ÀÆ\€àe mR]½´Ž ÐuùšIÁ+Oât—ªÍªí¸ ½]‰ nÙÁRíë¼pæ"×ô²©¤'#bhÌ—ëìÚwL.‡ÛjðùÇž éÌT›,sn¾Ö™ÎÍÆ:­Ù¤ìTÁ óT°•LFu>Ó½R¶õƒHØéVt²‹•Ó?¾Øàød…ñ¥šìˆM™˜‰åd&1S§åø¼8¾ˆ†écôÐ8Ùû†sÌW-ŽM–Y²Å‘/=?þW|ÿ¥.åР ]ßÐv« _-C Ñĵºñ²¯–Ö«¶ÏMLŸ:°9×ÍöàˆÙLšZ½Õ²ØÜ›%“Ëc·„4Ñ„âÌ‹‡˜¯´˜i<ú–;x‹pèè)j&“¥çæëœÙ;ûÒä’º|Ãö1:c|—2C“˜ºì‚¨i`v5õP)ÚnÀ³ç—86Y¦íúdRQƒDD'fhFÒz›½CYâ¦ÎLÅb±Öf[_ŠvÛ&Køu_r¾äX^¨MŸ«?õüèô?:Ž»LçÑr6/_ñ2ÈN@·a+.€«3€¦ò‚@M_ÍâÕVYSZi iýÑçŸüÚï¾kÿmÙå…x>ÁLêœiwæíý `~¹JÄÔY¬µ±½„ï:¤‹ü›|¯6Ó¶¸ý¶Û8óÌã(_“ÔmŸCg¦93Wc¤˜dÏ`†TT' ;Ö—]DM ] "ÝÉ!/©µ=Æë¤c&MÛãñ³’ZN@Ói1œ‹sïî>„€›¶èIÇèëíåųÈÄØÞ›"7™kúíc“å/й¶e͹ž·Hç‘r:•³ºû°rs׊†®¢µ4K8WyÙ{ªõ#_º¨!.õ^‘hÖaé䚢ÿú¦œLˆ·Ô5Lš\i.à½'/ÎO~ú©ä_üܽ[qi¹fôeã”[gf«’,×'3i´..ÕyÏí;éß¶{i‚¥óGùö g([så&µ¶Ç¦|œB2ÊÍ[ øaÈtÅ¢éøÝËžuâ¦N:Þ· ÃNóéB­MÕêHÌuË¥íèÝÖ.ÇWX®„¼Ôªâ!}™Åd”™ŠÅŽî% Ûc cªQ§®‚j¹Z›^Uû/u_ï6¬Ü ¤¯n •RaÍfö™gi_¸ú5!ZTƒlê0ÀUv(,ž¥ýÔÓ6¦ÕŸExž»ñéÃ+ůè _áü‰ñ…êŸ=zëæF#~þÖýô¥£L—-vô¥püS—Œô$™+UY(?G6›F\öe[¨³½˜ Öö¨Z“¥&Ï/qq©Ù©ê &ŽßQ÷nÜR 3.]èPm¹L•[RQ21ú{äsI*õ¥éŒN"Ãñ£(Ï¡˜«Z8~@*ªžK`Ô”K.¥p}åõ®ÁKÀ2/?P²Õ}ïþ ½34ƒPB—¹1åU)u© ]•‚«j¯ø?J!ty¹ P\ X=²t}actzyʽió¶\¾íx$cnß‘d©ÞF—‚RÃ!3HF B¥X¨Í_š&Ë-jïMF zU‡OsqzÓqŽN,“‹›”ZùdÇ hÚ>sU /‰‰¨ = !ÇN”¸n[’|VGžœ‰¦EX.»èš •0°€x\c¨ÏÀÔóø"™Ðxü™úÊ£dW¯Æª€•'†¬~ú ”0$rƒUGã×4®î$è×8*…0$š._UKØzi`°Æ X+ È$ãa$cj±ŠaèÌVšD ˆ®wnÞ:ùd€rÓF)ðCEÕr)&#´ÜÎ<ÿP.Î ›sR16õæ9¸9ËD©‰í…˜ÝF‘“ÓZ®”JkÄò!í@%iZ>º&Ÿ¶’qÙ%”¢ÕÐ5A¹î±Xr¸~WŠå² R÷Ù4e¹â29çNñò<ýê‡J;«N¿¶V…ºFèÀHÛ€¬$èÆ«€þÚ¤ë±Àj´‚À·§J_¤7»T§·\Ÿýæ*-"ºÆÄrç´ÏV,†òqÚNÀB½Í];{Ñ5ÉT¹E_:J&nR·ž>=‰çwžXµ\võ§B°s8Í\³Ùõ<J!ùŒÑ ouªŽõ¦O#ßÑ 4 Sâù )ÕZ§f k’…’K½å3Ô¥aÌ.Ú3«:VnÕXÉûƒU2ð÷ d ® ¡R]¸º¬$¯ÂÐqÆk£\®AÔ¬—¦k£·öÝì!³‹jÛ£/›TK­6×¾Ú–£1×$589Sívºv§ˆ´NZ·Üt˜®XììOcv¿¾{  oŸaÇŽ(×È3=oSmzÐ ; ³4M°s$A2®a‚¨©1·d“KH m'$Ð ‚zË'—6Ð$Ä"šª5Y\cøµÏù“¬óDÑð]€¢ÓêÕqWa!þ .@»âHøµ2ÀzAû±c“‡½cû»³ñøÅåN÷äx}Ñ mýà.Yع%ÉÉ…É@.Ë‘ñ2#Å$ù„‰®I&þ¿îÎ4Ʋô¼ë¿÷ìçî{íU]Ý]½MÏj;ÎÄNˆÅXA> l|@ ¾`á8„‰J"/`ƒeóÇÄÇ“xlÏx›Ïô23]½/µÞ{ëîËÙ>œêîšš^ªzºgN먤ê[·êÜ÷yÿÏÿù?ËÛJzõŽNåØÉ$°KøË‹ ,Ëa¦’§˜V9½:ÂÈD\YÓz;˜aª¦“˨¬7,'¤VÒ±rQC‘Qcè^ G…ɹ¹ŒÂ`bÙ!³“½ª¥ÆÍ­vŸ·Ÿô¹û,áø.±ò¾àVx·%0–@~  ¡(Òž§ˆßÞ ìÁhÜ]ëÚ¦Šé§7{®í“S#]¤¤Ðõ"²i™SË6żÊäœÃ!aRo)lÚ‚¡í³Úè¢o·kÍWr¼´Ògq>Ä\z®Gœ3”Fʽ•²ìfÇÃÐ%.¯XÌNš„¦IºÄDY£\Ðè }Ö6¦k•‚†ª FVÂ2)™´)#I‚éZŠË«ž³cñÃ÷}-F£$ë÷¼HR´íîó3’@Q÷é¢Ädõáq€Ý!áÎf‘ÑÅõÎùŸ>R|zª¢9è fŠÎÉ£Nºš‡™ ƒ‡2œ½0$Ÿ±¨Vt2bÈ+oŽXߊ%¬ñÖV'«Ï;ÌN¤™®iYÛ#+$ £¤ 7ŒI™2ín2°»7L ?*y IJfªŠÄ`pmÕÆñBòY…‹×ÇØn’]|êxC—éô} ]fsËåÊŠµ²cç{Ùù;Ã:¡& ™{õ±`OQ@bì¹ DÚ£Ü4‚"£Ó«7Dã!A“6 %—ËGÕ’ÆÕ5‹‡³ÌNDQò—”ò*Ÿü±<&A—ÄXÔª2q4 ŸIêäšmÓ˜Ÿ2)æTž9‘g¢¬#I‚(‚ZY»•ê½±náŠ,1U5$Áp`è“BN%Œ’bM•p܈Wßè±²a3tû.+›n{ò|'CˆoJÁB‘¸9*î~· 1Òvp¯û¦ìå}oÞ1w$ïšÜ)°á©Këçº?q`3Ž™R$5l›?8ûµŸ/|dùꈔ¡ðôñ<™´Ì¹Ë#Ö›'eøÔÇ*|ëå61 R\ß°èô|FV@© Ñêzs›[.ÅœÂÒBš‰²Nwàã1 Ó)7¤ÓóÛ!“UƒË+cFV@.­¢È‚|%ùZ+iŒí]“жJ³“æ-7ÐìÍ;@t—Eßͺc¡HhªŒªÞ? È‹½éÈ"©ÆU÷ž ’e‘¸€äÍÅÃr»³ƒ· ÀóåkgëÿñÙÅü禽(õ½kƒ¯·×G®"—ž~ìpV¹ºjñÉg'X¾ÜçÍKJyþ( Uxö©"ížÇµ5‹µºƒ¦I\_·é|Š9•FÛKX~+Âócž<–e£éÒù¨Š`~*¡Ëx^DÇ” Yâ2)™Á( eÊØnH6½=_°ˆR…œJ6-ã1š*±Ù 6wÀ¸kñïÃâX¨ª.£iÒ @Ü‚äû„xBJò{yßÛ¹ ±›ˆ‡i;#[õqϽrᥗ–³ËªªTÚn¨þÙw:g~ùçªê |dYfi1ÃZÃfuÓÆ÷#£€™ ƒ8ŽÙhÊ(JÂÜeY0¶Cæ¦Lm—BFES–²¹å’6e®¬ZAD«ãáxå‚ʱŠݡÏܤI­¬33)áº!å¢IÁæ–e{ÌM¥Q‰8ŠÈf3 †cü 5Úåÿ#ö8iû&¨j‚.{Ù¡±à– ¸¯(ñžÞw§ õŽ$P¼[xS;ˆà­Ïa0†Ûß+òËgg>ñ‘Ì3'Åhì°ÕqùÐcŶû>}²€ãF䳟úx•ïþ¨ÍÕ5 C—øèEt-™¾¹ÕñH™2#+deÃfnÊ$—Q’J¡ "tbLCÆ b®®ZÜXOF¹ŒÂSÇ ¼~®ÏütY–˜›21t !)ÈRÈÐ é”ñúF½¿+¸×â¿íûaœ(ª&£íª%9‘‚%îï„$TÐ4yÏSÂdY€"¡¼½&pÏC¢ö x»Èá浕õÞ›µÁ¡y‘?³< Þrùø3e?š£ÛóI2§Ï p¼ˆ‡2ü̳UÔ%chg& 4U¢VÖéô<\?Ùíù¬ŠiÈŒÆA²“âdÐB>«2ùTŠIèIh'¥è7Ö‡¤S2¥ËW¶#xòX Wø¡ºuøöhñ-Ðöæ$IÜæ÷ “ú~0 ™pÉ=Y‘Šxd.@ìàï'Çí©©0 ‡a$5äç§LÚ=ŸK7F˜1ÀËgz\Z3]Õ¹±aSÈù|ô‰ËWG|ã»[;˜AS$t]âøÁ,ù'6·‚BVÅvBFvÀØ É¦dê­!AE°8›ÑÉúŸ´©Ü"œA6­3?eòúy§·+ yg¤¸GØ•pMNvêý B¶Û¾%!Pî3úEÚ'cè~°·Eâx×!÷ƒ¿›| Þai Élá10vý¸ã‡ ÏOlu<2i !|Ž,fè }ú£Iº÷'ô]IDAT”‹š*1U1Œ6·4U²CÎ^pâP–Có)£‰²N:­ «–àú¥‚†®J¸^ÄzÓ¡UqýI$1U5YƒQ@¹ a»ª*¸²2ÞÜcpwØF€½°ué¦H G÷q²)ÆÐå=' A, ä»—…¿+à.ìXìÊ8€ÓÒPµ,kõ:³&Š,ÑxœZîcêOÏñÖå®1;a°ÞpxáåýcU£dÞ¿Z•pÜb^E–o^0¶Ò¦‚®I,L›ÔJÕr–é‰]b£a#I‚Óç<ûT‘#‹Þ¸0@’² c²i™• ›8Nz’Òp Eœ¿6æÂµŠ,(ä4ŽÌpô@†z½C±T$ŠQõQ,0ÓAà³Ñ´Â×ÞܺvèÞu{Ñvcˆº7  EÈÒÝI "0Õ&žðÃÓaš¶wÅ1’,v#À]‰àƒÀnN°[*ö× ÔÞp8 ³iEî }J“™‰â…¬lZ<~´Àì¤Á7¸…e[ÌM«<ût‰™µÍ1a8&›VX¾2ÂrBT%ÙmżÊÂŒIo°´fl'Ç¥¼Êµõ16–³ŠiÈ|âÇ'9wq“Nßçø¡Ë—¤3™q«3îÝ%Ü£öžìMe[ Ú“¤§w§zª\G‘†¬mHœyCÁqÚ>äà›äW~Í¡ûu» À«7Z½vgl—sjF–%ÞºÔãÄ¡,ݾÏjÝ¡ZÔД˜·.yâHŽÙ ƒVÏgl’BÊ9¶4Á✅ïsBâ(~(#bök;À¼8ŽÝ•MûZ)«<^o¹\¸6BðØRÇ‹¨•tÞ¸8äÍKC>òx]W™ª)¬m޹p}€çE|ç•Éqh>G­’¥\Êr2J†tl6:€àäRÛ Ð5 â˜LZÅñBfjª"H§’0±Ùq98—ÅqBr…ó×F½wC!zË´½K¶‰‡|D¼`ÈÆ¦ÂùK¾/ö ùwŒ2n#@ü¾à¾y±ég>öÔãÍÎeòY…þ(`­nsl1ÃÙ N-÷“H–h´r™VÏO’?\?bªfrñú€óW‡²J-ÔRœX*"Kó³5dÅ@ÑÒ´škèš`jú B’™˜‰q­6A(1?¾; —ËbšêòÈ¿‹¼€º­î D2ÀA¦ QǹxɤÝI HU|!%ÉŸß ÍâGawrà¼òÆà‚›ñìdF4Û[?P"‰ÉÇvH»çqòH™BVbec„ëF 'ð^.¨¤L…v/}IJòúë ‹Ç– |ý;(rBÞ¤XHqhÎ$Š l{€¢j!#«:šž|¸ž d !Ǭ7ÃÖƒæn“Àh.à–å´ˆãˆFSãúŠA¼»…Hdæ=Õ5(aáÅ®dÑ­hà•Æú… —ºªä–Φ‘„À4e~pºK6­pòH–·. ùÑ›~î³ÌN˘+bYÃqÒk˜24FV’ÜɤÂÐg²¢3»„!ä³2ƒ¡Íõµ¥iÞ<ՠѲ1u™ÅÙÇX«h´\žy|‚WϬpä@šz;hóΣÕöäûo¾æA\€@à¸pýF–ÁPA’x`È¿ÂÄĨŠïUÞ~Ø.à•Cë wmqšR &«:¯œíÑéû Ç!iSfnÒ¤ÞrYÙèóÆ…>ó³%Žš`bRg8h0»<ûT™õ†ƒç‡¤L™©ªãÅó*•¢†, *Å€|FF×àèbÓH fd…hª`¢¢¡HIMBÆ\_·7x{ ؾàˆoºEÞ;´;&íŽNíŸäíˆAU¥˜Û½Œá½¸òÿ;p¾ºsî#Ï?1Ù˜ºJÚ”™¬$5|®q`&ÅÓ' ¬ÖmÚ]—««+|ëû+ÌO§xêxžRÁäèÒGG¨ªÁhØÁ÷múCŸƒó9†#×™ŸJ!„ÄLÍÀ4’BÍb>E«à¸ªšÄÈiSfh…Q«c·vTA+ŠÚóöý~*é ”òâK‚“O$•Ö!Æâv…sp/#x˜`7L ൭³ÿàþÝÉZAî÷{|üÃU®¯ éB>úÔ$ÃᘠS—Ð5‰‰ŠŽíD² †®ñüw7°5¦3äs)¦&2TKJ•,  ×Ðl6ˆ‘X\˜!Ñéö±ˆt:…¦ZH&«ÖšþplÙÖ="€ø.y€]]ÉËdyŸà!_FJçÇ>öµ©Rüú[õõsÏ}Û-mÞ½B܇åv× Ü"‚Ýþ¸seÕ½ž2ÌCËWÇùãÔ[­ŽƒiHÔÊ).]ï33a2d†ã€ZIB$e…þ0@Udƒ¥¼Âpä"K0»ÔÊͶs¯:À}p€˜ľj÷Þí¥ë*'ž>B©ZŒpjýÕ_ûÜs_n¶­ ’æÖIW³µƒÜõù”‡ü·ÝI²_9ÝXþþk¹îãG²Å¡å£© ©”IÚ´(tÖ6‡¸nÈF£Çp4äèbtZ£T®119Ë„¸NŸA¯…$*E Ë Ié2¦!3;i $ƒÉŠŽ, ²…ù™Žã È1#;dzB¦ZÒÚýxëÝ$n¾6Ž‰ÙŽ»ß+(”ò}b $ÙýÃ/¼ö•ù¾ûí(Šo.|Û-í÷…ÿ‡M¹ t-Û¾úæàô”ú‰3)6›6‡²ÊÅÄ.õ–K:•,P؆ Ó³xéÕÒ©‹”‹)f&³>P" }ž|lYKCcÛc»;ZÃvúƒª"(í®ƒm‡(²Lµb0;luƒú=D =§Â·-&–ñÈI ‚é…ifÎÒhY›Ÿùí¯þ/^¼vÛ³ úÛ~'ùÛíÞ)àdð¸pͺØþZÊ”¥—^ëÐîG\(“ÏH|øq‰±åSoÙØNH º=M² ïrvyL­¤ÐléŽdf'ó”ŠrÙ4q#0tƒŠª1 <»ÃDyÊDÀDuB©‚46·®ùõ~ë.À¾”À›> Ñ PT…G’+åùÁ©õWõןûr³mÕ·ç®·vÅÿáý’[ÂÜ1/pöüÖåÎ`¶þĉÚôÂÌß÷ج·ÐæŠD"Ãñs´û¬­­E>[](†´™äÚóYÃPi÷|N/7xíì:#+ääR–Ÿý©#¼rjlÆÀLäR!…¬Â¸±H¨jŠz£E·ÓçêšÕµl׺‡ïóaãG¦sYf—‰…äþáç_ûÊoýû—¾Ç´¶w}dŽÁMÂçÝö©|7Yx§*èž$IîôTÙPU>^ ÝóˆQ¸z£Ãõ5‹µGO1==Áx4àè¢@ˆÏh´m&Ë:šª"‰§Žå°œ¤ÂøÉã%#›K×;DQ’iû›?=KI¬¬5hw=ž:QåÂå6¥œÊf+hÆq|·FÐý=p2è2–eÁC”&&(OOÒl[õò¯þêóñâµó¼}|ÍpÇ®ß ùÑ^ÐìQ!@ÌÛOñ¢(r-;Æq\ÊdóXN—\.E£=Æ4dš­1K]ÌP*d8°x˜t&$Ëd”‹F½9ä/ý€…Ù¥b‘¹™<ª\ ›ÖøØO,â{QÐÙZÅ4uÔᘣ‡ ôú.qG®ö‘D„iÈLM¦qÝ€b^Ãñå6Vx—P€ÐR)JÓ³„Hîþ¯ÿé¿øƒ¿óÈ¿)õîòß+xǃ§¾ª½ü“*>9h¶FLÕ²üìOŸ`<êÓjÙhŽ)äM^?×e~Ê@“}N½qõ '•¸p)ÀÌô©eÊ啉«ÇÑãG’DˆhÕ¯ K>O>~E‘q¬©tš#ÇæiÖWX«[õ»hûŽz¶‡œ>œ0PRù©R•fÛªæw¾ùù¯ûêyÞ>®nÄíñ5ùïÜBÏ—úÅÚvÿ •ˆvwLoÕáð¡––*LTšôc»xoÒ|aä‘REm«þÙó¾ö•åG ùï¼Í¼u©ÓÜÚ8O®PæØá2ë›}FV°=ÁK°¶ÑáGg7Ñ5•ƒ&9zÀDSGB’%¢Ð¦ê¸H„„‘Ø%h÷< $ VÖûTŠ:Ù´† É(¦L…‘íÞ>Ð3ns€X‚xs‚2"]!VSñËg6^ÿû¿ñç_ÞhŒ6Þ È¯ ànÑ@øššN™:£AUËñÄÉ*ö¸Ï`d17qñÚ€>Q Ùv©×ëLäsø¡„¹LOd©Ôæ0Ò.Û' =ž~æÃ!1¶ÙjnbY>SS¨ ©«LÎÖpF lgйCø@2ð΄û壈t/Þû⩯þæï¿ø­0ŠwN'Pô,Áxƒn§I§3`zf–ª‘âòµu$žQ)j”òùœI£íñÍï^& c*¥ódL8v0‹$Kd26ùBYÄT+E`¤ „‹ë ‘ƒáp@¿;âGoõop÷Jàxß®.Ž·µØãeäf‘fÛn|ö_¿ð…?{áòò´üG ùï¥ àn zõìúµOòSÏ úmd)Àql^~õ-ž™4¤3y6Î]¥˜Sp½˜ñØáØbtJáÜå>ƒQ÷V­À§ršõÆCopñڈŹ ¹´ÌÕÕ¹ŒJoîÎÆïÒ$ÿÄÞ 5Å«g7_ÿ•_îK›Í[S˽ÿ~!À-W0:‘5\CÕ ú£þ-R×éŽù«ïÕyêäŽ,¢: ýÖEúƒ!¥¼Š©KŒíd`ôìd I’‰c(d2)™‰JŠl.Çt +‚Ë7`¢bÒêX(²`dãµ»·ZÁ¸ðm:Àm)à¾ïG’ÿ_¿xúOÿÙï}ç…8¾Åòßsȯ9»eá‡Ó3‘?D×àØ¡*Wn´è}ó Å|Ò„i*Õ²AwìŒîÓ ¸_bÅÜv@þÞ¨¿þKÿôϾTß ÿ½’‚ïšxñÕ­3¿ö‹ÞßòüXÝê4C™C %bÉF·\FVr¢øÂ\Ž^ß!#®\ߢÞè¢(‚ƒ³i2)/´)Wd\*å2žå Wk¨FkÐDˆš‘e®tVã:/Ÿn­òîzß)qÇ·Ž“'ä‡Âû“/ùêoü»oï†üáŽï=‡ü÷3 m³×úîË×Þzæ˜òÔp0UU D"ÇÒ‘ƒŒ[ =$IC`c 3áö°ÉZ%E»kqæ|!šÄ±`ªºÊÒ Î4)óH"¦œƒzc‹úÖ òY…z;ܸ‡ïs÷‡€ƒ#ñnÈouìÆçþí·ÿûWž¿¸¼ ÷È¿uðÖ6zOü ¶ã“I«¸nÈ÷^½ÊÃ!‹ ÓTª:žça ³SY¢Ðc£é27iâ¸1ŽSÊkr A³0“#C4Mpõúé”ÌLµJ»g“Ë(AL·o xwÍ wœ˜*BFd&@MÅg–›¯üòg¿öÅ•AsîoʹÈ¿8ÀÛ  ÙZ¾ï°07ÁúfrÖÎÌ„Áp8âÅœ#›V9v¸Fmb#UÀwG^gÔàÆzßOÛrB*lÆ`8´¨•4–²¬7,‚À'BĶ®Ðé‡÷ »Ÿg¹Yñ<B„hi‚0rþ÷ŸŸÿŸÿø·žÿfF£íE>j-ÿÿˆw€k9ÑX– úý.å‚I³ma2²ٔ´ÉÊZ›kë«_šb²V$“I¡j).!#°m›^€" †ã„;´:a£ªåbš Rì¶»Vç>.`/‹°³ÊÉU¢‘åÝøÝÿòÃßûO_xý ·g)߬Ñ{W;ÿ¿¸€œ»Z·WÇNL·ï’2¦¦¦˜“ac£®…èšÀõBd Ö7š\¹V' cž}¦F§ï233K:“"›Ÿ&Š.Ç1Ì>¶=f8“Jg033Øã6ßûË+áÈÞaöëv>‡ H›ÍÑÿùG¿õü_>½aíB‡·×é} ÿýPßqìÌÕÎÚÚ–¼VËefãÈáÍåN_`éèI\»ÃÊÊ:ƒq€, £M•8z0ƒ¦ –/÷øá©é´Žeš3(4ã>Õr„ b®¯vYÛ\cªªQo9­]¾w?‰ ;•¼{¶DÏüüŸünsó¬ÞÏéñ*åõ(ÛYvY"o› èaé¹T4ù3_\’¥˜NϱGlÖÛ rY“™©<ùŒD†D­l„!£qH>§"‘ ‰^œ1é |F–ÇÕ•>Ë—;œ8œ¥ÝsȤ’‰"g.Xoœ»Ü{e!»é÷*‹{¸¶›á;HÎÑe׿àm¾]^ñÎnln}º\Ф©ªŽ" šß³Õu™™;ÄÁ#KÌ- °FmÖ6š^2Aø Aþ{rЀ–¦g/7š#SK%Ï÷$YòD}ËM‡R^åÆzõzŸ©ªA!opuuLµZ!_œ$­ŒA4!M3Põ,q1ê×ã06ãB!™©l´²Z÷WëÞ™]п¯£av<‡ØCäßåëòú¿Áë 20£þIEND®B`‚crispy-doom-crispy-doom-5.6.4/pkg/osx/Resources/app.icns000066400000000000000000001446511360717211000232510ustar00rootroot00000000000000icnsÉ©ics#H ¸?üþþ¾?¾?¾>>>8  ¸?üþþ¾?¾?¾>>>8 is32z’ˆ±Õˆ‰ÆÖÒ¾Fÿƒ ¿Ê³Ü½«¯9$Hÿ€MÔ¿¶ŸŸ~u¾š0Qʱ‡‹{ey¢¤ 0™˜’˜ª¦qt-7>H­¡op’§‡|—AEO)W¿…whvch~™K^h9Y€ lmaUWufAfy5D •xt…¬ŸtGq‡8J ™–š£­u”k˜ed ¥¦—¤¨ª–ŸŸ€ ¯£°¶°ªÅÀµªƒ§¨«¤ÄÔÿ„ÿ·µª…’v•³ˆk¡¼±•]„ ªžŠÂ™}~M¿—†xxUL‘h™€Y]NAPon]krhjuoKN5#$.-vIJ_nWPgR<8ND‡WMCLCJ]wWQOUJ€ HSJELcy"AU( fbq’U#Ka. €‹”bjIjr@K €Šqwvjb •Œ—œ•œ”Š…ƒŽ’ˆœ¬¶„ª™œ’…’d‚žˆX„«›x7$„ Š€k°~\Z).*MªzdZ[6.pS-30$rZ89.&1KN3/27:IUHGNH-6L%&M.^X.,8G6/Og7(m?W50'-"$'wd>1k?€ '$"%"  , !)6*   +//8:5"%# 462;8?-'+(* =5>@8BB:3$ƒ587.DI<„UAB7*…s8mkR%ºçÿè)EÔÿÿþÿÖüÄJÔÿÿÿÿÿöLòÿÿÄNÿÿÿÿÿÿÿ’êÿÿÿ¸Jÿÿÿÿÿÿû^ïÿÿÿÆNÿÿÿÿÿÿÞ öÿÿÿÄ òÿÿÿÿÿ{÷ÿÿÿÄUéÿÿÿÿg÷ÿÿÿÄÕÿÿÿð÷ÿÿÿćÿÿÿñ ÷ÿÿÿÌXÿÿÿÝ÷ÿÿëeéÿÿŒÿé}×ÿÿnyÙÿòICN#ÿÿŒÿÏÿÏÀÿÏðÿïøÿïüÿïüÿïüÿÏüÿÏüÿÏüÿüÿüÿüÿüÿüÿüÿüÿüÿüÿøþàþ€þþüüÿÿŒÿÏÿÏÀÿÏðÿïøÿïüÿïüÿïüÿÏüÿÏüÿÏüÿüÿüÿüÿüÿüÿüÿüÿüÿüÿøþàþ€þþüüil32È袩½Ú–½×ëӽ땔 ›ÅÓÔî¹­Ô¹£€9UÚÓ¯§Äî댛΄º$)UŠÓÊĘx×îÄ¡f¸Ï˜09R‡ž¹ªžˆÄ¹uÓÔ)€II„ÚÔĪª½Œp‰WlfQ~ÎÂ0YƒË·Œy‰‰žuQ„T˜´º¸>€ 5‚‰„†u\^uux·F] :‚‘„‰~Äx’¡œ¼­uQKpYHBS $_‚‰¡Äˆµž¡“­´½Â³¸ˆaT)]$i‚§¬t„\Tl–£³³¢|c–k""]8\"",o‚·˜lK€x„–tee|w02iIl''2p‚ ·ŠŠ‘^\‰‰•eKx€ w6>v]u66>tƒ ‰c¨KepQxBIY|Š€ q7?€d~77?lƒ peecv]^NNjN_Š€ c42†t†424N… ~ZMgZW_dZŠŽ€ c42ƒ254S… ¬vjjgNN›¸± c42ƒ224S… °v~ŽŠ~ŠŠ°˜u88™–™€8Y… ¬Š~v“‚Ÿ¸¸Ä|RR¤¡Ÿ€R_††¬—±¬—¤½Š‹fm«±¤€fi†¬~Ÿ—~~²½Š™†²”i†¬Š´~¬œœ½Ž¨„–¡‡´¬“½“¸´Ì‚¨ƒ¯Š—´²´°Á›‚½ÇŒ›§¬µv§´‚ÌààŽ¤¸†Ÿ½›£‚Ѱ´¬²´¸—̦²´¨Ÿè† º–¹Ø¿ Ø]” ~ž³¬Ü“q¬“w€ ´£‘ƒ§âØ`~ŸTƒ г“”fP¹Ü›m@z¦m‡{‰}r^§‰lP^MP£¬€„º¬›}} `I`8E@5UŸŠ%ƒš`KXXPdL5T6mƒz(€‚Š`TPV[L[8?MdLPP50‚_TXn”Po|o[qM5/ID((+&(-,‚X„”drm`qŠvzdP44'04454‚ƒ†GT86EcnvvhN>c]BB4#8BBE>‚tE5PT[cGEENiNRB1GOORI‚ ZZ_?8XX]dE/PP€ i\^RBM\\^Gƒ X>m/EI5P01D\q€ gRUULYRRUGƒ IEE>MB?CC[CQv€ 8XYX1… lM>XMKQUMqy€ 8cfc:… “e[[XCC…ž 8cfc:… –efyqlqq–‚Lmpm€?… “vfe}oˆ  ¨\..{€w€.N†r“‚“‚Œ¢qmDJ†{€DV†“lˆ‚ff™¢vzXY‹nYV†“qœf“‡‡¢y†„p€‡œ“}£} œ¥‚†ƒ…Š‚œ™œ–§„‚žšŒ…Ž“‘eŽš‚¥¯¯ŽŒžrˆ£…‚®—š”™šž—¥™œˆèszŒ –g¥Í®ŒÍ†8” f™ŽÑvEŽvX€-"œƒxm“ÕÍ=f~3[52."Š™onE-¥ÑyE$OƒN/022-‡ed^S@“dQ/70/ƒŽ.€020+„ Žy^^Œ=,B )$!8~a/2202200+ƒtX=+11/9*!3N\[O6020€206‚aB3/55*5"'0?*-/XB:0(600:7‚>31dn-Y`K5SE0!,MBB0HBH‚1lnDsSE:E\gaKOD€R (RRO$‚mj-3")'"118?&/-€ dzx."0zzx-ƒ 1'B&,!-"*'€ \fd'.ffd/ƒ ,&&'%"'*€%€… '!'&€ , … 9 !2<6,€ …4 #&'4/*"*"€%… 9*# *#5LL?* '7€ *†&966962A'2 )6'€,†1'56##?A*8"!",†9'H#9::A&=„*7‡H1*H*LHN‚=ƒ1Š6H?H4L*‚L?Œ2,9A ,8‚NEEŽ2D&5H22‚N<8A?8<—N(G]!%{2ƒ).ŠbdR$J µ|S“W­ºY=03y9xDG@2${2„(=WHt–Ôäܶ¦½½²†•Ķ­‡‘}E6JJ&=4%~0„($–«Î•j™š|ˆ™¢¬«ÀÌÀ¯¬º¾J‚PO1(D< (~/„(Q¢½¹^>A`rp±ÁÀ¼¢Œr}ŸƒX T91JB +€3„(’ÌÍ…<b]LL]ƒŠ £ŸŽ\Ua~#‚_XD;OI,3„(¨Å•R1P€usokm}‚‡ŒpgTPW8d^MFXQ0€2„(–¨€dh‡^Xdx}yƒŽ€`PmwhFt3kbVP`V2‚1„(Y–€Œ°n`Wlz‚vd3#QolVk 'gb]j`'! 8‚0…m’{wu^RQIO*-70>ASkD€}g$#(mlhrd)%#9‚0…bxWifHYimoU::06I@Nn=~i$#'owq~i(%#7„3…1n:a`U2JTR4?EO_UQgu5}l$$'s€~‡o)%#6„3†]>|€cH1BPOKF]`ogpt|p,,/wŒˆ’v1-,<ˆ.†o“rsN@TSUP`f|†œ‡z~;;=—–‚@;;IŠ1ˆm„Zia`tvrr|—¤}x‰KLMˆ¢¡¨ŒPLKXŽ1ˆh¥xmkvu]ni}‘¢¯j x•€\ “­¯¨y]\[g”3ˆR€quˆz‰€˜ž«_ vŸmmlœµ¡{kmmkv˜3ˆ–‡k™‘†oŠ€“¬¦_s¬}~~š“€|~‰¤6‰~šx™uŒ”—§ª®£M‚r·‘‘€‘•ª¡7‰M§©›žŸq‡{…©®–7‚sÁ£…¢¬¼‰Š‰ ‘‚••¦—ª¢±³•‚rË„³´Á»|Œ ˆ™–©‘•x”‹®©{‚oÕ‚ÆÅÉ×¹9Ž ‘¸–Ž’œ¥ª ¯²q ‚láÙØáט €´™§šŸ‰ˆŸ¢U‚ këîììîõÕk ^²§–•§ ¯«¯¸¨Aƒhðø÷úð¹F’ ]°¡¦®w“„©¡–'ƒiðøý휓 P«§™£‘¦¬¦±³“ƒiòÿÓj ” “¢¨­™¢‹˜­zƒ{ìÆ;— «¡“©ª±ªµ|ƒ£˜ jª°¢š•”h¡ P¨œy‹¢§¢¦¦p–ë'?©sM5:33£ )`kŒ©¯²¬›o@¡ k~AFsw„ޤ§”IŸ ƒtލ˜rn’o`e%žb£³È¾¾Ë›¢¢¤{uk3€– /z¤º¡¡ÇÂuVx¯°\_x!“Už¦™‚ž¯ÕÓÊqT‰Z'qX €‘m¤š‚p\P:kÐÏÇ…;?…ŠGOe)B£¤tkcD5Vȱ~]Z0L†e[-Œ„œ|q^E<=B7$?ggtnb.€  ƒƒ"t—gQ9/>>:8@G?/*"9I\]`_v6  ƒ)Y‚XD+?E<;AD''1/0%-/007<„"6l‡w20:9IO^mngSF;?ZH3H88:. 4€7?C„"lL20%&/CFQSPOG/+1?I6OAAB5%$'<€@IH„yuS*-E<:767?BCGI83,(-I:\€L >.-/EKJIQO„(^]C49Q6.2=?>BHI@1'8>5#<>dSSTE437LRRQ[X„(3P@JHrc;0-6>A=3*;91 =?;+*)3I…:M=9$.4::0+/$+:0?X3D/8NOQ2G…8)OF9.>=(26?IDAQ])L3=WW[8L†/+beO9'5?>;7HMWQXYP> E_`eB V† XtZ[=1BAB?MPak|k WMMjjoN a"ˆVqhGRLJ\][[ary‚e ]Z"!"XqqwZ&"!.g&ˆU‚`UT]b^HWSbu‚ŽT ah//0a|}tI1/09o$ˆA~eZ^kasnohy~~ˆKaq€= ilJ<==ZhR! ‚"#"ˆ6m`E48MitW& ! 1Pa|= „"#!…~€__RS\MX>&% /7d‰C †"#!„XYFC8.$ *ACNLA"€$"#ƒ$'ƒYtA0€ %" *79=3N**-,,%*,-ƒ#C]6'#%"' 0,-4 /344 .3 ƒ) .WF$<@1->@/ (8€=-€=<$„(+:{¢`]]J5-+1NA?+31 =FEE2EEDC'„(E[t:3>,+*$.3;P_C60CODOPN7MONL-„(*PiU%)2BF>,# 5)LYXV%;VWWU1„(Ykl."$++()# (Qaa`'  >]``^3ƒ)`M2 &""$%&Zlmj, Ajklh8 „(C8%"4" "%%  ^ttq0  Cqstp: „!/"('SE"" €5775€ 4556+…"*+  …"    €…   " € †   $ €# †  ‚ '  €% ˆ‚( " * ˆ#(‚)#€ # + ˆ ‚*'   , ˆ "%"‚(+€ ‚.‰€ $' ‚(.€ƒ)2‰$ %& ‚)0„ 4-Š%$#&$(&‚'4! ‚! +7*Œ !(! &%ƒ'7ƒ%&5<Ž $#!)().)('ƒ&:*)0:1 (' &% "! ƒ $=1/006>& '#!%+,.()#ƒ$?324=8’ *&' " ƒ%?27@6“ '  $'*%# „$>=>#• (&„*H>— !!  %+&!(„.8™ !*&¢$(,€#—h8mk  '4+  <‹¿ŠmeYA, O¬ôÿôèçßÁ”c.0°ûÿÿÿÿÿÿÿÿé€.  $fãþÿÿÿÿÿÿÿÿÿßn+10 "Q³ûÿÿÿÿÿÿÿÿÿÿÿÌi)šªf; /gÂøÿÿÿÿÿÿÿÿÿÿÿÿÿÜc%-¶ûë®e5 <‡âÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶E:·ûÿÿß™X/ 6¡ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôvH·ûÿÿÿüÚ‹N' (zøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿU¸ûÿÿÿÿÿô¿yD! %cÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶_¶ûÿÿÿÿÿÿÿñ¸h9  #gËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌl¶ûÿÿÿÿÿÿÿÿÿÞ Y' Q×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞz·ûÿÿÿÿÿÿÿÿÿÿÿ×o žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö˜¸ûÿÿÿÿÿÿÿÿÿÿÿÿ­/ )Åÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©·ûÿÿÿÿÿÿÿÿÿÿÿÿ³5 £þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷”²ûÿÿÿÿÿÿÿÿÿÿÿÿµ8 bêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýò¬úÿÿÿÿÿÿÿÿÿÿÿÿ¸8  1™ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüЃQ¡úÿÿÿÿÿÿÿÿÿÿÿÿ¹: #oñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤E7™ùÿÿÿÿÿÿÿÿÿÿÿÿ»; 'ƒýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄC1–ùÿÿÿÿÿÿÿÿÿÿÿÿº<  4¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäV3•ùÿÿÿÿÿÿÿÿÿÿÿÿ»<  ?Ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿód5”ùÿÿÿÿÿÿÿÿÿÿÿÿ¾=  2¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý÷ÌK1’ùÿÿÿÿÿÿÿÿÿÿÿÿ¿> !{ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞ…H!'ùÿÿÿÿÿÿÿÿÿÿÿÿÀ?>Ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·A"ŽùÿÿÿÿÿÿÿÿÿÿÿÿÀ? $œÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–( ùÿÿÿÿÿÿÿÿÿÿÿÿÁAaòôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿú|!ŒùÿÿÿÿÿÿÿÿÿÿÿÿÂA2ºÛõÿÿÿÿÿÿÿÿÿÿÿÿÿÿíf‹ùÿÿÿÿÿÿÿÿÿÿÿÿÃB=jÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞR‰øÿÿÿÿÿÿÿÿÿÿÿÿÅC7®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉG‰øÿÿÿÿÿÿÿÿÿÿÿÿÇC"•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯7 ˆøÿÿÿÿÿÿÿÿÿÿÿÿÇFyþÿÿÿÿÿÿÿÿÿÿÿÿÿž/ …øÿÿÿÿÿÿÿÿÿÿÿÿÇEQíÿÿÿÿÿÿÿÿÿÿÿÿÿ&„øÿÿÿÿÿÿÿÿÿÿÿÿÅA 7Çÿÿÿÿÿÿÿÿÿÿÿÿþ€ ƒøÿÿÿÿÿÿÿÿÿÿÿù£. #‘þÿÿÿÿÿÿÿÿÿÿÿôj‚øÿÿÿÿÿÿÿÿÿýï˜AdíþÿÿÿÿÿÿÿÿÿÿëWøÿÿÿÿÿÿÿÿÿÞ‰9EÐýÿÿÿÿÿÿÿÿÿÿ×F€øÿÿÿÿÿÿÿÿÌb* 6Ãüÿÿÿÿÿÿÿÿÿÿ¼7 øÿÿÿÿÿÿô¤M! )«úÿÿÿÿÿÿÿÿÿÿ¢+ ~øÿÿÿÿÿã…9 !•ùÿÿÿÿÿÿÿÿÿþˆ%}øÿÿÿþÅe,‹øÿÿÿÿÿÿÿÿÿõo{øÿÿùªL" |÷ÿÿÿÿÿÿÿÿÿæ\w÷ÿÝ: YäþÿÿÿÿÿÿÿÿÓMk÷Çc-AÐýÿÿÿÿÿÿÿÿ½= Gz8  *žúÿÿÿÿÿÿÿÿ©. fåíííííííî„it32[Ãÿÿÿÿÿÿ£ðIÜÛAøÀòöèÉkƒï9ÚíêÙÖͳ†žÀ¶oļì¸ÉÀÁÑÖÙãìíðæïëíçÍÌ—W€ãaÂÙ»˜—¡ÈÜâìíîîïíêëïñíàÖ 3ã ªÍÒÁ›xy›Öëêê€ì îðíçïóæâáÚÛØ”ß½ÝÛ¯‚{pcr{– ¢¢¦­Íʲ´ÂÝÜåéêæßÄÞPºÊÙfTc]`\bfaPKPa_cs€¢²ÆÜØÐãäËÝ¢ÞϳƒGe¦«’…~ƒeM@@hÔIŒÂÏÙ¾½m–“­~Z22Stztƒur|‰€lpy…„yzuŠ‹d€nz[a=.4KCf€¥uˆ¦˜›¼—¢½³»²»ÁÆš¢Ð†GËS¡Í9hÔ"C”ÓãÝɶ´yħ…p; o‡tz„ˆƒ€ƒ~ˆ‹†˜¬ÄfQw‰•IKONywŸžmnbjxƒš¯“zvž®ÁËɪR€žÑ† GË'J§Í—4ˆ gÕ'CfÎÔÓ¿ž²sŒŸ€„9 !–†…€y‚v{ŒŠ„Ї—¦°†P2:[nbXqWqŸ°¦ˆwfh†—˜ep‰‹”·Áʶ?€Ó † FÌ(P¬Ì‘0Š fÖ/B¹ÂÄÀ¨°†‡gNx< ?›­•†‹~}…|skh|§ªT67/(,HCmu‹¢Âº©‰sm~m<:Fn”¥§¢'€›Õ$„FÌ,Y³ËŠˆfÕ7B·«³Â»Š`µeO~A Y½˜€wu…}y‹­»”E$,8/3)@Tª¥³±³´¬zecqZ>$-@\ƒšªš¯¾€š×)†FÍ1ƒ!}͈eÖ=BM¯•‹ =G¢—bŽ>&{ÂÄ«Žzyt`EL°]C‚½š9;:exž Íþ©š™¡p<93,.6)#7W{¢¨ž•§€ŸÙ-†EÍ5„!Y͈e×F‘Aa’IIiHtq(/)I¯³ÓçÖÎÅ¢žŒ\13‘Î×ÔÔÅ·®z¨ÌÉÂªÉÆÄ·Â¿i@?@LRWcSF4Qp˜œ“|ƒ€¡Ù2†EÍ:„$VÍ%ˆe×b‘@ Š«vbl"LWQ5]c³îçêîåÙÆÄ½~Ekq ÚÝÔÊϾ¶¶²¡¡¶¥µº»ÂÁ¼¤q¥°œvilsi^.@n”žˆB¢Ú8†EÍ>„(TÌ,ˆeØi’*q„d`84HG\¦®ÎáØçíóèÛÔØÇÌÜåáÕËÁ¸¸³¦«¨{X`A\u¸€Á½¸ÁÄÇ€[lx¡¾ŽNK^m‚¤Ú=†EÍC„,SÍ3ˆdÙn“+KnejX9l·¾¾”žÐÁÑÕÝêèåÙÕËÍÎг³«¥¢˜¤¹–‰¡­z€ÀÉÆ€Ã ÁÃÁ°ª¹ÄÐËLJ†£ÛB†EÍG„0QÌ:‚ƒdÛs“:2­¨Ñ¹mËÒΚ’„¡­ºµ²ºÂ¬Šve‚©ª•°­›Ž”¼¸—„ޱÄÉÌÐÎȾÁ½­±·ººÊÕÂÀ]…£ÜG†EÌL„4PÌ@ˆdÛw“:+¿¢ÌÖÜÝÙÍÃM&S“¡‘ƒznzij™Ÿ™„–„“´°»ÁÿÆÉÉËÍÉÀ»¼°•ŸŸ­º¶¤µÊºq… ÜL† EÌP„8OÌGˆ cÜx“;)Ȫ8ºßÔÖ¤±½Qmz}xrehzuŠkllyvˆž«©¯»ÈÉÆÈÉÈÈÉŽ·°¥š¡u—¯¹’¬®´£„¡ÝR…#"EËU„;NËMˆ"cÝ{“;ªÎ‡X®ËË¡´…|'!3c;BNPUq~…•ºœp𱬍¨µ¼ÀÃÂÄÂÆÃ¹¬¥¡™˜ƒ”ˆ“”†’¼Î³E„ ÝW†%EËY„?MËS%‡$cÝ|’< ÐÁ«·ÐÁÓı~[2bD=LUfŸ—ˆ€‰Kežµ£——¡®¸»¾¾º¾¿¹³¤ž™›zH}‹…Œ©ª:„ Þ\†'EË]„CMÊYˆ'cÞ~’=ŒÍ–Ô×ãÑÂzyO(Hˆl[ZCOI22gyF§œ•”¢®µ¶²²¨¯­®¬¥˜”‘kP}w2Un‚‚· ƒœßa†)EÊa„GMÉ_ˆ)cß‘>ÛѨÌÈÔÐÆsC[R6)%EŠua4AAN]<=Gv‹”ˆ’¡ª¨¯ª¤œ™ ¥¡W\:Y•rz™~¤šƒ›ßf†,FÊf„JMÉeˆ+c߃‘>¾×¡ÐÐÈ»­Ñ yXB**2\‹|z€ƒpsxft{tz„ƒ€šž œ™š’”–•˜˜ŽQA^t[Ÿ’vk_š¨®kƒ™àj†.FÉj…NÇj.„-càŒ‘>°ÓÙÉÊŶr· m_G9!6¬‰yz‡ƒsqvv{xynwy}–˜“’”‘˜“‘šš—o?NHœ_\e_Zo•¡´oƒ™áo†0FÉn…RÄpˆ0cà™‘>§ÎÑ·´À³MXWoMG5$g¾‹|z…‚uwvrrsrqw‚„‚„†’‹”“”‘’uEt•\,OGHUaUe¦­¨ƒ–át†2GÉr…VÀuˆ2cᜑ?¢È«©¼¼T# 2( 0Lºl€x{€~}}~~||wy‚„€ˆ‚ˆŠˆŽ‘–’•œˆhq›jWHVkm L¥¡›‚•âx„544GÈu…Z½zˆ4c⣑>.ÉÇ¬ŽŒ¶vEBKU<^†¸O,Lhu~||~ƒ†ƒ……ƒ}€ƒƒ‡†‹“‘—¢˜^^Zk{…‘«©Ž`=Rd«°„ƒ“â}†7HÈy…]ºˆ6c⪒=˿Ɨwƒ¢“¨¯—½ÌÆuY,'B[ny|‚‡„††ƒ~ˆŠ›—”𙢤 €R038Gaq„†zv¤:S~f|~)ƒゆ9HÇ}…a¸„ˆ9cã®’<ÆÄ·x˜±À¸•À¤ŒZ®fW962Kp€…„„…“”Ÿ£©­«¢l=4D=HQ30Z{`Jl›AE^da„Žã††;IÇ€…eµ‰ˆ;cã²’9Z«¶¢¨€q”™ ‰—ÙÔÍÔ¬©® iHh‹‹”œ §¨£šx†z':)(D…†ƒ˜¯~yƒ“… ‡Žá{†9EÆ„…i³Œ‡98\Þ±“5{¬¤nF„zˆr_°ÈѼ{iPA^qœeL\clv„ˆ|z{dTJ<3733>UihŒdWpf`ŠŒßa†0<Ƈ…m±Œˆ0Rઔ4?ƒ•£…i†x—™©­¥‚Œ}mS; #GUNN>!4MTfU6D>76>Š…ãf†06Ä”…|«œˆ0J井3±bdTjUd`•}nlAHB€pjbb‡Ž—x7[uYIKEB2)AOT\CTV=Ko[l}+Š‚äh†05Ä—…€ª ˆ0H引2®|\W0)W|jl…SaswagŽ€~YB8Sd4<8Iqr€e[XObfdlVUTAYaQw}‡‹åi†05š…ƒ©£ˆ0FæÁ–1­•cU=?P‰K\„v^ZM07]{>_J2RW^B5,Zi}€qck[T\~`hŠm‹wçn†26½¢…©¯ˆ2AéÉ–1¦tMDP¡mŒ” v[…>=cC1PbevQbugVYaYGOrYM[_zƒqfm„b„‘Y‹tèt†7;¼¥…“©³ˆ7DêÌ—0›Œh@%¦˜v¸J\v†wdC'-0?PNHJ\‹qbg‡€…„‚Œƒ~pƒ‚r‘Œ•:‹qéy†=A»¨…–ª·ˆ=GëΘ/)-nµˆ»´sVqx’vJ++04\EU[Rr|NJSK^†u’¨«¤·µœ’¯³²¿±6‹nê~†CFºª…š«»ˆCKìМ*3˜´¯š¦k_–Ž…T2:qx^ISFTq\\WHg€SVTb’r“¯·¸»²§Œi냆IK¹¬…ž¬½ˆINìÔœ*€¼…m©˜‡ f^ˆ—YYŒ’Ž|uapzPSRMvrgzuv‘oq|ª†ƒ²‹˜Œf숆OQ¸¯…¢­¾R‡ORíÖ)н£r„¤™iA[iygEmszž­””Šujƒkhvs”š›Š•©¡¢¿©‘Œcí†UV·±…¦¯¾Z‡UVîÙ)¸±{°¥YL]|–€YTZPLv™‹¡¬³©¢•œ•z¤Ÿ™²¦“¦¶®tŒ`î’†Z\¶³…©°¾b‡Z\íÚ)ˆ½œ¬” oxŠ_X˜};‚tnsk‡œ_T_f…›ªÄŲ³ÉËÉÂÆÇ¿ÇÄrŒ]ï—†`a¶µ„­®¹¾i‡`bíÜ)7Ç¡¦¯y\a‹˜~šMt¨{vyh”~^Y]l‰sLP`ˆ¯±Â¾ÂËÉÈÄ«*ŒWðœ†fg¶¸ƒ±µ¾¸†ˆfgëÝ)2»®ž¸”klmššWCœµ´³—v¡kTYc}¨yqfdšŽtjs´˜†¯†˜ŒTð †lm¶ºµ¶»¿£t‰lmêßž'®º±·ƒf{ u…¡‘’œ¿¸³›—€…Œ¥fy„Œ©¡š‚˜¬² À™›Nð¤‡r¶¼€¹»¾·‹‹rséàž'¥¹™f®{‘sG|¨¦Šc]]Š­±ÄÃꪒ‰µ¡µŸªžˆª¸§‰Ið¨‡x¶¾¼½¾¿¥}Œxyèãž'‡Ì£u€’£uBD€‘Ÿvz|u£‡_ouޏ»ÈËÏ»ÂÎÏÌÅÁ¼·ÆÁ’Eð­†}~¶€¿·‘~}~ççž'HÆ¿x¤jW¬¼žovs†­tdqis¹rv†—ºÀÅÉÊÈËËÅÇÂ}?𱇃¶¿¾§‡ƒ„æëŸ&«¥¯˜kx–kxĺ¾´§¤·hn}n‹­oxf®‚kt•½¡Š¼‡­f<𵇉··—‘‰ŠåóŸ&¡¦®§qY^ƒˆp¦y—¾Ë¾³¡†€x¤¥€•˜Œ½‰’›§±§¡À™ª>6𹇤‘’åúŸ&pº®°|qh|£¹œfWVt¤¶ÈÇÌÀ»¬¢•‘ª¾¤­¯À¬Ÿ¤¾¤3–Âï Ÿ&!±Æ·¸˜‰À³¸¥mne„¬pg†•®ÃÍÍÐɳ½Åų··²²À¼ª1îÀœ›£ÙîùN ¢ºŽ½²¡¾ÈÆž‘}q•­nltb†¯t•«¦¸Æ€ÏÈÐÔÎÎÉ•Ž/îÄ›¡µèïÖ¢$‡Á³¼·‡¶ÀÌɵ¯™ajhi¬šfhWc´u€†šÈª“©¡§{Ž/îÈ™§¨Êî$iĨ—³º•œªg“½Ëʧ£‹w€Â‡‹~”´w—ˆŸº¼¬•«kŽ-î̘¬³Ýîå.¤$’·›¹Ä ²–`y†­¸¹ÐÑʼÛ‘›”°µš•½µ¹‘·ª¬ZŽ+îÏ—²ÂéîÆ¦#p©»¢Å³³~kŽŒ¶¦it‡¢ÆÄÊÍÍļ¼º±¢Â¹¦ˆ»¿³CŽ)îÒ•¸¹Óï#K™³e±À²§ƒ´•o„ƒt¸¢‘­¿ÍÃÂÊÎÌÆÐͽÍΨŽ*îÖ”¾Ââðå.ª!•Ç„Š˜·°ÐÉ­²€m•’À_^R~ÃŒ™¢°À»»Àµ¤'îÙ“ÄÏëïÈ«"¬Ì»¨§•u”»Ç¼§¨¢‹£Æ‘…s­µgeg²¾ŠºŽ }%îÜ’ÊÜïî„­!ªËDZ¿~~z¡³ÇÒÓÍÀ¿—œ–œ»¦zzƒ¿º»¨±›ªq&îàÏÒæîç.®!žÁÇɼyx|®›l‹º¿ÌÒÍ˹ī¡‘¾“•žÁª»r#îãÕÛíïȰ!l¿¼¦¿Æ»§¾|Š‘l‚±˜¡·ÊÍÅÒÓÓ¿ÂÂÆ¾ËкS!îæŽÛåï!mÐǽ¬œÀ̾¯v}™‹²Åni_a³ºš®³µÄ½¼½«—îéŒáâëîç2²!CÂŽ©ŽœÆÈÇ´œÄµ’¢Š‚½™jt}»¸•u —‹Œ îì‹çéïïÊ´ÏÊ‘†’—„£¶ÐÒÈÇ¢¦«š£Èޔޤǽ¨ª™¢‰îîŠíîðbºÈ¹ž’˜ˆ‡ov˜¿ÅÈÊÈÀÂÅŸ¡Ž¡Ä¤¸µ¶¨²‡ïð‰ïðîæ6·ļ¾Ìõµ¦€¯ª¤¾ÒÐÌÉÐÑŰº½Â¾ÇÎÆˆîðˆïðî̸¬È¸§¬ÆÊ¦‘ ¤¢¾§a^r~¦¨´ÃÆÅÆÎËÄÉÁ²Mîð‡ïðÔºª¤¾È˽¦°Érkgš±‡ymt¼¡©ƒ¤²ž;ïð…ïððç4»oÊ¿› Ÿ¬zªÐÒúŸ–‹‹¶¸†‰…œÉ»¾…»§””îð„ïðî̽”ÈÆÃ ™‡—‰¡Á¿ÐÑοơ‚}|µÀ¨¯šÁª£‘ïðƒïð„ι¾Èµ£š®–›¶ˆ–»ÇÂÆÆÍȶ¶¯¡¹«Ã²¿£‘ððïððè2¿7¼³¤¡Ä´ª¢™ºª„s~‚¤°ª½ÀÊÄËÐÆºÉÏ»t‘òð€ïðïÏ™°—°¶²Ïö¼«Ÿœº§xyx‘¶Ÿ¡´±™¨b‘ôðïïðð”ÂƧ¼µw¥ÆÊij©§ª²ŸŠš–µ²•}ˆ¼©§¤O‘÷ððïôCÄqÁÆ««’˜ˆ•½ÉÑÌË䚤µ«ºŸ¸¥¦ž;‘ûðòúÅhÄ·¾¨›ž•´Ÿ~§¿ËÈÆÇÄ»«Æ¨«¦œ½¸´ª‘&ÿÇG¹¦°ÁÅ«¥»˜Œ„º¨ÃÎѹÅÈÆ½°ÇÐLj’3XÚÈ ¯³²•ÁÐɫЄ~„¨Àp{q”§™®®¬§‹¤wá€ÊÁ™¥¡ÆÆÃ²””´¶ŽŽ†z¨—Œ{kµ›Ÿ§ság°Ã²¶ Á¬ÇÔÏÅ©¥ˆ’~“ªŽ™“†¶£¨yá+ Èƹ¡Æƒ€’¯½¹ÆÂ·­¤¥¦¬«¨¶¢¨¹wá7ÂÉÎŶÀs‡~ƒŸ±§¾ÏË¿¿ÆËʾÇÈÔ¶bÃÿÿÿÿÿÿ£ð>Á¹4ø¯æîÒ Vƒï/¿Ý×µ°¥–o†¤žY¤ì—£Š‹¥±µÉÙÜâÎßÚÜÔ´­wH€ãMžµƒb`i–»ÆÛÜßßàÜÕØáãÜ˾-㈦§‡dZMNf²ÙÖ×ÚÚÛÞâÝÑàèÏÆÆ»¾¼wß“¼¹uSNH?IOhruu{€¤Ÿ€~½»ÌÕÖÎÁ™ Þ=…™bB6?<=:>A>303>=?IRly”¼³¥Èˮ݀À¢xT-Ko^RUA1)*'M™‡y}qgdpq”¹zÜ!GŸ¡ –CNÅéççâÔÖ·oR6XÈéÜÙÚÒ¯UENZZn}3Û"„¦°½nŸÇÒáÜÛàÚßèßÜÐÓÖåÐɧug§gUS]q¦ˆ‰fÊ#_®È•¤ÏÌÊÚÙØØ¼ÍÕÍ×ÝÓÑÂÍ˾¼¿§™Éa`{±´i‰CCEÉ&‰È¸ÐÙÑÌÔææØÐ«Œ±×ÖζkkœÉÜÑÊÑÚÜÛ™tw¥µŠ—‡B??A,Å)¢º—¥¹²ÏÖÔÄ´ˆ›³³´ÔÀ…JLZv†ÀÍÊÍŽTu¡¯Â¿†A0$<>>Â*h»Æ¯³Ô¿¸¸«…}ƒv¥ÝÜÛÙ­lZXOOev³¹½Ä´ÊGlx‡—¤|†@0*>?8¾-¼µ ŸÅÊÚݸ‚” ¹²¶ÔîäÞâçУ•’zZZ{©·´·¸¾«”1Rt®® …@1€0@>2¼.O´ÅŒ¥ÏÒßâרÑÄÆØíÙÕ×ãááßâÜÈÆš\_s®Æ¿¸¯´¡€:f¤¶¨a…?1‚6@=&¹0xÆËƒ™Î¼žÑϽpŒÉ¹½ßºÃÇßÓÓÝÞá輑j2ƒ";?<´4²ÄÓÅ|½À‘Á°€_bTF4¡¢¡ÜåÒÉÚáÎÏ¿¡~F%0Bz” ­°®–'7Dcˆ¥‰ƒ=3…(>>: ²4•¡»Å‡Ã㇧e/O„~h|`;HA_èíáØÜȽßà¶~d-(0,[Ž¥®¯);cbr¤£`ƒ=4‡.?>4¯6™¶ÉË ·Â…Vœ©gUŽ{rXN1[çàâØÖßÍéѾšnk/4<‡¯¬§¯¯1GŠxctŒ€ ‚<5‰4@>+­7bÅÃн¤²„tu’¡–’ši[FODGCßæÔÖßïìʇ|Œ…‡WR/Iz€£«¯·W+Flpx†U‚=6‹ 9?= ª8ºµºÂ²¼|t•|pŽyYRP?L8@ÃØäÔÚâÙâù¹vZŽ|u3#/h[…§«ª¥»bQ^misR‚<6Œ&=>;¨8ϽǶŸ¡^…ŠqPLg6DHXJw¡¿ÉÞããೃvz^Ug\mW>3[cu‹‰£šº†otyd‚;7Ž,?>7¥9 ¥™¢¿Žª¦‘’„qq}^Z?,VTNzuƒ{€ªÌµ‚VMYTX\sokXB(4Jm„†x‹š¤¢inoR‚;82@>/£; •°°¼œ¤s©j œujD.\B1\t£”¥™²´}WGKXP\]UcrfF )SVpªu‚¤©‹gq]:9’8?>$ <Á®»¯§iʼn„w`WV>;,Mbj~³°›™…ƒlhPELTMYYaTigZC!?aus£®x¦š©¨|u#9:“$Mg`u¥Ä¸ÑÕãδ}z]E=83Fbk„Y:?9QSHXPWH" 2h†gwΣ~¥§ÅˆG?D™/Až]q©q\j¦ˆ`qk…<€BP„“2RIŽª¨žš„–Pzf^iuOOKUMQ^WZZPGQa]^[WZX]RdM9/:8$!!!*>DCs±‰bŒ~•‰uw`eky””CS†#8"’4TI ¼˜˜‡ŽƒX€oONVKMQRV[UW[VPKBGUV\afe]N]7627E/"5nZGª{n•~±™Œlqx‰«¦NDV…%@@7#7W IfŒ¤¶‡FibsQ9 5JNJTQSKIOXREHNUUNNK\Y@RGN:>'!0+ARkKXmadealƒw~s~‡‘oDY!†'?2@7$„‡9Z Cs©É¼¡x|N•rVH%GWJNTWTSRTQQWYSVbvŽA4LY`.032MMjgFG?DMTcr^NLeZq‡›—r<€E\%†)? 4@6&Œ;]CM¦¬©‹f|K[hST$ `ZVURNSLOZXTYVamvX3 %_B‹ŽŒ†t|YYB2M' (dq`VYQPUOJEBPlt6"#.+FK\YlŠ}lX\JFSRPF&%-GS_jlk€Gc-†&.@  9A8„&‚%@aBˆsv‹ƒ_?‚A3Q)9‰‚bRLKUPMYs‚b,$!)5_yp^ytwvqNA@H:();Tbmcu–€Gf1†)1Bƒ +D*ˆ)CdB<{a[n'.lg>[' SŒŒoZNMJ>,1g{>-a‘k$&%AMhiž‹„mcdgH&% ##8Ohle`r€Ji5†-3E„"F.ˆ,Eg!‘0F^Z//C.JSH1ˆŒ´Ð¯ ‘hfZ;!a¡²«ªŽyrNqž˜Œq—‘{ІC€) 158?5-!4Hbd^O\€Kl9†06G„#H2ˆ0Hi)‘@ ZrK?E073"PV@=$".-=]lr¡Å´ÑÛèÔ¹«´¢¦¼ÍÅ®™‡zytjmlN8>*;Kz‡†‡€y‡’U;GOm‡b27AJ‚OqB†7ˆ:Os5“:'€‡«L¦¨¢hiUio|wu‚‹rYLASmn_uqd[_€zdaU[t‹•¤Ÿ”ˆ‚†‚or{|›®‰‰F…RvJ†>BP(„!)QA‡>=Rv8“:%ŒŽt±¼¾¶ŸŽ15^g]T\NFNDDbfbT`UQ^uq}‰…•–šŸ–…|~s`ffq}|k}˜€Q…RyO€BƒAER+„$+SEˆAUy<“ £‡$‘¬°v}ˆ4 FNPMIACNKY€E NKWenlp}”—‘”—“”–ŽwrjbgKbt^u~zp „S|S†EHT/„'-UHˆDW{?“;‹£d8Œœ›o|UP !?&*236HQU`deGctnkku~„Šˆ‰‘Š{ojgbaT`W_`V^‹ w5„T~W†HKV2„*/WL‡HGZ~D’< ­”©ª™Q:  ?,'06AfaWRX0@eui`agox{€{„zsieebcN].RQYVZop*„U[€LƒKNX6„.1YOˆK]F’=w¡d³³É©`QN2 .WE;9+3/ BN-[kd_[^gpuusskpoonia_]E3PM 7FTS\|lƒS„_†OQZ9„13[SˆN_„I‘>蟔¬¥’K+:4#,XKS>!*)2<&'.LY_ZW]gmlpmiedbgig\[S8;%9cJNcPib[ƒR‡c†RT\=„45]VˆRb†N‘>¹¸s­¦•†v¦iN9* ;YONRTQHIMAJOJNUTRcefdbc^^``adb[Z4)¡­µ™™„I~gF=-%#wXNNSWTJIKLOMNGLMP`a^^_\]b^]cca\G(2.g=;@=9G`gvKƒUŒk†YZ`C…;`\ˆXgŒ[‘¦ª¨{y†y188G1-"EˆYONRVSKLL‚ILSUTUSV\]XZ_^_\]J,J_;3..6>6@korƒRo‚]\]bG…>a`ˆ\jŽ`‘?ƒ–Œpm€6 1ŠaERMORQPPRQQPOLNSURWSWXW[]`]_dWBH[ecD8.7EF0jgf‚R‘s‡`eJ…Abb``†_m‘e‘>#›”q[Z|M,*06&=]…21CKQPPQTVTUUTPRTT\ZWVY^]aha<<:DSOU]po\='4@nu\ƒU”w‡cgM…Dceˆco”j’=œ‡bLTm`nsaŒ’L9*:FMOSVUVVTQWX\c`^bbhigR4!$->IUVNLl%5QAPX ƒR—{‡giQ…Hehˆfr–o’<• c[Mbv‡|al[?}uE8%" 0GSZRUTUUZ^_filonhE'!,'.3 :O>/Fc-0@FH„R™‡jkT…Kfkˆju™t’9Bq{noRH_bhR^Xl»¬£«rpskC.B\ZXYZ^dfklicMVN%+UVTbxQMT^Y‡S“j€QƒPRmW…NgaN„M\”q“5VqZjG-TNWI=t–¦‚OE3*+:2.)*'31>C7UQWŠUˆ3†q^…UjOˆ'Žm•3[zs^gYX@IYXSUSTG8:=4%6D%/$94**BNR_ubOJM>%<9@@#&CMONEHJ7]k]vmOŠV‘7†ud…[nXˆ%—x•3kkBQPQK_NXL=,>7)NG5P\ULHH>VN4(! )8=2 8BDJAG;8`XMuc7ŠV•9†wg…_p\ˆ$›~•3Yx?@7F;EFpWFE*.*RQHD??WZ[aM&JbJ>?:8*#7BGM8FI4?]M[wi)ŠWš;†yj…bq`ˆ# ƒ•2vO;9'$IhYWW5>IL>B[RQC1';F)2/=_`lUMJBSVT[IGF6KRDeis‹Zž=†zm…eseˆ"¤ˆ–1x`?6+7Cs?NkXD@7 #MdqdT8 &(5CB=>Mu_SWkrlkpoymvoj^nn`zv}7‹V´Q†|…v~ˆ&¹£˜/&+_˜rŸ™aH_e{c>$$),N;GLE`hB>E?Opc{ޑЛ™„{”˜–£–4‹U¸V†!$‚…y€„ˆ!)¾ªœ*/€™”‚ŒZPxpzF*1_eO=F;F_NMI=WlFHFR{`z}”›Ÿ™ŒT¼\†&)„‚…|‚‰'‡&,­œ*mŸp\Žq‡UOrKKv{xhcQ^gCFE@d`Wgcdz^_iqo—uŒUÀb†,.………€…‹/‡,0Ʋ)vŸ‰`oŠX7LXfyW:\`g…‘|}tlcYnZXca}‚‚t}ˆˆ¢Ž|ŒQÅh†23‡ˆ…ƒ‡Ž6‡23ʸ)zœ•zh•‹yK@Oi~kKGLC@cu‡’—މ~ƒ}gŠ…†–Œ|Œ›“cŒPÉn†78‰‹…†Š=‡78ͽ)w¢„‘}‡^etPJ€i2ma]aZr„PGPVp‚‘¨©˜—®°®¥ª¬£¬§fŒNÍt†<=ŠŽ„‰Š“D‡<>̾)2®‡Œ“fMRuj‚AbŽgcfX}jOJN[sa@CQs“–¨¥¦±°®ª‘&ŒKÐy†BCŒƒŒ”Ž`ˆBCËÀ)0¡’…œ}Z[\ƒ‚J8ƒ™˜˜cˆZGKSjŽf_VT‚xaYa™€q”p‚ŒGÐ}†GHŒ’‚“•{N‰GHÊž'••wšnVg‡cp‡wz|„¤œ—ƒ€lpv‹VfouŽˆ‚m€‘–‡£„DІLMŒ”€“”•Žd‹LMÈž'ŽœVx“hzaÐ…‡RŒ€–••|WŒRSÇÄž't±Šcl{‰b89lz†dghcŠrP]bxŸ¬°¶ ¦³µ³©¦£œª¤|;Љ‡W——•ŽiŽWXÆÇž'@¯¢wewŠyYI’Ÿ…]daq“bT_Xa`cr‚Ÿ¥ª¯°®°°©­§k7Ї\•”~`\]ÅËŸ&’‹“xZeZf§Ÿ¢˜‹›X]i\u’]meV”nZa} ˆt¡r’Z1Б‡bŽo’bÅÑŸ&‰Œ’_KOnr^f‚¥°¢—ˆqleŠŒl~€v t{ƒ–ˆ£9-Д‡g{i’ghÄÙŸ&cŸ“”h_Wi‰œ„UIHb‹›­¬±¤Ÿ’Š~{¡Š’”£’†‹¢Š…+ΘlnžÏäŸ&˜©šœ€t¤—œ‹[]Uo’_Wq–¨³³·®˜¡©ª™›™–£Ÿ+Λœq{¶ÎØB $ xŸ–ˆ¢¬ª…zi_~“][bRq”b€•œ©¶µ¶­·½´´®Ž)ÏŸ›wŽÇϺ¢$t¦w—Ÿšxr›¥±®™”RYXX’ƒVWIS™clq‚¬}ˆŒiŽ)΢™}~¥ÎÏw£$_¨—~„W}£±®Švdl¥rmuj|™dr†žŸ„‘}[Ž'Ϧ˜‚‰ºÎÇ(¤$|›ƒœ§‡—~Pfq’ ¸¸° §ƒ{‚}•›x‚~¡™œ{›‘OŽ%Ω—ˆšÈά¦#`Žžˆ©˜—jZxvšXbs‹«¨®´³ª¡¡Ÿ—‰¥Œrž£˜=Ž%Ï­•Ž­ÏÎr¨#A‚—Uy˜£—yn˜}]onb‰}•¦³¨¨¯´³ª¸´¡³µŽ#ΰ”’—¾ÐÇ'ª!€¬pt€˜·®’–l\x~{¥yPOEj¨wƒ—¤¡¡¦œŠw#ϳ“—¥ÊϬ«"–±~b~¢¬ Ž‰v‰ªzxpa“œWUW—¢…tžx†k!ζ‘žµÏÎq­!•°ª•£zjjgˆ˜®º¼´¤¤€„„žŽfgn£ž–ƒaι¢¦ÃÎÈ'®!ˆ¤«­Ÿlfeh“‚[wˆ£¦±º´²§‘‰zy¡|}…¥žfμ§±ËÏ­°!^£ Œ¢¬ w¢itz[n–ž±´ªº»¼¤¦©­£°·ŸKο­®½ÏÎu±!_¹ª ‘…¨²¡”cju–«\YPR˜Ÿ„–œš§¦¤¢¢’€ÎÂŒ²µÇÎÇ+²!;«ª xy…ª¬­š„z¨š{‰tm ‚YbiŸ}b‡uw ÎÄ‹·½Íί´y·°zq{oŒ ¸º­ª‰Œ‚‰¬x}xŒª¥ ŽˆuÎÇŠ½ÅÏÎx¶U­ž…{€rr^d„¤©­°­¥¥ª†ˆxˆ§Š›™™–tÏȈ¿ÀËÏÈ/·ˆ¦Ÿ¤±¦ššyŒyl“Ž¥»¸±®¸¹¬–£§£ªµ©uÎȇ¿ÃÎα¸™­›’­®z‡Šˆ¡ŽROajŒ›«¬ªª´°¨®¨—EÎȆ¿ÇÐÎwºp½žŠx¢­°¢Œ•­w`ZV‚–rf\bŸˆn‹–y…5ÎÈ„¿ÀËÐÈ-»`°¤ƒ‡†‘g“¸º¦Ÿ†~uušœqsp„­¡pŸ|}Îȃ¿ÃÎϱ½€«ª¦‡yrtФ£¸¸µ£©ˆmji𣔣‰{ÏÉ‚¿ÇÐÎu¾r´£­˜‰’‚šr£®¦©ªµ°ž›”ˆœ¥—¢‘ÐÉ€¿ÀËÐÉ+¿1¦˜‹ˆ§šˆŸoajnŠ”‘¥¥±§°¸ª­· c‘ÒÉ¿¿ÃÎδÂ…•”™š¶¨š¡w†„„žefezšx‰Š˜–ŽV‘ÓÉ¿ÇÐÏ€Ãn©Ÿ˜d¬¯©˜w—‡t‚š—~isŸŠE‘ÕÊËÏÓ9Äb¤ª‘‘{€s~¢¯¹²²¦‹‚zzЦ™†›‹Œ…5‘ÙÐÒÙÅ[§›¡Žƒ…}˜†k¦²¬ª«© ‘©Ž‹ƒ œ˜‘!ÜàÛÇAŸŒ•§ª‹ž€mvolžªµ¸ž¨®ª¡•«·¬s’3)¾È ™˜–~§·®uojoŽ£k^i`}‚•–‘ŽuŠfám¯¤‹‰«ª¨—|}˜šwxpgvgZ™ƒ†cáZ–¦–™‡¤“°½¶ªŠr{j|x{q™‰„ká(ˆ¬©œˆ©nk|˜¢ª¨“Š‹Œ‘š‰Žgá3¨ª´¨™£`qjn…–¤·²£¢©±¯Ÿª¬¹›YÃÿÿÿÿÿÿ£ð5­¢,ø¢ÞèÂHƒï)¬ÑÉ›•‰‚`v‹Hކì~ˆbe…•œ·ÍÐØ¾ÔÍÏÆ¤–_<€ã?„œZ:9Ar¤²ÏÏÔÔÕÏÇÊÖÚл«j&ãq‰ˆ^<5-.?˜ËÈÉÍÍÎÓ×ÑÁÕß¾²±¤«¦aßt¥ J1.*%+/FQTU\_†\Vk¦£ºÆÈ½«z Þ/^ue;' %#$#%'%%$%+0EPp¤™†µ¸˜Ýh«€M29€}VA23&9…nibfP?=CHq¡[Ü!6€s(=ºáßÞ×ÇÉ¥U<$D·âÐËÍ•4).56DU'Û"i†”§O‰³Â×ÏÎÕÎÔàÓÐÁÃÇÛ¿¶ŠQBŒL217D†i‰KÊ#J”µr‰¿º·ÌËÊʤ¼Æ»ÈÐÄÁ­»¸¨¥¨Œw·n<8P–šP‰223É&sµŸÀÌÀºÅÝÝÊ¿Že•ÈǽœDBz¶ÏÁ¸ÁÍÐÎ~IIU…Ÿ~‹‡20/0 Å)¤r‰ —½ÈƯšaWx™š™Ä©`+,5OaZl©»·»°©3I“¬©‚†0.-Â*^¨²“˜Å¨Ÿž\RYKh…ÒÐÏËC54./=5+`‚€ÐܶÍÖ¼½©€S*(Xp”’z !(;_†iƒ-‰0/+²4{£±a¯®…gŠF7]TCU9#*'EàæÖÊϵ¦ÔÔœR<;f…’“o#E=H„ƒGƒ.‹0.'°6ŠŸ¶¹ž¬_8ƒ‹B6iUgOM7.FÞÖ×ËÈÓ¼áÁ§wCD$c’‡“’x *gP:GdZ ‚-0. ­7U²®¿¦†—]MRm€plzF8*/(**^{ÔÝÅÇÓéå·^Rd[^40,PZzƒ’¢D)ABK[;‚,Ž0/-ª8¥œ¢¬—¤\RYrVIk[61/&.!*±ËÚÅÍØÌ×®¡ M6ZgTI?6]ˆ‹…¤O58A>E9‚-0., ¨8~¾¦³„9_bJiY/-@ (+4,Oƒ©¶ÒÙÙÔ˜YLO82@e6C4%6;Ibecƒev£_BFKB‚,’0/(¥9 Žw„©lŽˆlq\GIV=8%32/PM\NTeŒº›Z3-4246HBD4',BU\`Tew…‚CAC2‚,”0/#£;}”•¤n|‡Oe‹C‚zQL+7'@Tƒko„u—™U4*,4/672;F<)13ChŒoO_„Œj=D:+•0/- <„«‘£“ŠG²c\R:75&#-=AS˜”yw[YVD@/)-2.54:2>=5(%9GEƒ‘UŠx‹]WM+—0.,ž=Nzµ½¦O40&!).49*'/:=/446 6KG`}Rpо‰M-+1—01/+›=h¢¥†‡tWaHM\VAR¼„Z…ˆ±b-,3™01332"•@3¥¸ª|wr_tm|\>8h§¶–c†©ˆd`iY:/%&(=78+- &$*3' >Q\8eŽ}‹¬Ï£oL,4›01433’Aš³ Žty‡‚pŽ €° c?'#7;+))*',4.*'$+B)*7* $"+% 1RGF7;Tt©ª°BFX,50355:‘Bvš  ƒtg‰XdzSR^[;0%#$-749;82'&(,(+'5a1#*,OkA9E]_KÅe98_ €,7ž0156‘B»§‡qc}O/fSV;,032%0&.=945784360/20-B?)!(!%(|h7OŒT7?‡d9F@^4€,8…2—157HyŽŒ€wdXr0V=8@I//,3.08455/*0978635470=-"#!&('R•`:gUp`HK9<@Npw,9‡5303’578H¥}ws\f_7XF//4,./03623630,'*2369=<7.7! !) F5*ZŒOApSk–ud@DL`މ?,;‚9‚85//15899GLdƒ›lU`)I>H0",.,201,+/40)*.22..,:5&1)."%'1@-4D:;S>9EYKRFR]jP+ˆ<8.#/28€<Œ;:C\‹·¥„KT/sK3*  *3+.231101003513)*%(.2:F7.-<5D]wrI+€,=‡?:. %/3;Œ?>;C=‰ŽŠf=U-8A32  95330.1-/541439CK7%*%!+!+=IK3-1''359:&*458O_sS#€,>B€CƒB =.&/5>ŠB@< Bjhd[NV88'- BfJIbZ@&]&0 "_W:1-,2/-5JX>  =VI:NHKIG.&&+" #1:@:Ky€,AH†IB.ƒ.HˆIE?B-U;9JED%6  6cdD5.-+$@T(HqH&.AA{cZA:<=* !.=@<:L€.BK†LD- „-JˆLH@‘0286(,1+lpž¿“k>=5# >€–ŽfKH/H|teHrjePa](€ !%*:;8/>€/CN†PG- „-L‚PƒOJA‘! 8H,%) !#%sÓÁÉѺœjgT259aŸ¥Žs€V€G?J^KNOR^\XB+KPA.()+(#*8<7,0DQ†SJ, „,NˆSMC’*75&$&8AF€±™ÀÏßÅ¡š†Š¤»±‘u]MKF?A@.!$",M€\TK]dk5#+2F_C'+1‚1ES†WL, „+PˆVPD“9%/'5#:al`;Iƒ^†‘¦Èûœ’ƒƒKJB?=:@L904>F/5`qj`b`Z`]GBNd‚xmC†3GV†ZO+„ +R‚ZƒYRF “: _or4‹Š‚CK3ABOJIZdH4-'1AB8KF<68UM;926Gbpzƒ}n^U[VCEOOTy‘_`5…2HY†^R*„ *Sˆ]UG"“:"eiS{–¤§œ}h8=727.*.((:=:29208FCOai[iqqv}qZMQF9<99=BHMSTNTXLE><<:;.830535DF„2Mb†k^'„'UˆjaL(’=h‚?š—¶Œl?3.4)#"'.6?;968=BFFDE@CBBA>:87)/- )217PIƒ2Nd†nb'…&VˆndN*‘>±Ša~nŽ…m."4,1%#,58547=@@C@><;:=>=661!#"?..<0?:7ƒ0Ogƒr€qe&…&VˆqgO-‘)µ¡R“ˆo_O‡B.!$4/.010*+-',/+.221:<=;::88€9;:55#,#C?,)$™’›uvh`+U>)$ P4..132++,,.--*--/9977867977::96*A$"&$"*9=I2ƒ3Rk†xl%…$UˆxmQ4‘¤ŠPN\N!!* ,a5..031,--‚+-121213674587877,,9# % &@BIƒ1Sl†|p$…#Tˆ{pS7‘mreEBTV g>)0-.10//€0//,.12041€457978;3'*5<;(! () ?=?‚0Tn†s#…"SˆsT9‘>{oF65R/ %@_',0//0232232/12265335879=9$#"(1.27DE8$&BJ?ƒ0Up†‚w#…!Rˆ‚vU<’={^j;-2E;DG9izm." ")-/2323320347;98::=>=0%+23.,C0&0;ƒ/Vr††{"…!Pˆ…yV@’qgB;.;K]P;hC8+ZW-!*151€23578<>@BA>)"/%); "*/6„/Ws†‰!… Oˆ‰}WB’91FPIF1+8;?2;4M¦Ž„HFHD('654558;;-3.221:P0.287‡0VYb‚a€`Z!‡ :^‚]‚\ZW@“5•3:MH9>54&+541211*!"$ *"  #$$!$+-,/(Š3R†"…$ ˆUA•3FV==269*)184,,+$($'.09K:.,-$   %!)&&Š/T†#…& ˆ VD•3FC'0/0,9.4-$$!.*/62-+*$2.  ")#Š0V†$…' ˆ XF•3=O%&!,"$!2-*)10+(%%3569."   )%%Š0X†$…)!ˆ ZJ•2N/#" %!1%+-%'6100#(!!&#$/‹0Z†%…*!ˆ \L–1Q:% ()(,-(#1!#)$!'(%&),-74‹3[†&…,"ˆ ^N–1IB/1&&0"&'$$!%% ','*0.‹/]†&…-#ˆ `Q–1?A,97,&/&$" %#& %&!%%&)*‹/_ †'…/$ˆ bS–1 D,#(1 *,/#'%#""$&! ''*+)‹/a#‡'…0%ˆ dV—0P8*(2-#:+#'# (!%(&%''*&)&%!&&"*),)‹.c&†'…1&ˆ fX˜/&((6(=7"!#+" !$'"+241::1+686B70‹.e(†(…3'ˆ hZœ*,95.3,*(+!#!&,".04<@=?<Œ-g+‡ (…4(ˆ  j^œ**:' 3-(/(-)+*$"!$#!$"#+!!$2''7)3Œ-i.†  (…6* ‡ k_)1:0"'0-#* "$/3+,)&"& #"+--(,2/0>22Œ+k1‡)…8,ˆmb)494*$61+%,&#-*04620,.,$0./-51+1:3*Œ*m4‡)…9-ˆne);F.3,0!#(-%&" "(.'-9HD;5NRPBHPCKE4Œ(n6ˆ)„;7 ‰nf)U/14#*.%."2$##+% (")585*7&$/"'/*+/2E;7/-&')0$')2/-&-44/<-5#p=‡*€?/‹khž'=8.*4$+"$43()7@821< % )5!&#8(",<0)@(3-pE‡%!’%hpŸ&;251!&(!4%7NU>71)&,-);(+.291/@-2$pG‡' “'(htŸ&/>56$!$08/#2;PNVD;64.+3=023=9/0?0.oH*+MozŸ&AE59,)A6:1 '8!+5AKXY_Q:?HJ;;CA7?<9nJœ,3^os" $CD*94/DLH/+%!,7 "'9%6J>36EQYKLTZ[Ia]BV\;ŽnT”;?apjª!5K'(->DaR44& *,+C.%K08@=AEGLB3+oV“=Jko[«HR715,"0IK=542)0H+*'"7B€:?.(=*/+nW’@Won;­!KQF5?*%%$0PA4)?%(+ &84FIYZJhikHCSVFQ`B)o\GHZon=±!5hE;36QU?4#%-)6M ,"0.).  n_‹LUlo]µ6aQ+'+,':TfgNH012-1I*+*6D?:22-// n`‰NO^on>¶$9L>/+,((!&>GHSULEBJ/0*3@0787150 oaˆORfoj·>@9GSD:8*1*&46ANibSMceR9;GGBG]F4 na‡OWln]¹RP619UN3*/00=7&&35DTQJJZSFOO:& na†O^oo@º9n=20*>ORC44L*! .7($ "<13'36*/& oa„ORfpj»0RE.//3&Fff@?/,))8;(('1J89'<2+/ oaƒOWlo`½6EEC/*.(,*8B@bc]CD1&%$;<13->20.ob‚O^on>¾5Z;GM50-3,/7(6OTFBK_ZI;5172?7?<‘ ob€ORfpl¿T:03E<50-=5&!%&159OH[AQbL8LaA,‘pbOOWln` =7,47E^I9C4*€.;1##$+7+:987-1.‘qbO^ooDÃ-E296#8RPJ871*3:0)-,:7,%(=410"‘ refoqÄ0=I33+,(-CTdXXA1-**0B829/811."‘ upptÅ0E8<2..,60&?PZKIJJA3C331.:<87‘vwtÇ)D14KK417-&)'&<5Q[d@BOJ?4FaN/’ eÈN=5-L`N3)'%'2=%!($,23>?35*0*á-PA-05MIH9++66**'$1,)$6-/1*á3:A45/B;^m_L60(+%+3*-+'70.15á6GD7/D'&2GE=:51-*(%$!  '2>IZ„aUPKFB?<961-(#  +9HWŠéÿÿãŠoid`\YUQKE>70)#   -=Oa|öÿÿÿÿüÍ«®¿±”ìjcZRI@7.&  -?Rl½ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿãð­ƒf\QF:/$  +=R}àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùä§vaRD5( &8N—øÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÕ¿xK9*   1GdïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàdM:) *>W’ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐfL8'    #5LgÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½aJ6%    .C\’ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû†^G4%  !%&%!  *OczËüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²YB0"'8MåÿÿÿûÄz`QC7+!  '4CTh’âÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþœU?-!!-?Väÿÿÿÿÿö´r]N@4)   ,:J[q×÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæiN:+$&2E^ãÿÿÿÿÿÿÿì¢kZK=1&  #0?PcŠõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§_H6,,6JcâÿÿÿÿÿÿÿÿÿßfWH:/$  $3CVi¥ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ{VB52;MgàÿÿÿÿÿÿÿÿÿÿþÎbSE7," $3E] ÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏfN?9?Pjßÿÿÿÿÿÿÿÿÿÿÿÿù¼u_PB5*   "2E[±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš[H@DSkÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ©m[L>2'  /BY‘éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿájRGHVmÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿä—hXI;/%  *=T„úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–[NMXnÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÔ†dTF9-#  &8Njçÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­dUQ[o×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÂy`QC6*! #4IaªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅkZU]pÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô¯p]M@3( "1D[wÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçr_Y_rÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèjYJ<0& !/BXwÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðxd\bsÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÙŒeVG:.$ !/AVnàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ‡h`dtÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÈ~aRC6+    /AUl©þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“mcfuÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö¶s]N?2%  -@Ul©þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸqfhvÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí£iWG7)  *=RŸèýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºvjkxÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿß_K9)  $7MqýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂ{onzÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþËqK7% .D^ÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍ€tr}Èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù«D/  $8PŒüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì‰yvÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúQ9$ *@\áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸ}z‚Åÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû\@* /G†ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì‚|„ÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûeF.2LÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ~…ÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿülJ1 3Mœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ䆅ÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüpM22Lÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶„~„¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüsN4 /H}ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥|ƒ¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüvP5  +B_âÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú”}y€¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýxP5   &;VÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙwt}¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýyQ5    3Kwðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàooyµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý{Q6! +@[˜þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ«egt²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþR6!  "6Nlìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéƒ`Z_o®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþƒR6! ,C]¤õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÞÓt\QNWi©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ„S7!  %9Sp¨ðüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ馋r\LCDOd¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…S7"   3Li“ëôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ±eO?8Y—þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜeI7/2BZ˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠT8# ,DdìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõnN;14BZ—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒU9# /I™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“R>35CZ—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿU9# 2L›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—T?56C[–ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘V9#  3M¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂS?46CZ•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”V:$ 2K—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæO<35CZ”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—W:$ /Gmöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±F6/3AY“ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™W:$ *A]éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍVX‘ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX;%  1Ivõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö©waO>/% *=WÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX;% )>X“úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæt^K:,!';VŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœX;%  !3Kg•íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ|aJ8)&:UÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžX;% *?ZxÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊmR;) $9UŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸY<%  "5NkÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­bG0 #9U‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡Y<% -D`Ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’Z>)  #8TŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£Y<%  &&  0Fyÿÿÿëðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµ^A)  "7S†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬[>' '9OæÿÿôÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸY=&  "7S…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­\>'  ,=yîø«¤ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†U9#  "7S„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯\?'   -[­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËaC+  !6QyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁ]@(  #8S†þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·]?(  !6QxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁZ='  2Lkíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤Y<%   5QwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀU:$ -EbÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠT8#   5Pvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿N5!  '>Z§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿývP5    5PvþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµE. "7RyüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðlL2  5PvþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÀX:' 1JgÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞgH/  5Puýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ŸYB. *B_µÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËbD+  5Puýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿå„ZE3#  %:V™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµ]@(   5PuüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÉrXE3%   4Mpîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ X<%   4Otüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø§gTB2$ -Fc¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆT8" 4OtûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæˆaN=."  (?[˜ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿútO4  4OsûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊs[H8*  #9TtåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéjK14Osúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø§hTC3&  3NmíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓeF-4Osùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç‰aN=/" /Hfàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½`B* 4NrùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÊt[I8*  +CaÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿª[>' 4Nrøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø¨hTC3&  '>[§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’V:$ 3Nrøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç‰aO>/#  $:V˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýyQ6! 3Nq÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþËt[I8*  !6QùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñnM23Nq÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø©hUC3& 2LkÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞgH/3NpöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçŠaO>/# /Ig·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËbD,3MpõÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÌu[I8* ,EdÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹]@) 3MpõÿÿÿÿÿÿÿÿÿÿÿÿÿùªiUC4&  *C`ÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšY<& 2Moôÿÿÿÿÿÿÿÿÿÿÿÿè‹bO>/#  (@^¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…T8# 2LnóÿÿÿÿÿÿÿÿÿÿÿÍu[I9+  '>[ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùuP5  1LmóÿÿÿÿÿÿÿÿÿúªhUC4&  $;WÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîkK11Jjòÿÿÿÿÿÿÿÿé‹bO>/#  "8S«ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâfG./HfñÿÿÿÿÿÿÿÍu[I9+  4N|úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅaC+ -D`ïÿÿÿÿÿùªhUC4' 0IhÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ«\?( *?Xîÿÿÿÿè‰`O>/# ,Db½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿX;%  %8NìÿÿþÈnXH8+  (?\©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…S7"   0Béÿ÷œYM@3&  $:UžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøsN3  &5çàkHA7,"   4NÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáfH/ (™?52-% .FeãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍ_C+  #$!  (>Xµÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹V<&    "4L‡ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢K4!  +>^úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚?+ !1JÖßáãäååååååååååååååååäãáßÚS2" crispy-doom-crispy-doom-5.6.4/pkg/osx/Resources/app.png000066400000000000000000000107651360717211000230770ustar00rootroot00000000000000‰PNG  IHDR00Wù‡sRGB®ÎébKGDÿÿÿ ½§“ pHYsÍÓ3¼tIMEÙ 5 5• ‚uIDAThÞÕšygyÆ}wÏ=³³³§vu®nÉ–…%aclQ6C( *ØE€Jll“@ª(R.ˆ ÊT1˜ÃÈØÄ6‡/˧lK–´^­®=%íj¯Ù™{º{º;hVš]–"•tU×þѳÓïó>Ïû¼ï÷}ÿÏ/á-þ (µ¿.`×n¯öïÿ*Ѐ€”€2P­©Ôn»ðÏHz“Áë@XyËÍ7ýÕ#÷|÷ÞDSÓÒÿzþ¥2Ðôî뮽dhhØÂ5 B àŸ Ä›a@5?ÿÁíw4ån2-›­¹ Y÷ã¹§âëí鱆Ɠ¯ìÚ{`ÏÏ~yÿcÀ1`ºÆˆW{§P'5€wÿóÍßܶ$úîŸÿúA¬ªKÔ¯ hlDólV¬\Åòu—âV L•¼c×|ô ŸŽS5IÉ5v¤;fÔþ,šÓ} °ö–OÝðñcýG‚›Þõ>yëWØ|ÅvŠ®JEÐøègn#ð3<0ÈïÀ(NFÃ]—WºôŽ×êÄ|ŸîH\¦KRÛhÙÔk̺oUfÒE¸¿üš~óË·lY³xíê-×Ðѵ–ôÀ~ŠS'X±a3~Ñ"Ù×M´¹Dë"Ö\~N%Ïåk–nZuÙ¶Ö§Ÿ}¡·–íÀµ‰†]ÝÔôÝË"ÕÝÙlÚt=¡ü›"] x¥VŒÀú¿ëÛ·^½vÑ•‰Õ[˜xý¦¾†#é´m¼AÙ·“£ö1Ý÷ñæv„ªÅ‰ñI^zæ)Þ³iéRµ¹+ûÚ¾îQÀXömþ}.xÕ²ºôæÎØö%†œØ7[HÕØöjL]T}H(ذ얛>w×>õ¡¯.§WÊ í éÅ*dÈk $ÂZ¸Å¦±s! žÛw”hc+†`oˆò­ûþ@ÜÙ¸8¾îÁ{wâê€ÍŒÜÜS„§³’Õ|«ÿ¾³á] År±–À‹"GóA`Én¾éÆonúbSÔg$§SdÓÓŒŽîf×®]üü?Ÿä†Oþv)c—)'ORš:ÎoŸxž•Í!ü¡(‚]¢IÑ”Õíïû`ôϼԿ* wæ…Ðe'­S2l â³9)°%Ø|ckôÊ©r%“´ìj-·î~CbMó‹€K~ý­[îÐü!žxü1‚¢I!_äåÝ»‘t»¾ÿ}dÃ]œex×ãìÚùK:Û™ÍÍr|tœåM,ïl'ÒÜ]Γž8{jßÀ«+üF³)…/±„Ó.è=%QÞ[Rb7´„ß±=¼ä`¶0Uv].TÒ9‚o6ìüÍ=wT§…Õ =j0x|„ÁÑ  ÚÚqíÛ7Sš$9ØÃó?ÂÞ×{è*®UÆvž;2Åò ÞІWL‘Xq)ÖdŸþðžcÏ/óé1G _rÜβñŠ » ¢:j«m7uF·¯ êíû3¹i܅Qª Þ¨¹Íú§ñý»šu»ÝÌ$É¥“¼úúaÊ™$ï¹v;ë7n¦ký&Ò³Y{z'{ôcH.³JɪbÚ.Š"ñX÷I6,j@õ,–mÜF:•¤©})÷þþù—:ýzXÖ#— •ñ|ÚžužÉIºª]7-o|WH8œ-&k#‹U‚XÇDXúêSßÕ¢;í‚ "ÐØÖÉÚ¥ml»úZ^é$™JñÂ3OÓ¨‡.aV|~×u9<6Éé<½'2芢ˆeÛÌ õ°xõ%TŠy€€…')ÒÅõÐ]YAüҰܨø[>ÿ½-ëîšj}iž„4 åίþÃWÖ7oK`bl ×uyù…LŒójw/rµÀÔè |n‰Tf–’íÔUܪÃ`2G2k’3-¦sÞ¾"Á_^w“™"UÛdìÐÞùi¯ïÞ“‰†ãkûË’(J"¢(r}Ô¡*Jä< Qç݈"‡J¢tE\Tž=9ñG ]kŒÈõÝ6 «Žc¡~*ÓÇ9rx‚'{ÇYÜè§3D—$²e‹Ãã!UƧk=9CÔ¯‘Î[˜¶Ã;×´Ñà—9:x‚ˆP¢dGPÒ$ã‚n»‰;dª§òéL¯5*´3M$ã¡B˜ñêuÍU­ U$ÀW_»r XTM{fxI3x¾{×séŸeÏÐ4·ýÅZlÇÁó–BSEÚc:â~&³:bÉÉq²% E)Ù#éû¥È—mìªK¹ç»qÃ}¿Ú1m»ž£ `¾„dU@•Œyƒƒ|ŠÎ@4Å3;­‰~þ´;‹ä9¸ž‡®H¬íH025Ãö5-äÍ*0”ÌÔdÆgK芈낦H˜¶ÃÄl‰Eñ“9b »ê°mUK×}°³ìºÎBDwM@UŒy>*éêY;õ ÒsàÐsmù™«£’Å®‰YâÍ|~ó&~ù§—hùèh ñ)TlQÐMq= ã3%öKÑÕ¢-æcU[”'ºÓÖñR¥Ý@ÑòœŠfHóÈU ¿¾€¶°ä…­a ˜=:–Þ[Å»úðÀI¶­jGÅÅhŒpûß¼Oí"3™MÚc>ºŠOq\©û†R¤ &oïŠ3«ÎWX’²|˵¯œL=üØÝ?~,Ï«¤'&«NÏáÓšp—äæ×€Á9ôè<™Ë›ÎÚ¦Y óÃ{~úÌí·ýí׬µ¶MÎ)[‘Ê’®G¶d!Óù Ý#3H4…é|… +Cèa®V¯H(¢ÐÖ½È=wÿâ^à5`ˆŠ‚Pµ§GªÖà‘3ZâøÑ3ÙöŠØƒó¿j“}! ¹@HÉŠ’;8”&W±)”mºÃþ íq?é¼Å¦Åq4E"W¶,¥ð«cù"é‚ÅÒˆ¡ äÌ ÁÈÚ•»¡ÿ0^ë5~G *iÒ¼@ |ú™é^rÏöU$ ç ˆúqÖ«íݤÿéŽý8âט ­8îÄJÏgð'4öžÈô) gx%™â¥±)\`ÃêKÛ .]ħI4ÅuÚ>Z‰½R ¼<·DtÁÕù“´+zøSVê3dýlž,b¨’x¾>0WÈy`Ô×ÖÕ¯ŸÑ6­"Ë"Ét™±¬É@yšÎV ×%5«P2Æ&+ý2¢¡ LÅrQd±d…æ€hÕS=ýO5t­.ã*ø ùtj[˜÷üTQE¢ìÌõo!(£Ÿûίþظò+ïÿF‹6š˜Lš\³¥}½9^Ù?Ck“Ž, ,j2He,V. P1â…PP#Ö(”ª<Õ=´€ç«‡4"aíÌè ‹ø ¯–\M‘æ=Pý*Ê00WÌY`8Rcßl~8qÙúûåX½"Hs£Š$ ´%tÂ2Ó&>C¤\ñð°pªUµ¹ǃûëx–ëxFP!RçÐåÓĪH$¬âÕUŠâMå707Í“ƒ#¥ý×oò½ãðP‘rÅ&[° ùZ›TöçXß"“wX¿2B8DQf2EjŒšõ» .‚«û4"¡3Öt©Æ€N†t®B88ŸE‘ÐdEª›‡<ù«9(þô¾ö|øŠm´7ûp\Èæm¶o‰sr²‚, lXÝŒ,V™Hæio6˜™-3=S!à—èo«­œ¬º]϶OÓD/>À¦Ow)Çé믒JKDÂók@SDtMÏg£çb¢ dG“Z%ŸÍê!¿D±TEÓD±Œ‡@ßÐëW†èl ÑÚÚŽ,Yø ϳ-¶_XÀsß­já:dÉ&“ž`蘂ëÊ„Cg$Ë"†&9s ú7àÕÌ–¥µ{º®züù$‹Û}ŒNVˆÅb4Ç Ç®¸@•\f„jAÁ¬TP´ÅvM>Ný÷Ú¶ëj²D¤® …|^ 8w0ª¦ÐÜ)LgºŽÑ  öòlo_º»s“wÕû®iÂq¦³U\+ÏËû*dó6[7µ2t"ÇÆÕO"äW°¬"G’Øõ[ §mÔqFN¦YÔbÐ}8s–…X–㪊äÕ»Ðù®pcÜõz>üɇÿŲœ£@òÍ0àÖÈNµŽg§{[7¬Š²"f_Ï –åÑÔè£X–I4ÆH¥ËHJÅ"’ä×;ÚŽððUœçBgíyÊJ4‘{àÉáß|ç'¯ý®ô”/¶æ2fy¤}ð’¥éÖÃ)*#9º–ʼn54R©˜X¦…H‰‘±4ÍŠªqððñsZh¨ás3 (º›wƒSŸýÚswîéx¹6fj™w/Ö…ê[ѶŠ"Cç¢BUöL2™Ìá8.¡H”h¬…•]~ŸA6W¦"¯ƒCõ:ßÓe ÿ9( ~óàpåÀGo{ðŽò)ÉLÔÆ›ê¹¾çbØ@ñæ/ÿëƒûþxãuûR*ÊDã´4(ŠN¹”¡0{œ À*å˜ÍTÕ%ç³ÐS‘üjÝë‘dÅŸ»ïч¾ýãÝ;j’IÕMóºsq¥R>K{kAprS˜e•?<=É5[0½ŠW&oÅI4–™Mb]À¼C‹ª'»£³ÚÔg¾öÌ{zNKfö\ò{+æNâËŽcbš6…B…XS;†nsÍQüjŽc£'i\¡ZL2S°™É:çàUA8XÖ6Ìžárï 7?zGÙr޼‘dÞ €¹;óxÒÈ7‡ªÁ@ЇàféÈ`W=–vDè\ÔLÉò—–h‚ùÞ‡êNï2ZPÅKWC3÷=ÚÿÈí?Úý@Ë\P2o€ ”òvÛFïÐG¦éhõ³bY'A¿ÌÄdC-âØ.Vy†ÚêÎ Select a game: Command line arguments: WAD wad WAD wad WAD wad WAD wad WAD wad WAD wad WAD wad WAD wad WAD wad WAD wad WAD wad WAD wad crispy-doom-crispy-doom-5.6.4/pkg/osx/Resources/launcher.nib/keyedobjects.nib000066400000000000000000000745461360717211000273350ustar00rootroot00000000000000bplist00Ô ` aX$versionX$objectsY$archiverT$top† ¯”#$*/QRSTUam|}˜™œ¦§¨±¹½ÁÅÈÍÎÓØÞèëïð056<=>AEFGJKORW[knz{|}~€‚ƒ‡ˆ’–—𦧝°¹ºÅÊËÐÑÖãæçèó÷ùû    #$,-8;<EFGIJKLMPƒ‹Ž”™œž£¦§¨­®³´¹ÅËÐÑÒÞßäåæèìðñö#.34;<ALQRYZ_jopwx}ˆŽ•–›¦«¬³´¹ÄÉÊÑÒ×áâãíîóôþÿ&'/0:;CDNOWXbcklvw€‹Ž—˜£¨©°±¶ÁÆÇÎÏÔßäåìíòý  '12:;EFNOYZbcdefklqrwx}~ƒ„‰Š•–›œ¡¢§¨­®²¹¿ÀÁÅÉÐÑÒÓØßäåæëòóôõú  !"'./04;<=BCHINOTUY`defgkrstuy€‚ƒ‡Ž”›œž¢©ª«¬°·¸¹º¿ÆÇÈÉÎÕÚÛÜÝâéêëð÷øùþ  !&-./«®¯´µ¼ÇÈÉÙäí÷øý *3=>ENOYZclmnx}~ˆŽ’–šŸ©ª®¯³º»¿ÀÊÏÐ×ØÝ Y L® ³ ´£P µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ÿ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W Z ]U$nullÛ VNSRoot]NSConnections\NSOidsValues_NSObjectsValues]NSObjectsKeys_NSAccessibilityOidsValues_NSVisibleWindows_NSAccessibilityOidsKeys_NSAccessibilityConnectorsV$classZNSOidsKeys€€yæä¨’€’‘“åÒ !"[NSClassName€€]NSApplicationÒ%&'(Z$classnameX$classes^NSCustomObject¢')XNSObjectÒ+,.ZNS.objects¡-€€Gß0123456789:;<=>?@ABCDEFGGIJKLMNAP\NSWindowRect_NSUserInterfaceItemIdentifier]NSWindowTitleYNSMaxSize\NSWindowViewYNSMinSize_NSWindowIsRestorable_NSMinFullScreenContentSize_NSMaxFullScreenContentSize\NSScreenRect_NSWindowContentMinSize_NSWindowBackingYNSWTFlags]NSWindowClass[NSViewClass_NSWindowStyleMask€€€€v€ €u €w€w€t€x€ `x€ €_{{350, 488}, {532, 195}}_(Package Name) LauncherXNSWindow^{243.529, 107}ÖVWXYZA\]A_`_NSNextResponderZNSSubviews[NSFrameSize]NSNibTouchBarXNSvFlags€€ €r€€sÒ+bl©cdefghijk€ €"€;€N€R€Z€b€h€m€$ÚnopVqZYrstuvwDD`ALFVNSCellWNSFrame_NSAllowsLogicalLayoutDirection[NSSuperview_%NSTextFieldAlignmentRectInsetsVersionYNSEnabled€€€!€ € € _{{18, 173}, {360, 11}}Ù~€‚ƒ„…†cˆ‰ŠFŒŽ[NSCellFlags]NSControlViewYNSSupport_NSBackgroundColor_NSCharacterPickerEnabled[NSTextColorZNSContents\NSCellFlags2€ € €€ €€@_Select a game: Ô‘’“”•–—VNSSizeXNSfFlagsVNSName#@"€€_.AppleSystemUIFontÒ%&š›VNSFont¢š)ÕžŸ ¡¢£¤¥[NSColorName\NSColorSpace]NSCatalogNameWNSColor€€€€VSystem\controlColorÕ©ªž«¬­®¯¡WNSWhite\NSComponents_NSCustomColorSpaceL0.602715373N0.6666666667 1€€Ô²³´µ¶·¸TNSIDUNSICCWNSModel €€Òº»¼WNS.dataOhhapplmntrGRAYXYZ Ü.acspAPPLnoneöÖÓ-appldescÀydscm<ècprt $#wtpt HkTRC \ descGeneric Gray Gamma 2.2 Profilemluc skSK.„daDK8²caES8êviVN@"ptBRJbukUA,¬frFU>ØhuHU4zhTWJnbNO:hcsCZ(¢heIL$ÊitITNîroRO*¾plPLJüruRU:FenUS<€arEG,¼Vaeobecná sivá gama 2,2Generisk grå 2,2 gammaprofilGamma de grisos genèrica 2.2C¥u hình Màu xám Chung Gamma 2.2Perfil Genérico da Gama de Cinzas 2,2030;L=0 Gray-30<0 2.2Profil générique gris gamma 2,2Általános szürke gamma 2.2u(pp–ŽQI^¦ 2.2 ‚r_icÏðGenerisk grå gamma 2,2-profilObecná aedá gama 2.2ÒÐÞÔ ÐäÕè ÛÜÜÙ 2.2Profilo grigio generico della gamma 2,2Gama gri generic 2,2Allgemeines Graustufen-Profil Gamma 2,2Ç|¼ ÖŒÀÉ ¬¹È 2.2 Õ¸\Ó Ç|fnpp^¦|ûep 2.2 cÏðe‡NöN‚,0°0ì0¤0¬0ó0Þ 2.2 0×0í0Õ0¡0¤0듵½¹ºÌ “ºÁ¹ “¬¼¼± 2.2Perfil genérico de cinzentos da Gamma 2,2Algemeen grijs gamma 2,2-profielPerfil genérico de gamma de grises 2,2#1*5A!!2@#"L1H'D 2.2Genel Gri Gama 2,2Yleinen harmaan gamma 2,2 -profiiliGeneri ki Gray Gamma 2.2 profilUniwersalny profil szaro[ci gamma 2,21I0O A5@0O 30<<0 2,2-?@>D8;LGeneric Gray Gamma 2.2 Profile:'E' 2.2 DHF 1E'/J 9'EtextCopyright Apple Inc., 2012XYZ óQÌcurv #(-27;@EJOTY^chmrw|†‹•šŸ¤©®²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ€Ò%&¾¿]NSMutableData£¾À)VNSDataÒ%&ÂÃ\NSColorSpace¢Ä)\NSColorSpaceÒ%&ÆÇWNSColor¢Æ)ÕžŸ ¡Ê£¤Ì€€€€_controlTextColorÕ©ªž«ÏЮ¯¡B0C0 1€€Ò%&ÔÕ_NSTextFieldCell¤ÔÖ×)\NSActionCellVNSCellÒ%&ÙÚ[NSTextField¥ÙÛÜÝ)YNSControlVNSView[NSResponderÚnopWVqZYsßàvâãDD`AF€&€%€:€#€ € € Ò+él €$Ò%&ìí^NSMutableArray£ìî)WNSArray_{{18, 141}, {250, 26}}ßñò~óôõö÷…øù€úûüýþÿ®dF   FF_NSPeriodicInterval_NSPreferredEdgeZNSMenuItem_NSAlternateContents]NSButtonFlags_NSKeyEquivalent_NSAlternateImage_NSMenuItemRespectAlignmentVNSMenu_NSPeriodicDelay_NSUsesItemFromMenu]NSAltersState_NSArrowPosition^NSButtonFlags2Kÿÿÿÿ„@@€+€)‚@€*€(€" €,€' €9Ô‘’“–—#@*€€Ô‘’“–—€€PPÛù !"#$%& ()*+ß./]NSMnemonicLoc_NSKeyEquivModMaskXNSActionYNSOnImageZNSKeyEquivXNSTargetWNSTitleWNSState\NSMixedImageÿÿÿ€,€4€.€5€)€&€-€2Ó#1234[NSMenuItems€6€7€8YGame nameÓ789:;[NSClassName^NSResourceName€/€1€0WNSImage_NSMenuCheckmarkÒ%&?@_NSCustomResource¢?)Ó789:D€/€1€3_NSMenuMixedState__popUpItemAction:Ò%&HIZNSMenuItem¢H)ZOtherViewsÒ+Ll¡€+€$Ò%&PQVNSMenu¢P)Ò%&ST_NSPopUpButtonCell¦SUVÖ×)^NSMenuItemCell\NSButtonCellÒ%&XY]NSPopUpButton¦XZÛÜÝ)XNSButtonÝsopnqZ\V]W^YF`vbD`FDfAhiAZNSEditable_NSPlaceholderImage[NSDragTypes €H€I€ € €M€€<€=€Ò+ll €$Ò+o.©pqrstuvwx€>€?€@€A€B€C€D€E€F€G_/com.apple.pasteboard.promised-file-content-type_Apple PDF pasteboard type_#com.apple.NSFilePromiseItemMetaData_Apple PICT pasteboard type_1NeXT Encapsulated PostScript v1.2 pasteboard type_Idyn.ah62d4rv4gu8yc6durvwwa3xmrvw1gkdusm1044pxqyuha2pxsvw0e55bsmwca7d3sbwu_NeXT TIFF v4.0 pasteboard type_Apple PNG pasteboard type_NSFilenamesPboardTypeÒ%&„…\NSMutableSet£„†)UNSSet_{{384, 47}, {128, 128}}Ø~‰Š‹Œ„…·ŽŽF‘WNSAlignWNSStyleZNSAnimatesWNSScale€L €JÓ789:•€/€1€KW128x128Ò%&˜™[NSImageCell£˜×)Ò%&›œ[NSImageView¥›ÛÜÝ)ÚnopVqZYrsžŸvwDD`ALF€P€O€!€ € € _{{18, 125}, {350, 11}}Ù~€‚ƒ„…†fˆ‰ŠFŒ®Ž€N€ €€ €€Q_Command line arguments: ÚnopVqZYrs±²vwDD`ALF€T€S€!€ € € _{{20, 49}, {346, 68}}Ú~€»‚„ƒ…¼gˆ¿FÁFŒŽ_NSDrawsBackgroundÿÿÿÿ” €R€ €U €W €)€Ô‘’“ÆÇÈ—#@&€V€]Menlo-RegularÕžŸ ¡Í£¤Ï€€X€€Y_textBackgroundColorÕ©ªž«ÒÓ®¯¡B1C1 1€€Ûnop×WVqZYsØÙvÛÜÝDDàAF_NSHuggingPriority€^€\€]€a€[€ €  € Ò+äl €$_{{372, 46}, {25, 25}}Z{750, 750}Ýñ~õôö…÷„€úþÿ†éìhðñ òÿÿÿÿ†‚@€)€*€(€Z€)€`€_¡Ô‘’“ô–—€€Ò%&Vø¤VÖ×)Ò%&Zú¥ZÛÜÝ)Ûnop×WVqZYsüývÿÜDD`AF€f€d€e€a€c€ € € Ò+l €$_{{273, 143}, {93, 23}}Z{250, 750}Ýñ~õôö…÷„€úþÿ†éìiðñ €)€*€(€b€g€`€_ \Configure...Ûnop×WVqZYsvÿÜDD`AF€k€j€e€a€i€ € € Ò+!l €$_{{14, 13}, {149, 32}}Ýñ~õôö…÷„€úþÿ†éìj)ðñ €)€*€(€h€l€`€__Run Setup Tool...Ûnop×WVqZYs./vÿÜ3DD`AF€p€o€e€a€n€ € € Ò+9l €$_{{389, 13}, {129, 32}}Ýñ~õôö…÷„€úþÿ=éìkBðñ ÿÿÿÿ„€)€*€(€m€q€`€_[Launch GameZ{532, 195}Ò%&ÜH£ÜÝ)_{{0, 0}, {1440, 877}}^{243.529, 129}_ {10000000000000, 10000000000000}V{0, 0}Ò%&NO_NSWindowTemplate¢N)Ò+Ql¯0RSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€€z€€ƒ€‡€‰€‹€š!#%')+-/5:?DINRVZ\^`bhmrv{€…Š”˜œ ¢¤€$Ô„…†ˆ‰ŠXNSSource]NSDestinationWNSLabel€€{€}€~Ò Œ"€|€]AppControllerXdelegateÒ%&‘’_NSNibOutletConnector£‘“)^NSNibConnectorÔ„…†•g—Š€€€R€‚€~Ò š"€€_LauncherManager_commandLineArgumentsÔ„…†• ¡Š€€€„€†€~Ò ¤"€…€^IWADController^iwadControllerÔ„…†•k«Š€€€m€ˆ€~\launchButtonÔ„…†•-±Š€€€€Š€~^launcherWindowÔ„…† ¶·Š€„€Œ€™€~ÛnopVq^YZsº»¼v¾¿¿ÁAàFF_ NSControlAllowsExpansionToolTips€‘€€˜€€€Ž€ ÖVWXYZAÇÈA_`€€Ÿ€€sÒ+Ì.¢x΀F€€G_Apple URL pasteboard type_{{225, 136}, {228, 26}}Ù~Ó€ÔÕÖ…×¶Ù¶ñLÜÝ·ZNSDelegate[NSPathStyle_NSPlaceholderString^NSAllowedTypes €Œ€—€Œ€_€’€“_Click to select chex.wadÒ+àã¢á—€•€–SWADSwadÒ%&îç¢î)Ò%&éêZNSPathCell¤ëÖ×)ZNSPathCellÒ%&íî]NSPathControl¥ïÛÜÝ)]NSPathControlTchexÔ„…† óôŠ€„€›€~ß0123456789:;<=>?÷AùC¿üFGGIJLMNAP€œ€€€v€ €w€w€t€x€ž€ €_{{377, 417}, {470, 376}}_IWAD configurationZ{213, 107}Ò+ l¯    ¶ !€ €¦€¬€²€¸€¾€Œ€Ä€Ë€Ï€Ó€×€Û€ß€ã€ç€ì€ò€ø€þ €$ÛnopVq^YZsº$%v¾¿¿*AàFF€£€¢€˜€€€¡€ Ò+/.¢x΀F€€G_{{225, 304}, {228, 26}}Ù~Ó€ÔÕÖ…× Ù ñL9:·€ €—€ €_€¤€¥_Click to select doom2.wadÒ+=ã¢á—€•€–ÛnopVq^YZsºBCv¾¿¿HAàFF€©€¨€˜€€€§€ Ò+M.¢x΀F€€G_{{225, 276}, {228, 26}}Ù~Ó€ÔÕÖ…× Ù ñLWX·€¦€—€¦€_€ª€«_Click to select tnt.wadÒ+[ã¢á—€•€–ÛnopVq^YZsº`av¾¿¿fAàFF€¯€®€˜€€€­€ Ò+k.¢x΀F€€G_{{225, 248}, {228, 26}}Ù~Ó€ÔÕÖ…× Ù ñLuv·€¬€—€¬€_€°€±_Click to select plutonia.wadÒ+yã¢á—€•€–ÛnopVq^YZsº~v¾¿¿„AàFF€µ€´€˜€€€³€ Ò+‰.¢x΀F€€G_{{225, 220}, {228, 26}}Ù~Ó€ÔÕÖ…× Ù ñL“”·€²€—€²€_€¶€·_Click to select heretic.wadÒ+—ã¢á—€•€–ÛnopVq^YZsºœv¾¿¿¢AàFF€»€º€˜€€€¹€ Ò+§.¢x΀F€€G_{{225, 192}, {228, 26}}Ù~Ó€ÔÕÖ…×ÙñL±²·€¸€—€¸€_€¼€½_Click to select hexen.wadÒ+µã¢á—€•€–ÛnopVq^YZsºº»v¾¿¿ÀAàFF€Á€À€˜€€€¿€ Ò+Å.¢x΀F€€G_{{225, 164}, {228, 26}}Ù~Ó€ÔÕÖ…×ÙñLÏз€¾€—€¾€_€Â€Ã_Click to select strife1.wadÒ+Óã¢á—€•€–Ûnop×VqZYrsØÙvÛw¿¿àALF€Ç€Å€Æ€!€€€ _{{18, 252}, {204, 17}}Z{251, 750}Ù~€‚ƒ„…äˆñŠFêëì@€Ä€ €_€ €É€È@_ Final Doom: Plutonia Experiment:ÕžŸ ¡ð£¤Ì€€Ê€€ZlabelColorÛnop×VqZYrsõövÛw¿¿àALF€Í€Ì€Æ€!€€€ _{{178, 336}, {44, 17}}Ù~€‚ƒ„…äˆñŠFêì€Ë€ €_€ €É€ÎUDoom:Ûnop×VqZYrs  vÛw¿¿àALF€Ñ€Ð€Æ€!€€€ _{{168, 308}, {54, 17}}Ù~€‚ƒ„…äˆñŠFêì€Ï€ €_€ €É€ÒXDoom II:Ûnop×VqZYrsvÛw¿¿àALF€Õ€Ô€Æ€!€€€ _{{55, 280}, {167, 17}}Ù~€‚ƒ„…äˆñŠFê.ì€Ó€ €_€ €É€Ö_Final Doom: TNT: Evilution:Ûnop×VqZYrs12vÛw¿¿àALF€Ù€Ø€Æ€!€€€ _{{170, 224}, {52, 17}}Ù~€‚ƒ„…äˆñŠFêBì€×€ €_€ €É€ÚXHeretic:Ûnop×VqZYrsEFvÛw¿¿àALF€Ý€Ü€Æ€!€€€ _{{176, 196}, {46, 17}}Ù~€‚ƒ„…äˆñŠFêVì€Û€ €_€ €É€ÞVHexen:Ûnop×VqZYrsYZvÛw¿¿àALF€á€à€Æ€!€€€ _{{181, 168}, {41, 17}}Ù~€‚ƒ„…äˆñŠFêjì€ß€ €_€ €É€âWStrife:Ûnop×VqZYrsmnvÛw¿¿àALF€å€ä€Æ€!€€€ _{{143, 140}, {79, 17}}Ù~€‚ƒ„…äˆñŠFê~ì€ã€ €_€ €É€æ[Chex Quest:Ûnop×WVqZYs‚vÿ܆¿¿`AF€ê€é€e€a€è€€€ Ò+Œl €$_{{374, 13}, {82, 32}}Ýñ~õôö…÷„€úþÿ†éì”ðñ €)€*€(€ç€ë€`€_UCloseÛnopVq^YZsº™šv¾¿¿ŸAàFF€ï€î€˜€€€í€ Ò+¤.¢x΀F€€G_{{225, 332}, {228, 26}}Ù~Ó€ÔÕÖ…×ÙñL®¯·€ì€—€ì€_€ð€ñ_Click to select doom.wadÒ+²ã¢á—€•€–ÛnopVq^YZsº·¸v¾¿¿½AàFF€õ€ô€˜€€€ó€ Ò+Â.¢x΀F€€G_{{225, 52}, {228, 26}}Ù~Ó€ÔÕÖ…×ÙñLÌÍ·€ò€—€ò€_€ö€÷_Click to select freedm.wadÒ+Ðã¢á—€•€–ÛnopVq^YZsºÕÖv¾¿¿ÛAàFF€û€ú€˜€€€ù€ Ò+à.¢x΀F€€G_{{225, 80}, {228, 26}}Ù~Ó€ÔÕÖ…×ÙñLêë·€ø€—€ø€_€ü€ý_Click to select freedoom2.wadÒ+îã¢á—€•€–ÛnopVq^YZsºóôv¾¿¿ùAàFF€˜€€€ÿ€ Ò+þ.¢x΀F€€G_{{225, 108}, {228, 26}}Ù~Ó€ÔÕÖ…×ÙñL ·€þ€—€þ€__Click to select freedoom1.wadÒ+ ã¢á—€•€–Ûnop×WVqZYsvÛÜ¿¿àAF€]€a€€€ Ò+l €$_{{18, 16}, {25, 25}}Ýñ~õôö…÷„€úþÿ†éìðñ ò€)€*€(€)€`€_Ûnop×VqZYrs()vÛw¿¿àALF  €Æ€!€€€ _{{164, 56}, {56, 17}}Ù~€‚ƒ„…äˆñŠFê9ì€ €_€ €É WFreeDM:Ûnop×VqZYrs<=vÛw¿¿àALF €Æ€!€€€ _{{99, 112}, {123, 17}}Ù~€‚ƒ„…ä ˆñŠFêMì € €_€ €É_Freedoom: Phase 1:Ûnop×VqZYrsPQvÛw¿¿àALF€Æ€!€€€ _{{97, 84}, {125, 17}}Ù~€‚ƒ„…ä!ˆñŠFêaì€ €_€ €É_Freedoom: Phase 2:Z{470, 376}Z{213, 129}\configWindowÔ„…† iŠ€„€ì€~Udoom1Ô„…†  oŠ€„€ €~Udoom2Ô„…† uŠ€„€ò€~VfreedmÔ„…† {Š€„€þ€~Yfreedoom1Ô„…† Š€„€ø €~Yfreedoom2Ô„…†  ‡Š€„€²"€~WhereticÔ„…† Š€„€¸$€~UhexenÔ„…† d“Š€„€"&€~\iwadSelectorÔ„…†  ™Š€„€¬(€~XplutoniaÔ„…† ŸŠ€„€¾*€~VstrifeÔ„…†  ¥Š€„€¦,€~StntÔ„…†ˆ•«Š€{€€.€~_launcherManagerÓ„†¯°±034Øù !#%&³(+*·/1€5€.€)2€2Ô1#º4¼½¾VNSName€8ÂÁÄ_Bring All to Front_arrangeInFront:Ò%&ÂÃ_NSNibControlConnector£ÂÄ)^NSNibConnectorÓ„†ÆÇ±694Øù !#%&³(+*ÍÎ/1€5€.87€2XMinimizeQm_performMiniaturize:Ô„…†ÔÖ±;€>4×ù !#%+Ú&*Ý/€5<€.€)=€2Ô1#º4áâã€8±¯»XAbout..._orderFrontStandardAboutPanel:Ô„…†ç é±@€„C4Øù !#%&Ú(+*ïð/<€5€.BA€2_IWAD configuration...Q,_openConfigWindow:Ô„…†öø±E€H4Øù !#%&Ú(+*þÿ/<€5€.GF€2THideQhUhide:Ô„…†±J€M4Øù !#%&Ú(+* /<€5€.LK€2TQuitQqZterminate:Ô„…†±O€Q4Øù !#%&Ú+*þ/<€5€.GP€2[Hide Others_hideOtherApplications:Ô„…†#%±S€U4Øù !#%&Ú(+*,/<€5€.€)T€2XShow All_unhideAllApplications:Ó„†12±WY4Øù !#%&³(+*9/1€5€.€)X€2TZoom\performZoom:Ô„…†i @±€b€„[4_openConfigWindow:Ô„…†k•F±€m€€]4Wlaunch:Ô„…†j•L±€h€€_4YrunSetup:Ô„…† R±€ç€„a4_closeConfigWindow:Ó„†VW±cg4Øù !#%&Z(+*]^/d€5€.fe€2Ó#1ab4½¾€8SCutQxTcut:Ó„†hi±il4Øù !#%&Z(+*op/d€5€.kj€2TCopyQcUcopy:Ó„†vw±nq4Øù !#%&Z(+*}~/d€5€.po€2UPasteQvVpaste:Ó„†„…±su4Øù !#%&Z(+*Œ/d€5€.€)t€2VDeleteWdelete:Ó„†‘’±wz4Øù !#%&Z(+*˜™/d€5€.yx€2TUndoQzUundo:Ó„†Ÿ ±|4Øù !#%&Z(+*¦§/d€5€.~}€2ZSelect AllQaZselectAll:Ó„†­®±„4Øù !#%&Z(+*´µ/d€5€.ƒ‚€2TRedoQZUredo:Ô„…†»•½±†€€‰4Øù !#%&Ú(+*ÃÄ/<€5€.ˆ‡€2_Command Prompt...Qt]openTerminal:Ô„…†Ê•̱‹€€4Øù !#%&Ï(+*ÒÓ/Œ€5€.Ž€2Ô1#º4×ØÙ€8ÇÆÉ\IntroductionQ?[openREADME:Ô„…†Þ•౑€€“4×ù !#%+Ï&*ç/€5Œ€.€)’€2\Set up guide\openINSTALL:Ô„…†ì•€€—4×ù !#%+Ï&*õ/€5Œ€.€)–€2_Command line reference\openCMDLINE:Ô„…†ú•ü±™€€›4×ù !#%+Ï&*/€5Œ€.€)š€2_More documentation..._openDocumentation:Ô„…†• ±€€Ÿ4×ù !#%+Ï&*/€5Œ€.€)ž€2_Software license\openCOPYING:Ô„…†•±€€¡4\openINSTALL:Ô„…†h•±€Z€€£4\openCMDLINE:Ô„…†"•$±¥€€§4×ù !#%+Ú&*+/€5<€.€)¦€2[Autoload...]openAutoload:Ò+0ã¯y1-Dctdß ebfžg±hØiüjk.HIÚÔ»Mç"PQRSö#WYZ‘­]Vhv„Ÿc³Æ1g¯iÏÊÞìúo• ó¿ $ B ` ~œº¶»Øõ 1EYm™·Õó( <!Pˆ¦§¨©©€€ € €€"€&€,€+€;€I€N€P€R€T€Z€^€b€f€h€k€m€p«®<;†²@¥³´¶¹EOSºJ¼dw¿cins|À16WÃ0ÅŒ‹‘•™È€€€„€›€€ €£€¦€©€¬€¯€²€µ€¸€»€¾€Á€Œ€‘€Ä€Ç€Ë€Í€Ï€Ñ€Ó€Õ€×€Ù€Û€Ý€ß€á€ã€å€ç€ê€ì€ï€ò€õ€ø€û€þ  €{ËÎÞá€–Ò ¬"ª€]NSApplicationÔ1#º4±²³€8­¬ÊXMainMenuÒ+¶l¤IYci®¼ÀÅ€$Ûù !"½#%&H(+*ÚÚâÅ/YNSSubmenu«€5€.€)<<¯°€2XLauncher^submenuAction:Ò+Êl­Ô»Mç"PQSö#W;†²@¥³´¹EOSºJ€$ÚùÚ !Û#%&Ú(+F*F/]NSIsSeparator\NSIsDisabled<€5 €.€* €*€2ÚùÚ !Û#%&Ú(+F*F/<€5 €.€* €*€2Ûù !"½#%&Ú(+*RRôÅ/<€5€.€)¶¶µ°€2XServicesÔ1#º4úôü€8·µ¸Ò+þl €$__NSServicesMenuÚùÚ !Û#%&Ú(+F*F/<€5 €.€* €*€2ÚùÚ !Û#%&Ú(+F*F/<€5 €.€* €*€2\_NSAppleMenuÛù !"½#%&H(+*ZZaÅ/«€5€.€)dd½°€2TEditÒ+ l¨‘­]Vhv„Ÿw¿cins|€$ÚùÚ !Û#%&Z(+F*F/d€5 €.€* €*€2Ûù !"½#%&H(+*³³½Å/«€5€.€)11Á°€2VWindowÒ+?l¤Æ1g¯6WÃ0€$ÚùÚ !Û#%&³(+F*F/1€5 €.€* €*€2^_NSWindowsMenuÚù !"½#%H&+*ÏÏØÅ/«€5€.€)ŒŒÆ°€2THelpÒ+[l¦ÊÞìúo‹‘•™È€$Ùù Ú!Û#%&Ï+*FF/Œ€5€. €* €*€2[_NSHelpMenu[_NSMainMenuÚnpºV^YZXs§vF¾AtAàvFÎ €˜€Ì€Í Ò+y.¢x΀F€€GY{200, 20}Ù~€Ó„…צق¦„…†‡_NSPathComponentCellsË€—ÒËÓÔÏÓ‰ŠAŒWNS.base[NS.relative€ÑÐ_file://localhost/Applications/Ò%&‘UNSURL¢)Ô‘’“Æ“–— €€Ó—ž˜L¡UNSRGBO'0.8980392157 0.9254901961 0.9725490196€Ò+›l¢œÕÚ€$Ø~ €ƒ„…¡¢£‚ÁŒ§¨UNSURL@Ù×Ò€W€Ö\Macintosh HDÓ‰ŠAŒ­€ÑØ_file://localhost/Ò%&°±_NSPathComponentCell¥²ÔÖ×)_NSPathComponentCellØ~ €ƒ„…¡¢µ‚ÁŒ¹¨ÙÜÒ€W€Û\ApplicationsÓ‰ŠAŒ¾€ÑÝ_file://localhost/ApplicationsÚnpºV^YZXs©vF¾AÆAàÈFá €˜€߀à Ò+Ë.¢x΀F€€GY{228, 26}Ù~Ó€ÔÕօר٨ñLÕÖ·Þ€—Þ€_âã_Click to select freedm.wadÒ+Ùã¢á—€•€–Ò+Þã¯y-DcDdß DeDfDgDhDiDjDkHIÚÚÚÚÚÚÚQÚÚÚÚÚÚHYZZZZZZZZHc³³³³HiÏÏÏÏÏÏó¿ ¿ ¿ ¿ ¿¿¿¶¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ ¿!¦¨€€€€ € € €"€&€,€ €;€ €N€ €R€ €Z€ €b€ €h€ €m€«®<<<<<<<´<<<<<<«¼dddddddd«À1111«ÅŒŒŒŒŒŒ€€€€›€€ €€¦€€¬€€²€€¸€€¾€€Œ€€Ä€€Ë€€Ï€€Ó€€×€€Û€€ß€€ã€€ç€€ì€€ò€€ø€€þ€€€ €€€Ë€Þ€–Ò+ Z㯪1-Dctdß ebfžg±hØiüjk.HIÚÔ»Mç"PQRSö#WYZ‘­]Vhv„Ÿc³Æ1g¯iÏÊÞìúo• ó¿ $ B ` ~œº¶»Øõ 1EYm™·Õó( <!Pˆ¦§¨©RSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€€©€€ € €€"€&€,€+€;€I€N€P€R€T€Z€^€b€f€h€k€m€p«®<;†²@¥³´¶¹EOSºJ¼dw¿cins|À16WÃ0ÅŒ‹‘•™È€€€„€›€€ €£€¦€©€¬€¯€²€µ€¸€»€¾€Á€Œ€‘€Ä€Ç€Ë€Í€Ï€Ñ€Ó€Õ€×€Ù€Û€Ý€ß€á€ã€å€ç€ê€ì€ï€ò€õ€ø€û€þ  €{ËÎÞá€z€€ƒ€‡€‰€‹€š!#%')+-/5:?DINRVZ\^`bhmrv{€…Š”˜œ ¢¤€–Ò+ 㯪                    ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ 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 [ \ ] ^ _ ` 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 { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ±çèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ€–      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ªÒ+ Xl €$Ò+ [ã €–Ò%& ^ _^NSIBObjectData¢ ^)_NSKeyedArchiverÒ b c d_IB.systemFontUpdateVersion]IB.objectdata€"+5:?kqž¥³ÀÒàü)ELWY[^adgiloru~ŠŒŽœ¥°¹ÈÍÖßêíïñ8Ees}Š”«Èåò '5AUWY[]_abdfhjlnsuwy”®·Æßñü    ! # % ' ) , 5 H J L N P R T V X Z \ … Œ ” µ Á é ó õ ÷ ø ú ü þ   ? K Y c w ’ ž © ¶ » ½ ¿ Á Ã Ä Æ È Í ß ð ÷      , 5 < A V b o } … ‡ ‰ ‹ – £ ¸ À Í â ï þ     ( * , . 0 9 A­¯¸ÆÍÔÝêïü ')+-/BWZ^`bk}†“š£¯ºÄË×   %4;C\«ÀÒÝó   & C J \ q  ‘   ¢ « ­ ¯ ´ ¶ ¸ º ½ ¾ À Â Å Æ Ç É Ë Í Þ ç ê ì î ÿ!!!!!!4!B!V!_!i!t!}!…!!š!Ÿ!¡!¦!¨!ª!¬!®!°!²!´!Á!Í!Ï!Ñ!Ó!Ý!ê!ö""" " ""%"."A"F"S"U"W"Y"l"€"‰"”"™"¤"­"°"²"´"½"Ä"É"Ò"æ"ó#####<#q#|#‘##ž# #¡#£#¥#¦#¨#ª#¬#®#°#²#»#¼#¾#Ç#Ú#Ü#Þ#à#â#ä#æ#è#ê#ì#î$ $<$b$$³$ÿ% %<%T%]%j%q%w%‘%²%º%Â%Í%Õ%×%Ù%Ú%Ü%á%î%ð%ò%ô%ü&&&&!&-&8&a&c&e&f&h&j&l&n&o&ˆ&­&¯&±&³&µ&¶&¸&º&Õ&þ'''''' ' ' '$'M'a'j'l'n'p'q's't'v'x'‰'’'•'—'™'§'¼'¾'À'Â'Ä'Ú'ï'ò'ö'ø'ú('(;(=(?(@(B(D(F(H(J(M(O(P(Y(Z(\(t((´(½(¿(Á(Æ(È(Ê(Ì(Î(Ð(Ò(ã(æ(è(ê(ó(ü)))=)?)A)B)D)F)H)J)L)N)O)X)Y)[)t))´)¶)¸)º)¼)¾)À)Â)Ä)Ñ)þ****** * * ******4*i*k*m*o*q*s*u*w*‹*¸*º*¼*½*¿*Á*Ã*Å*Ç*É*Ê*Ó*Ô*Ö*ï+$+-+/+1+3+5+7+9+;+G+R+[+b+z+‰+¬+³+¼+Ï+Ô+Ý,@,B,D,F,H,J,L,N,Q,T,W,Z,],`,c,f,i,l,o,r,u,x,{,~,,„,‡,Š,,,“,–,™,œ,Ÿ,¢,¥,¨,«,®,±,´,·,º,½,À,Ã,Æ,É,Ë,Ü,å,ó,û,ý,ÿ--- ----'-0-G-N-]-n-p-r-t-v---ƒ-•-¬-½-¿-Á-Ã-Å-Î-Ð-Ò-á-ð..... ..'.).+.-./.>.O.Q.S.U.W.„.§.©.«.¬.®.°.².´.¶.·.¸.Ñ.Ó.Õ.Ø.Ú.Ü.å.ê.ì.î.ð/ /&/K/V/b/x/‡/Œ/Ž//’/”/–/˜/³/¼/Á/Ã/Å/Ç/Ë/Ï/Ø/Ý/æ/ñ/ú0000'050:0K0M0O0R0T0›00Ÿ0¡0£0¥0¨0©0«0­0¯0±0³0µ0·0Ò0ç0ò0û1.10121416181:1<1>1@1B1D1F1H1J1L1N1P1R1T1V1Y1\1_1b1d1‘1“1•1–1˜1š1œ1ž1 1¡1¢1«1°1²1´1¶1Ð1õ1÷1ù1û1ý1ÿ222&2+2-2/212^2`2b2c2e2g2i2k2m2n2o2x2}222ƒ22Â2Ä2Æ2È2Ê2Ì2Î2è2ñ2ö2ø2ú2ü3)3+3-3.3032343638393:3C3H3J3L3N3h333‘3“3•3—3™3¸3Á3Æ3È3Ê3Ì3ù3û3ý3þ444444 4 44444484]4_4a4c4e4g4i4‡44•4—4™4›4È4Ê4Ì4Í4Ï4Ñ4Ó4Õ4×4Ø4Ù4â4ç4é4ë4í55,5.50525456585T5]5b5d5f5h5•5—5™5š5œ5ž5 5¢5¤5¥5¦5¯5´5¶5¸5º5Ô5ù5û5ý5ÿ6666#6,616365676d6f6h6i6k6m6o6q6s6t66˜6½6Â6Ä6Æ6È6Ê6Ë6Í6Ï6Ô6÷7 777777L7N7P7Q7S7U7W7Y7[7\7u7š7œ7ž7 7¢7£7¥7§7­7Ú7Ü7Þ7ß7á7ã7å7ç7é7ê88(8*8,8.808183858>8k8m8o8p8r8t8v8x8z8{8”8¹8»8½8¿8Á8Â8Ä8Æ8ä999999999 9!9:9_9a9c9e9g9h9j9l9u9¢9¤9¦9§9©9«9­9¯9±9²9Ë9ð9ò9ô9ö9ø9ù9û9ý::1:3:5:6:8:::<:>:@:A:Z:::ƒ:…:‡:ˆ:Š:Œ:”:Á:Ã:Å:Æ:È:Ê:Ì:Î:Ð:Ñ:ê;;;;;;;;;(;U;W;Y;Z;\;^;`;b;d;f;g;p;q;s;‹;À;Â;Ä;Æ;È;Ê;Ì;Î;Ô<<<<<< < <<<<<< <"<$<&<@>>>> >)>2>7>9>;>=>j>m>p>q>s>u>w>y>{>|>}>†>‹>>>‘>«>Ð>Ò>Ô>Ö>Ø>Û>Þ>þ?? ??????B?E?F?H?J?M?O?Q?S?T?]?^?`?w?¬?®?°?²?µ?·?¹?»?è?ë?î?ï?ñ?ó?õ?÷?ù?ú@@7@:@<@>@@@A@C@F@N@{@~@@‚@„@†@ˆ@Š@Œ@@¦@Ë@Î@Ð@Ò@Ô@Õ@×@Ú@ïAAA"A#A%A'A)A+A-A.AFAkAnApArAtAuAwAzAAšA¥A²AÃAÅAÇAÊAÌAÒAãAåAçAêAìAòBBBB B BB$B&B(B+B-B7BHBJBLBOBQB[BlBnBpBsBuB}BŽBB’B•B—BB®B°B²BµB·BÄBÕB×BÙBÜBÞBçBøBúBüBÿCCCCCC C"C&C7C9C;C>C@CRC_CbCeChC‰CŒCŽCC’C•C—C¨C¯C±C´C·CºCÏCáCêDD DD%D(D+D.DODRDTDVDYD\D^DgDiDDD“D•D˜D›D¸DºD½D¿DÁDÄDÆD×DÙDÜDßDâDëE EEE!E$E'EHEKEMEOEREUEWEoEqE…E–E™E›EžE¡EÂEÅEÇEÉEÌEÏEÑEÖEØEÞEïEòEôE÷EúFFF F"F%F(F*F/F1FIAIbIeIgIiIkInIpIwIIŒII’I•I¶I¹I»I½IÀIÃIÅIÊIÌIÒIßIâIåIèJ J JJJJJJ#J%J0J=J@JCJFJgJjJlJnJqJtJvJ{J}JƒJ”J—J™JœJŸJÀJÃJÅJÇJÊJÍJÏJãJåJóKKK K KK0K3K5K7K:K=K?KPKRKUKXK[KhKjKvK‡KŠKŒKK’K¯K±K´K¶K¸K»K½KÊK×KèKëKíKðKóLLLLLLLL7LDLULXLZL]L`L}LL‚L„L†L‰L‹L£L¸LÉLÌLÎLÑLÔLñLóLöLøLúLýLÿMMM0M3M5M8M;MHMYM[M]M`McMpMM„M†M‰MŒM©M«M®M°M²MµM·MÃMÑMÚNÏNÒNÔNÖNØNÚNÜNÞNàNâNäNæNèNêNìNîNðNòNôNöNøNúNüNþOOOO O OOOOOOO"O%O(O+O.O1O4O7O:O=O@OCOFOIOLOOOROUOXO[O^OaOdOgOjOmOpOsOvOyO{O}OOOƒO…O‡O‰O‹OOO‘O“O•O—O™O›OOŸO¡O£O¥O§O©O«O­O¯O±O³OµO·O¹O»O½O¿OÁOÃOÅOÇOÉOËOÍOÏOÒOÕOØOÛOÞOáOäOçOêOìOïOòOõOøOúPPPPP'P)P,P/P2P;PDPMPPPSPVPYP[PˆP’P•P—P™P›PžP¡P¤P§P©P²PÁPÊPåPèPëPîPñPôP÷PúPýQQQQ Q QQ7QEQRQUQWQXQZQ\Q]Q_QaQŠQQQQ’Q”Q•Q—Q™QÆQÉQËQÍQÏQÒQÕQØQÛQÝQæQ÷QùQüQÿRR R RR RIRLRNRORQRSRTRVRXRR„R†R‡R‰R‹RŒRŽRRRÊRÍRÏRÑRÓRÖRÙRÜRßRáRæRïSSSS S SSSSSSCSFSHSISKSMSNSPSRSS‚S„S†SˆS‹SŽS‘S”S–SS¦S¯S²SµS¸S»S½SæSéSëSìSîSðSñSóSõTT-T0T2T4T6T9T\A\D\G\J\M\O\Q\S\U\W\Y\[\]\_\a\c\e\g\i\k\m\o\q\s\u\w\y\{\}\\\ƒ\…\‡\‰\‹\\\‘\“\•\—\™\›\\Ÿ\¡\£\¦\©\¬\¯\²\µ\¸\»\¾\À\Ã\Æ\É\Ì\Î\Ð\Ò\Ô\Ö\Ø\Ú\Ý\à\ã\æ\é\ì\ï\ò\õ\ø\û\þ]]]] ] ]]]]]]]"]%](]+].]1]4]7]:]=]@]C]F]I]L]O]R]U]W]`^·^º^½^À^Ã^Æ^É^Ì^Ï^Ò^Õ^Ø^Û^Þ^á^ä^ç^ê^í^ð^ó^ö^ù^ü^ÿ____ _______ _#_&_)_,_/_2_5_8_;_>_A_D_G_J_M_P_S_V_Y_\___b_e_h_k_n_q_t_w_z_}_€_ƒ_†_‰_Œ__’_•_˜_›_ž_¡_¤_§_ª_­_°_³_¶_¹_¼_¿_Â_Å_È_Ë_Î_Ñ_Ô_×_Ú_Ý_à_ã_æ_é_ì_ï_ò_õ_ø_û_þ```` ` ```````"`%`(`+`.`1`4`7`:`=`@`C`F`I`L`O`R`U`X`[`^`a`d`g`j`m`p`s`v`y`|``‚`…`ˆ`‹`Ž`‘`”`—`š`` `£`¦`©`¬`¯`²`µ`·`¹`»`½`¿`Á`Ã`Å`Ç`É`Ë`Í`Ï`Ñ`Ó`Õ`×`Ù`Û`Ý`ß`á`ã`å`ç`é`ë`í`ï`ñ`ó`õ`÷`ù`û`ý`ÿaaaaa a a aaaaaaaaaa!a#a%a'a)a+a-a/a1a3a5a7a9a;a=a?aAaCaEaGaIaKaMaOaQaSaUaWaYa[a]a_aaacaeagaiakamaoaqasauawaya{a}aaaƒa…a‡a‰a‹aaa‘a“a•a—a™a›aaŸa¡a£a¥a§a©a«a­a¯a±a³aµa·a¹a»a½a¿aÁaÃaÅaÇaÉaËaÍaÏaÑaÓaÕa×aÙaÛaÝaßaáaãaåaçaéaëaíaïañaóaõa÷aùaûaýaÿbb b bbbb b/b4bFbOblbz eb|crispy-doom-crispy-doom-5.6.4/pkg/osx/Resources/wadfile.icns000066400000000000000000001260741360717211000241030ustar00rootroot00000000000000icns¬ÛââÚÿ ×ããäWcQßååÚÿ ×åæç\zMäèèÛÿ ×çè骉‘~èìëÜÿ ×éêëÀ¬¯ÆìïîÜÿ×êìíØê߀òñÜÿ ÖëíîÏåóôõôòÜÿ ÓìíïäðñòóòñÚÿªÓÔÖÔƒÖµÿÿ ÍèçåäãàÝѼ­‚ÿ àíëéÏåâàÝ彪ÿ àêêèkrÖáß×ɯÿ ßèèçg2…ÞßßÓÿÜ€çl!3ÙââÜÿ Ûææå`?7JÝääÛÿ ÙååætNNUàççÛÿ Øçèè}7W,äêêÜÿ Øéêë—ckZèíìÜÿ Øêìí®…°íïïÜÿØìíîÃÁÑ€òñÝÿ ØìîïÂàóôõôóÜÿÔíîïäñò€óòÛÿªÓ†Ö·ÿÿ ÇåäãáÞÝÙ˵™‚ÿ ÞéçåËàÝÚ×á·ªÿ ÝæåäqyÒÛØÐÄ©ÿ Úäãão0G‡ØÙÙÎÿ Ùãââq1/GÓÜÜÙÿØ€á`D.\Øàß×ÿ Õáàál?/YÜããØÿ ÖâãäjßææÚÿ Öåæçx $(ãêéÚÿ Öçèêƒ37€éííÛÿ ÖèêìŽ_­ðñðïÛÿ ÕéëìŸÏñóôóñÛÿÓêëíâðð€ñðØÿ¥ÐÒÓ€ÔÓ²ÿs8mkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿICN#ÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿàÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿðÿÿàil32Âÿ¬³ƒ² °°±°°¯¯¬«¨ª‰ÿ¦àù÷ööõ€ôó€òññï䨝¨ˆÿªóëêéèçåÚãâáàßÝÜÛÙÓùº©‡ÿ§óêéèçææÃÎÞáàßÞÜÛÚÔ÷õº¦†ÿ§òèèççæåÅr­ÐáàÞÝÜÛØÌÀÇ«¤…ÿ¥ò€çæååÅ#z¸ÒÞÝÜÛÚÏÀ«Ã¢…ÿ¥ñææåæåäÅ)1ÂÕÝ‚Ü榅ÿ ¥ñåæååääÅ1NœÆÜÞ€Ýð¨…ÿ¢ðååäåääÄ<L#UÂßòª…ÿ¢ðä ãäÄJF5=\Å€áààòª…ÿ¢ïääãääãÄSR2M[Ç‚âó«…ÿŸïããäããäÃa)^FW)cÉäãô«…ÿŸïã ääÄk5kZb5jÊ‚åô«…ÿœî€ä ååæÇj4tof4hÍçæõª…ÿœïååææççÆn0zƒj0gÐéèöª…ÿœïæ€ç èèÈ{?‡—x?nÐêéö¨…ÿšïçèèééêÉ^˜¬‰^ƒÔìì€ë÷¨…ÿšïèééêêëʦ}§˜}}—Õíì÷¨…ÿ šïéêêëëì̸œŸÇÚîï€îø§…ÿ —ïêêëììíÌË€»ÄÖÞïðïù§…ÿ—ïêëìííîÍßÛÛßÛçòññðù¦…ÿ—ïëììíîïÍëïçÞíóóôóóòòñù¤…ÿ”ïëìííîïÎêâáóóô€õôóòòù¤…ÿ ”ïëììíîïÍ׿òòóôóóòñù£…ÿ”ïëëìíîîÒêðññòò€óòòññù£…ÿìîíîîïîìñ„òññú …ÿ‹¥âã¿“†ÿ€ˆŠÂÿÂÿ®µ€´³³€´²°¯¯«ª‰ÿ¦áùø÷÷ööõõôôóóòòðæÚ²¨ˆÿ­ôíìëêéçÜæåãâáàßÝÜÖù½«‡ÿªô€ëêéèÂÎáäãâáàßÞØ÷ö½©†ÿªó€êéèç·C“ÒãâáàßÞÛÎÂɯ¤…ÿ§ò€éèèç¹W§ÕâáàßÝÒ­Ť…ÿ§òè çç»&fºØà‚ß訅ÿ ¥ñçèççæç¾#7xÈÞàñ«…ÿ¥ñ€ç æçæÁ/">Æâáó­…ÿ¥ñƒæÁ>*#-*EÇ‚ãô­…ÿ¢ðæåææååÁI<3<$€"$=Ê€åääô§…ÿšîâããääåÄ)*%Ë‚æõ¦…ÿšîääååææÅ22(Ìèçõ¦…ÿ—îååæççèÇ=$82Í€êééö¦…ÿ—îæççèèéÈH $" 9Îìë÷¤…ÿ —îçèèéêêÊQ-/oÕì€íì÷¤…ÿ ”îèééêëëËZ€:JÛîïï€îø£…ÿ”ïèéêëììÌbGGf®å€ñððïïø£…ÿ ”ïéêëëìíÌgP~Ëë‚òñððù£…ÿ‘îéêëììíÌm”Ýñòóôôóóòñðù£…ÿ‘îéêëëìí̯åðñòò€óòòñðù¢…ÿ ‘îééêëìíÐèïïð„ñðïù¢…ÿ ëìëììííêïðð†ñðúž…ÿ‹¢à‡â„ãâ⾆ÿ€ˆ…€Âÿl8mkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿich#HÿÿÿÀÿÿÿðÿÿÿøÿÿÿüÿÿÿþÿÿÿÿÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿ€ÿÿÿÿ€ÿÿÿÀÿÿÿðÿÿÿøÿÿÿüÿÿÿþÿÿÿÿÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿÀÿÿÿÿ€ÿÿÿÿ€ih32Åÿÿ“ÿ¥†©‹¨¤¡ª‘ÿ©Äêìì€ë€êééèè‚ç åãâÛÒË·¤¤€ÿ «ýôóóòòññððï€î€í ìëëêêéäàçå²£ªÿ Ÿ°ýëêêééèçæäßäãâááàßÞÝÜÜÛÚÙÓéï¹¥ŒÿŸ¯ýêéé€èçæåÍÕáââáàßßÞÝÜÛÚÙ×ÒÿëÁ¤™‹ÿŸ¯ýéé€èççæåͼÊÖâááàßÞÝÜÜÛÚØÒÿýâ·¢‹ÿŸ®ý€è€çæååͤžÁÌØáàßÞÞÝÜÛÛÙÎÐÈÅÖ«ŸŠÿŸ­ýèçææååÍŒj¨ÃÎÚßßÞÝÜÜÛÚÏĹ®ªÏ¡ŠÿŸ¬ýççæååäÍŽ°ÃÐÜÞÞÝÜÛÛÙÖÐÈ´Ì¥ž‰ÿŸ«ý€æ忀åäÍ$¶ÅÑÞÝ…ÜåÅž‰ÿŸªýææ‚åääÌ‘ƒBš»ÇÓ…ÝèÖŸ‰ÿŸªýƒå€äÌ“„`¤ÀÔ„Þëߟ‰ÿŸªýååääå€äãÌ—Lr¼Ö„ßì埉ÿŸ¨ý…äãÌ› .TdI€ v¾×„àíå‰ÿŸ§ü„äããÌ 2I!ez¿Ø„áìæ‰ÿŸ§ü€äããääããÌ 6U/b"x¿Ú„âíç‰ÿŸ¦üãä€ãääããÌ¥##;_"6AA@N»Ô„Ýë䛉ÿŸ£üâ€áâ€á àÊVMMHM€L½Ö„Þë噉ÿŸ¢ü…áàÊOYYR WYXJ¾Ø€àßëæ˜‰ÿŸ¢ü€áà€áàáÊUee\addR¿Úá€àìæ˜‰ÿŸ¡üááàáàáÊZqqgjpp[ÁÛ‚âááìæ˜‰ÿŸ üá€àáâË^}}r  q||cÁ݃ãâíæ–‰ÿŸ üá€âããÌcxwn$ gttfÀÞ„äîæ–‰ÿŸŸüá€â€ãääÍf $#3¿ß„åîæ–‰ÿŸžüâ€ã€äååÎj")'4Áá„æïæ”‰ÿŸüã€ä€åææÏm$.+ 4Áãèè‚çð甉ÿŸýääååæçÏr '30:Âä€éèñ甉ÿŸýå倿€çèÐx€)84?Ãåê€éñ甉ÿŸœý€æçç€èéÑ|€-0€AÂæë€êòç’‰ÿŸœýæçç€èééêÒ}€"!"FÃèì€ëóç’‰ÿŸ›ýççèèéé€êÓ…+-iËè‚íììóç’‰ÿŸšýèè€éêêëëÔ‚„3>|Îßê‚îííôè‰ÿŸšýèééêê€ëìÔ…ƒ<X˜Ùåððï€îôè‰ÿ €™ýèéêêëëììíÕ†EFo·Ýêñ€ð€ïôè‰ÿ €˜ýééêêëëììíÕ‡€MT‚Ðâî‚ò€ñððïõè‰ÿ€˜ýéêêëëììííÖˆOOe™Úæòò‚óòòññððõè‰ÿ€—ýéêêëëììííÖˆPu¶Þêòòóó€ô óóòòññðõè‰ÿ€–ýéêêëëììííÖ‹ƒÐâîñòòóó€ô óóòòñððõè‰ÿ€–ýééêëëììííÖ¸Øåððññ€ò€ó€òññððõè‰ÿ€•ýééêêëëììíÔÙçïïðð‡ñ€ðïõ狉ÿ€”ýèééêêëëììÛêîî€ï‡ð€ïîô狉ÿ ª‘ýìëììííîîíí€ï€ð‡ñð÷݈ŠÿŽË÷†ù‘úîš„Šÿˆ‹‡„ŽŠŽƒÿÿÂÿh8mk ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿit32@Aÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ…§¦¥¤„£¶ÿ‚§¦Ž¥¤Š£´ÿ§€¦±ÄņĀÄÁŽÀ‚¿¸´³³²±±°±¨‚£€¢±ÿ€¦Æõþ´ÿ ýùõñíìïéÚɰƒ¢®ÿ€¦´þ·ÿ ýùõñíêìñòëÔ²‚¢­ÿ€¦åÿÿúóó‚ò„ñ€ðƒï…îííƒì€ëƒêéëì€ëêèåâãäáìöðæÐ©¢¢¡¡¬ÿ¦¦¥÷ÿþì€ë€êé€è‚ç€æ€å€ä€ã€â€á€à€ß€Þ€Ý€Ü€Û€Ú ÙÙØØÖÕÔÍçøòèÛ³¡«ÿ€¥úÿúëëê‚éè€ç€æååâãää€ãâ‚á€à€ß€Þ€Ý€Ü€Û€Ú€Ù ×ÖÎÊñùòèÞÀ¢€¡ªÿ€¥úÿúƒêéè€çæ€åÚßãää€ã€â€áà€ß€Þ€Ý€Ü€Û€Ú€ÙØØÑÊÎÿúòçÜÇ£¡  ©ÿ€¥úÿúê‚éèçè€çæåå×ÖÛàã€â€á€à€ß€Þ€ÝÜÝ݀܀ÛÚÚÙ€Ø ÔËÇïÿùðäÙÈ¢€ ¨ÿ¥¤¤ùÿúêê„éèçæ€å×ÌÒÖÚà€ã€âá€à€ß€Þ€Ý€ÜÛ€Ú€Ù ÕÎÈÚÿÿ÷îâ×Ê£€ §ÿ€¤ùÿú€ê‚éèçƒæ ååØÊÉÎÓ×Üá€âá€à€ßހ݀܀ۀڀÙØÖÏÊÉ€ÿôëÞ×Ê¢  Ÿ¦ÿ€¤ùÿúƒéèçèèçæ€å ×Ê¿ÆÉÎÓ×Üáâ€áàáá€à€ßހ݀ÜÛ€ÚÙ×ÐÊÊ€ÿûðçÚ×Ç €Ÿ¥ÿ€¤ùÿú€éèéé‚è‚ç€æå ä×ʾºÂÆËÏÔØÝâáà€ß€Þ€ÝÜ€Û€ÚÙÙØÐËÌÿöìãÙØÄŸ¤ÿ¤££ùÿú€éƒè‚çææçæ€åØÊ¾¼¼½ÂÇËÐÔØÝááà€ßހ݀ÜÛ€ÚÙÙØÐÌÎÿüñçßÙØº€Ÿ¤ÿ€£ùÿúƒè€çè‚çæåä×ʾÂÏļ¾ÃÈÌÑÕÚÞááà€ßÞ€ÝÜ€Û€ÚÙÒÎÎæßÙÐÓØÚàÝÚØª€ž£ÿ€£ùÿù„èƒç‚æƒåØÊ¾Â•Ìû¿ÃÈÍÑÖÚÞ€à€ßÞ€ÝÜ€ÛÚÙÓÏÊÆÂ¾ºµ±­°ÅÜÚР€ž¢ÿ€£ùÿùè„ç…æåä×ʾÀƒ1«ÇÀ»ÀÄÈÍÑÖÛ߀à߀ÞÝ€ÜÛÚÚÕÑËÈÄ¿º¶²®©¥³ÛÚ¿€ž¢ÿ€¢ùÿù‰ç‚æ‚åää×ʽÀ‡K¿Å½¼ÁÅÊÎÒ×Üà߀ÞÝÜ€Û€Ú×ÑÌÉÅ¿¼¸´¯«§¢·ßÙ£€¡ÿ€¢ùÿùçè„çƒæ…åä×ʾÀŒgÊú¼ÁÅÊÎÓ×ÜßßÞ€ÝÜ€ÛÚ×ÑÍÊÅÀ½¸´°¬¨£ŸÕß¿€¡ÿ€¢ùÿùƒç‡æ‚å€ä×ʾ¿ƒ…͹½ÂÆËÐÔÙÝßßÞÝÜÛØÕÐÌÆÂ¿º¶²­©¤Ÿ°âÛ¡¡ÿ€¢ùÿùçç€æçç„æƒåä×ʾ¿”… (¢ÉÀ¹½ÂÇÌÐÔÙÝ€ÞÝÜ†Û ÚØÕÒÎÊÄ·¨¡žÛã¹€ ÿ€¡ùÿù€ç…æåæƒåää×ʽ¾—‡ A¹Å½º¾ÃÈÍÑÖÚßÞ݇܅ÛÚÐµÊæÊ€œ ÿ€¡ùÿù…æå‚æƒåä×ɽ½›ˆ]ÆÃº»ÀÃÈÍÒÖÚÞÞÝ“ÜäêÝ€œ ÿ€¡ùÿùˆæƒå€äååää×ʾ¼ Š {Í·»ÀÄÈÍÒ×ڀݔÜçí顜œ ÿ€¡øÿù„æ‰å‚ä×ɽ¼¤Œ ˜ÊÁ¸¼ÁÆÊÎÓØ‘Ý‚Üæðí®œ› ÿ€ øÿùææ‚åææ…åƒäã×ɾ¼§Ž 7°Ç½¸½ÁÆÊÏÓØ”Ýèôñ°›› ÿ€ øÿùå倿†åäå‚ä×ʽ»¬ SÄĺº¾ÃÇÌÐÕÚƒÞŒÝé÷ô±›› ÿ€ øÿùˆåä‚åƒäã×ɽ»¯‘ p͸º¿ÃÈÍÒØÝÞêú÷²›› ÿ ŸŸøÿùŠå…äãää×ʽº³“ ÍÁ·ºÁÈÐÚ‘Þëýú³›š ÿ€Ÿøÿùä†åƒäå„ä×ɽº·•.¬Ä³¾ÇÑÛŠß„Þëÿýµšš ÿ€Ÿøÿù‚å‚ä€å…ä€ã×ɽ¹¼„……:Ä´¾ÇÑÛ‘ßìÿÿ¶šš ÿ€Ÿøÿø‚äå†äããä×ɽ¹À„#¹V‚†:ŵ¿ÈÒÜ€àŽßìÿÿ¶šš ÿŸžžøÿøä€åäãã×ɽ¹Ã„$ħ´O €†;ǵ¿ÉÒÜàßßìÿÿ¶™™ ÿ€žøÿøä‚ã×ʽ¸Ç„ %¹O¬®I ‡ ;ȵ¿ÉÒÜ‘àíÿÿ¶™™ ÿ€žøÿøŒä€ã‚äã×ɽ¸Ë„ %¼T±¨B† ;ʶÀÊÓ݇á‡àíÿÿ¶™™ ÿ€žøÿøˆäã…äã×ɽ·Î„&¾\¾Q…<̶ÀÊÔÝ‘áíÿÿ¶™™ ÿ€øÿøƒäã†äƒãää×ɽºÒ„'¿ƒ “^…<Ï·ÀÊÔÞ‘áëÿÿº˜˜ ÿ€øÿø€ã†äã‚ä€ã×ɽ¹Õ„(ƒ%e…<Ñ·ÁËÕßâáëÿÿ»˜˜ ÿ€øÿøƒä‚ã„äƒã×ɽ¹Ø„)Ã*j…=Ó¸ÂËÕß‘âìÿÿ»˜˜ ÿ€øÿø„ãƒä„ã€äã×ɽ¸Û„*Ń/Šq…>Õ¸ÂÌÖà†ãˆâìÿÿ»˜˜ ÿ€œøÿø€ã‚ä„ã‚äã×ɽ·Ü!ƒ+ƃ5ˆw…>Ö¹ÂÌÖàãâìÿÿº—— ÿ€œ÷ÿø‡ãƒä…ã×ʽ·Ý'ƒ"+ǃ:†|…!?عÂÌÖà‘ãíÿÿº—— ÿ€œøÿøƒã‚ä…ãäã×ɽµÝ.ƒ%-ȃ?„ƒ…$@غÄÎ×á‹äƒãíÿÿº—— ÿ€œ÷ÿø€ãä‡ã‚ä‚ã×ɽµÞ3ƒ(.ɃDƒˆ…'@ÛºÄÎØâ‘äíÿÿº—— ÿ€›÷ÿø‡ã‚ä„ãää×ʾµß9ƒ+/ʃI‚Ž…*AÜ»ÅÏÙãƒå‹äíÿÿº–– ÿ€›÷ÿøƒã€ä…ã„ä×ʾ´à?ƒ.0ÉO‚N€“…-BÞ»ÅÏÙãŽå€äíÿÿº–– ÿ€›÷ÿøãââ‡ãˆä×ʾ´àE„1ÉU‚S€™…0Cà»ÅÏÙã‘åîÿÿº–– ÿ››š÷ÿø†ã‰ä€åØË¾µßJƒ43È[‚X€ž…3Dá½ÆÐÚ䉿…åîÿÿº–• ÿ€š÷ÿøƒãˆä„åØË¾´ßP‚766Ça‚]¢…6Eâ¾ÆÐÚ呿îÿÿº•• ÿ€š÷ÿø€ã‡äˆåØÌ¿µßU:€9Åg‚b€§…9Fã¾ÇÑÛå‚çŒæïÿÿº•• ÿ€š÷ÿø†äˆåæÙÌ¿µÝZ=€<Äm‚g€«…;GàÁÇÒÜåŒç‚æïÿÿº•• ÿš™™÷ÿøƒä‡å…æÙÌ¿µß9„0Ár‚l­…09àÁÇÒÜå‘çïÿÿº•” ÿ€™÷ÿø€ä‡åˆæÚÍÀµà:„0¿x‚q‚°…06áÁÉÓÝç‡è‡çïÿÿº”” ÿ€™÷ÿø†åˆæçÚÍÀ´á;„0¾}‚v„´…04ãÂÉÓÝçèççïÿÿº”” ÿ€™÷ÿùƒå‡æ…çÚÍÀµâ<„0¼‚‚{†·…02äÄÊÔÞè€éŽèðÿÿº”” ÿ™˜˜øÿù冿‡çèÛÎÁµä=„0»ˆ‚€ˆº†0åÅÊÔÞèŠé„èðÿÿ¹““ ÿ€˜øÿù冿†ç‚èÛÎÁµå>„0¹‚†Œ½†0åÆÊÔÞè‘éðÿÿ¹““ ÿ€˜øÿù„æ†ç…èÛεæ@„0¸’‚‹Ž¿†0åÇËÕ߆ê‰éñÿÿº““ ÿ€˜øÿùæ†ç†èééÜϵèC„3¶—‚’†3åÇËÕàê‚éñÿÿº““ ÿ€—øÿùææ…ç†è‚éÜϵéL„;¶œƒ•Á>…;åÉÌÖáë‘êñÿÿ¹’’ ÿ€—øÿù…ç…è…éÜÏöéS„Bµ ƒš¿H…BäÉÌ×á‰ë†êñÿÿ¹’’ ÿ€—øÿù‚ç…è‡éêÝÏöé\„J´¥ƒŸ½S…JåÊÌ×áë€êñÿÿ¹’’ ÿ€—øÿù€ç…è…é‚êÝÏöëd„R´©ƒ¤»^…RäËÎØâ„ì‹ëòÿÿ¹’’ ÿ€–øÿùç„è†é„êÝÏöìl„Z³­ƒ©ºg…ZäÍÎØâ‹ì„ëòÿÿ¸‘‘ ÿ€–øÿùƒè…é†êëÞÐöìt„b´²‚®µ»m…bäÏÏÙâìëëòÿÿ¸‘‘ ÿ€–øÿù‚è„é…êëÞÐöì{„i´¶€³µº­u†iãÎÏÙã‡íˆìóÿÿ¸‘‘ ÿ€–øÿù€è„é„ê„ëÞÐķ탄q´º¸¸º¼‘ˆqâÐÏÙäí‚ìóÿÿ¹‘ ÿ€•øÿùè„é„ê…ëìßÑķ슄yµ½½¼®‰yâÑÐÚå‚îŒíìóÿÿ¸ ÿ€•øÿùƒé„ê…ë€ìßÑÄ·ì’„¶¿¼˜‹áÒÐÛåˆî‡íóÿÿ¸ ÿ€•øÿú‚éƒê„ëƒìßÑŸ뙄‰µ¯ŽŒ‰áÓÐÛåî‚íóÿÿ¸ ÿ••”øÿú€é„ê„ë„ìàÒŸ렄š‘äÖÒÜæ…ïˆîííóÿÿ¸ ÿ€”øÿúééƒê„ë„ìííàÒŸ꧔˜¡ÜçÒÓÜæŠï…îôÿÿ· ÿ€”øÿúéƒêƒë„ìíàÒŹ뮓  ¶êáÒÕÙÞãîððŠï‚îôÿÿ· ÿ€”øÿúƒêƒëƒìƒíàÓÆ¹êµ‘¨ ©ÍîÛÒÖÚßãçì…ð‡ïîîôÿÿ· ÿ”““øÿú‚êƒëƒìƒíîáÓÆ¹ê¼° ¶áéÕÓØÜàåéíŠð…ïôÿÿ·ŽŽ ÿ€“øÿúê‚ëƒì„íîîáÓÆ¹ê· ÆìáÓÖÚßãçëïƒñˆð‚ïõÿÿ·ŽŽ ÿ€“øÿú€ê‚ëƒìƒíîáÓÆºéÉŽ¿ ØîÜÓ×ÜàäèíŠñ…ð€ïõÿÿ·ŽŽ ÿ€“øÿúêê‚ëƒìƒí‚îáÔǺêÏŒÇ ËåêÖÔÙÞâçëï‚ò‰ñ„ðïõÿÿ·ŽŽ ÿ€’øÿúêƒë‚ìƒíƒîâÔǺéÖ‹Ï ØíäÓ×Ûßäèìñˆò†ñƒðõÿÿ¶ ÿ€’øÿúƒë‚ìƒíƒîïâÔǺéÜŠ× ãïÜÔØÝáåêîò…ñðõÿÿ¶ ÿ€’øÿú‚ëƒì‚íƒîïïâÔǺêâˆÞ àêì×ÖÚÞâèìðˆó…ò„ñ€ðõÿÿ¶ ÿ€’øÿú‚ë‚ìƒí‚î€ïâÔǺêè‡æ éïäÔ×ÛàåéíòŒó„òƒñððõÿÿ¶ ÿ€‘øÿúëƒì‚í‚îïâÔȺé‡î ïïÜÔØÞâæëïóƒô†óƒòƒñðõÿÿ¶ŒŒ ÿ€‘øÿúë‚ìƒí‚îïâÕȺé‡ï ìÖÖÛßäèìðó‹ô„ó‚òƒñõÿÿ¶ŒŒ ÿ€‘øÿúë‚ì‚íƒîïâÕȺé†ïäÔ×Üáåéíòôƒó‚ò‚ñöÿÿ¶ŒŒ ÿ€‘øÿú€ëƒì‚í‚î‚ïâÕȺé…ïÜÔØÝâæëïóƒô…õƒô‚óƒòñöÿÿ¶ŒŒ ÿ€øÿú€ë‚ìƒí‚î‚ïãÕȺé‚ï îìÖÖÚÞäèìñóƒô‡õ‚ôƒó‚òñöÿÿ¶‹‹ ÿ€øÿú€ë‚ìƒí‚î‚ïãÕȺç‚ï äÔ×Üàäéîòóóƒô‡õƒô‚ó‚òñöÿÿ¶‹‹ ÿ€øÿú€ëƒì‚í‚î‚ïãÕȺèïÜÔÙÝáæêï‚ó‚ô‡õ‚ôƒó‚òñöÿÿ¶‹‹ ÿ€øÿú€ëƒì‚í‚î‚ïâÕȺçïîìÖÕÙÞãçëðò‚ó„ôƒõ„ô‚óƒòñöÿÿ¶‹Š ÿ€øÿúë‚ì‚íƒîïâÕȺçðåÓÖÛßãéíñòòƒóŽôƒóƒò‚ñöÿÿµŠŠ ÿ€øÿúë‚ìƒí‚îï âÕȺäÛÓØÜàåéíƒòƒóŠô„óƒòƒñõÿÿµŠŠ ÿ€øÿúëƒì‚íƒî€ï âÔȺÎÔÙÝâæêïññƒò‡óô‡óƒòƒñðõÿÿµŠŠ ÿŽøÿú‚ë‚ìƒí‚î€ï âÔÇÃÕÚÞâçìðñ„ò‘ó„òƒñððõÿÿµŠ‰ ÿ€Žøÿú‚ëƒì‚íƒî ïïâÔÈÓÛàäèìð„ñ†ò‰ó†ò„ñ€ðõÿÿ´‰‰ ÿ€Žøÿúƒë‚ìƒíƒîïâÔÎÜàåêî€ð…ñ—ò„ñ‚ðõÿÿ´‰‰ ÿ€Žøÿúêƒë‚ìƒíƒîâÔÛâæêïƒð†ñ‘ò†ñƒðõÿÿ´‰‰ ÿŽøÿúêêƒë‚ìƒí‚îáÙãçë€ï…ðŠñƒòŠñ…ðïõÿÿ´ˆˆ ÿ€øÿú€ê‚ëƒì„í€îááèíƒï†ð—ñ†ð€ïõÿÿ³ˆˆ ÿ€øÿúêƒëƒìƒíîîâé€î„ïˆðñ‡ðƒïõÿÿ³ˆˆ ÿ€õÿý‚êƒëƒìƒíìêƒî…ïžð†ï÷ÿÿ«ˆˆ ÿ€ŒÛÿÿùòñƒòˆóòó…ô«õô÷þÿû‰ˆˆ ÿ€Œ›þÑÿÔ€ˆ¡ÿ€Œ´òÎÿûÕŒ€ˆ¡ÿ€Œ‹‹†´ƒµŽ´µ´™³ª‰ˆ£ÿŒ‹ŠŽ‰£ˆ¦ÿ…‹ŠŽ‰§ˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªªŽ©¨Ž§Œ¦¶ÿީލ§Ž¦¥´ÿ©µ†ÆŒÅĆńÀ¹·¶µµ´€³ª…¥±ÿ€©Èõµÿ ýùöòïîðêÝ̳ƒ¥®ÿ€©·þ·ÿ ýúöòïëíòóîÖµ‚¤­ÿ€¨æÿÿúôô†ó€ò„ñ€ð„ï…îíƒìíƒî íìêèåæçäîöòéÔ«¤¬ÿ€¨øÿþ€íìëê€é€èƒç€æ€åä€ã€âá€à€ß€ÞÝ ÜÜÚÙØÑéùóêÞ¶¤«ÿ€¨úÿûí‚ìëƒê€éèäåçæ€å€äã€â€áà€ß€Þ€Ý€Ü ÛÚÙÑÍòúóêà䀣ªÿ€¨úÿúì„ëê€éèççÜáåææ€åƒäã€âá€à€ß€ÞÝ ÜÜÛÕÎÑÿúóéßÊ¥€£©ÿ€§úÿú‚ìëêéè€çÙÙÞâ€æ€åä€ãâ€á€à߀ހÝÜ ØÏËðÿúòçÝÌ¥€£¨ÿ€§úÿú€ì‚ëêééêé€èççÚÏÔÙÝã€åäã€âá€à€ßÞ€Ý€Ü ÛØÑËÛÿÿøïäÚÍ¥€¢§ÿ€§úÿúƒëêëëêéè€ç ÚÌÊÐÕÙÞãåäåäã€âá€àßÞ€ÝÜÚÓÌÍ€ÿõíâÛΤ€¢¦ÿ§§¦úÿú„ë‚êéèççèçÚÍÂÇÌÑÖÚßäå€äãâ€áà€ßÞ€ÝÜÜÚÓÍÌ€ÿûòêÞÛÊ£€¢¥ÿ€¦úÿúëƒê„é‚èççÚÍ¿¼ÄÈÌÑÖÛßääãâ€áà߀ÞÝÜÜÛÒÎÎÿ÷íæÜÛÇ¡¤ÿ€¦ùÿúë‡ê‚é‚è€ç ÚÌ¿‹£¿ÄÉÍÒ×Üà€äã€âáà߀ހÝÜÔÐÒÿüòéâÝܼ€¡¤ÿ€¦ùÿúêë„ê‚é‚è‚çÚÍ¿s@k°ÀÅÊÏÓ×Üáãâ€áà߀ÞÝÜÕÑÑçáÙÒÕÙÛãàÝÛ­€¡£ÿ¦¥¥ùÿúƒêƒéèèéé‚è€çÚÌ¿u35B}·ÁÅÊÏÔØÜáãâ€áàßÞÝÜÖÒÌÉÅÀ¼¸³¯³ÇßÝÓ£¡  ¢ÿ€¥ùÿú€êˆé‚è‚çÙÌ¿w0‚  ?1('5h¸ÂÌÖß‘ãîÿÿ¸›› ÿ€ øÿùåŠæå…æåØË¾žf„-/Bƒ57…,9j¹ÃÍ×áŒä‚ãíÿÿ½›› ÿ€ øÿù†æå‡æåØË¾j„13Eƒ7;ƒ100HsºÄÎØâ‘åîÿÿ½šš ÿŸŸžøÿùƒå†æƒå€æÙ̾¥zEƒCDRƒ&>L…CLw»ÅÏÙãææåîÿÿ¼™™ ÿ€žøÿù€å„æƒåƒæååØË¾§~KƒHIUƒ*@P…GO{»ÅÐÙãæåîÿÿ¼™™ ÿ€žøÿùææ†å„æƒåØË¾©‚O„LYƒ.BS…LS€¼ÅÐÚ䑿ïÿÿ½™™ ÿ€žøÿù„儿„倿ÙÌ¿ª…U„Q[ƒ3EV…PW„¼ÇÐÛå‚çŒæïÿÿ½™™ ÿ€øÿù忆儿ÙÌ¿­‰Z„U^ƒ7GZ…U[ˆ¼ÇÑÛåçæïÿÿ¼˜˜ ÿ€øÿù‰å‰æÙÌ¿®Œ`„Za<‚;J^…Y^Œ¼ÇÑÛå‘çïÿÿ¼˜˜ ÿ€øÿù†åŠæççÚÍÀ¯d„^dA‚@Ma…^c¼ÈÒÜæƒè‹çïÿÿ¼˜˜ ÿ€øÿù‚剿„çÚÍÀ¯’j„cfE‚DOeccƒbf”¼ÈÒÜæŽè€çïÿÿ¼˜— ÿ€œøÿùååˆæˆçÚÍÀ°•phƒgiK‚ISh…gk—¹ÈÒÜæ‘èðÿÿ»—— ÿ€œøÿù‡æŠçèÛÎÁ²—r‚ihhlO‚MUjgfi™ºÉÓÞç…é‰èðÿÿ»—— ÿ€œøÿùƒæ‰çƒèÛÎÁ²„„lS‚QXa…Š»ÉÓÞçŽé€èðÿÿ»—— ÿ€œøÿù€æˆç‡èÛÎÁ³Š„oX‚V\f…»ÉÔÞè‘éñÿÿ»–– ÿ€›øÿùæ‡ç‰èéÜÏ´„p\‚Z_l…•¼ÊÕßé†êˆéñÿÿ»–– ÿ€›øÿù…çˆè‚éÜϵ•„ra‚^bp…›¼ËÕßéŽê€éñÿÿ»–– ÿ€›øÿù‚çˆè…éÜÏ´›„tf‚cfu†¡½ËÕßé‘êñÿÿ»–– ÿ›ššøÿù€ç†è‰éÝÏö „wj‚giz†¦¾ÌÖàê†ëˆêñÿÿº•• ÿ€šøÿù†èˆéêÝÏö¦„yo‚lm†ª¿ÌÖàŽëêñÿÿº•• ÿ€šøÿù„è‡é„êÝÏö¬ „zs‚pqƒ†¯ÀÌÖá’ëòÿÿº•• ÿ€šøÿùè‡é‡êÝÐö²)„|wƒt†…´ÁÍ×âˆì‡ëòÿÿ»•• ÿš™™øÿùèè†é‡ê€ëÞÐö·0„!{ƒyˆ&…!·ÂÍØâŽìëòÿÿº”” ÿ€™øÿù†é†êƒëÞÐö»8„(‚ƒ}Š0…(¼ÄÎØâ’ìóÿÿº”” ÿ€™øÿúƒé‡ê…ëÞÐÄ·Â@„/„ƒƒ‹9…/ÀÅÎÙãˆí‡ìóÿÿº”” ÿ€™øÿúé†ê‡ëìßÑÄ·ÇH„6†ˆƒ†B…6ÅÆÎÙãŽíìóÿÿº”“ ÿ€˜øÿú€é…ê†ëìßÑÄ·ËO„=‰Œ‚ŠŽ‘G…=ÅÇÐÚäî‘íóÿÿº““ ÿ€˜øÿúé…ê…ë„ìßÑĸÍW„D‹€Ž‘„O†DÃÈÐÚäˆî‡íóÿÿº““ ÿ€˜øÿú„ê…ë†ìàÒŸÍ_„L‹“’jˆLÂÉÐÚåî‚íóÿÿº““ ÿ€˜øÿú‚ê…ë†ìííàÒŸÍe„SŒ——“…[‰SÂÈÑÛæ€ïŽîíóÿÿº’’ ÿ€—øÿú€ê…ë…ì‚íàÒŸÍl„Z–’p‹ZÂÊÑÜæ‡ïˆîôÿÿº’’ ÿ€—øÿúêê„ë…ì„íàÒÆ¹Ìr„aŒ†fŒaÁÉÑÜæŒïƒîôÿÿº’’ ÿ€—øÿúê„ë„ì†íáÓÆ¹Ìy„hqhiÄÎÓÝç€ðïîîôÿÿº’’ ÿ——–øÿúƒë…ì„í€îáÓÆ¹Ë”oy»ÌÏÔÝç‡ðˆïõÿÿ¹‘‘ ÿ€–øÿú‚ë„ì…íîáÓÆ¹Ì‡“wÊÌÑÕÙÞãîŠð„ïõÿÿ¹‘‘ ÿ€–øÿúë„ì„íƒîáÓǺ̑~ ©ÎÏÓ×Ûàäèí€ñŠðïõÿÿ¹‘‘ ÿ€–øÿú€ëƒì„í…îâÔǺ˓… Œ¿ÍÐÔÙÝáæêî†ñˆðïõÿÿ¹‘‘ ÿ–••øÿúëëƒì„í„îïïâÔǺ̙Œ žÌÍÒÖÚßãçëï‹ñ…ðõÿÿ¸ ÿ€•øÿúëƒì„í„î€ïâÔÇºÌŸŽ“ ²ÏÐÔØÝáåéî„òˆñƒðõÿÿ¸ ÿ€•øÿúƒì„í„îïâÔǺ˦Œ›  ÃÍÐÕÚÞâçëï‰ò‡ñ€ðõÿÿ¹ ÿ€•øÿúƒìƒí„î‚ïâÕȺˬ‹¢ ®ÍÏÒ×Ûßäèìñò…ñððõÿÿ¹ ÿ€”øÿú‚ìƒí„îƒïãÕÈºÌ±Š© ½ÏÐÔØÝâæëï‡ó‡ò…ñõÿÿ¸ ÿ€”øÿúìƒí„îƒïðãÕȺ̷ˆ° ³ÈÏÑÖÛßãèìðŒó…òƒñöÿÿ¸ ÿ€”øÿúìƒíƒîƒïððãÕȺ̽‡· ½ÎÎÔØÜàåéíòó„ò‚ñöÿÿ¸ ÿ€”øÿú€ìƒíƒî„ïððãÕȺˆ¾ ÇÐÐÕÙÞâæëïŠô†ó„ò€ñöÿÿ¸ŽŽ ÿ€“øÿú€ìƒíƒîƒï€ðãÕÈ»ÌÄ¿ ÀÌÐÒÖÛßäèìñŽô„óƒò€ñöÿÿ·ŽŽ ÿ€“øÿú€ìƒíƒîƒï€ðãÕÈ»Ëÿ ÃÏÎÔØÜáåéíó…ôõõ†ô„óƒòññöÿÿ·ŽŽ ÿ€“øÿúììƒíƒîƒïðãÕȻ̿ ÈÐÐÕÙÞâæëïƒô‡õƒôƒó„òñöÿÿ·ŽŽ ÿ““’øÿúììƒíƒîƒïðãÕÈ»ÌÀ¿ ÀËÏÑÖÛßäèìñƒô‰õƒôƒóƒòñöÿÿ· ÿ€’øÿúììƒíƒîƒïðãÕÈ»Êÿ¿ÃÏÎÓ×Üáåéîòóƒô‰õƒôƒóƒòñöÿÿ· ÿ€’øÿúììƒíƒîƒïðãÕÈ»ÌÄ¿ÈÐÐÔÙÝáçëï€óƒô‰õƒôƒóƒòñöÿÿ· ÿ€’øÿú€ì‚íƒîƒïðãÕÈ»ËÅËÏÒÖÚÞãçìñ‚ó„ô…õ„ôƒóƒòññöÿÿ· ÿ’‘‘øÿú€ìƒíƒîƒï€ðãÕÈ»ÊÐÏÒÖÜàäéíñ„ó‘ô„óƒòññöÿÿ¶ŒŒ ÿ€‘øÿú€ìƒíƒîƒï€ð ãÕÈ»ÊÏÔØÜàæêî€ò„óô„óƒò€ñöÿÿ¶ŒŒ ÿ€‘øÿú€ìƒí„îƒï ððãÕȺËÕÙÞâæëð‚ò†ó‰ô†óƒòñöÿÿ¶ŒŒ ÿ€‘øÿúìƒíƒîƒï ððãÕÈÃÕÛßãèìðñ„ò—ó„ò‚ñöÿÿ¶Œ‹ ÿ€øÿúì„íƒîƒï ðãÕÈÔÛàåéíñ…ò’ó†òƒñöÿÿ¶‹‹ ÿ€øÿú‚ìƒí„îƒïãÕÏÝáåêï„ñ‡ò‹ó‡ò…ñõÿÿ¶‹‹ ÿ€øÿúƒìƒí„î‚ïâÕÜãçëïðð…ñ›ò…ñððõÿÿ¶‹‹ ÿ€øÿú„ìƒí„îïâÙãèì‚ð‡ñ•ò†ñðõÿÿ¶ŠŠ ÿ€øÿúëƒì„í„î€ïââéí†ð‰ñ‹ò‰ñƒðõÿÿµŠŠ ÿ€øÿúëëƒì„í„îïïãêï†ðžñ†ðõÿÿµŠŠ ÿ€õÿý€ë„ì„íƒîíë„ïˆð—ñ‡ðïï÷ÿÿ­ŠŠ ÿŽÜÿÿù€ò‹ó‚ôóôô”õ‹öŽõ÷þÿû‹ŠŠ ÿ€ŽþÑÿՀСÿ€Ž¶òÎÿûÕŽ€Š¡ÿŽŸ°¶™µ¬‹Š£ÿŒŽ‹£Š¦ÿ†ŒŽ‹§ŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ£¢¡Ž ¶ÿ‰£¢¡ ƒŸ´ÿ£®€ÂÁÁÀ¾½½†¼»µ‚±°¯®­¬­¤…Ÿ±ÿ€£Äôþ´ÿ üøôðëêíçØÆ¬ƒŸ®ÿ€¢±þ·ÿ ýùôðìèêðñêÒ®‚ž­ÿ€¢äÿÿùòƒñ€ðƒï‚îíî€íƒìëë‚êƒéèèçç„ê éèæãààâßëõïäÍ¥ž¬ÿ€¢÷ÿýê€é€èçæ€å€ä€ãââ€á€à€ßÞހ݀܂ÛÚÚ€Ù€Ø€× ÖÖÕÓÒÑËæ÷ðæØ¯ž«ÿ€¢ùÿúééƒè€ç€æ€å€ä€ãßàâáâáá€à€ß€Þ€Ý€ÜÛÛ€Ú€Ù€Ø××€Ö ÕÕÔÒËÇðùñæÚ¼ž€ªÿ€¡ùÿúééèç€æ€åäå€ä€ã×Üá€à€ßÞހ݀܀ۀڀÙØØ€×ÖÖ€Õ ÔÔÍÆÊþùñäØÃŸ€©ÿ€¡ùÿúèçæçæ€å€ä€ãâÔÔØÝá‚à€ß€Þ€Ý€Ü€ÛÚÚ€Ù€Ø€×€Ö ÕÕÑÈÄîÿùïãÖÅŸ€¨ÿ€¡ùÿùèèçè‚çæ€å€ä‚ãÖÊÐÔÙÞáàà€ß€Þ€Ý€Ü€Û€Ú€Ù€Ø€×ÖÖ€Õ ÒÊÄ×ÿÿöíßÓÇŸ€œ§ÿ€¡ùÿùèè‚çæåæå€ä€ã âÖÈÆËÐÕÙÞà€ß‚ހ݀܀ۀڀـ؀׀ÖÓÌÇÆ€ÿóêÛÓÆž€œ¦ÿ€ ùÿùçææçæåä€ã ââÖȽÄÈÌÑÕÚ߀ÞÝÜÜ€ÛÚÙـ؀׀ÖÕÓÌÇÇ€ÿûïæ×ÔÄ€œ¥ÿ€ ùÿù‚çæ‚åƒä€ã âÖȼ¸¿ÄÈÍÑÕÚßހ݀ÜÛÜÛÚÚ€Ù€Ø×ÖÔÍÈÈÿõêáÕÔÀœ€›¤ÿ€ ùÿùçç‚æååæåäãââÖÈ»ƒ›¼ÀÄÉÎÒÖÛ߀Þ݀܀ۀڀـ؀×ÖÖÕÍÉÌÿûðåÜÖÕ¶€›¤ÿ€ ùÿù…æ‚åäƒãâÖȼi0`©¼ÁÅÉÎÓÖÛÞހ݀܀ۀڀـ؀׀ÖÕÎÊËäÝÖÎÑÖ×ÝÚÖÔ§€›£ÿ€Ÿùÿù‚æ‚åääååäãââÖÇ»j144s´¼ÂÆÊÏÔ×ÜÞ€ÝÜ€Û€Úـ؀×ÖÐÌÇÄ¿»·³¯ª®ÂÙÖÌ››š¢ÿ€Ÿùÿùæåæ‚å‚äãâÖȼm2004:…·½ÂÇÊÏÔØÜÝ݀܀Ûڀـ؀×ÖÖÑÍÈÄÀ¼¸´¯«§£°Ø×¼€š¢ÿ€Ÿøÿùææ„å‚äãää‚ãââÖÈ»o30 4H–º½ÂÇÌÐÕـ݀ÜÛ€Ú€ÙØ××ÔÎÊÆÂ½¹´°¬¨¤Ÿ´ÛÕŸ€š¡ÿ€Ÿùÿùƒåääåƒä‚ãâÕÇ»q3ƒ0 3Y¤¹¿ÃÈÌÐÕÚ€ÜÛ€Ú€ÙØ€×ÔÏÊǾºµ²®©¥¡Òܼ€š¡ÿ€žøÿùƒåƒä†ã€âÖÇ»s3„033k¯»¿ÃÈÍÑÕÚÛÜÜÛ€ÚÙ×ÔÑÌÈÿ»¶³¯©¦¢­àÙž™™¡ÿ€žøÿùåå…äã€ä‚ãâáÕÇ»v4†0 48~¶¼ÀÅÉÎÒÖ‚ÛÚ€Ù†Ø ×ÕÒÏËÇÁ´¦žœØàµ€™ ÿ€žøÿøˆäƒã„âÖÇ»y4ˆ0 4B·¼ÁÅÉÎÒÖÛÛÚ€ÙØ×ͳÈäÈ€™ ÿ€žøÿø…ä‡ã‚âáÕÇ»z4‰0 13SŸ¸½ÂÅÊÏÔ×€Û€ÚŽÙ‚ØáçÚ€™ ÿ€øÿøäãä„ã‚â€áÕÇ»}5‹0 33f¬¸½ÂÆÊÏÔÙ€Ú”Ùåì瘘 ÿ€øÿø€ãää…ãâã‚âáÕÇ»60 55w³º¾ÃÈËÐÕ‹ÚˆÙäï쫘˜ ÿ€øÿø‡ãâãƒâ€áÕÇ»€60 4?‰¶»¾ÃÈÌÐÕ”Úåóï­˜˜ ÿœøÿø‰ã„âá€âáÕÇ»„70 16Nš·¼ÀÄÉÍÒ×€ÛÚçö󮘘 ÿ€œøÿøâ„ãƒâãƒâ€áÕÇ»†8’0 35a¦·¼ÁÅÊÏÕÚÛÚèúö®—— ÿ€œøÿø€ã‚â€ã„âƒáÕÇ»‰:“0 178s°¸¾ÅÍבÛêýú±—— ÿ€œøÿø€âã…âá‚â€áÕÇ»Œ;•019O±»ÅÎØ‹ÜƒÛêÿý²—— ÿœœ›÷ÿøãŽâƒáÔǺ<–13F²»ÅÎØ‘Üêÿÿ³—— ÿ€›øÿø‹â‚áââ€áÕÇ»‘>„65/3ƒ6†56F²¼ÆÏن݈Üêÿÿ³–– ÿ€›÷ÿøˆâáá‚âƒáÕǺ“?„:8.'.6‹:H²¼ÆÏÙ‘Ýëÿÿ³–– ÿ€›øÿø‚â€áƒâ‡áÕÇ»–A„?=,)/:?ˆ>H´½ÇÑÚ‘Ýëÿÿ³–– ÿ›šš÷ÿøáƒâƒáââƒáÕǺ˜B„C@-€*0=†CBI´½ÇÑÚŽÞ€Ýëÿÿ³–– ÿ€š÷ÿøâ„á€â†áàÔÇ»›C„HE.‚,>…GEI´½ÇÑÚ‘Þëÿÿ³•• ÿ€š÷ÿøƒáâ‹áÕǺ“C„LI-ƒ"?…LJIµ¿ÈÒ܉߅Þêÿÿ¸•• ÿ€š÷ÿøááâˆáâ…áàÔǺ‘C„QM-ƒ!@…PMCµ¿ÈÒÜ‘ßêÿÿ¹•• ÿš™™÷ÿø†áâ‡áààááÕÇ»•E„UQ,ƒ B…URE¶ÀÉÓÝ„àŠßêÿÿ¹•” ÿ€™÷ÿø•áÕǺ–G„ZV+ƒ C…YVF¶ÀÉÓÝàßêÿÿ¸”” ÿ€™÷ÿ÷à‘á€àÔÇ»šH„^[*ƒ C…^ZH¶ÀÊÔÝ‘àëÿÿ¸”” ÿ€™÷ÿøƒáàˆáà‚áÕǺ›Jbƒc`)ƒC…b^J·ÁËÕÞŒá‚àëÿÿ¸”” ÿ™˜˜÷ÿ÷€à‡áà†áàÔǺžKfƒgd(ƒB…gcK·ÁËÕÞ‘áëÿÿ¸”“ ÿ€˜÷ÿ÷„á€à†á€àááÕÇ»¢Mjƒlj'ƒ@…kgM¸ÂÌÖàˆâ†áëÿÿ¸““ ÿ€˜÷ÿ÷ƒà†áàà„áÕÇ»¤Omƒpn&ƒ?…pkN¸ÂÌÖà‘âëÿÿ¸““ ÿ€˜÷ÿ÷àà„á€à‰áÕȼ¦Prƒut%ƒ=…tpP¹ÃÍ×á‚ãŒâìÿÿ¸““ ÿ€—÷ÿøá…à‡á‚âÖȼ§Ruƒyx$ƒ;…yuR¸ÃÍ×áãâìÿÿ¸’’ ÿ€—÷ÿ÷‚à‡á†âÖȼ¨Sxƒ~}$ƒ8…}yS·ÄÎ×á‘ãíÿÿ·’’ ÿ€—÷ÿ÷àà†áˆâãã×ɽªU{„‚#ƒ6…‚~U¶ÄÎØãŠä„ãíÿÿ·’’ ÿ€—÷ÿø…á‡âƒã×ɽ«X‡€†#ƒ3…†‚V´ÅÎØã‘äíÿÿ·’’ ÿ€–÷ÿøá‡â‡ã×ʾ­Y‡‡‚†"ƒ 0€„‚ƒY³ÆÐÚä†åˆäíÿÿ¸‘‘ ÿ€–÷ÿøá†â‡ãä×ʾ­N„„!…Q´ÆÐÚäŽå€äíÿÿ·‘‘ ÿ€–÷ÿø„â†ã…ä×ʾ®P„ƒ#"…S´ÇÑÛ倿Žåîÿÿ·‘‘ ÿ€–÷ÿøâ†ã‡äåØË¾¯S„$‚%$…V²ÇÑÛ勿ƒåîÿÿ·‘‘ ÿ€•÷ÿøâ…ã‡ä‚åØË¾°U„&‚'&…X²ÇÑÛ呿îÿÿ· ÿ€•÷ÿø„ã…ä†åØÌ¿±X„(‚)'†[²ÈÒÜæˆç†æïÿÿ· ÿ€•÷ÿøã…ä†å€æÙÌ¿²[„)‚+*†]±ÈÒÜæçææïÿÿ· ÿ€•÷ÿøãã„ä†åƒæÙÌ¿²]„*‚-,†_±ÉÔ݆è‰çïÿÿ· ÿ€”÷ÿø„ä…冿ÚÍÀ³` „,‚/.†b±ÉÔÞè‚çïÿÿ¶ ÿ€”÷ÿøä…å…æçÚÍÀ³c „-ƒ1†d°ËÔßéŽèðÿÿ¶ ÿ€”÷ÿøää„å…æ„çÚÍÁ´e„/ƒ3…e°ËÕߊé…èðÿÿ¶ ÿ€”÷ÿø„儿†çèÛÎÁ´f„ 0ƒ5 … h°ËÕßéèèðÿÿ¶ ÿ€“÷ÿù‚儿„ç‚èÛÎÁ´j„1ƒ7…j±ÌÖà‡êˆéñÿÿ¶ŽŽ ÿ€“÷ÿù€å„æ„ç„èÛϵl„2ƒ9†k¯ÌÖàê‚éñÿÿ¶ŽŽ ÿ€“÷ÿùåƒæ„ç…èééÜϵo„3;:(†k°Í×á„ë‹êñÿÿ¶ŽŽ ÿ““’÷ÿù‚æ„ç„è‚éÜϵp$„4€=5†k¯Í×áŠë…êñÿÿ¶ŽŽ ÿ€’÷ÿùæƒç„è„éÜÏöq'„5?>(ˆk®ÎØãììë€êñÿÿ¶ ÿ€’÷ÿùææƒç„è„éêêÝÏöp*„67‰k­ÎØãˆì‡ëòÿÿ¶ ÿ€’÷ÿùæ‚ç„è„éêÝÏöq-„! ‹!j­ÎÙãŒìƒëòÿÿ¶ ÿ’’‘÷ÿù‚çƒèƒé„êÝÐöp1„%#Œ%j¬ÏÚä…íˆìëëòÿÿ¶ ÿ€‘÷ÿù€çƒè„éƒêëëÞÐöq4„($()k®ÐÚäŠí…ìóÿÿµŒŒ ÿ€‘øÿùççƒèƒéƒêëÞÐ÷p7”+2dzÂÒÛåîŠíìóÿÿ¶ŒŒ ÿ€‘øÿùç‚èƒéƒêƒëÞÑÄ·r:“.@nÌÓ×Üâì†î‡íìóÿÿ¶ŒŒ ÿ‘‘øÿù‚èƒéƒêƒëìßÑÄ·q=‘2 3Sq«ÑÕÚÞâæêŠî„íóÿÿ¶ŒŒ ÿ€øÿùèƒé‚êƒë€ìßÑÄ·r@5 ;f{ÀÓ×Ûßäèì…ï†îíóÿÿµ‹‹ ÿ€øÿù€èƒé‚êƒëìßÑŸrC8 GnÍÔØÝáåêí‰ï…îííóÿÿµ‹‹ ÿ€øÿùèèƒé‚êƒë‚ìàÒŸrFŽ; Wr¬Ò×Ûßãçì…ð†ï„îóÿÿµ‹‹ ÿøÿùèƒé‚ê‚ëƒìíàÒŸrIŒ? DgzÁÓØÜàåéíŠð…ïîôÿÿµ‹Š ÿ€øÿùè‚é‚ê‚ëƒìííàÒŹsL‹B NoÍÕÙßãçëð„ñ‡ðƒï€îôÿÿ´ŠŠ ÿ€øÿù‚é‚ê‚ëƒì€íàÒÆ¹rNŠE \r«Ó×ÜàäéíŠñ…ðƒïîôÿÿµŠŠ ÿ€øÿúéƒê‚ë‚ìíàÓÆ¹tQˆH LizÀÕÙÝáæêîñƒò‡ñ„ð‚ïôÿÿµŠŠ ÿŽŽøÿúé‚ê‚ë‚ì‚íáÓÆ¹uT‡L UpÎÖÚßãèìñŠò„ñƒðïõÿÿµŠ‰ ÿ€Žøÿúéê‚ë‚ìƒíáÓÆ¹tW†O arªÓ×ÜàåêîŽòƒñƒð€ïõÿÿ´‰‰ ÿ€Žøÿú€é‚ê‚ë‚ì‚íîáÓÆ¹uW„O Rk{ÀÕÙÝãçëï€ò‡ó„òƒñ‚ðïïõÿÿ´‰‰ ÿ€Žøÿú€é‚ê‚ë‚ìíîîáÓÆºuWƒO WqÎÖÛßäèìñòó‚ò‚ñ‚ðïïõÿÿ´‰‰ ÿ€øÿú€é‚êë‚ì‚íîîáÓÆºuW‚O arªÔ×Üàåêîòòƒóƒôƒó‚ò‚ñ‚ðïõÿÿ´‰ˆ ÿ€øÿú€éê‚ë‚ì‚íîîáÓÆºvX€O RjzÀÕÙÝâçëðòò‚ó†ô‚ó‚ò‚ñ‚ðïõÿÿ³ˆˆ ÿ€øÿú€éê‚ë‚ì‚íîîáÓÆºuXOOWpŽÍÕÛßãèíñ€ò‚ó‡ô‚óò‚ñ‚ðïõÿÿ³ˆˆ ÿ€øÿú€éê‚ë‚ì‚íîîáÓÆºwXO`r©Ó×Ûàåéíñòƒó…ô‚ó‚ò‚ñ‚ðïõÿÿ³ˆˆ ÿ€Œøÿú€é‚êë‚ì‚íîîáÓÆºv[jz¿ÔØÜáæêïññ‚ò„óô„ó‚ò‚ñ‚ðïõÿÿ³‡‡ ÿ€Œøÿú€é‚ê‚ë‚ì‚íîáÓÆºvqŽÌÕÚÞâçëðñƒò‹óƒò‚ñ‚ðïïõÿÿ³‡‡ ÿ€Œøÿú€é‚ê‚ë‚ì‚íîáÓÆ¹~¨ÒÖÚàäèìðƒñ„ò‡ó„ò‚ñƒðïïõÿÿ³‡‡ ÿ€Œøÿúé‚êëƒì‚í áÓÆ¹ÀÓØÜàäéîðƒñ‘òƒñƒð€ïõÿÿ³‡‡ ÿ€‹÷ÿúé‚ê‚ë‚ì‚í áÓÆÂÔØÝáæêîƒð„ñŒò…ñƒðïõÿÿ³†† ÿ€‹÷ÿù‚é‚ê‚ë‚ìíàÓÇÒÙÞâçëïƒð—ñƒðƒïôÿÿ²†† ÿ€‹÷ÿù‚é‚êƒë‚ì€íàÒÍÛßäèìƒï…ð‘ñ…ðƒïîôÿÿ²†† ÿ€‹÷ÿùè‚é‚ê‚ëƒìííàÒÙàåéí€îƒïˆð‡ñ‡ð„ï€îôÿÿ²†† ÿ€Š÷ÿùèƒé‚ê‚ëƒìíà×áåê„î„ï–ð…ï‚îôÿÿ²…… ÿ€Š÷ÿùèèƒé‚êƒë‚ìßàçë€í„î‡ïð‡ï„îóÿÿ±…… ÿ€Š÷ÿù€èƒé‚êƒëìâç„í…î›ï…îííóÿÿ±…… ÿ€Šõÿýé€èƒéƒêƒëìëè€ì„í‡î“ï‡îíöÿÿ©…… ÿ€‰Ùÿÿùñ€ð‰ñ„ò‹óôõôƒóöþÿû†…… ÿ€‰˜þÑÿÒ€…¡ÿ€‰²ñÎÿûÓ‰€…¡ÿ‰‰€ˆš€²ˆ³…²‡³²™±¨†…£ÿ‹ˆ‡†£…¦ÿ…ˆ‡†§…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿt8mk@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcrispy-doom-crispy-doom-5.6.4/pkg/osx/Resources/wadfile.png000066400000000000000000000043111360717211000237200ustar00rootroot00000000000000‰PNG  IHDR00Wù‡sRGB®ÎébKGDÿÿÿ ½§“ pHYsÍÓ3¼tIMEÚ yÏIIDAThÞíšMŒG†Ÿ¯ºº{úgfwg×v6¶qœ`çH‚#åÅ$‡H(‘1¬. å$8!qÈÉ7Ä ¤ˆÉ²CD!a$$X†€¢„8!19²-ll¯â]ïìÌîÎtqèžîê™ÙÙõ¬/–Ò«™žéîézßúÞï­¯ªWŒ1ÜΛâ6ßn{z­ ^ýÍ«F €ôœIJßú„˜¬vÇò‰¶NǸ-™™yᦠȰøõk¿0[·~ŠÝ»îE©4XùõÆX  éáì˜éÊ>d¿I0$c KË-š­E–––¸rù W¯]„S'ÏÈn ×~û+³ßWh¶Z“X@3¨¦KÈd8ó“ùñ.iÓ%mŠc+íÚí|¿B’t8~ümænÜÀwdfæ›#pøða&&«æ™/~‰kׯ”N¤° †$IXY^ÆuÝâx~]ö¢øŒ},1,//³yÓ4žçòÖÛorev––täùçžßXw©•XòÈ¢ "\ûø:ssó$&Õxž1’¾¤ø"é‹t¯µæÒå ŒÕùü#{ض}¾QæàÁƒLb»×±u]h=§i NŸ¢6=MÅÄq”â3) c³2ÛDQŸbçÝ÷°¸Ø$IfÍ÷¸‰$–†»¡ï&j®ûâú‹/¿Ì?_ú1—OŸbvv–f³i¡´R„"ý¡ÝnóÞ‰wøûñ7ùïÙsì¸kAqøç?3#GÀX‘0"ÚÂuì /—œà…Ïóøc÷³uß>¢±qâZŒëºd÷À ’vB—ÄBcF£Ac¡Áùÿc×îݼÿþ{ÈLÏ»÷-Ë4”$¦DhøÉ×øÆ+ãè÷~ÀGGŽpéÒ,s×çèt’rºi"Š0Œ¨×ëlÙ²™;·N†Ní8:ôÓ#瀩~Mª—@ÍuàÀ[øô¿þÀ·Þ=Á½O?ÉØCW«T«1ݤèþÆqÇñð<8®255EP Yj¶8ùá«æÂ’À¼ÇÅä20 åôôT£Ã·ý‡/œü%Ï=ú;÷=ËâöíÄQD…CrTpÍÄÄ2æM¹öèJ¿)oõÛ½°È±ßÿ›¯ºÈSÝǶý_FD2ƒÐq4A«KÖª…ÊÀM>úaÙbáJÚ×8® À´kx8Xæó~©ùWNÝ ^;Ã6ÊQˆÈàf³±Ãó¼ÑÇIJQÛÃM÷KÏ(®ŸJ§~îv¨4Nóõñ Îë)þÒŠrtºâ¡$_"`([næ·‰ÙF“’•öuS7².sç´^¡ÑêpüÊG+Ú3 7—‹"jij¸¸P:&MH6sðýI<¼ÇG”PyÀ²ItMDp]I#€‹QìÓ2i"ëèÊ0 e"’5“lLBRK8Ò=T|FЋ/+` š6a¤©IBŇ@ñ’TBF;@Y} c£"1VÝ"V© Å»H‰ŽqXüÝ÷Ó„þÛ©Fkh¿~ˆæµåôçOîAÉWQC XÎ=z5jJ2éïu«*Á©h‹»! +‰K <¯j´à(…… ÐtˆÛ¨Xá,ÎÏ¥$•ï”Öü@QßéàUŠ:©ã€R@w,(óeÙ‚ ÿžù€ú).e€€ºLÕ+T|! \墕bjÒãê|zǰꡔ ”-!)Ï/²N>[焦p¡Àn“Hÿ¼Ècj2Às!ˆÇE‰01é15ŸF!ªºy÷VÙ²¶€nJBô(ZéΨtE³i2€v 5"-.]¼ÆÂ‚fÓdÚ\(D•]¨({%˜‘s€ÈAÀs‹Áq„MY”eæ9}F17§©OMÅ‘Û?¨…d-ý¬Û…lð½À-™iGåÎOæIk«wï¾Ç­gz×aLOU<òÚ¨)Õ$½€Kç´ãä´…qÄŽ‡ǹs?A4‘F€UÕØË·r$¶Z¬rB”ã”"`oc[¶nê{‰ã˜(Šz«¬ì¾i©b¤<ñÀYŒ™¦d8}à­=‚ÖÂÔTÐcåBeóý´ëOãÖvÇU|ß_½x³J-¹e²GÞòàk néÊ”-!Çg9þ계Õ:Õ8ÆqôðæÄ¿Q¹*»³ÐžtV¦S¯OüÍܨ<ÊJm/cqLÅ¥2¤wöfŒ d ië(¬×µ.Ä è˜l¦¿íbÎß‹®ÝÇD5Æ÷+}’³Û¤Xª±–l6ìBƒê“ó1Ë£=,Ôž!ŒëÄQŒ£Òuk®Öì÷\è,0m@_®ËgÑwQ‹«„Qd÷sæ,¦X’¡ü ¤ôÐäV=#+IÈØo±ì"Õ­I&·ìd¬VïTz„…2rÓßß«,–a &I6F ] ’T¯bí1HFÂó}ÆÇÇR—±À§+Òó±wé¦hÕŒ¢˜m^È 3'=³°.ÑÉÉz^Ûkÿý™?Tÿ%¢‚ã8,6é$›'033ÃK?ú!ïþã|ð3ùSÊá (ÜÊm¡±ÀÑ£‚¤-#I¨ÙX–?¿~̼ñÆ_slfˆ9÷Ú®¬aÉk{‘Ð\jðâw¾;ÚcÖÛaûä%>!°ÁíÿRï}/†«MIEND®B`‚crispy-doom-crispy-doom-5.6.4/pkg/osx/cp-with-libs000077500000000000000000000034501360717211000220600ustar00rootroot00000000000000#!/bin/bash # # Copy a program to the specified destination, along # with libraries it depends upon. src_bin=$1 dest_dir=$2 # Returns true if the specified file is a dylib. is_dylib() { case "$1" in *.dylib) true ;; *) false ;; esac } # Returns true if the specified file is in a system location # (/System or /usr): is_sys_lib() { case "$1" in /System/*) true ;; /usr/*) true ;; *) false ;; esac } # Install the specified file to the location in dest_dir, along with # any libraries it depends upon (recursively): install_with_deps() { local src_file local bin_name local dest_file local lib_name src_file=$1 bin_name=$(basename "$src_file") dest_file="$dest_dir/$bin_name" # Already copied into the package? Don't copy again. # Prevents endless recursion. if [ -e "$dest_file" ]; then return fi echo "Installing $bin_name..." # Copy file into package. cp "$src_file" "$dest_file" # Copy libraries that this file depends on: otool -L "$src_file" | tail -n +2 | sed 's/^.//; s/ (.*//' | while read; do # Don't copy system libraries if is_sys_lib "$REPLY"; then continue fi #echo " - $bin_name depends on $REPLY" # Copy this library into the package, and: # recursively install any libraries that _this_ library depends on: install_with_deps "$REPLY" # Change destination binary to depend on the # copy inside the package: lib_name=$(basename "$REPLY") install_name_tool -change "$REPLY" "@executable_path/$lib_name" \ "$dest_file" done # If this is a library that we have installed, change its id: if is_dylib "$dest_file"; then install_name_tool -id "@executable_path/$bin_name" "$dest_file" fi } # Install the file, and recursively install any libraries: install_with_deps "$src_bin" crispy-doom-crispy-doom-5.6.4/pkg/osx/disk/000077500000000000000000000000001360717211000205605ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/pkg/osx/disk/background.png000066400000000000000000006740431360717211000234230ustar00rootroot00000000000000‰PNG  IHDRX¼ 3 pHYs  šœtIMEà /±ö IDATxÚì½g°,Ûu¶ÖÚ»ÃÌI7¾„ðDF0„$“” ѤÄ"EZE•ŠR-—-Cå@].-9°,ª\%‹´‚Y,–i1¨D€„@0‰ˆŒàá¼|ï»÷œ3¡»÷^kùÇîîéîéIçÌ9çއׅpîÌžîÝ;­ø} ?ú®_s¿‘Âß" ªdìÖŸ²äÎ*¢*óÝ8‹—öz·Ý¹¾Šéè™G>-Ân<™^½~ß•ë÷æÓ‘/ò|2Ð8Ý1ÖÞ|ö)"²–ÐKdˆ½cváVÆXñªjL„ˆd,"Úx  * ztt4®_¿nŒ1Æ¢ªª »¢3¹*b£ØØ¨È&õ8yïމè¾ûðÞ±+B7ˆ "Ù8E€"Ÿˆ°1Ù( ƒ‰ˆõýÉFÖFO?õ¤ˆìïYkÀ{GQ’¾é;½[s ë8¿ê–ýŠ ç®d郎ûUf€ÆÚóí§ æ,618ÿù]*"Âð•w…ݽèèV‘jGÛÎt¬ÆÞ;7Ìo.{Aƒ€< ¶‹ׂS8H ¨lPUÄ0t Zkª  ªZ­@­>®?©G[ë“T{ç±zʶœþOªîCx/ÄðFd°BÙ™zs¡±(¶vì–ÏGP¼€-‰ÍíÑÙ-í–:ûW5hçÒM<#)xqgÑ‹§ÐüZDݺ°hŽóܘ[öɘó|OÖ®.Æg¦å-¼ó|7î–KØ«ÂY˜ð3|*›ï]¾WØ‘1Hä]±³påÞ—H6: Æè`ç >Ó45ÆÚ$…"’±%ÊÞ {ˆÑ!"ÙÄ;Pa@@WdBÆF1”†#Ù(QPv9"!UUï´Ö"UŒ[£$<"Uev@"²%i5„h“w¹ËÆd"c¬÷޽»rå ¨ŽÇã = !ZCÀÅg?ôNeÏ×^òªk/yU¿@$¢æ6F"„ÕÒ‘ÈÐ ö¥óâÉΞ`ãù $½Š€ Ri·«hp´Tª{wÒ•¨Á+°Þ«Iã'e§×·Ë×¶ÝUƒ¶·Uƒ¯kÖ„¯‹T´¯ˆ‹™k­·@ªÚ±¿Ù×ÿTc£¶K"4ÆÎ0Öw^¤ 7~5³ïiлðÎ7Xz¶ïÅÞy— ;aˆŠ„d¢H¼ " 0˜E @ˆˆHd´aQ!!x ö#ÑÌì_«Bixµ 5UP¬×+"*€Pë@iýpþüš}‚ƒRH•êYþ;¸gk ×Xº'",„ˆ E>QïYØ# .œ ®»Œó³³Fô\Ž Õ¯Pù·b¡ªn°žËÆz¢- AªpáNj"Ó»VT…îïŠ4O= ¾æ%¸æ‚ßÐæ݇ˆœ~g!âttKØ;W—ç¿ËsQÉó£tP .aaHˆ 8Érk˜U™½¯ô²à?tÞ«jdE”Œ16 ÞÅð6A" xöFÉF ‘™y_A‘L+D¢2‰Þj¢µÕ@¨0·"ö@på¡°  ‚ÊüþQQM’ØZ“ ©ˆ+òp#DŠ;Ý~úÑÏî_¹w¸w©ù¬fBEJ窪ª–ÚÃ2Ñ ë¯U‘ „œÖO‹Mq(¥E8S ¤6g æExð¨‡_!,‰††ÆËÍAk ‘0£"‚§„ªRN:¨Šž|Ç.ØÂõÔk©â Jc^–jNÛ9£:+ðŒ=»„X¾QØ/ƒ=¨³aîáüDÀù6Íi³½Û®—rw¬Óg[­‹VOú¡¨ˆâ#EÚÇ%"BØQ²å;o¨Zm'žO49ºÉ¾ðEÁ¢*ÂÌE‘‹È4+â/×–Š+{%£ˆD„ˆÓ,·Ö3{"LÓÔ{ï\Ëm•ej!xc£8TÐÊ›‡HD&L6{¯D)"5_ ‰TË”Â(„U@mYD$ »«%œwT™;³ômßs‡fÇ`lTÅt:ËE6IÒéèöí瞊âd¸ZÏ¢æM©Š8 (-_ϪªÊëëA-@ NËS¬CSËšfb{p,øR1*»¡€f¡ÔG¤U‡µª0š 5A@•UäTh†ÊéØž—&ø¥ÍLJ=JÍá:Û£v®g/©ôÒˆúö2™}ænÉ*^42ÍøBÓ¾dkÛ Ud>Û†x±eVå ®íôÐ9g‘ÞñN„ú8ô$w^[?ÝŠ‚)ìÕ‡óK÷5“èTƒJÃ×åêdk­1d •¶š*±”6=$²ÒY –¿H™M#"ÚNAf®3UÊ”œ`þ¢¬Á‘*¢ HT¹ÑfíCB±Ÿ23¦ÒCp2l"(“xp~žÀ TºÈfﲩª¢â–TYTkÛ0zr¿ßÙ»(ûíÇE«x¹¼‡ànÅE;%¸ØU¡å•=­˜8+?³ná°š;Eû'EW¬žå§ôƒË!=Í¥ XÇ/6ØBMɺèç³NÙ NIŽî‹JTc£Ùùèõ£˜EK; ¤º. z‹0Ü©éÅZf¯ºo`Uvù)=Ãåy=§!JéR+­±[Ï>vû¹/ï]¾‘¬!ˆ(ìZöQ2`ïÇ3»Ì;'ÞQÅiÌ̪róÆ<Ï^þà+Ëè ™ƒ«¾ÈŠl2÷._ÛÙ¿ ª|óé/yÂ<Ýç8HÐ,wYîJÝ@UGG·`œ†(‰[ Éyvž“ÈSX*Ç€š¤»¾Š®³wá w»û·Ÿþxt¼“ƈÇvö¯(³s¹ŠäÙÄIÒ¡wE‘Ol”˜ö îF„×®] G|žMÇ££8Iw÷öo<þù/þqlŒüºoì]î說Õ'A`÷ØGdt>Œ¿®]È'7n–ìîÅÓüy‚HhV›#*ÂÂD¶¶ {wŠª°—“m%Ž®µ<(kìîž³íDù;ó§h郎ÄZÔï Cë$”Æ‘{Ê«“±²ô|ÖÓg3À×Uÿ¤‘z£„³Ñ˜ïó&æó‚~3¡qdã²ã{Ë™ñ/Ü ñÜ⣕g²á¸ 1)=<ØÔªTKݳôB”HƒÊûWÿ)¥¿®Ìˆ)㾕¡Ö¶™»½™>S3ÚŠ}ãŽá)³d™òá0 Ò”x l¹IKÛ·þ–Æcù/œù¿ËlO,Mb)Mç°ø‰Œ"c#c,{ Pì]¸/æsgNk[œ¥ÿæŽÝè½§hÏ9‰ëŸs€ÜΉt÷ ,¶xY·ñëÿè8d±/\wÆØme½«2»Œ5õfÜl÷Únºv­51{%€(Þm%nÑÎÃ\k}n=“nÞ‘(2zõ¾W*yï{ø¡½ýýýý"ƒÆˆ+˜ýè趪\¾j£Ø@õ)MCD2†ŒuÙÔ»Üåì^6QTLÇdŒ±IÈ iŠaajXc»W˜Ù™Š¸|%ƒd°¦;ü½ ÞQDœßR‘"Ÿ â`÷²âx0¼ñÌ“7žy*‰£ÈR•¬ð,2LZºmm˜æ…#kwvv‘ÐyÞP"ìÝ|:†¨! F†1IRPùÚoû3Ea/°±˜™mÃ+sâdÐÊ1ÆD ì™=ûÜØ8MÓP×âé§žæá 6ãd·$ÕþÉ~ïdž³¼`ƒIHcöÏ?ýÈþÕ†{WUe}¤|¿3³½ÿ{Ë>f³8ß8ˆ‹9»‘_ý¼). ۦrÓ™Åü¥Øð*o°óX(dÙóç°Û>cÞ|ùtì=„‘l7Ñi¬Ê›:rç „‚<ãÎÚ¨› ³Š_Ô{$ª*3_èbPê‰x±šŽnÅ>žŠ5D[%«|…ú¼F]Jœ¸ÁL¶Ûwz¹èóå_Õ[¯[¦‹?ÕŒÿš½}Á]óî»Þe-Æae‰hà±%“K‰hˆøÜDët¬Ÿ®0Ò8ó®`O7J³‰Œ!rÕÇ Ý1¯ëc•¾SõJgK¢Dl(¢ŠÖü¢áVÚZXù_…ƒ®@ÆQ Ãèu¶ûÚ`>=½Ðh`Šû”ªö ®¦âlù2 I-¿¡önÿ¾Jwï7ž¸¾‡q_ÙJfs쌜.Áƒ7ó¶¥`é6_ò&}, «<—¸É‚ÂUYE+Ž!—¹UÃÅ÷Ä*Ë 7Zîk{vëò5}ÌPëŠ6°eŽFÃ^¢J°Ë×§ed „â±ÎUaØœ {[ºL !W¤.$K¼›eÜÚÔTØ+{²Q¿ïTÄÄéÜ{®ÐÓ£×Ojé)-p¼Ô°wĵ¨ææ›Õ™5ËÎì\žù¤Zˆ¥ÁG6V$Óôòå+Ì\LG>ÏœËÒá~ÈÆ öbðIªªˆQ_á m4ˆ’ÔåUe_€‰â`w‚jÙðôdtEVV Dîí™ÛÏÛ(@ÄýEv:ØC2E6.2ˆT)ƒÎù,wÆØm69 ˜ec¢8ÖJ%™(ˆ:fÏ(Eõ`ˆ’ᾉâ§}ÈFÑ`°³\¥Hb‹ˆyîU5wžˆvwOKvµ$ïTED<’©M„âÁ¦ ³fL´Ès ÷ßDI“ý\•Å;2i%éÿÒÆˆ\dý¶Í*D¿±1¨0‡l#3K?ém q}<.Iî_#¶m]éJ¼9vÎgDZ‹÷±7Ëfåе&ÝFá•Jì¹U3‹h‹wÑ_+»Ñ³žY¼ÃõÀ¸Ñ@eÇ~Ùé½\´—«sV‘®dðZØãÙW½ç>.âËAD-ÛhÓL\^ N?Gôúj5B³Þ›b=˜䡲´T±kr6n€35g² ÛS<Çõߨ0€-.ó1,|ès…Ð=jû´¥jSqì¼LE.­Í²‹‹Ô\â_Sƒ9Él!žb3ÎÛº¦¹- pCœ†njÓ¬h(sµÖäw\F#¦ó\ÆéÕs\ÞËö$oiÿ;®G©?â\ãʃ:›£²±žxÍœègºta/·h컩DQ&_¸ÖX»w›i¨Âì¦ó]é¨<]E¸­ ÏÌÇ`ÞÍÿÄ;€–’µRñYÙÓªóë…å—4Û¿êþÔ ÈYužd’ì(³W‘||H&Jw/êËã(ŸNŸ{ú‰Áp7îØ8µÉ I)2« ” v]Q0g¥V[äû(b<°+ Â…·ÖFQd£ÄÆIhò-'Ç·äöqG>àóJ|…š V‡" €d’d ªùä¸üd˜Æq‘vvö ÈÆâ}h%Uß’†2k£Ø˜x<:RUï "³pÙ9÷Ü›0H¢0#ƒÏìêeí½ˆh’UHS+Ï=÷¬ÚÁÁõª„’_Ðe®¿ª÷Θõ²ÒUÙåD¦F¯ ¨¯IYK ·1öLÃÿKö`“MbÖxAÿçŸ.X@ÆÄõAefü[  7Ù¶el–‚(°kÜ,6…W[®ÍÛ‹ð–`©&ŠçSçQ"QºS¹åk+ÖF ¦Ã¦:blÒÐWOvdEåÁRÛÄìÖÑxÐX3·†K„OÅe¼dèl•»Ž³¾\«íúdUVýå·ÛhW8ë’hVêÁ‹|lçç©ílì 8—4רrTDj±C0—ÓŠ€D0çÝ­ˆšèu¬Ì¸:®0‹¶xVµžg†f…BlX™X;rç·i;å®FÊ—QÄND(ëõz´qÚõeôû!°áYµã¼!»¶À7Yߊ܂IÛe“Xè•YÐø¼÷Æ…¶Tk©Cù°½$†¾[i3ÒV±FàœRŽõîRÝl„6îØiüšrté}l”«$´Õ#³ÛܿՇ¶í[[Ô%«•½¨5Ã,QIºÝão" îûY^rc¬T¦æçX΋èè6\Èý¬®ÎSÊ4̆fS{0Ü ™Š­\%°TI)ªD$"Ç“I‘å÷Þÿ@ ,R…d¸御°áÛÏß¼uûpfX:0aš;"Œ­QÕiî"f†Í!Áo—/ï\*Ë™þ1‡m´L"]p 1Tq¯j#ATRµ×–EDR½#!j6ta;åŒx£ùÇ™e¬ ôm %  r»²ñ¾ÿÌö¤à²°a7¯^LK÷ Bbï‹gW‰È`>‹HÇ¥*òÑK§_áƒÇ/øriž› wŒ¬¨ªw¹°¨ªµÑpw?Pr‘±wQTW;Ac cí`¸§ v6N¢8’Aœ³ñQ‘M°Ôqè|Ƚ֒ËTê2R,¢ E„sÓ1Í2ˆ""Âø4N†U±Ca–Ê*—OP9M"k#kcÂ¢Ž«Ø("2y6£Æ¯ãª*¢ÂMäoà©0QºäBÁJfar†HWRVdj®žc‘ªêZ"Ë-ÂPG~¾ÏÛvT”ÑèN$;°­6k˜±uêP¯ J[ã,=j‘Íʼn†KCßW›ß Y^Á-¯xBÊÖ3„Ûtâ¹NtÖÒ*³Îå6¯ÙáþOµ’«u3*‹|i–pB6¤bÛý“Ò¬Q¥/‘±6øÐUE˜›©_Þ.Ð{ …Š©<œ˜ÍÑÕŠ qæy¬²¨jflà ÊZõ‹hËéX¼“®w´$ºkNB› `NÛ Š_´êU靖Þc ”…UmE(å!a=•+³ô!› Ì_¸¨2*¯¼vûÖv>­å ÓUšÓV)ù[T’’M³‘/ÿô²ëeŒ+X‹Oá ^û0«ýôp·\Ës•[ ýŽ{)ÛËÒtòËD¶B²²1fqºN×±iì:ëLy–M6êÝkÕ«ˆÛ+äI〠X~U'ýÕ’±b!hz-ÂÆ©ª°saÿâ‚иº.Úi ´²±—ïyéþ•{òÉ‘°?~þ™éq––É& *ùä8€]E”½ì\$ƒýË×Ô{§%sB)ÉŠ|RE ÏÎùýÝfã#Pì]RÕ8Š™nú"Ÿ=r£‚iާs,âòé*7¥°/¦ã"› ¢±‘/r(Ù#Ëü0¸þŠ´!ôjg8HböÝšǃD46fyêÑýKû—¯=óÆ›ÏI“:!ÂÁ`HÆììîU¢­ i{ç]‘¦ÉÞÞîs7nŠH9DŒc‹ÖR‹ó %(¨^€1ZrjSƒ-lVtŽ «Vá ÑÖ6”ª²G¤;‡aX„x¾Ÿ´xi¦×à)')‰¾M+§‘¦žµu~|°* ºÒ&fa"klwÞµõ%~aF¾Î'¬¡È­ãçlE zµAǽ.¾ÎgFÌ>\˜• ýé¿ÚÈõŸÝA%Úqi’EÕ«ò-ã[hÔÀ¤ Êez–!“ªó¸HQ¯£2œS'riçqTa$f„p5ž Á²†ËÎl F-Ä*¦‡½mf®Vdû0«gª íAn 'šØünr´jÕáQ -=Ý–é0œw|Eïüß뙺›™›Å ñ\Ë=ž|‘à¯vG^Ëá\42׉˸„é'"ÖDÖÆ®ÈƒÉ…ˆÑ`GÙ{Wì_}`ïÒ=‡7Ÿ<¾ùÌpïR°xšyؘv™wE§6J³é8Ê3ïƒè+Êo%ƒ¦¯Äû¢ÁIP‚Ó%I¬*ù¸^„y6Žâ4ŠSWL]‘…ìLdl yòE®ªâ]¨¯Y³“Æy^&—Ä Š²ˆ‰ƒÅ/èÝtrx#¶xß½÷xW°/#Ùt,¢ql )ŠÓ0 ÂZ>IÓ4I;굫W@u:/°Öå IDAT0óhœCI!({‡ˆ6J{jžx§Â3À¥ ›—ÿÃæcûÎfþ,#2hLÓÒ^ñI¶aq5&‚'µ=I¯*ÊL¶ýÑ`½5K“ž¸ÈvÝÞC¦‰ <F½Ž· œîõ ¸¯³6ZÚg+~†õï¸5§mZñÜ,ÂóWF6Øйv¢«¼Ûkhþº0ß>ÄÅJàþgé ƒ@çâ3»s5÷nIO^šeÚ2°€¹àA§ƒ8G¨^=»|m2-àMDªÍÖZ#ì]>%Ä4M‹¢PÕÂ1 ï\‘" kât Ì5Ò_؇<¾jm`˜ˆ¢˜FQbKJ£x';ÇG·TXLJ'ÃJô³EqÅEž[S&Z Iº£ ÂŽE¦¹Wȱʸiæíì_ %™"k"a“""{—+…%Ö’ˆ†—ˆÐD1¨NF·{WŒº–‡B¨Þ,€ eX޶µÕUë’C-@ý:6 ¨¬)©CDvþþÂ¼Ž‘W÷p™yw¢°ß‚€ŸV ì|;j4£CsèzØ –‡D„²²K“E¡¥—4¦{£µ±u $¼`¸kF3‡-ý¨f½i\±øWZ„[T µý¿¸ô>Ú7k[ÑWês¹y-à4 äAeìŒ V!*Üp‰`C…\}žâ‚y¨Ig˜¹$óly˜*åÇ9ŠëÊDš±›–(s å +ÙPZ¢øh„¬iº±A•F3bo¢2Ö)Z¡àµm‚·¸ÜDu†”ŸY­u²ªôz`Œ1![¶žÖ!ÀÖº¬È!BŠ)‘)Áe Íuõfe™õm+Îzâ“æÔ-×dÖ>£ìBÌÀ ¨ÃqÅ oëdÖ»{¨·äR±kêYX€q½y±EÜ,¡FsÇsJ‡Ã|®0¬4iýg ÙV‰@»š|H²oÂi×Ql;Nÿ[©ˆ¢ ØÆòÖ'µò,U=h¬¼yRÚ‹•;뙪_¹>î‰L]ªKö“€b°È32ÑÞ¥kª¢·Ÿ·ÖpŠˆ¢i+°3c=m”†‡9ï½/¦–ˆ½ËÀD1ŠÀ Qü ,n  XâÃêŠØ"Ìž|ŒÝð~žYDŒ¡–‚`mŒä¡\]b YC:+óÊÛF ]£YUEÔ áO˜&‘µ6Nw4›Œa¦æ=3 bN•%EA`P¶ª–î»%˱·èü:Ó|'3–¢îb^Âê^ê¾UQlVDõ~©0?Té­ýÒQš?¯ŽyV7XPìïâQØ——Õ2ëe9h/Mnœ D´°¤—¨ ®•Ù:ÙçÏêµå•.ÁÄS'´Ü$sXc=¯Õ G–3Ôë­¹8«RÕí´kÂCÁëŒtæÕ&¼´ æÕªŒÍÜ¢Ò10ÿžŠÞ'ÀêöC_ :¸‚ºtëòlÇmÐk¡‹hÍ÷q²RÚ£±~T…JK(×g"ðe›F]V""cœó‰‰÷._U—I˜EX…‰ Dé®Í74‘„={çH"D4† €+226\ BQæ¦Á‡d”Ñ‚#‘PfEdBGÆK JžE„cí¸Í±J.àZÊadkAÂLdÈ…yO`V¢’ñ_¤‰56ŽÓUõ:«UÕyff€Ü!c)ŠÆÆD¦$+Põ$e¾d9(øHHõr ©™oº¤[CaÃOÛ‚QÎzS¢Î'ž,Û/•ÊeZ töÏεþÔn¸ð«TçÎöûõ±T t*0É*Þ¼õ0ûÝ;#"PèÍI†mÉ n®8œü,]³«);ë™ç=íaû¯›,³ 㢷Vö¦ãQA]ÝêNÛQ«½ƒÐóâ±ÂUn¾hW»êT œ9é*¨¹¨€`I˜åû)"–ò¡r]–™>d°Z—Œ° —ÞL/™%Í–*’Vú¯Î° R.×àY•V"Šˆ"©ˆ¢ª!(µÝ$­1lg"”Ð €úJüã ö^sV£BU¥Ì öRƒPëjVsc­A³ŸOeRÜÞ_VÙh¿«Þ¼†‡*v–=â¦+¹N8Ó 3Õ.ÚG·ö\cÄžÀÅÂÞëºGœêéοF´Ï|^qPÌõ­a·nÛC'™¯%”ëf>ö­çö!‚]=:W_©B~,ªYÕ~‚kI6vàŽ<Ù=¡'Ý\Å{4ÔÑb5^‚¾o‚ÈØÒ¬éd"„º.*&^J¯ÚÈîr&4r¦:¢tf’ADïE„‹b¬ªWïy@³ÉHUJºñÖ&‘XAÈçV- sÔ,—qëæÍÉtþN“hwŒŸfEÄÁ`bod¢`ªRU}þÖ-Ÿe—®^¤@ÊÆÆ6ŠÉXc£²„½w"š·<;ƒÈ˜hgÿ’ŠÞºî¶wéž+×îñìÄûÝË÷ ’ˆ…lr\*õgÓétÖŒóì<4À`˜ÂÍçИ2™È¹¢(òPt¾¹ã¢x`Œ)ŠÃùŒât¸»7ŒŠl:™ÞVÕÈšš›4I‡šMG ÂÎ-¤Jˆâ-VßèâSTèlL‹€ÞËW]ݸӓ Âƒ‰` ’YB±¦ºÅ¶QãÞ¨gS^TØë‚=®*ìòõÏÞ%}eÖ"Ùà\š[HÜÑ¢1ëntÖ-wI^< ~jn˜ ð/4ù…úžÒ%}~o|îjøJìÄŒ¥lf¾´ªD51¥J2 ‰)(h‰R¯A|5mY“Ƭ“ 3>8­¡ æÆÍê. .nèÔ²î–o²ª…@“ ¯™® %ľSÇ~ͳ4C±©ƒëݰ·¹q]­¿Ó±Ÿi#TxMpÇP¬Y7ñâ^pÑdö€j¤)ôÍëY_v¤D¬ê °w5®«×Õ^Qˆ5E}7ŠuÝí†Á$m p!Öu‰azÁ†E0á–N%­Ä¹U ˜_³·¡ŠzR< ¾¥õ/+Ynl¬"Þq’\¹öäã¨J[DØ»tM„]ž©JÍŸà½cöáŸû{;T¹óñ±‚ ;Dì^ò®ðEV°µÑЈ8 MGÕ›–ÌÎ",Þ±w6N4‰-¡&Ibl†g (—ž(Ÿ‡‰P‘(IEÄ»ãÁŒ"Ń(Nó|âòép÷ MFGÞ—®Þ£¡tâ~²ã²ñt|èYòB QUÖW½ËÙ7ó}ÔÓ€VfŸ¤i:f“cQ) /¬ÃA ®˜®ãbïNô«·UÏ—ÆFåÈ4î_A›KžŠMÁºžb޽è½ÈWßh<ç–>)*7"XŒ;¨ðΉ¤Ë0”k?C**;Ucc@v ï|`’@ßxE²¶>–õyNô‘©È7„ËKd0™kÚòãk†â“6ïÊÚâ“™ƒktn°9=[qqËå“WQ…!.^1w í”®Eœv ÿVoºNùï’ˆÙ‹°ØªNY9¡ÚèQÍP TE ¡ (Öi­~ˈB‚éð™Õˆ•í€[÷ÌH |CÔ£ÀãZÎUÑfÒÜ!†Mä7¢mD2Æz¢€¥¬©ÈS<ÛÀÚŒk‚‚*…2ô%´É²¶¥e¶ê·ºj‰Âé뾞êén¸„—ü¤¨ð³§d Ø|pïX¾ù~âÆ}Öž!ì¯Ó®(=I Ø€»œ©97ÐñÖsèN/Fs¹!‹p£ª r5¬W«]2;Wë>/‚‚6Ú÷ŒÆ*Ízb´‚ºCn´©–†j×dì læ”AÔs€ÂΙ!$4ª -)…q²S¸"ŸN“4M’”eÂ, HÀ½“´#dam„ð@]üVU<;fGlÃ[†pg“#5ÖÀ "¥ª * ª »¢vÉžî(NÉA@ÂRUô UQTv¹ÝÐZc#BDÂé4ÓŠ f´²êÒóZY´Ö‰bfÉó@#KuˆtŸÃ5ä õí#^ÐJRK§’# R X„2þ7Iõ¿`è9s’w¯<æ5ªúøZ¥C¢ú¬[ù”ÙL‰†ÚC£}Çîb}Ù¨6ûÖÒ‰G]\Ù´¥ÒmÉå¨Ê ÍàúWØþ^©(WìQ ÇL#¿üÌ_á )‚uøò\(-Ol•Òž{5w?nôìjX¯˜±œ@DdTfä–óe©µ%Sx¹R³iÙåfEï¥fSŸTÀFÍÂpœõÐ^k‹ä#à »÷×Ê;EÈì]‘Yƒ¡(SsÂ)J‡…ç¬ðñÀÄéÐ;ÏÞáBW’kó«å>¿+5Ñ :‡>{Ç& Áˆ U8€1$Ò"Q ¥~ƒjg7h‘M¢8âAMOƒdB$‰Eœç} ï 6²¦¤ÅãÑDDÂqY£:£ØEÄ@eSe¯–ÂÁƒÖƪ./<ÄÖ¨H¥,p戆ltâ=¡"½Ù½ z=E~Á)Ø©÷CáL‰žì\&S‡'æg½åìËî”…”7D ³ “‰jnö>ýucA¨Û›  „'Iõòޏ’ACK–®"š@Œµ¤¿`mô™~Þ¬×;Mlãi¡ ç.—šøKËRŸç³ƒ§Þ´µöÛà ~¡ÚšD Fc…Ÿãë Ù3/%.Òû[¹NZ™U&L·qË…:!ÔÕ kfÑ@QÆÌºî[#µxã`–nÝ“—Ñàói¹ëü— óŠhEþ.½þ± ´„¬Â’*¶Ý/X[¸Èíø‚J²YPÙW1„ÌßGgd4ÚONܨÓÙtm9áOp–ô?zÑ“¦ª/ØÕ‚¸ÎŠj1 4ׯœ­Ð¼­…v‰çSU¥·l”KÞôby=rÓ|Üö,¯‚6¯ò*ÌÝn-ÜhÏYÁ¾´oL”$;M´(ŠC þ=ŒÓA$6Mñpw:÷*ÚDÙðޮȱdh<‰s™ DQ”†66NñxRü•Ÿû'½g`ÇW/í¼öÕ|Ë×Üûàý—#kT¤7µÙ!b¨1عT–Ö¢±qo¹\ dÿ°|ilE„YDuš»Êy@×®îi–å¹ ‘F_äÞˆø;ðÙÑ$ѯyðú¾ê¾ÝaŒD6J ÉåSèUœÈUiVž«|†ZqÀž÷%­ niÁ‹v˜¬@)(’­óÑš÷‘¸Ñ×C®j‹õ[áEvbêa|ªQ… ý:çÕÍΤÌ7–U.ÁРӦºFDw>3Ë SoðCó®øT%c±i°ñ•5çàM fý·6Ç_—Èçè…ç â]<´¡Uï~fßÚ¼à²è\•×^¶AldUõÁXÓQ£ Ó¼øò3Å—Ÿ¹õž~æ/~ß¿ÿÛ¿ºG_ä({‚uÁŠ ú(fÒÆéÐÄé`ó—:ëÿ;ÿð³·Ž¦àYÞøšûEÏêO1Ë\wwËìÖ°l«ó'À™^ÐëeÁ‹PQËwÚØ%rU¼p]5j¶{Ïj%u›Ê¾®Œ¤wW/(k,˜†aIùoa¯ìÉFë8>%l³Ë!×@þ¶ÆvR\3G·Lsm}' {—gÃýˈx|x‹™C÷½ý}UÝŒÃë÷½$›Œž}ì!iЂ†öÁörE†D»ûײéh||”¤Ãt0, ‘QUW¡eˆLº³¾ÈT´˜Ž×©pÉ"¿ú}Ë7¼Q-\œ 1ÏÆÊÞ˜¸.(è]Áì¼+‚4ð€³+{7"º|ZËlÌ4çra*ˆ¸p`­½qã 2 ‹<ý̳áxf–YᔆXsžs'ûûšMãÁîš¡%Êû"‡êšöDÍþþnÞm‘©·Ò<¥¥²ä'½6JÃÛáÀKhþÑÆ5©ì¦#sÊÆk®ŠS©+¿º@q+ç[ï † õx^¶‘jŸ‘ºÌê¯ÞÿÄŠIè"´BÄ Î!ö‹óÆú6 ªêŠ}5º6"ÎÑ=”%á„ QG°wå `“¢ºIÔ ¾ûÍ_÷ç¿÷›Øª’eù“ÏÝþ·úìC>¾ýßû‰o~íKÓÄb•©V÷tÞÒê²%—hù®a: Vb›"|1$¤Èl«r͋׋׋×E]vM5ðü®yçoCŒ-1„=ÌEÎ`QÐ9†”¹'z‡ŒF¿¶Ë3°Q\â(f im’ndB‡å _j•Éè¶‚"Jd)J"ì‹ T‰AÑ»ÂØh¸wåøøp2Çq‚̲,S…Ü9ôþÙPÀ½È³¢È#k1ÞsíY=edŠ<+1Žã@çíò™¾6ˆáž]@ `–™—^~Ë×Þ÷÷þÅïüóOÀh’àþîo~õÎÎÎïýû‡ÿÏ_ÿ8Øüßý‹ûÜc¿ñ¾O>öÌíŸù ßñ–7¿©èÇzâ}ý¿q<ÉãÈ\Ù¾æå×ÞüÚ—Ýsý§Ã€ÅöÅô‰gnþæû?ý©/>}4É/í Þòõ¯üóßõºÿá—ßù©/> ßúú—ÿÕ·} "¤‡8³?<¼Eô?ýÓ÷}æ‘gëþ¿ç#_xÏG¾ðÆ×Üÿ_ÿÔ÷¤ƒ]cl‘çïüݼÿƒòðcO¦ÃAòÒû¯}Ç7¾î‡ð-;ƒt‹Šü¢fŸ¬´*N`vlå'²UZ²`Ĭù”EÞtè^¨—ˆÜ9YÓñ™ ¼³®%‹lùúë/fváKV·>Í:ìM³¦ç·HªkôAk÷Ï;­°&¾«ÀãІœ£ˆ†I µìop­µ@FP„쉚Sš˜ÎZ´ `)¡ðG¾û AÀÇzê­ßøªÎà|ôÓÿoïxù¸ªÊè¯üë¼ëß}v¦CxO‹ÇŸ¹ýÞ|áÇ¿ÿøGþô7!Q°?ýðÓ?ÿËÿ_áÊÎß¼=þÍ÷ò‘'ŸŸ[$*c¬À,Öâ™WE¤ÃñôíÿëÿþЗ¾\s4š|úó}úóýú»~ÿþö_õƒ¼¨•Ÿå‰¢/‹×2A¨ªwй3´-? cKt|(J7Çm¦Uù‹M’®ôÜOÚ*DàÿZV~á5%±0;ç†{—1›‹w¥pÒªrb°PYsaˆYQâp84Ö:çL2ÜCÔÀu„Â^„É$²Öªª1VŒ0² „B¦ šñtö‚6J‡ûe…÷Á^˜”×\9ØOàËÏ^¾zݪC­yáé×ÿ°9ÓÉ蓟ÿrS îí$ÞKÿù«ïúè·½áå÷]@˜fÅ/üÊ»k)X_{èICØyDDæ@lªÆ˜õ[ßðòï¿ü¾?úb^xxÅW¾öÁë÷_Ûs^¬•Ÿÿåß|èKOöûó·ßþó¿ôýÂß:ØÛiP}ÑãúÂ+Þ¸^@n+¼ÞwãzÓ>`òÖáE-‹„óPPcAE ë µ ƛч3‡÷ùš…˜×œLq…K‡{H”MÆ,êʪŠe<{ažˆ¸³wÅId§&Š»— ÈFˆÙÞåD‘Œµ R‡âtÀ 0#S5ã|ÖWcâd¸þN‡»¢ÂÞƒÂËî¿vxü8L¦E²s‘Ôü¬pPÀ0ßðÕ/Æxe8ÞûáÏ•"p˜¼ý'¾ëúå4‰þðãþãßúp0R?ùù'ö~û>s4*sÁ^óà½?þýß¼7Œ?öÙGÿŸó'ƒ¦ìpU¢/Žm”$ìòï{ók¦™ûà' ‚ðk¼þ£ß÷&Ïì=¿ï£ŸÿìÃ¥|ÝW?ø7þòÛ^ñ’{Ÿ{þðŸüÚ»ÞÿÁÀ­Ããô«¿ó7ÿê·•mÕ QÞ‰ÝZ ô6ëe’/` 8Á‘²šâãNžÙSN´}q«Ì+GMÏvª%Ô Ô\þÍ"7æ¢Yì‚·—Ȩ%-gH©EE±7 » Ud©®§ÐpÁ7¡Á02ƨbE?$ª¤uvkZÓ¢€*¡S„Å]ý‚ÌQÚ–V—«Â9&³:¢“Éth›t-/»ïòß}û_ÚMÌñá Q +Ão~íËà ¯¾ï¥÷^rž ™×¾òÞú'㬠càß}ü±ðÉî0ù¹¿öidUåþ+ƒAbÿÑo~xNoƒר”ƒÛ©Á³¡û7*MÒAÿ¿õS{»C@|éý×~î?ûK?öÔcO>ïþ½?þ™Ÿ|[’Dù»àP=à‰Ò~áŠÝ¿îÏõt‡ÌZmpƒþleÂõ †ô.qÞ!ý8}v+i>ÌšE¸Îyü¯‰R/k”O ‹ŽŒU]kðûà­{7Ùãj“‘È,-¥Ý=Bã(ÝV€i–Îgã#@´6RÕ<‡ìKfe–ƒ«÷ìì_.¦£Šá ²,3αwˆf°{Àì¸ÈƒÙç}ÎÞY`°¶ÇG7‰’áj°ÉH(­ªÂJ Ž‹ªåx2©ÛãÛc‡ÅtöÉ|ï7\ÚæY6gDæe¯|å_ÿ±{]1À¬p?öôÏÞzööô÷ÿø õO¢(¹|í¾iVe—âcíkYqžáWÖ5«wŽ´_[¼¯@ ϰê-Æð|=µNËÈ"ÎnÚ x×;옌†mðD¥r6ªATEé@¿`¨èÖ¬Q8šV¾SÚ¦Â-²’W¾ôzƒ"x†õCŸøÒo½ïc=úŒ.AÈó‡£úÛ®íÏ ªÄ«;O<׬ʋ59W2çœQ„ÃQVþÀ½W˨~_~ÏÞ¸}'­gl¦ÛªÉ~‡‹«*í}%ÆW˜ExšÌ=ÖÉb wÓ¬9 %â9Í[ʨΨNðë%Œ"Ùø¨ú¹Z×yý«îÀkgæv^øwü·Â߯yÙµÿü'¾çþ{®²wãi^ ÂP‰qw8#3zúæ14ƒšª7'3o6!ª ®ð¦%(fû;ƒšÔ퉧ov^ñÉgŸ¯ÿ¾~õàXç¶ìëÿÇ òEÀË×Ý*—;Ùe½—ìB\¯ÚûYi7r>”-øÿ‰V>Å{@¿ÒŽDB Õˆ²ñm ʻ˧®ÈÇ£c™n7 ÷/wÎ(-«ñ £5N€]½òt2†Pí"c#,ŸÖƨôña IDATª’OŽeŽ”™Œ!2Ó£[HôôÍѯ½û£õWßúú—Ã\ù!¯ì=û¬ðDøä#OdR⇿çWöâlrh£ôɆŸ“Œ%c¯^Þ¯úÄ—þ“?÷æØ„Â‡üÉ/6ÒJ’cíèµîBÅA™ùZœgÝa¿}<ýÂcÏ~ññ_|ü¹þË¿öîþ7¿ø/+ôÚWÞûU/»ÖÑ!f“W½–oÀ‚U'"ãiñ¿ó#óñ›_ÿªðïç&¿øŽ÷>ûüqáüG?ùè/¾ã½ó†k ÈY`Êd¹¯™ãþô·]øp<Íþö/üÓ/=þ´¨>sãÖßýÿâK?¾ú¾ÿàÓFBìy®÷*AW×ÿÁ©Ÿ¸bSli~Ù”ÚÿÎ/ZÕk[„K48¤ aãÔf—êJÓ(ö´qàÞQ˜ÓEÕ‚‘‡ífó=oQè²w¾ÈŒI1€d¸G6÷Þ1ó$»%Â.Ÿ"YE¢Ê,ql‘(Üz:Ím¤Q:XÔUcLÝ UFW꼡æPcÞ?ðG}àê½Õî0ùézs•¢ÒZd"PEÄ$¶ˆøòû÷ Q¨·õ+ÿú#ŸøÂS,ò釟™d3ÓÓ{§"€ðCßûõ¿ûÑÏ@ý‡?ùè‡?ùhÓKQÕd”4MK_YÅ—õí!çŠA\†ïc_zòÆÑ×ö©/üÔÛÿ—ÎíïúÇ~ðÜŒ,Ë+ʶ—¢,ÚM*¼ÙA¢›ÿìRÃl‚€(`ÀbÖpØ7Z*-!€ämR£nãJÏQUP)Wݪ€C»ðº,é7†T«úYœ?ÆyYmU­ÎOÜŒTkáW›’31j}ß]*D‘MD¼]¶ô¢j¡ñ\/ðlŽ…u1vz÷Â’ ¨ÇU‚°Äö±÷E–î”Yªñ`L$ÞyïàÖ-f_d“(Ø(QaAc+'Yž.3Yk›SŒˆÀØ~¬3÷\Ùû›?ñÖëWv)°½Pë¨*3PãØ"`:ˆßöÝoü÷| œç}òÑ0*o{ëëïß?|ëhÏŽTﻲ÷7~ì­ÿïãvÑò¯{å½ÓÜ?òäÍpž¥iŠˆ6N]žåÙq°–Ak©(Н{å=!¿Ôyþì#Ï&qlmTdãŸýkîçùwjû¯yìíüÏÿíO_½¼> fÓšì›ÜZɰ1é¦?A4VUêV 2€"a/"QTùJX… ´Êz7ij*#™Õ\]WÅ’’TÕm®Ÿ—8êÖ0—Ëûè6gpé©yøÀ]Åf§*ùôˆ]~Á'ί|üÝrÕYÛÏe‘©àádH™›¼XêÒ%ÒLDËHa¨üµöU L­Ü™‹ŽhDØßÜ{uÿ;¿þU꛾*ލ.ÍZ9r[޾P‹WàÇÿÃoº~0x÷?÷ij‡qd^õ’«ö-¯{ÓkxäÉ›A¾ë?óg¿ëM÷\Ù€o{Ã+®ÿÌü«|æ3_zz<-®^Úùö7<ø½åµ?ûÞîY£*$" HDÁµˆÁ.üÑï{Ó M>ò©ÇnÜy–jpù`ç—ÿÎñÎ÷äw?üñ/>úÔh ÷®àøè¶÷©vÔ%6Åq Y–±há¼5YãYœçKû»ˆX…ªŒ§…1”Dƒg²Þîãi6ÉòabÇa^<óñhBDW¯\vE‘eÓt0LÒA¨©Tzb™U9ݹTÙÃêòi°n˽+¢(5Ö*@áü_ùïþY`Yû¡?õº¿ð½o(YFmE cG£ƒ½!²Q‚hŠ|‚”AÔ\»ÿúK_­çNÞß‹ˆXg],Ig+7ç‹$%’­ ¦™uÕvùtî–bDtENDëïë"¢”(8/ÑCÉ̳a2Q-x„Ýú'KËÖÜ`òÊÒ©)ï¬â 4æô9G/ìKUFÏ?íï*‹p±°Ý²|/^e©S‰¾¡½6jÿá «€óöœV‚±­àëÜ Ëv¬….s\­&×ç]´3oŒÖ¼gM=¦?©<Ý×Ïù{ÿü}=ý|ð¶þ—?ù–—Ü“"¢ˆþú{>Vs¾ê%W+8GWBmxžµYÚk#GBå^½©ÄÓ,ƒÝ[‰Þ‹ñB§ÊÙîÿ!ÞÕ;±=Œ¸öœÖ5A_<—»ÔÊñì Âfm3ýWæŠêu€±c8ªr;ç~»PýE)ìÕÝîýù)AK Óù"Ú‹×o¡ª"llœö‚rj}Å鎈/²)3#¢0»"'c‘L§Ï<ý”*\½v}/ŽÃ‚ À‰€ŽÄx°“ v²ÑQ> ‰b"“­¨¦;<[{d&££|2FD@Ú»t…Èî³#2&ŠÀ;, ï2Wd×®ß;Ø»4=>TDTc­w «Èñám/Z8žd…êaK¤9šNg8úû® ?ø‰/…¿ÿ«¿ÿÛ¯xàJÛ§nÕ Â{¯ì¾é5÷7Œ-ÏÞ*ö8Ñt2aöõàÑÁå«ép÷LÏ#Þª¹9ã!›GDôymsÒ+´’þ}‘/Ïw Þ¬ý¡ª¾s ˸k«Ùh;9ºXK„—¿ ²÷"ÅÉIïŒ*¬À 3Ý ç«.³6ÇYJ¨†^«~Në'Â+C•ˆ*Æ¡èЉ-Ây`졽Õ?ÜŸ™l!hØÈ?E µ)$ª42ÄÚ¨…f`FEïÆfòÑL^ÅF†n]>$fÒÌvœ©Ée8šŒà¨¥r¦u‹J-h¹j›Ï€üίýüã7>õð3àY¾ðøæÐ\=ØyûO~—i—")—?6³æf«­Ís¨âŠ€w®Ú¿^…s¶êøúl–L„[èg?ãÝ`?ã´¬N $ÄÒÏd¥V»ì©L´Híkc+éí» î|õ‹¹Ø†žiqSSrý'³kqÕ¶’iI㊉l”L]*5²0y¡èÒt:ÍóܲƤƒa2Ø»§¬Èìr$St¬S±ñÖ§'Óidˆ¬5ˆh£$ènÂÌù4ÄÛ¬lœºbêŠCö€"^™t°{¹^ZÂ^A•Œ‰Ø»éøÈY"‘OFH´wp¥t ©Šp‘gyž ‡»q’–>J)kDÆ&)¨z—ÿ÷?óì³¾ÿ¾ðè“7o{ÏÃ4~àúþ7¿öÁ?ó¯CÎj¹é=GQ§ {_ä“zÕ!b:„]æ½?iÈNù,x¹”½ß K€cw@ßóplºÙ³˜ý¶Ö¹®¡ã7/ñ¸‹³“®£ x¾5?0Pê«°ïʨwú¹†Œ7}à}¯°Ú´Ráêü×þ ¡ê0?§PZC ­ëÚí23¬¡;NG‡">àh,ÀjHÌb™º†Ô½ËæEãîdúg ÁnärâlG†¬f-!RH¡b­/kÿö$ã1‹+ Š-©¢†ºÁh¨ -k%}›%Q¡ÊÆÌ6¡,¥I8ËÒQT$¢: ˆÁ†E"2¦lˆ7\‰ÈQb¨ßøÚßü¦¯¶q29¾•OGÇãö÷¢ÈæÜ9”*¸XÇR Å™y|g¯v]ócÝüq[ʧ×!q›· ±çs<<Ý|¨E¶p¶¹zôÍ´ÚÚ/´Ã+·°»Èݹà—hcµ¬S)­>ߥ‚ª[ÉA—7h6›ÿª¿±°°G$¤ÕÄ섹–‚q’¤iE‘bTPaÑ:%„Â8—õ+ˆDD… %M"CD„EáÀy€ƒƒË"@d”½_„ã2N†Ežqe?!"°÷€ $~“«O¼¿öÁ ý²V³Ü³y®"ƨۄlù~¾È!Ô>T‘àð RæB¤Ô‰É*Ša_h…61„Ì^ÛLƒ”¢d`¢x¹²¶š¬ Wó–ψÖQÐÇ[¾7¯Ý0Œ @g`GBE··þ›¸ˆ~l»ñ]°«UeKs©ùé6cCš`†ÓÝðÎOØV`‚ÃV& qºKdºÌ2¡Å"Öôš.ivG] x>š»SÎgÖ¬)ùˆHMö„¤ "^DÄ;‘®9Žãýý§Q2ÈÆGÅt¬õ}ðÿgï½£î8Ž;Ѫ›¾ˆHL`Τ$R–¨œ¥cY²‚åµÖ¶V¶×Ïö“×»«µ½ò“ìõÓ±Ÿ×–,‡cI”WÉ’IJ$M ÁF$r¾tÓÌtwÕû£gæÎM_P¼‡çðÃܹ÷ÎôtwUýªê÷C&›vÇ£Tž%cuŒïPl4cb0D4<ºQ`f¶Ö5ÚŠ~¡dŒˆs .’Õ€èfmÇ´f²éæÍ– [+ •¤¥,¹0¥¼€±®Qhu\9¨a&)ýD”ÙèÔ‘µÖf( ˆ6ÖXÏ—€(•ÇÌ$5@KÇ„@²šóÛ±¥b…ð‚¢Rþ4Íž³5„L§4ÝØ¶pÏy(o˜;wsÓªs$Nµ/¸ˆ'Ïû#‹°;-3OœÝS@¬ê¨ DA¡FóLØl‘Ùö½<B€ JRjÞ³ò%8ñÌ·Ÿ-×gy2ç[\»:‘ue® »6¥¥(ÇÇ«ÿ龜}æo¾þ¯¾,óÆ07“’(رÁ´(Ise/ÌD–˜™ûÙ†N}ðGŸ9ð™¸Óýý™_}ëšåÀôÀ“{ÿ쟂Ðÿç7ß·ñüsû…Ã~ÓrhñХɌI/~zƒî_è">D´dóÄœßÛ´½kiýªáë/=ÇÜòôÿ÷«‰öÓŸüçw]¾qp¾|f¼ˆÑ,Õg… ¶æ0 €]Ú2³N¶õ³ìY«ÌK¯™#]œ¥¼€i“ÿ!žâÇg$Ø»Úó~‘Qs¢Eu!ÎÞש辟ñ;…T¹Ìö¬Š™ÁıóㄲQ›´ÖVëM(dÛ¬MJ?4Q“ˆñ¾ÇvççØýí~÷k¯v…6B(xqÅq¤”+%Œ!kÉ FÇQXgr¥1l 5tÜl†&Ö&Œ "Ž˜ sýDÖê^ÊyLG|­¼ÑaX‚¤f*Žãl…Š Z1€T¾ÑZkkBkøJI¡uËCJyFGƘññI(¦ßvû¦gCÍën¸àºKÏé^÷& Scˆ¢48Js©ZÜŽf6q]H_*ÚÕM3Æ((¶r ‚B…ØrZ('„³-‘C"Óý‹ˆèjÜ·‰âl·‚Êó‰l‡ÂO¿œˆÑ±R.xÇf2YN>iÕ·qšïßkÁò“/Ô°õŠiQymâ8ÿDénð‹ðŽ˜{L+8x¶w ®w/0fJ RR”6SIü÷‡Ûx±÷Ûd,¿Zîd"b‘\[Úî‹}žNª`ßcOgKºßkS(„vV6§(ؽ­dk&4Èó)É…Æó@êN73¬Ž9ó¶þÏ<¯áÉ”Oz|œóT óûòY}ª'[?ýœ~'OóÍ<;Mž,·’—¢äEð*2äOg9ì ò’R"÷4S¬¥á]çž-¥BįY‰RÎSrˆ9FQä68"Z¾z \¹®b>´g»ÂZ-H 1Ñ®}Gëøž»zæo¼¦Pìh˜¹40Šˆ:j’5õ‰ãÜê¯c¥”çùÒó•òQÔT:nrrÊZ»bù2f2qˆ~P$'ŸÈéµ^žøAY*ïŠÞ§~>ab[µt´±0<%Vœ{¾rìùƒQØð e@Êg&'Íõž’¾'­åHO)D ´ÇSjÙÒ%î4ë(jöçë–ýæ‡^íþ^¹dPy>8ŒèCD/è·‡: ™spᙬt{7¢D9«(°£‘ÜéA é'ÁS[ ä($ Åä¨ÅærËR ”ÖÆÓ; …B2™¤WQÕ©O‡ÀlÙšÞ'£ õþ渹[mõ­“¨ï5ó,£mDQ( æÃôt´N#€Bgµ]@ÄÚøÑÆÔ˜W(g³ý%¨óEúÎ}Ãu¹lsÆ]oUÏkc½ÿñ„‡¥xç­Yºm÷Øüäþ¼ñÚVà߽ýðqX¹tè/~ë½›ßsǦ퇎Mž»ì­7o¼ìüÕY€ø{žúê€å#•ÿþËoxø™C÷ß¶éàóBˆ‹Ö-ïn¸tÃÊéoý™=G>—&ÿä×ÞyþÚ¥DyÅæ¶Û´é±‡ŽžÔÆ ”6®_ù¦W_}õÅçv|Éã;ŽÜûØî½‡Ç«(ðÕ’áÊeç¯ºåº 7¬I á§¿ð½gö<Ÿ}äGíüÑC;¯¼pÕ§~î–çöÿ‹¯$9ÂOüM£££æß¬¥;îÞr׿Çví;R­5J¥ÂÚ•Koºî²÷¼ù•åb‹Fõ÷>òGŸÿg÷÷¿ü™¶nÿêwîÞ½ÿ¨6æÜÕËÞðêëÞý†›:¼±Ç¶íúæ÷7mÛ±o²Ú¨”‹Ë– ]ù…o¿õå+—œJD„ç~>÷¢MèêzŸ?½âfïX—3š‡™K+*Å\ Úq]ržYF5ûg÷7ÏÀsìdî¸fžïÀa:ÏHçý“ŠT? yŠ/Âù¼H±6çŸë£MO:Ñžõ!²0ÇÜ’•h†ÑØdµàûB`y`H*/nT 15ÆLˆB*?(]õ&¢ØôÄ^÷ñë.]wå…kœ!<9Qߺýà¬J‘Dh ä2Ó—¾½é{›··¶ïg=öì¡÷üÔåïxõeõFÓ‡q”­äÿó£'ïܲ#;yëö[·xß­W½ó–Ë;µ‰È ˆùHTJÏ "ÖÀpôdõ}å¾õìÝ“µMíÜôØÎ[_~é|÷+ÒÑÀþÞÖ<ÐB}M3®7Çö»ã¾§?ðÆëÞsë5ýR}ÖR£©£Èt<>wISµOþñß<»û`öÖTµ¾­Zß¶cß7¾wïgï—Î_·ÊÓáòçGþù¿‘ýsÇÞÃ;ö~ôÉÿã·~.›cÿðõþÃ×ïÌΙ˜ªMLÕvì9ô/·ßûÉ_|ï­¯ºnÆÈ®_C}'sUZÊ;Mø˜—°ÏªÓö|ì” ˆÞ&ßÓ-NÄ ¤bè§ÃŽiWøÌ€1¢ÊE´Ø ÉHpWŽ˜É6qÒsÂÊ/õÛú✣Ñ.«™»Áy>Øï¾×—6p7&Ó‡‹â¥ñ8k‚¼~úò}=ÍÙÿ@ÄÖ&-b"í=g"²I9ƒk½qûÚÓ»¥$œ7]uþWléšà©}”ä;•ZOŒ×òV0{}ã®§¶>{ȵ-dŸ89ÕÈ[Áìõµ;䙃}’% íi?D@kûùÛîÏ[ÁüëÎ¶Ýæ;·ï9ž·‚ƒå +€¯þà‘ccU¸éª o¹ù₟ìSœ»ü /ßxÝÅkú<&¢ßýÓ¿Ë[Áükl¢ú›ÿãOVëÝY¢Ïÿ÷¡«&bóÖg|ìY÷÷–ǟͬà`¥tÅÆõëÖ,OL¸µŸûâ7Ç'kó ë¸ó½YvÇ·µôõ8Öë¿ßêõn+]=×ïœþûûØËŒ+ó? ù#ÿÍb„zýhÛ Îó¿ü5·¾dÛÝ‘4n~1G„Ä ââ#Ô‚~ˆl×OqÞ‘ŸÅ¥YÁÐÐ#1×:²V  #7±±ÄQd,ù¾ïûþ=©Ù|µñÜ%‚ÍÅVlÛ}¶>{¸Pt-‚d-“ÉŽ-±§äÞtí5—nh6ãïÜóØæ4¬üö¿?sÃçm2¡%r'¿ùú‹Ö[w>´ûÞG’­ÿ›wo{ÕW !½Âdv^PŠڔΕÕEQØlÔ´Žï{|Ïóc‰=¸æâs>ü¶JÁ–'÷|é_4–àŽûŸ~Ùekàž­»Ýi•RðÉß²fù RrÓã{ÿ÷7p¶àé]‡nºrÃ-×GÖn~r¸dÊ÷Ýz3Çqœßm„FGDöŽ»·<ý\r³—o\ÿk}÷ºµ+ŽM~ñ«wܵù1Ÿ¬~á«wü_¿ðž÷TJñÛÿ™Wßpyë¿ùÊíßùуîø#Oîxù5Àw?äŽlX»â¯>ý+AàÀæGŸù½Ïþ=D±¾ïá§ßþº—Í4{5›b›4wZ_2`¿¦éœ |Î Í’‚ƒg†[D×q +Hæ:t³Š×Né5Ÿ1¯~Û/ƒSêL‰Ω:Œs¼TóŽ+„ò²¾ù³!LFê…0„§Í B¿kë'äì !Ù ‡†&ÇNYJ@@DĤ5–Â(.31óƒO&{úU®2: ›ö•×^ä a½oÛ?vÃØZ«c£#ßkET~ÛËßøŠå¡%díEëWüÆgÿÅšî=|²ÃP¹Z*µyÇMo{ÍUµÉ~P¼þÚ+þ÷/í9x ö:Ñ0rÙ’¿ÐÊÒ)¿è—*'™\û„ŽÂ°Q7Æ>¼- Å*Eÿ—ÞuCàKsÓç<·ïù{Ý ûŽŒ×êÍrÑ(ÞpÙ9déâõË——œPâÅ–çðÌ†Ž£ŽhÊíäM{u»Ò蘬ýö7»#ÅBð'ŸúØ`¥ç¬Zö_~ýûöÞè|ÿž‡?ñ‘w‚¶ûþ·ÝòÚW\JÉèmwÜý°±NŒO%ÑdðÕ›Ñó''Ï]½ ^qí%¿þÞ­µ€õkWÌlZ°çaìg˜©ß,Æ>ÁÐ,)8fä:îîÔ~É=æýÓg !É‚‡Gôó.ˆM&¸6×pí ¡+Re3ø'$G8/åèšÚŽiÐÙ-ºìy‹Ø3¡Þ¶¯&Õn/»ì\£^é¹›–ÜõàÓ7\¶rÙ—¼æº ó îënÜø¥o'bïáWœ¿"O%úšë/d¢¤1Ÿè¯¼ò¯¿š`€»÷]22@¹"C&bkÍhûJ tàØ„;pÙù+‹ÅB"eŽâƒo¹þ§_ 1W«uÏWRª÷¿éZ)°F“µp÷¡±ã㵓-OíŸa‚LàÜŒâçvpÿ¼åW9+˜XJÄ·½öåõOÿꢷgvî¿æ² òù5gÌ…Àªœ›Ì[ÜõkW<¹};9ñÑßúì9«–]}éy—_´þ¦k/Y::4Çe‚ X'Ó||½û“5x xœåšè.<-În— Ît[ÓïÅo»¥ÈfŒÕNCèlÔ'Ò¤Ádm{T~ÛÄd6:’R¡Ó‹P^à—*8~Œ!fV*v©8,ž”BJa½kK’ç+øÞ«_~›ˆÉ;9¾aõèîCc°å‰]ÕjµT,1dtV?2Pò„‰cíÇGmºfyk§ž˜¬6êå(LRC•‚¤(jh)=¶vüØþÑbËì?v¬:65ªÙ‘fm¼:ΕÁB©ef ÅÒÀЈž¨eºÁkV­X²bMÔ¨Z£ýbYéxÝö<·­TX²l…Ññæ­ÏþË÷¶ì:p¼çR^Áó3Óº¥‚‰”ª-b­íäxÒǽfÅÒŽ/\½rIö÷±ï.vriŽ—²›&ðCïzí½[žœ˜JÒŸŽ?pä¸CP¯¹ìü_úÙ·^´aÍl¡…„€qWge 1¢èôüf—EîÆðÏRÖŽÓ³o¼¨PS¹¬ç*+ÄÌdM³6Ö˜<([ËH?QáK¯>àlZ@Œýßwå)‚JµfôäΣîœ0Ö?ýÕýݱ6>¹ç§n¼$i'O{žLû䓟Ì×ß*%!×Hî) 2]yD‘/Q÷”Dy—{V:03³Éq—8Íᜂb«ÉßÀß½{ëß|í®ìüeÃåóÖ.¹üü•_üö–v7Û=ÆÞa7p{A|÷)ÓgG„˜Áí]6:ôÅÏüÆmÿvÏîÛzr¢škëÓ»~õ¿þÕg÷?^±qýKSþ¥×Oô‹³|dB»ßæÝÎòK¤T „ý `Ö:²béH¿üqp`kZfNñÒ%#ŸøÈ»>þÁ·ïÚwèñgv?úÔÎGž¹w- IDATÚF®îÉ|á¶ïýÅùåYxè^ÿEAl­Pª…(¦ÉI÷üf"ëˆpyn!åP2&cPÊÓ,zpêX;^z-²ç/„”RH™ð?(ç”'PÙKá,Fð'!©‹3‡ a#$Ð=?;›/|â¹ÕF¥Ðæ@D±yô™×n\•–ŸÃ›ŸÎ.`íòáö°Ò>öì¡/?7 ¬~¼åÙ,¨:wõ(äÍBú¤zUj`1ðWŒV\Õè;YbHÔ¾|Ç–ûÝ¥à/?õî]GÇ](¼ó–Ë–”Ÿ«Îì8.°£_)ˆaúAqÕ†K >qŒ‰<¼m×!wΕ®ùð[¯gc5 ``¸oëžÛ7m"¾÷‘çÞôŠ‰×œ¾þöüü;®¿ñŠB3Ò_¿óÑû”õò ×,_:lMœ¿ê¿ûׇàÚKÖEÚ~ëî§v8žœ|ÁšJ©Ð&6f¢F£z²ã®ã8"«‰ìõ—žóo÷=c“õ¿üÊöM×o~lWr;¬ŒbÅ­šÏ£'¦â¨ €µFô»žÊÅ7²V’²-[׌´[SJJ•K~„ÍFƒÒà;^Óçþæ_ Þ ÿ³_úõ_xïúsV?9ñ…¯Ü¾ç@2¯åu…À‘»‹éãþJië¶®:´Zo\~Éyå"øœ@Í0:4€R©n^ºÙúI(Q‰~3Ÿ¬™S»Nß`7û6Dœ3ƒâ…X›9íL\XÒë¥×é nÒ%f¢™f7uæË{ûâñ%~âfJjb•ò…æûÛ•e»^uíkW sÇDäy’n¾z½3„Â69˜Z3úüm÷Ãm÷·ï³ð3o¼^Aíf¬ÖŒ?Ûý'¿ï ×u›yîé$É@xóMïzx—+vý÷Gwþû£;ógïÍ7_ÂÌk— ®²æ«?xü¹ý'ˆùé]Ï;;—ìÔÌLB`1ðÆ¡ ÷<¼ãàѱ‹Ö-{Ï-—·ÿ~B,ðö[_ñ;7?·û l}zçÏýÆg:®t°RúÅŸ}ë\ŸS!ðo}åµwܵžÙ¹ÿÝ¿ø_×­Y¡¤Z¹sËŽÃǧO­[=òÖ›/¹ê¢U‡Ž×Æ&ëpÇ}O½ñ”¼0Òï~Íå¾’l?46Ù0–3d­î>¤ò¤RB¨åËF¿ôç¿óÝ;7ݵiëνkµf¡àŸ³zùÍ7\ñ¾·þT1˜§£Y.>ÿß~å{÷êÝs_ ‹*ÆrF(›ÏoQ3 /ñVž{8˜8¬M<ßN Ái˜ˆxÏ×þ¬¿tL:ÿ­ÑÝ*á3|– X—ð\ Îäž=nlGÚɉÍ긎êÇQP„^Pb"££D€É£üÀQ¦1ƒ‰šÌä Ñf )|%QJ!„B*Ï—^ðõlùçÛrá—þðgˆl©2ìšÈÒñ'J¥R¥R1:2:¾}Ós_ýþ£îäüô‡€ÈQ~«äR‰¬5Êó¥ò…8qü¸cCeGþ‰ÄÐ ãÀW…Às†P*Oé’dn=0“KGÙ‰“Ç…™œ5“Ëöyžß/BKÓžÂ0ªÕž’^š<+KˆX¯×µÖ###Öš(  X*UFVŒ®ÜЦSŸ‘j$+’©Ã+‚8cÞùÂy!š='Nézy¡ZõQ*5íÞÈ]€È‹dW9ÃQ½Fu¬zòˆ¬w\ðyª5½kÖáÝä{³ƒéì³µæ,/©< šó53£¥˜­$Û þ깱Ŋ”LY£ã°®#](ë8¬×jìy ϤŽÈÆ0p±2ŒˆBÔÉÉ”R@¥”c3:Òq«Í ÞŒµ1ÖŒ!¢TÂÑ»4›Íf3‘Ì+ÈKå)ÏŸ8qLy¾¸YŸÒÚ6B=888P(EÍšŽ›å¢Èè "*%˜¹ @ÄZ[¾'="cuÜÚY˜UPJ-4"] ¼bÁ³–­%ß—BxZT^ rÌ… r½óJ(%•ç+åתSDd‰0ŽI³!B6º{WÜWºmu4LÁ©Q1tÕäÖèEQIœSC}þdfËikŸX¾y¿VýÓv4_å·GG‰ÐoGí4&’ž/½Ã-’ByA1ïÿ9`Iùœû·”´¸W;öÙ:\=…fyƒ/ŠQh-Ô6íxîøwNo¹Õ™. Ó=+Ui¤D—Z^ßœ§úç™S^õžö]ÊqÀµ}4í¡t´ùm:ML/‰³ Lpå– êR]À4öB¥ó§ÍÌËüâ÷ú±ÿ1ìá‹öÞ[ϧÃÌf<á¥×"ó»’ÊŸÝiÞü/ˆÈ ¡PœÆ%1kFTg¹ã†ˆÍÚxXŸŒ›u¥)é)å貕ÌÔ¬Nºý:( ˃'Ž˜+5Ñ!phd“5:ÄÁ‘A"KÖú…’W(•öå#ÉZ®X Òóq¥X£M93VÜv$»$©<@±dù*Wæ(–¬¬OMžxÞS’™†—{~Aëˆ,;z( •‚ÏÌZ[D±rÅrÇaØŒÂfØläï7ÒÆZ.¼l(d¹V¥l!ž'3ñ©v_‰H"â8¶JÄz¹2ˆˆNk¢P¬cÂj•™Ãˆ ÚtlhÒ fù\ñÔ@£Ö%>iþˆHÞ™;Øá™¨ƒ_›:´˜fƒÍ vÇš¡$‘ͰèÓ\ôGdú²ˆ3÷ ¹è…,ü5«ü΋Ú³dâ°.„ÊÆÁíónÛ9£ê¸¤õ"cD™OÎ9?Êu>)Úž 夃;úäO೫¶SõfÀä^8y!äHÙ2 +rÛ|Ö·Êÿ_îTJâE×§Ÿ{4¤§9í +J9ÑÒKƒLÇ<æ«d±¬ÑúQ¢¶3óÜrÎuçk°M3sדñ·%Áñtðæ .¢:Ý™µà‹'’\ìÕýÒLCŽ2]±ÌÕÒGˆBœ<´ËZmuDD~Pb¶FÇD\k„ÅB±\.Y£•ç ¡tÜåùˆBù­ã‰ñ .—!¥”¾µÚíù!äÈÊõg /"Ö&GµIr̘DÌ,=Ÿ¬‰›5@ôƒ’Ž£(l¸ÈŒÈZk‡F– !Èjf2Æ0QLj BH©_Á4]P˜ÀùDûœôâqBî'9×8CÑò‹k´›-³w!3ÆÍª‰C£#²ÖKDEÄÇ:ADÓM´Ó¹¶¤ò\ÿ,hm•§”ˆ&çêØQ׿ŒBÔë )¥“_7:r×åà8¥”¢4¸OWö¢»í—­Žâ¨¡übv¿Î˜Xˬã&"Ëî=k´µÆèÙZÜ!|?ÈŒ\V>çØ°ÉD±6ÆXG™ò1!‚@(P áI”*B¶Ö5gµÅDq¥šŸ,„²¹èJd&÷€u°”¾TJHåº\Œ…Mkµ”‰Œã$ÃÖkÙig+¥”ò…@DTJ !êõ"ņ1ðccf-%ÊTÎÂZãh ¥"Ñfk´5†Èd呈æü8Ì Ü‘ÝÃu‰"‹iEÛ´›?õ~“ÎDˆêONnK8øÚN¿ 3Ù¤tÑB`n¥ÐÊpÆê°sTa/:ƒÉYöþ¿2‹L¶6uL‡ ²ÀsŒL‰¿ì÷‹t;pô5„n!GÍjcjÌ舉±D sžp2eÍh¥ÁÕÝ&´;Öý’6VHBØ4—…Õñ£Ì¬¼@Jõü±ã¾ï @جº³\¥~¡XTR*#òôBæî2D4:ŽÂºò 3„˜ôZ°Ñ¡‹Å 3¢›FGj$ˆèùž3“Ld‰á(d6¬%mŒµdtôÝ{ŸnF€/X;zÕÆsDºqH)•…â€ÒáÕž¬µ6Ž"D!¤DD”’u”ÙMïü³ú±ûûÓ¿ô†ó×­pÄ.‰!d+¤b­c‡¥Š¦¤f€,YKÄ–ÈZ*—ËAd;,¢Ð“ÖR,„(—F…±üã‡vEÚ2ó†Õ£×_º– =¿àÙ1ƒ5š¬á\é<§,©›Ýö©?ù[wðþìS绊™7=²íw>ówðï?÷É ç¬œÓãu#&‘Û{ûûψ>á); Ô¢ˆŠ¬i= îFríL?b¹×Ü[Ð+±ÍŒˆˆ …IÌ@‹;Ñ9³|òÌ6ç³\Õ±£Ìl­F!!yQ23¿¨õ»*ýï mDB:K ÁB –Rr2RÉK…ô„Hj,Ÿ¥ àÈÛ~ ‰œxý®ÅÜM)!ÿ9‰'·«XgH`‹saVÕ¹sÁCxL(­:q9B¢d" â„`!Ùø()®ÁKl‹h’þ ‘U¹ ‚“Š`¾ý¾mcS xËÍ_}ñ¹¹¢w"1aÚŠ‡iÅ­(=±%"¯ Øù¸³h¾¥˜@Aÿ¼9¢³bI†/+“qOŠØ:çÀY²<¸c¢Ú€×¿ìÂë.Y ]ImtzC‹VÏ3oæ=¡Ÿ™>ÃÓ½—^|›r%Ï `DœŸ:ƒ€ML1mä¹ lÙ8œSñ°0žýý-®M*×WšŸŸp¶E„sRàëV`@ÕÉ£Çîôƒ’ç*(Gêõ•Êegÿ2 …5¥$‘U^àÊ.ÃWòGÀZÌÖh¥äèè" /Hv[!˜Áó‹~©¢‚ë°Ád+”ÒÁ­ÅÊp£^Ÿšš¬”KåJIë(ŽšGö<ÉÌf¬ ݃RžÎÆi¾¤l-ñlKäÉ’ÖÖZ‹HzÕÀBÈ ˆy|b*ˆµC¿P ŠmL³‹ED”R !tqŽ?A*Ïй¤!‹efÒv ¬M*\Zð‹T~ÁENžïûA7ªœÚ>¿P)$0²af²a£*¥ÊºôˆŒó-r³E:F1cbŠš^P”žïPY)ýÎø[Hô<éV‡1º^‹Üdp}ÃÃÃÆ˜Z­ÆÌãããÜ6H!ÂH T~2K“FKQ,)¿ÐEW4gÄ©»{ú“ç°R„Ä>AlÑD‘•W ٨߷YkØÚTìÍk[ƒˆÊ ˜­5¦w`Ú?ÇÑ7Í‘×Qˆb~¥°,¤Bé™¶Ù‘¿ 2À$dÐyaK³Þ¬ÄL ¤\ˆ§·ý£ÿDz¡„Zçç硤ò ©*ÎkIþdé&ŒÃRº2ÅTüZQæÊ>: 5ZpknWKÊ)-è`ùL—0#wø­ÌùþuŽÊNJ”R¹ßG!…Ý~n7`Ø÷ùωí»}êöìíwlй^nªV[E[×£ö$ÝóÒÎöP«•ÊÌßîÅëWþöx£+X]±d ›&O+¾œ6®hyQ˜´Ü»ŒfŸ}»uÙ…ŸUµ ¸˜¤Ùˆ<ýðÎáÌQÄy7„àl[×;§§‘ò‚w•Å}.²ó^“æ_gË—hý«-ºFe^Ä0ZJï”Ê é,¶FÛ·ÕiM~Ñ ÊCqXÓÍz#Œ˜Ù ÈC…5)¤ Û¯Áú…DÎ+› cÌÐðYÇMD ŠÒÍH)U¡8H&nLžŒ£{~±Ã‰³F¾·bù2·›z^àyò Ìéã–âñ©†çyCƒƒ¼ëñ»;†”­]}þՋν$•ðdï}lïý[wí=|¢ÖˆJÕ²¡k7®}óÍ— ”1ÍfÓó<—üC!òñÖRAQܼÿ‰}ÿßÿÙì~å?üèöCߺû‰ƒÏOZâ5ˇ_sÝoºéÒŽÂÎgöûÁÏí:t²Úˆ*EÉP媋Ϲõe•=‰ˆåÊÀüå¿>³§%xû½ÍÏ~oó³Wœ¿ò“?w‹ÑP(â£ÏøÑ–í;Ÿª‡ß[¾tôšË.xó-7^¸aMn>·áºÝÓÛ5‹,ÊÚ¤¾z¢Ld€H¨ mo˜wô`;2O³¹øù‹>ïÌ\óó‚F‘Œ!˜9†vntò‚cTGœ.>žõöÔaÒ…ꦈs,L–9)ÊÒŸß–»ðÎ."{VÈ0¸)-•ÊçtŠ2³qT‘Ì‹žéiU!±é‚Ž[ÎA6té/º!-œéºv‡m® ÛÂìµh]ª=HLÉ’6—,úI¨Ì¤RfÍäŽhl¡;¥“Ùˇj”DŘ¢å2"Ð.çcµ¤Ã$y —'›ÆÓÏÞ¬5ÿø ßß}¨e)«õ°ZŸÛûü÷6mûÃ_~ûÊ%å|08ã„ëx,?Ú²ã ßz ûçžC'ö:ñäŽÃŸüèë³3¿ñã'óJ¼“µp²î>tâ_ï~üÞqÃMW®Ÿé·øKß~àöûZßP3Qmÿ‘Ýû|ýŽ{?öþ·|ø=·ÎÃ],·¸÷[¸XaÍÙ†á!“Y„¼‹2|Ó]X–¬Å—:ågÆœÅtM¹ÌÌ‹#ì ýÏßn ±˜Å»ÌQXC”ž'ÙÄam¥”®H2­ÂÐÚJ J JCÄÄY@GQ©œÛ……€~PÊ[5c´5‘Tž”.¡ÂFG:J9=ë ŠÃPëØóš€PðEàü ˜CŠÐXÒÚ3•Ë¥bipïsO2À@¥,¥R)/(/[Å0…2-ED&[«Nè(]Îl]»ßØñç]öެe²ZkcLy›DDZkˆÂÐXеñ"㩦±¤-*a"½îâ5ׯ¸óÁgÃØÀ†Õ£¯_¾réÀäädlìŸù޼̿&ªÍÿö×ßýŸ¿ö2ºˆB)ˆ¢ÐZëû~¿]CÕJÚÀßgK·ãòÈ3ûÛqøê WZ€'vɬ`¥¬Y6XkêCÇ&ÀZúûï>|Ý¥ë¡:uãeçlX=òã‡vº»X¿jäò V¯Y>R( lÛu4oËcÉiÀo»ãÖW^»jùh÷ÚqË'TM_ÚG `´g΋ç1§ñýÉ`AgÌ‘2“5VH¡¦mÓæ^1bdzãÙˇõJ+Î5(œKÖº:²ÖSëÿD.…æîZ$)íÖo£…”=3mÓ>\îwz—˜.¡HÖÎÉïq:-g—´F7«c\Ynt¤› 7w³">DQ]!=Ñs„gt^„™9]0„ {.BN¹¡™síîÄdmwÝVw—aŸ„Yï¹ÅYï6QÖ.„ìä%s wÄ®rU똙­ñ]¬F‹Á_“›èɵÚϘ‰ ³jјõq±8c#"r_ƒÖZk,ˤnâU×lX22tÿã{œ Ù¸nÙûn½´±÷?¶gÇþãî«.Þ°òçßuóÚåÃ'Æ&nûÁÖMjãk?ÜúÞ[.É~hz´¼ÕJŸ!ÀB|ü§_yõ…+Ã(þæ=Ûî|0‘N|âÙƒW_¸î~d—;²vÅЧùžR¾_xdûÁÏ|ékûð3^qÙš×Ýp¸ùÉ}Ù]|øm/sÁïz65Áïý­Ö®(V–l~úÐÿå—Ý5?¹}w·!̧gqwÄ 'çZ':ˆëZ¨À´ÏþvõÎi?»;ê¯Ê ÞKùôìÕ|Vü®4†…("ÄÔ]3!¤ÒSLÔ^WzfÝÌøÍ>dd`k QJ)‰ LlŒÀB€T^š#Ê„A- ë8D§¬W( å å°!ÖÆz…YKF‚ò‚0l6ë5"CÖ RZ­'r´¥™9r€,1Û„·€ÉÅuHdr[¼q‹GI% ~Òè-%3+)˜9޵1Æ÷=ŽU!U¡<ÄieÿÜ"«]ÌŠe>g&D¡TqTÔRyJÚÔŒÃ(F!”_œ ¸Åò€{(ÖER )¥´Ö·„@ì{Š$I¯Ã)?(U†Áh³éÉýî`!ðþà?½g @¹RþäÇÖþÊýÓÁ£cpßÖ]zÛËJ…@IB ©­M½Ó„næÚ(ŽˆÀ˜V\õæ›/¹vã*`.ü÷¾öÊ»Þi-À‰‰ª1¦êI¾§š±©hõò ^é¹{÷M±6ÖÚ#§6eÛk­qLcbd xãåë€áòó—¯Z2Ðl†ª_yɆÖZ­'ÑØLOЧk|f¦ù7§»iÖ;âÌQËvîþyÅ\w|«8x.{/3wsËÎxÝ=Nneæ&TJ. €&Ý®È9¢pWgg­µÆx~ •rÏEyAÒRÕu£DÔQS—ñdÜ´QqÛØã¢”¡0¢ígÈÀç4O蘠ūtæšÂÞîo•YG k5 È’aŽX ã°.t¤qò3òyæ.ݹéA“%–à¨2 “µl­-WP§Ú ‰P»/„ðƒ¢0q(„RùRA€d 3YbmŒ”¬ÑüB©Ù kµZÚ5HB 6””iù!e…µi+ ¾/•”RzˆHÚf!h¶§K©/ÈÇšJI&Ò:&"ÄDQ³&•* ŽÒ¼(*1‰ú ‘q“JäıPª0Y2Z(¥¼ê m¬±„ˆ¥²•¬@Y(U@)•‰#ån…ÇÌN¢OJÊ ²µîy~i`jõÚÞÃcîà+¯»xtd8…ozõ5_øÚ ÖæÈxxÅ…Kj-¥¬×j™í7º(Y2qGÚêbñº%N°W"’µ¥À5Ʊ6ÚH‰kW oß{ NNÖóÏ¿½zÙÐe篺xýÊë/]7Xöëõ:(%H–"k J!?ø–„ô¬ÕccãÏì9vøøäD㙟Úۚة(Íì²p·†™í£GjÏ ÷‹{:QÇ–¯f;¬µ³_Ý=¢æyDcÉñ<í¶wX›(tí=̤ãÐý€PžR~Gq•ʃ~±HF¢žè§%IdÛùžƒÓ}Ú©ÛŽsÈb&IŸ”^ÀfÄNgíLýzÓ¸ÀÀF’Ê|G´D`I(㨹!÷4§m;e?w Ç—ñYÔÙ¡ yš W"Ÿ óÌÝH$§}åIÝ|²ø-SRABd™)áßttN¯ÑÑVKID˜–SºÉÇL(œŒ»ëá@׿ΌiÁŠ#Û©ñãŒ6 S)<×ÅˆŽÆÓZ€FGôßÍkè’ºJFŒÕ‡}R IDAT­‰ÁÄÖZТR^V Ã) ™(ÙµªxÒŒ¬ï¾%2ؾ@Ç'ÙB]¹t(qÑ’«ÁUK[vñøXÕQeɘ,´È‹ ¡„TŠÛšö†*%)¥S"TJfo&·ôžŸºjËSû§ê¡;~øøäáã“?|`;\vþª÷ÜrÙºUÃ3ë–'wóÇ[wì?¶P,¹?‡çLH¿hèŒÂqšMfúçŒòeî¢á(J“-$×I“ÈŒ´tE,¶I±´; ힸuý‚S—am¿½í‹9O®;ÍN;=“;.xW_Ð,mEü­ìXRgäj Ý^ªRˆ¥§«bRì¥w-ozZRËË Ê/^[ȳ/.·F7#-¤çûN¸Ü”+CA±¤£FêÛ&ðŽÑ‘«1qE eµTژȚ„8Ÿ¬EÄúÄqN«BuÔÀK–ŒfºØ(ÄñcLj¨\®xžrÔ>BH&ªWÇ‚‚¯”ïžÓTµV«7‹Ú"‚µDÌÖX!U±Tà8jXk¬Ñ€À Ê ”ò”Â÷‹( Çñ±£‡ :vh`tÕ’UçÏ̓?îòŒŽ˜Yk£µ JFGdb·\Â(œwŸZ¾jÍòUk]YX›´F{AÑ)H³5 DRY£ã ÌžW­C kâ[aº1:b"› /¬ŽÃzÕáN:j¢L¹PO‡:l³C­†G³½¦¡÷qž&вû£cÖÍ5G£ãh>æ“l³:(X*_yž‰ckµ˜­efÖ–¤A ò;ª”Âó$ƒµ:ª×ª®_Há%&ë…T.CnMì–-0ûA u2Ù PÊ+T†ûm•s‚tæ´ßæOvDÁY¹%Ó¯ÅO©:¾œ{¶Ö0 ••ädÙœ\n>]^ŽØ/ ô¤?›çÛ-oéüp¿X!²a}Ê^9®+tíÊÄ´­$i寬†ÛMÆ3]ª>#KópD)²sòÕÈÙÈ\$Õ†˜çÌ JÛâv·žÓx¨U‘) çê9²žö$vrð-¢²tÙ'­¶·P•L82f×P‘kÛhëóhOR`KH!+5r Yx×¶Ø2ºÅ;ŒÙo@ÊIÐî3"Ž•³JÚçONe3Ð½Žž˜j³¡ æ• Ü#éîyIX½Ûh») º¹û¼œDæçUŠ?ó†kÞùšKöÜyàÄ®ÃãOí<âhµ¡Û~øø|ìÖ^ø! B¤Í—ïxعðÜ¥Ÿøé›×¬\V¨ ƒ?7„³îU˜]“8.p] Ÿž2–l>ðbÜÆ´ 9]¡O~Ì1âIeIòØF^ ‹ÓãŒ~° ;„N ÝEÒ®¡I2¦„C"å5ퟬ˜û[ ÛkÊpVc›3.Øíd+ÇiÑ)ž6U3—ùßwvÈ“D÷ÿi5MÍt»ËÙé d¦…DÈ<‚yº¥§I·Ù Šåá%:lÄaÃ/ýB‰­e¶QXc¢F¤}ÆÒ€B€¸Y·dÑmt\(–‡–ÅÍ*YjÆS–™…9nÖ,Y£#?(ù… kIÊ8ŽFcP(ƒSäcBW+¯µT¾ò|)„’¢RHõÍMiÀ…8ÂZ{ìøX¡àO)_y¾Ž£0®vTBzžZ¶t ;q²îŸ8qpxé9CËΙqÁ HÔØ…Ñq£:VZæ* F)k´Ž#":|äèÈ’e^rEÒчq³Ú¬MsPR‘5ÄÖFq+ f JÙ!{&…'°­&Ö)8Jå}µ~õ’=‡NÀæ'vÿü»Ã ÄaQ|ÿ¾Ç“›UòÂs—“µÌä”=L:Tƒ˜tÔÈ%À¬ÕQK³°Ý¥CÄfuÿÍ]OÀÁc“ZÏ +ù%¦µ€½GŽe:…o»ùÒáJÁá ûmËÌÍÏïZP&ŽiÎÆæõÔ”NÅhö±5šˆœˆc Þ´IW8n3åùù“[)PÏûf]™µÕå! ‡BJ/(ó‰± çÛ E4†™K•@aMœ¡îÑW~ÁaüBùžORJ¢‚•”€›ÂuG(¿`Mì¢ öü’ô|åàs™™¥TFÇÏØ…B,[½®»jšø˜È’1BªLj£»‘³¾íO0 µ‡îºïþoô¬Lè¨Û•q±&3E©~¼x³fi¡º‡¡>q’ÈDõ)P~Á-UW#J&¶ ÝÖäBÉyãÔxz‹iqQ~Ñeü0 ÑÜ<Ê·|·|!)Wrz,M“"¶ºïsþ ‚è*ÖÏE¢8Mn& RsÜnIì…)Y'·oÁ¶@δðA D!„(5"Î~ö¤wÔyqBˆ¤ÈƒÈ•xtÜ;tS.áLMÜF: †_ÿŠKÜŸÍ0þÌïØä$œ˜¨ýåm÷8šÖÑ\}^xù‘˜ŽÉa:îüšÏ•báé]GÛ~à±í¾öý‡«Ð=)D–M,˜{ÜE²}äJIǪ wN½}ñ¶ï/"¬ñ‚'—æž§éõܹ“` 5Wܯ•Œù-ÐqâgykèÑ:’æ³f‡VmP‹û¤m1g–ò«ÚøÓuIFZtßc.ÞëpºšÒi(+«ÎbsÆžÛIû ù3ñÔŒ4K5Û5ï8Œçi¨³ ²ýûww"³q Pb~È/_¶ Ö§ÆLÇDLvrrÜé20"ZmÆOžÈpH@ÄÄL4¥¢zj"¶Æ"âää$N•J9Öq½†‚8Tž_, Õ›'˜zÐGMÕšÅb¡\*Yc\ 0›úÊ ãdŒL–-]â¦TGÕZ½\®”*Ããc®$Eˆ¤£_H‰B.]2êf£FõØþgP`\¶V•Ÿý”Ûqjr ˆ ˜ýB™˜üÒ€5f|2ɽ‘Ñdµ5Ú”ÀšX­uÜ*>ê‚ï)‘Ïå¤Â±$‘â=ì<||ê’óWð-/{ýË/ýÁýOï=2Oí<ôŸÿçWº Uð¾×_c¢f¢¬”T4°Ãú1/ÖѳÙÑýº ½ Ðƒ¥däèæ«6ÜýÈNرÿØÇþð×,VR>>™uÄßrÃÆÁá‘(Œ&&ƃ´÷þ'ö9Yݸ~ù;^s¥ÂÀ?ßñè¶ÝÏ£Oí:ÚhFùˆH§}­¬¡Ã]øÔÐVe°v‡N™{BÌi‡L°¥\Ç8ÀB*—xÿÍ/Nr7-ÞçàãR L¹!0-ŽqQNR Ïà¤ô¬;˜¥á˜v:qjÜ’ZKd1Í×2‘Kó¢=멘¯›y}lìúÇÈ¡®š1UnrE’iA+SK>QºRTNך$ÿ?‹ñO~?)»àDaÝiêbâMç6ÐD I8µölåô)$zÅ«dÅØm»ì:pÜ= _ÿà-«—öüü@¹ðÛ?ÿºáB—…ùd5r9èxFÀLyÛ ­[ž]ÛÞÃ'w8žYÁë/]÷®×^#„DD"ºdCëÌg÷ß{xl \xÇ-WdÑáCÛlyj_3ŒÞÿö×,Iîk|²–¤œÚSš³x@§(e0»ÌÍ|WìÈFw<´üÏÏ®YL§fØyÌDdI,º[$ÖZkE"$²—Räù4Q0äé"ZðLÏ'}·sJµ®Û|ŽÄcC´ÖÙ¬0½U”Þö[ùqèžï¶_vîS½{UNqj¦F®>·°Àÿ¸­©†¸×,÷zšŠN?k*»B’Þ¼JÝñJ;¡‘ne»QÌ)¶f²”ªÃcµ1Ò/"ŠB¡Hdމ(Š¢P(:ì‚ÉZ£@Á¬¼Àóôƒ’ÛmÈbf2ÎÅ$â(6€ ?@­µÖFJ¡¤ph„ÓôR¡:n  ƒÄÍ#°–-0¸|ž™8 !•&u+“–)›.EŒŒ:=¼é `âd# ž›ÉÌg)²b(nq/LßÚ‹co˜-“¥”¢#›e :™]oÿÛß3à•7„ª¿®‚5:ã”Ó²öJºš±#û\&¹FÍ0]º|`h8ªW™m6˜íÄTÓ÷ýááaG&££0 «nÈüBÙó‹ÚE±±–FGGΛOÎ ©„ôÈÆLtrlœ™J_^ÖºG©€ˆ™êµZ£^%fk¹(gƒ…TqTË¢¿PvnN£Ñ¬7•b!pߤuHÖV†–Æq4vò$".]2êL¦5ÚZ­µµY²t¹Tª<¼ÜeìÛá))ÀØÑ=ScGʃK1lT­‰—¬Ü@d­ÑdíØñ#Åò@epØè˜¬I ÑRQ¸€µ>5.¥ò EfˆU©¼ì9ºäŸ¶_¬8pŒ™MÜLoV)§¦¦êÆ`¹ ¤RãͲƥ»ón§Ž àEÊÄ fÖ:BÏ/%Xµx5ó…3Rz(„D´&Ž£†ç•8Ã5é8Ê[#D̾ÙÉLMMÕëu·z_ DÏ“Ì`ŒB HÏ÷‚Ò"®”Þ¼š®k–„½zº—•C„TÝŠ<>/ÔËÖ'¬¥©ZX.*FÇFGÅ‘ X±Ö0Qu⤎å+Ï%«uÔtÀò倉™²)ÊÌaYKÚXDTR!†††ÍFµZ/^à+Ï/H0”ž'„¬N—RùA ÈZ£u…B ”Âó Ò œõšM×#$¥,Kù58 =Â9§Ÿæñ)7ŸåGë>»Ëè–Ÿ\ -DÄêØóù/q)ÃŒx‹™¥R™9tV‹Ò?Ô#ýÞå´öã˜ï>>›²ÝþÞh+ìíøf²ÆôqË•‘Ñ¥ÖÄÍêxâ(Q( ä¨Ycæf3.–ÊåÊP£Qo6ë(bpу³šuH^ ¥Aëf£æû?H¯áá!f®ÕêÆ˜ðØQ¥T¹Ra¦ññ ßW§±TôRÛ–©;qP(c¦¦ªÇÆ÷|¿XˆJ #¯1:L¹ñ°Q³d"ž˜˜‚À)p±TòýÂÑ£G°88Ú2“5‰ô‚âÀð2­c +…D™Dr&jZk›‘‘¾.&$ú²Å±Á\Ÿš0F7"-^üi†Q¬=%=%=Ïóÿ9!UOó0»’«Îq¶L Ñ|ˆ» Ó6U¡Tïzº|êĉ`÷r;ãËi¨Ò÷sdMÆrå!‘–Ó; ]µˆVðÌxM[²ŸÅ4 YüzÀy‰y·î¶90™pR*Øzó y‡_šJfÐG"Ô-Ö%h§§Çöê‰öÖý\Qr«H0í Oc/G.˳f©ÀÔ·u9ÜL‚ZÄR=tñ¥™jl5 3ç.’ÄKKD£Ãñä¬.Ý%AÓ>\‰ÉÚh•è$áWÊØ*ši‹K[CÖ’{î¹»qK3ßûߦMßJb+ŠÎÕz´D$ól!êé].§[g<Òë ì¾flëÍçŽP7ù¢´=ús®Ø¸©wøЂ@DÆÄ‘ 8"] ]òô}N &ñÿ³÷î1–mi}Ø÷Xkï}U]ý¸wîÌÀ0“L„c$Æ&ɱ"ÀÁ‰!NH ¬Ù1I,ì¬(Ä![8Žˆ£È" –llp$ÈÃcË0a`fîÜÛ·»ºê<öÞk­ïûòÇÚÏsªª««û^fŒúJ·ªÎÙg?Öã{üó¿â̈íån&¾Ò'ïâÃÅ[Ú^â1¶L=B¶º§4;è¬ç³r/Õ¶I°vC„{{•¦W%rqx„j}–RƒõNR³½Ø—‹µ/—û˧fZT+b·\x"ç|©ª›©Fˆ"æ«„¦eµB¤Níå°¼ŽEáxÔºÌëØ!cI ÏnRˆÏÚßÞ;D‹¡És˜Ü¿]'¶õÖÔ ïêý~»ÝЬEþ:ïýûÞ8$"—Àååååe}rBˆøÆûßÄÅQ:x0Z%…ÐìÆöÛÃë/•ع˜¼Ûo›z—Eù}àÃåbµ=‹È±/ ë÷›™JÛÔõ~·:}°:½o`˜ã/ƒ¦ToŸågä|áŠjÙ%¦;/)šj³»TIçOeuïá)"‹5³s›ówž=}{ŠùÞîZQ]FQíöÿÍåE¶Ẽ¡,Ü ÑžÓº®§7™˜1FIDMD«5!e{ȶÙ+“ôS Î>ßXÄÐÖCx]Î ˆ‰ßøàg=¿ôr“kñ‘ÜÄQʼn~s² ;vãçNL=´¡8Á!DhüH íðU«SUÉ6™ßy¢¦1´Î—ÕòÔÌB½K¡Õ,¾e†ì}‘ïuµ¶û4ÅVb@"U õFuTG@b§jß~k±X, IQ$ˆXJéþ£ª¶Å¶Ù²s%;DPI)ŠB[Kj«Åڗ˺ٛ‰í7ˆ€H)¥óósD\.¼ó¥/SÓ˜ìÅ=Ö±¦«GŸô™Ùêo¾ÏWõ”^LüÄÙ6$&vš’ÂsY×—@Gòþ!”D%u-³UaWL„„˜™&_­‹j›]ŠY2$¶`¯¢Gˆp [öÓ. ÆyŸ£ãÍYOα &ªaÖ·¥z<ôð'œwÐ}6K¶‘õ’s:8ó XËÊnFƒ/}ŸlMÍßÇd¬ cÿ;Ä,O3©O çcð;ǣ߮V~SÛ G<=¦ÌÔzúGwM€FƒaßU=Tò$W\À¿‡ˆqœ|Ýñ~0…k«NDÓ:±¥éMÍÔæƒÂMŸÔöšàHÓÐÒŽƒNœ·Ùà í˜ÕÐÆÄ÷¥Ú$ŸÆ¯÷àšrnGc©cÔ=Ô·'f™Ód]§XÄlL # ©W˜·Œçj‹‡'‘×qÙÆQq–‘tj³N”þŠJâÃw¿Ûý|!Ž£|z"‡CFx¥µÉ1Á¥‘Ÿ·ÉgðÕ•xÎ1ˆ@”‡orã†üiñ2S‰­¤£EYVÅnW?yr¾ZxD ínÇ$Ǩ‹ÒWËÕr}:ªy!å½m¿¯SŒ‹Ê'Õº‰DT”©(«ÅÉYž BJíÙ럅Hj†Hû•€ä²Óáƒ÷-›ýn{ùŒ˜˜(&‰)Ý_œaJÁTŠj©¢ûÝœ£|E5&ñžKö¾8IAUV§c OžË÷4µu··y[ެf:ÆŸN¯>WÈ\W2Õ$bæÇTcÌ3Š”:îb¾íI÷Cˆo=Ã;ÝØ$×iëöÔœQ\)o#«¦ƒ‘2øqâ3˜3Ÿˆh Ñ4SBžàÝŸÏÔ¶ƒ”‡À¯ï޼d‘”:ûb‘œóuL»Z ²±à!~¤!w8ÑN~¾k(‘™ªHáíí¨(sù;Að.(—Aa ‰Ñ±ó«"bf§Ä7•¨kü!$ÕÃÔó胿ø>ñá7NÁž;QpÒÜýÇ1#|µ)á5.‰™ 8yv˜[ ìÒ¾È64äTDݰk÷l^_œÛ Ø(vxT&ÌÎk3?ë“ÏyJ:<á¨{±ØíMêíå–?<.¯ß)Ÿ´ÛV2ßÍBÁ‹EMÉ•ø—¯cpš¾¶6Ö7n?|_€ªoð2ä•z©ZLꊌ‡&ÏÄnf­‰D]Ñ!7ç².°Š€¥ú9"pÄ®PH±ËØRÐ]긩’DRh묃DLœû%ª„Â3;ÏÌAD³DYŒIU“îM-%Aç²p(«‚dßBѦi‘™œ+B³O"Ž ÷uÖ‘.Ä"{ÏìwÛ- .NÝàIm8­¯¶m””ª²Œ¼öþF“àESJêÌ7?³¼Ûå QTRJyÆö'Ì4EC@‰ÐÑø¢¤HÎåM Ð4ªm·[f·\­²‚9'Gl6ÒrâîÊj²å ¬K¢14LÔ‘ÖuÌòÙy“”rC3WŸÐ{.È‚ó%"9UÛïkë‰DÒ_ùë?÷w~þW¾á+~ç×ýÞjhÏ1äۚ؂é2Ð]IcèÍ[f°'÷q2»YyÉì2õMÀQœªðÞrÒßP±ïÑŒ½QI¡mïØ•HmÐéúæÉå«¥S]¯[ï84{b&rÙ,ÅÐö¡,+bΙJJ1¶&¢*;b—óû{QˆBQcJ¾\d£1D*  óã$"¤•zt¾2ˆªZ”…/Ù),=;7UbïŠrÂãÊÄh=¾x¸¥“ÚË$ñGªJ6ݪ{V ÝbGÒ[ìÂx7X¥~‡ãá9j´Ö«^!³¨¤zÛ‰0g† Îx„fjr‡Iø ßü^m„h1I©™JHÞ±s~ö„&Å}I)ØÎ *&I5;0`γÌë¤Ï•R”ÔNuÈb»ï¼ ™‰c¨åa IDAT0×1¬ÙoÑ{çœw¾Ñc BLª!õÙ b•÷ìBÙX Ä,†×ëµs®­·à˜ Þï½cïYUû‚¯›ÆÌÎR4ïo Ü̆â±ñ4 ¡fçß÷9ŸßÖ›vw¡**‚ ¡Ùód}ï«ÏiÈZóc,ŠCG1bo*ÉZP@ì.6#€ruš §ˆí÷Û²Zœž=0°xñDCäNÚ£¯£XJ­ŠTË;ê æªrh[4Íçck*Ì13;ȱ‹¤–¹ç™û²"¢ryÂÎ7»Ëã¾Þ ‡ýñ¿õ÷~ú~þçÿíï¿s¾ýŽoú2rܳÇÉCãÊEGŽì'¿üFxWÀö$/9†á\¿^gg?]¾_d* ›n/‹ÕýBØn·§§§eU¹¦`]$*ª¥™­S0ÕØì¹(GDvÖ6Ãø,Ê À$¶èŠR$5û]ÞM}¹ vÙ³“GÅ@êzðïféûÖd1Έ>Ë<äçSo A˜ùÅS?»>üº9eì O伄¦­÷¾(}QtÊ쟆„ú—t -ð+ŒÐc£Õ†À‡ŽCìLB â¼ÖgñÍ6wÊaSéÛ‘Ž CAÑÌtè*à~1·ë;3úƒ½¸wº ÙßÃF*…ªŠHn¹åo8¿=t@U•˜Áž·h"¨©ˆæ(;çsÄÔ‹H©$•¤,a_M¾¶ÃߟêÄÁã°ñ>·¹0ЃB–Ssމr„0°&æc>ÀlÌ~G‰Èt?À9ìgâ’¹WÕ‰ÑfÔ+üÂÏ{ÿßý??šy+ÿû/ýú¾üý¾ªð0²˜æ4ŒWÌ_1\_IíÒ&ÑÐmI¼êqÇâšá¶èº¯&$fšfù£–b·CÛ4:03À)òªo t¿ªÀ¹Fb}2ÜíH„j^ƒ©"v¾bY#t¦Ñ…×]‹:Ì]Ö9ÔNËp’cuGPÕ©KÆ•ë7¡·„·]l?mÚ]Çôt»b²XÎ{±Y˜×ÃÜ!}õ7éuKï+>ÈÞæ‘²J:&lŽ–ff®¨ µEé™@%å©C3[£É•Äf¶oÂi¹\Ÿ=jö—ûÍÓr±öåBR_®ˆðŸøDÞu mwľ(—)IÑ9Ï\´Í.ÃC˜]Å$Cg›s®L1´Í r!‰™-+Î"ùbÑ©‹©EéÀ¢ÙëçÙÅ¥ª~ðƒ””°ÝâbyšeQ$†”Blöm½}íõ7ˆœ/Š›Çÿv»=çñ²*˜ù}üpY­Úz‡„d¦ï|<4Í~·99{¸>{ê]lë>“´¾Ú© ©‹s ѼÏÝJˆ˜õQ‰q–Xk¬§½å"´u »jyŠHÕreª÷,³5™å ³,ÜT6%p®EB³Ï£%y ‰#b 0;`ΙD ÷yûŒ!4Ò8ÇÄXï÷¹ŽJ½>zô¨›9¾üìÏýüüC?þ3ù7ï½ùgþ›Ÿúî?ü§«ª(¸‚è‹…ãâU-j€5$Fr¦bš^źbp=ŠK›gšÉxšI/°fçH§Ë;gÑLFä]ù¡åjY•¾çû›™Õ»Ë!·öí‘uÖ†1ˆ$SišÖÌÚ("òè>‘óÕúÔL7Oß*—ëGgÊEòj M–Ua¥Ûí[&IY^ªmöï|b*c €ìNÙ¹˜ ´õ6°)Yˆ)¥‹¶¾ô¾t®È;h[o46ŽL///‰èììì:qµLüuY†jŠäÜG†ñ )Â#›´I{füº+j;Øç¨Ðç¥4=ÉÈc_ù%¿ý¿êw ûµ7ÏàGÿöãgÛ)Ñÿ]ŽˆïbÔpSU毒¹¡ÌîÞ­ÿÙžnþ§kÌjlæ*й[ :éÃÀ L^vŒïØJŽ"|ý,BÀ©Ç!àœM3¥4 Ð|äP ì¾cJ«˜J|ü“×»ŸêN5•ãK•ÔŃD“!3{$~>æõ:BýírÍÜ๥6ësE~o lš™„:´í® ~q²<}P?~{WoNO„´|˜ÿäÉS5{øð¡÷¾Xœ´MýäÉ3DxX,R Y=²èvQuî}Ç ¡é´~øèõ‡^ËeõÝÅ;—?Q,ÖÐÇMˆ¨3Ÿ÷ÞzÞ,…V$9I–õÆÌDâ´JÌD~ðrSÕØÖª’BkªMÛ8ç½÷DLÎ;0H¡W”ä‹§ßtÞ¯Ö§¹rÀγ£¡ )åUT/LØ©j»ßä ôޱ* ïÉEgXhàËE½Í¬ÞœgÛfbç çŠgÏž©ŠjG¥wÎ-×§ô$6û-Hú¦¯ù¢&èßøÙ_Îïyûéö?ù‘Ÿú®oþ>ç÷E‰–Õ 4nø.¤#$Fx5ñ;»ƒZÓ͹&Þº.;íÓKNâ€ÅÑ”í7¤cNͼñ9ToÏ5ŧççÞ9v"š$SUIÅr].NšýEØï¼ö~@l÷•´ßïë½Ý%Æv¿‰¡Ž¡aöÃ"æ|…„1\ÓqEʹ"¥vvþf…wÜë–Õ‰¤à\›’ìë°D7$@á™(Û†r±:í´únÕ¾{ÒHèèªãVã+yýW'‘/'~KÉ-G3ÑÜ‚•Ìdàu˜i[o‹jY.ÖSýý|?s|Oó{©‰èÔóÄ xËú½‹ ð€ÛwŽRY‹ÖFßêáš“‚›dÌ&Dß+L°ÏHÆÎÁaJrtŸlî®m6 ™'ŠÏ0'‚ß§kˆõ}’.ÊîËýºò‰OíGM+¢‰ÚÙÑû‡Á6*¥ qºMñTdåW¬ÂxMólš/★LÓʱ$2¹«‡ødààPLÊçû­ßð¥¿÷‹>wø¦Í®ýÁ¿òwþïü33Þ?ÎÒžûæ-h]g³w}93¬Ïôó{û~ÌÐÈg}åíPŠŸ&ƒ«kpô>Vyzk϶ïÏwbw7WaJ½ü}¯>Ê vD î*l^Á°ÛÞ¶÷J ÏŽ–¿ƒþ²¾°/|2w+“¼ÈqÜ`>2]Ñr=7Ç)êtHþ"GÙ.· ®k9Ü.Øyy¥·+–M”GR D”bSoŸU…+_{´Ûn ´¨‘w›§DœõïTÎÞgX]³¤Ü]WI`V-OT…©Îø ç¼+ªlS”•¯–'jfy‡Cæ±/«H ù&gì51óf³Q3³}˜y¹\XŠM†ò›¦¶Ùõ+²å„51²/ôÑ£‡ªzþìiQ”‹Å*´!„–™ì—'÷ˆœÍor6\ì@DMQUêíE‡WF4b.×LåòOújùàõ@è‘“™’o{h÷عxÄ$M›ò‰V¥>Õ*/OD–RÊÐu½¨ˆp D{vÎù¢ÞïÌ ÚêXTô˜Õm\,'''9²cç}Q…v?Tu3¢'´{0pÞ!©5’HÔ&V‹DTƒ.ïì[é¨Éb_eQˆÄ¦Þžœœ ûnh÷Ì4T¨%¥¶m|Qägú]ÿúWnöí/ýÊ'‡Ëÿá¿þ|ó×þ³ßô/}ñ|p†Ý:çáJ‡? ×q¹É:s¼1’´£붯Üñ5U$FxÅ"oîà²ã †3ˆ¸ß^^<}²X,Š¢¸xü cöÒ„È"H m6»]±oœ;¯ªåb}¯Ý_æ…©yŸÇËJDß~ûíÂ{ï8&‰I ŸáìÑû‰9µ©œ=|=ds— ¨Õò$ ¬Çb”\ù\—K_.•óÅâäþk`°ß<-ªåâôAg kj`¯ûÊLw—u]‹(š†ý–œcw«ÇÄÄ9^ìqÿx­Æú]Ÿøy|x<¨Ü´F}X™ÀWí†q»\©ÓÖ`Ø«¥Þ³«Õ=Œ¡!bb—iÊ9×4U¥çDn×X_Îùô mcªaÁ´œ„1³MätUEM¦º\HË™™ˆ‰¸O}r G•Ù~96@êK§Ô±‰ybæ2øXf3Ìñ¤íP æ-Bíߥ/‡=?WäúƒC{ïæ»Ó?VÕ4Qް¾ýIjšigJÁSÖÑAØ‘¡–ÚùàèÐ%é¤ÒÆ`݆ÚTróuüUS«ˆö?ΫòCÀÞKnõq=vÞ:8ÑU•硳 ‘ˆgŸÅø¦øO›Ø÷ ¢wü'¾ík?üÓ›ú×þÖÿýþûŸ Q^$´¿)+z÷¿Ê鉯ø¾Æ1ÿ7ævc¾ª½lG°lO/ù‘³™W½ö:!bo´Õ)µáqwêRÄýsG¢Ñ5pÖŇcšÁøì3½*«þ2;vŽ8kÖ8ÎÜÙλ¸WúŸÏšç7ò‡ß0Çôþ_§ô6ñœºâß…ÞÞ ¬øx Ò¤£"Ðôà.´-¹žjùq9ÜÎÂúJî9!GKh{ƺìgdÑeÔ~þ¥+ªl+ðnîz†‡’;k=ĉpµZVÕÂUÎʪ‚>™Ès.#Óºê0±¤ )x"–r±³òëö$dç‰(ícT“p ½èL߄פ–)J܉ޚ¦ØÆ¨UUõ9«!÷±»BDbìúsÞ{v0d×{fRM1˜in? ã€Ù±ãØÖíÈÁàbœV‰g¹ $ˆ¡1SïË\jÏ.ßÐ[€Äh&EµÄÎíˆÅ—´}ÆŒnMv·•FéL¤)í}JôÎñÇØžÉ;–Ýq­Cã›O×û¢ª*ï‹^²KCè=;"S±¾4î@LZŠ˜¦”’™¦”ÔÌ;UMQU½cçØ9‡D,â˜1ÃVsÚ]‡`È] U‰ÁT2NÇ1µQP-´{0D•ToŸ9Ÿa¨9ÍÙ„ `EQ8ç2L]D‘R$çØy³[Þ¨žC…˜k1¦t$@fn¼»™uËä•» ¾À>}—Bo,:;¤Ü»™òˆÔšeèhv°Ó\¾ÊÞ…Ð2³s}¤”ýD0½òDM5ƒò˜JfÆÀÌbØT‰Øy‘ïæF7Šbä…;ÅXTÕ²¬¸¨\¹ˆf ©ªùNIŠy·êLÌsœJ”绂¤8©áŒfCÀ\ f¹AI|Š.±Œ¹yw*É RŠÎaWç0K1¤Ô.V÷±mº.çðÕù"IbÊ(ƒ8ºR ;ç\ÇÀ¼Ïö™JhÄÞeæ9ørq„H:ÊTŽºÙf†¡çK•ÔÖ‘îÉÜûõ½åôG_.ªåZRTIm½ õ®˜œûBS4UÀdfEY>x=Ç^m½‹Í¾ZV`ÖìMS¬Ö§Än±>M1½ý©7cŒ1FÇ”—¼áQ„˜D 'ú8uÛ"Æ$IDÍ€°¾’lZ·1Ëÿ¤4‚øCˆÈĽÏÀÀëfŸª¤dì‘‘àìdñ=ßúÕ?ðßþÔÓËýpð~âÉðŸÿÕïýÎoúç¿è·÷3vÜÛìöáõΈ„ØÕÍpœ»Ë68?dykìF¢÷T—ÄD{èæóço„‹Å‚œÏ¼^0!ˆèjYä°&Ï\BôŽcÒ˜D$ë%5Ícƒ:µd¦Þ±cvα©2f%MÑsY"6û©ÜšªÈ`}€Àޤ‰šÚRD„¼"Òòä¾HŠm·ÆP§Šr‰ˆEY X0ÔÑ$g;ç^²]™…½'ÍiÄ#‚GvXëj]ô¡É¾~* é3cvÖ#¶‰9sG²5JæCÏ˹Y“FXüdª÷¤é ýüEŠÑ3dG¶‡YQk¨GÍj­xWž»TL{D§^h–mÖûx Õ®I—üIÞ®‰¿ú @HŽa%Ó’`ÿ6ê#ï‘ÀÐAr4C¹P¹Í†ƒ,\îÉO¾+—쌈 LrBû»z€#1Q‘áÒØ9W0;ê тڔZ ")ÆcŒ1ŸÞ;;yCÉ4÷/M5ûåv…ªþA‰H9U•þÛpúÕ™(ËÙB|<«® =1Gž°ûÎõ+dêJXã‘ʑ͠óGLÓÐûõëïù¶¯º2ó]ÝîÛ?ùçÿ»þñ¿™’\»HÙÕ®·~jýdðÒ¢”×üteCäV§jÏù¼U)ÌTE$(+ÇNŒWº™BYÃiÜÖ»ÎÓ@AêM_§çØR )îlâ|9Ör¡gOô€GëÍ}6ë?À`p?-·¾Êâ!^Q¾ó ^äß]>2AÆÁV¢·;IP=~¦ýJr­>A~âîäÞýaµÍê¡ÙÕr±< Í.¶uQ-Ì`{qB{ïìþP7 MŽŸry*£Bö›ó¼Aç2º¤$)fv¼jÊ;%9_­:ˆü ÌZ•kÁ2×Ãp2Ìšˆ±\»¤Ú\tÙm¶1=óŽq±XN£x}|ñ´p®pEc’Ü€ðÅbh´s§¬4ˆÍ.ÆX×MYVEY²óÄ^;™i€…œ7PÝn7eUÝ¿?Æ6ÅÓ õ²4³ìVÿôÉïýz½ÎRœYMßûb}ro·Û^^\œ±™öŠ€7qZRU-Š®0SÜ<{‹Â³sÞûÕÙkë³×Ûýel÷¹—ß ’®¿ˆÍnÛ4û”4$9{ð`½:ivÐaÓ)Ô;“rAb¬·—9}”$Åry‚â+^<}ìØe¶òƒ³{]Mß—ìK05Ó<Ò˜]®ìãsÕ‡ð¶ß‡¸_-eáŠÅI—C€{$dW¨ˆ¾ù±ÕéÙÉýGùòf\ï.Lµmk3Û7‘9®L©¬–ù+œ/«Å2'åã^xý=ßöÕÿÙþgÛ™ßáÿ¯?û‹ÿð׿÷ýÁG÷Oœ/æÑgg ï|©š4%bw{ÝA3Zªˆý]RB³@íÓk¼Êo&>(ãTtúõ¦)!"g VÖ²Ÿr·¯>2"ïw»z¿[.K":½w}ñô­O:V‚™={öl}zïÁk¸xö´ çÈŽ\¹\AVx0³”€æf†z9_ˆ$”n:ÄPƒš¨jµX«J -"VËS@@d31Uô%¸Â·ÉL%“0 ýu¿yšw¢llYxçzÞ‚©BŒBDά­»'×Ìe_¼Õ0ƒêbÞ݆oÀþˆ¤”Õbo‘ S¾|hw›ÐìŠjÙ1©r¸¡–¤ž"e½bÃÔ#1˜)Ùs“h{Y4ÿÌR1ÒÏûžï5Lì±% x{ fƒQh;6“›˜Í©õG*bÓþÄG`L ¦Ù߈62›[Ã4„B¢#ï»)£À¦Ý½Ã‡Öákßô›ÂðçHœãÈhþÖ'~vÈñÇñaH¬xtÄLî cÐ=‰{ùRœâj¦2ÿWb»&ˆœY¶;±®Ó#ûÃtØxQ3üÍìþ\qß÷`ý½ä«ï­„¿ü«ÿ®?õ#?÷‹¿zpù³QÙÝ„÷hðj‘5“ÿŸ›ùÍ|ã^8UU¡sådC/fÄ1fÂ:>\<0}½+3Ðë©L÷árZ“õ‹¹\¨êXXŸõs´èº0s¨˜õ›ÐI¡ Õƒ¢Zs íÆ…ˆ|ò㿾^­NNÖ’¢Hòå’]‘ùÂÄŽˆ~ã×?V–åÙÙ"š&$—»›š"1#²JT³‹‹‹¶ Ÿý¡ÏM!_~ŠmhëÜÃcW²/êªrzzÆŽ}¹ìÁªrþøÍåêtuzsy¾½¼P3QóLˆP•žËj=€Gc¨»FŒ˜ˆ9G} „EQea$D"çó®VË“õ:ßOvžœ—Á€½¿¹“Ó»Ëó'ªÒ„$ª«ª(ªÅëø0l/ž˜ª¤ÐÃp<ÎjGØì·C#Z$23‘‹1Ф¥ ½/³^yv°IŸz󓋪\V%{Ï®H1ääÔdài8_šAh÷¾¨Š²“C""·»|b¦Eµ"Ê4ÿì §õî"Ÿ¡Š©Y¶‘;»wB™>a²XÝG"VÕ‹§‹ª,Ë l hb¨‡móäþûRl7窓˜ˆ­VËÕr™»¼E¹€¶©s9š™ŸlÂ÷ÿÐÿr±mŽïöG¾æKþè7Má_AØn¦ÄîÀRT2$¤(ß“ÍÏ1£ÒÓaZ`@îeO#…&Å&¶{‰á'OUu¹ðŽébÓ8¦å¢èêEY”ëíær³¹\-¥Ï C#"ë³×4¥ØîEôübSxvœ¹K$¢)ɽ¯‘¤`ª9·p¾4‰A%¦¶a_²/œ/Ùù|•š’Ô—Oˆ˜‹EÞó6›Ëó'Oœ#&Ѥº(<"´1‰Zá²l‚ªœÜ{tÿõˆÄr)¸nQ=^™?_w‘YUI’R^Ìóž")L[QÌž\¡TY\¹Àé<ÄüV·oH/s¯æ3âõOçÿÅâ£SåŠ&Ûk½óTz8.4»R<}„tÃ\Èwêq†³ÀN'z]6Ê’õ´ÜÅïNû,•zËáDIn–³ÚA•`Ðå:ʂǛmðÁ×Ïþãïøú“ÕÛÀOüí_øãúG>úo½Ô\‰eÇ÷J7yàEßÿW‚òëC³àßæßÞ ¹…§ñQf"û„Þ>Y­ìˆ›ÅƒQ‘aÔi;€¦LYγÑù*…6´Ûju¯ZÝÛ_>­·—Õr=—B»gçªå=K¡†¨GDÄH$±U•Í *‘Õ—¸gþæ+*Ê%û¢çk_=±/ªõ½f·‰uãqÁ¾¸53Ó”¯NRºxvn)¦jQ­–«Ú £‡Þí(£?ö›§j&Iq±\Oñ„)I1¦¤ª›Ë DL¢Þ÷ÎÎrƼßm//7Là<;33X®Vˆ()Ø@•ÉõÞ³wì‹¢b/š(¹Ù `ÄôÚ£¾\Õª­7í~ëÍ`[o ZÝC¦ÖÒf·köÛ‡ïû,bM­*a¿q…Ï{{ ™JJì<±k›¤P.ÖØÔ»”ÒjµÎ¶™×¡š$"*Ë’(1%ç¶M£"÷¾>­©³/¦:àlUsfÙ¹½#";ߥ‰’B³'öì\†2m÷­©®€˜yJs,Êe~"™!ÚÖqØl÷½3‡•ÕÊ T¢¨Öm2h:È;"œ Mì.Ÿ Òb¹Ê£¶iv»-3»Úµi/ ‰¨ÚéºÊ=§7î/¾ï|õú£?½Ùæ…¿ñæ“ïþ¿ôoþk_õ_û¥/ƒ˘F=“Í^B.ÞÇ÷UrwX¸ˆ…H;=ÍÛbF!]«pë}°Ù=Û_>Í\ŽÒ3€;¹ÿ¨(ª]ýÿ9: h*[AÄ}Ýìëf½*°nBLéìµ¥ª¸¢P‘è}é|ÄÄÚ} mh÷Øß2Ãg¿9ï+$òE¥*±ÙJò̾Þ]˜©óEoÆ¢)¶N˜N×N(§e¹D¢r€(±‘ý~eéøå´µ¯9WûðÌÛÒoûŒK"BÕƒÁÚ=‘ËE$'ªÒì»Hœ=9_B¨·wu®=3\éBúÙ÷OoÙ4QÃiñv¼Ñc*ÖWV3Z¡ñ°ƒ†cWןó±×ËžéeÃE£Ã^ÐÌ’õê»7.—x42'×̤˄f•k#œƒ² A ÁaÇ*ãA’± 'i¦I¸£÷Yî¤ ÂÄ©Óy‚™…ýu—Öñý;SSèù×+&Íž»ŠËDà@ŠaæÒ9 z;B]W·TÑIU§:ýC˜ÊOçÛ®óùö±7Ï£>[,—ÌMv¾Ñ"f숈]Q‚AÛì9‹0çÒ:^\^˜Ú¢*ruâÇ;«5¥˜$Åtðߺ÷x×G{v¹Ù>[9š©Óñ Ó»ø"³‹¡Ý×uUUeY…¶N1f;ûü¼VËbÚ}þ†ñwÿÕŸü¹+¡IÿõÿÔÏÿâ¯þ‡ßþ ÏN^²Bz]Ý즶Ì+[ûl.­~`6h/yìì™úÖoåIüýò¯ý»ßÿÃÿÞ·|ýïûç~Ç0§!¾ú…â9ùÄ oUjši©Ý0À¾öÎ{½©!å¼*˜-‹Ü%FbWøBKÉmì²*À¬ —ˆÐÖÛ¬}‘c,DAŒÐo ¬HG)mÌØõ\áT ³à õŠ“€&ûòº|ŒbsÕ ,KD`Ç„”yYïJ‘Ñ çÃéæ§ÿ´'Û´žbv‹ÄI·ÂUϯͺ.DŽ»%HUÍ$6©Ê¡Ë=hø“é7DÁ×Ý£© IÏ@ Îÿ—§ŠËÄ® Î¥WrE¹\kJ(ŽYQVf"=q‚È!´mSãa{oŒÎála>ÓÛ•™åqzï^ M³»5íˆóMžˆªÅô`fò…wEU×µj—Ò°ó¨¢&Øyç ü¶»Ÿ½F"N ÜÙð¼hްÈ2NØÅC8ò 2 l`rwè }é«©jF}?ØD$/Ï7Jü-óZ”Å|Þ½sŸv+ÎѤ;L4¯@Æ~ñ7wªn_÷Õ¬w~Æó) ½€Ni0ý̰†¾Ê ½Ó}¯ijιÁ‰² p¤RëØu9µÈðˆî8Ø¥­ªƒ¶‰€ŠåÌÌ&ò¶¨ª¿åü¯‘¹íPœd™=ØÁf?çî[_#<8¬ÍéGV8‚æ2c0ìv SÞT 7ëuðÈyf—åX$…žÐší,ƃÙd€¾×HìòG¼/ü½"sðSdG@ØÝùźÙow›Ëj¹*KQ•I^ ¾\H )¶f*±•c””iïçO·ÏžÞ{øz–#AÄGï³+ªõY6tFf"n›º©÷Õª(ªÕë«3kwCµ:£„ÍųwÞyüðµ÷Ý?{-ߟÐìUesþÎ~·Ijc½}ff¢&!5mÌû¨š™wu'Ì}Mž¤jI´½¼LI=zÈ„)¶ªé÷Ù—ªéòâœ8pàìJXÕr¹$W ’+Š^+ÒÄÄÌÀ¬”Eí%iúúxY­bŒM½'j@$iJ‹“{Xï¶)Å]}YÅýû÷1»½›Šª¨¤cÝÆeé1Ä$fM{ÎL«…7ÀÒ#úó>ø}ûGÞxíìö¾WS7{ÞÍÏÔæ^çúà³äœ‡# ÞœE3¦ŸJ1äñöBkgérÛ,EYðTØ'´;êšúìrØAW%cöÕ²Ùœg¹"èÍë[S+ V…”4Æ-ì¶ÙƒbZûÐç~ž¦”R³¢Ä¢\«S ¤°Û^¶õ^ÔÌ íU[- 3ÙíðŽ’Xˆ)Ëûe?™ÍöÉðDÑ{ï}¦ÛÍå °\žÌ tÅqÜÿÜ1¼ç@Ùê–ŸzWDvîXNïxEºöã¾¶ÌÚ hzCŒÎ35ç;ì ëM&ϬÕCývææ>?}N~àÒ~ÕQ쀀ø4œœä…vž·Ù)ÛøN8ìŽÂ@Ðî!Ë4{À)ü>-;$æOýè:oúΉÐ`T6E.¡ã}9ó¼spk‡@ë9¨ë·YöïØ…1³@ÜfqÏí‘20FËãÃê™TøÀÎÉúT„o.J0#«R´ ¦Cêø-_ÿÅ¢cøôôd¹\…fof)%3Û7Á9ZV…™mv vð]vLÕbY–‹Íå33õÞ#bµ:µ^tûðEÔT4u Ý¢÷î?ìÌçT7çEQe•U’Ùyf¿ßí¬¬̼Ý\:çÊjf1Ô!IÓ¦åb±Xt5®ýn«j¢bf…÷ˆÀL€è}õ ÿà“ÿÕÿðÓWÏm¢ã|ù7Ý—1Ñgè.þ¢IÞ]ÖW„# ë!tWg8P}ȦáçN¢½I &³ ئ³naÎz¼‹t9« 䎮1ôEwrô—ܽnÊþ_ÍP»#”T˜hgB$os¦oÿkî;&¡\$rCzw# ¦óˆè|¡’R ÉÁy"NK¢bfƒy÷¹„È]ãYcPIH Ø•f¥ïÃ¥yXSU¯U3#ç)ó RœŽ¿¶Þ‰$BtEuòà}f°ö8¦ØÖµóÞ{WTëb±üøÇ~MU<|T–åöâ1±+Ê¥™µõÆ—‹²Z‡vÛz"¨ˆ\[ozD/O ]\<»xölYEÁyR•ÕŠœ¯·çìŠjyªªï¼ý¦Ë]ò®† fP” Dr¾$æ'o¿imÌ*é¶\.NÖëÝn¿ÛïONÖ«årsyaf¹‰BdvÕb‘JE¹,V'í~êmßõd@dW\wsÔÛ] m&0„Däáëï—”Úz ˆÕò4†:4»ü‘º‰!nM¨ª_ð»¿84õæé[fàËÊ—‹r±VI¢R”U&?¨êÇ?ö«ûÍåïøÂ/N14û- å™™\¾½Íö¢­·¾\šÙÓ§OB¦+àÌ}Á ðì›6@éQ‡JmCBÄE9j#@Q–•À“¨.K„Ì)uåë\-XT¾ð.%1ƒ|ŸcĬî@e¹ª›z³Ù®W«åj™B›RÆØ¢+*&þÔ[½ãå¢ Â_úè“?û—þfvÈ:x}ÖÿÄýÈçÎw^¶ÞË ìšø ‹²¼Ã꩹îÜP9Èé#3³óOêÉÛŸZTEá³c.Ö÷ÙùØÖ¦Ò6;UÛ욪*—U9]7›¶5³D’|ð³?„HÅbÕ—H *bÉH[ÎÓ'ï´m{ï¤B䢨r ·ß×——‹Ê—…÷EÉ\„v`¾¨ˆ¸©7D.¿꺹ÜlªÒ—…ó¾rÞ·Ín0µ>¿¬‰:gà墨–'‹Ó‡ðéW }IJÏ«=ÀÐß½Œ˜Ú¡p¾({ä¨Jhö©­; °,òÐÑ®Úfß ‘ ª¾¬ºþ®©;Ú!í¹úm¢·íB§ Ö,Gº6r¼áK{+êëGNsÆyýØ(ÇÔÿGj0ÎÇŽtzuÚ¦üò.KDÄ6QÑžKxÁè~4:Co'×Ù•OùƒFyqÖ.®Ù¬=ó4 LRÊ6{Ýó"B$UI1tq\Jˆ¸½x§7êƒg—Û¼‘;GŽ9[sœž¬zŒÕÛ Õ4ô}c[gïFK¡A$SQS•„H›§oAçÜfÛó·]Q¸¢Ê)·ª;_”P–Þ{ðFJ±Þ^NÇCY-‹j¡1ˆ„j±öhv^Ucl°\®SlB³÷Eg?)’œ÷P… ’Î^ÿ ¦š œ–«ØìêÝÅêäa¹\ǶÖvõ¥ªb PNDë6€«ƒ¨öÜŸ….Å6ÅvQy"ü•ßxçÏýØÏÄt(u²ª¾û[¾î÷|á‡É¡]ãMvˆü-ãœi£èàÍSI +[PÏM;Ó´ý1E`f)Åi•SRès¦½Ã\všÆ[ΕeUf‘ IASȲGYÆúCŸûù±­c³%r½z¬ÖÎ ŠÐªÊnó„Øe­†PïØyöSkxÌÌ'V”K@f3kê-³{ôðAÓ4—Ûfµâg¥l`_0û¶¯”ÀÙŒ½÷å*„ |¹dv›šÙŸ¬Wyqerª242 À¹«c£#1¹™üq^-Ÿ'}›átËÑuƒœÛt„¤y{¢ó.} 3¾ƒ”@bH¡mëí¸bg¹í.íëö"3»œöø²ôÕRbÖ÷ÓM¢£[ÔLZ¯vÇØÁl7Ôí6…àÛ¥¦]ds¨e:‘¼éæÔ¦ÓÆ(±ëú8¹Ï­‘†,ÐÀ‰PmbßyÓQç¹jcª9÷™!D=ÐDëÙnD13޵ÎѲ­P/. W"iÍ L°bòû{¼¶§‚>ñ4æ 1ÌLRÂþºŒºûBƒ^Z'Olc¸7õíR5ì æ½pŒ ¦ÑƒÕ>×eÙ}ÊË̦z0 ‰ˆ3I(¹¶Ù/îD#vÎò0èº0!‚³Ï‡ÊùDÙU˜œóƒ!Í~™e­fŠ×®;Öß™_ûäùŸýË?݆C/…/ømøÞûœT£?ÌÕÓÃÞ-cÕëë:·)–ÚÕYÍÝ›=yÆccñ™LG_ÿÐãoÍû‡¤¶»¥8ãÈÐT ;• U!s݇‰§Ò¬ÕC!ÓáÌ8Ÿ+ï×USm¢/ÃDî¯n*}ÕÊæPŸAI¡½Ô ÿüÄ®3>J0µï$ˆ¯Ú¹'bÀ}Ûa¦ñìàDxP‚JFƒ½t Ø-  Ý2JÕ,»Ùjˆ£Ãu_þÒÑÔëú˜³à©»Á ¢˜ïE§¥2kLT^Fæ˜î7€@ÌEá¡ÓK›m ±m‡8¢ )_º™2£¨†¨ÙÀ3{¥ý>ï~ˆÈ.kæâP«qLmÛî MHÎÐ)w,:÷y"¦®ÚÖ• ç¶0´©,¦Ì}ìˆDÎû¡®«Ò%yyÅ’ˆ¤Ä.+°Œ”^ë_È`&³„I"Ä$’d}r ˆHÞÌ|SKŠ¡mËÕb¹Úo7Vï§ûh ™I 8è¥õú/Ø+uÌæXRD"cg3ÊJv}”¨ó8bFÍNª¾0³8¶DSÓ5¥j` ¾o‹‚¤„€¾¨Øy"ˆ*’“€RŒá$¶*QU°“æH¡­á"[Ǥ]!D»¨›ˆÖ'§mÛî÷ûS]×Ù *÷³¨ØG?þäìgrú8}ý«_û{¾õ~9s¦ŽA®‘ô$ô—Ë\sè‘×üiŒš¯Mœ×°~xÚ™0½W¶± ÊÂeðÑô÷»ýž¼/ˆH5e<”Æ|Q°ó1KŠ˜YLâe'‰žeÑòƒn›&%a½ $ż¤©jRpÎT„™bŒªš3Ý×DmQ6£B”4(cl[IÚ´ÑL£ìéÖÜÙ.7ÅY x.okf1U_¤Ï³€Æ¡·rÄ ¿a3ÒQ‘õƱô ñY±Þ© ¨ jÆÃÊßñcTlVMÔÌV'v0Ëmgƒ­ \ˆcáænµ•ƒ¸2Ë4ƒÞ4ÜAëjî£ ^ïpÝ‹—f‘´>_ºž“¨lrëi¸û¦ŠÀDÙÞìjëADDrjºÛ^–UU– ï!;·¼9ô¾y#„Þ틈BŒmH…Ôm„RÝUwË"j&Ÿt¿!f !"bD U2³…™™™æ)(¦¤Úå¹°ÖËgXvä%Dà,ãIÙ]/„Œ}¾U1ƦiÔb\®ï#Ì8ˆŽæˆs›HL©\¬)c»‹ý¦­÷šBµX®ÏФ›é‡ShL$!Q劉±Â^dÈ1qJ˜yÆ"fƒÍ™º¤ì"7BÊP_fÔ@^&ò9µ‚ëäÛ°“CR‰D싊Ø!3€©æ¼CŒMÓ¦ÐäªTVQM€˜b›bã}ÅÎ{Ǫ˜¤“ÜÚ†ë“S³Í~¿O)© ÷[y.¾ùÎæìgvõŒT°^–ÿþ·ü+_ú…¿-WÜÇÅnH}^b# •v•µïMûÖ5<.¨,wXºôªé‰HÄœ}½^]… «2o„ ÙìöuéýâÞ)i–¦51e_xö’6fÙYÚš6 IY-zî"¶M³«ëeéQMP$¨ZJš—&ËŠLcjšÖ;‡1í &AT'’ƒ'QîP ­j݆hÒí7 ë% î·1©ÀC–©¨Ý Ã>¬½f7ä·W¯ÏG£ÅÞe0mëM6Þ餮üƃA•!åÄŽ™û\Ÿ‘(K¬¸)vr¼+<¥›LüÏ/ó „e€¹¹ÜX^í!¥W1%û’ñ¬?›ß³r`WŠÌ…®~ÑÓ´ŸðHÔÌèˆÀ˜úŒ³û]çkŽDDF6­Ì2ûÞ¢¯+ÃtA†êÜSï¸6އ~WŒÈ¡ÈD ÔCoÂw¾ àÌ 1 R›Yö"w>ºQä†þv5Õ«Õã_fØ¥)¥SÌ×C£/äû~°‚ÀÑ;m¬ÐEç¡2p@ÇI¹攞ñì Ñ:x3ôVØ =ﳋf2žèSO.ÿÔ_üÉËÝÌ)ìó?ûµïù·>òÚƒ“k× ÷’LùŠú¯jI»»g‚mÉò~ðÐà"3jKö©MÖ™3Õz‰c¾(CRH8âwçà&Å&´û³GŸ…D±­Uåt]qÞ¡b³ëk’ˆõ~[ï·Ódå÷`hE¥š&¨ wöðuDª·ç)†rq D,"Ûý'™qQù%&)Kïå ‘\Y1»7?þ±ApøäÞƒ›ØÖ®¨œ/‘ˆÙ5o~REJψXNT7›K\V™ „.r@Ä»ù­ÞüŽÓã0yÉáw·Ñ2p-Ðlp¨ç~I/ Ù> ¹ôIEµÊ悈„c…c Ö)Wÿr› ™DR5AÉô »NÇÌŽ­$^ ¾¸Ê“z¶6Ûõ $N“¸ç˜[Û•Á±P>ÇdÀüÏhB`Ÿz‹Ù°öU“\eèï¡átã­Í¯8Q›9AgîCì‘MHþSLÿJN¦ó §Û‹ìFtÒEèYß2¥¿Û‘{à^{÷PÃ9Àa™ÁìÊ"ÌX¸*3¾a¬âÁXËYüÔ’qšâ 3æyë$]™‹áagÜØ=™s±Ùÿùï<ÛMú‘¯øßøU¿Ë;¾Ê/­¿ï)‡Ìnq3“_W.¦μ#´ÌFá`K #;é¨Xgý"6Õ31‡6-%(7íÇYÞárÁWÉ®H9F¦Ô¬­Óýh3O<¾tŽïE*ÿ.EG3°ÒäyõÈ'ëý]‡TíÈ ŸSŒœŒ°ü?îìÁ£±ª=ÚˆÏY`þTg ßÖ’âv³A„ÕúDU®ÄÂú²"âL¥ì ^¦"*õ²úÿÉ{“˜É¶-=h­µ›s¢ù›ÌÛÕ{ūƶ…-ËÈ2è,!‹’‚-„€1 bÈ„#&HŒ<ñ„#$„˜" @ Ø2F.ªŒëÕ{uoÞÌüÿ?"N³÷Z‹ÁÚçœ}"âÏüo“ÕP©÷®îÍŒ?2âœ}ö^k} 'Nfµ]­7™£é@” _ç¶ÏØØièfNDÉ +'îI˜Dî7ÿé¿2t‡þø¤ª9.Wþ:óíåy1Šê˜$VÎÎy¢}Ó‚Y:å¤!¶Ú-‹˜3“p Ò+Úêå^`lwÎÕùòtxòÞ9GífâfN§ãÃÝ݇œónÛÌêöî¾^ÐCߥqìÇ,Çn˜Z {‡Âi<Ã鉈Èùýþ>Ķ?=‚j(uë9¼ÿ¦;<´›žNÝ8ŽF$QΪ2ôGÚíþÉ9ç «¨Lœãcó+òqþ²©?²óHt:D„™t³ÙóÍö@‡ãˆ §GD7…¨-"™Öu»¿æa8Â=Ëò¦E?Ùr2§3TØÔ2!69§áðÞûè|Pa±ˆ>‹¬ãœ&ëWû#s@ï£óRˆLÇ2mÁÁ;ãÚÀ<*qà}ìGøÏÿ›ÿáß<Ì#÷ý;í_øK¿)"¡ÙÆv«/˽ ²6 fÖÛOÔFhgÞfÓôÛ!y¶ ï—Q6ø…Éé—úžvW­ó-%¶°»7Æõïý¿¿+»mã{ÿØÅî|€q8zßl¶7HŽœÎgÒ”îé½™{hÕ$åœs.ÖÁ‡÷€ˆ!nšö†s*AÕK” ˜kšÊæ0&È:¦ìœóŽTõtx[ %’‹lØ2ëëÎîö5 ýè- ª¦¡Ÿ£ÊljÂÌ&Gù0¶øòÉùäÅ\]¢DŽ¢OcןTDL†a½õÔ>Fþö—?ow»ÝþÎõª",!¶¹(F›†mIß=úЄ¸E<_v½^1ÌÅÅ&{…náŽøÌXy£+Å™ »®`^PÉ^ƒÇ*”ç#}m­`XxÙˆ€:ÑqȈBgòÖåb躕<û^ë øU_I£¹¾j–´ŽÏý b¹9k`v©–,wÝ>B-ŸÍÄëÁ;Ìâãyég~H}ƒJx©ô¯çò5½PŸÙUk‡«©¾G@XÓѵº»sQVÒ™üº}T]Óuº)Ë(báÉU+}âÐãÇÑUû¼§nü/þöÿôóê|u³ùOÿÝ¿ögó§KÓ÷Q·ŠkGÝGŠçµ‚àì.À‡›ö?i¿JÛ6;üMÁ£kÿ‡Z.­*2QéÏ ¬"OÏ<œ©â?ÎÓÙиÖPÖv ËYRoZ¹BÏŸð‡™}Pprç@Ä?¾s€Ë`Œ)ð r*9Ç•«gõ#H§®wñ‹Ðóô ¤àB°„'£(ãvèž@€ýÍ †-Ì»>`&íAUwwŸÛPO$¿ÿƒß3Û­œ†œÆØl\z—9&Ï r“}œÙ½¯ç„³63§Éåû¢H™¦íË!,Ìo~þÿ¨¶¤€¨œ9 i<©Bl÷Îû_þâ1†ÛÛ[…CoˆbNƒ¥ñÍïŸÓ`rSXæ­üò IDAT¨ýхƼ٘GïçÃ8œT¯5“XX亽ON£ó¾iwÖQåœ3¯øºãpf6Š©w¡¹ÝðÊîv[Ëdáþtï]ʹò~¿ßíoÞ¿}s:,Â;Æ@íöV$§¡„ØîB³i·7¶&¼ D>4[Äà ûãã„ËÎבǾÎ]?cŽ Ì¦£ÎQá<"Òf?ô§îxhÚMÓnrN91FUÍÜ3s"›U9g%EçPˆHNýp:ö͆‰4ƒH>ŽÖè!Òþæ¶ëû1EX$ÛéÒlö3C§†½L5Aä-‡Ëùbc.€ó´Éµû;á„`AÎ ˆÐƒwä=å<ši\1–Q0"±=Þ9D8Þ‡¸‰››OqSŒF WöýTWg:É‘™_âå}õ—qM@„óÐÛxE™™ó\jÌK} Ê%$4%Õ­Yx—¦ ¼À…Æ#¡H§ ÕÈ[Ó5YMJλÐpÍ›ýµî‰ðÞ³bÕd‚ÅØåúÅ-l=ë?PiŠB)1Ðå:CM`-V…5oè;Ý€óZ²$°g#Ù¯18;;əʑt¶í¹ˆVÑl÷­åj`‹Í{E…:ãµÏÚÿI»­«ˆú*ê^§rØHŠ0‹u¡ž£G,Â9'‡ˆÂ¨h|+±¥f@ÇbV4y©Íý¹ª)#ݪ¶*ÙôÅ`ÚœÕ,º¶Åó‚eqåžÔIø©kKºŠ}%2³tŸdÊê œêuP¼è#ë!•5r•·üÁÝcù¿úoÿ—øßÌ¿óÿÉŸügÿþ_'¦ŒúC=uð%“•ï)þ1ÀŽæÄ""š™•饬ÑÙŸx¦Ý‘R(M¹NûfSVˆR.ÔÄæS‰¨Ô‚÷j´†kšÌóhjr¥60Ρóç³æ3Ùª•8„É °RMX†ªJÎ"y}ˆÞ¢óñééIU|d„d=–Aåmô1š ’ó¶u“÷0K|÷»ÝBˆü|H.øØj¥Ëi¢÷ÞÙu·A¶ªädŘpÂçÍ} Í ÌÏ,DÆÔ*휧íͨªä†Äù¼çœ‚÷»íÖy‡€ŽÐn/–J  ç±;4;Ç̼á,»ívº3Ú4!†èCCäD;ÑCl‹a¹ªzÛíöL¾ªä½-+t‘¶ñDΞ•[ÉÆÆfͲiB1oC‹°w¦]á4"¢s$¬"–LÄR=!:rm»UÕñôä›l‚Á×n„V"8lÁpóÐaa ª<éJÚÝ-sæ4"ç9Ñ»¨–qFƒ¦ ÝlU 9çcCD±5’ÈÍÖ  ’sœSJ‰Hª C‰º´µAëAñ`je›LZŒaW/Å)}ZgçVʼiBéVÇBùém^mFoœG+DœsÌ&¨ÍW}}ÿÎÿøüÝßþåüŸýŸÿóÿÁ¿õ/.Ûê3ûÐTÁÔÏÎQ®¡%ôüV…Vê}ªsTÌãnÚ”ФïßgLðÇwm|íè‡Ôi³ÝQQÆî€Î…€ªêœa„ÂÌ9gUa…ync³DaN)Yx=¡ä‘Ï©B1bIjvE3uHsæÝn¿ÙßÇ'U1ׯ6Î)¾›v2"ÙÃ%<ªÈ0fÙßÜŽÃÐwÝK®}²Z§ä¶¾hò©Ï¬ßß÷lY¨*ªX¨±ª;WZƒögã¢åÄ•i®ÂH´Ù¼NsFNƒ¤Ñ…€’“ªúïR™¡÷WaI=‹ÓZÛ^‘®ê–ŠÚ4÷›:ß Çgÿê2Ô ™ž©·3ÃÄ„àv¹kìPWjN­*z¢<ˆ ªù –HcÕ›Yh÷³»¦À•öIÔH6ò$Â┩ŧ¤ÍM¤p›¡®¦¦¢JS5 ‹{Àb˜.WGËåÚÃ…@õzÊy4/`™lŠL…IE“ˆX¿2 ¹Š;RÁœ³õjöÙŠ‚“AḊ`= +hF$û –?F^¨€(T~©s…%1±Dœ÷öHØŸ8¢`¢iG Îy»µXG ³Ý*'“Êkå ‹?À¿ü—óû{ÿØ’%þÆ¿úþí㯞ÍV±r²|ùÖ_Ó™VE÷åÃ÷CŒü_Ü}Š)Ù9¹ÿzÒýÕŸÃ53ȺLã¸zV6{ÊŠð§0©“‚J…ÈåWÔóSyR=­]9.O&Æ%hŬ2 s"%æiM¾¬©ú·×”Fø>1"?¢îì Hˆ2‘Wõ„âŸ$¬"Íf§¢Ì‰™¿ùæMÛÄí&XoÆ9 gó“´ 8'È™0'’ÓPn4U¥ïºœóÝý½0cˆÍævw#ó˜Gwzz[šñµÊeVw˜Fþ²†²ZÕÆ2˜™‡¡ÈC’ÉÎrTFUpÞ£²ïž ‰í6¶;ëÑߪJlwÓ½(.M’“o6ÍfŸ†S:r¾q~"bý£ßþιÝ&@7ä ºýÍý7¯¾züö÷Oo67¯HE {zçC³¹y Ãé‘ÐQ÷ôþiüç+ë‘ß¿{×C³»uÎsU¥ÝÞ ‘óQU»§w"ܦÁù8¡èPÖ±ÀÍþ~è»á—?'¢Ïòë©? ÝÁ-ò ÍRÎÛ»ÏÍÓ füx*0}l-X@„Þ¿Oi¬+¶1åÜ_ú³?ùÿÆ_ýÛÿýÿþïý›å_ÿgÿ)Î=Ƹ™’Ï`²DPç ÉÁã¿KœaˆS?á-!o~žmÐ2±^ØäÞ?zXjÝ'8UÉû³Ô{Ë[0úÛ~Ôï,Æ$¥A…‘™OÝ#‘ûò«Ÿ G)«ö!¶M»Ux¯"9Ëä¡ÁéÕ›Û;ú“sÞF¬g~Ië pÞ{ßô}Ÿ9IfÎIŸi€l[u9&úÄí~GèopÓx jš-9ÿ"#¿€½õC†ßÅ•B×Ñ:ÿ¾Ëª[÷9kǬ*-Ùô<Þ$FÑUÒÈã@Þ óŽÈõC‡D$¬ Ê!:óë@ÉVµN–u„äŒqIÎçóÐCÛ­ß•žÞ7- ùŸúg]a *¸âˆvá7µ(ãêâ?ç§|˜É>+=!<ÂâÛ¥Ïh´ÏoÙTa_ JœƒéfóµZõqQšWL^›KVý]vV.­zÙåÏçGxÆ“~6áƒ4»ŒÆ^Ø@:Å>#­ZTÓ–Í\¶—¯_\È–ZÞ Õ±rÆ:ûÎÅ@´J(¬86Õàff"Tr4©ê ÁI¦ZçC®# ëFpù«`Í`‚åŸù3¿õO|öÓ/ /éôeýÔ™|Îótõï44}vô²žƒ!«o£/¶¦\Ì0¬ý]¹ah5ê°µ Ëý•ºU¿ZŽàª•¿x¢ë’…ˆ*ÕJ>a¤/¬õ´¬Á2WÃÅòïC.×h5äûqX66nÑÙedõ³dÕÙ­g/<ÖwôbGF´Dr>ç4åké@„3ª~ñÙ½ªp%·GÄ”™™Ãp²LCr´kw:ô§¹ÎCßcss‡UZçôì3#rN(¨™ÃÍ[fÎcN£ÍT‹in7Xí¶ºiÛ››Æ^cë|8<=¼¡ªwŸý4ÃÃÛo4„UeÃ9qNÍf?Ô¡Ùˆˆñ†…3§á8öDŽ|HÃ)Czz@t?û3¾Ù쬂ûSJããoÿ_¡Ùì^}¦"‡w¿º'–lD›f³w>œN'ç\S¼ªŒÂÙ°l椪ÎÍúY”œ $¬šÆž™ï69§]vèœÆíÍ+Qæî09”¯f:Ú4vZlBxŸr¶ë–‡.¥žÓÈyT5^CNO³+ÏñýDrÎ)çTgX§±Kãɪ»×¯È$4vƇØ2ç<}ßßíÚaÈÆfR…ÇÃ[fµ6Ú!·wŸ¹ñùé“ͪY4òKâܪ(÷ŸhGüè;“‹€ «´9Te•lŸ|Y$P%׈ù,ÎôäÜäRrÖ9GaŒöCN)}õ“_æMUåñí/ClC³S:öch·H®iw@>"RJ^ŠˆžÍÔ‰ŒLOä~ù‹ßá&$"HYQÓxUíú0;úæÍ7úÍ×ößßÝÅÆ‘‰È{RÕSŸˆÈ2 çñL¿<¯“œ€UžBÜÄOJzzYc'üRç„—rˆä¼ÿðY¿ä íîÖ¦ *"’­‚A"çS4‰…›8ù%øp„$sòOµPËrRæÇ7¿_Læ‘(}:Û·û‡7"ìỤâ‡4ë¸:†_T5Tü‡æÍ/ujV>Ô‰®X6X8¸“ÀßJFrÆA•ùCa„‹ÓnÁsf7î¥%˜ð‰éÕy¤ŽµMÝü¾@&8w²z¥ ?÷ŽÒÅÍm¶‰š×9‘Lšãº5Yú-­TÆuŸˆ0eÊú"pÎâì6—’õušë8TÔºâ|9¾Py´-ÄgWã’-ya›†‹À†®ªÛõÕÆ³ÒºÖ¥kÑR8.>¿Éå²ì™ðiüˆËë•uûÇú—^Û'ïl¼ø"“·Ôµ¯vÖ‚àe|–Ê©EगmòÂ%¨žA®Êþ³n[ëŒZ¬dYõÓ5ãT`õwŸý³Ö㬞Û9ñS§ ynÒ¢çñG}Lö, `º–ˆÓIÔ‰ð—çÁÌ*±`9¡–¨z_×ÂÙ8– Ò<;½Í‰YNPm«0CÐyìÁ€® Œ%ç ÅѪT&OúÜ7™ ÜÖ„Õ%¡¾Dñ ö ",c± €„ˆ*Ú÷½ÉÒ‡þÔŸJ÷m=R¼¹ÿ"6íïþÃÐn6ŸÝ݉ÈÃÃcdà<¦Ì–ƪoß½Ÿ¢æôÈ¢³Û–*TËD˜¦Ü–éØËnLå¤eÄÀŽp»ÛÙc¡¸ CA„Ì™{ã0 ]ß[¶Tˆ­Å°Âv»ÄØn­íºþí»‡ÝnÓĈÎO²wäq˜!zk²çÝÁùHä]N0ßçþ0Ï—-•7C÷øÖ…èC“3§ÄD@s‘s8q=dæDè f›wœÂ3B㻸‚­Í#!ƒ:çC[ø–óΉ™Sbï½÷˜“-HÎÂLëõ\“ÊÒ8¨ž|hM¤¡*Íöf&A8ïó8ÃÑûà\@rD4 ªÚu‡n>d‡áhû[Œq³ýâx8œºn¿mˆp³ÝØ;ç§Ã Ãû·›ýí±gÏ@¯IÇr ¬Á‰ÊôÃ7/ç­Üf#]¾€szùÛU€eù*àâeyBä§ßTа~¸ÅžnËnÓ⌬çÝ'"<=>>>=>Ýî7ˆøþá1¥ô¿õç Õ^¡ÙßY…¢Ì"Ìiúñõë×végÆ‹wÛ›WP¤ßå"’äѺy"º¹{5gMË—š¹®ŽÂÌÒÇ Eþ£(g¾oR|ÝöÍåÉô4ïÜùåËIçûŸ•3ç\%(0¨óÑÜôUØߪƒ‰,Ïœ§(Çj–i@£ªú]hrê9%ûùÍþ¾N¨_X/³á¾òï3M±V•~”õšõüç8÷I¨Ã¦?ËÀëåCÉ$@PðÁœQ¹`òDÂ’É("("³AO¶/†@¨,!jvª•Ë €ö0® N(n;Ei+Ej‰ E)P»0ãÀ´f £)œ&Α‰IMý6)5çÎd®B"5Z®2ù”¨˜êЉ®»K³½Õ« •ÖkÅËT0ãìß½XÑ)ŒSWn7^f¸\ÿ%U8™®Â,]7à‹‡³Ôî]Õ¼n .ÇÒˆãT¢•”Ofýxçûì&ñ)Œ±ðÇ~¼NÀç_ 8#ip…a¾Âò@EÉûÀiK8š(-ºšw€Uæ2÷_"ÌÌDN&«>'“ §*»[dJ÷U¥jO£"gžÜž‰(Ok¯Rã!较Tyx"xQõOÊ à;áÇ𲇞'†œh‚G€ri ½˜àG6mX­K"\È2³3Ù%,1/¯:z¢m¨¨j}êL>)2OR!þÏ Ev=ÈcN™¸.UYBfg¼ÊÇ@B‚’Ód xfeaH™CÓn÷79¥¹`qd:ÅAE9McÛí®§dç£s>§$ªÃ00³÷‘Hí"’¨ ÌóAÂÃá"Þ!šµ=09 ι€TžO»‘v?bã4º9sw:š(ÊyO¦œ³ÏBDDUΙMìoNV !‘÷®‰Þ‡ècs<A¡Ýl̆gÝþú^[»`2/£PÆv«"9ÌÅú.Ć\€¾ˆÎyS "’÷Þ9çCœFÐXô„*Fz¸tr2ª—­×4œ·¹‹5ßœ4Ô0™9k×Îrž›hZ•‰`aý1¢s~’œ¬4IÂlú [HgG’]D’â®0ãy&]7Ñ`ˆ± T.sU~–Én¥ #,å6þ ðúµ9³<žƒMÀsñôË' 1°Êì" Ÿ¥r ¦átz$jÈ}",8×äê^Éï›èBÜ18Àn0\YL+ªÊy0onΉӘsÊ9‘9›zL™ˆbŒ j ÏX^AE.3PfE1×»^9 À"%+Ñ¡äLz„¦^EhÛÖPO"ÿû¡Úud~™¾,aôaD~Î/©I/“éì0»Ù½¤:±Œ’§ä²NÝÞ$/ër²˜}  "øÐL†šàc+œ9§’R)¬Âä‚qLeQ‰¦¼†“—;®%Èke¸¼Ç›óPÕz+Oęɬzž µªÔ³|XûlÍìPeÓÿJN¶øÈyr%Ç\RÊèÂöæu:;í^É82 ¦Éµ0¼ÍvW$Û6Ä6½ˆ¤”˜ÙùHªÆáGröây£!¢ãñ¤ΑsÔ´­•ª"´§ã×"â½CÓ BÀÈñ³‘‚y¯YÏ$œAÐù`t›ù 4W )÷ÉûBzB Þ“ó¡Ù€jNÖÊDDgª›y@J•õqÁ~–í €—,<r>x˜ëK·n“´’iìQE5Fó×£þœ´X]=×±Ù–gO„É:ÏsÅÌñ@MVÍcç×Äà⼿ºqg²Û}TUe³%±Â ¯¯TÆ+nÚy•²! Êð‰N ÷€g™Ìˆ0'àa{óÚRkrN±‘rªjT ç}ð ûíþ&nvÌYEËAÈ\nÓ@!":áœÓSÊ9«02gîûÑ{B(à‹UÌP”¼3¦`…ã,µPUΣˆ™@a‰Q%Ђ‘Ʊ÷>l¶[›|”¬à—tYs§¡d¤á‹î’eù?˜"Riˆ¨ì¥R¡-ûRÅ Ÿ&;v(¸’>¤€äPÁÇÆn7†f›ÇÞìÉ„³ ù3Ô_>£Ÿ»Å N ®þ2<Ÿ{>p¡«±èjŽº|™Hò…+a&­ÂiÙ€މÂ~ž·T*{}–ɣט·õ­}z¬+2ÂÍyÙ¹‰Næ®¥Þ ZØ)jêLPf¶'m¶y›¹êfÌýù!Öú’µ!¨‚žé€ émEc`Q ½ò­fP NW&ÇÕw/Ý ¹ÕTmÂr`Îj¸&>™Çh–QvA¦Õÿ§žuUT•Baúl“ˆZWœ¦Õ×{ñ(Q?B<ÃçþcA¾×¤ôYG4äBÆ›GLu ‚¤®ÂÓÆÍŒ³ò›¶·OQÑÚ1ùGÃ8ukÛ$©xî”.Wd‰ \qWDTªq>®BV…µ¨¢yþ¼Ê@±D*f¶ç©æ²µ2D+ Ô4L_ö#]8VßÕkôu„äp¤.[ìÄ|Ä3übºÖAYÉ%ÿõr_. …¯¯3–‘&LD½Î Ô^ÆxU5y5ó÷òeË8ç8ç£eGUxûE̯jÊÉ‘CçÊ7AY0Ïk õ3ÇË€1à 6-¼äŒD.4ð'?ÝŽC÷æ¿Û]l¶&ÕOC'"iìUØ:³¹´gN¹(©‘sí/NcòÍT‡îpn*½ MWV´p–œœ±ÙæÓ)åB0ó6“˜î· "Z¢$>e7Ûͦm}l­SQ‘¸ÙCÿí׿DÄ/>ÿ¬,Î6¶æœ>ûüK›ôéÄ<žÂÁGB‡Ó]˜BÚüœ|šÓ8{ÊØ6Ç9MÐŒf/|÷ðØ´íëØ`i$„91+³Äs´­š VUÇî‘È“sCwÇ!³ Ñ»oßÑÝ«×>4H8SñºÃCÙ|§EÈë‹LÞ§QžŽ¶í®ú»#$kŒGÓìnf,0 5ínèûîôÔ¶›ã8t(6;UNPo£¹r?•½søaÞÚj"*¡i^¾µÕ¶dßU8QŠgZÀ_þÎßÍytä nvœóéðT|ø¼!ˆ°¨„ÐóÛÛÏ,DžÞíBÜìî §dëãét8›àáv㜿ûâgæ`ð£ür>6íæ›o¾QÕÏ¿ør³»rH´ßµÂüxèsN‡·¶ž™Ç4§AETxríUÍvYs"ç›mãSNét<ÀXDbðÌüøøˆÞ‹Šˆ9ßÜì7m;»v À›o߯îoo'„[XdÌÂ,–2£À·ß¾I)í7ͼ¥Ã)ú°¹âü$bc­pº,Tã|líMõì§ò{›˜¶íÎe(0¨ªéml^†Ž…»Ó‰œûêg¿•Ç> Ñ«C]ˆH®?=!!‘@{™elF~ssH§ÇwœGÿ\øã[û¼èíðCôÏ$Ôã³XýÄY7(4ƒŒZÏh*Ú…KaÝ*áMÎÞmg øñlèë52®i8MŸc0-älœÌÇg5ñÜG!âùüí.ÃylülÒVeFS±OœE$Ë•3«}-¶R5ëáj^ö,-(oWÛÀ¢+¡ùŠ#V7–’«Xë-Ú•ZÌR‡×Á²~æ:Voµºz³KA5;˜münEú÷˜é§íJ'Eލèùˆ¼’…Æà\© (ju)`ùAëɈ§7,6DÓRAü±·ÅÊènÂ’uÖ+T‰§“0ÉìEw ³Øcžvèd°€¸JÛ¬gCK¬(âl‚_?€ÏÏ áråC½ñE„?¿ž1$ѳ!àù–¨óì–RÏ—ÇÙ‚yg»2+"89' bžêÇ€ë~lÙ˜#º&\•î*g6ÕüDt|®Ëô¡ÃÛL¿Q¡5WêE ű‚›B»%rc÷¤*ièÌOÈ9·ÙÞpÎýá¡ÙìB»í»G³¾´e.̇§GUè†1½K™ÇÌw·a·‹_ýµ !ñíÛ·Þ‡›[@D"§Áöª›Ùs_®e}÷pÚí67û}NãÐxJKW¢å’΀„KôâÐ÷yB8eùW IDAT˜|‘†ÓCÊ ªH®ÙÞŸÞ¿Ž‚^Å4-fD^¨˜nÒ¢ÂÜ¡«™"ýÉÅÍž™ÞÇãñèïKÎüpø}¸ÙïáŸû yA‡ÓÓéñjÉ)¥”Òe§r<<©j7ä\tT[¾z‡ˆøæ›¯™e×FVŠLeµçÄལ³…„môðôð>‹Ž)OˆóDµì¬«Œ@T§0HDìTÇÇC¯Šww!==¼wÚ1-4ÌÒû8æcÊB8gæÌH´ÙÅ3-$Ê)ÁräÑÒmï~yz|[Lð™UåáÝ7Þ‡›»W¶¼9眆âùØ|óóßÑÍvçœCº“½8¥±ï:ïèþvË,93–Áé÷îø²ýEtHávߪªÊ†d›]ÓîDøñ0ˆè8–¹̬_Aˆè÷~ï÷Úvóå¯üÔy×~~+b›ÒÈiØÝÜß¼ú<½Š¼}÷އa»‰ˆbSLTUÅ"O}ÿððøÅçŸ9çC³%r_} ³n{óêt<ŽÝcÛ„›]ã|ã\Q©}þùçDôóŸÿœˆº¾GÄíd¼ðÜ釈~“<úÃû—Hrr!úئ¡›¼-5Ëuˆª! mö÷pz|G4Û)«*«f3á4jiVßCÉ{ßl ^2¨Žý©8;:ïëæ`å ¶òsÂçÁ†וaú…éö¹ZùEç¿~¼™\%Ô¿èyù›Z˜u©®5‚µ$ Ú?EôÃdç•5—>_ÞMñ[ à¸ÊRÇ•’a´¾ü‹±²eF„ÉÝžËÛ› œJD_¨0kN–žxz-žG·xYú*)gUoÍ*š xòܽ}…øÕêT3-Xû©UÜ9n[ZO]Ù’—ÚPAWP²Ö^ÏŠ¸‚ÿf…8èÊû zF>¤ø!Mϧn´Ô¨~…²/nÆt­•—•)Jä›­®Ö,GDü1m™DvžPÀl?O•®»Y–e@‹çˆ+™R½x@EŠ‘M ºkU›Å¼˜Ï—‘*MOÜl|9œÑ?Å`Áƒ ’ÂLðêWÊESŽ—Ï»®M€Ý,P«´KÞÒF†ñ &‡kçBhw­»7“âÅMÕÆë T™3ù`™&*¹Ùì]ˆièUU9Ù¨A„óp*” |hÍ¡sRž@xc½ «J@ˆq3-ú9–TÏs æ„z{Ùyƒ(9+æÉF öýÐõ7·w7¯¾´m¾Ùì—aúSçÈïnîˆðŽ<§qŽ›Ý}(¥Üßß©H×u)çûû{˜ðDŽœÍ–œ#ç…¹vVÕÐlLQI~õg¿65êÊœÚv³ÝºØlàéñ½õ ja@7 èœs½Ã¶ibÓäÔ«êT°"}öÙgˆ0œѫןå4rBŒ¡Ù¼ùækU½½½õÞ…f›s:<¾ÀÍv%ñÎuß6  Ð¶-3»oì,Ùîv··wÎyS§`;ÉcÏœûã“ KN€Å?¡ÙÞrÜÚ%Ë䜑3ôè†ó,ñFÀÓé0t]›&ªêÍë¯TØÜ¶Èù)Zxä4†Ø:rš¶8Å7:$jvwÂ)§4ö]ß÷w¯Úí¾.ËÆþ€F…µ‹© ÊXá| ç]ìT$c‡~È€|êDl‚3­Ið gHYшÇ0Ù#bN ‹ü¼6q×L×Ð$äÂl˜ç¶û{Wë0ôýñð8$A„ûW¯ƒÛÛÏTÄ.&§ÑR´rr‘,™ú1«jl„”švǙ߾{ ¢/¾üJBÞ‡®ë2ËÓÓ‘ÛÞuá›öZ„y{‹Ñßî[ïÐáPìÊèýû÷W€¾úê+;¼º„ª÷Aîïžžb(½ìáí×ä’Ëiä4¤¡€Ðn°ü’E‡ÎšsžÈ‡o ý]yìpN·Øî¬­©5„ä|x|x¯ªÛíÖzçüÍ~‡)õÿÿ>ádO_%O9··¯Uõøþ$’œ÷äö¥É @™+›ãJù bÛ¬a~Dˆ¶ÿÌóó¥ÚCÌczzÛmh¶ó€ÁFeÂÙÏD­²Ô§25SЩB´9Ìb‰³G)æô%ë Ç ­­W+Y>¬ÒmdŠRü8Lò!ñŒ‹TÇ·0³¢ss¸9(.q$ðã¼·hFË׳-ÒYtži Š®H+ §ü ®-¥p•¬Tæ½&£Ô P™%‹ù…Š(¡ÕJT»‰.†ΑÕ+8YË’W¸ð¡ˆžC §Du¬Z)ÎyâÑ÷žœ'g„R1ã\›¤-Heí¸6ï S¿IHŽŠº¡SØ¡Ùâ ‚g v/UHíº9‡Lp† !‘s@Ý$”&rÎ$JÓ+‰É9çT8O4YQ1N"9"HÅ€¡ðÓ”´nfVâÅ2œœ¹®Âø}Ãmφß·Á2dBC‰¼WfÌ«k9Û·k½>õ{¶*½¨•µ„ýhý V|}åæK®Ðë®L™ÎhKfEBPZ1/nÊéÏõ‘̧nCDЫû.êþéOWæ>ŠðÀ+{:""yTY À*o ðlÌ6[à¡ÂÙÃ…WžµÊ s&¥_Nq ¤g–(DÈ"<Ž˜3ô‚€Hl[dN*H*<¹sšåÃ9õÂ9[½¶<-b‰©t!úØX9D£H¶Tzüа™ºÐ¨°Î›òGJG‰ž1ïà³s5!Aô¡Q !å±;”øMè”*MôDx:À{O!¶ÂÜŸ ]ΠàÐ;ëQLê¼g–4Þ«àœEò¦mT%§Ìq8Í¡Týp$k ‘‰u*DBðΕ³*çdè#(0 âÌó^5ÄZ–B×uäTûÞbâ—+’Ñ*¦MÛ*¨s”3¨Æ¦…"Ûš\›Í¶Ù쎇Pm·{rž<@hoDøéý· <ôGÚÐ`Q%39›LAå!O9÷ý` ÔDî8 “‘ˆs¶{Ã9‰E«¨æ|Nqœb-F-)­X¦B³3uî;ÍÑÍ–Ä8t"ì ^dIäÊcf¾M“,LÀnMÎàCôÈG˜ûÞ9¦œC !Dt±i¡ïO`Än°º¤dlp}lȹ:oz> ðG uÓ鼤çB"Ün6P OUm¨£âÈ9ïAÁ¹èÈO©“@δ© Á;$r!zÑ ¥tlÚíŽÈqê'ômè%ÂŒ‡u›‚HÌYUÇSŒ16ш¢ jŽQØ4 9dVNCW\©¦¹”÷‘TÙ¼dpMFï¡‘O=]â×GÒ µü?ý£úo¼ºDxìsˆë|¿Êg>‹aŸ¬µæºhrf Ê‰e93…3€¦aP#UØDÓ|çVÍW %rÂÌœÌú‹À9‡„9 ÊlRÇ4Úž»Íœx,Épy†ZäP”GTx†‚ò8ä”ÉôN“Ú.ƒDä‚H¶4)¢© žëü.êU®ø_¨è*LÑ…@ÆîHÎÍ}ÂY„Û&0ËéxT€à¨i7»ý¾ïNCðÞÍT1GèÈå<.ç=€0[â92'Ωm·ððø”RNý ˆBhDôøôàˆÌõÔ9afN`·ª¤c'ï£%-¨Š‘ö2Ä4=HDX¨i·ª)§~‚…sÚlZ°öË&ÓAˆ`1ðDˆ›6ÄMw|vw›6 9ÛçÜ=½E‘±?™I)ç$œ&ˆŸß¹Èò¦kž3÷Ãh·Ì{,lÂ)j£¸È» œÓØ1ωN«ÍbL€WýÜú ¤)Pwš‡pNÓÚ@ûnèNmÛ"¢9Ü;g|ÌF‚³PYçvÇ”Æyçi2é$gÔ¢Mhœsît:2³¨Ì6.æ{“sŠªÅ\æbø£rC-húE!a³Ù˜ÿ‰ˆŒÝATDZ¬j±™‘óÁô-S›î¬ÎpªÞÙ¸¤Q@rnŽÇ“Á´MÓ8ï‰è‡%!$?eÎdU CÓ‚*5McqÐVõ8tÖů¬‚6û;Éã€.6Ê,œD²*#r®n̼³øúx:b»ÙŒ<Œã˜2§ÌÁ»é &fQ…ecœ·&ËÉ™jÍKà Dô£9/Y—4ËIùåGøT«÷3—ýaiÍ+’ÁÙg@"Tes€š-!M¡¼øãC­™Veé±ÝÄÍ®$!9½ØWdcQ‚3/g‘êN„U¬¢ ÏÓ?θµGL@Q•ÌÀlr:IBjd02™$ü0ê'|BÐç³ÚÌ W§µ¡HŽT–ÿ %ˆ Š*á’>§TÚ€”­E;ï—_?,¿pE&±jåÕ©³ôfYý³MÒê[€.6]¾âÊÔ}éÚÞáZáBD„¤ˆB†ãò,у ¬0­ ÷ªÚId:?Ö ~BÍG¹‹?TA‡øjL•åz¾ÏêŠ_©×i“UÒ‡ïŠÿ--ƒÃ·m&=!ø4žÐLÐÇ‘qÂýÈ¡÷ôœûŸªŽÃñÂü ÓØ”/i$ÿX¤Ð4 `,!óa/ì\0Ù;‘G¢ªpEŸ9ò̪¶›ˆœp>æ”RâÌ’2× ›® ý¡:ŽÁù›mNcw|4¡ÑýÝ-¤Ô s»¹©ém’óÛŸÛÖè›–Èïï¿|É ó£Î,UP ]Òå…3 Íœš¹ÄFÄ®}lwŽT9«êöîµpC/‡§Gû‘®ëO]s³ßn6Môª*yÌÊVÙÚ¾q|÷µmkÑÓ矽>}ß§Ô‹8£Ë3Šþ—ÿò¡m÷–¯™on 9ÒœÆû»;$G>¨¥RÀ©ëN]ßÄàÙN9$6ÏÞ7÷_ý€ß£äÀ¬b¸c»OO""0ôG“Ìdr¾i·M³é»ã€ÝÍÝ=!&uCŠ ínnzç4ôbB4¸íMp¡1CÚÇÇGDÜn(8ßÔ;ü4)1ó Ÿ <‹‡ü¨ƒ.í„Îy~?òé‹xzz_d]>¶SR ùØ÷"}{¢@J¶ëtIë¡§¡9 iìšvçBcsDÓZØÀÏ…h¾wT%)‘÷D.½ªÚ1á?P”:á,‡ý<Æì¿ØháÙ4¼Îõ+òç ŸSœÙ3±bUJc-À¥Ãú­¡v[u{8©½/öµr¶Ä­ë”ÀUý+"D`ß9—Zëü»êõ0¥-ÏŒ].E{@U‹Õµ­" '%…®éK<â:Øa-R™!æ2p8s8»V.‡®U¾3|¸v‚9ZñùÒWY~аvBX¿?œÍ'qU"_ºA©Î)C8K¯Öd˜œógOy\Ü÷*æÈ¥¸¨²_Xq.`%E0I×d%<[|ô ÔOÞ ÔБ^ï„*ÂòŒÍŽÂ6†u#¥+¾çÓÖ#³i‰:­FNˆZõ uû·ö¬x1é©:’…øT_¢s£½:– ¶q˜ßå\C´Dã-·Îpuª±˜>ÖŠßçJëwøÙ«æ!µA‘RÏy‚óž|6HCüNݯ.¦(S> ÎTš•´^'»Åë¬3‹ašâ’¡i\ ŽY‚ðx8€u‡CP…œBpó)Íy¬/}Iï« Ó¡;@ŒSô‰¤¾9Dk"™óÓÃ[#ŒØË†¾;Cw áMN¨ ˆ}a$û#(ŒãÉ Gˆ™“p²¯› æS/"àcûúW~TOo&3ëáxêÇt·o‰¨ÝÝùM»³“szøÝß±Õ›û/>ÿÉÖà«ÓÓ[aÆœRÚmcJùýã·DþWök9KÊÒîÚíÝkËFÿìW~Ä´Dôÿ½ÿÈûØ"Ñ—»»ãáñáÝ·ÃÙ’eÓ·„häû³ef\š§cÛ ;Âíþ•p¶]Ucp÷7´››¸ÙÂãÛ_¦±o7ûY²2»(ˆ°Ž]hw¡Ýà»·ððtdæŸþô§“×-¿{8î÷ûÛÛ;sÜvÔ±0#Šóžœs±5+{D ¸˜sN)íö·q³ÍC¯*}×Õ%ΫÂúc€>¸±’*4Ñ#€ÑŒb-P€9¡ÝÆfÇyœ p$ï‘:g4 ÉÉ7›Øîôé]/û{ˆíœª’Ç"àÝí¶¯›öñép8uw7- ñi÷Ìc¿öÕs>¨êññ0 ã}aŠ5ª0œžìkš66 §‘Óðíéq~ɘ·³ôÞØ¹›ý«¸ÙÃìï¬rV§X4}õÅ6ãÝ´áþv³Ùß":PÐþøPÜab,ðyðÛc$ld’Æ2×Aræÿ÷øð¾iš¦Ýt]w:öûýçŸ.69W•œ„‡k\AóØ3Ë«ìi8ö§ƒÕøFzúöÛo3Ëýí–œ÷>"Q³»aÙÝçc|ºãîöu±…S9™óÛßÿm@,VH¡ÙÅv'œúÃC‹¼Ðãit„F–1{¶ãûoœÎGBhš0tØno]p ÌýéÁ*1|u»M‰Oݳ„lä íŽïSO1x"l›€H±Ùóéé½snn׊œsPÏÔÇ¡ïºã!6mŒqsóªÙÞœžÞâYð“»âÉðZóº1}æ¦ti85›}h6iæš) ýÈùÐZ›NÎ;99>4Î¥t§s¾‰­ªdã9¯*–*J.¨Š¤|yöÚÊÌi°ªawû:´»ÔÆá´¿ÿÒÜ<„’GKXBmv·ª2óN$'–Áà‰<ž@ÕŸ ]¿oqñKUœÖÝ\#׺¹Jª@6ÔÊA ]Ç”P}öCg®Ø°dãÒó¢ñÒ7 êŸÆJ3ö  õI¯·#X·Ú³TfTo-Ã/³ ùÎ×k‰užþcrœ†E k¶â³Ó|¼¯!R„s×ÕspyBWõÞÜ›.Åù¤UXL³ªîúªüü"X‹—õÊ*¼Ö²jí^ÿ\ô…‰>^K†|¶G D˜… W –ªÄBÉ·ûMD“Ç÷ìfGSCS:!8PÖðt¨ÏÐÿtaßÍ-J:Nt%!׳fëöQë?]b?ô\O$üøîõH‰jùûlÓW#MP=3Uó¤8QÀ"®V°¸Vß_üùŠqþ#Ë` g½¶í+ž.þ‹ÓæP1üë!É5ë’ª\®kýD^ïv^6Ðg€Y˜<#—]dµÉÍÓ?$EÐòÍ`¦àliYÔéÞAµyU¾ü“_9»W’¼Q5ST@âVU‡™—`ý‹:ç9KDl¶"ç/6tgÍ©–jíãÕË9·Åj±å—sd ìãÏÎIc§ ) €:š‡à¼óÝé4ãù!¸œ†‡7?GÄv{kzl÷›ÝŸõÅêžÞ™¤Ê\wMF–ÇÁÂ\ ƒ÷Æ1O©2tO i»‰œŽoº²é/Ùô‚÷†]»#9wo:ŽÃ8 »ý >>RJÛMœ"›Ö¾º Ñû`.ß|ý3{èÝÛoU!O:$×4Û1iŽcJC‡H>D¢9[¢ùq›ð¼Ô¡i)×9‚ûNAŒå±Û4œÓ×_½Ýn¶› €úÉys&S5„FrÌ£`†¡ï‰(?ôÇîô´¿}…‡a€áô´>ôŒƒ@WË ÎɶfDô͆8y›%ys•œGÍf.f9g€@‡¾ï‡‚'„›»WXrVqìOD¤r%䄃§c7À¶“i’õ@ Cw¸Ü}8gÞ9Œ‡ž¡‰>ßnv¶ çd;ÃìæcÌy/¢M+9q•sïßF.‘©Œf޾p62¶äT|‡8 ª0i4Áùh¹Eg—5ÄÍvÿêôônÞC÷n·Ù áfGèÐQÈÙœ©Uåöþ3Èy‘q<Í^}·÷¯‹êÒ…Ív×w§wïÞÝÜÜ8G*I…r//BzážÅFN‡C?ôMpDôůüÔùøôö€œ†®ðs†¾4vÎQx´|MMIœg¯€(©?MƒJïã”MôÁcJ¢ ‡Ó UõÈ9Û[} ¾Ív"ÝŒ*žÓ Áè¦siŠD—”M°„î’´¤õsNˆ‚óÞ“ÝÓ»ãû7¶4›ýÚ‚@…Ó‡¶…³‚JÎ6M!$$šQdœaÒÞÅf«*œGt¡aIœ9b›Æ~èµTÓlK9* ’“Qâ ]½Ù²h=ŸÈ¹ÍvæJ¯Ïù+Í®iE-“…y莄„H§§w¤cw Ü„WV ²4- 9*â+›ãå`ÕE]ŠkPm,­î §w[Ê4ãÁ®ôŒ/ ]4/Ú´Êjž±ŸãF™¥…Ù_ÌGj¹7š…)ú×LNã_ÖVîxP ³TCU”'Ú7ÃdGPÃH»å¦8À%­  à&ãg2 ú:Ș/(A%À’°ª"JÆOÓbÖrvY·åï]b©¤@*̲ÏàléubçÁœ…4‡ËÊ”.¶ªºç&µ4³øAÀCëËU–×dE§¥¼–uÿ5ëæh3†.%*§ @R½R#2 bÕëLQ«Ï°RKÛaj DZk~±”±ê ¦j›¹(&'þ80—!85â‰Ñ¤A…óÒ2ãqÉl[ÔzÕ¥Þ6¾‰SF´ .Í(é´k "ãdŠ‹µ 5¤€gŠGmXö}{–%ÿDD˜!9ç¼µAbqþ“"Ô)¨´Š®"0§-W´(˜§“ ·ùòfz9P¸ø„ólcvj_v}}N²PVÀ¥µô3;^…šKQÁgöÍ—\Ù%÷ /ײ”ë£\p6+£ÁŸR½oÌg;Ö¶¥¿=oÏì¡‘gNë,RK ((ÁärP_.=¿SP ¤í§j4˜kšùÎû€„*)çlî·ˆÐuñu`žúA‰ï²g`U óB4g¶±Òwñá-î*ï|ÎûWá<bh2ëE:^Iàd]¥ûªj¤$9QÛÌXlDÎ9 VÛï|t §îq~²2gü.,'A¹Í,âd‘òÔ›¾ùrq´þÄEÜi‰qKQ0¯{t>8çÚ–`‚U™™3÷)åœÙyï¼·—‹Š9ÖUÞ=FÝRï4í†È)§ÉfUÀµ ŠðéÔÙÆC„œ³ˆçDe""˜3 Ú5´æ±G{ng/7USò^‘j9»Û§n BïæÌó¦”3"åœ@5ç,ªˆƒªfp,š™sJs…1ö§y¼Æ9‘VÕ®,)8¥”˜SJ"êÝ¢‚H‰b`1V°9Þ"šEv»­™4ÂÕM´”kóè²Ä'jlï‘KÄ5¢âK6hš&ˆE.9 'ÎÙ¨w¶M઱i8§áøhhÓu–’Ö-ªå¤32³óç”T-ó€’ŠÃTÓ8Úoˆ°ç¤`÷ZM(å3ª-s¶œ¯?æ«iîepæô;!x„MÊ#€49#%Û†8'@ô>ˆjÊ\æBtÎã ÓÚ`Q aÐqÛv»Ùß›TǨ)дQu>ƒjÎ)ÆèCÉÂLÎ;çû®SP3Y¸¹½%DO`™ –ƒbã\°{#@l·Þû¾G„Ì€)ŸBMÓ,Gõd:S¬4ÌÍ„E®—ÆT:-Ü]=Êu>lâ§ (¤ 35LT&S-¨Åvæ3£Ùì‘hsóÚÇæëô÷°‚•g€ 023—œÅR4í¢Mr“úqޫ˹cðâ¡Áá…D¦Sêž®½Éj.ÑäÐ]q °¬X$@ ) GÉc?k,¦ -IÎÌÉUî%ÒÁ¹ ªÌ #ç½÷qèû”Æ”Ùæ ˆ0Ž=!ú58?ÔõrG¨E jÿN8¢”D?Ð(Xv(çä|ˆ.æ<góÎYÎPNC¶“Ð 8ªw­UÛ….$js‹$}N.6ÿio³+ɲ¤ ™™»ÇOf®UUûçtŸîÛ€ºˆž‰€)Or…ÄG`p%$ž$L‘@H ‚Û¿çœ>{×ßZ™înf ÌÃÃ#3WíÝ·KÝG»ªñk]ý IDAT²reF„»›}öý8ò¦V¶I{Ê\[ÎñÎÛÉž-D‡(¢,òP£jל9µ“HU°vdc o@b9ÉœóÎû¡7ÇȦÂR‘ä43ëÙ­•®sT%.¿F ot?°Ç|Þû˧5I¦ô|eã‚õ ´Ï?’÷ùz•˜r©ÉŠCž^çØ§ñøl›Wš§g£ÚC€a4â¨ÆE]¾rœ…³sÞwC>_*EöééYU9ED@ò  öd-gßEí‡Ñ…žþYÁ!ªª\—ùt<އƒpÔ–ÃÉŒ“Ö 5D¸Æ¸’œs,Jd¶,H„ ¸ÊÒ7"¹y,Áî¡EEUGPÉ9uÐo¥bQ$rÖÕ滑ä¬<ç¼h­¹Ëö…@¼&0­b«2k¹Öø'¬zÿ‘â[òd|“ÃTÕ!xKº}­‹g…Ú•nc¥­ S•‹’÷ˆRÓ¥‰­Ëa{ VOQE$©o±Ãþ™¿ŠÍÞÚjhã ëœƒmçõªv¬ùb7¾¤@b“ןÅV¯_§6o‡} Œ¶`à'³æ±@•qHUæ­”ìàr·~—j¸‘œTq³J½‘ Ñj绽ö{×[EEÝ6æƒnœ(‘ ŒœbŽs)¿öªœ'Ú¨ú+”·ŽØÈãwþ¡5ádµŒ.ì­~¨¢€b&«÷µ%ëPÃgÜ0UÕâúËùìœÆqjfæ€>¸€®î!зGƒVz{ïõxzj òÑdëû«ˆ¤¼Kwû>€BJE×H£ygH‰‰Lï}Ìü0§˜–ëCp¼$Ã!ÖÔ)Î1NgÉ m|÷Ãõüõz~1¡Î0Ðw=‘ù¹Ž»Þ}ÿcZ®ËtÝèCoÎ¥9Îb§lÝÈVÎã®ÎõÝ«)¹@ÎuÃ!çüùó¿¾ÿñOâtMËÕ’çÌ.+¥Y8ÿÙð—q™>ÿô{ÓädјŠ#~²'¬Ó¾à}\®«‡Ñ*ÌtÞtß:Ëž:kòl÷t¾÷ý¯¯*ê°@o†@”ò‚ѹЅ~4Œ”œp‡S¸×]OÇ9`ŒË´ÌçÃé"师HŠi™lOr>¸ÐÍ×Ët½¡#4MËÐ8=?å”^__é»~¸œÏé:;G!¸%改®Üüí?äçë«u}pªz=¿ ÀeIÃ0þæ‡?EÔëËçq<<=¿}yyyùŠÎ“ïÇmº<]_9oîÇ"¯ÿ׫ÎÄ#R',¶g%€0Å ûáÛIˆ9Í9ÍvìqŽ2O¯5,µùÌS^á;®Ò2qŠ]0c:2E™ª\/_±ùëDp.˜]¼ý,u.„àȹÕÕ6U Q­9眣Q‚­Åg…yvÞãѪyks9EÎqž§´Äe¾Q޳ý0>m³Œåʬ™• Á¡?Ö[çkÁÀº5·çÀ8ˆHZÔØ@k×W‚lE†ã3æ8 ³0§ùZw§*ÞE"ïÜå峪öiD¤çþ,§8Ÿ?àðô!/SZ.9'k²qº^ÔTjß$(à=/õ—s×wuÓ:FÄ:Iþ|Xl ÐwªÍŸ¬7^[.ÃMרÈÿëãX=ÈAk˜@«©oìö÷Ô›{/SÑ]¨4ŠpÝ« ÚH …ó÷¦RYÖ¶j£Ï­î`yã!oBþV[Šwœ{8Ý¥&¼·^3\+JY¡ù›LQÝÜr÷f]•{½‚±·ámícÓÞÛ}®œnd}h²D‰v‰]zYC÷_I#Fi½ºÿ×{iÇÒv…ZpíCºÏ;Ç_œÿWV÷/‘p§<Ñ7õ9i+öÐ2ô Wq/zØ»Uõ¬ÛMWн«Úã'ëŸûkÕBÁÍzß¡«Fóu»¦5]tM§Á*Û©H6àÊýñôþN’QýÕî¨|º“Oì<ëܤí=iw°oÇe"ç†aøúõ«ªžN'ïK´-¨ùúâÃÐõ%‘| çÍŒªÊWE8λ>t¡Sœ§ëëp|î†ãû áâ|OV†·¡ÛǧÖxÅ!/ÂÜÇæaV“ñ^Î/ç󫵤›”ñüå'£õèRHáõ¨fÉÉ{§*Óù³‚öà ÖIaë ÏHŽsPç<M¯ŸïúÃ|þÇ¿U‘œ¢*‹pCaU ‡£óÝïþúÿÁ’vDfh;_^TE™}ד ÎwºLgÎééÃoH½ïξ¾.ÓåëÏ ‹%ôýåõEŽ. Ã®+Hs0!úáhvržœ{ýòI„Çñà¼ÿÃ?þÑ;gICß»® ý!ΗX®ŒŽ‡w€˜ã"ÂýxTáEA…O‡AÓôñÿÎÖrL9&>a×Ô^?ÿOO϶ä¦yޝçÓñx8ŒíÖ7ž—¯?8‘ÏŸ> =®nÄ/ /ÁÆ¢òe7gNµÚ”ƒ*§+mA8Ù¨ÓBä•õ+;€È­ÌI\µ} ,1æÐ¥ê cî†ñ»?ù zyùÄ9^Ï_|è>|ø`t¡ˆ8Œ"êÇ£Š„®3qúÓóóñx´K—_¾æùòõu&¢ñY~Má[Pj-_æ%}~™žŸOã0 ãV¢…p€¯?ÿ½éœE¡œªIBZfO"lƇ—9½µÈÌY8sZªÎïzþ²’¢Éù Âb Øà¹LyÓ,"ýpÔ¦òÍ˲\/e‰€ÿóÿú¿ ýáÿø_þ‡/ü»¸\+¶1ŒO)FÕ‰h•À­Ÿ7¥E……L„öùó'û»a†a¡/yDªfFØ'µÑáp*C(NÂÙ £åúZ‹NůÃÔ÷ä<:¯œ…Sè¾MÎØOäÃëÏÿ`.s º\^8GŽKã;®ÇÓ SšUÄWÆ04¥7Þçpí±Ø·Ô±wõòFö½ÿ·-Õ¾mŒ¾Í¡/%ü-ÙïÕÓoyÿÈ.€ìÖ©yK§Ö•÷_Ç]bqI[£sÓ_ì©ð«¦ ¨Ê„¹…7É=Öl­ûù>¼¸í´¡²¶o‹¸:Ì(" m¢¬Ö’ Y›n‰´›£ö K»¡5yŠ{R{iÅ¡± ·ÿ^ Ï•S°~Äu$ˆ›z¡ Øa›½ *R{ÛwmÍÞð®êlŸÅFÉp§1Ò»Nn+Wë °µˆÓ»áâÝ2Óûý¨‰¼±(ª&¶7[;:B|\¦¯iïå~¨>ÒMAõ²¨†ßÔn?ÞTÈç}ªûÎJ{Ýî±Ý׿ˆÃÍŸ›PÊÌieÁida9êÝÝ*»º>œeÖuלYKW´q*æ´›ïÅeACø†á¯œ°Þ<2€jY{0±õ¶nÕ ºwfSüQ™ûw!?ØêﱷDZw3ÏšF¸]pj¦±dtëMf·Ç}™ºÅ ûáhùA÷×чžˆâ2›VÓ5B•‡Y”q¹Vó¶”^‰ ߊ&C!ç¨% чT¯×Ë~î˜8'æ÷ÃHD¦î¬Þlí†ãˆÀ—dp:Dg®âë@ãõüåΙ¯f.KÂ97__ê“a`çCZ¦§n8…9-@35®•]7¢2眧9!͘–©‰×ª§£Æåê}ç|Pɲ= 9 3#^ɇ^U/ç×®ïû~p¤‡±kJ ]oóDr¡CÀñôaºž¿~ýì=yGª"œZÒËùÕ¼­+¸›3ç|¸Nï†Ã1N—:ó?¼û^8Ï篪Ê[ŠSÎ1çHáz~QÐ92¢€sŠäÒ¨Â"²eñX(±Ã´\­Ð.ôW†ÃSŠ‹‰úãl‘§ã,9Z¨›¹¦¥8sF$p 9)§9&ñ!Ê8PXb¶ )„Ê©32ž¹³-PÎI8%ž—DÃ:—eÉYUuF„Ðuá%›X8·ÌË? öñâõ%Ï—‚Näˆ!ôvFU ã[U™®—º®ëMÿÎ9ÆE *ÌÆþ`Ì@UMË5ÎW~Ñ”2"žÏ—óåz@ä‹Ù:ì;¬Š½m"»~0,ÎΠRgëlÕTì…­ÛÌJµÞ Zt-ÓÛIn3U½S¸É*ݱU°noè9 uºu`¾“ªæãÝ?ow¾v¸-(=ê7ïïÛñps…‹"²lk,¶Þ¥\Ù>‘37DAYÿoklWª€gªb9pâÉÚæññø 䜎®qQ¢Ö°• —B)¿W3Œ3½]ý&>C•ïýŒ6ã?@ªÉÉeg!Ø9$ÔÿµÉ4ãÍ®VôÊ®ÅJjÞ˜ªºFKŽüÒ°Øýßðáñ[,‹5À®¸|ìÈ®z·­ÖïBâçóW z~ÿy_¶ËN¿2ö’TQ6{6]eŽ·Z‹íùƇþÆK¿wÔu£*¼U$»9ñnco×x‰zf)BFm›3¼ù|Ì…íό濫BB@ßÈÓ*/VPÐûçM8•†e… 0' R±b™9gã¯ÛªÉ ØîMÎûâ9»]úrx5Ÿ‰<9x~îˆ §ÅšM:ߦi¤$ä¨ ÁÖÆÃ.±®q‰)°D{0G A4c5TUrT«oYûÝ™sÊbº+kéìráÞ´#PÉ)¶ß±¬jßlé"øŠ‡ãÉÒ·,ºÖw…µ¬w³<ý-1Æ–æ@-9טÀz]ËùÊÔ¯DUÍ,ÌÜygÿ ªi™ÑycÏ*È:{Ï– rF¸X{áœVëKBÄUœ§"l‰¬T9sÎ fç‰Þ‘àÎ<0x§ )3bv¹°ïì:¯5\Ô¶î%xrÍ3jЂmZÎy$ämžTÿ¹ùJ{@dÑö#G…#‚#cŸQH=²Ú¦@‡5†¥<ÕX_\\59ÅÝ\Ð4gå{©r^ñD€e¾9ß•xXb¯Jä˜gîû¬}Gr&?€¾ïu¥ƒe–Ðu¡æsNCïM,ˆ«dÖ„&ÕÌœŒD¸tZX&Ó9/䜣 ’™³<™ Î9oùºõ"u}Oˆ9G“Yæ{ñÏcÕÓÓ;Y•—Ë%øÐõ½óœ3;¨:Pã ûГc3.žÏ_K~ź(,ÏÌOчRœMªo—Ødp>tÎwkG•Ð9_×¥È9'@p¡Sá´\Mi?šSáaTU%#m'Äz¤úeùЙ5ÇÊã2A=GXl)I©ÿJÝŒDÝpBR‡åÉ),¢‚͔݉œªšµ-¯®¶ºsæ”Òñù9—Ó¬ÎqY Û6B¢4,hKçv䵸<™4rY}]-8K‹¨Â<Ï­ H¾ë€ãR’ÅšÂ3ÇVL;ƈkº¸‚Ú¼ßÐÛ$cŒ"ö†ÆN5=·s䈬†eÌHèvaQÚÜ}od­m¹“ÂfÉð ¾UÃbSe•†vV-œóð"b‡vi·ErûÁûŽ9ÕC¼Žo?ój6?‡ç8‘<³ÿÝÅJ™ýºWæò —f(.sÊìåÞŒ”s×™rÅQéklÓ”Ä÷ zæ¼,Ü‚³qVm,Ý>aÜr³sŽ7ˆYãÙ»NT9ÎäüáøT¦ÍDˆºNUã Ón’oï;U1l2ò‘ "ø®W‘y:ã¦a¢v&Ë")Ëq ,Ê, ãìû‘|XM`LïkÎJÈÚpNëKK^X©{TœkwÛ^  ¬äÈ,q©¥8‚w¤ªÓ’‰Ðgt.XÌ:®âtÔ†K‚ÂÁï’ÿˆVåZ^£‹Ó™œ_QePVí¼#s“i+P"§" €ÞwM'Qß߀h¶TuEÈ« Ô!‘@Ƶ r ê2óÎù¶ÝD"’œª¹†ýÕ²,އЈÀ%ª–‰’Ós^ŒÄa%‚“# ¬èúAT$Û/ûC×÷ÌDæyÊ™‡>@‰´¬*µè­á½ÆD¨\0ôÄ93G½''œTÁ‘¿éY»®É9Ek²É9绸\UtI™3¿ûþ7ÈÙ*ËyIÎ…¾˜sðº|0øÏÕ躞DœgU™._9$gL´®.sOÂãd ‘S$N1.WÄgúÌ–›aÁ–O*’sB„ì<çĽïïrZrŽÑpœìû^Uã­¦ºO˜*x‹FáXŽêB¡Ê¼nn¦dï;OdIÅ®ëGåMƒVÞYD™]èÀ­U¥ýÎOD.g^–ø›çÎûùü5Í—”r.„~Gð1Q0h7¿ŽâEjFµ~õ—©mر¤:ϳ÷Þ¬¬B7¬*;¼éÔsZTÅùNXS\À¶5ȲÀj‡b¥Ût½äœ?|øpÖm§ ˆzÎaJ¬ˆ¼µ•ûruEŒš8ø;(9Ö0Ý+1ˆ+PW°ÅÁZtzã/°Íï±²ÛÁêZI‘0›Ïq Q«ˆí>í¬ü\³êW%˜kôi"Hdȇ&ð«$bæ5žÑ`dÃZ­R2£[X‘¨’åÈ,TÍ“d#"5¨îÉÓ7"Z)7ã-éÉ[PT ÄÑb2©5¤QZhdý­ d^²º%ylÍô-‚¹Á˜÷ø‡4\žJ*Ñu¯ÇJR}„TAL­ Žf æh¹ôcßQoVÚŽ± ?þÁÚò_ÇÙßjƒxË…¹Éƒ¬yeÐödèÎEµ2¯ ¢y`t¥Æ3¬L¨†Z¥÷°ŠõñNí‡wÚ"í.˜TCvºŠU¾ ÛˆI?¶=;·1:Pó5Q6T¤¹…Æpk)pˆXói FÖl—gå'I_lÃŽÕI,ª³>gÞbM¬ÇâóP¿òê]!¦A¯˜óí¸á>æ°þ¨ë‹ªˆÜ¢wõxÞâQ›1Ö´}øFN„‘±ua-.z^«¨÷^@­¹n‹<^.!iãY»Ž•¥S7™õŸŠÈÕ€o 7Øz;Úá>6rõÇÓÉTºëGa)ÙQ>8×ÅåR#)Úï™2ÛG4|YYÌÄ[î×$˜j¢ý³Ð+{•WÙ{yn˜s\¿-ËUUËákZ%UÉZÕ÷ÐFUįvû‡cïâ|Íiùðþ¸b?¯ˆ†õ‡gUf;2§Sè×—O"¼ÌWfùîÇߪŠ÷¡sƒó]×9?¸Ðýô‡áçç÷λÐÖ”cÌý0œN#9O>¼~ýr99Œ=†npÎÿáÿèœëC²&5×ßjZ¬aâ¼dÕˆTÉ­Â)æsŠ]?V¯?³þ*xõ=¾çkš/‡§ï¨ˆÈÌÂy¾¾À0>‰dÎÙù@>HŽ9¥áxBDΜ^¦è™n9ÉÌi™B×û~ä%Í•Öo–xÙÂíPÁ¹`ö¡DÀYQ9žÞè|)VÞwÖª"™…§9*Àiè ñ"X͉\°» ¥‰APõý¡ž#×%}‡P„@~Cf8/œ¡Š@Äéz±•6t§N|ËI ŠDä§XOrÁÌ|1ô'æÄ¹tÏÎ÷¾rIy´NËŒ#" §÷jþþqÉqFçÈrhò y~ÿT€Øõ£ž‘­IÊÓ.×%eþ³ßþi=·B?ª0gRŠÆ³-m8Au™ÏH.„¡ð}È™Çæºðü£ˆ¼÷ºA‹îc6qúfè‚!]7@Z&O-wšÈSºN뺡#ò¯Ÿÿ«°Ú¼e¾ :ëz½ï–éU™U9-€ú©Ðœ÷DŽœGtDfšW¤—ù…8 )^ Ô÷¡Æ£ª¤éì}_IÅÓõÕ9oÚ¤6¡>§º¾§e¹€J\@ûáÉ®†ª¦Äˆæœã²Œäœ/i*¶Rúá„H¾4§H®ŒÕ­ßž‡´ÌÅuy^¾BãmVFøÂ,l(†ýv=¬•¾ƒn\ ‡àÃë—Ÿj\šy²_§Ð2F„~8ŒGÓ`À|ýº³R"º@ãrÍ)ÍÓlЏÐõ]ârQ€ÃéHè–ù윷áôõå£ó çÄ9›ÝÇÓ»kŒÆOü=ùµ$bÎ9-V¼úÐW˜Ä‚wq¹ ºÐõš;çBpÄJˆŽîr^ ß·é¡Qò«oŒmñWè M”¾iìôÉ¿–cýí¾óFdqOC7ø áe`¥¾´ñF#ªÔÝ}í2Ÿ®ÔJÄæAj[ßoHÆû÷Žy w™€7JáGzæÍ xÍßTU÷ Ùö x®Â>&‚Öi4ÕÚó†ì®[¨·öýÛä M¥ÖÀˆ›¯V™ õ…pÓô´: ½!ß|i|HéÀ-{CwçãGð­ì^䳉Oîô÷vkM¥}'NØ"-ñ†W†{ÒA½B¸ûT·Ñôų{½ª{ø#éÆM§€ šâlæ6<7]:Þ‚ÞJ°W&ÜYæµ2ö(¸²V_J –‡_6@‘6ÔЈ&¸ù Ü‹V´¬ýßÚw¶ŽaÚØ˜ôp·¦öñŸí­oÉæ7ÆzOøz´ bZÚÐFxä¤q ø5æ†Ý ôÞ(SÛЈê­»a­æ«Æb»uÛòjö¤:8ü[Q$|€¦À>‹ôÍ“ÿÕ¿ü/]½‚Æe²æOTE  ÎûÛSє엩]yFÄ?žžÑ›°y›A®0Ê|p®»ãÀ­ÑHÛÆüñ—œ¢9UkÿïñËìÊèøô}Š×eºØÝN¡?Äélè3ýÃßÿ]kº0ŽÃóé´Ä%ÅÇ”ß=ˆxbf>ôA×kr<=åsZ|è½ïì«}üôEDŒCSîºîûïGwb¦ IDATPЗÏYt‰yèýÐy1§È™E´ïyß·’Ä´,q™ûa ]—‹*tý`b[Bò]¯*ÓëçÓõz%BB†Ñ‡àC¯qzÉ12ZŸT/i×õ]ß—úëýo\•cÇ?þü“ªŽ½_mö‚÷>¥T³cb9Ž}%RÕçÁ9r„6;´rÞ^|ºÆk¿œ1)sÊ<¡ïüËyV…>x"ÜŸÊYc*I½ `I™EëMA¤ÓÓS\–yï²]X$)3ëqÚ …5±=üêÒN{§]ðäÜ8 .טx^YZ Qå—‡C­Ùõ  o%¹Nœ.@dýqa»3²Œ ]­s9' êoœª½X8 gòÁ9Ÿ–©qîßB ,S°Ðˆ–ùºÌ³)šœ DôÓÇÏ]ïß=#‘ï†ËåüåóçÓñx<l‰žÏ¯ª:ŽÞû?u]xz~§ªŸ>þ¬ ™yèÃÐ{+ÌøÞ”+`ŸÞÿÿÍ÷oþ÷ÿùÿü_ÿÇÐuŽÐ…Þû°ÌWÙ,%É…®3z4`ܸ*œ“]?zÓÃØoEø§Ÿ?Æá0)¥”’=¨ó<«jÌ\m ŠÄñ8ŽOOG ODÃá¹Ëõœ?ýô‡ežþä·¾&4•Ô@"ŠË5Ηãó÷Ãáyº|m£á½ïÆçÀ9Ÿ_¾þüÓÞ½ÿðüîç(ešˆãÓ{ßõÿö_ÿßÎÙ`c‡è ú%çRZ8Gï;t. ¡±TÈyó6s>ìçm·Î7$ Ë­)æ‘ia7/îºn)-œ¢ïz"ß G@4cü¶UéêÕ–ED$÷ýÑžaD´€rNU.¯çnè‡a4xÉæôRlI²ˆ ã‰3OÓ§§†¡½r‹r2ü‰Î_?ªbÌŒ6¥¾ù•¯JO0ï~ƒ ºÎÒóÖáwaø¦sXšÄ[Ý<5>|̓ª·µÛç þ{ Eo ¸7-©–êLQ¡‰/,£Q…5)kí­GÁF{{¤Œ5ë õN% o˜™-lù{–3°’ÃnœÖ“µ._Øý?6ã‡J ÝdÕøì¦A¼17ºòjç½5Z24´fèj„nîdxk?ݮɛSw…ok1µ{†ö9äí8L¿i¡­´¶õË{Ôs¯aUUodö…ý=EڡŽIDëpó^;ç;+·–ç}ϸoug ^Ânî’ÄM3N„·†ûí¾nnfôÍ<{3cÛ­Í;œèNèÜL£Öñd]૘}súné’¡w×Î¥ Û§ W}ÑÆo­lxÓ†ï³Ë2¹AeÖ«Q]²›1±¶*)m¦>ÌpDÄ_íi~#ÑÑ›žÞj Þfÿß³1JŸ‹÷¨“ÝØ]ØÜ5ô—;—ê § ØPòN믦7ÿzs;h%gø­³ÿÕ¿ü¯Ú‚B¤X²Šê8Ž]×¥8ÝY,¢*¤” ö5¬?säœéÆéqí›èy¼-QkW©¾³{Æ,uZó0…¡„Y8Ù ¬ëz‚áïÌ""ÇÓS}Bɹ”âþüÇ,ñ‡cbëjÖŽPTÕ&£¶øBËÕÈɈ¬ª0/IAC[ƒ‚Æùâ|Wç‚Æ4ùêtþ,9åB=·–˜%&¶cW»ëBµ_è‡#ùnT•ËןC7†a Ý¡_~úÝë—ŸÞ}ø‘ˆLÚáœágÓny3¥sΉl±±_>Ž1þéoÿ¬šŠ.׳Y@1gÎÉ9OΙWÜpx2€œïÇ“ª^_>¦§ëõpzOO¦†6oœ¯Ìy<¾Îæ•lNÁ+;”Z5¤­êáø$ÂgP@çs\–åb;™)ÒÇ£ùÏ!ùÀ9½¾|EÄÓédo5Ïó²,µ#„&MEcÎ8tÖLëáéýáé]š/9Îí´ÿðîá”æ TŠ¢€Èº"tÓÊʨ®ªšãìCçB/véBçœ_¦óJÆUyk ¦óH./WUµ‘C÷ë˜ÉÙùbÌ9MKÀç§“÷^U™ù|¹"âû÷ïm%œEòsLyè"zï¡ëÖÕA4ߥ8ÇùÚœxÃ?h· “cÛoç%Ù;Ñó‡}×¥e*y„̼Äx½N}çƒw®QËLÓ•™‡a ç‡Ã3€šq—ŠsDÎ…€äLQðñ§\–Å9ç½·­ Îç”e޹ ®óä»Þ»..Wõ¾Ctq¹’sF€÷¡³fÚØ=ÇãqGëΗkάÂÞ»Ãá€)Í¡ë}ˆ:_B¤ETªø[ç Îù¶ 4`“yTte¼Ù(®%(ç\/² ™¬åEDa6¹—wýÁþÄÞ¹*"j‰ˆdÞ=&<[À5õz >èfCŠ»¡•‘Ù íxÍŽ'P²0³Û$;´qÛš"FUv—T·¦J„jA ï}aYxk0.¢™VXö’åu­3){96Ò†b&§Z’Œ3Fkl®*:;Wh -Ž\U¨¸9ò>HN˜±™¢XË@­»îv(¶ó|U!ç@ȹà Kiwd·QjxÖFç{ƒû'ªÂb©};ÕÄÝlÆÔlˆ ÷ë}¡¾wÖ»ÐÛ‚o*É9¶%Äf´æGBI6ð*,Ê«Žhë*ÌI’™×{dO¤)åuCꆨNç/P.,Â[ÕñÝÔ¿¾Ûº]¬:$]Onãp󰾟Yu×è%Gä¼PG$›Ù‚qÉL—]àê`ß^8gu…N°¬LTaEm6¹é¢8ç”Rð®Í-*”j–êÖè(Ü‘!ß0ÐG3ã±ÜÍ ½Máv:‡Õ4Ú|·ÍŦþjuÒm‡¾f±5Cžý]£¯vØ`‹!5½rI4ç¼;GhµÄ Å‚ã¡‹¥Ù4sEÿVkêˆT%çt´™‹Hß÷¶ØU„!Ú㊳ h›†ê}'ÂûZnFDí„éWã¢E°IÎ{rä¤Õš>2o ï@äÀ{ÏbÂÍéüÂp¨ 9xDdž&Ó°fÌÌ¢º,‹Q¯dågÏó¼Êe@t¶Uj#ingŠQ…E•™Sœì¹b–ˆ`6ÓÌ*R2žmít³¬K$mÍ3Ës¡ ¡äfÐ-QÈ{Sú gfÎÖL˜!33³¦ÅA ë§Äl8Ãá¹€øbÚ LqáiެX8uª’æ‰ Ä’ƒ„í1¨òS.Áè°Ù· "ÇE­ªÊ¢ MŽET3T(á<œÞ#@\&Pu]OvÁsNËTÌJ¤DrÛý%¤êD\Ö?eÏ–ê2OÌ)ôG‘¬å •93+Ѷ/Ç1t=! d¶³’s2ôÙãØÖ¹¡³t\`æœÅ\ª-ü…sŠó%Ç%çT.8‘5ë*"œ@Á.®¼!nÏZ4ióÃá„« 9çÅ4AÊ9 #"¢!rª%ÄQE€È‰°ªÄyÕœ"œž?¸ÐÅù HNÁjIqZý bŸrQ®»v•‚³ñ9'+‚w¡ëQ…U - ¬_^o–Ž œ …ߨbÿ½É ºÅÄi0ÅkÎ3ç´å :¢¡DÈ,æç’9BÊÌÌ>gNªšs´p  `e©æ>ªúôôÔ"«!xç;PafçÖp¸AWTÓr±µ !t÷ÞúZU†¡ë‡eºl 6tâä|p.”)†H½%¤·,R[Ä‚(„¢k'Wö.Ü8LZ¾æfëÈßš‹¼•â´‰¬p¥Õ ÊuC€¤JÔ£Ö:€E0gïÑ;g^=µR^Gת’KÓ‚í(ßÉBãSJªIØðq§è!r„³°4["Vê5"8BP0k`2mš;Šso„`1g¹?{˜‘ÆCÏœŠãɪE5-¬Jô¦OÀðÖAX¿Ô¯»h”ìÉòéni”£uªðÞg–⑸Lßÿé_üHõ÷"Ré̦ÁŒ1æœx-˜qžçV‰oªêà(x·MlRZ8£*°pZfûÑTŽÛöϸR«ÑçDÎùœe^Rèú®ëa ³¾¹oŠªHÎ1EsUs“bÎ"b¾íu¶ÿfº‡ã‘36¶mÇi™˜ešUAÍ6 ´3ÆFñfV œEÄH1MÍ”U¤`}MuœÓR×€ˆ 0‰3A˜-õ¢Ú£üÔ œÓ2]¡ëö½˜SZ“WKš|TlZÓ29çmëC‰œ¤Sœ§´ÕgÂVæ¬&€ˆ)F3×D’Y™íü œ]?.ó\áÇ®/4îœó<ψ4Œ‡yžRŒœcœ.æNbL¥® „8?#P+ì˜ïVLŒ9ôýÁ½30U‘”­¥0w7Z¨ "‚2³í>¨",qYRZ2+½ }?ÄF"PÍf/”ç:çƒóÞ#ÎË¢ªkS^í¡pY u]qžE%ÆÅkÕ|Øj>VíƒsÎlž€9)³s]nÙaP¿{ßudŠ €´\eõ(]o{ˆwÔwÞæ;Zj©¨ª™9g 9+ ç ª9Gï{ºæRW»­Óé´2þT|¹ÑÓ<¥C{Kn*S«¸\9 †®3]×tˆ~88æéÒt½pJ–nxCµ´¸’.k–7lÞ(Ò^ŽLù\ÿ‹~ƒÅ\{œUª"ÂŽBm¤ ÜGyí{NYu²¦¹°»cá²uüéRMÝ›‹Ê‚¹q•6y@ta(>´›º0Å 8䃊Ló%3Gª‚˜ÚîÏDV¢’±Qdµ;¿sÄ,™µCgñÕµÀûPäo5[oüa ¦€ÏûÎ}Ë*kàA¸!þ“X 7ÿpÿhnJ½3´4q Ø[·0E0²åº%™f–JyUxLTׂ­h¤QŒ°Šrmû%kEjSßx…êžnSm~ÅCŠy´©à(Yñ Ð6êë@x(´0¿>(E ³÷¾-Œ1oõ÷{|kn$ÁßöDh¿*ÜÄžXjvÙ «^ƒÜ]`ãö|"®C·¥„%l¬üÆÓµ+öñ†‘ „£5²ˆ±ä564Ý}rXCꃅwr— F³ð®\¹u""Š(Põà(UŠMXqÃŒ+í‹›óšò¨ÅDDWÍ6¿1‡û‚,B¨´NVï©Ä.\­“Úœ*ÜÚ ýË<¢åÜW'½§šoℳCe[7ásÖyGÊ8ïˆ<: Î9‚”gDKÐRA3µnáÖ‘ÅZÚê òα÷Bf_fÏp1¸X=WŒÝ\=ì>ÀmÔE³m6¾Ã7ñ EË mö²ßw¢­FÕê.fs÷Ðo÷‚Z©U°‹ Õ]°šX¤aÞ“FÓ‚²º·Q›î•>ú é–;ËËÙ.Û*ÙVoe6f‘½wKIRfç 좕p_²¸\ïhJÓC›‰¸\E4gq#fe.Z‹jÂôë»À®?Z W±9æÜZ=©Bß‘§+‹T˜»ñãç‹#z|~8=ÿüû¿WáÃéÙyÿ|óÝpPÕùò†±N—óëõrî‚wm€lIZ‡Ó{æç "Ÿ¿7üååëËׯãÐ ½7Rn7ÉùéüÙùn8<‹Èå2uï‚·ôDoö ¦G€ÐT0ìú#s\–«'|>¡ë° ¬ît¡?0§éü†Ã»9^Ïç×®ó]pÌš3#ù2ªƒÛ£Ë?Œ=®£8›úhΟ~þyž§ñÿaåñ}è‡qªšS ]×õCZ®m B´ gÉÉu½ïX=EbÎï:ß Å@6Ÿ¿Ðœóç¯?×¶O…•S”ãrõ¡w.X9Y¥úµ#T(-cÎQ–)ô£ ]Ž‹ª:ìnåüävª‚ÃfÑáx‘Ëù×PbáĬõÅ "œÍqÊ;Ðéò¥XC(Çx5w×y[‰¬œ’jØZO´þ ¶H½w„YuY^?ýÊfËj“© }1ô•'{Ö•R ZJç;½~š_?ßÿ˜sŒÓYEæe)=ñ|M™Û=ååå@¿ûî;:/¢?ÿü±ïü8„yÉKÌãÐõÏiÀÐ ë£Å).ˆ`«¾ #ºéòEU90‰áÆòPr>tCN±vá…€×9y¯>(XúDâiI¦âÈYRšŠ?€ww Àœ_¾|,_-/0Ï)sLù·ñ—~ü­a]? §œ"!"9fþúz}ïǧÓÃp8-óy™.‡çï‰\7¼ï~÷»ß‡ãÓwÏD4ž>¨Š€Ñ9Gþ§ŸRÑÃóÐãûwKf~}}Pï\×õýpæœÓ ŒàMDæûMÈÝeœ+®Â*’#©ªS%rX†ñÈ"eúsŠ–L¢ªÌɪ%£Ú©µˆ€ÅNÒ ÿ~x² V³Àì¼79ÎÉÊâœ“ŠøÐµY„9ç3˲–x:×2‹s2 Tw—”Éúf³Ä9sŽ€Hw“µI.¤83³ä¸L‰ÙfáS}ïÉ2%ð¯Í@­Õ÷Î¥ùÑ™‡oÙ­æqŸ9o2¿ñá1ß–Õ#ê.X¬¡TyæI.+[éTà»bi݈ÙsÎÖŽ¿Vh½-f¶ÈB­B†& ½M¿PÜinÉ µ)…Ö:õïºð·Ë)½u‚‡6¼¡_ì²ÞH½»1[ø¶2f‹_×[Xæ6d¡íà^k ».xìZÎÊï7> þ’ü}3z»6¥`Ü똤x¾u¯°Šv 5i½ûýǾɾÿÍîY*…¼Ö«zëô°õô,¥–qc:´í²>rf¸A•*7Cw‹Š–´¢H×`@Ñ7XþøÑI1¸7jhU¨;†;ТUÔìuåØhдY曊gÝæ Ñ‚k­?ÃcQ# z`Þ ïߘx} žÙIfn‡ˆøæþ±#¶´ ´{ã7µ7èTû(×U¦ßf]¾­Ù¢xT+Ápç½áCp5í½Šy,¾Û(€ùFŽÇPÑUržsÊqnG6  Ue¶:×Ù͉8"fe‘ËùµPET‰Ü~HhðŒ¨Þ¸\¾½]Î熵º@Dþþïÿî0Çè)µp¶šeÔºÐ/‚èç´Lˆx‹ÚÚ#b¡‡Ø%KËõ:M¯¯çqžÓ?}½üøýwÆEÔ1G€Ó±W…™ïZáœÙ¾ ‹Ä”UõõòS®[õ'"Z.) :'Žèoÿ毃÷ÇC‡ˆ—×ψx;ß !ô*ЍŸ?þ¤"}V,ºÌiBpœ3çdOi½ó!Í“F?t£I›»ñ«#«rv¡ó¡Ÿ//Ë|6åïnHéÂãBˆ<ÇyõoÎÑjJUÍË D|p¡C ³ä¸0¦ã»T˜sTsá²¢‘¼6¡TUd"ÒB°¶¸>DæÄq²`§´\íx‹1‹ðAþ)hÄš‚ ]ç“Íld©cRåË_´jßÌ)qßù¾1æÀp"Ðb36Õp¼ïÈyÄÉRÚ™˜uš“-U"t´;h¹ w]â/|èB×/óÅÖ2‘ËqÕþø"H¯Îw·ež–ùzxz7ާ/ÿQÄäL°L¯èœ) ™¿ö]?Œ…‰Yì1ëú­ÊŸ(¢1fD ²•„EØÒÞËË„`™.%Õ±ïœãcZb|÷Ýóáé}œ/pxþà\xýô£[{z~÷ãoßqŽœ³ Á¹°L¯õh çEx™Î–½j–§®Ýpyýøÿþᯟ?|OHq¹pÎãéƒùX03"Î××Ïÿ8mð-Òõõ"æüd¦ê1.¿ûÛÿÏ9÷ýã5Nçß ÀYJ !/Ÿþà.j´Æwï?Øá–eá}ׇn´ÓDx7E¤0Œ9-i™V²ÛFìÓE„C™´UkÀÄËdúÌÉæ”Z¨ªï†êÄ,ÉAÄö|%BñF¤²I^ééçËjÖÊ9-9ͪEên/¾[)¡‘ó]AP83s¬0¶FÈxßy笙†uË5îqpÔuÎFãˆH¡/„ÏÐ…®çœ8'Ù®˜öé_¯çÏo¥z‡®GlÈ2ªxû°…Þî@züF‰ð` Øþ¶uÄ«{âÃ?Kh·.Þ»¹Â]‘xã‚¶¶°°7îÚɱM²nÊa„;))6&@7¥Ñ}]³ùC­‰ëXÓÞõ‘ûWSF6Ùð[¬à/7>¸‰ˆw!‰«E÷43mùÖë[.õÎÔN÷ƒ9Ýõ»«{­ìöMµ¸3’noì#Ar3¥º•™ë#Ä£²¹±øÚ¯uh{cXµ¶>[Ï´óÿ…’ozäÜ6#Åú‘n®ðC÷>¼ }·øIU£2«4˜G Ö´é[‰ºll´n­È‡ %Ûe¾‹{|ÐëCãíKâYÍٱŖTk›¸†~í6¢Ñ/í-.Þ.…™‚€7®-˜°6yuÁ¬ªzþÙ‚½÷Oß”<M¡13ÓG~º9bèczãíóÔšÞ° x¼mœ|¼—G_Ú!îí_î¦oÕjî}Ç*]k¶Ÿ›•ÍîMª®®}ð¶å†ÍèTï—Û͆åsfDQZåYµZ7B‰e+¨ËI • r–•{¼rÀXBÓظ%„íçw…t\=‘B ›‡?g1°Çƿ“`<Û»\ž@ß=Þ:$rŽlàϬ’Ytèƒp~ýòIU§eé‚ï»R+Ì1ǘOÇcbfFÌæ¥Ì´äùiPÑ”²( BŠ3ùйiÎÞƒs³ª^§dšµà‘ÜDÔ®IŒÌÂÇÓSÎy‰s•”µú¾˜S‰¥ª?þð=ˆä!ô‡y™/çëéÔõcœDäùÝ{U]æ«™¢™óúÏs–Uö³`ZÆÓ{Bœ'M18< ç眖%¿žçwOÁ9ôÝè|Hñc…5ÂpÈ)¦8‘s$ÞØ.Ѥ#búŠDÎí†(,××Rаp?žŒ›€9NºY"„óëyú~è»—¸\»áXñ®±ÖC×”âD„3sê†cè†åúZ÷Û8_ÍNl%ém´æ®ï‰¼Õ¼D;B8žœ“ÑqÍRVãºjã@ýpâœrŽZ29¸JÐLäS¹)š³4â2—Räy¶Éq}7È„¥‘ñi¥_î^£—ŒH]?®é9¬Â—9Í1ˆ|?†®ÿû¿ùw!„§§!öÇçªætÎyç8§ÃáX±êJ@ÄqÝÐ…~Ì™sŠ!ô!;:|7óŸú½óþôôÔºÀ13Y·Än8‘ä,5¾ @€™“s0Žázþòúõóûï¾C¤/?ýŽsvÌŒPU§9)Eï—”ægsÓ¶†Ãä›Å¬‹Œ¬Ø“° M×éã§ÏÁÍÓ+¢…vuœ3"à#_x“:¤8ÝN»‰õòò‘È­jôŒäöu«öÃ1çãEU—éì\p¡ƒMz˜ŒéfnË|!rÖQYz­*ÞÚö­Z‹U۟ת€³0d@GÎRên´÷=¾>«âÎþðŸÞ Ö&ê¦iÐâš¾õx3Ë«êÙV¼‰hêkd?\3 ‘«¯YÇ û,x½qT«uêÊÉj-Fîcæ”+Ы֮ԤÏ=oµxàÊ«pSÄ­sƒÝÜÊcz^Xå1÷߸Ÿi6dI½™ºX¾´I Ð^W‚þΨh%ÚB£Ì©Ôí:]pm(•š¢aeãÒ¾ÔßL¿îýŸ×|®»þ¹ö‚F2–»¼²¶øl=©ï2ç[Ò«±%µuÒ+ºÖ¾øá¶±'Ãeú}ü¤®t…P30rή„Ú•¾f%Jša‘-Þc÷duRM[‡TÒˆˆ r[2É@P¥±ÞCDÈx_›.çdÃÌæÂSךlΫçÖïNí”Ê>—®IñÌì I-ýÔ¯1>çiº›ÙãÖ°—þDðv%¦m7økm·¾jmw  Tº›‚·™¤»¾ZÛ…­-‹¾µs+Š8)¹Ê  âÎ,°¬Jr6Ý‘Qwj‚]Dšî!Sq]iüû@1ÝÍþ‰`ÿÙöì ¸ö¿†•uÿ·%¤ÔòPŒAûɦ¡®Û\™âÎÝD'‚Ö8º· “ fBk†õºYìÔ~~­Rý¬ªÕïç>¡Þ,“]´´ÕÆv¨’ä<"¡s€(ÌΕHo°;"r]9ßõSл¬DìCGD¡?ùX-:Øû®„l9“îi'âƒ/–+D,B«ÜmGÉ9Y`,@®‚–Ð/°~QAû¾ÇÒÆy«m q^²ªôëäCI‚ªNÓÇÃ’0ÕÈ IDAT@§ë9çÌ™§#%Þ€‚” ¼U¥ôà â2]í¡uÞ}þøG(óaf‰)ErS\""Î1¢¹ÌYmê©€&N·÷žK’s ‘«‚Æ%ÚyæsÎeê–Ma/fPG˜‹Ÿ–äœm™Åe© žr¶…mª2áÌ–ãÌÌó¼”8$Äë´ ¡K žÐ„á†m Sç &‘”u¨¨A™sÆe±xtfaSÇ[ýdÙ+}j}OÂà× Ý€ã~6yÂz0gas2\ÍÛ`ÄR*K!¶™ÿ«à.þ¢:(•]ÃZX· ïh胊2ˆdfjy×:OçrZ¨HŒ‘ ˜f .‘‰Tu…œEçÈüšS,,AŽÎyÐ’ö€*’×ôTL–kh¤mœ“`6eˆóá>ñCŠ¿"(*¡0'Uí‚•yºæœRÊ!€w°H‰ylrÌ×@TæŒ)A‘œ'ç%g»œDôþ»ï •Ñ™Ÿ°Ÿ4§”b4´ºä+[äYŒ«¨†Š©RÊ’2ûySŠÕ±ÍÅÎlüÇiÍ…_!œ"N_e·{³+R)f{Û²d³Ì±ü¸òo½èöXÂæêP÷m³ÅÈ–á…¤Â5÷{ëKr©U±+™ióÖy`^&âH…i ÌE¤›ómÍÇFf]™»÷aH°†:ÔÖÂù°rñtY¬Ñ„ˆêEQÑygÔX“ +sº?*UoÅ:Ì8?A@mÓmóu›|°Ænõ=€é^¿Ùü©¾$ç;c'ß'Ô‹(3÷eöž•ÙíÌÚMêç‘”Öì=ն͹Îð}!çC?š69ÅL}ès¡?sóå«ñ’MTþV’‘Ñ:d;‡pÇeY–eŽœëÚ§¹7!¢Í1o'Qpˆ>ó¥>a&16"À×/_Çq<q™çk¡†‡n–Pú 0²ÊòæéjL}ïÝç?ïODZ äcº\§ÖŸiY¢]CZ÷å%fIJc€H‚”LªßÖ€)eH)ªê´${Oï(xg {aþúÎcLU"WÆéó X_œY–ÄÁSð.. À2Ǥ }çâu^ÌÃÌÁÒt 1e;\bËŽ/7Å‘*¤,©~MeQ"ËáRåÅ<8«C·sDî¯{?ŒÅ¢¨ÔA† J…I-_Ó9BÐÌBˆä‹7ô/Fµ¿½­ÌÛÞä†yrsfeaΘ©e©Í×˪.Eçh^Ò-ÈúÎ[)æÜÊw`ɼ,Ëb¤{†ÌÒJNºnLv:²fµ÷ôÁL>W+Ân{0'3' ]UÈ„¡ë™sœÿÒÞnI’$YÒ3wˆÌ¬êêž9gv»È"ppÉÏÃÓð&< Ü È"ìÂr~z¦»«23"ÜÝLU¹P3sóˆÈêÙ³-GŽôTgEz¸››©~úý\,FVÕe¾¦,)+"2y%Q ôFG2³ër‰càž£ä'qä84Mþás^çu~ç8rÍÄDâx0€o¿þS^Wïi´(½[ÝUö`„)‹ªò*Rˆ”"VüÂ6`ÉǶ›ÓYUÕ”R òTýê…H ˆæ'7Q@"35A´8Ž7áUÛMΫ›"µi·Zf‰Ä’“äÅëêÎK¬D÷Qچͩg¥qoþŽ’ÍnÌHû„/_ü»~š£ËMÛSš“rÓ.£è¶H¾å,î³HÞ²tsåÿÔìÁü÷)-¥Iü³£Lé«ãÄÎ8ÿnðþQ¬!"¹‹ÕU‘ø†aÖ|²@ ò vQ|ˆUÞwÃ÷‹—ù•évÇ-Ú·Ú²ÇŒê ½j{võ Õ+)b`µSôÌfÅѯ,Æ!vÛb·’ 1î`+d&G„JÛê9D~«eCÌþÌÍ Hõ“(ž¬®[ ½m<ª¹¸Ÿ˜ ‰;Ÿk¼]ojˆ£¢°—8S¿í\½ 7/x£búh\᎚Šn`»pvõð÷ —JÅ¡¢{²ª¿Çï ‚½´ïRÄ7hô£¿u‡wF¿=ú])ÔñœÐ)ÙXº 0êðªFs=àÕÈEˆèÅM•µ‘·½€™zíŒ.{™ýÎq÷žh;C`ð_Ú¢(úê³³6@­Ÿ¯m΄»Lö\?ƒÈª+/C o¥ÛU¨doý­3¿x7#¢ÍkÝè­ùjíòÓ Ê"Ó³i7q‹qþž6lÇO¶FÞþmÄ[CÕõ[7k4|ÈÒ¹=“v!€bSO¼¥T~ Þ ¥¾Ïy4SÕÞi„ÉL*yêÎî£Ú¡0îTFÛ‰|/¨ƒß}ô Lÿ¯&ª€Wú-ª´fEFD,aouN‹ªL‡§^#ÿÙ×ËÀCûp].íý¹I¨/RÙu¾Ÿ/"â8=­ëüúëŸ9G+0;¾üˆˆóåÕUù.¬vy%¯9'D<Çf–ó«‡'½³€‰Ùç‚v<0­³äô_ü×ÿ­HÎ9å”þÝ¿ýßøñÿúïþ™€™ûLzýµÎS}ÿú3‡ar’ǼÎËõÝ«Q?'Öå`O§#ŒOŸˆx<œ*×%§õõõÛ¼¬?üáoKqg‡ÑDÙì<š.­’tÛeÓm¯ q]–åç$æ¿ýWÿÕr~»¾Ó1ŽÇùümϧ§Á¿ Jþáê&—nï)®ªvðü±òh¬5ýþ»rZ–ùBä0¼ÏyM1Nc@ÄeÉD%“9ÐH!FÓ»mæËkïT_ L!p]§ ¹ù^›î{z"$b`ë3ÝÞÁ:²Œ¿ª×÷÷óùú‡Ÿ¾03˜æu>LùR?==;ðµÌ×e¾"â8‰b¢Ÿž¿”°Äãéó2_®—skC ¥3"žž_8Äiš@$!â0Š“ä"¬Ìã³™µYI«ù=¡žÇx>_Þ/×ã4|zš8D$¦˜9’‡€.óe¦8ßßÞÖ··†q:˜Í4gU•ãñˆÄÃtTUýõÏþÞM‡)†1KR‘DÔ¼®y]æsc,»%"¬ËÀÆÃSˆ‡œ0dž!ð¹ |xþ¢9©$ß7 s04#˜ŽŸT3á; ŽãI5KZ‰ƒIÔCÍ‘ç8öc%zªÒä î<Àøfu5ûÃrdñðªÏ+æoPKÜr-²©ò0‚ª(‚™,õ‰Äq¾|3³8LDÖ¡¨&j(BÕ+ Ï‘+ƒ틵á®ÕñˆAÿ“q ˆâ]ÝçüG3–ù`ã89ÆÐñ¹Â0žÆÓ‹¤UÒê5Ãá}Xf*á;[;|ÿã]оG ß3pO3¶ïŠBñQí€~w/¾QäºMmÏ¢í ˆÒ½\µ RÞ±ï‘7­>0Ø„·T~õÓnoãrXìF[ÁV‰ïZª'û}­K¥ê”y›–<®mpUà{3WˆQÇ j­³m ‹ÍÝÿƱ:súí²àW~ië "7Lýjž{KGª¼îHý`jP))åCµP9¬«ÔèA«9Õʸc&ôOÇoþÚãÔ+øòÖ™-|óù×¼šõ®Ýw’…ÃT]üÔnýq„sIö†°IuöMîì謕ë½)nZœžr£ºBØ7L;­HGQÝžJgfÍ+¤®áfê[lô7üû·¸ÒövZ©í…,rƒòƒÛïºÕ£Ù¶È{ù9”¿VˆRõ®Úæx°z÷Ô›Xe/<éFwëîûîÚ{„pÛCwo±¡ÝÇãÝìtý®øÀhÍvòïhjk.HКõÊ_´{ó‡\8´û—µ>åzgU·ô‰>ù=Æ]Q™:ÇßÖäõrì.:f^æw³â.ÃLÞ¬vš8—ù݉,€€˜‹Uˆ£Ç˜}§}ì'pÉpÿŒÌlOÓѧ²ïpÃø„S O묪è3BâÈš%/’s׫qI~[çë[¦qz20Ÿ|p–ùÝ LEsÃ߂˜‹ždCêéuQ³5Kκ®™‰§ã©ZÙµŒi%Š!`•‚Bù„" —«ûOsˆ`ÆL`(¦ƒ"š³ž?M‡Óõý›™†qŠpXç‹™HÎ’3ªä¼\¬®H3}½¼Ùx|"ÃtB$Çë5»ÆMÕ@‘ˆCLëÜØuóû7 ¡¨ß<~̹’Ýâ5§Ù´Ti—o¿sˆ£¤åêX”ogD‰}D4ND¬9ÚŸžñüíW§X®ï×÷¯Ì1Ä8¦¯o¿!±;BùðÛSU[¾ÏfàOÐLÒ ˆãôÔŒ]Ììtz""· t%{J‚d‡î>§$âÓâÎv£µÜ ¿ŽÞ™Ècµû7ô¦8»×Ë›ùꂜUÕ†?‚›žŸžžŸžüƒ<ìíÉ ÝáÌôòúk§8Çq†a™çu]>ýô¥Aˆ¯_%¦¸×ùq "ô=ÄMä×ù"Da5KJ+¢cÞ%I6¢Pr ú¨2³–P¿¹ äuFçbì¥Dþ_K‚#rä8M;ËV#fŽ©Æ!0DbŽnë ¨Ë|nÕçåºÆŸ_>A QI9­%!5à3jƒ]ö*€¨þÙK‚”5%yzz§ƒG^¤4ƒJNk­ëìõëoq†aò+¤BœŽ%> “¹'µ[©…0ŒãN~0÷q­ÖüÖ÷J›,"qoè ãôt¯68"Bª|Ÿ[íPZÊâ÷ëÌëB §EUÇg7;ÀOŸ¿¨$Ée N€D”Ö¥t±!ú·>Æ÷n¬Ýj!H"QɬdüÝóß«¸¡„p¸ž;ý3ì½la¢•L=BÄõúֻɇ6× ·}ŒÈx‡J[/ÌßÐå–мwàj>H=güƒŽ®kẠ3èþð¦È±w7tŸàî¿[7Ý•¨U'¬„n¼ˆº­mã~Œ‡­*ùܶ>Tº/T¡»ŽÍÖ®40 7ðXc»9lQá¥!nɈ;5-"þUÃÖ”WW}Ü%vŸÞTD¸'‹7«åÝUÞxš•¶›Z— wÇ;Ÿm}FqÕÞ¾ò} ¿SΗLZìî¢uýú ÛúB¸ÛhðN}‚Å$;{´J²ï«íGŠ}˜R¹{îÛú›Û`B7£Rê Ú¨¬L ûI¡u7û¯³ýÐ7TiÚãT¸ºƒ›föNmÝk¥oð’­ïð6é>¥ l¶aW'Šß/Ú½èîÿiüÞÓ¹ŠÐö¨T¯aÇ:¤Üçmüúû½ÚÙ±ÝbúhÀ‡=“¥¶Pö!ˆÐS”wá)â»Ëÿ¦{µGPVS*<ú^7VuÛ‰[—¿·³¿ÿ5ì]KxþôCͰ6±p4§â£Šä"ãš_œp4¤8$'IK Ônƒ ‡#§OÍ„ƒž'~ö‹•C¡š{ģ̙ªi 1ÓÈ$„€¦¹â UPGlªo¯ßÜzœ¦éôÉk_þéïUåôôÌ-,~zš†i@â'‹À9©¤x8!Ñry§Ãßüé_š™¬W"Êi±æ¸…t|ùÌ–ë[NËËÓ¡šÉÒáôÉc9ªè|ˆ£M'Ùl] ªž…;R΋׭fv~GÄ,ÊD12\KZ:éœTß{$›@ tcGBˆˆ$¯×÷¯\Òf•<ž^\Pï.Smª¹uÕ"Õ÷‹Á8NÍÆCNKšÏ®iñYoñX_WV Q°U¨*Ù§A’|2MÔcAŽ£ ÇÇ缤$) 1Qˆ„%ŠRc w å‡–‡)¶}J³bÇ%gFæà\Sf ÖUœÔJ„C% ¦´¦uÇaœÞ_¿™Ùé™Bþò˯M€«ªÃª‡)-k^Ö\*Ç··ö»QÌÒ’ ¥_³Èšac0Yó÷ŽCøöí[áxz€´–c“Eqȵã¹Ù»9žP¯*íÆ¨d #â_~ùµ—fÁ%!¾©"¾½¾¾½¾½<ñ|s–ÃÕ,å_ªÊp@ç*ûS[Öl`L„‡i0³o_3¨tS+ƒËåm¾žs 1Ø;m¶<µÀx˜âºÌ×ë%‹6Pô‘ˆ.k€iŒÄTRÒÄé h}‘o¿nå‚L%§µm¹ªÂe§C@´¶CŒö{®i ÃèÉtªÒ–þr¹h¤!ªâÆñ…ÕŸœØ+Ù‘˜‰%g qØÂËj ŠŠ !1GUÕu¥’S¶¨ê0ús‘‹wvZ¼­ 9 ªúöë?8‹Õd8>‡ñÀï_ÍÔ$'É®t/ ï öàFߣžžˆJàr-šˆ(©‘”ŸÞ>h Sñ ËH´XÐ-¸ï™¯º™Û*~\²  ÑÍÄ y#~4×$Q¤Æ­žD2tÕòŸG™ê¹Â]ûUÕ÷› D"MpÌÄVsû@/BjfÚ®½Þ€+« ÷´<ì^*Ý«p ™!cû%.{*;õèï’ Ÿ^ˆPDüµD¼×áîF©ÎÆÇn¼vÇ~îœÆ›à½ ÝOÞLìnõÚí °ûiY!ÍöˆDÅ÷Áz´íÎÜìZy¯è¯Y@¸Y·L¥¾ƒßuüªoBâ ú ™Û™ns轕Ë`®ÉÐÿzJZkC?˜«”fo?ÎÜÕÂZ=6:€x*qGûlÿ@•ö·‚˜6ÐCÅ Ü{ƒ •È?Ä¿¨þGÒÓw–Z=naE±GˆðhY÷ñnlZ €r-)‚*6y˜xˆ”µ‚ìAÐh* ß%AXiþÄC]úi±O*ï<µÝV¡ÖZçvݰµ[bÖÀ<ûΔº¸<sGŸ‘ïz=kÍhû÷{fõ¦S(LÏ?ÜC‚Í¡esØ,$¼[O{ø¨JÓ/9ÿ–œì­ &·«åÁÄtßVÛè®ùÌ«“¸pë±\h=Êäãœzv²–“T·MPµ#†h…v>°!«8ˆ„ÄÐßËH}7ß…öÕpASMY(V‘¼ÌjŠ¢ZÂÀÍ$§¢Žó˜’rål‘4à5e5L–%ë èÝ^–Äërõwð^ RõÎÒÀ5=‡Íˆa£ûÇæœt]<†~ÄÌBˆ„8c•|¨YˆCŒCNkÎ)ÆCôÐdVRÊx_˜ÙyeXka®»»TÕi3ïà™ƒ¤õ~…1`ûä)Á¥*ãP}#Gw\5t¿"°>œSStEs9¶}‘dã8"Q îͶì㟈ÈM~ÑgœžIT¦Ö5tôºçƒT©"Î@Ìk²¦5|¸$kTìÖòigÙÓ,ŠRŠyþç.£ÜrXý|ÚM¶"‰™¼rå‰{¬{v¹÷ìDh*)­út:5€ÏLs,Š"ã@LÖæ ÌDfFȾ=!½Wòq8 {SnYùâBõ(ûˆ×n×m§$LÄ9Ä3y_âˆD/Ÿ>£ûˆ™^.×aÆiR•†‘8‘¯ X>>½¨ÚXHõN=Æ µ)¬°å¤ "ÆÀ€0O>)>d` vû[3Ч*‚3Â8ê6fBD‡ƒ %…ˆ9ħ’#_S?¡&Œú|šC,)E%\jƒ*΀Àa¨ì³’`¦nžžÓ"y%ä>™ùÎm›Z‰ÃàQe’$ÞÈB$"#އçåú>¿u÷¸a:…aº¾ýêžÝ0ž Zî£æ@ÌÄÁÍtúä¼kÖ[ª«oªÎ w<‰9;_c˜/¯N_{I†'UÄ÷=g ‘ܾNóö(5©&-b¦á& Ðë7V‘A9±è¤ ­ÖJج¯w0i{SÙ4Ëâ¨á·w0&ôe$"Þ 9o ·{H©r(_EÔÜÖ!"ra~¸¼§>›ªä•8ÇâÒjPí'k/çivïïï9ç/_¾¨Èº^q:¾l[+ÅÁ¼|{x©RÕÔ•ã˜ÖënJI+fX‘½¼,ó4Â0ŒC€'$âa2U)øÇqœNërI‹ÓÁqE›/o*i§V“®Ë…¹PEn öv¡½Ã¯ïˆˆ!vU!8é|}ýˆn™Ãñ¢x˜ŽÌìèV]÷²RtU2˜¶ü¨,ÒâgárC¬Î[f*f6ˆˆœrâ^¯Í.Ò¼z›|/ô­ä®Â݆(ö€‡% Ù#fšWûR®CDÖYuô„‘r¶™ˆÕ²êþ $DððÂ’ÂTõH˜bæóV`3×°«[sÓí Tɧ§–´?ð.7Ìiš¦aœ|º™ãšAJ9Æ0NÓº,ëºø÷==ŽJ‚«‚¸e~ØÃ·ú„ú!zÌ!Ñ|y3°8LHüò鳩È:‹Èù|‰Ãðüü’×%§y<=ãÑ­m^Íìøô²e©çœÓ¼ÎïqC ˆb moußQÀŸ^ÔĶ®®½¶åv™ ¹wG9esrtéøô„uÌtyÿÊ\rÒ—ùŒDÆ#ÇAÖ¥Ù{ÿ?bSuÒ9ë­¾Šä7±òŒHc23T×% Ói˜Ny¹æ¼ø|Blܽñp23¤à×<Nãáy¹¾¥e·1‡xxúŒÄ‡Ó§óë/ê¾Ó"Óéåðü%¯WŸ^™ÙñåUIËìþ_\ÎãH!æuÉiñÏ‘4›ér=ûþ €‚J–´rˆÄA%©‡M©µ\^«§X ,ÜF6iÍëRîÆÀHŒyE"ïjÖù‚{ÓSÏÄ fáÃñþkúÎpÛì#ïÄïót»œ®í?‡$þ;ÚÀªÆm;“¿nµÊk¼h»Ñ°÷ :o£ŠÜíiô›mT‡¶áMP}°#šWÖF­øúÃQÄú¤}ûè5!Mö€xmÐLñûì*l@ø˜³±w °-âc›ÆŽ„÷ñ xCྠ7~^íCz¬o‹zØ4:í›€í ‘Ë&³ªöaâ¦oxˆ¨h)wRb"3ÚPÙí[Uýzižzû¢>Ü}REP“ ôö.U´7Ñ8Z|¸éˆíÜ7]ñ˜ƒMñýר¤o¤8¦™¾<¼CMë¼Ìïãá)Òñé8š)3µÁ›k$ÑÔ?ÛLZEÏ„Æä6€ãÄwµJÎ`×Â0ÍV´YÒâ‘â!"Žãvv ÛsˆžGáˆMÎË8ªé®nœ:å˜q(I À¼%ÓÒt8Ö¡š÷ÈAÿ"Lµ±O‡ç/Ž8ÍçoËõ\¬PâÀ–ë™9pÛ€qË%bŽq8ìÕq:o1²·àÌä¬Â.AOÄÑãîÔ †éDT—ºkfœÈn’Í,އ‚'™9u +}£PÌ $­F¢yé«Éy¹¼q§ã³™¥å*iq;hn«›$lŒÏ“ÿA^¯9-!ºl`6ÓyN!„§§'DTIU!.Ìq¾¼šÙáô‰B3ËE „")§5gMYŽÇÓ8fbâÎ&œ’YÎsŒÓé%Í—åòæ¯jñtc¥$\ möfÆÃ8=½ûÙT³Îu¦ÞF3hÙw­¬²æ4§uî%’¦ˆ"šÊº„8ÆéäR«0ŒâüþÕL;„ z˜ÌWg‡! Ñn䆄âû»HÀ>5Df6w§ÂéøB®çoäæéi™Ù]-sÊëƉÇãz}o8„çɦ˜äe1€]ϯª’’b¼¼QŸÿø¯:0%-*i™/õÓ |—Êü½,cÛ¢¥? /þiäìûþõíßÞ®é¾#iU§Pnô¿ePø¦ýñ_.ÐåzÁmBàfªUÜ1lãÂo±…[òW½’ûü2¼©‚ºËÄætU8+[…Q_h¥U·Ür9n;2yw.í\¶<É‚*Ç·–w!g{bÎΩ±Ûq)@Ø«ýÖXOÉ—Mïl{ó²‡öKûX„þÄ-6k½ ÚFùGÚ™£u½9Þj#nVoÄS0B’’€5gÄÃÄÛ‡ôæI1x8b³Ñ‚êl€DÀD¢¦ªn[BÆ™aB½\ðiXÍ9Fz}› àÓ§—ÂÞÃÍT¥DÞÞΗëå0ˆ°$ÑÏ/‡& };¯ýÆ=!”ø'XÖ,ªHĈÃÇ6 pÛd +gSfrgçyÉþ†€ˆóš|~ÖÞ£Ã8ôX膛-)‹èé0ªÚš2 NCÈ¢k*kcª_À’DTc¼—ÖõS7RÌ„C ª6¯ÉŸ©‰”€xi“{¦"D¦ašÆÃ » “/~­î"ëv‹Ñ –«i&œØÑWIfbªÈLH%?²6—WX²›ñ©dS9ŸßçëÕ¹þcä¶Ÿ3“™}{Ÿ‡ÀC`5S" ÀëšÍÌW]¿†]–ÐŒ8DUb $úñ‡Ñ¯0Íä8ôò 7µ¸Ù|êV°Ý®ëår¹œûÕ4 {½l#aÑØi!B¨ÉB—¹4‹ÜÃX‚RÝäýþþŸC«óî«•‘ÃqQDÌ`Mâ–x›ê\Ä7ÿß¹s²HY‘€‘!0O/OŸ¿8ÐÒ_€³Øˆ£©¤å‚HÇ—/Ë|žÏo~Ì–"x KVÉóåŸ>ý¤…zÅÚB…Â)Ù5SÉlÏDLìaUÆa@boé$-ªÇ#;lSQd" ÅÉÁ¡áÐW±"éòö">}þ㥚Gaøâw‹í´"‘ãÎÁÔ´RˆÄÁiqéúîN­Èpœ@rò¹æ5‹Äñàó‘FÓ®\±}!àö¡(†fŸ¶s-3|Œ>ß0_îÓêömßõì±[M¤=PƒßÏ*¶RŽ97[ÞÍt º`8ì¤ñÅȧk]nº¶™Jµ9ˆÝ5Ï[Ùp;¼Éõ,œýû@£‡ÒâýpËk”w±dØù‰{Mb-(ÄvÁеf1E….ÏÞÍÕl³¤Ú¾ã>Ðlkm›%tóÂ÷Ø[ò·¼{kýõŽ©¿ù÷·õm@RÊyóX ´Ž nwÔƒiJyvmµË=o“mEvÉŠí+~´°ïÞâÝ*ÞÖv½Ž[EµõZ74¼ 0·9\×ü×YN£¹m)ê]mÚÛMZñ®Ÿ°T ö] ©–zÜlcG0º)íí>ZÜÝþÕ|Ø*™Áǯ$V§‚Çæþp?ï|j¡dwðhôA–µ{®TfìÃ/»¨vëÔèž#Y ýÝüšº¿Woá­éåå!C‚;ÏkÚ]?*Ý4|]GØpôu7ð@oÅñ¡É@/gº;96 Æ|û}ÿúpÓ‘Õ”pÉ«€r£QÑ>spaìþÅ•ìç¶·>«ptžœ£Ol¨fâÄÜ~Fxøn¸Paœn0f/Vró¡Êø@I"ÅõO¥ŒÀŒ#üíŸFÍ9§ÙîñéÅqÿúVãÏ?ÿBøôé"x̧À)„‘CXæ‹gag‘q:©âŒÛø¤ÙÐ#þòË/f: ãx,”<Óùúî!×®¢MYR’ãé„H9-"2N'SIi€8øØ®ó|¹\‡Ã4NÝ UT ‘žki™Ó*y q` ?~9À>Ï¡;?¶ÿáÇŸ~ü›âi¾ŠÈ0Ž¥&5=¼~õ:Ôq8pr^Àl½ž]¦Eäde´8:S§¼Î’’¿%ÃT&yBš³?ŽÃæp*Ekf‡§OZ©d]Â0Ó1-sZg"îW…„Mt]®ˆ8=½x ú|9_ÏoŸü›ÓËË|1s’ícO›BÎVióT¿uš{骲JNërÃâè,ïÛÜá>µd(ÄÀÁÀL²¿µÇ!É郲ívèâsÙV+x ¯y¤ñøìÌ@ ˆ>Áê?ÊœÓ3N"Ù# ¯ª_~øL.À\—K¥òÚgŽ!f§«ä¤š—5âéé˜)Ĝҷo߈臟þ&gw Ë*i^󲿧§'fF0ÏŠóɽ©fѵR¼ð÷e" IZ¼î7Ó¼\9ñp81" ­ó»™Åa$âiÉ!ÆãÓ³3+IØÐT¬Zs ãÞßÞBŒ‡Ós dd’´æ´¶†ÃÌÎïo9ç‘âx³ùú‡)ŽÇ´^Ó:«˜ˆœžžˆ»iº›¨HJéz½ÆÀ!P&Ãr}7°qzâÿñþ>„øütò=L#c·J€¼Îµ§¼®] ®…a:yŽ Ç¡Øq˜å´˜ !ØÓ矊 ¢p’“æ,, RÜ$›™.W$Ãä‹ÖÌ4gßr# £mU𠍩f+>NÑB@ C€v(Ìç˜Ù'@t½YX¨¦Â§OHè YçÂ}CdM[efº.¹¢Œ ! ò| ÚÁ]äRâàß’ÂþÄPSí1¿Qáw¨–Á\»Ùn5ém×c·Üª:ƒ¿Úó«F”á_{µ²µ¦j­µ 3£©J©_˜ˆ8è¾.k¯Ž…NX÷À•f¹w1#/­ã¿ú•—ёۨz` 6GÛBb*+˜¬ŽáÚƒÇmüpKþtwµjËâ1:eš¸eÃ÷¿FÄ!Ä#jHþiŽË™*—ÜäˆCÇÁ£R9”2e`fÊî!0UM`–s*–oÄÈìÍMZ.%ž‘¨©{Š@ù ˆfƉ ›z1‹ {t</^QdõI@°­Ñ/–Á{%ûN&"y½Ù[¬L—!Hr&¡Š˜Zˆc9#«Ãl””öNköä¼Î¿Qvr¥ªÿ•"Ò¦8Œf@yñã͉€ª’V€b„FrZÓ:ûºö\V"V‡TDÄÀÔÇ´DÔBH€(i50‘’ÿŽ-`̼9\ö­Ï&r'f7¥kSS‘,2_ÏHX¹ÄKµ*šbÉ%”ÜÒO${Ñãô`טªD¨¶÷ÞÐ;HO–œ)Ä`i½ºØ¶šy€;Qî%bfš1ã1¥L‘•tõ&i%“<‹ˆª"æH‰£Ãª‚~K¡ÌU¯!æœ$¯^.ûÁGLDAÒRþ¢Âryï“'Í”9‹d0PI5;¬ß¨Ñ‘¹æ]^»pF#7¼ôf½:özÑÉD €ÊŒFw×õš¶ijÛ*-$A2U4õž­úHè4TUASTõD(tØÏˆÍ7?zî¶Àb“­ *°XB·ÑËêµ¥êÔ3¢Ç2U ºáŽ*æ"â šìA§ Œíý'̬‹¿¥ì¾íQÉ;³®mÆSÍß|Ž¢™å|-¤ÄÁ4ë#ò'1˜d½=O™³®®j4d6ñ=Be] y8Dˆs^M%V¬¼fжªÓOM˜±Õ8fˆâfÙ€Õ¿},ÒTUæè õT”郿Ú'³ïÕÖˆˆ¦Y%µ‹Q³†Ñ+a'Jz¨½Ý8¹ø×ÌTÒåí·ÆQ$ PH‰€rZ¯—³oµZ²ª $'S!f *`¦9a$q¹¼-×÷a<„8¦õjÅŠÅ{2Én»ÎÌåÞG¦èaå)%U]®gæx8=¡»Èåœaö‚¦CoüÔ+ød9Ë–sÊ:_Ï&«ƒ®)-¦Â.+Úd¶¥Œ¹ñŠ9¦ªE±Sš'¢–{^SªÜÅ—wˆƒËÒ‘˜ˆ)XÝd‘Üñ †fA DŒåý¦¬’À¨ß‹ =¢¼»LK$5Fžw<°³²¿Bœ±WboŠ^ö,kzN¨Æ7ˆØq^>úÅ£J×¢[&…W43¥d_Þéã÷%ÄæÕÕrÄúDïŽ^uÈÝŵ#¹7í¸3-/Ð6>H±àRD 3ÄGIf¥´|a{*8nî$ÐYÂvÛÅ B/‰í¥—˜wJüHäœk,OÇ¿Ø ¦áα‹u‡ŽA`¦x‡å4-µU©Ë®~Fj-KS¤lëÙvdŠ{ÌÞ†b»A¿›ÿ±Ýžû嬪¤5wô¹ö¶q6æÓFz3õ†|y2s1ÝE&¢”ײ_#æœETÕLAûKUÐ>ÅKa+nUu-'¼KÅŽct÷ôº<™-I°þ§º0 Æ[œ‹W‹w~¾‡ª ¢‘ÞPx¬ØcUjW!x•"ÝÎN4q[tª¶l ä’}Qié†jFh ¶åÚ;–/"€¹Åb6v’{àˆÏ×ûøR¿¬³Ìõ[w×XÏâk‹áqž=t/xyÅÂ=K¨§qí©yS¤ðkZ^ 3…/ž"Nºc"í&ßXcgÜBïÎio/ܺ¡wYfô;ÿÔ«µËïÝ;oš3‡HQAÜïès™ûz‡é 9åuÁŠ Šõåz}·’kº¹¦•ÚÙý¯‹Tßn2‡­¬ÊÚœçý:Êšp˜8 /LTÅ$•þ’ˆÖùÜ8²^ƒ3‡ež—ׯãá0 #Ü€«ÛxÏ$§œ–eÍó’>}út:~þùg©ß¾} !>¿°³$-ërõo;Œ‡8LSQTíÛÛõt:>?–e¾^ß’hÊrHJ„‡Ã‘8¸¢?—f÷BCË\3 ãtøË/¿ªêé0„@—krWIX×W—"ä,)y‘óòŸ_^Bˆ7HrNË:_v•zvû²&ãñ–Ë›WR†ñøœ–«äÅÍ,$%‘ÌqÕ’—ÇdrÎ) ÀÚöGI«b><}RÇýD pˆU1‘arT6«êuIQñx‚b  êû :„»L›©;áINbÆþáV&b¦a:Z5ø'â0Ò:/ó9ã8œ®óWUy{ó“ÌL‡ ¬ª) ’r™qºIž6¿5çìk7 ÑDØØÏê$¯ÕL3s¤aô«Ï¯ërî½§Ã3cÀLòŠ›*©ñÎ\käowòs]ˆƒšÓ4>=?"އÇáßýÿK¹³ë’ªwD ”(/éׯ_UõtÍLÔô·_éÕÌ„fÇç/̼MF‰c6o¬÷j‰P(un£ŒJ¦º.×â<¥"9§—ñøä üáô Ã02‡_þáÿ6€yÉ™²êš$†"ðïGPfðöÛÏf–³ö•}βŠ–e8%iÛžkQôN¢c”Ã$)å5IDHo?ÿlUµf!Ä@ˆ„Çàˆp¹^/×+A€£j`@‘üíÛ7H)??¿|®z‰2®–Ñb×La˜nõÞþʼ­çoî\êÈLDÁPjØ/Šlf Ÿ~üÛœ×åüêKÅë¡´\Òr ãÄâtBD翨d¢0N…~!9's#´qz ôÌonô¨*Älžè©˜ƒHVqWáüüÓßQ±X³ùòVѪwqà‚Úê2pˆ*’–‹0U²v~)÷§8O® q¦S^ç¼÷ñðÄŒ°WªþŽŒ~Gh¶ïžÂÌ3ñw?}ÿœ°çaÀ{î4Çÿ0Ȫ•W7a„qvÊÅï²Éc 717~Dš¸m>¬ËyÄ»YòÁÞ; øýifÛQä­Û²|hváÖ÷_];|§Á¾“¤ãö<¬S©7=Ü-õܾ?m½+;eK—¢÷‘óB'ÒøHõøÐ;ðáòlBûPékðº>¶(ëFýÞÅ>(öoµ7¸q¼|y7ŽC%q@UL–:Þ)°S…ßÃŽC„7–7 }ët[îãÃ'Ñ„ÕíÙWlÐzw#ua~VeMõõPŸjûÇÚ¯;<á‘×ñæô`U*PõêM»IIð‘Ö{ë:€­™:|Àþ¨=C_àöL·wÚ ÇÍ—±ÿl’ –hö^÷y®;ƒŠÛíôa!ü§`>^²Ç?þ[ˆ“°Z¬m .j¦ëÒ÷ÅÕÌÉ;¶HfÒ.”ã°dåæÍIËՃ߼"h•u›¬ s£‡•Ô·ª@°M ³3o6ʥͫ-W Â\jU¹Yh ¯OŸ¼ÀœŽOÇçÏkN/ ɇà|ngy…†—/OÃáÉG?ý¤*òíõu]ÓOøcC‡8縇çñð$i–œ^ž¦â(è®oˆ†€øŸÿ›gÉ)¯ó8Nã8å¼jNVZ´9­v8>« ­3ŽÓIòº\ߥŠúUõ?ýØnûtx‘ëåŒHŸúÓ:_–냛·y¨^!ËôÊŸ0»±¤@S3"¢u>—jÓëåu:>ÿá_ükSKËõöAøô”Ã`¦óùÀ Ù¤þÈ0nÉ!ä¬Î]BÄB€F&b©œiõôé3s|ûúO†" N?Ü/èNœë|.L×½»^í:Ñ ‰Íìúö›jQ@NkZ—ÏŸ?µ Ü2¿‘I¶ {X&ÇÁjꂤ,"6D&¢q:ØR{n"†~é"rˆÈ9õ–¢Z¶µÎŒC˜ðtúü@º¾ÿ&É .ÜÍ ãx¼Ê2#sqëßL¥Wô÷wzùbfôö›ßï}Ó:çëy].€øÃÿŽ9|þñ9ç_ýÿÅŸþvYæùzÆqF§’…ñèØ€ªÌçWB0Dˆ‘‰cÃùr9_®™ ×ùìbLj9 д@ç„ 9™­þ’ºMã_þñÿ£B¹ épÑœR~ûíòþ›o¯ï |ùñ§a:ž|Oi¯×B!ޱó"´.H4ƒ‡™Pˆa˜òrõÈ{|šÙ×o¯"ëŸþæþ²ˆèŸÿò—0œNŸ~Hó%-QËYþðãôàæÿçßÿ»qøá‡b ³Íééét:ýkþå—_Típú<ŽÓùò˜i#" ã!‡a:ŠÏé%«ä¼\”ésG8:Bîâº\,ÄÉYffš×"ÝIóE$böÕå‰:Wž¿h÷~jΪsN«¤Õ‚ókÂ-‰6©dI3 †a*SIS¸¾ÿ¦òçŽi’YUÜó*’™/QÇ_ÿüšÊ°à+{ùœJJóƒJs9«‰½Ép¹ré¶I§‰Ì«ÿ‰¤eÎiÇDóýÁ Ã_ÕÑÝV¡øÏéÐꀿócöý?øøo~¯²º­nÐç-8­ïZ;Sç]tËéÛƒàM‡¿ió‹šÜ$lAµïì!ªwã­RÉï;›:ÚçÌטŽ`ú¡¶=(©°ŽhóhÅéGõšuªã.Æl/•ícï{å°íd}¿ö°«øØ}×®<”£Ûîqg#fð=×ðUæ¶`>¨>7»¶¸ˆ?zõ¬ºHã­îof÷ í¡Ù[ LØýÍO»Ì۴Θa ÍÓ:kÔò¦¸…G þÚ¥NÖ*t™Þk#zÞ:þ.Þ³)Ëž ïqcTl. ¶›ïîs1{=‚YÿïÖZ›–Šn»ß½$eGø3µ.?µsKØ©¿nÄÞ73^Øwm7Ëï 3îÜüê½uíh‰§7@‚¡ae¯@çŒÐúÇ ·Ç½!6 ¾!±›šy›Z‚; :Û¢ƒýÿ*iÙ/¬$1ÝÆï>jÓï‘13¸ñ‰xp‚àCô¨Ÿ÷)Æ’ùÕLïêzrZ½u6Áλ‹¸oÅÀ@r&Âa:Þk!Ź­ç‹³¯ú»ÙÍo]#±Ù+øí@DäB¾árß^_Ç! 1OYS°u9»/çüíëoˆ8â4LÇóûÛ/?ÿSŒE…hL0Ä0_^ûkdãôëõœó*9¹Ìàýý=åLô H!ަöööÊ5 a¥!p»Ë|6«¹Øpöb"/ër¹\}2q<ÙÓÈc$\—K¥JÛÝlR„ã`ï©l5Ýl¤Ãiýõ/Ê!0‡œVS]®ïˆøôòSÎëÛoÿÂâ@Ì-ôÙŸKZ®Í+¹¡FÌDd!ºv0‚ª"Àõí·F¶HI˜9ÆÀaà8ú47§Ìæó+¼—•9zÂÀÃèÀ…Áƒ¹Ûj‰ãAEÒ:ƒyö!©j`:Œ‘в¤i˜ÑÙF­vö}T³¥$q†8dI*ùr>û·À»1d5öPӜӚóêÖª3av‡ñ´^}W¼^sòàFƒGºUƃÇiÙõý+Rä|d;ŒG q¹¼™–‘JòD³bÕØ³ˆ dÓñ×Ë›³Aõòú+"ŽÇçÑÆH'/df×·o×÷o‡—/£¤‚™‰HŒcü<ùÎïÌÆó·?ƒÁñåKÆóÿû…0‹?nßå*È4 ¡¤C¬Ë{éä³8Ò3" ‡gvn:ƒ k^—ÅýâpàCiÀÛ_þ¾1­zÍ8DW¦i^ @ê&¥fš HN¾S½¿}[æåp< ÃàœÏJ4K­Wp§oD6\çëºÎ‡ãSXê"`'Uñ¬PÿÀãKÎmøWØÂ0‡“w±ûC¡E5ÇÔ™P¦½Y…[j¡}G÷ &¶ýHãþòɤø{Mž}¯}ˆQ|e«Lq6t¡çÝ ]£ÍÈ«%gì3™3Ý7)ðNvmf¼ekT+dDPÕuY«Æï\d1ÌÂM(Yöb3¥;âmšàǦøñ,Q­X[õdf NÚÎ`"ÆÚñTmg"êöÊ¢#Îo·*ì³ì7Y.¨á¦UuªG³Çk^5Ÿý†UØ]᮪í3_°‹Î¾™ #¡4j5ÝVß7ÈHG‹}à¡·Ít Çz{+""6ÕÎס¸Š‘‹ú\jľEÙ-þÂßÛÚRÊp³[á.’»"˜‹öŒ™ÙL%‹_Ô¦ás+¨”mM¤`ÒÅQn%~׌—ÜõÝàùþíöC—î×å>î¼>ý0ª…Œ6ôÜUÇ[ŸÐ–ëãQ1‚iÕ×ÝåtVDïÞ>»‰ÄÚ#u=€±‹loÌñÎ=Ï ¤ÑÖÃÍÄnzº™óf|XÇu&j;ü¡j¾éV·¾7‡ëÙ”¾§5u™ŠäœTØhçt{¯6s{jž1IÐ3zÍzÏýWlw U#§$ºf~°åµ@º†Üt„ZVA}Ñù¶™êæžÞ¯H½;יּ3ìâì[j² [ëÕz>d9Ã÷½fÁjÚq.ƒÅn4eÝk †OA¯ Ù+2‚ÅÀÌÜdŽ@£³× RâgD×/DR÷©àÉ`f¶. 3;1®MÌ”’cÉÜ©¹J½ IDAT?yåÑ?MÑÔ%’·Ï•‰b(møuž‰œî¯®S›Ý6%«å,f~L¬H‡‘¼×ïBwÏâÜBWr:>°´Ì²ÌÞ¬»øýíõ±Š¬:{êK번Èá°»ÓY$¥š`ƒÍËR“ nÖYP1‰Â¼!!,Ë‘b`&Hi- IÇ1ÆXD¾¸_Ç®ëš²‚!.óŒˆ9gU#^ q¢O4E²¨-ëšDü6ºòJD @ÕˆÔßQ‘”Õ ‰3†8¸Â7ŽSD@ÜtXûžz0{ËëÉJ ìß §ˆÉ)­sù(‚¡[.¸•]N‹‹¸‹Ô„S0±0LÒê|Ý[kE½I˵’{knNÙ ÌDÒt|)½ÑrÇfѧc¿c²çÜnu6Ì^Ëïòvj(„aB$Ékµ˜ñÊYl]mYó0 1†¸µéë² ²7L)‹’ÛsX×ÅÌÖ,`³"À©JÎ2¯¹ á|ž[yœ†öÉžc€5`Œ!0Íkƒãa@$·ðOk»`©¡Æ„"ÉiñªÊD›JLEÕD4eÉ¢DDë2›Áš ‘'0J裊ªåœÔVO.8""ÎKR5ÿ„b²#E>¦º¦´Ça]ô2bá;ìu¥öB$ljæev/3Ëi&fDœ×ùJ€Ì y7æ'‡µÍŒ|0AL!P5»r[Î8L*bõøÚׯbªÄ±ÀGHi¹l}FçdVt·"Ç—q¹¼§´L‡cûjq<¨l±ˆ!ŽÆQ%›AïôQ‚ϰ$]˜Z _eµŒa˜8ÄëëR7èú÷LEm^Ò0L! D #ÁëåJ9Žž?gT²X}ÏùrQÓ5•ÇÍDÑ ®ó«°¯ €lftšJ‘Jœ’¨Ù²Î€PÍ9å,9«ÇˆÆÀˆ@8« ¨©Y%ÄqH…9¥&*«Ö@“ûVõ €ã4Pa”„á€óå­E±Çq2³\cY™#£-å›–jCÒJÕßÇ̘×ÛâäÒÀ’¬èžˆ(†0N².9U51 †8ñr}G"O¨™Â€BðYœDÝb†¸Ö>@!VÐBMu˜žÂ8½ÿög0éG×ÈܬN¼(×¼†0„qZίªÚx[â˜×«g­l(>V“âÄnú¨å¥ÀÛq[…UÂ]‘ˆ¿§üµŽ6üÐ n“¹ a›$ï±Ëzk|önN_Ú2¬Jµ\vŠ›Q‹í“=a£qScçß³(俢π¸·“«Êö¢o5‡ËKqáºZ ÞhÊ[¦8q02VD²œÖö[†ñ@Ô®«Q´ÍG™#yH&V 9š6숈ïxU5œŒ ˜Ù½ÊfÉ•m¨Dº”[W6‘~힨f”Äw¼P@õäqR2¢Æ..ÀÔ›7W—E3cf³­šB$fV@C"vO½ÙŽÝŦ-Qf47óël3}¨TÓD=¨ÜPÈPÝs´d¬ì÷Ÿ™ëûÜï ÉPÕSnv,ôº‘ƒgÜ™z=î”° ¢¹BRhÓ!Üç^6ç‹Îá†4¶…)xDN¥±¨yèž:è&qQ‡¡ª™ùÞ\j£2Îîõ´›°Q,ò ÛY"X¹ZѪ¯ÝT7Ès9SuG|ÀÂ,Л*Rµ×ï´ ÑÄž@îšlÉfJ›œ›r"%/üa1µ-GЭ{7QD×.8RÜf%ˆ„¨¥3§Êqz+UJ uy…Ôâ;IÑn&ú¯¶Sw™Þ ‚®¸Çß™ùÔØ•FZc1¹Œ»ú„ì´we‹Õ{——õo[DÅ•·4ÑùZSˆu:œ½÷TmÕ>`u—¦i¶Å«z¦¼nÃ.Ô¥¤þ!~”×ó¾3t =ñp]Î! n ¾y×Þ!¬ñøErNKˆCC} µí}ç·Wa:»žl‡U¦ùb9Ífº®âuˆ4Œ’–uY×ežÇ0 ËÛ;€ÅñX´ DTëf¢9¤`š]ˆóùÕrŠÓIUÒrU'Çã†1Äéë×_Ïïï~y-eB 3HY‚dÓ,"¢v¾\ ÔùŸŽCÎùüv& ÿò?ûWiÍYôÆÃÓy¹¦4þÃßùöMDÿçÿö¿"GŽc¦ÿò¿ûæóÛõüUrYó²H^ÓrFÀOøÏÂ0…8¨æu~W‘ËÛ7Ӝי˜‰Ømɮ˯ˆøãO? ÃPØJ*¦:_¾¹CX-âàÚäÆ6òwÔ9ÊD4Œ'b& šW0}ûå `Óá¸Îç8N/?þIU~û‡„fpzú4^$¯Mœ°\Ï’×—ÿ…ªäZAÔž5ç´ä40øÃ¿ ¡‰˜™Cvy]Dòùòóx8þðåGŸ“åt•”NǦÙ{ˆãáé»|û5 S&7,Q9™ÿaµUr)®º;©ä•ãè ìˆèÃÚ¦i‘óßÿû§—ž¿ü$iUÉËõ}]®~ãÊ Œ8„ÑÈ.—Ëׯ_Çi<%`oASùõ—?gÑÓa(g$‚pˆ! Õô9p¦ãs›N…8ÔpmñIU®o¿­kZÖF‹õ@åZ5‹¤ôíì¸äœÒ<žyoÁ ï¿ýŒè[¿¾þöëx8ŽO[XtíZ¨[“0Ù~HáoÜåõ—&|ì%]×u¾^§Ãq:‰#1ÇщcïÍÁÀG!i¾"ÇÑOAS1‘éôâÿiÍéøù'(/Vr‘®ó²Ìž{Žq˜8Æ&o¿ý#CÆ8ßßÞÖ”D%­Ëáxj—†‰ˆ‡ÃIDÖùßþÿœ½Ù’lK’¦¥ªf¶wˆ½Ï”SUQ=IµÝ"pÁußñ<\ò }Å[@‹ôcÀ"Ì 4MfeÊ<ÃÞ1¹ûZf¦ª\¨™-[±³¤I©:•Ç#b f¦ªÿÿýaB(öa> ãáùÓJ1§z÷ð±É@»×jUY½wD˜À|¸w!ˆä†ÚÃ\$BK&±/Zœ}&«® ¥½Çì½waÌiɉ—´öúX^^K~^ë˜U™8ªjŠ ’[/gÂdˆ:ŸÖkŽ×¼^ãõ<ßD¤õò"9Ùtìs¶@¡írùëõU8‡;K”,å¤#çý0)'Nëf{‡6IˆÆO0¶…Á SNk\^Šê1­×6aMqçã0Í> Ã|²·{<~P•´^ ×úçÇÌ‚èôV(Ð òjK9gò@éçbΦ¶êlìúwÛÞõ™L¯Ç^ízë[Å#°ê—YU ߀д+Üê9{N{¯úÙ» p— Ö£Ëz÷¼öCéw=ÒÍŒ±ù1t ¢/‡­ƒQ,CJæÄÁá,’M-f¸–¡gÔ¥Îñ]óö¬K”3礜3;f  ´_éNw²vØ¥ w8øÚWAEÖÆ¯ÚSÒ$õÍù¢»ŒõrŒ/£Ç¦>é¡[Ý”º×Ú÷ÜDÈÚëàµ#ÚëVà†»ëˆ\5>q÷ ÜÛEµú£êñ¶?Mô62°Ük­ñ½þ¾`;zÁ:Ôb'ƒr{ÂÀû°Ígï  ¬¹\PÀ}‡7È[mò¾J ÿ-­P_êm oäXúVç†AÜɵ´½£U…oYØ7¨Zžê­ú©sÅ:Pª`mÙ×Ðÿ§/?{ jg¯Ðí™mþ»5Zêk]¥N«Ú5v© ýúrË,ì¥7’øîÒ­Ýóƒ¤õ ­£Žè—àob5ûlÑ> ÔŒ=o™Ì-ÙþW{«ÛÎýôΣ«ïÍBç×Ú§cêÛŒO/œ…ùz½¹ßþF8Yò@³R˜{Ñ’. ¨®2(ý†C§08Gæ,i€ñpOξÉñz7Lv6„OÝý0"Àag 3øÿúüûßýù_ýëÿåøþßüï¿ü³¿$rœÖ7é9¦Ãiþ6^Ïq¹HNÂy¹¼”Ñ8Çåâ|8~øVU¯Ï?/ËúôüôðáãÃá4 ƒæxº»CÄW>Ì\˜wÌò‡¿ý^8­Ë9„0Ïû„—*ŸÃ”òeI*üôÓßÀñ8r|ùüÇgFçÂr~.½xœï>üãÿè?Fr/ŸþàÇñà¿1ÿ¬i=~øíÿÆ9=þð[üÅßû'¯/Ÿþ¨Ì)­Î~œÏ¯//OŸÿê?ügþþéåùg¾¼>æ¸þò/ÿ½¸œü="ž>~÷ó÷ÿú÷ÿ×ÿhyO¢Â׳ªÃÜÂÊÛ4>?|Æi˜ŽŸÿøÛóãËòª*>­ úðíoTÔ LaœÄНåœãbt%;̧@Z^Ÿ Ë•L1®k©ͤoÎ}a–ÒºrÞÉ´^Ï?VÕ`ï…÷~@Àõü¬ Ì‰©xVA))³ÏWWN&W•ëùÉVfÑóõ%¦üñáŽÌ>¡<?"‚ZV`N×ËÓOvàæ›×=FvlF|~þ”2·ÝÇ`|¦ô3©Åùá<9ºÿðUM0á|~ü)sTDK!§œ#9_¸%Ÿ/1 ®e 0Fûk¡æ$#yˆœÕõò,," £íi]XxŒ¶¾!˜~4ÐG¼¾®ç—ùøˆËåYr²bûõõæ)XEë…AÙ‡Á¹!¿¾XE¥’»î¦®ËKÝ„_/—eYó`S7ç||ÉDä=!Â0¸aÆù$ãl¸¹b'³¼3¤×¶VuŒs&ça˜Ã|L뢠ÎyrŽ(\É„á—Ö>Væåõ±m,n˜@Œ ©æÉÙ|è5ä+ ÔÚ×. Ûì Q8§õj„0í_áÃýGá´^^ñxÿuŽ×´\¨æ(xÖë«idTôî›_)sN«ˆÄëk¼^rŒX`ª¹K;¦Z~:>ˆdÉ)Œsi97všý,7L–ô5מat“p–œMû#9ªŠß=p-\æÖØ»mX½uvÕ@;XïÓÄßÊ»vØš¨õeV„Ò¼þbY½ƒwÓëß}öiX%ÁCD”° Izgð.E}+2D¨¯÷udóøöçK .3ÍíÛt/kuù^Ï Û¢I‰[¤p™˜ÞÔêÌŒ%Y‚] ¥ ŸT³e6èö>Û©1b›ì¾?LîŠòž´‚\{×ó®à®ÿç¦P0iº«vÚjÜ´ˆõÓzÙuµü7ûDg3ß 'Üæ»b‹¾ä<éý¸û‡_¢¼¥ Mi9v~˜ÊV$üòøGç‘».ëeY§à½w}Ê<Ï"’rVÑ˲Z òažæy²]¢ä‚qV•‚xëTCæa:^ί×Ë«¥Ö˜LڎˇãQDÌÍ2RZ -Ù¤*¶”L‡{çÃ÷¿ÿkïÝ< úr^‡ùt:•°óHn±Ì0 ºý`™3§è| ï%gæì¼·ë€Î=ÿü·Î‡qÇ•sÅï #¢  /ê/nn!Û]{k@-y[eÀTQl¬Ñ>c½ÏHÞo/S#ýTu;¹wvæ€2ïÃ8¨Âk,Z¥hÅJ»áÈSö~Ã%!ï­Q²6¥U;ñÒmû¦94L&Vƒˆ±)0;ÓIÑùf&}/E jê4Ôàx$‡*¨D@ÜÛu©1u .õA,.{m8'D"ò¦Ò„.Œ И2误[–ªÇÂâC5àºõ‚Q±Ýˆ#·¶ ‘ã2ÇÔ )׎Uo]{ a`ßÔ嵆–½•j[¤7‡Î&îÂ}†õ.¹fwWuCòGÍ[2Ù¹&"í°vj:ÀÖl(pÀ®UÌîï Ç蟴sJtXgUjcÑÚYÚZ=èÊÞÚMß0º¶ Öêx,vC¸åi5//MùþëNôÖ«Ú%·õ%­¾ûYþ]À†îs3æÙÐ{4Ú»Å&UÕξj"D çän »£°’Kƒ)Õ¥s¶÷Ÿ.ëá6–«…¦U¥+R+‘\kAwƒ®íoî&Û`ÉQʬdg͆+Rµ@·Û'BTY„PÈB!‘"©*ªî©ýߵݨü #ÕMÅjÂ¥^ÆŒ½ššHYËh¯X£U—k½¤BËcI¢RMpUXnÂü=y»2Ûø¤|åÍ;¨{ð^-™ß»õ{}0ý^ZÔÂÄý0Ûm'»0Nµ4´øM2kHZ °šíX#ÏX·¼{PQ‹,·˜É0ÞÙ¹À6ä׺Éqia`q¹ ý˜qãœãryùTÎhºé¬}¬œ ·m•™ˆfæ°= ÌZgÈ&ñW«Ò-4¼Ž%rŽö«ö} Èb°P¿:ÁlG"§ 9­˜ ©x$šxRL, Óá.®×´.YXk€7r\¡"ïÒ’( > ãáþ«§~yùD›U¿u¸n‡nËèRÉÕ Ýc!µ`ByL ¨{ëBžââ¼÷áØîºFç=ÔoæˆÎŠ!õŠ×Õcã–åšsžÆ±Ùƒ´è, TÓzY¯g[(ßÌw?ÿñ·Ä¼^^ª¤åáøðµý>Ëù)-gûõ^__rÎ_}õU;MûéhÊ|úp÷Õ/S¼>ÿô}ƒNÙs©Um6Î'U‰×{#J£#Œº¯¢œÌw/"*ÌÂÙH öHpŽ*ìü°‰NɉdåLä,××Ì6ÆøÈ™ËØ²þVäœY¿EØ8v  žŠZ×HUsNDÎú±&¨!rI‹: ã\¥™JäØÈ’Øœ#Un'BNki›§Ì=åÀyï\H)ÝÆ‹Þ†äœT×¶–ÕRf"rÕÍBÎ!ðl#LëÕ®§½ !Yû(Œ3‘Ëiµž¿TÉ#5:¶|iVG–˜ê¼*¤¸"BˆÛÁNI0L‡7¬¾BÀŽ ÌÌ<˜•B¤ÕÃÆµ1} "2çe}PxGÚS$qNœ":êüÇlºØ~óno"ÔŒGÞô¦ž`‘ee¡xç‚÷a˜È;Æ/—”y˜Í*’ã%øÑ…¡Ø®rfÎã8ŸT›0¤‘FDØ;ççƒ5/¯ŸT! #‘Nˆä|ñ€Ú¿h€ÖþìWµHŠòjÉpÁ­}Ü@BH¯¹: çY-ÈDœOËŶ0RN©¼œ#DU4t8¿É™"ef(ù4„äXSNÉ„uÂÊ,FZOqA$“ •²X•ÓÊÉŒ—ӊ䎪֫8ÇÙ[á˜S”C"GàR\¼t<€Èåù³Æ0Ìu¸+Ö„¯WFJ³nkº˜ÈMûÆÌ.X¯UPaÍÉž)©ˆê®ùb' _²X‘TQlœÕPe£Â]8V"Qد&Ñήš@Mm›býä^î]¼Tk<£ ídaB²:Õ.ÃŽ-QíÖ.a»‰ï@°gÞëΚ½Ï'C„ªR±µ„*7nË_î…*Í` E"Rg459‘#ÒwcÌ!TWHN8ç´ÚZç©Vï&­¦ægF…³KZëµFíÊnkÒ´ÛÒ’í’ê†o.¿«Ë-C‡¤À‚„–´Ç½•ewˆÑ.H}k3¶;ú·~AÛ­ªUi‚¸Çð1®dÇ·ÜGvøÒ›|Êv•D…™[#«×òX¡³‰Â:eÒƒªR¡rµ‘@± ‹§³çªy[A@´äÀw&÷ò¥&×|Ǧ°5¦pû¶íBkãÃ&³Ú)Ýo]ÕØ4¯XÿÞæ·ÚÚÞåïu7Å[ È­f­az}?¾Õ8”@oÕ[AüÍó‡¸=Â%ñ­¹¨¤ЛϦڮ´”ì²#p÷|i/ yÓ7ÆN-¢nT©Ë«l?EÉÛ”×ytÎVQ$׌aÐx=ÃSßÈŽ°ÜèrpÜd_¢[N†|I?Ò+ÅÒ½ßR ËžTN¥¸ËñØú®x£ø+™_D= ¸LÞ˜ÜÊü¥)óßF}êMç¿ïÓÚûøv(ó]mØÍ÷2AM¹ã_t~8Ü¥ªËËç×õúJäɹÁæÐéxïüpyù €œ¢aSü8ó).çëËcwAɹD×—OäÂ|÷¬—gŽñeùc³â:ç‡ù„ˆëåE%sZ€ˆÈ!¡f‘Ìy}üáw?ýõ¿w~˜Â8›éØâë$çÜnìcÍÿå¾ IDAT§‡ãƒãœ`˜D~½¾6_§µ¤Â|Lë5WGs‚ñpoo"Îw_™ðׇчÑBòÆùd ™Fa°&Ò0ÆÃÝñáëåü¼^^Êjpÿ0` :°F–É"à8ß ãÑþŸ?ü.ÇõŸü³ÿd9?}úþ_«êáþëÇ~ÿã_ÿŸvvƹIî>ÔÑiñE¤õj‚eFNi].•s4®ëö ‹ ç,9šA^$Ç5Ûb‹ˆ¯O?•æ:¹ãý×1^^Çù0Ÿ>\ž>­ËyvH¶I—ÑWíïeëvZW%.KŒEf¼ ÞNÊHΉÀó¾Ùè <€ª$ë´$çÃ0ŸDäåùÉ{uŽj’Cp>¬ËÚNyݶÉ.ì;-500›êëÁVæ1ŒÃ8Œ“Uóö?öê<==‹( BLçëÞ‘ÑG/×ë²,Þ¹=0®kBBO„ˆq=û0ãa¹^–ëešfB¼žÀyïCÈqÄlBù½ÕP7Rò²žk?™Žw÷eeÙЃ¸.gõÞ£óWtþððÜÉùÀ2"욯×W`€Êòú¸!¯\ˆ/i]×uý±ýÃà„u‰Ùî©mâÞóñ׳E0"â0kg-I±Aì! ÆcYÎ+ §4ÖTÓz…Úhña<¿¾.ËÕ;‡ˆÞ Ý£”ˆœ ÂÌœ€ÞëõE™ þ¯¯€ÆRbÕèr ãÁùR^–ËÞÉPoVáx(×Ùº&Tjb–b=D VLK:æ9Ça.¯Oq½\žaÉI8ó©iv8-6SÑÍÂìÂ0iÕ¿Ü=|¬OqïR^[úãz‘œ‡yà ´ØDœï¿RΜ“äÌÊèÃ` <¢.׳#7Œ³Í l»Ê9æÇéàÂØž†é U€3Ÿ>§œ¢ª( 9ïÃÀ95-KJ̬œFÄi>A—D‹HèÈ…Ñ8«Êy8Ü™[º€£ ëã"Æõʅ¡UN±–õ€SD2°ƒÃ. FêëiB{½öȯbtèÙH¸?õìmòMÙNu3Ъÿ¦/NÕ·˜ø"« Àj”m›KóWëžý†ªRqQýJ;×ë—C춃Øþ“;ÝÂ>ûª‰tÿìOÕ½Ë×ì}[Þaý´JYÿHo:LÙK¼«ÅG·„ÀZ¼6hk+øX· ¬#`•ò7ëºH·Ó;¨Ôc'ö„N”~cè¾µV©µ¸£qnÔ+ÔR÷"à-Ÿ¬öv òþàÆÚÑ%½ïìÝÙwâ«ÞÜ»åµÝzqzïžûQÿ´5½e;lQãˆojÙw¬Äv S¢ºävÞè X7®ý@ª-îûö{â–qÒ¶¤ó8á{ÞrІÍë"‚bïEP¬Fòêè]Iz뎨ŸÔÒýÚBHB ‡C`i®·i+XuW„tÏü>ÛO÷²¯N'ˆ»Z¥o7´»¹áϺůõq® un€ 3Pí µÒ!¬göÈoºÇ®!}pL¿ÄIsŒ¢áM”cÖ€»Š÷GߪQº¸TÕw|­Ë‚]FÍmÝÖT|z«yiùí_tûô>–­]ß%&ýËÿæ¿lÃ94Îuçe6-,"î>ºaxúñû抳Øp¸N¬jƒÓ¥‹{ñxÿ•ª<ú#ícåM ΀$œÈùñpW6óœrŠ&xÃáøüóTd˜H..—O¡i9;çÉuì0Ÿü8-/ªB†ö_¯Îûa:6‰?tú½v¿]‘ˆãBäÂt0÷=:O䬵7Ù"úŒ÷ãÂÐj ;}ˆpÃtŒË9¯W¤-‹#Œ‡ò¨¶ìn»& a´C+ç´œŸÈù_þå¿»^^®¯ÆÙÉqåœJÂ.¯H®(˜â2ŽóñÃx¸w‡ûoÆÃ½äUrþÛó?/—k­0TD$›òØÇ–Š÷òùi½Þõ "7îrJÿ÷ÿñ?¹ª6nÏë4xDX›…Çà!¿Æ|]â4†i Ì[öš2³¦@ä¦ù ª?ú;‡=Ãr§´ Ž€Yœs¿úõŸÅå²\_Â0…a6_Ñ;‡H——Ïmf?N‡‚0ŒkZ×qžCím¶ÉŠ©*Å̓ndysXƒÍ¾Ù¾xy}AÄ»‡¯rŽ9®> [ùÆr"TàoNŠ[ñä’ç´´Vêåå³ k­z³51§˜ó:NG&ád4/á|ÿõ¯S\_F„û_õ‹¦äÄ9 ó±õê¾Q{C?ÿøûq>M‡‡0NÃ8çœDxy}TÑÂC—s|ùü“ÖHKçY™ešæ0 ½æüòú™œ§#’›—×§çÏ?µ5 ®q+>æÑ·]s‰ÙšöSÌá3k2èÎ虳¬1Ïó<Ï3¹€ä¬>³=û?ü¼3®Þ8×u½\.!¸ài;G(¬13Ëé8æ,çk$ç~õë?Μã8ŸÆù´®—W[*9EŽ÷6â@òþ8TOüÌYElÌ ˆÊüüóÆÃ©¸Î™Çù†)§¸R²1‡*Ç0†Ãqºãr¶PCU]¯g ƒ,]t[rÃà‡©µÄŠr ´¬¢)6V_áw£Ûfíˆy½‰?ýTñ4ïßêÎìw¶„†>›¾OS°ÿ’·Å¶9”¡l5°åœÖÃÝÇùôq½<Çå<Ìw¦ÃßêÛÁöf#­ZGÝi«á戭¨w›üêæÇÔ/ÒNw _ÝVpCèÚ²å¨R®`?xÓ÷|¿©S«÷©<_‚oiÒø%Áqs…ã-r¨þD6Âo;ÕEªM›hªyÅÞW©×Tí÷1Z}Îæ7ïeðÚ$ùöß;©øžÑV¹Ú)¤U¤Üèݘ[}ß:ôûIí1púe‡­þØTÀºG—íÛ¡ºK ¬.]Ý6ï–òÝÀ Z=U›U´'`½?òÒþhÚÏ“ôÆ„ðFuþx¬­¼ögj:ãmņ;œ×—Yˆ_`ªíß™nÂÚÒ·?»’p#ãIgbAÝZ…þ%¿t?•½;ä?~ù¥Ô]n;öÿEÊ«û¶=p®ò½ÅøéNy‹…š|s%›¬¡:ÞVâF ¸½á[N;ÃxOxŸ=YCûö&¸mbu!ˆ­þWØýëŒ÷æ£Ú-éàpÕAAÕA×½ª}©w\ô5?”¡~÷c+I±±ceV ŽÖ‚ªÔyô•XHií ܧ±¾s{jgîñ`›Å÷_ÿgäü0ß@N«Š˜ Kœœ¾C"αVÍZ?׳];’¨Ø„‰ Ê¥ºs©—$kç8%áÔ"V’óãtP€õòb_l€TKjV;A+ÜógœãzyVΫdÎiu!Xö7¢³éN?a®O|9˜˜Ò5-—‚„6×¼Y†AmB¦çhóãÁ ãzy±³mµ!º0Uu}}*vdaÎ) 9o%©ó­ç'Ø’5vGž0Îaœ–×g5gŒ©¥MnŽ8L';¸¥¸äX&+>@¥o,äü8Ÿt½¼šôàîã/ï¿þe¼^R\T’ª¬×³¯ç—–ó9LGN©äY«¢sˆÎ ¯ÏŸsŒ¾ùE3²‰HZ×åúZŽi,)óÃÝÉ9wüð ãåégË€0NÂ9Åhš«È9Ux}þœâúðñkD ã$"¯?y\ˆ:W17¢*O1¥yÎûãé^U_ž>‰êšøt<O'CQXv¹„S¼ªH\¯8ÎG¨ê{@â¸æ}F€Îñg’s\Œ8§år=?VV²"àË˨ŽãLÞ==>¶>×ñ81KNvLFçB˜1çØÚÚÌ‚ˆ‡ãIì¹È)§4Îs›Ž€âr6Ç‹ª\^>÷R¡0LTæÍÀ)ŠŠƒ2Çhñ=Ø%#"ĸTW „q.NvÕœ¢õ6r\R\lÒܬ/ˆ¸\^Èûa?%­1ÍÉ!b\®µÈÓõü¦9ŒG£fú0ºÒrÕy§bVvΪ.Ë8´]Ÿsn^gæ¤"Ã8‹ŠÁKNEŽ+R|8DEÂí<" ù>VMãê=¹`ïu¦zÀëù¥Ê}Ѓå_އ»éx}}Œ×WrB[EE8.@ÌïÀ\Òê}D€œavaÔÒìÔÒï5SJ›ÞâìU}‘€¾?Ê( ODÎZj¶;¢*ÕƒW§/ÝÍgº&ê®P€c{ð·OVîNݪäœídÂ)U5åzÛD.©ŠÔ©K©Ï 1 tÏ%"Ù/¡%óôl[¬_Æf:äìU`³ZÒË—RT¬òsuÌcf(ÎÑ9¯è ^÷£¸ ªî 4ûÃD2ç(’Ò¢¯Þ•RöÍö¾H75÷Ážõ½;Øš>„œ³5¹É"Љ<8)dÑŠØ0-™á JŽ51Wc­ÓºBË»Ùx~ª­«£UÆiëegÚs¶`×FSçœ!™;”ˆ¼ºŠªñÕÛE›ß"^÷(sPu“B ê[³rcNïuÆ_šL¤òæÖoûJ©b{^DÓ£bß#RÀvJ+õqã=×)\1rëQ­MgùÞ4ÓfHµr  {“Ò[ mál¦m^nO£€lÛíVfk ÿ²Àm!‹«EÚð*¸ ˆPJ½Ò?½D¤J–O©dBÜ*+G%ðK‰Ñ‘ó*¹A´H»ƒ Xàd¸Í .ãfÙ¼)‘7bI5Ô“ýØpjEáÆÒ®€˜–Wõ¶Ecw½ÒÔ´™háÓÖPgo:3Û”•kyÉ›´Õž"ä(å½ÍBñ†€hû`‹#¨§‡ÒÒ¬™*åUÀµ“sÁ¨4ö^ÍïÚ±¼²Éê„x+î±_fzõl_xrÑq±ã˜Ó—Ô(""¯/FŽ.–Žâ­öÃ臣5ˆe›²–R:Œs-r%.×8iµf%( 5Üs\9§XUÍ¥¯a˜Ý0.¯*%½ny}´PSEá¼"97Œvôó~paŒË¥õí1ÛV3Øš^°dE¬ÉÔn˜“J¡á伪ŠIlwªÊõ+&`6,!©9Ê…cŠ †©e²+gs¶‘óÎK/2pQ5ªgDÚ€ašל֜VrŽœz›-"¦¸-‚‚.çg“i‰rŽK“Z•-ÅûþèS©͙kñ°™3‹iöÌYœãªÂçF΃åt½¼”,•úZÙÏBrV²Û2Z>™-P—ÔpnMÛ­(J’ÓÊ)ùaD@ïœzöÞ›ºRE!ïÀ qM¯š«ÂPUm·“l½ D-Ix"ìh€sJñ.­ÂÆhFN«ImËÒÜÞk‘ yWÄœ¤n–å+Áüm.iÞ†sÊ9Yã伯ž¢"‘5 ‚y°,ƤÑ9§#ÕÔ3©s zfBs‚3'ç<‘·wÑ!–™ŠÝ—b#RUIÑ9ïwoÚ½f»,¨œu”ytΓ³»m†æÊ—þPò©J+ g‹Ú oÐŽfaª®¾[uÿ €.Œ€¨Òôbˆ%ZY™YEmjNöœ¢÷twwO벘ú%s…”9ç|º(0UFq¥ó",Â(ÅômJµåúŠˆÞUëbkoΉYh]UíGÅ?‡7¿™n`E±3bŠqG-”&Þ&Þ6%– gHÐ ó’™Upb–A&Þ#RN+‘ì×#ç\ªbú;L–cMa)” m”mð-ˆts”’óꀻYšÏN;îÁß‘_+…flˆ¸Å~Õ“™p2 º$+F ãrÎi­C>)‰‰V¦Wf‹!ÖLBuOõÃÔšZé¸0=ª"Bä§ÃÝzy][ „)bT­þ-óEáåõ™FÙCL.8ïÈiµûÚ3Ãæép¿^^Ú!k9?¹a>)‘.€DžFûf†a>X{JŠy\ÈDƶ%Ør_¹s57œ±/ç’‰ZÆ)Zþ‘Éñ`œï8GÛrÚÇ“ç¼.f3WÕ´^™Ó‡ïþ¢jTUX”S\†ñ€T8söãl-bUΈè‡QUs¼ +0ä´æ¸¸ara˜q§`”¶HÙqq½ ñúª*hïjaúUA9ï¨iÉ5áSæ´^ „ ).êùŒ£Â¶í¬—WU=Ü}rhPΩØWU8ÅÆª·-6e$P¶NK«¤ÅqŠq½„aBï ”¼_ü "äÁV`Š™ß0­W1P-V!;8-¹¯%JcJ˲eÔ”9E‘lMòòm6ÙS"ô¨meno—YÌí+j[ æ-~Û½œ•,u„ä|0‚.¨z?Ø7ˆ¨H”oÁS'¼#rÂÉ®°ód…›)0-ŽàüòÙ9OƒQZœ½¼aœÛ#zÀ:ð3›³o-\rDîz~VG*¢ADíÐh+CœsÎÑúo½h±ébÉ)³Íèª* ¸0(³¹•À"Ƶê9'“³™`Í;7Üß/—óõòjd™‚ aN™§Ã0JT˜…„ÊßË*\gÛu¹œóîèUu]®Õѯ¢J¸ÇrBð䜜óåä*Ü$½"‚äÃ8½<}î ßâD3ï«vÅœúPIÎeSÜÚeÂeÌT(¬á‰Óªä­܈½9µˆ\éOºÐ ôÈ`À¦­Q¦µØ%k2 Æóó6N®xŠÎùSÿ‘Âôa@U(ýÅT¹Äâü*œ"x9Ÿ^mƒ.ìhˆEƒVë1UÁ2‹ß07Bùä:è»2äœsž¼_ÏšÖ¥°Tˆ@4«‚¯OhÇ%×½‡w‹©Ãý< '¬vAa»C‘¬…—Õºš¼·©vfÜ0qSy¨0sBØý{zÓnmǰ·‰=³¾~_ГW}Tet”€9êòÒìgi-¦¦b¨]鑸k›´¾†1FìЭu”›£Xìk¤¤I­Â0Rò¤Øbnš1ЉcñÆs]À Úr¯ªü…€€œsv¨7j¢6û»Àm~zùpÙ'„h?™îõ„¨Z[·³ÞGi7wÂ+Éo1f;ÞiÉ-µ‡|¾§DØFb·ªŒ.JmË’Øê›Æ]E úigT* =ö:oký‰J‹[¼O”2"´Tw4S3¡£2;ÐÚÀÀŽ ³™ˆ6Å9ÒYVΖyù%ýZÕm4ö&¾¡9÷z™ÊªßÄÿ-دÌþ 5Þ™lᦛ…¼“ ’(¼Rà·ºyJÙÌÝÇjßYé»|†êG€å”t£X»ý+º{äÌìÐÉãÐooñV+ãm{p/®Q!µE_EPrKK*¼ s8 Úévû­ÐÔ^@TÁÜ7’@{6¹i™ºùOwW·N,èÐö€àÒ@¼QÿÀγ·5äv’IÄþ£qŸßÑ“QMëÑvìÛ ’máÂ=JI|å‡éÓ÷ÿ¯¥jY)ªÌ——GDtÃ(qÍœ‚Ń"º§þ†|˜N¶Ñä´¦¸Ø°Ôù¡(þËü9õ9y¶} ¢××Ç—O8~ø‘Œôfõü ˆäƒ²ˆdӯ˥Gû[Ék]>çCZ¯ëåe¾û8Ÿ>Z‘´^ž[›Ëøèa:ÙþD·é0Œs˜fNY$§u±*TžþCÛT†éÈœ%§¯i½šÑ;EFÀûo~íø.̵Câ|paÃä‡y½>çuåœëT\ɇa<|øÅ¿£*ŸÿøÛ‡¯óñW/Ç+Ç5.縼~ûç…ä_οý_ÿ{³”6•fUQNK $3ciß²¸š.1Ç…K.‡N‡;@ a¬QÝ¥<ÌGûcS\–Ës¿ØÓ´œŸ ‰¼o}<Ž›'GäÈÅë""Á‘›†Ëó§6é*“€a’œÒº°äÊ@"¡ óŸ~ú[›§¾ IîOÐi½Z“j÷k+Éahj¿V„Ù!ãz~ìg)GÎɹí+™Å9ƒGÄHDs.FûC.—3"xïZ÷„DóáÁ’ ×ëK¼¾²hÎ|ÿá+B\^?sÎã|j»¦«ˆËqš§iÃì†a=¿˜ýíà—Ñ-烃-úÎ0ÌÕQœÌLJZºÉåüZ——¶§¶¤nÚtUç‘'ò½[Æ~Éq>9?Äë‹cŒ%DE¸dÚSÎܬœsë:ïUÕQ1ŠXKÀ·ó;9H†0ŽCSÌÙ1ñz¹¬ñé›ï~éœ3Õûz}µ£† ýU!gecÎ9¥Ô‚‘èÓß‹èš2"<œfÎɶ¡‚ m*d7üîá£XCO5g õçËeY£÷ÜÎP›dkÌq:ŽÖòžÈ‡‘¾ûøm e[ÎO~< Ó‘sŒë2ŒÓ0˄ȮIsáøö7œSιq"ñz~Ta#pÍÓQEÒzA¤ßýyŽ1§UrVasõ´¤F Z//ØNÛŠ(Œ­‰•óÚšâÆ¤î Ö¢u®T„jß`í(ݽ íwÞot4Uá$œ¥H«;»÷ƒ;†ñà‡CNQ£õ{¬²ÔJí›?sóM‰°H\–róÑp‚V¦çœ˜Ï*ÜTÀ´„úLÑÛ¯u¹LïØ'à& Tš=Z{gE¥îxóˆ7%ÓfþÓLú÷œo…?Ú”{‡ð—’¸ 7]ë^®~‹S‚’,ØŒí0\k[íHö;|Õ¦î1ë;ÀvéD87 3m^úW;øý—Œ+ÍnÕÝÝÆ¼úVÐÑq[6Yo[è->› ¾û°ÛOÞ¤=ý—¤¡µ³s(`öË;¿áþCwWo£,õµ…ìÎ’[sàÖ¼×v[©Kª_ôÈß\ô6Á²_åv¡}~N—Z‘Ðû`{l雿 jiZ4x¹öœ¼öñ°U½ÚbÉ{ø„êw¤=ìx½¸«o9·¸ýkÔ:ð–ž±É3ocM¡Ý¼~¾« 53è¿øçÿÅöÖI®"µÕÀØõ¿<âM)fæô—éx?ß}eïiŠNq9?µq]ñ-LUµsM].ć‰|¨¯z+„U˜Ñ…QsÊi%œ¬oƒ–ËÕW$…F­ÊZä" "óý׈èCP…È·fF¡þ—ƒ¼µAJ>œ÷9KŠÓéa:Þ¯Ë%­ËéÃwˆx~ü!§¢CqÎÒ8ŸÖËóõùÓ¯þÁ?ýåßÿ'Hž\xúñwœÖŸ¿ÿÖålž“®EÉœ­¦×a> ãáéÇß ³fr.^^ tÃ0…é˜ÖKZ¯ä’ã¸ì€ –ûÀq!¼s޼/CÐy1Xà9ozýõòÚ6†éèÇéúòÙðëˆèÂÔ:À> cŠ{XÃ8û0^^>µq·M€Ú7‹°d«5=³éo=ÎÜ•ŒlÉmäüZ¹ð•NW¤åõ³Á”ò¤ž@Á³¨ÛÙór~ú‚C}‰ÜËó“*¬)Ûs8îïïí_ç“çËóϪ:Îw·ËÓO }2ï8Ư}Ço:}DÄåõ‘s<}ü…Á Æår™Ow‡ãUfý~#œ¬µ^ÒF%KÎÎ.CÈ.—ç/4œëÒ¿y]µŒ6UñóÏ?ÞVÍFâË™SΧ»{"êiFÀZ–eœ¦ép²y˜}Ãг¿U8­WæSΉS¼.ëuY-Öô¿ü­×gÉy‹QT¸.i>̧»{NÑhˆaœ×ëK{¯-í]$#â0`‹ƒ¦b`õÖ0ßÙžÂ9ÿûßN§ûûûN« ƒsC\Ï0î¼~ÿ׿†áãÇD䇙9r\È&aýþo~'"?|!ŒÇ{å\†úä,¾ÍfifE¨`„Ée‹â±Ñþ0Å岞Ÿ\˜ü0³ˆXD_c˜Ø{‡HÃá¤Õœ®É<îÉı>ŒœãòúˆHßýÆ^Õlò@D\¯gs”`A$Çë™\øî/þñËç?<þá·õ¡u7'œ´^ "¯i½ Î*\´î¥Kgâ/Ž÷ZzEÃh‹˜-•èC@椢aû#‚ ű!—R\@a˜{攜sHžÓºÙw¥¡¿9uÁÊû†Ð¥ë’è;ñÈ·ƒ;ÓAãýtˆ½AF틌Ûóã{ è7ÙÏø·2¼9Õï‹]mÆQì˜J;o,"(êîô­_€ 7ÃÆ IDATÜÑg©³¶öd°wËSk&Õ¼ÆòÏÂõ.RoÆÒš¸Á$×Y˜Þ@ºTåÜ»ùèá}ý|'¾Fh(¦Z `ó |\‡½Å]{êÂp;³Ýb®«Ç¿b7ë6~!ËûöÊb»2½­¡¸í@uë ·â>%±ÄÑÝVÚf‡­r….È^ëñ[º±o´ë/pÏðÖ\«X¼nãŸpÑko¢ÐýM爭ûÚ®,¸µ…nüÔøиDí;ØÝ¬=œ¯ãPÔÑ\àºDb¥16+}-›öI–Ú„ÄnSe¶ú³Ý»›º²õÝŠ¢£¨Þ4%¶›Ø¾CKw¨o¬aË—ê("oL)dÉ"-Òã¬á ÌBwFŽ-% #B4Jƒ–[È•¶ÆÜ{+i›Tø"Ö¬µ'¤¤m÷Huììûz;íß—ãû¿¬ËVÄ­°ñ÷Õ'@'qør+ñ¿ý¯þS†ÃÝGDB8¯9.9®9.a:úaŠ×“¢•i­*s²:`Ó^Vþp¸CDkX‡a–¯Ú æCŽkN«ó:o MÍöä|˜N*òúø“E…UÐ-³äq>!b\.*Ù…©b‡¶—íÆšŠË¹`M:èü0 D_¸YVm°l'ŽÄÅY…ãõ HóÝGΩhô­:Á: vÎ4ZµaݲÍ´¥!×—ÏùPy?è‚]ºdþ%»µa< B\.Û¿ˆh"‘ÃÝWw_ý2.ç¸^ ÅkEyñE¤Tù[­kRN^ÃtôØÖTý0b5Øøg:=( sN’“*K‘>$¼€X¥b®ð2þ\/9®&‰ÆîxUB¯L‚œ‘‘õíÚr' Ä=èê}Tç:ªô‚7ð‚=Hض^|Ç›‡E]U¸·­`²IXÎQ˜O¾íIC°g>pŽ“YÈñúòÉÙ|‚âlÓTŽÓñÓrÎÃt4 yGܪq H>§ßø0¬—g‰Ë¹…ÂÛÑJ1PõãÁF課œŸKnŸ!j*>°´pd¿HÙ¼ õªqª-u5_+³™P8o¿÷˜3È>ó¡\ÞÍ»5Æù€H9.ÐÑCª„GAaY.i]ífU ÞµF®ÞÞ%¦eMÇãaÇa:ÀóÓ眢=KCpªz]Òáxº{ø˜ÖkZ/f»êû9Gr~f›XJN9­™™3gæ˜ò7ß|ƒˆ×Ë+ç|W¹qÂüÃ?ŒC‚·Ú{GD)e@<žü0|ÿûßÙOÇQXræašÇqTøéçŸEd>|ø¨ý6dª®®¨a:  é™ÙT€\°‰£I‚ áÂÕ¾3Ý(‰:f$¥Ëëgœï>"asàXú §µ;VÚ C$çsâ”Z޹4É[uàT3 ¹aœÇùn½¾¬—Wh1€0Œæëoœ0Œöà‡ûñp—–KNñüô£—m¢›:8ç‹™¤NQ@…\hÛx›³1öv˜!RSu5¯ci‡DÎê KUr t®ÛFÈÑ Z¾‘•#–½ö]j³PvDg-áìýÝ"$iÄÒzÕMý= oø,»C™¶ÞýöÉ›¡Él•°…ï53”‰f-›èM»×ƒW‹·¤•î ôŽ`u_Êš³Í¬H•$ô&BZ™ÍJ—sŽ‹¤È9Rq£— ªÒþÔ_UYf³ÛÀØ]”yÑ ÖL³lÉwf ¥(ÂM‚µEü¼¡çvfUØ'¿Aà °?ú™Õºß *ºLÛøpw¢}§ôï~B€Âç¬ÎÛ¨Þ4ë[È{C«?¢å”z®>0ä¼Vúb½¤JäÑTíä=J‡h–]‘¤ Êl‰H­+"øÖ‘]•€åÐ5³¶œîÖÌ|w,W.B‹ÕÝW`vA’ Ø+i7*: Þj¡AmP¯m3¤ѓ٠nFEš¨ÇvJƒÝ;NÍhÝÌô&;½OÒ†£½@{݇HÑ“OªÏþ!lgµVÓßv˜™Ðø½rCá¯:s¹•»ë ˜ÖºÚ“Y´-Û«2@ ì ˆz+ïå¸é5 7‚¨â–²W»>}u¬ÕbÈ9·áÂp·¸…²®šâÝù@äk×·EÎSs–7Ëy{ PZI“ªxê´çJ _&DÀÕ­¡VÕm]-í8$|O¥ìÉ@Jë€È%#´šS„ÓziÁ:æô+ƒÍƒ¹¿¾&_D"P’œZ®!˜™7§ªEd`mJ­Šê0 †ñ¨Â T¤¨vsuD?Œ šsÂz'"ÂÀ„D¨´½"q¹@qþ‚m{rŠhËš0˜ Òz©)½0¤Ð^Z€Ÿ}JNK˜ãtàs»2­ã¤já«V˜¢sž³³¸Kòj8ˆ #™*Õ–?¤a>!R¼žãzIëÕžÓÃÝDº¾>JNít_•;Xƒ˜9i„œSE¢¸œ‘œóU-ø×ØÎ%")®‡5f²¦„縚·*¸JCaÚKu†ÈFSúYL£ñY¶=Ô”ÙVK1×ÉSŽ—gŒÐÕoî¤ÕoP”~˜JÅi™yH.¸b„·¥¹,  ƒHdŒr$‡–ň"dµ;9%çf¹%rXYp­@Áû0ÖhPmdan%` Q2K¯+dAÁl£m\d+‡UWEDgÔ1ƒû„J@±ä¸è0Ê3_Mmž¼ñA¶pŒ›@Î3'ޱÇÖ¤kBÂ0L­?á€%íh@r>Œä=‘³ØUanl*Ú¨±Ø.èæá¸éhYqƒÎ†^ˆ40Œ³á rF²ÒPDÉë0Á0ç½-mC¼s9GG΢b}fåÌ×eeQ—gqÙ‡°ôožŸŸ<á¾19E;”:G%§scV€sÔ2Újì •’ Â9­f“*d¾»ïÃt< ç¸^‹ö¾˜ÄÕ@fwnþœvVÓ<©;¿S3ÌÅõÒŽ‘lÂ]$¨1­Š²-§Å›+¨ƒa?.D4¹ì7^YÄò“-+ÑðMšŽí!š68ÅõjÉØ=ÐÊŽ¼œÖ:#—“0Æ'bZ¯& µ§Á–²XÒg‘l›äœ@a˜ŽD>Ç‹=ºFíU¦Ò…Z—ù«ÉµãrÝѦP)Åùî®×WU‘Ìö;(çÜ«z cYAÍ’í-ê `Єz'6Ã? ±°¯×3"Íws\ŒáR±@z;Dò!¨õÊȵ®¦°äâë/º)På¸Ä^ n@ôÊ0Kýý&çÇÃIU—×§=¼ªÀÜ ’f_Ô¤u±Ü‰UD[ô`÷b˜¹{˜*ÈQ$;Ï9²ªsž\Í»0XÞ!(†ùˆˆqyMË%¯Wr^AçÓGr..ç›çµth‘Œ'9—@y¢u¹8ï IQÖë ‘GçÉrÈ© ˜£MR²ÇââÇɇÙ4vÄ)¿mINwm7æÒë/«X[ˆÛõ`]Š §Æ#B‘þKAK”û…ĺô¦ï·S¨0)ÅEE4IûdÈzshUçC9 4%wW%[Üù@Þ;ç_?ý`sûâüµ."neŠsÁ3Ç2^­ù¨™·h×êG"ç«  (Î’p6qœ}8ŲÁ‰×9N‚nm6!œS\¬£Åi•ÈMaÎqñho({ÕÃ8c$ɹ•ˆˆuÉ)t¢$F6®’sa°sg)¡r²x {Ç7³Šhf¢}ˆÑ^O‹*‚à­ÿLäìG_^>©jçVÝ2'Kî´¹øà| yG‹¸†Ö%]†jH‹s)çë² ³sÔiqË“gõËóó¶Z§öó±³S¾;uRÛ5U9'va ×6Âã4¹0pŠi]Ô‡ÑlWù(¿F\*‡v7¢å´ÚZ×0~HÏ—F9`ŽH.„ÑÈ´½Ž‰\@" ªr¹±|¨ª(×…Q„s¦Þt¨*’²UTbM,9ä®íÆq*’s„õf+| %à¡Ú"…C5Ô£9¤ÛˆÄ9Hœc€·s!•€ Ä(€q:úaº¾0ù0LGЭaúzns.ßSä»*xOéÜív7yĽVÚä6eëÒÖsëõvµ–9À½z›k¾?E6éôm«"Ú2êºë«.6%W÷±æý|GLÇ-F;}´Õx«Ü-f·öóù7­Ñ´ºt"¸‡4î7Yrv¿¡ÒlÁÂ5ÙR›ë ·¸8"‡a7{WxsïŒýXêïN–…7V+ÜË@bÓµ(ìºT›â`{º´óc3`È&v1¹R“Wô~ó~B¾3j4ñv¿öŸŒïÉÝ»«bãæî~!ß6Ëm÷œdÑòAëFn;+ÖÖYg`Ù:úF×´µ³vj6™±ï±ðú­()“¡-©~³VãOytm^nD‰>¿•;´ ¼:CEõeD#úâÞOe={D[w~ ”DP·åïmBMiå™ïòa L¤ôxw!”{çµî.‰Èj,Ü»$ qi¤ÛB—uàÔS»&{­›÷¡Zu·›†-2¢ªý¡X]ߥ,Xv2Ÿ{mÑK#$tRÄž¢iŸUxƒ‘úL Ò;1å¤ÍÆQÌkßAºÆeÍØò¤Ýïšä·K|ÜVb%l]_Ü·JÒÊ>dq—yÐó¥nµ„7<“®á¿ó˜Ü¤ßî›ÿuÅQ)}ì’|¥¹—dn?ÙçåÜD}b„"êuv×ó3 ¸0zt€” bmò’íR& ·Û¼\_œæÓGDä¸sn˜ˆYTÌÁ}úê»vw>97L— "Z01ªdÁ¼žŸ‹ÝPQÑ5–(5ôa˜9%¤  ìÇ Ó’Œ•åüp¸ÿh!àÖõŠë5­Wå,œ]w6ó@^@taReÉ)ŒSq9§õâÃHv.Ëë¥ÍŒÆù„ ——Gçüx¸Ëë’ÓjBŽUnÍz=ƒªu¡UX®çv[Ž÷_¹a¼<§Š§Ò¸\N¾ýðÝ_àz}=œ>øaüðퟹ´^ãr^./›°z]%'æÕO³ÓéÃwøüÓßä´¤”UihÙ õªœ ³ˆÐ*2˜s¬W>.’ãš’eŒÎ…²«ˆ(¤Õâm ΜSb¤l­QP%áښвˆç´{Û8–UÀbFšß¼”ª†Œ²~£ä$Tå\ƒªI8#[Úû-Z)*Lk[»-ô*^_†édüXÃKr½qf¹íßÉ×a:Þ}üeN‹`«­HÉy@\//`þ¤ÃéA˜E²Ô¦÷ͼX% ”¾[®mgï?Œ–Gh­Ø¸œEØ3øá*—çO.ŒV):çúo–œ@y<Ü ç¸œd´ã1û§ãý0_Te(ÁJJΙ»|©®$r~˜ëåõ²~2æ–cçh²~?T4.ÀñîÇo)^9E5-[N êÆÙ97Öˆ›×dËNÁdáó󳪞Nâ½3׊ݻáp¯çåü†ÙPZgê輪ÎÇÛ~µLsŽ‹ÕÓÃ8‡OŸ>5Y„±®‘!|ûÍ×N 2Zv)‘³‘ü€eZdE¿ÿ‹¿÷S¼®—WÛ@Œ2L3"Úäþ4ÕÓÍwµ£ö_þãǪÊq½¦èœ×"¡°¡•³†¶Ÿ¯ä àåù§0NaÇqUn‹0s–ùxˆn˜ˆÂO¿ÿWÎÉ+¿´KJ éV`¼^Ô¼IªëåÕ…àÃÄ9ŒM%çXâÍ~ЩÖ,NÒ9aMD未u˜‘È—ŠV“hmRXºGˆ ê‡ ¬qZJ¿0º0ùácdîÚB7w H;¸æ,*9%].Ïä®D$ÂëåYAs•öˆ°c¸Šx²ïäûxkZ؇Fßdõ©Q÷nu e°¹Sf7ý´¾É‡zöþÆ>¯ÝQ½ :á1vL6½Í)¼ÑÏßêqßîõ=íï^#}ËJÃn˜^Õ}Ú…žÝ$¢mí8à7rð7 øÒæÿ±wÙ‘,YÎõÌÌÝ×%"2³ª{wïÛáE8ôVšPá@Ðh,@P€æš "DhªÇÐD#Ž€’{÷¥*3#ÖZîf¦ùm­ˆ¬ÍÝ›YYëâîföÿß/9mÌ™š[»ŒñÏUcÏVÛ›ÊwÀ„Riw*’ÒEÄc¼5Þ+%éWßY§{M Þ ðz[?ê>Iú¬ŠÛï†ó÷&nè/{û~TÒ jÿ«^üªé Bm-ƒôÊfï,ê’2s¥ŠŒåªÆ“Ã]â£ßÙñëªÍ¸å<öòlçýc0ÙZT^­½ôËêÉ]²A­•»Pò–ú»(÷ñ¼åâbOßÚIj›÷Ia¥·£ôAƒÝ«ó0èä}VˆvæîcèºV±}"ìIåÁ«=ž†jÇ—¼…’-]½ï(ãwæÎöB‹¶àÏ^¯V¥syŪ*¬«(@J…ö/6w»GïÑ“Û+öj¥¶ŽaÛA0—Ç•—Vjé2SÄ>_¤Ó4ýš 6뽡º¯2Q?pNtÚsü‡¿ûoíP i½‘÷ä)ÙxØtFoS5Ôúüü]ÚnÛÕ¸Mj[}Ó‹b;Ÿ¯o¯ÃtžÎÏq¹ÆõF.äD¤òæXÑÀM\ëüЦ)–®°¼ÀùÓ>Œæ^_¯¯Ú™(Æùi<]„YT¸ŸÒ¡ëòѽ|]˜ìf_ÍNpæµOO ×÷Ì_¶qw)–{²— UuyýÅ®’? |òŠ@D8®Zd`Ìi¾¼Ì—Olžåf² Uµ˜rN‘»|þa~þþùû?Žóe<=oËÇíŸÿéÿIq}ÿò§´-çO?¦´Þ¾þ‚ˆ§çɔDØœìo?ÿ‹%‡” ƒÐyë* '‰º@Î1 °¡¡ñ!#‰€DpÎ>Æóýõ«"²è<ŸæùÚ~¸øOÚ%2ÒU›Ñ’GGR& µÙb3£T·Ùi±ºi@{Žó¶®ëš—çO„¸®ïÂéòü½XE˜Öw;GÛ³—âÂ1æ<&rh£Ð%†)Œ'+ëÃ8糪j\o"lS«\a8o¿Óºœ"7ù€ê0Í·ë×Ÿï ®è<’+ðkDÄÛÛ¯6PTÕ´¼£#DçÂàÔ¶…ÓÚæªa¾¨Šߺc'[î㺤¸a„’—¡Äê«¶ûgU`+ÉÅ$£Óå"®×WáhU¬ýGCö'ìÍv¡L „Îç6²ðíë/@  >Œ6‘éüŒˆ•¶Ø†Íi#瑼¤ÓÖŒ%ˆãé‰üðËþ'ò> ³-ÓÌ1mk¶"°M¦³<ß¾~±9æN  ã|ÃéýëO ƙȭË[çKÆ0œÖåº.7ï‰2aˆÎOŸÉsOm×WóÅL/ÕDO]˜ nׯHÞÂXê+&"V>"‘)­|ŒaRå!}WÓH´ñ  J²îÝ0_¾ÿýßÀíírÑsÚ,÷ YãÊzÝËû+¨¦íh“é-ÅœP†Ù…!m·œ•!B>ž†ÝÛùТçU‘P–÷/̱œ9=f„¶“^M· 7¹nF˜uFÂÉ…!kŽT®¯¿€êxzª¡oÎ wÌÅ(""në·Õ˜…¡^ûáI;Mí|¼X༦t¯ùîkµîtÙŸ8Û‹ÿ12K?¶zg[l;=Cœa/ª| ÷:h‹[!¢ŸDÚ1XŠê ý¨WÊî¯]¦|fßµªÇƒœu¡³m”œ'ázRÁîÂb+Wµ¨” :Ã6áv îÇúÈÕÐ yŠŠýÔ°ËŽïùf¹C^FewƒÒ:^¬gè=òýÒ ½s£RÙº!k}‰?5² dr¡Þkú²¬ÁqGëϦOË_M$Ø# jÉÆ¤¬3€Wƒ]÷šôÐêîgàwͽì Þʵ°âÃÀS=:ã8WØá1°Ÿ·á¡«Qš÷òÝÎ}³·eò•…ïá.&¼%vEl¹³pHQ?òºjh{k—™¯@¬þPEÝUE”wÞ÷³ú£1j]uÀf+tþùÝ|l?ïÂ;:FDÀ=Ûrúñâ]=Ö×j÷¥´N°wˆóöL6¼âÞÙ£p(»ïèuú)ýHÃ7êCؚܿpç B<‚óƒå%-Lû ˆwüÇ¿ÿïLY¦ªqyÆÓ0Ÿ5ÕWIÉt8ÖžŸ> §õönXmI1Å%Œ§0Òz3tYŽvæ'6èó0_òPG¸„ãì^ƒ—ßüA„ã¶Úî%wB8ùar>ØÇ(zgTUŽ[7èϯÒ0]ü8-¯¿švÿ=PuaðÃlj=;‹Y¶Ÿe𯔓A¹ùÁùãZ…O™ãgÓù‚D>L¢¬Â¶ý(m‹ñ½:gêÙâŽh\–õöVGÊ™*kî©!´jŒö›ËÙÄÄÃÍŒihû‚ÓåY|\o¢jãÒÚ¾¿—Ì•`6Æy<]ÖÛû¶\±ÃÖØ´ûbbÚçßü1[é…URÜ–´­˜6;ºVM¹óARLÛbsDDÞ–wÆÙê¼—¸­Îçì›úF®Ë5·…§ù©oüú0’œÓs’Ÿž¾«&¿¸ÞŒ'Âf2P?̦!çÌ.}ý’çvšsÓ@U}®x ÈÍU„Ðsq ‘KÛZc˜JxáµÉnœs.¸`ýfoÜùå{D\—kf#ØíFÌQš9ØÒ‰$ãàÛ@‰¶Û[ŦõZC.£Ñë½GׅѸw¿æ¼*œöZ¯È$ÓÓùE™ÍB@ä™9¥Æq :õv‹7 `‘2i¼½—íÜÖ?Db³´lN—à~A†›[ïÄÌÐ)æÔ-$:=}'’8%sÇ×ö}§º"-°a¶šºåhOuŽðþå'†éüR”ç‰8n)®~É öÆù0"¹·_þ…œãt7^Qh˜Ü’¼~ùÉãùåDØZÎg§œ$#3ØÇ°5ßäÜóÓ§7C*Z¼–ü£rª†’Î-×/?^¾þîw)Õä;丨Êz}ælŸ(9̈„¦u7‚ZýSUO„f³ñÞ^ms%)›õK2·¤ÁTT·õFÎÀ„œ+ï;Ä>DØÅ$õvË}.@w)A fRiîξž°8@"²ÀêÙÜß&Ar˜Y0Ò§‡”>y±ºW²T§üixO£Á"j_–é]ùH„¼'Vlá%±†KìÞSr¤?–¶¡vV§¼ææNˆ¶˜$­€½EÀþÙ‚©"~Õ³yKœwÙX*‡¢Nû×N”Z=òØØ­„]uÑëK­Ieƒ¥¦Ò"žÎš@ì,1ý»=ÇõŠ[Šsjf>DQ€ã_}_Ô›ÒùûDPló¤’cåà¤ÈH¨‚̉(AkðîO¢bð~¹¯´W‚–hlr š·P¨)ñ´GC½«tXV¾•‘§î§žÚ¦ndÉÏ=Ù ëâ ´ìŠk\4ÕÇ3T¢nö¦&Ñ< ­mF`Eðƒ³|#ZfX]Yî°«ºû¡6Áó®DÀnêe’`{Í’7N^B§%¯=ž‘«9f\jˆ2¢z½«›¶vC4ƒ"Û‘£ö¨ºuO ‘ë¹-r}õöè~Ü–Ú_(x0É»@6y¥vh@S$ð®D×4ƒNRU4ÐnSæFTvº®é¥L*×|B#%Øc˾=)îG- ñª—A_‚Hó«ûÊís:„jÖä¨&;“fœÏ6ž-SX…·u)Ší ­¡0( Ç ÈùüDÚZè½jîÝ# Œ[¨=åå5ÞÖ›”ì8ï|ðÃÈ)Jrfìµa^f+P—™R&÷Š¢D¢œÖÅ—0žˆœW³?—œ)Ù Ölïgöo™Uœ“™² À¡e:Uc‘­æhÇD«_9%rDÎIJÂQŠÒdrõµI¸Ö%Ôp_Î#œwìšfRV1[»-©È®ŽkÑ0_lQÂCÚŠƒéò©–­æŒF"aÞÖ›¨aäj–çö_^>v´šüyŠë IEVŸ_k¦e*PIÕQK¢ámB‘”g„ÐçöåCW}zXúÀ9ïÃXuÒõݰØŒP·×Ÿˆ¼ !Lˆ. V¦k#Ÿ) g ~€|ùc]·EEle aÌ'k 8ÃdTk3Î[Ó“Y‰( Ø£ XÑÌ1?T»ÇÛíÝþ=¯EIÕün"I™­>æ´)‹ ß9 `Þ>avaÈWƒÙyŸ±p€F¥`µ’—£½È†DМ#"Œ*Ö È÷˜S½Ëè¼Z•’"JÚ˜SOy)(/B=ú…ßþ¸ˆ °™J‹ûÞU\\vÁ"£7çÃ2sª+RžÓ;ç4`QÓ„av^ÆÓY„Éá1sL¼5ìåRPˆÆ)6a"çœ*kÊÓ;M2§<¹*¶«â?&»"lWʘ+ÕPÁɰ$ɶÞ×·aœÉ…` ñ³1$EV!RºëÂ@ä,xÝ<œ¢ #¡äðzVÝŒì_Kjã•—žŠõ$¨JЭ¢²ºPÄ(z»…6-vÎÚÖòã¤"IWD´¸Æ3˜p[ËáÞ–Çæ¸T‘ã¶^ãzµÌ5á­JCЮ1`‰n̓AqÞY' 6áò°\{Ïzk~ …{C7Å©¦ùÉ4>³óÍ®µ"4•R…‹³òÃŒ¶ÆmüîÉ]¿þ$š‰é(”Ð{?Xe IDATç†êI'8­%Šˆœªšû!ÛbªhBj'´¦Gï~³p aÆ9‘K¸Ù¶<ëBÛʦi3†m#Ù²y1ÀŠå³Ç¸! ×8c ÈÖ~[8l&lqƒ7‘|´WI‘uËŽÔæ!ªšÿÍú¨aœÂxŽÛRJu."§Õž›ŠCsaTI’"9çÃÀiN÷nCk@Õ² ‘ÌC%Âø(‘ÆÓ“% ôÕXV–+«Ê0Î’º„ãb]I-¨­Àý9:›á¤é†Ð8­Î í‡ 7@…3¹Åžúqvοù ¡xˆš–ÇQSî?ùW¶¿œ÷Ã(œ c¸@Á-±áúõçq:“.}Nržârí™×—<‡D„HÞ§mIquçiœŸ¶åM…9®¢‚@ˆÖÎmY¸Ö½1Qáy10 ’³eΰ…Zÿºw(Úmy·^"Ú#far^˜9nÖõ帩9L:„èÃdñ×ÎrAÒÆ­Ïiò–´^µ´ªœ*I…Ù¶À¼‚Pæ:ÔHUq3ä‘:a6û<"š:=q¨bívä7n×-- ¬Ü2&È?©lAÊÓ%‚R'AB"ràÍU­†Æù""~˜Uøöú«óíFäœìvÛ1úzªÊW€Ñ‘³ÏY’DE²ÕŠœ"9¢F6rÞ˜½Cœ [µÔ I…‘<:/ë5ÅeO@ä‡‘È Gä”¶E]Éï4p0g!’frÞû€äléŽûh‘œÒzSób‰ŒÓEE ÁêC¨ÝªúVÖ{j-!Ρ0`á ó® $mµ<¿\˜“eû™öŠÈÙ ª©`êY>§ÆKñ´§m»½-ï_LVÖ£ûÃÕÏ)"”2Úž fUžNOšÏfµäGKÌ@D{‘í»Ѷ.D¶ ‰*Ç´­D® ?Pl·ÊaçÈÍ'÷€€$Âô0º¯kê!ØA+ ή›Ãß÷ºvµ¿æä9“JÕÔùi§«2¬nÉ:àÚÃj«l#íÖáQ×lÞb)šO1" Jäë|µ2ûûVI[ K aÙ!pßÀÌ SÑtJÁªÏœKÐÚBç;wGÆ›áÉLQûÑu㾓od©аTO©v°€§[–Ä^ú´—˜h¹òhîÝÑ¢ÑAQ©´#Þ‰üª£´ûŠŒ +‘ ïêúZÆXµ¡kE:ìµ]Xæ#?Ç"VjgÏ©œåy'$Å\Éz\Q´*bg›¾S°ùïìöåÑÂ;ï0€Úí.û‡uZK¤šdÃs‹›2")¿¶?×õîø1XÄVØ¿z½éqîC ÒÝÛÍö:µ:ŒÐbH×.ö[£¸°)ÊÖ^bGë}КúÐõª!Mu|Åänªý¿šDrì >°ÔÔu¦Ä׊Hf ds7ßGI¶×«¼–ùŠ€ÍÞZS>@Ë`N °¥ˆz1> sïXÖÕoˆQë" ¸bK‘Ü$ëØ¹°ÇP”Ùõ0¬]ÊÇ~ìZÙcñ¼V…Â׺Úu(üwR¥m„;ŒxùËþáïþÉÙxÖ9/œJ¨^~9Óv3ùòÑJ‘ïT²¿lœ/9s®·¯VŽÃAÑ«"Ì&ƒ6õžOá >.[ ²ý 7§—ÞÅåÓHèœ_¯oëõ•ü€ä¤FÙ0¶~˜q>Oçûvœ’HÚ–¨(*¤íÓ¹—ÿ]Šk\ÍdfNóÓg«ÆðýËŸÛŒ!‚Ô”9Þ¬Ôã b6ÛWÙÖ9ª\"Ûz-£Ù39ÿþ럈¼‰ÂS\óè¼pâ´=ÿ‡óËúÿþ_á4œžœó¦Ã·5æ0ÎÃ|QÛð8‰ãZ„ :çƒH\ÍSì|  @„hµl¯K):Ã}¥X)™TI»I­WL3_>_ß~Þ®oµ’³ª¢¯bM\Pâ9ŠZU·åSšNOÙÓâfGQÉ„F¶~ žž>;ÖÛ{Ý‚‡éÄq‹ÛzàŸôRDtk±"IRº|þÑJy°;dÜ"î‰"ÌOßÿÖäéªW,1œ’YÈytÞçíö¦ª9i²¦ròˆv~Óö¶Õ Ÿã×å=„ÉVÉaZ——ùé3"1G1»wö;=}r.¸aì†OV=˜Ô¢–¸.•¹ê|XÞ¿l˵l±¬"ÃéI„Óº B˜.äÈKu”UØfÛóå“ÞmðDc’–šõn˜œsã|1¶†]†¸-¼­Fä¦ ¦õ&’NÏß ³™ß]˜È‘k¯ P¬Gý´-}¾¦¦0ŸãzK¥• UÄsþÍ µnË—Ž™Í€¦9¿¼þzèaˆð¶®Û¶ˆ³~úô’AäªÃéIE:Žš- žœKq•¸å¢™æËçºhÇu)¡»I8…avÃpýú‹Š˜»© úd]8hKí‘g·;^tŒk²{¡`‘Ê,*! õþùa²~Œ‰ ̳­7ÈÏ3¶Y©Èíí—q¾L—OýBdO]ÅVC}EóXwݤ½t9•ÀKg´)lïØö! ºËç}˜~þçB@[­Ùɹ›¿ñv{¯Í!ß 56äV¹|<«ÝEF=ŽíÆûÈ<„ǹ MEœùËõ&u4¯ŠVík5eüàÖxôëí…nõ !{m§–FU̇©®€®ê„¬Æ;8óz¶RG÷"`©rƒ{—}ÞÜÖE.Œ¸çPíª²ó5Ô¬¿ƒC½'æ‰ÔsS‹ ?ZÚ—Ð¥Þ=4Ü»S0»‘±Y0>ÔÂÎuq'‹Ò*ÑÆ«I¸‹³¬‰ZEv«ûÜÃÎÆ›ë*,q„–œp¬6ÎB7ùϱ-ýì9 š¡Y{è„vA µð3‚Zîuc5ô_ñÀ®ª ¬yzPàwR+íluXú8Ýïï2nðΜpììí7¨_‘Ó×\‰ƒwžk=xsºç³ö@ö"¢C;J[Ä"ôŽ’º}­RÔú—ìÞ{m>ðe5@díÃölR£õ©cÚ[LêSúíË*&ïç­wŽlzÐ{­Uã T;Gý÷<;q9@»èøÖZÔúO¡m=¬Î½3ºèîce½õÏÎnñù?þç¿­áX·qâÏŸ@¤åõWN›Oª£Õ2¾'4Eæè‡ íøôýï%E‹3LÑLš¾ýfŽOŸ‹„Ëõ]„›>×­µ8žžÌ¹\¿F®YAd½½NççÓÓç¸.q[úíÕÆ~öiÛ‘Äiç§éü´¼}Yoï9»`B³Ó‡ýfD‡äãú‹â|:?9ç'Uá´í.h®{${ûYE¿e•Šõ²‡ùl™ìHœ+»»ezlÂé»?ü—"I9 óë/ÿj÷š\@òEù²eG*’ Óå“ZU¸×{y±·›æ§ï€ëíÕøÍõ¦Tc¯i4~óïþƒsZE$ã Š¡•|¨LÅ ÝÎPDÕ¸¼M§çùùó¶\·åj½t³MgËd@Ó¶Z_7[Uyúþ÷HNâ¦ù ¼ëCp‘Vï_µ“~}¥Ãt1Qƒô÷ÕUý ¦32è~ø8G¤p~þÎ…a[Þ®oEŸ–ý!Æxä˜>ÿî¯Éy“†“9­ï¿þ«ªVX p”ÉÙRµŽÇ›]CV1«ž^~3ž^¾üé? Ǹ­–ëY-V[ç’·Ô aœ°¤a3¿½½Ño~÷Þ¶oHŽJ÷Ì•éüLÎÇm5)"N—Ïýë`ú2†ºpÆüZ!"†qNåE“a¾ ãéýõÃsçí¤¿Ó–ËF7Œj¿ymÝPgªŸ¸!¹çï·ÞÞno¿’ äƒl+óf3‹a:“sq]¬°DçG•Ä)îŸÃà]̦³ÙHâzKeݘŸ ðÆqûî÷ãzýò3 ÌOߥõ׫ãy›Ûeµ QÚ– Ã|1ÆanêšP‹üž&‘‡ºÍjÍq¾ÔÉŠËuy·‘˜óÞˆ"q»M§ç0sjà#¹@n´ì³Qí3â8_D8m‹ˆl·7—ECbý0˜/Ÿq»½‡RElÙ1lÈvfR ãœešDŠªºm7¾9Û…Ùcî~‘Kq³¤q(æwëñ¤õæÆÉ±ŒUö÷‡ZS0™"Æ’|hœŸï_þÔ[`ïêcˆË›ö3BØ»5œ;bÓ!WN¨«ÀôpÌS|˜%Ÿ‘]ßÔ÷ƒÃÆjê ýØo;²ÎÃŒ”ª§®gdì}ÿÒ¬±÷´¾|ùíÅH*wõo‡!@}ä“ï Ü{ÜŠÝÆïêܑɠÿl5ãÝ®S¨ã±¡¯}Qª»j»xØuWþ~¼§CªZM zªÇ^ƒj•u`ìⱱˣëágëöˆñlè`=¥xÀlGA"í”ðwµ2–ïaÉ]„}…è=¬»9žÏUÏ¥Û$òøä5wI2ˆÇÙ×>yvöŒþgj¨L˵« ‹åvã![›P¿ÂîÉÒØ» ·o%t33ÜÛÄ[eü1I÷_û° ÚÛ”ë3c3µl¸kè1¬…ïhÇ5‡{&âãQÛþÛß?Lß7<Ú!ü%n™?Æ]ccßÞªõ¾«S©ÛЭ·Gwîb w‚ó¶¸5P÷ƒ­uÆß¶ƒ†3ý ¦ ´ÿ¿þ×ÿÁ$R€àü¨Ê’²¤Ø¦e9œo¹2§Ëç…Ó¶\U…ã6Ηéüb4êþƒZܹO5š’Ójô£rÀÜ8Ek=óy_eeŽêjHÕ<&17E\>ÁYã8Ç˳2‹j¼½ÇíÖ3HKœU¨Ã’’çœM_ …ùå‡?–BYß¿BV¸Y¢·*ok¶Ç*<}÷Û~}Jq‹ëÍ–VfÌQ‹IE©;@µ<Ø,­dádÏÅt±³ÌÏí]u6µ† óÑq\­FD1Gª}/çs 9:"Ci›P;Dbfr. “ªn·7›L©Wñd‰pÚœóÓgûÛÓ¶l·Wû#V"Ô§MUÏŸ~Iq]lqΓ 9Þ²ëXqÖ÷Èy¨íû21Íq Ì*|zùMDh\¯öp ׫ћ. .W†ÓÓgU½~ýï ·ÅwǹaÁj—tzÌOŸHA8ÞÞ~-¨qß{$§œDRQ¥'eùü»ÿB$IÚTe[‘ÄÛÖ·aí¦T9‘óä*ËÛ—a>KÚdý °¼Qƒ€cëÁf¿f /n“”d[næNQUf!¤óËgŽ1ÅՇх!ï:ù5õl¬ç.òáÎÎSdH.X¡Ów¨„SŠ«sÊàgœ/Hˆèqyÿ¢¹ªDϬtµk’âVwñmy¯Ýx$B ÌÉêÎ`çǹq*ŠÝH8 G›ïa£-}›2'›ûPëûaš‘ÇM„·Û|˜ì è‡Ñ#Çh×¶n¦·×ŸmõEç.Ÿ~Hqã´qŒF}¤ÞUeýµmã‡ÉeS¤z *>wËáÊÃÚ¶v¦±šÞ Y×ÇŒO†Ææ’í×ßÊm]„Ó|~ÑI’'ãªÙ–.Ìóù¹de]°&Hi3ØBõÎg «jÜn…Qo‘´™ŠIU…ótÐÈ×FíÈ*è1ç’Cr ¬"FYÁñHœT(Æ®«¯Ù– D¼© '&GDΤ˜&¸µ‹f–#m'GSÚk½¥zŒhÉ3¨<m²«½n—.­÷¢BÍä¼›â 5ŠE» é¶ìð<]Ùª÷Íý]Vsº¥Ífˆœ™ JUȘ¶ÐT@²>ÁDóbêrªjéÎz ™«!ùŠóD¾›Ih¶Øa‘ªf¡ofJN.¶Só¾f툠""ÀÝ tó1®F=4ÅW~îQ@Ä%§ÔÕ¿ÈfZ½Mg÷5ïjÝy´QK­.‹Ðæhž¶U­qÐìª{êï>Rºö^‘}\•Üó6Ú–_ˆ09M0Oèï@»)­ 1Y¯Š ¥†!!z³´u'ã* kœ܉‡0@ŽËé+õjâÞ‡™ý¿„ì<Іß©Ÿ‡7Àl'ÈRUEB£‰Z꯹+»–ºó Í;´Œê•™H-:4³¾:‹*ˆ4‘0*p½¢ùóÜU^ûId/œè$çœOÛÚ#¨à òJ¬Jûsm7¤%""é¶ŸcSDbl,rý®°>€å°È&¤B·©o 4BÖx2i=Œ*TYr>†îó“Ë},"䃪‰j°fða¥–O‡ûņîkX<40:ÆžízÁ¶dÐC,ÓèûºP[Ì_mÜ :ðÑvж o–á*ñ"çÇá)»Öˆ]Ún* D>nkv) ¢¨¤¸t'5Iý0³}JÛ]$–adã×›HJ^8 ø  ÅÁªÂ@8…éTù&Ñt˜½Û¢×[¶ù7ã<îßöÌCQFQ—)ÀbÚ'±”v©V†Ù…PÊV•m½õ«[iúg÷ ‘X¤mýLܶ"F?Li½Õá\Š‹Xôàáô“Q#*Êå~qÔ 7Ïo”U™E´ KÚÂ8’"mË{y-Íı3ÚY5½˜§k¾¼ ’‡™£•­V1û0Ò4oËÍ"Ö@u[¯*¬Ùelçw´ 7 yoW#r*‡V¨]C7¬  æêÑ´.,lg[»ªaœ›B$'Ûc“s0_^ .ïåu"ÓCV³ÆzÎÝ–k×é­ÅMþ»TuØÈÖ!)ùõö–•Ãe”ÛIÁ¾´¬U-³IÕeëõ-ƒ„UÅŒÖ@3Dƒì§ñf ®«­-³œ¶ªqЂn¡qÆI¬/„hª"H‘œƒ‡Œ1¤òñ  OæË'ûq')Ö`²>³-FÅdz«-wr†oßœóÃ|Þ„™ìc±×[ìù*t•rW?Ÿ‡ùœ¶ÕÎ  —kÏPà|B·”»qfæŒÀœ ˆ8gÚ¼÷Ìçq>×(HÑ9' 1ǧ© +e­GÕŒ,™ÎÏ·ëë/…xˆÝ´@A4 #’³6Ýòþ1Ÿ`ì0§ˆª¦-×7Ni˜N5?y÷M…-¢¯žÆ`»½õ?T¼á¬œ×)?ΈdNö´."¢–›á¼0¯ÝW‘é‡Oæˆà`á‹¥Ó®"Âñ0è5ä eÌJ˜Î/ªúþåÏHd°Å;vškx¶¼¹F—MAl¢Y&ð€(1ª°Æ"ËRë;ÕB8I¶àxJä|Zo`é·ˆŽÜnlj™í|“¢Ø(T’QLiñòñëaÈrL՜ұ´kÃyI‘™3º…r¼¡ó>œ4n‹rO²œJÒ¶BW¢*§ „2@&a6—¡Î—æ´^ßTd}- 6g²"ˆ)FM6H*´Š¸íÈyç<Ç5óYˆx[ 3_4Å-ÐäÂ`§+“ùÓ„„ÛrN¶g“‹I]›sq[ É£0WK9BòJŒœT…LÑÌqcNÎESÄ0 DÛÅϹ(e#p%¡Ïâb4¥8LŽ|PNÊb~˜°t}ÖÛ[±ý¡sÉP>æ]1ÑiçÔ¢0žÒ¶pÚÌ (ÌšÒx ÕFÉ·èœ'4Æ…SÜ–kßQ/?”ã)úƃ°¸.Ÿ9 ¶H+u.ÔÞ q&«» x©£tjsE7Þ=¨v¾oÐC»âA« EËìœoîõc|yËÖ„êX/©êw÷:OÀ®í€X#èö@?ûÌ·Ý¥-S¥C=4–ÔfZãh«"ùÂíàÙ™ ô0üß͸ÉYG»o˜h|äp¢ÒLAÌ-J’jiÓÍþt¶ó×®Çe.Ú\  àÎñ/¹«Ó0¡½i]Û »ÖЕx,W1]Yñ¿S¦T–nA×6ËK¿ÕÕÓîiˆ‡ÆŒ¨’{ŽÄΚAhí.=Hh×¶vÅ‹ØÊ6dí%NZ‹»ˆukòSx§‰ïÜ>œ€\-;¹i×íÁÚ3¦aœ1Þ´±“¶÷=¤#6÷ÞÉyë=híEçÆ€|`¼ÂæÓϧj©M•“KËÉ©¥pô]tÝ5“ ¶Bwck½¶Øæ‚¦úPU-7a¦ì»r-¥s³ìúïØ‹Šº%Õ†Ù’¶Å×ßÜS–N£wn¢ãj×-Œ¡‰‡V#üùHßø{‰TN»Ì´<íÆF¹€Óþ7×Ȉ²-`½Œ½ÍÛe9QuÊïþxïAç›–÷/ö;%ÅÒÜemºa†lÌh/Êtù4]>ßÞ~Þnï¶ïº0˜ÐÆ0ª¢ÆƒNÌl µ•tÍ#¡×›‰ŒÈÖ&oñÁÔÌ]‡`39:÷ôù·ÛvÛn× EËrgË|ož¹2çS–}ý0Æõ–¶Õ #*'UqÃEÍœ /àÇ9 “åÃ-·÷ê—ïòo•·e˜Îãù)["ûÁyâ´ˆðz}ûÀ¤Šv£…£JÖsÙ»añ0ž.ãé).·W§»0d#WnªŠ’ó1ƒªª†ùb™jH´¼­7´C—IÁX¤ÃÓçý0ÚS—lHQ.Ýá‚HççïÝ0¼ù¹)>@Í}o| Á°Ûm ˜óç9.ï¿þÙrK¼ü8µ ·I-vÇ—üØKŠ~šÇùÉù1 Óû¯º½ÿZÔÿ‘–ë+‘3%]ýjzgÜL)*³ ©Rf`z3Àô¿m[®ªÆÉ>µ´…éB”‰¼­ÃÐDòÈä¶Û;9çÃd1&ÖGn­a:^Îø~y‚ŸÒÏÿüO>Œ#åý8y?š×Ëšçʇ©¢Â8 ã¼.׸.¥Š]U$­7†ÓË÷ªz}ýÕ‡¡þæÝÑ®yyÒú’—QlK:]Ãt’9%³Õ?¤¨)8x—›Ú I’æÒI¢éòÌÛj˨ΗOÃéòúÓ?›ÛAUœ -ß=}ÿ»íö¾\_ ñ1^Â|º½þ¢Âëò.œÆéÉô)H4žãv[¯où­Éèª Þ$úõç?»šû¨†1Œ§¶Š’3ˆ Z;7¥Tfy‰ÓZÆ\zìGöçgŽ[&hŠkû¢5™ýþ PRÚû‘V\÷$¦LÂoºQ{N·:ÆÐïRX󨺊ò{aöûtì½BBw§¤½×¢:HIxØNˆ`ÂB~¬žÇoy¶¡3yªÞgXßG!îM¾ðÃÞL¡ZŒM~ÒôêTý0mÖJà ë†üÃ…{;NÓ×Ùlé­™€;Á½¥S* ¤_\ÒZHö;ÁsOêïd2ðâ[ÊðÞ]ö^(ß÷í¤·áà^க °ñx¹öR­Cq¶³‘k/"Èy1ÔÌ@Í£ÚB­ÿ‚¾tãsPÆc 4!’ápo¼yð”?²g‰é<¼šÕD»L‰öèSÚz€U žŸÕf,À*õøÐ^¢ív•J`ç_©¦‡s^î’ÀfÚÇ v }ûÇáðFd$¿¿ÎºoìýwÊÕÞ/í(ݼ–Eº3*µï­¤(z’õE[în5zã.ÒR«Ý^wì€G2ÉnÙÄ]Ë£.A»QÑÎxñô؇tV ‚U_uõÿßÿ§ÿƇééû?¨Êןþ³EYæÃÚ~CF$Ó¦ÛPêý×í#ìíÄ”x˜¨dË¡Í*Mêmÿ yïÈ­×W­T¤|εÉóÅ–ªf¨Ž[ÍèþjGd!ÝÃz{ë2Œäü6ÝÞ¿X'-wwÃàÃäÃàøÞÞÒz£0˜ÙY™ÇËKQ¢¶¹c˜.Ÿ1›L£ 3Ô¼'¤’è-ªõÇa¾XÌ›2»q®f4‰5¼ÚÉ®?MW7’»|úa[¯›UHÊÎè|ï¢íô«äÇIó¹=vÆ<½üÆ…áöõçúŠpŠäR-¦Ëí.–ÛÂ7Øìÿ†é<žžºsnþg:?-·ŽIJ™,kÍ*€ÓÓ'D\noÂÌë­¤½üðWÂQ%¥¸¥ž:F IDAT¸¥¸pÌ”ícÜ!¦ó§éüÉx»Ö´Ü–÷R¨ì?aφž^¿ ÍÉ ÒhóÓwo¿üKÕ1ñ¶˜ö}DØÄ€h(»þÓÆÛ›Æó§ª‹c÷Nš>Û2(LÔCUÒ²[q¬¾Ìœ°rª±„rîÓµ­×ív-Ä€ª©Ó"Öð>Œ—Ï¿-Ñ•ˆè–ë—õú§ Gã±½'ޱœrÚç²Øúáô\SkªG~˜/ãé×[Š[ZÞU! #©ElËýÞk¢ëõU3Í@C}S¦Ð4¯ )ƺ§†ùÙbœèKÌng@çUæò:t‡$ÚíÌÒl¯•ío>ŒÎû¢;œ{h<]LS ÁJI+y˜.™¶X§¹V¦m8‡q~ýé_JNjÖc›€¨â2lô¨’$%7L»+æý4_¬G5ž.Óåe½¾®×7sq ãlyNñÓLq½~ýY¬0uaàKm{öU6Ô™Ça<$Ï`OßÿžÈÛüæ×ýOYTe9¦{GÝA3Q=·ëÌ)Œó0?¥¸r\+ÊToï_8ÅÓÓç>õ°6 kø‰±âr•ìª1ØF?Œf¶]¯_K¼†ú®í]±Õ »3ö@#mcžŽ2ÕæªG¸èÑþ0ų{ÊBðÍ#ð½IC3—»ÌÞrB±¶”si†ã*#in\„{'w;;w¥mõ¦š`r—ªÕ1D:þtÍn–’ Ö µ‘€ê¯Q<¦q=8ØïIi»²ÇOÕr‰¾Ãžt?ñ¤‡@ÝÝ¡þþ$ÛH÷rŽœ~°èj×öodbÝ?5xàV`Öýžqw}¡Ÿ cëÃŒÝè³÷êî@z]ß…}Ÿèþù,ž¢½=ïÄÚw€ï;p§ #Y«™ívÖ#m@è<æåöõ9åÙqAílßh pW›âGß?ÈØÃµ‹2ÇÆŽ¯o•ªu÷ uf+í p;0v‡üëþ»TòfZš; ßx_Aµ á>joöú.ÿs¦øà‰)7Lz¾ÄrWEõ>Œû_æ¡—º{´mŒ+ÁâèUìvŠý ­.;¿Ç¾'ñ­àF¼keì õÿçßÿG,`Ï´Ýjc}˜Îq»¥3V§È ::pª±…ÊlÇ«a¾˜¼ÍŽä\gPX—+Y¹)VÏ•iͱø´à «cLA.ÌçO? Q\wéØözÚ` i t× ì[‘9´p<=M§g‘¨Â)%U‰·wÇÖñ¸ëN£HÎê6çÇn—Äü1,2­ôâÇÓe[—<êPstè.&Wi˜ÏÃtÚ5ùðöö%÷¾ŒÂ hcTrN³tÛ·l?Œ´¿RÊYIUÏ/ß!Òz}ã\7Õ¨ºN@t@¨™Q{¸)ªäÍ#/*lËž˜–« î!‡ 9rÎlÂÙu. ,ç,çÌ•€:sþŽóÓ0_D’fƼn·WNÑ…±žF-pÃüÚÃébßñöú‹ˆPýÍ޼…\ö¸Šlîìê6K„Ƀ£a²]${ úmÉi[´;ÚPž¦÷ë‘ùúßMDNÏßÅåº\_ûî™óƒ™ß *ö r?"š=Zè|Æ»¿üæ ŠàÖÛ×õú5'…u‚¬Un‡€a:ÓE5©Š€"^¿ü¹†Ò5UBï åÑÏ—Oäƒ+œs%ÙÛÁ)qŠÆ¦è-¶å]ÆÊqìÞJѬûÕ¸­&m$ç‡élgÓ¾ž(_P:xÕ¨P.ý Ó,R몄ÃöaÍ­þÂ8‹Š¤TŒFÍzÆ“óƒýÕq}—ü勯Y„sè)'›çÙÜ1?‡œrB=‚&+òŠ]­:ÁÛ;ˆD6¹¾Ýg×èîUmË›!§xøà˜¶›Õúˆ8žŸœ ËõL¦ŽàüÊEC—·ý~Ê1ö‡iÆ0žª¤Wï^ã yge}8Y#®ÆHV¶åéÁ¦Þ¹Îµçëù×"¹ÍŽÈVí–#M…HiY÷a÷¢Š}yT¾”Nx† XçãB±CBÓ•Pâ]ª>$»*»ß£¸=8,£ÖËž#4KÖn;-Ü hõ['¹œ3Õi&ûK Ôœ;}!¢¥83‡¢#gýÉ.@aÍg)EtæìÔ….ÏÃáî˜3b,ñµẍaÕðá±ÑB0ò˜ ‹»Ñà‡GÎ2G‹Ý)¢Ð{6Õêàgi±èÝKŽHˆÖÜ…ØU3rçúÈ‘ŠÞ]iGXù „íØ<àˆä¼ •ø© ødß>QmD:ÌÞªž°ËIס/»Vµ¬î׆öÀƒ)¼“0èAº«­šWèüÈfUÌ/—jÅAôU—]rOÀ¦§X þÒô "@€–›õ­"Jûèú -險LªZ²D×9g´šÎÒûÖ~«Û=#<ƒé;Â8vÏç7ÄÓÇI3tŠ4=ög•#ÎŒ1ˆ6…oìƒ|ßDóf7 ãdG;ˤ¸Áõ-î@e–‡E‡ñl£1ÅõÊœ$¥&­PÕcB7èãí_ öj'¦ÒÏ8EæØcÕ Y`­ ­çDVÑÓów–Ìi³+sÿpÚÖë×ïd¿94e ÍDä†é”—QrD^ÒíõC¬Ï“Š–ðú†ùG÷Qk… h?LÓù%n s\ß¾dテQ޼û,ÙT߀ ¨ð _í¢sŠ)nöXsܺÌ^×oÍä<ù!m7‰ ùáÒ^Á’ æ§ïD¢MÔSÜøw&óh"€÷Ã0Ÿãz+ánW+žbé/Ýaôö±`:#T”™FÆÙ$òú€Š’šs®‘êvnñz>´†Šíƒ’ÌÜv¨;û >cÖÔ{ûpi[¤l£&‰b‰.Eá”¶%m«EHZ󄜄;Ù° y©—ÓÓ§¾“#ÌÛº àééå®~ÂõöndY•$â;EÛjî¼ç3,˜Ô";rsØúDD±x‡ó³§`D=U™Ÿ¾ã´ÞøgU]kìY6QéØš’œ >Œªºò× sðàÈH=à9@BŠˆäÂP×[JŒÖa^RPé7|O¥a€M8mœ8n’6òJàT{²cU‹:g°Ó¼H˜îŸ=S]¹ù<Ìç¸Üª…»?Ãít»Î$xZA‰†ùlÇV3b›N§§6÷[lAò%“á „ù¡Ô İZú¦§¸8ñ É[/Ú¼ŒÈy?ÆÙ¨Áä|Š•æJŠ[ÿfaA¯ÙM€@ΩÚÌ(n7Í›1™ù㈮ðQ•! ÅÕ¡R Ça#‚Þ-†ÖHÅÃM)‹¹m´–±ºWÙäVT¼€YW;-äá^×þ¤)Ŭ›šâªÌa>g8âÁžK.7uòbï÷Øç˜pjVí“mï_3™!Ÿ„Ä´{‡“Ù_ØÍžgaŠˆ÷ñ9q­sS–ÕNèšQ¤Ù×NO(À ‘¡ßvôò†ïŽð¶£¨È=Ù\]@í­£R¶rƒ¹Ëé¾=Õš·½±{ÿ,y@Þ»#ìèªåsæ#ö}Âò‰¨lÜy¨þóÉ *Y-Yhúº‹Y¯"Ž:ÃG@t€fq%E±=q7½?†W”ïg9&ªX€¿¡Pj£;çnF+ð…¿³EW!¡:ç–N¦ ƒHW=ÀŽ'©@Î* C¥¨ü ¤»Ôè5LŠ{‡Iá«kï.ß!¹±“hßõQ÷ òGüîŠ$1‡uMáFÝ“Tc/îq¾ÖÔÅü$k3§×ðB± ÷·§ÓTí¨šfW?Šm·QuÚ @û Ò­¾Ø[;î5½å ÄÚIɺ÷ «H³2ñ?î[ø† µ°E Lë}¿®ÆktÖí’!š–ª·j4»C§ m™šp§;U±fMýÏ>x]¤ÈߤYb2?¡¾°¢šÝM¦ XEYÙ¹½ºÇ¤x0`yŽŒ\ìÖ5Eí×-]ÓC»‘\%©ŠÝ¿]n¼_¥päsa“§4_þ¸w§óÖ¦\:2:Uc~c\õm·™±Ylá®Ùl³¼Áª¢JøÿËßûûÑ´é_Ο~¨uÒz{G"ÓÊÇ]ÎôwÅöHò!›¦MjÑåî–Œ´åÀBÆÓ“ªÆÛì"m@-é°?_ØÇhqz**2žÂtJÛªªa·‰¼ ¶p«êÕâèÂ~o+§Œ¸!âpzò~(îN4¥~þ1HxµÞ¯ªl·÷&c+'šjrPÕO?ü±.aׯ¿˜Œ¢%zwWC˜?ÿþoTX%ªêr} Ãèø­7sÂrJãéÉÎøª°Ý^ Àèæ§ÏÆçmÍiÎÚøèPÀ}M0ŒÃtF"$¿¼Ý–·ÓÓg"²¢üúõçö´9w¸âmE„˧ߺaº~ýs-rX¤6=êŸ1!ø§ÿ=Çíöös›¿–WWÄ(]+ˆlË•Óö»¿ù¯SÜöylˆ Nf€±ÿÕ’†Æ7h8¢Æ)L§åíËv{·ÉZ aÅ ª†€@òaÃ8¯o_×å½?Û…q–ªÙÉ~¡,©Ãÿl‰è|8=}6S¹¤”âúPm«¼Ýœæ§ÏSTfN›µFûø{­–÷¯–ßæœþá·¸­ÙFç½Ýh‹‘³Ï&"Dtùü[Çš¸#;5Ea<=9ç9™©©&”ó~@";t›ˆ¿¶púrA˜UR F %s®Úx†él9yä¼sŽ9嫪»®ŒUNa:‰Mð>Ø_ùúóÎ]p°ÁT‰|Z—”6“Pí{¬v»'êCU­ÉiS#™eØpy•j%œ¦ó ši' -MŇQr%oa˜ê}[®UÊS3vrYË!A¢q~"ç×ë×ZF[7XT‡ñÆS\ßk«œG¤åýת8ú;­»;)9?ÌOõžfË„j˜Î~œ—×_*½Ë S½Â.L*IRôãl 'N‰ãÒÀ^ªDD™¾ÖñÒòoξ~I›tŠžÚ s>XHäοg·»Ë1¯_mça>Ý^¿¬·×ùé;D\Þ~Mq;¿üF8mëµ?jôÏF_‰ù‘x-ŠzŸÂýñ?²/è]8>`2aŸ‡Œ}ñØvï,Féõñ÷½ÕRÑj&°nV¶6tpã;WvÚ·&¹{—lÝ-;I¹öÂA#pwevõÈE²ÖxNq®]œ†î]°ÚçînÐN[|ï½íý-ºsPô0.”ÙËÊñ­Ñ•íIï8šsÛû“pË–kÚÆðý[¶‚ïÞd‚= ;¼îÓÌ?­ëc»"›ªk.Õ’TˆBŸ×þ-RÓ~ô’ŸÈ,µÈ¾“§‰wòû.Ò$ÿÿàCDÛ?p_,šr&Ÿí: hÍOV)I÷¨Îó¡ï½)°v?R+6i°…RE)è.°¤ú©ŠªÃG¨íµú¹¸i©ˆÝG*¥òÏAw/\Q¡vSÏu@²;ÉOïé½F;Ýn}è‹®‹-7èAûG¼·û.GIÑ·EI95¶mÅãžò`Û…Éæ£l] ëŠô!w³{Hå~ ÒÞ®sØzì%­ñ€€ÿðwëüpzþΊœ0™ã’âÚË÷ËñpÈtàZæü`† QéO£~•y[¯àüHŽˆ¼Ul™Ø"Œˆ§çï\–÷לð‡tzþ¼-׎ÅL÷aŪbs ÒÍH¾«´R•×~þí_) sR•åúZ´lˆ·ö®I¯²Û'¬^Ìn¢ 9o™pY*7‘´§§ñôTeeÙä lìòa~ÂÖLÎóä¨ìlÄÎ pWåÛë/,‰×Å–õÜ$7ûŽóˆhâµ2—•éô¥m© ¬ɹݮõÜæ¿uP½?6÷.ãÇ^îî|(Ç;b[>ì6£×~Kù~0t+àѾŸOTɲ/–ö7r_Ê<:·ö=~pÜ>”æõ¸Ú—S ÊôÎo[y¹ {T«‰¥ø‘Òt Å®š® ðz~ìéuT mø£vW÷ìº=C û³VÄÉ\w²ÎCŽ:1’â)ÚÏ„õ!ÃïØ:8¾v—ÇÅÞ¿õ×ýã ÙõQéüGÙ]ç†åkP¬ý­„ûÙø#|ý¦Ú;äqß¿Àn„€»T†ÝéøÛ6ß&Çu·£*ùzx/Zé/éU0]YŸq•ï}¨ên²u,±Ì¾Wç°h´ÖSnr”um·hã¡¢¬€½.KUû÷¢-ÝÓÕ%îž%…C×ZK¿6SÎ}-÷¡IãAë[KQ¬Ÿ}§ÁòP*àý‚mßèÿþ?v’n›¬%tÎøÔœâÓçße'»ðv{­7ɳM¬(j£€óå…|° {³R¢ Ö4È4/¨$ëúl¹­ùÅ‚+ÂiöÃdZö¸^™Ùù–>Ñì8R &ÕѲ•…ë[­’ý<ý}²‘†ˆ àéù³Ñ³„y½½À8ŸT¬¦5¡y¶Í–ÇA?ýðGNÑ~É!bê„qòaÜ–›…ý"Ñíí ½SUÓy˜Ï)nœbÚn6XE$N¢óaÎy+yŒ1žÂ8¯Ëµ6ˆ8EB²Cw\®Î. ¦Ú20Uª°‚Jr.L—gXÞ_K®ÍÆy½¾™[¼ˆ„`²ô@­Op¿Þ¨¦m¦Óxz’œ´µ1'*âl§×7\jЄ¥;GDcMù.ïw:¿Øè:^ —¦N——ü`@F—¥u1%½y¹¹”'Ç«H:]¾k½ê‚5—ˆÚ@î‹Õj…ÙˆÆß2Äš°XàuÚÖÌi³8û0¬×7"çà )æ¨ÅˆÌ-a DŠgô„Q&DX˜È‘#.áˆ9×PL’'ÃvzþÎêäm½ÆõºpFÉí?ä©[–9wØUKŸ%Qs\œæçïAu¹~ÍÄó-të¬Á1Û5ó<Ïùà˜uÅÎÕÙ°™Gíe—”´lZÉi¾¼„éÌE(`Oο‹H¤\îŒÓÉ£ S‘¯¯?[}/Â’Rêf·IsRqJJ.’*¾ô@@í”ú*¢ã|ž.ŸURv‰¯×WP! ˆMŽŸýˆ÷Õ¹ ÿ²€[y§ªw{[ö&!9ç «’ AÍ^ÉQ_áÛûâ0Í>Œ}À@¿"EBW|´ A1ïVÞ÷Fò¼a¨ Rµ¨£sÙ`Ôšè "L¹&k†Œ¡Ê9­…MŶ_*­Â±Úõ‚z¶=*©j‚K‰Y‘~äp,.[#>Ë1 gî]Ô…ÞfjƬÊ%­é<9³°˜^µ =7 hõJ«°¡£ÚÁ ¢FPzp µÌoŠÈmwXËÃ.íLÇ¥[‚ ãuTÍPÕðÿŠ»àí'¤˜SÖîNõZ¬?ðèK-aâÆiYµkÕtNoØÛ–÷Å7Æ2!9 ÆLíéÊ_K&îÈA•"µäEòŠèrQéFRö#¢¨˜°ªï¾’ ZÓIí÷P+sRY—aô(𺯄rOÂêAXYÙšræGÑñô¬"–VìC€âöãS\k=Ôâ³4±ÇC “ã”°+y„´%^+t7{ŒRŠXÓµJ:±e¨ŠpÚÖœ€ŠMž SÐxvf,å Qó­‹”ôMU.˜iaVf-³Û cRõÃlµ¢†aIu4~¯÷·wǦ˜>ŒväÁÞkÑúäy¶ÁvÜÛ’RÜêxu˜/ˆA…QU€5é’Ÿ7|í,¼“+‘CŽ œÖëWDË»1ü\:ð+È9U2§™ú9 ô•n¶¯UUËwÛb‘HX¶u1Y¦#¢‹Eyh P>tZøjJ–¸­uû ãI…ýƒ®ðnVÒ9îÉäâœâ1foœ­E 9Ååý‹¾ [K"Rè/#¡B˜Nå!"*!¿¢’ƒ.ïÙm¢’ kÎH¨"¬‘… 9eö$vûe§…fD)qí ’’íd"b<ürÑÎi€•ØWïZÚnf95£Í\ó=³Ü]óޏØwÚûÞÎÄœ,Y>oö‘˜ï…Bª`–îjå#rÎÊSXªî—ãI‰Dd[o»ß\м)„X× ö* 9+Ñ9T®2-tœß–w@çv¶Ç<À7×sðÓ·…ÀôýiÖ9Ç ­À —¹˜ÊOÓ…9Š2 Ó©ï’t5A“BTðxÙ‘rÁÆ/¨â‚Ú¯Pf$ò~`ؤã €¼ŒByŒr¯ÌÉ-á¯XdªÇ¬ý5Ì—{?okääL:d.Q"r>ŒçNÚã„7ሄ¤äÇ]Ÿ9^ØÈ5ªœ+ke‹Etji€.³Þ3­?¥dáÚ¶´Ù&ŠD6+uaPQ¤*)nFí•ú*ŒBD¢)Y§‚šh;‹9NCÖ=Ü ë«’]Û ‰œ(ç(`?Îv¸±^ªÄô«tç*;ÆÙýÊz®lÕ°«í#¬ Ssµý• <Îå軸Z—òòùG"‡„9†T+CK IDAT$n+¨¤¸RMÁì§òVûa®+ªC\®’"9{Jo~²–à0êyk§ƒß®í+ÇuÉFìÎ3è‡Á9k @ À‘/®°äñº,X5#Âëõ+=}þm9t.‡uÇvM[ˆ‹´'v‚ Ž«M“GÙ-³ÆÓ³£ªÙöí–yUÉÅ:sbNÕÁ’V€0Œšb¬¨3Ø[õ‹µfÛ…Ü}ªž,S²\6ÂÓ:ž^ÂxR°ƒuFÝìÊkBï'Æ”ììI¤Eˆ±FÚÄAEœqtU}ìÌúÿsö.?’$IšŸˆ¨ÚÃ_‘Y™ÕÝÓ==| °xáFp®O<ñÀ9GòB€œy ý_ˆáîÎtWUfF¸»™©ª"ú0s‹¬Ýž¬¬w355‘ïû}ÌÂ)Amq µyªôÅùŽä\×ë¹J5;êzŒa¡Du¹ÿ)ØÒõë/Î{²isI’X} ¬.ÚŒÚfÎ ùÚ#Ã|ýúS±"4»aõAëóÙ G‘¤jìü—m7hƒÅü{’¬¶&§§…ezû²‡E«b0ƒsº|ürmòíH‹UOúk(ŸkdžY„Ï>wN Z¹Ÿ²ì°t±Š†Þ˜^8¦°Œççñü¬{‡¦[ÅœõHîòñw:ør¾'×+!–Ó,DÝZ2§€9Ì÷ÕAûûë^,Ω2ƒ ã2‘óºÆâJ®]C"U“â»þíëO»¶‡:Ÿ|7b˜®œyOÎÇ ,ÓÍáJði¨iÚù ËÜšv¬ùIŽœÓÈ7²‡Ë½ƒgBŽK ópz*4 u…7Õ*ªN=Þ1†Y]íàN4¿}%ç»ñ("óõ+’Óá*Ççn<¶ä‘éöÖàÀRO ‡“ëßõêþA@rÅ>ÁËôf­<=Græ~,S\&•>éce†-¢-nPšèÁ÷c×Ê=Õˆ†8_UŸ"ÌÓõh h4üáôõ¯ÿN¯•ˆôãÑú XÕÎY½å¼'¤°Ì…5§«ÚHŸ;UZËm™ O+!k«É#§ÀÌŠÕ´ã”®_¯¾ëA¿”¾Ø¿€äm¤mº¼£·Ý}?¹×_þ"r5:ç÷HÉ…éÃ<Ý^W»üÖl·\³Oø~™®+—Q×"¦Ø8ôÃaž®%DÓÞV{öÞ¬ µðyQ>™u;wm±j…Æ ÖôÍ“óǰãBúÁFK^·W@”}õmûÞh¾~…»gy|AC¯)n¸ÊJ}//“~ò–'íQxO;q†eú›dåJ,ÃG;F6* ”$,|—UWDˆõÊæ£rƒ2ѯ‰¸>=ÊyJ-»v.¯_Nå ·¶5TluÖÆöÊll+°R«×Lµ6o~«Àº€³òWÆGP#ã0JÖ{Z˜Ç5X™tHHRì(ëpÊwÑ+Å]Yï#¸¶Õuq‚Ãw#ëµØE\y6°VØÖMA¶ð‘•õÿ ÈÕ8>æ;ìɉ¶«Ö‹Pýòn©ŽE¶Ó¦ý˜SùÎÀºo˜yæñßÚ_Àœ ²SÍã&k^Áên­l6Å(_Àîú„šFO·PD|×i вúZ;TùÜeÛ²vwEiÀMæž ñq÷ÚnVøp%sJ­l÷v-Céòý§ç=2€v°[£ ÂÖòˆÿûÿø_•­rkܧÄ1¤ž>ýÞê Äë—ŸÊ;Ã’z-”‰Zÿ)9ï\w¸|пäÉõa¾†ù—YgW`&Óé†ÖÛÄáxÑ2_eå "Ë|ã”üpÀÌßSM¶u…ˆ<}ú•B¨Žƒ38JÏ¿HŽ Øû['Úä€S<øq<}à8‹ðtã˜Rœí‹#’ï$ñZ› Å& ™1_ìØ:ðÓ&¾Y˜chç ™W|èÇ£¶ã—ùæ"©r=aS±n8øáàœ'çÂ<‹p\&ÎQÞwZyk©$¤·æÉŽ‹^ t~òlcÑIrJåËŠŠþZæ¦-HÄ ÃïÙ13ºÉ%޼F¨s Îw‡ó ä1 [Ç%…à3àJÂýZzžª¡xänkIÊ̇ó §0]¿ s˜ï®ëœëÆó³‡€\/a¾©Ï9.·7ÝÆˆÖÈ@£4D=ÝÚn¯_÷Y¶ üðÓ2¹®?>}D¤\Ç–æ®?ø®¿¾þ¢vc%ØU?r %Œ­O®8FÓýáž ¡ÎÑÉ{—û »Òs8á·/ñ]ï»^›uÓõ›º¡Ê¢?œ `ƒXÓ0ôãùoß~NqAÓ+ÚDCs-°iŸi üòùÏÝxÐáåýõç,@Ãf†9¥ÒNÄéíK7ž?ý¦·oªÐ·ŸþV9ýÃíÎo/µ©ø~$çÇãY˜_ù×äD|?BR¸£i8:༠*±ÏpkªL$'"O?ü.ÃCx™nß߯ºaä”TB•”[81¥ä»ÞUŒYDh,¢e§8r>ÎS sŠ!…åpù` s±´`ít“‘Œw.õJ¼8×ݾý”Õ‹XÉyrÎùžr’eãÔ²¶Y˜ïäœsÎwßR˜R ͨT°bùñÐ"¥Ñj-÷kJ±ŽÂQQ6~eis×Tq¦Î ʽ=Ëú?¥¹»ªŸòq¢T:h{:0Ýmý×¥ôxvŽõ=´~uWQaÎZîZlO«o>8¶ÎÖ=?À~eÃ431¯]ãÅŠnF[ÁwÞ¦’ã¿ë©Ú€ûæÏ²V½Œ]Í{fáw‹ƒ"ûd²¦Ë.ð`·Î^ÏþäW»î¥Ø’íﱺ‹öNçí©¿ñÔqQP½XZÞ…y§ QJÛlDÉd¬ïU$%(¯ÛM…T\(ò^iñæÈ\VÂú@ƒÍÄ_/1Ûæ~ÑÿN[¤^1Y÷Ø+Ò×…«†Ó Þ‚ÚÛ_^Ív{8‹’>‰m–z¶Žl“AqMrP–t殣lE³ÅqK§kći#)a)N¥¦~ȃ1„ÇžLYT¸ßvÇM nÜ÷«A5j)n2Õ+“ZÓXë0>;€“¥ô¾KaxhW"È÷Kr‘‡ÇÿÁ»¿ú¿þÙã¾ÚüQCå6Ò!‘¾à ‘°Ø¤ÜÕAÔnEµ7 ›i\Z@›Õµ!õ&f‘ÆŠÂŽÿôÿÐ ¶Tu#ýá2.,QRRöÿ<ݘ“ftm®ŽIç—n<":@Žr{ýjf”Æ>AÎ;ï Þ¦5)†;= ‰€¤à|ïúáqÖYf¶7±€&ÃAnZr8wý¨ñoY7·Fɪ¹XÿpUººn¨6?ŒÃx’Ì—2êOM°”’Ïæ)g•­}Ð*S,Üa½`9õ¾þµ”žž>:ß‘ëXRdæéö­’æöqõE ç<°ˆ¤áø4ŸôQá´•‡Žúœ÷"bt:DÉ‹rÚ8œŸÉùò1Dx™ïmæg—"Àp¼èäïo_•!P˜s¢…$‹¢J4|L° åxùˆËtã”Ô Â)æwí*ÓC)É ñåˆq™dàÄqY¦Ûì F ìúA8-ÓMÅh‡óËåÃïR ̼ÜßÂr÷]9Ín¾ß¤­0ÊXh<^üpЦb 33§¨f æ°ÖY¾#ç´1@W‰Å.‚drJBqIaQ/‡¤XÖ³ë•á@JQ¡œ -Æ)ž?ü‰¦ëW×ÚpQ¤ŒI²õ u\€y*61aá£r*§ýñ|{ýY8IbÎz¢‡RŠ Çàû±´CôñL)èÇÑ+€ä(ÃÕR\T§ÍœÆã"úÞ@êc “ëz´©Ïºà¨R˜-†q™‡ÃyD­Ò‰ˆÄ¸ld€¸^·ÛèMÏVÉ('föý Ý>DLa2B¤HŠ‹n_œC"âºAÓÎ1.wåjAozŠª-ñø”UxûŹ®õ܉è,¾MÌÓÕìÒ%÷q<>·q›7’6³L¯Æ`Nl~6uѺ~0›N3¬ã&=ÔU æåÖ.«ä› šð¦ôj\3b‚MfNÚy@TMÊÊdjg?®š«½ÓïÆ¤ÙêÁDôSlœïåËþáº)ÑJ­JÎÚÂYe`¶²æ@kôÎém‘¶’ãfE¥Z¸2ÿzq–9OåÞ„í}&”{$Y0ëÁe¾÷;É"qž §]½˜õçPªH7&}Á,ñ­€:ë+ó> ^¥b"@yä`Pÿ:ÎY%{ƒXP\;R5c·w¾æP,ÚDÑ…¹[ ›ÃŸH‡ Q$%!"BhaÇ‚"´3ö˲¦b׬é3M…úà»,Ê%$»rjýTÐ|3Ô¡VxÙ”CÒ*$ÊÛþMfÎ x¯ï9AyU_Yzé¼9²e[n<<ƒ")-)FNA­ß[Š76ÊUjUy͈ŢIž¨Ù“+E3EÌu.ÚöW°ñ"›II'D°êžˆŠ€f^«hxsß;Ç)/ã ðkPkXïãêÙ–•>gKˆ\ òÀ²!È·¾òµÌßcî5"¼V«ô}¬íºcm¡­± -þ¢ôóGUÁÖ"»b@\±õéæ"aÖNçmP5ÌXëZÔ<ý/‘ïÇ“뉌ÙëRÓ ë"T-oA{—Z§ë»þ gFr^mËM»³®]}ºÔU#3ƒËÆT<˜ ¶‹ÑŸ}דëºáˆq™¬¯]Sʵ/)NœhP¢’S4}NcH¥3ÙìÍûR=vV¹Š~¤˜B˜ooúŽ×œ÷ÛÈ):ïUEä•; G–G¯…©€¼ëíž3G"OÎÏ·WÝ|!†B–l”!º¥výÁuƒ¢’Œˆ&æš}SXæÛ×ìšHå˜bÐ\JórhÄ•ï]×kM¬¨fŒ\¨bE¾4-¡t›ãœÅH¾·^mP™§¥FÉ€Ú.eór5~1 €„eFÂl„`¨* h#Ùra rzþŒ¨‡zŽË$™¡iÒ€¤ÕI˜5ØIJyã—ùþjpsÍ5‰ÁÙÁuˆ„ù®Ÿ@˜3sŒ­cIQyvì#ª®p"AÄGý'êÚË | `‘Ða[ K1í!Zü]J, šsÉ IÐ9qÞç£*¹~È˾¼˜Å(ðD™¶Š]"à ¼ŽGë„ìÏ5âÊûÞöw"u|“ó).cŒK>³¬1èÈS/ÝYï{Bw¸|ÌI–åSR;69ªoÝ—ŠqJ)Üß¾–p>eä&X¦k–ìi±ËÌœƒ^‰Dá=¨ŠPj®xy`-ÇŠÙ–«sÀšF/{àRŠÓí5…9å(ï;@L)f)qJFAÊGç|Þ £™#ÄŠÒ97NA1U~ì:$Òí·yow9È)İrµKR¹¹ÕBRÚfbqQ¬Œ-¥# ïwìí×2³¤ÖV_í†"ªÕµ`˜@nR†œ/Í|æ)Ž#ö³ÊÑs¬)o^¦D$¹á|¯gJt™ãQ^7ÎkôÕ‰• zþˆKI%4?¸¡gJrºˆx?Œ§gæXBΖéÍÊäf‚ª÷Fiý5°9)¬Ý¸Š$Mf½ä¼ë°ÏHD¨Ø”I„91`ÁSU«§dØÈbI]¦å•|Ä5¬‰BTËÛ #ÄœXÏ}Zz[ƒ—í·¢3W¸x׈èä°„åÆ)¥wPÎåc2  ëúápÖKç»Álù¿iÕÖȇ$Dôý8/Ó˜†f}Mº §Šq ËM-äùQñ€’°v†ÁuýxzŽË”r–‚>QHK‹f@ÛÁu i<½£éú5åP7ÈkóJÔPU­µ*6èO=†KÃ7Ђ‰c˜i<žÉù¸Ì’s l]¡"IíàóyNɧ—O),Óõ s ˬíሾɅE8,SJi8œÊ-ú/ÜßVòz,ùª¶§®UÎN9¿[%]Ú¥,'Tí‡g6P—É8ös0Æe= D=½­:] §ªCׂÎáÃZ·š¶Ð¤Iˆ"vÃh`RC›ÐRFå÷‡Û+¢]U^ß=%èk5!Š""‘ºa°N9Dç»Áõƒ&üiÕ “ë²cÇižEäøpù Hɪ(ý¬åUœV˜FþÚL.©tÈuÙdœ´ý¥;órýV5ÛHzé¼3€8×…}Фh2®òÀ /Ó-Í“S¡»ˆäS#z8EáH®¤~<èî‘[v¥B7Ñ­g-R“S‡ §º ÒñòœRLa‰a.²DÝœïȹ8ߨ*»\6ÎêVVý]¾wZn’b ‰´ë™‚˜(»KŽ8wÐŒÍ ¶ r/J ;Ñw‡ÀÄÐ"¤BåK”߄Рæ¸Ü‘¨‹Ç,©ÍÖÞøM; €7òCU{•Ü­CÆïy°‰[ÃC…çž³µì¿Âmm[ô¹'@»,8kEÖfb ´4³?‘=Þ–fÀVØAìÔL”©]¸áljîjÕL ®QÑ%ép5'ÜÈmËQÝÔJŠi6w<¯úíˆ[Y½l®[žnœ°Bû‘¸ H3£¡‘0Ü(mâÙp-BtEÄÎ)±hÇ_öÕ³ÒÐåź?"I‡³P2¤%m´srÜ÷Ø6÷¢)ÖÕ[’Su6­r\‡>fÓ7æq”ÒOœóuëG}“9U-éöDÎ réÛ®ªcz”̇ŒÚ”kD*TnÚõUÄ&mPߊô€múâJ¤b`X»­&"yW°„¸rIÎsc6mNƒV01ꥸ5¸¨V’»é‚…3\9""j‰“†ô/œ"F&ØWl «f|AE=µX‹(‹n¾‡¬•ò«0Î6{²vGa5BÛ Z„9²ê¶ö¤p,oKÑE²cÌ ,!ȯXa.w#ËÂ\mQ5È·Õ‰ÇÚò­to«£R¹~>ax{pj~ËÞIi>÷ÕÿÎËte@”_LPçhäJ>Ý®‰Fžx¼ë¨¯lkU멤£mp1³A1ÊñcDn£^Ú^:i4.øüOÿµòïÍÉ^Yr8èçûõK9Ù%ÃkÕ ¹õeæñRä1Ga)’â6Œ;L·"V®›³8À•‘ã—y<½Œçæ ]+ÉkQDÎwÂéõ§éÆc?}7úá櫵gU|—9[Ay¾½f™'rýñÔ÷Õ¦·~‰âdw]¯3BŒËœtȯ™s¾_æI$…éjM椲~¤œPЍ|?œ_~§w1.Ó<½jyÚN]7¤DÍõèÞ¾þÔÉ9…´LýáÜNa™T  .Z›5†‘¤¨Õ6ƽ8j[Øp¼hÒá2]—éª'to)kî1£Þh8=ý0œžo¯?é D¨™øê´Ëa†)ƒ(·)äÚdÓó´F…Ççn<™MåöšbDáÒ“Î`'gèú4EOŸœïc¸9×uã¹l@ÖÓ¶Ó~ûùß óñò|× GN)Z'<.q¹ûþØõ£>fóýU-="òôñ)…ezæƒ:¬µúèИno­8MÝñÌIRòý@®ki%±1öc«w9$rë̱äN,o댧‹s~žî5†!¡sœBRTžïôù~Ð…Tš4q¾«<¿Œç—8ßS ªD×yæóç¿M)„ùf²~1Â"¶ï¤ßîò\É ¬'ID—~Ï)¦˜R\öê„s¾¹(Ó°ZΩ}"ך‘ô:ëi³pc8\†ã9Åc8^>X×ÇD¦—› µ4…¦ÝHŠÔ º±‘jå´rÅJŽÎ¹]ˆIm*ÙkáºþôôIç"¬*ÿù~Õ£}`ºalo½†6ú2þôÇ¿Gçnß~Žazúø7yr€¸Ü_o¯¿¬s'feaʵ¬Ö—*BDÅ ´oAmõk¬ÚçDäüòãpzvΉȗýLPi–6+60ÿü:ÃËq¡õ¶*BبŸ€ä{ò>-SicƉ¼vXý…Ñ¡Åäd$›w„ :PæÄ1tã©m“Ìp{ÕæÀ€ûFTiý€¶9ëážñ·ôK7¡q+_r>±J•œ#"CS\HM¥Øü’í›5ŸÓ›9ïn0ØÃw´ î\Hnÿy¡–™JøAm,°jË›M.:ŠX¼ü;MP;ZžªÔüxl·¸¶<î8Sw¿]$`}¼þ¸oÎЮ¦W&imà ÚÓ«À;v×Ví_üÌ­üwq eõ=Xu‘¨ZkdÏz[«!aà–Z׸ë±ÍGiFô€²A—RSm•AVì”{Œ+ŒÁ»ÙMR_#ƒ[/ÛÖk¿R(´a&ÛqMÖ5hÜÕã#8-×`åSÁ*½!¯_3P ³ 3X{ó«.Í{S Šm,Mµ·xì'º@«Nz×’Õ"nˆ\\ìÝÒÚ!àÇ·y¸p'Qý †ì+AJSg¿¶Z·yÚÙ¶T„U„ý¯$^>X$³à×9¡WW1Y‹·ÖåÖ íëuZ¸G©H·Á¥íŘÃ.B,@³ŒËUÃÿûýï8†ÛÛ/eŽZòq¹§evýá]¬C WÊÑ€—@tËý•9n‚Œµ"Ñì‚Ç=1Ìwß Ç§Ô}–i¾½µW k£ÕöN=’ Ç'N‰SXç~6^±,ðE¢ËËçÌíÕfÙľwJídzŽäöú%÷š|ómÙÄz óŸüS×´æ»Çeq]çád7l¾¿iù"JsQ}Çóç?–iʧ0ƒ )Ûuåzª½$iöNåÎw‡ó˜ïß8¦¨ÆÛ›…äÇ—ÏççÏÓíë2]!Ó× 0OŪ ¬IÖ$&0ýkÏŸÿœ;Ê=»Ç8ûnÀ,.˜Þ¾BUñÞœ÷ˆ–E§ Õ»Y#u´kêADû?þÝÎ)¤8 §Ûë×f·”ö±M9ÛoóçåÿHq1GD.su@¨hDHa vR†ßjG€æ Šˆ¤ñô2žžõ³ø®S7±pTD\ŠQUBƨ‰Þ¨’ýËÇß»nTvŠ3sº½~ÁÚá40fa ¶Öæ,Ã)|A9œ? ‡Ëõë_ôX­ "߇’VLÍýxêúÃÛ—áÃùƒó}JÊ ˆtzùœbˆa!"µâtý’—Ì÷·–õÂ)%µcÇç~]¯¯ã8Žã`¯lç‘èôôÑu½ß1ò¶½TüÕàÒjÇsc+x]õê3ÌRi “ˆ­9»Í€.6ëBƒk¹A;gasÆ[Lsûm~·ŸÓˆn×çQ;3Ö?·|Šö„^½ýõLMå@^M¥ØÌ[–y>öÿJ ×~å·C Xó¸uÕòÎÏ.".γŸ,Ê—ïQÞöÈÔ IDAT9ûÖZªÊ6¹÷ëu'Ûâî}B©sSfýeš±F™C}!¾²H×lÀퟷ6‘í!6Súêq å>VÚ‚ìq¿ÿ<–y˜ì”S¹÷NË&{‘êGÕ«Óì­#é­w ÛÒ¼TÉîrUÛþ{°ý½¿­»ÿ\»mêyž¥PÖZœ}¯–Û)­w/,>úLJ•YI((Ì[¶ÓY EÊ{Œj#ëì±XrÆB6Ömí)+«Ãîņ‡hmoµ×VÊ–(²Â Èc“¶©5ô&U±ÉÞ+d÷ªgÙ'W6@þòGÿçÿüßTkóN Ô«°û»Èyï;67h’”,e†YÖ«­I"\ks§'õiê{ ‰ç—’'§VtÕaZ~Òtû1SÜfæúRvíéR¤A6@­ÌªÑ› õãS"2ݾKlMÓ¹ý[´…õ,£“©‹;ßÃáœÂ’RàR²Ø3=1ù®×,4YÛ}Ê«‚Ûƒ°ôãœÓÌ&tÎbpã’ î6‹r¤ëDzÕò´ú¬«‚‰…ùøü :׋ðtýb¦&¹ÉîCIZI)hG¿ÃñI8ðr¿.ó½è6×U{žû"˜ˆ¬’09ßGMDãã2iWépzîÆÓ2_®pẎ±=º>žÖä=PN^¥ÚcO)rŒó÷ÿEŠótýf¹é)¦<&ÇVj¤ßÂæšÈ®³Ó¯àû¡¸÷Jú Ìôè껡Ë$zeþ­sÖ ½9+¸á)q;rFõüˆöØê„®Ü,ß 3ÇÇÓS»q„eŠ™ ÊsY‰©Œó¢öjUI/? sar}Ùn|˜oßnß~RûØüÞª|s3Šºá )%Ž«ó~ÞjÖù0è¼·)/V,sB„ñøL¾[¦7a)V«U󉵫.[È!ãIÚÓmë<af­® Ì“ê$Š"ц[­ô]r€{óZT™ºúÓj·“ˆ´K1"QãëOœR7Œ dy)^>þã2ëà’SÒÊœ8,…~å­¾(Nóñô<Ÿrˆ\áeº«õ«q÷Šbú-˜½¡.«S««LäOå klÈV**+g¤°X„³Hâ”—ej¸ÌwR}Ío²Ç‘Ä6“áè¨4 l=ÖO›È÷¡Á‡Ë|„¾?„e™¦{? }&2*›¢Z8Ë;üÜÝ!Ân{4Ûsv|nLí.ÖÒ®‚"–‚ˆN¹–ÌÓDTÑ^M°ïÕ/«Ð×’%$ȶlrˆ¤D’fì N@Òw©e\J䘹hÆZÔï‰5gæJT*{ìª •á—I±åáÔVEí?”Üþjç€;g@\$íÈy¶÷+óÖm[ÆKM’´µÑº.0ÉŒ°Ò\!£Õ‹‹YÑ¿Usñ®LF••Œ-Þà ~§ºÅ:ßÅöŒœÑì9#›ï–ßÛÒ46âm›ÐìkmAïµ!n¢ˆ +ÉBÅëÔvzòù[ êM›µ… /"ß#ã€J+4˾¦Cî“=mͳ¦¢0ËqMÞCËUQ.›²Ï¶g©b·÷R¯ZïNîyù-õø( ®Ã]ÙpâÞÛëÛe¡ÚÊ$ÚÛØ.Um(êƒä†‡È.Š A¥6­д¸Ôê…[d ¤„²;ÉÎ[eS`*{Äø¨o&«U¾‡I/iÙ‹¬·9ß6[•‰ˆ±åzj@Ef"Ñã1×”¨ ñ® ÿñ½]¡y%ù"jÒyÚZ÷ÕUš¼÷\b¶²#µõB´÷Òw=g!›RcX0¥b³‘éöM .¶XÔ\8‡ù^þ]?j£¯2•äeñ*·El(3ÙœbI&1UìGw£MÞcIšïoõ9ßd²7™ŸZx­?Œï@Àþ_ç1,SY%jGÕLöýE’N°²  ]7è§Ô’+*½G1,J¹*„C»ý´ßGçÆñD–ù¾}Š:®Yy­Lë‘…¥Ø›d8<•T@ì†C?ž˜M”›â¬c¹R¨AO! qÄ”ÍÂú{å«QÅúxPÊ9Ä·_þEIåö—Â")‰hÍa¢Ó²žmŒQª™k£%0Y¥br¥šéüC°j*`N¹Õ[, Zݼ¨ìíO 8Ç©Àh6FU¶¡xÝšT}ºÌ“¦šêéÄuÃárLQՌɞ”LM`N CÑÏì|ïU¯‹DÔ™(I„³‰|vg‹Û4ÐÚ¶«E¥g¾’~BrN„0ÇÅ•âÌØz»Íûˆ1.jl/™0"컡ëµûmVNi&Æ©Ó]ÍÁR›ùÓ¡…‹È<]µ¦!çüá)Ï9XýÚoIEµñ‡Ö¯ºs³ç”5Q=w8„¡¸B ºÊU›®_Uj®wÐuƒÕYbZ¬®åE¾Çœç¾LoùçG¯ZÞ/Q?Ú·µ¹N‘Ú¶G9'ÄŠE84˜Àì([¥&å´m65b7‘œÕm‡s^Hm£Ðå3©4"CDjý˜%2yØë£šRȱíª4ăH"Ò*Vßz¢f_ÕÕûuÙd¯ ívX^dY®ë%¥bæÄ¬Ÿ¦u¤ÖÓ€Xµ«ˆÑnÃÛò^œÙ°€èVœÞ&’âái°*™EEà ï¶ÝPyd#‡bé¨á}IM™¨Wƒ7/ms•Øå£V·$3ïÔD[î ÙQƒK†""uÑœ Ó9úþ(uR9æ3J~Aj+K¿`ÙÆI­À¢>ß«Z‡cˆ+©3„,tõ"Ôi”_ëJ Ô¬/a"¤Â ÊÝœu'7©{ Œ@)¥ö*g|Ù3UlºsyR‚Uòn1iv ©õ«uh5æ¹\Ðü‡Mé³åÌ®ª~luÒhvþ,#Ö>¤’£Å¤•ä«¥mKÂûÁ ò©C“”Žk3DÕø0'MjÒ?ÅÌÇâb?oš&ÛöšÈ&½f gïüJ¦ÄÅAlnÈ’yÙ߬Äþï´ŠsÛÄžd|Ûúƶ§òÐßO XÙÛ±6Bìy.4”bÚhØ×]ýÆí³+FoÄë¹#OØ”Ñ-þ¿)›ðÑx°^R;IÂI<¿YKO¾T7§¦Œ|6M²GO¤ Úܘ¢4³´,•Ù“þZ´Ë¶Í‚„õÝŒ¨íÊúrµQ€½c´ÂÊ×ÕºM;ú°æ·—3U=ÓFá ŠÖ@üfÜZ#d­"k $»1¨Ú‡p®¶@Ùk›OIŠYÔãrWZ _íåíèÁc`­ rÉÔ´ç\æ;&—'+Ú~: . |=CXÈåCa ›ƒI~Ó³$afŒ¥dl·m¨fÓDJÛÓ€²™ËFÑ#jðo+Z­ûY8kˆVKDðŸþ‡ÿ2ã(€cOO‡Ë­ê3ÌWE݈ðp8qö¶°IŠSâ\Ä(àãÓßþN!…YWDŠË2O´mÚÜå1F$× Ââ}çû!>0vÃhµpJšME¾ïúa8œRFD2s×+¡¿C$õD‡yF„n8pŠ­š´tqýˆ|? ¹~8ù~ŽçùöºÜ_çû5L×§ÏKäR˜˜S7^„c\&át{ûÒv®½ïȹ°Ì65­ZпeâÅ[•ëz FåÏ1 ˆÚ'dý¿¿0úéÚß^îWupo?¹*‰ìÐCÞ+Ñ÷"/+åUq—ˆË| óM×¢bB•Õç|Çœâ<écàœ'‰:GH,‚F5£’Xf€ˆü+Èwš¯Ù8+PG¿j¨O1Œ§§Óóçç—8O)ÌÚÕtÎQ sÓçþ¶Ù4Ј%ór'½3zï†ãÓx|JqfŽqžD8Ìs"EhúÈwfšÞñ‡t…¢[ß|ûæû±)Æ”CTüÕ¾¼[YGéXsZâ2þÛC·ù*œÞ¾þüŤÒ5Y`Èûáø„9dÍðœ$¥ÄIêÛ YFj¶ßN ¦q½í%½”¿\n·þüVå¡ô ëvη~< Ç‹®ê#sšÞ¾Ú-Ó|=#fp“ã»É0[ÞÌ‘SO/ýx¶}—ˆ„e.î´C«ƒK1±OÙè²}R\ˆ|±dln7|üýÄ))SH¢”êl rÞ Àrå»1(Ìótsnÿ:7ÂI1’sÏŸÿVù-zÃt ™ùÙœšZJ…wV«õ‡c× ®?"@ Sî™#º)†0g(¦–bÎês'aºç3(vãÉ9ïû¡Ê“AìsÒ–^9^;ßUI¶¡6Œ™à|ˆ§§OŒ0]I)Æyê†Qs7óׯ?)‰BD|5èb -û™¶Åmò×x ŒdXå!R“ÌjŒ™|÷°Ø¾²9ƒöËÏæ5É«=Üa@ˆ)û{ÇÕæÔÜæäɃôyŬҿ›ÃÞñ7ˆ6þý§ö6õ¨«•EPPÞ·¹4¦l¬6hÚñH<ÆG?”_Ü^jiîòjnŽ5€ }}«4Y½´¥œm±«”­r{#ø~ø¸Æhs$KVü¼m]®‡zm ¶êô\*kùõ÷*žÖÜÒ¬ $¬öFWw¢^ù-±… °1™ï¼ÃP?b…Ô‚ã]‡¿4 8ʲ¡kÇÙzz+-œÕͯ{~šËºÕÙ5b&YK; J±¬ÝºZضê´ d¯èA>ÚÝJéSäåÈžpäÁ¯¦á¶‰ #лÉ2¹ªiö5·oU—g37¬oìÿȦe;@™W¹m|”ÈÐæÖàJƒ'õakšIÙÕ¿²R@Òç+¿&8TßD©’+_Q6ð¶3TØÅ‚fáÂEScŸ»yŠñŸþñršj`=ª+tXy91³Í“‡Ì‘ó¡;7БÎ~t~0þH "2߯҈8ôÇêX«!›˜j?…I{eãñi8>µ» ÏœÔ°_Ò q¸Äo€ïDÊĦ%Ï\¹®m‘E¦¥fD/ÙÉtpŠâ=^^ŠTÕš™žE=l rÈ¡óV"hã!BÛ#ÕãÒ2!9ò"öãI™ÎVää¬ "=¬ÍM±?œ9qó“%§c[VÃõë_œïUIogF"=“÷T‚´r³Ëê ‘¤QÚ"ÇçO§çÏŠ¼ê†#¢½õ(=ßÞ¤ÞÓÒhEãîôô÷ôñ"Ì)”ÙmX¦•ê’tØÊÙ° ÌµÑäµÑ G"£m鄉|‡ òJuFq¾[ÇZ¡¤¨ãa9\^çÌQon³Ï¤²5èM×0ôÇaXSª~/а¤ÔÏÃáÜîAóýúà "‡Îµ%‚V*Ùö09µØ—>[Ó[ÐAaF›‚Z8Ft„èÂtÕ‰¯0þó¿1Hb˜ÞDX—ÓwZ+™$ ÌJýÃùƒ2Õ_—»ÁÉÊ3h |IE´Õ ’ÕmM îêʲ˜7üãŸÿ³c\nHtzþQ¡Û"I8i?)…y½¯#Ô®LVr"yt”r=Ñò]µ?ADµó!" ½EèÊ÷0¦9]AðÜl éÂwÃéåGDGÞs ).a¹‡yz|e¹æÖkhQ²]ÔŽ¹ÇË3’ë†#9ÿöå/¥Á¦u!7®UÂ@N¨WÜÿéåG¸¿ýLÎ;çS œB ǘÔ»‹u8º>ó@Ú“¿¤ô·‘ÿ—çĉó‡ÑæPv/ô­I©=,(:Q#|?üîïйéíKJA?•mÙédpÉ‹Ÿcdaç»bèòÃ!ãJ¤¦òbEøÇñ^ýÇ«Hâ9¿í”¶~õ„‚'V‰Ï©qP{µN0®Í¥6Âê]ƒjÃznk‘ò¿ìèÇRHXD匱V²mÚ÷¥óÒêºP¿ªk>ÕUÏ3oi‡¡2Ž©9†>©«VÓÑÖÊ4ܵùœhô6Ùx“sáVݨRgxëyÆ ›$ûåG9d5[¹Õ†¸û/–w9˜ ´)\nz8²þƲ[cö´ç«ƒ"߯եºV°ñ¿cFÚm*›aüû2úMŠ5«=SÓ„7+ÜÒKm %¤¸Ü¯_­R$"´ÄÍœí×À|ÿ–b8=ý lj€ùþ ëñW¯3gʉ´‹ÐuÒ$R”¬\Ut@Ì%B­éËvO9!KXÙ:‘=^^4ZY”È‚=×3é†cÙ‹R4I§>ìãé¹N!L…¿:߯Yj‹Îû”BÒÙžH?É÷Ãá¬h - Œ0̨DÖJç[ ³…’ÓËï$ŰÜt8GŽ9çÈ¥”8EmNHm⑆óÍ÷«÷½ë:ç{ç{}½}ù‹ŠˆsN„5 Ñ)Ú[cª2›Š/‚œWœ·9.t‹ÖØEMŽQ7)»)"Ì‘cŒa>>}<\~"<]_EÄy_Þ,µ¯®võ”·6¸Õ¦EXªU[eÉ„£Çˇþx ÓU„•ÀbËùÞ€&Jg.¢Å"z":\>2ýØ1,hqö9  šî(Íb7¥é'iªâÖ>ñn#ÿ!Ò¨Šñ,qœÐy´ádÐ÷ú­S9;§æl¾!ï¥1 6ÆÕ|ND5/ÛB¶}ZÊ8P„q—TÑÞ;_{)3£`­ùÖ ´‰W9é·‡}½äsÈ ¬4=é·Ù„ÀF°*ÓвlL”Ý ÐWmC°¤Òhnù­ÂÑæ¯g(²oAf1aaTg!îôÐ0›ë(j*gÕf0ƒÓýòá{ëá¯']=…¹5¢ÕÐFÎUA.³Ç”õʳ’N×xžÂ?iK%¬Zt晉·ß¥rÇ6‘@š¶:‹eæÓÓ"¬3$DçÈ/ÓUÁN†îm>•:µs©JmµJDè<ç„&@tD­Öüñòîdæý˜L ÀŠ»ç»&¼#RŠÑº±DÝpÔ Î÷0߻éëFm~¤Œ=MNŸûYZlÈ…ƒ2ÎZÄÚfä”’5-Ê9”¨ÆGç|ç\·ÌWl²’·Ø™­ÄHζáñøTÈ)a¹€"„0Á-—½œŒLÍQ·Ž0ßsŒ,圾F]ׂ>q:ÇÂl:ªVè’E*Õ㬪Q}¿Æe*aIbÜs[¨eƒg1Ss#J$U´# à|{“|£ïGÌ}š ŠO›QeÃY·À¶õe¾¿ê¼Ü¹ ûáÈl徤$•#)›:çh¤sºÃ/JÛPS  8XÔ\˜¼Ì÷7Cº#žž~Èn“Èœ$‡¶¹1¥«Xúa ’‡¨¤4NAòE³·@/ñVݧæû·Ÿ²á ´<77Qo:9/¤þ9 ‚ïœïaÑè1€´ÌÐG=ÖÅ)úÊ rikL.çÊfÞ¸6Áâé$êK†…Ãr×~~µ)k ^(6JYÝFVêƒj¯<ž§7I1¥À)qŠýxt¾Ç}Ì”ÌýX mVàe?j’y9¯{N‘ÀåÃ… (& ,@©?%Š@4çO½Ž—)AËAÕf‹k'ò I æÅ=š;‡=Î/崛⒗»w´›šÕ ,’@ka7˜›…A­ô© gP3Q5bÄ—g•9´éàU€ž¢"û GŸcÔP{ß "–Ùž@Á—ÞotÜ[„ §¸Ä rs× º¼¬cc˜®ÒA¸Vdp†Ígë®o“Ԝ۶¼‚2¶Ã@X&ŽÑþSA«óæ0_õ¦¨«¡5xXë_ÝZëBP7JIDˆ¼¨àBÈ€䴈̥FÁi² ‘¨ÛžŠ!„“êJTÿâûƒâ*ÀéÚHÓ}™oýáìûCך·Ö”«è‰é îXÖÚY0'æDÎåz.«ºv‰­(4ŒÝp rç{ç¼±F@ïÈüô· Õùáô”Üy¾~åf\—‰ch³©çûkU|L7õ ¹â"BÝ21.w˜Ù ’ìŒÍôºy”œOZq˜Ãr3«À– Qú¨ùQµDÉÚëÉo€ùöŠäÈ{$×g} pB{+çÎ5: Ý:t£P>*Ä Ûˆù&{DÁáðã‚ÓU2lA¹3 šWgî/Õ’h¾] s®¥{×õU|ˆµ~ËýUû%DîøüÇÃæÛ’m0ê>tkrøn(ÔP°l>?ÿˆD)-£Ú&p HY¢…¨ïKç{ûâ)ÞÂŒHÃùÙL#ššËÖñ(Hö,ç©7È÷Ì).÷·<:™\§áÄ„…c çI5Aù¬†ãÇJÎl(cNa¾Å¬`ÀñôÂ)†pæ0ÝV`í-÷«ƒPd¹¿•ñ“ˆx?ô§'§›yJ%Œ^74D\¦+§Ec´}wPeh5¼­Û/ø@¨ú mmiE¥òBs2pJœ :›¢x-Dn£´[˜ƒÅ5€>ó¸a¿¯wòžì¡1rSÇ*½‚ªùé¦WË-ÂÊ•h1ðF —B­ÄªÙ}Tmoj‹B¡ü7RoµÊBn|¯uÜ€­ÿ\Úþ°ˆh·ªøL2¾Y[”I=mO] ¨{®sÚ±CVx¤Í}û½òÀp]¢æ­4ع&uÞ– [%)R©°M[£0\H[ß–JN˜ ¬£Æ¼U*èt4¨'3E¸Ùn’!^À­Ë”V°Ä‚…;Aä‰\Ò*ª¸ °Ú.ËLKñ³I“ܼ޴°Qk‘!±‡ü‹šß[ö-6!ŽUÎTó.M"J+ÿ¯ÁÚÒæíU„D&wc›Lù˜o®¼ aÜœ-âkí=JæRåˆ ¬÷XìAÂ=YÅõ’+Ôܽ³H#Ù4þk0h–X7{ãbe5h¤icβ—¶€^Ùÿ7ž‘õq‹mma( IDAT9¡m­\¼=EH%¢jñÒítHèœîHå“jCY?€íz“Üô^’ ˜#'WÄ)Ë€…û_»kÛ»€yWúe'i€ö©•à™G…è£!ëÕùÝѨÏRJ"¨ÞP»&6`»žu¼*4mv²úÇ rU¤+–¬ùN–J…ԠЧ0ߦÛ7Ò>êJW'£ák]ʼnS°:š/WÐêµ×þ¡ïztÞw}“²íq;'9Ä¢,7¼EJ®H•÷ i™¬Óel TU…þN‰œG‹H.2K&çÇÓ“êÔMã=Åe²Ê¸Y<%y Å…Ãâû‘|§2r–ûµlýpÐLD]ë¾ë‘Üáü¢ÃpXÑ>të^b˜ÚT½óËí•St9˜SÖâxzOÏ÷×_æû›*fNÏŸ¼Âüæ\×."éÛÏÿâ}ç¼Gt˜/šµ5éÛ_ÿÙuÃáô¢O]zq¹ëÁY˜uÙ„eâ”úadfUí§0;çs+ãñ¹OóôÙZÚõƒ0ÇJò‰Í*r×÷#rJ"’–;‘ï‰ÜÓ§?¦8§¸è¥Ô9¹Í”z£kNM ÂHD–¶H±œñÕáÄüñÿ©Ó«Y(År0›ús—û[)ïNÏŸ¤ÈmRôÃq8^ôÈyûe¾½Î‘H8ÀÛ/AµêˆÈx¼.õÙ ó-Å)†ÊÓKWYƒ-4••°ÕGµU8çÇÓ ¿þôïõd"y†-À*1×ú¸PúÑõ£¤¤ž9^~ègíÂ-÷7³Üˆ¤ PtÎ;ïSŠ)& Ó„z«bA\7œž?#"§L©tóä¼_'’7~_µdL"×9çbX6/ mP§§—ÓË1Ì)-aºi¡V^w¾ä)"öápŒ Ûfß>}üƒ¾œ˜ùíË¿æ—Dyejô7joF£c´XGŽü =¡Ì¬°Ü9%­3•µQ¡4ŽKŽu„ÍiµÝ JgœS$ïËvôôùÏÂI8 ¹^$ Ç”bŠšä/II$ž>ǸÜ_±¶¿sÎYܼïGçûv˺~û 9_ª½zªFäeN)X3Ÿ†[~B×Ýx Ë-.÷”§d´mˆ°îÿº—‘î°óý-L÷þp6=¿3—‚$ÅЧñô$)ФyºéÈ©ž ›ŠE³_ÌJ¡t”bÿëGr®ʨË<26°óZãë¹2;‡ó3±´)g¶Í6)Ñt;ð2p.êQ·æSøõD;y¬ä_Š5í}ã2nhb¨_ÊÓ sz?÷®¢x¡Õ䶅ѦYyF«#%®e%Õ´±/5o¡\yk­ÐÚ–ýžVWò VX¯ÆK³G—‘5ä³dûÃÖ¿Ñ¢AK†FVEU«ö&æ2Ÿ-×P‹‘âÖ•¢oâ…±‘øWFÙÚnaåce+j(®ÿ¤„«ïKrd_ˆ´6Mî%¥`ãgÀ•EdœRŸÇLZ( •ÕñsC¼ƒÇÓ8lŠxhÒVwG¼6ò…ó»!¿ö¨„‰´eðÎÞTì +% ¶uRý’E/¦"á†tˆHúKw$l²B›@ÃhYRb“É.»¸•„lµÙgíX–ž#á*ئ|Õ5¯7þv×€µ‰åA% Ö³Õ‚uc¯ïïg ^Bjæº!møOÿøå~kð2]—ûÛóç?/?¨)ÌW«®òXnóƒ¬ÍýøHÔ23sO/ãù¥`ãÊ=[îß@ ¥„ÝpŒaZ¦;6»ŠºÂUl’ÉÅ 9C˜•ŒšÂœâ¢ý=ÓŠ¦èûÃå‡?ädâ}ðý˜Â’blïîíÛ_-‘\2hçël=¿jØl$¹9”îÑ$ƒDÇó³žÒ2Ç0«™W]¶é¯ŠrÌ^ZV—<ýðrGDõ¨.÷׌!5C«*Њ¾©T†–Õ.Ãáܧ®\×ß_N±$Ý?<4‡¦¡ÝP¹/?þ”ØnÄfu2è]SNzÍ4=GØ÷ç®…Gäž>ýI·•°ÜÂ|·|vý æÉé÷ ˆ„DÊJnÿ¼ŠVCd5–E£:0‰SòýAÓÞ!Wœˆ cµ|hÁTмBôÉŒºt¸|<^>¦8¥ë·¿ 'KîDÂ5w~ž2‚\˜?ýñ²Ù®_þ%—òº6 ê­ l@ ÷×ò^T¨X9¦ÓË's"º&H)ª”F62ĨCëL§3«¹—ÿ„ˆH§…Sóƒ p<]œïR²˜”ÁæêðÁ öZ·»pJaÑÕÞõƒóý2ݶöâõSi¥XX Û,k1 ¼ûø‡ÿйâÝη`Õ¤%$ ËÜv¬c˜R\´›–‰¯ÄãH,‰Cèçþp^?;DRâÒÛóÝ Ë$†9†Yµ%—þ¿ýõŸÃtë—Bµ¬C8ç*µ£Xb\Œ OžÚœGrãñ©PTý"ËtSû„4¢J"OÎ1GNì»2“º‘Ïór¿ §dŽ{ðÝ@Î]>ü‰R ÂB®cŽËýmíð¬·Þ5R©õzÐÝ=êJFóÝHt8p¾OqB"¤N˜ïo¿P>É•¶‰°˜$,³^±è Î+æ%‹Ò7ãrfŸ ®§$ÀêÇUlÕæP¼£õGØV!h-VYÉ€¥%×ói98”1Iô7¾c,Ìî÷1T¸9WÊVànƒÃúÁêö\¾ aõ}¿Ã•^yq¡õ«ªã§°:—®]—ŠglÑRóoí=_]ã2•4š6àÆ\‘÷½†yXEÔT?ÓÊtŒ+ZÞN–úÊ¹Žæ `•ô6çX“pkÌ{ØNåçÂ;7ºÀÛ°Jñ·º··’ê¶~×k-ÐØí«{&O)¶ ©GÏï8R~³e}mÑi›Ey›1p`(˜ùWQà&onªí¦/òýø¶2½´DÒ0wjGÍRVõ&'àk×0Sﮬ8Ù˜„¿¡ykFµ—Tß ™ÅE[?w­Aóûþey'CÑ0 ‡MVIê`8?ò{Ìù‡oZ%yÝbí:¬ú© ®g±yæ× š!œ´áõöžhÝ΋@µûËw=ZþnÃqUÕgHK! òÆ_‰’é\(Ì ÈT´NÌ•ªÞ¡û¿ý÷êô$BÖ‘óÊQU”FÉgNOWtóJ¯á³G>1§¿ÿ…£ž }7rŠªƒÚ ´. óд‘ó”S„øpù¡?˜*…YXÂ|µƒjyÑ™>ˆ¾KA.:gŠ‹±’CtýxêÇSJ‹©'ò^îWãÉ5Û¯ª½sT +O±„y§ç/@äîo¿,ÓU[ÿ‡ó39O®/r›éúm Ôô8°¤Ùå×u–àÛµ:^>öãIg0×oMaá%Š-f¥Ä²øŽÈëw/y+fx ß ‡Ëñé£~’0ßcœÍ>_›+ƒÄj˜ÁIk²¿û»n<*Ò),7ŽËBŽJì‘daz q§kIPq¼zB2–· ³¸ ’;¿üÈRŠq¾/ó¬M ¨9VÚÅ!ÒuXÒÁÉ9"Ϲ`BÄÃùƒì´ÈæéúU÷#áä8è°ŒäËÁVƒñ4/…‰SÒ†s^!òÝpƳ~eÞ¯_×±m¤Ô4fn ¶§°€l€|ï|7žž ÎWfiÊ\)rMSY§( £4Ìq™TM÷üùÏä1ǸLHx8½H1ãQå‘­Ú–”XÞLÙªÒà•×È"hÌõ\o’/rŽ•†€@Î ‰cy@¡Û£Â¬WÇW"g$òŒ¬CéŽ^çvœh¢ãH «lA„Àa“UÔ”‹ÔµX‘²^?ÃøÛëÙäV·$ VígD½¶B±C‚~·ÆBá$ºi9¯çÛû8 é£Öõ`«ÝZlNªï俯ªù2&’]Vr´×s¶=àÛªèÔóJmÓ6LS»­ÄP„e l?·!S™9\ÖÕf£#†:þNM¸NF“Úª ~ÓÔPÀÈ9+×9±rV±P›ym;øÖEÞŒ^ö§‡ë'82wÂçžJ¹4¶ÕF@ö"D³Šï¦Æg1…õÎÌužÂŽv ßVZø16攌âl_„æ}Tñ¤ÔIŠÃž³^·œŸñõ“6œL07É€¤¶’n)Äy5ZêÜ: ]´kˆŠÆE¹­¨tñT¨¯h^ àJǹ!ˆg}ž«™„µ^»ÕrçQyoÚŽjÄ´¸z`×(h†ÃE“¯Íyl»H¨zÒlê/ ó §MïžWIOõÿÓ·ŸïoX_ïz*OºAé>«q*¾Äu¾ËãÙD&: Šæ¤ÖôðùúMk Ë¢¿7©\sÕµ³®=”Ô¨ÕäûÁùã¢<æ›1|ÍšS„†£VŒz¾‰úëNϟʨ2¥ q¹³˜Ï·ýE´@>¡$ÉÜdÆ $!0d¨ŠœYDBΟÒ[®ç,ï;@êúÑù>†E³vE˜Œ"AÅÛŽ°Ú’°&«‰ø~ìÇ£žÍuœ°L7u鶦Òé*ÉuŽARÒÃuñ·‘3´x tÄ)…RÈ'ñªfÖQâ2_7NÏŠ çØDYéâ }Ì49œU*ú “ c솘D³oÈèM-Kő9@,ß<£¤Q€4µ¤}å÷ú|u`\Í 6ëŽ:C;•=ëp92§…Ú$P–ž GÅ+&J{ÕH^F_*r ŒÂ]?äuU* 8oúÂÔA¤¿|DÄfÎ /.·¼‰ò†Q‰MÆ$§$µ°¶¯Þ§Ãù¥lF•ÆÂ,À)Ë—gNv=4Œü0ÈB€YÙi)Å%ÌÈÑ'ßù~ß“÷&Ï\î߀¡€ï³]8ÔätIz4ÏçòràØ:p¹¿eÓ^?Nåí¦+ el!Drö!§&°×ù”Âýõ}î\×wÃQ÷CK‡EÓäí±nÊwI1qr¾Ëùü±›½½lô)Ì3ë¼Í9oøxN2ß¾©,#¥€ÎyrŽSäìå5`–¼j §˜ËªïÊ9¬mØú ·¤\µ¨i-hõ%,ó•â¬mÍ£ïgf$'žuŒÝÚtÐh6…”,'9_Jʈ­jZ“ž*\=„ ¦\5ì0CO Ñx|*áÕÍq  îCý"Óõ«ˆs’`ÏWœïp¯rÖ°_No¯?ë*(BšºYé‹p¹¿¶º^M(Ö‘©¶PÎDtêRðýI Yn_›ã ¬á¶¦Û·ê¾HÑ:-Ú%“ìÒ ô%è@RDðàÈuÃp|â0Çp×SÛ2]9À ¹ å«~6åDò‡ãÓzø ó4Oo)†°Üu¹-Ót}õB³@Òaì9?™RC&cþiÚ"ÖßíºÉùþà»^_á!ÌjY#Gå·ä¡žkÅã¥Ç%¾Ž—1,1,"1…éþö‹0V ’¦ÃJ’ ’XD†îŒˆ![È‘Õý«’>,÷œû ¢ç†˜½aÎwƒpJosv•ÊLç=xÆ0‘Ê2ÐmTä€@‘¹,’ÒW‘¸Ü×ÇY"ç¸Õ  R7žêÌ5wä¬ÈoÂwê T„c¢}MdÓı?^„“NéSŠD^ûÜZÂóáü!†YÓ  ¸‰•[hy­Ÿ'ãj¥ à –"pðý R‹ü1lKêÆÎ‘wý¦«pRëñò‘î×/¥;æ»"H6´¼ÿ²wé•-YÒ„ÌÌ}="b¿Î#Ÿ÷V]¨®‡˜ñŸ"5#@H 3ëfB 1`Š„º BŒù 5”¨êª{ofž³ÏÞk-w7c`þ0_ûä­l©+ofžˆkùr7ûì{t~lmbú€ ç§ÓãG΢guQaÅâÔãׇâQJ "T3"XÎ",Afæä<…M;B—m•D·ž?°D¶r÷ÔňƒÏÚ§Žip=AÚ–WD<>|Ð*¹>Ðmy¡–ç•c+´/±hÜ|Š!,gt‘Nß ãA[X‘™Qñ½t#"F71?Œš‹›Ù °˜V ›lË0~ƒä´­MqcΡ}º nõãÖ‹¤Õ“ƒ«”%½ffˆ–iŸi2MΦkäZ‚ r’2›ëU8¯“ÓÓ·œ‚p¤¢d¨à|F¿‹ ³¢çDΠ]˜M-œ- ÊVf#& ÅØêTc=Aà§ã½ÚÄ”bI);@…õ¢Ø8§¸¼~Öú·Õ‘ :–¶µ®%šTDÖó‹Ê¬ñVöŽÐ(¤~F%–]12dÎ~ïúõ,Œ–Ý‚Án$J£±TØ)«eîê@H¼†R<Ô ¨T÷t;éàF!"“Sª$ ²s”2!oÄ^k|¸Êm€BÁÞv@`'x×¶OÑBÞ§K÷9}_c”`AJL¼@uÙ&Úq“Fk ¼0WW'íØÎ¨w9pZZæÚlIÉ,E$-צhÒ³9Ì.ͧ"KdWŒ¶¦s]IZ,Û¾ƒ‘)ZÓOt Ò”þ¹—Ô«jn™tšMÅä(’ZzÓ†VvØÉ¿&›w¸\)çLù]®B¿Š„™‘)EnnÈ)²µG>ÒñËo[ ¡uàÕp±üåùµº¾«bBE,L œçGf‰fW Ò®!·¹joVY´ìì±8©Ž Fl ×½²Ð­›«®mtT8T Q+1²Œ*¨L¹'•KVÊF»°ª  41ØŽàb+©Z®XÁy€”5òUÏ.­`B=K´½‰I‡Õ—ÉQHl¦˜…)MäXݸˆ¨˜\ê§îÌ©8o4ûºŒV:gñJ¼ÒE`ç>ˆ] vµ¡îF :®Ë\¢òËôlDm÷»Þ¥E¿ì=F,é®7úè>îÿøŸþ`1sX/PRâD”ä Oßý턉aýòó?،ȑ#}ü*@V¢,ã-†o)·Jg'DX/¯ªHu~<=}«Ë7®çåü¬èPé—ªiÚ´‘§ˆ¢.ˆx|üèý¤†1,̼|ù„ŽH_MãÛñ]µÂÕÀ ã0Î×îEÑ1™Š2? OÎóFU…‡å szýü3y?N½â¶m½¨0ß¹Ñy·­Ëns×ûôøMâÈ1ª¢¿toÙ-5[®[m‹[ŠQÅ*=þøÛ¿aŽ7‘L ×A­ túX¯`9¿H ¡°±Û]:=|’]?lËKµàjè\X8D¼¼~©+Òi&{M&±Ï±d…»a<Þ€°¼ÄÂr)!‡]T4‡‰0Ì{JNNŸÜdžq~}þc•½“ó1l,—yF} Ä•^SAǶ6òÜ6föãì‡éþý÷Ù¡0«ð§M]-È©9f§D9Zš’¢/ä|\—ì/Š­M‡žr®’°ÑðùV1Ù%Âq ßüöôø1,gf¾¼üR³Bõ y?0'Ž‘¼ÆÔEXÔJäpºgæ°^t“"rÎ9ݯ¶¦¸ÙÞ®¢’»ÿ)˜p½¼Äõ¢ì3MÓ¡e¼=¥VÑ×ÇßþU† ëà 1†-jœ§MDºpŠãáT«Ujçv–§·õ›?ûVŠÊ"aN/Ÿ~_yÿä=‡-åIm~TFú‚Ù¶­ŸÙUióÛ¿ŽÛòúüG»ÑÅ_·i~úæÏ‘\X_ò4„ÈùIº/© AY9ö•ëH‘¦ã£~ãëç?¼~úÃý‡ŽÖ×O6BN8…mÕšàú2>üHÅÛS`NëåKEhJ;—ת‹Ì¬÷ГF[ó™þÙßp I•3—W…Ç« ± §JIK/ŸþPç ãáŽ[:nS÷#"ù–²”•ªqWKÕ€Üð‚‚–Šlÿ¾aßÅöº#j !J'~x«ù3ål¾eB¾ß¦n”*lÈ‚Jƒ0)Ù¯üv±î—ÍÒ§Q¥Y¤zvsÓùæ3¸¥b)iötô7rØûj¶$n­½£÷Š{v\®¡š˜C5(ÊÌ{¼9Jª*qÓOv¼­ò¾Á–¶lu;S3¹axƒ%Òl¡0[Ô’ÙD&6ô¿Fßèšðk }ëÏnd¦_:E¾-ÝªÏ xSBc#`?¶0¦²ÃUjõm‡Ö{onëpÖ‡®eÐ"wüµrʪÙÏ]-Ȱ¿òµùÊ×ä÷³° ‰õRm߈«\3$»µ^5¼¹ÚIëÌÏíìÙný¢ÛÏËv|í*Ö`ÂŽˆÂÊ+Ï×ö»¹ŒØy¤ÒÉ‹Ga[E9ØRé÷öŠ‚²‹­Þë&¦rënHBX·‰Ó#þÒÞyáºì¾»^óÈ}ÂÞ+i/¦Ùƒó˜›h2hý™L³þDô?ÿ·ÿ 9?Ì'"w|xŸÂÃ’ -‹‡z-Š9E*ó •'§¸a—‡"ÚC0#ÚèWjâtz§S —œ|†t~ùDHè\µzÔ+§ÙkÕ¦y:=œ>j‚ü8Ü0®——5‡evÅjv…%° ¦Ãý8_~ùGžïž¼Ÿ¾üòïÉùa:Èrþì‡y:>gõ#¨Ìluwã¶ ê¥Tp¦°œCX”Ū¥+Æi¸sм{ûFív'ôã”›¥†#" Fò5{$NZºþ3Uz!Ê–RT/%ç}F/L{­pnѳsŽá®ÖØË’eæˆ7jÄÃÝ#¹!l‹’ªEd;¿€Ép~Èa¡eƒ¨Æx’ ܆§Ý"<|üfäë¹¹d)‘—Y”H¥,Ä|7ª[RL I‹<Ž5|>=j "Èï IDATïÊîä­ìn¼`înïÌ -hõîû¿à”KLnÜ._Î_ríìœWô’ç/?9çÇ@Ŭ.ÆÎûLd·®=ýþU¼Ói“šŠÁO.jœw~øæÏþúõÓŸú;‘”Nï¾;Ü=¥¸é«)ÂÃt”Š%ÂRHƒ‰SRr¿µÆƱòÃJžÛ~(n+€ ó‘È©}sá£NÂ"’˜¹&²‰Þ¿ÿ¡z@oËk¦³ª“;Hcôe6uΜˌ•Öã±5..&ˆ/–Íꬭý„eëÄÖ¸ˆæ*ä4Yo¬q­8ß="’b$ëåµ6|·«/=«;:(k˜pµÄ¢ÃéqœïôNjRØt8• _^?• ݘbÌc­Ìž®)l€0;rM]ÍÊBõ„»ÂN4`Dó¤têœR„œÖ«òƒ*öΉCLJóñ^“îS\Eäòò»®ÁdˆÚ²mJÈOË™rÅ£EÔJ%ùq®³?ÎÓán¾{Ò ËkŠë¶.k¦CíhSŠT+Q€ÄIXûã|÷¼ $"ç%%–Ñš4äVYI· D“\¼+j„Jæ‘ó”¨A¼ÌB€Ò)å¿^ØWBu‘R Q¦ŸÙTEèàï’%âÊq«Þ9©¢£ªÇD$¬ÐªÚv$e\îU¥3õodµçê ÷%’B°Æœ üå‰Û±K»Lœ2®:ú¬mç$"$nGìnÖDùlŽ•ÿÍœ€p®YTœ¢Bw7ž¸ˆÍX`ib*1ŽJû´úܪv¼Gh‡“&Œ·8IÔ‚ÈÒߨJш٠,Ù3Oº¤Æ,pEâý•)‚”Á˯uZ "£Ù¾¥ Ëv }RKÆf¯Ž•+#œnmsÚ‚®²°N=!M¯S áB¤Å¡—êC&Ð Éo I¹&oß¹+Hˆ @vÝp ÈeΤAÅË*ٯŠWþµ‰DKÜ5ÖÒõårUÀê/Ë<ÝÃÔÐ Á[ŠËË­y5öD¸28ÍT˜ÅÒïË”-@³ñÐDõõG£°°Pp³¢Î¹ùî‰ÈiqJ¡f·îì‰wbXCA$Gå]BDpú3eP±ZåeÑy ØÀ°.1lZs9wD¤q:d¯>Lœ2“Ûš¦ˆHë;YÏÚÙøar÷KŠÛt¸Ó¾P½¾4æ ‡é˜—¨hða=KZ†é„ˆH«pçS}»2‘’ÙXTåÇY™å•áœga."§ë¿l9Ÿ½Ù´ÌÌåN–Ï9¼|ù%ÿBrƒóã0Îä—}àr£¡ïHÖnöNi)F-€Jâ±Azˆ´sÒ PÜEÑcåÑ`Ô¡)9?!Ñç?þ}X^ÕÏšSܶ˗m½d!³gРéôªMcVs)Ür=0ï†Õ¨]D#º,4—'@¥BÑ@ùb é`9?ãååîé$†Y„y=€8§Ãô*ªzÙÆ Tá9²Xît”zUge¦~ ¦FπǭŒS@5·ËŒÿR]…õ ˆ)® 5¹q:’ó>Å-lK‡ô”ø§òùÿ!9­Åô]Þ./q[¦ùN-Ÿˆ\1@Dœ÷Ê­ëy[ÎM.YŠƒì‰VîsÙX£0"±p,ã4ïkê2‘säp˜ªƒX1Äoû³uLE ÁŠÄlËùY;M n$¤ÃýkßÏ9EI †éˆè†i® ›A”03gw?jäLyù¤÷6…-¥0Î'(î-·²J§Z‰GèM-¬Ñãý{V^óúš5òâÄ¡Î-©$VÑ@ UÝXiÐA-r]³æ7L2Dµ°]Ò¥£ã;?T¯‰åƒÐ¦˜ƒÝ0Ÿž ¬/yÇŒaU’ž.8?N*w3$pŠB¨ÏÞ¶Äí‚䕊¢›f*fw"ãq¸+âw¬ eÙ–³ó£št¤ã¶,çgM-PÓíî½â„ˆzW[¬]q<(Ô ú¤oa.Ö<Õ}‡ù¤—± 'æ˜b¨uÓŽõ€è|9G{š¤n%=CÏŒ®]cfL6'lƒŸŸq˜fÔ`ñò4[8 §€'RXY€Ý0ÎÇû §Öéx§úã8+3}˜q]bXý0Ó!ÅMVƃRþ8Åãý{EÞjØÈFMÞK ·@ÑæØ`æ(å0pÅqJG  //¿wODä¼wÃè§y~Õ{ÁzEcŠ”Øù¨ÒÖó.©ºþ‘õõ3§x÷î[Dò㜘Áy†”Š­Òšô¬¢XèD©†².èœækg…UákC朻ᙡ²î}áX lgÍ#Ô—×Ó0Ÿˆh˜Žëò’¢ú*§¾ÝI׋ÊÐ&d[^8&çG"?N3Õ³ˆÆÃ}åøÎe=Wfi ã”z!b©Oêè*îŸ8Aâ’*FŽÒ!ƒ" ’°ØL×ô3ic²Š½Ú7ƒ%Ø6ùÁ~î]½oÀ¤3gI,PöÝ/äˆJ:UÉÿŽ&[ a°Y6ê¡m9·’:l%—\""¢Ù›z#ìóÞ³¤E ˆÈ¯Á{Xé3"WÓï~$\Æ×Rnfu9TgH5à(îØ ifg‡¦aÎáºJ+/”îö5E0—¥s9‰DhÇí"Y®í 76âÂØŒ0°t®ž¹â^Ë_3®úœµ&À(?¹MןéiºÁuÙïrsî ÇUo€7ˆ`×¥›A«EÆæÊ&@R‡‘RèNU2eô×WTw¤fë¹÷©¨œ2I ææÌºðš·õ q–€uÙCÄ"fµ—Åié±ÐGÐ/õ+!É€ Â<ú*3HqbP‡6&ò L7UWÛZ׌jN€¶ûBœ±"m÷‡>nåVkƒ« È.é‚·©}AsŒÐ7¬A¼­«úz›Å(»ŒÈÌ^NVá§¿ÅŠ9cÐ=3„“Ãݺ:ÌncÆÕú  ·ÎœÇù Ìyf^„ع¶NAÎëaùîÛß±è<\‹ÎËùY¹^:¹ÍaèYúìƒT1ooj̈ôðáG?^?ÿ!Çœ²ÔÏÔ„áÈ)‚5]LÈ¡båJq~,cùhåHt¼*Æ¡[ [ÊÙHDÆÃËçLÏæË˧ |™%^™Íˆ°iÞB¹;Î¤Ž¦õQƒ«…ÙS]·U‘iÐJ¥ÈLô$‰?þÙßpŠœ6}S )l™C¡RC¾·¶wçQˆÃ8¹m]$K¾£>ôT0 $m –ü™ïÕÇ«ô1¸-/"B”ùD)Ååõ‹=­•¿žOZrˆè‡‘ˆÖåÒô¹~`N©8‘:?TI\ÉBsÅ­jOlÀŒÛb]­ýtxúæÏAýlQ‰ìaU§°­õ -ù^GÂ"œÃÖSdfîç»~˜îÞ}×äzˆ*Я¦#„õU8­ËÅX…%-±Nždk0[†ËÛCÁ[ó¹ÌÀl¾}5 ßÒu+I1°p¹»§ït—OaKi ë%mëîÐERçE¬{ðÆŽwTé”Ú(‹‡Ûù'€Èòò‰œ÷ãL~xÿý¸œŸ/_~)ë“5z¾Ýäq’”¶õ’E;ΑÒ¶ÄmõÓLnÔN¦¶zÅ< ü0’säǰœ·å¬ÿÓ¶FNÃtpÎ9ïóºI©øÒÕ™Nbæ´-êº|züøøñ7Ñ8vš1†í,Ìëò Åû·F£¨*¼À<¤Æz3Éùwßý®š“qŠ:Y^Ÿµ®¶2~˜$,MÚ!çœsjòœ§òS­2,ï §è§Ù¹!w–œDdœÂö îõ' v>‘Jlʉ )¥N¦%¤Oäòú¬LY_¿B™+]xŤF;¾'NAÕ2j®–³æl‘&ð<|øQ½åüy=?ÕJÜgç x[w,R³ÐTÁbM}ªŽÑr7 qjѤÜ]IƒåVí‹;ŸõL¯¯k#‘cí_ÊàJ~Ø}­ƾÙÜE!£ 8î\è q«6Í%ÜM‘þš@_`“¾¶é´«VÒØ0UkZâ.ÅTƒ;÷;„?A¡ýÆÔ?7×rí˜`ÙR…'‚Ðé>jç³ÛĵÀîÈÿ»=O7«Tט`6^(ìÖæD…²Ë¢kŠR¬¶·ðõT4T«XÆfêöæb“DÁh‡xLUò'Äííò¥DI߈q0½k“(W\®9? Ð3® ùz¡ €Î­n ™vp‚a‰;$‡èŒ…”ŒˆŽ6س˜¶Y׉®ŽÕX‚›½´Fh–3 ‘Nˆ4iˆ9`Ð>{QЬ>Þ~¢uõšôŒý˜·‡”¤úÓX‘‰MQÄý4DªN$öz¿²/(1îöê¸Q46¢ RgkP\óÞQ¸±R÷é¶µ¦ºQøIIs)ÉãJoå·ßb4ãÏò«ÿÍ÷ŸAŽëµÂRq¡áJ…UØP'÷Y!Ì)Ýøa¾{g™bëù¹Z!¤²¨ ˆ8l©¸.Y;×´£)#âÝÓ7óéa[.:'rŸ~ÿ·Õ3“ïûûbÿ‰fm—I§S“¤Tm¦‹F^·$ÕnÇmáåüœÂÖWm«ç»§q:å~?mRXûÆy˜N1\â¶ Óч/?ý½ú´¹Ë—OØȨ¶CÌI¬of Æm¦ÃÓÇ߈ÈÏÿø·÷ï¿{úöÏ–×çõü¢·t˜¨ÂŒ"ÕŸ ¬—· oÛtÄqš‰ÜRj.@ôÃÈ1rÜh5ª:K¥7 òj•Ôò1h޼ލi>=ä›Y‰>îº㤹ۻN lÌz-Z±óJ.£utH1© ¾ §±ùÞTsGDHÀIÓBjðº¾wOßê¯Û%n‹ÂòV¬.³jìªË|lN~œ>üéZWÔ¾"ÌGDüô‡¿Ëµ3¨e¹GrÎ{ç•ç„Nm¶œŸˆ8ŒSÙÙ¸˜7B2"{ya½øqR"Råéé-¨O…þÐÃýÓáî]¶ËØÎ5©îåÓOÖ$ÏJ›bõ¶ÞTTןã;"§è† ô¶ÕùqVhA÷õíü%l—”"‡uNn¡:9?8FÅxk[(Æú sۖר1ôˆã|Ôí[˜up«¦ëMvjú]+©­'Çp¸{æ#¹¡nñ—/?CŽS톅ï¹Þé³Ê%»È|÷¨”V ëœÅlÊê"7ˆ)ŒÁ À¼R´lcªŽóåu "\ª+®íi5.N)ukäa¥e4ÿaÅ9Žï¦Ã¹Q›H¹¼|›Í‘·WéÚ'cŠk=÷•ÒÛ8\Í‘šD¾ô )¦îž¾›NU_ "ª`z'k¬ lS™Ñ‹×Û,=KŽt%1kžË®2„ƒªsgÖL'á"ܱn¦6ïo›Ã0„ÎI«VyãnÒøZºI©Õz©… tIÓm X{Ùç5Âu¹²qoØ(·€¿ Üc_€â®‡“ëÒ¯ÐõosåTØZj0 SP÷šðýÓ³uå-g¯æ—u›.ûšÛüU½ÁˆrŒ€R±²^ä«<ölû«jŒßЂ[‹/ÄÝ}Ú, t`‚­^»gYæ851åW¡‡ŠvVÈ( •ѹ]Õ IÌîIf8)æ})ÙufÕÜ¢oL¿›DhÙ¸·ˆéu€7žôÕ)xõ?å­E|ë©åjç¥uÛÁ!áU‚YæO•Áx³þoˆ¾±™xe9b³ç¤¼rûÇݼ`±ÎÖ®LÛÀÃÙ#h»I9þgßÁºZºÁæ˜ö`ÆŠ—[œ;R…Üüâõ‰ÿö_ý—æ^°>65`w~<(qöòòK ëýûŠM»` ë¶\ô! ãL~ÔþFMk´JMaCÄÚ,’‚¿…!"ççŸÂ¶¼ûîwq[^?ÿ>üØiþš‰¥„õ,Ìç/¿ÀuŒd­k²zÂZ¼  Û*n…_Tqÿá"§Ù(ÓñJ`a¦]©ù@J!¥8 cÕÈëè5†­!}™ Qg± §ãæõÈ®åÍáÑ=›’ë ‹S ûÅ"+,"Ìßþî?J)„õ’®cØ4í]™ºèr‰Ú)Á )hnÐrÕØÖ@™¤Ù(’+_Í‹1.ò-pU¶äªjœï߀a}­êšÚòêh™œÇBDªŸ-¡>YèÆb„zƒÇùÀÌu~\ÅC€à‡‘È…UG•¤*ÚoÜer¢¥Ï‰r¾€y:ÞÏ÷ï ‰œ‹Û’Ò¶-‹rxºÑ{¯¡ÈÙ>,EæÔ¤ÂwOßB!åk®·­Ê»œŸJq‹aͲÖZ(6|ëJõf ã™cXrÌœRçã0•˜à*ÄRt>ÌEƒÕö¬l7–³€¨yŸ§§µrì¼?DñþÝw~˜ÖåµnNÛzÖ+÷äc™w€°]^™ùåÓï÷Êþ!´áI‰n¨¹ €&í½Š‰Æù4î´¿YÎÏ•·¥÷NkÞ ùøé­wzç}Ø–¸­•4qM¬ƒ®’JX7žÖ½.Kmöo|5·¬Õû÷?øéðË¿ÿwÐ$n fv®T1ª½ä·œñ%f½Ngh>=ê ÖTV˜Â¦óHÎôç†äÊ"K³„mßõKûÁÌÎyÊ@‹S-i€M­{Ô¬ßïÞ}w¸{²þçÏÈy®Í_¬Ýìc¹ÊŠÊÁÏ)»=ä·µ¢ÅÃt@ÐÝOÅWÌ1†7Í,Æî ¾æÀ‚ý¢7 0PІ›é—‰þ,é ÔU)©ÄNßmE„¤_/råŸ#j¤gI¯6Q>E%¾‹ÿ¾Q½uP&—¦‹…Ca.IvÅdN$WeAI Õ<É’Zcÿš/”´ѸÙž´¤ýuâ.;[¸Æ†ûGN‘õ{ÉPt L±y£ú*c†®Å±Nè4W±š§èÑQ¸bÂ"&'üz`680§B£YÕ7š)ìŠu"xÝâ5•½KÝËw›A°z>å(]Ý·˜ˆ•¢™ªŸªõ C¦½‰å7Ö±¸¾,Ìà,ÐåËî€xƒyÛfÒ\¨³„{÷úÝé[¶Bd]u‚,œ$%áÌŠfô·»lÃG-ó4jRÝþncåÍÎÞÿì9Qi8\o¨y»…ü€H®Br„$«8Kg‹œËš{‘K*Vˆ½+ú­‘ÿ[m<¾ dˆuì#D”Ô¯ÄR\½n)× t&aVg¹ª*~»­Ùb¡:ZŠnþèì€W?PšC[—¡°{o8eïI½oxìõw§°s¯fØvÏsb-\Ü=·ŸšÙ-Q$‰\kóÅÌA ÅÑì˜Ãå ÷Ç£up'bÎ)€˜Ô¬!ŲœŸAŠ/z©ßsÕ¦›¸&ÊÆšØÂJmÛYÛ—|àµ$²LX^>eéÃùóõ¦Óq˜å‹P§ˆÂ©å+V…xÙ뫱„G缦°ŠHvX¦ƒó£ªJ5à^û×ë̹ ×⣩ž3›tUó[(îõ6¯*˳4‘¹m›¬œÄ»Ðœ&Y+ £2^8%ÎçÍP©˜Â¦e¬Aq̪”*ý "'ä@ï @&ÇbKÅeC‡RݸB¸fãÊÌbætÿî;Äì„CÈ¢"upÍLQBuêI!ÔE®§{JI?HíÄÌó%=@,ôhq~€Â}K)"'•꛳–ˆHH•6-»h…œ€8Î>#BäÄ š|[$ä1³½m²|ù ÞÌE¥Ùl,,~˜j_Èqƒl³À1¬úº!’˜ó€Èë]µâEr^(]ô çE²¹y\æÄÂêH®¥4š€:O^÷:PD@7ÎýIêè´­¼bU”Ú—_VrÃ|¬8—ó³Í §¸eNcØó[SÔc YÀ§f7™\öXE‡‰S a9KR7tÃXï”f±’Ë”:3KÊ‹A“QÖ"3$¨ eÙt+0$,žS™~œ[€¥„ع½Xƒ\݈²¾M£@E÷ï*xžR¨Éò*k&±!Ž™¼¤e!$å1ɶ¼ªVÉ&£ùô¨Ï&p)uQↅ´.gHb=y,1¿Aeö¯iòêÑ®ÞÚŒsŠ!†Õ“Ff>ùùpzD"æ$ Î2bÎ÷.1“JIÀÍ’ÍŽ¦@Z¶—03ÄSløhIEBó q ËuИÖ*—D Ký‚6›MZÏψä‡ADb¡S×òPoˆ`ªaК ›ó…•ÿ‚D`êPë./ŸÔe^?ÿQÿèññ£-³8ëÝ“³ç sà ŒŽÌB@•I_–]ãL1NÇéøÖ3KÒtìpyE×ÂÞPÿPjô “>–P®êàýA˜]@“0_Å»`s•Þ JÄÚâÕíÌ^Fá¤<¹aLq«Âü`åËäUm™ü8Q%L«Ð]鈴ب%ön£—¯d®|9!YR<=~“â–µ(agŠ'%…Ha¹TÎŽâÆ1,¥DÈûÞF`ÏJ¨M®Y ˜‚ˆkÎyY íªˆ"iùbôCP±B?øả¸äÀ׬À.*^ÔgºëSÑ95©oŠY{2ŸütЫ_ «ˆ)o>•È DÎ3D(^õ6VŽFÎ(?3Ũ´ŽüÚBÎÕ‰™Ô‹^LÅ7¸Ê¼ F0–þOË>®òƒËË/œÒ»ïW—Dûd5UIÛºâK]/ÎùÝQS\ÊOd–U¯V«,uš ë+¹ïßÿ@õòòÚìû‚\–'N‰ ÕB28œwv§Þ@Õ¢š{E‰è+ã|~ 5ö¯¼J×!yjy¡[Y~w˜çÓ#¹œ‘ÃÝ“âö ¶sg0¦ÑÊïEf”'Ëk…ÓÉä†ÓÓGrÎs{܈i;‹¹€( 9۸ŒCâ.MÉ4š’š7lâ¤+9À0xÅtt%7ÙŠ[Ïœâ0çãÃååÓåüépz@D%½ëÚp~aNv¢f¶²Tÿ§ˆ†d„6åS ²õE°u”ºñöÌ;P¯QdF¬Jí_¡HH&¶ ~Mˆm(!hh·‚ÓjãS=à´ Ãê@_emZn[ï-v“º~…•d„Lªx¥’›²(#½õ›¯"Ù¤û™7†ïHH‚R-€¯…V#aé?* Å·ùÅæˆ‘Z@„è¹7Ý1úÜ y‹ÀmbòöbmiOm„¬ÞÐÚËjz‘UðöÐ…@±ÃØÝ­6Ÿï!A;-6D yB$ìb–mfÙŽ?ƒ-Ä‚*[ªlÐ žMüopb\¡£lèEsm©wÁu»Â6¸-ÞØ1!{RUFg{v £ ¶ÅuÜþ…ÿ ™4ã‰|ƒ=•Ç]_; ± ûý¤¥ñ–@ãé€É·«£ATokÒy ŠöUÊvP¿ËqØÜŸÍ/Ü‘3ð­—ÏlAY(é¼d[2Ðȧ.kU˜¥,¢ÆÝ£ºE î=F°‡sñÈl<21v*õ%(±<Í¥9S¬ô´£fî!·9zÕzBíQÅ ˆPv¬<& pÕ×GE~ˆ¤ÅŸp*Õ  9ï{k‹Û™( F+¤x‹;S¥ø¹ãTã=Dü7ÿòŸçÇ=,3Ñ»¡¥Žý:"{ øñPú 6ôb ÛVUÉ;²J•k«Z"7LÇùô˜Sc8æô)II€ß0Ö`q+lØ–s-µÇù ?~>>L§½oq»ˆ€Guj,Y°éùçÐû¥º5~³_¨°¹Ë$"räÝŠ¼Ð9,õ "QœBØ J†¨À¹ Ï_>]ÍBÀ&#–q: ó‡ßü%§˜T޲]Î×TÆŽ)——]+OÎìª}ÆôB +eÓ¼³ã0ðሺpº|ùIa²TFÓHèýz39îÞ §Né‹ý”…äÎéÑ”;ª°†å¬»ÿ0Ü0®ËŠ´:6í1·ÌËè:ËÉ&·åøðþáÃÛzë…œW‘ö@šŸWøªËÁ[È´]ju•R˜Ž÷‡»'£ß°-SßÌ8/’¤PtXçÇSغ‘žHŒAÍE•B•W¾@ç©™`Þ` ­€SN•3Õtr–³õMO1HŠ«:7 IDAT:xøø?ÂòRÜ^?ýÁŸâüˆä4 1† ñ#õßf†‚0Éöîq×ü“kÒ£z¢*x|øà¼’òR ªÃE’pŒ¡xz!*E©öι’ãÅërväй 8!*YÆ:‚îÈ´õ|M!”UIdðÍoÿj9?ÿñÿû¿Ý·þ7ªæÖG­˜Ü®‘CT00/ËíüŒÎyæ$œÙLç—OM©‰Ý›Xie=óSUpºÍ>}û;F¿]ž·õ\Ü*-ªv¡}:ãn—v~T³a:¤Õ‡¹&b*÷ŠÜHD¯Ï¬íMŠ¡:€+-ËùQ½5t²¼|ª7Î~œJÞ!¤S>&%¶9]“”¢±èÎouºcùÕåÇÍIR:Ü¿›Ž¯Ÿ~/"§ÇÎ>‡Ú}MpS) ·ÈϹÊxÃÜêObâg$:{Tþ.äXƒ^@{£ï±³w¬ú M^n¢fã‰P]ê±EyU "üSt]~Ù¾¹áÐòKQþ)_ƒPÅãT)KÒXûýÃ²Ã†Š•¿Á+è¤0×r”Îj3ÈAv ×G#oј÷loyÛþÖ?4B©­56öðVu«ÿè˜Éx[§Rç WñW_ÙÕñ¨&Â;:›õu2ÿî?Dì0·´FÛŽUG}‹R"{éŽkl:åùP•íß#R³96½6þi]iù«,¡9 ˆeˆ6²›œN•*5!“ 4/ì<1lÿN0Ór›#9T¤¤¶ìDæ’°ÛÊ* ·£Ÿ”kî‘\+PvØG l®YÂQ¨d¶íCØëðä+ò‡>á¾ )„Œ¤Årš¨c|æÙ3!”ÜÙŠð"ö_Ž`<(ÚŠj<7ùÈu°T¿lªB'+(ç‡üÛÿþ?¯hŒ_¿UwU8rŠÿíêQ,¬é}Z^·îµÖË”eÖ”q”pÒJüÃoþJ•àÂ)SQÏ_>·Ï¬³å?á/NÑ ãéá£Êwœ÷üV}=Ô×üË/ÿhý[+ÑŽ÷¤ñz …ånŠNóÑ ãõƒ©%[§{Õ§V•ïL„ µ¯™Õ6Ç œTzüFWUÚÖm}ṁïßÿ€H¯ÏŒëåÇ¿ü×ËË/ÿðïðîÝ÷Ì‘9b™3¥”4¦CÙ Ù¢¬¼?*̨;©ß?}‹Î!9La-É|yý "Ì1· W@ß®0›­býŽS¨&/Ÿ"tTè!e+ÉCß,¥@t^=WÛ>ÖÓ¬Óú®™+EDwïîÞ§d°^R\·å,…ckó‚0%øºq«gDœÓñ!ß=Þj¤ÇË矨0­êbË N%ò¥uEæKQ8qܬ§‰¾Åú«³Z¦d( 'N9^RJœªC·âÖ¦¶"òðá‡Ãý{ý*lõÓßýß¶ ´Ç2ýÆõãN–€jmºJJT‘" ãœ2ÚQ\g@[6?Î÷~ËóOn8¥”b5ˆ@ÄããGïG)¯„寗—mÉïK +"Þ¿ÿÑsX_Èùaº°æ‘v2.ÏεX/gQŸHç} [X/PæâÂüáÇ–%üˆÛå@R\µ ‰q Ë9§â #9¯þy‡»wÎåšC>ÂSŠÅœ“ã–B(»±Jk&$7î1ÏkCØ.¹1Í ¦µb‰VÕCf~œ§ævwELÛb*fŽa¾{šOêm¦,]À¯ÏÕlaïÐÒZÊ_9[Fó…RØ¢¢)"§§oçÓcÉ•N °œ?“óÃxI[J1¬‹&? H¾^3s¨îÖš“:Î'Ãõƒhzzr€¾¶–o§’ÕºSÞ,r{sc¤%7a.Ì‚•p•mÄŠFØ¥äÖ”ñÍ‘fy¨äa;G±²lñ «9¹ñ¯¤ŒÓ:v»˜wº6›¿––Â>´ïÍŽûøÝç—ë \+íF͈ZöcÊþZ2´@K êËŒsBöû¾©y {Ùí[Ý*ùŠ™EH?[)Íò€sgМ)E™ãWÝŒ0´vW—ûÕEßkç±åX5Y:‚H–¨ˆtštýõ[ö²“¼ý.È®«ÜY+î­±«´#K³8*óŒä-@¼…½©=ï™úÒSèåm4êfÖ!^ :cˆÜƒ!X×¼º{XSñ²ˆØüúÛ+²'uÖ¡)š|u¬bÊÝ#7uPý½Ú´>NTâ·ÀFª;½ ‘Ý^3xõ–M»¥c0³"}B0º2ó¢v›ç-ä@êD³Ž9-Ú!–5PÿK¨ñã·®¸t2MŸÖ¯”æÂXAÿûÿø_×w:†5[¯Ö2MijN½1l›üéxÿÄ)Ɖ\ å)¨`j­QŒÐÚ,ºX|Ìóý»ïœj®ëï aåÂvçGDwþòÇ<ÝU9’:½õ:øñ@D)Eç‡ùô¨1͹ú@D€VmzŠl¼ã€µÒ•‘³ õÝ ¥¢Ý€·6å³i†u)…¨Ž©UPÕYrBeë"-¯Ï‰©ÞEÕ6× l"Ì~üKéY¸œ’ˆª"'¶ÃEM{ß–‹*Ž4 ÉŽ-!ûÞ5LT8nl(¦Ð†æt¸ç²aUn;Î_~Îz©]iY®Ú £0§@ G„n;ÞÅgˆ* zÔJ-PP=ÇóÆ]ù·D9b@D ·ùÛÎÏ?óññãoˆŠ7i 4ß–—°žû‚¯ú×j©Ì6-¯"ìyÌÕ€ðxÿn<Ü÷ä¤Â-Ð3™‘(¬K +—½ÂfÁ)Ö¸ô ¥8ª\Ô·°-Æ<=OèË{ÄÃ8ßøA­Éý8 Ó±qw˜‹ayýTݽ·ËËÎlºQÄâ@ä°Ì¬¶üó¨Ôt7Œ~˜2D”KR6Ê8P­90pa9ƒ°STro6pÀݱºf7û6#âÝ»ïý8›´,'Ïâô¦²¨‚w=ůÅ7—)Ï0ÎyŸ>Þ`­ŽÅŒp9?/¯Ÿul–²£žìJ®¸-}NŽCÂÆòyúö·Õ•cÔµ›Âªff°.çÚúa”rëüÀ%~®>A—Õo1)na]ˆrª—Ž–ñpÿÎù!l+Ô¸¦j žÕ/Ó!?ñ®\ç,—B(–ý™§£:?#’æÖ°ž3ºæ[ÐTdÖ=ä|×òfjd~U ¼=-ï~ðVŒ‚J׌5‡­i5ÜHj‘.œÙ¤×w‘â¨2”E!"Æ L"Mö!ò;Wî›]¬´<zLlG‹Hß3¯ßÅ àŽ.X;¿ªÝ®ŽYùdÚ1æ4sÇÐÚrÚ§%~•ä#ÄÝñ¦'ê3–¤ …CJST^¹þBÎCv•dîèoIÞ¡ûBì-­:¾{‹)ßw„oŽœqïf”÷k.TØÎé:o7x{˜vÓlswÖ%¾¸+‹2Løµ_rÝð4@›Qef*åót³º²^MŸaôÞ]Š€à×ÛP¼žî7J¾ˆ àUó!e4¨;N"Gnн2£p¶ülh-ñWˆæõTx“û×±=mÓj=om€TÙ«ªj DêÆ°#–ëg6}ȯg¡ ¼‘Ø&;Â×k ¡-ÃŽà†X.%‡;Iaðé> æäœF±9ì¥{»DîßÝ.TJk' ¨e.ç¸i›¢Õ6+¼f*¿eîŽoÁ<6ï^’qÚ£ìNNë T#mÄ~Y–<¼Æ?ê{ÒVEEîô.¨‡@U¢šÓN:³áµ"&ÿG >ª›WžO¸šû˜쮥$·a:¥.,ªbóžÈŠ1Ëæ3íšQ6q»t0ÀI‰FøÖº n‹€¤‰cr¶L†– ’Y$Zk”k¥“¨T1l‹‰Š56§V:ãÜ ?3sLˆdGÖ2é6ùR‹Mh ®ä–ˆK&5€i,¥NL±8¯#ˆ†Âs6³Ž›^’¢üúC§ã}1±ã¸-œbXc%ƒPï¶deL*§ÉõTû1£”¯Ü¢¬³.?œ‰éÖË1u„Æ"=&Õêµ½©èCòÙ €HMè]¢Â¬ª1;Çe h¶C“VxÅ.Ábã̉ê{†Üæ¶gDîþÝ÷n³ÑyX±æÚ k3‘²61i„}GúaMC×k$G5wÐ(X-¨A£Å¾‰9%ý!ê_¨¿ð  $¥·•†ÑûQé Ãt¬Ei-ä¶°„ÕQ§’µ–e,z»Æù.më–y’Iﳟ¦*Írny}.‹9lË‹ÞY7ŒDÞ"s9ø-EÁ}²„Ø¡D]–%+¤’ÔW¯%ÊÞ3hg$Lqs~¦cX^y½L pÐ3§°nKf,gã)® ´n@Ñ V›i7Œöå-@:Öér¯‚ú):?å€u»‰õ]lùõ„ˤ AÒÍ]ÿìòúQ‹~¼¼~&òΈn<Ü¥¸¥¸åpÄü¸ë]ŸVž[¡¦züŸŸ®Iíæ‰dKýè†$ï™z7´«æ«?0”T…7uPæ|ƒC‹ ¸ Ì‚€Â"˜ÖËZ=œ÷ÃÌÙL¿dàˆ¤°1’Ònûî‚)tjAÇ6&WTc˜ïÁHnSà‘â¶ì¹RDú¾H:)PbÕùV$¨mè AŒLM$·%Åp¸ûÐU(Å„«“bWKqŸ©ðJGüEMu8^^~Q\KDÒv®¾ )nz<73:Í™ÎÛt=h¹BUB~B" æc±÷âzFÃ#—>U9Ó‹Ÿ•­dålb|ÃUº7A½SÈ,;.ÜítYuqÉãTµÛ1¬j¡†zÃÇÑSaIÜ.œâzù¢‡‘×··‹,¤$%²W¬¼Ùn¢¸eêòËBD?ÎXxC{l)Å3ÊùAí7ókÃêòÀ¹Ø*'Y}e°Îµ(©y•Ó¤èºFQÀDÝI®ð€Rå03‰!,g7Nž¦R %DœwÎûÓã;DrÃ(¼¤"̪†Vã=aŽÂIÁm5F¡Ùq¤<®¨Þ¾]¨NlÐ$—zJGÂæmá-’D„ù”„c$?T™š Õ¾D8­çÏDn€;D Û2Œó0‘h˜OücØFU"šê¡¼Vž…9Fûб±"ç/¿TÀyœO¥s¤>´ªÑRŽÉßÈŒ’2È3`nfª.nKM-®FÖôC-„d=±yˆ8N€#ÅåÑäîPw{?Í;›4$f&7ôdļDúæð&DçÇ|,ÞîÛzÞ Zoe pcë òë©é«Éü üÊÂ&%Ô8ƒoE_ »xå¯2Vš›jžÜ°ÎoR Ì\<„vÆ m0»›>6½DGdÀ Üo*aV†¡ìÍÊÙ²H\PæÝyÓø;Í(vhêWÔ'ö‡—RºèsI9ʈ„‚J•‚zË.ò6 ¹g´_{êw±g£ìÏ›B:oê×¾ËÁrT)©s˜3¢éTÛlä†aR­U‰‹'}-ñöÙ“?0£O~ÈN=äÚ>«Iˆ‚*NUÆyFû[d ¨šß²µ\xƒÀT°±ÎÙï¶ê;T’4sF˜ž«EË/Ìzäký¨ƒ¥ wä,Ó2»Fƹ"¤rèkQ9VÆM ›@µÆúlR(dȾHD$°S45 b¹âE³ï¼ ó¬Õ›öÇ8vö€Å 'òÅÜGÏVëµ’…ƒµW³Øužk´ªtèáýêÞÁ if@¨˜¬r3B“>ð¦ð£òGŠƒ *GApÚéÞÜ+=PÚ«×#ä]¬£pÖítÃbqÜ{¡t’¡vß$ÒíÆ·EJ:BSQ¦Ê¬‡¼¼³g;Gzˆ´Å˜@ŸJ ůðÕì³d‘kF°@n NZåü¿þ«ÿcQÇcK–ÚGKVßѰ-Î6†ê­ãPõŒTgˆ÷ï¿?ܽ»¼~â”–—_jß}a–ÙDœCï6¶_Šˆ1¬Õ²K™âwOßjý)jÌXFJª~V« ;Õß9(\ )A±±¯E…§Ü0ŠÚ#Uæˆ:!„m­Ù~S¡ ŸÇJè0ôŠ¡"]*SU06Åœ÷ý_¤¸éÄ;ÆÍ9ïœ}þÙºƒÚW¦ ­th½¯NšJʸaª™¥ápñGµ>aÅD1q Ãtòã¤ùôP ±Úˆ?ëˆö —˜Mèóþ¥Nf62† 3ÒŒ T=R ËË'$zúæ·Ëëç×çŸOŽ%…ŒG„uá”9…íRач–Öa>ùarn([^^~*Hµà0ÎÃ|²ï fAý§·qš­ß?§ JŒ.#ÂÈ´å%r£Ï:¬—°]°,ZðÓ‘È?ÿôwÎãᤉ*”Ë>%eöq>‘óç/¿:òN“åu×ÒP—a:úa¾¥¤¸©¦Å‘'ïj£¯º 3ó0˜™?&Ã-È)ÖUȨeqòcŠ[Ü$WŽÆüåÛòjZ4AtLJ'N1Å8îçãýùåÓzùòðþrN1j…OUc £ì:òÙ./a»ô¸â†éîé[ÝÇS\Ãz†šÌ)'·îÞó)¥"),"²^^º[ç\m‚¯Òƒ¦@Éž[ òS{¶ÿW2¨°#FY•ä¹[(r'Ãü*µ^ÞðA·.WJÇl1‰•`-7yð•pÿv‹usø¯¹-ŽLb—-¥7%S+LgdtÜ©„¥(@k“,7ØÈøfÇ){+¯FÑ1^E79,†ÏYý@çhµèbIúÍÀìºtÊ?…E û¦Ðɪô~ך}¶%¸v¦˜'äî–…Y0j=Ú’¶DWÜ `¸ÅûnþUÓ-Ua2Jr ]müIjeX&µC„Æj˵ÎïìQÞoí\kî”?~…Õ#VeÔž³b1 óÔ¾¼àÒÖ€€q§“ª‚÷<}¾­|èEÏûGh¬Ÿsío¦q ‡Â(Á]Z„ì&½ŠfÿŽ”î[` Òÿ-çÅëÖ¡cÒì%úÌÂ*È“¤ä§J}²i‚}ާ‘«c]åM¯uëöwr«7e\·Émòš!î„\ZCÔj»åðìdh¹ú. ›ãf*¦Uà[1”vß\‹}¦¨ >WýÒ0\l#+áô&ÚÙ½`°“hRvŠ«Î´çãÃññ£‚OÛå%l‹Fè!yÀ7’¬M´Õ˜h' 륎µª©Ýã7fœÅ±ü45ûß¶åâœ×a¸‚d `í®¿–;aiåvsŠDä†I[U¿QÃ.l†59+åÑR¨X^Iá{ÒãÇý0W‹&áôúù÷%Ä`KÅFVû4ŸtȤí£*F„µÈª$+¬þ¿†íˆà‡)lËzyÕ•1ŽÃ8²øÊ%¯CÓÚX¹äœTtáü É”q[¸ä¹›ˆ¯ö‚øqªBøãÝ;?L¿üãÿë§ù^ yÄíòzyý<ï§ã½ú*¡Õ*‚0§°¼Ø­G{S?Žä<'ÎD*½æâã¥ÎyØ…ÃãtrE_ê\ä.¯Ÿ@ûfÉ0"îÞ¹aúôÿO–h»{ú–%JŠœRŠa>=óѪþ¶Ë¹r(*&CÈëDL"cæI¹xðã4Î'ÎÜ( Nµb‹Å\[-Ö:¦yyúæ·D.Å` ñÂüòù×6‡ÙÂ{˜¨ [mL¦*p :dª ÎÓã{k\ Ÿìüè†‰ÓÆ1úqÀõò%Åððþ‡”bÜ.ºñÄmY/¯¹N'BBE¥ÇÑȧ°uþ¥WÄ#¨—Î# ¹ÃéñøøM\/J¬aÕkF4C·úÛ ÖbËÄTC-¼6LÇñpWv¹­Lë-ˆ „è‹S”Éèüvy‘éxŸÿ9…õRŸ:¯ÃáþÝ8ß•ýsQ†®DÆéz²Cn¬‰——ŸE8¬«Ê®²€#nºêDäáÃoJ’®—ϵ\ ëÙ³'s£0„­o5Ö‰Í>[ ŰU§hÑ Æû?yÅ›~ùýß^¸¶§íü8#‘¤X›<廡s±$J‚ˆvðº¡N÷bjp"_Ìí0l¯qS— ïöÙer?üU1lë¬j°M]ÄL“2ùk–ÍX)rÅË…½Xþk&àØæÕRw%Þ.H¡ö[×ÕÆæ~UØí¤Шɵ‚•‹èí=+ê+퀗šzctâuš¢üè"ÖÙ+CuêðëÚ¹é ‚mHÐ~~4ìÀtÛ™µ2] IKÔN½ÛµÖÅ'°6@3Xâb#™ºíæBïØdD½Ù^N£e$ÏÌ´¦¼ÑT×–"°óúë]YÝÔôÈØ<¸Úèªv¿Æ£ëÞ DÛ4mJEVÚÅãaíu[¿ß>Þ6<»Y£ïÐŽb] *„„s/è´£¦ý ,é»XE&EæHáADœ1åOXÕÖЫyjWƒßÆ]„Ò¹ý{ß`<4’DÿÿٛмÛäêƒåm«Þ€Òâ²´«ôÙÞhìEŒýƒa*#] UíϬ_º+é‚}{ÚŠQøujˆXad¹Î”œ Þ0/¸ùóË#¨É¸Æ1 ÜÁjÕR~KökR;ÿíø¯¾&Bª²w…ã­ynÊuÃä†1mkJAý®ž¾ý­ú–UµÖr~&C}¼vÂÌNä1Ï{œ†1Ï®RŠ»®Tʸ¯kL‰ñpzçS®g¼9fq´a€Cò:»Ê¤^Àœ&Ú›ÿ¶R¶?݇H”ý+OÒýà†Aï¥æî—¦¸^¾ Òéé›°¼.¯Ÿç»w‡»§b½Ý¿8œbX…ÓòúŒ&Ž«Ò2KŽh9üSlâ'BB¢œüR85 €Îï4=ÚΖ¢>SÂj&»s~œïŠ8Õ”sµ?Ža“räëÚ(¯ŸÂ)l&TO·<-¨+sg:ÞîÞI-3…×ós-$ëòãÛ¹4aəѽÚY§°‚ ;?ºaÔC^C|ô\^>Uà9jT¸ÊC¶\ úajîþ¬»{ú¦å")’VI12ÇB—±ƒ·R~˜sˆ#"9?Tã1Ó(hG ;ª”ÞíK¦ã=Ñ·×Rn±Y-©KD¦Ã‘äÞxýmÛT™ÛÂüÍŸÿMÉøl‚úmyÍve"zÃõ­ÜÖ¥ÅÞ§7¼I¹¡¯eʈ~œ &>9%µøè ÏfÒæÉùép?ïÃvIaÛ–—,ëó%8kAž#U]`¨ul¥sÃxðãA/IÃóÖËKŠq˜`¢.[/®Š~¢K®]r–W¶,H§ŒQ©ú^¿~œï˜“ºŽùaI)&+vGª“ûÕ”‡u[İ^Ê ä¿ù+œ‰Èzþ\âjcŠ‘¼/™ è½¯°jŠ‘¦Ã=sP÷|n{ûþ¡¦m"Û’¾{ú–S ÛYÏsNQ:…¹˜ÈSç{ÐLaô¦QÜÖVrNµææ‡g¡‘{øð£žßä=¹!l—´­~Kàø¯É™oÈÞÑØzqÕ0IcRd ³¦²ê.¹Žˆ¥¡O­¾n ®t…^-|嫨lw¹Î©>Æî0h¬oK‰dâ–jàNñ‘C¸•ƒÃ׃"Ø÷ù“ur#º%µ( ¹É9W“$ëÃ+*(0>OoM_+Ú^¯¶Lª4hW ãõS®~UçI…Ñ–ES€°SU—ËC|K"ÝàúöÇ’Ùa[OÎqáD`gr %»Ñ£mKmè–¨IÅjõP-¤ô—ׇ‘Z–KJ×CĽ϶±#ì¶Ô·è¦ùO€r;©xÄí=Ànø ÷ürì˜À™GŠ×÷LÄŽaÞuÙý¤ œúœ™ {¸#1WWxIQÌëvó&·7Q°ÿ±PÆÏoºH'뢒JD§-BoÞžG‰Ù&º qØ7ëû­G¬7^ Ìåæ]»á= 9ªº±Õ±¹d—½ÔÚS\•L· ØŒâË»iê[d7³šÑhšÝy#I®`M¦}3å‘dο›y|¦[ïÝí•fUŸ—Š&+·±÷Fi®ü)?{ßÄ‚´èð.—)Êž%U8V¦›ÿŠÜKhxö¬ÒE¦Ãz¢ùô”RäÜ8ùašwYePóµKÌ«^-Å ZTD®d€\(Óù¢ë„:Ùââŧ;YuNšOù‰"UQ:®Ëë0!lNi<ÜYòw@£#G~˜Ü8µ›§À´öU)FMÛ­vûü:¢'lN)FÝÖIoõ8ŒÛª.¸ˆx~þ)ÅM8­—NÉùAYK9£YÛ ¸.™Y.°;ƒ“é +ß]JýEucÊJÎXSȱwZ`ræÞ!€ª*Õ¸ /Tt®nd¯>EÂvÖjÓN‡ZC9°ÅQ„²Yö´{²œŸ…™ÓˆÓáŽRr~äR ºÅ,Vïªî™ äøa΂ú°è¾#0_¾ü’ÿ”ó䜦a:¤°”Aú·±]¶2°€Ó‘óÓé‰ãšâæ‡Ù “ú¦§°ŠH ¹kV÷£¬‘â-®Ë 25å:ÛØî†],ºl§ö¡˜b¨‚"R/ùa˜0nK ët¸³X_^1"ˆä†É Ó>ÑLãóä¼ZZŒH•ì;àT]!Ô%ÉQÜ.#"·œŸ+܃ZÛ¸íòºž_wOãá.s»ãZS+qÃ$9I»»6«^ïæ«†G'ÂÀ¨Ê“¬ë®ªð:{ƹ>ö¯q>J¡–(ÓÁª•;N§Häh«ø9EUÕg–W8ºx 籸Ôc ë¶œkè|=W<™¸ÁIÙÏͦ¿'ûTŠã-Òt¸›NËËsÑíaÜ.äü@𛛍ì4U,œUPY;4Ö•ñÔáa˜æ’ƒXª¤•_•R”­_uå ’þ`¥ÓÕÂC+‹œ7µŸÖøÀ`¼”¿ß VZóŸ|–{ë†Q¦^¾üìü@Eô¦N4¶Q.4Štý¾[p9qóí¬u ùaRá X‡lDµ×¸ÇÔÆ]D@7LÎ š±€´‚Èåõ3!y@ôÆ|’STÏÉøõñjŠ[ «ïœË+Cµåâô–uÒYnÄÅ)¯ õ”©Ài…‚JŒ+«MóÔTRÈH —Vdä¥*dÌNß´_4±²öËù³¦épD$?Ìœ6áä Ê"Š)£63™g׋çaœ‘QÖÄ-oŒ(hXåÍ{ä WTVošâYÉz‘–\­®‹g¥©€9ÅMÅIÂIR"ç±Xˆ„UDØÑÎÝ‚uRŽºúv8>¬çÏ ݀€^ç%&θÍN‹?–ˆsž1ý[ÏX¢dÇ–2KVÆ].©”ªÀ\™™V¢u›Ÿ{KÏ€ä@(A1ÒËö}¥dFDa¬x•¨2Æ(À2΢ÆÜÀ áj›&YØ“"|ôj¤Ä5È2ÞJ»ÊïC“Šk}lÅ3ˆ¬ë ¼ ™ÒšOË`²QÎÕXÉÓ¨6QÍ;#!Æ!6O[PºÙ¸M¡LZc¯E-ö €o83Xs‘.1Ñð•ªÙ?îŒx{8˜¯R[å1¨e„–Öš°g_©#½)b®Ï¬ÞLŠv³iÄc2‹_¡¡WΣ>[0ÌÜ·ú7Ý·,ŒF³­‹°¶™_ Š’ÛÄwÄ^Œ\µF°ƒˆ­ñØ€…¾ÚÓÕê-'B¦ }C¹ÑÆÁïv† -ôí )Q †N;WŒª¸54¢}¢ª !¥èÙÙOé¤Ì^±U¢nalö0RQfŽWAÕ4gÉ—î WÆ·¬Zú,¸‘?Ñ!¹­Ÿ+dÜ9¯añˆŒä ±¥ÐÛòXǾU1•ýÈÍàæjw*,´}sÊc ™cuiaD—£¬͉Úfs×z°`Wê°ãý{)•üxñÓ9uÛ~æ,ÓĬköDR1IrKþ€Ê~¨üZסqãù‹Î^/釡À_….'xµívÆØË×?hRè*N„]¿Ëï`Úçd¬‹9†‚ ¾ÿãr‘¢#’‹—&¢Àg ê—ÿpÿè\ÿùç¿tÝp8=óùéë×ïüxžÇ³±Ñ*M*t}ј›n8¬&ò8Oò1×' S,@KÌ—!kŒbøðÛ¿ïÇË×_þï¿!„àÅPÅu;Á'9Ë)/O%b»aƒ~‘Cn=¢ô¯"l@Dé¼KE*ûÌšÞáº>¦4]íOÃþÎOW&’wPÓz… ·tºS²ÜS”À“ÂyH,‘‹jØUõÃ’3–̲8/2J«‘æ4"_%'Ptj»Çy¬À5ˆ+Ç ëìt!|èºæ.æÀe“0‘³4<·;Ù+¨ZÅ+R½ÈÅŒT BŽÆ ï‘l®l®—üæm_‚En2¯ÄUöZ°¬& »šl¤µq {»QËV©;gþzÛphvüôóE²~I7Šký4ób-sa¬<–¢7ÏÏ,µU$oS>8?(•ÌÞÄ@ò ™oñ€¸%ãcŒLŰÈ+_™ÄÈK?lIÜ/z:Ü’5/Ö¼¢ðæÀÛª(„›bŒ‚qH šbœåqþŠ ¯Ô ÍàMÅvGÛÚL+ÇBÇS;ØŽ¬ÑõV´tŒíÛPV7¤?dmú°0é…µæVzCÒ¯nIJ.Þ}›Žf7äå½Mûë=3t”‡jXtPÕƒµã5Æ`n¥t©xÇ-Zôæ]/M$j«jÔ¦k•CÄ –R¥n¼zh[æÙS6e^8ñÿ]æ^%Ç»aïúP4€0WfŠab¢i¼¤ÊNR™À0"«qÅéá‡à§ó—Ÿñîá{&!sD©Sbðwß#â|}Ža>¼ùý$ÖÛÃþ$égŠ>ÌS|¯¬¹B9S%¨º0>¢Êšwý®`QEŒ¬„ôLõ»ßþKƒR’–¹ Ž—/~¼Èpý®œúœÃK‹ÅZùyÉE’·P5—Φ®ßuƒÄ1ø€Ñ8pý9 ‰ßuû0Óø<ìïúÝ1VˆÀ<ž¿f/40©¬F×ÝîPº“£À°;k¯ç¯¥Ñ¡Œu¶ëE‘ª&ŽÉ¬ýýOƒŸÆ3SœÎ_„@™¢¸Ó©™‡Y(@Tp]'"ÖB§Ö“’ãý{ŒÑSô1[%M—§9›$šµ¶“?4ÎXüçiÿPÕãùK§ŠVî¼s Hä7µ2t üxÍìý˜¸àD~ž“óuAŠâ4?Þ¿ÿ£â0]¾–ìùÀëYlŒ>Kšà†f¹zRoi–‘8R^)¡DßÿIvì _?þÕ:Òå\Cðó$<‚2¤—4©ñS$³t„ú¡ˆg^þÆ´È]ŸÂ¯_.%cú9ía"‹_¾x¾ž­uÝîÀÌÓ嫱]é>£Ÿ\¤\ͲVAow®ëµ§‡Ñ?>S ¶Ë[Ñ$GSUg8Þ?±K ^¾X|õîÞ~Øîeà2?‹ÿ+u^æ§à'çzë¬ôÇ+ÛöiGâ²±—^Sœ>jUƒašÇ‹µV¢Q(†®Q#j5{ÀXc»LÀI’n±i_*Îùò´»»?œæñ*2!ጱ¢ëO ·¦ñü9Æý„Õ3íuL@®ß‹ÅGösÖu‚C笱-H@‹Ÿ ÛôÆ‹¥`ÏÆ~!̘Äñé—ÔNÅcìw{Û Ôº¨ë}£®êàs­,¹äý6tc°µ{Ö  d Ód\å!pÉ).çy*ŠoúŽlr–ÓçMÖÚƒ»×V:ÚZgÙ àjz³QloÎWÖz©VlTfëüO™ÒoT›åzËË^6Qé©Ë¬¶ZÚÕý^‡ÜªÊ¼¨ô²,Ü4®DØp©›ÁÁ¢GÅ6µ«˜‘ó õ'¾òÿgn#¨0•~y—×ׄ7bÐ¥¶]LaA_Œ—VF Ûí^«SÀ­¦cË}Ü-m\4*1®ü¼¾Ñ-ªš|ÃàƒUƒµð@‰‡KþÈiÐÅKˆï¥ôH\†ÈÝh@²bõ™ÚDF嘜Yˆ ¶1ÀT¦hbvÕéž‹Ô,ÓŒß7„TˆÓ ¢pšvuø¢rGË.e€\šòŒ«rf¹Ã jŽÚw¶,Ÿ¥·Á"9 Mò[¬µ¸‘ew¬R å·Ms]“c`“ß®î™Úܒׄºâ2`6ÚÙ"2Ä„¼NÎJX!«iqÚÜÖ»æ {«XÃDJÓ\Vëícu¨µ”ÿéü;ÉÂÀõ;Á »~çú]×ï­ëŒ5À0^ž(Æy|B4Bà‰®½TQ„$'QŒŠõŸëqû‘Pãýñ¾ŽœxF#I(Å‹ŸÎcH¦´ %´Î`±l®nªkj´•ò3”Øc»¢ü]Ü2c¢ÙŸÞZ›$á2Û‹aŠaöÓUc´¢³Æš-Ø y%7 ~‚¸Ü:cìýû?òÓyº>Y×[×ûy¤0K!&¾Ay XÄõ•nc]³¶ 9b†£d³;Ü%gW€ÌsÆMWñ•xÀ–,{ùÎH±<k¬^Ûõ‡ÓÊ *Ææ1ú‘¡²ðš©Q:(š[å·dB­ÝhV²¹-3•œ£’”+¢ŽýÝQà ‡'f“ý2õÌ;9utMBÃâlJ$“1ÖHˆ[¶¸£Rˆ+“u½ë†»·ÀÏ£\ª1ÎX'YiÁOœÿYÇEä«CTKv9m§ÆÞ Š‰È¦£%ÎÀ€…—Ó—2ó§6Ƥ_KÖž6foxÝ@y6ù–÷÷7{F#Üèš× fòº±É<³öÄ)ãp¥Ǜ㕭ǜ9´ŸvñΤ{[='Ë¿±¥n³•±M.¼ñ+ Ù,f>56ký°’;¶²³5ÖZ7e7nn&È[Ù“ë©ÅËLt(öÁ($^Êâ2/¦e*©¨(~VLRö½Áû-ÞÞ*ÛGNÀ¶Õª ˆV •’«߯€ oÑÊ_ì¶W7¹-‹µÎjO~ yàÖ[µ¤“ïp±-Zv¬2Î1™(sܰü „/:ý{S²Xb5ç+4ÁH,Ú|8±!™BÇ-õÆ:ÇÄBm³ÖÁEŒ1Ö’›÷pÙÀ¥›£þUf1Bz_ÍP¸5YvŸŠ‘º`¹ÂbüÛ¼1%(EPfc&ç$yR­ ·T–%2nua¸zU™B¾ªr[ã°½è4,§£Ø2W³%¸¸æF¬–êû¿=D¤˜#K¢h^§¼tc-'h×׿Ç^¼ÝNÌ:×co?ÈLB‘Ÿ¯4>»®Ç<›ó_yÆF*¨¨ƒJê 5g\N5× ]¿·¶z‚€@™r0›1¹| $ *96åa¸¨ïEIꘄ¨­ŸEx”½EJÝh²se¦è‹A‹æ@ÃCRã-Ôš\ië¸Îh5Æ’Ø×íŽÆÚ´ñ+Wi¹Ò´ÃÏ5) ЄW‚Ö°*ê]îsãÊ΢äÆPEî˜@,aIPa+cd/rdf×£Ÿ.2]}ªI]È&× ˜b&hvš4¤Iž]¿Ï‘l`­µ¶“#±öV ìÙ0½™9ÉÛÛ5Ï(8ål¶™FU †At›Dò,ëÝÎ ‰(VÓ¸ª”7àEã2ôă5ó)¦67½ˆkv%ãŒ!‰¹IáŠþ.( ©}0Ÿg2­WL ÀègDc]Ÿ÷7J›»Ië_þ×vx×i´3½\FÊ8™9™ äK˘î!ÄàÇËW× nØIFƒn¤¢;ç³MÌÒ‚ëvÁÏ—§ò±ó—\ë„zJbÌÅœù~«3Mü,va¾kÝáôèúÛïŽ;ñÓµœ/I–5ÆXD+û¨ø‰‚ŸÆç¤«é›i7 H˜ì+‘¢‡â š¡`õÎð%‚~Œ€hÁ)—³6?]žÄÄ™LqkC@?ž•Uo§rÅÃL¨JÇ’¤©ûÉ.#wFÆvhLðS–ÅD¹'è–ÛGvÝPÚP1WÓÒ ƒUÑœèŠYÔb ˜+žH)Ì€•DJt&ÆuÀ‰Ùp}þŒÆôÃЕH4†©³]Ìâ^‚Ŀ̢/·½xåwÐÉxCrÚî~òó$‚åp}žÆ§~§Ò±]ßVÈÑdQš– ä «µº\7 ‡ûÓMT ãxfAœ]·3Öj²ç+ˆ#sâOç©Ô£ÅoS6£@RKh•"¬jaF:шwÎ<žKÅ@µa5Ù©–m7`«çUÇ?Úuiy33®öwhRÊ+G1ù$窲—8Ìð#1Ë>k)xÊÞc&[¯'pT ZsUž>oóej. ž§'®"L’™ A‹ª¨ÓµõŽt£‰W†sÌÃáDDažØë\ÇÞb¬‚k×µ¿î‡$/ém$!–”ðt<ôû»~w8Ü¿Kâ$ÉO-bH9©«ÓÍDkÅ®¡ Ê‚IFEEÅ,p` >E#ÚBÌWk5F>`­CcŒíµ¶´ØòÈ ‚ÏŒT³hél°¶|”óÉXëp—Yy!©J•&C¦ ÑKúARkg)‚Õå0Ç4ÞÃfºszìúÝ<]ã<ɳŽyïcŠóx„®ßSôÑ϶߹nýä§³Ü{ 3*ªéÒXA˜1Ì}5®ÌŸ]bù(FЦëñí‡ýý#d³ˆj0–Uˆh³•±Nù"â$l‘k|G*.~¹™?•l‰ÐBz}Œ±ÖX§×s¹‡›0D-¿Ð‚ɦ̌ì§ëWc»ãÛI¡alLrãçnm0ƒÅ©_eqT;D·yŽÊk;]¥à’“AƒÖ–ñ3No”$fРÂ<]Â<Eh8ZëDhEY=0GZtüòÂhúúüÙX»;¼1¦f‰Ôr–Yæâb™8\ˆä'?—¼ÕXjÛ F¤ñåDÙ°Cª°|ÂãJ¡Ùdä<-+~°ùr¹Tî€õ/)D¡†/ó -íJó›.DY„ò vë¿ÞêJåo²?VÊ£ËDI4ƒjyªÏ!Ç,î.ºxÌ8Þ eCeç¿È ¸ 7`²2`P‰e·¾Ø²D5È¥Ãnù€…—Á9¯`¥žÞ43= %~(“êqÑ´ú°Eû"ŒÔ!Vzq¹i¼€«T:AR%KÓSaù…Wa“$°ˆQ\ßÿd¢kŠ“iýöÌ\_{µ3–AdurÄà·l±Õ1ßÒR(2…ÑMôÍ“×AÜÊñ\¶ƒ¦Ñ+&Z®Ì•r@Oqª›Ç®-Ú'lbÓolˆLD9‰Œ˜Q?è´cÌ‹w±½aò%~ùNNU1 µ@ao|{9nKhŸ”Ö’òÿÓ ãË—Ÿ‹Ž…™öw§ÇÚ`¶ 3I½`ÈÃá-Q`òšî;>)ßë§+ÞS3óîî1vë‰n æñ™™ý¤è´µ4Äéòœ§_öôîG >Ì“ízëzÁp®ÏŸ˜cSƒbu.-¹Ö渺'Öõ‡Ó#`ÑOWŸm6ªKITº©$‘ó¸.4+7yw÷v÷’ÙÕµ,ÎëÓ/…£˜ð´Å£X+ý42G"6Öö¹““ÄGÙë»®7ÖÍãY¼àe —•¼/‚.b:œúÝlÐÑÏR¦0“—ÊйöëyyþHar«1^¬ó0áÚöì`ð¿ëvûëS5Ð3‚ÖÚèZ¡Ê² ûéÄ1úãÛïŽo>p …Ká§Kž+¥ûý,f D‘c £–UGª¼3ÕŽqÛÉ=˜)#‹HDÌT)-jdƒÁO:&©ßÅa¢èåvùécÜO£ÈH´$¦²ÖÁ ’€±nwì†C7ì]¿ûô»ÌD÷ïÓ ‡¯?ÿ¿®ö§Ç$!M8 ˆ×çÏÓåépz@´Ò-8E¶mÜsáI:+kµ^ÆûØpIü¼Pа䛚®Ý`Žc©¯_m¸RuƒÖ¢r"ŽRqÃ*4¹£Vâ 3 n³É±bòÕU(O×µ&Ó4šÓêC¯àDÖeò‹áÑ[LÞÝ3®Í£Š­sà€X¼ßb{;±ŠY«Ø–oK¿1,5úñ¬5n ò¿’ 9ÕäI7ê[ªF†(bvdj­ú¡^qïg‡î6ë›èyÔv^¼ŠYç†.dx‹´j‚  õÄ|Ε¦TÕK]†6`kôÏ E«öfzN¡·®xËÆ½Uý×׳†×ó3e±EaPyE³'¡¥«1œ¥ûÔ­Œ èù“ÄK™i‚M äˆ(£ ÊšY ¿îÏš)³º¼V1·ºƒ† #[LaʨxË1'õZÂMjÛ·þˆ “xùòVÆrTª›ÅªÇª¤`b#2¼ÅAÈM‹fºjÿ–lXŒAPEÞ'… ƒ]\õÒ=ú_ÿëÿ°< ƒëz 7.Y¯Ê€±îŒu×ç_ gÉÏS‰†/+;ø±("ß|øm=ÕÑ?Íã3*¤«v |nëúÖnù"Íã%9G#äuq„¡ÊtZž©zL¸;¾-#7a©ÄyºŠý¹xhÅâ-»øQˆ n†dûin³M0Ñ»Ÿþ¬PÒÏ_?ðvŸek XÛ=éINÁhØg§ruŸo–¨ áR“tˆ%iÖˆ…™÷Ç·‡·€‰üxöÓµ p}þƒÏ)q(¼¢¢ÝfcÅ9ZL ;9™â<«TR>>|·¿{ðÓ\×£1óxF“´Û~¼H÷³ùj¡1?ÿÿg7ìO?0ÅõO乇û4lK§V'KÐO×ì½àcðR›_¾|d& ËÀ?ø ÑìïÖcQXƒ6:G,áY|A$OÀÏŠQØ@RY–ayðך/_ }° –dÜ¥ÂF¸ÄÔbáI™ÅƒD!Ì“uh–Åî ‘õ»Óã‚eùù:_Ÿs_x ÊwBm/kì‘¥§%µ ú’ÎØ¼nÌÄÕ+/é§O¿ aÑ‚¬ôyÀýîÅu¥w IDAT¨—7ÅÃdŒ3 ^3µDa¶ÖÅy›,³òÜ¡!ft™ãåëÏEï ‡7ï²Ù!Ì×gŠñüõçÕ¥Â-Îöâž«\ª¶ò\¥ªiŒr^ö@…:Çâ>ÈLLwo¿Ûßd[ü(ÇÅ0^¾3Ht‰Ú—ƒý“ÅëÎ@NRcëeôî?çiZoÔ]?”‘Ít=·ÚÊ@…’­sÿøƒëAÂ6K7¹¡¾ƒ8çr|?Êýü$>2‰ûyz1™!)7l×9ׇ0Ç\?”ëqjòÌ+#V­E®ÍµÊ¢0µËP WúŒ¸ÿCƒïs=Þ™ùˆ<ßòéB|1¿W$cf6¬$ša‘sÏ[oøªœÇ›4-9cJ¥“8b¸ÄìÅ®¼ÍIo ý•Íè±…F_ÈåÚ<ÂÛôp¦b#Å”Nk`y“‚\êÁ¶!‹5-H;/5´™N‚EðŽEù‹ Í\… Ê T·,ÅÔœHÅÝéÌaj\ñr‰(c‰¢ƒ`h/µyÄ©;Àåt§JoʯK㢠ã,ʃÕõ`o Mõ‹r³¸èQ¾©G¾õ¥æ-és_^9?ùÛ‚å|‹1³‹ó”˜†elù ŽÇ†S¡ò>ÏÒûõëPÔ_i%s3„•…D4T€ÀG‹cïÖõÞDD/ÎMp+í¢/Ë{ªÉZr믆û1–¹~ùøš§ÕÜp¬¸T›Þ­Þ ³¶‚ÃÌ8m l·ë„|€V5asµ6ª˜+¯;Y†Æ E¹=€€+3 =E¢Sÿùzþ·ÿæ?RRÎ$ûÈDbq+<]YO_~þËõ]ˆÁS˜åâŽo¿KCQ€nØQŒa¾äù]ŒaFSÈ~Â[M»í<>—…éºnsÀÁÏ"–|ùÇàË “iÙáþ15aÁÍDvO!*QÕ#¯ž´‚sÅH¾Àõ» ­¨y8Þ÷ÃEr 2ƺ>'>HæƒÏ±‹lL×´j`8Rëvp Yû¥°Œ•|$ŒMVb2ŸC4ÆÞ¿ÿ1Îv½PLež'¥t9hvlj¯»A:9 a*ÿüî§?óãåËÏÿ ïßý£ÁG?Å0‰Ì#„9ü3÷»;AÔ…„&Û§Ék….Xž¦Ÿ*ˆƒFᤰ-·Ì™òKHÓõ ³bbp®7ÎÍ×3[× ³ðš mU²á\ž>•¾Œ(B¢JöbWï‡ûGë†Òù!¢ë¹ýÓõyº>Ý` àfiå:1X¼ TF¹âP‘À¥­ìhëúÃý»• §ó—R_Kð$¤<‰˜ÐHÅV _h ›õ¢@§w¿1ÆŠ0wºž‹†jÑJnqàPW[ªp'‰`rÁ®Û‰aeYgrÀŽ~¿?%+Š2b^hðb3Úñûw¿Ac§Ë“„k…Yän…#¯zÔ1‹}ÿ:Gš™ûa_ZŸ)µú‹c˜£Ÿ]¿·­ê|ÀÌ!ÌÑÏÒË¥Þ¿ÿÉv»nØS ó˜ÒÞ£ŸýtÑZÒædÚZ)/ÍZ4VDh²ªeL›#xAž@„à}ù« \ò‡bL§5a)÷³K¾ŒNïßý¦fZå(Jä/hŠ—§B(M£*“•Ò±öUø[¼Š’Ú $Ô/i¡È‘]¡z›Õ}Ìž|D(y ‰n)ÏhAX@dm²\ˆ°i…µ©ñâ”l…U¶éÞi ´Ü8_ó³ÊØælÓ´.Â5È0›Í! -²›3æ³EK[™/gâ 4^¸ÜOqSù^ÄÂÆ_æR}Ó©Huöj’Ä`l‡¢íC! K¸¥L^cÏ:ò,"måùóZäG6FBeÞ :+ ÝÊÖʪSÌ;ÖìSjÖéxÛ•å„ȯadÉ®Êîí&7»Kµƒ‚_Ó®}sJ´…R¤ÄŸ[|%ؤí#6jeÇzÙᦠ½Œ£¶,ß*G´Hî˜É“Â]o}×pP êð²â^ºIÛH <$gn©ÓÒ£‚[m•Mžâ“zQÑ®‹xáÅ4¢Å¶>ÑÊ©ÜZÊ7¶8TÕEqºá2*ãš"p;Éý6`Pw`ܨK–V$¨Ž[±™æMú‹1ûF5ÅiCEÐáSº0mQ7¬V:Ët®‘-7§Õâ†8SÂ2}ºþ2—ì ]AÖ$Êc.ÁXçº!#/üUÜLL*U¿ËWž_]‚a'š}„l³PÉ8Õ¶ kQ#ErTe¦û äÒ²ÑRXƒ·=Yˆ"dñÊ<0eãˆã%[=Œ,%KV¾ žý¸®/€1$¶d™ #Ek#O×'?_˱-B’Ñf]'û„ö|Ðg¥ c“ýKqÓAWü )"Àr´3´gy*§ë»¿{`JÙœ@dœ±9d«<1sO[º+®ÊâS!ú]B¸ØWŠL­< ‰D¡“ÿ`fD£óøRø.'¢YËKL!ñøÓ]Ãh­e°@\Ì=Ê ~Ä`\¿`çz0¶£0{Da‰UÂxþ‚ÆXÛKâ¼Å®ßÝ1“\m×Dc¡Ïmc2§0a?.—°¬7W©JOÔâº\P»´д¨rQ6§1ó|=‡y´®gŠ9Рëûì~½'JóÅ M·çYr÷æ¨gP5ƺ.ÉÀ™ ‘®²Öù3¹Tb;ç:XyÚ •ÏŸÿÀ@ÀÐ ».ÙÓc˜«£^ÆÏ€ë±ÚTx‰:k,‚)8¦ ÈÌcA^oëꎺê*ù¢×÷NM…ç±Ûݰ§~8(}+E?VŸy1ÑXc\ýTùMÛÝ=‘$®M×'°ÎšMÁA$]ö•<ª<Ôƒ°nšâ_ ª} sâ¡:)äÅþ›n«1¥­ë#©hZËSvý®ˆyó¯èïy Þ¸®ß$Úuû~w¸<f¦nwèwÇçϸ-ÆŠ"4²"ÏO¥ŒHØ& vý>ÌÃl]ß {¢fêww[UEb4Y²€bKX©1x½£Ë«®î¡yþô×…Jz}ú$µŤr_xÒ*Έæ $8·¥«¼„l>*ªM³Ôf· \±mc5‘ba‚¸u§dÑPï …„·¡:bÈøÔpŒ¢Mœ.OϘ~w(¡Ðé‡$ÁÓz÷gI dcŠÔu›C„‚]®þ9Ã3‘™£Ÿ’­ëî¾a鉉Œíj:Jº$y/lFù*‰CûTÐ]ߥî/3×BÄ@ëPm÷Ð ûi|Ó2isýuo!ËǺΠæ“^̺RI[™QÈZ‚I2ë+¬,ÌŽƒŸÊ+|}þlŒ==þ`û]˜®Ð['@´öoÒ¦•ÙðÓ²5 ÷|íÈH[š÷ää¹ô’MnMˆ6•bhŒ³‹o,[œ¬Õy<3ÑáôN\ÓÎ`zëz!RM×s˜Gí¨¢à#ûH–=¥NˆU¬s1F b¢ì¨ÀN9`eÙT68"æ™'`FkòB•÷1ž¿üA¾çîítaë®BÞ6F¤#¿[ÂZõO`6ê$t L#]h‰†i˜NXÃÄ#˜]o¬­ˆ‰t÷™ÍÛÖ{: +Õ8M]•‡›5þ ¾¦¼Š€Hqö¿s€¦RÆ- C1J¯“A¼m[Õ¥\Û$× †&¦œb&¦å±²“$@œcPã@TÐ_Ù)Y"7+ý'†òŸDb‰ Üµ«'ªØZ¦të¼Ã-Iݼ¼˜¶<`Œß<¹¤¬MÆÖô×’Óoþ1:ú*9Ê£ò-‚·eÅá¯ç9e²÷ ™— »2@ÇÂ-WÈU Æ$¢¬"W”ã&¿›3C}1žÐDƒå§y Ç?§ÕCÒ3–»xåe}%pž±ÈTT(S2}H°j„Å罉å.SÄ>1ïn)ÁùeÎÚ:­~™e¿˜`–Ä޻ꇚó˜(b#ÇÄâaT±h(R U,¼d{­Å¯/é)(žþXb–ÑÊv/Ó_±ß[²ÈÖ”ðúLöacdt½\ôd »"3h94‹‰f ¨òV´KÙ«5ë§³õ¢ZÀ¿eõ£Ò@ã:$™Hð¶t”UaÉÈkqÝ&št±NÊw•EùþÛÿ¸Ü’B.H2;Æê¥¼úBÑŽa6Ö}ø£¿+ õæèSÈ»ŸÎ9ßjº$Ô¾M>W×m×ç©í‚P Ú/®ai/M’Koú §Ç±Ò_VüŠiº|É+Ûa2Äø«ÕçæSqh9õÛXÒÚ Ç”ß¿ÿMbÌ×0Ϙ«c®.SÏÐí¼÷S×ï]¿q¦2Ö‘ë¯ì2%¾§E%üDÁK–Î)Ø"GŠû»·û»·Öõ&ùƒëLÖwaº<ПbàŽªµŒ ŒÏÄäÇ+×IÍz€š™-6Ì£±ÖÚŒADçú’dƒÏGTu£U‰67ýÄUÆ4ï›÷?îßɆy’eÃ|ùú Ë–ô-yjy™çéšMàÊ””]_d˜®ç-Š4(ú9 ÉŒ3Ž=%aÀ°ï²‚ÛØ^GöÄà/ÏŸ€¹mÊüÕ©;6Oœr0”œÉåHœëüÛLTÍ€ñòõca—ôû»²¥åÄŒ)Å)ù9F¿;>Dn¼|®gvÓî @pþèï©FA¹‡pŒƒŸ/~ºRôe”B^-u`5GLð³1Öu¢Ù3G¹?ˆüWIƺaw—QÇ”ÙiŒ‘‰I6Ûõ×ߒع‚0Óõ9cC9§ëS ó›¿ Óõéã?C4?þi ³èjb˜ef”#b!Õ'ÇTæ®ßuûãùóž?ýþÍûß>üþŸþãÃ÷,@nN ýxñs2IØŸ\7|þý?E\LýaØŸ†Ãýæ›Ã|}þÄU(­ëm׋2ÜÏÓ­ö£ ô ©8ûëÙó°?Ê^ô³,ƒÝñAÿ\6óñüeM×ÒÙ)òAdÏêø¨1¢sUÝh·_SÕLÌjâ`Ë¡.Ì3¾q°oÙ¢+;%æ×tX˜K/}½f†0¶ë¼0«•_&-è¡U]=¸ ƒr,Œ¢äT)H,áÆäŒ!Sîvñ2­éæÐZí³ÌíåK¹ô@‹tlDÃHºþäJ¢Q&mYS ɯ¤¢‘Ù íY’Љp39vÍ0ÂJA[ÜpþÖ“~½¼wA1H‹„įûƒ˶‘sàM g 1ƒ¹QÐ6fX¥)šX¡ûCíå¶LÓ^%†l¿bUî_ÃÿTÖy5ÊÊAåªJ‘ŸÅ‹Ì@”Å0ÙhÊËÎÌYÛž›Ò¥¬šN¨n¸b1¨^Òר?p‘/³Ô/¦|¥¸¨[´]€|’¤Ë —K‘áwÅ_•G³ W·ÎŽ…ðµco‘0fééפB™ì´š{(1ÙC_--®èÊ«6J9ùŒzïUó¯ˆ5.ˆZýRY/Ê_0Û–`›¹¨Ð8Þ|5·,ÈóÕª·£y›ðüÏÿíò¥ÁO¸€ Åœ™oÞã†ÃI{zÍãyº>cJ…î’çE ./Š!Ì“À«ûãÛ.ýƒŸ8Æn”º€ˆ®ÏŸT&+—¦fÔ õòöwýþnÏ¥§<ý¹Â èÚØ”X×Ǥ‚‘ˆD·°\´%'ÏõÃéᩆ(øÒ¶JÛ9ž?KÎ2 ˜Ï¥Ó±ßŸ+8üLIï‘nüîx*‹]"Ñe7w·:WLÖ‡TÐeâ;NÃþ”× û„IÖ*3OãyØŸöwoÕHÆ<ú]µßr'ØšWYÕbÅ5rE1P˜…Ä?]Ï©»mæØNK«A5H,‹%(FÁŽ˜ùøæÃp8É+!ÍÒâÏ™rJ("¢(ÞlÊs®BÚËæ#s¯ÖMä²’œ=×k‰ˆ(¶&yÚ bº?ÌÇ·ÊÇ7¶cŽ”µ(1x?®„I/„{^ãé;ßïï†ýÝøüyº<Þýˆh._‰~:>|Ÿw3ìïÆóççÏ¿?Ü¿;Þ¿ÏŸçëYØF»»Gåóž:`Š1øY͑Ў÷ÖuçÏ?[× Çûò˳{i•L»(ŽÀ<]Ÿ¨uß\å0si8Dó#qöÚTúòô¹8/&¦>3Z{züQ’0×™÷pxÓtTÌ1f ó%;FÙrÏ)ÎEmuyþbL¡I ¦hD^¸—1Þ¼/›Å”»E!"XD0¶Ë ò”âá˜8 „Ó ~¾Älf–-±Ld‘¢÷Ó(6wo>ìŽâ€®Šôh/) $øLaKþ ÷R†Ìé²­íLÆí¬ëŒé¦ke-dõ= Ú^ÂV³§?7dF@==mù(4ãâÑAú³]ÖÏŸeqø¹úT$¹˜Éõæ(ï{yjEÝ'¾Ê`4S›ZØ_­njÙ$"Šj®¦š&Í@YÍZJJý¢Ãk»¼r§…%¬^ÿuázÍJÆ[®áV€¯n;nuBVPÔSÄ–UST+ÆÇ ¥p¾?•¸Ø^*6ÕiVVh%¸ ¬t ]¥8#ÓˆÜÛìˆâNÿBÿÍHVÄã¯âÍç–¤¨¨äÿbþð­>ðÅo(^h [Τ¥#ÔÅ{Ö?°Jwg®ÝXÛCZ¹–š3—ö¬™M«I &¯d#ƒÕÂJU¶y• ¦&XÍü¨¬4?wã¹4r eNb‘®s–ñ+Þ~77Õ+­}î Sß+(–ٹɶmMãähŠ¿¿n@EBÓO·¼²4ÑT}uŠœY~Ý&<¶Êƶ\콦úÉIÊ]”¦Î@TA¦-Bïê>ãÚeBŸúÌ2Ž.~Œ¿&_”DGî71¢®Ô5 k+‹j{\[Œ3ƒýþÑ÷Ÿ”ÏT°ìŒ Wƒ®½èuƒ(˽óó ¾ @=æ]»8îîÞ"™*IA*Óæœ³ô‚˜S|¶LP7_×ï»~/9 1òÓeÁ’j—Ùf3bˆíˆ*uEÊù¤b¦ûÇŸØËÌÒì–K ó(ÔYP2ØRZ»aÏ%²<õp,Ìf-´èú”sUjÐÑà<Žåse} æ‘ÀÝþîA:®’uÅÌ~|¦ì[ hÐØÓÃÒ?¥’ ñúü©0ã5²„[I=|#ÿElæ9Eh1ȶ]ŸªŸì˜\y7&yÅó½Q­¤6àøæýîpR$^Àâv­¸T 7]¦\}–‡BÁµ”w4·:B1'D-Ès1g§òõ9¶ë÷ïŒ1®ß…yœ¯g›ºÝ¡ëwç/`¦áðƺþüù¯u®ßˆNâS)z¢`Œe€0D¡ŽœòÙÓô%„$ÄÖR7ìòóUÉës¦$' ¨Uª,š¹¶ÖÌ!ÌûãÛãÃw½©MŸ®ÏKöÐæ8ZxœëQÛ-ã¨Òðb.ò­¶¹<Ü?žXSûïŽ÷ƹR¥fój*Y²ÜZJr&¥ßè…[ölúy1®Ÿ,æØg©æsú<6£±[ŸNešB¡â¯ÿÁ «úC^4ÏUeQ»¦®vŒA2뻸(ŒÖÜÔÚÔ3»éú¤D StŽ]?Xש܆ô[g¢ƒçÕñ›Ömz&ä4–™-"QÀ”yêCâýpÚª»G®,?š~”+´¶K§(<´|Á ¬·v ]²ZO"@IÓa‹ßž£"YKC>#Þ1£ŸsJuûæ¬ßOeZ) u†‚OPD‘½„›Æi—Рˆèë°Æ›%µ°^"âí²w­ëzŠ’1ÒrÆz!¯ç†ý}1ˆÑE4Frk³_Útf[ñ$“ºYd Zë2”— vë\3áWäõš¶Xu ÑÓ"zʹœúª”Y²çÞšbjëz`¨žaPÒ%Ö s¢Yný«u'¶ ¼ Ê°²¤©kùl¯c«e_«9?_±ÝîPqô³u}:BbtýÑHÌž”Ãak˜RÕCÌ "†+o.äŒèŰ<Æ€„˜,µ)*k Êâ(pÞ°5ˆ+šJ’u7$6’ì¾0_Ÿ?ÿAÔýÃþΉtš¹ÛlìVgUÑ*]L kÓ¯•K~¡4³ÍLÚ2‘‘ ¨§O¿sý¾ëw¢Úž¡Ä–6ÙeTÎ…øñá$'(ÌMä‚r[~wÓR7…¬þ\©¦É·¾c{n±¶rûA¹²ehC6 wÄã§fÛ¥â9­ºéj]çúä¢Jƒ[ L±Z%T’Qº1H‹¯BI)rؘø*¿uúòr¤Îñû”»vþü±g±]ŸÈ؈™¶V–X‰\× NÓà¦Ë%l“Ù}V0¢7ﻬ‘g&Æô:‰³@˜ÏE¨¾ž–Åfj·'ƒ9 Û2†4ÖJɦõÏ\OÑå“ û{õz QˆJ![–„ ë¤ïJdàÀ Ù‚@…­W¸ÅOÒì'ÛíäôÌÄAäÎ*N„Ã\h}Ù¥%I¢eigÆ~(Z]!Häô›o¬³®ßßd—& h ƒÓuqbãvÇû¬Š1éC,ÑÕÏ×fƒƒÕ£³U´ùæ Ç8×ÄÇX ˆaž˜¥0ª½i‹¥³ó¶à’Ù?QŒÙÃ=IbeTA šßìc XésqÑ©¶þi[°"±ë÷É;ƲtoD9ym’ˆ Íã“ì »»7w%U ø¼u}K+£çùE˜¯°"ÍgˆþZ&ÒÎì!G¤íÄ”êà­à7cf¸BþO¢DZæ!¯†ǤaEÉâ7N>l:ûýø6ßoOïÓ|¾˜B,Ýø“]¶°Uudfþ†à$'gl›äb¡Íä*KU˜6½(šÂ‚Òt†uÐK`r:ÉæPuÐÄxÖ¾®yDn Ëý1bæ·1°OâýÄÜ:æ¹Í \﫬ī¾Èx`™ÑjËÓ&vÑnŽÆê)Ñ8¸¶ôL™šiK³lN ^”ï,Þµ‹àÖŠßFl™™)Ô¿ìah“=Ã&Ô8?”dØHC]-2F¸©~‹3%³ùìq–Ô÷D%z0Í×ÄŒr›ÈË*ý+"þ÷ÿé¿YÏO€Ä‹­·&Þ¿ûIøA½Jžb(©„+£‡Æ'©özE5­^Hö`…1Ñ û¨ Vb 5a9Ë'l×ï߯Ҳ¹Ïã³4ò¸Pâ3‹'x¡­.<Ò43;‰t.|ññ‡?5Öùùš¿2Àóå QTò‰5_µ˜Dú6É}Äôó¥ eµáy!C§¸ƒ¶Œxûá·ÙHZ¬ý‰)01ûiÔŸ‚(þø§ÿZaˆ]Ÿ>éœgÎÛÔc1]ðãf˰Á8m(d-³F¨Ûˆ¸+ú •‚DëªÌ< €qïcæû÷?íï»aˆqžˆÑ\jXLR„ë°Jy -ã£ÂS¶ls*l’C²²¶m± d×í@4Ò¡öÃNß(Š1ª¬A;Ë‘™Gæ~w7ïeG¡w7ìÀOg¢˜8c}Í1¼ÊÖY–ÃC Þn>ëÍ IDATu½¹Ý[TºS¾'eH¢¤<ÚÐjQòk9’íwe?”Ô@¡Ÿ¸~üä§«Éú¶Å&£]ƒŸ®óõ‰á¥Š¢t޼ʿn×3PrÿHiõq÷¸?=¸®ÇäÌÓå)?ìD¤.e X×ɀ藿úDzGqr82]¿KSŠÅ•-5ÁÓ5¹%äè±,VDëœu=çJCðÀÕ'ìÖé§7Æ Þ_–7¬M˜õÚÖ½ ïŖª,òH%Æ0õýê’ýj,ü ¯ñšßÎŒ«¼¹F/ÔÐpf]ØiòÚý»Å²|ÁtùB1ÌÓ˜jª>D2(Ü FÖ}ãBQâ_Ë'Y JO°”¯‹Ñ6Vä—š¶dýlÌÀÞ²©9XÌ‹Ùòí=5¼¶ØbŠŒdKò¿f%lñi¶ÚSY¥Š4?3+ŽdÝtý¸$­°®£7RÓX-8ÜìûùåO»uŸA_@†. 2G^Ð…@(¸V Ô*[¡ê}ŠÆ€™­ø“•›Oc9ÕÇ¥€—Õ`¦Qû¼ü·^ ¬–;ª5üøèV€ó†u¼øŠ6ÿÆwá‚v•ù Ê€Q0Dc±Íj©–úeüR\â«>Žö:¨Úª,,ÁÍkåo X.\ÓêOÖ“B®í`>ij/²¤Jr%}óS|sWYú¾Âذ¸g4kÆ Qµk µ?õðKk{™~“@‡ˆ:C¸ˆ‚Š#•þ ØB¿ìø?ügÿ–y/ž3îÞ”°ž¬ƒN†7qžæé9!k1 çBÊdoûÍw5†°H–WbfŸ¬Ä™ˆca~ÿGϺÎuëÇs©kütÍf8)¹àïoñúô)ƹ8v®_ ã:ã:•€œK \kkíõ)x÷ö‡ÒÕuÞˆ¢Ÿ˜éúü”ücèæ£U2ÛìØ$fÇDÔ»rŸ)ιѩDh¹±w?º~Šo:?B?ìåxº<}¾>œ6—.†ùôþ7Eª_H[ Äzåuý®Û¯ÏŸÆçÏÝp°®m94ùæ+Ên튴Ѡ9Ü¿¯O×§Çû÷Ç7ï/O¿\Ÿ>ýø§ÿª lñéã| ÷ß½ýpÿþ§âÆPz ™¦ú‰b|þü{louu*·íÊ(»ˆÚøÉˆý°3ÆN—gÈÌ‘Š ¸Îº>ø)†yw|+¼ƒjBM1U‚h`1Ÿ®Ø4s ëvwoïÞ~7g?^²îb.áÉ“Ä7.@c’L…–veí§'¥_f¨†È¦àx‹ÎlៈýîÎG±-icÖ º®ÇÕŽÏDÒôHOV<ó¬ë Ûn¼'o2çD…ˆ¡xY¼‚›ŒHà´¢òãæJŽ£®ß›N^s|óÞvƒšõr˜FÛuÆõÚGæ/ãóç¼ÓkU¬ø¬ëÊsÝŲ.ÎãÒƒÙv½k’åAàšç‰ÆR˜ëdzÅF–ù+0×¹®G“¢çp<ÞÜ·T\òë%úéïþB³\¹n>¦ë“ÏÁ–ò7ˆF²Ø„DòÂ#Ó›¡-j~ˆ+²+€àgcLq\#?ûéŠÖë8†äÓÐï25sýîþÝo$ÆØ_Ï×óçì¹2Á%Õ®Qþ¾4MÙ¬5¹Ûm~5×nO•²/Õ/pŠ3!ÕØ°-DºRßÒ,©j4p9 ºQ¸ˆo'"š¨*~ÚÌÂnˆ¸GiÜè3ð@‘ÓªÛlÇ`AÕÍÖ§y9Óë|oµ<ÈŠZS#bТ±ÆXféˆL¢n5[]Û½åS_ìâV3ÎÒó&ù ™°–-Qíe/6¾ª/¬éÞøŠeÎ-†yî ®ÍÁî ^¶Å°PíhW¡ß7„(ù‚|°Ôü3`Š¡´S).D<=þ s©æËÓÇähL$Òr&ö§nw¸|ý¹Ä\<þôwÊ߯OŸõ=·®3Ö—¯É uª¥~wÜß&o<$ª´·XÓfº>éú]Ñ( }ýå¯üxùîÿ¼,0?]D®‚êõõã™™-`¥É1Ó<žS&§É‚“Œ‚îï ?³¸œK3ƒ×]8ס15+\4º¥Øn%Ï£í/««R7ì²e”ØÑ›ÇŠ9Å·>r=™nCÛyz<=|Ÿl c çëוÍlq }Œ#1èç’ D1p ¶pÃU²ÒƒµÖب­°âN2êÝjò¾h—ýº11,¼X!«úÊWZ׋!_Å ³5NçÏe(¾.,Ê –,ˆš‡jM7ì!¤1s2‡JOÑ:ÌóÑÓ»sy‡¶ë‰"+ÒiEWEc®OŸ F-sD ÿJ¡ýþ>ÿ•‚QœâÚ!ëýîάFõÕ †!F_úBMõ‰1pîY…\JÉWB@´–3•·¼b±¤åG#´ÛTö†Yƒ¬û»‡ýéAžEʤó£HøYRq2-Csqº>çSŠ­ë™c˜GcÜÃ2_Ÿ§Ë×n×ÇÔL÷»R'ýòWÿW7ìïÞ~—Èä1„u,~f>ùC˜ÇÇþ”9fÝ]Ú9³+pŒóXmá\Òü!-Š ^P–¶{¸v¶u’ÁV´ñ˘ïªw¶-h Ÿº2ÍÛh!¨"V±ºÚvà±I\LÒ·î[~‰ Ëcnê°—g¢øâô?UÙ|£².³\”<ºçíNÍ`©ìÆM®‹Ê†Fí 5Bïn\¿)·>"ë‘-þ :öS4FÆvI"œãVoDkŒ•Ø[4)]²P EŠÑcÁr;µ^îŒÉäe3ÑñÕ³º—Wo6ƪ¶¥h®+#oÄ~)F­*.¡±Ç[q޾Õó`ŽR¢[óAh~ëæh_ ü5òµDÄ~½~5ÛÈm_¿&¿×ˆ7„"«niEPf^0ÁëV—ç—À‰0º d2ÉÜôÉ4[~Ê=ra±-1I)¯¼)ÓQCYäc^öxÙ¢Ó'Í ëñ³¾õÛ7_ÒE?Ã*{5YG‘šƒ™cqye EÑ\ªÌ›^9p¤J€O=ëZ*IœuÙΩ‚®Öu(”ÊãE„X“)Mú6DdšÁU}§º‰4Ðàç ÑtÃ!Ÿ¨\Äiû“â±É =(–5e]ﺔ箬ë§ë30wÃ^â¶K̺ëvr bô¼˜$Uõ cݰO‹ÆX×?1uý¾ÄíëŒqnØÉPÔs$ŠÁÃþ§ó׿ýé±ÔAm[ª›fc,£ñÓµhǺ~/Í¡ò©*{´Žÿ%Šf•1VÖbÛVÉQó˜B´IÛ} »ã›’©$ßÓïï˜xŸ]7à°—WOãú݉cœ™âty‚D[WD"Y‰a¶[Š¦Ã¢M>¹ýt}b^Эíd§ àùú,n&ÑO$ƒB=°]?ài¾|-t©ÍЍ|LÈH*{sU(ú×:B¦x¸g¬-4H&ªþ^*,&mý‚ð¤`ñ¼ìÛy›,HáUC p÷3"¢M»žP€F¨„2TÓ#pÙ(Ý|~ ž”,I¢1ØnHï3 Nˆ‹y˜‘HLt¨[¥†8•2;—ûTÑ g'6 ˆ6ØZOd¿1a X9‘°ˆ¯-ÞÀäÊk…ˆfr-_ºšñüEOR]/‘åÃñ˜“‰ƒWF)€1VäÚ´˜„ 1ùEKÌûä«"»ª°uz-cÑI@`ËGCñ 1¼Q0Ô¦Øfëm±ŒŠ~‚"«Ç´¹£±&Óv(ÎL†˜ê䊢u½ëw™ø]3ïNÆ:&ˆÕR'é|Ú«›0D©AÅ!R䀦I÷,ÞåˆÆƒ(ÿä}Aæè§ñé“,!Jóãƒ1NÈç¶ëS4BÚèQ<Âîøüt‰!œHx 1YÛ ‡"Îãs>ð$(ƒÄ‰ÑÌ×gHVm1z/ë-ÆY¶†´ç{ùú‹ó+–†Æv½JÁõC¢q§vÁ¤.ÖÑN^ažP§ôÈiA<1Æ.Zxe˜½ ¬í‡ÃiÏ1†nØ÷»;?]pËæx^¨á5Ñ{=Q˜G?]»~Ð2ëÛïʜȺÎOWa0óP2Ùq¶Szój£ƒa¾0³¸ûñ£?Ü?ÖŸšú&»9¿NÖ Žç¯å’ÜpˆóÃÄL7Û3ìOLÑOÏÖõÝîP˜¹Ž6…ùú©2R–+LÌ1†€ 3 †!eS1 -&ßlÝ@1äý Z@äèÅLTõ75vÇ ÈÏPãÚÀÏ£ëcc“6†ž™Ÿ¾üoð‚¥ÞC•n‡ÓC7 Ô/ú—|š¦K¢àïÞ¼4’(~ÿø¥)I<ùh\7¸~Ÿ§ës?ìíÄVª@%ÒSã\b°eV".%—¯¿,íO™‡ã½¼ÂéPËp‹[LÚ_]RÅ•$Š2ŠÛö÷3âÐÍÀɢЩhR"Å\D—y¤²³ÕtEs²aWeoˆÀEÌ“¹6*öZÁ¤Ä@%”EP”¬²‘’*ߘHƒ"-BÉeÖ …TE–¬jܾ¶°_ÜÊ~^N@™³‡ýbÃÌól#õÊÉg¬3Òñ¤Jª´)b±aÅŠe)¾ ÷myëJ©VrKh6ò(ͫІ6olã"ê¼½*Ö!‡× c¬œbuŽ…(¸íêç¬Rà—úW^ÂÎ)ÒV'¢Q±\y9N[¼që%[⓼•Å{PÎÓÙ¥¥¬«•OGyq¸Ñ“n¹¤–Ô:ÅŸàPÓ$`ÝLÔ+£UQkê.ãd v3SôÙª©LkP6k ˜¢òЕͯèâs¹Àz$góÙÜLÆåНYð·Ê‰šÚá4‘ ùnû£b÷Ýú÷ú•ðZì …ÖXb%kx.ÕºŽ˜M[çÖ.ÙÓ«œ¦ÝèÊZÈWR‡Ä K [(#É9Ë:dL~O™p#¬×lOoи4YgÝB S¯ŠnZÿdlIC¼|q2ê°È`“JùcLy£ñù¯þƒò#|n>Ö(Íéñ{ÁgŒ±®ˆèòõc7쇜j­é-ãó§è§»…Ì4]Ï…9Ísœý˜L 3+DŽC˜8R°®p/Ó5±ëwÅY›2ê•6úeæQƒLR[S ‚ž©Ä0§Uá8 ÅèÃÝÇ»·ß =W™ 1 ¡0_ž>)Ü +š”,šˆÝ"óüfÚÍÓ`c.Bø“Ùv½ã剉æñŒÆœHVœ(âáÈL~º2@×hÜùÓï™ÙÏ—•¿Æ‹©{j›­©¢0Qn¶§¼hŒuÅŸ§ë×}Ð÷c^»ˆlrl,RýBÐ [¡í°®ïwÇâÖa»n¾<¿þ¢+GÝý”¬Ç–‰føå+ƒŸ‰hØí‰¨P®1%‡ÎH#Ï󨲻íË…E"„²¸¿{s¼_²*Õ`ñòô ®Ö9+‡„ô…í çëwû#@•ˆÕç7kc¬±V.CpA¤§Çò¾Ïã6É«K^vm¤­Ñ?yÁ£Q4×JÖ"zøþ·Ç·ïåk„‚ ¶äSüúñ¯]×[×?fi‡70ž??Ÿ\Òß*×//¸ÝñÞØŽ‰¢Ÿž>ýyûá·1ÌÚ= [aãp8Y×]¾~Lð~ŠSÅDëšI³ÿò@Û9Žr ãõ ]ß×Ôñ|{Å\bw¼ =¥)?ÅÐj-¤N‡epBÝ|ò–c¶Ý YcE‹’˜áan:B¥ŸÁn8¼ýá' 1x“ÜCŒÑ:›+sùúk»abàñü¹HP«o@ð[>Aµ†WL1ç§ äÀEdN–‚£Rhmq8=þXpw[¶°Áç<õ’,\bÊ”X! h·èËi¼’åÆ˜\}¥âaHÁ=Œô¢Ýê`Û­3 }´+g“ù»Éu ¨mÀì7.w ,uh3DÞ;ÿ›ðôµæDÕE2Á‰'ÏË‚«Q4š0¿¤QYÝd~é¥Ýèþ5Œ‰-.AC9V”‡Ü`´®ØVæEЛØÅ™á›ür\&0•¹é2QZÙôÚf‚õ—ÿMhþ ¦•³ ¿îv¾¨“À–Qò+©ªÅÿá¦1ØI?5[ü­‹Ïbú"îÌyŠ\€‰åÉpNaš¤íKH=ܤå×e}•8‘~i‚…Œ¦»+GkTíò?§‡¡ÙIêk *‚ªýš h6}FX"ÜfËçvkhðâ^‘Ý+’Ù¡.w²¥_ºoXb Xc\â~•Û›¨ ™uqû}¿}ßpáÚÁ ]?˜Ja#}.à_üÃr ÇBamœ3h•>?~øí߯f¦™üü,T…|z£ Š•°ëw‰ê œcŽ‘aHöá·ÿ¤)”,ˆ4Àœ >jEÏѸ^¶ÍLE*¯\^l‡`A²W³„xÿþ7‡Ó£à%µ™)úéœÛS̃hÖ{м0tÄÕº}ºþpªTû ‡ÈÊ%IÜ]Ãæ5.9`,Fed<3KÚÆ f rŸ“M 3Óîƒýx…ÜÜ”&;ÌWJVaL1"€uçµôèOGH,]K·;9eØß‡“q]‰žˆ~½–493àÆ&–â#ªˆ«¼el¤ójьŠlÇôßaºJ;XŒ-vÇS"šÎØŽh.¯­u…oÁÒÿ×g¶y9â}/ø‰+Ô,l3£TB¡‰Åšqî»ßþyô>F™J‚ˆótM ÄjÛI4^¾áÓv½u¡i#)>\¿[¿;)]k忟Ž-D½}•õÀLîEȆØÜ$ï*„Ï`Q\V¦L°HO’q)G]H¿âÌG=_Ë…¥ Ò†OÛvÑ‚üú±4ó„¥/3n(ŸWIÓ·yϼpS+íuå©€inœÞÖiª‘­» ‹º•¶‡•Û¶o/'•p –ª_|ÝòEÏ0Ö¢…:Eà 7}·oÙ½5ŸT­òšˆ¹ñ&TšÈ†3ݯòçKÕCrjÆ:\É(üU74q—•ÆH¥,<¨¸hÛQÔ—¦·e×>óœ†ÿ†O¸}Ö¸rÐ]/[ÖŸ ß4£æò2Ïc‘aîl¨i%ˆÖž¤$M¬ã¢ 0ÚVìl.³é5ßʧÅñ†¬«(´,£æèßÂ'x½9âj1¼€H¨†`id"DTŽ€€ŒÍ¡²›Tÿwƒ€ò­æðæ6Îm®"ÐÉ^ªg„aº¢µ6-2>œÞ ‡S˜§²¾º®/‡Äyœ§sæy‘Në€ÚΓÔǘ0Éì•‘Iìú¡É.i¼‘Ú7³+4Îa“”Ÿ¬J<'×L1‰‚ªeT:6¢ŸËe+i3Z—ª„~wìwGùù­š/_˜ØÏcyckÀB–ຠl]DóŨI’0uSó°?Ç·ò3]¿³®›Î_s܉0~Nhœ_òј_þ꟤ôöLyíú¡ ¬±Ê„’Z#W¶ê•#Ú4«öàg]ÆæÀXÐõ£¦ï³þ±è•¡QŒ1 jßÖv¬Ü¼˜ùôð°ØDÜâ?g¨c¸î)ä¶j´S«DìJ9Ɖ{©|ȸbEùön–O ™lò¥C@YuÒ[×˧:¾y¸GÁS òËջØhqfu&èRtP]\S Æ84iï±®/±y™@NE¡±߉‡ŸÎÅžm¸{ýTìd‰æüG*Dü6?[k _^¡$ffàà½ð/ê°ѸÎ{ÿî'7ìÂT²StŸ¬´ƒµ±öòô±ØFûiÌÀ6X× k_ÅJ`6¥ü#,ÜÇÔ]ÙÎXw|ónØŸTÎÆõL”wó8]Å{’u(iî{Õ¨ø:z·}Šû±Ö‘íÀL›ek1ÚòÉϳn‚t=(ßÚ ûRN‰yd†^šÇQuý[-‚¨ d-¬QRß&†ò1¢0ìO»ÓC©jqº|I¼?^µb¾!’!j$~bŠ[i ““~uæÝ û†Ì§%"B³Šã_Ã]peßmSÙBòSILñ›ÙÖz°„µ—Ê…6p÷›?oK® þ$úÞ¢ê•(p=_ld¢_/ åBË7h·ø²e[h…ùd5¹Ì¶V ¡’óf¯‰Šâ+Ë&Ët*·¤6:ˆ¯-¬ôhoÛ%mG¨ºè¥_‡ðJÂj;L•€ç53ï¦æx¡›ãí¡í²G} ˆ°ú©Xv[‰ŸÌï|zÖÅ[Jt^ Û~M{µ8•™)sºVF÷ͼv£¡6úEÁr´/¬çËô5=,,ÿy«-@LÆII;ˆjbOÊfÊa´ÊŸ){´cõÁ¾ÙsèÛ•v9¢È¥ˆi Z•Ôàz†åˆ÷›Ïg!§© $Þ~ÖØ°·RAÒƒnÞêDñfo ü­uüJ? ŽÊÿVwÍ™T,Ò£ û±º_‡ºð­½ [‹ ‚‹!èÚvÃîø¦à3~ºP e!QˆÙç‘cðé¬ 1”î2ù¨-‚†ù’úU<“ä.ýÿ­}I$IvÞ{fæî‘Ke­ÝÓ3É!(AĦ« €WýÝ'ý]ñ   RC‰3˜ž^ª*3w7{O‡gË3s÷Ȭ$°§*3ÂÝÖ·|KPOF0¡fò…_ÐÑ (&ãe/LöåôPÉ 6ŤèNrDy~±Ú‘P ÕŒ,Q4TOýØäÖi²ôbT9JýnRº3‘½ œáþM-[hÑ!Tü<ާG×ïºa/úÂèOßå@w]/€‚æ0Î)~²à 3 ¥­›If$Gm4!“(°¤ƒ±ÚFC cË‘ÈÇêk<8–]Þ»…yfDˆºÃ¶$‰É¬X¯#XªM‰ÅBèdæÛ‡I‹DDÙ8ãóxnŠ¥yš°› Æ\jZu¶À¶«Ò|nfdî]äÀ$ðFv¨gÀP95I A@E+B°v€`\o$/L'›ŽoìÒp@¿HšMyàB¶>¯N?ƒ@¦2Ú”ÏÚ˜ÉÏL1]ð£ŸÆàçè)-©XlL üªvQGc Dè8C«ÀÆØØœS Cüì§‹‚]°­ª8Óá#õžu˜ž„ÒkQ‹i<…àEˆCEiCæ³#¬‹„ á™ï!2ääJi$uLy‘ÎÅTÇ&Å€”l03øyÌGµ$Œ‘ºh![²2ç²Ì›à„Ž(÷Ýh“ÉzKpgclþf*€ Äérb&©F94&‹9a‹ª¬Õ…“–`T·Á¥(ns"ë:P Œäƒ…ÌìT‚ëv E¸¦YMaÌ1×t9IM)jakfði¶›²è9©Ö&›¡ýg39¨Ý±uqIÈ(IÊ)L1H\Ãpá0D"‹]¡gE1=/R,L$¼Ú¼—(ó$¿S“Ëï T”–¸xéʳåeWb´ö"Œf¬PB~™ÉXg\Gó‚âm´ÞUë®"²cœ§Xœc±ÖÏs¶Ü–«hÒ7¹”ëmêGO€H`r=3sÆ*!Ê„1±q$!khšòEˆ~¼b €ˆ]wh(tK]MDÅ,^4äÖtBLäÁtýÐ 7ºY6ž>i[b Þ‹N¾LH·»ÉbWk5“(<ËaŽ`¨µmäë0V®Üàºù1¨UĺƞìÍVTÉb¥\Oa6®ûê—ÿ2(ÔµÜÆß?}úåC›!“Ÿ‚0‹%a"²]ÏÄjœ\?t»mPW E‚×ëÞ|ýgÇøôý?IËêîõ‡ÃÝëoó÷ÄáÝÏÿr8ÜÿãÿúŸ²äl×ó}züþñ‡ßîÞìï^ˇù鬱ã!øËÓÇJÉH”šÑ@e(ˆf4¶ë÷À<]ž¶Ž´¤ÌÑä5¦N1n+Ìzñìç¨ÀÄT,\ÖÛ ]?,— ®¶®*«j1…¯þì_sb/œŸ>'lÔÓx~Zf¢\‹bKaIw¾CRæk§ëúàçb»­ïXÀMÿ7¯Þ•DPJeq»Áx~l¸ÙHEtDs]•ö„¤¹œj†ü|8%LaN˜bþ5]çéRŽJ¤‹êdÚÕݰÏ9ËT¯r¬DŒ šÂïg`/èw·»Û5k•áé1 ¡¡µ&jk(VÒbíáê‚̇­ª|­aBIјsÖºyšŠx›ªÔeeŸÆà®e×=¾œð[·\ýG{o¿ÀžKÅ^P©¼¶‚‡J/Öš°/ë„WÎ5ëúj/¿.Ãæ‚rÃ[ç’N1¼…/ÀíÜ+xg“9¥\Óÿ)Ñ3nò¡j¯¹«ùÇÚ5Úðò[t’boûh,23Û¨Í&ª¬±œƒÜ9Ãg,›e©”v$'µ¤Ì—ºM¡”ÇŏЕóbÖú…Z‡¬‡(Õ$\è2”üž¯Ž7¿˜¦ÀWRË«¿Z5,÷®Bp©ð÷Ó¶¬ÂÒe XÔ®¿ùœXÊד9KŒ   ¼—Å3â™aEXô¿tÙçåò¾Åxµ¦•6W›rfˆ¶È +^‚ÈšÆ7IZ¥xøÇå‰I,ûcJ‹¡ë‡»×_ÇrmðÁÏ~ºÌS¤ßFpùöŸ(ø«®úg]¤QxÜLq W‘kµ&²G‘È“²ÒhA%}‹ˆF~8'4(ˆçææ–1N~¸¥Óª±Üîw¯‹¤ CsTlþ#3Û1‡Æ¶)*b/ëŸÌhm&‡ö1PôoíýÛoˆYºÞ8·4|áº9ž§ócÄI'éd®óì0_Œqƒur/`¢RÕW£awk»JÊÀØ€H5ω£ï˜}ôµØø“<Ê™¬uÉ=jõ ÄÒÄÕ+ЀŸ“£dR{` ïKv?f<=Q˜åkTD®Œ*´›ÞXh¬½+HÞ?¹n0®Oàöá+ÉþÃ<=}ü=Þ¼þjºÇã'ÙbýpãúÝãÇoSu„E¼7?ùn·»{-8ÈÝŽó51ðåé­Û„ØmÀ^X±†£‘bM0!¢"@­¥'šfø’°Z•‰ “u€FŠØÏóR\í…[Ò|(Eë:×ïîß~ˆ~ºPaåÝæéüx…Ò§èÿÀÁSð¨Ì:–W¸1&‘ǧó3ÅŒ°öP”UG¡PõE€^>IÖ[£jd»ÁvƒŸ.¦Õ[\’ÈŒ*’Ö’ø€„Üë‡=Q ¼é¢(mmÀõ‹B ¦áp[é¶#˜¤—¹Ð“³]Ä¿0‹dàí«wœ $h»ãÇßçš§qy>/ÎLLªi+MÖB¢H\8ë¢Âeãy<‹.ÄÚâGpªéUQ“dRÂ=Z%Û/J•Gs‰…A]5Rß Øyqü¤à"ZjqjÃRiàe1°Ò—úÒ/R©Û&MU¤±‘Ù×U\à Uk¨¬îKn…=È&‹À—µ6K·¼ íSF›}îp¥ýñl‹Pn[B\¥p•!‘ô±ê,sV…ËD Ùdx}:Væ!¸XŸ¬Dì•,PŒ¥¨è•e š4£€Ò]•¹í4Ú߀kYà‹ÏàjV¿±­Pk|ñWðjÃí z`-½Iò~k±(-él ¯oæÿøâ,£°ü X+{)4!·¬°Ð1ŠXõïJùms+²_Éþ±ln^k²^U«h‡·zæxmÒ±-FèN$rQÐ^0C®Ï??C%¥ägæ¶æBáÿø/ÿ!?PòÐ8RöTÉû³ž•âX]CC“‡Ã²ª ¸Æ¹êÿg?¸½†² ‘§à»~—)DýîVü’õØ!Jb*/0çC\¨˜8˜9‚V¦w%¾6€ôÃD¡ Ñì÷ÃáN̺äÎOŸD‰[³ÊBô†¤Ð^È+’´Îf×i¢@Þÿ2š^¢ÙßÜ·«R;±9›òWDð¥U+W2Êí¢^¼–0/‘eôùí¶i4¦¡é7µ¥²^T-X{œT¨[;LÚu¹0U§hÂLÒ3©3ÚZ„6±%h ¢Ïks7­E6:Áµô´(–‰ZkIubR,Eçø¤îjÖ #æþDÐa2T]ßWîÞ,Eê³ëÐØ»7_ˉÜõûnØëprØß1Óxz4âNåý(i„1½1Îû)ø©ÙXc:?¢µ]¿K×Hé™I/A gò`ãùÑX×ïn¤Õ±œr×ïd$íó”x4ÕìÆ¦5É– •~¥*\V* u¶kŽG cœŸ/€âåºá&ºˆIȃ–‘òÈz@cŒ ýä[LÙçÇbŠLD" "D£X).Eý_ŽªÊŒûÚŠS"îwÈ.öÆã¤MNä­ë±—§O» JèÌI¾©*X³,žÀé[¢ U’!fÂ`¬ÕmÈ.ÕåÁ®¬ëqU Ùö»<ɧ X%þ§wˆ œ€*níìçAb©Üñ¥4AñOù}PyÞµ’&¶^›båcRE¹P©‡g’ =±1?®ÑŒˆÈJ£YètÙ‹\†4ø’³ÊÄkµÐD6&„$­ ºTQ“c ÁzÈÓ&>Jå¥ <þ&BVëvõ¦ì(€![<ÇÏd,±Säu1eýDc^öMÆÆ˜¯éÊ\ÖWb- )„Y”-sAGM«%ÊI)6d*ª<©±nØß2óxzŒA¤tyÃ\µÅ$<¦/ÛÞt@F¼øJæû%ÇO™¤c8JHŠÚŸ|uBÛåþ.TÛLƒËókp d4mÀ'E>îpLjRgËèŒëíƒ}7Pɇ˜ÈgŒ#ì‚'åP5­Õ:wÂUrRC"(¤oD,¼™k]ÿök‰î¥*B~βœýþÖ;‰J^70Ÿ¢Cžëv®ßÁù‘Â,¡|âx~´ÆvÃMl ‘u-Äñô9„óîæU–zùá·ÿÇ »ÝÍ=úñ¼ s\¿G@?O²ŸIá¹—ñx{–Ê—]FF¥È–º²½õmƒèç€É{¢0ìosÛ#WŠÉ5ñE$çú 2õcXc0à3ò@$Úƒù"LÝ’ÈòLÂü’F­ 2Á ýp`f?Ï€ñaææ0] ´ÝùéÇ|ín_sày¾ˆ+'q*e¯~ ¨²9Çz å^Â"šÆ&ñ°¶šJºŠ­6ˆ¼Š\ QIí V¢Þ-G¾Ø¸3-Ò…5Êë!ßjWPË%W±…rn2\’`ME­Ñ‡O R çèÅ]¾…ÈkOD Ÿ% Ñ«±ÆGPUaEš±hW— e´=‡Â^¿øé$ò¤ñäí[ÁºAž$#í-Ë 2@DãtµÓºÎXëC)£kµ|,G-E¢þ N(+ÙXg*œÒÎeM£œ™Q•·\¶ë÷wÌ$µß(À>ú],±] ¹Œ¡f1HAc0ÏÓYdY•Rlôl¶Ì>,’šSŠ·m·Ëì 1è48Õ?²lØ¢¬ˆ›yÙ™äí]"Mæ(ê£Í'ã¾}ֺױψËù»&¾§@G]Ѹ¼Hû$øL¼á€“á C€‘4®}Œ¸)y÷Œˆ‹÷ÅRx̶Yi¹0ż6سåÄ|L>ÃÃÎnà€:Q•Õ’pH÷Q+#½š¥_1Pñï¹”‘p}*1ãA”Ço\R[6Js ±õÈš‡Í¢–ɪüIôÁ´Ô{«ºÜ.{D\þ6ŽÕƒ…ˆk] äó}²%æ¹Æ°nj€¨†´¾Zi榱RRh†S!Ë–™è5ØN#Âܨ zÖ¹Ò1R¼¯’.–SÞIÛÁS¥Ux÷ÏAÉÔEÊEid¡‡TLwbX9&™Å×tµp±§``bÂV“Sõ6–®Ç…̦àK}&N†Q ”’Ѹ¹µùºYw›¸5ê¡‘ÚX,-Z·³\UÆ–¼¡@…-šÿîoÿ†™(Ú›Ymo¶,…g9ÿUåä¬vÖ ¢|ÉÁDëÝÏOØõCJ ?€\ÌA°®·]¯„h¤Ákü±âZ€±Ц³Äëè27/Çyeê¬u`LHè뺯~õ¯d­ûé"†ãÌÐïoE|N„9Z–Ç_±®ÏOãùqiT+Çhn@u²GýÒnØ£±R¸ǃ×_ÿ¹B-#ùIª¸ <ŸRâ JD*uˆEíq:?šmrz?Õ²Ah4AŘlèÅóåX–  ±VʹªÙëB˜9ø„2²1fôB³}:—}~S**í8_Žºªœ“õŸ1Ì}n_ß¾þ Ë x‚·®ËO8]NhPùhrɘƒŸÇ0ÏñH26§¹’ÏãY×NŒëò ¬ç<è•j`…¾ŠX5òI¸®Gcn>äY¾cž)<}úƒRß5[}Ôea Á­´RæŠîFØS º8?\Ï5G¾ë"a¿È@ ó”ý—mÈ”|dÜYq³»?Kb$AL¿»y÷ó_Ëf›ò¸œÈ3…ñ|Ìš Óùó• ¸i"„yÊFA®ßeý¼àg˜‰{_´6«§[Ícìõº®7ÆÞ¼z+²·hŒÈÏãE¹J‹·ëÜÆüê‘Ã!ˆœdÅ k|b,æÉë\—ÚEçHCÉd‹Ù‡D‰5¬ò¹+(Õ¥qWu!/.ꌇàÂˮڿ\)t%É0ÎÝhÆôî£Ö{—±TøÀ™ EÆ&¦1Œê‚‰­Í£¨­|"Ô¶Äh¸ÉjŠî»QYÑ—é_rüK§® aIÁ: Åz˜–ùÌÈi§{Íàh‹QÃý1³ŒÍ¼?îPHñÊ_d}Å¢ºý[ŽÁPåb¯¦QËÂv1&ÖC)ŽèZ">ûü׌:}d^ä¨Yµ°±f8bð±@\/«`“ýà!–`^53ù’'Ä¢ˆ:˜Žîo•¿«á¹fyòò?•ëgCëºd¦Š´aü¤äÖb™•ðÂo5Eö˜6ㆭâ –jÕ¼‡êƒ=š8‚’Æ­þJµŽ ¤+sRé%­ "A s¾è(³Ö‘ªm•¬šÃ p µ<(p%ÛÌ~„€¨ ¾SùŽô\6 ZLçóM+OM“X®ÿ–ÿ›%ù™ÈO#ÜÝ>øi“6‘ËNxE8=U&™.¿*x¿è¯ ‘D?OÞϱ\gQÂÏë5)l•îE Þ‚d¼ŸFc­b@ÍÞŒŸÆMþà‰|1?#úæ×ÿ&3“8zgTQ˜Ç 3‰ïvÕRIA™±Î˜®NÐǸèEsK«Ó…)Sž5fzÑÕW daa¡«¤–›q]“UHb*Ýš—œžõò¥0ÏK‰ŠªöPĽb>ÁÌ»›W‡ûw Ž¤óñc^Ñ6¯¼”÷ó”åžm#˜›T"©éçŒmËt¢Ÿ—õó$Àîˆn(ôÄ =­uÆZAß½þÐTG)|æËérú|½?·NwÎ$‘¿¨E [nÕ×±±ò‚¼–…3yÖ ÚeßQ þNx8Ó ‹k]§­á—'FFØ‹~BLÅê ›‰~þ—­;UÆ–ÂQ[£½~üÃ?Uy¼VíˆëÔ¤`zfÎ;Zk„®´äënñRôÑX Ìïþä¯ ôLÄ?SB#Y¬nôä]&‰iðÓ<]ä8·Æç~üýo„]–Ò¦±‹Y,j^óÇvýÍý;&úñ÷¿‘fáþîõáîÍç~wüø‡o~ýׯºÏ?üÖ£¤Yû»7 ð=¹±˜¡ëw¶ëOŸ¿Kò" ˆUˆšpˆäçàç7_ÿ*)ÕŠk±ô8žçéÝ|?,ªfˆm¯ôº&=¯PµÓ¯¤]ÅMïOGB’j—;­1¦më1×fÚÎ÷BŒB "òuè'$’ JD5ÜaÁ¢Ñ™d2-H&„ íû³1®!¦ŒI²Hž®˜(ÿ?••qôÀ‘´Oî¼—Å87×½0G-ˆ•½]MçÐ %4•€mÊ*ulQ|ĤZö24Ö¨i}ík¥ïøÿý?ÿûÅT!SÈæ[ËúF%ï‚V<êw‡\Pj.*¹']ºÄãÓùü|ÿîçQlÂñÓwˆ¥IÛïNßêל`% £¢y^—ÂsbçS"qÕÞ¿û™ëwrkÏzÞgçi<6H¡¨…=ž±ùÝÅ ×v}ö&‘ØÊ s˜&ÙnÆVýdf~øê—˜RÕXCOuÁérôÓ¥®=êŒÅl…ÄLýpËøˆ‚ßo%45(Ž­ÒÒ+…ä²Éˆ\ßgäÞB×4þXv¨—Û.ÓõÊšI¶J ¼h§J+P¼xò×kïßþ’™\†Ëí8ž?GîAbntýJ:boËúG tRùè¹ýX&Æ&r‘ÈUTV‚®K}z²p.5ËbR¿»Y’Îq[ð¥#ØÎÄîXhÕÎ+UqŒöIK³T•åaæi,Ò¶µszuhÄcáFÃ_< x?çNÞxz4Æûrülmg¤f#îzÉ`ïpÿκNÊ-]? b78¶'Ǹ…Î\ã-yžNòB"/ ÚD!ßÝݰKm?†+¸þQŸEKÔ.½ýæ/2ëÀõ;h¤dèrüütóêCÜkLçÇï»þÐínæñ8'xÛï+z›¸îéƒQ;¨ôU!L–¹`& R{@! vƺóç„`$jgÓxŸdôÃÁöƒÖ…o, d!åJ’ô5ubí\xõnq×°Ÿ.RTcày¼,ø¸¹Ðbš«J|ºaWK+ˆL3Ÿ‹…>±U¤ÚÖjZm¶­g¯çb‹›˜ !©~ Â軫a¯æp… ‹`¬—,¢ â#Mõ:dµmWÕ'.^ˆÃZ‚¨Ó@ÖAhòOÑY»èû­ŸéºƒåVÈ¢Tülófu9‹5ô—uÑÚ¤aÍ_W;U\Qó³ —° [œD\£ùcŠ˜¼†ÔxŠk£×’0Äzñ-„Óš¬ÔÚã*õÓ,[ ×µªpeF®(¦%¾ /Ó;nØk“«üÂVC‡ê %bŒ‘› ªe›„g…É1®°×Öo Òœ¼ÁS+ÉÝ•–"W­î¦?1eeW×&ʧÀÄDÑâT ¨!"‘Y˜<:Œ˶­wKjCøˆ%H1ÁQ8 #Vc^,ž¥)1e^Rõ·M!ºÅU*¾´ÂnÄ(ùi0zAmC xÅZhMFE-6]JÉ¡Jf™B0Æ‚uR‚k€FMb´Z)°˜J®bÕb΂ó4¥r†É39×s‘iËJE¥7›)@-ÚT`,¤œß¡Æ2øñÊ I28vKZB$H¢2€¹l>Ñ,èÜì}ãåßûý]þÅÜèjç3;½eîö³·—¼—ŸÎ’Ÿˆ=o¦¬”»'5-šé+]}æø¥â„•”Lÿȱ ¢¬ ÌBc›ÊX¸hÖBïʲ¼ýéˆP®Ý>|È·rIH |òsˆT³(¥¤Y ÂmùrM-'©XÎJ–œ³·­Y0Öæh)×µŒ1\Wl#_­ôÅt—¥ßߢ1Î ¹/¹ d(oTÛ²šBáZQ/Nt Œ)8¢Š!¥æXê“ø`šEŸDc’†u#'ÍÀ‚<@Ó°wÝüÂ|sÿV8…Íå铵nûúÝÍ<]ÆÓãp¸w†ÊÉÐõ»8brÚÄ–<¢1‰ìû¾yŒµÅ.3Ù#F%0‘,þÜh®œG¡Uoi6WµSbÝÝ AŠ'D€ÝáEß#Õè8xIZ(½ZÜÌB¯šÎ¥…ü<Ÿü|¡äÇütQp*FÀ9*e¦î莣(Ê…‘ì*pšhQF”âÐt9#Yf çÄß(±nÈÏcð^uâMÞ;ÆZc b'S´.—Fäe˜Â|yúÐXëä Nj–â½ÄÁ×+ßZyÆÐzòÄÆ¡ày!Ò Ìù*±6ËsBæX‰˜) í¬T´sÅiyç[Nªä Í"QTÅ.ç`<³ÝŒž<Ûõºª@Ìü•³†™×IÍjê"Ø)9¿W‹˜0.¤˜(,£æ\(ˆ9Òm.8T]Ãôa~IDAT³L ‹ƒÜÕe%4ý°·Ý.)a„eÜžý_2¼®;€k-f?]ÐÓï™!Ó±Sä• ÀVv8TrƈF^3Õå&ERâ\“ŒŒæ´å ´´ïå+(\ƲÐTpFÀ|óð>f–.L¿Âìô¶nJ¿…¦¸ÇÆ8ó&$‚ŒO‘ðÕ‘­0!":D ÆŠ½â00%IêàçóÓGøä%f]¦œ/Ý`˜‚îŒ …Ùôú#.¨1*Íú;Ò>ð4ˆ!êz çàZ&{•cý¤?ß ™•.‡ßÏÕëÒ?=ÿ<ÜüX,DµëFH}þýõ¢ ÖÂ…­»S%æE ˆ¼1ÏšÔ¬ò®‰ª×@æå¯óêÖf%¬q` À„mŽÙúµÌ)9SâêáR­ŸÚ¡ºp† Ú7&{ ýªª¸^bü…@抅:g.ò‹Ûëòÿºé— yÉ =‡Ã…–ž Z•,>Z›äÊ”E¢ZäDc•œ'\GUCVKšµA9¦`:u(iŠ–¢ÕGÁÇ8À˜üX"̾BF²yc¢Ôªq]lƒ‰6&ÐÌj…\Ú~¨ìÔ›YÆÅh:)æ™Ú~ ëÓj?ŸRÐS¥ÈÒLA¨$)V,>VognŠÿ I±ÂiËíRÃLÆG27³²¥”Ee0:ØØ²æŠˆ®U×:IW` ºs໢Õ-©^u+EfS9dâ®M ¶¤»u2 ŠD% ÿîoÿfI­öªš 4œŠ˜óVîÕvÙ^#ædšIDƒÒ¿*¦ylí¨Òh †ýÍ#˜3ýÚZ!ÅÎNµ¦™èþÝ7û»7²Æó“|£vrP7ò âý¸ë…? ¥Ì(Åœñ|¦FjHRpiZ¼þð§U'²òzœ©öö“YÚE"§ Ú_ ®ü@ôÉS^çÆ¹(‰ bŸ ¡ƒÈ¦ñu¶­ÂLÄ~wÈ~Ù-™$ýª{Ÿ5)Ìaºèfûý»_äD'ªø'ô0_ÆË“¦kóBQlZ=X˜ö‚db"¯Ž è†}¥_•€ì.ÅβSD‰#=‘‘RUnF{?i½J¢¾9öû–±¶ÛßÞ#Û •"ÏZãP`8Ì4žºï÷`¬õhÌxþŒÒJr£ÁûDr`fr”j ›P]·ëö7ãñóxú,T¨7?ûs4öó÷¿O›n8<|ø%SøöÿþpÿööáC´"%/˦¸Íø —„Œ­ëm·vGðùi¸yÈo':,h#<$L—ñüÔ ‡nw¸<}œÎÒÜÚß¼2Ö™n€ò-“f)\NŸ§óQªâ]¿7Ææ"ºnü¤ÿ&·K5¸,–}ÑÒù7¢É„z þ|ü"˜öÀL7XÛÉ_œŸ>?ínî`qä&[ÄœS²>f<­àSô1+òËéÓ¬qîõW¿òÓy¾ˆš#ãÐZòs/§OºÄ±Ê*tIOÊh¹$ÝÆ?@TÙ‚ÏÇ`áÉ1xûð~w¸×âBòÝóÑôyx™ÍÁ³ñ¢üYµ9¯¾56üœÅ òÕfõ¢tí_¹úÞXÅ-áç¦+EÇ9$\Îf4¥Ô|\ÄÜÊnNe¤Ñ®¬ìqÓmqÅvàå3^ópÃï~i¸àØ”,kISˆ"é&¯Ì6ÝM?³ø.¥Õ²È˃k6wªõÅHŸ¡ ·ˆsäÔ|-2ïI1!àäÓˆ)æ‘L$ñ†ÆWI†V‰-]ZDäXˆ@õ°£Î‘!IªbÙ×R,æ§‘þŸ\ä©SâYä@˜µå—o­H綺¦T(¡P q¹_ø•×ÔQ ø9uvk³\dN/» VO¯š¬¿Ø@ܨxªõ`2k¨J j#Û´(Ë%f°Ñ5¥ÊPãÌ®Ò'¶®™5#c]UÖ~WlŸºû AÉã®bŒ!Æ¡Á—K°¦,5]Nºj!Öðû»7‡û7Qh<3s˜'AcÅOØ„?”΄è½-uÆ›_1†kf1óýÛŸ¥G‹ç$Þ&’NÁÁG̺´¦q‘²ô»[ï'?]6®pVÑ"WbNQÅÊÌÓEG¸…Bžòa½æüì:!ïc½ËØnDÒLCûÖ®S¥¼•ÂíÛ‡÷·Dë@eñ yüþŸ€Áû9ÃdZ÷,³Ä9­é+j‡ê¬4b 2ƒˆh»^ Þ?…y¶ÝÐ*r0;§ëܪ­Eý0•LsN¦Ñܽþåmm7„yœ.gÉfN "?×õ»Û‡÷z_‡yòóäºÞ6ÙÏ#‡Ðïo3´l< òÌ€HäuúÎLþ˜D|wwóŠüìÅ>£ëLœ×Ïßý?×ï¯Þ3ÓñãwòFýnßínÆãÇér%æxkªÑÖÃßõ;ë:é·îßX×ûâÍ"„7ŸeÛäøò³HOŒ/rÃîþí7À|üømTÉïw®ÛiâY^Zf ê2–0„bVË«=\÷o¿1ŠD!L—ÇnÀ\-p®³Î?2Q²ùêc¿r®æô¢ óØ‚û¤ŒÉ`’pÁÂòÓ3Q-’‡Ýî°¿y•-ZCZ£©$O—§óã°®æ/Å¿kâmz'’ºš¼­ég1ч_þ•´/›¿âG†’çǶÜU¦ñ,jšˆ¸»}°¶sðÓÿ\Â^i´h [ä*àí‡_Hc]~βÕųËäá‘Á|yø“o)8W -`µ’SáöƳ·ÂC¼".´,¤o7R×PãWû¦kó¶¢ê¶êsá‹¿&&±,"’~~ˆdÎô‰ŒÈ×­áöêeXçr¬¼Ñ”j–©¸Ã‚:©»•¼~Ž”h})Àѯ)Ñ%c†$ÐÐ&½*Ú 9²nª:’lQ~猎‰‰pÕ1“#XÐRip,þ'mîTÅwHÃü|EAIDJ:¸Lîê mr"U )TcÌÚ–GuRt²,UZ`Šm™L‰ý’vVÆ÷Üd9\ ÉBÃ>Lüü…zR%²XÙÔ37|šV–— e^,\ÓÏ¡¶‰~„(õLÝ2ld¦JÌú¢ù‹ŽDíWŠPi ˆÀeØZ~ž^¨HrYœµ!ãúÿÛúwˆF¤¹ò-*ÉÁ3÷¹ít9fžÎI±:’4»~¨;y¦ ¬Þ[ÐR5¤äß×=¬U®‰|ÑmfÞß>ܾþ€ÊrOj8ÇOßùy¬Céh¤³ØÔe +”-æ o6\—Y:FzÌo^½Ï}—l¢&ÍùŸÓù8OçŒoZh‚ÿ¬Ô°¸¶>@”d4J6,õð¸–MW¼¦¢25XëÐXÝ ¡àµ®ß·ïèÆÇ®3wý°µ9)„üêî²Ù…¼ %Ň£XŸ¿ÿ-&–Ã<^®¯ánØ¥ ÇY›¦ò‚ò±4J} ÈÅÒ1)(vý~«¯ ¸_½úyFÁzÔ%DÜß½±®W-M—£º¹Ø:gŒ‘½5^N¼°\Œ †ýaó0žŸ¦ËQÎüý훌ǎ'±ôÛ8bÇÓãxú¬ÃÖÝÍ+k16ÝFzœÑÊ1x˜Š  H“ÈÎ¥ZMȦÁqYÖöóTA"%Ãîv7©áb}ž™ ËöùñÓô*Í4;qõƒF;– înriêüô÷·¯‹72@3“Y ¬¾mtݮۦËi¾åÚØÝ¼6&ê>'ñH¢™‚÷~ÖjÜŽbjØÚ#Wæ~8¨“S$åL #Šlˆ,0–hEUGœ¡hôH+úÕû_$)h¶SÞ‹¤›Mú¥ë( RǺnëfaŽlÝÝd Þûü)ݰ·®;ܽÀ\êÛÚ0_’' ÛU¦y<× T¦ËÓ4žn_}é šGÈ_Ò÷C-„ŠÌË›¡÷vkœ¿ÔÃZ­’ÅéȘ­&U±Þ('£-ÍX¯;¿4ƒäÕ*»êèÚÈhËÎÄÆÌF ÆLÑìT%'K©h^CB­üMƒsSù¯E£QŸ7¶:ðJûjƶÙldfãzl0½«êáIìêìp½†y½Ozý-ꥻu\§Õëtòµ—ŒD`{¥#®QìÉ»êÒeˆ ‰-"qò»§ Ö±bû»!h“öLU&e™ŒSˆIf`[×*ŠØé1ªÈr?[ë Žë•þƒÐ¿ß¼R(¬Ï÷š\Q­°¥Þ3¬˜Bæ’ç¾âzó-ÍE$û'om®Ë™Û‡å½™7ëA!H Ë hütIÞdèB‰/¿pïX¿e%olÎðf¶“´wÊ?Ò$x£ÂèD9ˆôaž’1bñ¦o:ĺ+ÄÖ$”*}¬Aøfmçd]Ÿ æ9–0Þ²,!Ìãù˜Õ!aº„YÕ`qÌ錱$»õ1@ˆV±œ lÐÈu…‰µ-ê—‰ËÄ€B‘Dnqó Å@Þ‡0‰T[.åíÈ¥FÉL”lô NÓ gcŒ¥(½ÓåI7›uÆZ­ðÛiiZÛ‰…À­dÊòµâ(#œð!‰Ô"—º&º§­K†ã@[ž$˜,‹“m—ÎýBáærü”ѹEä2ec˺^!üU·6 ¼"®dÖ Ë5ˆ&37Xh™4ž>£±»›; A$0Ñ•¨ôS –®˜ÊZ’ºˆ]t“§ ½ rå8+«Š(v]#Š…DNÕ¡†Kªø_ÒÛ[0|“ÿmÍ?­Ú°˜ìL´°à2ò1šOLZbuCYSÔ㥊| \~qU7¨© ç—’„ã™™v‡{VÅ7³‘±Îº~•a& ˆîçk@ÖœS!2rÔíž—·ªEy ´ÜFY?Su4«›PzN¤Н´NáX­¬Fˆ©‰ÛïnÐX?O.ÂÛ×Ô£ÔG댟.åt@d&™ÜåªC\ÈØ/f×õœ°¡»È&gæ¨{À,†×ÖXôpŽJ¼À~žŒ1ÈÑšxÍ ƒÏ‹Ö— *èúÑ2šrã Sž2" wj_ÁÏ?éÜe)¥/°6þ•€d+ ¼•Â3_Oî¿ôÑÿ»½£±æj…“ahM£Ã¥á8·Â€Í9ŒÛ®· EñklC¦QÌ™Äê6¹<¢þFc]ÂàÜocf6±Äa\ò[6Nòh7oøªo`BÉø²aS$ØDyÙM.i=œ¹Ô—òæ kЉoÕcsý-&h"Û’”KÉ„+ðùóí­§«mÛ%òx§7Tsëg³üG#Îê3dDÇ"5À)XŒÄí j\îoë¸óܡ͖b,#‚’›ÁCÜÖ­wÅYü%GP΋ǥQÓÎ~édxùÈc-Dlb¹QØØP^eeŒúÜaþåL;m9 • B¦! ©ê°©–»h'a³ø#XÆu(ú† Z:ùµ©žô~uÿ`žÆìJwž¯¿éÒî`ËS^’9"êw7Ò×›ŸG?åE}÷‹_»V0hÑ2€à'?ÏÉWÍ _øÌ‹LßHåVÕZ­´¨µ7Ú`ÑUÙÞ>¼—z,H :ú-ŒR°EƒÖöÌ<ž>Õ"à"2âÊ33#ãš0M{ÑEN“¦·D‚È Çp q?5zÐêJ[R„ ­ªnØ?|ø%QøÃÿý9Èn>ܽþ >ò²²÷wI ÝO磞Kï-ÑÜʆ‹â²½-”|¨. ãä-r[Ë ûd¶NÙrF–Wΰ›’íoÔÅ}ZTi»á€ˆa›qÎOÕïnºá¦î·…¦q0]žP‰6ÈÐe—Í}¦y#Ï,uæ¤Krß>¼ßݼÊÚWµ¯^9øh *(QPeƒÍ @Ï%D ~Îj©Ç“eN™‰\¿»{û3Ô¬!fö7®ämÏOŸ‚÷~:– ªí‰¼¢3or~ŠýdÌ Õ+¥CåH³»}¸}øê”ž)ÈÁXª ²Èçñ(øÕÐÔ¨Vëq bK6¸WB?€h]ßÇdúêñeT—džZ,LæuÈ[¿ÿ“¿,šDº*“k¡·ÁNkº—Lðì·m”¾Kcç)Öx…MÓWøg˜ßü†‚Ç:Úet -E¡Êfcà‹+ߺ’·->ùù(y;2-¹C³Hq¾Q”–ˆwP4Av|Ñp'ÂO=|–k/›G¼¾ŠÖŒ$ñ WÈ"G\ÂRžƒäðR(rmôçà?ù'Ú¢N†/“†ˆœ Þ¨‚¼`i}I¹¨@^—ݦÔÕS^:Å|½þý¥éj!§ãÆòâ ÀpÆå½p” a:Ó2/_n›ØbaêäuÉÓþ'ɾø)…´ÖΧEÖü„ˤüùÿŽ«PÏI3„IEND®B`‚crispy-doom-crispy-doom-5.6.4/pkg/osx/disk/dir.DS_Store000066400000000000000000000670441360717211000227550ustar00rootroot00000000000000Bud1 ‡   % DSDB@€ @€ @€ @ .BKGDblob PctBZ.ICVObool.fwi0blobÈÈÍicnv.fwswlong.fwvhshord.icvoblob icv4Pnonebotmd.pictblobZZ Macintosh HDÂÌZgH+y‰žbackground.pngyŽÌɼìÄÿÿÿÿI ÂÌLWɼ޴background.png Macintosh HDWUsers/ilaria/chocolate-doom/build/chocolate-doom-svn/pkg/osx/dsstore-xml/background.png/ ÿÿ.vstltypeicnv ApplicationsIlocblobdÿÿÿÿÿÿChocolate Doom.appIlocblob2dÿÿÿÿÿÿREADMEIlocblob–ÜÿÿÿÿÿÿSoftware LicenseIlocblob,Üÿÿÿÿÿÿcrispy-doom-crispy-doom-5.6.4/pkg/osx/dmgfix000077500000000000000000000040741360717211000210370ustar00rootroot00000000000000#!/usr/bin/osascript -- -- Copyright(C) 2009-2014 Simon Howard -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License -- as published by the Free Software Foundation; either version 2 -- of the License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- -- AppleScript script to automatically set the view properties in a -- .dmg file - ie. the background image, other Finder view options -- and icon positions. -- -- Usage: dmgfix -- on run argv set dmgFile to POSIX file (item 1 of argv) set diskName to item 2 of argv set appName to item 3 of argv tell application "Finder" --activate open dmgFile delay 1 set theDisk to disk diskName open POSIX file "/" -- window options: set bgfile to file "background.png" of theDisk set win to the front Finder window tell win set target to theDisk set current view to icon view set sidebar width to 0 set toolbar visible to false set statusbar visible to false set bounds to {200, 200, 717, 545} end tell tell icon view options of win set icon size to 96 set background picture to bgfile end tell -- hide background file: set bgloc to quoted form of POSIX path of (bgfile as text) do shell script "SetFile -a V " & bgloc -- icon positions: set position of file "README" of theDisk to {110, 220} set position of file "Software License" of theDisk to {400, 220} set position of file appName of theDisk to {110, 80} set position of file "Applications" of theDisk to {400, 80} eject theDisk end tell end run crispy-doom-crispy-doom-5.6.4/pkg/osx/main.m000066400000000000000000000013201360717211000207240ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "Execute.h" int main(int argc, const char *argv[]) { SetProgramLocation(argv[0]); return NSApplicationMain (argc, argv); } crispy-doom-crispy-doom-5.6.4/pkg/win32/000077500000000000000000000000001360717211000177575ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/pkg/win32/.gitignore000066400000000000000000000000201360717211000217370ustar00rootroot00000000000000staging-* *.zip crispy-doom-crispy-doom-5.6.4/pkg/win32/GNUmakefile000066400000000000000000000035041360717211000220330ustar00rootroot00000000000000 include ../config.make TOPLEVEL=../.. DOOM_ZIP=$(PROGRAM_PREFIX)doom-$(PACKAGE_VERSION)-win32.zip HERETIC_ZIP=$(PROGRAM_PREFIX)heretic-$(PACKAGE_VERSION)-win32.zip HEXEN_ZIP=$(PROGRAM_PREFIX)hexen-$(PACKAGE_VERSION)-win32.zip STRIFE_ZIP=$(PROGRAM_PREFIX)strife-$(PACKAGE_VERSION)-win32.zip ZIPS=$(DOOM_ZIP) # $(HERETIC_ZIP) $(HEXEN_ZIP) $(STRIFE_ZIP) DLL_FILES=$(TOPLEVEL)/src/SDL.dll \ $(TOPLEVEL)/src/SDL_mixer.dll \ $(TOPLEVEL)/src/SDL_net.dll all: $(ZIPS) $(ZIPS): zip -j -r $@ $ 0: filename = to_process.pop() for dll in file_dependencies(filename, objdump): try: dll = find_dll(dll, dll_paths) if dll not in result: result |= {dll} to_process |= {dll} except IOError as e: missing |= {dll} return result, missing def get_dll_path(): """Examine command line arguments and determine the DLL search path. If the --path argument is provided, paths from this are added. Furthermore, if --ldflags is provided, this is interpreted as a list of linker flags and the -L paths are used to find associated paths that are likely to contain DLLs, with the assumption that autotools usually installs DLLs to ${prefix}/bin when installing Unix-style libraries into ${prefix}/lib. Returns: List of filesystem paths to check for DLLs. """ result = set(args.dll_path) if args.ldflags != '': for arg in shlex.split(args.ldflags): if arg.startswith("-L"): prefix, libdir = os.path.split(arg[2:]) if libdir != "lib": continue bindir = os.path.join(prefix, "bin") if os.path.exists(bindir): result |= {bindir} return list(result) args = parser.parse_args() dll_path = get_dll_path() dll_files, missing = all_dependencies(args.source, args.objdump, dll_path) # Exit with failure if DLLs are missing. if missing: sys.stderr.write("Missing DLLs not found in path %s:\n" % (dll_path,)) for filename in missing: sys.stderr.write("\t%s\n" % filename) sys.exit(1) # Destination may be a full path (rename) or may be a directory to copy into: # cp foo.exe bar/baz.exe # cp foo.exe bar/ if os.path.isdir(args.destination): dest_dir = args.destination else: dest_dir = os.path.dirname(args.destination) # Copy .exe and DLLs. shutil.copy(args.source, args.destination) for filename in dll_files: shutil.copy(filename, dest_dir) crispy-doom-crispy-doom-5.6.4/quickcheck/000077500000000000000000000000001360717211000203465ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/rpm.spec.in000066400000000000000000000060711360717211000203170ustar00rootroot00000000000000 Name: @PACKAGE@ Summary: @PACKAGE_SHORTDESC@ Version: @VERSION@ Release: 1 Source: https://www.chocolate-doom.org/downloads/@VERSION@/@PACKAGE@-@VERSION@.tar.gz URL: @PACKAGE_URL@ Group: Amusements/Games BuildRoot: /var/tmp/@PACKAGE@-buildroot License: @PACKAGE_LICENSE@ Packager: @PACKAGE_MAINTAINER@ <@PACKAGE_BUGREPORT@> Prefix: %{_prefix} Autoreq: 0 Requires: libSDL-1.2.so.0, libSDL_mixer-1.2.so.0, libSDL_net-1.2.so.0 %prep rm -rf $RPM_BUILD_ROOT %setup -q %build ./configure \ --prefix=/usr \ --exec-prefix=/usr \ --bindir=/usr/bin \ --sbindir=/usr/sbin \ --sysconfdir=/etc \ --datadir=/usr/share \ --includedir=/usr/include \ --libdir=/usr/lib \ --libexecdir=/usr/lib \ --localstatedir=/var/lib \ --sharedstatedir=/usr/com \ --mandir=/usr/share/man \ --infodir=/usr/share/info make %install %makeinstall %clean rm -rf $RPM_BUILD_ROOT %description %(sed -n "/##/ q; p " < README.md) See @PACKAGE_URL@ for more information. %package -n @PROGRAM_PREFIX@heretic Summary: @PACKAGE_SHORTDESC@ (Heretic binaries) Group: Amusements/Games Requires: libSDL-1.2.so.0, libSDL_mixer-1.2.so.0, libSDL_net-1.2.so.0 %files %{_mandir}/man5/@PROGRAM_PREFIX@doom.cfg.5* %{_mandir}/man5/default.cfg.5* %{_mandir}/man6/@PROGRAM_PREFIX@doom.6* %{_mandir}/man6/@PROGRAM_PREFIX@setup.6* %{_mandir}/man6/@PROGRAM_PREFIX@server.6* /usr/share/doc/@PACKAGE@/* /usr/games/@PROGRAM_PREFIX@doom /usr/games/@PROGRAM_PREFIX@server /usr/games/@PROGRAM_PREFIX@doom-setup /usr/share/icons/hicolor/128x128/apps/* /usr/share/applications/* %description -n @PROGRAM_PREFIX@heretic %(sed -n "/##/ q; p " < README.md) These are the Heretic binaries. See @PACKAGE_URL@ for more information. %files -n @PROGRAM_PREFIX@heretic %{_mandir}/man5/@PROGRAM_PREFIX@heretic.cfg.5* %{_mandir}/man5/heretic.cfg.5* %{_mandir}/man6/@PROGRAM_PREFIX@heretic.6* /usr/share/doc/@PROGRAM_PREFIX@heretic/* /usr/games/@PROGRAM_PREFIX@heretic /usr/games/@PROGRAM_PREFIX@heretic-setup %package -n @PROGRAM_PREFIX@hexen Summary: @PACKAGE_SHORTDESC@ (Hexen binaries) Group: Amusements/Games Requires: libSDL-1.2.so.0, libSDL_mixer-1.2.so.0, libSDL_net-1.2.so.0 %description -n @PROGRAM_PREFIX@hexen %(sed -n "/##/ q; p " < README.md) These are the Hexen binaries. See @PACKAGE_URL@ for more information. %files -n @PROGRAM_PREFIX@hexen %{_mandir}/man5/@PROGRAM_PREFIX@hexen.cfg.5* %{_mandir}/man5/hexen.cfg.5* %{_mandir}/man6/@PROGRAM_PREFIX@hexen.6* /usr/share/doc/@PROGRAM_PREFIX@hexen/* /usr/games/@PROGRAM_PREFIX@hexen /usr/games/@PROGRAM_PREFIX@hexen-setup %package -n @PROGRAM_PREFIX@strife Summary: @PACKAGE_SHORTDESC@ (Strife binaries) Group: Amusements/Games Requires: libSDL-1.2.so.0, libSDL_mixer-1.2.so.0, libSDL_net-1.2.so.0 %description -n @PROGRAM_PREFIX@strife %(sed -n "/##/ q; p " < README.md) These are the Strife binaries. See @PACKAGE_URL@ for more information. %files -n @PROGRAM_PREFIX@strife %{_mandir}/man5/@PROGRAM_PREFIX@strife.cfg.5* %{_mandir}/man5/strife.cfg.5* %{_mandir}/man6/@PROGRAM_PREFIX@strife.6* /usr/share/doc/@PROGRAM_PREFIX@strife/* /usr/games/@PROGRAM_PREFIX@strife /usr/games/@PROGRAM_PREFIX@strife-setup crispy-doom-crispy-doom-5.6.4/src/000077500000000000000000000000001360717211000170235ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/src/.gitignore000066400000000000000000000004041360717211000210110ustar00rootroot00000000000000Makefile Makefile.in .deps *.rc crispy-doom crispy-heretic crispy-hexen crispy-server crispy-strife crispy-doom-setup crispy-heretic-setup crispy-hexen-setup crispy-strife-setup crispy-setup *.cfg *.exe *.desktop *.txt !CMakeLists.txt *.metainfo.xml tags TAGS crispy-doom-crispy-doom-5.6.4/src/CMakeLists.txt000066400000000000000000000171771360717211000216000ustar00rootroot00000000000000foreach(SUBDIR doom heretic hexen strife setup) add_subdirectory("${SUBDIR}") endforeach() # Common source files used by absolutely everything: set(COMMON_SOURCE_FILES crispy.c crispy.h i_main.c i_system.c i_system.h m_argv.c m_argv.h m_misc.c m_misc.h) # Dedicated server (chocolate-server): set(DEDSERV_FILES d_dedicated.c d_iwad.c d_iwad.h d_mode.c d_mode.h deh_str.c deh_str.h i_timer.c i_timer.h m_config.c m_config.h net_common.c net_common.h net_dedicated.c net_dedicated.h net_io.c net_io.h net_packet.c net_packet.h net_sdl.c net_sdl.h net_query.c net_query.h net_server.c net_server.h net_structrw.c net_structrw.h z_native.c z_zone.h) add_executable("${PROGRAM_PREFIX}server" WIN32 ${COMMON_SOURCE_FILES} ${DEDSERV_FILES}) target_include_directories("${PROGRAM_PREFIX}server" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../") target_link_libraries("${PROGRAM_PREFIX}server" SDL2::SDL2main SDL2::net) # Source files used by the game binaries (chocolate-doom, etc.) set(GAME_SOURCE_FILES aes_prng.c aes_prng.h d_event.c d_event.h doomkeys.h doomtype.h d_iwad.c d_iwad.h d_loop.c d_loop.h d_mode.c d_mode.h d_ticcmd.h deh_str.c deh_str.h gusconf.c gusconf.h i_cdmus.c i_cdmus.h i_endoom.c i_endoom.h i_glob.c i_glob.h i_input.c i_input.h i_joystick.c i_joystick.h i_swap.h i_midipipe.c i_midipipe.h i_musicpack.c i_oplmusic.c i_pcsound.c i_sdlmusic.c i_sdlsound.c i_sound.c i_sound.h i_timer.c i_timer.h i_video.c i_video.h i_videohr.c i_videohr.h midifile.c midifile.h mus2mid.c mus2mid.h m_bbox.c m_bbox.h m_cheat.c m_cheat.h m_config.c m_config.h m_controls.c m_controls.h m_fixed.c m_fixed.h net_client.c net_client.h net_common.c net_common.h net_dedicated.c net_dedicated.h net_defs.h net_gui.c net_gui.h net_io.c net_io.h net_loop.c net_loop.h net_packet.c net_packet.h net_petname.c net_petname.h net_query.c net_query.h net_sdl.c net_sdl.h net_server.c net_server.h net_structrw.c net_structrw.h sha1.c sha1.h memio.c memio.h tables.c tables.h v_diskicon.c v_diskicon.h v_video.c v_video.h v_patch.h v_trans.c v_trans.h w_checksum.c w_checksum.h w_main.c w_main.h w_wad.c w_wad.h w_file.c w_file.h w_file_stdc.c w_file_posix.c w_file_win32.c w_merge.c w_merge.h z_zone.c z_zone.h) set(GAME_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/../") if(MSVC) list(APPEND GAME_SOURCE_FILES "../win32/win_opendir.c" "../win32/win_opendir.h") list(APPEND GAME_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/win32/") endif() set(DEHACKED_SOURCE_FILES deh_defs.h deh_io.c deh_io.h deh_main.c deh_main.h deh_mapping.c deh_mapping.h deh_text.c) # Some games support dehacked patches, some don't: set(SOURCE_FILES ${COMMON_SOURCE_FILES} ${GAME_SOURCE_FILES}) set(SOURCE_FILES_WITH_DEH ${SOURCE_FILES} ${DEHACKED_SOURCE_FILES}) set(EXTRA_LIBS SDL2::SDL2main SDL2::SDL2 SDL2::mixer SDL2::net textscreen pcsound opl) if(SAMPLERATE_FOUND) list(APPEND EXTRA_LIBS samplerate::samplerate) endif() if(PNG_FOUND) list(APPEND EXTRA_LIBS PNG::PNG) endif() if(WIN32) add_executable("${PROGRAM_PREFIX}doom" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc") else() add_executable("${PROGRAM_PREFIX}doom" ${SOURCE_FILES_WITH_DEH}) endif() target_include_directories("${PROGRAM_PREFIX}doom" PRIVATE ${GAME_INCLUDE_DIRS}) target_link_libraries("${PROGRAM_PREFIX}doom" doom ${EXTRA_LIBS}) if(MSVC) set_target_properties("${PROGRAM_PREFIX}doom" PROPERTIES LINK_FLAGS "/MANIFEST:NO") endif() if(WIN32) add_executable("${PROGRAM_PREFIX}heretic" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc") else() add_executable("${PROGRAM_PREFIX}heretic" ${SOURCE_FILES_WITH_DEH}) endif() target_include_directories("${PROGRAM_PREFIX}heretic" PRIVATE ${GAME_INCLUDE_DIRS}) target_link_libraries("${PROGRAM_PREFIX}heretic" heretic ${EXTRA_LIBS}) if(MSVC) set_target_properties("${PROGRAM_PREFIX}heretic" PROPERTIES LINK_FLAGS "/MANIFEST:NO") endif() if(WIN32) add_executable("${PROGRAM_PREFIX}hexen" WIN32 ${SOURCE_FILES} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc") else() add_executable("${PROGRAM_PREFIX}hexen" ${SOURCE_FILES}) endif() target_include_directories("${PROGRAM_PREFIX}hexen" PRIVATE ${GAME_INCLUDE_DIRS}) target_link_libraries("${PROGRAM_PREFIX}hexen" hexen ${EXTRA_LIBS}) if(MSVC) set_target_properties("${PROGRAM_PREFIX}hexen" PROPERTIES LINK_FLAGS "/MANIFEST:NO") endif() if(WIN32) add_executable("${PROGRAM_PREFIX}strife" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc") else() add_executable("${PROGRAM_PREFIX}strife" ${SOURCE_FILES_WITH_DEH}) endif() target_include_directories("${PROGRAM_PREFIX}strife" PRIVATE ${GAME_INCLUDE_DIRS}) target_link_libraries("${PROGRAM_PREFIX}strife" strife ${EXTRA_LIBS}) if(MSVC) set_target_properties("${PROGRAM_PREFIX}strife" PROPERTIES LINK_FLAGS "/MANIFEST:NO") endif() # Source files needed for chocolate-setup: set(SETUP_FILES deh_str.c deh_str.h d_mode.c d_mode.h d_iwad.c d_iwad.h i_timer.c i_timer.h m_config.c m_config.h m_controls.c m_controls.h net_io.c net_io.h net_packet.c net_packet.h net_petname.c net_petname.h net_sdl.c net_sdl.h net_query.c net_query.h net_structrw.c net_structrw.h z_native.c z_zone.h) if(WIN32) add_executable("${PROGRAM_PREFIX}setup" WIN32 ${SETUP_FILES} ${COMMON_SOURCE_FILES} "${CMAKE_CURRENT_BINARY_DIR}/setup-res.rc") else() add_executable("${PROGRAM_PREFIX}setup" ${SETUP_FILES} ${COMMON_SOURCE_FILES}) endif() target_include_directories("${PROGRAM_PREFIX}setup" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../") target_link_libraries("${PROGRAM_PREFIX}setup" SDL2::SDL2main SDL2::SDL2 SDL2::mixer SDL2::net setup textscreen) if(MSVC) set_target_properties("${PROGRAM_PREFIX}setup" PROPERTIES LINK_FLAGS "/MANIFEST:NO") endif() add_executable(midiread midifile.c z_native.c i_system.c m_argv.c m_misc.c d_iwad.c deh_str.c m_config.c) target_compile_definitions(midiread PRIVATE "-DTEST") target_include_directories(midiread PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../") target_link_libraries(midiread SDL2::SDL2main SDL2::SDL2) add_executable(mus2mid mus2mid.c memio.c z_native.c i_system.c m_argv.c m_misc.c d_iwad.c deh_str.c m_config.c) target_compile_definitions(mus2mid PRIVATE "-DSTANDALONE") target_include_directories(mus2mid PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../") target_link_libraries(mus2mid SDL2::SDL2main SDL2::SDL2) crispy-doom-crispy-doom-5.6.4/src/Doom.desktop.in000066400000000000000000000003221360717211000217160ustar00rootroot00000000000000[Desktop Entry] Name=@PACKAGE_SHORTNAME@ Doom Exec=@PROGRAM_PREFIX@doom Icon=@PROGRAM_PREFIX@doom Type=Application Comment=@PACKAGE_SHORTDESC@ Categories=Game;ActionGame; Keywords=first;person;shooter;vanilla; crispy-doom-crispy-doom-5.6.4/src/Doom.metainfo.xml.in000066400000000000000000000036651360717211000226630ustar00rootroot00000000000000 @PACKAGE_RDNS@.Doom @PACKAGE_SHORTNAME@ Doom @PACKAGE_SHORTDESC@ CC0-1.0 GPL-2.0+ @PACKAGE_MAINTAINER@ @PACKAGE_URL@ @PACKAGE_ISSUES@

@PACKAGE_SHORTNAME@ Doom is a conservative, historically-accurate Doom source port, which is compatible with the thousands of mods and levels that were made before the Doom source code was released. Unlike other source ports, the goal is to preserve the original look, feel, limitations, and bugs of the original DOS executable.

Full support for single- and multi-player games is provided, for all of the original Doom games, Chex Quest, and Hacx. Unlike the original executable, network play is implemented on the IP network stack, allowing it to function on modern LANs and the Internet.

https://www.chocolate-doom.org/wiki/images/9/97/GNOME_FreeDM_DEMO4.png FreeDM, DM05: Metal https://www.chocolate-doom.org/wiki/images/a/a6/GNOME_Doom_II_DEMO2.png Doom II, Level 5: The Waste Tunnels https://www.chocolate-doom.org/wiki/images/4/41/GNOME_Doomsday_of_UAC.png Doomsday of UAC (uac_dead.wad) https://www.chocolate-doom.org/wiki/images/2/2a/GNOME_Freedoom_DTWID_DEMO3.png Doom the Way id Did, on Freedoom. Level 3-2: City of Corpses
crispy-doom-crispy-doom-5.6.4/src/Doom_Screensaver.desktop.in000066400000000000000000000003441360717211000242620ustar00rootroot00000000000000[Desktop Entry] Name=@PACKAGE_SHORTNAME@ Doom Comment=@PACKAGE_SHORTDESC@ TryExec=@PROGRAM_PREFIX@doom Exec=@PROGRAM_PREFIX@doom StartupNotify=false Terminal=false Type=Application OnlyShowIn=GNOME;MATE; Categories=Screensaver; crispy-doom-crispy-doom-5.6.4/src/Heretic.desktop.in000066400000000000000000000003351360717211000224070ustar00rootroot00000000000000[Desktop Entry] Name=@PACKAGE_SHORTNAME@ Heretic Exec=@PROGRAM_PREFIX@heretic Icon=@PROGRAM_PREFIX@doom Type=Application Comment=@PACKAGE_SHORTDESC@ Categories=Game;ActionGame; Keywords=first;person;shooter;doom;vanilla; crispy-doom-crispy-doom-5.6.4/src/Heretic.metainfo.xml.in000066400000000000000000000035351360717211000233440ustar00rootroot00000000000000 @PACKAGE_RDNS@.Heretic @PACKAGE_SHORTNAME@ Heretic @PACKAGE_SHORTDESC@ CC0-1.0 GPL-2.0+ @PACKAGE_MAINTAINER@ @PACKAGE_URL@ @PACKAGE_ISSUES@

@PACKAGE_SHORTNAME@ Heretic is a conservative, historically-accurate Heretic source port, which is compatible with mods and levels that were made before the Heretic source code was released. Unlike other source ports, the goal is to preserve the original look, feel, limitations, and bugs of the original DOS executable.

Full support for single- and multi-player games is provided. Unlike the original executable, network play is implemented on the IP network stack, allowing it to function on modern LANs and the Internet.

https://www.chocolate-doom.org/wiki/images/9/93/GNOME_Heretic_E5M4.png Level E5M4: Courtyard https://www.chocolate-doom.org/wiki/images/1/14/GNOME_Heretic_Shareware_DEMO3.png Shareware Level E1M9: The Graveyard https://www.chocolate-doom.org/wiki/images/3/34/GNOME_Heretic_E4M1.png Level E4M1: Catafalque https://www.chocolate-doom.org/wiki/images/4/42/GNOME_Heretic_Shareware_DEMO1.png Shareware Level E1M3: The Gatehouse
crispy-doom-crispy-doom-5.6.4/src/Hexen.desktop.in000066400000000000000000000003311360717211000220670ustar00rootroot00000000000000[Desktop Entry] Name=@PACKAGE_SHORTNAME@ Hexen Exec=@PROGRAM_PREFIX@hexen Icon=@PROGRAM_PREFIX@doom Type=Application Comment=@PACKAGE_SHORTDESC@ Categories=Game;ActionGame; Keywords=first;person;shooter;doom;vanilla; crispy-doom-crispy-doom-5.6.4/src/Hexen.metainfo.xml.in000066400000000000000000000034601360717211000230250ustar00rootroot00000000000000 @PACKAGE_RDNS@.Hexen @PACKAGE_SHORTNAME@ Hexen @PACKAGE_SHORTDESC@ CC0-1.0 GPL-2.0+ @PACKAGE_MAINTAINER@ @PACKAGE_URL@ @PACKAGE_ISSUES@

@PACKAGE_SHORTNAME@ Hexen is a conservative, historically-accurate Hexen source port, which is compatible with mods and levels that were made before the Hexen source code was released. Unlike other source ports, the goal is to preserve the original look, feel, limitations, and bugs of the original DOS executable.

Full support for single- and multi-player games is provided. Unlike the original executable, network play is implemented on the IP network stack, allowing it to function on modern LANs and the Internet.

https://www.chocolate-doom.org/wiki/images/0/0f/GNOME_Hexen_Guardian_of_Fire.png Level "Guardian of Fire" https://www.chocolate-doom.org/wiki/images/5/5c/GNOME_Hexen_Effluvium.png Level "Effluvium" https://www.chocolate-doom.org/wiki/images/c/c1/GNOME_Hexen_Dragon_Chapel.png Level "Dragon Chapel" https://www.chocolate-doom.org/wiki/images/a/a7/GNOME_Hexen_Darkmere.png Level "Darkmere"
crispy-doom-crispy-doom-5.6.4/src/Makefile.am000066400000000000000000000233251360717211000210640ustar00rootroot00000000000000 SUBDIRS = doom setup # heretic hexen strife execgamesdir = ${bindir} execgames_PROGRAMS = @PROGRAM_PREFIX@doom \ @PROGRAM_PREFIX@server EXTRA_PROGRAMS = @PROGRAM_PREFIX@heretic \ @PROGRAM_PREFIX@hexen \ @PROGRAM_PREFIX@strife noinst_PROGRAMS = @PROGRAM_PREFIX@setup SETUP_BINARIES = @PROGRAM_PREFIX@doom-setup$(EXEEXT) # @PROGRAM_PREFIX@heretic-setup$(EXEEXT) \ # @PROGRAM_PREFIX@hexen-setup$(EXEEXT) \ # @PROGRAM_PREFIX@strife-setup$(EXEEXT) execgames_SCRIPTS = $(SETUP_BINARIES) AM_CFLAGS = -I$(top_srcdir)/textscreen \ -I$(top_srcdir)/opl \ -I$(top_srcdir)/pcsound \ @SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@ # Common source files used by absolutely everything: COMMON_SOURCE_FILES=\ crispy.c crispy.h \ i_main.c \ i_system.c i_system.h \ m_argv.c m_argv.h \ m_misc.c m_misc.h # Dedicated server (chocolate-server): DEDSERV_FILES=\ d_dedicated.c \ d_iwad.c d_iwad.h \ d_mode.c d_mode.h \ deh_str.c deh_str.h \ i_timer.c i_timer.h \ m_config.c m_config.h \ net_common.c net_common.h \ net_dedicated.c net_dedicated.h \ net_io.c net_io.h \ net_packet.c net_packet.h \ net_sdl.c net_sdl.h \ net_query.c net_query.h \ net_server.c net_server.h \ net_structrw.c net_structrw.h \ z_native.c z_zone.h @PROGRAM_PREFIX@server_SOURCES=$(COMMON_SOURCE_FILES) $(DEDSERV_FILES) @PROGRAM_PREFIX@server_LDADD = @LDFLAGS@ @SDLNET_LIBS@ # Source files used by the game binaries (chocolate-doom, etc.) GAME_SOURCE_FILES=\ aes_prng.c aes_prng.h \ d_event.c d_event.h \ doomkeys.h \ doomtype.h \ d_iwad.c d_iwad.h \ d_loop.c d_loop.h \ d_mode.c d_mode.h \ d_ticcmd.h \ deh_str.c deh_str.h \ gusconf.c gusconf.h \ i_cdmus.c i_cdmus.h \ i_endoom.c i_endoom.h \ i_glob.c i_glob.h \ i_input.c i_input.h \ i_joystick.c i_joystick.h \ i_swap.h \ i_midipipe.c i_midipipe.h \ i_musicpack.c \ i_oplmusic.c \ i_pcsound.c \ i_sdlmusic.c \ i_sdlsound.c \ i_sound.c i_sound.h \ i_timer.c i_timer.h \ i_video.c i_video.h \ i_videohr.c i_videohr.h \ midifile.c midifile.h \ mus2mid.c mus2mid.h \ m_bbox.c m_bbox.h \ m_cheat.c m_cheat.h \ m_config.c m_config.h \ m_controls.c m_controls.h \ m_fixed.c m_fixed.h \ net_client.c net_client.h \ net_common.c net_common.h \ net_dedicated.c net_dedicated.h \ net_defs.h \ net_gui.c net_gui.h \ net_io.c net_io.h \ net_loop.c net_loop.h \ net_packet.c net_packet.h \ net_petname.c net_petname.h \ net_query.c net_query.h \ net_sdl.c net_sdl.h \ net_server.c net_server.h \ net_structrw.c net_structrw.h \ sha1.c sha1.h \ memio.c memio.h \ tables.c tables.h \ v_diskicon.c v_diskicon.h \ v_video.c v_video.h \ v_patch.h \ v_trans.c v_trans.h \ w_checksum.c w_checksum.h \ w_main.c w_main.h \ w_wad.c w_wad.h \ w_file.c w_file.h \ w_file_stdc.c \ w_file_posix.c \ w_file_win32.c \ w_merge.c w_merge.h \ z_zone.c z_zone.h DEHACKED_SOURCE_FILES = \ deh_defs.h \ deh_io.c deh_io.h \ deh_main.c deh_main.h \ deh_mapping.c deh_mapping.h \ deh_text.c # Some games support dehacked patches, some don't: SOURCE_FILES = $(COMMON_SOURCE_FILES) $(GAME_SOURCE_FILES) SOURCE_FILES_WITH_DEH = $(SOURCE_FILES) $(DEHACKED_SOURCE_FILES) EXTRA_LIBS = \ $(top_builddir)/textscreen/libtextscreen.a \ $(top_builddir)/pcsound/libpcsound.a \ $(top_builddir)/opl/libopl.a \ @LDFLAGS@ \ @SDL_LIBS@ \ @SDLMIXER_LIBS@ \ @SDLNET_LIBS@ if HAVE_WINDRES @PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc else @PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) endif @PROGRAM_PREFIX@doom_LDADD = doom/libdoom.a $(EXTRA_LIBS) if HAVE_WINDRES @PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc else @PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) endif @PROGRAM_PREFIX@heretic_LDADD = heretic/libheretic.a $(EXTRA_LIBS) if HAVE_WINDRES @PROGRAM_PREFIX@hexen_SOURCES=$(SOURCE_FILES) resource.rc else @PROGRAM_PREFIX@hexen_SOURCES=$(SOURCE_FILES) endif @PROGRAM_PREFIX@hexen_LDADD = hexen/libhexen.a $(EXTRA_LIBS) if HAVE_WINDRES @PROGRAM_PREFIX@strife_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc else @PROGRAM_PREFIX@strife_SOURCES=$(SOURCE_FILES_WITH_DEH) endif @PROGRAM_PREFIX@strife_LDADD = strife/libstrife.a $(EXTRA_LIBS) $(SETUP_BINARIES): @PROGRAM_PREFIX@setup$(EXEEXT) cp @PROGRAM_PREFIX@setup$(EXEEXT) $@ # Make "make" aware of convenience libraries in subdirectories doom/libdoom.a: $(MAKE) -C doom heretic/libheretic.a: $(MAKE) -C heretic hexen/libhexen.a: $(MAKE) -C hexen strife/libstrife.a: $(MAKE) -C strife # Source files needed for chocolate-setup: SETUP_FILES= \ deh_str.c deh_str.h \ d_mode.c d_mode.h \ d_iwad.c d_iwad.h \ i_timer.c i_timer.h \ m_config.c m_config.h \ m_controls.c m_controls.h \ net_io.c net_io.h \ net_packet.c net_packet.h \ net_petname.c net_petname.h \ net_sdl.c net_sdl.h \ net_query.c net_query.h \ net_structrw.c net_structrw.h \ z_native.c z_zone.h if HAVE_WINDRES @PROGRAM_PREFIX@setup_SOURCES=$(SETUP_FILES) $(COMMON_SOURCE_FILES) setup-res.rc else @PROGRAM_PREFIX@setup_SOURCES=$(SETUP_FILES) $(COMMON_SOURCE_FILES) endif @PROGRAM_PREFIX@setup_LDADD = setup/libsetup.a \ $(top_builddir)/textscreen/libtextscreen.a \ @LDFLAGS@ @SDL_LIBS@ @SDLMIXER_LIBS@ @SDLNET_LIBS@ EXTRA_DIST = \ CMakeLists.txt \ icon.c \ Doom_Screensaver.desktop.in \ manifest.xml metainfodir = $(prefix)/share/metainfo metainfo_DATA = \ @PACKAGE_RDNS@.Doom.metainfo.xml # @PACKAGE_RDNS@.Heretic.metainfo.xml \ # @PACKAGE_RDNS@.Hexen.metainfo.xml \ # @PACKAGE_RDNS@.Strife.metainfo.xml @PACKAGE_RDNS@.Doom.metainfo.xml : Doom.metainfo.xml cp Doom.metainfo.xml $@ @PACKAGE_RDNS@.Heretic.metainfo.xml : Heretic.metainfo.xml cp Heretic.metainfo.xml $@ @PACKAGE_RDNS@.Hexen.metainfo.xml : Hexen.metainfo.xml cp Hexen.metainfo.xml $@ @PACKAGE_RDNS@.Strife.metainfo.xml : Strife.metainfo.xml cp Strife.metainfo.xml $@ appdir = $(prefix)/share/applications app_DATA = \ @PACKAGE_RDNS@.Doom.desktop # @PACKAGE_RDNS@.Heretic.desktop \ # @PACKAGE_RDNS@.Hexen.desktop \ # @PACKAGE_RDNS@.Strife.desktop @PACKAGE_RDNS@.Doom.desktop : Doom.desktop cp Doom.desktop $@ @PACKAGE_RDNS@.Heretic.desktop : Heretic.desktop cp Heretic.desktop $@ @PACKAGE_RDNS@.Hexen.desktop : Hexen.desktop cp Hexen.desktop $@ @PACKAGE_RDNS@.Strife.desktop : Strife.desktop cp Strife.desktop $@ screensaverdir = $(prefix)/share/applications/screensavers screensaver_DATA = @PACKAGE_RDNS@.Doom_Screensaver.desktop @PACKAGE_RDNS@.Doom_Screensaver.desktop: Doom_Screensaver.desktop cp Doom_Screensaver.desktop $@ CLEANFILES = $(execgames_SCRIPTS) $(app_DATA) $(screensaver_DATA) .rc.o: $(WINDRES) $< -o $@ %.o : %.rc $(WINDRES) $< -o $@ if HAVE_PYTHON icon.c : $(top_builddir)/data/doom.png $(top_builddir)/data/convert-icon $(top_builddir)/data/doom.png $@ endif midiread : midifile.c $(CC) -DTEST $(CFLAGS) @LDFLAGS@ midifile.c -o $@ MUS2MID_SRC_FILES = mus2mid.c memio.c z_native.c i_system.c m_argv.c m_misc.c mus2mid : $(MUS2MID_SRC_FILES) $(CC) -DSTANDALONE -I$(top_builddir) $(CFLAGS) @LDFLAGS@ \ $(MUS2MID_SRC_FILES) -o $@ crispy-doom-crispy-doom-5.6.4/src/Strife.desktop.in000066400000000000000000000003331360717211000222560ustar00rootroot00000000000000[Desktop Entry] Name=@PACKAGE_SHORTNAME@ Strife Exec=@PROGRAM_PREFIX@strife Icon=@PROGRAM_PREFIX@doom Type=Application Comment=@PACKAGE_SHORTDESC@ Categories=Game;ActionGame; Keywords=first;person;shooter;doom;vanilla; crispy-doom-crispy-doom-5.6.4/src/Strife.metainfo.xml.in000066400000000000000000000034701360717211000232130ustar00rootroot00000000000000 @PACKAGE_RDNS@.Strife @PACKAGE_SHORTNAME@ Strife @PACKAGE_SHORTDESC@ CC0-1.0 GPL-2.0+ @PACKAGE_MAINTAINER@ @PACKAGE_URL@ @PACKAGE_ISSUES@

@PACKAGE_SHORTNAME@ Strife is a conservative, historically-accurate recreation of the Strife engine. It is completely compatible with the original game and mods created with the original engine in mind. Made with a great reverse engineering effort, it has the goal of preserving the original look, feel, limitations, and bugs of the original DOS executable.

Full support for single- and multi-player games is provided. Unlike the original executable, network play is implemented on the IP network stack, allowing it to function on modern LANs and the Internet.

https://www.chocolate-doom.org/wiki/images/b/b2/GNOME_Strife_Rowan.png Talking to Rowan https://www.chocolate-doom.org/wiki/images/1/1f/GNOME_Strife_Town.png The Town https://www.chocolate-doom.org/wiki/images/8/8a/GNOME_Strife_Opening.png Opening Cinematic https://www.chocolate-doom.org/wiki/images/c/c4/GNOME_Strife_Sewage.png In the sewage
crispy-doom-crispy-doom-5.6.4/src/aes_prng.c000066400000000000000000001226431360717211000207750ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // This implements a cryptographically secure pseudorandom number // generator for implementing secure demos. The approach taken is to // use the AES (Rijndael) stream cipher in "counter" mode, encrypting // an incrementing counter. The cipher key acts as the random seed. // Cryptanalysis of AES used in this way has shown it to be an // effective PRNG (see: Empirical Evidence concerning AES, Hellekalek // & Wegenkittl, 2003). // // AES implementation is taken from the Linux kernel's AES // implementation, found in crypto/aes_generic.c. It has been hacked // up to work independently. // #include #include "aes_prng.h" #include "doomtype.h" #include "i_swap.h" /* * Cryptographic API. * * AES Cipher Algorithm. * * Based on Brian Gladman's code. * * Linux developers: * Alexander Kjeldaas * Herbert Valerio Riedel * Kyle McMartin * Adam J. Richter (conversion to 2.5 API). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * --------------------------------------------------------------------------- * Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. * All rights reserved. * * LICENSE TERMS * * The free distribution and use of this software in both source and binary * form is allowed (with or without changes) provided that: * * 1. distributions of this source code include the above copyright * notice, this list of conditions and the following disclaimer; * * 2. distributions in binary form include the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other associated materials; * * 3. the copyright holder's name is not used to endorse products * built using this software without specific written permission. * * ALTERNATIVELY, provided that this notice is retained in full, this product * may be distributed under the terms of the GNU General Public License (GPL), * in which case the provisions of the GPL apply INSTEAD OF those given above. * * DISCLAIMER * * This software is provided 'as is' with no explicit or implied warranties * in respect of its properties, including, but not limited to, correctness * and/or fitness for purpose. * --------------------------------------------------------------------------- */ #define AES_MIN_KEY_SIZE 16 #define AES_MAX_KEY_SIZE 32 #define AES_KEYSIZE_128 16 #define AES_KEYSIZE_192 24 #define AES_KEYSIZE_256 32 #define AES_BLOCK_SIZE 16 #define AES_MAX_KEYLENGTH (15 * 16) #define AES_MAX_KEYLENGTH_U32 (AES_MAX_KEYLENGTH / sizeof(uint32_t)) /* * Please ensure that the first two fields are 16-byte aligned * relative to the start of the structure, i.e., don't move them! */ typedef struct { uint32_t key_enc[AES_MAX_KEYLENGTH_U32]; uint32_t key_dec[AES_MAX_KEYLENGTH_U32]; uint32_t key_length; } aes_context_t; static inline uint8_t get_byte(const uint32_t x, const unsigned n) { return x >> (n << 3); } static const uint32_t rco_tab[10] = { 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 }; static const uint32_t crypto_ft_tab[4][256] = { { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, }, { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a, }, { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16, }, { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616, } }; static const uint32_t crypto_fl_tab[4][256] = { { 0x00000063, 0x0000007c, 0x00000077, 0x0000007b, 0x000000f2, 0x0000006b, 0x0000006f, 0x000000c5, 0x00000030, 0x00000001, 0x00000067, 0x0000002b, 0x000000fe, 0x000000d7, 0x000000ab, 0x00000076, 0x000000ca, 0x00000082, 0x000000c9, 0x0000007d, 0x000000fa, 0x00000059, 0x00000047, 0x000000f0, 0x000000ad, 0x000000d4, 0x000000a2, 0x000000af, 0x0000009c, 0x000000a4, 0x00000072, 0x000000c0, 0x000000b7, 0x000000fd, 0x00000093, 0x00000026, 0x00000036, 0x0000003f, 0x000000f7, 0x000000cc, 0x00000034, 0x000000a5, 0x000000e5, 0x000000f1, 0x00000071, 0x000000d8, 0x00000031, 0x00000015, 0x00000004, 0x000000c7, 0x00000023, 0x000000c3, 0x00000018, 0x00000096, 0x00000005, 0x0000009a, 0x00000007, 0x00000012, 0x00000080, 0x000000e2, 0x000000eb, 0x00000027, 0x000000b2, 0x00000075, 0x00000009, 0x00000083, 0x0000002c, 0x0000001a, 0x0000001b, 0x0000006e, 0x0000005a, 0x000000a0, 0x00000052, 0x0000003b, 0x000000d6, 0x000000b3, 0x00000029, 0x000000e3, 0x0000002f, 0x00000084, 0x00000053, 0x000000d1, 0x00000000, 0x000000ed, 0x00000020, 0x000000fc, 0x000000b1, 0x0000005b, 0x0000006a, 0x000000cb, 0x000000be, 0x00000039, 0x0000004a, 0x0000004c, 0x00000058, 0x000000cf, 0x000000d0, 0x000000ef, 0x000000aa, 0x000000fb, 0x00000043, 0x0000004d, 0x00000033, 0x00000085, 0x00000045, 0x000000f9, 0x00000002, 0x0000007f, 0x00000050, 0x0000003c, 0x0000009f, 0x000000a8, 0x00000051, 0x000000a3, 0x00000040, 0x0000008f, 0x00000092, 0x0000009d, 0x00000038, 0x000000f5, 0x000000bc, 0x000000b6, 0x000000da, 0x00000021, 0x00000010, 0x000000ff, 0x000000f3, 0x000000d2, 0x000000cd, 0x0000000c, 0x00000013, 0x000000ec, 0x0000005f, 0x00000097, 0x00000044, 0x00000017, 0x000000c4, 0x000000a7, 0x0000007e, 0x0000003d, 0x00000064, 0x0000005d, 0x00000019, 0x00000073, 0x00000060, 0x00000081, 0x0000004f, 0x000000dc, 0x00000022, 0x0000002a, 0x00000090, 0x00000088, 0x00000046, 0x000000ee, 0x000000b8, 0x00000014, 0x000000de, 0x0000005e, 0x0000000b, 0x000000db, 0x000000e0, 0x00000032, 0x0000003a, 0x0000000a, 0x00000049, 0x00000006, 0x00000024, 0x0000005c, 0x000000c2, 0x000000d3, 0x000000ac, 0x00000062, 0x00000091, 0x00000095, 0x000000e4, 0x00000079, 0x000000e7, 0x000000c8, 0x00000037, 0x0000006d, 0x0000008d, 0x000000d5, 0x0000004e, 0x000000a9, 0x0000006c, 0x00000056, 0x000000f4, 0x000000ea, 0x00000065, 0x0000007a, 0x000000ae, 0x00000008, 0x000000ba, 0x00000078, 0x00000025, 0x0000002e, 0x0000001c, 0x000000a6, 0x000000b4, 0x000000c6, 0x000000e8, 0x000000dd, 0x00000074, 0x0000001f, 0x0000004b, 0x000000bd, 0x0000008b, 0x0000008a, 0x00000070, 0x0000003e, 0x000000b5, 0x00000066, 0x00000048, 0x00000003, 0x000000f6, 0x0000000e, 0x00000061, 0x00000035, 0x00000057, 0x000000b9, 0x00000086, 0x000000c1, 0x0000001d, 0x0000009e, 0x000000e1, 0x000000f8, 0x00000098, 0x00000011, 0x00000069, 0x000000d9, 0x0000008e, 0x00000094, 0x0000009b, 0x0000001e, 0x00000087, 0x000000e9, 0x000000ce, 0x00000055, 0x00000028, 0x000000df, 0x0000008c, 0x000000a1, 0x00000089, 0x0000000d, 0x000000bf, 0x000000e6, 0x00000042, 0x00000068, 0x00000041, 0x00000099, 0x0000002d, 0x0000000f, 0x000000b0, 0x00000054, 0x000000bb, 0x00000016, }, { 0x00006300, 0x00007c00, 0x00007700, 0x00007b00, 0x0000f200, 0x00006b00, 0x00006f00, 0x0000c500, 0x00003000, 0x00000100, 0x00006700, 0x00002b00, 0x0000fe00, 0x0000d700, 0x0000ab00, 0x00007600, 0x0000ca00, 0x00008200, 0x0000c900, 0x00007d00, 0x0000fa00, 0x00005900, 0x00004700, 0x0000f000, 0x0000ad00, 0x0000d400, 0x0000a200, 0x0000af00, 0x00009c00, 0x0000a400, 0x00007200, 0x0000c000, 0x0000b700, 0x0000fd00, 0x00009300, 0x00002600, 0x00003600, 0x00003f00, 0x0000f700, 0x0000cc00, 0x00003400, 0x0000a500, 0x0000e500, 0x0000f100, 0x00007100, 0x0000d800, 0x00003100, 0x00001500, 0x00000400, 0x0000c700, 0x00002300, 0x0000c300, 0x00001800, 0x00009600, 0x00000500, 0x00009a00, 0x00000700, 0x00001200, 0x00008000, 0x0000e200, 0x0000eb00, 0x00002700, 0x0000b200, 0x00007500, 0x00000900, 0x00008300, 0x00002c00, 0x00001a00, 0x00001b00, 0x00006e00, 0x00005a00, 0x0000a000, 0x00005200, 0x00003b00, 0x0000d600, 0x0000b300, 0x00002900, 0x0000e300, 0x00002f00, 0x00008400, 0x00005300, 0x0000d100, 0x00000000, 0x0000ed00, 0x00002000, 0x0000fc00, 0x0000b100, 0x00005b00, 0x00006a00, 0x0000cb00, 0x0000be00, 0x00003900, 0x00004a00, 0x00004c00, 0x00005800, 0x0000cf00, 0x0000d000, 0x0000ef00, 0x0000aa00, 0x0000fb00, 0x00004300, 0x00004d00, 0x00003300, 0x00008500, 0x00004500, 0x0000f900, 0x00000200, 0x00007f00, 0x00005000, 0x00003c00, 0x00009f00, 0x0000a800, 0x00005100, 0x0000a300, 0x00004000, 0x00008f00, 0x00009200, 0x00009d00, 0x00003800, 0x0000f500, 0x0000bc00, 0x0000b600, 0x0000da00, 0x00002100, 0x00001000, 0x0000ff00, 0x0000f300, 0x0000d200, 0x0000cd00, 0x00000c00, 0x00001300, 0x0000ec00, 0x00005f00, 0x00009700, 0x00004400, 0x00001700, 0x0000c400, 0x0000a700, 0x00007e00, 0x00003d00, 0x00006400, 0x00005d00, 0x00001900, 0x00007300, 0x00006000, 0x00008100, 0x00004f00, 0x0000dc00, 0x00002200, 0x00002a00, 0x00009000, 0x00008800, 0x00004600, 0x0000ee00, 0x0000b800, 0x00001400, 0x0000de00, 0x00005e00, 0x00000b00, 0x0000db00, 0x0000e000, 0x00003200, 0x00003a00, 0x00000a00, 0x00004900, 0x00000600, 0x00002400, 0x00005c00, 0x0000c200, 0x0000d300, 0x0000ac00, 0x00006200, 0x00009100, 0x00009500, 0x0000e400, 0x00007900, 0x0000e700, 0x0000c800, 0x00003700, 0x00006d00, 0x00008d00, 0x0000d500, 0x00004e00, 0x0000a900, 0x00006c00, 0x00005600, 0x0000f400, 0x0000ea00, 0x00006500, 0x00007a00, 0x0000ae00, 0x00000800, 0x0000ba00, 0x00007800, 0x00002500, 0x00002e00, 0x00001c00, 0x0000a600, 0x0000b400, 0x0000c600, 0x0000e800, 0x0000dd00, 0x00007400, 0x00001f00, 0x00004b00, 0x0000bd00, 0x00008b00, 0x00008a00, 0x00007000, 0x00003e00, 0x0000b500, 0x00006600, 0x00004800, 0x00000300, 0x0000f600, 0x00000e00, 0x00006100, 0x00003500, 0x00005700, 0x0000b900, 0x00008600, 0x0000c100, 0x00001d00, 0x00009e00, 0x0000e100, 0x0000f800, 0x00009800, 0x00001100, 0x00006900, 0x0000d900, 0x00008e00, 0x00009400, 0x00009b00, 0x00001e00, 0x00008700, 0x0000e900, 0x0000ce00, 0x00005500, 0x00002800, 0x0000df00, 0x00008c00, 0x0000a100, 0x00008900, 0x00000d00, 0x0000bf00, 0x0000e600, 0x00004200, 0x00006800, 0x00004100, 0x00009900, 0x00002d00, 0x00000f00, 0x0000b000, 0x00005400, 0x0000bb00, 0x00001600, }, { 0x00630000, 0x007c0000, 0x00770000, 0x007b0000, 0x00f20000, 0x006b0000, 0x006f0000, 0x00c50000, 0x00300000, 0x00010000, 0x00670000, 0x002b0000, 0x00fe0000, 0x00d70000, 0x00ab0000, 0x00760000, 0x00ca0000, 0x00820000, 0x00c90000, 0x007d0000, 0x00fa0000, 0x00590000, 0x00470000, 0x00f00000, 0x00ad0000, 0x00d40000, 0x00a20000, 0x00af0000, 0x009c0000, 0x00a40000, 0x00720000, 0x00c00000, 0x00b70000, 0x00fd0000, 0x00930000, 0x00260000, 0x00360000, 0x003f0000, 0x00f70000, 0x00cc0000, 0x00340000, 0x00a50000, 0x00e50000, 0x00f10000, 0x00710000, 0x00d80000, 0x00310000, 0x00150000, 0x00040000, 0x00c70000, 0x00230000, 0x00c30000, 0x00180000, 0x00960000, 0x00050000, 0x009a0000, 0x00070000, 0x00120000, 0x00800000, 0x00e20000, 0x00eb0000, 0x00270000, 0x00b20000, 0x00750000, 0x00090000, 0x00830000, 0x002c0000, 0x001a0000, 0x001b0000, 0x006e0000, 0x005a0000, 0x00a00000, 0x00520000, 0x003b0000, 0x00d60000, 0x00b30000, 0x00290000, 0x00e30000, 0x002f0000, 0x00840000, 0x00530000, 0x00d10000, 0x00000000, 0x00ed0000, 0x00200000, 0x00fc0000, 0x00b10000, 0x005b0000, 0x006a0000, 0x00cb0000, 0x00be0000, 0x00390000, 0x004a0000, 0x004c0000, 0x00580000, 0x00cf0000, 0x00d00000, 0x00ef0000, 0x00aa0000, 0x00fb0000, 0x00430000, 0x004d0000, 0x00330000, 0x00850000, 0x00450000, 0x00f90000, 0x00020000, 0x007f0000, 0x00500000, 0x003c0000, 0x009f0000, 0x00a80000, 0x00510000, 0x00a30000, 0x00400000, 0x008f0000, 0x00920000, 0x009d0000, 0x00380000, 0x00f50000, 0x00bc0000, 0x00b60000, 0x00da0000, 0x00210000, 0x00100000, 0x00ff0000, 0x00f30000, 0x00d20000, 0x00cd0000, 0x000c0000, 0x00130000, 0x00ec0000, 0x005f0000, 0x00970000, 0x00440000, 0x00170000, 0x00c40000, 0x00a70000, 0x007e0000, 0x003d0000, 0x00640000, 0x005d0000, 0x00190000, 0x00730000, 0x00600000, 0x00810000, 0x004f0000, 0x00dc0000, 0x00220000, 0x002a0000, 0x00900000, 0x00880000, 0x00460000, 0x00ee0000, 0x00b80000, 0x00140000, 0x00de0000, 0x005e0000, 0x000b0000, 0x00db0000, 0x00e00000, 0x00320000, 0x003a0000, 0x000a0000, 0x00490000, 0x00060000, 0x00240000, 0x005c0000, 0x00c20000, 0x00d30000, 0x00ac0000, 0x00620000, 0x00910000, 0x00950000, 0x00e40000, 0x00790000, 0x00e70000, 0x00c80000, 0x00370000, 0x006d0000, 0x008d0000, 0x00d50000, 0x004e0000, 0x00a90000, 0x006c0000, 0x00560000, 0x00f40000, 0x00ea0000, 0x00650000, 0x007a0000, 0x00ae0000, 0x00080000, 0x00ba0000, 0x00780000, 0x00250000, 0x002e0000, 0x001c0000, 0x00a60000, 0x00b40000, 0x00c60000, 0x00e80000, 0x00dd0000, 0x00740000, 0x001f0000, 0x004b0000, 0x00bd0000, 0x008b0000, 0x008a0000, 0x00700000, 0x003e0000, 0x00b50000, 0x00660000, 0x00480000, 0x00030000, 0x00f60000, 0x000e0000, 0x00610000, 0x00350000, 0x00570000, 0x00b90000, 0x00860000, 0x00c10000, 0x001d0000, 0x009e0000, 0x00e10000, 0x00f80000, 0x00980000, 0x00110000, 0x00690000, 0x00d90000, 0x008e0000, 0x00940000, 0x009b0000, 0x001e0000, 0x00870000, 0x00e90000, 0x00ce0000, 0x00550000, 0x00280000, 0x00df0000, 0x008c0000, 0x00a10000, 0x00890000, 0x000d0000, 0x00bf0000, 0x00e60000, 0x00420000, 0x00680000, 0x00410000, 0x00990000, 0x002d0000, 0x000f0000, 0x00b00000, 0x00540000, 0x00bb0000, 0x00160000, }, { 0x63000000, 0x7c000000, 0x77000000, 0x7b000000, 0xf2000000, 0x6b000000, 0x6f000000, 0xc5000000, 0x30000000, 0x01000000, 0x67000000, 0x2b000000, 0xfe000000, 0xd7000000, 0xab000000, 0x76000000, 0xca000000, 0x82000000, 0xc9000000, 0x7d000000, 0xfa000000, 0x59000000, 0x47000000, 0xf0000000, 0xad000000, 0xd4000000, 0xa2000000, 0xaf000000, 0x9c000000, 0xa4000000, 0x72000000, 0xc0000000, 0xb7000000, 0xfd000000, 0x93000000, 0x26000000, 0x36000000, 0x3f000000, 0xf7000000, 0xcc000000, 0x34000000, 0xa5000000, 0xe5000000, 0xf1000000, 0x71000000, 0xd8000000, 0x31000000, 0x15000000, 0x04000000, 0xc7000000, 0x23000000, 0xc3000000, 0x18000000, 0x96000000, 0x05000000, 0x9a000000, 0x07000000, 0x12000000, 0x80000000, 0xe2000000, 0xeb000000, 0x27000000, 0xb2000000, 0x75000000, 0x09000000, 0x83000000, 0x2c000000, 0x1a000000, 0x1b000000, 0x6e000000, 0x5a000000, 0xa0000000, 0x52000000, 0x3b000000, 0xd6000000, 0xb3000000, 0x29000000, 0xe3000000, 0x2f000000, 0x84000000, 0x53000000, 0xd1000000, 0x00000000, 0xed000000, 0x20000000, 0xfc000000, 0xb1000000, 0x5b000000, 0x6a000000, 0xcb000000, 0xbe000000, 0x39000000, 0x4a000000, 0x4c000000, 0x58000000, 0xcf000000, 0xd0000000, 0xef000000, 0xaa000000, 0xfb000000, 0x43000000, 0x4d000000, 0x33000000, 0x85000000, 0x45000000, 0xf9000000, 0x02000000, 0x7f000000, 0x50000000, 0x3c000000, 0x9f000000, 0xa8000000, 0x51000000, 0xa3000000, 0x40000000, 0x8f000000, 0x92000000, 0x9d000000, 0x38000000, 0xf5000000, 0xbc000000, 0xb6000000, 0xda000000, 0x21000000, 0x10000000, 0xff000000, 0xf3000000, 0xd2000000, 0xcd000000, 0x0c000000, 0x13000000, 0xec000000, 0x5f000000, 0x97000000, 0x44000000, 0x17000000, 0xc4000000, 0xa7000000, 0x7e000000, 0x3d000000, 0x64000000, 0x5d000000, 0x19000000, 0x73000000, 0x60000000, 0x81000000, 0x4f000000, 0xdc000000, 0x22000000, 0x2a000000, 0x90000000, 0x88000000, 0x46000000, 0xee000000, 0xb8000000, 0x14000000, 0xde000000, 0x5e000000, 0x0b000000, 0xdb000000, 0xe0000000, 0x32000000, 0x3a000000, 0x0a000000, 0x49000000, 0x06000000, 0x24000000, 0x5c000000, 0xc2000000, 0xd3000000, 0xac000000, 0x62000000, 0x91000000, 0x95000000, 0xe4000000, 0x79000000, 0xe7000000, 0xc8000000, 0x37000000, 0x6d000000, 0x8d000000, 0xd5000000, 0x4e000000, 0xa9000000, 0x6c000000, 0x56000000, 0xf4000000, 0xea000000, 0x65000000, 0x7a000000, 0xae000000, 0x08000000, 0xba000000, 0x78000000, 0x25000000, 0x2e000000, 0x1c000000, 0xa6000000, 0xb4000000, 0xc6000000, 0xe8000000, 0xdd000000, 0x74000000, 0x1f000000, 0x4b000000, 0xbd000000, 0x8b000000, 0x8a000000, 0x70000000, 0x3e000000, 0xb5000000, 0x66000000, 0x48000000, 0x03000000, 0xf6000000, 0x0e000000, 0x61000000, 0x35000000, 0x57000000, 0xb9000000, 0x86000000, 0xc1000000, 0x1d000000, 0x9e000000, 0xe1000000, 0xf8000000, 0x98000000, 0x11000000, 0x69000000, 0xd9000000, 0x8e000000, 0x94000000, 0x9b000000, 0x1e000000, 0x87000000, 0xe9000000, 0xce000000, 0x55000000, 0x28000000, 0xdf000000, 0x8c000000, 0xa1000000, 0x89000000, 0x0d000000, 0xbf000000, 0xe6000000, 0x42000000, 0x68000000, 0x41000000, 0x99000000, 0x2d000000, 0x0f000000, 0xb0000000, 0x54000000, 0xbb000000, 0x16000000, } }; /* initialise the key schedule from the user supplied key */ static uint32_t aes_ror32(uint32_t word, unsigned int shift) { return (word >> shift) | (word << (32 - shift)); } #define cpu_to_le32(x) SDL_SwapLE32(x) #define le32_to_cpu(x) SDL_SwapLE32(x) #define star_x(x) (((x) & 0x7f7f7f7f) << 1) ^ ((((x) & 0x80808080) >> 7) * 0x1b) #define imix_col(y, x) do { \ u = star_x(x); \ v = star_x(u); \ w = star_x(v); \ t = w ^ (x); \ (y) = u ^ v ^ w; \ (y) ^= aes_ror32(u ^ t, 8) ^ \ aes_ror32(v ^ t, 16) ^ \ aes_ror32(t, 24); \ } while (0) #define ls_box(x) \ crypto_fl_tab[0][get_byte(x, 0)] ^ \ crypto_fl_tab[1][get_byte(x, 1)] ^ \ crypto_fl_tab[2][get_byte(x, 2)] ^ \ crypto_fl_tab[3][get_byte(x, 3)] #define loop4(i) do { \ t = aes_ror32(t, 8); \ t = ls_box(t) ^ rco_tab[i]; \ t ^= ctx->key_enc[4 * i]; \ ctx->key_enc[4 * i + 4] = t; \ t ^= ctx->key_enc[4 * i + 1]; \ ctx->key_enc[4 * i + 5] = t; \ t ^= ctx->key_enc[4 * i + 2]; \ ctx->key_enc[4 * i + 6] = t; \ t ^= ctx->key_enc[4 * i + 3]; \ ctx->key_enc[4 * i + 7] = t; \ } while (0) #define loop6(i) do { \ t = aes_ror32(t, 8); \ t = ls_box(t) ^ rco_tab[i]; \ t ^= ctx->key_enc[6 * i]; \ ctx->key_enc[6 * i + 6] = t; \ t ^= ctx->key_enc[6 * i + 1]; \ ctx->key_enc[6 * i + 7] = t; \ t ^= ctx->key_enc[6 * i + 2]; \ ctx->key_enc[6 * i + 8] = t; \ t ^= ctx->key_enc[6 * i + 3]; \ ctx->key_enc[6 * i + 9] = t; \ t ^= ctx->key_enc[6 * i + 4]; \ ctx->key_enc[6 * i + 10] = t; \ t ^= ctx->key_enc[6 * i + 5]; \ ctx->key_enc[6 * i + 11] = t; \ } while (0) #define loop8tophalf(i) do { \ t = aes_ror32(t, 8); \ t = ls_box(t) ^ rco_tab[i]; \ t ^= ctx->key_enc[8 * i]; \ ctx->key_enc[8 * i + 8] = t; \ t ^= ctx->key_enc[8 * i + 1]; \ ctx->key_enc[8 * i + 9] = t; \ t ^= ctx->key_enc[8 * i + 2]; \ ctx->key_enc[8 * i + 10] = t; \ t ^= ctx->key_enc[8 * i + 3]; \ ctx->key_enc[8 * i + 11] = t; \ } while (0) #define loop8(i) do { \ loop8tophalf(i); \ t = ctx->key_enc[8 * i + 4] ^ ls_box(t); \ ctx->key_enc[8 * i + 12] = t; \ t ^= ctx->key_enc[8 * i + 5]; \ ctx->key_enc[8 * i + 13] = t; \ t ^= ctx->key_enc[8 * i + 6]; \ ctx->key_enc[8 * i + 14] = t; \ t ^= ctx->key_enc[8 * i + 7]; \ ctx->key_enc[8 * i + 15] = t; \ } while (0) /** * AES_ExpandKey - Expands the AES key as described in FIPS-197 * @ctx: The location where the computed key will be stored. * @in_key: The supplied key. * @key_len: The length of the supplied key. * * Returns 0 on success. The function fails only if an invalid key size (or * pointer) is supplied. * The expanded key size is 240 bytes (max of 14 rounds with a unique 16 bytes * key schedule plus a 16 bytes key which is used before the first round). * The decryption key is prepared for the "Equivalent Inverse Cipher" as * described in FIPS-197. The first slot (16 bytes) of each key (enc or dec) is * for the initial combination, the second slot for the first round and so on. */ static int AES_ExpandKey(aes_context_t *ctx, const uint8_t *in_key, unsigned int key_len) { const uint32_t *key = (const uint32_t *)in_key; uint32_t i, t, u, v, w, j; if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 && key_len != AES_KEYSIZE_256) return -1; ctx->key_length = key_len; ctx->key_dec[key_len + 24] = ctx->key_enc[0] = le32_to_cpu(key[0]); ctx->key_dec[key_len + 25] = ctx->key_enc[1] = le32_to_cpu(key[1]); ctx->key_dec[key_len + 26] = ctx->key_enc[2] = le32_to_cpu(key[2]); ctx->key_dec[key_len + 27] = ctx->key_enc[3] = le32_to_cpu(key[3]); switch (key_len) { case AES_KEYSIZE_128: t = ctx->key_enc[3]; for (i = 0; i < 10; ++i) loop4(i); break; case AES_KEYSIZE_192: ctx->key_enc[4] = le32_to_cpu(key[4]); t = ctx->key_enc[5] = le32_to_cpu(key[5]); for (i = 0; i < 8; ++i) loop6(i); break; case AES_KEYSIZE_256: ctx->key_enc[4] = le32_to_cpu(key[4]); ctx->key_enc[5] = le32_to_cpu(key[5]); ctx->key_enc[6] = le32_to_cpu(key[6]); t = ctx->key_enc[7] = le32_to_cpu(key[7]); for (i = 0; i < 6; ++i) loop8(i); loop8tophalf(i); break; } ctx->key_dec[0] = ctx->key_enc[key_len + 24]; ctx->key_dec[1] = ctx->key_enc[key_len + 25]; ctx->key_dec[2] = ctx->key_enc[key_len + 26]; ctx->key_dec[3] = ctx->key_enc[key_len + 27]; for (i = 4; i < key_len + 24; ++i) { j = key_len + 24 - (i & ~3) + (i & 3); imix_col(ctx->key_dec[j], ctx->key_enc[i]); } return 0; } /** * AES_SetKey - Set the AES key. * @ctx: AES context struct. * @in_key: The input key. * @key_len: The size of the key. * * Returns 0 on success, on failure -1 is returned. * The function uses AES_ExpandKey() to expand the key. */ static int AES_SetKey(aes_context_t *ctx, const uint8_t *in_key, unsigned int key_len) { int ret; ret = AES_ExpandKey(ctx, in_key, key_len); if (!ret) return 0; return -1; } /* encrypt a block of text */ #define f_rn(bo, bi, n, k) do { \ bo[n] = crypto_ft_tab[0][get_byte(bi[n], 0)] ^ \ crypto_ft_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \ crypto_ft_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \ crypto_ft_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \ } while (0) #define f_nround(bo, bi, k) do {\ f_rn(bo, bi, 0, k); \ f_rn(bo, bi, 1, k); \ f_rn(bo, bi, 2, k); \ f_rn(bo, bi, 3, k); \ k += 4; \ } while (0) #define f_rl(bo, bi, n, k) do { \ bo[n] = crypto_fl_tab[0][get_byte(bi[n], 0)] ^ \ crypto_fl_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \ crypto_fl_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \ crypto_fl_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \ } while (0) #define f_lround(bo, bi, k) do {\ f_rl(bo, bi, 0, k); \ f_rl(bo, bi, 1, k); \ f_rl(bo, bi, 2, k); \ f_rl(bo, bi, 3, k); \ } while (0) static void AES_Encrypt(aes_context_t *ctx, uint8_t *out, const uint8_t *in) { const uint32_t *src = (const uint32_t *)in; uint32_t *dst = (uint32_t *)out; uint32_t b0[4], b1[4]; const uint32_t *kp = ctx->key_enc + 4; const int key_len = ctx->key_length; b0[0] = le32_to_cpu(src[0]) ^ ctx->key_enc[0]; b0[1] = le32_to_cpu(src[1]) ^ ctx->key_enc[1]; b0[2] = le32_to_cpu(src[2]) ^ ctx->key_enc[2]; b0[3] = le32_to_cpu(src[3]) ^ ctx->key_enc[3]; if (key_len > 24) { f_nround(b1, b0, kp); f_nround(b0, b1, kp); } if (key_len > 16) { f_nround(b1, b0, kp); f_nround(b0, b1, kp); } f_nround(b1, b0, kp); f_nround(b0, b1, kp); f_nround(b1, b0, kp); f_nround(b0, b1, kp); f_nround(b1, b0, kp); f_nround(b0, b1, kp); f_nround(b1, b0, kp); f_nround(b0, b1, kp); f_nround(b1, b0, kp); f_lround(b0, b1, kp); dst[0] = cpu_to_le32(b0[0]); dst[1] = cpu_to_le32(b0[1]); dst[2] = cpu_to_le32(b0[2]); dst[3] = cpu_to_le32(b0[3]); } static boolean prng_enabled = false; static aes_context_t prng_context; static uint32_t prng_input_counter; static uint32_t prng_values[4]; static unsigned int prng_value_index = 0; // Initialize Pseudo-RNG using the specified 128-bit key. void PRNG_Start(prng_seed_t key) { AES_SetKey(&prng_context, key, sizeof(prng_seed_t)); prng_value_index = 4; prng_input_counter = 0; prng_enabled = true; } void PRNG_Stop(void) { prng_enabled = false; } // Generate a set of new PRNG values by encrypting a new block. static void PRNG_Generate(void) { byte input[16], output[16]; unsigned int i; // Input for the cipher is a consecutively increasing 32-bit counter. for (i = 0; i < 4; ++i) { input[4*i] = prng_input_counter & 0xff; input[4*i + 1] = (prng_input_counter >> 8) & 0xff; input[4*i + 2] = (prng_input_counter >> 16) & 0xff; input[4*i + 3] = (prng_input_counter >> 24) & 0xff; ++prng_input_counter; } AES_Encrypt(&prng_context, output, input); for (i = 0; i < 4; ++i) { prng_values[i] = output[4*i] | (output[4*i + 1] << 8) | (output[4*i + 2] << 16) | (output[4*i + 3] << 24); } prng_value_index = 0; } // Read a random 32-bit integer from the PRNG. unsigned int PRNG_Random(void) { unsigned int result; if (!prng_enabled) { return 0; } if (prng_value_index >= 4) { PRNG_Generate(); } result = prng_values[prng_value_index]; ++prng_value_index; return result; } crispy-doom-crispy-doom-5.6.4/src/aes_prng.h000066400000000000000000000016131360717211000207730ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Pseudo-random number generator for secure demos. // #ifndef __AES_PRNG_H__ #define __AES_PRNG_H__ #include "doomtype.h" // Nonce value used as random seed for secure demos. typedef byte prng_seed_t[16]; void PRNG_Start(prng_seed_t seed); void PRNG_Stop(void); unsigned int PRNG_Random(void); #endif /* #ifndef __AES_PRNG_H__ */ crispy-doom-crispy-doom-5.6.4/src/crispy.c000066400000000000000000000024041360717211000205000ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2014-2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Crispy Doom specific variables. // #include "crispy.h" // [crispy] "regular" config variables static crispy_t crispy_s = { 0, .extautomap = 1, .extsaveg = 1, .hires = 1, .smoothscaling = 1, .soundfix = 1, .vsync = 1, }; crispy_t *const crispy = &crispy_s; // [crispy] "critical" config variables static const crispy_t critical_s = {0}; const crispy_t *critical = &critical_s; // [crispy] update the "singleplayer" variable and the "critical" struct void CheckCrispySingleplayer (boolean singleplayer) { if ((crispy->singleplayer = singleplayer)) { critical = &crispy_s; } else { critical = &critical_s; } } crispy-doom-crispy-doom-5.6.4/src/crispy.h000066400000000000000000000071211360717211000205060ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2014-2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Crispy Doom specific variables. // #ifndef __CRISPY_H__ #define __CRISPY_H__ #include "doomtype.h" #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifndef MAX #define MAX(a,b) (((a)>(b))?(a):(b)) #endif #ifndef BETWEEN #define BETWEEN(l,u,x) (((l)>(x))?(l):((x)>(u))?(u):(x)) #endif typedef struct { // [crispy] "crispness" config variables int automapoverlay; int automaprotate; int automapstats; int bobfactor; int brightmaps; int centerweapon; int coloredblood; int coloredhud; int crosshair; int crosshairhealth; int crosshairtarget; int crosshairtype; int demotimer; int demotimerdir; int demobar; int extautomap; int extsaveg; int flipcorpses; int freeaim; int freelook; int hires; int jump; int leveltime; int mouselook; int neghealth; int overunder; int pitch; int playercoords; int recoil; int secretmessage; int smoothlight; int smoothscaling; int soundfix; int soundfull; int soundmono; int translucency; #if CRISPY_TRUECOLOR int truecolor; #endif int uncapped; int vsync; int weaponsquat; // [crispy] in-game switches and variables int screenshotmsg; int cleanscreenshot; int demowarp; int fps; boolean flashinghom; boolean fliplevels; boolean flipweapons; boolean haved1e5; boolean havee1m10; boolean havemap33; boolean havessg; boolean singleplayer; boolean stretchsky; const char *sdlversion; const char *platform; void (*post_rendering_hook) (void); } crispy_t; extern crispy_t *const crispy; extern const crispy_t *critical; extern void CheckCrispySingleplayer (boolean singleplayer); enum { REINIT_FRAMEBUFFERS = 1, REINIT_RENDERER = 2, REINIT_TEXTURES = 4, REINIT_ASPECTRATIO = 8, }; enum { ASPECTRATIO_OFF, ASPECTRATIO_4_3, ASPECTRATIO_16_10, NUM_ASPECTRATIOS, }; enum { BOBFACTOR_FULL, BOBFACTOR_75, BOBFACTOR_OFF, NUM_BOBFACTORS, }; enum { BRIGHTMAPS_OFF, BRIGHTMAPS_TEXTURES, BRIGHTMAPS_SPRITES, BRIGHTMAPS_BOTH, NUM_BRIGHTMAPS, }; enum { CENTERWEAPON_OFF, CENTERWEAPON_CENTER, CENTERWEAPON_BOB, NUM_CENTERWEAPON, }; enum { COLOREDHUD_OFF, COLOREDHUD_BAR, COLOREDHUD_TEXT, COLOREDHUD_BOTH, NUM_COLOREDHUD }; enum { CROSSHAIR_OFF, CROSSHAIR_STATIC, CROSSHAIR_PROJECTED, NUM_CROSSHAIRS, CROSSHAIR_INTERCEPT = 0x10 }; enum { DEMOTIMER_OFF, DEMOTIMER_RECORD, DEMOTIMER_PLAYBACK, DEMOTIMER_BOTH, NUM_DEMOTIMERS }; enum { FREEAIM_AUTO, FREEAIM_DIRECT, FREEAIM_BOTH, NUM_FREEAIMS }; enum { FREELOOK_OFF, FREELOOK_SPRING, FREELOOK_LOCK, NUM_FREELOOKS }; enum { JUMP_OFF, JUMP_LOW, JUMP_HIGH, NUM_JUMPS }; enum { TRANSLUCENCY_OFF, TRANSLUCENCY_MISSILE, TRANSLUCENCY_ITEM, TRANSLUCENCY_BOTH, NUM_TRANSLUCENCY }; enum { SECRETMESSAGE_OFF, SECRETMESSAGE_ON, SECRETMESSAGE_COUNT, NUM_SECRETMESSAGE }; enum { WIDGETS_OFF, WIDGETS_AUTOMAP, WIDGETS_ALWAYS, NUM_WIDGETS }; #endif crispy-doom-crispy-doom-5.6.4/src/d_dedicated.c000066400000000000000000000022401360717211000213760ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Code specific to the standalone dedicated server. // #include #include #include #include "config.h" #include "m_argv.h" #include "net_defs.h" #include "net_dedicated.h" #include "net_server.h" #include "z_zone.h" void NET_CL_Run(void) { // No client present :-) // // This is here because the server code sometimes runs this // to let the client do some processing if it needs to. // In a standalone dedicated server, we don't have a client. } void D_DoomMain(void) { printf(PACKAGE_NAME " standalone dedicated server\n"); Z_Init(); NET_DedicatedServer(); } crispy-doom-crispy-doom-5.6.4/src/d_event.c000066400000000000000000000026231360717211000206160ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // DESCRIPTION: Event handling. // // Events are asynchronous inputs generally generated by the game user. // Events can be discarded if no responder claims them // #include #include "d_event.h" #define MAXEVENTS 64 static event_t events[MAXEVENTS]; static int eventhead; static int eventtail; // // D_PostEvent // Called by the I/O functions when input is detected // void D_PostEvent (event_t* ev) { events[eventhead] = *ev; eventhead = (eventhead + 1) % MAXEVENTS; } // Read an event from the queue. event_t *D_PopEvent(void) { event_t *result; // No more events waiting. if (eventtail == eventhead) { return NULL; } result = &events[eventtail]; // Advance to the next event in the queue. eventtail = (eventtail + 1) % MAXEVENTS; return result; } crispy-doom-crispy-doom-5.6.4/src/d_event.h000066400000000000000000000073101360717211000206210ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __D_EVENT__ #define __D_EVENT__ #include "doomtype.h" // // Event handling. // // Input event types. typedef enum { // Key press/release events. // data1: Key code (from doomkeys.h) of the key that was // pressed or released. This is the key as it appears // on a US keyboard layout, and does not change with // layout. // For ev_keydown only: // data2: ASCII representation of the key that was pressed that // changes with the keyboard layout; eg. if 'Z' is // pressed on a German keyboard, data1='y',data2='z'. // Not affected by modifier keys. // data3: ASCII input, fully modified according to keyboard // layout and any modifier keys that are held down. // Only set if I_StartTextInput() has been called. ev_keydown, ev_keyup, // Mouse movement event. // data1: Bitfield of buttons currently held down. // (bit 0 = left; bit 1 = right; bit 2 = middle). // data2: X axis mouse movement (turn). // data3: Y axis mouse movement (forward/backward). ev_mouse, // Joystick state. // data1: Bitfield of buttons currently pressed. // data2: X axis mouse movement (turn). // data3: Y axis mouse movement (forward/backward). // data4: Third axis mouse movement (strafe). // data5: Fourth axis mouse movement (look) ev_joystick, // Quit event. Triggered when the user clicks the "close" button // to terminate the application. ev_quit } evtype_t; // Event structure. typedef struct { evtype_t type; // Event-specific data; see the descriptions given above. int data1, data2, data3, data4, data5; } event_t; // // Button/action code definitions. // typedef enum { // Press "Fire". BT_ATTACK = 1, // Use button, to open doors, activate switches. BT_USE = 2, // Flag: game events, not really buttons. BT_SPECIAL = 128, BT_SPECIALMASK = 3, // Flag, weapon change pending. // If true, the next 3 bits hold weapon num. BT_CHANGE = 4, // The 3bit weapon mask and shift, convenience. BT_WEAPONMASK = (8+16+32), BT_WEAPONSHIFT = 3, // Pause the game. BTS_PAUSE = 1, // Save the game at each console. BTS_SAVEGAME = 2, // Savegame slot numbers // occupy the second byte of buttons. BTS_SAVEMASK = (4+8+16), BTS_SAVESHIFT = 2, } buttoncode_t; // villsa [STRIFE] Strife specific buttons // TODO - not finished typedef enum { // Player view look up BT2_LOOKUP = 1, // Player view look down BT2_LOOKDOWN = 2, // Center player's view BT2_CENTERVIEW = 4, // Use inventory item BT2_INVUSE = 8, // Drop inventory item BT2_INVDROP = 16, // Jump up and down BT2_JUMP = 32, // Use medkit BT2_HEALTH = 128, } buttoncode2_t; // Called by IO functions when input is detected. void D_PostEvent (event_t *ev); // Read an event from the event queue event_t *D_PopEvent(void); #endif crispy-doom-crispy-doom-5.6.4/src/d_iwad.c000066400000000000000000000555131360717211000204270ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Search for and locate an IWAD file, and initialize according // to the IWAD type. // #include #include #include #include #include "deh_str.h" #include "doomkeys.h" #include "d_iwad.h" #include "i_system.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" static const iwad_t iwads[] = { { "doom2.wad", doom2, commercial, "Doom II" }, { "plutonia.wad", pack_plut, commercial, "Final Doom: Plutonia Experiment" }, { "tnt.wad", pack_tnt, commercial, "Final Doom: TNT: Evilution" }, { "doom.wad", doom, retail, "Doom" }, { "doom1.wad", doom, shareware, "Doom Shareware" }, { "chex.wad", pack_chex, retail, "Chex Quest" }, { "hacx.wad", pack_hacx, commercial, "Hacx" }, { "freedoom2.wad", doom2, commercial, "Freedoom: Phase 2" }, { "freedoom1.wad", doom, retail, "Freedoom: Phase 1" }, { "freedm.wad", doom2, commercial, "FreeDM" }, { "heretic.wad", heretic, retail, "Heretic" }, { "heretic1.wad", heretic, shareware, "Heretic Shareware" }, { "hexen.wad", hexen, commercial, "Hexen" }, //{ "strife0.wad", strife, commercial, "Strife" }, // haleyjd: STRIFE-FIXME { "strife1.wad", strife, commercial, "Strife" }, }; boolean D_IsIWADName(const char *name) { int i; for (i = 0; i < arrlen(iwads); i++) { if (!strcasecmp(name, iwads[i].name)) { return true; } } return false; } // Array of locations to search for IWAD files // // "128 IWAD search directories should be enough for anybody". #define MAX_IWAD_DIRS 128 static boolean iwad_dirs_built = false; static char *iwad_dirs[MAX_IWAD_DIRS]; static int num_iwad_dirs = 0; static void AddIWADDir(char *dir) { if (num_iwad_dirs < MAX_IWAD_DIRS) { iwad_dirs[num_iwad_dirs] = dir; ++num_iwad_dirs; } } // This is Windows-specific code that automatically finds the location // of installed IWAD files. The registry is inspected to find special // keys installed by the Windows installers for various CD versions // of Doom. From these keys we can deduce where to find an IWAD. #if defined(_WIN32) && !defined(_WIN32_WCE) #define WIN32_LEAN_AND_MEAN #include typedef struct { HKEY root; char *path; char *value; } registry_value_t; #define UNINSTALLER_STRING "\\uninstl.exe /S " // Keys installed by the various CD editions. These are actually the // commands to invoke the uninstaller and look like this: // // C:\Program Files\Path\uninstl.exe /S C:\Program Files\Path // // With some munging we can find where Doom was installed. // [AlexMax] From the persepctive of a 64-bit executable, 32-bit registry // keys are located in a different spot. #if _WIN64 #define SOFTWARE_KEY "Software\\Wow6432Node" #else #define SOFTWARE_KEY "Software" #endif static registry_value_t uninstall_values[] = { // Ultimate Doom, CD version (Depths of Doom trilogy) { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Ultimate Doom for Windows 95", "UninstallString", }, // Doom II, CD version (Depths of Doom trilogy) { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Doom II for Windows 95", "UninstallString", }, // Final Doom { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Final Doom for Windows 95", "UninstallString", }, // Shareware version { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Doom Shareware for Windows 95", "UninstallString", }, }; // Values installed by the GOG.com and Collector's Edition versions static registry_value_t root_path_keys[] = { // Doom Collector's Edition { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Activision\\DOOM Collector's Edition\\v1.0", "INSTALLPATH", }, // Doom II { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\GOG.com\\Games\\1435848814", "PATH", }, // Doom 3: BFG Edition { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\GOG.com\\Games\\1135892318", "PATH", }, // Final Doom { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\GOG.com\\Games\\1435848742", "PATH", }, // Ultimate Doom { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\GOG.com\\Games\\1435827232", "PATH", }, // Strife: Veteran Edition { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\GOG.com\\Games\\1432899949", "PATH", }, }; // Subdirectories of the above install path, where IWADs are installed. static char *root_path_subdirs[] = { ".", "Doom2", "Final Doom", "Ultimate Doom", "Plutonia", "TNT", "base\\wads", }; // Location where Steam is installed static registry_value_t steam_install_location = { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Valve\\Steam", "InstallPath", }; // Subdirs of the steam install directory where IWADs are found static char *steam_install_subdirs[] = { "steamapps\\common\\doom 2\\base", "steamapps\\common\\final doom\\base", "steamapps\\common\\ultimate doom\\base", "steamapps\\common\\heretic shadow of the serpent riders\\base", "steamapps\\common\\hexen\\base", "steamapps\\common\\hexen deathkings of the dark citadel\\base", // From Doom 3: BFG Edition: "steamapps\\common\\DOOM 3 BFG Edition\\base\\wads", // From Strife: Veteran Edition: "steamapps\\common\\Strife", }; #define STEAM_BFG_GUS_PATCHES \ "steamapps\\common\\DOOM 3 BFG Edition\\base\\classicmusic\\instruments" static char *GetRegistryString(registry_value_t *reg_val) { HKEY key; DWORD len; DWORD valtype; char *result; // Open the key (directory where the value is stored) if (RegOpenKeyEx(reg_val->root, reg_val->path, 0, KEY_READ, &key) != ERROR_SUCCESS) { return NULL; } result = NULL; // Find the type and length of the string, and only accept strings. if (RegQueryValueEx(key, reg_val->value, NULL, &valtype, NULL, &len) == ERROR_SUCCESS && valtype == REG_SZ) { // Allocate a buffer for the value and read the value result = malloc(len + 1); if (RegQueryValueEx(key, reg_val->value, NULL, &valtype, (unsigned char *) result, &len) != ERROR_SUCCESS) { free(result); result = NULL; } else { // Ensure the value is null-terminated result[len] = '\0'; } } // Close the key RegCloseKey(key); return result; } // Check for the uninstall strings from the CD versions static void CheckUninstallStrings(void) { unsigned int i; for (i=0; i 0) { return; } install_path = GetRegistryString(&steam_install_location); if (install_path == NULL) { return; } patch_path = M_StringJoin(install_path, "\\", STEAM_BFG_GUS_PATCHES, NULL); test_patch_path = M_StringJoin(patch_path, "\\ACBASS.PAT", NULL); // Does acbass.pat exist? If so, then set gus_patch_path. if (M_FileExists(test_patch_path)) { M_SetVariable("gus_patch_path", patch_path); } free(test_patch_path); free(patch_path); free(install_path); } // Default install directories for DOS Doom static void CheckDOSDefaults(void) { // These are the default install directories used by the deice // installer program: AddIWADDir("\\doom2"); // Doom II AddIWADDir("\\plutonia"); // Final Doom AddIWADDir("\\tnt"); AddIWADDir("\\doom_se"); // Ultimate Doom AddIWADDir("\\doom"); // Shareware / Registered Doom AddIWADDir("\\dooms"); // Shareware versions AddIWADDir("\\doomsw"); AddIWADDir("\\heretic"); // Heretic AddIWADDir("\\hrtic_se"); // Heretic Shareware from Quake disc AddIWADDir("\\hexen"); // Hexen AddIWADDir("\\hexendk"); // Hexen Deathkings of the Dark Citadel AddIWADDir("\\strife"); // Strife } #endif // Returns true if the specified path is a path to a file // of the specified name. static boolean DirIsFile(const char *path, const char *filename) { return strchr(path, DIR_SEPARATOR) != NULL && !strcasecmp(M_BaseName(path), filename); } // Check if the specified directory contains the specified IWAD // file, returning the full path to the IWAD if found, or NULL // if not found. static char *CheckDirectoryHasIWAD(const char *dir, const char *iwadname) { char *filename; char *probe; // As a special case, the "directory" may refer directly to an // IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH. probe = M_FileCaseExists(dir); if (DirIsFile(dir, iwadname) && probe != NULL) { return probe; } // Construct the full path to the IWAD if it is located in // this directory, and check if it exists. if (!strcmp(dir, ".")) { filename = M_StringDuplicate(iwadname); } else { filename = M_StringJoin(dir, DIR_SEPARATOR_S, iwadname, NULL); } free(probe); probe = M_FileCaseExists(filename); free(filename); if (probe != NULL) { return probe; } return NULL; } // Search a directory to try to find an IWAD // Returns the location of the IWAD if found, otherwise NULL. static char *SearchDirectoryForIWAD(const char *dir, int mask, GameMission_t *mission) { char *filename; size_t i; for (i=0; i static void AddXdgDirs(void) { char *env, *tmp_env; // Quote: // > $XDG_DATA_HOME defines the base directory relative to which // > user specific data files should be stored. If $XDG_DATA_HOME // > is either not set or empty, a default equal to // > $HOME/.local/share should be used. env = getenv("XDG_DATA_HOME"); tmp_env = NULL; if (env == NULL) { char *homedir = getenv("HOME"); if (homedir == NULL) { homedir = "/"; } tmp_env = M_StringJoin(homedir, "/.local/share", NULL); env = tmp_env; } // We support $XDG_DATA_HOME/games/doom (which will usually be // ~/.local/share/games/doom) as a user-writeable extension to // the usual /usr/share/games/doom location. AddIWADDir(M_StringJoin(env, "/games/doom", NULL)); free(tmp_env); // Quote: // > $XDG_DATA_DIRS defines the preference-ordered set of base // > directories to search for data files in addition to the // > $XDG_DATA_HOME base directory. The directories in $XDG_DATA_DIRS // > should be seperated with a colon ':'. // > // > If $XDG_DATA_DIRS is either not set or empty, a value equal to // > /usr/local/share/:/usr/share/ should be used. env = getenv("XDG_DATA_DIRS"); if (env == NULL) { // (Trailing / omitted from paths, as it is added below) env = "/usr/local/share:/usr/share"; } // The "standard" location for IWADs on Unix that is supported by most // source ports is /usr/share/games/doom - we support this through the // XDG_DATA_DIRS mechanism, through which it can be overridden. AddIWADPath(env, "/games/doom"); // The convention set by RBDOOM-3-BFG is to install Doom 3: BFG // Edition into this directory, under which includes the Doom // Classic WADs. AddIWADPath(env, "/games/doom3bfg/base/wads"); } #ifndef __MACOSX__ // Steam on Linux allows installing some select Windows games, // including the classic Doom series (running DOSBox via Wine). We // could parse *.vdf files to more accurately detect installation // locations, but the defaults are likely to be good enough for just // about everyone. static void AddSteamDirs(void) { char *homedir, *steampath; homedir = getenv("HOME"); if (homedir == NULL) { homedir = "/"; } steampath = M_StringJoin(homedir, "/.steam/root/steamapps/common", NULL); AddIWADPath(steampath, "/Doom 2/base"); AddIWADPath(steampath, "/Master Levels of Doom/doom2"); AddIWADPath(steampath, "/Ultimate Doom/base"); AddIWADPath(steampath, "/Final Doom/base"); AddIWADPath(steampath, "/DOOM 3 BFG Edition/base/wads"); AddIWADPath(steampath, "/Heretic Shadow of the Serpent Riders/base"); AddIWADPath(steampath, "/Hexen/base"); AddIWADPath(steampath, "/Hexen Deathkings of the Dark Citadel/base"); AddIWADPath(steampath, "/Strife"); free(steampath); } #endif // __MACOSX__ #endif // !_WIN32 // // Build a list of IWAD files // static void BuildIWADDirList(void) { char *env; if (iwad_dirs_built) { return; } // Look in the current directory. Doom always does this. AddIWADDir("."); // Next check the directory where the executable is located. This might // be different from the current directory. AddIWADDir(M_DirName(myargv[0])); // Add DOOMWADDIR if it is in the environment env = getenv("DOOMWADDIR"); if (env != NULL) { AddIWADDir(env); } // Add dirs from DOOMWADPATH: env = getenv("DOOMWADPATH"); if (env != NULL) { AddIWADPath(env, ""); } #ifdef _WIN32 // Search the registry and find where IWADs have been installed. CheckUninstallStrings(); CheckInstallRootPaths(); CheckSteamEdition(); CheckDOSDefaults(); // Check for GUS patches installed with the BFG edition! CheckSteamGUSPatches(); #else AddXdgDirs(); #ifndef __MACOSX__ AddSteamDirs(); #endif #endif // Don't run this function again. iwad_dirs_built = true; } // // Searches WAD search paths for an WAD with a specific filename. // char *D_FindWADByName(const char *name) { char *path; char *probe; int i; // Absolute path? probe = M_FileCaseExists(name); if (probe != NULL) { return probe; } BuildIWADDirList(); // Search through all IWAD paths for a file with the given name. for (i=0; i // iwadparm = M_CheckParmWithArgs("-iwad", 1); if (iwadparm) { // Search through IWAD dirs for an IWAD with the given name. iwadfile = myargv[iwadparm + 1]; result = D_FindWADByName(iwadfile); if (result == NULL) { I_Error("IWAD file '%s' not found!", iwadfile); } *mission = IdentifyIWADByName(result, mask); } else { // Search through the list and look for an IWAD result = NULL; BuildIWADDirList(); for (i=0; result == NULL && i #include #include "d_event.h" #include "d_loop.h" #include "d_ticcmd.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "m_argv.h" #include "m_fixed.h" #include "net_client.h" #include "net_gui.h" #include "net_io.h" #include "net_query.h" #include "net_server.h" #include "net_sdl.h" #include "net_loop.h" #include "crispy.h" // The complete set of data for a particular tic. typedef struct { ticcmd_t cmds[NET_MAXPLAYERS]; boolean ingame[NET_MAXPLAYERS]; } ticcmd_set_t; // Maximum time that we wait in TryRunTics() for netgame data to be // received before we bail out and render a frame anyway. // Vanilla Doom used 20 for this value, but we use a smaller value // instead for better responsiveness of the menu when we're stuck. #define MAX_NETGAME_STALL_TICS 5 // // gametic is the tic about to (or currently being) run // maketic is the tic that hasn't had control made for it yet // recvtic is the latest tic received from the server. // // a gametic cannot be run until ticcmds are received for it // from all players. // static ticcmd_set_t ticdata[BACKUPTICS]; // The index of the next tic to be made (with a call to BuildTiccmd). static int maketic; // The number of complete tics received from the server so far. static int recvtic; // The number of tics that have been run (using RunTic) so far. int gametic; int oldleveltime; // [crispy] check if leveltime keeps tickin' // When set to true, a single tic is run each time TryRunTics() is called. // This is used for -timedemo mode. boolean singletics = false; // Index of the local player. static int localplayer; // Used for original sync code. static int skiptics = 0; // Reduce the bandwidth needed by sampling game input less and transmitting // less. If ticdup is 2, sample half normal, 3 = one third normal, etc. int ticdup; // Amount to offset the timer for game sync. fixed_t offsetms; // Use new client syncronisation code static boolean new_sync = true; // Callback functions for loop code. static loop_interface_t *loop_interface = NULL; // Current players in the multiplayer game. // This is distinct from playeringame[] used by the game code, which may // modify playeringame[] when playing back multiplayer demos. static boolean local_playeringame[NET_MAXPLAYERS]; // Requested player class "sent" to the server on connect. // If we are only doing a single player game then this needs to be remembered // and saved in the game settings. static int player_class; // 35 fps clock adjusted by offsetms milliseconds static int GetAdjustedTime(void) { int time_ms; time_ms = I_GetTimeMS(); if (new_sync) { // Use the adjustments from net_client.c only if we are // using the new sync mode. time_ms += (offsetms / FRACUNIT); } return (time_ms * TICRATE) / 1000; } static boolean BuildNewTic(void) { int gameticdiv; ticcmd_t cmd; gameticdiv = gametic/ticdup; I_StartTic (); loop_interface->ProcessEvents(); // Always run the menu loop_interface->RunMenu(); if (drone) { // In drone mode, do not generate any ticcmds. return false; } if (new_sync) { // If playing single player, do not allow tics to buffer // up very far if (!net_client_connected && maketic - gameticdiv > 2) return false; // Never go more than ~200ms ahead if (maketic - gameticdiv > 8) return false; } else { if (maketic - gameticdiv >= 5) return false; } //printf ("mk:%i ",maketic); memset(&cmd, 0, sizeof(ticcmd_t)); loop_interface->BuildTiccmd(&cmd, maketic); if (net_client_connected) { NET_CL_SendTiccmd(&cmd, maketic); } ticdata[maketic % BACKUPTICS].cmds[localplayer] = cmd; ticdata[maketic % BACKUPTICS].ingame[localplayer] = true; ++maketic; return true; } // // NetUpdate // Builds ticcmds for console player, // sends out a packet // int lasttime; void NetUpdate (void) { int nowtime; int newtics; int i; // If we are running with singletics (timing a demo), this // is all done separately. if (singletics) return; // Run network subsystems NET_CL_Run(); NET_SV_Run(); // check time nowtime = GetAdjustedTime() / ticdup; newtics = nowtime - lasttime; lasttime = nowtime; if (skiptics <= newtics) { newtics -= skiptics; skiptics = 0; } else { skiptics -= newtics; newtics = 0; } // build new ticcmds for console player for (i=0 ; iconsoleplayer = 0; settings->num_players = 1; settings->player_classes[0] = player_class; //! // @category net // // Use original network client sync code rather than the improved // sync code. // settings->new_sync = !M_ParmExists("-oldsync"); //! // @category net // @arg // // Send n extra tics in every packet as insurance against dropped // packets. // i = M_CheckParmWithArgs("-extratics", 1); if (i > 0) settings->extratics = atoi(myargv[i+1]); else settings->extratics = 1; //! // @category net // @arg // // Reduce the resolution of the game by a factor of n, reducing // the amount of network bandwidth needed. // i = M_CheckParmWithArgs("-dup", 1); if (i > 0) settings->ticdup = atoi(myargv[i+1]); else settings->ticdup = 1; if (net_client_connected) { // Send our game settings and block until game start is received // from the server. NET_CL_StartGame(settings); BlockUntilStart(settings, callback); // Read the game settings that were received. NET_CL_GetSettings(settings); } if (drone) { settings->consoleplayer = 0; } // Set the local player and playeringame[] values. localplayer = settings->consoleplayer; for (i = 0; i < NET_MAXPLAYERS; ++i) { local_playeringame[i] = i < settings->num_players; } // Copy settings to global variables. ticdup = settings->ticdup; new_sync = settings->new_sync; // TODO: Message disabled until we fix new_sync. //if (!new_sync) //{ // printf("Syncing netgames like Vanilla Doom.\n"); //} } boolean D_InitNetGame(net_connect_data_t *connect_data) { boolean result = false; net_addr_t *addr = NULL; int i; // Call D_QuitNetGame on exit: I_AtExit(D_QuitNetGame, true); player_class = connect_data->player_class; //! // @category net // // Start a multiplayer server, listening for connections. // if (M_CheckParm("-server") > 0 || M_CheckParm("-privateserver") > 0) { NET_SV_Init(); NET_SV_AddModule(&net_loop_server_module); NET_SV_AddModule(&net_sdl_module); NET_SV_RegisterWithMaster(); net_loop_client_module.InitClient(); addr = net_loop_client_module.ResolveAddress(NULL); NET_ReferenceAddress(addr); } else { //! // @category net // // Automatically search the local LAN for a multiplayer // server and join it. // i = M_CheckParm("-autojoin"); if (i > 0) { addr = NET_FindLANServer(); if (addr == NULL) { I_Error("No server found on local LAN"); } } //! // @arg
// @category net // // Connect to a multiplayer server running on the given // address. // i = M_CheckParmWithArgs("-connect", 1); if (i > 0) { net_sdl_module.InitClient(); addr = net_sdl_module.ResolveAddress(myargv[i+1]); NET_ReferenceAddress(addr); if (addr == NULL) { I_Error("Unable to resolve '%s'\n", myargv[i+1]); } } } if (addr != NULL) { if (M_CheckParm("-drone") > 0) { connect_data->drone = true; } if (!NET_CL_Connect(addr, connect_data)) { I_Error("D_InitNetGame: Failed to connect to %s:\n%s\n", NET_AddrToString(addr), net_client_reject_reason); } printf("D_InitNetGame: Connected to %s\n", NET_AddrToString(addr)); NET_ReleaseAddress(addr); // Wait for launch message received from server. NET_WaitForLaunch(); result = true; } return result; } // // D_QuitNetGame // Called before quitting to leave a net game // without hanging the other players // void D_QuitNetGame (void) { NET_SV_Shutdown(); NET_CL_Disconnect(); } static int GetLowTic(void) { int lowtic; lowtic = maketic; if (net_client_connected) { if (drone || recvtic < lowtic) { lowtic = recvtic; } } return lowtic; } static int frameon; static int frameskip[4]; static int oldnettics; static void OldNetSync(void) { unsigned int i; int keyplayer = -1; frameon++; // ideally maketic should be 1 - 3 tics above lowtic // if we are consistantly slower, speed up time for (i=0 ; i recvtic; oldnettics = maketic; if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) { skiptics = 1; // printf ("+"); } } } // Returns true if there are players in the game: static boolean PlayersInGame(void) { boolean result = false; unsigned int i; // If we are connected to a server, check if there are any players // in the game. if (net_client_connected) { for (i = 0; i < NET_MAXPLAYERS; ++i) { result = result || local_playeringame[i]; } } // Whether single or multi-player, unless we are running as a drone, // we are in the game. if (!drone) { result = true; } return result; } // When using ticdup, certain values must be cleared out when running // the duplicate ticcmds. static void TicdupSquash(ticcmd_set_t *set) { ticcmd_t *cmd; unsigned int i; for (i = 0; i < NET_MAXPLAYERS ; ++i) { cmd = &set->cmds[i]; cmd->chatchar = 0; if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; } } // When running in single player mode, clear all the ingame[] array // except the local player. static void SinglePlayerClear(ticcmd_set_t *set) { unsigned int i; for (i = 0; i < NET_MAXPLAYERS; ++i) { if (i != localplayer) { set->ingame[i] = false; } } } // // TryRunTics // void TryRunTics (void) { int i; int lowtic; int entertic; static int oldentertics; int realtics; int availabletics; int counts; // [AM] If we've uncapped the framerate and there are no tics // to run, return early instead of waiting around. extern int leveltime; #define return_early (crispy->uncapped && counts == 0 && leveltime > oldleveltime && screenvisible) // get real tics entertic = I_GetTime() / ticdup; realtics = entertic - oldentertics; oldentertics = entertic; // in singletics mode, run a single tic every time this function // is called. if (singletics) { BuildNewTic(); } else { NetUpdate (); } lowtic = GetLowTic(); availabletics = lowtic - gametic/ticdup; // decide how many tics to run if (new_sync) { counts = availabletics; // [AM] If we've uncapped the framerate and there are no tics // to run, return early instead of waiting around. if (return_early) return; } else { // decide how many tics to run if (realtics < availabletics-1) counts = realtics+1; else if (realtics < availabletics) counts = realtics; else counts = availabletics; // [AM] If we've uncapped the framerate and there are no tics // to run, return early instead of waiting around. if (return_early) return; if (counts < 1) counts = 1; if (net_client_connected) { OldNetSync(); } } if (counts < 1) counts = 1; // wait for new tics if needed while (!PlayersInGame() || lowtic < gametic/ticdup + counts) { NetUpdate (); lowtic = GetLowTic(); if (lowtic < gametic/ticdup) I_Error ("TryRunTics: lowtic < gametic"); // Still no tics to run? Sleep until some are available. if (lowtic < gametic/ticdup + counts) { // If we're in a netgame, we might spin forever waiting for // new network data to be received. So don't stay in here // forever - give the menu a chance to work. if (I_GetTime() / ticdup - entertic >= MAX_NETGAME_STALL_TICS) { return; } I_Sleep(1); } } // run the count * ticdup dics while (counts--) { ticcmd_set_t *set; if (!PlayersInGame()) { return; } set = &ticdata[(gametic / ticdup) % BACKUPTICS]; if (!net_client_connected) { SinglePlayerClear(set); } for (i=0 ; i lowtic) I_Error ("gametic>lowtic"); memcpy(local_playeringame, set->ingame, sizeof(local_playeringame)); loop_interface->RunTic(set->cmds, set->ingame); gametic++; // modify command for duplicated tics TicdupSquash(set); } NetUpdate (); // check for new console commands } } void D_RegisterLoopCallbacks(loop_interface_t *i) { loop_interface = i; } // TODO: Move nonvanilla demo functions into a dedicated file. #include "m_misc.h" #include "w_wad.h" static boolean StrictDemos(void) { //! // @category demo // // When recording or playing back demos, disable any extensions // of the vanilla demo format - record demos as vanilla would do, // and play back demos as vanilla would do. // return M_ParmExists("-strictdemos"); } // If the provided conditional value is true, we're trying to record // a demo file that will include a non-vanilla extension. The function // will return true if the conditional is true and it's allowed to use // this extension (no extensions are allowed if -strictdemos is given // on the command line). A warning is shown on the console using the // provided string describing the non-vanilla expansion. boolean D_NonVanillaRecord(boolean conditional, const char *feature) { if (!conditional || StrictDemos()) { return false; } printf("Warning: Recording a demo file with a non-vanilla extension " "(%s). Use -strictdemos to disable this extension.\n", feature); return true; } // Returns true if the given lump number corresponds to data from a .lmp // file, as opposed to a WAD. static boolean IsDemoFile(int lumpnum) { char *lower; boolean result; lower = M_StringDuplicate(lumpinfo[lumpnum]->wad_file->path); M_ForceLowercase(lower); result = M_StringEndsWith(lower, ".lmp"); free(lower); return result; } // If the provided conditional value is true, we're trying to play back // a demo that includes a non-vanilla extension. We return true if the // conditional is true and it's allowed to use this extension, checking // that: // - The -strictdemos command line argument is not provided. // - The given lumpnum identifying the demo to play back identifies a // demo that comes from a .lmp file, not a .wad file. // - Before proceeding, a warning is shown to the user on the console. boolean D_NonVanillaPlayback(boolean conditional, int lumpnum, const char *feature) { if (!conditional || StrictDemos()) { return false; } if (!IsDemoFile(lumpnum)) { printf("Warning: WAD contains demo with a non-vanilla extension " "(%s)\n", feature); return false; } printf("Warning: Playing back a demo file with a non-vanilla extension " "(%s). Use -strictdemos to disable this extension.\n", feature); return true; } crispy-doom-crispy-doom-5.6.4/src/d_loop.h000066400000000000000000000053221360717211000204520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Main loop stuff. // #ifndef __D_LOOP__ #define __D_LOOP__ #include "net_defs.h" // Callback function invoked while waiting for the netgame to start. // The callback is invoked when new players are ready. The callback // should return true, or return false to abort startup. typedef boolean (*netgame_startup_callback_t)(int ready_players, int num_players); typedef struct { // Read events from the event queue, and process them. void (*ProcessEvents)(); // Given the current input state, fill in the fields of the specified // ticcmd_t structure with data for a new tic. void (*BuildTiccmd)(ticcmd_t *cmd, int maketic); // Advance the game forward one tic, using the specified player input. void (*RunTic)(ticcmd_t *cmds, boolean *ingame); // Run the menu (runs independently of the game). void (*RunMenu)(); } loop_interface_t; // Register callback functions for the main loop code to use. void D_RegisterLoopCallbacks(loop_interface_t *i); // Create any new ticcmds and broadcast to other players. void NetUpdate (void); // Broadcasts special packets to other players // to notify of game exit void D_QuitNetGame (void); //? how many ticks to run? void TryRunTics (void); // Called at start of game loop to initialize timers void D_StartGameLoop(void); // Initialize networking code and connect to server. boolean D_InitNetGame(net_connect_data_t *connect_data); // Start game with specified settings. The structure will be updated // with the actual settings for the game. void D_StartNetGame(net_gamesettings_t *settings, netgame_startup_callback_t callback); extern boolean singletics; extern int gametic, ticdup; extern int oldleveltime; // [crispy] check if leveltime keeps tickin' // Check if it is permitted to record a demo with a non-vanilla feature. boolean D_NonVanillaRecord(boolean conditional, const char *feature); // Check if it is permitted to play back a demo with a non-vanilla feature. boolean D_NonVanillaPlayback(boolean conditional, int lumpnum, const char *feature); #endif crispy-doom-crispy-doom-5.6.4/src/d_mode.c000066400000000000000000000126731360717211000204270ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // DESCRIPTION: // Functions and definitions relating to the game type and operational // mode. // #include "doomtype.h" #include "d_mode.h" // Valid game mode/mission combinations, with the number of // episodes/maps for each. static struct { GameMission_t mission; GameMode_t mode; int episode; int map; } valid_modes[] = { { pack_chex, retail, 1, 5 }, { doom, shareware, 1, 9 }, { doom, registered, 3, 9 }, { doom, retail, 4, 9 }, { doom2, commercial, 1, 32 }, { pack_tnt, commercial, 1, 32 }, { pack_plut, commercial, 1, 32 }, { pack_hacx, commercial, 1, 32 }, { pack_nerve, commercial, 1, 9 }, { pack_master, commercial, 1, 21 }, { heretic, shareware, 1, 9 }, { heretic, registered, 3, 9 }, { heretic, retail, 5, 9 }, { hexen, commercial, 1, 60 }, { strife, commercial, 1, 34 }, }; // Check that a gamemode+gamemission received over the network is valid. boolean D_ValidGameMode(GameMission_t mission, GameMode_t mode) { int i; for (i=0; i= 1 && map <= 3; } else if (mode == registered && episode == 4) { return map == 1; } } // Find the table entry for this mission/mode combination. for (i=0; i= 1 && episode <= valid_modes[i].episode && map >= 1 && map <= valid_modes[i].map; } } // Unknown mode/mission combination return false; } // Get the number of valid episodes for the specified mission/mode. int D_GetNumEpisodes(GameMission_t mission, GameMode_t mode) { int episode; episode = 1; while (D_ValidEpisodeMap(mission, mode, episode, 1)) { ++episode; } return episode - 1; } // Table of valid versions static struct { GameMission_t mission; GameVersion_t version; } valid_versions[] = { { doom, exe_doom_1_2 }, { doom, exe_doom_1_666 }, { doom, exe_doom_1_7 }, { doom, exe_doom_1_8 }, { doom, exe_doom_1_9 }, { doom, exe_hacx }, { doom, exe_ultimate }, { doom, exe_final }, { doom, exe_final2 }, { doom, exe_chex }, { heretic, exe_heretic_1_3 }, { hexen, exe_hexen_1_1 }, { strife, exe_strife_1_2 }, { strife, exe_strife_1_31 }, }; boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version) { int i; // All Doom variants can use the Doom versions. if (mission == doom2 || mission == pack_plut || mission == pack_tnt || mission == pack_hacx || mission == pack_chex || mission == pack_nerve || mission == pack_master) { mission = doom; } for (i=0; i #include #include #include #include #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "deh_defs.h" #include "deh_io.h" typedef enum { DEH_INPUT_FILE, DEH_INPUT_LUMP } deh_input_type_t; struct deh_context_s { deh_input_type_t type; char *filename; // If the input comes from a memory buffer, pointer to the memory // buffer. unsigned char *input_buffer; size_t input_buffer_len; unsigned int input_buffer_pos; int lumpnum; // If the input comes from a file, the file stream for reading // data. FILE *stream; // Current line number that we have reached: int linenum; // Used by DEH_ReadLine: boolean last_was_newline; char *readbuffer; int readbuffer_size; // Error handling. boolean had_error; // [crispy] pointer to start of current line long linestart; }; static deh_context_t *DEH_NewContext(void) { deh_context_t *context; context = Z_Malloc(sizeof(*context), PU_STATIC, NULL); // Initial read buffer size of 128 bytes context->readbuffer_size = 128; context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL); context->linenum = 0; context->last_was_newline = true; context->had_error = false; context->linestart = -1; // [crispy] initialize return context; } // Open a dehacked file for reading // Returns NULL if open failed deh_context_t *DEH_OpenFile(const char *filename) { FILE *fstream; deh_context_t *context; fstream = fopen(filename, "r"); if (fstream == NULL) return NULL; context = DEH_NewContext(); context->type = DEH_INPUT_FILE; context->stream = fstream; context->filename = M_StringDuplicate(filename); return context; } // Open a WAD lump for reading. deh_context_t *DEH_OpenLump(int lumpnum) { deh_context_t *context; void *lump; lump = W_CacheLumpNum(lumpnum, PU_STATIC); context = DEH_NewContext(); context->type = DEH_INPUT_LUMP; context->lumpnum = lumpnum; context->input_buffer = lump; context->input_buffer_len = W_LumpLength(lumpnum); context->input_buffer_pos = 0; context->filename = malloc(9); M_StringCopy(context->filename, lumpinfo[lumpnum]->name, 9); return context; } // Close dehacked file void DEH_CloseFile(deh_context_t *context) { if (context->type == DEH_INPUT_FILE) { fclose(context->stream); } else if (context->type == DEH_INPUT_LUMP) { W_ReleaseLumpNum(context->lumpnum); } free(context->filename); Z_Free(context->readbuffer); Z_Free(context); } int DEH_GetCharFile(deh_context_t *context) { if (feof(context->stream)) { // end of file return -1; } return fgetc(context->stream); } int DEH_GetCharLump(deh_context_t *context) { int result; if (context->input_buffer_pos >= context->input_buffer_len) { return -1; } result = context->input_buffer[context->input_buffer_pos]; ++context->input_buffer_pos; return result; } // Reads a single character from a dehacked file int DEH_GetChar(deh_context_t *context) { int result = 0; // Read characters, but ignore carriage returns // Essentially this is a DOS->Unix conversion do { switch (context->type) { case DEH_INPUT_FILE: result = DEH_GetCharFile(context); break; case DEH_INPUT_LUMP: result = DEH_GetCharLump(context); break; } } while (result == '\r'); // Track the current line number if (context->last_was_newline) { ++context->linenum; } context->last_was_newline = result == '\n'; return result; } // Increase the read buffer size static void IncreaseReadBuffer(deh_context_t *context) { char *newbuffer; int newbuffer_size; newbuffer_size = context->readbuffer_size * 2; newbuffer = Z_Malloc(newbuffer_size, PU_STATIC, NULL); memcpy(newbuffer, context->readbuffer, context->readbuffer_size); Z_Free(context->readbuffer); context->readbuffer = newbuffer; context->readbuffer_size = newbuffer_size; } // [crispy] Save pointer to start of current line ... void DEH_SaveLineStart (deh_context_t *context) { if (context->type == DEH_INPUT_FILE) { context->linestart = ftell(context->stream); } else if (context->type == DEH_INPUT_LUMP) { context->linestart = context->input_buffer_pos; } } // [crispy] ... and reset context to start of current line // to retry with previous line parser in case of a parsing error void DEH_RestoreLineStart (deh_context_t *context) { // [crispy] never point past the start if (context->linestart < 0) return; if (context->type == DEH_INPUT_FILE) { fseek(context->stream, context->linestart, SEEK_SET); } else if (context->type == DEH_INPUT_LUMP) { context->input_buffer_pos = context->linestart; } // [crispy] don't count this line twice --context->linenum; } // Read a whole line char *DEH_ReadLine(deh_context_t *context, boolean extended) { int c; int pos; boolean escaped = false; for (pos = 0;;) { c = DEH_GetChar(context); if (c < 0 && pos == 0) { // end of file return NULL; } // cope with lines of any length: increase the buffer size if (pos >= context->readbuffer_size) { IncreaseReadBuffer(context); } // extended string support if (extended && c == '\\') { c = DEH_GetChar(context); // "\n" in the middle of a string indicates an internal linefeed if (c == 'n') { context->readbuffer[pos] = '\n'; ++pos; continue; } // values to be assigned may be split onto multiple lines by ending // each line that is to be continued with a backslash if (c == '\n') { escaped = true; continue; } } // blanks before the backslash are included in the string // but indentation after the linefeed is not if (escaped && c >= 0 && isspace(c) && c != '\n') { continue; } else { escaped = false; } if (c == '\n' || c < 0) { // end of line: a full line has been read context->readbuffer[pos] = '\0'; break; } else if (c != '\0') { // normal character; don't allow NUL characters to be // added. context->readbuffer[pos] = (char) c; ++pos; } } return context->readbuffer; } void DEH_Warning(deh_context_t *context, const char *msg, ...) { va_list args; va_start(args, msg); fprintf(stderr, "%s:%i: warning: ", context->filename, context->linenum); vfprintf(stderr, msg, args); fprintf(stderr, "\n"); va_end(args); } void DEH_Error(deh_context_t *context, const char *msg, ...) { va_list args; va_start(args, msg); fprintf(stderr, "%s:%i: ", context->filename, context->linenum); vfprintf(stderr, msg, args); fprintf(stderr, "\n"); va_end(args); context->had_error = true; } boolean DEH_HadError(deh_context_t *context) { return context->had_error; } // [crispy] return the filename of the DEHACKED file // or NULL if it is a DEHACKED lump loaded from a PWAD char *DEH_FileName(deh_context_t *context) { if (context->type == DEH_INPUT_FILE) { return context->filename; } return NULL; } crispy-doom-crispy-doom-5.6.4/src/deh_io.h000066400000000000000000000023111360717211000204200ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Dehacked I/O code (does all reads from dehacked files) // #ifndef DEH_IO_H #define DEH_IO_H #include "deh_defs.h" deh_context_t *DEH_OpenFile(const char *filename); deh_context_t *DEH_OpenLump(int lumpnum); void DEH_CloseFile(deh_context_t *context); int DEH_GetChar(deh_context_t *context); char *DEH_ReadLine(deh_context_t *context, boolean extended); void DEH_Error(deh_context_t *context, const char *msg, ...) PRINTF_ATTR(2, 3); void DEH_Warning(deh_context_t *context, const char *msg, ...) PRINTF_ATTR(2, 3); boolean DEH_HadError(deh_context_t *context); char *DEH_FileName(deh_context_t *context); // [crispy] returns filename #endif /* #ifndef DEH_IO_H */ crispy-doom-crispy-doom-5.6.4/src/deh_main.c000066400000000000000000000314661360717211000207450ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Main dehacked code // #include #include #include #include #include "doomtype.h" #include "i_glob.h" #include "i_system.h" #include "d_iwad.h" #include "m_argv.h" #include "w_wad.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" extern deh_section_t *deh_section_types[]; extern const char *deh_signatures[]; static boolean deh_initialized = false; // If true, we can parse [STRINGS] sections in BEX format. boolean deh_allow_extended_strings = true; // [crispy] always allow // If true, we can do long string replacements. boolean deh_allow_long_strings = true; // [crispy] always allow // If true, we can do cheat replacements longer than the originals. boolean deh_allow_long_cheats = true; // [crispy] always allow // If false, dehacked cheat replacements are ignored. boolean deh_apply_cheats = true; void DEH_Checksum(sha1_digest_t digest) { sha1_context_t sha1_context; unsigned int i; SHA1_Init(&sha1_context); for (i=0; deh_section_types[i] != NULL; ++i) { if (deh_section_types[i]->sha1_hash != NULL) { deh_section_types[i]->sha1_hash(&sha1_context); } } SHA1_Final(digest, &sha1_context); } // Called on startup to call the Init functions static void InitializeSections(void) { unsigned int i; for (i=0; deh_section_types[i] != NULL; ++i) { if (deh_section_types[i]->init != NULL) { deh_section_types[i]->init(); } } } static void DEH_Init(void) { //! // @category mod // // Ignore cheats in dehacked files. // if (M_CheckParm("-nocheats") > 0) { deh_apply_cheats = false; } // Call init functions for all the section definitions. InitializeSections(); deh_initialized = true; } // Given a section name, get the section structure which corresponds static deh_section_t *GetSectionByName(char *name) { unsigned int i; // we explicitely do not recognize [STRINGS] sections at all // if extended strings are not allowed if (!deh_allow_extended_strings && !strncasecmp("[STRINGS]", name, 9)) { return NULL; } for (i=0; deh_section_types[i] != NULL; ++i) { if (!strcasecmp(deh_section_types[i]->name, name)) { return deh_section_types[i]; } } return NULL; } // Is the string passed just whitespace? static boolean IsWhitespace(char *s) { for (; *s; ++s) { if (!isspace(*s)) return false; } return true; } // Strip whitespace from the start and end of a string static char *CleanString(char *s) { char *strending; // Leading whitespace while (*s && isspace(*s)) ++s; // Trailing whitespace strending = s + strlen(s) - 1; while (strlen(s) > 0 && isspace(*strending)) { *strending = '\0'; --strending; } return s; } // This pattern is used a lot of times in different sections, // an assignment is essentially just a statement of the form: // // Variable Name = Value // // The variable name can include spaces or any other characters. // The string is split on the '=', essentially. // // Returns true if read correctly boolean DEH_ParseAssignment(char *line, char **variable_name, char **value) { char *p; // find the equals p = strchr(line, '='); if (p == NULL) { return false; } // variable name at the start // turn the '=' into a \0 to terminate the string here *p = '\0'; *variable_name = CleanString(line); // value immediately follows the '=' *value = CleanString(p+1); return true; } extern void DEH_SaveLineStart (deh_context_t *context); extern void DEH_RestoreLineStart (deh_context_t *context); static boolean CheckSignatures(deh_context_t *context) { size_t i; char *line; // [crispy] save pointer to start of line (should be 0 here) DEH_SaveLineStart(context); // Read the first line line = DEH_ReadLine(context, false); if (line == NULL) { return false; } // Check all signatures to see if one matches for (i=0; deh_signatures[i] != NULL; ++i) { if (!strcmp(deh_signatures[i], line)) { return true; } } // [crispy] not a valid signature, try parsing this line again // and see if it starts with a section marker DEH_RestoreLineStart(context); return false; } // Parses a comment string in a dehacked file. static void DEH_ParseComment(char *comment) { // // Welcome, to the super-secret Chocolate Doom-specific Dehacked // overrides function. // // Putting these magic comments into your Dehacked lumps will // allow you to go beyond the normal limits of Vanilla Dehacked. // Because of this, these comments are deliberately undocumented, // and if you're using them you should be aware that your mod // is not compatible with Vanilla Doom and you're probably a // very naughty person. // // Allow comments containing this special value to allow string // replacements longer than those permitted by DOS dehacked. // This allows us to use a dehacked patch for doing string // replacements for emulating Chex Quest. // // If you use this, your dehacked patch may not work in Vanilla // Doom. if (strstr(comment, "*allow-long-strings*") != NULL) { deh_allow_long_strings = true; } // Allow magic comments to allow longer cheat replacements than // those permitted by DOS dehacked. This is also for Chex // Quest. if (strstr(comment, "*allow-long-cheats*") != NULL) { deh_allow_long_cheats = true; } // Allow magic comments to allow parsing [STRINGS] section // that are usually only found in BEX format files. This allows // for substitution of map and episode names when loading // Freedoom/FreeDM IWADs. if (strstr(comment, "*allow-extended-strings*") != NULL) { deh_allow_extended_strings = true; } } // Parses a dehacked file by reading from the context static void DEH_ParseContext(deh_context_t *context) { deh_section_t *current_section = NULL; deh_section_t *prev_section = NULL; // [crispy] remember previous line parser char section_name[20]; void *tag = NULL; boolean extended; char *line; // Read the header and check it matches the signature if (!CheckSignatures(context)) { // [crispy] make non-fatal fprintf(stderr, "This is not a valid dehacked patch file!\n"); } // Read the file while (!DEH_HadError(context)) { // Read the next line. We only allow the special extended parsing // for the BEX [STRINGS] section. extended = current_section != NULL && !strcasecmp(current_section->name, "[STRINGS]"); // [crispy] save pointer to start of line, just in case DEH_SaveLineStart(context); line = DEH_ReadLine(context, extended); // end of file? if (line == NULL) { return; } while (line[0] != '\0' && isspace(line[0])) ++line; if (line[0] == '#') { // comment DEH_ParseComment(line); continue; } if (IsWhitespace(line)) { if (current_section != NULL) { // end of section if (current_section->end != NULL) { current_section->end(context, tag); } // [crispy] if this was a BEX line parser, remember it in case // the next section does not start with a section marker if (current_section->name[0] == '[') { prev_section = current_section; } else { prev_section = NULL; } //printf("end %s tag\n", current_section->name); current_section = NULL; } } else { if (current_section != NULL) { // parse this line current_section->line_parser(context, line, tag); } else { // possibly the start of a new section sscanf(line, "%19s", section_name); current_section = GetSectionByName(section_name); if (current_section != NULL) { tag = current_section->start(context, line); //printf("started %s tag\n", section_name); } else if (prev_section != NULL) { // [crispy] try this line again with the previous line parser DEH_RestoreLineStart(context); current_section = prev_section; prev_section = NULL; } else { //printf("unknown section name %s\n", section_name); } } } } } // Parses a dehacked file int DEH_LoadFile(const char *filename) { deh_context_t *context; if (!deh_initialized) { DEH_Init(); } // Before parsing a new file, reset special override flags to false. // Magic comments should only apply to the file in which they were // defined, and shouldn't carry over to subsequent files as well. // [crispy] always allow everything /* deh_allow_long_strings = false; deh_allow_long_cheats = false; deh_allow_extended_strings = false; */ printf(" loading %s\n", filename); context = DEH_OpenFile(filename); if (context == NULL) { fprintf(stderr, "DEH_LoadFile: Unable to open %s\n", filename); return 0; } DEH_ParseContext(context); DEH_CloseFile(context); if (DEH_HadError(context)) { I_Error("Error parsing dehacked file"); } return 1; } // Load all dehacked patches from the given directory. void DEH_AutoLoadPatches(const char *path) { const char *filename; glob_t *glob; glob = I_StartMultiGlob(path, GLOB_FLAG_NOCASE|GLOB_FLAG_SORTED, "*.deh", "*.bex", "*.hhe", "*.seh", NULL); // [crispy] *.bex for (;;) { filename = I_NextGlob(glob); if (filename == NULL) { break; } printf(" [autoload]"); DEH_LoadFile(filename); } I_EndGlob(glob); } // Load dehacked file from WAD lump. // If allow_long is set, allow long strings and cheats just for this lump. int DEH_LoadLump(int lumpnum, boolean allow_long, boolean allow_error) { deh_context_t *context; if (!deh_initialized) { DEH_Init(); } // Reset all special flags to defaults. // [crispy] always allow everything /* deh_allow_long_strings = allow_long; deh_allow_long_cheats = allow_long; deh_allow_extended_strings = false; */ context = DEH_OpenLump(lumpnum); if (context == NULL) { fprintf(stderr, "DEH_LoadFile: Unable to open lump %i\n", lumpnum); return 0; } DEH_ParseContext(context); DEH_CloseFile(context); // If there was an error while parsing, abort with an error, but allow // errors to just be ignored if allow_error=true. if (!allow_error && DEH_HadError(context)) { I_Error("Error parsing dehacked lump"); } return 1; } int DEH_LoadLumpByName(const char *name, boolean allow_long, boolean allow_error) { int lumpnum; lumpnum = W_CheckNumForName(name); if (lumpnum == -1) { fprintf(stderr, "DEH_LoadLumpByName: '%s' lump not found\n", name); return 0; } return DEH_LoadLump(lumpnum, allow_long, allow_error); } // Check the command line for -deh argument, and others. void DEH_ParseCommandLine(void) { char *filename; int p; //! // @arg // @category mod // // Load the given dehacked patch(es) // p = M_CheckParm("-deh"); if (p > 0) { ++p; while (p < myargc && myargv[p][0] != '-') { filename = D_TryFindWADByName(myargv[p]); DEH_LoadFile(filename); free(filename); ++p; } } } crispy-doom-crispy-doom-5.6.4/src/deh_main.h000066400000000000000000000027631360717211000207500ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Dehacked entrypoint and common code // #ifndef DEH_MAIN_H #define DEH_MAIN_H #include "doomtype.h" #include "deh_str.h" #include "sha1.h" // These are the limits that dehacked uses (from dheinit.h in the dehacked // source). If these limits are exceeded, it does not generate an error, but // a warning is displayed. #define DEH_VANILLA_NUMSTATES 966 #define DEH_VANILLA_NUMSFX 107 void DEH_ParseCommandLine(void); int DEH_LoadFile(const char *filename); void DEH_AutoLoadPatches(const char *path); int DEH_LoadLump(int lumpnum, boolean allow_long, boolean allow_error); int DEH_LoadLumpByName(const char *name, boolean allow_long, boolean allow_error); boolean DEH_ParseAssignment(char *line, char **variable_name, char **value); void DEH_Checksum(sha1_digest_t digest); extern boolean deh_allow_extended_strings; extern boolean deh_allow_long_strings; extern boolean deh_allow_long_cheats; extern boolean deh_apply_cheats; #endif /* #ifndef DEH_MAIN_H */ crispy-doom-crispy-doom-5.6.4/src/deh_mapping.c000066400000000000000000000114161360717211000214450ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Dehacked "mapping" code // Allows the fields in structures to be mapped out and accessed by // name // #include #include #include #include "doomtype.h" #include "i_system.h" #include "m_misc.h" #include "deh_mapping.h" static deh_mapping_entry_t *GetMappingEntryByName(deh_context_t *context, deh_mapping_t *mapping, char *name) { int i; for (i=0; mapping->entries[i].name != NULL; ++i) { deh_mapping_entry_t *entry = &mapping->entries[i]; if (!strcasecmp(entry->name, name)) { if (entry->location == NULL) { DEH_Warning(context, "Field '%s' is unsupported", name); return NULL; } return entry; } } // Not found. DEH_Warning(context, "Field named '%s' not found", name); return NULL; } // // Get the location of the specified field in the specified structure. // static void *GetStructField(void *structptr, deh_mapping_t *mapping, deh_mapping_entry_t *entry) { unsigned int offset; offset = (uint8_t *)entry->location - (uint8_t *)mapping->base; return (uint8_t *)structptr + offset; } // // Set the value of a particular field in a structure by name // boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping, void *structptr, char *name, int value) { deh_mapping_entry_t *entry; void *location; entry = GetMappingEntryByName(context, mapping, name); if (entry == NULL) { return false; } // Sanity check: if (entry->is_string) { DEH_Error(context, "Tried to set '%s' as integer (BUG)", name); return false; } location = GetStructField(structptr, mapping, entry); // printf("Setting %p::%s to %i (%i bytes)\n", // structptr, name, value, entry->size); // Set field content based on its type: switch (entry->size) { case 1: * ((uint8_t *) location) = value; break; case 2: * ((uint16_t *) location) = value; break; case 4: * ((uint32_t *) location) = value; break; default: DEH_Error(context, "Unknown field type for '%s' (BUG)", name); return false; } return true; } // // Set the value of a string field in a structure by name // boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping, void *structptr, char *name, char *value) { deh_mapping_entry_t *entry; void *location; entry = GetMappingEntryByName(context, mapping, name); if (entry == NULL) { return false; } // Sanity check: if (!entry->is_string) { DEH_Error(context, "Tried to set '%s' as string (BUG)", name); return false; } location = GetStructField(structptr, mapping, entry); // Copy value into field: M_StringCopy(location, value, entry->size); return true; } void DEH_StructSHA1Sum(sha1_context_t *context, deh_mapping_t *mapping, void *structptr) { int i; // Go through each mapping for (i=0; mapping->entries[i].name != NULL; ++i) { deh_mapping_entry_t *entry = &mapping->entries[i]; void *location; if (entry->location == NULL) { // Unsupported field continue; } // Add in data for this field location = (uint8_t *)structptr + ((uint8_t *)entry->location - (uint8_t *)mapping->base); switch (entry->size) { case 1: SHA1_UpdateInt32(context, *((uint8_t *) location)); break; case 2: SHA1_UpdateInt32(context, *((uint16_t *) location)); break; case 4: SHA1_UpdateInt32(context, *((uint32_t *) location)); break; default: I_Error("Unknown dehacked mapping field type for '%s' (BUG)", entry->name); break; } } } crispy-doom-crispy-doom-5.6.4/src/deh_mapping.h000066400000000000000000000054051360717211000214530ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Dehacked "mapping" code // Allows the fields in structures to be mapped out and accessed by // name // #ifndef DEH_MAPPING_H #define DEH_MAPPING_H #include "doomtype.h" #include "deh_io.h" #include "sha1.h" #define DEH_BEGIN_MAPPING(mapping_name, structname) \ static structname deh_mapping_base; \ static deh_mapping_t mapping_name = \ { \ &deh_mapping_base, \ { #define DEH_MAPPING(deh_name, fieldname) \ {deh_name, &deh_mapping_base.fieldname, \ sizeof(deh_mapping_base.fieldname), \ false}, #define DEH_MAPPING_STRING(deh_name, fieldname) \ {deh_name, &deh_mapping_base.fieldname, \ sizeof(deh_mapping_base.fieldname), \ true}, #define DEH_UNSUPPORTED_MAPPING(deh_name) \ {deh_name, NULL, -1, false}, #define DEH_END_MAPPING \ {NULL, NULL, -1} \ } \ }; #define MAX_MAPPING_ENTRIES 32 typedef struct deh_mapping_s deh_mapping_t; typedef struct deh_mapping_entry_s deh_mapping_entry_t; struct deh_mapping_entry_s { // field name const char *name; // location relative to the base in the deh_mapping_t struct // If this is NULL, it is an unsupported mapping void *location; // field size int size; // if true, this is a string value. boolean is_string; }; struct deh_mapping_s { void *base; deh_mapping_entry_t entries[MAX_MAPPING_ENTRIES]; }; boolean DEH_SetMapping(deh_context_t *context, deh_mapping_t *mapping, void *structptr, char *name, int value); boolean DEH_SetStringMapping(deh_context_t *context, deh_mapping_t *mapping, void *structptr, char *name, char *value); void DEH_StructSHA1Sum(sha1_context_t *context, deh_mapping_t *mapping, void *structptr); #endif /* #ifndef DEH_MAPPING_H */ crispy-doom-crispy-doom-5.6.4/src/deh_str.c000066400000000000000000000226041360717211000206230ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses Text substitution sections in dehacked files // #include #include #include #include #include "doomtype.h" #include "deh_str.h" #include "m_misc.h" #include "z_zone.h" typedef struct { char *from_text; char *to_text; } deh_substitution_t; static deh_substitution_t **hash_table = NULL; static int hash_table_entries; static int hash_table_length = -1; // This is the algorithm used by glib static unsigned int strhash(const char *s) { const char *p = s; unsigned int h = *p; if (h) { for (p += 1; *p; p++) h = (h << 5) - h + *p; } return h; } static deh_substitution_t *SubstitutionForString(const char *s) { int entry; // Fallback if we have not initialized the hash table yet if (hash_table_length < 0) return NULL; entry = strhash(s) % hash_table_length; while (hash_table[entry] != NULL) { if (!strcmp(hash_table[entry]->from_text, s)) { // substitution found! return hash_table[entry]; } entry = (entry + 1) % hash_table_length; } // no substitution found return NULL; } // Look up a string to see if it has been replaced with something else // This will be used throughout the program to substitute text const char *DEH_String(const char *s) { deh_substitution_t *subst; subst = SubstitutionForString(s); if (subst != NULL) { return subst->to_text; } else { return s; } } // [crispy] returns true if a string has been substituted boolean DEH_HasStringReplacement(const char *s) { return DEH_String(s) != s; } static void InitHashTable(void) { // init hash table hash_table_entries = 0; hash_table_length = 16; hash_table = Z_Malloc(sizeof(deh_substitution_t *) * hash_table_length, PU_STATIC, NULL); memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length); } static void DEH_AddToHashtable(deh_substitution_t *sub); static void IncreaseHashtable(void) { deh_substitution_t **old_table; int old_table_length; int i; // save the old table old_table = hash_table; old_table_length = hash_table_length; // double the size hash_table_length *= 2; hash_table = Z_Malloc(sizeof(deh_substitution_t *) * hash_table_length, PU_STATIC, NULL); memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length); // go through the old table and insert all the old entries for (i=0; i 6) { IncreaseHashtable(); } // find where to insert it entry = strhash(sub->from_text) % hash_table_length; while (hash_table[entry] != NULL) { entry = (entry + 1) % hash_table_length; } hash_table[entry] = sub; ++hash_table_entries; } void DEH_AddStringReplacement(const char *from_text, const char *to_text) { deh_substitution_t *sub; size_t len; // Initialize the hash table if this is the first time if (hash_table_length < 0) { InitHashTable(); } // Check to see if there is an existing substitution already in place. sub = SubstitutionForString(from_text); if (sub != NULL) { Z_Free(sub->to_text); len = strlen(to_text) + 1; sub->to_text = Z_Malloc(len, PU_STATIC, NULL); memcpy(sub->to_text, to_text, len); } else { // We need to allocate a new substitution. sub = Z_Malloc(sizeof(*sub), PU_STATIC, 0); // We need to create our own duplicates of the provided strings. len = strlen(from_text) + 1; sub->from_text = Z_Malloc(len, PU_STATIC, NULL); memcpy(sub->from_text, from_text, len); len = strlen(to_text) + 1; sub->to_text = Z_Malloc(len, PU_STATIC, NULL); memcpy(sub->to_text, to_text, len); DEH_AddToHashtable(sub); } } typedef enum { FORMAT_ARG_INVALID, FORMAT_ARG_INT, FORMAT_ARG_FLOAT, FORMAT_ARG_CHAR, FORMAT_ARG_STRING, FORMAT_ARG_PTR, FORMAT_ARG_SAVE_POS } format_arg_t; // Get the type of a format argument. // We can mix-and-match different format arguments as long as they // are for the same data type. static format_arg_t FormatArgumentType(char c) { switch (c) { case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': return FORMAT_ARG_INT; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': return FORMAT_ARG_FLOAT; case 'c': case 'C': return FORMAT_ARG_CHAR; case 's': case 'S': return FORMAT_ARG_STRING; case 'p': return FORMAT_ARG_PTR; case 'n': return FORMAT_ARG_SAVE_POS; default: return FORMAT_ARG_INVALID; } } // Given the specified string, get the type of the first format // string encountered. static format_arg_t NextFormatArgument(const char **str) { format_arg_t argtype; // Search for the '%' starting the next string. while (**str != '\0') { if (**str == '%') { ++*str; // Don't stop for double-%s. if (**str != '%') { break; } } ++*str; } // Find the type of the format string. while (**str != '\0') { argtype = FormatArgumentType(**str); if (argtype != FORMAT_ARG_INVALID) { ++*str; return argtype; } ++*str; } // Stop searching, we have reached the end. *str = NULL; return FORMAT_ARG_INVALID; } // Check if the specified argument type is a valid replacement for // the original. static boolean ValidArgumentReplacement(format_arg_t original, format_arg_t replacement) { // In general, the original and replacement types should be // identical. However, there are some cases where the replacement // is valid and the types don't match. // Characters can be represented as ints. if (original == FORMAT_ARG_CHAR && replacement == FORMAT_ARG_INT) { return true; } // Strings are pointers. if (original == FORMAT_ARG_STRING && replacement == FORMAT_ARG_PTR) { return true; } return original == replacement; } // Return true if the specified string contains no format arguments. static boolean ValidFormatReplacement(const char *original, const char *replacement) { const char *rover1; const char *rover2; int argtype1, argtype2; // Check each argument in turn and compare types. rover1 = original; rover2 = replacement; for (;;) { argtype1 = NextFormatArgument(&rover1); argtype2 = NextFormatArgument(&rover2); if (argtype2 == FORMAT_ARG_INVALID) { // No more arguments left to read from the replacement string. break; } else if (argtype1 == FORMAT_ARG_INVALID) { // Replacement string has more arguments than the original. return false; } else if (!ValidArgumentReplacement(argtype1, argtype2)) { // Not a valid replacement argument. return false; } } return true; } // Get replacement format string, checking arguments. static const char *FormatStringReplacement(const char *s) { const char *repl; repl = DEH_String(s); if (!ValidFormatReplacement(s, repl)) { printf("WARNING: Unsafe dehacked replacement provided for " "printf format string: %s\n", s); return s; } return repl; } // printf(), performing a replacement on the format string. void DEH_printf(const char *fmt, ...) { va_list args; const char *repl; repl = FormatStringReplacement(fmt); va_start(args, fmt); vprintf(repl, args); va_end(args); } // fprintf(), performing a replacement on the format string. void DEH_fprintf(FILE *fstream, const char *fmt, ...) { va_list args; const char *repl; repl = FormatStringReplacement(fmt); va_start(args, fmt); vfprintf(fstream, repl, args); va_end(args); } // snprintf(), performing a replacement on the format string. void DEH_snprintf(char *buffer, size_t len, const char *fmt, ...) { va_list args; const char *repl; repl = FormatStringReplacement(fmt); va_start(args, fmt); M_vsnprintf(buffer, len, repl, args); va_end(args); } crispy-doom-crispy-doom-5.6.4/src/deh_str.h000066400000000000000000000024731360717211000206320ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Dehacked string replacements // #ifndef DEH_STR_H #define DEH_STR_H #include #include "doomtype.h" // Used to do dehacked text substitutions throughout the program const char *DEH_String(const char *s) PRINTF_ARG_ATTR(1); void DEH_printf(const char *fmt, ...) PRINTF_ATTR(1, 2); void DEH_fprintf(FILE *fstream, const char *fmt, ...) PRINTF_ATTR(2, 3); void DEH_snprintf(char *buffer, size_t len, const char *fmt, ...) PRINTF_ATTR(3, 4); void DEH_AddStringReplacement(const char *from_text, const char *to_text); boolean DEH_HasStringReplacement(const char *s); #if 0 // Static macro versions of the functions above #define DEH_String(x) (x) #define DEH_printf printf #define DEH_fprintf fprintf #define DEH_snprintf snprintf #endif #endif /* #ifndef DEH_STR_H */ crispy-doom-crispy-doom-5.6.4/src/deh_text.c000066400000000000000000000052261360717211000210000ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses Text substitution sections in dehacked files // #include #include #include #include "doomtype.h" #include "z_zone.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" // [crispy] support INCLUDE NOTEXT directive in BEX files boolean bex_notext = false; // Given a string length, find the maximum length of a // string that can replace it. static int TXT_MaxStringLength(int len) { // Enough bytes for the string and the NUL terminator len += 1; // All strings in doom.exe are on 4-byte boundaries, so we may be able // to support a slightly longer string. // Extend up to the next 4-byte boundary len += (4 - (len % 4)) % 4; // Less one for the NUL terminator. return len - 1; } static void *DEH_TextStart(deh_context_t *context, char *line) { char *from_text, *to_text; int fromlen, tolen; int i; if (sscanf(line, "Text %i %i", &fromlen, &tolen) != 2) { DEH_Warning(context, "Parse error on section start"); return NULL; } // Only allow string replacements that are possible in Vanilla Doom. // Chocolate Doom is unforgiving! if (!deh_allow_long_strings && tolen > TXT_MaxStringLength(fromlen)) { DEH_Error(context, "Replacement string is longer than the maximum " "possible in doom.exe"); return NULL; } from_text = malloc(fromlen + 1); to_text = malloc(tolen + 1); // read in the "from" text for (i=0; i #include "deh_main.h" #include "z_zone.h" #include "doomkeys.h" #include "doomdef.h" #include "st_stuff.h" #include "p_local.h" #include "w_wad.h" #include "m_cheat.h" #include "m_controls.h" #include "m_misc.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" // Needs access to LFB. #include "v_video.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "dstrings.h" #include "am_map.h" extern boolean inhelpscreens; // [crispy] // For use if I do walls with outsides/insides #define REDS (256-5*16) #define REDRANGE 16 #define BLUES (256-4*16+8) #define BLUERANGE 8 #define GREENS (7*16) #define GREENRANGE 16 #define GRAYS (6*16) #define GRAYSRANGE 16 #define BROWNS (4*16) #define BROWNRANGE 16 #define YELLOWS (256-32+7) #define YELLOWRANGE 1 #define BLACK 0 #define WHITE (256-47) // Automap colors #define BACKGROUND BLACK #define YOURCOLORS WHITE #define YOURRANGE 0 #define WALLCOLORS (crispy->extautomap ? 23 : REDS) // [crispy] red-brown #define WALLRANGE REDRANGE #define TSWALLCOLORS GRAYS #define TSWALLRANGE GRAYSRANGE #define FDWALLCOLORS (crispy->extautomap ? 55 : BROWNS) // [crispy] lt brown #define FDWALLRANGE BROWNRANGE #define CDWALLCOLORS (crispy->extautomap ? 215 : YELLOWS) // [crispy] orange #define CDWALLRANGE YELLOWRANGE #define THINGCOLORS GREENS #define THINGRANGE GREENRANGE #define SECRETWALLCOLORS 252 // [crispy] purple #define CRISPY_HIGHLIGHT_REVEALED_SECRETS #define REVEALEDSECRETWALLCOLORS 112 // [crispy] green #define SECRETWALLRANGE WALLRANGE #define GRIDCOLORS (GRAYS + GRAYSRANGE/2) #define GRIDRANGE 0 #define XHAIRCOLORS GRAYS // drawing stuff #define AM_NUMMARKPOINTS 10 // scale on entry #define INITSCALEMTOF (.2*FRACUNIT) // how much the automap moves window per tic in frame-buffer coordinates // moves 140 pixels in 1 second #define F_PANINC 4 // how much zoom-in per tic // goes to 2x in 1 second #define M_ZOOMIN ((int) (1.02*FRACUNIT)) // how much zoom-out per tic // pulls out to 0.5x in 1 second #define M_ZOOMOUT ((int) (FRACUNIT/1.02)) // [crispy] zoom faster with the mouse wheel #define M2_ZOOMIN ((int) (1.08*FRACUNIT)) #define M2_ZOOMOUT ((int) (FRACUNIT/1.08)) // translates between frame-buffer and map distances // [crispy] fix int overflow that causes map and grid lines to disappear #define FTOM(x) (((int64_t)((x)<> FRACBITS) #define MTOF(x) ((((int64_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))) // the following is crap #define LINE_NEVERSEE ML_DONTDRAW typedef struct { int x, y; } fpoint_t; typedef struct { fpoint_t a, b; } fline_t; typedef struct { int64_t x,y; } mpoint_t; typedef struct { mpoint_t a, b; } mline_t; typedef struct { fixed_t slp, islp; } islope_t; typedef enum { no_key, red_key, yellow_key, blue_key } keycolor_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 R ((8*PLAYERRADIUS)/7) mline_t cheat_player_arrow[] = { { { -R+R/8, 0 }, { R, 0 } }, // ----- { { R, 0 }, { R-R/2, R/6 } }, // -----> { { R, 0 }, { R-R/2, -R/6 } }, { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >-----> { { -R+R/8, 0 }, { -R-R/8, -R/6 } }, { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>-----> { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } }, { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d---> { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } }, { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } }, { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd--> { { -R/6, -R/6 }, { 0, -R/6 } }, { { 0, -R/6 }, { 0, R/4 } }, { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt-> { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } }, { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } } }; #undef R #define R (FRACUNIT) mline_t triangle_guy[] = { { { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } }, { { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)(R ) } }, { { (fixed_t)(0 ), (fixed_t)(R ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } } }; #undef R #define R (FRACUNIT) mline_t thintriangle_guy[] = { { { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)(R ), (fixed_t)(0 ) } }, { { (fixed_t)(R ), (fixed_t)(0 ) }, { (fixed_t)(-.5*R), (fixed_t)(.7*R ) } }, { { (fixed_t)(-.5*R), (fixed_t)(.7*R ) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } } }; // [crispy] print keys as crosses static mline_t cross_mark[] = { { { -R, 0 }, { R, 0 } }, { { 0, -R }, { 0, R } }, }; static mline_t square_mark[] = { { { -R, 0 }, { 0, R } }, { { 0, R }, { R, 0 } }, { { R, 0 }, { 0, -R } }, { { 0, -R }, { -R, 0 } }, }; #undef R static int cheating = 0; static int grid = 0; static int leveljuststarted = 1; // kluge until AM_LevelInit() is called boolean automapactive = false; //static int finit_width = SCREENWIDTH; //static int finit_height = SCREENHEIGHT - (ST_HEIGHT << crispy->hires); // location of window on screen static int f_x; static int f_y; // size of window on screen static int f_w; static int f_h; static int lightlev; // used for funky strobing effect #define fb I_VideoBuffer // [crispy] simplify //static pixel_t* fb; // pseudo-frame buffer static int amclock; static mpoint_t m_paninc; // how far the window pans each tic (map coords) static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) static int64_t m_x, m_y; // LL x,y where the window is on the map (map coords) static int64_t m_x2, m_y2; // UR x,y where the window is on the map (map coords) // // width/height of window on map (map coords) // static int64_t m_w; static int64_t m_h; // based on level size static fixed_t min_x; static fixed_t min_y; static fixed_t max_x; static fixed_t max_y; static fixed_t max_w; // max_x-min_x, static fixed_t max_h; // max_y-min_y // based on player size static fixed_t min_w; static fixed_t min_h; static fixed_t min_scale_mtof; // used to tell when to stop zooming out static fixed_t max_scale_mtof; // used to tell when to stop zooming in // old stuff for recovery later static int64_t old_m_w, old_m_h; static int64_t old_m_x, old_m_y; // old location used by the Follower routine static mpoint_t f_oldloc; // used by MTOF to scale from map-to-frame-buffer coords static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) static fixed_t scale_ftom; static player_t *plr; // the player represented by an arrow static patch_t *marknums[10]; // numbers used for marking by the automap static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are static int markpointnum = 0; // next point to be assigned static int followplayer = 1; // specifies whether to follow the player around cheatseq_t cheat_amap = CHEAT("iddt", 0); static boolean stopped = true; // [crispy] automap rotate mode needs these early on void AM_rotate (int64_t *x, int64_t *y, angle_t a); static void AM_rotatePoint (mpoint_t *pt); static mpoint_t mapcenter; static angle_t mapangle; // Calculates the slope and slope according to the x-axis of a line // segment in map coordinates (with the upright y-axis n' all) so // that it can be used with the brain-dead drawing stuff. void AM_getIslope ( mline_t* ml, islope_t* is ) { int dx, dy; dy = ml->a.y - ml->b.y; dx = ml->b.x - ml->a.x; if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX); else is->islp = FixedDiv(dx, dy); if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX); else is->slp = FixedDiv(dy, dx); } // // // void AM_activateNewScale(void) { m_x += m_w/2; m_y += m_h/2; m_w = FTOM(f_w); m_h = FTOM(f_h); m_x -= m_w/2; m_y -= m_h/2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; } // // // void AM_saveScaleAndLoc(void) { old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; } // // // void AM_restoreScaleAndLoc(void) { m_w = old_m_w; m_h = old_m_h; if (!followplayer) { m_x = old_m_x; m_y = old_m_y; } else { m_x = plr->mo->x - m_w/2; m_y = plr->mo->y - m_h/2; } m_x2 = m_x + m_w; m_y2 = m_y + m_h; // Change the scaling multipliers scale_mtof = FixedDiv(f_w<automapoverlay)) { markpoints[markpointnum].x = m_x + m_w/2; markpoints[markpointnum].y = m_y + m_h/2; } else { markpoints[markpointnum].x = plr->mo->x; markpoints[markpointnum].y = plr->mo->y; } markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; } // // Determines bounding box of all vertices, // sets global variables controlling zoom range. // void AM_findMinMaxBoundaries(void) { int i; fixed_t a; fixed_t b; min_x = min_y = 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; } // [crispy] cope with huge level dimensions which span the entire INT range max_w = max_x/2 - min_x/2; max_h = max_y/2 - min_y/2; min_w = 2*PLAYERRADIUS; // const? never changed? min_h = 2*PLAYERRADIUS; a = FixedDiv(f_w<automaprotate) { AM_rotate(&incx, &incy, -mapangle); } m_x += incx; m_y += incy; if (m_x + m_w/2 > max_x) m_x = max_x - m_w/2; else if (m_x + m_w/2 < min_x) m_x = min_x - m_w/2; if (m_y + m_h/2 > max_y) m_y = max_y - m_h/2; else if (m_y + m_h/2 < min_y) m_y = min_y - m_h/2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; // [crispy] reset after moving with the mouse if (f_oldloc.y == INT_MAX) { m_paninc.x = 0; m_paninc.y = 0; } } // // // void AM_initVariables(void) { int pnum; static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 }; automapactive = true; // fb = I_VideoBuffer; // [crispy] simplify f_oldloc.x = INT_MAX; amclock = 0; lightlev = 0; m_paninc.x = m_paninc.y = 0; ftom_zoommul = FRACUNIT; mtof_zoommul = FRACUNIT; m_w = FTOM(f_w); m_h = FTOM(f_h); // find player to center on initially if (playeringame[consoleplayer]) { plr = &players[consoleplayer]; } else { plr = &players[0]; for (pnum=0;pnummo->x - m_w/2; m_y = plr->mo->y - m_h/2; AM_changeWindowLoc(); // for saving & restoring old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; // inform the status bar of the change ST_Responder(&st_notify); } // // // void AM_loadPics(void) { int i; char namebuf[9]; for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "AMMNUM%d", i); marknums[i] = W_CacheLumpName(namebuf, PU_STATIC); } } void AM_unloadPics(void) { int i; char namebuf[9]; for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "AMMNUM%d", i); W_ReleaseLumpName(namebuf); } } void AM_clearMarks(void) { int i; for (i=0;ihires); AM_clearMarks(); AM_findMinMaxBoundaries(); // [crispy] initialize zoomlevel on all maps so that a 4096 units // square map would just fit in (MAP01 is 3376x3648 units) a = FixedDiv(f_w, (max_w>>FRACBITS < 2048) ? 2*(max_w>>FRACBITS) : 4096); b = FixedDiv(f_h, (max_h>>FRACBITS < 2048) ? 2*(max_h>>FRACBITS) : 4096); scale_mtof = FixedDiv(a < b ? a : b, (int) (0.7*FRACUNIT)); if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } void AM_ReInit (void) { f_w = SCREENWIDTH; f_h = SCREENHEIGHT - (ST_HEIGHT << crispy->hires); AM_findMinMaxBoundaries(); scale_mtof = crispy->hires ? scale_mtof*2 : scale_mtof/2; if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } // // // void AM_Stop (void) { static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 }; AM_unloadPics(); automapactive = false; ST_Responder(&st_notify); stopped = true; } // // // // [crispy] moved here for extended savegames static int lastlevel = -1, lastepisode = -1; void AM_Start (void) { if (!stopped) AM_Stop(); stopped = false; if (lastlevel != gamemap || lastepisode != gameepisode) { AM_LevelInit(); lastlevel = gamemap; lastepisode = gameepisode; } AM_initVariables(); AM_loadPics(); } // // set the window scale to the maximum size // void AM_minOutWindowScale(void) { scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // // set the window scale to the minimum size // void AM_maxOutWindowScale(void) { scale_mtof = max_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // // Handle events (user inputs) in automap mode // boolean AM_Responder ( event_t* ev ) { int rc; static int bigstate=0; static char buffer[20]; int key; rc = false; if (ev->type == ev_joystick && joybautomap >= 0 && (ev->data1 & (1 << joybautomap)) != 0) { joywait = I_GetTime() + 5; if (!automapactive) { AM_Start (); viewactive = false; } else { bigstate = 0; viewactive = true; AM_Stop (); } return true; } if (!automapactive) { if (ev->type == ev_keydown && ev->data1 == key_map_toggle) { AM_Start (); viewactive = false; rc = true; } } // [crispy] zoom and move Automap with the mouse (wheel) else if (ev->type == ev_mouse && !crispy->automapoverlay && !menuactive && !inhelpscreens) { if (mousebprevweapon >= 0 && ev->data1 & (1 << mousebprevweapon)) { mtof_zoommul = M2_ZOOMOUT; ftom_zoommul = M2_ZOOMIN; rc = true; } else if (mousebnextweapon >= 0 && ev->data1 & (1 << mousebnextweapon)) { mtof_zoommul = M2_ZOOMIN; ftom_zoommul = M2_ZOOMOUT; rc = true; } else if (!followplayer && (ev->data2 || ev->data3)) { // [crispy] mouse sensitivity for strafe m_paninc.x = FTOM(ev->data2*(mouseSensitivity_x2+5)/80); m_paninc.y = FTOM(ev->data3*(mouseSensitivity_x2+5)/80); f_oldloc.y = INT_MAX; rc = true; } } else if (ev->type == ev_keydown) { rc = true; key = ev->data1; if (key == key_map_east) // pan right { // [crispy] keep the map static in overlay mode // if not following the player if (!followplayer && !crispy->automapoverlay) m_paninc.x = crispy->fliplevels ? -FTOM(F_PANINC) : FTOM(F_PANINC); else rc = false; } else if (key == key_map_west) // pan left { if (!followplayer && !crispy->automapoverlay) m_paninc.x = crispy->fliplevels ? FTOM(F_PANINC) : -FTOM(F_PANINC); else rc = false; } else if (key == key_map_north) // pan up { if (!followplayer && !crispy->automapoverlay) m_paninc.y = FTOM(F_PANINC); else rc = false; } else if (key == key_map_south) // pan down { if (!followplayer && !crispy->automapoverlay) m_paninc.y = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_zoomout) // zoom out { mtof_zoommul = M_ZOOMOUT; ftom_zoommul = M_ZOOMIN; } else if (key == key_map_zoomin) // zoom in { mtof_zoommul = M_ZOOMIN; ftom_zoommul = M_ZOOMOUT; } else if (key == key_map_toggle) { bigstate = 0; viewactive = true; AM_Stop (); } else if (key == key_map_maxzoom) { bigstate = !bigstate; if (bigstate) { AM_saveScaleAndLoc(); AM_minOutWindowScale(); } else AM_restoreScaleAndLoc(); } else if (key == key_map_follow) { followplayer = !followplayer; f_oldloc.x = INT_MAX; if (followplayer) plr->message = DEH_String(AMSTR_FOLLOWON); else plr->message = DEH_String(AMSTR_FOLLOWOFF); } else if (key == key_map_grid) { grid = !grid; if (grid) plr->message = DEH_String(AMSTR_GRIDON); else plr->message = DEH_String(AMSTR_GRIDOFF); } else if (key == key_map_mark) { M_snprintf(buffer, sizeof(buffer), "%s %d", DEH_String(AMSTR_MARKEDSPOT), markpointnum); plr->message = buffer; AM_addMark(); } else if (key == key_map_clearmark) { AM_clearMarks(); plr->message = DEH_String(AMSTR_MARKSCLEARED); } else if (key == key_map_overlay) { // [crispy] force redraw status bar inhelpscreens = true; crispy->automapoverlay = !crispy->automapoverlay; if (crispy->automapoverlay) plr->message = DEH_String(AMSTR_OVERLAYON); else plr->message = DEH_String(AMSTR_OVERLAYOFF); } else if (key == key_map_rotate) { crispy->automaprotate = !crispy->automaprotate; if (crispy->automaprotate) plr->message = DEH_String(AMSTR_ROTATEON); else plr->message = DEH_String(AMSTR_ROTATEOFF); } else { rc = false; } if ((!deathmatch || gameversion <= exe_doom_1_8) && cht_CheckCheat(&cheat_amap, ev->data2)) { rc = false; cheating = (cheating + 1) % 3; } } else if (ev->type == ev_keyup) { rc = false; key = ev->data1; if (key == key_map_east) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_west) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_north) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_south) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_zoomout || key == key_map_zoomin) { mtof_zoommul = FRACUNIT; ftom_zoommul = FRACUNIT; } } return rc; } // // Zooming // void AM_changeWindowScale(void) { // Change the scaling multipliers scale_mtof = FixedMul(scale_mtof, mtof_zoommul); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); // [crispy] reset after zooming with the mouse wheel if (ftom_zoommul == M2_ZOOMIN || ftom_zoommul == M2_ZOOMOUT) { mtof_zoommul = FRACUNIT; ftom_zoommul = FRACUNIT; } if (scale_mtof < min_scale_mtof) AM_minOutWindowScale(); else if (scale_mtof > max_scale_mtof) AM_maxOutWindowScale(); else AM_activateNewScale(); } // // // void AM_doFollowPlayer(void) { if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) { m_x = FTOM(MTOF(plr->mo->x)) - m_w/2; m_y = FTOM(MTOF(plr->mo->y)) - m_h/2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; f_oldloc.x = plr->mo->x; f_oldloc.y = plr->mo->y; // m_x = FTOM(MTOF(plr->mo->x - m_w/2)); // m_y = FTOM(MTOF(plr->mo->y - m_h/2)); // m_x = plr->mo->x - m_w/2; // m_y = plr->mo->y - m_h/2; } } // // // void AM_updateLightLev(void) { static int nexttic = 0; //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; static int litelevelscnt = 0; // Change light level if (amclock>nexttic) { lightlev = litelevels[litelevelscnt++]; if (litelevelscnt == arrlen(litelevels)) litelevelscnt = 0; nexttic = amclock + 6 - (amclock % 6); } } // // Updates on Game Tick // void AM_Ticker (void) { if (!automapactive) return; amclock++; if (followplayer) AM_doFollowPlayer(); // Change the zoom if necessary if (ftom_zoommul != FRACUNIT) AM_changeWindowScale(); // Change x,y location if (m_paninc.x || m_paninc.y) AM_changeWindowLoc(); // Update light level // AM_updateLightLev(); // [crispy] required for AM_rotatePoint() if (crispy->automaprotate) { mapcenter.x = m_x + m_w / 2; mapcenter.y = m_y + m_h / 2; // [crispy] keep the map static in overlay mode // if not following the player if (!(!followplayer && crispy->automapoverlay)) mapangle = ANG90 - viewangle; } } // // Clear automap frame buffer. // void AM_clearFB(int color) { memset(fb, color, f_w*f_h*sizeof(*fb)); } // // 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. // boolean AM_clipMline ( mline_t* ml, fline_t* fl ) { enum { LEFT =1, RIGHT =2, BOTTOM =4, TOP =8 }; register int outcode1 = 0; register int outcode2 = 0; register int outside; fpoint_t tmp; int dx; int dy; #define DOOUTCODE(oc, mx, my) \ (oc) = 0; \ if ((my) < 0) (oc) |= TOP; \ else if ((my) >= f_h) (oc) |= BOTTOM; \ if ((mx) < 0) (oc) |= LEFT; \ else if ((mx) >= f_w) (oc) |= RIGHT; // do trivial rejects and outcodes if (ml->a.y > m_y2) outcode1 = TOP; else if (ml->a.y < m_y) outcode1 = BOTTOM; if (ml->b.y > m_y2) outcode2 = TOP; else if (ml->b.y < m_y) outcode2 = BOTTOM; if (outcode1 & outcode2) return false; // trivially outside if (ml->a.x < m_x) outcode1 |= LEFT; else if (ml->a.x > m_x2) outcode1 |= RIGHT; if (ml->b.x < m_x) outcode2 |= LEFT; else if (ml->b.x > m_x2) outcode2 |= RIGHT; if (outcode1 & outcode2) return false; // trivially outside // transform to frame-buffer coordinates. fl->a.x = CXMTOF(ml->a.x); fl->a.y = CYMTOF(ml->a.y); fl->b.x = CXMTOF(ml->b.x); fl->b.y = CYMTOF(ml->b.y); DOOUTCODE(outcode1, fl->a.x, fl->a.y); DOOUTCODE(outcode2, fl->b.x, fl->b.y); if (outcode1 & outcode2) return false; while (outcode1 | outcode2) { // may be partially inside box // find an outside point if (outcode1) outside = outcode1; else outside = outcode2; // clip to each side if (outside & TOP) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx*(fl->a.y))/dy; tmp.y = 0; } else if (outside & BOTTOM) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy; tmp.y = f_h-1; } else if (outside & RIGHT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx; tmp.x = f_w-1; } else if (outside & LEFT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy*(-fl->a.x))/dx; tmp.x = 0; } else { tmp.x = 0; tmp.y = 0; } if (outside == outcode1) { fl->a = tmp; DOOUTCODE(outcode1, fl->a.x, fl->a.y); } else { fl->b = tmp; DOOUTCODE(outcode2, fl->b.x, fl->b.y); } if (outcode1 & outcode2) return false; // trivially outside } return true; } #undef DOOUTCODE // // Classic Bresenham w/ whatever optimizations needed for speed // void AM_drawFline ( fline_t* fl, int color ) { register int x; register int y; register int dx; register int dy; register int sx; register int sy; register int ax; register int ay; register int d; static int fuck = 0; // For debugging only if ( fl->a.x < 0 || fl->a.x >= f_w || fl->a.y < 0 || fl->a.y >= f_h || fl->b.x < 0 || fl->b.x >= f_w || fl->b.y < 0 || fl->b.y >= f_h) { DEH_fprintf(stderr, "fuck %d \r", fuck++); return; } #ifndef CRISPY_TRUECOLOR #define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(flipscreenwidth[xx])]=(cc) #else #define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(flipscreenwidth[xx])]=(colormaps[(cc)]) #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; } } } // // Clip lines, draw visible part sof lines. // void AM_drawMline ( mline_t* ml, int color ) { static fline_t fl; if (AM_clipMline(ml, &fl)) AM_drawFline(&fl, color); // draws it on frame buffer using fb coords } // // Draws flat (floor/ceiling tile) aligned grid lines. // void AM_drawGrid(int color) { int64_t x, y; int64_t start, end; mline_t ml; // Figure out start of vertical gridlines start = m_x; if (crispy->automaprotate) { start -= m_h / 2; } // [crispy] fix losing grid lines near the automap boundary if ((start-bmaporgx)%(MAPBLOCKUNITS<automaprotate) { end += m_h / 2; } // draw vertical gridlines for (x=start; xautomaprotate) { ml.a.y -= m_w / 2; ml.b.y += m_w / 2; AM_rotatePoint(&ml.a); AM_rotatePoint(&ml.b); } AM_drawMline(&ml, color); } // Figure out start of horizontal gridlines start = m_y; if (crispy->automaprotate) { start -= m_w / 2; } // [crispy] fix losing grid lines near the automap boundary if ((start-bmaporgy)%(MAPBLOCKUNITS<automaprotate) { end += m_w / 2; } // draw horizontal gridlines for (y=start; yautomaprotate) { ml.a.x -= m_h / 2; ml.b.x += m_h / 2; AM_rotatePoint(&ml.a); AM_rotatePoint(&ml.b); } AM_drawMline(&ml, color); } } // // Determines visible lines, draws them. // This is LineDef based, not LineSeg based. // // [crispy] keyed linedefs (PR, P1, SR, S1) static keycolor_t AM_DoorColor(int type) { if (crispy->extautomap) { switch (type) { case 26: case 32: case 99: case 133: return blue_key; case 27: case 34: case 136: case 137: return yellow_key; case 28: case 33: case 134: case 135: return red_key; } } return no_key; } void AM_drawWalls(void) { int i; static mline_t l; for (i=0;ix; l.a.y = lines[i].v1->y; l.b.x = lines[i].v2->x; l.b.y = lines[i].v2->y; if (crispy->automaprotate) { AM_rotatePoint(&l.a); AM_rotatePoint(&l.b); } if (cheating || (lines[i].flags & ML_MAPPED)) { if ((lines[i].flags & LINE_NEVERSEE) && !cheating) continue; { // [crispy] draw keyed doors in their respective colors // (no Boom multiple keys) keycolor_t amd; if (!(lines[i].flags & ML_SECRET) && (amd = AM_DoorColor(lines[i].special)) > no_key) { switch (amd) { case blue_key: AM_drawMline(&l, BLUES); continue; case yellow_key: AM_drawMline(&l, YELLOWS); continue; case red_key: AM_drawMline(&l, REDS); continue; default: // [crispy] it should be impossible to reach here break; } } } // [crispy] draw exit lines in white (no Boom exit lines 197, 198) // NB: Choco does not have this at all, Boom/PrBoom+ have this disabled by default if (crispy->extautomap && ( lines[i].special == 11 || lines[i].special == 51 || lines[i].special == 52 || lines[i].special == 124)) { AM_drawMline(&l, WHITE); continue; } if (!lines[i].backsector) { // [crispy] draw 1S secret sector boundaries in purple if (crispy->extautomap && cheating && (lines[i].frontsector->special == 9)) AM_drawMline(&l, SECRETWALLCOLORS); #if defined CRISPY_HIGHLIGHT_REVEALED_SECRETS // [crispy] draw revealed secret sector boundaries in green else if (crispy->extautomap && crispy->secretmessage && (lines[i].frontsector->oldspecial == 9)) AM_drawMline(&l, REVEALEDSECRETWALLCOLORS); #endif else AM_drawMline(&l, WALLCOLORS+lightlev); } else { // [crispy] draw teleporters in green // and also WR teleporters 97 if they are not secret // (no monsters-only teleporters 125, 126; no Boom teleporters) if (lines[i].special == 39 || (crispy->extautomap && !(lines[i].flags & ML_SECRET) && lines[i].special == 97)) { // teleporters AM_drawMline(&l, crispy->extautomap ? (GREENS+GREENRANGE/2) : (WALLCOLORS+WALLRANGE/2)); } else if (lines[i].flags & ML_SECRET) // secret door { // [crispy] NB: Choco has this check, but (SECRETWALLCOLORS == WALLCOLORS) // Boom/PrBoom+ does not have this check at all if (false && cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev); else AM_drawMline(&l, WALLCOLORS+lightlev); } #if defined CRISPY_HIGHLIGHT_REVEALED_SECRETS // [crispy] draw revealed secret sector boundaries in green else if (crispy->extautomap && crispy->secretmessage && (lines[i].backsector->oldspecial == 9 || lines[i].frontsector->oldspecial == 9)) { AM_drawMline(&l, REVEALEDSECRETWALLCOLORS); } #endif // [crispy] draw 2S secret sector boundaries in purple else if (crispy->extautomap && cheating && (lines[i].backsector->special == 9 || lines[i].frontsector->special == 9)) { AM_drawMline(&l, SECRETWALLCOLORS); } else if (lines[i].backsector->floorheight != lines[i].frontsector->floorheight) { AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change } else if (lines[i].backsector->ceilingheight != lines[i].frontsector->ceilingheight) { AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change } else if (cheating) { AM_drawMline(&l, TSWALLCOLORS+lightlev); } } } else if (plr->powers[pw_allmap]) { if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3); } } } // // Rotation in 2D. // Used to rotate player arrow line character. // void AM_rotate ( int64_t* x, int64_t* y, angle_t a ) { int64_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; } // [crispy] rotate point around map center // adapted from prboom-plus/src/am_map.c:898-920 static void AM_rotatePoint (mpoint_t *pt) { int64_t tmpx; pt->x -= mapcenter.x; pt->y -= mapcenter.y; tmpx = (int64_t)FixedMul(pt->x, finecosine[mapangle>>ANGLETOFINESHIFT]) - (int64_t)FixedMul(pt->y, finesine[mapangle>>ANGLETOFINESHIFT]) + mapcenter.x; pt->y = (int64_t)FixedMul(pt->x, finesine[mapangle>>ANGLETOFINESHIFT]) + (int64_t)FixedMul(pt->y, finecosine[mapangle>>ANGLETOFINESHIFT]) + mapcenter.y; pt->x = tmpx; } void AM_drawLineCharacter ( mline_t* lineguy, int lineguylines, fixed_t scale, angle_t angle, int color, fixed_t x, fixed_t y ) { int i; mline_t l; if (crispy->automaprotate) { angle += mapangle; } for (i=0;imo->x; pt.y = plr->mo->y; if (crispy->automaprotate) { AM_rotatePoint(&pt); } if (cheating) AM_drawLineCharacter (cheat_player_arrow, arrlen(cheat_player_arrow), 0, plr->mo->angle, WHITE, pt.x, pt.y); else AM_drawLineCharacter (player_arrow, arrlen(player_arrow), 0, plr->mo->angle, WHITE, pt.x, pt.y); return; } for (i=0;ipowers[pw_invisibility]) color = 246; // *close* to black else color = their_colors[their_color]; pt.x = p->mo->x; pt.y = p->mo->y; if (crispy->automaprotate) { AM_rotatePoint(&pt); } AM_drawLineCharacter (player_arrow, arrlen(player_arrow), 0, p->mo->angle, color, pt.x, pt.y); } } void AM_drawThings ( int colors, int colorrange) { int i; mobj_t* t; keycolor_t key; mpoint_t pt; for (i=0;imo) { t = t->snext; continue; } pt.x = t->x; pt.y = t->y; if (crispy->automaprotate) { AM_rotatePoint(&pt); } if (crispy->extautomap) { // [crispy] skull keys and key cards switch (t->info->doomednum) { case 38: case 13: key = red_key; break; case 39: case 6: key = yellow_key; break; case 40: case 5: key = blue_key; break; default: key = no_key; break; } // [crispy] draw keys as crosses in their respective colors if (key > no_key) { AM_drawLineCharacter (cross_mark, arrlen(cross_mark), 16<angle, (key == red_key) ? REDS : (key == yellow_key) ? YELLOWS : (key == blue_key) ? BLUES : colors+lightlev, pt.x, pt.y); } else // [crispy] draw blood splats and puffs as small squares if (t->type == MT_BLOOD || t->type == MT_PUFF) { AM_drawLineCharacter (square_mark, arrlen(square_mark), t->radius >> 2, t->angle, (t->type == MT_BLOOD) ? REDS : GRAYS, pt.x, pt.y); } else { AM_drawLineCharacter (thintriangle_guy, arrlen(thintriangle_guy), // [crispy] triangle size represents actual thing size t->radius, t->angle, // [crispy] show countable kills in red ... ((t->flags & (MF_COUNTKILL | MF_CORPSE)) == MF_COUNTKILL) ? REDS : // [crispy] ... show Lost Souls and missiles in orange ... (t->flags & (MF_FLOAT | MF_MISSILE)) ? 216 : // [crispy] ... show other shootable items in dark gold ... (t->flags & MF_SHOOTABLE) ? 164 : // [crispy] ... corpses in gray ... (t->flags & MF_CORPSE) ? GRAYS : // [crispy] ... and countable items in yellow (t->flags & MF_COUNTITEM) ? YELLOWS : colors+lightlev, pt.x, pt.y); } } else { AM_drawLineCharacter (thintriangle_guy, arrlen(thintriangle_guy), 16<angle, colors+lightlev, t->x, t->y); } t = t->snext; } } } void AM_drawMarks(void) { int i, fx, fy, w, h; mpoint_t pt; for (i=0;iwidth); // h = SHORT(marknums[i]->height); w = 5; // because something's wrong with the wad, i guess h = 6; // because something's wrong with the wad, i guess // [crispy] center marks around player pt.x = markpoints[i].x; pt.y = markpoints[i].y; if (crispy->automaprotate) { AM_rotatePoint(&pt); } fx = (flipscreenwidth[CXMTOF(pt.x)] >> crispy->hires) - 1; fy = (CYMTOF(pt.y) >> crispy->hires) - 2; if (fx >= f_x && fx <= (f_w >> crispy->hires) - w && fy >= f_y && fy <= (f_h >> crispy->hires) - h) V_DrawPatch(fx, fy, marknums[i]); } } } void AM_drawCrosshair(int color) { // [crispy] draw an actual crosshair if (!followplayer) { static fline_t h, v; if (!h.a.x) { h.a.x = h.b.x = v.a.x = v.b.x = f_x + f_w / 2; h.a.y = h.b.y = v.a.y = v.b.y = f_y + f_h / 2; h.a.x -= 2; h.b.x += 2; v.a.y -= 2; v.b.y += 2; } AM_drawFline(&h, color); AM_drawFline(&v, color); } // [crispy] do not draw the useless dot on the player arrow /* else #ifndef CRISPY_TRUECOLOR fb[(f_w*(f_h+1))/2] = color; // single point for now #else fb[(f_w*(f_h+1))/2] = colormaps[color]; // single point for now #endif */ } void AM_Drawer (void) { if (!automapactive) return; if (!crispy->automapoverlay) AM_clearFB(BACKGROUND); if (grid) AM_drawGrid(GRIDCOLORS); AM_drawWalls(); AM_drawPlayers(); if (cheating==2) AM_drawThings(THINGCOLORS, THINGRANGE); AM_drawCrosshair(XHAIRCOLORS); AM_drawMarks(); V_MarkRect(f_x, f_y, f_w, f_h); } // [crispy] extended savegames void AM_GetMarkPoints (int *n, long *p) { int i; *n = markpointnum; *p = -1L; // [crispy] prevent saving markpoints from previous map if (lastlevel == gamemap && lastepisode == gameepisode) { for (i = 0; i < AM_NUMMARKPOINTS; i++) { *p++ = (long)markpoints[i].x; *p++ = (markpoints[i].x == -1) ? 0L : (long)markpoints[i].y; } } } void AM_SetMarkPoints (int n, long *p) { int i; AM_LevelInit(); lastlevel = gamemap; lastepisode = gameepisode; markpointnum = n; for (i = 0; i < AM_NUMMARKPOINTS; i++) { markpoints[i].x = (int64_t)*p++; markpoints[i].y = (int64_t)*p++; } } crispy-doom-crispy-doom-5.6.4/src/doom/am_map.h000066400000000000000000000023331360717211000213650ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // AutoMap module. // #ifndef __AMMAP_H__ #define __AMMAP_H__ #include "d_event.h" #include "m_cheat.h" // Used by ST StatusBar stuff. #define AM_MSGHEADER (('a'<<24)+('m'<<16)) #define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) #define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) // Called by main loop. boolean AM_Responder (event_t* ev); // Called by main loop. void AM_Ticker (void); // Called by main loop, // called instead of view drawer if automap active. void AM_Drawer (void); // Called to force the automap to quit // if the level is completed while it is up. void AM_Stop (void); extern cheatseq_t cheat_amap; #endif crispy-doom-crispy-doom-5.6.4/src/doom/d_englsh.h000066400000000000000000000607411360717211000217250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Printed strings for translation. // English language support (default). // #ifndef __D_ENGLSH__ #define __D_ENGLSH__ // // Printed strings for translation // // // D_Main.C // #define D_DEVSTR "Development mode ON.\n" #define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" // // M_Menu.C // #define PRESSKEY "press a key." #define PRESSYN "press y or n." #define QUITMSG "are you sure you want to\nquit this great game?" #define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY #define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY #define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY #define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY #define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN #define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n"PRESSYN #define NEWGAME \ "you can't start a new game\n"\ "while in a network game.\n\n"PRESSKEY #define NIGHTMARE \ "are you sure? this skill level\n"\ "isn't even remotely fair.\n\n"PRESSYN #define SWSTRING \ "this is the shareware version of doom.\n\n"\ "you need to order the entire trilogy.\n\n"PRESSKEY #define MSGOFF "Messages OFF" #define MSGON "Messages ON" #define NETEND "you can't end a netgame!\n\n"PRESSKEY #define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN #define DOSY "(press y to quit.)" // [crispy] remove " to dos.)" #define DETAILHI "High detail" #define DETAILLO "Low detail" #define GAMMALVL0 "Gamma correction OFF" #define GAMMALVL1 "Gamma correction level 1" #define GAMMALVL2 "Gamma correction level 2" #define GAMMALVL3 "Gamma correction level 3" #define GAMMALVL4 "Gamma correction level 4" // [crispy] intermediate gamma levels #define GAMMALVL05 "Gamma correction level 0.5" #define GAMMALVL15 "Gamma correction level 1.5" #define GAMMALVL25 "Gamma correction level 2.5" #define GAMMALVL35 "Gamma correction level 3.5" #define EMPTYSTRING "empty slot" // // P_inter.C // #define GOTARMOR "Picked up the armor." #define GOTMEGA "Picked up the MegaArmor!" #define GOTHTHBONUS "Picked up a health bonus." #define GOTARMBONUS "Picked up an armor bonus." #define GOTSTIM "Picked up a stimpack." #define GOTMEDINEED "Picked up a medikit that you REALLY need!" #define GOTMEDIKIT "Picked up a medikit." #define GOTSUPER "Supercharge!" #define GOTBLUECARD "Picked up a blue keycard." #define GOTYELWCARD "Picked up a yellow keycard." #define GOTREDCARD "Picked up a red keycard." #define GOTBLUESKUL "Picked up a blue skull key." #define GOTYELWSKUL "Picked up a yellow skull key." #define GOTREDSKULL "Picked up a red skull key." #define GOTINVUL "Invulnerability!" #define GOTBERSERK "Berserk!" #define GOTINVIS "Partial Invisibility" #define GOTSUIT "Radiation Shielding Suit" #define GOTMAP "Computer Area Map" #define GOTVISOR "Light Amplification Visor" #define GOTMSPHERE "MegaSphere!" #define GOTCLIP "Picked up a clip." #define GOTCLIPBOX "Picked up a box of bullets." #define GOTROCKET "Picked up a rocket." #define GOTROCKBOX "Picked up a box of rockets." #define GOTCELL "Picked up an energy cell." #define GOTCELLBOX "Picked up an energy cell pack." #define GOTSHELLS "Picked up 4 shotgun shells." #define GOTSHELLBOX "Picked up a box of shotgun shells." #define GOTBACKPACK "Picked up a backpack full of ammo!" #define GOTBFG9000 "You got the BFG9000! Oh, yes." #define GOTCHAINGUN "You got the chaingun!" #define GOTCHAINSAW "A chainsaw! Find some meat!" #define GOTLAUNCHER "You got the rocket launcher!" #define GOTPLASMA "You got the plasma gun!" #define GOTSHOTGUN "You got the shotgun!" #define GOTSHOTGUN2 "You got the super shotgun!" // // P_Doors.C // #define PD_BLUEO "You need a blue key to activate this object" #define PD_REDO "You need a red key to activate this object" #define PD_YELLOWO "You need a yellow key to activate this object" #define PD_BLUEK "You need a blue key to open this door" #define PD_REDK "You need a red key to open this door" #define PD_YELLOWK "You need a yellow key to open this door" // // G_game.C // #define GGSAVED "game saved." // // HU_stuff.C // #define HUSTR_MSGU "[Message unsent]" #define HUSTR_E1M1 "E1M1: Hangar" #define HUSTR_E1M2 "E1M2: Nuclear Plant" #define HUSTR_E1M3 "E1M3: Toxin Refinery" #define HUSTR_E1M4 "E1M4: Command Control" #define HUSTR_E1M5 "E1M5: Phobos Lab" #define HUSTR_E1M6 "E1M6: Central Processing" #define HUSTR_E1M7 "E1M7: Computer Station" #define HUSTR_E1M8 "E1M8: Phobos Anomaly" #define HUSTR_E1M9 "E1M9: Military Base" #define HUSTR_E1M10 "E1M10: Sewers" #define HUSTR_E1M4B "E1M4B: Phobos Mission Control" #define HUSTR_E1M8B "E1M8B: Tech Gone Bad" #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_E5M1 "E5M1: Baphomet's Demesne" #define HUSTR_E5M2 "E5M2: Sheol" #define HUSTR_E5M3 "E5M3: Cages of the Damned" #define HUSTR_E5M4 "E5M4: Paths of Wretchedness" #define HUSTR_E5M5 "E5M5: Abaddon's Void" #define HUSTR_E5M6 "E5M6: Unspeakable Persecution" #define HUSTR_E5M7 "E5M7: Nightmare Underworld" #define HUSTR_E5M8 "E5M8: Halls of Perdition" #define HUSTR_E5M9 "E5M9: Realm of Iblis" #define HUSTR_1 "level 1: entryway" #define HUSTR_2 "level 2: underhalls" #define HUSTR_3 "level 3: the gantlet" #define HUSTR_4 "level 4: the focus" #define HUSTR_5 "level 5: the waste tunnels" #define HUSTR_6 "level 6: the crusher" #define HUSTR_7 "level 7: dead simple" #define HUSTR_8 "level 8: tricks and traps" #define HUSTR_9 "level 9: the pit" #define HUSTR_10 "level 10: refueling base" #define HUSTR_11 "level 11: 'o' of destruction!" #define HUSTR_12 "level 12: the factory" #define HUSTR_13 "level 13: downtown" #define HUSTR_14 "level 14: the inmost dens" #define HUSTR_15 "level 15: industrial zone" #define HUSTR_16 "level 16: suburbs" #define HUSTR_17 "level 17: tenements" #define HUSTR_18 "level 18: the courtyard" #define HUSTR_19 "level 19: the citadel" #define HUSTR_20 "level 20: gotcha!" #define HUSTR_21 "level 21: nirvana" #define HUSTR_22 "level 22: the catacombs" #define HUSTR_23 "level 23: barrels o' fun" #define HUSTR_24 "level 24: the chasm" #define HUSTR_25 "level 25: bloodfalls" #define HUSTR_26 "level 26: the abandoned mines" #define HUSTR_27 "level 27: monster condo" #define HUSTR_28 "level 28: the spirit world" #define HUSTR_29 "level 29: the living end" #define HUSTR_30 "level 30: icon of sin" #define HUSTR_31 "level 31: wolfenstein" #define HUSTR_32 "level 32: grosse" #define PHUSTR_1 "level 1: congo" #define PHUSTR_2 "level 2: well of souls" #define PHUSTR_3 "level 3: aztec" #define PHUSTR_4 "level 4: caged" #define PHUSTR_5 "level 5: ghost town" #define PHUSTR_6 "level 6: baron's lair" #define PHUSTR_7 "level 7: caughtyard" #define PHUSTR_8 "level 8: realm" #define PHUSTR_9 "level 9: abattoire" #define PHUSTR_10 "level 10: onslaught" #define PHUSTR_11 "level 11: hunted" #define PHUSTR_12 "level 12: speed" #define PHUSTR_13 "level 13: the crypt" #define PHUSTR_14 "level 14: genesis" #define PHUSTR_15 "level 15: the twilight" #define PHUSTR_16 "level 16: the omen" #define PHUSTR_17 "level 17: compound" #define PHUSTR_18 "level 18: neurosphere" #define PHUSTR_19 "level 19: nme" #define PHUSTR_20 "level 20: the death domain" #define PHUSTR_21 "level 21: slayer" #define PHUSTR_22 "level 22: impossible mission" #define PHUSTR_23 "level 23: tombstone" #define PHUSTR_24 "level 24: the final frontier" #define PHUSTR_25 "level 25: the temple of darkness" #define PHUSTR_26 "level 26: bunker" #define PHUSTR_27 "level 27: anti-christ" #define PHUSTR_28 "level 28: the sewers" #define PHUSTR_29 "level 29: odyssey of noises" #define PHUSTR_30 "level 30: the gateway of hell" #define PHUSTR_31 "level 31: cyberden" #define PHUSTR_32 "level 32: go 2 it" #define THUSTR_1 "level 1: system control" #define THUSTR_2 "level 2: human bbq" #define THUSTR_3 "level 3: power control" #define THUSTR_4 "level 4: wormhole" #define THUSTR_5 "level 5: hanger" #define THUSTR_6 "level 6: open season" #define THUSTR_7 "level 7: prison" #define THUSTR_8 "level 8: metal" #define THUSTR_9 "level 9: stronghold" #define THUSTR_10 "level 10: redemption" #define THUSTR_11 "level 11: storage facility" #define THUSTR_12 "level 12: crater" #define THUSTR_13 "level 13: nukage processing" #define THUSTR_14 "level 14: steel works" #define THUSTR_15 "level 15: dead zone" #define THUSTR_16 "level 16: deepest reaches" #define THUSTR_17 "level 17: processing area" #define THUSTR_18 "level 18: mill" #define THUSTR_19 "level 19: shipping/respawning" #define THUSTR_20 "level 20: central processing" #define THUSTR_21 "level 21: administration center" #define THUSTR_22 "level 22: habitat" #define THUSTR_23 "level 23: lunar mining project" #define THUSTR_24 "level 24: quarry" #define THUSTR_25 "level 25: baron's den" #define THUSTR_26 "level 26: ballistyx" #define THUSTR_27 "level 27: mount pain" #define THUSTR_28 "level 28: heck" #define THUSTR_29 "level 29: river styx" #define THUSTR_30 "level 30: last call" #define THUSTR_31 "level 31: pharaoh" #define THUSTR_32 "level 32: caribbean" #define NHUSTR_1 "level 1: The Earth Base" #define NHUSTR_2 "level 2: The Pain Labs" #define NHUSTR_3 "level 3: Canyon of the Dead" #define NHUSTR_4 "level 4: Hell Mountain" #define NHUSTR_5 "level 5: Vivisection" #define NHUSTR_6 "level 6: Inferno of Blood" #define NHUSTR_7 "level 7: Baron's Banquet" #define NHUSTR_8 "level 8: Tomb of Malevolence" #define NHUSTR_9 "level 9: March of the Demons" #define MHUSTR_1 "level 1: Attack" #define MHUSTR_2 "level 2: Canyon" #define MHUSTR_3 "level 3: The Catwalk" #define MHUSTR_4 "level 4: The Combine" #define MHUSTR_5 "level 5: The Fistula" #define MHUSTR_6 "level 6: The Garrison" #define MHUSTR_7 "level 7: Titan Manor" #define MHUSTR_8 "level 8: Paradox" #define MHUSTR_9 "level 9: Subspace" #define MHUSTR_10 "level 10: Subterra" #define MHUSTR_11 "level 11: Trapped On Titan" #define MHUSTR_12 "level 12: Virgil's Lead" #define MHUSTR_13 "level 13: Minos' Judgement" #define MHUSTR_14 "level 14: Bloodsea Keep" #define MHUSTR_15 "level 15: Mephisto's Maosoleum" #define MHUSTR_16 "level 16: Nessus" #define MHUSTR_17 "level 17: Geryon" #define MHUSTR_18 "level 18: Vesperas" #define MHUSTR_19 "level 19: Black Tower" #define MHUSTR_20 "level 20: The Express Elevator To Hell" #define MHUSTR_21 "level 21: Bad Dream" #define HUSTR_CHATMACRO1 "I'm ready to kick butt!" #define HUSTR_CHATMACRO2 "I'm OK." #define HUSTR_CHATMACRO3 "I'm not looking too good!" #define HUSTR_CHATMACRO4 "Help!" #define HUSTR_CHATMACRO5 "You suck!" #define HUSTR_CHATMACRO6 "Next time, scumbag..." #define HUSTR_CHATMACRO7 "Come here!" #define HUSTR_CHATMACRO8 "I'll take care of it." #define HUSTR_CHATMACRO9 "Yes" #define HUSTR_CHATMACRO0 "No" #define HUSTR_TALKTOSELF1 "You mumble to yourself" #define HUSTR_TALKTOSELF2 "Who's there?" #define HUSTR_TALKTOSELF3 "You scare yourself" #define HUSTR_TALKTOSELF4 "You start to rave" #define HUSTR_TALKTOSELF5 "You've lost it..." #define HUSTR_MESSAGESENT "[Message Sent]" // The following should NOT be changed unless it seems // just AWFULLY necessary #define HUSTR_PLRGREEN "Green: " #define HUSTR_PLRINDIGO "Indigo: " #define HUSTR_PLRBROWN "Brown: " #define HUSTR_PLRRED "Red: " #define HUSTR_KEYGREEN 'g' #define HUSTR_KEYINDIGO 'i' #define HUSTR_KEYBROWN 'b' #define HUSTR_KEYRED 'r' // // AM_map.C // #define AMSTR_FOLLOWON "Follow Mode ON" #define AMSTR_FOLLOWOFF "Follow Mode OFF" #define AMSTR_GRIDON "Grid ON" #define AMSTR_GRIDOFF "Grid OFF" #define AMSTR_MARKEDSPOT "Marked Spot" #define AMSTR_MARKSCLEARED "All Marks Cleared" #define AMSTR_OVERLAYON "Overlay Mode ON" #define AMSTR_OVERLAYOFF "Overlay Mode OFF" #define AMSTR_ROTATEON "Rotate Mode ON" #define AMSTR_ROTATEOFF "Rotate 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..." // // 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!" #define E5TEXT \ "Baphomet was only doing Satan's bidding\n"\ "by bringing you back to Hell. Somehow they\n"\ "didn't understand that you're the reason\n"\ "they failed in the first place.\n"\ "\n"\ "After mopping up the place with your\n"\ "arsenal, you're ready to face the more\n"\ "advanced demons that were sent to Earth.\n"\ "\n"\ "\n"\ "Lock and load. Rip and tear." // after level 6, put this: #define C1TEXT \ "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ "STARPORT. BUT SOMETHING IS WRONG. THE\n" \ "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ "IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ "\n"\ "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ "OF THE STARBASE AND FIND THE CONTROLLING\n" \ "SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ "HOSTAGE." // After level 11, put this: #define C2TEXT \ "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"\ "THE NIGHTMARE. NOW YOU ARE THE ONLY\n"\ "HUMAN LEFT ON THE FACE OF THE PLANET.\n"\ "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"\ "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"\ "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"\ "THAT YOU HAVE SAVED YOUR SPECIES.\n"\ "\n"\ "BUT THEN, EARTH CONTROL BEAMS DOWN A\n"\ "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"\ "THE SOURCE OF THE ALIEN INVASION. IF YOU\n"\ "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"\ "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"\ "YOUR OWN HOME CITY, NOT FAR FROM THE\n"\ "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"\ "UP AND RETURN TO THE FRAY." // After level 20, put this: #define C3TEXT \ "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"\ "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"\ "YOU SEE NO WAY TO DESTROY THE CREATURES'\n"\ "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"\ "TEETH AND PLUNGE THROUGH IT.\n"\ "\n"\ "THERE MUST BE A WAY TO CLOSE IT ON THE\n"\ "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"\ "GOT TO GO THROUGH HELL TO GET TO IT?" // After level 29, put this: #define C4TEXT \ "THE HORRENDOUS VISAGE OF THE BIGGEST\n"\ "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"\ "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"\ "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"\ "UP AND DIES, ITS THRASHING LIMBS\n"\ "DEVASTATING UNTOLD MILES OF HELL'S\n"\ "SURFACE.\n"\ "\n"\ "YOU'VE DONE IT. THE INVASION IS OVER.\n"\ "EARTH IS SAVED. HELL IS A WRECK. YOU\n"\ "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"\ "DIE, NOW. WIPING THE SWEAT FROM YOUR\n"\ "FOREHEAD YOU BEGIN THE LONG TREK BACK\n"\ "HOME. REBUILDING EARTH OUGHT TO BE A\n"\ "LOT MORE FUN THAN RUINING IT WAS.\n" // Before level 31, put this: #define C5TEXT \ "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"\ "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"\ "HUMANS, RATHER THAN DEMONS. YOU WONDER\n"\ "WHO THE INMATES OF THIS CORNER OF HELL\n"\ "WILL BE." // Before level 32, put this: #define C6TEXT \ "CONGRATULATIONS, YOU'VE FOUND THE\n"\ "SUPER SECRET LEVEL! YOU'D BETTER\n"\ "BLAZE THROUGH THIS ONE!\n" // after map 06 #define P1TEXT \ "You gloat over the steaming carcass of the\n"\ "Guardian. With its death, you've wrested\n"\ "the Accelerator from the stinking claws\n"\ "of Hell. You relax and glance around the\n"\ "room. Damn! There was supposed to be at\n"\ "least one working prototype, but you can't\n"\ "see it. The demons must have taken it.\n"\ "\n"\ "You must find the prototype, or all your\n"\ "struggles will have been wasted. Keep\n"\ "moving, keep fighting, keep killing.\n"\ "Oh yes, keep living, too." // after map 11 #define P2TEXT \ "Even the deadly Arch-Vile labyrinth could\n"\ "not stop you, and you've gotten to the\n"\ "prototype Accelerator which is soon\n"\ "efficiently and permanently deactivated.\n"\ "\n"\ "You're good at that kind of thing." // after map 20 #define P3TEXT \ "You've bashed and battered your way into\n"\ "the heart of the devil-hive. Time for a\n"\ "Search-and-Destroy mission, aimed at the\n"\ "Gatekeeper, whose foul offspring is\n"\ "cascading to Earth. Yeah, he's bad. But\n"\ "you know who's worse!\n"\ "\n"\ "Grinning evilly, you check your gear, and\n"\ "get ready to give the bastard a little Hell\n"\ "of your own making!" // after map 30 #define P4TEXT \ "The Gatekeeper's evil face is splattered\n"\ "all over the place. As its tattered corpse\n"\ "collapses, an inverted Gate forms and\n"\ "sucks down the shards of the last\n"\ "prototype Accelerator, not to mention the\n"\ "few remaining demons. You're done. Hell\n"\ "has gone back to pounding bad dead folks \n"\ "instead of good live ones. Remember to\n"\ "tell your grandkids to put a rocket\n"\ "launcher in your coffin. If you go to Hell\n"\ "when you die, you'll need it for some\n"\ "final cleaning-up ..." // before map 31 #define P5TEXT \ "You've found the second-hardest level we\n"\ "got. Hope you have a saved game a level or\n"\ "two previous. If not, be prepared to die\n"\ "aplenty. For master marines only." // before map 32 #define P6TEXT \ "Betcha wondered just what WAS the hardest\n"\ "level we had ready for ya? Now you know.\n"\ "No one gets out alive." #define T1TEXT \ "You've fought your way out of the infested\n"\ "experimental labs. It seems that UAC has\n"\ "once again gulped it down. With their\n"\ "high turnover, it must be hard for poor\n"\ "old UAC to buy corporate health insurance\n"\ "nowadays..\n"\ "\n"\ "Ahead lies the military complex, now\n"\ "swarming with diseased horrors hot to get\n"\ "their teeth into you. With luck, the\n"\ "complex still has some warlike ordnance\n"\ "laying around." #define T2TEXT \ "You hear the grinding of heavy machinery\n"\ "ahead. You sure hope they're not stamping\n"\ "out new hellspawn, but you're ready to\n"\ "ream out a whole herd if you have to.\n"\ "They might be planning a blood feast, but\n"\ "you feel about as mean as two thousand\n"\ "maniacs packed into one mad killer.\n"\ "\n"\ "You don't plan to go down easy." #define T3TEXT \ "The vista opening ahead looks real damn\n"\ "familiar. Smells familiar, too -- like\n"\ "fried excrement. You didn't like this\n"\ "place before, and you sure as hell ain't\n"\ "planning to like it now. The more you\n"\ "brood on it, the madder you get.\n"\ "Hefting your gun, an evil grin trickles\n"\ "onto your face. Time to take some names." #define T4TEXT \ "Suddenly, all is silent, from one horizon\n"\ "to the other. The agonizing echo of Hell\n"\ "fades away, the nightmare sky turns to\n"\ "blue, the heaps of monster corpses start \n"\ "to evaporate along with the evil stench \n"\ "that filled the air. Jeeze, maybe you've\n"\ "done it. Have you really won?\n"\ "\n"\ "Something rumbles in the distance.\n"\ "A blue light begins to glow inside the\n"\ "ruined skull of the demon-spitter." #define T5TEXT \ "What now? Looks totally different. Kind\n"\ "of like King Tut's condo. Well,\n"\ "whatever's here can't be any worse\n"\ "than usual. Can it? Or maybe it's best\n"\ "to let sleeping gods lie.." #define T6TEXT \ "Time for a vacation. You've burst the\n"\ "bowels of hell and by golly you're ready\n"\ "for a break. You mutter to yourself,\n"\ "Maybe someone else can kick Hell's ass\n"\ "next time around. Ahead lies a quiet town,\n"\ "with peaceful flowing water, quaint\n"\ "buildings, and presumably no Hellspawn.\n"\ "\n"\ "As you step off the transport, you hear\n"\ "the stomp of a cyberdemon's iron shoe." #define N1TEXT \ "TROUBLE WAS BREWING AGAIN IN YOUR FAVORITE\n"\ "VACATION SPOT... HELL. SOME CYBERDEMON\n"\ "PUNK THOUGHT HE COULD TURN HELL INTO A\n"\ "PERSONAL AMUSEMENT PARK, AND MAKE EARTH\nTHE TICKET BOOTH.\n\n"\ "WELL THAT HALF-ROBOT FREAK SHOW DIDN'T\n"\ "KNOW WHO WAS COMING TO THE FAIR. THERE'S\n"\ "NOTHING LIKE A SHOOTING GALLERY FULL OF\n"\ "HELLSPAWN TO GET THE BLOOD PUMPING...\n\n"\ "NOW THE WALLS OF THE DEMON'S LABYRINTH\n"\ "ECHO WITH THE SOUND OF HIS METALLIC LIMBS\n"\ "HITTING THE FLOOR. HIS DEATH MOAN GURGLES\n" \ "OUT THROUGH THE MESS YOU LEFT OF HIS FACE.\n\n" \ "THIS RIDE IS CLOSED." #define M1TEXT \ "CONGRATULATIONS YOU HAVE FINISHED... \n\n"\ "THE MASTER LEVELS\n" // // 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 crispy-doom-crispy-doom-5.6.4/src/doom/d_items.c000066400000000000000000000035141360717211000215540ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // We are referring to sprite numbers. #include "info.h" #include "d_items.h" // // PSPRITE ACTIONS for waepons. // This struct controls the weapon animations. // // Each entry is: // ammo/amunition type // upstate // downstate // readystate // atkstate, i.e. attack/fire/hit frame // flashstate, muzzle flash // weaponinfo_t weaponinfo[NUMWEAPONS] = { { // fist am_noammo, S_PUNCHUP, S_PUNCHDOWN, S_PUNCH, S_PUNCH1, S_NULL }, { // pistol am_clip, S_PISTOLUP, S_PISTOLDOWN, S_PISTOL, S_PISTOL1, S_PISTOLFLASH }, { // shotgun am_shell, S_SGUNUP, S_SGUNDOWN, S_SGUN, S_SGUN1, S_SGUNFLASH1 }, { // chaingun am_clip, S_CHAINUP, S_CHAINDOWN, S_CHAIN, S_CHAIN1, S_CHAINFLASH1 }, { // missile launcher am_misl, S_MISSILEUP, S_MISSILEDOWN, S_MISSILE, S_MISSILE1, S_MISSILEFLASH1 }, { // plasma rifle am_cell, S_PLASMAUP, S_PLASMADOWN, S_PLASMA, S_PLASMA1, S_PLASMAFLASH1 }, { // bfg 9000 am_cell, S_BFGUP, S_BFGDOWN, S_BFG, S_BFG1, S_BFGFLASH1 }, { // chainsaw am_noammo, S_SAWUP, S_SAWDOWN, S_SAW, S_SAW1, S_NULL }, { // super shotgun am_shell, S_DSGUNUP, S_DSGUNDOWN, S_DSGUN, S_DSGUN1, S_DSGUNFLASH1 }, }; crispy-doom-crispy-doom-5.6.4/src/doom/d_items.h000066400000000000000000000017351360717211000215640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Items: key cards, artifacts, weapon, ammunition. // #ifndef __D_ITEMS__ #define __D_ITEMS__ #include "doomdef.h" // Weapon info: sprite frames, ammunition use. typedef struct { ammotype_t ammo; int upstate; int downstate; int readystate; int atkstate; int flashstate; } weaponinfo_t; extern weaponinfo_t weaponinfo[NUMWEAPONS]; #endif crispy-doom-crispy-doom-5.6.4/src/doom/d_main.c000066400000000000000000001740001360717211000213560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM main program (D_DoomMain) and game loop (D_DoomLoop), // plus functions to determine game mode (shareware, registered), // parse command line parameters, configure game parameters (turbo), // and call the startup functions. // #include #include #include #include #include // [crispy] time_t, time(), struct tm, localtime() #include "config.h" #include "deh_main.h" #include "doomdef.h" #include "doomstat.h" #include "dstrings.h" #include "sounds.h" #include "d_iwad.h" #include "z_zone.h" #include "w_main.h" #include "w_wad.h" #include "s_sound.h" #include "v_diskicon.h" #include "v_video.h" #include "f_finale.h" #include "f_wipe.h" #include "m_argv.h" #include "m_config.h" #include "m_controls.h" #include "m_misc.h" #include "m_menu.h" #include "p_saveg.h" #include "i_endoom.h" #include "i_input.h" #include "i_joystick.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "g_game.h" #include "hu_stuff.h" #include "wi_stuff.h" #include "st_stuff.h" #include "am_map.h" #include "net_client.h" #include "net_dedicated.h" #include "net_query.h" #include "p_setup.h" #include "r_local.h" #include "statdump.h" #include "d_main.h" // // 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 // void D_DoomLoop (void); // Location where savegames are stored char * savegamedir; // location of IWAD and WAD files char * iwadfile; boolean devparm; // started game with -devparm boolean nomonsters; // checkparm of -nomonsters boolean respawnparm; // checkparm of -respawn boolean fastparm; // checkparm of -fast //extern int soundVolume; //extern int sfxVolume; //extern int musicVolume; extern boolean inhelpscreens; skill_t startskill; int startepisode; int startmap; boolean autostart; int startloadgame; boolean advancedemo; // Store demo, do not accept any inputs boolean storedemo; // If true, the main game loop has started. boolean main_loop_started = false; char wadfile[1024]; // primary wad file char mapdir[1024]; // directory of development maps int show_endoom = 0; // [crispy] disable int show_diskicon = 1; char *nervewadfile = NULL; void D_ConnectNetGame(void); void D_CheckNetGame(void); // // D_ProcessEvents // Send all the events of the given timestamp down the responder chain // void D_ProcessEvents (void) { event_t* ev; // IF STORE DEMO, DO NOT ACCEPT INPUT if (storedemo) return; while ((ev = D_PopEvent()) != NULL) { if (M_Responder (ev)) continue; // menu ate the event G_Responder (ev); } } // // 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 boolean setsizeneeded; extern int showMessages; void R_ExecuteSetViewSize (void); boolean D_Display (void) { static boolean viewactivestate = false; static boolean menuactivestate = false; static boolean inhelpscreensstate = false; static boolean fullscreen = false; static gamestate_t oldgamestate = -1; static int borderdrawcount; int y; boolean wipe; boolean redrawsbar; redrawsbar = false; // change the view size if needed if (setsizeneeded) { R_ExecuteSetViewSize (); oldgamestate = -1; // force background redraw borderdrawcount = 3; } // save the current screen if about to wipe if (gamestate != wipegamestate) { wipe = true; wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); } else wipe = false; if (gamestate == GS_LEVEL && gametic) HU_Erase(); // do buffered drawing switch (gamestate) { case GS_LEVEL: if (!gametic) break; if (automapactive && !crispy->automapoverlay) { // [crispy] update automap while playing R_RenderPlayerView (&players[displayplayer]); AM_Drawer (); } if (wipe || (viewheight != SCREENHEIGHT && fullscreen)) redrawsbar = true; if (inhelpscreensstate && !inhelpscreens) redrawsbar = true; // just put away the help screen ST_Drawer (viewheight == SCREENHEIGHT, redrawsbar ); fullscreen = viewheight == SCREENHEIGHT; break; case GS_INTERMISSION: WI_Drawer (); break; case GS_FINALE: F_Drawer (); break; case GS_DEMOSCREEN: D_PageDrawer (); break; } // draw buffered stuff to screen I_UpdateNoBlit (); // draw the view directly if (gamestate == GS_LEVEL && (!automapactive || crispy->automapoverlay) && gametic) { R_RenderPlayerView (&players[displayplayer]); // [crispy] Crispy HUD if (screenblocks >= CRISPY_HUD) ST_Drawer(false, true); } // [crispy] in automap overlay mode, // the HUD is drawn on top of everything else if (gamestate == GS_LEVEL && gametic && !(automapactive && crispy->automapoverlay)) HU_Drawer (); // clean up border stuff if (gamestate != oldgamestate && gamestate != GS_LEVEL) #ifndef CRISPY_TRUECOLOR I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE)); #else I_SetPalette (0); #endif // see if the border needs to be initially drawn if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) { viewactivestate = false; // view was not active R_FillBackScreen (); // draw the pattern into the back screen } // see if the border needs to be updated to the screen if (gamestate == GS_LEVEL && (!automapactive || crispy->automapoverlay) && scaledviewwidth != SCREENWIDTH) { if (menuactive || menuactivestate || !viewactivestate) borderdrawcount = 3; if (borderdrawcount) { R_DrawViewBorder (); // erase old menu stuff borderdrawcount--; } } if (testcontrols) { // Box showing current mouse speed V_DrawMouseSpeedBox(testcontrols_mousespeed); } menuactivestate = menuactive; viewactivestate = viewactive; inhelpscreensstate = inhelpscreens; oldgamestate = wipegamestate = gamestate; // [crispy] in automap overlay mode, // draw the automap and HUD on top of everything else if (automapactive && crispy->automapoverlay) { AM_Drawer (); HU_Drawer (); // [crispy] force redraw of status bar and border viewactivestate = false; inhelpscreensstate = true; } // [crispy] draw neither pause pic nor menu when taking a clean screenshot if (crispy->cleanscreenshot) { return false; } // draw pause pic if (paused) { if (automapactive && !crispy->automapoverlay) y = 4; else y = (viewwindowy >> crispy->hires)+4; V_DrawPatchDirect((viewwindowx >> crispy->hires) + ((scaledviewwidth >> crispy->hires) - 68) / 2, y, W_CacheLumpName (DEH_String("M_PAUSE"), PU_CACHE)); } // menus go directly to the screen M_Drawer (); // menu is drawn even on top of everything NetUpdate (); // send out any new accumulation return wipe; } void EnableLoadingDisk(void) // [crispy] un-static { const char *disk_lump_name; if (show_diskicon) { if (M_CheckParm("-cdrom") > 0) { disk_lump_name = DEH_String("STCDROM"); } else { disk_lump_name = DEH_String("STDISK"); } V_EnableLoadingDisk(disk_lump_name, SCREENWIDTH - LOADING_DISK_W, SCREENHEIGHT - LOADING_DISK_H); } } // // Add configuration file variable bindings. // void D_BindVariables(void) { int i; M_ApplyPlatformDefaults(); I_BindInputVariables(); I_BindVideoVariables(); I_BindJoystickVariables(); I_BindSoundVariables(); M_BindBaseControls(); M_BindWeaponControls(); M_BindMapControls(); M_BindMenuControls(); M_BindChatControls(MAXPLAYERS); key_multi_msgplayer[0] = HUSTR_KEYGREEN; key_multi_msgplayer[1] = HUSTR_KEYINDIGO; key_multi_msgplayer[2] = HUSTR_KEYBROWN; key_multi_msgplayer[3] = HUSTR_KEYRED; NET_BindVariables(); M_BindIntVariable("mouse_sensitivity", &mouseSensitivity); M_BindIntVariable("mouse_sensitivity_x2", &mouseSensitivity_x2); // [crispy] M_BindIntVariable("mouse_sensitivity_y", &mouseSensitivity_y); // [crispy] M_BindIntVariable("sfx_volume", &sfxVolume); M_BindIntVariable("music_volume", &musicVolume); M_BindIntVariable("show_messages", &showMessages); M_BindIntVariable("screenblocks", &screenblocks); M_BindIntVariable("detaillevel", &detailLevel); M_BindIntVariable("snd_channels", &snd_channels); // [crispy] unconditionally disable savegame and demo limits // M_BindIntVariable("vanilla_savegame_limit", &vanilla_savegame_limit); // M_BindIntVariable("vanilla_demo_limit", &vanilla_demo_limit); M_BindIntVariable("show_endoom", &show_endoom); M_BindIntVariable("show_diskicon", &show_diskicon); // Multiplayer chat macros for (i=0; i<10; ++i) { char buf[12]; M_snprintf(buf, sizeof(buf), "chatmacro%i", i); M_BindStringVariable(buf, &chat_macros[i]); } // [crispy] bind "crispness" config variables M_BindIntVariable("crispy_automapoverlay", &crispy->automapoverlay); M_BindIntVariable("crispy_automaprotate", &crispy->automaprotate); M_BindIntVariable("crispy_automapstats", &crispy->automapstats); M_BindIntVariable("crispy_bobfactor", &crispy->bobfactor); M_BindIntVariable("crispy_brightmaps", &crispy->brightmaps); M_BindIntVariable("crispy_centerweapon", &crispy->centerweapon); M_BindIntVariable("crispy_coloredblood", &crispy->coloredblood); M_BindIntVariable("crispy_coloredhud", &crispy->coloredhud); M_BindIntVariable("crispy_crosshair", &crispy->crosshair); M_BindIntVariable("crispy_crosshairhealth", &crispy->crosshairhealth); M_BindIntVariable("crispy_crosshairtarget", &crispy->crosshairtarget); M_BindIntVariable("crispy_crosshairtype", &crispy->crosshairtype); M_BindIntVariable("crispy_demobar", &crispy->demobar); M_BindIntVariable("crispy_demotimer", &crispy->demotimer); M_BindIntVariable("crispy_demotimerdir", &crispy->demotimerdir); M_BindIntVariable("crispy_extautomap", &crispy->extautomap); M_BindIntVariable("crispy_extsaveg", &crispy->extsaveg); M_BindIntVariable("crispy_flipcorpses", &crispy->flipcorpses); M_BindIntVariable("crispy_freeaim", &crispy->freeaim); M_BindIntVariable("crispy_freelook", &crispy->freelook); M_BindIntVariable("crispy_hires", &crispy->hires); M_BindIntVariable("crispy_jump", &crispy->jump); M_BindIntVariable("crispy_leveltime", &crispy->leveltime); M_BindIntVariable("crispy_mouselook", &crispy->mouselook); M_BindIntVariable("crispy_neghealth", &crispy->neghealth); M_BindIntVariable("crispy_overunder", &crispy->overunder); M_BindIntVariable("crispy_pitch", &crispy->pitch); M_BindIntVariable("crispy_playercoords", &crispy->playercoords); M_BindIntVariable("crispy_recoil", &crispy->recoil); M_BindIntVariable("crispy_secretmessage", &crispy->secretmessage); M_BindIntVariable("crispy_smoothlight", &crispy->smoothlight); M_BindIntVariable("crispy_smoothscaling", &crispy->smoothscaling); M_BindIntVariable("crispy_soundfix", &crispy->soundfix); M_BindIntVariable("crispy_soundfull", &crispy->soundfull); M_BindIntVariable("crispy_soundmono", &crispy->soundmono); M_BindIntVariable("crispy_translucency", &crispy->translucency); #ifdef CRISPY_TRUECOLOR M_BindIntVariable("crispy_truecolor", &crispy->truecolor); #endif M_BindIntVariable("crispy_uncapped", &crispy->uncapped); M_BindIntVariable("crispy_vsync", &crispy->vsync); M_BindIntVariable("crispy_weaponsquat", &crispy->weaponsquat); } // // D_GrabMouseCallback // // Called to determine whether to grab the mouse pointer // boolean D_GrabMouseCallback(void) { // Drone players don't need mouse focus if (drone) return false; // 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 && !advancedemo; } // // D_RunFrame // void D_RunFrame() { int nowtime; int tics; static int wipestart; static boolean wipe; if (wipe) { do { nowtime = I_GetTime (); tics = nowtime - wipestart; I_Sleep(1); } while (tics <= 0); wipestart = nowtime; wipe = !wipe_ScreenWipe(wipe_Melt , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); I_UpdateNoBlit (); M_Drawer (); // menu is drawn even on top of wipes I_FinishUpdate (); // page flip or blit buffer return; } // frame syncronous IO operations I_StartFrame (); TryRunTics (); // will run at least one tic S_UpdateSounds (players[consoleplayer].mo);// move positional sounds // Update display, next frame, with current state if no profiling is on if (screenvisible && !nodrawers) { if ((wipe = D_Display ())) { // start wipe on this frame wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); wipestart = I_GetTime () - 1; } else { // normal update I_FinishUpdate (); // page flip or blit buffer } } // [crispy] post-rendering function pointer to apply config changes // that affect rendering and that are better applied after the current // frame has finished rendering if (crispy->post_rendering_hook) { crispy->post_rendering_hook(); crispy->post_rendering_hook = NULL; } } // // D_DoomLoop // void D_DoomLoop (void) { if (gamevariant == bfgedition && (demorecording || (gameaction == ga_playdemo) || netgame)) { printf(" WARNING: You are playing using one of the Doom Classic\n" " IWAD files shipped with the Doom 3: BFG Edition. These are\n" " known to be incompatible with the regular IWAD files and\n" " may cause demos and network games to get out of sync.\n"); } // [crispy] no need to write a demo header in demo continue mode if (demorecording && gameaction != ga_playdemo) G_BeginRecording (); main_loop_started = true; I_SetWindowTitle(gamedescription); I_GraphicsCheckCommandLine(); I_SetGrabMouseCallback(D_GrabMouseCallback); I_InitGraphics(); EnableLoadingDisk(); TryRunTics(); V_RestoreBuffer(); R_ExecuteSetViewSize(); D_StartGameLoop(); if (testcontrols) { wipegamestate = gamestate; } while (1) { D_RunFrame(); } } // // DEMO LOOP // int demosequence; int pagetic; const char *pagename; // // D_PageTicker // Handles timing for warped projection // void D_PageTicker (void) { if (--pagetic < 0) D_AdvanceDemo (); } // // D_PageDrawer // void D_PageDrawer (void) { V_DrawPatchFullScreen (W_CacheLumpName(pagename, PU_CACHE), crispy->fliplevels); } // // D_AdvanceDemo // Called after each demo or intro demosequence finishes // void D_AdvanceDemo (void) { advancedemo = true; } // // This cycles through the demo sequences. // FIXME - version dependend demo numbers? // void D_DoAdvanceDemo (void) { players[consoleplayer].playerstate = PST_LIVE; // not reborn advancedemo = false; usergame = false; // no save / end game here paused = false; gameaction = ga_nothing; // [crispy] update the "singleplayer" variable CheckCrispySingleplayer(!demorecording && !demoplayback && !netgame); // The Ultimate Doom executable changed the demo sequence to add // a DEMO4 demo. Final Doom was based on Ultimate, so also // includes this change; however, the Final Doom IWADs do not // include a DEMO4 lump, so the game bombs out with an error // when it reaches this point in the demo sequence. // However! There is an alternate version of Final Doom that // includes a fixed executable. // [crispy] get rid of this demo sequence breaking bug /* if (gameversion == exe_ultimate || gameversion == exe_final) */ if (W_CheckNumForName(DEH_String("demo4")) >= 0) demosequence = (demosequence+1)%7; else demosequence = (demosequence+1)%6; switch (demosequence) { case 0: if ( gamemode == commercial ) pagetic = TICRATE * 11; else pagetic = 170; gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLEPIC"); if ( gamemode == commercial ) S_StartMusic(mus_dm2ttl); else S_StartMusic (mus_intro); break; case 1: G_DeferedPlayDemo(DEH_String("demo1")); break; case 2: pagetic = 200; gamestate = GS_DEMOSCREEN; pagename = DEH_String("CREDIT"); break; case 3: G_DeferedPlayDemo(DEH_String("demo2")); break; case 4: gamestate = GS_DEMOSCREEN; if ( gamemode == commercial) { pagetic = TICRATE * 11; pagename = DEH_String("TITLEPIC"); S_StartMusic(mus_dm2ttl); } else { pagetic = 200; if (gameversion >= exe_ultimate) pagename = DEH_String("CREDIT"); else pagename = DEH_String("HELP2"); } break; case 5: G_DeferedPlayDemo(DEH_String("demo3")); break; // THE DEFINITIVE DOOM Special Edition demo case 6: G_DeferedPlayDemo(DEH_String("demo4")); break; } // The Doom 3: BFG Edition version of doom2.wad does not have a // TITLETPIC lump. Use INTERPIC instead as a workaround. if (gamevariant == bfgedition && !strcasecmp(pagename, "TITLEPIC") && W_CheckNumForName("titlepic") < 0) { // [crispy] use DMENUPIC instead of TITLEPIC, it's awesome pagename = DEH_String("DMENUPIC"); } } // // D_StartTitle // void D_StartTitle (void) { gameaction = ga_nothing; demosequence = -1; D_AdvanceDemo (); } // Strings for dehacked replacements of the startup banner // // These are from the original source: some of them are perhaps // not used in any dehacked patches static const char *banners[] = { // doom2.wad " " "DOOM 2: Hell on Earth v%i.%i" " ", // doom2.wad v1.666 " " "DOOM 2: Hell on Earth v%i.%i66" " ", // doom1.wad " " "DOOM Shareware Startup v%i.%i" " ", // doom.wad " " "DOOM Registered Startup v%i.%i" " ", // Registered DOOM uses this " " "DOOM System Startup v%i.%i" " ", // Doom v1.666 " " "DOOM System Startup v%i.%i66" " " // doom.wad (Ultimate DOOM) " " "The Ultimate DOOM Startup v%i.%i" " ", // tnt.wad " " "DOOM 2: TNT - Evilution v%i.%i" " ", // plutonia.wad " " "DOOM 2: Plutonia Experiment v%i.%i" " ", }; // // Get game name: if the startup banner has been replaced, use that. // Otherwise, use the name given // static char *GetGameName(char *gamename) { size_t i; const char *deh_sub; for (i=0; iname, "MAP01", 8)) { gamemission = doom2; break; } else if (!strncasecmp(lumpinfo[i]->name, "E1M1", 8)) { gamemission = doom; break; } } if (gamemission == none) { // Still no idea. I don't think this is going to work. I_Error("Unknown or invalid IWAD file."); } } // Make sure gamemode is set up correctly if (logical_gamemission == doom) { // Doom 1. But which version? if (W_CheckNumForName("E4M1") > 0) { // Ultimate Doom gamemode = retail; } else if (W_CheckNumForName("E3M1") > 0) { gamemode = registered; } else { gamemode = shareware; } } else { int p; // Doom 2 of some kind. gamemode = commercial; // We can manually override the gamemission that we got from the // IWAD detection code. This allows us to eg. play Plutonia 2 // with Freedoom and get the right level names. //! // @category compat // @arg // // Explicitly specify a Doom II "mission pack" to run as, instead of // detecting it based on the filename. Valid values are: "doom2", // "tnt" and "plutonia". // p = M_CheckParmWithArgs("-pack", 1); if (p > 0) { SetMissionForPackName(myargv[p + 1]); } } } // Set the gamedescription string void D_SetGameDescription(void) { gamedescription = "Unknown"; if (logical_gamemission == doom) { // Doom 1. But which version? if (gamevariant == freedoom) { gamedescription = GetGameName("Freedoom: Phase 1"); } else if (gamemode == retail) { // Ultimate Doom gamedescription = GetGameName("The Ultimate DOOM"); } else if (gamemode == registered) { gamedescription = GetGameName("DOOM Registered"); } else if (gamemode == shareware) { gamedescription = GetGameName("DOOM Shareware"); } } else { // Doom 2 of some kind. But which mission? if (gamevariant == freedm) { gamedescription = GetGameName("FreeDM"); } else if (gamevariant == freedoom) { gamedescription = GetGameName("Freedoom: Phase 2"); } else if (logical_gamemission == doom2) { gamedescription = GetGameName("DOOM 2: Hell on Earth"); } else if (logical_gamemission == pack_plut) { gamedescription = GetGameName("DOOM 2: Plutonia Experiment"); } else if (logical_gamemission == pack_tnt) { gamedescription = GetGameName("DOOM 2: TNT - Evilution"); } else if (logical_gamemission == pack_nerve) { gamedescription = GetGameName("DOOM 2: No Rest For The Living"); } else if (logical_gamemission == pack_master) { gamedescription = GetGameName("Master Levels for DOOM 2"); } } } // print title for every printed line char title[128]; static boolean D_AddFile(char *filename) { wad_file_t *handle; printf(" adding %s\n", filename); handle = W_AddFile(filename); return handle != NULL; } // Copyright message banners // Some dehacked mods replace these. These are only displayed if they are // replaced by dehacked. static const char *copyright_banners[] = { "===========================================================================\n" "ATTENTION: This version of DOOM has been modified. If you would like to\n" "get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n" " You will not receive technical support for modified games.\n" " press enter to continue\n" "===========================================================================\n", "===========================================================================\n" " Commercial product - do not distribute!\n" " Please report software piracy to the SPA: 1-800-388-PIR8\n" "===========================================================================\n", "===========================================================================\n" " Shareware!\n" "===========================================================================\n" }; // Prints a message only if it has been modified by dehacked. void PrintDehackedBanners(void) { size_t i; for (i=0; i // @category compat // // Emulate a specific version of Doom. Valid values are "1.2", // "1.666", "1.7", "1.8", "1.9", "ultimate", "final", "final2", // "hacx" and "chex". // p = M_CheckParmWithArgs("-gameversion", 1); if (p) { for (i=0; gameversions[i].description != NULL; ++i) { if (!strcmp(myargv[p+1], gameversions[i].cmdline)) { gameversion = gameversions[i].version; break; } } if (gameversions[i].description == NULL) { printf("Supported game versions:\n"); for (i=0; gameversions[i].description != NULL; ++i) { printf("\t%s (%s)\n", gameversions[i].cmdline, gameversions[i].description); } I_Error("Unknown game version '%s'", myargv[p+1]); } } else { // Determine automatically if (gamemission == pack_chex) { // chex.exe - identified by iwad filename gameversion = exe_chex; } else if (gamemission == pack_hacx) { // hacx.exe: identified by iwad filename gameversion = exe_hacx; } else if (gamemode == shareware || gamemode == registered || (gamemode == commercial && gamemission == doom2)) { // original gameversion = exe_doom_1_9; // Detect version from demo lump for (i = 1; i <= 3; ++i) { M_snprintf(demolumpname, 6, "demo%i", i); if (W_CheckNumForName(demolumpname) > 0) { demolump = W_CacheLumpName(demolumpname, PU_STATIC); demoversion = demolump[0]; W_ReleaseLumpName(demolumpname); status = true; switch (demoversion) { case 0: case 1: case 2: case 3: case 4: gameversion = exe_doom_1_2; break; case 106: gameversion = exe_doom_1_666; break; case 107: gameversion = exe_doom_1_7; break; case 108: gameversion = exe_doom_1_8; break; case 109: gameversion = exe_doom_1_9; break; default: status = false; break; } if (status) { break; } } } } else if (gamemode == retail) { gameversion = exe_ultimate; } else if (gamemode == commercial) { // Final Doom: tnt or plutonia // Defaults to emulating the first Final Doom executable, // which has the crash in the demo loop; however, having // this as the default should mean that it plays back // most demos correctly. gameversion = exe_final; } } // Deathmatch 2.0 did not exist until Doom v1.4 if (gameversion <= exe_doom_1_2 && deathmatch == 2) { deathmatch = 1; } // The original exe does not support retail - 4th episode not supported if (gameversion < exe_ultimate && gamemode == retail) { gamemode = registered; } // EXEs prior to the Final Doom exes do not support Final Doom. if (gameversion < exe_final && gamemode == commercial && (gamemission == pack_tnt || gamemission == pack_plut)) { gamemission = doom2; } } void PrintGameVersion(void) { int i; for (i=0; gameversions[i].description != NULL; ++i) { if (gameversions[i].version == gameversion) { printf("Emulating the behavior of the " "'%s' executable.\n", gameversions[i].description); break; } } } // Function called at exit to display the ENDOOM screen static void D_Endoom(void) { byte *endoom; // Don't show ENDOOM if we have it disabled, or we're running // in screensaver or control test mode. Only show it once the // game has actually started. if (!show_endoom || !main_loop_started || screensaver_mode || M_CheckParm("-testcontrols") > 0) { return; } endoom = W_CacheLumpName(DEH_String("ENDOOM"), PU_STATIC); I_Endoom(endoom); } // Load dehacked patches needed for certain IWADs. static void LoadIwadDeh(void) { // The Freedoom IWADs have DEHACKED lumps that must be loaded. if (gamevariant == freedoom || gamevariant == freedm) { // Old versions of Freedoom (before 2014-09) did not have technically // valid DEHACKED lumps, so ignore errors and just continue if this // is an old IWAD. DEH_LoadLumpByName("DEHACKED", false, true); } // If this is the HACX IWAD, we need to load the DEHACKED lump. if (gameversion == exe_hacx) { if (!DEH_LoadLumpByName("DEHACKED", true, false)) { I_Error("DEHACKED lump not found. Please check that this is the " "Hacx v1.2 IWAD."); } } // Chex Quest needs a separate Dehacked patch which must be downloaded // and installed next to the IWAD. if (gameversion == exe_chex) { char *chex_deh = NULL; char *dirname; // Look for chex.deh in the same directory as the IWAD file. dirname = M_DirName(iwadfile); chex_deh = M_StringJoin(dirname, DIR_SEPARATOR_S, "chex.deh", NULL); free(dirname); // If the dehacked patch isn't found, try searching the WAD // search path instead. We might find it... if (!M_FileExists(chex_deh)) { free(chex_deh); chex_deh = D_FindWADByName("chex.deh"); } // Still not found? if (chex_deh == NULL) { I_Error("Unable to find Chex Quest dehacked file (chex.deh).\n" "The dehacked file is required in order to emulate\n" "chex.exe correctly. It can be found in your nearest\n" "/idgames repository mirror at:\n\n" " utils/exe_edit/patches/chexdeh.zip"); } if (!DEH_LoadFile(chex_deh)) { I_Error("Failed to load chex.deh needed for emulating chex.exe."); } } } // [crispy] support loading SIGIL.WAD (and SIGIL_SHREDS.WAD) alongside DOOM.WAD static void LoadSigilWad(void) { int i; struct { const char *name; const char new_name[8]; } sigil_lumps [] = { {"CREDIT", "SIGCREDI"}, {"HELP1", "SIGHELP1"}, {"TITLEPIC", "SIGTITLE"}, {"DEHACKED", "SIG_DEH"}, {"DEMO1", "SIGDEMO1"}, {"DEMO2", "SIGDEMO2"}, {"DEMO3", "SIGDEMO3"}, {"DEMO4", "SIGDEMO4"}, {"D_INTER", "D_SIGINT"}, {"D_INTRO", "D_SIGTIT"}, }; const char *const texture_files[] = { "PNAMES", "TEXTURE1", "TEXTURE2", }; // [crispy] don't load SIGIL.wad if another PWAD already provides E5M1 i = W_CheckNumForName("E5M1"); if (i != -1) { return; } // [crispy] don't load SIGIL.wad if SIGIL_COMPAT.wad is already loaded i = W_CheckNumForName("E3M1"); if (i != -1 && !strncasecmp(W_WadNameForLump(lumpinfo[i]), "SIGIL_COMPAT", 12)) { return; } // [crispy] don't load SIGIL.wad if another PWAD already modifies the texture files for (i = 0; i < arrlen(texture_files); i++) { int j; j = W_CheckNumForName(texture_files[i]); if (j != -1 && !W_IsIWADLump(lumpinfo[j])) { return; } } if (gameversion == exe_ultimate) { const char *const sigil_wads[] = { "SIGIL_v1_21.wad", "SIGIL_v1_2.wad", "SIGIL.wad" }; char *sigil_wad = NULL, *sigil_shreds = NULL; char *dirname; dirname = M_DirName(iwadfile); sigil_shreds = M_StringJoin(dirname, DIR_SEPARATOR_S, "SIGIL_SHREDS.wad", NULL); // [crispy] load SIGIL.WAD for (i = 0; i < arrlen(sigil_wads); i++) { sigil_wad = M_StringJoin(dirname, DIR_SEPARATOR_S, sigil_wads[i], NULL); if (M_FileExists(sigil_wad)) { break; } free(sigil_wad); sigil_wad = D_FindWADByName(sigil_wads[i]); if (sigil_wad) { break; } } free(dirname); if (sigil_wad == NULL) { free(sigil_shreds); return; } printf(" [expansion]"); D_AddFile(sigil_wad); free(sigil_wad); // [crispy] load SIGIL_SHREDS.WAD if (!M_FileExists(sigil_shreds)) { free(sigil_shreds); sigil_shreds = D_FindWADByName("SIGIL_SHREDS.wad"); } if (sigil_shreds != NULL) { printf(" [expansion]"); D_AddFile(sigil_shreds); free(sigil_shreds); } // [crispy] rename intrusive SIGIL_SHREDS.wad music lumps out of the way for (i = 0; i < arrlen(sigil_lumps); i++) { int j; // [crispy] skip non-music lumps if (strncasecmp(sigil_lumps[i].name, "D_", 2)) { continue; } j = W_CheckNumForName(sigil_lumps[i].name); if (j != -1 && !strncasecmp(W_WadNameForLump(lumpinfo[j]), "SIGIL_SHREDS", 12)) { memcpy(lumpinfo[j]->name, sigil_lumps[i].new_name, 8); } } // [crispy] rename intrusive SIGIL.wad graphics, demos and music lumps out of the way for (i = 0; i < arrlen(sigil_lumps); i++) { int j; j = W_CheckNumForName(sigil_lumps[i].name); if (j != -1 && !strncasecmp(W_WadNameForLump(lumpinfo[j]), "SIGIL", 5)) { memcpy(lumpinfo[j]->name, sigil_lumps[i].new_name, 8); } } // [crispy] regenerate the hashtable W_GenerateHashTable(); } } // [crispy] support loading NERVE.WAD alongside DOOM2.WAD static void LoadNerveWad(void) { int i, j, k; if (gamemission != doom2) return; if ((i = W_GetNumForName("map01")) != -1 && (j = W_GetNumForName("map09")) != -1 && !strcasecmp(W_WadNameForLump(lumpinfo[i]), "nerve.wad") && !strcasecmp(W_WadNameForLump(lumpinfo[j]), "nerve.wad")) { gamemission = pack_nerve; DEH_AddStringReplacement ("TITLEPIC", "INTERPIC"); } else // [crispy] The "New Game -> Which Expansion" menu is only shown if the // menu graphics lumps are available and (a) if they are from the IWAD // and that is the BFG Edition DOOM2.WAD or (b) if they are from a PWAD. if ((i = W_CheckNumForName("M_EPI1")) != -1 && (j = W_CheckNumForName("M_EPI2")) != -1 && (k = W_CheckNumForName("M_EPISOD")) != -1 && (gamevariant == bfgedition || (!W_IsIWADLump(lumpinfo[i]) && !W_IsIWADLump(lumpinfo[j]) && !W_IsIWADLump(lumpinfo[k])))) { if (strrchr(iwadfile, DIR_SEPARATOR) != NULL) { char *dir; dir = M_DirName(iwadfile); nervewadfile = M_StringJoin(dir, DIR_SEPARATOR_S, "nerve.wad", NULL); free(dir); } else { nervewadfile = M_StringDuplicate("nerve.wad"); } if (!M_FileExists(nervewadfile)) { free(nervewadfile); nervewadfile = D_FindWADByName("nerve.wad"); } if (nervewadfile == NULL) { return; } printf(" [expansion]"); D_AddFile(nervewadfile); // [crispy] rename level name patch lumps out of the way for (i = 0; i < 9; i++) { char lumpname[9]; M_snprintf (lumpname, 9, "CWILV%2.2d", i); lumpinfo[W_GetNumForName(lumpname)]->name[0] = 'N'; } // [crispy] regenerate the hashtable W_GenerateHashTable(); } } // [crispy] support loading MASTERLEVELS.WAD alongside DOOM2.WAD static void LoadMasterlevelsWad(void) { int i, j; if (gamemission != doom2) return; if ((i = W_GetNumForName("map01")) != -1 && (j = W_GetNumForName("map21")) != -1 && !strcasecmp(W_WadNameForLump(lumpinfo[i]), "masterlevels.wad") && !strcasecmp(W_WadNameForLump(lumpinfo[j]), "masterlevels.wad")) { gamemission = pack_master; } } static void G_CheckDemoStatusAtExit (void) { G_CheckDemoStatus(); } // // D_DoomMain // void D_DoomMain (void) { int p; char file[256]; char demolumpname[9]; int numiwadlumps; I_AtExit(D_Endoom, false); // print banner I_PrintBanner(PACKAGE_STRING); DEH_printf("Z_Init: Init zone memory allocation daemon. \n"); Z_Init (); //! // @category net // // Start a dedicated server, routing packets but not participating // in the game itself. // if (M_CheckParm("-dedicated") > 0) { printf("Dedicated server mode.\n"); NET_DedicatedServer(); // Never returns } //! // @category net // // Query the Internet master server for a global list of active // servers. // if (M_CheckParm("-search")) { NET_MasterQuery(); exit(0); } //! // @arg
// @category net // // Query the status of the server running on the given IP // address. // p = M_CheckParmWithArgs("-query", 1); if (p) { NET_QueryAddress(myargv[p+1]); exit(0); } //! // @category net // // Search the local LAN for running servers. // if (M_CheckParm("-localsearch")) { NET_LANQuery(); exit(0); } //! // @category game // @vanilla // // Disable monsters. // nomonsters = M_CheckParm ("-nomonsters"); //! // @category game // @vanilla // // Monsters respawn after being killed. // respawnparm = M_CheckParm ("-respawn"); //! // @category game // @vanilla // // Monsters move faster. // fastparm = M_CheckParm ("-fast"); //! // @vanilla // // Developer mode. F1 saves a screenshot in the current working // directory. // devparm = M_CheckParm ("-devparm"); I_DisplayFPSDots(devparm); //! // @category net // @vanilla // // Start a deathmatch game. // if (M_CheckParm ("-deathmatch")) deathmatch = 1; //! // @category net // @vanilla // // Start a deathmatch 2.0 game. Weapons do not stay in place and // all items respawn after 30 seconds. // if (M_CheckParm ("-altdeath")) deathmatch = 2; //! // @category net // @vanilla // // Start a deathmatch 3.0 game. Weapons stay in place and // all items respawn after 30 seconds. // if (M_CheckParm ("-dm3")) deathmatch = 3; if (devparm) DEH_printf(D_DEVSTR); // find which dir to use for config files #ifdef _WIN32 //! // @category obscure // @platform windows // @vanilla // // Save configuration data and savegames in c:\doomdata, // allowing play from CD. // if (M_ParmExists("-cdrom")) { printf(D_CDROM); M_SetConfigDir("c:\\doomdata\\"); } else #endif { // Auto-detect the configuration dir. M_SetConfigDir(NULL); } //! // @category game // @arg // @vanilla // // Turbo mode. The player's speed is multiplied by x%. If unspecified, // x defaults to 200. Values are rounded up to 10 and down to 400. // if ( (p=M_CheckParm ("-turbo")) ) { int scale = 200; extern int forwardmove[2]; extern int sidemove[2]; if (p 400) scale = 400; DEH_printf("turbo scale: %i%%\n", scale); forwardmove[0] = forwardmove[0]*scale/100; forwardmove[1] = forwardmove[1]*scale/100; sidemove[0] = sidemove[0]*scale/100; sidemove[1] = sidemove[1]*scale/100; } // init subsystems DEH_printf("V_Init: allocate screens.\n"); V_Init (); // Load configuration files before initialising other subsystems. DEH_printf("M_LoadDefaults: Load system defaults.\n"); M_SetConfigFilenames("default.cfg", PROGRAM_PREFIX "doom.cfg"); D_BindVariables(); M_LoadDefaults(); // Save configuration at exit. I_AtExit(M_SaveDefaults, true); // [crispy] always save configuration at exit // Find main IWAD file and load it. iwadfile = D_FindIWAD(IWAD_MASK_DOOM, &gamemission); // None found? if (iwadfile == NULL) { I_Error("Game mode indeterminate. No IWAD file was found. Try\n" "specifying one with the '-iwad' command line parameter.\n"); } modifiedgame = false; DEH_printf("W_Init: Init WADfiles.\n"); D_AddFile(iwadfile); numiwadlumps = numlumps; W_CheckCorrectIWAD(doom); // Now that we've loaded the IWAD, we can figure out what gamemission // we're playing and which version of Vanilla Doom we need to emulate. D_IdentifyVersion(); InitGameVersion(); // Check which IWAD variant we are using. if (W_CheckNumForName("FREEDOOM") >= 0) { if (W_CheckNumForName("FREEDM") >= 0) { gamevariant = freedm; } else { gamevariant = freedoom; } } else if (W_CheckNumForName("DMENUPIC") >= 0) { gamevariant = bfgedition; } //! // @category mod // // Disable automatic loading of Dehacked patches for certain // IWAD files. // if (!M_ParmExists("-nodeh")) { // Some IWADs have dehacked patches that need to be loaded for // them to be played properly. LoadIwadDeh(); } // Doom 3: BFG Edition includes modified versions of the classic // IWADs which can be identified by an additional DMENUPIC lump. // Furthermore, the M_GDHIGH lumps have been modified in a way that // makes them incompatible to Vanilla Doom and the modified version // of doom2.wad is missing the TITLEPIC lump. // We specifically check for DMENUPIC here, before PWADs have been // loaded which could probably include a lump of that name. if (gamevariant == bfgedition) { printf("BFG Edition: Using workarounds as needed.\n"); // BFG Edition changes the names of the secret levels to // censor the Wolfenstein references. It also has an extra // secret level (MAP33). In Vanilla Doom (meaning the DOS // version), MAP33 overflows into the Plutonia level names // array, so HUSTR_33 is actually PHUSTR_1. DEH_AddStringReplacement(HUSTR_31, "level 31: idkfa"); DEH_AddStringReplacement(HUSTR_32, "level 32: keen"); DEH_AddStringReplacement(PHUSTR_1, "level 33: betray"); // The BFG edition doesn't have the "low detail" menu option (fair // enough). But bizarrely, it reuses the M_GDHIGH patch as a label // for the options menu (says "Fullscreen:"). Why the perpetrators // couldn't just add a new graphic lump and had to reuse this one, // I don't know. // // The end result is that M_GDHIGH is too wide and causes the game // to crash. As a workaround to get a minimum level of support for // the BFG edition IWADs, use the "ON"/"OFF" graphics instead. DEH_AddStringReplacement("M_GDHIGH", "M_MSGON"); DEH_AddStringReplacement("M_GDLOW", "M_MSGOFF"); // The BFG edition's "Screen Size:" graphic has also been changed // to say "Gamepad:". Fortunately, it (along with the original // Doom IWADs) has an unused graphic that says "Display". So we // can swap this in instead, and it kind of makes sense. DEH_AddStringReplacement("M_SCRNSZ", "M_DISP"); } //! // @category mod // // Disable auto-loading of .wad and .deh files. // if (!M_ParmExists("-noautoload") && gamemode != shareware) { char *autoload_dir; // common auto-loaded files for all Doom flavors if (gamemission < pack_chex) { autoload_dir = M_GetAutoloadDir("doom-all"); DEH_AutoLoadPatches(autoload_dir); W_AutoLoadWADs(autoload_dir); free(autoload_dir); } // auto-loaded files per IWAD autoload_dir = M_GetAutoloadDir(D_SaveGameIWADName(gamemission)); DEH_AutoLoadPatches(autoload_dir); W_AutoLoadWADs(autoload_dir); free(autoload_dir); } // Load Dehacked patches specified on the command line with -deh. // Note that there's a very careful and deliberate ordering to how // Dehacked patches are loaded. The order we use is: // 1. IWAD dehacked patches. // 2. Command line dehacked patches specified with -deh. // 3. PWAD dehacked patches in DEHACKED lumps. DEH_ParseCommandLine(); // Load PWAD files. modifiedgame = W_ParseCommandLine(); //! // @arg // @category mod // // [crispy] experimental feature: in conjunction with -merge // merges PWADs into the main IWAD and writes the merged data into // p = M_CheckParm("-mergedump"); if (p) { p = M_CheckParmWithArgs("-mergedump", 1); if (p) { int merged; if (M_StringEndsWith(myargv[p+1], ".wad")) { M_StringCopy(file, myargv[p+1], sizeof(file)); } else { DEH_snprintf(file, sizeof(file), "%s.wad", myargv[p+1]); } merged = W_MergeDump(file); I_Error("W_MergeDump: Merged %d lumps into file '%s'.", merged, file); } else { I_Error("W_MergeDump: The '-mergedump' parameter requires an argument."); } } // Debug: // W_PrintDirectory(); //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp. // p = M_CheckParmWithArgs ("-playdemo", 1); if (!p) { //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp, determining the framerate // of the screen. // p = M_CheckParmWithArgs("-timedemo", 1); } if (p) { char *uc_filename = strdup(myargv[p + 1]); M_ForceUppercase(uc_filename); // With Vanilla you have to specify the file without extension, // but make that optional. if (M_StringEndsWith(uc_filename, ".LMP")) { M_StringCopy(file, myargv[p + 1], sizeof(file)); } else { DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p+1]); } free(uc_filename); if (D_AddFile(file)) { M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name, sizeof(demolumpname)); } else { // If file failed to load, still continue trying to play // the demo in the same way as Vanilla Doom. This makes // tricks like "-playdemo demo1" possible. M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname)); } printf("Playing demo %s.\n", file); } I_AtExit(G_CheckDemoStatusAtExit, true); // Generate the WAD hash table. Speed things up a bit. W_GenerateHashTable(); // [crispy] allow overriding of special-casing if (!M_ParmExists("-noautoload") && gamemode != shareware) { LoadMasterlevelsWad(); LoadNerveWad(); LoadSigilWad(); } // Load DEHACKED lumps from WAD files - but only if we give the right // command line parameter. //! // @category mod // // Load Dehacked patches from DEHACKED lumps contained in one of the // loaded PWAD files. // // [crispy] load DEHACKED lumps by default, but allow overriding if (!M_ParmExists("-nodehlump") && !M_ParmExists("-nodeh")) { int i, loaded = 0; for (i = numiwadlumps; i < numlumps; ++i) { if (!strncmp(lumpinfo[i]->name, "DEHACKED", 8)) { DEH_LoadLump(i, true, true); // [crispy] allow long, allow error loaded++; } } printf(" loaded %i DEHACKED lumps from PWAD files.\n", loaded); } // Set the gamedescription string. This is only possible now that // we've finished loading Dehacked patches. D_SetGameDescription(); savegamedir = M_GetSaveGameDir(D_SaveGameIWADName(gamemission)); // Check for -file in shareware if (modifiedgame && (gamevariant != freedoom)) { // These are the lumps that will be checked in IWAD, // if any one is not present, execution will be aborted. char name[23][8]= { "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9", "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9", "dphoof","bfgga0","heada1","cybra1","spida1d1" }; int i; if ( gamemode == shareware) I_Error(DEH_String("\nYou cannot -file with the shareware " "version. Register!")); // Check for fake IWAD with right name, // but w/o all the lumps of the registered version. if (gamemode == registered) for (i = 0;i < 23; i++) if (W_CheckNumForName(name[i])<0) I_Error(DEH_String("\nThis is not the registered version.")); } // [crispy] disable meaningless warning, we always use "-merge" anyway #if 0 if (W_CheckNumForName("SS_START") >= 0 || W_CheckNumForName("FF_END") >= 0) { I_PrintDivider(); printf(" WARNING: The loaded WAD file contains modified sprites or\n" " floor textures. You may want to use the '-merge' command\n" " line option instead of '-file'.\n"); } #endif I_PrintStartupBanner(gamedescription); PrintDehackedBanners(); DEH_printf("I_Init: Setting up machine state.\n"); I_CheckIsScreensaver(); I_InitTimer(); I_InitJoystick(); I_InitSound(true); I_InitMusic(); // [crispy] check for SSG resources crispy->havessg = ( gamemode == commercial || ( W_CheckNumForName("sht2a0") != -1 && // [crispy] wielding/firing sprite sequence I_GetSfxLumpNum(&S_sfx[sfx_dshtgn]) != -1 && // [crispy] firing sound I_GetSfxLumpNum(&S_sfx[sfx_dbopn]) != -1 && // [crispy] opening sound I_GetSfxLumpNum(&S_sfx[sfx_dbload]) != -1 && // [crispy] reloading sound I_GetSfxLumpNum(&S_sfx[sfx_dbcls]) != -1 // [crispy] closing sound ) ); // [crispy] check for presence of a 5th episode crispy->haved1e5 = (gameversion == exe_ultimate) && (W_CheckNumForName("m_epi5") != -1) && (W_CheckNumForName("e5m1") != -1) && (W_CheckNumForName("wilv40") != -1); // [crispy] check for presence of E1M10 crispy->havee1m10 = (gamemode == retail) && (W_CheckNumForName("e1m10") != -1) && (W_CheckNumForName("sewers") != -1); // [crispy] check for presence of MAP33 crispy->havemap33 = (gamemode == commercial) && (W_CheckNumForName("map33") != -1) && (W_CheckNumForName("cwilv32") != -1); // [crispy] change level name for MAP33 if not already changed if (crispy->havemap33 && !DEH_HasStringReplacement(PHUSTR_1)) { DEH_AddStringReplacement(PHUSTR_1, "level 33: betray"); } printf ("NET_Init: Init network subsystem.\n"); NET_Init (); // Initial netgame startup. Connect to server etc. D_ConnectNetGame(); // get skill / episode / map from parms startskill = sk_medium; startepisode = 1; startmap = 1; autostart = false; //! // @category game // @arg // @vanilla // // Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of // 0 disables all monsters. // p = M_CheckParmWithArgs("-skill", 1); if (p) { startskill = myargv[p+1][0]-'1'; autostart = true; } //! // @category game // @arg // @vanilla // // Start playing on episode n (1-4) // p = M_CheckParmWithArgs("-episode", 1); if (p) { startepisode = myargv[p+1][0]-'0'; startmap = 1; autostart = true; } timelimit = 0; //! // @arg // @category net // @vanilla // // For multiplayer games: exit each level after n minutes. // p = M_CheckParmWithArgs("-timer", 1); if (p) { timelimit = atoi(myargv[p+1]); } //! // @category net // @vanilla // // Austin Virtual Gaming: end levels after 20 minutes. // p = M_CheckParm ("-avg"); if (p) { timelimit = 20; } //! // @category game // @arg [ | ] // @vanilla // // Start a game immediately, warping to ExMy (Doom 1) or MAPxy // (Doom 2) // p = M_CheckParmWithArgs("-warp", 1); if (p) { if (gamemode == commercial) startmap = atoi (myargv[p+1]); else { startepisode = myargv[p+1][0]-'0'; // [crispy] only if second argument is not another option if (p + 2 < myargc && myargv[p+2][0] != '-') { startmap = myargv[p+2][0]-'0'; } else { // [crispy] allow second digit without space in between for Doom 1 startmap = myargv[p+1][1]-'0'; } } autostart = true; // [crispy] if used with -playdemo, fast-forward demo up to the desired map crispy->demowarp = startmap; } // Undocumented: // Invoked by setup to test the controls. p = M_CheckParm("-testcontrols"); if (p > 0) { startepisode = 1; startmap = 1; autostart = true; testcontrols = true; } // [crispy] port level flipping feature over from Strawberry Doom #ifdef ENABLE_APRIL_1ST_JOKE { time_t curtime = time(NULL); struct tm *curtm = localtime(&curtime); if (curtm && curtm->tm_mon == 3 && curtm->tm_mday == 1) crispy->fliplevels = true; } #endif p = M_CheckParm("-fliplevels"); if (p > 0) { crispy->fliplevels = !crispy->fliplevels; crispy->flipweapons = !crispy->flipweapons; } p = M_CheckParm("-flipweapons"); if (p > 0) { crispy->flipweapons = !crispy->flipweapons; } // Check for load game parameter // We do this here and save the slot number, so that the network code // can override it or send the load slot to other players. //! // @category game // @arg // @vanilla // // Load the game in slot s. // p = M_CheckParmWithArgs("-loadgame", 1); if (p) { startloadgame = atoi(myargv[p+1]); } else { // Not loading a game startloadgame = -1; } DEH_printf("M_Init: Init miscellaneous info.\n"); M_Init (); DEH_printf("R_Init: Init DOOM refresh daemon - "); R_Init (); DEH_printf("\nP_Init: Init Playloop state.\n"); P_Init (); DEH_printf("S_Init: Setting up sound.\n"); S_Init (sfxVolume * 8, musicVolume * 8); DEH_printf("D_CheckNetGame: Checking network game status.\n"); D_CheckNetGame (); PrintGameVersion(); DEH_printf("HU_Init: Setting up heads up display.\n"); HU_Init (); DEH_printf("ST_Init: Init status bar.\n"); ST_Init (); // If Doom II without a MAP01 lump, this is a store demo. // Moved this here so that MAP01 isn't constantly looked up // in the main loop. if (gamemode == commercial && W_CheckNumForName("map01") < 0) storedemo = true; if (M_CheckParmWithArgs("-statdump", 1)) { I_AtExit(StatDump, true); DEH_printf("External statistics registered.\n"); } //! // @arg // @category demo // @vanilla // // Record a demo named x.lmp. // p = M_CheckParmWithArgs("-record", 1); if (p) { G_RecordDemo (myargv[p+1]); autostart = true; } p = M_CheckParmWithArgs("-playdemo", 1); if (p) { singledemo = true; // quit after one demo G_DeferedPlayDemo (demolumpname); D_DoomLoop (); // never returns } crispy->demowarp = 0; // [crispy] we don't play a demo, so don't skip maps p = M_CheckParmWithArgs("-timedemo", 1); if (p) { G_TimeDemo (demolumpname); D_DoomLoop (); // never returns } if (startloadgame >= 0) { M_StringCopy(file, P_SaveGameFile(startloadgame), sizeof(file)); G_LoadGame(file); } if (gameaction != ga_loadgame ) { if (autostart || netgame) G_InitNew (startskill, startepisode, startmap); else D_StartTitle (); // start up intro loop } D_DoomLoop (); // never returns } crispy-doom-crispy-doom-5.6.4/src/doom/d_main.h000066400000000000000000000017671360717211000213740ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System specific interface stuff. // #ifndef __D_MAIN__ #define __D_MAIN__ #include "doomdef.h" // Read events from all input devices void D_ProcessEvents (void); // // BASE LEVEL // void D_PageTicker (void); void D_PageDrawer (void); void D_AdvanceDemo (void); void D_DoAdvanceDemo (void); void D_StartTitle (void); // // GLOBAL VARIABLES // extern gameaction_t gameaction; #endif crispy-doom-crispy-doom-5.6.4/src/doom/d_net.c000066400000000000000000000155541360717211000212300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM Network game communication and protocol, // all OS independend parts. // #include #include "d_main.h" #include "m_argv.h" #include "m_menu.h" #include "m_misc.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "g_game.h" #include "doomdef.h" #include "doomstat.h" #include "w_checksum.h" #include "w_wad.h" #include "deh_main.h" #include "d_loop.h" ticcmd_t *netcmds; // Called when a player leaves the game static void PlayerQuitGame(player_t *player) { static char exitmsg[80]; unsigned int player_num; player_num = player - players; // Do this the same way as Vanilla Doom does, to allow dehacked // replacements of this message M_StringCopy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg)); exitmsg[7] += player_num; playeringame[player_num] = false; players[consoleplayer].message = exitmsg; // [crispy] don't interpolate players who left the game player->mo->interp = false; // TODO: check if it is sensible to do this: if (demorecording) { G_CheckDemoStatus (); } } static void RunTic(ticcmd_t *cmds, boolean *ingame) { extern boolean advancedemo; unsigned int i; // Check for player quits. for (i = 0; i < MAXPLAYERS; ++i) { if (!demoplayback && playeringame[i] && !ingame[i]) { PlayerQuitGame(&players[i]); } } netcmds = cmds; // check that there are players in the game. if not, we cannot // run a tic. if (advancedemo) D_DoAdvanceDemo (); G_Ticker (); } static loop_interface_t doom_loop_interface = { D_ProcessEvents, G_BuildTiccmd, RunTic, M_Ticker }; // Load game settings from the specified structure and // set global variables. static void LoadGameSettings(net_gamesettings_t *settings) { unsigned int i; deathmatch = settings->deathmatch; startepisode = settings->episode; startmap = settings->map; startskill = settings->skill; startloadgame = settings->loadgame; lowres_turn = settings->lowres_turn; nomonsters = settings->nomonsters; fastparm = settings->fast_monsters; respawnparm = settings->respawn_monsters; timelimit = settings->timelimit; consoleplayer = settings->consoleplayer; if (lowres_turn) { printf("NOTE: Turning resolution is reduced; this is probably " "because there is a client recording a Vanilla demo.\n"); } for (i = 0; i < MAXPLAYERS; ++i) { playeringame[i] = i < settings->num_players; } } // Save the game settings from global variables to the specified // game settings structure. static void SaveGameSettings(net_gamesettings_t *settings) { // Fill in game settings structure with appropriate parameters // for the new game settings->deathmatch = deathmatch; settings->episode = startepisode; settings->map = startmap; settings->skill = startskill; settings->loadgame = startloadgame; settings->gameversion = gameversion; settings->nomonsters = nomonsters; settings->fast_monsters = fastparm; settings->respawn_monsters = respawnparm; settings->timelimit = timelimit; settings->lowres_turn = (M_ParmExists("-record") && !M_ParmExists("-longtics")) || M_ParmExists("-shorttics"); } static void InitConnectData(net_connect_data_t *connect_data) { boolean shorttics; connect_data->max_players = MAXPLAYERS; connect_data->drone = false; //! // @category net // // Run as the left screen in three screen mode. // if (M_CheckParm("-left") > 0) { viewangleoffset = ANG90; connect_data->drone = true; } //! // @category net // // Run as the right screen in three screen mode. // if (M_CheckParm("-right") > 0) { viewangleoffset = ANG270; connect_data->drone = true; } // // Connect data // // Game type fields: connect_data->gamemode = gamemode; connect_data->gamemission = gamemission; //! // @category demo // // Play with low turning resolution to emulate demo recording. // shorttics = M_ParmExists("-shorttics"); // Are we recording a demo? Possibly set lowres turn mode connect_data->lowres_turn = (M_ParmExists("-record") && !M_ParmExists("-longtics")) || shorttics; // Read checksums of our WAD directory and dehacked information W_Checksum(connect_data->wad_sha1sum); DEH_Checksum(connect_data->deh_sha1sum); // Are we playing with the Freedoom IWAD? connect_data->is_freedoom = W_CheckNumForName("FREEDOOM") >= 0; } void D_ConnectNetGame(void) { net_connect_data_t connect_data; InitConnectData(&connect_data); netgame = D_InitNetGame(&connect_data); //! // @category net // // Start the game playing as though in a netgame with a single // player. This can also be used to play back single player netgame // demos. // if (M_CheckParm("-solo-net") > 0) { netgame = true; } } // // D_CheckNetGame // Works out player numbers among the net participants // void D_CheckNetGame (void) { net_gamesettings_t settings; if (netgame) { autostart = true; } D_RegisterLoopCallbacks(&doom_loop_interface); SaveGameSettings(&settings); D_StartNetGame(&settings, NULL); LoadGameSettings(&settings); DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode); DEH_printf("player %i of %i (%i nodes)\n", consoleplayer+1, settings.num_players, settings.num_players); // Show players here; the server might have specified a time limit if (timelimit > 0 && deathmatch) { // Gross hack to work like Vanilla: if (timelimit == 20 && M_CheckParm("-avg")) { DEH_printf("Austin Virtual Gaming: Levels will end " "after 20 minutes\n"); } else { DEH_printf("Levels will end after %d minute", timelimit); if (timelimit > 1) printf("s"); printf(".\n"); } } } crispy-doom-crispy-doom-5.6.4/src/doom/d_player.h000066400000000000000000000124141360717211000217330ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __D_PLAYER__ #define __D_PLAYER__ // The player data structure depends on a number // of other structs: items (internal inventory), // animation states (closely tied to the sprites // used to represent them, unfortunately). #include "d_items.h" #include "p_pspr.h" // In addition, the player is just a special // case of the generic moving object/actor. #include "p_mobj.h" // Finally, for odd reasons, the player input // is buffered within the player data struct, // as commands per game tick. #include "d_ticcmd.h" #include "net_defs.h" // // 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, // [crispy] monsters don't target CF_NOTARGET = 8 } 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 + 3]; // [crispy] showfps and mapcoords are now "powers" boolean cards[NUMCARDS]; boolean tryopen[NUMCARDS]; // [crispy] blinking key or skull in the status bar boolean backpack; // Frags, kills of other players. int frags[MAXPLAYERS]; weapontype_t readyweapon; // Is wp_nochange if not changing. weapontype_t pendingweapon; int weaponowned[NUMWEAPONS]; int ammo[NUMAMMO]; int maxammo[NUMAMMO]; // True if button down last tic. int attackdown; int usedown; // Bit flags, for cheats and debug. // See cheat_t, above. int cheats; // Refired shots are less accurate. int refire; // For intermission stats. int killcount; int itemcount; int secretcount; // Hint messages. const char *message; // For screen flashing (red or bright). int damagecount; int bonuscount; // Who did damage (NULL for floors/ceilings). mobj_t* attacker; // So gun flashes light up areas. int extralight; // Current PLAYPAL, ??? // can be set to REDCOLORMAP for pain, etc. int fixedcolormap; // Player skin colorshift, // 0-3 for which color to draw player. int colormap; // Overlay view sprites (gun, etc). pspdef_t psprites[NUMPSPRITES]; // True if secret level has been done. boolean didsecret; // [AM] Previous position of viewz before think. // Used to interpolate between camera positions. angle_t oldviewz; // [crispy] show centered "Secret Revealed!" message char *centermessage; // [crispy] free look / mouse look int lookdir, oldlookdir; boolean centering; // [crispy] jumping unsigned int jumpTics; // [crispy] weapon recoil pitch fixed_t recoilpitch, oldrecoilpitch; // [crispy] weapon sound source mobj_t *so; // [crispy] squat down weapon sprite fixed_t psp_dy_max; // [crispy] variable player view bob fixed_t bob2; } player_t; // // INTERMISSION // Structure passed e.g. to WI_Start(wb) // typedef struct { boolean in; // whether the player is in game // Player stats, kills, collected items etc. int skills; int sitems; int ssecret; int stime; int frags[4]; int score; // current score on entry, modified on return } wbplayerstruct_t; typedef struct { int epsd; // episode # (0-2) // if true, splash the secret level boolean didsecret; // previous and next levels, origin 0 int last; int next; int maxkills; int maxitems; int maxsecret; int maxfrags; // the par time int partime; // index of this player in game int pnum; wbplayerstruct_t plyr[MAXPLAYERS]; // [crispy] CPhipps - total game time for completed levels so far int totaltimes; } wbstartstruct_t; #endif crispy-doom-crispy-doom-5.6.4/src/doom/d_textur.h000066400000000000000000000016401360717211000217710ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Typedefs related to to textures etc., // isolated here to make it easier separating modules. // #ifndef __D_TEXTUR__ #define __D_TEXTUR__ #include "doomtype.h" // // Flats? // // a pic is an unmasked block of pixels typedef struct { byte width; byte height; byte data; } pic_t; #endif crispy-doom-crispy-doom-5.6.4/src/doom/d_think.h000066400000000000000000000032221360717211000215510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // MapObj data. Map Objects or mobjs are actors, entities, // thinker, take-your-pick... anything that moves, acts, or // suffers state changes of more or less violent nature. // #ifndef __D_THINK__ #define __D_THINK__ // // Experimental stuff. // To compile this as "ANSI C with classes" // we will need to handle the various // action functions cleanly. // typedef void (*actionf_v)(); typedef void (*actionf_p1)( void* ); typedef void (*actionf_p2)( void*, void* ); typedef void (*actionf_p3)( void*, void*, void* ); // [crispy] let pspr action pointers get called from mobj states typedef union { actionf_v acv; actionf_p1 acp1; actionf_p2 acp2; actionf_p3 acp3; // [crispy] let pspr action pointers get called from mobj states } actionf_t; // Historically, "think_t" is yet another // function pointer to a routine to handle // an actor. typedef actionf_t think_t; // Doubly linked list of actors. typedef struct thinker_s { struct thinker_s* prev; struct thinker_s* next; think_t function; } thinker_t; #endif crispy-doom-crispy-doom-5.6.4/src/doom/deh_ammo.c000066400000000000000000000044271360717211000217050ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Ammo" sections in dehacked files // #include #include #include #include "doomdef.h" #include "doomtype.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "p_local.h" static void *DEH_AmmoStart(deh_context_t *context, char *line) { int ammo_number = 0; if (sscanf(line, "Ammo %i", &ammo_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (ammo_number < 0 || ammo_number >= NUMAMMO) { DEH_Warning(context, "Invalid ammo number: %i", ammo_number); return NULL; } return &maxammo[ammo_number]; } static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; int ivalue; int ammo_number; if (tag == NULL) return; ammo_number = ((int *) tag) - maxammo; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); // maxammo if (!strcasecmp(variable_name, "Per ammo")) clipammo[ammo_number] = ivalue; else if (!strcasecmp(variable_name, "Max ammo")) maxammo[ammo_number] = ivalue; else { DEH_Warning(context, "Field named '%s' not found", variable_name); } } static void DEH_AmmoSHA1Hash(sha1_context_t *context) { int i; for (i=0; i #include #include #include "m_misc.h" #include "deh_io.h" #include "deh_main.h" static boolean bex_nested = false; static void *DEH_BEXInclStart(deh_context_t *context, char *line) { char *deh_file, *inc_file, *try_path; extern boolean bex_notext; if (!DEH_FileName(context)) { DEH_Warning(context, "DEHACKED lumps may not include files"); return NULL; } deh_file = DEH_FileName(context); if (bex_nested) { DEH_Warning(context, "Included files may not include other files"); return NULL; } inc_file = malloc(strlen(line) + 1); if (sscanf(line, "INCLUDE NOTEXT %32s", inc_file) == 1) { bex_notext = true; } else if (sscanf(line, "INCLUDE %32s", inc_file) == 1) { // well, fine } else { DEH_Warning(context, "Parse error on section start"); free(inc_file); return NULL; } // first, try loading the file right away try_path = inc_file; if (!M_FileExists(try_path)) { // second, try loading the file in the directory of the current file char *dir; dir = M_DirName(deh_file); try_path = M_StringJoin(dir, DIR_SEPARATOR_S, inc_file, NULL); free(dir); } bex_nested = true; if (!M_FileExists(try_path) || !DEH_LoadFile(try_path)) { DEH_Warning(context, "Could not include \"%s\"", inc_file); } bex_nested = false; bex_notext = false; if (try_path != inc_file) free(try_path); free(inc_file); return NULL; } static void DEH_BEXInclParseLine(deh_context_t *context, char *line, void *tag) { // not used } deh_section_t deh_section_bexincl = { "INCLUDE", NULL, DEH_BEXInclStart, DEH_BEXInclParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/doom/deh_bexpars.c000066400000000000000000000034671360717211000224230ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2014 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses [PARS] sections in BEX files // #include #include #include "deh_bexpars.h" #include "deh_io.h" int bex_pars[6][10] = {{0}}; int bex_cpars[32] = {0}; static void *DEH_BEXParsStart(deh_context_t *context, char *line) { char s[7]; if (sscanf(line, "%6s", s) == 0 || strcmp("[PARS]", s)) { DEH_Warning(context, "Parse error on section start"); } return NULL; } static void DEH_BEXParsParseLine(deh_context_t *context, char *line, void *tag) { int episode, map, partime; if (sscanf(line, "par %32d %32d %32d", &episode, &map, &partime) == 3) { if (episode >= 1 && episode <= 5 && map >= 1 && map <= 9) bex_pars[episode][map] = partime; else { DEH_Warning(context, "Invalid episode or map: E%dM%d", episode, map); return; } } else if (sscanf(line, "par %32d %32d", &map, &partime) == 2) { if (map >= 1 && map <= 32) bex_cpars[map-1] = partime; else { DEH_Warning(context, "Invalid map: MAP%02d", map); return; } } else { DEH_Warning(context, "Failed to parse assignment"); return; } } deh_section_t deh_section_bexpars = { "[PARS]", NULL, DEH_BEXParsStart, DEH_BEXParsParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/doom/deh_bexpars.h000066400000000000000000000014051360717211000224160ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2014-2019 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses [PARS] sections in BEX files // #ifndef DEH_BEXPARS_H #define DEH_BEXPARS_H extern int bex_pars[6][10]; extern int bex_cpars[32]; #endif /* #ifndef DEH_BEXPARS_H */ crispy-doom-crispy-doom-5.6.4/src/doom/deh_bexptr.c000066400000000000000000000172321360717211000222560ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2014 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses [CODEPTR] sections in BEX files // #include #include #include #include "info.h" #include "deh_io.h" #include "deh_main.h" extern void A_Light0(); extern void A_WeaponReady(); extern void A_Lower(); extern void A_Raise(); extern void A_Punch(); extern void A_ReFire(); extern void A_FirePistol(); extern void A_Light1(); extern void A_FireShotgun(); extern void A_Light2(); extern void A_FireShotgun2(); extern void A_CheckReload(); extern void A_OpenShotgun2(); extern void A_LoadShotgun2(); extern void A_CloseShotgun2(); extern void A_FireCGun(); extern void A_GunFlash(); extern void A_FireMissile(); extern void A_Saw(); extern void A_FirePlasma(); extern void A_BFGsound(); extern void A_FireBFG(); extern void A_BFGSpray(); extern void A_Explode(); extern void A_Pain(); extern void A_PlayerScream(); extern void A_Fall(); extern void A_XScream(); extern void A_Look(); extern void A_Chase(); extern void A_FaceTarget(); extern void A_PosAttack(); extern void A_Scream(); extern void A_SPosAttack(); extern void A_VileChase(); extern void A_VileStart(); extern void A_VileTarget(); extern void A_VileAttack(); extern void A_StartFire(); extern void A_Fire(); extern void A_FireCrackle(); extern void A_Tracer(); extern void A_SkelWhoosh(); extern void A_SkelFist(); extern void A_SkelMissile(); extern void A_FatRaise(); extern void A_FatAttack1(); extern void A_FatAttack2(); extern void A_FatAttack3(); extern void A_BossDeath(); extern void A_CPosAttack(); extern void A_CPosRefire(); extern void A_TroopAttack(); extern void A_SargAttack(); extern void A_HeadAttack(); extern void A_BruisAttack(); extern void A_SkullAttack(); extern void A_Metal(); extern void A_SpidRefire(); extern void A_BabyMetal(); extern void A_BspiAttack(); extern void A_Hoof(); extern void A_CyberAttack(); extern void A_PainAttack(); extern void A_PainDie(); extern void A_KeenDie(); extern void A_BrainPain(); extern void A_BrainScream(); extern void A_BrainDie(); extern void A_BrainAwake(); extern void A_BrainSpit(); extern void A_SpawnSound(); extern void A_SpawnFly(); extern void A_BrainExplode(); // [crispy] additional BOOM and MBF states, sprites and code pointers extern void A_Stop(); extern void A_Die(); extern void A_FireOldBFG(); extern void A_Detonate(); extern void A_Mushroom(); extern void A_BetaSkullAttack(); // [crispy] more MBF code pointers extern void A_Spawn(); extern void A_Turn(); extern void A_Face(); extern void A_Scratch(); extern void A_PlaySound(); extern void A_RandomJump(); extern void A_LineEffect(); typedef struct { const char *mnemonic; const actionf_t pointer; } bex_codeptr_t; static const bex_codeptr_t bex_codeptrtable[] = { {"Light0", {A_Light0}}, {"WeaponReady", {A_WeaponReady}}, {"Lower", {A_Lower}}, {"Raise", {A_Raise}}, {"Punch", {A_Punch}}, {"ReFire", {A_ReFire}}, {"FirePistol", {A_FirePistol}}, {"Light1", {A_Light1}}, {"FireShotgun", {A_FireShotgun}}, {"Light2", {A_Light2}}, {"FireShotgun2", {A_FireShotgun2}}, {"CheckReload", {A_CheckReload}}, {"OpenShotgun2", {A_OpenShotgun2}}, {"LoadShotgun2", {A_LoadShotgun2}}, {"CloseShotgun2", {A_CloseShotgun2}}, {"FireCGun", {A_FireCGun}}, {"GunFlash", {A_GunFlash}}, {"FireMissile", {A_FireMissile}}, {"Saw", {A_Saw}}, {"FirePlasma", {A_FirePlasma}}, {"BFGsound", {A_BFGsound}}, {"FireBFG", {A_FireBFG}}, {"BFGSpray", {A_BFGSpray}}, {"Explode", {A_Explode}}, {"Pain", {A_Pain}}, {"PlayerScream", {A_PlayerScream}}, {"Fall", {A_Fall}}, {"XScream", {A_XScream}}, {"Look", {A_Look}}, {"Chase", {A_Chase}}, {"FaceTarget", {A_FaceTarget}}, {"PosAttack", {A_PosAttack}}, {"Scream", {A_Scream}}, {"SPosAttack", {A_SPosAttack}}, {"VileChase", {A_VileChase}}, {"VileStart", {A_VileStart}}, {"VileTarget", {A_VileTarget}}, {"VileAttack", {A_VileAttack}}, {"StartFire", {A_StartFire}}, {"Fire", {A_Fire}}, {"FireCrackle", {A_FireCrackle}}, {"Tracer", {A_Tracer}}, {"SkelWhoosh", {A_SkelWhoosh}}, {"SkelFist", {A_SkelFist}}, {"SkelMissile", {A_SkelMissile}}, {"FatRaise", {A_FatRaise}}, {"FatAttack1", {A_FatAttack1}}, {"FatAttack2", {A_FatAttack2}}, {"FatAttack3", {A_FatAttack3}}, {"BossDeath", {A_BossDeath}}, {"CPosAttack", {A_CPosAttack}}, {"CPosRefire", {A_CPosRefire}}, {"TroopAttack", {A_TroopAttack}}, {"SargAttack", {A_SargAttack}}, {"HeadAttack", {A_HeadAttack}}, {"BruisAttack", {A_BruisAttack}}, {"SkullAttack", {A_SkullAttack}}, {"Metal", {A_Metal}}, {"SpidRefire", {A_SpidRefire}}, {"BabyMetal", {A_BabyMetal}}, {"BspiAttack", {A_BspiAttack}}, {"Hoof", {A_Hoof}}, {"CyberAttack", {A_CyberAttack}}, {"PainAttack", {A_PainAttack}}, {"PainDie", {A_PainDie}}, {"KeenDie", {A_KeenDie}}, {"BrainPain", {A_BrainPain}}, {"BrainScream", {A_BrainScream}}, {"BrainDie", {A_BrainDie}}, {"BrainAwake", {A_BrainAwake}}, {"BrainSpit", {A_BrainSpit}}, {"SpawnSound", {A_SpawnSound}}, {"SpawnFly", {A_SpawnFly}}, {"BrainExplode", {A_BrainExplode}}, // [crispy] additional BOOM and MBF states, sprites and code pointers {"Stop", {A_Stop}}, {"Die", {A_Die}}, {"FireOldBFG", {A_FireOldBFG}}, {"Detonate", {A_Detonate}}, {"Mushroom", {A_Mushroom}}, {"BetaSkullAttack", {A_BetaSkullAttack}}, // [crispy] more MBF code pointers {"Spawn", {A_Spawn}}, {"Turn", {A_Turn}}, {"Face", {A_Face}}, {"Scratch", {A_Scratch}}, {"PlaySound", {A_PlaySound}}, {"RandomJump", {A_RandomJump}}, {"LineEffect", {A_LineEffect}}, {"NULL", {NULL}}, }; extern actionf_t codeptrs[NUMSTATES]; static void *DEH_BEXPtrStart(deh_context_t *context, char *line) { char s[10]; if (sscanf(line, "%9s", s) == 0 || strcmp("[CODEPTR]", s)) { DEH_Warning(context, "Parse error on section start"); } return NULL; } static void DEH_BEXPtrParseLine(deh_context_t *context, char *line, void *tag) { state_t *state; char *variable_name, *value, frame_str[6]; int frame_number, i; // parse "FRAME nn = mnemonic", where // variable_name = "FRAME nn" and value = "mnemonic" if (!DEH_ParseAssignment(line, &variable_name, &value)) { DEH_Warning(context, "Failed to parse assignment: %s", line); return; } // parse "FRAME nn", where frame_number = "nn" if (sscanf(variable_name, "%5s %32d", frame_str, &frame_number) != 2 || strcasecmp(frame_str, "FRAME")) { DEH_Warning(context, "Failed to parse assignment: %s", variable_name); return; } if (frame_number < 0 || frame_number >= NUMSTATES) { DEH_Warning(context, "Invalid frame number: %i", frame_number); return; } state = (state_t *) &states[frame_number]; for (i = 0; i < arrlen(bex_codeptrtable); i++) { if (!strcasecmp(bex_codeptrtable[i].mnemonic, value)) { state->action = bex_codeptrtable[i].pointer; return; } } DEH_Warning(context, "Invalid mnemonic '%s'", value); } deh_section_t deh_section_bexptr = { "[CODEPTR]", NULL, DEH_BEXPtrStart, DEH_BEXPtrParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/doom/deh_bexstr.c000066400000000000000000000254241360717211000222630ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2014 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses [STRINGS] sections in BEX files // #include #include #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "dstrings.h" typedef struct { const char *macro; const char *string; } bex_string_t; // mnemonic keys table static const bex_string_t bex_stringtable[] = { // part 1 - general initialization and prompts {"D_DEVSTR", D_DEVSTR}, {"D_CDROM", D_CDROM}, {"QUITMSG", QUITMSG}, {"LOADNET", LOADNET}, {"QLOADNET", QLOADNET}, {"QSAVESPOT", QSAVESPOT}, {"SAVEDEAD", SAVEDEAD}, {"QSPROMPT", QSPROMPT}, {"QLPROMPT", QLPROMPT}, {"NEWGAME", NEWGAME}, {"NIGHTMARE", NIGHTMARE}, {"SWSTRING", SWSTRING}, {"MSGOFF", MSGOFF}, {"MSGON", MSGON}, {"NETEND", NETEND}, {"ENDGAME", ENDGAME}, {"DETAILHI", DETAILHI}, {"DETAILLO", DETAILLO}, {"GAMMALVL0", GAMMALVL0}, {"GAMMALVL1", GAMMALVL1}, {"GAMMALVL2", GAMMALVL2}, {"GAMMALVL3", GAMMALVL3}, {"GAMMALVL4", GAMMALVL4}, {"EMPTYSTRING", EMPTYSTRING}, {"GGSAVED", GGSAVED}, {"SAVEGAMENAME", SAVEGAMENAME}, // part 2 - messages when the player gets things {"GOTARMOR", GOTARMOR}, {"GOTMEGA", GOTMEGA}, {"GOTHTHBONUS", GOTHTHBONUS}, {"GOTARMBONUS", GOTARMBONUS}, {"GOTSTIM", GOTSTIM}, {"GOTMEDINEED", GOTMEDINEED}, {"GOTMEDIKIT", GOTMEDIKIT}, {"GOTSUPER", GOTSUPER}, {"GOTBLUECARD", GOTBLUECARD}, {"GOTYELWCARD", GOTYELWCARD}, {"GOTREDCARD", GOTREDCARD}, {"GOTBLUESKUL", GOTBLUESKUL}, {"GOTYELWSKUL", GOTYELWSKUL}, {"GOTREDSKULL", GOTREDSKULL}, {"GOTINVUL", GOTINVUL}, {"GOTBERSERK", GOTBERSERK}, {"GOTINVIS", GOTINVIS}, {"GOTSUIT", GOTSUIT}, {"GOTMAP", GOTMAP}, {"GOTVISOR", GOTVISOR}, {"GOTMSPHERE", GOTMSPHERE}, {"GOTCLIP", GOTCLIP}, {"GOTCLIPBOX", GOTCLIPBOX}, {"GOTROCKET", GOTROCKET}, {"GOTROCKBOX", GOTROCKBOX}, {"GOTCELL", GOTCELL}, {"GOTCELLBOX", GOTCELLBOX}, {"GOTSHELLS", GOTSHELLS}, {"GOTSHELLBOX", GOTSHELLBOX}, {"GOTBACKPACK", GOTBACKPACK}, {"GOTBFG9000", GOTBFG9000}, {"GOTCHAINGUN", GOTCHAINGUN}, {"GOTCHAINSAW", GOTCHAINSAW}, {"GOTLAUNCHER", GOTLAUNCHER}, {"GOTPLASMA", GOTPLASMA}, {"GOTSHOTGUN", GOTSHOTGUN}, {"GOTSHOTGUN2", GOTSHOTGUN2}, // part 3 - messages when keys are needed {"PD_BLUEO", PD_BLUEO}, {"PD_REDO", PD_REDO}, {"PD_YELLOWO", PD_YELLOWO}, {"PD_BLUEK", PD_BLUEK}, {"PD_REDK", PD_REDK}, {"PD_YELLOWK", PD_YELLOWK}, // part 4 - multiplayer messaging {"HUSTR_MSGU", HUSTR_MSGU}, {"HUSTR_MESSAGESENT", HUSTR_MESSAGESENT}, {"HUSTR_CHATMACRO0", HUSTR_CHATMACRO0}, {"HUSTR_CHATMACRO1", HUSTR_CHATMACRO1}, {"HUSTR_CHATMACRO2", HUSTR_CHATMACRO2}, {"HUSTR_CHATMACRO3", HUSTR_CHATMACRO3}, {"HUSTR_CHATMACRO4", HUSTR_CHATMACRO4}, {"HUSTR_CHATMACRO5", HUSTR_CHATMACRO5}, {"HUSTR_CHATMACRO6", HUSTR_CHATMACRO6}, {"HUSTR_CHATMACRO7", HUSTR_CHATMACRO7}, {"HUSTR_CHATMACRO8", HUSTR_CHATMACRO8}, {"HUSTR_CHATMACRO9", HUSTR_CHATMACRO9}, {"HUSTR_TALKTOSELF1", HUSTR_TALKTOSELF1}, {"HUSTR_TALKTOSELF2", HUSTR_TALKTOSELF2}, {"HUSTR_TALKTOSELF3", HUSTR_TALKTOSELF3}, {"HUSTR_TALKTOSELF4", HUSTR_TALKTOSELF4}, {"HUSTR_TALKTOSELF5", HUSTR_TALKTOSELF5}, {"HUSTR_PLRGREEN", HUSTR_PLRGREEN}, {"HUSTR_PLRINDIGO", HUSTR_PLRINDIGO}, {"HUSTR_PLRBROWN", HUSTR_PLRBROWN}, {"HUSTR_PLRRED", HUSTR_PLRRED}, // part 5 - level names in the automap {"HUSTR_E1M1", HUSTR_E1M1}, {"HUSTR_E1M2", HUSTR_E1M2}, {"HUSTR_E1M3", HUSTR_E1M3}, {"HUSTR_E1M4", HUSTR_E1M4}, {"HUSTR_E1M5", HUSTR_E1M5}, {"HUSTR_E1M6", HUSTR_E1M6}, {"HUSTR_E1M7", HUSTR_E1M7}, {"HUSTR_E1M8", HUSTR_E1M8}, {"HUSTR_E1M9", HUSTR_E1M9}, {"HUSTR_E2M1", HUSTR_E2M1}, {"HUSTR_E2M2", HUSTR_E2M2}, {"HUSTR_E2M3", HUSTR_E2M3}, {"HUSTR_E2M4", HUSTR_E2M4}, {"HUSTR_E2M5", HUSTR_E2M5}, {"HUSTR_E2M6", HUSTR_E2M6}, {"HUSTR_E2M7", HUSTR_E2M7}, {"HUSTR_E2M8", HUSTR_E2M8}, {"HUSTR_E2M9", HUSTR_E2M9}, {"HUSTR_E3M1", HUSTR_E3M1}, {"HUSTR_E3M2", HUSTR_E3M2}, {"HUSTR_E3M3", HUSTR_E3M3}, {"HUSTR_E3M4", HUSTR_E3M4}, {"HUSTR_E3M5", HUSTR_E3M5}, {"HUSTR_E3M6", HUSTR_E3M6}, {"HUSTR_E3M7", HUSTR_E3M7}, {"HUSTR_E3M8", HUSTR_E3M8}, {"HUSTR_E3M9", HUSTR_E3M9}, {"HUSTR_E4M1", HUSTR_E4M1}, {"HUSTR_E4M2", HUSTR_E4M2}, {"HUSTR_E4M3", HUSTR_E4M3}, {"HUSTR_E4M4", HUSTR_E4M4}, {"HUSTR_E4M5", HUSTR_E4M5}, {"HUSTR_E4M6", HUSTR_E4M6}, {"HUSTR_E4M7", HUSTR_E4M7}, {"HUSTR_E4M8", HUSTR_E4M8}, {"HUSTR_E4M9", HUSTR_E4M9}, {"HUSTR_1", HUSTR_1}, {"HUSTR_2", HUSTR_2}, {"HUSTR_3", HUSTR_3}, {"HUSTR_4", HUSTR_4}, {"HUSTR_5", HUSTR_5}, {"HUSTR_6", HUSTR_6}, {"HUSTR_7", HUSTR_7}, {"HUSTR_8", HUSTR_8}, {"HUSTR_9", HUSTR_9}, {"HUSTR_10", HUSTR_10}, {"HUSTR_11", HUSTR_11}, {"HUSTR_12", HUSTR_12}, {"HUSTR_13", HUSTR_13}, {"HUSTR_14", HUSTR_14}, {"HUSTR_15", HUSTR_15}, {"HUSTR_16", HUSTR_16}, {"HUSTR_17", HUSTR_17}, {"HUSTR_18", HUSTR_18}, {"HUSTR_19", HUSTR_19}, {"HUSTR_20", HUSTR_20}, {"HUSTR_21", HUSTR_21}, {"HUSTR_22", HUSTR_22}, {"HUSTR_23", HUSTR_23}, {"HUSTR_24", HUSTR_24}, {"HUSTR_25", HUSTR_25}, {"HUSTR_26", HUSTR_26}, {"HUSTR_27", HUSTR_27}, {"HUSTR_28", HUSTR_28}, {"HUSTR_29", HUSTR_29}, {"HUSTR_30", HUSTR_30}, {"HUSTR_31", HUSTR_31}, {"HUSTR_32", HUSTR_32}, {"PHUSTR_1", PHUSTR_1}, {"PHUSTR_2", PHUSTR_2}, {"PHUSTR_3", PHUSTR_3}, {"PHUSTR_4", PHUSTR_4}, {"PHUSTR_5", PHUSTR_5}, {"PHUSTR_6", PHUSTR_6}, {"PHUSTR_7", PHUSTR_7}, {"PHUSTR_8", PHUSTR_8}, {"PHUSTR_9", PHUSTR_9}, {"PHUSTR_10", PHUSTR_10}, {"PHUSTR_11", PHUSTR_11}, {"PHUSTR_12", PHUSTR_12}, {"PHUSTR_13", PHUSTR_13}, {"PHUSTR_14", PHUSTR_14}, {"PHUSTR_15", PHUSTR_15}, {"PHUSTR_16", PHUSTR_16}, {"PHUSTR_17", PHUSTR_17}, {"PHUSTR_18", PHUSTR_18}, {"PHUSTR_19", PHUSTR_19}, {"PHUSTR_20", PHUSTR_20}, {"PHUSTR_21", PHUSTR_21}, {"PHUSTR_22", PHUSTR_22}, {"PHUSTR_23", PHUSTR_23}, {"PHUSTR_24", PHUSTR_24}, {"PHUSTR_25", PHUSTR_25}, {"PHUSTR_26", PHUSTR_26}, {"PHUSTR_27", PHUSTR_27}, {"PHUSTR_28", PHUSTR_28}, {"PHUSTR_29", PHUSTR_29}, {"PHUSTR_30", PHUSTR_30}, {"PHUSTR_31", PHUSTR_31}, {"PHUSTR_32", PHUSTR_32}, {"THUSTR_1", THUSTR_1}, {"THUSTR_2", THUSTR_2}, {"THUSTR_3", THUSTR_3}, {"THUSTR_4", THUSTR_4}, {"THUSTR_5", THUSTR_5}, {"THUSTR_6", THUSTR_6}, {"THUSTR_7", THUSTR_7}, {"THUSTR_8", THUSTR_8}, {"THUSTR_9", THUSTR_9}, {"THUSTR_10", THUSTR_10}, {"THUSTR_11", THUSTR_11}, {"THUSTR_12", THUSTR_12}, {"THUSTR_13", THUSTR_13}, {"THUSTR_14", THUSTR_14}, {"THUSTR_15", THUSTR_15}, {"THUSTR_16", THUSTR_16}, {"THUSTR_17", THUSTR_17}, {"THUSTR_18", THUSTR_18}, {"THUSTR_19", THUSTR_19}, {"THUSTR_20", THUSTR_20}, {"THUSTR_21", THUSTR_21}, {"THUSTR_22", THUSTR_22}, {"THUSTR_23", THUSTR_23}, {"THUSTR_24", THUSTR_24}, {"THUSTR_25", THUSTR_25}, {"THUSTR_26", THUSTR_26}, {"THUSTR_27", THUSTR_27}, {"THUSTR_28", THUSTR_28}, {"THUSTR_29", THUSTR_29}, {"THUSTR_30", THUSTR_30}, {"THUSTR_31", THUSTR_31}, {"THUSTR_32", THUSTR_32}, // part 6 - messages as a result of toggling states {"AMSTR_FOLLOWON", AMSTR_FOLLOWON}, {"AMSTR_FOLLOWOFF", AMSTR_FOLLOWOFF}, {"AMSTR_GRIDON", AMSTR_GRIDON}, {"AMSTR_GRIDOFF", AMSTR_GRIDOFF}, {"AMSTR_MARKEDSPOT", AMSTR_MARKEDSPOT}, {"AMSTR_MARKSCLEARED", AMSTR_MARKSCLEARED}, {"STSTR_MUS", STSTR_MUS}, {"STSTR_NOMUS", STSTR_NOMUS}, {"STSTR_DQDON", STSTR_DQDON}, {"STSTR_DQDOFF", STSTR_DQDOFF}, {"STSTR_KFAADDED", STSTR_KFAADDED}, {"STSTR_FAADDED", STSTR_FAADDED}, {"STSTR_NCON", STSTR_NCON}, {"STSTR_NCOFF", STSTR_NCOFF}, {"STSTR_BEHOLD", STSTR_BEHOLD}, {"STSTR_BEHOLDX", STSTR_BEHOLDX}, {"STSTR_CHOPPERS", STSTR_CHOPPERS}, {"STSTR_CLEV", STSTR_CLEV}, // part 7 - episode intermission texts {"E1TEXT", E1TEXT}, {"E2TEXT", E2TEXT}, {"E3TEXT", E3TEXT}, {"E4TEXT", E4TEXT}, {"C1TEXT", C1TEXT}, {"C2TEXT", C2TEXT}, {"C3TEXT", C3TEXT}, {"C4TEXT", C4TEXT}, {"C5TEXT", C5TEXT}, {"C6TEXT", C6TEXT}, {"P1TEXT", P1TEXT}, {"P2TEXT", P2TEXT}, {"P3TEXT", P3TEXT}, {"P4TEXT", P4TEXT}, {"P5TEXT", P5TEXT}, {"P6TEXT", P6TEXT}, {"T1TEXT", T1TEXT}, {"T2TEXT", T2TEXT}, {"T3TEXT", T3TEXT}, {"T4TEXT", T4TEXT}, {"T5TEXT", T5TEXT}, {"T6TEXT", T6TEXT}, // part 8 - creature names for the finale {"CC_ZOMBIE", CC_ZOMBIE}, {"CC_SHOTGUN", CC_SHOTGUN}, {"CC_HEAVY", CC_HEAVY}, {"CC_IMP", CC_IMP}, {"CC_DEMON", CC_DEMON}, {"CC_LOST", CC_LOST}, {"CC_CACO", CC_CACO}, {"CC_HELL", CC_HELL}, {"CC_BARON", CC_BARON}, {"CC_ARACH", CC_ARACH}, {"CC_PAIN", CC_PAIN}, {"CC_REVEN", CC_REVEN}, {"CC_MANCU", CC_MANCU}, {"CC_ARCH", CC_ARCH}, {"CC_SPIDER", CC_SPIDER}, {"CC_CYBER", CC_CYBER}, {"CC_HERO", CC_HERO}, // part 9 - intermission tiled backgrounds {"BGFLATE1", "FLOOR4_8"}, {"BGFLATE2", "SFLR6_1"}, {"BGFLATE3", "MFLR8_4"}, {"BGFLATE4", "MFLR8_3"}, {"BGFLAT06", "SLIME16"}, {"BGFLAT11", "RROCK14"}, {"BGFLAT20", "RROCK07"}, {"BGFLAT30", "RROCK17"}, {"BGFLAT15", "RROCK13"}, {"BGFLAT31", "RROCK19"}, {"BGCASTCALL", "BOSSBACK"}, }; static void *DEH_BEXStrStart(deh_context_t *context, char *line) { char s[10]; if (sscanf(line, "%9s", s) == 0 || strcmp("[STRINGS]", s)) { DEH_Warning(context, "Parse error on section start"); } return NULL; } static void DEH_BEXStrParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; int i; if (!DEH_ParseAssignment(line, &variable_name, &value)) { DEH_Warning(context, "Failed to parse assignment"); return; } for (i = 0; i < arrlen(bex_stringtable); i++) { if (!strcasecmp(bex_stringtable[i].macro, variable_name)) { DEH_AddStringReplacement(bex_stringtable[i].string, value); } } } deh_section_t deh_section_bexstr = { "[STRINGS]", NULL, DEH_BEXStrStart, DEH_BEXStrParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/doom/deh_cheat.c000066400000000000000000000067571360717211000220500ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Cheat" sections in dehacked files // #include #include #include "doomtype.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "am_map.h" #include "st_stuff.h" typedef struct { const char *name; cheatseq_t *seq; } deh_cheat_t; static deh_cheat_t allcheats[] = { {"Change music", &cheat_mus }, {"Chainsaw", &cheat_choppers }, {"God mode", &cheat_god }, {"Ammo & Keys", &cheat_ammo }, {"Ammo", &cheat_ammonokey }, {"No Clipping 1", &cheat_noclip }, {"No Clipping 2", &cheat_commercial_noclip }, {"Invincibility", &cheat_powerup[0] }, {"Berserk", &cheat_powerup[1] }, {"Invisibility", &cheat_powerup[2] }, {"Radiation Suit", &cheat_powerup[3] }, {"Auto-map", &cheat_powerup[4] }, {"Lite-Amp Goggles", &cheat_powerup[5] }, {"BEHOLD menu", &cheat_powerup[6] }, {"Level Warp", &cheat_clev }, {"Player Position", &cheat_mypos }, {"Map cheat", &cheat_amap }, }; static deh_cheat_t *FindCheatByName(char *name) { size_t i; for (i=0; i= cheat->seq->sequence_len) { DEH_Warning(context, "Cheat sequence longer than supported by " "Vanilla dehacked"); break; } if (deh_apply_cheats) { cheat->seq->sequence[i] = unsvalue[i]; } ++i; // Absolute limit - don't exceed if (i >= MAX_CHEAT_LEN - cheat->seq->parameter_chars) { DEH_Error(context, "Cheat sequence too long!"); return; } } if (deh_apply_cheats) { cheat->seq->sequence[i] = '\0'; } } deh_section_t deh_section_cheat = { "Cheat", NULL, DEH_CheatStart, DEH_CheatParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/doom/deh_doom.c000066400000000000000000000036131360717211000217060ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Top-level dehacked definitions for Doom dehacked. // #include #include "deh_defs.h" #include "deh_main.h" const char *deh_signatures[] = { "Patch File for DeHackEd v2.3", "Patch File for DeHackEd v3.0", NULL }; // deh_ammo.c: extern deh_section_t deh_section_ammo; // deh_cheat.c: extern deh_section_t deh_section_cheat; // deh_frame.c: extern deh_section_t deh_section_frame; // deh_misc.c: extern deh_section_t deh_section_misc; // deh_ptr.c: extern deh_section_t deh_section_pointer; // deh_sound.c extern deh_section_t deh_section_sound; // deh_text.c: extern deh_section_t deh_section_text; // deh_thing.c: extern deh_section_t deh_section_thing; // deh_weapon.c: extern deh_section_t deh_section_weapon; // deh_bexstr.c: extern deh_section_t deh_section_bexstr; // deh_bexpars.c: extern deh_section_t deh_section_bexpars; // deh_bexptr.c: extern deh_section_t deh_section_bexptr; // deh_bexincl.c: extern deh_section_t deh_section_bexincl; // // List of section types: // deh_section_t *deh_section_types[] = { &deh_section_ammo, &deh_section_cheat, &deh_section_frame, &deh_section_misc, &deh_section_pointer, &deh_section_sound, &deh_section_text, &deh_section_thing, &deh_section_weapon, &deh_section_bexstr, &deh_section_bexpars, &deh_section_bexptr, &deh_section_bexincl, NULL }; crispy-doom-crispy-doom-5.6.4/src/doom/deh_frame.c000066400000000000000000000077711360717211000220530ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Frame" sections in dehacked files // #include #include #include "doomtype.h" #include "d_items.h" #include "info.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "deh_mapping.h" DEH_BEGIN_MAPPING(state_mapping, state_t) DEH_MAPPING("Sprite number", sprite) DEH_MAPPING("Sprite subnumber", frame) DEH_MAPPING("Duration", tics) DEH_MAPPING("Next frame", nextstate) DEH_MAPPING("Unknown 1", misc1) DEH_MAPPING("Unknown 2", misc2) DEH_UNSUPPORTED_MAPPING("Codep frame") DEH_END_MAPPING static void *DEH_FrameStart(deh_context_t *context, char *line) { int frame_number = 0; state_t *state; if (sscanf(line, "Frame %i", &frame_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (frame_number < 0 || frame_number >= NUMSTATES) { DEH_Warning(context, "Invalid frame number: %i", frame_number); return NULL; } if (frame_number >= DEH_VANILLA_NUMSTATES) { DEH_Warning(context, "Attempt to modify frame %i: this will cause " "problems in Vanilla dehacked.", frame_number); } state = &states[frame_number]; return state; } // Simulate a frame overflow: Doom has 967 frames in the states[] array, but // DOS dehacked internally only allocates memory for 966. As a result, // attempts to set frame 966 (the last frame) will overflow the dehacked // array and overwrite the weaponinfo[] array instead. // // This is noticable in Batman Doom where it is impossible to switch weapons // away from the fist once selected. static void DEH_FrameOverflow(deh_context_t *context, char *varname, int value) { if (!strcasecmp(varname, "Duration")) { weaponinfo[0].ammo = value; } else if (!strcasecmp(varname, "Codep frame")) { weaponinfo[0].upstate = value; } else if (!strcasecmp(varname, "Next frame")) { weaponinfo[0].downstate = value; } else if (!strcasecmp(varname, "Unknown 1")) { weaponinfo[0].readystate = value; } else if (!strcasecmp(varname, "Unknown 2")) { weaponinfo[0].atkstate = value; } else { DEH_Error(context, "Unable to simulate frame overflow: field '%s'", varname); } } static void DEH_FrameParseLine(deh_context_t *context, char *line, void *tag) { state_t *state; char *variable_name, *value; int ivalue; if (tag == NULL) return; state = (state_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // all values are integers ivalue = atoi(value); // [crispy] drop the overflow simulation into the frame table if (state == &states[NUMSTATES - 1] && false) { DEH_FrameOverflow(context, variable_name, ivalue); } else { // set the appropriate field DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue); } } static void DEH_FrameSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "deh_misc.h" // Dehacked: "Initial Health" // This is the initial health a player has when starting anew. // See G_PlayerReborn in g_game.c int deh_initial_health = DEH_DEFAULT_INITIAL_HEALTH; // Dehacked: "Initial bullets" // This is the number of bullets the player has when starting anew. // See G_PlayerReborn in g_game.c int deh_initial_bullets = DEH_DEFAULT_INITIAL_BULLETS; // Dehacked: "Max Health" // This is the maximum health that can be reached using health // potions. See P_TouchSpecialThing in p_inter.c int deh_max_health = DEH_DEFAULT_MAX_HEALTH; // Dehacked: "Max Armor" // This is the maximum armor which can be reached by picking up // armor helmets. See P_TouchSpecialThing in p_inter.c int deh_max_armor = DEH_DEFAULT_MAX_ARMOR; // Dehacked: "Green Armor Class" // This is the armor class that is given when picking up the green // armor or an armor helmet. See P_TouchSpecialThing in p_inter.c // // DOS dehacked only modifies the behavior of the green armor shirt, // the armor class set by armor helmets is not affected. int deh_green_armor_class = DEH_DEFAULT_GREEN_ARMOR_CLASS; // Dehacked: "Blue Armor Class" // This is the armor class that is given when picking up the blue // armor or a megasphere. See P_TouchSpecialThing in p_inter.c // // DOS dehacked only modifies the MegaArmor behavior and not // the MegaSphere, which always gives armor type 2. int deh_blue_armor_class = DEH_DEFAULT_BLUE_ARMOR_CLASS; // Dehacked: "Max soulsphere" // The maximum health which can be reached by picking up the // soulsphere. See P_TouchSpecialThing in p_inter.c int deh_max_soulsphere = DEH_DEFAULT_MAX_SOULSPHERE; // Dehacked: "Soulsphere health" // The amount of health bonus that picking up a soulsphere // gives. See P_TouchSpecialThing in p_inter.c int deh_soulsphere_health = DEH_DEFAULT_SOULSPHERE_HEALTH; // Dehacked: "Megasphere health" // This is what the health is set to after picking up a // megasphere. See P_TouchSpecialThing in p_inter.c int deh_megasphere_health = DEH_DEFAULT_MEGASPHERE_HEALTH; // Dehacked: "God mode health" // This is what the health value is set to when cheating using // the IDDQD god mode cheat. See ST_Responder in st_stuff.c int deh_god_mode_health = DEH_DEFAULT_GOD_MODE_HEALTH; // Dehacked: "IDFA Armor" // This is what the armor is set to when using the IDFA cheat. // See ST_Responder in st_stuff.c int deh_idfa_armor = DEH_DEFAULT_IDFA_ARMOR; // Dehacked: "IDFA Armor Class" // This is what the armor class is set to when using the IDFA cheat. // See ST_Responder in st_stuff.c int deh_idfa_armor_class = DEH_DEFAULT_IDFA_ARMOR_CLASS; // Dehacked: "IDKFA Armor" // This is what the armor is set to when using the IDKFA cheat. // See ST_Responder in st_stuff.c int deh_idkfa_armor = DEH_DEFAULT_IDKFA_ARMOR; // Dehacked: "IDKFA Armor Class" // This is what the armor class is set to when using the IDKFA cheat. // See ST_Responder in st_stuff.c int deh_idkfa_armor_class = DEH_DEFAULT_IDKFA_ARMOR_CLASS; // Dehacked: "BFG Cells/Shot" // This is the number of CELLs firing the BFG uses up. // See P_CheckAmmo and A_FireBFG in p_pspr.c int deh_bfg_cells_per_shot = DEH_DEFAULT_BFG_CELLS_PER_SHOT; // Dehacked: "Monsters infight" // This controls whether monsters can harm other monsters of the same // species. For example, whether an imp fireball will damage other // imps. The value of this in dehacked patches is weird - '202' means // off, while '221' means on. // // See PIT_CheckThing in p_map.c int deh_species_infighting = DEH_DEFAULT_SPECIES_INFIGHTING; static struct { const char *deh_name; int *value; } misc_settings[] = { {"Initial Health", &deh_initial_health}, {"Initial Bullets", &deh_initial_bullets}, {"Max Health", &deh_max_health}, {"Max Armor", &deh_max_armor}, {"Green Armor Class", &deh_green_armor_class}, {"Blue Armor Class", &deh_blue_armor_class}, {"Max Soulsphere", &deh_max_soulsphere}, {"Soulsphere Health", &deh_soulsphere_health}, {"Megasphere Health", &deh_megasphere_health}, {"God Mode Health", &deh_god_mode_health}, {"IDFA Armor", &deh_idfa_armor}, {"IDFA Armor Class", &deh_idfa_armor_class}, {"IDKFA Armor", &deh_idkfa_armor}, {"IDKFA Armor Class", &deh_idkfa_armor_class}, {"BFG Cells/Shot", &deh_bfg_cells_per_shot}, }; static void *DEH_MiscStart(deh_context_t *context, char *line) { return NULL; } static void DEH_MiscParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; int ivalue; size_t i; if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); if (!strcasecmp(variable_name, "Monsters Infight")) { // See notes above. if (ivalue == 202) { deh_species_infighting = 0; } else if (ivalue == 221) { deh_species_infighting = 1; } else { DEH_Warning(context, "Invalid value for 'Monsters Infight': %i", ivalue); } return; } for (i=0; i #include #include #include "doomtype.h" #include "info.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" actionf_t codeptrs[NUMSTATES]; // [crispy] share with deh_bexptr.c static int CodePointerIndex(actionf_t *ptr) { int i; for (i=0; i= NUMSTATES) { DEH_Warning(context, "Invalid frame number: %i", frame_number); return NULL; } return &states[frame_number]; } static void DEH_PointerParseLine(deh_context_t *context, char *line, void *tag) { state_t *state; char *variable_name, *value; int ivalue; if (tag == NULL) return; state = (state_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // printf("Set %s to %s for state\n", variable_name, value); // all values are integers ivalue = atoi(value); // set the appropriate field if (!strcasecmp(variable_name, "Codep frame")) { if (ivalue < 0 || ivalue >= NUMSTATES) { DEH_Warning(context, "Invalid state '%i'", ivalue); } else { state->action = codeptrs[ivalue]; } } else { DEH_Warning(context, "Unknown variable name '%s'", variable_name); } } static void DEH_PointerSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" #include "sounds.h" DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t) DEH_UNSUPPORTED_MAPPING("Offset") DEH_UNSUPPORTED_MAPPING("Zero/One") DEH_MAPPING("Value", priority) DEH_MAPPING("Zero 1", link) DEH_MAPPING("Zero 2", pitch) DEH_MAPPING("Zero 3", volume) DEH_UNSUPPORTED_MAPPING("Zero 4") DEH_MAPPING("Neg. One 1", usefulness) DEH_MAPPING("Neg. One 2", lumpnum) DEH_END_MAPPING static void *DEH_SoundStart(deh_context_t *context, char *line) { int sound_number = 0; if (sscanf(line, "Sound %i", &sound_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (sound_number < 0 || sound_number >= NUMSFX) { DEH_Warning(context, "Invalid sound number: %i", sound_number); return NULL; } if (sound_number >= DEH_VANILLA_NUMSFX) { DEH_Warning(context, "Attempt to modify SFX %i. This will cause " "problems in Vanilla dehacked.", sound_number); } return &S_sfx[sound_number]; } static void DEH_SoundParseLine(deh_context_t *context, char *line, void *tag) { sfxinfo_t *sfx; char *variable_name, *value; int ivalue; if (tag == NULL) return; sfx = (sfxinfo_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // all values are integers ivalue = atoi(value); // Set the field value DEH_SetMapping(context, &sound_mapping, sfx, variable_name, ivalue); } deh_section_t deh_section_sound = { "Sound", NULL, DEH_SoundStart, DEH_SoundParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/doom/deh_thing.c000066400000000000000000000120161360717211000220560ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Thing" sections in dehacked files // #include #include #include "doomtype.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" #include "info.h" #include "p_mobj.h" // [crispy] MF_* typedef struct { char *flag; int bits; } bex_thingbits_t; static const bex_thingbits_t bex_thingbitstable[] = { {"SPECIAL", MF_SPECIAL}, {"SOLID", MF_SOLID}, {"SHOOTABLE", MF_SHOOTABLE}, {"NOSECTOR", MF_NOSECTOR}, {"NOBLOCKMAP", MF_NOBLOCKMAP}, {"AMBUSH", MF_AMBUSH}, {"JUSTHIT", MF_JUSTHIT}, {"JUSTATTACKED", MF_JUSTATTACKED}, {"SPAWNCEILING", MF_SPAWNCEILING}, {"NOGRAVITY", MF_NOGRAVITY}, {"DROPOFF", MF_DROPOFF}, {"PICKUP", MF_PICKUP}, {"NOCLIP", MF_NOCLIP}, {"SLIDE", MF_SLIDE}, {"FLOAT", MF_FLOAT}, {"TELEPORT", MF_TELEPORT}, {"MISSILE", MF_MISSILE}, {"DROPPED", MF_DROPPED}, {"SHADOW", MF_SHADOW}, {"NOBLOOD", MF_NOBLOOD}, {"CORPSE", MF_CORPSE}, {"INFLOAT", MF_INFLOAT}, {"COUNTKILL", MF_COUNTKILL}, {"COUNTITEM", MF_COUNTITEM}, {"SKULLFLY", MF_SKULLFLY}, {"NOTDMATCH", MF_NOTDMATCH}, {"TRANSLUCENT", MF_TRANSLUCENT}, // TRANSLATION consists of 2 bits, not 1 {"TRANSLATION", 0x04000000}, {"TRANSLATION1", 0x04000000}, {"TRANSLATION2", 0x08000000}, // unused bits, for Boom compatibility {"UNUSED1", 0x08000000}, {"UNUSED2", 0x10000000}, {"UNUSED3", 0x20000000}, {"UNUSED4", 0x40000000}, }; DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t) DEH_MAPPING("ID #", doomednum) DEH_MAPPING("Initial frame", spawnstate) DEH_MAPPING("Hit points", spawnhealth) DEH_MAPPING("First moving frame", seestate) DEH_MAPPING("Alert sound", seesound) DEH_MAPPING("Reaction time", reactiontime) DEH_MAPPING("Attack sound", attacksound) DEH_MAPPING("Injury frame", painstate) DEH_MAPPING("Pain chance", painchance) DEH_MAPPING("Pain sound", painsound) DEH_MAPPING("Close attack frame", meleestate) DEH_MAPPING("Far attack frame", missilestate) DEH_MAPPING("Death frame", deathstate) DEH_MAPPING("Exploding frame", xdeathstate) DEH_MAPPING("Death sound", deathsound) DEH_MAPPING("Speed", speed) DEH_MAPPING("Width", radius) DEH_MAPPING("Height", height) DEH_MAPPING("Mass", mass) DEH_MAPPING("Missile damage", damage) DEH_MAPPING("Action sound", activesound) DEH_MAPPING("Bits", flags) DEH_MAPPING("Respawn frame", raisestate) DEH_END_MAPPING static void *DEH_ThingStart(deh_context_t *context, char *line) { int thing_number = 0; mobjinfo_t *mobj; if (sscanf(line, "Thing %i", &thing_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } // dehacked files are indexed from 1 --thing_number; if (thing_number < 0 || thing_number >= NUMMOBJTYPES) { DEH_Warning(context, "Invalid thing number: %i", thing_number); return NULL; } mobj = &mobjinfo[thing_number]; return mobj; } static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag) { mobjinfo_t *mobj; char *variable_name, *value; int ivalue; if (tag == NULL) return; mobj = (mobjinfo_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // printf("Set %s to %s for mobj\n", variable_name, value); // all values are integers ivalue = atoi(value); // [crispy] support BEX bits mnemonics in Things fields if (!ivalue && !strcasecmp(variable_name, "bits")) { for ( ; (value = strtok(value, ",+| \t\f\r")); value = NULL) { int i; for (i = 0; i < arrlen(bex_thingbitstable); i++) if (!strcasecmp(value, bex_thingbitstable[i].flag)) { ivalue |= bex_thingbitstable[i].bits; break; } } } // Set the field value DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue); } static void DEH_ThingSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include #include "doomtype.h" #include "d_items.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t) DEH_MAPPING("Ammo type", ammo) DEH_MAPPING("Deselect frame", upstate) DEH_MAPPING("Select frame", downstate) DEH_MAPPING("Bobbing frame", readystate) DEH_MAPPING("Shooting frame", atkstate) DEH_MAPPING("Firing frame", flashstate) DEH_END_MAPPING static void *DEH_WeaponStart(deh_context_t *context, char *line) { int weapon_number = 0; if (sscanf(line, "Weapon %i", &weapon_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (weapon_number < 0 || weapon_number >= NUMWEAPONS) { DEH_Warning(context, "Invalid weapon number: %i", weapon_number); return NULL; } return &weaponinfo[weapon_number]; } static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; weaponinfo_t *weapon; int ivalue; if (tag == NULL) return; weapon = (weaponinfo_t *) tag; if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue); } static void DEH_WeaponSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "i_timer.h" #include "d_mode.h" // // Global parameters/defines. // // DOOM version #define DOOM_VERSION 109 // Version code for cph's longtics hack ("v1.91") #define DOOM_191_VERSION 111 // If rangecheck is undefined, // most parameter validation debugging code will not be compiled #define RANGECHECK // The maximum number of players, multiplayer/networking. #define MAXPLAYERS 4 // The current state of the game: whether we are // playing, gazing at the intermission screen, // the game final animation, or a demo. typedef enum { GS_LEVEL, GS_INTERMISSION, GS_FINALE, GS_DEMOSCREEN, } gamestate_t; typedef enum { ga_nothing, ga_loadlevel, ga_newgame, ga_loadgame, ga_savegame, ga_playdemo, ga_completed, ga_victory, ga_worlddone, ga_screenshot } gameaction_t; // // Difficulty/skill settings/filters. // // Skill flags. #define MTF_EASY 1 #define MTF_NORMAL 2 #define MTF_HARD 4 // Deaf monsters/do not react to sound. #define MTF_AMBUSH 8 // // Key cards. // typedef enum { it_bluecard, it_yellowcard, it_redcard, it_blueskull, it_yellowskull, it_redskull, NUMCARDS } card_t; // The defined weapons, // including a marker indicating // user has not changed weapon. typedef enum { wp_fist, wp_pistol, wp_shotgun, wp_chaingun, wp_missile, wp_plasma, wp_bfg, wp_chainsaw, wp_supershotgun, NUMWEAPONS, // No pending weapon change. wp_nochange } weapontype_t; // Ammunition types defined. typedef enum { am_clip, // Pistol / chaingun ammo. am_shell, // Shotgun / double barreled shotgun. am_cell, // Plasma rifle, BFG. am_misl, // Missile launcher. NUMAMMO, am_noammo // Unlimited for chainsaw / fist. } ammotype_t; // Power up artifacts. typedef enum { pw_invulnerability, pw_strength, pw_invisibility, pw_ironfeet, pw_allmap, pw_infrared, NUMPOWERS, // [crispy] showfps and mapcoords are now "powers" pw_showfps, pw_mapcoords } powertype_t; // // Power up durations, // how many seconds till expiration, // assuming TICRATE is 35 ticks/second. // typedef enum { INVULNTICS = (30*TICRATE), INVISTICS = (60*TICRATE), INFRATICS = (120*TICRATE), IRONTICS = (60*TICRATE) } powerduration_t; #endif // __DOOMDEF__ crispy-doom-crispy-doom-5.6.4/src/doom/doomstat.c000066400000000000000000000017541360717211000217660ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Put all global tate variables here. // #include #include "doomstat.h" // Game Mode - identify IWAD as shareware, retail etc. GameMode_t gamemode = indetermined; GameMission_t gamemission = doom; GameVersion_t gameversion = exe_final2; GameVariant_t gamevariant = vanilla; const char *gamedescription; // Set if homebrew PWAD stuff has been added. boolean modifiedgame; crispy-doom-crispy-doom-5.6.4/src/doom/doomstat.h000066400000000000000000000156061360717211000217740ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // All the global variables that store the internal state. // Theoretically speaking, the internal state of the engine // should be found by looking at the variables collected // here, and every relevant module will have to include // this header file. // In practice, things are a bit messy. // #ifndef __D_STATE__ #define __D_STATE__ // We need globally shared data structures, // for defining the global state variables. #include "doomdata.h" #include "d_loop.h" // We need the playr data structure as well. #include "d_player.h" // Game mode/mission #include "d_mode.h" #include "net_defs.h" #include "crispy.h" // ------------------------ // Command line parameters. // extern boolean nomonsters; // checkparm of -nomonsters extern boolean respawnparm; // checkparm of -respawn extern boolean fastparm; // checkparm of -fast extern boolean devparm; // DEBUG: launched with -devparm // ----------------------------------------------------- // Game Mode - identify IWAD as shareware, retail etc. // extern GameMode_t gamemode; extern GameMission_t gamemission; extern GameVersion_t gameversion; extern GameVariant_t gamevariant; extern const char *gamedescription; extern char *nervewadfile; // Convenience macro. // 'gamemission' can be equal to pack_chex or pack_hacx, but these are // just modified versions of doom and doom2, and should be interpreted // as the same most of the time. #define logical_gamemission \ (gamemission == pack_chex ? doom : \ gamemission == pack_hacx ? doom2 : gamemission) // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; // ------------------------------------------- // Selected skill type, map etc. // // Defaults for menu, methinks. extern skill_t startskill; extern int startepisode; extern int startmap; // Savegame slot to load on startup. This is the value provided to // the -loadgame option. If this has not been provided, this is -1. extern int startloadgame; extern boolean autostart; // Selected by user. extern skill_t gameskill; extern int gameepisode; extern int gamemap; // If non-zero, exit the level after this number of minutes extern int timelimit; // Nightmare mode flag, single player. extern boolean respawnmonsters; // Netgame? Only true if >1 player. extern boolean netgame; // 0=Cooperative; 1=Deathmatch; 2=Altdeath extern int deathmatch; // ------------------------- // Internal parameters for sound rendering. // These have been taken from the DOS version, // but are not (yet) supported with Linux // (e.g. no sound volume adjustment with menu. // From m_menu.c: // Sound FX volume has default, 0 - 15 // Music volume has default, 0 - 15 // These are multiplied by 8. extern int sfxVolume; extern int musicVolume; // Current music/sfx card - index useless // w/o a reference LUT in a sound module. // Ideally, this would use indices found // in: /usr/include/linux/soundcard.h extern int snd_MusicDevice; extern int snd_SfxDevice; // Config file? Same disclaimer as above. extern int snd_DesiredMusicDevice; extern int snd_DesiredSfxDevice; // ------------------------- // Status flags for refresh. // // Depending on view size - no status bar? // Note that there is no way to disable the // status bar explicitely. extern boolean statusbaractive; extern boolean automapactive; // In AutoMap mode? extern boolean menuactive; // Menu overlayed? extern boolean paused; // Game Pause? extern boolean viewactive; extern boolean nodrawers; extern boolean testcontrols; extern int testcontrols_mousespeed; // This one is related to the 3-screen display mode. // ANG90 = left side, ANG270 = right extern int viewangleoffset; // Player taking events, and displaying. extern int consoleplayer; extern int displayplayer; // ------------------------------------- // Scores, rating. // Statistics on a given map, for intermission. // extern int totalkills; extern int totalitems; extern int totalsecret; extern int extrakills; // [crispy] count spawned monsters // Timer, for scores. extern int levelstarttic; // gametic at level start extern int leveltime; // tics in game play for par extern int totalleveltimes; // [crispy] CPhipps - total time for all completed levels // -------------------------------------- // DEMO playback/recording related stuff. // No demo, there is a human player in charge? // Disable save/end game? extern boolean usergame; //? extern boolean demoplayback; extern boolean demorecording; // Round angleturn in ticcmds to the nearest 256. This is used when // recording Vanilla demos in netgames. extern boolean lowres_turn; // Quit after playing a demo from cmdline. extern boolean singledemo; //? extern gamestate_t gamestate; //----------------------------- // Internal parameters, fixed. // These are set by the engine, and not changed // according to user inputs. Partly load from // WAD, partly set at startup time. // Bookkeeping on players - state. extern player_t players[MAXPLAYERS]; // Alive? Disconnected? extern boolean playeringame[MAXPLAYERS]; // Player spawn spots for deathmatch. #define MAX_DM_STARTS 10 extern mapthing_t deathmatchstarts[MAX_DM_STARTS]; extern mapthing_t* deathmatch_p; // Player spawn spots. extern mapthing_t playerstarts[MAXPLAYERS]; extern boolean playerstartsingame[MAXPLAYERS]; // Intermission stats. // Parameters for world map / intermission. extern wbstartstruct_t wminfo; //----------------------------------------- // Internal parameters, used for engine. // // File handling stuff. extern char *savegamedir; // if true, load all graphics at level load extern boolean precache; // wipegamestate can be set to -1 // to force a wipe on the next draw extern gamestate_t wipegamestate; extern int mouseSensitivity; extern int mouseSensitivity_x2; extern int mouseSensitivity_y; extern int bodyqueslot; // Needed to store the number of the dummy sky flat. // Used for rendering, // as well as tracking projectiles etc. extern int skyflatnum; // Netgame stuff (buffers and pointers, i.e. indices). extern int rndindex; extern ticcmd_t *netcmds; #endif crispy-doom-crispy-doom-5.6.4/src/doom/dstrings.c000066400000000000000000000044131360717211000217640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Globally defined strings. // #include "dstrings.h" const char *doom1_endmsg[] = { "are you sure you want to\nquit this great game?", "please don't leave, there's more\ndemons to toast!", "let's beat it -- this is turning\ninto a bloodbath!", "i wouldn't leave if i were you.\ndos is much worse.", "you're trying to say you like dos\nbetter than me, right?", "don't leave yet -- there's a\ndemon around that corner!", "ya know, next time you come in here\ni'm gonna toast ya.", "go ahead and leave. see if i care.", }; const char *doom2_endmsg[] = { // QuitDOOM II messages "are you sure you want to\nquit this great game?", "you want to quit?\nthen, thou hast lost an eighth!", "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", "get outta here and go back\nto your boring programs.", "if i were your boss, i'd \n deathmatch ya in a minute!", "look, bud. you leave now\nand you forfeit your body count!", "just leave. when you come\nback, i'll be waiting with a bat.", "you're lucky i don't smack\nyou for thinking about leaving.", }; #if 0 // UNUSED messages included in the source release char* endmsg[] = { // DOOM1 QUITMSG, // FinalDOOM? "fuck you, pussy!\nget the fuck out!", "you quit and i'll jizz\nin your cystholes!", "if you leave, i'll make\nthe lord drink my jizz.", "hey, ron! can we say\n'fuck' in the game?", "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", "suck it down, asshole!\nyou're a fucking wimp!", "don't quit now! we're \nstill spending your money!", // Internal debug. Different style, too. "THIS IS NO MESSAGE!\nPage intentionally left blank." }; #endif crispy-doom-crispy-doom-5.6.4/src/doom/dstrings.h000066400000000000000000000017001360717211000217650ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // DESCRIPTION: // DOOM strings, by language. // #ifndef __DSTRINGS__ #define __DSTRINGS__ // All important printed strings. #include "d_englsh.h" // Misc. other strings. #define SAVEGAMENAME "doomsav" // QuitDOOM messages // 8 per each game type #define NUM_QUITMESSAGES 8 extern const char *doom1_endmsg[]; extern const char *doom2_endmsg[]; #endif crispy-doom-crispy-doom-5.6.4/src/doom/f_finale.c000066400000000000000000000555241360717211000217030ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Game completion, final screen animation. // #include #include // Functions. #include "deh_main.h" #include "i_system.h" #include "i_swap.h" #include "z_zone.h" #include "v_video.h" #include "w_wad.h" #include "s_sound.h" // Data. #include "d_main.h" #include "dstrings.h" #include "sounds.h" #include "doomstat.h" #include "r_state.h" #include "m_controls.h" // [crispy] key_* #include "m_misc.h" // [crispy] M_StringDuplicate() #include "m_random.h" // [crispy] Crispy_Random() typedef enum { F_STAGE_TEXT, F_STAGE_ARTSCREEN, F_STAGE_CAST, } finalestage_t; // ? //#include "doomstat.h" //#include "r_local.h" //#include "f_finale.h" // Stage of animation: finalestage_t finalestage; unsigned int finalecount; #define TEXTSPEED 3 #define TEXTWAIT 250 typedef struct { GameMission_t mission; int episode, level; const char *background; const char *text; } textscreen_t; static textscreen_t textscreens[] = { { doom, 1, 8, "FLOOR4_8", E1TEXT}, { doom, 2, 8, "SFLR6_1", E2TEXT}, { doom, 3, 8, "MFLR8_4", E3TEXT}, { doom, 4, 8, "MFLR8_3", E4TEXT}, { doom, 5, 8, "FLOOR7_2", E5TEXT}, // [crispy] Sigil { doom2, 1, 6, "SLIME16", C1TEXT}, { doom2, 1, 11, "RROCK14", C2TEXT}, { doom2, 1, 20, "RROCK07", C3TEXT}, { doom2, 1, 30, "RROCK17", C4TEXT}, { doom2, 1, 15, "RROCK13", C5TEXT}, { doom2, 1, 31, "RROCK19", C6TEXT}, { pack_tnt, 1, 6, "SLIME16", T1TEXT}, { pack_tnt, 1, 11, "RROCK14", T2TEXT}, { pack_tnt, 1, 20, "RROCK07", T3TEXT}, { pack_tnt, 1, 30, "RROCK17", T4TEXT}, { pack_tnt, 1, 15, "RROCK13", T5TEXT}, { pack_tnt, 1, 31, "RROCK19", T6TEXT}, { pack_plut, 1, 6, "SLIME16", P1TEXT}, { pack_plut, 1, 11, "RROCK14", P2TEXT}, { pack_plut, 1, 20, "RROCK07", P3TEXT}, { pack_plut, 1, 30, "RROCK17", P4TEXT}, { pack_plut, 1, 15, "RROCK13", P5TEXT}, { pack_plut, 1, 31, "RROCK19", P6TEXT}, { pack_nerve, 1, 8, "SLIME16", N1TEXT}, { pack_master, 1, 20, "SLIME16", M1TEXT}, }; const char *finaletext; const char *finaleflat; static char *finaletext_rw; void F_StartCast (void); void F_CastTicker (void); boolean F_CastResponder (event_t *ev); void F_CastDrawer (void); extern void A_RandomJump(); // // F_StartFinale // void F_StartFinale (void) { size_t i; gameaction = ga_nothing; gamestate = GS_FINALE; viewactive = false; automapactive = false; if (logical_gamemission == doom) { S_ChangeMusic(mus_victor, true); } else { S_ChangeMusic(mus_read_m, true); } // Find the right screen and set the text and background for (i=0; imission == doom) { screen->level = 5; } // [crispy] Hack for Master Levels MAP21: Bad Dream if (gamemission == pack_master && screen->mission == pack_master && gamemap == 21) { screen->level = 21; } // [crispy] During demo recording/playback or network games // these two packs behave like any other ordinary PWAD if (!crispy->singleplayer && (gamemission == pack_nerve || gamemission == pack_master) && screen->mission == doom2) { screen->mission = gamemission; } if (logical_gamemission == screen->mission && (logical_gamemission != doom || gameepisode == screen->episode) && gamemap == screen->level) { finaletext = screen->text; finaleflat = screen->background; } } // Do dehacked substitutions of strings finaletext = DEH_String(finaletext); finaleflat = DEH_String(finaleflat); // [crispy] do the "char* vs. const char*" dance if (finaletext_rw) { free(finaletext_rw); finaletext_rw = NULL; } finaletext_rw = M_StringDuplicate(finaletext); finalestage = F_STAGE_TEXT; finalecount = 0; } boolean F_Responder (event_t *event) { if (finalestage == F_STAGE_CAST) return F_CastResponder (event); return false; } // // F_Ticker // void F_Ticker (void) { size_t i; // check for skipping if ( (gamemode == commercial) && ( finalecount > 50) ) { // go on to the next level for (i=0 ; isingleplayer && gamemap == 8) F_StartCast (); else if (gamemission == pack_master && crispy->singleplayer && (gamemap == 20 || gamemap == 21)) F_StartCast (); else if (gamemap == 30) F_StartCast (); else gameaction = ga_worlddone; } } // advance animation finalecount++; if (finalestage == F_STAGE_CAST) { F_CastTicker (); return; } if ( gamemode == commercial) return; if (finalestage == F_STAGE_TEXT && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT) { finalecount = 0; finalestage = F_STAGE_ARTSCREEN; wipegamestate = -1; // force a wipe if (gameepisode == 3) S_StartMusic (mus_bunny); } } // // F_TextWrite // #include "hu_stuff.h" extern patch_t *hu_font[HU_FONTSIZE]; // [crispy] add line breaks for lines exceeding screenwidth static inline boolean F_AddLineBreak (char *c) { while (c-- > finaletext_rw) { if (*c == '\n') { return false; } else if (*c == ' ') { *c = '\n'; return true; } } return false; } void F_TextWrite (void) { byte* src; pixel_t* dest; int x,y,w; signed int count; char *ch; // [crispy] un-const int c; int cx; int cy; // erase the entire screen to a tiled background src = W_CacheLumpName ( finaleflat , PU_CACHE); dest = I_VideoBuffer; for (y=0 ; y HU_FONTSIZE) { cx += 4; continue; } w = SHORT (hu_font[c]->width); if (cx+w > ORIGWIDTH) { // [crispy] add line breaks for lines exceeding screenwidth if (F_AddLineBreak(ch)) { continue; } else break; } // [cispy] prevent text from being drawn off-screen vertically if (cy + SHORT(hu_font[c]->height) > ORIGHEIGHT) { break; } V_DrawPatch(cx, cy, hu_font[c]); cx+=w; } } // // Final DOOM 2 animation // Casting by id Software. // in order of appearance // typedef struct { const char *name; mobjtype_t type; } castinfo_t; castinfo_t castorder[] = { {CC_ZOMBIE, MT_POSSESSED}, {CC_SHOTGUN, MT_SHOTGUY}, {CC_HEAVY, MT_CHAINGUY}, {CC_IMP, MT_TROOP}, {CC_DEMON, MT_SERGEANT}, {CC_LOST, MT_SKULL}, {CC_CACO, MT_HEAD}, {CC_HELL, MT_KNIGHT}, {CC_BARON, MT_BRUISER}, {CC_ARACH, MT_BABY}, {CC_PAIN, MT_PAIN}, {CC_REVEN, MT_UNDEAD}, {CC_MANCU, MT_FATSO}, {CC_ARCH, MT_VILE}, {CC_SPIDER, MT_SPIDER}, {CC_CYBER, MT_CYBORG}, {CC_HERO, MT_PLAYER}, {NULL,0} }; int castnum; int casttics; state_t* caststate; boolean castdeath; int castframes; int castonmelee; boolean castattacking; static signed char castangle; // [crispy] turnable cast static signed char castskip; // [crispy] skippable cast static boolean castflip; // [crispy] flippable death sequence // [crispy] randomize seestate and deathstate sounds in the cast static int F_RandomizeSound (int sound) { if (!crispy->soundfix) return sound; switch (sound) { // [crispy] actor->info->seesound, from p_enemy.c:A_Look() case sfx_posit1: case sfx_posit2: case sfx_posit3: return sfx_posit1 + Crispy_Random()%3; break; case sfx_bgsit1: case sfx_bgsit2: return sfx_bgsit1 + Crispy_Random()%2; break; // [crispy] actor->info->deathsound, from p_enemy.c:A_Scream() case sfx_podth1: case sfx_podth2: case sfx_podth3: return sfx_podth1 + Crispy_Random()%3; break; case sfx_bgdth1: case sfx_bgdth2: return sfx_bgdth1 + Crispy_Random()%2; break; default: return sound; break; } } extern void A_BruisAttack(); extern void A_BspiAttack(); extern void A_CPosAttack(); extern void A_CPosRefire(); extern void A_CyberAttack(); extern void A_FatAttack1(); extern void A_FatAttack2(); extern void A_FatAttack3(); extern void A_HeadAttack(); extern void A_PainAttack(); extern void A_PosAttack(); extern void A_SargAttack(); extern void A_SkelFist(); extern void A_SkelMissile(); extern void A_SkelWhoosh(); extern void A_SkullAttack(); extern void A_SPosAttack(); extern void A_TroopAttack(); extern void A_VileTarget(); typedef struct { void *const action; const int sound; const boolean early; } actionsound_t; static const actionsound_t actionsounds[] = { {A_PosAttack, sfx_pistol, false}, {A_SPosAttack, sfx_shotgn, false}, {A_CPosAttack, sfx_shotgn, false}, {A_CPosRefire, sfx_shotgn, false}, {A_VileTarget, sfx_vilatk, true}, {A_SkelWhoosh, sfx_skeswg, false}, {A_SkelFist, sfx_skepch, false}, {A_SkelMissile, sfx_skeatk, true}, {A_FatAttack1, sfx_firsht, false}, {A_FatAttack2, sfx_firsht, false}, {A_FatAttack3, sfx_firsht, false}, {A_HeadAttack, sfx_firsht, true}, {A_BruisAttack, sfx_firsht, true}, {A_TroopAttack, sfx_claw, false}, {A_SargAttack, sfx_sgtatk, true}, {A_SkullAttack, sfx_sklatk, false}, {A_PainAttack, sfx_sklatk, true}, {A_BspiAttack, sfx_plasma, false}, {A_CyberAttack, sfx_rlaunc, false}, }; // [crispy] play attack sound based on state action function (instead of state number) static int F_SoundForState (int st) { void *const castaction = (void *) caststate->action.acv; void *const nextaction = (void *) (&states[caststate->nextstate])->action.acv; // [crispy] fix Doomguy in casting sequence if (castaction == NULL) { if (st == S_PLAY_ATK2) return sfx_dshtgn; else return 0; } else { int i; for (i = 0; i < arrlen(actionsounds); i++) { const actionsound_t *const as = &actionsounds[i]; if ((!as->early && castaction == as->action) || (as->early && nextaction == as->action)) { return as->sound; } } } return 0; } // // 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 = F_STAGE_CAST; 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 || castskip) // [crispy] skippable cast { if (castskip) { castnum += castskip; castskip = 0; } else // 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, F_RandomizeSound(mobjinfo[castorder[castnum].type].seesound)); caststate = &states[mobjinfo[castorder[castnum].type].seestate]; castframes = 0; castangle = 0; // [crispy] turnable cast castflip = false; // [crispy] flippable death sequence } else { // just advance to next state in animation // [crispy] fix Doomguy in casting sequence /* if (!castdeath && caststate == &states[S_PLAY_ATK1]) goto stopattack; // Oh, gross hack! */ // [crispy] Allow A_RandomJump() in deaths in cast sequence if (caststate->action.acp1 == A_RandomJump && Crispy_Random() < caststate->misc2) { st = caststate->misc1; } else { // [crispy] fix Doomguy in casting sequence if (!castdeath && caststate == &states[S_PLAY_ATK1]) st = S_PLAY_ATK2; else if (!castdeath && caststate == &states[S_PLAY_ATK2]) goto stopattack; // Oh, gross hack! else st = caststate->nextstate; } caststate = &states[st]; castframes++; sfx = F_SoundForState(st); /* // sound hacks.... switch (st) { case S_PLAY_ATK2: sfx = sfx_dshtgn; break; // [crispy] fix Doomguy in casting sequence 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 (!castdeath && 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) { // [crispy] Allow A_RandomJump() in deaths in cast sequence if (caststate->action.acp1 == A_RandomJump) { if (Crispy_Random() < caststate->misc2) { caststate = &states[caststate->misc1]; } else { caststate = &states[caststate->nextstate]; } casttics = caststate->tics; } if (casttics == -1) { casttics = 15; } } } // // F_CastResponder // boolean F_CastResponder (event_t* ev) { boolean xdeath = false; if (ev->type != ev_keydown) return false; // [crispy] make monsters turnable in cast ... if (ev->data1 == key_left) { if (++castangle > 7) castangle = 0; return false; } else if (ev->data1 == key_right) { if (--castangle < 0) castangle = 7; return false; } else // [crispy] ... and allow to skip through them .. if (ev->data1 == key_strafeleft || ev->data1 == key_alt_strafeleft) { castskip = castnum ? -1 : arrlen(castorder)-2; return false; } else if (ev->data1 == key_straferight || ev->data1 == key_alt_straferight) { castskip = +1; return false; } // [crispy] ... and finally turn them into gibbs if (ev->data1 == key_speed) xdeath = true; if (castdeath) return true; // already in dying frames // go into death frame castdeath = true; if (xdeath && mobjinfo[castorder[castnum].type].xdeathstate) caststate = &states[mobjinfo[castorder[castnum].type].xdeathstate]; else caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; casttics = caststate->tics; // [crispy] Allow A_RandomJump() in deaths in cast sequence if (casttics == -1 && caststate->action.acp1 == A_RandomJump) { if (Crispy_Random() < caststate->misc2) { caststate = &states [caststate->misc1]; } else { caststate = &states [caststate->nextstate]; } casttics = caststate->tics; } castframes = 0; castattacking = false; if (xdeath && mobjinfo[castorder[castnum].type].xdeathstate) S_StartSound (NULL, sfx_slop); else if (mobjinfo[castorder[castnum].type].deathsound) S_StartSound (NULL, F_RandomizeSound(mobjinfo[castorder[castnum].type].deathsound)); // [crispy] flippable death sequence castflip = crispy->flipcorpses && castdeath && (mobjinfo[castorder[castnum].type].flags & MF_FLIPPABLE) && (Crispy_Random() & 1); return true; } void F_CastPrint (const char *text) { const char *ch; int c; int cx; int w; int width; // find width ch = text; width = 0; while (ch) { c = *ch++; if (!c) break; c = toupper(c) - HU_FONTSTART; if (c < 0 || c> HU_FONTSIZE) { width += 4; continue; } w = SHORT (hu_font[c]->width); width += w; } // draw it cx = ORIGWIDTH/2-width/2; ch = text; while (ch) { c = *ch++; if (!c) break; c = toupper(c) - HU_FONTSTART; if (c < 0 || c> HU_FONTSIZE) { cx += 4; continue; } w = SHORT (hu_font[c]->width); V_DrawPatch(cx, 180, hu_font[c]); cx+=w; } } // // F_CastDrawer // void F_CastDrawer (void) { spritedef_t* sprdef; spriteframe_t* sprframe; int lump; boolean flip; patch_t* patch; // erase the entire screen to a background V_DrawPatchFullScreen (W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE), false); F_CastPrint (DEH_String(castorder[castnum].name)); // draw the current frame in the middle of the screen sprdef = &sprites[caststate->sprite]; // [crispy] the TNT1 sprite is not supposed to be rendered anyway if (!sprdef->numframes && caststate->sprite == SPR_TNT1) { return; } sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK]; lump = sprframe->lump[castangle]; // [crispy] turnable cast flip = (boolean)sprframe->flip[castangle] ^ castflip; // [crispy] turnable cast, flippable death sequence patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE); if (flip) V_DrawPatchFlipped(ORIGWIDTH/2, 170, patch); else V_DrawPatch(ORIGWIDTH/2, 170, patch); } // // F_DrawPatchCol // static fixed_t dxi, dy, dyi; void F_DrawPatchCol ( int x, patch_t* patch, int col ) { column_t* column; byte* source; pixel_t* dest; pixel_t* desttop; int count; column = (column_t *)((byte *)patch + LONG(patch->columnofs[col >> FRACBITS])); desttop = I_VideoBuffer + x; // step through the posts in a column while (column->topdelta != 0xff ) { int srccol = 0; source = (byte *)column + 3; dest = desttop + ((column->topdelta * dy) >> FRACBITS)*SCREENWIDTH; count = (column->length * dy) >> FRACBITS; while (count--) { *dest = source[srccol >> FRACBITS]; srccol += dyi; dest += SCREENWIDTH; } column = (column_t *)( (byte *)column + column->length + 4 ); } } // // F_BunnyScroll // void F_BunnyScroll (void) { signed int scrolled; int x; patch_t* p1; patch_t* p2; char name[10]; int stage; static int laststage; dxi = (ORIGWIDTH << FRACBITS) / SCREENWIDTH; dy = (SCREENHEIGHT << FRACBITS) / ORIGHEIGHT; dyi = (ORIGHEIGHT << FRACBITS) / SCREENHEIGHT; p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL); p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL); V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); scrolled = (ORIGWIDTH - ((signed int) finalecount-230)/2); if (scrolled > ORIGWIDTH) scrolled = ORIGWIDTH; if (scrolled < 0) scrolled = 0; scrolled <<= FRACBITS; for ( x=0 ; x 6) stage = 6; if (stage > laststage) { S_StartSound (NULL, sfx_pistol); laststage = stage; } DEH_snprintf(name, 10, "END%i", stage); V_DrawPatch((ORIGWIDTH - 13 * 8) / 2, (ORIGHEIGHT - 8 * 8) / 2, W_CacheLumpName (name,PU_CACHE)); } static void F_ArtScreenDrawer(void) { const char *lumpname; if (gameepisode == 3) { F_BunnyScroll(); } else { switch (gameepisode) { case 1: if (gameversion >= exe_ultimate) { lumpname = "CREDIT"; } else { lumpname = "HELP2"; } break; case 2: lumpname = "VICTORY2"; break; case 4: lumpname = "ENDPIC"; break; // [crispy] Sigil case 5: lumpname = "SIGILEND"; if (W_CheckNumForName(DEH_String(lumpname)) == -1) { return; } break; default: return; } lumpname = DEH_String(lumpname); V_DrawPatchFullScreen (W_CacheLumpName(lumpname, PU_CACHE), false); } } // // F_Drawer // void F_Drawer (void) { switch (finalestage) { case F_STAGE_CAST: F_CastDrawer(); break; case F_STAGE_TEXT: F_TextWrite(); break; case F_STAGE_ARTSCREEN: F_ArtScreenDrawer(); break; } } crispy-doom-crispy-doom-5.6.4/src/doom/f_finale.h000066400000000000000000000016261360717211000217020ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __F_FINALE__ #define __F_FINALE__ #include "doomtype.h" #include "d_event.h" // // FINALE // // Called by main loop. boolean F_Responder (event_t* ev); // Called by main loop. void F_Ticker (void); // Called by main loop. void F_Drawer (void); void F_StartFinale (void); #endif crispy-doom-crispy-doom-5.6.4/src/doom/f_wipe.c000066400000000000000000000122101360717211000213720ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Mission begin melt/wipe screen special effect. // #include #include "z_zone.h" #include "i_video.h" #include "v_video.h" #include "m_random.h" #include "doomtype.h" #include "f_wipe.h" // // SCREEN WIPE PACKAGE // // when zero, stop the wipe static boolean go = 0; static pixel_t* wipe_scr_start; static pixel_t* wipe_scr_end; static pixel_t* wipe_scr; void wipe_shittyColMajorXform ( dpixel_t* array, int width, int height ) { int x; int y; dpixel_t* dest; dest = (dpixel_t*) Z_Malloc(width*height*sizeof(*dest), PU_STATIC, 0); for(y=0;y *e) { newval = *w - ticks; if (newval < *e) *w = *e; else *w = newval; changed = true; } else if (*w < *e) { newval = *w + ticks; if (newval > *e) *w = *e; else *w = newval; changed = true; } } w++; e++; } return !changed; } int wipe_exitColorXForm ( int width, int height, int ticks ) { return 0; } static int* y; int wipe_initMelt ( int width, int height, int ticks ) { int i, r; // copy start screen to main screen memcpy(wipe_scr, wipe_scr_start, width*height*sizeof(*wipe_scr)); // makes this wipe faster (in theory) // to have stuff in column-major format wipe_shittyColMajorXform((dpixel_t*)wipe_scr_start, width/2, height); wipe_shittyColMajorXform((dpixel_t*)wipe_scr_end, width/2, height); // setup initial column positions // (y<0 => not ready to scroll yet) y = (int *) Z_Malloc(width*sizeof(int), PU_STATIC, 0); y[0] = -(M_Random()%16); for (i=1;i 0) y[i] = 0; else if (y[i] == -16) y[i] = -15; } return 0; } int wipe_doMelt ( int width, int height, int ticks ) { int i; int j; int dy; int idx; dpixel_t* s; dpixel_t* d; boolean done = true; width/=2; while (ticks--) { for (i=0;ihires); if (y[i]+dy >= height) dy = height - y[i]; s = &((dpixel_t *)wipe_scr_end)[i*height+y[i]]; d = &((dpixel_t *)wipe_scr)[y[i]*width+i]; idx = 0; for (j=dy;j;j--) { d[idx] = *(s++); idx += width; } y[i] += dy; s = &((dpixel_t *)wipe_scr_start)[i*height]; d = &((dpixel_t *)wipe_scr)[y[i]*width+i]; idx = 0; for (j=height-y[i];j;j--) { d[idx] = *(s++); idx += width; } done = false; } } } return done; } int wipe_exitMelt ( int width, int height, int ticks ) { Z_Free(y); Z_Free(wipe_scr_start); Z_Free(wipe_scr_end); return 0; } int wipe_StartScreen ( int x, int y, int width, int height ) { wipe_scr_start = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_start), PU_STATIC, NULL); I_ReadScreen(wipe_scr_start); return 0; } int wipe_EndScreen ( int x, int y, int width, int height ) { wipe_scr_end = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_end), PU_STATIC, NULL); I_ReadScreen(wipe_scr_end); V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr. return 0; } int wipe_ScreenWipe ( int wipeno, int x, int y, int width, int height, int ticks ) { int rc; static int (*wipes[])(int, int, int) = { wipe_initColorXForm, wipe_doColorXForm, wipe_exitColorXForm, wipe_initMelt, wipe_doMelt, wipe_exitMelt }; // initial stuff if (!go) { go = 1; // wipe_scr = (pixel_t *) Z_Malloc(width*height, PU_STATIC, 0); // DEBUG wipe_scr = I_VideoBuffer; (*wipes[wipeno*3])(width, height, ticks); } // do a piece of wipe-in V_MarkRect(0, 0, width, height); rc = (*wipes[wipeno*3+1])(width, height, ticks); // V_DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG // final stuff if (rc) { go = 0; (*wipes[wipeno*3+2])(width, height, ticks); } return !go; } crispy-doom-crispy-doom-5.6.4/src/doom/f_wipe.h000066400000000000000000000022131360717211000214010ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Mission start screen wipe/melt, special effects. // #ifndef __F_WIPE_H__ #define __F_WIPE_H__ // // SCREEN WIPE PACKAGE // enum { // simple gradual pixel change for 8-bit only wipe_ColorXForm, // weird screen melt wipe_Melt, wipe_NUMWIPES }; int wipe_StartScreen ( int x, int y, int width, int height ); int wipe_EndScreen ( int x, int y, int width, int height ); int wipe_ScreenWipe ( int wipeno, int x, int y, int width, int height, int ticks ); #endif crispy-doom-crispy-doom-5.6.4/src/doom/g_game.c000066400000000000000000002212141360717211000213460ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // #include #include #include #include "doomdef.h" #include "doomkeys.h" #include "doomstat.h" #include "deh_main.h" #include "deh_misc.h" #include "deh_bexpars.h" // [crispy] bex_pars[] #include "z_zone.h" #include "f_finale.h" #include "m_argv.h" #include "m_controls.h" #include "m_misc.h" #include "m_menu.h" #include "m_random.h" #include "i_system.h" #include "i_timer.h" #include "i_input.h" #include "i_swap.h" #include "i_video.h" #include "p_setup.h" #include "p_saveg.h" #include "p_extsaveg.h" #include "p_tick.h" #include "d_main.h" #include "wi_stuff.h" #include "hu_stuff.h" #include "st_stuff.h" #include "am_map.h" #include "statdump.h" // Needs access to LFB. #include "v_video.h" #include "w_wad.h" #include "p_local.h" #include "s_sound.h" // Data. #include "dstrings.h" #include "sounds.h" // SKY handling - still the wrong place. #include "r_data.h" #include "r_sky.h" #include "g_game.h" #include "v_trans.h" // [crispy] colored "always run" message #define SAVEGAMESIZE 0x2c000 void G_ReadDemoTiccmd (ticcmd_t* cmd); void G_WriteDemoTiccmd (ticcmd_t* cmd); void G_PlayerReborn (int player); void G_DoReborn (int playernum); void G_DoLoadLevel (void); void G_DoNewGame (void); void G_DoPlayDemo (void); void G_DoCompleted (void); void G_DoVictory (void); void G_DoWorldDone (void); void G_DoSaveGame (void); // Gamestate the last time G_Ticker was called. gamestate_t oldgamestate; gameaction_t gameaction; gamestate_t gamestate; skill_t gameskill; boolean respawnmonsters; int gameepisode; int gamemap; // If non-zero, exit the level after this number of minutes. int timelimit; boolean paused; boolean sendpause; // send a pause event next tic boolean sendsave; // send a save event next tic boolean usergame; // ok to save / end game boolean timingdemo; // if true, exit with report on completion boolean nodrawers; // for comparative timing purposes int starttime; // for comparative timing purposes boolean viewactive; int deathmatch; // only if started as net death boolean netgame; // only true if packets are broadcast boolean playeringame[MAXPLAYERS]; player_t players[MAXPLAYERS]; boolean turbodetected[MAXPLAYERS]; int consoleplayer; // player taking events and displaying int displayplayer; // view being displayed int levelstarttic; // gametic at level start int totalkills, totalitems, totalsecret; // for intermission int extrakills; // [crispy] count spawned monsters int totalleveltimes; // [crispy] CPhipps - total time for all completed levels int demostarttic; // [crispy] fix revenant internal demo bug char *demoname; char *orig_demoname; // [crispy] the name originally chosen for the demo, i.e. without "-00000" boolean demorecording; boolean longtics; // cph's doom 1.91 longtics hack boolean lowres_turn; // low resolution turning for longtics boolean demoplayback; boolean netdemo; byte* demobuffer; byte* demo_p; byte* demoend; boolean singledemo; // quit after playing a demo from cmdline boolean precache = true; // if true, load all graphics at start boolean testcontrols = false; // Invoked by setup to test controls int testcontrols_mousespeed; wbstartstruct_t wminfo; // parms for world map / intermission byte consistancy[MAXPLAYERS][BACKUPTICS]; #define MAXPLMOVE (forwardmove[1]) #define TURBOTHRESHOLD 0x32 fixed_t forwardmove[2] = {0x19, 0x32}; fixed_t sidemove[2] = {0x18, 0x28}; fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn static int *weapon_keys[] = { &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, &key_weapon5, &key_weapon6, &key_weapon7, &key_weapon8 }; // Set to -1 or +1 to switch to the previous or next weapon. static int next_weapon = 0; // Used for prev/next weapon keys. static const struct { 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 } }; #define SLOWTURNTICS 6 #define NUMKEYS 256 #define MAX_JOY_BUTTONS 20 static boolean gamekeydown[NUMKEYS]; static int turnheld; // for accelerative turning static int lookheld; // [crispy] for accelerative looking static boolean mousearray[MAX_MOUSE_BUTTONS + 1]; static boolean *mousebuttons = &mousearray[1]; // allow [-1] // mouse values are used once int mousex; int mousex2; int mousey; static int dclicktime; static boolean dclickstate; static int dclicks; static int dclicktime2; static boolean dclickstate2; static int dclicks2; // joystick values are repeated static int joyxmove; static int joyymove; static int joystrafemove; static int joylook; // [crispy] static boolean joyarray[MAX_JOY_BUTTONS + 1]; static boolean *joybuttons = &joyarray[1]; // allow [-1] static char savename[256]; // [crispy] moved here, made static static int savegameslot; static char savedescription[32]; #define BODYQUESIZE 32 mobj_t* bodyque[BODYQUESIZE]; int bodyqueslot; int vanilla_savegame_limit = 1; int vanilla_demo_limit = 1; int G_CmdChecksum (ticcmd_t* cmd) { size_t i; int sum = 0; for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++) sum += ((int *)cmd)[i]; return sum; } static boolean WeaponSelectable(weapontype_t weapon) { // Can't select the super shotgun in Doom 1. if (weapon == wp_supershotgun && !crispy->havessg) { return false; } // These weapons aren't available in shareware. if ((weapon == wp_plasma || weapon == wp_bfg) && gamemission == doom && gamemode == shareware) { return false; } // Can't select a weapon if we don't own it. if (!players[consoleplayer].weaponowned[weapon]) { return false; } // Can't select the fist if we have the chainsaw, unless // we also have the berserk pack. if (weapon == wp_fist && players[consoleplayer].weaponowned[wp_chainsaw] && !players[consoleplayer].powers[pw_strength]) { return false; } return true; } static int G_NextWeapon(int direction) { weapontype_t weapon; int start_i, i; // Find index in the table. if (players[consoleplayer].pendingweapon == wp_nochange) { weapon = players[consoleplayer].readyweapon; } else { weapon = players[consoleplayer].pendingweapon; } for (i=0; iconsistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; // fraggle: support the old "joyb_speed = 31" hack which // allowed an autorun effect // [crispy] when "always run" is active, // pressing the "run" key will result in walking speed = key_speed >= NUMKEYS || joybspeed >= MAX_JOY_BUTTONS; speed ^= speedkeydown(); forward = side = look = 0; // use two stage accelerative turning // on the keyboard and joystick if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || gamekeydown[key_left]) turnheld += ticdup; else turnheld = 0; if (turnheld < SLOWTURNTICS) tspeed = 2; // slow turn else tspeed = speed; // [crispy] use two stage accelerative looking if (gamekeydown[key_lookdown] || gamekeydown[key_lookup]) { lookheld += ticdup; } else { lookheld = 0; } if (lookheld < SLOWTURNTICS) { lspeed = 1; } else { lspeed = 2; } // [crispy] add quick 180° reverse if (gamekeydown[key_reverse] || mousebuttons[mousebreverse]) { cmd->angleturn += ANG180 >> FRACBITS; gamekeydown[key_reverse] = false; mousebuttons[mousebreverse] = false; } // [crispy] toggle "always run" if (gamekeydown[key_toggleautorun]) { static int joybspeed_old = 2; if (joybspeed >= MAX_JOY_BUTTONS) { joybspeed = joybspeed_old; } else { joybspeed_old = joybspeed; joybspeed = 29; } M_snprintf(playermessage, sizeof(playermessage), "ALWAYS RUN %s%s", crstr[CR_GREEN], (joybspeed >= MAX_JOY_BUTTONS) ? "ON" : "OFF"); player->message = playermessage; S_StartSound(NULL, sfx_swtchn); gamekeydown[key_toggleautorun] = false; } // [crispy] Toggle vertical mouse movement if (gamekeydown[key_togglenovert]) { novert = !novert; M_snprintf(playermessage, sizeof(playermessage), "vertical mouse movement %s%s", crstr[CR_GREEN], !novert ? "ON" : "OFF"); player->message = playermessage; S_StartSound(NULL, sfx_swtchn); gamekeydown[key_togglenovert] = false; } // [crispy] extra high precision IDMYPOS variant, updates for 10 seconds if (player->powers[pw_mapcoords]) { M_snprintf(playermessage, sizeof(playermessage), "X=%.10f Y=%.10f A=%d", (double)player->mo->x/FRACUNIT, (double)player->mo->y/FRACUNIT, player->mo->angle >> 24); player->message = playermessage; player->powers[pw_mapcoords]--; // [crispy] discard instead of going static if (!player->powers[pw_mapcoords]) { player->message = ""; } } // let movement keys cancel each other out if (strafe) { if (gamekeydown[key_right]) { // fprintf(stderr, "strafe right\n"); side += sidemove[speed]; } if (gamekeydown[key_left]) { // fprintf(stderr, "strafe left\n"); side -= sidemove[speed]; } if (joyxmove > 0) side += sidemove[speed]; if (joyxmove < 0) side -= sidemove[speed]; } else { if (gamekeydown[key_right]) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left]) cmd->angleturn += angleturn[tspeed]; if (joyxmove > 0) cmd->angleturn -= angleturn[tspeed]; if (joyxmove < 0) cmd->angleturn += angleturn[tspeed]; } if (gamekeydown[key_up] || gamekeydown[key_alt_up]) // [crispy] add key_alt_* { // fprintf(stderr, "up\n"); forward += forwardmove[speed]; } if (gamekeydown[key_down] || gamekeydown[key_alt_down]) // [crispy] add key_alt_* { // fprintf(stderr, "down\n"); forward -= forwardmove[speed]; } if (joyymove < 0) forward += forwardmove[speed]; if (joyymove > 0) forward -= forwardmove[speed]; if (gamekeydown[key_strafeleft] || gamekeydown[key_alt_strafeleft] // [crispy] add key_alt_* || joybuttons[joybstrafeleft] || mousebuttons[mousebstrafeleft] || joystrafemove < 0) { side -= sidemove[speed]; } if (gamekeydown[key_straferight] || gamekeydown[key_alt_straferight] // [crispy] add key_alt_* || joybuttons[joybstraferight] || mousebuttons[mousebstraferight] || joystrafemove > 0) { side += sidemove[speed]; } // [crispy] look up/down/center keys if (crispy->freelook) { static unsigned int kbdlookctrl = 0; if (gamekeydown[key_lookup] || joylook < 0) { look = lspeed; kbdlookctrl += ticdup; } else if (gamekeydown[key_lookdown] || joylook > 0) { look = -lspeed; kbdlookctrl += ticdup; } else // [crispy] keyboard lookspring if (gamekeydown[key_lookcenter] || (crispy->freelook == FREELOOK_SPRING && kbdlookctrl)) { look = TOCENTER; kbdlookctrl = 0; } } // [crispy] jump keys if (critical->jump) { if (gamekeydown[key_jump] || mousebuttons[mousebjump] || joybuttons[joybjump]) { cmd->arti |= AFLAG_JUMP; } } // buttons cmd->chatchar = HU_dequeueChatChar(); if (gamekeydown[key_fire] || mousebuttons[mousebfire] || joybuttons[joybfire]) cmd->buttons |= BT_ATTACK; if (gamekeydown[key_use] || joybuttons[joybuse] || mousebuttons[mousebuse]) { cmd->buttons |= BT_USE; // clear double clicks if hit use button dclicks = 0; } // If the previous or next weapon button is pressed, the // next_weapon variable is set to change weapons when // we generate a ticcmd. Choose a new weapon. if (gamestate == GS_LEVEL && next_weapon != 0) { i = G_NextWeapon(next_weapon); cmd->buttons |= BT_CHANGE; cmd->buttons |= i << BT_WEAPONSHIFT; } else { // Check weapon keys. for (i=0; ibuttons |= BT_CHANGE; cmd->buttons |= i< 1 ) { dclickstate = mousebuttons[mousebforward]; if (dclickstate) dclicks++; if (dclicks == 2) { cmd->buttons |= BT_USE; dclicks = 0; } else dclicktime = 0; } else { dclicktime += ticdup; if (dclicktime > 20) { dclicks = 0; dclickstate = 0; } } // strafe double click bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; if (bstrafe != dclickstate2 && dclicktime2 > 1 ) { dclickstate2 = bstrafe; if (dclickstate2) dclicks2++; if (dclicks2 == 2) { cmd->buttons |= BT_USE; dclicks2 = 0; } else dclicktime2 = 0; } else { dclicktime2 += ticdup; if (dclicktime2 > 20) { dclicks2 = 0; dclickstate2 = 0; } } } // [crispy] mouse look if ((crispy->freelook && mousebuttons[mousebmouselook]) || crispy->mouselook) { cmd->lookdir = mouse_y_invert ? -mousey : mousey; } else if (!novert) { forward += mousey; } // [crispy] single click on mouse look button centers view if (crispy->freelook) { static unsigned int mbmlookctrl = 0; // [crispy] single click view centering if (mousebuttons[mousebmouselook]) // [crispy] clicked { mbmlookctrl += ticdup; } else // [crispy] released if (mbmlookctrl) { if (crispy->freelook == FREELOOK_SPRING || mbmlookctrl < SLOWTURNTICS) // [crispy] short click { look = TOCENTER; } mbmlookctrl = 0; } } if (strafe) side += mousex2*2; else cmd->angleturn -= mousex*0x8; if (mousex == 0) { // No movement in the previous frame testcontrols_mousespeed = 0; } mousex = mousex2 = mousey = 0; if (forward > MAXPLMOVE) forward = MAXPLMOVE; else if (forward < -MAXPLMOVE) forward = -MAXPLMOVE; if (side > MAXPLMOVE) side = MAXPLMOVE; else if (side < -MAXPLMOVE) side = -MAXPLMOVE; cmd->forwardmove += forward; cmd->sidemove += side; // [crispy] lookdir delta is stored in the lower 4 bits of the lookfly variable if (player->playerstate == PST_LIVE) { if (look < 0) { look += 16; } cmd->lookfly = look; } // special buttons if (sendpause) { sendpause = false; // [crispy] ignore un-pausing in menus during demo recording if (!(menuactive && demorecording && paused) && gameaction != ga_loadgame) { cmd->buttons = BT_SPECIAL | BTS_PAUSE; } } if (sendsave) { sendsave = false; cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<fliplevels) { cmd->angleturn = -cmd->angleturn; cmd->sidemove = -cmd->sidemove; } // low-res turning if (lowres_turn) { static signed short carry = 0; signed short desired_angleturn; desired_angleturn = cmd->angleturn + carry; // round angleturn to the nearest 256 unit boundary // for recording demos with single byte values for turn cmd->angleturn = (desired_angleturn + 128) & 0xff00; // Carry forward the error from the reduced resolution to the // next tic, so that successive small movements can accumulate. carry = desired_angleturn - cmd->angleturn; } } // // G_DoLoadLevel // void G_DoLoadLevel (void) { int i; // Set the sky map. // First thing, we have a dummy sky texture name, // a flat. The data is in the WAD only because // we look for an actual index, instead of simply // setting one. skyflatnum = R_FlatNumForName(DEH_String(SKYFLATNAME)); // The "Sky never changes in Doom II" bug was fixed in // the id Anthology version of doom2.exe for Final Doom. // [crispy] correct "Sky never changes in Doom II" bug if ((gamemode == commercial) && (gameversion == exe_final2 || gameversion == exe_chex || true)) { const char *skytexturename; if (gamemap < 12) { skytexturename = "SKY1"; } else if (gamemap < 21) { skytexturename = "SKY2"; } else { skytexturename = "SKY3"; } skytexturename = DEH_String(skytexturename); skytexture = R_TextureNumForName(skytexturename); } // [crispy] sky texture scales R_InitSkyMap(); levelstarttic = gametic; // for time calculation if (wipegamestate == GS_LEVEL) wipegamestate = -1; // force a wipe gamestate = GS_LEVEL; for (i=0 ; itype == ev_keydown && ev->data1 == key_spy && (singledemo || !deathmatch) ) { // spy mode do { displayplayer++; if (displayplayer == MAXPLAYERS) displayplayer = 0; } while (!playeringame[displayplayer] && displayplayer != consoleplayer); return true; } // any other key pops up menu if in demos if (gameaction == ga_nothing && !singledemo && (demoplayback || gamestate == GS_DEMOSCREEN) ) { if (ev->type == ev_keydown || (ev->type == ev_mouse && ev->data1) || (ev->type == ev_joystick && ev->data1) ) { M_StartControlPanel (); // [crispy] play a sound if the menu is activated with a different key than ESC if (crispy->soundfix) S_StartSound(NULL,sfx_swtchn); return true; } return false; } if (gamestate == GS_LEVEL) { #if 0 if (devparm && ev->type == ev_keydown && ev->data1 == ';') { G_DeathMatchSpawnPlayer (0); return true; } #endif if (HU_Responder (ev)) return true; // chat ate the event if (ST_Responder (ev)) return true; // status window ate it if (AM_Responder (ev)) return true; // automap ate it } if (gamestate == GS_FINALE) { if (F_Responder (ev)) return true; // finale ate the event } if (testcontrols && ev->type == ev_mouse) { // If we are invoked by setup to test the controls, save the // mouse speed so that we can display it on-screen. // Perform a low pass filter on this so that the thermometer // appears to move smoothly. testcontrols_mousespeed = abs(ev->data2); } // If the next/previous weapon keys are pressed, set the next_weapon // variable to change weapons when the next ticcmd is generated. if (ev->type == ev_keydown && ev->data1 == key_prevweapon) { next_weapon = -1; } else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) { next_weapon = 1; } switch (ev->type) { case ev_keydown: if (ev->data1 == key_pause) { sendpause = true; } else if (ev->data1 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); if (mouseSensitivity) mousex = ev->data2*(mouseSensitivity+5)/10; else mousex = 0; // [crispy] disable entirely if (mouseSensitivity_x2) mousex2 = ev->data2*(mouseSensitivity_x2+5)/10; // [crispy] separate sensitivity for strafe else mousex2 = 0; // [crispy] disable entirely if (mouseSensitivity_y) mousey = ev->data3*(mouseSensitivity_y+5)/10; // [crispy] separate sensitivity for y-axis else mousey = 0; // [crispy] disable entirely return true; // eat events case ev_joystick: SetJoyButtons(ev->data1); joyxmove = ev->data2; joyymove = ev->data3; joystrafemove = ev->data4; joylook = ev->data5; return true; // eat events default: break; } return false; } // [crispy] re-read game parameters from command line static void G_ReadGameParms (void) { respawnparm = M_CheckParm ("-respawn"); fastparm = M_CheckParm ("-fast"); nomonsters = M_CheckParm ("-nomonsters"); } // [crispy] take a screenshot after rendering the next frame static void G_CrispyScreenShot() { // [crispy] increase screenshot filename limit V_ScreenShot("DOOM%04i.%s"); players[consoleplayer].message = DEH_String("screen shot"); crispy->cleanscreenshot = 0; crispy->screenshotmsg = 2; } // // G_Ticker // Make ticcmd_ts for the players. // void G_Ticker (void) { int i; int buf; ticcmd_t* cmd; // do player reborns if needed for (i=0 ; icleanscreenshot || crispy->screenshotmsg == 1)) { crispy->screenshotmsg = 4; crispy->post_rendering_hook = G_CrispyScreenShot; } else { G_CrispyScreenShot(); } gameaction = ga_nothing; break; case ga_nothing: break; } } // get commands, check consistancy, // and build new consistancy check buf = (gametic/ticdup)%BACKUPTICS; for (i=0 ; iforwardmove > TURBOTHRESHOLD) { turbodetected[i] = true; } if ((gametic & 31) == 0 && ((gametic >> 5) % MAXPLAYERS) == i && turbodetected[i]) { static char turbomessage[80]; extern char *player_names[4]; M_snprintf(turbomessage, sizeof(turbomessage), "%s is turbo!", player_names[i]); players[consoleplayer].message = turbomessage; turbodetected[i] = false; } if (netgame && !netdemo && !(gametic%ticdup) ) { if (gametic > BACKUPTICS && consistancy[i][buf] != cmd->consistancy) { I_Error ("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] = rndindex; } } } // check for special buttons for (i=0 ; i>BTS_SAVESHIFT; gameaction = ga_savegame; // [crispy] un-pause immediately after saving // (impossible to send save and pause specials within the same tic) if (demorecording && paused) sendpause = true; break; } } } } // Have we just finished displaying an intermission screen? if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION) { WI_End(); } oldgamestate = gamestate; oldleveltime = leveltime; // do main actions switch (gamestate) { case GS_LEVEL: P_Ticker (); ST_Ticker (); AM_Ticker (); HU_Ticker (); break; case GS_INTERMISSION: WI_Ticker (); break; case GS_FINALE: F_Ticker (); break; case GS_DEMOSCREEN: D_PageTicker (); break; } } // // PLAYER STRUCTURE FUNCTIONS // also see P_SpawnPlayer in P_Things // // // G_InitPlayer // Called at the start. // Called by the game initialization functions. // void G_InitPlayer (int player) { // clear everything else to defaults G_PlayerReborn (player); } // // G_PlayerFinishLevel // Can when a player completes a level. // void G_PlayerFinishLevel (int player) { player_t* p; p = &players[player]; memset (p->powers, 0, sizeof (p->powers)); memset (p->cards, 0, sizeof (p->cards)); memset (p->tryopen, 0, sizeof (p->tryopen)); // [crispy] blinking key or skull in the status bar p->mo->flags &= ~MF_SHADOW; // cancel invisibility p->extralight = 0; // cancel gun flashes p->fixedcolormap = 0; // cancel ir gogles p->damagecount = 0; // no palette changes p->bonuscount = 0; // [crispy] reset additional player properties p->lookdir = p->oldlookdir = p->centering = p->jumpTics = p->recoilpitch = p->oldrecoilpitch = p->psp_dy_max = 0; } // // G_PlayerReborn // Called after a player dies // almost everything is cleared and initialized // void G_PlayerReborn (int player) { player_t* p; int i; int frags[MAXPLAYERS]; int killcount; int itemcount; int secretcount; memcpy (frags,players[player].frags,sizeof(frags)); killcount = players[player].killcount; itemcount = players[player].itemcount; secretcount = players[player].secretcount; p = &players[player]; memset (p, 0, sizeof(*p)); memcpy (players[player].frags, frags, sizeof(players[player].frags)); players[player].killcount = killcount; players[player].itemcount = itemcount; players[player].secretcount = secretcount; p->usedown = p->attackdown = true; // don't do anything immediately p->playerstate = PST_LIVE; p->health = deh_initial_health; // Use dehacked value p->readyweapon = p->pendingweapon = wp_pistol; p->weaponowned[wp_fist] = true; p->weaponowned[wp_pistol] = true; p->ammo[am_clip] = deh_initial_bullets; for (i=0 ; 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 // void P_SpawnPlayer (mapthing_t* mthing); boolean G_CheckSpot ( int playernum, mapthing_t* mthing ) { fixed_t x; fixed_t y; subsector_t* ss; mobj_t* mo; int i; if (!players[playernum].mo) { // first spawn of level, before corpses for (i=0 ; ix == mthing->x << FRACBITS && players[i].mo->y == mthing->y << FRACBITS) return false; return true; } x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; if (!P_CheckPosition (players[playernum].mo, x, y) ) return false; // flush an old corpse if needed if (bodyqueslot >= BODYQUESIZE) P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; bodyqueslot++; // spawn a teleport fog ss = R_PointInSubsector (x,y); // The code in the released source looks like this: // // an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) // >> ANGLETOFINESHIFT; // mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an] // , ss->sector->floorheight // , MT_TFOG); // // But 'an' can be a signed value in the DOS version. This means that // we get a negative index and the lookups into finecosine/finesine // end up dereferencing values in finetangent[]. // A player spawning on a deathmatch start facing directly west spawns // "silently" with no spawn fog. Emulate this. // // This code is imported from PrBoom+. { fixed_t xa, ya; signed int an; // This calculation overflows in Vanilla Doom, but here we deliberately // avoid integer overflow as it is undefined behavior, so the value of // 'an' will always be positive. an = (ANG45 >> ANGLETOFINESHIFT) * ((signed int) mthing->angle / 45); switch (an) { case 4096: // -4096: xa = finetangent[2048]; // finecosine[-4096] ya = finetangent[0]; // finesine[-4096] break; case 5120: // -3072: xa = finetangent[3072]; // finecosine[-3072] ya = finetangent[1024]; // finesine[-3072] break; case 6144: // -2048: xa = finesine[0]; // finecosine[-2048] ya = finetangent[2048]; // finesine[-2048] break; case 7168: // -1024: xa = finesine[1024]; // finecosine[-1024] ya = finetangent[3072]; // finesine[-1024] break; case 0: case 1024: case 2048: case 3072: xa = finecosine[an]; ya = finesine[an]; break; default: I_Error("G_CheckSpot: unexpected angle %d\n", an); xa = ya = 0; break; } 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 i,j; int selections; selections = deathmatch_p - deathmatchstarts; if (selections < 4) I_Error ("Only %i deathmatch spots, 4 required", selections); for (j=0 ; j<20 ; j++) { i = P_Random() % selections; if (G_CheckSpot (playernum, &deathmatchstarts[i]) ) { deathmatchstarts[i].type = playernum+1; P_SpawnPlayer (&deathmatchstarts[i]); return; } } // no good spot, so the player will probably get stuck P_SpawnPlayer (&playerstarts[playernum]); } // [crispy] clear the "savename" variable, // i.e. restart level from scratch upon resurrection static inline void G_ClearSavename () { M_StringCopy(savename, "", sizeof(savename)); } // // G_DoReborn // void G_DoReborn (int playernum) { int i; if (!netgame) { // [crispy] if the player dies and the game has been loaded or saved // in the mean time, reload that savegame instead of restarting the level // when "Run" is pressed upon resurrection if (crispy->singleplayer && *savename && speedkeydown()) gameaction = ga_loadgame; else { // reload the level from scratch gameaction = ga_loadlevel; G_ClearSavename(); } } else { // respawn at the start // 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 (&playerstarts[playernum]); return; } // try to spawn at one of the other players spots for (i=0 ; isingleplayer ) { if (secretexit) switch(gamemap) { case 4: wminfo.next = 8; break; } else switch(gamemap) { case 9: wminfo.next = 4; break; default: wminfo.next = gamemap; } } else if ( gamemission == pack_master && gamemap <= 21 && crispy->singleplayer ) { wminfo.next = gamemap; } else if ( gamemode == commercial) { if (secretexit) if (gamemap == 2 && critical->havemap33) wminfo.next = 32; else switch(gamemap) { case 15: wminfo.next = 30; break; case 31: wminfo.next = 31; break; } else if (gamemap == 33 && critical->havemap33) wminfo.next = 2; else switch(gamemap) { case 31: case 32: wminfo.next = 15; break; default: wminfo.next = gamemap; } } else { if (secretexit) { if (critical->havee1m10 && gameepisode == 1 && gamemap == 1) wminfo.next = 9; // [crispy] go to secret level E1M10 "Sewers" else wminfo.next = 8; // go to secret level } else if (gamemap == 9) { // returning from secret level switch (gameepisode) { case 1: wminfo.next = 3; break; case 2: wminfo.next = 5; break; case 3: case 5: // [crispy] Sigil wminfo.next = 6; break; case 4: wminfo.next = 2; break; } } else if (critical->havee1m10 && gameepisode == 1 && gamemap == 10) wminfo.next = 1; // [crispy] returning from secret level E1M10 "Sewers" else wminfo.next = gamemap; // go to next level } wminfo.maxkills = totalkills; wminfo.maxitems = totalitems; wminfo.maxsecret = totalsecret; wminfo.maxfrags = 0; // Set par time. Exceptions are added for purposes of // statcheck regression testing. if (gamemode == commercial) { // map33 reads its par time from beyond the cpars[] array if (gamemap == 33) { int cpars32; memcpy(&cpars32, DEH_String(GAMMALVL0), sizeof(int)); cpars32 = LONG(cpars32); wminfo.partime = TICRATE*cpars32; } // [crispy] support [PARS] sections in BEX files else if (bex_cpars[gamemap-1]) { wminfo.partime = TICRATE*bex_cpars[gamemap-1]; } // [crispy] single player par times for NRFTL else if (gamemission == pack_nerve && crispy->singleplayer) { wminfo.partime = TICRATE*npars[gamemap-1]; } else { wminfo.partime = TICRATE*cpars[gamemap-1]; } } // Doom episode 4 doesn't have a par time, so this // overflows into the cpars array. else if (gameepisode < 4 || // [crispy] single player par times for episode 4 (gameepisode == 4 && crispy->singleplayer) || // [crispy] par times for Sigil gameepisode == 5) { // [crispy] support [PARS] sections in BEX files if (bex_pars[gameepisode][gamemap]) { wminfo.partime = TICRATE*bex_pars[gameepisode][gamemap]; } else wminfo.partime = TICRATE*pars[gameepisode][gamemap]; } else { wminfo.partime = TICRATE*cpars[gamemap]; } wminfo.pnum = consoleplayer; for (i=0 ; ihavee1m10 || gameepisode != 1 || gamemap != 1) players[consoleplayer].didsecret = true; if ( gamemission == pack_nerve && crispy->singleplayer ) { switch (gamemap) { case 8: F_StartFinale (); break; } } else if ( gamemission == pack_master && crispy->singleplayer ) { switch (gamemap) { case 20: if (secretexit) break; case 21: F_StartFinale (); break; } } else if ( gamemode == commercial ) { switch (gamemap) { case 15: case 31: if (!secretexit) break; case 6: case 11: case 20: case 30: F_StartFinale (); break; } } // [crispy] display tally screen after ExM8 else if ( gamemap == 8 ) { gameaction = ga_victory; } } void G_DoWorldDone (void) { gamestate = GS_LEVEL; gamemap = wminfo.next+1; G_DoLoadLevel (); gameaction = ga_nothing; viewactive = true; } // // G_InitFromSavegame // Can be called by the startup code or the menu task. // extern boolean setsizeneeded; void R_ExecuteSetViewSize (void); void G_LoadGame (char* name) { M_StringCopy(savename, name, sizeof(savename)); gameaction = ga_loadgame; } int savedleveltime = 0; // [crispy] moved here for level time logging void G_DoLoadGame (void) { // [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 (startloadgame == -1) { netdemo = false; netgame = false; deathmatch = false; } gameaction = ga_nothing; save_stream = fopen(savename, "rb"); if (save_stream == NULL) { I_Error("Could not load savegame %s", savename); } // [crispy] read extended savegame data if (crispy->extsaveg) { // [crispy] first pass, read "savewadfilename" P_ReadExtendedSaveGameData(0); } // [crispy] check if WAD file is valid to restore saved map if (savewadfilename) { // [crispy] strings are not equal if (!savemaplumpinfo || // [crispy] case-insensitive, so "doom.wad" matches "DOOM.WAD" strcasecmp(savewadfilename, W_WadNameForLump(savemaplumpinfo))) { M_ForceLoadGame(); fclose(save_stream); return; } else // [crispy] strings are equal, but not identical if (savewadfilename != W_WadNameForLump(savemaplumpinfo)) { free(savewadfilename); } } savewadfilename = NULL; savegame_error = false; if (!P_ReadSaveGameHeader()) { // [crispy] indicate game version mismatch extern void M_LoadGameVerMismatch (); M_LoadGameVerMismatch(); fclose(save_stream); return; } savedleveltime = leveltime; // load a base level G_InitNew (gameskill, gameepisode, gamemap); leveltime = savedleveltime; savedleveltime = 0; // dearchive all the modifications P_UnArchivePlayers (); P_UnArchiveWorld (); P_UnArchiveThinkers (); P_UnArchiveSpecials (); P_RestoreTargets (); // [crispy] restore mobj->target and mobj->tracer pointers if (!P_ReadSaveGameEOF()) I_Error ("Bad savegame"); // [crispy] read more extended savegame data if (crispy->extsaveg) { P_ReadExtendedSaveGameData(1); } fclose(save_stream); if (setsizeneeded) R_ExecuteSetViewSize (); // draw the pattern into the back screen R_FillBackScreen (); // [crispy] if the player is dead in this savegame, // do not consider it for reload if (players[consoleplayer].health <= 0) G_ClearSavename(); // [crisy] once loaded from the command line, // the next savegame will be loaded from the menu startloadgame = -1; } // // G_SaveGame // Called by the menu task. // Description is a 24 byte text string // void G_SaveGame ( int slot, char* description ) { savegameslot = slot; M_StringCopy(savedescription, description, sizeof(savedescription)); sendsave = true; } void G_DoSaveGame (void) { char *savegame_file; char *temp_savegame_file; char *recovery_savegame_file; recovery_savegame_file = NULL; temp_savegame_file = P_TempSaveGameFile(); savegame_file = P_SaveGameFile(savegameslot); // Open the savegame file for writing. We write to a temporary file // and then rename it at the end if it was successfully written. // This prevents an existing savegame from being overwritten by // a corrupted one, or if a savegame buffer overrun occurs. save_stream = fopen(temp_savegame_file, "wb"); if (save_stream == NULL) { // Failed to save the game, so we're going to have to abort. But // to be nice, save to somewhere else before we call I_Error(). recovery_savegame_file = M_TempFile("recovery.dsg"); save_stream = fopen(recovery_savegame_file, "wb"); if (save_stream == NULL) { I_Error("Failed to open either '%s' or '%s' to write savegame.", temp_savegame_file, recovery_savegame_file); } } savegame_error = false; P_WriteSaveGameHeader(savedescription); // [crispy] some logging when saving { const int ltime = leveltime / TICRATE, ttime = (totalleveltimes + leveltime) / TICRATE; extern const char *skilltable[]; fprintf(stderr, "G_DoSaveGame: Episode %d, Map %d, %s, Time %d:%02d:%02d, Total %d:%02d:%02d.\n", gameepisode, gamemap, skilltable[BETWEEN(0,5,(int) gameskill+1)], ltime/3600, (ltime%3600)/60, ltime%60, ttime/3600, (ttime%3600)/60, ttime%60); } P_ArchivePlayers (); P_ArchiveWorld (); P_ArchiveThinkers (); P_ArchiveSpecials (); P_WriteSaveGameEOF(); // [crispy] write extended savegame data if (crispy->extsaveg) { P_WriteExtendedSaveGameData(); } // [crispy] unconditionally disable savegame and demo limits /* // Enforce the same savegame size limit as in Vanilla Doom, // except if the vanilla_savegame_limit setting is turned off. if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE) { I_Error("Savegame buffer overrun"); } */ // Finish up, close the savegame file. fclose(save_stream); if (recovery_savegame_file != NULL) { // We failed to save to the normal location, but we wrote a // recovery file to the temp directory. Now we can bomb out // with an error. I_Error("Failed to open savegame file '%s' for writing.\n" "But your game has been saved to '%s' for recovery.", temp_savegame_file, recovery_savegame_file); } // Now rename the temporary savegame file to the actual savegame // file, overwriting the old savegame if there was one there. remove(savegame_file); rename(temp_savegame_file, savegame_file); gameaction = ga_nothing; M_StringCopy(savedescription, "", sizeof(savedescription)); M_StringCopy(savename, savegame_file, sizeof(savename)); players[consoleplayer].message = DEH_String(GGSAVED); // draw the pattern into the back screen R_FillBackScreen (); } // // G_InitNew // Can be called by the startup code or the menu task, // consoleplayer, displayplayer, playeringame[] should be set. // skill_t d_skill; int d_episode; int d_map; void G_DeferedInitNew ( skill_t skill, int episode, int map) { d_skill = skill; d_episode = episode; d_map = map; G_ClearSavename(); gameaction = ga_newgame; // [crispy] if a new game is started during demo recording, start a new demo if (demorecording) { G_CheckDemoStatus(); Z_Free(demoname); G_RecordDemo(orig_demoname); G_BeginRecording(); } } void G_DoNewGame (void) { demoplayback = false; netdemo = false; netgame = false; deathmatch = false; playeringame[1] = playeringame[2] = playeringame[3] = 0; // [crispy] do not reset -respawn, -fast and -nomonsters parameters /* respawnparm = false; fastparm = false; nomonsters = false; */ consoleplayer = 0; G_InitNew (d_skill, d_episode, d_map); gameaction = ga_nothing; } void G_InitNew ( skill_t skill, int episode, int map ) { const char *skytexturename; int i; // [crispy] make sure "fast" parameters are really only applied once static boolean fast_applied; if (paused) { paused = false; S_ResumeSound (); } /* // Note: This commented-out block of code was added at some point // between the DOS version(s) and the Doom source release. It isn't // found in disassemblies of the DOS version and causes IDCLEV and // the -warp command line parameter to behave differently. // This is left here for posterity. // This was quite messy with SPECIAL and commented parts. // Supposedly hacks to make the latest edition work. // It might not work properly. if (episode < 1) episode = 1; if ( gamemode == retail ) { if (episode > 4) episode = 4; } else if ( gamemode == shareware ) { if (episode > 1) episode = 1; // only start episode 1 on shareware } else { if (episode > 3) episode = 3; } */ if (skill > sk_nightmare) skill = sk_nightmare; // [crispy] only fix episode/map if it doesn't exist if (P_GetNumForMap(episode, map, false) < 0) { if (gameversion >= exe_ultimate) { if (episode == 0) { episode = 4; } } else { if (episode < 1) { episode = 1; } if (episode > 3) { episode = 3; } } if (episode > 1 && gamemode == shareware) { episode = 1; } if (map < 1) map = 1; if ( (map > 9) && ( gamemode != commercial) ) { // [crispy] support E1M10 "Sewers" if (!crispy->havee1m10 || episode != 1) map = 9; else map = 10; } } M_ClearRandom (); if (skill == sk_nightmare || respawnparm ) respawnmonsters = true; else respawnmonsters = false; // [crispy] make sure "fast" parameters are really only applied once if ((fastparm || skill == sk_nightmare) && !fast_applied) { for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++) // [crispy] Fix infinite loop caused by Demon speed bug if (states[i].tics > 1) { states[i].tics >>= 1; } mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT; mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT; mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT; fast_applied = true; } else if (!fastparm && skill != sk_nightmare && fast_applied) { 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; fast_applied = false; } // force players to be initialized upon first level load for (i=0 ; iforwardmove = ((signed char)*demo_p++); cmd->sidemove = ((signed char)*demo_p++); // If this is a longtics demo, read back in higher resolution if (longtics) { cmd->angleturn = *demo_p++; cmd->angleturn |= (*demo_p++) << 8; } else { cmd->angleturn = ((unsigned char) *demo_p++)<<8; } cmd->buttons = (unsigned char)*demo_p++; // [crispy] increase demo tics counter // applies to both recording and playback, // because G_WriteDemoTiccmd() calls G_ReadDemoTiccmd() once defdemotics++; } // Increase the size of the demo buffer to allow unlimited demos static void IncreaseDemoBuffer(void) { int current_length; byte *new_demobuffer; byte *new_demop; int new_length; // Find the current size current_length = demoend - demobuffer; // Generate a new buffer twice the size new_length = current_length * 2; new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0); new_demop = new_demobuffer + (demo_p - demobuffer); // Copy over the old data memcpy(new_demobuffer, demobuffer, current_length); // Free the old buffer and point the demo pointers at the new buffer. Z_Free(demobuffer); demobuffer = new_demobuffer; demo_p = new_demop; demoend = demobuffer + new_length; } void G_WriteDemoTiccmd (ticcmd_t* cmd) { byte *demo_start; if (gamekeydown[key_demo_quit]) // press q to end demo recording G_CheckDemoStatus (); demo_start = demo_p; *demo_p++ = cmd->forwardmove; *demo_p++ = cmd->sidemove; // If this is a longtics demo, record in higher resolution if (longtics) { *demo_p++ = (cmd->angleturn & 0xff); *demo_p++ = (cmd->angleturn >> 8) & 0xff; } else { *demo_p++ = cmd->angleturn >> 8; } *demo_p++ = cmd->buttons; // reset demo pointer back demo_p = demo_start; if (demo_p > demoend - 16) { // [crispy] unconditionally disable savegame and demo limits /* if (vanilla_demo_limit) { // no more space G_CheckDemoStatus (); return; } else */ { // Vanilla demo limit disabled: unlimited // demo lengths! IncreaseDemoBuffer(); } } G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same } // // G_RecordDemo // void G_RecordDemo (char *name) { size_t demoname_size; int i; int maxsize; // [crispy] demo file name suffix counter static unsigned int j = 0; FILE *fp = NULL; // [crispy] the name originally chosen for the demo, i.e. without "-00000" if (!orig_demoname) { orig_demoname = name; } usergame = false; demoname_size = strlen(name) + 5 + 6; // [crispy] + 6 for "-00000" demoname = Z_Malloc(demoname_size, PU_STATIC, NULL); M_snprintf(demoname, demoname_size, "%s.lmp", name); // [crispy] prevent overriding demos by adding a file name suffix for ( ; j <= 99999 && (fp = fopen(demoname, "rb")) != NULL; j++) { M_snprintf(demoname, demoname_size, "%s-%05d.lmp", name, j); fclose (fp); } maxsize = 0x20000; //! // @arg // @category demo // @vanilla // // Specify the demo buffer size (KiB) // i = M_CheckParmWithArgs("-maxdemo", 1); if (i) maxsize = atoi(myargv[i+1])*1024; demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL); demoend = demobuffer + maxsize; demorecording = true; } // Get the demo version code appropriate for the version set in gameversion. int G_VanillaVersionCode(void) { switch (gameversion) { case exe_doom_1_666: return 106; case exe_doom_1_7: return 107; case exe_doom_1_8: return 108; case exe_doom_1_9: default: // All other versions are variants on v1.9: return 109; } } void G_BeginRecording (void) { int i; demo_p = demobuffer; //! // @category demo // // Record a high resolution "Doom 1.91" demo. // longtics = D_NonVanillaRecord(M_ParmExists("-longtics"), "Doom 1.91 demo format"); // If not recording a longtics demo, record in low res lowres_turn = !longtics; if (longtics) { *demo_p++ = DOOM_191_VERSION; } else if (gameversion > exe_doom_1_2) { *demo_p++ = G_VanillaVersionCode(); } *demo_p++ = gameskill; *demo_p++ = gameepisode; *demo_p++ = gamemap; if (longtics || gameversion > exe_doom_1_2) { *demo_p++ = deathmatch; *demo_p++ = respawnparm; *demo_p++ = fastparm; *demo_p++ = nomonsters; *demo_p++ = consoleplayer; } for (i=0 ; idemowarp || demorecording) { nodrawers = true; singletics = true; } } // Generate a string describing a demo version static const char *DemoVersionDescription(int version) { static char resultbuf[16]; switch (version) { case 104: return "v1.4"; case 105: return "v1.5"; case 106: return "v1.6/v1.666"; case 107: return "v1.7/v1.7a"; case 108: return "v1.8"; case 109: return "v1.9"; case 111: return "v1.91 hack demo?"; default: break; } // Unknown version. Perhaps this is a pre-v1.4 IWAD? If the version // byte is in the range 0-4 then it can be a v1.0-v1.2 demo. if (version >= 0 && version <= 4) { return "v1.0/v1.1/v1.2"; } else { M_snprintf(resultbuf, sizeof(resultbuf), "%i.%i (unknown)", version / 100, version % 100); return resultbuf; } } void G_DoPlayDemo (void) { skill_t skill; int i, lumpnum, episode, map; int demoversion; boolean olddemo = false; int lumplength; // [crispy] // [crispy] in demo continue mode free the obsolete demo buffer // of size 'maxsize' previously allocated in G_RecordDemo() if (demorecording) { Z_Free(demobuffer); } lumpnum = W_GetNumForName(defdemoname); gameaction = ga_nothing; demobuffer = W_CacheLumpNum(lumpnum, PU_STATIC); demo_p = demobuffer; // [crispy] ignore empty demo lumps lumplength = W_LumpLength(lumpnum); if (lumplength < 0xd) { demoplayback = true; G_CheckDemoStatus(); return; } demoversion = *demo_p++; if (demoversion >= 0 && demoversion <= 4) { olddemo = true; demo_p--; } longtics = false; // Longtics demos use the modified format that is generated by cph's // hacked "v1.91" doom exe. This is a non-vanilla extension. if (D_NonVanillaPlayback(demoversion == DOOM_191_VERSION, lumpnum, "Doom 1.91 demo format")) { longtics = true; } else if (demoversion != G_VanillaVersionCode() && !(gameversion <= exe_doom_1_2 && olddemo)) { const char *message = "Demo is from a different game version!\n" "(read %i, should be %i)\n" "\n" "*** You may need to upgrade your version " "of Doom to v1.9. ***\n" " See: https://www.doomworld.com/classicdoom" "/info/patches.php\n" " This appears to be %s."; if (singledemo) I_Error(message, demoversion, G_VanillaVersionCode(), DemoVersionDescription(demoversion)); // [crispy] make non-fatal else { fprintf(stderr, message, demoversion, G_VanillaVersionCode(), DemoVersionDescription(demoversion)); fprintf(stderr, "\n"); demoplayback = true; G_CheckDemoStatus(); return; } } skill = *demo_p++; episode = *demo_p++; map = *demo_p++; if (!olddemo) { deathmatch = *demo_p++; respawnparm = *demo_p++; fastparm = *demo_p++; nomonsters = *demo_p++; consoleplayer = *demo_p++; } else { deathmatch = 0; respawnparm = 0; fastparm = 0; nomonsters = 0; consoleplayer = 0; } for (i=0 ; i 0 || M_CheckParm("-netdemo") > 0) { netgame = true; netdemo = true; // [crispy] impossible to continue a multiplayer demo demorecording = false; } // don't spend a lot of time in loadlevel precache = false; // [crispy] support playing demos from savegames if (startloadgame >= 0) { M_StringCopy(savename, P_SaveGameFile(startloadgame), sizeof(savename)); G_DoLoadGame(); } else { G_InitNew (skill, episode, map); } precache = true; starttime = I_GetTime (); demostarttic = gametic; // [crispy] fix revenant internal demo bug usergame = false; demoplayback = true; // [crispy] update the "singleplayer" variable CheckCrispySingleplayer(!demorecording && !demoplayback && !netgame); // [crispy] demo progress bar { int i, numplayersingame = 0; byte *demo_ptr = demo_p; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { numplayersingame++; } } deftotaldemotics = defdemotics = 0; while (*demo_ptr != DEMOMARKER && (demo_ptr - demobuffer) < lumplength) { demo_ptr += numplayersingame * (longtics ? 5 : 4); deftotaldemotics++; } } } // // G_TimeDemo // void G_TimeDemo (char* name) { //! // @category video // @vanilla // // Disable rendering the screen entirely. // nodrawers = M_CheckParm ("-nodraw"); timingdemo = true; singletics = true; defdemoname = name; gameaction = ga_playdemo; } /* =================== = = G_CheckDemoStatus = = Called after a death or level completion to allow demos to be cleaned up = Returns true if a new demo loop action will take place =================== */ boolean G_CheckDemoStatus (void) { int endtime; if (timingdemo) { float fps; int realtics; endtime = I_GetTime (); realtics = endtime - starttime; fps = ((float) gametic * TICRATE) / realtics; // Prevent recursive calls timingdemo = false; demoplayback = false; I_Error ("timed %i gametics in %i realtics (%f fps)", gametic, realtics, fps); } if (demoplayback) { W_ReleaseLumpName(defdemoname); demoplayback = false; netdemo = false; netgame = false; deathmatch = false; playeringame[1] = playeringame[2] = playeringame[3] = 0; // [crispy] leave game parameters intact when continuing a demo if (!demorecording) { respawnparm = false; fastparm = false; nomonsters = false; } consoleplayer = 0; // [crispy] in demo continue mode increase the demo buffer and // continue recording once we are done with playback if (demorecording) { demoend = demo_p; IncreaseDemoBuffer(); nodrawers = false; singletics = false; // [crispy] start music for the current level if (gamestate == GS_LEVEL) { S_Start(); } return true; } if (singledemo) I_Quit (); else D_AdvanceDemo (); return true; } if (demorecording) { *demo_p++ = DEMOMARKER; M_WriteFile (demoname, demobuffer, demo_p - demobuffer); Z_Free (demobuffer); demorecording = false; // [crispy] if a new game is started during demo recording, start a new demo if (gameaction != ga_newgame) { I_Error ("Demo %s recorded",demoname); } else { fprintf(stderr, "Demo %s recorded\n",demoname); } } return false; } crispy-doom-crispy-doom-5.6.4/src/doom/g_game.h000066400000000000000000000036021360717211000213520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Duh. // #ifndef __G_GAME__ #define __G_GAME__ #include "doomdef.h" #include "d_event.h" #include "d_ticcmd.h" // // GAME // void G_DeathMatchSpawnPlayer (int playernum); void G_InitNew (skill_t skill, int episode, int map); // Can be called by the startup code or M_Responder. // A normal game starts at map 1, // but a warp test can start elsewhere void G_DeferedInitNew (skill_t skill, int episode, int map); void G_DeferedPlayDemo (const char* demo); // Can be called by the startup code or M_Responder, // calls P_SetupLevel or W_EnterWorld. void G_LoadGame (char* name); void G_DoLoadGame (void); // Called by M_Responder. void G_SaveGame (int slot, char* description); // Only called by startup code. void G_RecordDemo (char* name); void G_BeginRecording (void); void G_PlayDemo (char* name); void G_TimeDemo (char* name); boolean G_CheckDemoStatus (void); void G_ExitLevel (void); void G_SecretExitLevel (void); void G_WorldDone (void); // Read current data from inputs and build a player movement command. void G_BuildTiccmd (ticcmd_t *cmd, int maketic); void G_Ticker (void); boolean G_Responder (event_t* ev); void G_ScreenShot (void); void G_DrawMouseSpeedBox(void); int G_VanillaVersionCode(void); extern int vanilla_savegame_limit; extern int vanilla_demo_limit; #endif crispy-doom-crispy-doom-5.6.4/src/doom/hu_lib.c000066400000000000000000000155231360717211000213750ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: heads-up text and input code // #include #include "doomdef.h" #include "doomkeys.h" #include "v_video.h" #include "i_swap.h" #include "hu_lib.h" #include "r_local.h" #include "r_draw.h" #include "v_trans.h" // [crispy] colored HUlib_drawTextLine() // boolean : whether the screen is always erased #define noterased viewwindowx extern boolean automapactive; // in AM_map.c void HUlib_init(void) { } void HUlib_clearTextLine(hu_textline_t* t) { t->len = 0; t->l[0] = 0; t->needsupdate = true; } void HUlib_initTextLine ( hu_textline_t* t, int x, int y, patch_t** f, int sc ) { t->x = x; t->y = y; t->f = f; t->sc = sc; HUlib_clearTextLine(t); } boolean HUlib_addCharToTextLine ( hu_textline_t* t, char ch ) { if (t->len == HU_MAXLINELENGTH) return false; else { t->l[t->len++] = ch; t->l[t->len] = 0; t->needsupdate = 4; return true; } } boolean HUlib_delCharFromTextLine(hu_textline_t* t) { if (!t->len) return false; else { t->l[--t->len] = 0; t->needsupdate = 4; return true; } } void HUlib_drawTextLine ( hu_textline_t* l, boolean drawcursor ) { int i; int w; int x; int y; unsigned char c; // draw the new stuff x = l->x; y = l->y; // [crispy] support line breaks for (i=0;ilen;i++) { c = toupper(l->l[i]); // [crispy] support multi-colored text lines if (c == cr_esc) { if (l->l[i+1] >= '0' && l->l[i+1] <= '0' + CRMAX - 1) { i++; dp_translation = (crispy->coloredhud & COLOREDHUD_TEXT) ? cr[(int) (l->l[i] - '0')] : NULL; } } else // [crispy] support line breaks if (c == '\n') { x = l->x; y += SHORT(l->f[0]->height) + 1; } else if (c != ' ' && c >= l->sc && c <= '_') { w = SHORT(l->f[c - l->sc]->width); if (x+w > ORIGWIDTH) break; V_DrawPatchDirect(x, y, l->f[c - l->sc]); x += w; } else { x += 4; if (x >= ORIGWIDTH) break; } } // draw the cursor if requested if (drawcursor && x + SHORT(l->f['_' - l->sc]->width) <= ORIGWIDTH) { V_DrawPatchDirect(x, y, l->f['_' - l->sc]); } } // sorta called by HU_Erase and just better darn get things straight void HUlib_eraseTextLine(hu_textline_t* l) { int lh; int y; int yoffset; // Only erases when NOT in automap and the screen is reduced, // and the text must either need updating or refreshing // (because of a recent change back from the automap) if (!automapactive && viewwindowx && (l->needsupdate || crispy->cleanscreenshot || crispy->screenshotmsg == 4)) { lh = (SHORT(l->f[0]->height) + 1) << crispy->hires; // [crispy] support line breaks yoffset = 1; for (y = 0; y < l->len; y++) { if (l->l[y] == '\n') { yoffset++; } } lh *= yoffset; for (y=(l->y << crispy->hires),yoffset=y*SCREENWIDTH ; y<(l->y << crispy->hires)+lh ; y++,yoffset+=SCREENWIDTH) { if (y < viewwindowy || y >= viewwindowy + viewheight) R_VideoErase(yoffset, SCREENWIDTH); // erase entire line else { R_VideoErase(yoffset, viewwindowx); // erase left border R_VideoErase(yoffset + viewwindowx + scaledviewwidth, viewwindowx); // erase right border } } } if (l->needsupdate) l->needsupdate--; } void HUlib_initSText ( hu_stext_t* s, int x, int y, int h, patch_t** font, int startchar, boolean* on ) { int i; s->h = h; s->on = on; s->laston = true; s->cl = 0; for (i=0;il[i], x, y - i*(SHORT(font[0]->height)+1), font, startchar); } void HUlib_addLineToSText(hu_stext_t* s) { int i; // add a clear line if (++s->cl == s->h) s->cl = 0; HUlib_clearTextLine(&s->l[s->cl]); // everything needs updating for (i=0 ; ih ; i++) s->l[i].needsupdate = 4; } 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++)); } 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 } } 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; } void HUlib_initIText ( hu_itext_t* it, int x, int y, patch_t** font, int startchar, boolean* on ) { it->lm = 0; // default left margin is start of text it->on = on; it->laston = true; HUlib_initTextLine(&it->l, x, y, font, startchar); } // The following deletion routines adhere to the left margin restriction void HUlib_delCharFromIText(hu_itext_t* it) { if (it->l.len != it->lm) HUlib_delCharFromTextLine(&it->l); } void HUlib_eraseLineFromIText(hu_itext_t* it) { while (it->lm != it->l.len) HUlib_delCharFromTextLine(&it->l); } // Resets left margin as well void HUlib_resetIText(hu_itext_t* it) { it->lm = 0; HUlib_clearTextLine(&it->l); } void HUlib_addPrefixToIText ( hu_itext_t* it, char* str ) { while (*str) HUlib_addCharToTextLine(&it->l, *(str++)); it->lm = it->l.len; } // wrapper function for handling general keyed input. // returns true if it ate the key boolean HUlib_keyInIText ( hu_itext_t* it, unsigned char ch ) { ch = toupper(ch); if (ch >= ' ' && ch <= '_') HUlib_addCharToTextLine(&it->l, (char) ch); else if (ch == KEY_BACKSPACE) HUlib_delCharFromIText(it); else if (ch != KEY_ENTER) return false; // did not eat key return true; // ate the key } void HUlib_drawIText(hu_itext_t* it) { hu_textline_t *l = &it->l; if (!*it->on) return; HUlib_drawTextLine(l, true); // draw the line w/ cursor } void HUlib_eraseIText(hu_itext_t* it) { if (it->laston && !*it->on) it->l.needsupdate = 4; HUlib_eraseTextLine(&it->l); it->laston = *it->on; } crispy-doom-crispy-doom-5.6.4/src/doom/hu_lib.h000066400000000000000000000070701360717211000214000ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // #ifndef __HULIB__ #define __HULIB__ // We are referring to patches. #include "r_defs.h" // font stuff #define HU_CHARERASE KEY_BACKSPACE #define HU_MAXLINES 4 #define HU_MAXLINELENGTH 80 // // Typedefs of widgets // // Text Line widget // (parent of Scrolling Text and Input Text widgets) typedef struct { // left-justified position of scrolling text window int x; int y; patch_t** f; // font int sc; // start character char l[HU_MAXLINELENGTH+1]; // line of text int len; // current line length // whether this line needs to be udpated int needsupdate; } hu_textline_t; // Scrolling Text window widget // (child of Text Line widget) typedef struct { hu_textline_t l[HU_MAXLINES]; // text lines to draw int h; // height in lines int cl; // current line number // pointer to boolean stating whether to update window boolean* on; boolean laston; // last value of *->on. } hu_stext_t; // Input Text Line widget // (child of Text Line widget) typedef struct { hu_textline_t l; // text line to input on // left margin past which I am not to delete characters int lm; // pointer to boolean stating whether to update window boolean* on; boolean laston; // last value of *->on; } hu_itext_t; // // Widget creation, access, and update routines // // initializes heads-up widget library void HUlib_init(void); // // textline code // // clear a line of text void HUlib_clearTextLine(hu_textline_t *t); void HUlib_initTextLine(hu_textline_t *t, int x, int y, patch_t **f, int sc); // returns success boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch); // returns success boolean HUlib_delCharFromTextLine(hu_textline_t *t); // draws tline void HUlib_drawTextLine(hu_textline_t *l, boolean drawcursor); // erases text line void HUlib_eraseTextLine(hu_textline_t *l); // // Scrolling Text window widget routines // // ? void HUlib_initSText ( hu_stext_t* s, int x, int y, int h, patch_t** font, int startchar, boolean* on ); // add a new line void HUlib_addLineToSText(hu_stext_t* s); // ? 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); // Input Text Line widget routines void HUlib_initIText ( hu_itext_t* it, int x, int y, patch_t** font, int startchar, boolean* on ); // enforces left margin void HUlib_delCharFromIText(hu_itext_t* it); // enforces left margin void HUlib_eraseLineFromIText(hu_itext_t* it); // resets line and left margin void HUlib_resetIText(hu_itext_t* it); // left of left-margin void HUlib_addPrefixToIText ( hu_itext_t* it, char* str ); // whether eaten boolean HUlib_keyInIText ( hu_itext_t* it, unsigned char ch ); void HUlib_drawIText(hu_itext_t* it); // erases all itext lines void HUlib_eraseIText(hu_itext_t* it); #endif crispy-doom-crispy-doom-5.6.4/src/doom/hu_stuff.c000066400000000000000000000721731360717211000217620ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Heads-up displays // #include #include "doomdef.h" #include "doomkeys.h" #include "z_zone.h" #include "deh_main.h" #include "i_input.h" #include "i_swap.h" #include "i_video.h" #include "hu_stuff.h" #include "hu_lib.h" #include "m_controls.h" #include "m_misc.h" #include "w_wad.h" #include "m_argv.h" // [crispy] M_ParmExists() #include "st_stuff.h" // [crispy] ST_HEIGHT #include "p_setup.h" // maplumpinfo #include "s_sound.h" #include "doomstat.h" // Data. #include "dstrings.h" #include "sounds.h" #include "r_state.h" // [crispy] colormaps #include "v_video.h" // [crispy] V_DrawPatch() et al. #include "v_trans.h" // [crispy] colored kills/items/secret/etc. messages // // Locally used constants, shortcuts. // #define HU_TITLE (mapnames[(gameepisode-1)*9+gamemap-1]) #define HU_TITLE2 (mapnames_commercial[gamemap-1]) #define HU_TITLEP (mapnames_commercial[gamemap-1 + 32]) #define HU_TITLET (mapnames_commercial[gamemap-1 + 64]) #define HU_TITLEN (mapnames_commercial[gamemap-1 + 96 + 3]) #define HU_TITLEM (mapnames_commercial[gamemap-1 + 105 + 3]) #define HU_TITLE_CHEX (mapnames_chex[(gameepisode-1)*9+gamemap-1]) #define HU_TITLEHEIGHT 1 #define HU_TITLEX 0 #define HU_TITLEY (167 - SHORT(hu_font[0]->height)) #define HU_INPUTTOGGLE 't' #define HU_INPUTX HU_MSGX #define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1)) #define HU_INPUTWIDTH 64 #define HU_INPUTHEIGHT 1 #define HU_COORDX (ORIGWIDTH - 7 * hu_font['A'-HU_FONTSTART]->width) char *chat_macros[10] = { HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8, HUSTR_CHATMACRO9 }; const char *player_names[] = { HUSTR_PLRGREEN, HUSTR_PLRINDIGO, HUSTR_PLRBROWN, HUSTR_PLRRED }; char chat_char; // remove later. static player_t* plr; patch_t* hu_font[HU_FONTSIZE]; static hu_textline_t w_title; static hu_textline_t w_map; static hu_textline_t w_kills; static hu_textline_t w_items; static hu_textline_t w_scrts; static hu_textline_t w_ltime; static hu_textline_t w_coordx; static hu_textline_t w_coordy; static hu_textline_t w_coorda; static hu_textline_t w_fps; boolean chat_on; static hu_itext_t w_chat; static boolean always_off = false; static char chat_dest[MAXPLAYERS]; static hu_itext_t w_inputbuffer[MAXPLAYERS]; static boolean message_on; boolean message_dontfuckwithme; static boolean message_nottobefuckedwith; static boolean secret_on; static hu_stext_t w_message; static int message_counter; static hu_stext_t w_secret; static int secret_counter; extern int showMessages; static boolean headsupactive = false; extern int screenblocks; // [crispy] // // Builtin map names. // The actual names can be found in DStrings.h. // const char *mapnames[] = // DOOM shareware/registered/retail (Ultimate) names. { HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, HUSTR_E1M6, HUSTR_E1M7, HUSTR_E1M8, HUSTR_E1M9, HUSTR_E2M1, HUSTR_E2M2, HUSTR_E2M3, HUSTR_E2M4, HUSTR_E2M5, HUSTR_E2M6, HUSTR_E2M7, HUSTR_E2M8, HUSTR_E2M9, HUSTR_E3M1, HUSTR_E3M2, HUSTR_E3M3, HUSTR_E3M4, HUSTR_E3M5, HUSTR_E3M6, HUSTR_E3M7, HUSTR_E3M8, HUSTR_E3M9, HUSTR_E4M1, HUSTR_E4M2, HUSTR_E4M3, HUSTR_E4M4, HUSTR_E4M5, HUSTR_E4M6, HUSTR_E4M7, HUSTR_E4M8, HUSTR_E4M9, // [crispy] Sigil HUSTR_E5M1, HUSTR_E5M2, HUSTR_E5M3, HUSTR_E5M4, HUSTR_E5M5, HUSTR_E5M6, HUSTR_E5M7, HUSTR_E5M8, HUSTR_E5M9, "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL" }; const char *mapnames_chex[] = // Chex Quest names. { HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL" }; // List of names for levels in commercial IWADs // (doom2.wad, plutonia.wad, tnt.wad). These are stored in a // single large array; WADs like pl2.wad have a MAP33, and rely on // the layout in the Vanilla executable, where it is possible to // overflow the end of one array into the next. const char *mapnames_commercial[] = { // DOOM 2 map names. HUSTR_1, HUSTR_2, HUSTR_3, HUSTR_4, HUSTR_5, HUSTR_6, HUSTR_7, HUSTR_8, HUSTR_9, HUSTR_10, HUSTR_11, HUSTR_12, HUSTR_13, HUSTR_14, HUSTR_15, HUSTR_16, HUSTR_17, HUSTR_18, HUSTR_19, HUSTR_20, HUSTR_21, HUSTR_22, HUSTR_23, HUSTR_24, HUSTR_25, HUSTR_26, HUSTR_27, HUSTR_28, HUSTR_29, HUSTR_30, HUSTR_31, HUSTR_32, // Plutonia WAD map names. PHUSTR_1, PHUSTR_2, PHUSTR_3, PHUSTR_4, PHUSTR_5, PHUSTR_6, PHUSTR_7, PHUSTR_8, PHUSTR_9, PHUSTR_10, PHUSTR_11, PHUSTR_12, PHUSTR_13, PHUSTR_14, PHUSTR_15, PHUSTR_16, PHUSTR_17, PHUSTR_18, PHUSTR_19, PHUSTR_20, PHUSTR_21, PHUSTR_22, PHUSTR_23, PHUSTR_24, PHUSTR_25, PHUSTR_26, PHUSTR_27, PHUSTR_28, PHUSTR_29, PHUSTR_30, PHUSTR_31, PHUSTR_32, // TNT WAD map names. THUSTR_1, THUSTR_2, THUSTR_3, THUSTR_4, THUSTR_5, THUSTR_6, THUSTR_7, THUSTR_8, THUSTR_9, THUSTR_10, THUSTR_11, THUSTR_12, THUSTR_13, THUSTR_14, THUSTR_15, THUSTR_16, THUSTR_17, THUSTR_18, THUSTR_19, THUSTR_20, THUSTR_21, THUSTR_22, THUSTR_23, THUSTR_24, THUSTR_25, THUSTR_26, THUSTR_27, THUSTR_28, THUSTR_29, THUSTR_30, THUSTR_31, THUSTR_32, // Emulation: TNT maps 33-35 can be warped to and played if they exist // so include blank names instead of spilling over "", "", "" , NHUSTR_1, NHUSTR_2, NHUSTR_3, NHUSTR_4, NHUSTR_5, NHUSTR_6, NHUSTR_7, NHUSTR_8, NHUSTR_9, MHUSTR_1, MHUSTR_2, MHUSTR_3, MHUSTR_4, MHUSTR_5, MHUSTR_6, MHUSTR_7, MHUSTR_8, MHUSTR_9, MHUSTR_10, MHUSTR_11, MHUSTR_12, MHUSTR_13, MHUSTR_14, MHUSTR_15, MHUSTR_16, MHUSTR_17, MHUSTR_18, MHUSTR_19, MHUSTR_20, MHUSTR_21 }; static void CrispyReplaceColor (char *str, const int cr, const char *col) { char *str_replace, col_replace[16]; if (DEH_HasStringReplacement(str)) { return; } M_snprintf(col_replace, sizeof(col_replace), "%s%s%s", crstr[cr], col, crstr[CR_NONE]); str_replace = M_StringReplace(str, col, col_replace); DEH_AddStringReplacement(str, str_replace); free(str_replace); } static const char *cr_stat, *cr_stat2, *kills; void HU_Init(void) { int i; int j; char buffer[9]; // load the heads-up font j = HU_FONTSTART; for (i=0;ileftoffset); laserpatch[i].h -= SHORT(patch->topoffset); // [crispy] special-case the chevron crosshair type if (toupper(laserpatch[i].c) == '^') { laserpatch[i].h -= SHORT(patch->height)/2; } } if (!patch) { patch = W_CacheLumpNum(laserpatch[i].l, PU_STATIC); } laserpatch[i].w += SHORT(patch->width)/2; laserpatch[i].h += SHORT(patch->height)/2; } if (!M_ParmExists("-nodeh")) { // [crispy] colorize keycard and skull key messages CrispyReplaceColor(GOTBLUECARD, CR_BLUE, " blue "); CrispyReplaceColor(GOTBLUESKUL, CR_BLUE, " blue "); CrispyReplaceColor(PD_BLUEO, CR_BLUE, " blue "); CrispyReplaceColor(PD_BLUEK, CR_BLUE, " blue "); CrispyReplaceColor(GOTREDCARD, CR_RED, " red "); CrispyReplaceColor(GOTREDSKULL, CR_RED, " red "); CrispyReplaceColor(PD_REDO, CR_RED, " red "); CrispyReplaceColor(PD_REDK, CR_RED, " red "); CrispyReplaceColor(GOTYELWCARD, CR_GOLD, " yellow "); CrispyReplaceColor(GOTYELWSKUL, CR_GOLD, " yellow "); CrispyReplaceColor(PD_YELLOWO, CR_GOLD, " yellow "); CrispyReplaceColor(PD_YELLOWK, CR_GOLD, " yellow "); // [crispy] colorize multi-player messages CrispyReplaceColor(HUSTR_PLRGREEN, CR_GREEN, "Green: "); CrispyReplaceColor(HUSTR_PLRINDIGO, CR_GRAY, "Indigo: "); CrispyReplaceColor(HUSTR_PLRBROWN, CR_GOLD, "Brown: "); CrispyReplaceColor(HUSTR_PLRRED, CR_RED, "Red: "); } } void HU_Stop(void) { headsupactive = false; } // [crispy] display names of single special levels in Automap // These are single, non-consecutive, (semi-)official levels // without their own music or par times and thus do not need // to be handled as distinct pack_* game missions. typedef struct { GameMission_t mission; int episode; int map; const char *wad; const char *name; } speciallevel_t; static const speciallevel_t speciallevels[] = { // [crispy] ExM0 {doom, 1, 0, NULL, NULL}, {doom, 2, 0, NULL, NULL}, {doom, 3, 0, NULL, NULL}, {doom, 4, 0, NULL, NULL}, // [crispy] Romero's latest E1 additions {doom, 1, 8, "e1m8b.wad", HUSTR_E1M8B}, {doom, 1, 4, "e1m4b.wad", HUSTR_E1M4B}, // [crispy] E1M10 "Sewers" (Xbox Doom) {doom, 1, 10, NULL, HUSTR_E1M10}, // [crispy] The Master Levels for Doom 2 {doom2, 0, 1, "attack.wad", MHUSTR_1}, {doom2, 0, 1, "canyon.wad", MHUSTR_2}, {doom2, 0, 1, "catwalk.wad", MHUSTR_3}, {doom2, 0, 1, "combine.wad", MHUSTR_4}, {doom2, 0, 1, "fistula.wad", MHUSTR_5}, {doom2, 0, 1, "garrison.wad", MHUSTR_6}, {doom2, 0, 1, "manor.wad", MHUSTR_7}, {doom2, 0, 1, "paradox.wad", MHUSTR_8}, {doom2, 0, 1, "subspace.wad", MHUSTR_9}, {doom2, 0, 1, "subterra.wad", MHUSTR_10}, {doom2, 0, 1, "ttrap.wad", MHUSTR_11}, {doom2, 0, 3, "virgil.wad", MHUSTR_12}, {doom2, 0, 5, "minos.wad", MHUSTR_13}, {doom2, 0, 7, "bloodsea.wad", MHUSTR_14}, {doom2, 0, 7, "mephisto.wad", MHUSTR_15}, {doom2, 0, 7, "nessus.wad", MHUSTR_16}, {doom2, 0, 8, "geryon.wad", MHUSTR_17}, {doom2, 0, 9, "vesperas.wad", MHUSTR_18}, {doom2, 0, 25, "blacktwr.wad", MHUSTR_19}, {doom2, 0, 31, "teeth.wad", MHUSTR_20}, {doom2, 0, 32, "teeth.wad", MHUSTR_21}, }; static void HU_SetSpecialLevelName (const char *wad, const char **name) { int i; for (i = 0; i < arrlen(speciallevels); i++) { const speciallevel_t speciallevel = speciallevels[i]; if (logical_gamemission == speciallevel.mission && (!speciallevel.episode || gameepisode == speciallevel.episode) && gamemap == speciallevel.map && (!speciallevel.wad || !strcasecmp(wad, speciallevel.wad))) { *name = speciallevel.name ? speciallevel.name : maplumpinfo->name; break; } } } void HU_Start(void) { int i; const char *s; // [crispy] string buffers for map title and WAD file name char buf[8], *ptr; if (headsupactive) HU_Stop(); plr = &players[consoleplayer]; message_on = false; message_dontfuckwithme = false; message_nottobefuckedwith = false; secret_on = false; chat_on = false; // create the message widget HUlib_initSText(&w_message, HU_MSGX, HU_MSGY, HU_MSGHEIGHT, hu_font, HU_FONTSTART, &message_on); // [crispy] create the secret message widget HUlib_initSText(&w_secret, 88, 86, HU_MSGHEIGHT, hu_font, HU_FONTSTART, &secret_on); // create the map title widget HUlib_initTextLine(&w_title, HU_TITLEX, HU_TITLEY, hu_font, HU_FONTSTART); // [crispy] create the generic map title, kills, items, secrets and level time widgets HUlib_initTextLine(&w_map, HU_TITLEX, HU_TITLEY - SHORT(hu_font[0]->height + 1), hu_font, HU_FONTSTART); HUlib_initTextLine(&w_kills, HU_TITLEX, HU_MSGY + 1 * 8, hu_font, HU_FONTSTART); HUlib_initTextLine(&w_items, HU_TITLEX, HU_MSGY + 2 * 8, hu_font, HU_FONTSTART); HUlib_initTextLine(&w_scrts, HU_TITLEX, HU_MSGY + 3 * 8, hu_font, HU_FONTSTART); HUlib_initTextLine(&w_ltime, HU_TITLEX, HU_MSGY + 4 * 8, hu_font, HU_FONTSTART); HUlib_initTextLine(&w_coordx, HU_COORDX, HU_MSGY + 1 * 8, hu_font, HU_FONTSTART); HUlib_initTextLine(&w_coordy, HU_COORDX, HU_MSGY + 2 * 8, hu_font, HU_FONTSTART); HUlib_initTextLine(&w_coorda, HU_COORDX, HU_MSGY + 3 * 8, hu_font, HU_FONTSTART); HUlib_initTextLine(&w_fps, HU_COORDX, HU_MSGY, hu_font, HU_FONTSTART); switch ( logical_gamemission ) { case doom: s = HU_TITLE; break; case doom2: s = HU_TITLE2; // Pre-Final Doom compatibility: map33-map35 names don't spill over if (gameversion <= exe_doom_1_9 && gamemap >= 33 && false) // [crispy] disable { s = ""; } break; case pack_plut: s = HU_TITLEP; break; case pack_tnt: s = HU_TITLET; break; case pack_nerve: if (gamemap <= 9) s = HU_TITLEN; else s = HU_TITLE2; break; case pack_master: if (gamemap <= 21) s = HU_TITLEM; else s = HU_TITLE2; break; default: s = "Unknown level"; break; } if (logical_gamemission == doom && gameversion == exe_chex) { s = HU_TITLE_CHEX; } // [crispy] display names of single special levels in Automap HU_SetSpecialLevelName(W_WadNameForLump(maplumpinfo), &s); // [crispy] explicitely display (episode and) map if the // map is from a PWAD or if the map title string has been dehacked if (DEH_HasStringReplacement(s) || (!W_IsIWADLump(maplumpinfo) && (!nervewadfile || gamemission != pack_nerve))) { char *m; ptr = M_StringJoin(crstr[CR_GOLD], W_WadNameForLump(maplumpinfo), ": ", crstr[CR_GRAY], maplumpinfo->name, NULL); m = ptr; while (*m) HUlib_addCharToTextLine(&w_map, *(m++)); free(ptr); } // dehacked substitution to get modified level name s = DEH_String(s); // [crispy] print the map title in white from the first colon onward M_snprintf(buf, sizeof(buf), "%s%s", ":", crstr[CR_GRAY]); ptr = M_StringReplace(s, ":", buf); s = ptr; while (*s) HUlib_addCharToTextLine(&w_title, *(s++)); free(ptr); // create the chat widget HUlib_initIText(&w_chat, HU_INPUTX, HU_INPUTY, hu_font, HU_FONTSTART, &chat_on); // create the inputbuffer widgets for (i=0 ; ireadyweapon].ammo == am_noammo || plr->playerstate != PST_LIVE || automapactive || menuactive || paused || secret_on) return; if (lump != laserpatch[crispy->crosshairtype].l) { lump = laserpatch[crispy->crosshairtype].l; patch = W_CacheLumpNum(lump, PU_STATIC); } dp_translucent = true; dp_translation = R_LaserspotColor(); V_DrawPatch(ORIGWIDTH/2 - laserpatch[crispy->crosshairtype].w, ((screenblocks <= 10) ? (ORIGHEIGHT-ST_HEIGHT)/2 : ORIGHEIGHT/2) - laserpatch[crispy->crosshairtype].h, patch); // V_DrawHorizLine(0, (screenblocks <= 10) ? (SCREENHEIGHT/2-ST_HEIGHT) : (SCREENHEIGHT/2), SCREENWIDTH, 128); } void HU_Drawer(void) { if (crispy->cleanscreenshot) { HU_Erase(); return; } // [crispy] translucent messages for translucent HUD if (screenblocks > CRISPY_HUD + 1 && (!automapactive || crispy->automapoverlay)) dp_translucent = true; if (secret_on && !menuactive) { dp_translation = cr[CR_GOLD]; HUlib_drawSText(&w_secret); } dp_translation = NULL; if (crispy->screenshotmsg == 4) HUlib_eraseSText(&w_message); else HUlib_drawSText(&w_message); HUlib_drawIText(&w_chat); if (crispy->coloredhud & COLOREDHUD_TEXT) dp_translation = cr[CR_GOLD]; if (automapactive) { HUlib_drawTextLine(&w_title, false); } if (crispy->automapstats == WIDGETS_ALWAYS || (automapactive && crispy->automapstats == WIDGETS_AUTOMAP)) { // [crispy] move obtrusive line out of player view if (automapactive && (!crispy->automapoverlay || screenblocks < CRISPY_HUD - 1)) HUlib_drawTextLine(&w_map, false); HUlib_drawTextLine(&w_kills, false); HUlib_drawTextLine(&w_items, false); HUlib_drawTextLine(&w_scrts, false); } if (crispy->leveltime == WIDGETS_ALWAYS || (automapactive && crispy->leveltime == WIDGETS_AUTOMAP)) { HUlib_drawTextLine(&w_ltime, false); } if (crispy->playercoords == WIDGETS_ALWAYS || (automapactive && crispy->playercoords == WIDGETS_AUTOMAP)) { HUlib_drawTextLine(&w_coordx, false); HUlib_drawTextLine(&w_coordy, false); HUlib_drawTextLine(&w_coorda, false); } if (plr->powers[pw_showfps]) { HUlib_drawTextLine(&w_fps, false); } if (crispy->crosshair == CROSSHAIR_STATIC) HU_DrawCrosshair(); dp_translation = NULL; if (dp_translucent) dp_translucent = false; // [crispy] demo timer widget if (demoplayback && (crispy->demotimer & DEMOTIMER_PLAYBACK)) { ST_DrawDemoTimer(crispy->demotimerdir ? (deftotaldemotics - defdemotics) : defdemotics); } else if (demorecording && (crispy->demotimer & DEMOTIMER_RECORD)) { ST_DrawDemoTimer(leveltime); } // [crispy] demo progress bar if (demoplayback && crispy->demobar) { HU_DemoProgressBar(); } } void HU_Erase(void) { HUlib_eraseSText(&w_message); HUlib_eraseSText(&w_secret); HUlib_eraseIText(&w_chat); HUlib_eraseTextLine(&w_title); HUlib_eraseTextLine(&w_kills); HUlib_eraseTextLine(&w_items); HUlib_eraseTextLine(&w_scrts); HUlib_eraseTextLine(&w_ltime); HUlib_eraseTextLine(&w_coordx); HUlib_eraseTextLine(&w_coordy); HUlib_eraseTextLine(&w_coorda); HUlib_eraseTextLine(&w_fps); } void HU_Ticker(void) { int i, rc; char c; char str[32], *s; // tick down message counter if message is up if (message_counter && !--message_counter) { message_on = false; message_nottobefuckedwith = false; crispy->screenshotmsg >>= 1; } if (secret_counter && !--secret_counter) { secret_on = false; } if (showMessages || message_dontfuckwithme) { // [crispy] display centered message if (plr->centermessage) { extern int M_StringWidth(const char *string); w_secret.l[0].x = ORIGWIDTH/2 - M_StringWidth(plr->centermessage)/2; HUlib_addMessageToSText(&w_secret, 0, plr->centermessage); plr->centermessage = 0; secret_on = true; secret_counter = 5*TICRATE/2; // [crispy] 2.5 seconds } // display message if necessary if ((plr->message && !message_nottobefuckedwith) || (plr->message && message_dontfuckwithme)) { HUlib_addMessageToSText(&w_message, 0, plr->message); plr->message = 0; message_on = true; message_counter = HU_MSGTIMEOUT; message_nottobefuckedwith = message_dontfuckwithme; message_dontfuckwithme = 0; crispy->screenshotmsg >>= 1; } } // else message_on = false; // check for incoming chat characters if (netgame) { for (i=0 ; i exe_doom_1_2) S_StartSound(0, sfx_tink); } HUlib_resetIText(&w_inputbuffer[i]); } } players[i].cmd.chatchar = 0; } } } if (automapactive) { // [crispy] move map title to the bottom if (crispy->automapoverlay && screenblocks >= CRISPY_HUD - 1) w_title.y = HU_TITLEY + ST_HEIGHT; else w_title.y = HU_TITLEY; } if (crispy->automapstats == WIDGETS_ALWAYS || (automapactive && crispy->automapstats == WIDGETS_AUTOMAP)) { // [crispy] count spawned monsters if (extrakills) M_snprintf(str, sizeof(str), "%s%s%s%d/%d+%d", cr_stat, kills, crstr[CR_GRAY], plr->killcount, totalkills, extrakills); else M_snprintf(str, sizeof(str), "%s%s%s%d/%d", cr_stat, kills, crstr[CR_GRAY], plr->killcount, totalkills); HUlib_clearTextLine(&w_kills); s = str; while (*s) HUlib_addCharToTextLine(&w_kills, *(s++)); M_snprintf(str, sizeof(str), "%sI %s%d/%d", cr_stat, crstr[CR_GRAY], plr->itemcount, totalitems); HUlib_clearTextLine(&w_items); s = str; while (*s) HUlib_addCharToTextLine(&w_items, *(s++)); M_snprintf(str, sizeof(str), "%sS %s%d/%d", cr_stat, crstr[CR_GRAY], plr->secretcount, totalsecret); HUlib_clearTextLine(&w_scrts); s = str; while (*s) HUlib_addCharToTextLine(&w_scrts, *(s++)); } if (crispy->leveltime == WIDGETS_ALWAYS || (automapactive && crispy->leveltime == WIDGETS_AUTOMAP)) { const int time = leveltime / TICRATE; if (time >= 3600) M_snprintf(str, sizeof(str), "%s%02d:%02d:%02d", crstr[CR_GRAY], time/3600, (time%3600)/60, time%60); else M_snprintf(str, sizeof(str), "%s%02d:%02d", crstr[CR_GRAY], time/60, time%60); HUlib_clearTextLine(&w_ltime); s = str; while (*s) HUlib_addCharToTextLine(&w_ltime, *(s++)); } if (crispy->playercoords == WIDGETS_ALWAYS || (automapactive && crispy->playercoords == WIDGETS_AUTOMAP)) { M_snprintf(str, sizeof(str), "%sX %s%-5d", cr_stat2, crstr[CR_GRAY], (plr->mo->x)>>FRACBITS); HUlib_clearTextLine(&w_coordx); s = str; while (*s) HUlib_addCharToTextLine(&w_coordx, *(s++)); M_snprintf(str, sizeof(str), "%sY %s%-5d", cr_stat2, crstr[CR_GRAY], (plr->mo->y)>>FRACBITS); HUlib_clearTextLine(&w_coordy); s = str; while (*s) HUlib_addCharToTextLine(&w_coordy, *(s++)); M_snprintf(str, sizeof(str), "%sA %s%-5d", cr_stat2, crstr[CR_GRAY], (plr->mo->angle)/ANG1); HUlib_clearTextLine(&w_coorda); s = str; while (*s) HUlib_addCharToTextLine(&w_coorda, *(s++)); } if (plr->powers[pw_showfps]) { M_snprintf(str, sizeof(str), "%s%-4d %sFPS", crstr[CR_GRAY], crispy->fps, cr_stat2); HUlib_clearTextLine(&w_fps); s = str; while (*s) HUlib_addCharToTextLine(&w_fps, *(s++)); } } #define QUEUESIZE 128 static char chatchars[QUEUESIZE]; static int head = 0; static int tail = 0; void HU_queueChatChar(char c) { if (((head + 1) & (QUEUESIZE-1)) == tail) { plr->message = DEH_String(HUSTR_MSGU); } else { chatchars[head] = c; head = (head + 1) & (QUEUESIZE-1); } } char HU_dequeueChatChar(void) { char c; if (head != tail) { c = chatchars[tail]; tail = (tail + 1) & (QUEUESIZE-1); } else { c = 0; } return c; } static void StartChatInput(int dest) { chat_on = true; HUlib_resetIText(&w_chat); HU_queueChatChar(HU_BROADCAST); I_StartTextInput(0, 8, SCREENWIDTH, 16); } static void StopChatInput(void) { chat_on = false; I_StopTextInput(); } boolean HU_Responder(event_t *ev) { static char lastmessage[HU_MAXLINELENGTH+1]; const char *macromessage; boolean eatkey = false; static boolean altdown = false; unsigned char c; int i; int numplayers; static int num_nobrainers = 0; numplayers = 0; for (i=0 ; idata1 == KEY_RSHIFT) { return false; } else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT) { altdown = ev->type == ev_keydown; return false; } if (ev->type != ev_keydown) return false; if (!chat_on) { if (ev->data1 == key_message_refresh) { message_on = true; message_counter = HU_MSGTIMEOUT; eatkey = true; } else if (netgame && ev->data2 == key_multi_msg) { eatkey = true; StartChatInput(HU_BROADCAST); } else if (netgame && numplayers > 2) { for (i=0; idata2 == key_multi_msgplayer[i]) { if (playeringame[i] && i!=consoleplayer) { eatkey = true; StartChatInput(i + 1); break; } else if (i == consoleplayer) { num_nobrainers++; if (num_nobrainers < 3) plr->message = DEH_String(HUSTR_TALKTOSELF1); else if (num_nobrainers < 6) plr->message = DEH_String(HUSTR_TALKTOSELF2); else if (num_nobrainers < 9) plr->message = DEH_String(HUSTR_TALKTOSELF3); else if (num_nobrainers < 32) plr->message = DEH_String(HUSTR_TALKTOSELF4); else plr->message = DEH_String(HUSTR_TALKTOSELF5); } } } } } else { // send a macro if (altdown) { c = ev->data1 - '0'; if (c > 9) return false; // fprintf(stderr, "got here\n"); macromessage = chat_macros[c]; // kill last message with a '\n' HU_queueChatChar(KEY_ENTER); // DEBUG!!! // send the macro message while (*macromessage) HU_queueChatChar(*macromessage++); HU_queueChatChar(KEY_ENTER); // leave chat mode and notify that it was sent StopChatInput(); M_StringCopy(lastmessage, chat_macros[c], sizeof(lastmessage)); plr->message = lastmessage; eatkey = true; } else { c = ev->data3; eatkey = HUlib_keyInIText(&w_chat, c); if (eatkey) { // static unsigned char buf[20]; // DEBUG HU_queueChatChar(c); // M_snprintf(buf, sizeof(buf), "KEY: %d => %d", ev->data1, c); // plr->message = buf; } if (c == KEY_ENTER) { StopChatInput(); if (w_chat.l.len) { M_StringCopy(lastmessage, w_chat.l.l, sizeof(lastmessage)); plr->message = lastmessage; } } else if (c == KEY_ESCAPE) { StopChatInput(); } } } return eatkey; } crispy-doom-crispy-doom-5.6.4/src/doom/hu_stuff.h000066400000000000000000000024761360717211000217660ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Head up display // #ifndef __HU_STUFF_H__ #define __HU_STUFF_H__ #include "d_event.h" // // Globally visible constants. // #define HU_FONTSTART '!' // the first font characters #define HU_FONTEND '_' // the last font characters // Calculate # of glyphs in font. #define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) #define HU_BROADCAST 5 #define HU_MSGX 0 #define HU_MSGY 0 #define HU_MSGWIDTH 64 // in characters #define HU_MSGHEIGHT 1 // in lines #define HU_MSGTIMEOUT (4*TICRATE) // // HEADS UP TEXT // void HU_Init(void); void HU_Start(void); boolean HU_Responder(event_t* ev); void HU_Ticker(void); void HU_Drawer(void); char HU_dequeueChatChar(void); void HU_Erase(void); extern char *chat_macros[10]; #endif crispy-doom-crispy-doom-5.6.4/src/doom/info.c000066400000000000000000004500531360717211000210670ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Thing frame/state LUT, // generated by multigen utilitiy. // This one is the original DOOM version, preserved. // #include #include // Data. #include "sounds.h" #include "m_fixed.h" #include "info.h" #include "p_mobj.h" const char *sprnames[] = { "TROO","SHTG","PUNG","PISG","PISF","SHTF","SHT2","CHGG","CHGF","MISG", "MISF","SAWG","PLSG","PLSF","BFGG","BFGF","BLUD","PUFF","BAL1","BAL2", "PLSS","PLSE","MISL","BFS1","BFE1","BFE2","TFOG","IFOG","PLAY","POSS", "SPOS","VILE","FIRE","FATB","FBXP","SKEL","MANF","FATT","CPOS","SARG", "HEAD","BAL7","BOSS","BOS2","SKUL","SPID","BSPI","APLS","APBX","CYBR", "PAIN","SSWV","KEEN","BBRN","BOSF","ARM1","ARM2","BAR1","BEXP","FCAN", "BON1","BON2","BKEY","RKEY","YKEY","BSKU","RSKU","YSKU","STIM","MEDI", "SOUL","PINV","PSTR","PINS","MEGA","SUIT","PMAP","PVIS","CLIP","AMMO", "ROCK","BROK","CELL","CELP","SHEL","SBOX","BPAK","BFUG","MGUN","CSAW", "LAUN","PLAS","SHOT","SGN2","COLU","SMT2","GOR1","POL2","POL5","POL4", "POL3","POL1","POL6","GOR2","GOR3","GOR4","GOR5","SMIT","COL1","COL2", "COL3","COL4","CAND","CBRA","COL6","TRE1","TRE2","ELEC","CEYE","FSKU", "COL5","TBLU","TGRN","TRED","SMBT","SMGT","SMRT","HDB1","HDB2","HDB3", "HDB4","HDB5","HDB6","POB1","POB2","BRS1","TLMP","TLP2", // [crispy] additional BOOM and MBF states, sprites and code pointers "TNT1","DOGS","PLS1","PLS2","BON3","BON4", // [BH] blood splats, [crispy] unused "BLD2", // [BH] 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 }; // Doesn't work with g++, needs actionf_p1 void A_Light0(); void A_WeaponReady(); void A_Lower(); void A_Raise(); void A_Punch(); void A_ReFire(); void A_FirePistol(); void A_Light1(); void A_FireShotgun(); void A_Light2(); void A_FireShotgun2(); void A_CheckReload(); void A_OpenShotgun2(); void A_LoadShotgun2(); void A_CloseShotgun2(); void A_FireCGun(); void A_GunFlash(); void A_FireMissile(); void A_Saw(); void A_FirePlasma(); void A_BFGsound(); void A_FireBFG(); void A_BFGSpray(); void A_Explode(); void A_Pain(); void A_PlayerScream(); void A_Fall(); void A_XScream(); void A_Look(); void A_Chase(); void A_FaceTarget(); void A_PosAttack(); void A_Scream(); void A_SPosAttack(); void A_VileChase(); void A_VileStart(); void A_VileTarget(); void A_VileAttack(); void A_StartFire(); void A_Fire(); void A_FireCrackle(); void A_Tracer(); void A_SkelWhoosh(); void A_SkelFist(); void A_SkelMissile(); void A_FatRaise(); void A_FatAttack1(); void A_FatAttack2(); void A_FatAttack3(); void A_BossDeath(); void A_CPosAttack(); void A_CPosRefire(); void A_TroopAttack(); void A_SargAttack(); void A_HeadAttack(); void A_BruisAttack(); void A_SkullAttack(); void A_Metal(); void A_SpidRefire(); void A_BabyMetal(); void A_BspiAttack(); void A_Hoof(); void A_CyberAttack(); void A_PainAttack(); void A_PainDie(); void A_KeenDie(); void A_BrainPain(); void A_BrainScream(); void A_BrainDie(); void A_BrainAwake(); void A_BrainSpit(); void A_SpawnSound(); void A_SpawnFly(); void A_BrainExplode(); // [crispy] additional BOOM and MBF states, sprites and code pointers void A_Stop(); void A_Die(); void A_FireOldBFG(); void A_Detonate(); void A_Mushroom(); void A_BetaSkullAttack(); 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 // [crispy] killough 9/5/98: make SSG lighting flash more uniform along super shotgun {SPR_SHT2,0|0x8000,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 // [crispy] killough 8/20/98: reduce first SSG flash frame one tic, to fix // Doom II SSG flash bug, in which SSG raises before flash finishes {SPR_SHT2,32776,5-1,{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 // [crispy] render Zombiman's firing frames full-bright {SPR_POSS,5|0x8000,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 // [crispy] render Minigun zombie's firing frames full-bright {SPR_CPOS,5|0x8000,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 // [crispy] render Cyberdemon's firing frames full-bright {SPR_CYBR,5|0x8000,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|0x8000,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|0x8000,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 ,// [crispy] additional BOOM and MBF states, sprites and code pointers {SPR_TNT1,0,-1,{NULL},S_TNT1,0,0}, // S_TNT1 {SPR_MISL,32768,1000,{A_Die},S_GRENADE,0,0}, // S_GRENADE {SPR_MISL,32769,4,{A_Scream},S_DETONATE2,0,0}, // S_DETONATE {SPR_MISL,32770,6,{A_Detonate},S_DETONATE3,0,0}, // S_DETONATE2 {SPR_MISL,32771,10,{NULL},S_NULL,0,0}, // S_DETONATE3 {SPR_DOGS,0,10,{A_Look},S_DOGS_STND2,0,0}, // S_DOGS_STND {SPR_DOGS,1,10,{A_Look},S_DOGS_STND,0,0}, // S_DOGS_STND2 {SPR_DOGS,0,2,{A_Chase},S_DOGS_RUN2,0,0}, // S_DOGS_RUN1 {SPR_DOGS,0,2,{A_Chase},S_DOGS_RUN3,0,0}, // S_DOGS_RUN2 {SPR_DOGS,1,2,{A_Chase},S_DOGS_RUN4,0,0}, // S_DOGS_RUN3 {SPR_DOGS,1,2,{A_Chase},S_DOGS_RUN5,0,0}, // S_DOGS_RUN4 {SPR_DOGS,2,2,{A_Chase},S_DOGS_RUN6,0,0}, // S_DOGS_RUN5 {SPR_DOGS,2,2,{A_Chase},S_DOGS_RUN7,0,0}, // S_DOGS_RUN6 {SPR_DOGS,3,2,{A_Chase},S_DOGS_RUN8,0,0}, // S_DOGS_RUN7 {SPR_DOGS,3,2,{A_Chase},S_DOGS_RUN1,0,0}, // S_DOGS_RUN8 {SPR_DOGS,4,8,{A_FaceTarget},S_DOGS_ATK2,0,0}, // S_DOGS_ATK1 {SPR_DOGS,5,8,{A_FaceTarget},S_DOGS_ATK3,0,0}, // S_DOGS_ATK2 {SPR_DOGS,6,8,{A_SargAttack},S_DOGS_RUN1,0,0}, // S_DOGS_ATK3 {SPR_DOGS,7,2,{NULL},S_DOGS_PAIN2,0,0}, // S_DOGS_PAIN {SPR_DOGS,7,2,{A_Pain},S_DOGS_RUN1,0,0}, // S_DOGS_PAIN2 {SPR_DOGS,8,8,{NULL},S_DOGS_DIE2,0,0}, // S_DOGS_DIE1 {SPR_DOGS,9,8,{A_Scream},S_DOGS_DIE3,0,0}, // S_DOGS_DIE2 {SPR_DOGS,10,4,{NULL},S_DOGS_DIE4,0,0}, // S_DOGS_DIE3 {SPR_DOGS,11,4,{A_Fall},S_DOGS_DIE5,0,0}, // S_DOGS_DIE4 {SPR_DOGS,12,4,{NULL},S_DOGS_DIE6,0,0}, // S_DOGS_DIE5 {SPR_DOGS,13,-1,{NULL},S_NULL,0,0}, // S_DOGS_DIE6 {SPR_DOGS,13,5,{NULL},S_DOGS_RAISE2,0,0}, // S_DOGS_RAISE1 {SPR_DOGS,12,5,{NULL},S_DOGS_RAISE3,0,0}, // S_DOGS_RAISE2 {SPR_DOGS,11,5,{NULL},S_DOGS_RAISE4,0,0}, // S_DOGS_RAISE3 {SPR_DOGS,10,5,{NULL},S_DOGS_RAISE5,0,0}, // S_DOGS_RAISE4 {SPR_DOGS,9,5,{NULL},S_DOGS_RAISE6,0,0}, // S_DOGS_RAISE5 {SPR_DOGS,8,5,{NULL},S_DOGS_RUN1,0,0}, // S_DOGS_RAISE6 #define BFGDELAY 1 #define OLDBFG_1FRAMES(x) {SPR_BFGG,1,BFGDELAY,{A_FireOldBFG},x+S_OLDBFG1+2,0,0}, #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,0,0}, // S_OLDBFG1 OLDBFG_8FRAMES(0) OLDBFG_8FRAMES(8) OLDBFG_8FRAMES(16) OLDBFG_8FRAMES(24) OLDBFG_8FRAMES(32) {SPR_BFGG,1,0,{A_Light0},S_OLDBFG43,0,0}, // S_OLDBFG42 {SPR_BFGG,1,20,{A_ReFire},S_BFG,0,0}, // S_OLDBFG43 {SPR_PLS1,32768,6,{NULL},S_PLS1BALL2,0,0}, // S_PLS1BALL {SPR_PLS1,32769,6,{NULL},S_PLS1BALL,0,0}, // S_PLS1BALL2 {SPR_PLS1,32770,4,{NULL},S_PLS1EXP2,0,0}, // S_PLS1EXP {SPR_PLS1,32771,4,{NULL},S_PLS1EXP3,0,0}, // S_PLS1EXP2 {SPR_PLS1,32772,4,{NULL},S_PLS1EXP4,0,0}, // S_PLS1EXP3 {SPR_PLS1,32773,4,{NULL},S_PLS1EXP5,0,0}, // S_PLS1EXP4 {SPR_PLS1,32774,4,{NULL},S_NULL,0,0}, // S_PLS1EXP5 {SPR_PLS2,32768,4,{NULL},S_PLS2BALL2,0,0}, // S_PLS2BALL {SPR_PLS2,32769,4,{NULL},S_PLS2BALL,0,0}, // S_PLS2BALL2 {SPR_PLS2,32770,6,{NULL},S_PLS2BALLX2,0,0}, // S_PLS2BALLX1 {SPR_PLS2,32771,6,{NULL},S_PLS2BALLX3,0,0}, // S_PLS2BALLX2 {SPR_PLS2,32772,6,{NULL},S_NULL,0,0}, // S_PLS2BALLX3 {SPR_BON3,0,6,{NULL},S_BON3,0,0}, // S_BON3 {SPR_BON4,0,6,{NULL},S_BON4,0,0}, // S_BON4 {SPR_SKUL,0,10,{A_Look},S_BSKUL_STND,0,0}, // S_BSKUL_STND {SPR_SKUL,1,5,{A_Chase},S_BSKUL_RUN2,0,0}, // S_BSKUL_RUN1 {SPR_SKUL,2,5,{A_Chase},S_BSKUL_RUN3,0,0}, // S_BSKUL_RUN2 {SPR_SKUL,3,5,{A_Chase},S_BSKUL_RUN4,0,0}, // S_BSKUL_RUN3 {SPR_SKUL,0,5,{A_Chase},S_BSKUL_RUN1,0,0}, // S_BSKUL_RUN4 {SPR_SKUL,4,4,{A_FaceTarget},S_BSKUL_ATK2,0,0}, // S_BSKUL_ATK1 {SPR_SKUL,5,5,{A_BetaSkullAttack},S_BSKUL_ATK3,0,0}, // S_BSKUL_ATK2 {SPR_SKUL,5,4,{NULL},S_BSKUL_RUN1,0,0}, // S_BSKUL_ATK3 {SPR_SKUL,6,4,{NULL},S_BSKUL_PAIN2,0,0}, // S_BSKUL_PAIN1 {SPR_SKUL,7,2,{A_Pain},S_BSKUL_RUN1,0,0}, // S_BSKUL_PAIN2 {SPR_SKUL,8,4,{NULL},S_BSKUL_RUN1,0,0}, // S_BSKUL_PAIN3 {SPR_SKUL, 9,5,{NULL},S_BSKUL_DIE2,0,0}, // S_BSKUL_DIE1 {SPR_SKUL,10,5,{NULL},S_BSKUL_DIE3,0,0}, // S_BSKUL_DIE2 {SPR_SKUL,11,5,{NULL},S_BSKUL_DIE4,0,0}, // S_BSKUL_DIE3 {SPR_SKUL,12,5,{NULL},S_BSKUL_DIE5,0,0}, // S_BSKUL_DIE4 {SPR_SKUL,13,5,{A_Scream},S_BSKUL_DIE6,0,0}, // S_BSKUL_DIE5 {SPR_SKUL,14,5,{NULL},S_BSKUL_DIE7,0,0}, // S_BSKUL_DIE6 {SPR_SKUL,15,5,{A_Fall},S_BSKUL_DIE8,0,0}, // S_BSKUL_DIE7 {SPR_SKUL,16,5,{A_Stop},S_BSKUL_DIE8,0,0}, // S_BSKUL_DIE8 {SPR_MISL,32769,8,{A_Mushroom},S_EXPLODE2,0,0}, // S_MUSHROOM }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = { { // MT_PLAYER "OUR HERO" -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|MF_FLIPPABLE, // flags S_NULL // raisestate }, { // MT_POSSESSED "ZOMBIEMAN" 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|MF_FLIPPABLE, // flags S_POSS_RAISE1 // raisestate }, { // MT_SHOTGUY "SHOTGUN GUY" 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|MF_FLIPPABLE, // flags S_SPOS_RAISE1 // raisestate }, { // MT_VILE "ARCH-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|MF_FLIPPABLE, // 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 S_NULL // raisestate }, { // MT_UNDEAD "REVENANT" 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|MF_FLIPPABLE, // 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 S_NULL // raisestate }, { // MT_FATSO "MANCUBUS" 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|MF_FLIPPABLE, // 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 S_NULL // raisestate }, { // MT_CHAINGUY "HEAVY WEAPON DUDE" 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|MF_FLIPPABLE, // flags S_CPOS_RAISE1 // raisestate }, { // MT_TROOP "IMP" 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|MF_FLIPPABLE, // flags S_TROO_RAISE1 // raisestate }, { // MT_SERGEANT "DEMON" 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // flags S_SARG_RAISE1 // raisestate }, { // MT_HEAD "CACODEMON" 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|MF_FLIPPABLE, // flags S_HEAD_RAISE1 // raisestate }, { // MT_BRUISER "BARON OF HELL" 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|MF_FLIPPABLE, // 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 S_NULL // raisestate }, { // MT_KNIGHT "HELL 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|MF_FLIPPABLE, // flags S_BOS2_RAISE1 // raisestate }, { // MT_SKULL "LOST SOUL" 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|MF_FLIPPABLE, // flags S_NULL // raisestate }, { // MT_SPIDER "THE SPIDER MASTERMIND" 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|MF_FLIPPABLE, // flags S_NULL // raisestate }, { // MT_BABY "ARACHNOTRON" 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|MF_FLIPPABLE, // flags S_BSPI_RAISE1 // raisestate }, { // MT_CYBORG "THE CYBERDEMON" 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 "PAIN ELEMENTAL" 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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 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 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 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 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 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 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_FLIPPABLE|MF_TRANSLUCENT, // flags S_NULL // raisestate }, { // MT_BLOOD -1, // doomednum S_BLOOD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP|MF_FLIPPABLE, // 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 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 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|MF_TRANSLUCENT, // 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 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 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 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 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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|MF_FLIPPABLE, // 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 } ,// [crispy] additional BOOM and MBF states, sprites and code pointers { // MT_PUSH 5001, // 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 8, // radius 8, // height 10, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags S_NULL // raisestate }, { // MT_PULL 5002, // 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 8, // radius 8, // height 10, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags S_NULL // raisestate }, { // 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|MF_FLIPPABLE, // flags S_DOGS_RAISE1 // raisestate }, { // 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 */, S_NULL // raisestate }, { // 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 */, S_NULL // raisestate }, { // MT_SCEPTRE 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 }, { // MT_BIBLE 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 }, // [crispy] support MUSINFO lump (dynamic music changing) { // 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 }, }; crispy-doom-crispy-doom-5.6.4/src/doom/info.h000066400000000000000000000627311360717211000210760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Thing frame/state LUT, // generated by multigen utilitiy. // This one is the original DOOM version, preserved. // #ifndef __INFO__ #define __INFO__ // Needed for action function pointer handling. #include "d_think.h" typedef enum { SPR_TROO, SPR_SHTG, SPR_PUNG, SPR_PISG, SPR_PISF, SPR_SHTF, SPR_SHT2, SPR_CHGG, SPR_CHGF, SPR_MISG, SPR_MISF, SPR_SAWG, SPR_PLSG, SPR_PLSF, SPR_BFGG, SPR_BFGF, SPR_BLUD, SPR_PUFF, SPR_BAL1, SPR_BAL2, SPR_PLSS, SPR_PLSE, SPR_MISL, SPR_BFS1, SPR_BFE1, SPR_BFE2, SPR_TFOG, SPR_IFOG, SPR_PLAY, SPR_POSS, SPR_SPOS, SPR_VILE, SPR_FIRE, SPR_FATB, SPR_FBXP, SPR_SKEL, SPR_MANF, SPR_FATT, SPR_CPOS, SPR_SARG, SPR_HEAD, SPR_BAL7, SPR_BOSS, SPR_BOS2, SPR_SKUL, SPR_SPID, SPR_BSPI, SPR_APLS, SPR_APBX, SPR_CYBR, SPR_PAIN, SPR_SSWV, SPR_KEEN, SPR_BBRN, SPR_BOSF, SPR_ARM1, SPR_ARM2, SPR_BAR1, SPR_BEXP, SPR_FCAN, SPR_BON1, SPR_BON2, SPR_BKEY, SPR_RKEY, SPR_YKEY, SPR_BSKU, SPR_RSKU, SPR_YSKU, SPR_STIM, SPR_MEDI, SPR_SOUL, SPR_PINV, SPR_PSTR, SPR_PINS, SPR_MEGA, SPR_SUIT, SPR_PMAP, SPR_PVIS, SPR_CLIP, SPR_AMMO, SPR_ROCK, SPR_BROK, SPR_CELL, SPR_CELP, SPR_SHEL, SPR_SBOX, SPR_BPAK, SPR_BFUG, SPR_MGUN, SPR_CSAW, SPR_LAUN, SPR_PLAS, SPR_SHOT, SPR_SGN2, SPR_COLU, SPR_SMT2, SPR_GOR1, SPR_POL2, SPR_POL5, SPR_POL4, SPR_POL3, SPR_POL1, SPR_POL6, SPR_GOR2, SPR_GOR3, SPR_GOR4, SPR_GOR5, SPR_SMIT, SPR_COL1, SPR_COL2, SPR_COL3, SPR_COL4, SPR_CAND, SPR_CBRA, SPR_COL6, SPR_TRE1, SPR_TRE2, SPR_ELEC, SPR_CEYE, SPR_FSKU, SPR_COL5, SPR_TBLU, SPR_TGRN, SPR_TRED, SPR_SMBT, SPR_SMGT, SPR_SMRT, SPR_HDB1, SPR_HDB2, SPR_HDB3, SPR_HDB4, SPR_HDB5, SPR_HDB6, SPR_POB1, SPR_POB2, SPR_BRS1, SPR_TLMP, SPR_TLP2, // [crispy] additional BOOM and MBF states, sprites and code pointers SPR_TNT1, SPR_DOGS, SPR_PLS1, SPR_PLS2, SPR_BON3, SPR_BON4, // [BH] blood splats, [crispy] unused SPR_BLD2, // [BH] 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 } spritenum_t; typedef enum { S_NULL, S_LIGHTDONE, S_PUNCH, S_PUNCHDOWN, S_PUNCHUP, S_PUNCH1, S_PUNCH2, S_PUNCH3, S_PUNCH4, S_PUNCH5, S_PISTOL, S_PISTOLDOWN, S_PISTOLUP, S_PISTOL1, S_PISTOL2, S_PISTOL3, S_PISTOL4, S_PISTOLFLASH, S_SGUN, S_SGUNDOWN, S_SGUNUP, S_SGUN1, S_SGUN2, S_SGUN3, S_SGUN4, S_SGUN5, S_SGUN6, S_SGUN7, S_SGUN8, S_SGUN9, S_SGUNFLASH1, S_SGUNFLASH2, S_DSGUN, S_DSGUNDOWN, S_DSGUNUP, S_DSGUN1, S_DSGUN2, S_DSGUN3, S_DSGUN4, S_DSGUN5, S_DSGUN6, S_DSGUN7, S_DSGUN8, S_DSGUN9, S_DSGUN10, S_DSNR1, S_DSNR2, S_DSGUNFLASH1, S_DSGUNFLASH2, S_CHAIN, S_CHAINDOWN, S_CHAINUP, S_CHAIN1, S_CHAIN2, S_CHAIN3, S_CHAINFLASH1, S_CHAINFLASH2, S_MISSILE, S_MISSILEDOWN, S_MISSILEUP, S_MISSILE1, S_MISSILE2, S_MISSILE3, S_MISSILEFLASH1, S_MISSILEFLASH2, S_MISSILEFLASH3, S_MISSILEFLASH4, S_SAW, S_SAWB, S_SAWDOWN, S_SAWUP, S_SAW1, S_SAW2, S_SAW3, S_PLASMA, S_PLASMADOWN, S_PLASMAUP, S_PLASMA1, S_PLASMA2, S_PLASMAFLASH1, S_PLASMAFLASH2, S_BFG, S_BFGDOWN, S_BFGUP, S_BFG1, S_BFG2, S_BFG3, S_BFG4, S_BFGFLASH1, S_BFGFLASH2, S_BLOOD1, S_BLOOD2, S_BLOOD3, S_PUFF1, S_PUFF2, S_PUFF3, S_PUFF4, S_TBALL1, S_TBALL2, S_TBALLX1, S_TBALLX2, S_TBALLX3, S_RBALL1, S_RBALL2, S_RBALLX1, S_RBALLX2, S_RBALLX3, S_PLASBALL, S_PLASBALL2, S_PLASEXP, S_PLASEXP2, S_PLASEXP3, S_PLASEXP4, S_PLASEXP5, S_ROCKET, S_BFGSHOT, S_BFGSHOT2, S_BFGLAND, S_BFGLAND2, S_BFGLAND3, S_BFGLAND4, S_BFGLAND5, S_BFGLAND6, S_BFGEXP, S_BFGEXP2, S_BFGEXP3, S_BFGEXP4, S_EXPLODE1, S_EXPLODE2, S_EXPLODE3, S_TFOG, S_TFOG01, S_TFOG02, S_TFOG2, S_TFOG3, S_TFOG4, S_TFOG5, S_TFOG6, S_TFOG7, S_TFOG8, S_TFOG9, S_TFOG10, S_IFOG, S_IFOG01, S_IFOG02, S_IFOG2, S_IFOG3, S_IFOG4, S_IFOG5, S_PLAY, S_PLAY_RUN1, S_PLAY_RUN2, S_PLAY_RUN3, S_PLAY_RUN4, S_PLAY_ATK1, S_PLAY_ATK2, S_PLAY_PAIN, S_PLAY_PAIN2, S_PLAY_DIE1, S_PLAY_DIE2, S_PLAY_DIE3, S_PLAY_DIE4, S_PLAY_DIE5, S_PLAY_DIE6, S_PLAY_DIE7, S_PLAY_XDIE1, S_PLAY_XDIE2, S_PLAY_XDIE3, S_PLAY_XDIE4, S_PLAY_XDIE5, S_PLAY_XDIE6, S_PLAY_XDIE7, S_PLAY_XDIE8, S_PLAY_XDIE9, S_POSS_STND, S_POSS_STND2, S_POSS_RUN1, S_POSS_RUN2, S_POSS_RUN3, S_POSS_RUN4, S_POSS_RUN5, S_POSS_RUN6, S_POSS_RUN7, S_POSS_RUN8, S_POSS_ATK1, S_POSS_ATK2, S_POSS_ATK3, S_POSS_PAIN, S_POSS_PAIN2, S_POSS_DIE1, S_POSS_DIE2, S_POSS_DIE3, S_POSS_DIE4, S_POSS_DIE5, S_POSS_XDIE1, S_POSS_XDIE2, S_POSS_XDIE3, S_POSS_XDIE4, S_POSS_XDIE5, S_POSS_XDIE6, S_POSS_XDIE7, S_POSS_XDIE8, S_POSS_XDIE9, S_POSS_RAISE1, S_POSS_RAISE2, S_POSS_RAISE3, S_POSS_RAISE4, S_SPOS_STND, S_SPOS_STND2, S_SPOS_RUN1, S_SPOS_RUN2, S_SPOS_RUN3, S_SPOS_RUN4, S_SPOS_RUN5, S_SPOS_RUN6, S_SPOS_RUN7, S_SPOS_RUN8, S_SPOS_ATK1, S_SPOS_ATK2, S_SPOS_ATK3, S_SPOS_PAIN, S_SPOS_PAIN2, S_SPOS_DIE1, S_SPOS_DIE2, S_SPOS_DIE3, S_SPOS_DIE4, S_SPOS_DIE5, S_SPOS_XDIE1, S_SPOS_XDIE2, S_SPOS_XDIE3, S_SPOS_XDIE4, S_SPOS_XDIE5, S_SPOS_XDIE6, S_SPOS_XDIE7, S_SPOS_XDIE8, S_SPOS_XDIE9, S_SPOS_RAISE1, S_SPOS_RAISE2, S_SPOS_RAISE3, S_SPOS_RAISE4, S_SPOS_RAISE5, S_VILE_STND, S_VILE_STND2, S_VILE_RUN1, S_VILE_RUN2, S_VILE_RUN3, S_VILE_RUN4, S_VILE_RUN5, S_VILE_RUN6, S_VILE_RUN7, S_VILE_RUN8, S_VILE_RUN9, S_VILE_RUN10, S_VILE_RUN11, S_VILE_RUN12, S_VILE_ATK1, S_VILE_ATK2, S_VILE_ATK3, S_VILE_ATK4, S_VILE_ATK5, S_VILE_ATK6, S_VILE_ATK7, S_VILE_ATK8, S_VILE_ATK9, S_VILE_ATK10, S_VILE_ATK11, S_VILE_HEAL1, S_VILE_HEAL2, S_VILE_HEAL3, S_VILE_PAIN, S_VILE_PAIN2, S_VILE_DIE1, S_VILE_DIE2, S_VILE_DIE3, S_VILE_DIE4, S_VILE_DIE5, S_VILE_DIE6, S_VILE_DIE7, S_VILE_DIE8, S_VILE_DIE9, S_VILE_DIE10, S_FIRE1, S_FIRE2, S_FIRE3, S_FIRE4, S_FIRE5, S_FIRE6, S_FIRE7, S_FIRE8, S_FIRE9, S_FIRE10, S_FIRE11, S_FIRE12, S_FIRE13, S_FIRE14, S_FIRE15, S_FIRE16, S_FIRE17, S_FIRE18, S_FIRE19, S_FIRE20, S_FIRE21, S_FIRE22, S_FIRE23, S_FIRE24, S_FIRE25, S_FIRE26, S_FIRE27, S_FIRE28, S_FIRE29, S_FIRE30, S_SMOKE1, S_SMOKE2, S_SMOKE3, S_SMOKE4, S_SMOKE5, S_TRACER, S_TRACER2, S_TRACEEXP1, S_TRACEEXP2, S_TRACEEXP3, S_SKEL_STND, S_SKEL_STND2, S_SKEL_RUN1, S_SKEL_RUN2, S_SKEL_RUN3, S_SKEL_RUN4, S_SKEL_RUN5, S_SKEL_RUN6, S_SKEL_RUN7, S_SKEL_RUN8, S_SKEL_RUN9, S_SKEL_RUN10, S_SKEL_RUN11, S_SKEL_RUN12, S_SKEL_FIST1, S_SKEL_FIST2, S_SKEL_FIST3, S_SKEL_FIST4, S_SKEL_MISS1, S_SKEL_MISS2, S_SKEL_MISS3, S_SKEL_MISS4, S_SKEL_PAIN, S_SKEL_PAIN2, S_SKEL_DIE1, S_SKEL_DIE2, S_SKEL_DIE3, S_SKEL_DIE4, S_SKEL_DIE5, S_SKEL_DIE6, S_SKEL_RAISE1, S_SKEL_RAISE2, S_SKEL_RAISE3, S_SKEL_RAISE4, S_SKEL_RAISE5, S_SKEL_RAISE6, S_FATSHOT1, S_FATSHOT2, S_FATSHOTX1, S_FATSHOTX2, S_FATSHOTX3, S_FATT_STND, S_FATT_STND2, S_FATT_RUN1, S_FATT_RUN2, S_FATT_RUN3, S_FATT_RUN4, S_FATT_RUN5, S_FATT_RUN6, S_FATT_RUN7, S_FATT_RUN8, S_FATT_RUN9, S_FATT_RUN10, S_FATT_RUN11, S_FATT_RUN12, S_FATT_ATK1, S_FATT_ATK2, S_FATT_ATK3, S_FATT_ATK4, S_FATT_ATK5, S_FATT_ATK6, S_FATT_ATK7, S_FATT_ATK8, S_FATT_ATK9, S_FATT_ATK10, S_FATT_PAIN, S_FATT_PAIN2, S_FATT_DIE1, S_FATT_DIE2, S_FATT_DIE3, S_FATT_DIE4, S_FATT_DIE5, S_FATT_DIE6, S_FATT_DIE7, S_FATT_DIE8, S_FATT_DIE9, S_FATT_DIE10, S_FATT_RAISE1, S_FATT_RAISE2, S_FATT_RAISE3, S_FATT_RAISE4, S_FATT_RAISE5, S_FATT_RAISE6, S_FATT_RAISE7, S_FATT_RAISE8, S_CPOS_STND, S_CPOS_STND2, S_CPOS_RUN1, S_CPOS_RUN2, S_CPOS_RUN3, S_CPOS_RUN4, S_CPOS_RUN5, S_CPOS_RUN6, S_CPOS_RUN7, S_CPOS_RUN8, S_CPOS_ATK1, S_CPOS_ATK2, S_CPOS_ATK3, S_CPOS_ATK4, S_CPOS_PAIN, S_CPOS_PAIN2, S_CPOS_DIE1, S_CPOS_DIE2, S_CPOS_DIE3, S_CPOS_DIE4, S_CPOS_DIE5, S_CPOS_DIE6, S_CPOS_DIE7, S_CPOS_XDIE1, S_CPOS_XDIE2, S_CPOS_XDIE3, S_CPOS_XDIE4, S_CPOS_XDIE5, S_CPOS_XDIE6, S_CPOS_RAISE1, S_CPOS_RAISE2, S_CPOS_RAISE3, S_CPOS_RAISE4, S_CPOS_RAISE5, S_CPOS_RAISE6, S_CPOS_RAISE7, S_TROO_STND, S_TROO_STND2, S_TROO_RUN1, S_TROO_RUN2, S_TROO_RUN3, S_TROO_RUN4, S_TROO_RUN5, S_TROO_RUN6, S_TROO_RUN7, S_TROO_RUN8, S_TROO_ATK1, S_TROO_ATK2, S_TROO_ATK3, S_TROO_PAIN, S_TROO_PAIN2, S_TROO_DIE1, S_TROO_DIE2, S_TROO_DIE3, S_TROO_DIE4, S_TROO_DIE5, S_TROO_XDIE1, S_TROO_XDIE2, S_TROO_XDIE3, S_TROO_XDIE4, S_TROO_XDIE5, S_TROO_XDIE6, S_TROO_XDIE7, S_TROO_XDIE8, S_TROO_RAISE1, S_TROO_RAISE2, S_TROO_RAISE3, S_TROO_RAISE4, S_TROO_RAISE5, S_SARG_STND, S_SARG_STND2, S_SARG_RUN1, S_SARG_RUN2, S_SARG_RUN3, S_SARG_RUN4, S_SARG_RUN5, S_SARG_RUN6, S_SARG_RUN7, S_SARG_RUN8, S_SARG_ATK1, S_SARG_ATK2, S_SARG_ATK3, S_SARG_PAIN, S_SARG_PAIN2, S_SARG_DIE1, S_SARG_DIE2, S_SARG_DIE3, S_SARG_DIE4, S_SARG_DIE5, S_SARG_DIE6, S_SARG_RAISE1, S_SARG_RAISE2, S_SARG_RAISE3, S_SARG_RAISE4, S_SARG_RAISE5, S_SARG_RAISE6, S_HEAD_STND, S_HEAD_RUN1, S_HEAD_ATK1, S_HEAD_ATK2, S_HEAD_ATK3, S_HEAD_PAIN, S_HEAD_PAIN2, S_HEAD_PAIN3, S_HEAD_DIE1, S_HEAD_DIE2, S_HEAD_DIE3, S_HEAD_DIE4, S_HEAD_DIE5, S_HEAD_DIE6, S_HEAD_RAISE1, S_HEAD_RAISE2, S_HEAD_RAISE3, S_HEAD_RAISE4, S_HEAD_RAISE5, S_HEAD_RAISE6, S_BRBALL1, S_BRBALL2, S_BRBALLX1, S_BRBALLX2, S_BRBALLX3, S_BOSS_STND, S_BOSS_STND2, S_BOSS_RUN1, S_BOSS_RUN2, S_BOSS_RUN3, S_BOSS_RUN4, S_BOSS_RUN5, S_BOSS_RUN6, S_BOSS_RUN7, S_BOSS_RUN8, S_BOSS_ATK1, S_BOSS_ATK2, S_BOSS_ATK3, S_BOSS_PAIN, S_BOSS_PAIN2, S_BOSS_DIE1, S_BOSS_DIE2, S_BOSS_DIE3, S_BOSS_DIE4, S_BOSS_DIE5, S_BOSS_DIE6, S_BOSS_DIE7, S_BOSS_RAISE1, S_BOSS_RAISE2, S_BOSS_RAISE3, S_BOSS_RAISE4, S_BOSS_RAISE5, S_BOSS_RAISE6, S_BOSS_RAISE7, S_BOS2_STND, S_BOS2_STND2, S_BOS2_RUN1, S_BOS2_RUN2, S_BOS2_RUN3, S_BOS2_RUN4, S_BOS2_RUN5, S_BOS2_RUN6, S_BOS2_RUN7, S_BOS2_RUN8, S_BOS2_ATK1, S_BOS2_ATK2, S_BOS2_ATK3, S_BOS2_PAIN, S_BOS2_PAIN2, S_BOS2_DIE1, S_BOS2_DIE2, S_BOS2_DIE3, S_BOS2_DIE4, S_BOS2_DIE5, S_BOS2_DIE6, S_BOS2_DIE7, S_BOS2_RAISE1, S_BOS2_RAISE2, S_BOS2_RAISE3, S_BOS2_RAISE4, S_BOS2_RAISE5, S_BOS2_RAISE6, S_BOS2_RAISE7, S_SKULL_STND, S_SKULL_STND2, S_SKULL_RUN1, S_SKULL_RUN2, S_SKULL_ATK1, S_SKULL_ATK2, S_SKULL_ATK3, S_SKULL_ATK4, S_SKULL_PAIN, S_SKULL_PAIN2, S_SKULL_DIE1, S_SKULL_DIE2, S_SKULL_DIE3, S_SKULL_DIE4, S_SKULL_DIE5, S_SKULL_DIE6, S_SPID_STND, S_SPID_STND2, S_SPID_RUN1, S_SPID_RUN2, S_SPID_RUN3, S_SPID_RUN4, S_SPID_RUN5, S_SPID_RUN6, S_SPID_RUN7, S_SPID_RUN8, S_SPID_RUN9, S_SPID_RUN10, S_SPID_RUN11, S_SPID_RUN12, S_SPID_ATK1, S_SPID_ATK2, S_SPID_ATK3, S_SPID_ATK4, S_SPID_PAIN, S_SPID_PAIN2, S_SPID_DIE1, S_SPID_DIE2, S_SPID_DIE3, S_SPID_DIE4, S_SPID_DIE5, S_SPID_DIE6, S_SPID_DIE7, S_SPID_DIE8, S_SPID_DIE9, S_SPID_DIE10, S_SPID_DIE11, S_BSPI_STND, S_BSPI_STND2, S_BSPI_SIGHT, S_BSPI_RUN1, S_BSPI_RUN2, S_BSPI_RUN3, S_BSPI_RUN4, S_BSPI_RUN5, S_BSPI_RUN6, S_BSPI_RUN7, S_BSPI_RUN8, S_BSPI_RUN9, S_BSPI_RUN10, S_BSPI_RUN11, S_BSPI_RUN12, S_BSPI_ATK1, S_BSPI_ATK2, S_BSPI_ATK3, S_BSPI_ATK4, S_BSPI_PAIN, S_BSPI_PAIN2, S_BSPI_DIE1, S_BSPI_DIE2, S_BSPI_DIE3, S_BSPI_DIE4, S_BSPI_DIE5, S_BSPI_DIE6, S_BSPI_DIE7, S_BSPI_RAISE1, S_BSPI_RAISE2, S_BSPI_RAISE3, S_BSPI_RAISE4, S_BSPI_RAISE5, S_BSPI_RAISE6, S_BSPI_RAISE7, S_ARACH_PLAZ, S_ARACH_PLAZ2, S_ARACH_PLEX, S_ARACH_PLEX2, S_ARACH_PLEX3, S_ARACH_PLEX4, S_ARACH_PLEX5, S_CYBER_STND, S_CYBER_STND2, S_CYBER_RUN1, S_CYBER_RUN2, S_CYBER_RUN3, S_CYBER_RUN4, S_CYBER_RUN5, S_CYBER_RUN6, S_CYBER_RUN7, S_CYBER_RUN8, S_CYBER_ATK1, S_CYBER_ATK2, S_CYBER_ATK3, S_CYBER_ATK4, S_CYBER_ATK5, S_CYBER_ATK6, S_CYBER_PAIN, S_CYBER_DIE1, S_CYBER_DIE2, S_CYBER_DIE3, S_CYBER_DIE4, S_CYBER_DIE5, S_CYBER_DIE6, S_CYBER_DIE7, S_CYBER_DIE8, S_CYBER_DIE9, S_CYBER_DIE10, S_PAIN_STND, S_PAIN_RUN1, S_PAIN_RUN2, S_PAIN_RUN3, S_PAIN_RUN4, S_PAIN_RUN5, S_PAIN_RUN6, S_PAIN_ATK1, S_PAIN_ATK2, S_PAIN_ATK3, S_PAIN_ATK4, S_PAIN_PAIN, S_PAIN_PAIN2, S_PAIN_DIE1, S_PAIN_DIE2, S_PAIN_DIE3, S_PAIN_DIE4, S_PAIN_DIE5, S_PAIN_DIE6, S_PAIN_RAISE1, S_PAIN_RAISE2, S_PAIN_RAISE3, S_PAIN_RAISE4, S_PAIN_RAISE5, S_PAIN_RAISE6, S_SSWV_STND, S_SSWV_STND2, S_SSWV_RUN1, S_SSWV_RUN2, S_SSWV_RUN3, S_SSWV_RUN4, S_SSWV_RUN5, S_SSWV_RUN6, S_SSWV_RUN7, S_SSWV_RUN8, S_SSWV_ATK1, S_SSWV_ATK2, S_SSWV_ATK3, S_SSWV_ATK4, S_SSWV_ATK5, S_SSWV_ATK6, S_SSWV_PAIN, S_SSWV_PAIN2, S_SSWV_DIE1, S_SSWV_DIE2, S_SSWV_DIE3, S_SSWV_DIE4, S_SSWV_DIE5, S_SSWV_XDIE1, S_SSWV_XDIE2, S_SSWV_XDIE3, S_SSWV_XDIE4, S_SSWV_XDIE5, S_SSWV_XDIE6, S_SSWV_XDIE7, S_SSWV_XDIE8, S_SSWV_XDIE9, S_SSWV_RAISE1, S_SSWV_RAISE2, S_SSWV_RAISE3, S_SSWV_RAISE4, S_SSWV_RAISE5, S_KEENSTND, S_COMMKEEN, S_COMMKEEN2, S_COMMKEEN3, S_COMMKEEN4, S_COMMKEEN5, S_COMMKEEN6, S_COMMKEEN7, S_COMMKEEN8, S_COMMKEEN9, S_COMMKEEN10, S_COMMKEEN11, S_COMMKEEN12, S_KEENPAIN, S_KEENPAIN2, S_BRAIN, S_BRAIN_PAIN, S_BRAIN_DIE1, S_BRAIN_DIE2, S_BRAIN_DIE3, S_BRAIN_DIE4, S_BRAINEYE, S_BRAINEYESEE, S_BRAINEYE1, S_SPAWN1, S_SPAWN2, S_SPAWN3, S_SPAWN4, S_SPAWNFIRE1, S_SPAWNFIRE2, S_SPAWNFIRE3, S_SPAWNFIRE4, S_SPAWNFIRE5, S_SPAWNFIRE6, S_SPAWNFIRE7, S_SPAWNFIRE8, S_BRAINEXPLODE1, S_BRAINEXPLODE2, S_BRAINEXPLODE3, S_ARM1, S_ARM1A, S_ARM2, S_ARM2A, S_BAR1, S_BAR2, S_BEXP, S_BEXP2, S_BEXP3, S_BEXP4, S_BEXP5, S_BBAR1, S_BBAR2, S_BBAR3, S_BON1, S_BON1A, S_BON1B, S_BON1C, S_BON1D, S_BON1E, S_BON2, S_BON2A, S_BON2B, S_BON2C, S_BON2D, S_BON2E, S_BKEY, S_BKEY2, S_RKEY, S_RKEY2, S_YKEY, S_YKEY2, S_BSKULL, S_BSKULL2, S_RSKULL, S_RSKULL2, S_YSKULL, S_YSKULL2, S_STIM, S_MEDI, S_SOUL, S_SOUL2, S_SOUL3, S_SOUL4, S_SOUL5, S_SOUL6, S_PINV, S_PINV2, S_PINV3, S_PINV4, S_PSTR, S_PINS, S_PINS2, S_PINS3, S_PINS4, S_MEGA, S_MEGA2, S_MEGA3, S_MEGA4, S_SUIT, S_PMAP, S_PMAP2, S_PMAP3, S_PMAP4, S_PMAP5, S_PMAP6, S_PVIS, S_PVIS2, S_CLIP, S_AMMO, S_ROCK, S_BROK, S_CELL, S_CELP, S_SHEL, S_SBOX, S_BPAK, S_BFUG, S_MGUN, S_CSAW, S_LAUN, S_PLAS, S_SHOT, S_SHOT2, S_COLU, S_STALAG, S_BLOODYTWITCH, S_BLOODYTWITCH2, S_BLOODYTWITCH3, S_BLOODYTWITCH4, S_DEADTORSO, S_DEADBOTTOM, S_HEADSONSTICK, S_GIBS, S_HEADONASTICK, S_HEADCANDLES, S_HEADCANDLES2, S_DEADSTICK, S_LIVESTICK, S_LIVESTICK2, S_MEAT2, S_MEAT3, S_MEAT4, S_MEAT5, S_STALAGTITE, S_TALLGRNCOL, S_SHRTGRNCOL, S_TALLREDCOL, S_SHRTREDCOL, S_CANDLESTIK, S_CANDELABRA, S_SKULLCOL, S_TORCHTREE, S_BIGTREE, S_TECHPILLAR, S_EVILEYE, S_EVILEYE2, S_EVILEYE3, S_EVILEYE4, S_FLOATSKULL, S_FLOATSKULL2, S_FLOATSKULL3, S_HEARTCOL, S_HEARTCOL2, S_BLUETORCH, S_BLUETORCH2, S_BLUETORCH3, S_BLUETORCH4, S_GREENTORCH, S_GREENTORCH2, S_GREENTORCH3, S_GREENTORCH4, S_REDTORCH, S_REDTORCH2, S_REDTORCH3, S_REDTORCH4, S_BTORCHSHRT, S_BTORCHSHRT2, S_BTORCHSHRT3, S_BTORCHSHRT4, S_GTORCHSHRT, S_GTORCHSHRT2, S_GTORCHSHRT3, S_GTORCHSHRT4, S_RTORCHSHRT, S_RTORCHSHRT2, S_RTORCHSHRT3, S_RTORCHSHRT4, S_HANGNOGUTS, S_HANGBNOBRAIN, S_HANGTLOOKDN, S_HANGTSKULL, S_HANGTLOOKUP, S_HANGTNOBRAIN, S_COLONGIBS, S_SMALLPOOL, S_BRAINSTEM, S_TECHLAMP, S_TECHLAMP2, S_TECHLAMP3, S_TECHLAMP4, S_TECH2LAMP, S_TECH2LAMP2, S_TECH2LAMP3, S_TECH2LAMP4, // [crispy] additional BOOM and MBF states, sprites and code pointers S_TNT1, S_GRENADE, S_DETONATE, S_DETONATE2, S_DETONATE3, S_DOGS_STND, 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, S_OLDBFG1, S_OLDBFG42 = S_OLDBFG1 + 41, S_OLDBFG43, S_PLS1BALL, S_PLS1BALL2, S_PLS1EXP, S_PLS1EXP2, S_PLS1EXP3, S_PLS1EXP4, S_PLS1EXP5, S_PLS2BALL, S_PLS2BALL2, S_PLS2BALLX1, S_PLS2BALLX2, S_PLS2BALLX3, S_BON3, S_BON4, 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, // [BH] extra dehacked states EXTRASTATES = 1089, NUMSTATES = 4000 } statenum_t; typedef struct { spritenum_t sprite; int frame; int tics; // void (*action) (); actionf_t action; statenum_t nextstate; int misc1; int misc2; } state_t; extern state_t states[NUMSTATES]; extern const char *sprnames[]; typedef enum { MT_PLAYER, MT_POSSESSED, MT_SHOTGUY, MT_VILE, MT_FIRE, MT_UNDEAD, MT_TRACER, MT_SMOKE, MT_FATSO, MT_FATSHOT, MT_CHAINGUY, MT_TROOP, MT_SERGEANT, MT_SHADOWS, MT_HEAD, MT_BRUISER, MT_BRUISERSHOT, MT_KNIGHT, MT_SKULL, MT_SPIDER, MT_BABY, MT_CYBORG, MT_PAIN, MT_WOLFSS, MT_KEEN, MT_BOSSBRAIN, MT_BOSSSPIT, MT_BOSSTARGET, MT_SPAWNSHOT, MT_SPAWNFIRE, MT_BARREL, MT_TROOPSHOT, MT_HEADSHOT, MT_ROCKET, MT_PLASMA, MT_BFG, MT_ARACHPLAZ, MT_PUFF, MT_BLOOD, MT_TFOG, MT_IFOG, MT_TELEPORTMAN, MT_EXTRABFG, MT_MISC0, MT_MISC1, MT_MISC2, MT_MISC3, MT_MISC4, MT_MISC5, MT_MISC6, MT_MISC7, MT_MISC8, MT_MISC9, MT_MISC10, MT_MISC11, MT_MISC12, MT_INV, MT_MISC13, MT_INS, MT_MISC14, MT_MISC15, MT_MISC16, MT_MEGA, MT_CLIP, MT_MISC17, MT_MISC18, MT_MISC19, MT_MISC20, MT_MISC21, MT_MISC22, MT_MISC23, MT_MISC24, MT_MISC25, MT_CHAINGUN, MT_MISC26, MT_MISC27, MT_MISC28, MT_SHOTGUN, MT_SUPERSHOTGUN, MT_MISC29, MT_MISC30, MT_MISC31, MT_MISC32, MT_MISC33, MT_MISC34, MT_MISC35, MT_MISC36, MT_MISC37, MT_MISC38, MT_MISC39, MT_MISC40, MT_MISC41, MT_MISC42, MT_MISC43, MT_MISC44, MT_MISC45, MT_MISC46, MT_MISC47, MT_MISC48, MT_MISC49, MT_MISC50, MT_MISC51, MT_MISC52, MT_MISC53, MT_MISC54, MT_MISC55, MT_MISC56, MT_MISC57, MT_MISC58, MT_MISC59, MT_MISC60, MT_MISC61, MT_MISC62, MT_MISC63, MT_MISC64, MT_MISC65, MT_MISC66, MT_MISC67, MT_MISC68, MT_MISC69, MT_MISC70, MT_MISC71, MT_MISC72, MT_MISC73, MT_MISC74, MT_MISC75, MT_MISC76, MT_MISC77, MT_MISC78, MT_MISC79, MT_MISC80, MT_MISC81, MT_MISC82, MT_MISC83, MT_MISC84, MT_MISC85, MT_MISC86, // [crispy] additional BOOM and MBF states, sprites and code pointers MT_PUSH, MT_PULL, MT_DOGS, MT_PLASMA1, MT_PLASMA2, MT_SCEPTRE, MT_BIBLE, // [crispy] support MUSINFO lump (dynamic music changing) MT_MUSICSOURCE, // [BH] 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 } mobjtype_t; typedef struct { int doomednum; int spawnstate; int spawnhealth; int seestate; int seesound; int reactiontime; int attacksound; int painstate; int painchance; int painsound; int meleestate; int missilestate; int deathstate; int xdeathstate; int deathsound; int speed; int radius; int height; int mass; int damage; int activesound; int flags; int raisestate; // [crispy] height of the spawnstate's first sprite in pixels int actualheight; } mobjinfo_t; extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; #endif crispy-doom-crispy-doom-5.6.4/src/doom/m_background.h000066400000000000000000000626751360717211000226050ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2018 Fabian Greffrath // Copyright(C) 2018 Julia Nechaevskaya // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // [crispy] Crispness menu tiled background // /* The following array contains a seamlessly tiling 64x64 icon depicting Crisps to use as a background texture for the Crispness menu. It has been converted from a raw lump in Doom's flat format with the following code. The actual artwork has been created by Julia Nechaevskaya, thank you so incredibly much for this! - Fabian #include int main (int argc, char **argv) { FILE *file; int c, i = 0; if (argc < 2 || !(file = fopen(argv[1], "r"))) { return -1; } while ((c = getc(file)) != EOF) { printf("0x%02x, ", c); if (!(++i % 64)) { printf("\n"); } } fclose(file); return 0; } */ static const byte crispness_background[] = { 0x8d, 0x8c, 0x8b, 0x8c, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8d, 0x8e, 0x8c, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x89, 0x92, 0x89, 0x89, 0x92, 0x92, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x89, 0x8a, 0x8a, 0x8d, 0x8d, 0x8c, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8a, 0x8a, 0x8b, 0x8a, 0x8a, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8c, 0x8b, 0x8b, 0x8d, 0x0d, 0x8d, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8e, 0x8d, 0x8d, 0x8c, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8a, 0x8a, 0x89, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8a, 0x88, 0x8c, 0x8e, 0x8e, 0x8d, 0x8c, 0x8b, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8a, 0x8a, 0x93, 0x8a, 0x8a, 0x89, 0x8a, 0x89, 0x8c, 0x8f, 0x8e, 0x8c, 0x8c, 0x8b, 0x8b, 0x93, 0x89, 0x89, 0x89, 0x92, 0x89, 0x8a, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x89, 0x91, 0x88, 0x92, 0x92, 0x92, 0x89, 0x8a, 0x8a, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x8b, 0x8d, 0x8e, 0x8d, 0x8d, 0x8c, 0x8c, 0x8a, 0x88, 0x91, 0x87, 0x88, 0x8b, 0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8f, 0x8f, 0x8d, 0x8d, 0x8c, 0x8b, 0x8b, 0x8a, 0x8a, 0x89, 0x92, 0x89, 0x8a, 0x8a, 0x89, 0x8a, 0x89, 0x89, 0x8a, 0x92, 0x92, 0x8b, 0x88, 0x88, 0x89, 0x89, 0x88, 0x92, 0x92, 0x89, 0x89, 0x8a, 0x8a, 0x89, 0x88, 0x92, 0x89, 0x8c, 0x8e, 0x8f, 0x8f, 0x0d, 0x8f, 0x8d, 0x8a, 0x88, 0x91, 0x90, 0x90, 0x88, 0x8f, 0x0e, 0x0d, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8b, 0x8a, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8e, 0x8e, 0x8d, 0x8c, 0x8c, 0x8b, 0x8b, 0x8a, 0x92, 0x89, 0x89, 0x92, 0x89, 0x89, 0x92, 0x8a, 0x8a, 0x91, 0x87, 0x92, 0x8c, 0x92, 0x91, 0x88, 0x91, 0x87, 0x91, 0x88, 0x89, 0x92, 0x89, 0x8a, 0x92, 0x87, 0x91, 0x92, 0x8c, 0x8f, 0x9e, 0x0e, 0x6c, 0x0e, 0x0e, 0x8e, 0x8b, 0x89, 0x92, 0x91, 0x90, 0x87, 0x8e, 0x8f, 0x9e, 0x0e, 0x8f, 0x8d, 0x8c, 0x8a, 0x93, 0x8a, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8d, 0x8c, 0x8c, 0x8b, 0x8a, 0x8a, 0x89, 0x89, 0x8a, 0x8a, 0x89, 0x89, 0x8a, 0x8b, 0x8a, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x91, 0x88, 0x87, 0x91, 0x92, 0x89, 0x92, 0x92, 0x88, 0x88, 0x91, 0x87, 0x88, 0x8a, 0x8c, 0x8e, 0x8f, 0x8e, 0x0d, 0x9e, 0x0d, 0x8d, 0x8c, 0x8c, 0x8b, 0x92, 0x91, 0x86, 0x87, 0x8a, 0x8e, 0x9f, 0x0d, 0x8e, 0x8b, 0x8a, 0x8a, 0x8a, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8d, 0x8c, 0x8b, 0x8b, 0x89, 0x89, 0x92, 0x8a, 0x93, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x88, 0x91, 0x91, 0x87, 0x92, 0x92, 0x87, 0x91, 0x88, 0x88, 0x87, 0x88, 0x87, 0x87, 0x91, 0x87, 0x87, 0x87, 0x92, 0x89, 0x89, 0x8b, 0x8d, 0x8f, 0x0d, 0x8d, 0x8b, 0x89, 0x8b, 0x8c, 0x8b, 0x8c, 0x8d, 0x8c, 0x92, 0x87, 0x88, 0x8c, 0x0d, 0x9f, 0x8d, 0x8b, 0x8a, 0x89, 0x8c, 0x8d, 0x8c, 0x8d, 0x8e, 0x8e, 0x8c, 0x8c, 0x8b, 0x8a, 0x89, 0x89, 0x92, 0x89, 0x89, 0x89, 0x89, 0x92, 0x89, 0x92, 0x88, 0x91, 0x86, 0x91, 0x87, 0x88, 0x91, 0x88, 0x89, 0x92, 0x91, 0x91, 0x91, 0x86, 0x87, 0x91, 0x87, 0x92, 0x8a, 0x8d, 0x8d, 0x8c, 0x8c, 0x8d, 0x8d, 0x8a, 0x8b, 0x8c, 0x8c, 0x8b, 0x89, 0x8a, 0x8b, 0x8d, 0x8c, 0x8a, 0x8b, 0x8b, 0x8c, 0x0d, 0x0d, 0x8e, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x8c, 0x8d, 0x8c, 0x8c, 0x8a, 0x89, 0x92, 0x89, 0x92, 0x89, 0x92, 0x92, 0x89, 0x89, 0x92, 0x88, 0x91, 0x87, 0x86, 0x91, 0x90, 0x91, 0x89, 0x8a, 0x92, 0x87, 0x87, 0x86, 0x86, 0x90, 0x91, 0x87, 0x87, 0x92, 0x8b, 0x8f, 0x0e, 0x9f, 0x0e, 0x0d, 0x8e, 0x8f, 0x8e, 0x8f, 0x0d, 0x8e, 0x8d, 0x8a, 0x89, 0x89, 0x8a, 0x8b, 0x89, 0x89, 0x8b, 0x8d, 0x8c, 0x8f, 0x0e, 0x8e, 0x8d, 0x8d, 0x8c, 0x8a, 0x8b, 0x8b, 0x8c, 0x8a, 0x88, 0x91, 0x88, 0x92, 0x89, 0x92, 0x88, 0x92, 0x92, 0x92, 0x88, 0x92, 0x87, 0x91, 0x91, 0x87, 0x87, 0x91, 0x92, 0x88, 0x91, 0x86, 0x91, 0x91, 0x91, 0x87, 0x86, 0x86, 0x92, 0x89, 0x8c, 0x8e, 0x0e, 0x0d, 0x8e, 0x8c, 0x8e, 0x0d, 0x8e, 0x8e, 0x0e, 0x9f, 0x0e, 0x0d, 0x0d, 0x8e, 0x8d, 0x8b, 0x8a, 0x8a, 0x88, 0x92, 0x88, 0x8a, 0x9f, 0x8f, 0x8d, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x91, 0x88, 0x88, 0x91, 0x87, 0x88, 0x92, 0x92, 0x88, 0x88, 0x87, 0x88, 0x88, 0x91, 0x86, 0x91, 0x87, 0x91, 0x87, 0x87, 0x91, 0x90, 0x86, 0x91, 0x86, 0x91, 0x86, 0x91, 0x91, 0x89, 0x8a, 0x8a, 0x88, 0x8a, 0x89, 0x89, 0x89, 0x8b, 0x8c, 0x8c, 0x8c, 0x0d, 0x6d, 0x0f, 0x6d, 0x09, 0x0f, 0x09, 0x0d, 0x8e, 0x8e, 0x8d, 0x8a, 0x89, 0x92, 0x0d, 0x8e, 0x8c, 0x8b, 0x8a, 0x8a, 0x89, 0x8b, 0x8a, 0x88, 0x92, 0x91, 0x87, 0x87, 0x91, 0x88, 0x87, 0x86, 0x91, 0x91, 0x91, 0x91, 0x87, 0x91, 0x87, 0x91, 0x86, 0x87, 0x91, 0x90, 0x86, 0x91, 0x91, 0x86, 0x86, 0x91, 0x91, 0x88, 0x88, 0x92, 0x92, 0x92, 0x88, 0x8a, 0x8a, 0x8a, 0x8a, 0x8c, 0x8c, 0x8d, 0x0d, 0x0f, 0x6d, 0x0e, 0x0e, 0x6d, 0x0f, 0x6d, 0x0f, 0x09, 0x0e, 0x0d, 0x8d, 0x8b, 0x0e, 0x8d, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x91, 0x86, 0x91, 0x87, 0x91, 0x87, 0x91, 0x86, 0x91, 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x91, 0x87, 0x90, 0x91, 0x86, 0x91, 0x91, 0x90, 0x90, 0x91, 0x91, 0x87, 0x87, 0x92, 0x92, 0x89, 0x8a, 0x89, 0x89, 0x8c, 0x8b, 0x8b, 0x8c, 0x8e, 0x8e, 0x9f, 0x0e, 0x6d, 0x0e, 0x0d, 0x0d, 0x0e, 0x0f, 0x6e, 0x0f, 0x0e, 0x6d, 0x0d, 0x0d, 0x9f, 0x0e, 0x8d, 0x8a, 0x8b, 0x89, 0x89, 0x92, 0x92, 0x88, 0x86, 0x90, 0x86, 0x86, 0x86, 0x86, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x86, 0x91, 0x88, 0x87, 0x91, 0x86, 0x86, 0x86, 0x91, 0x86, 0x91, 0x87, 0x87, 0x91, 0x91, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x8d, 0x8c, 0x8d, 0x0d, 0x9e, 0x0e, 0x0e, 0x0d, 0x8f, 0x8d, 0x8e, 0x0d, 0x6c, 0x0e, 0x0f, 0x09, 0x6d, 0x0f, 0x8e, 0x8e, 0x6d, 0x0d, 0x8c, 0x89, 0x92, 0x92, 0x92, 0x88, 0x87, 0x92, 0x90, 0x90, 0x86, 0x90, 0x90, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x91, 0x92, 0x88, 0x91, 0x90, 0x86, 0x90, 0x86, 0x90, 0x87, 0x92, 0x86, 0x90, 0x88, 0x92, 0x8a, 0x8a, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x0d, 0x0e, 0x0e, 0x8f, 0x8d, 0x8c, 0x8c, 0x8c, 0x8d, 0x8e, 0x0e, 0x0e, 0x6c, 0x0e, 0x0e, 0x0f, 0x6d, 0x0e, 0x0e, 0x0e, 0x8b, 0x87, 0x88, 0x92, 0x88, 0x91, 0x86, 0x87, 0x91, 0x89, 0x8b, 0x93, 0x8a, 0x8a, 0x89, 0x92, 0x86, 0x90, 0x90, 0x86, 0x86, 0x90, 0x86, 0x88, 0x92, 0x86, 0x90, 0x91, 0x86, 0x90, 0x91, 0x86, 0x86, 0x91, 0x87, 0x92, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x0d, 0x9f, 0x0d, 0x8e, 0x8b, 0x89, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8e, 0x9e, 0x8d, 0x8d, 0x8f, 0x6c, 0x0f, 0x09, 0x6d, 0x0e, 0x8c, 0x87, 0x92, 0x87, 0x91, 0x86, 0x92, 0x89, 0x8a, 0x8e, 0x0e, 0x0e, 0x0d, 0x0d, 0x8e, 0x8a, 0x92, 0x86, 0x90, 0x90, 0x90, 0x90, 0x91, 0x86, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x91, 0x91, 0x91, 0x87, 0x89, 0x8a, 0x89, 0x8a, 0x8d, 0x8e, 0x8f, 0x8f, 0x9f, 0x0e, 0x8d, 0x8c, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8c, 0x8d, 0x8d, 0x8d, 0x0e, 0x8e, 0x8c, 0x8c, 0x8c, 0x8e, 0x0e, 0x0f, 0x6d, 0x8f, 0x8a, 0x89, 0x92, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x0e, 0x9f, 0x0e, 0x0d, 0x0d, 0x0d, 0x8d, 0x8b, 0x87, 0x90, 0x90, 0x86, 0x90, 0x86, 0x90, 0x90, 0x87, 0x88, 0x87, 0x87, 0x88, 0x91, 0x86, 0x87, 0x91, 0x8a, 0x93, 0x8a, 0x8c, 0x8f, 0x0d, 0x9f, 0x0e, 0x0e, 0x8b, 0x8a, 0x89, 0x89, 0x8a, 0x8a, 0x89, 0x8a, 0x8c, 0x8e, 0x8d, 0x8d, 0x0e, 0x6d, 0x0f, 0x09, 0x8e, 0x8b, 0x8c, 0x8d, 0x8e, 0x0e, 0x8e, 0x8b, 0x8d, 0x8f, 0x0d, 0x0e, 0x0e, 0x6d, 0x0f, 0x6d, 0x0f, 0x6c, 0x0d, 0x0d, 0x0d, 0x8d, 0x89, 0x89, 0x92, 0x91, 0x90, 0x86, 0x86, 0x90, 0x86, 0x86, 0x91, 0x88, 0x91, 0x86, 0x86, 0x91, 0x8b, 0x8e, 0x8c, 0x8b, 0x8e, 0x0d, 0x6d, 0x0e, 0x0d, 0x8d, 0x8a, 0x8a, 0x89, 0x92, 0x89, 0x89, 0x92, 0x89, 0x8c, 0x8d, 0x8d, 0x8d, 0x0d, 0x0e, 0x6d, 0x4f, 0x6f, 0x09, 0x0d, 0x8c, 0x8c, 0x8d, 0x8e, 0x8d, 0x8e, 0x0d, 0x6d, 0x09, 0x6d, 0x0e, 0x09, 0x0e, 0x0f, 0x09, 0x0e, 0x9e, 0x0d, 0x8f, 0x8d, 0x8d, 0x8c, 0x88, 0x90, 0x90, 0x91, 0x86, 0x90, 0x90, 0x86, 0x91, 0x86, 0x90, 0x91, 0x8d, 0x0e, 0x9f, 0x0d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8b, 0x8b, 0x8a, 0x8b, 0x8a, 0x89, 0x92, 0x89, 0x92, 0x8a, 0x8b, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x0d, 0x0e, 0x0f, 0x6e, 0x4f, 0x6f, 0x6f, 0x0f, 0x8e, 0x8d, 0x8e, 0x8d, 0x8e, 0x0d, 0x0e, 0x0e, 0x0e, 0x6d, 0x0e, 0x6d, 0x0f, 0x6d, 0x0f, 0x6d, 0x0f, 0x9f, 0x0d, 0x8e, 0x8c, 0x92, 0x91, 0x89, 0x91, 0x90, 0x90, 0x86, 0x91, 0x86, 0x89, 0x0d, 0x0f, 0x6b, 0x8f, 0x0d, 0x0d, 0x8d, 0x8d, 0x8c, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x89, 0x8a, 0x8b, 0x89, 0x8a, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x8d, 0x8f, 0x6c, 0x0f, 0x0f, 0x6e, 0xee, 0x6f, 0x6f, 0x4e, 0x6d, 0x0e, 0x8f, 0x8d, 0x8d, 0x0d, 0x6d, 0x0e, 0x0e, 0x0e, 0x0e, 0x6d, 0x0f, 0x6d, 0x0f, 0x6d, 0x0f, 0x6d, 0x0e, 0x0e, 0x0e, 0x8d, 0x8d, 0x8c, 0x8b, 0x8b, 0x89, 0x8a, 0x8e, 0x0e, 0x6d, 0x0e, 0x0e, 0x8f, 0x8e, 0x8e, 0x8c, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8a, 0x8b, 0x93, 0x8a, 0x8a, 0x8a, 0x8d, 0x8d, 0x8c, 0x8c, 0x8d, 0x8e, 0x0d, 0x0e, 0x09, 0x0f, 0x6f, 0x4e, 0x0f, 0x6e, 0xee, 0x9e, 0x0e, 0x6d, 0x8e, 0x8d, 0x0d, 0x9f, 0x0e, 0x6d, 0x0e, 0x0e, 0x0e, 0x0f, 0x09, 0x0f, 0x0f, 0x0f, 0x09, 0x6e, 0x4e, 0x6d, 0x8e, 0x0d, 0x09, 0x0e, 0x0e, 0x0d, 0x09, 0x0e, 0x8d, 0x0d, 0x0d, 0x8e, 0x8d, 0x8e, 0x8b, 0x8a, 0x89, 0x89, 0x8a, 0x92, 0x89, 0x8a, 0x8a, 0x8b, 0x8a, 0x89, 0x89, 0x8d, 0x8d, 0x8a, 0x8c, 0x8c, 0x8c, 0x8e, 0x0d, 0x0e, 0x6d, 0x0f, 0x6e, 0x0f, 0x6e, 0x0f, 0x0e, 0x8c, 0x0d, 0xee, 0x09, 0x8e, 0x8e, 0x0d, 0x6c, 0x0e, 0x0e, 0x6d, 0x0e, 0x6d, 0x0e, 0x0f, 0x6d, 0x6d, 0x0f, 0x0f, 0x0f, 0x6e, 0x0e, 0x0d, 0x8f, 0x6c, 0x0d, 0x0d, 0x0d, 0x0d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8d, 0x8c, 0x8a, 0x8a, 0x8a, 0x8a, 0x92, 0x89, 0x89, 0x92, 0x89, 0x8a, 0x89, 0x89, 0x93, 0x8c, 0x8a, 0x8a, 0x8c, 0x8c, 0x8d, 0x8e, 0x8e, 0x9f, 0x0e, 0x0e, 0x0f, 0x6e, 0x0f, 0x09, 0x8e, 0x8c, 0x8e, 0x6d, 0x6f, 0xee, 0x0d, 0x8e, 0x0d, 0x0e, 0x9f, 0x0e, 0x0e, 0x0e, 0x6d, 0x0e, 0x0f, 0x0f, 0x6d, 0x0f, 0x6e, 0x4e, 0x6e, 0x0d, 0x8e, 0x8f, 0x8f, 0x0d, 0x0d, 0x6b, 0x0d, 0x8e, 0x9c, 0x8c, 0x8b, 0x8b, 0x8b, 0x93, 0x8a, 0x92, 0x87, 0x89, 0x92, 0x92, 0x92, 0x88, 0x92, 0x89, 0x8a, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8d, 0x8d, 0x8e, 0x0e, 0x6d, 0x0e, 0x09, 0x0f, 0x6d, 0x0e, 0x8c, 0x89, 0x8b, 0x0e, 0x6e, 0x6e, 0x4e, 0x0e, 0x8e, 0x8f, 0x0d, 0x0d, 0x6d, 0x0e, 0x0e, 0x6d, 0x0e, 0x6d, 0x0f, 0x6d, 0x0f, 0x0f, 0x0e, 0x8f, 0x8d, 0x8e, 0x8f, 0x9e, 0x0d, 0x0d, 0x9f, 0x8e, 0x8c, 0x8b, 0x8a, 0x8a, 0x89, 0x8b, 0x8a, 0x87, 0x91, 0x91, 0x88, 0x88, 0x88, 0x87, 0x92, 0x92, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8c, 0x8c, 0x8c, 0x8e, 0x0e, 0x0e, 0x6d, 0x0f, 0x09, 0x0f, 0x6d, 0x8c, 0x92, 0x8a, 0x8e, 0x4e, 0x0f, 0x6e, 0x6e, 0x6d, 0x8f, 0x8f, 0x0d, 0x0d, 0x9e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x6d, 0x09, 0x6d, 0x0d, 0x8d, 0x8e, 0x8e, 0x8f, 0x9f, 0x8f, 0x8f, 0x8d, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x91, 0x87, 0x87, 0x91, 0x87, 0x92, 0x92, 0x88, 0x89, 0x92, 0x88, 0x92, 0x89, 0x8a, 0x8b, 0x8c, 0x8c, 0x8e, 0x0e, 0x9f, 0x0e, 0x6d, 0x0f, 0x6d, 0x0f, 0x0d, 0x8a, 0x89, 0x8a, 0x0e, 0x6f, 0x0f, 0x4e, 0xee, 0x9f, 0x8e, 0x8e, 0x8e, 0x0d, 0x0d, 0x6c, 0x0e, 0x6d, 0x0e, 0x6d, 0x0f, 0x0e, 0x0d, 0x8d, 0x8d, 0x8c, 0x8e, 0x8d, 0x8d, 0x8e, 0x8d, 0x8d, 0x8b, 0x8a, 0x8a, 0x89, 0x89, 0x8a, 0x92, 0x87, 0x91, 0x87, 0x86, 0x91, 0x88, 0x88, 0x91, 0x92, 0x88, 0x92, 0x88, 0x92, 0x92, 0x8b, 0x8b, 0x8b, 0x8e, 0x6c, 0x8f, 0x0d, 0x0f, 0x6d, 0x0f, 0x0e, 0x09, 0x8e, 0x8b, 0x8a, 0x8c, 0x4e, 0x6f, 0x6e, 0x4e, 0x6e, 0x0e, 0x0d, 0x8e, 0x8d, 0x8f, 0x0d, 0x0d, 0x0e, 0x9f, 0x0f, 0x0e, 0x8f, 0x8d, 0x8c, 0x8c, 0x8d, 0x8f, 0x8d, 0x8c, 0x8b, 0x8c, 0x8d, 0x8c, 0x8b, 0x89, 0x92, 0x89, 0x89, 0x88, 0x91, 0x87, 0x91, 0x87, 0x87, 0x91, 0x88, 0x89, 0x88, 0x92, 0x87, 0x92, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x0d, 0x8f, 0x8d, 0x8f, 0x0e, 0x0f, 0x6d, 0x0e, 0x0e, 0x6d, 0x0d, 0x8c, 0x89, 0x8c, 0x8f, 0x0f, 0x0e, 0x0f, 0x6e, 0x6f, 0x6e, 0x0e, 0x8e, 0x8e, 0x8e, 0x8f, 0x0d, 0x6d, 0x0e, 0x8f, 0x8e, 0x8d, 0x8c, 0x8d, 0x8e, 0x8e, 0x8b, 0x8a, 0x8b, 0x8b, 0x8c, 0x8b, 0x89, 0x88, 0x89, 0x8a, 0x92, 0x86, 0x91, 0x87, 0x91, 0x91, 0x88, 0x92, 0x88, 0x92, 0x92, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x8c, 0x8d, 0x8e, 0x8e, 0x6c, 0x0f, 0x0f, 0x6d, 0x0f, 0x0f, 0x09, 0x8f, 0x8c, 0x8b, 0x8a, 0x8a, 0x8b, 0x8d, 0x8e, 0x0e, 0x0e, 0x9f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8f, 0x0e, 0x9f, 0x8e, 0x8e, 0x8e, 0x8d, 0x8b, 0x8b, 0x8d, 0x8e, 0x8c, 0x8b, 0x8c, 0x8c, 0x8c, 0x89, 0x89, 0x8b, 0x8d, 0x8b, 0x91, 0x87, 0x87, 0x91, 0x87, 0x91, 0x87, 0x88, 0x88, 0x92, 0x89, 0x89, 0x92, 0x89, 0x8b, 0x8c, 0x8c, 0x8b, 0x8c, 0x8e, 0x0d, 0x0e, 0x0f, 0x6d, 0x0e, 0x0e, 0x6c, 0x0e, 0x6d, 0x8f, 0x8d, 0x8e, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x89, 0x89, 0x8b, 0x9c, 0x8e, 0x8e, 0x8e, 0x6c, 0x0f, 0x8e, 0x8b, 0x8c, 0x8c, 0x8b, 0x8b, 0x8a, 0x8c, 0x8c, 0x8b, 0x8b, 0x8c, 0x8b, 0x8b, 0x8a, 0x8d, 0x8e, 0x8c, 0x8a, 0x88, 0x91, 0x87, 0x87, 0x88, 0x92, 0x87, 0x92, 0x92, 0x88, 0x92, 0x8a, 0x8a, 0x8b, 0x8d, 0x8c, 0x8b, 0x8c, 0x8d, 0x9e, 0x0e, 0x6d, 0x0f, 0x0e, 0x6d, 0xec, 0x0d, 0x0e, 0x0e, 0x8f, 0x8e, 0x8c, 0x8c, 0x8b, 0x8d, 0x8d, 0x8a, 0x8a, 0x8b, 0x8c, 0x8c, 0x8c, 0x8f, 0x0d, 0x0e, 0x6b, 0x8c, 0x8a, 0x89, 0x88, 0x89, 0x89, 0x8b, 0x8b, 0x8a, 0x89, 0x8a, 0x8b, 0x8a, 0x8b, 0x8b, 0x8c, 0x0e, 0x8c, 0x91, 0x87, 0x91, 0x88, 0x91, 0x88, 0x92, 0x88, 0x8b, 0x89, 0x87, 0x89, 0x93, 0x8a, 0x8c, 0x8c, 0x8b, 0x8d, 0x8e, 0x8e, 0x8e, 0x0e, 0x6d, 0x0f, 0x0e, 0x0d, 0x9f, 0x0e, 0x6d, 0x0e, 0x0d, 0x8d, 0x8c, 0x8b, 0x8c, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x8a, 0x8a, 0x8d, 0x9f, 0x0d, 0x0e, 0x8e, 0x89, 0x92, 0x88, 0x92, 0x88, 0x8a, 0x8c, 0x89, 0x92, 0x89, 0x89, 0x93, 0x8b, 0x8b, 0x8d, 0x0d, 0x8d, 0x89, 0x87, 0x87, 0x91, 0x87, 0x91, 0x88, 0x91, 0x89, 0x8b, 0x92, 0x92, 0x8b, 0x8b, 0x8a, 0x8c, 0x8c, 0x8c, 0x8d, 0x8f, 0x8f, 0x0e, 0x0f, 0x6d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x9f, 0x0e, 0x9f, 0x0d, 0x8e, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8b, 0x8a, 0x8a, 0x89, 0x8c, 0x0f, 0x09, 0x0d, 0x8a, 0x89, 0x92, 0x8a, 0x8b, 0x89, 0x8a, 0x8b, 0x8a, 0x8a, 0x89, 0x89, 0x8a, 0x8c, 0x8c, 0x8c, 0x8b, 0x89, 0x92, 0x89, 0x92, 0x87, 0x91, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8b, 0x8a, 0x8a, 0x8c, 0x8d, 0x8f, 0x0e, 0x09, 0x6d, 0x0e, 0x0e, 0x6c, 0x0d, 0x6c, 0x0f, 0x6d, 0x0f, 0x6d, 0x0e, 0x9f, 0x0d, 0x0d, 0x8e, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x88, 0x87, 0x8d, 0xee, 0x6e, 0x8d, 0x92, 0x88, 0x92, 0x8a, 0x93, 0x8a, 0x89, 0x8a, 0x8b, 0x8a, 0x8a, 0x8a, 0x8c, 0x8c, 0x8c, 0x8a, 0x89, 0x92, 0x92, 0x88, 0x88, 0x88, 0x92, 0x92, 0x92, 0x92, 0x89, 0x8b, 0x8b, 0x8a, 0x8a, 0x92, 0x89, 0x8c, 0x8b, 0x8e, 0x6d, 0x0f, 0x0e, 0x09, 0x0e, 0x09, 0x0f, 0x0d, 0x0e, 0x8e, 0x8c, 0x8d, 0x8f, 0x0e, 0x6d, 0x0e, 0x9f, 0x0d, 0x8f, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x8c, 0x4e, 0x0e, 0x88, 0x88, 0x88, 0x87, 0x92, 0x8a, 0x89, 0x92, 0x8a, 0x8a, 0x93, 0x8a, 0x8c, 0x8b, 0x8c, 0x8c, 0x92, 0x89, 0x8b, 0x8a, 0x92, 0x88, 0x92, 0x89, 0x89, 0x89, 0x92, 0x89, 0x8a, 0x91, 0x87, 0x89, 0x89, 0x8b, 0x8b, 0x8d, 0x0f, 0x6d, 0x0f, 0x6d, 0x0e, 0x8e, 0x8d, 0x8c, 0x8b, 0x89, 0x89, 0x89, 0x89, 0x8b, 0x8b, 0x8d, 0x8e, 0x8e, 0x9f, 0x0d, 0x8e, 0x8d, 0x8c, 0x8b, 0x89, 0x88, 0x8e, 0x6f, 0x8e, 0x91, 0x88, 0x92, 0x88, 0x88, 0x91, 0x89, 0x8b, 0x8b, 0x8a, 0x8a, 0x8b, 0x8c, 0x8e, 0x8d, 0x8a, 0x8b, 0x8b, 0x89, 0x89, 0x89, 0x88, 0x92, 0x91, 0x88, 0x88, 0x89, 0x8a, 0x87, 0x88, 0x89, 0x8b, 0x8b, 0x8c, 0x8f, 0x6d, 0x0f, 0x6d, 0x0e, 0x89, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8c, 0x8b, 0x89, 0x88, 0x88, 0x89, 0x8b, 0x8c, 0x8e, 0x8e, 0x8e, 0x8d, 0x8b, 0x8a, 0x88, 0x0d, 0x0f, 0x8b, 0x92, 0x88, 0x91, 0x88, 0x88, 0x88, 0x8a, 0x8b, 0x8a, 0x8c, 0x8e, 0x0d, 0x9f, 0x0e, 0x8f, 0x8c, 0x8c, 0x8b, 0x8a, 0x92, 0x88, 0x87, 0x87, 0x91, 0x91, 0x89, 0x92, 0x92, 0x89, 0x92, 0x89, 0x8a, 0x8c, 0x9f, 0x0f, 0x0f, 0x0e, 0x8a, 0x91, 0x8a, 0x8a, 0x89, 0x8b, 0x8d, 0x8c, 0x8b, 0x8c, 0x8b, 0x8c, 0x8c, 0x93, 0x8a, 0x89, 0x88, 0x88, 0x89, 0x8b, 0x8d, 0x8f, 0x8e, 0x8c, 0x8b, 0x0d, 0x8e, 0x89, 0x91, 0x88, 0x87, 0x92, 0x91, 0x92, 0x8c, 0x8d, 0x8d, 0x0e, 0x09, 0x0f, 0x6d, 0x0e, 0x0d, 0x8d, 0x8a, 0x92, 0x88, 0x91, 0x88, 0x92, 0x87, 0x88, 0x89, 0x88, 0x92, 0x8a, 0x92, 0x92, 0x8c, 0x0e, 0x0f, 0x8e, 0x8b, 0x89, 0x88, 0x8a, 0x8c, 0x92, 0x92, 0x8b, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8a, 0x89, 0x8b, 0x8a, 0x92, 0x88, 0x91, 0x8a, 0x8b, 0x8c, 0x8e, 0x8f, 0x8f, 0x0e, 0x8b, 0x87, 0x92, 0x88, 0x87, 0x88, 0x88, 0x8b, 0x8e, 0x8f, 0x0f, 0x6d, 0x0f, 0x0f, 0x6d, 0x0f, 0x0d, 0x8b, 0x87, 0x88, 0x92, 0x88, 0x92, 0x88, 0x92, 0x89, 0x92, 0x88, 0x89, 0x87, 0x89, 0x8e, 0x0d, 0x8c, 0x89, 0x87, 0x92, 0x8a, 0x8b, 0x8a, 0x89, 0x8b, 0x8c, 0x8b, 0x8c, 0x8d, 0x8c, 0x8a, 0x8a, 0x8b, 0x92, 0x92, 0x8b, 0x8b, 0x8a, 0x8a, 0x89, 0x89, 0x88, 0x91, 0x88, 0x8a, 0x8c, 0x9f, 0x0d, 0x88, 0x92, 0x92, 0x91, 0x91, 0x88, 0x8a, 0x8e, 0x9f, 0x0e, 0x0e, 0x6c, 0x0e, 0x0e, 0x6d, 0x09, 0x0d, 0x89, 0x91, 0x91, 0x87, 0x87, 0x91, 0x92, 0x8a, 0x92, 0x88, 0x92, 0x88, 0x8b, 0x0d, 0x8b, 0x89, 0x8b, 0x8a, 0x8b, 0x8a, 0x89, 0x8b, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x92, 0x89, 0x8b, 0x8b, 0x89, 0x92, 0x92, 0x89, 0x88, 0x88, 0x8e, 0x8b, 0x88, 0x92, 0x87, 0x87, 0x92, 0x8b, 0x8d, 0x0d, 0x0d, 0x9f, 0x0e, 0x0e, 0x6d, 0x0e, 0x0e, 0x0f, 0x8c, 0x88, 0x87, 0x88, 0x91, 0x87, 0x88, 0x88, 0x88, 0x88, 0x89, 0x8c, 0x8d, 0x8d, 0x8b, 0x8a, 0x8a, 0x8b, 0x8b, 0x8a, 0x8a, 0x8d, 0x8d, 0x8c, 0x8b, 0x8b, 0x8a, 0x8b, 0x8b, 0x8b, 0x8a, 0x8b, 0x8b, 0x8a, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x91, 0x8a, 0x8c, 0x92, 0x92, 0x89, 0x89, 0x89, 0x8b, 0x8c, 0x8e, 0x8f, 0x8f, 0x0d, 0x0d, 0x0e, 0x0d, 0x9f, 0x0e, 0x0d, 0x8c, 0x92, 0x92, 0x92, 0x88, 0x91, 0x91, 0x92, 0x92, 0x8b, 0x8d, 0x8e, 0x8c, 0x8c, 0x8b, 0x8a, 0x8c, 0x8a, 0x89, 0x8b, 0x8c, 0x8e, 0x8d, 0x8c, 0x8c, 0x8c, 0x8a, 0x8a, 0x8b, 0x8a, 0x89, 0x8b, 0x8a, 0x93, 0x8b, 0x8a, 0x8b, 0x8b, 0x8b, 0x89, 0x8a, 0x8b, 0x8c, 0x8b, 0x8b, 0x89, 0x8b, 0x8a, 0x88, 0x92, 0x92, 0x89, 0x8a, 0x8b, 0x8d, 0x8c, 0x8d, 0x8e, 0x8f, 0x8f, 0x0d, 0x8f, 0x8e, 0x0d, 0x6d, 0x8c, 0x8a, 0x8a, 0x92, 0x89, 0x89, 0x8b, 0x8d, 0x8e, 0x8e, 0x8b, 0x8a, 0x8b, 0x8b, 0x8c, 0x8e, 0x8d, 0x8d, 0x8c, 0x8d, 0x8c, 0x8d, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8a, 0x93, 0x8b, 0x89, 0x8b, 0x8a, 0x89, 0x93, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8b, 0x88, 0x88, 0x88, 0x92, 0x89, 0x8a, 0x8c, 0x8b, 0x8b, 0x8c, 0x8d, 0x8c, 0x8d, 0x8e, 0x8d, 0x8d, 0x8d, 0x0d, 0x0d, 0x8d, 0x8d, 0x8d, 0x8e, 0x0e, 0x09, 0x8f, 0x8b, 0x89, 0x89, 0x8b, 0x8c, 0x8d, 0x8f, 0x8e, 0x8e, 0x8b, 0x8c, 0x8e, 0x8d, 0x8b, 0x8a, 0x8a, 0x8b, 0x8c, 0x8a, 0x8b, 0x8c, 0x8c, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8c, 0x8d, 0x8c, 0x8a, 0x8a, 0x8b, 0x8b, 0x8c, 0x8c, 0x89, 0x92, 0x89, 0x92, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8a, 0x8b, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x0d, 0x0f, 0x09, 0x0f, 0x6d, 0x8f, 0x8c, 0x8a, 0x8a, 0x89, 0x8b, 0x8a, 0x8c, 0x8f, 0x8c, 0x8b, 0x8c, 0x8c, 0x8d, 0x8c, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8b, 0x8c, 0x8b, 0x8b, 0x8c, 0x8b, 0x8b, 0x8c, 0x8c, 0x8b, 0x8b, 0x8c, 0x8d, 0x8c, 0x8b, 0x8b, 0x8a, 0x8b, 0x8c, 0x8b, 0x88, 0x88, 0x92, 0x92, 0x89, 0x8a, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8d, 0x8c, 0x8b, 0x8c, 0x6d, 0x0e, 0x6d, 0x0e, 0x8d, 0x8a, 0x8a, 0x89, 0x93, 0x8b, 0x8b, 0x8c, 0x8d, 0x8c, 0x8b, 0x8c, 0x8c, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8c, 0x8b, 0x8c, 0x8d, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8c, 0x8e, 0x8c, 0x8b, 0x8c, 0x8c, 0x8b, 0x8b, 0x8a, 0x91, 0x88, 0x88, 0x89, 0x8a, 0x89, 0x89, 0x89, 0x92, 0x88, 0x92, 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8a, 0x89, 0x8f, 0x0f, 0x0e, 0x0d, 0x8d, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8c, 0x8b, 0x8a, 0x8a, 0x8b, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8c, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8a, 0x92, 0x92, 0x92, 0x92, 0x89, 0x92, 0x89, 0x92, 0x92, 0x88, 0x88, 0x88, 0x88, 0x92, 0x89, 0x88, 0x88, 0x86, 0x8a, 0x6d, 0x0d, 0x8c, 0x8a, 0x92, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8c, 0x8b, 0x8d, 0x8d, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8c, 0x8c, 0x8b, 0x8a, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x89, 0x88, 0x88, 0x92, 0x89, 0x89, 0x92, 0x88, 0x92, 0x88, 0x92, 0x87, 0x92, 0x88, 0x91, 0x86, 0x86, 0x91, 0x92, 0x0e, 0x0d, 0x8c, 0x89, 0x88, 0x89, 0x92, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x8c, 0x8d, 0x8c, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x8d, 0x8c, 0x8a, 0x8a, 0x8c, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8b, 0x8a, 0x8b, 0x8b, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x89, 0x91, 0x88, 0x8a, 0x89, 0x92, 0x88, 0x88, 0x87, 0x91, 0x87, 0x87, 0x91, 0x87, 0x91, 0x92, 0x87, 0x87, 0x8e, 0x0d, 0x8a, 0x92, 0x92, 0x92, 0x89, 0x89, 0x8b, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8d, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x8d, 0x8d, 0x8e, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8d, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8c, 0x88, 0x88, 0x89, 0x89, 0x92, 0x88, 0x91, 0x88, 0x91, 0x88, 0x91, 0x87, 0x88, 0x88, 0x92, 0x92, 0x87, 0x89, 0x8d, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8b, 0x8c, 0x8b, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8d, 0x8e, 0x8d, 0x8d, 0x8d, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8f, 0x8e, 0x8a, 0x92, 0x92, 0x88, 0x88, 0x91, 0x88, 0x92, 0x87, 0x92, 0x92, 0x88, 0x92, 0x88, 0x87, 0x92, 0x89, 0x8b, 0x8a, 0x89, 0x8a, 0x8a, 0x8b, 0x8c, 0x8c, 0x8b, 0x8c, 0x8c, 0x8d, 0x8c, 0x8d, 0x8f, 0x8e, 0x8f, 0x8f, 0x0d, 0x0d, 0x8e, 0x8d, 0x8c, 0x8d, 0x8d, 0x8e, 0x8d, 0x8d, 0x8e, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d, 0x8e, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8d, 0x8c, 0x8d, 0x8e, 0x8c, 0x89, 0x92, 0x88, 0x92, 0x88, 0x92, 0x88, 0x92, 0x88, 0x92, 0x89, 0x89, 0x88, 0x91, 0x89, 0x8b, 0x8b, 0x8a, 0x8b, 0x8c, 0x8d, 0x8c, 0x8c, 0x8c, 0x8a, 0x8b, 0x8c, 0x8b, 0x8b, 0x8c, 0x8c, 0x8a, 0x8a, 0x8a, 0x8b, 0x8e, 0x9f, 0x8f, 0x0d, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x0d, 0x0d, 0x6c, 0x0e, 0x9f, 0x0e, 0x6e, 0x6f, 0x09, 0x0d, 0x8c, 0x8b, 0x88, 0x88, 0x88, 0x8a, 0x89, 0x89, 0x8a, 0x92, 0x88, 0x92, 0x88, 0x92, 0x92, 0x88, 0x8b, 0x8a, 0x8c, 0x8d, 0x8a, 0x8b, 0x8a, 0x89, 0x89, 0x92, 0x87, 0x87, 0x91, 0x87, 0x87, 0x87, 0x87, 0x91, 0x92, 0x89, 0x8a, 0x8e, 0x0e, 0x6c, 0x0d, 0x8f, 0x8e, 0x8d, 0x8e, 0x8d, 0x8c, 0x8e, 0x9e, 0x8e, 0x8f, 0x0d, 0x9e, 0x0e, 0x6d, 0x0e, 0x09, 0x0f, 0x6e, 0x05, 0x02, 0x00, 0x07, 0x0e, 0x8b, 0x92, 0x92, 0x92, 0x92, 0x89, 0x89, 0x8a, 0x89, 0x89, 0x8a, 0x92, 0x88, 0x88, 0x89, 0x8a, 0x89, 0x92, 0x8c, 0x8c, 0x87, 0x87, 0x89, 0x91, 0x87, 0x92, 0x87, 0x91, 0x92, 0x89, 0x92, 0x87, 0x88, 0x87, 0x88, 0x8a, 0x8b, 0x8b, 0x8d, 0x8e, 0x0d, 0x9f, 0x8e, 0x8e, 0x8f, 0x8e, 0x8d, 0x8e, 0x0d, 0x0d, 0x9e, 0x0d, 0x6d, 0x0f, 0x09, 0x0f, 0x6d, 0x0f, 0x0f, 0x4e, 0x6f, 0x01, 0x06, 0x6d, 0x8c, 0x88, 0x87, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x89, 0x92, 0x92, 0x92, 0x89, 0x8a, 0x8a, 0x92, 0x88, 0x92, 0x88, 0x91, 0x89, 0x89, 0x88, 0x87, 0x88, 0x92, 0x88, 0x92, 0x88, 0x92, 0x88, 0x92, 0x8b, 0x8a, 0x8b, 0x8c, 0x8d, 0x0d, 0x9e, 0x8e, 0x8e, 0x9e, 0x0d, 0x0d, 0x6c, 0x0d, 0x0e, 0x6d, 0x0f, 0x0e, 0x0e, 0x09, 0x0e, 0x6d, 0x0f, 0x6d, 0x0f, 0x6e, 0x0f, 0x0f, 0x0e, 0x8b, 0x89, 0x92, 0x89, 0x8a, 0x93, 0x8a, 0x89, 0x93, 0x8a, 0x89, 0x92, 0x92, 0x89, 0x89, 0x92, 0x89, 0x93, 0x88, 0x88, 0x89, 0x92, 0x92, 0x89, 0x89, 0x92, 0x92, 0x92, 0x88, 0x92, 0x88, 0x92, 0x92, 0x92, 0x89, 0x8b, 0x8b, 0x8a, 0x8b, 0x8b, 0x8c, 0x8f, 0x8e, 0x8e, 0x8f, 0x0d, 0x9f, 0x0d, 0x0e, 0x6d, 0x0e, 0x0e, 0x8d, 0x8e, 0x0e, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x09, 0x6d, 0x8d, 0x8c, 0x8b, 0x8a, 0x8a, 0x8b, 0x89, 0x8a, 0x8a, 0x92, 0x89, 0x89, 0x89, 0x8a, 0x89, 0x92, 0x89, 0x8a, 0x89, 0x89, 0x92, 0x89, 0x92, 0x89, 0x92, 0x89, 0x8a, 0x89, 0x89, 0x92, 0x8a, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8c, 0x8e, 0x8f, 0x8e, 0x0d, 0x0e, 0x0e, 0x9f, 0x0f, 0x0e, 0x8c, 0x8b, 0x8c, 0x8d, 0x0d, 0x0d, 0x0d, 0x8f, 0x6c, 0x0e, 0x6d, 0x0f, 0x0f, 0x0e, 0x8f, 0x8c, 0x8c, 0x8b, 0x8a, 0x93, 0x8b, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x89, 0x88, 0x88, 0x92, 0x89, 0x8b, 0x8b, 0x89, 0x92, 0x88, 0x89, 0x92, 0x92, 0x92, 0x88, 0x89, 0x89, 0x8a, 0x93, 0x8a, 0x8b, 0x8c, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8c, 0x0d, 0x9e, 0x0e, 0x6c, 0x0e, 0x0e, 0x6d, 0x0e, 0x8c, 0x8c, 0x8c, 0x8c, 0x8e, 0x8e, 0x8e, 0x8e, 0x0d, 0x0d, 0x0d, 0x6c, 0x0d, 0x0d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8a, 0x8a, 0x89, 0x8a, 0x8a, 0x92, 0x92, 0x92, 0x92, 0x89, 0x8a, 0x8b, 0x89, 0x92, 0x89, 0x92, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8c, 0x8b, 0x8b, 0x8a, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8c, 0x8f, 0x0d, 0x8f, 0x0d, 0x0d, 0x0e, 0x0d, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8c, 0x8d, 0x8d, 0x8e, 0x8f, 0x8e, 0x8d, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8c, 0x8c, 0x8b, 0x93, 0x8a, 0x8a, 0x88, 0x92, 0x88, 0x92, 0x89, 0x8a, 0x8a, 0x88, 0x92, 0x8a, 0x92, 0x92, 0x89, 0x89, 0x89, 0x8a, 0x89, 0x93, 0x8b, 0x8a, 0x8a, 0x8d, 0x8c, 0x8c, 0x93, 0x8a, 0x8a, 0x92, 0x89, 0x8a, 0x8a, 0x8b, 0x8d, 0x8c, 0x8c, 0x8c, 0x8d, 0x8f, 0x8c, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8c, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, }; crispy-doom-crispy-doom-5.6.4/src/doom/m_crispy.c000066400000000000000000000265251360717211000217640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2015-2018 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] Crispness menu // #include "doomstat.h" #include "p_local.h" // [crispy] thinkercap #include "s_sound.h" #include "r_defs.h" // [crispy] laserpatch #include "r_sky.h" // [crispy] R_InitSkyMap() #include "m_crispy.h" multiitem_t multiitem_aspectratio[NUM_ASPECTRATIOS] = { {ASPECTRATIO_OFF, "none"}, {ASPECTRATIO_4_3, "4:3"}, {ASPECTRATIO_16_10, "16:10"}, }; multiitem_t multiitem_bobfactor[NUM_BOBFACTORS] = { {BOBFACTOR_FULL, "full"}, {BOBFACTOR_75, "75%"}, {BOBFACTOR_OFF, "off"}, }; multiitem_t multiitem_brightmaps[NUM_BRIGHTMAPS] = { {BRIGHTMAPS_OFF, "none"}, {BRIGHTMAPS_TEXTURES, "walls"}, {BRIGHTMAPS_SPRITES, "items"}, {BRIGHTMAPS_BOTH, "both"}, }; multiitem_t multiitem_centerweapon[NUM_CENTERWEAPON] = { {CENTERWEAPON_OFF, "off"}, {CENTERWEAPON_CENTER, "centered"}, {CENTERWEAPON_BOB, "bobbing"}, }; multiitem_t multiitem_coloredhud[NUM_COLOREDHUD] = { {COLOREDHUD_OFF, "off"}, {COLOREDHUD_BAR, "status bar"}, {COLOREDHUD_TEXT, "hud texts"}, {COLOREDHUD_BOTH, "both"}, }; multiitem_t multiitem_crosshair[NUM_CROSSHAIRS] = { {CROSSHAIR_OFF, "off"}, {CROSSHAIR_STATIC, "static"}, {CROSSHAIR_PROJECTED, "projected"}, }; multiitem_t multiitem_crosshairtype[] = { {-1, "none"}, {0, "cross"}, {1, "chevron"}, {2, "dot"}, }; multiitem_t multiitem_freeaim[NUM_FREEAIMS] = { {FREEAIM_AUTO, "autoaim"}, {FREEAIM_DIRECT, "direct"}, {FREEAIM_BOTH, "both"}, }; multiitem_t multiitem_demotimer[NUM_DEMOTIMERS] = { {DEMOTIMER_OFF, "off"}, {DEMOTIMER_RECORD, "recording"}, {DEMOTIMER_PLAYBACK, "playback"}, {DEMOTIMER_BOTH, "both"}, }; multiitem_t multiitem_demotimerdir[] = { {0, "none"}, {1, "forward"}, {2, "backward"}, }; multiitem_t multiitem_freelook[NUM_FREELOOKS] = { {FREELOOK_OFF, "off"}, {FREELOOK_SPRING, "spring"}, {FREELOOK_LOCK, "lock"}, }; multiitem_t multiitem_jump[NUM_JUMPS] = { {JUMP_OFF, "off"}, {JUMP_LOW, "low"}, {JUMP_HIGH, "high"}, }; multiitem_t multiitem_secretmessage[NUM_SECRETMESSAGE] = { {SECRETMESSAGE_OFF, "off"}, {SECRETMESSAGE_ON, "on"}, {SECRETMESSAGE_COUNT, "count"}, }; multiitem_t multiitem_translucency[NUM_TRANSLUCENCY] = { {TRANSLUCENCY_OFF, "off"}, {TRANSLUCENCY_MISSILE, "projectiles"}, {TRANSLUCENCY_ITEM, "items"}, {TRANSLUCENCY_BOTH, "both"}, }; multiitem_t multiitem_sndchannels[4] = { {8, "8"}, {16, "16"}, {32, "32"}, }; multiitem_t multiitem_widgets[NUM_WIDGETS] = { {WIDGETS_OFF, "never"}, {WIDGETS_AUTOMAP, "in Automap"}, {WIDGETS_ALWAYS, "always"}, }; extern void AM_ReInit (void); extern void EnableLoadingDisk (void); extern void P_SegLengths (boolean contrast_only); extern void R_ExecuteSetViewSize (void); extern void R_InitLightTables (void); extern void I_ReInitGraphics (int reinit); static void M_CrispyToggleAspectRatioHook (void) { aspect_ratio_correct = (aspect_ratio_correct + 1) % NUM_ASPECTRATIOS; // [crispy] re-set logical rendering resolution I_ReInitGraphics(REINIT_ASPECTRATIO); } void M_CrispyToggleAspectRatio(int choice) { choice = 0; crispy->post_rendering_hook = M_CrispyToggleAspectRatioHook; } void M_CrispyToggleAutomapstats(int choice) { choice = 0; crispy->automapstats = (crispy->automapstats + 1) % NUM_WIDGETS; } void M_CrispyToggleBobfactor(int choice) { choice = 0; crispy->bobfactor = (crispy->bobfactor + 1) % NUM_BOBFACTORS; } void M_CrispyToggleBrightmaps(int choice) { choice = 0; crispy->brightmaps = (crispy->brightmaps + 1) % NUM_BRIGHTMAPS; } void M_CrispyToggleCenterweapon(int choice) { choice = 0; crispy->centerweapon = (crispy->centerweapon + 1) % NUM_CENTERWEAPON; } void M_CrispyToggleColoredblood(int choice) { thinker_t *th; if (gameversion == exe_chex) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->coloredblood = !crispy->coloredblood; // [crispy] switch NOBLOOD flag for Lost Souls for (th = thinkercap.next; th && th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) { mobj_t *mobj = (mobj_t *)th; if (mobj->type == MT_SKULL) { if (crispy->coloredblood) { mobj->flags |= MF_NOBLOOD; } else { mobj->flags &= ~MF_NOBLOOD; } } } } } void M_CrispyToggleColoredhud(int choice) { choice = 0; crispy->coloredhud = (crispy->coloredhud + 1) % NUM_COLOREDHUD; } void M_CrispyToggleCrosshair(int choice) { choice = 0; crispy->crosshair = (crispy->crosshair + 1) % NUM_CROSSHAIRS; } void M_CrispyToggleCrosshairHealth(int choice) { choice = 0; crispy->crosshairhealth = !crispy->crosshairhealth; } void M_CrispyToggleCrosshairTarget(int choice) { choice = 0; crispy->crosshairtarget = !crispy->crosshairtarget; } void M_CrispyToggleCrosshairtype(int choice) { if (!crispy->crosshair) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->crosshairtype = crispy->crosshairtype + 1; if (!laserpatch[crispy->crosshairtype].c) { crispy->crosshairtype = 0; } } void M_CrispyToggleDemoBar(int choice) { choice = 0; crispy->demobar = !crispy->demobar; } void M_CrispyToggleDemoTimer(int choice) { choice = 0; crispy->demotimer = (crispy->demotimer + 1) % NUM_DEMOTIMERS; } void M_CrispyToggleDemoTimerDir(int choice) { if (!(crispy->demotimer & DEMOTIMER_PLAYBACK)) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->demotimerdir = !crispy->demotimerdir; } void M_CrispyToggleExtAutomap(int choice) { choice = 0; crispy->extautomap = !crispy->extautomap; } void M_CrispyToggleExtsaveg(int choice) { choice = 0; crispy->extsaveg = !crispy->extsaveg; } void M_CrispyToggleFlipcorpses(int choice) { if (gameversion == exe_chex) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->flipcorpses = !crispy->flipcorpses; } void M_CrispyToggleFreeaim(int choice) { if (!crispy->singleplayer) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->freeaim = (crispy->freeaim + 1) % NUM_FREEAIMS; // [crispy] update the "critical" struct CheckCrispySingleplayer(!demorecording && !demoplayback && !netgame); } static void M_CrispyToggleSkyHook (void) { players[consoleplayer].lookdir = 0; R_InitSkyMap(); } void M_CrispyToggleFreelook(int choice) { choice = 0; crispy->freelook = (crispy->freelook + 1) % NUM_FREELOOKS; crispy->post_rendering_hook = M_CrispyToggleSkyHook; } void M_CrispyToggleFullsounds(int choice) { int i; choice = 0; crispy->soundfull = !crispy->soundfull; // [crispy] weapon sound sources for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { players[i].so = Crispy_PlayerSO(i); } } } static void M_CrispyToggleHiresHook (void) { crispy->hires = !crispy->hires; // [crispy] re-initialize framebuffers, textures and renderer I_ReInitGraphics(REINIT_FRAMEBUFFERS | REINIT_TEXTURES | REINIT_ASPECTRATIO); // [crispy] re-calculate framebuffer coordinates R_ExecuteSetViewSize(); // [crispy] re-draw bezel R_FillBackScreen(); // [crispy] re-calculate disk icon coordinates EnableLoadingDisk(); // [crispy] re-calculate automap coordinates AM_ReInit(); } void M_CrispyToggleHires(int choice) { choice = 0; crispy->post_rendering_hook = M_CrispyToggleHiresHook; } void M_CrispyToggleJumping(int choice) { if (!crispy->singleplayer) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->jump = (crispy->jump + 1) % NUM_JUMPS; // [crispy] update the "critical" struct CheckCrispySingleplayer(!demorecording && !demoplayback && !netgame); } void M_CrispyToggleLeveltime(int choice) { choice = 0; crispy->leveltime = (crispy->leveltime + 1) % NUM_WIDGETS; } void M_CrispyToggleMouseLook(int choice) { choice = 0; crispy->mouselook = !crispy->mouselook; crispy->post_rendering_hook = M_CrispyToggleSkyHook; } void M_CrispyToggleNeghealth(int choice) { choice = 0; crispy->neghealth = !crispy->neghealth; } void M_CrispyToggleOverunder(int choice) { if (!crispy->singleplayer) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->overunder = !crispy->overunder; // [crispy] update the "critical" struct CheckCrispySingleplayer(!demorecording && !demoplayback && !netgame); } void M_CrispyTogglePitch(int choice) { choice = 0; crispy->pitch = !crispy->pitch; crispy->post_rendering_hook = M_CrispyToggleSkyHook; } void M_CrispyTogglePlayerCoords(int choice) { choice = 0; crispy->playercoords = (crispy->playercoords + 1) % (NUM_WIDGETS - 1); // [crispy] disable "always" setting } void M_CrispyToggleRecoil(int choice) { if (!crispy->singleplayer) { S_StartSound(NULL,sfx_oof); return; } choice = 0; crispy->recoil = !crispy->recoil; // [crispy] update the "critical" struct CheckCrispySingleplayer(!demorecording && !demoplayback && !netgame); } void M_CrispyToggleSecretmessage(int choice) { choice = 0; crispy->secretmessage = (crispy->secretmessage + 1) % NUM_SECRETMESSAGE; } void M_CrispyToggleSmoothScaling(int choice) { choice = 0; crispy->smoothscaling = !crispy->smoothscaling; } static void M_CrispyToggleSmoothLightingHook (void) { crispy->smoothlight = !crispy->smoothlight; // [crispy] re-calculate the zlight[][] array R_InitLightTables(); // [crispy] re-calculate the scalelight[][] array R_ExecuteSetViewSize(); // [crispy] re-calculate fake contrast P_SegLengths(true); } void M_CrispyToggleSmoothLighting(int choice) { choice = 0; crispy->post_rendering_hook = M_CrispyToggleSmoothLightingHook; } void M_CrispyToggleSndChannels(int choice) { choice = 0; S_UpdateSndChannels(); } void M_CrispyToggleSoundfixes(int choice) { choice = 0; crispy->soundfix = !crispy->soundfix; } void M_CrispyToggleSoundMono(int choice) { choice = 0; crispy->soundmono = !crispy->soundmono; S_UpdateStereoSeparation(); } void M_CrispyToggleTranslucency(int choice) { choice = 0; crispy->translucency = (crispy->translucency + 1) % NUM_TRANSLUCENCY; } void M_CrispyToggleUncapped(int choice) { choice = 0; crispy->uncapped = !crispy->uncapped; } void M_CrispyToggleVsyncHook (void) { crispy->vsync = !crispy->vsync; I_ReInitGraphics(REINIT_RENDERER | REINIT_TEXTURES | REINIT_ASPECTRATIO); } void M_CrispyToggleVsync(int choice) { choice = 0; if (force_software_renderer) { S_StartSound(NULL,sfx_oof); return; } crispy->post_rendering_hook = M_CrispyToggleVsyncHook; } void M_CrispyToggleWeaponSquat(int choice) { choice = 0; crispy->weaponsquat = !crispy->weaponsquat; } crispy-doom-crispy-doom-5.6.4/src/doom/m_crispy.h000066400000000000000000000070341360717211000217630ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2015-2018 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] Crispness menu // #ifndef __M_CRISPY__ #define __M_CRISPY__ typedef struct { int value; char *name; } multiitem_t; extern multiitem_t multiitem_aspectratio[NUM_ASPECTRATIOS]; extern multiitem_t multiitem_bobfactor[NUM_BOBFACTORS]; extern multiitem_t multiitem_brightmaps[NUM_BRIGHTMAPS]; extern multiitem_t multiitem_centerweapon[NUM_CENTERWEAPON]; extern multiitem_t multiitem_coloredhud[NUM_COLOREDHUD]; extern multiitem_t multiitem_crosshair[NUM_CROSSHAIRS]; extern multiitem_t multiitem_crosshairtype[]; extern multiitem_t multiitem_freeaim[NUM_FREEAIMS]; extern multiitem_t multiitem_demotimer[NUM_DEMOTIMERS]; extern multiitem_t multiitem_demotimerdir[]; extern multiitem_t multiitem_freelook[NUM_FREELOOKS]; extern multiitem_t multiitem_jump[NUM_JUMPS]; extern multiitem_t multiitem_sndchannels[4]; extern multiitem_t multiitem_secretmessage[NUM_SECRETMESSAGE]; extern multiitem_t multiitem_translucency[NUM_TRANSLUCENCY]; extern multiitem_t multiitem_widgets[NUM_WIDGETS]; extern void M_CrispyToggleAspectRatio(int choice); extern void M_CrispyToggleAutomapstats(int choice); extern void M_CrispyToggleBobfactor(int choice); extern void M_CrispyToggleBrightmaps(int choice); extern void M_CrispyToggleCenterweapon(int choice); extern void M_CrispyToggleColoredblood(int choice); extern void M_CrispyToggleColoredhud(int choice); extern void M_CrispyToggleCrosshair(int choice); extern void M_CrispyToggleCrosshairHealth(int choice); extern void M_CrispyToggleCrosshairTarget(int choice); extern void M_CrispyToggleCrosshairtype(int choice); extern void M_CrispyToggleDemoBar(int choice); extern void M_CrispyToggleDemoTimer(int choice); extern void M_CrispyToggleDemoTimerDir(int choice); extern void M_CrispyToggleExtAutomap(int choice); extern void M_CrispyToggleExtsaveg(int choice); extern void M_CrispyToggleFlipcorpses(int choice); extern void M_CrispyToggleFreeaim(int choice); extern void M_CrispyToggleFreelook(int choice); extern void M_CrispyToggleFullsounds(int choice); extern void M_CrispyToggleHires(int choice); extern void M_CrispyToggleJumping(int choice); extern void M_CrispyToggleLeveltime(int choice); extern void M_CrispyToggleMouseLook(int choice); extern void M_CrispyToggleNeghealth(int choice); extern void M_CrispyToggleOverunder(int choice); extern void M_CrispyTogglePitch(int choice); extern void M_CrispyTogglePlayerCoords(int choice); extern void M_CrispyToggleRecoil(int choice); extern void M_CrispyToggleSecretmessage(int choice); extern void M_CrispyToggleSmoothLighting(int choice); extern void M_CrispyToggleSmoothScaling(int choice); extern void M_CrispyToggleSndChannels(int choice); extern void M_CrispyToggleSoundfixes(int choice); extern void M_CrispyToggleSoundMono(int choice); extern void M_CrispyToggleTranslucency(int choice); extern void M_CrispyToggleUncapped(int choice); extern void M_CrispyToggleVsync(int choice); extern void M_CrispyToggleWeaponSquat(int choice); #endif crispy-doom-crispy-doom-5.6.4/src/doom/m_menu.c000066400000000000000000002214071360717211000214130ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM selection menu, options, episode etc. // Sliders and icons. Kinda widget stuff. // #include #include #include "doomdef.h" #include "doomkeys.h" #include "dstrings.h" #include "d_main.h" #include "deh_main.h" #include "i_input.h" #include "i_swap.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "m_misc.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "r_local.h" #include "hu_stuff.h" #include "g_game.h" #include "m_argv.h" #include "m_controls.h" #include "p_saveg.h" #include "p_setup.h" #include "p_extsaveg.h" // [crispy] savewadfilename #include "s_sound.h" #include "doomstat.h" // Data. #include "sounds.h" #include "m_menu.h" #include "m_crispy.h" // [crispy] Crispness menu #include "v_trans.h" // [crispy] colored "invert mouse" message extern patch_t* hu_font[HU_FONTSIZE]; extern boolean message_dontfuckwithme; extern boolean chat_on; // in heads-up code // // defaulted values // int mouseSensitivity = 5; int mouseSensitivity_x2 = 5; // [crispy] mouse sensitivity menu int mouseSensitivity_y = 5; // [crispy] mouse sensitivity menu // Show messages has default, 0 = off, 1 = on int showMessages = 1; // Blocky mode, has default, 0 = high, 1 = normal int detailLevel = 0; int screenblocks = 10; // [crispy] increased // temp for screenblocks (0-9) int screenSize; // -1 = no quicksave slot picked! int quickSaveSlot; // 1 = message to be printed int messageToPrint; // ...and here is the message string! const char *messageString; // message x & y int messx; int messy; int messageLastMenuActive; // timed message = no input from user boolean messageNeedsInput; void (*messageRoutine)(int response); // [crispy] intermediate gamma levels char gammamsg[5+4][26+2] = { GAMMALVL0, GAMMALVL05, GAMMALVL1, GAMMALVL15, GAMMALVL2, GAMMALVL25, GAMMALVL3, GAMMALVL35, GAMMALVL4 }; // we are going to be entering a savegame string int saveStringEnter; int saveSlot; // which slot to save in int saveCharIndex; // which char we're editing static boolean joypadSave = false; // was the save action initiated by joypad? // old save description before edit char saveOldString[SAVESTRINGSIZE]; boolean inhelpscreens; boolean menuactive; #define SKULLXOFF -32 #define LINEHEIGHT 16 #define CRISPY_LINEHEIGHT 10 // [crispy] Crispness menu extern boolean sendpause; char savegamestrings[10][SAVESTRINGSIZE]; char endstring[160]; static boolean opldev; extern boolean speedkeydown (void); // // MENU TYPEDEFS // typedef struct { // 0 = no cursor here, 1 = ok, 2 = arrows ok short status; char name[10]; // choice = menu item #. // if status = 2, // choice=0:leftarrow,1:rightarrow void (*routine)(int choice); // hotkey in menu char alphaKey; char *alttext; // [crispy] alternative text for the Options menu } 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 short skullAnimCounter; // skull animation counter short whichSkull; // which skull to draw // graphic name of skulls // warning: initializer-string for array of chars is too long const char *skullName[2] = {"M_SKULL1","M_SKULL2"}; // current menudef menu_t* currentMenu; // // PROTOTYPES // static void M_NewGame(int choice); static void M_Episode(int choice); static void M_Expansion(int choice); // [crispy] NRFTL static void M_ChooseSkill(int choice); static void M_LoadGame(int choice); static void M_SaveGame(int choice); static void M_Options(int choice); static void M_EndGame(int choice); static void M_ReadThis(int choice); static void M_ReadThis2(int choice); static void M_QuitDOOM(int choice); static void M_ChangeMessages(int choice); static void M_ChangeSensitivity(int choice); static void M_ChangeSensitivity_x2(int choice); // [crispy] mouse sensitivity menu static void M_ChangeSensitivity_y(int choice); // [crispy] mouse sensitivity menu static void M_MouseInvert(int choice); // [crispy] mouse sensitivity menu static void M_SfxVol(int choice); static void M_MusicVol(int choice); static void M_ChangeDetail(int choice); static void M_SizeDisplay(int choice); static void M_Mouse(int choice); // [crispy] mouse sensitivity menu static void M_Sound(int choice); static void M_FinishReadThis(int choice); static void M_LoadSelect(int choice); static void M_SaveSelect(int choice); static void M_ReadSaveStrings(void); static void M_QuickSave(void); static void M_QuickLoad(void); static void M_DrawMainMenu(void); static void M_DrawReadThis1(void); static void M_DrawReadThis2(void); static void M_DrawNewGame(void); static void M_DrawEpisode(void); static void M_DrawOptions(void); static void M_DrawMouse(void); // [crispy] mouse sensitivity menu static void M_DrawSound(void); static void M_DrawLoad(void); static void M_DrawSave(void); static void M_DrawSaveLoadBorder(int x,int y); static void M_SetupNextMenu(menu_t *menudef); static void M_DrawThermo(int x,int y,int thermWidth,int thermDot); static void M_WriteText(int x, int y, const char *string); int M_StringWidth(const char *string); // [crispy] un-static static int M_StringHeight(const char *string); static void M_StartMessage(const char *string, void *routine, boolean input); static void M_ClearMenus (void); // [crispy] Crispness menu static void M_CrispnessCur(int choice); static void M_CrispnessNext(int choice); static void M_CrispnessPrev(int choice); static void M_DrawCrispness1(void); static void M_DrawCrispness2(void); static void M_DrawCrispness3(void); static void M_DrawCrispness4(void); // // DOOM MENU // enum { newgame = 0, options, loadgame, savegame, readthis, quitdoom, main_end } main_e; menuitem_t MainMenu[]= { {1,"M_NGAME",M_NewGame,'n'}, {1,"M_OPTION",M_Options,'o'}, {1,"M_LOADG",M_LoadGame,'l'}, {1,"M_SAVEG",M_SaveGame,'s'}, // Another hickup with Special edition. {1,"M_RDTHIS",M_ReadThis,'r'}, {1,"M_QUITG",M_QuitDOOM,'q'} }; menu_t MainDef = { main_end, NULL, MainMenu, M_DrawMainMenu, 97,64, 0 }; // // EPISODE SELECT // enum { ep1, ep2, ep3, ep4, ep5, // [crispy] Sigil ep_end } episodes_e; menuitem_t EpisodeMenu[]= { {1,"M_EPI1", M_Episode,'k'}, {1,"M_EPI2", M_Episode,'t'}, {1,"M_EPI3", M_Episode,'i'}, {1,"M_EPI4", M_Episode,'t'} ,{1,"M_EPI5", M_Episode,'s'} // [crispy] Sigil }; menu_t EpiDef = { ep_end, // # of menu items &MainDef, // previous menu EpisodeMenu, // menuitem_t -> M_DrawEpisode, // drawing routine -> 48,63, // x,y ep1 // lastOn }; // // EXPANSION SELECT // enum { ex1, ex2, ex_end } expansions_e; static menuitem_t ExpansionMenu[]= { {1,"M_EPI1", M_Expansion,'h'}, {1,"M_EPI2", M_Expansion,'n'}, }; static menu_t ExpDef = { ex_end, // # of menu items &MainDef, // previous menu ExpansionMenu, // menuitem_t -> M_DrawEpisode, // drawing routine -> 48,63, // x,y ex1 // lastOn }; // // NEW GAME // enum { killthings, toorough, hurtme, violence, nightmare, newg_end } newgame_e; menuitem_t NewGameMenu[]= { {1,"M_JKILL", M_ChooseSkill, 'i'}, {1,"M_ROUGH", M_ChooseSkill, 'h'}, {1,"M_HURT", M_ChooseSkill, 'h'}, {1,"M_ULTRA", M_ChooseSkill, 'u'}, {1,"M_NMARE", M_ChooseSkill, 'n'} }; menu_t NewDef = { newg_end, // # of menu items &EpiDef, // previous menu NewGameMenu, // menuitem_t -> M_DrawNewGame, // drawing routine -> 48,63, // x,y hurtme // lastOn }; // // OPTIONS MENU // enum { endgame, messages, detail, scrnsize, option_empty1, mousesens, soundvol, crispness, // [crispy] Crispness menu opt_end } options_e; menuitem_t OptionsMenu[]= { {1,"M_ENDGAM", M_EndGame,'e', "End Game"}, {1,"M_MESSG", M_ChangeMessages,'m', "Messages: "}, {1,"M_DETAIL", M_ChangeDetail,'g', "Graphic Detail: "}, {2,"M_SCRNSZ", M_SizeDisplay,'s', "Screen Size"}, {-1,"",0,'\0'}, {1,"M_MSENS", M_Mouse,'m', "Mouse Sensitivity"}, // [crispy] mouse sensitivity menu {1,"M_SVOL", M_Sound,'s', "Sound Volume"}, {1,"M_CRISPY", M_CrispnessCur,'c', "Crispness"} // [crispy] Crispness menu }; menu_t OptionsDef = { opt_end, &MainDef, OptionsMenu, M_DrawOptions, 60,37, 0 }; // [crispy] mouse sensitivity menu enum { mouse_horiz, mouse_empty1, mouse_horiz2, mouse_empty2, mouse_vert, mouse_empty3, mouse_invert, mouse_end } mouse_e; static menuitem_t MouseMenu[]= { {2,"", M_ChangeSensitivity,'h'}, {-1,"",0,'\0'}, {2,"", M_ChangeSensitivity_x2,'s'}, {-1,"",0,'\0'}, {2,"", M_ChangeSensitivity_y,'v'}, {-1,"",0,'\0'}, {1,"", M_MouseInvert,'i'}, }; static menu_t MouseDef = { mouse_end, &OptionsDef, MouseMenu, M_DrawMouse, 80,64, 0 }; // [crispy] Crispness menu enum { crispness_sep_rendering, crispness_hires, crispness_uncapped, crispness_vsync, crispness_smoothscaling, crispness_aspectratio, crispness_sep_rendering_, crispness_sep_visual, crispness_coloredhud, crispness_translucency, crispness_smoothlight, crispness_brightmaps, crispness_coloredblood, crispness_flipcorpses, crispness_sep_visual_, crispness1_next, crispness1_prev, crispness1_end } crispness1_e; static menuitem_t Crispness1Menu[]= { {-1,"",0,'\0'}, {1,"", M_CrispyToggleHires,'h'}, {1,"", M_CrispyToggleUncapped,'u'}, {1,"", M_CrispyToggleVsync,'v'}, {1,"", M_CrispyToggleSmoothScaling,'s'}, {1,"", M_CrispyToggleAspectRatio,'f'}, {-1,"",0,'\0'}, {-1,"",0,'\0'}, {1,"", M_CrispyToggleColoredhud,'c'}, {1,"", M_CrispyToggleTranslucency,'e'}, {1,"", M_CrispyToggleSmoothLighting,'s'}, {1,"", M_CrispyToggleBrightmaps,'b'}, {1,"", M_CrispyToggleColoredblood,'c'}, {1,"", M_CrispyToggleFlipcorpses,'r'}, {-1,"",0,'\0'}, {1,"", M_CrispnessNext,'n'}, {1,"", M_CrispnessPrev,'p'}, }; static menu_t Crispness1Def = { crispness1_end, &OptionsDef, Crispness1Menu, M_DrawCrispness1, 48,28, 1 }; enum { crispness_sep_audible, crispness_soundfull, crispness_soundfix, crispness_sndchannels, crispness_soundmono, crispness_sep_audible_, crispness_sep_navigational, crispness_extautomap, crispness_automapstats, crispness_leveltime, crispness_playercoords, crispness_secretmessage, crispness_sep_navigational_, crispness2_next, crispness2_prev, crispness2_end } crispness2_e; static menuitem_t Crispness2Menu[]= { {-1,"",0,'\0'}, {1,"", M_CrispyToggleFullsounds,'p'}, {1,"", M_CrispyToggleSoundfixes,'m'}, {1,"", M_CrispyToggleSndChannels,'s'}, {1,"", M_CrispyToggleSoundMono,'m'}, {-1,"",0,'\0'}, {-1,"",0,'\0'}, {1,"", M_CrispyToggleExtAutomap,'e'}, {1,"", M_CrispyToggleAutomapstats,'s'}, {1,"", M_CrispyToggleLeveltime,'l'}, {1,"", M_CrispyTogglePlayerCoords,'p'}, {1,"", M_CrispyToggleSecretmessage,'s'}, {-1,"",0,'\0'}, {1,"", M_CrispnessNext,'n'}, {1,"", M_CrispnessPrev,'p'}, }; static menu_t Crispness2Def = { crispness2_end, &OptionsDef, Crispness2Menu, M_DrawCrispness2, 48,28, 1 }; enum { crispness_sep_tactical, crispness_freelook, crispness_mouselook, crispness_bobfactor, crispness_centerweapon, crispness_weaponsquat, crispness_pitch, crispness_neghealth, crispness_sep_tactical_, crispness_sep_crosshair, crispness_crosshair, crispness_crosshairtype, crispness_crosshairhealth, crispness_crosshairtarget, crispness_sep_crosshair_, crispness3_next, crispness3_prev, crispness3_end } crispness3_e; static menuitem_t Crispness3Menu[]= { {-1,"",0,'\0'}, {1,"", M_CrispyToggleFreelook,'a'}, {1,"", M_CrispyToggleMouseLook,'p'}, {1,"", M_CrispyToggleBobfactor,'p'}, {1,"", M_CrispyToggleCenterweapon,'c'}, {1,"", M_CrispyToggleWeaponSquat,'w'}, {1,"", M_CrispyTogglePitch,'w'}, {1,"", M_CrispyToggleNeghealth,'n'}, {-1,"",0,'\0'}, {-1,"",0,'\0'}, {1,"", M_CrispyToggleCrosshair,'d'}, {1,"", M_CrispyToggleCrosshairtype,'c'}, {1,"", M_CrispyToggleCrosshairHealth,'c'}, {1,"", M_CrispyToggleCrosshairTarget,'h'}, {-1,"",0,'\0'}, {1,"", M_CrispnessNext,'n'}, {1,"", M_CrispnessPrev,'p'}, }; static menu_t Crispness3Def = { crispness3_end, &OptionsDef, Crispness3Menu, M_DrawCrispness3, 48,28, 1 }; enum { crispness_sep_physical, crispness_freeaim, crispness_jumping, crispness_overunder, crispness_recoil, crispness_sep_physical_, crispness_sep_demos, crispness_demotimer, crispness_demotimerdir, crispness_demobar, crispness_sep_demos_, crispness4_next, crispness4_prev, crispness4_end } crispness4_e; static menuitem_t Crispness4Menu[]= { {-1,"",0,'\0'}, {1,"", M_CrispyToggleFreeaim,'v'}, {1,"", M_CrispyToggleJumping,'a'}, {1,"", M_CrispyToggleOverunder,'w'}, {1,"", M_CrispyToggleRecoil,'w'}, {-1,"",0,'\0'}, {-1,"",0,'\0'}, {1,"", M_CrispyToggleDemoTimer,'v'}, {1,"", M_CrispyToggleDemoTimerDir,'a'}, {1,"", M_CrispyToggleDemoBar,'w'}, {-1,"",0,'\0'}, {1,"", M_CrispnessNext,'n'}, {1,"", M_CrispnessPrev,'p'}, }; static menu_t Crispness4Def = { crispness4_end, &OptionsDef, Crispness4Menu, M_DrawCrispness4, 48,28, 1 }; static menu_t *CrispnessMenus[] = { &Crispness1Def, &Crispness2Def, &Crispness3Def, &Crispness4Def, }; static int crispness_cur; // // Read This! MENU 1 & 2 // enum { rdthsempty1, read1_end } read_e; menuitem_t ReadMenu1[] = { {1,"",M_ReadThis2,0} }; menu_t ReadDef1 = { read1_end, &MainDef, ReadMenu1, M_DrawReadThis1, 280,185, 0 }; enum { rdthsempty2, read2_end } read_e2; menuitem_t ReadMenu2[]= { {1,"",M_FinishReadThis,0} }; menu_t ReadDef2 = { read2_end, &ReadDef1, ReadMenu2, M_DrawReadThis2, 330,175, 0 }; // // SOUND VOLUME MENU // enum { sfx_vol, sfx_empty1, music_vol, sfx_empty2, sound_end } sound_e; menuitem_t SoundMenu[]= { {2,"M_SFXVOL",M_SfxVol,'s'}, {-1,"",0,'\0'}, {2,"M_MUSVOL",M_MusicVol,'m'}, {-1,"",0,'\0'} }; menu_t SoundDef = { sound_end, &OptionsDef, SoundMenu, M_DrawSound, 80,64, 0 }; // // LOAD GAME MENU // enum { load1, load2, load3, load4, load5, load6, load7, // [crispy] up to 8 savegames load8, // [crispy] up to 8 savegames load_end } load_e; menuitem_t LoadMenu[]= { {1,"", M_LoadSelect,'1'}, {1,"", M_LoadSelect,'2'}, {1,"", M_LoadSelect,'3'}, {1,"", M_LoadSelect,'4'}, {1,"", M_LoadSelect,'5'}, {1,"", M_LoadSelect,'6'}, {1,"", M_LoadSelect,'7'}, // [crispy] up to 8 savegames {1,"", M_LoadSelect,'8'} // [crispy] up to 8 savegames }; menu_t LoadDef = { load_end, &MainDef, LoadMenu, M_DrawLoad, 80,54, 0 }; // // SAVE GAME MENU // menuitem_t SaveMenu[]= { {1,"", M_SaveSelect,'1'}, {1,"", M_SaveSelect,'2'}, {1,"", M_SaveSelect,'3'}, {1,"", M_SaveSelect,'4'}, {1,"", M_SaveSelect,'5'}, {1,"", M_SaveSelect,'6'}, {1,"", M_SaveSelect,'7'}, // [crispy] up to 8 savegames {1,"", M_SaveSelect,'8'} // [crispy] up to 8 savegames }; menu_t SaveDef = { load_end, &MainDef, SaveMenu, M_DrawSave, 80,54, 0 }; // // M_ReadSaveStrings // read the strings from the savegame files // void M_ReadSaveStrings(void) { FILE *handle; int i; char name[256]; for (i = 0;i < load_end;i++) { int retval; M_StringCopy(name, P_SaveGameFile(i), sizeof(name)); handle = fopen(name, "rb"); if (handle == NULL) { M_StringCopy(savegamestrings[i], EMPTYSTRING, SAVESTRINGSIZE); LoadMenu[i].status = 0; continue; } retval = fread(&savegamestrings[i], 1, SAVESTRINGSIZE, handle); fclose(handle); LoadMenu[i].status = retval == SAVESTRINGSIZE; } } // // M_LoadGame & Cie. // static int LoadDef_x = 72, LoadDef_y = 28; void M_DrawLoad(void) { int i; V_DrawPatchDirect(LoadDef_x, LoadDef_y, W_CacheLumpName(DEH_String("M_LOADG"), PU_CACHE)); for (i = 0;i < load_end; i++) { M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); // [crispy] shade empty savegame slots if (!LoadMenu[i].status) dp_translation = cr[CR_DARK]; M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]); dp_translation = NULL; } } // // Draw border for the savegame description // void M_DrawSaveLoadBorder(int x,int y) { int i; V_DrawPatchDirect(x - 8, y + 7, W_CacheLumpName(DEH_String("M_LSLEFT"), PU_CACHE)); for (i = 0;i < 24;i++) { V_DrawPatchDirect(x, y + 7, W_CacheLumpName(DEH_String("M_LSCNTR"), PU_CACHE)); x += 8; } V_DrawPatchDirect(x, y + 7, W_CacheLumpName(DEH_String("M_LSRGHT"), PU_CACHE)); } // // User wants to load this game // void M_LoadSelect(int choice) { char name[256]; M_StringCopy(name, P_SaveGameFile(choice), sizeof(name)); // [crispy] save the last game you loaded SaveDef.lastOn = choice; G_LoadGame (name); M_ClearMenus (); // [crispy] allow quickload before quicksave if (quickSaveSlot == -2) quickSaveSlot = choice; } // // Selected from DOOM menu // void M_LoadGame (int choice) { // [crispy] allow loading game while multiplayer demo playback if (netgame && !demoplayback) { M_StartMessage(DEH_String(LOADNET),NULL,false); return; } M_SetupNextMenu(&LoadDef); M_ReadSaveStrings(); } // // M_SaveGame & Cie. // static int SaveDef_x = 72, SaveDef_y = 28; void M_DrawSave(void) { int i; V_DrawPatchDirect(SaveDef_x, SaveDef_y, W_CacheLumpName(DEH_String("M_SAVEG"), PU_CACHE)); for (i = 0;i < load_end; i++) { M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]); } if (saveStringEnter) { i = M_StringWidth(savegamestrings[saveSlot]); M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_"); } } // // M_Responder calls this when user is finished // void M_DoSave(int slot) { G_SaveGame (slot,savegamestrings[slot]); M_ClearMenus (); // PICK QUICKSAVE SLOT YET? if (quickSaveSlot == -2) quickSaveSlot = slot; } // // Generate a default save slot name when the user saves to // an empty slot via the joypad. // static void SetDefaultSaveName(int slot) { // map from IWAD or PWAD? if (W_IsIWADLump(maplumpinfo) && strcmp(savegamedir, "")) { M_snprintf(savegamestrings[itemOn], SAVESTRINGSIZE, "%s", maplumpinfo->name); } else { char *wadname = M_StringDuplicate(W_WadNameForLump(maplumpinfo)); char *ext = strrchr(wadname, '.'); if (ext != NULL) { *ext = '\0'; } M_snprintf(savegamestrings[itemOn], SAVESTRINGSIZE, "%s (%s)", maplumpinfo->name, wadname); free(wadname); } M_ForceUppercase(savegamestrings[itemOn]); joypadSave = false; } // [crispy] override savegame name if it already starts with a map identifier static boolean StartsWithMapIdentifier (char *str) { M_ForceUppercase(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) { int x, y; // we are going to be intercepting all chars saveStringEnter = 1; // [crispy] load the last game you saved LoadDef.lastOn = choice; // We need to turn on text input: x = LoadDef.x - 11; y = LoadDef.y + choice * LINEHEIGHT - 4; I_StartTextInput(x, y, x + 8 + 24 * 8 + 8, y + LINEHEIGHT - 2); saveSlot = choice; M_StringCopy(saveOldString,savegamestrings[choice], SAVESTRINGSIZE); if (!strcmp(savegamestrings[choice], EMPTYSTRING) || // [crispy] override savegame name if it already starts with a map identifier StartsWithMapIdentifier(savegamestrings[choice])) { savegamestrings[choice][0] = 0; if (joypadSave || true) // [crispy] always prefill empty savegame slot names { SetDefaultSaveName(choice); } } saveCharIndex = strlen(savegamestrings[choice]); } // // Selected from DOOM menu // void M_SaveGame (int choice) { if (!usergame) { M_StartMessage(DEH_String(SAVEDEAD),NULL,false); return; } if (gamestate != GS_LEVEL) return; M_SetupNextMenu(&SaveDef); M_ReadSaveStrings(); } // // M_QuickSave // static char tempstring[90]; void M_QuickSaveResponse(int key) { if (key == key_menu_confirm) { M_DoSave(quickSaveSlot); S_StartSound(NULL,sfx_swtchx); } } void M_QuickSave(void) { char *savegamestring; if (!usergame) { 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; } // [crispy] print savegame name in golden letters savegamestring = M_StringJoin(crstr[CR_GOLD], savegamestrings[quickSaveSlot], crstr[CR_NONE], NULL); DEH_snprintf(tempstring, sizeof(tempstring), QSPROMPT, savegamestring); free(savegamestring); M_StartMessage(tempstring, M_QuickSaveResponse, true); } // // M_QuickLoad // void M_QuickLoadResponse(int key) { if (key == key_menu_confirm) { M_LoadSelect(quickSaveSlot); S_StartSound(NULL,sfx_swtchx); } } void M_QuickLoad(void) { char *savegamestring; // [crispy] allow quickloading game while multiplayer demo playback if (netgame && !demoplayback) { M_StartMessage(DEH_String(QLOADNET),NULL,false); return; } if (quickSaveSlot < 0) { // [crispy] allow quickload before quicksave M_StartControlPanel(); M_ReadSaveStrings(); M_SetupNextMenu(&LoadDef); quickSaveSlot = -2; return; } // [crispy] print savegame name in golden letters savegamestring = M_StringJoin(crstr[CR_GOLD], savegamestrings[quickSaveSlot], crstr[CR_NONE], NULL); DEH_snprintf(tempstring, sizeof(tempstring), QLPROMPT, savegamestring); free(savegamestring); M_StartMessage(tempstring, M_QuickLoadResponse, true); } // // Read This Menus // Had a "quick hack to fix romero bug" // void M_DrawReadThis1(void) { inhelpscreens = true; V_DrawPatchFullScreen(W_CacheLumpName(DEH_String("HELP2"), PU_CACHE), false); } // // Read This Menus - optional second page. // void M_DrawReadThis2(void) { inhelpscreens = true; // We only ever draw the second page if this is // gameversion == exe_doom_1_9 and gamemode == registered V_DrawPatchFullScreen(W_CacheLumpName(DEH_String("HELP1"), PU_CACHE), false); } void M_DrawReadThisCommercial(void) { inhelpscreens = true; V_DrawPatchFullScreen(W_CacheLumpName(DEH_String("HELP"), PU_CACHE), false); } // // Change Sfx & Music volumes // void M_DrawSound(void) { V_DrawPatchDirect (60, 38, W_CacheLumpName(DEH_String("M_SVOL"), PU_CACHE)); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1), 16,sfxVolume); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1), 16,musicVolume); } void M_Sound(int choice) { M_SetupNextMenu(&SoundDef); } void M_SfxVol(int choice) { switch(choice) { case 0: if (sfxVolume) sfxVolume--; break; case 1: if (sfxVolume < 15) sfxVolume++; break; } S_SetSfxVolume(sfxVolume * 8); } void M_MusicVol(int choice) { switch(choice) { case 0: if (musicVolume) musicVolume--; break; case 1: if (musicVolume < 15) musicVolume++; break; } S_SetMusicVolume(musicVolume * 8); } // // M_DrawMainMenu // void M_DrawMainMenu(void) { V_DrawPatchDirect(94, 2, W_CacheLumpName(DEH_String("M_DOOM"), PU_CACHE)); } // // M_NewGame // void M_DrawNewGame(void) { V_DrawPatchDirect(96, 14, W_CacheLumpName(DEH_String("M_NEWG"), PU_CACHE)); V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_SKILL"), PU_CACHE)); } void M_NewGame(int choice) { // [crispy] forbid New Game while recording a demo if (demorecording) { return; } if (netgame && !demoplayback) { M_StartMessage(DEH_String(NEWGAME),NULL,false); return; } // Chex Quest disabled the episode select screen, as did Doom II. if (nervewadfile) M_SetupNextMenu(&ExpDef); else if (gamemode == commercial || gameversion == exe_chex) M_SetupNextMenu(&NewDef); else M_SetupNextMenu(&EpiDef); } // // M_Episode // int epi; void M_DrawEpisode(void) { V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_EPISOD"), PU_CACHE)); } void M_VerifyNightmare(int key) { if (key != key_menu_confirm) return; G_DeferedInitNew(nightmare,epi+1,1); M_ClearMenus (); } void M_ChooseSkill(int choice) { if (choice == nightmare) { M_StartMessage(DEH_String(NIGHTMARE),M_VerifyNightmare,true); return; } G_DeferedInitNew(choice,epi+1,1); M_ClearMenus (); } void M_Episode(int choice) { if ( (gamemode == shareware) && choice) { M_StartMessage(DEH_String(SWSTRING),NULL,false); M_SetupNextMenu(&ReadDef1); return; } epi = choice; M_SetupNextMenu(&NewDef); } static void M_Expansion(int choice) { epi = choice; M_SetupNextMenu(&NewDef); } // // M_Options // // [crispy] no patches are drawn in the Options menu anymore /* static const char *detailNames[2] = {"M_GDHIGH","M_GDLOW"}; static const char *msgNames[2] = {"M_MSGOFF","M_MSGON"}; */ void M_DrawOptions(void) { V_DrawPatchDirect(108, 15, W_CacheLumpName(DEH_String("M_OPTTTL"), PU_CACHE)); // [crispy] no patches are drawn in the Options menu anymore /* V_DrawPatchDirect(OptionsDef.x + 175, OptionsDef.y + LINEHEIGHT * detail, W_CacheLumpName(DEH_String(detailNames[detailLevel]), PU_CACHE)); */ M_WriteText(OptionsDef.x + M_StringWidth("Graphic Detail: "), OptionsDef.y + LINEHEIGHT * detail + 8 - (M_StringHeight("HighLow")/2), detailLevel ? "Low" : "High"); // [crispy] no patches are drawn in the Options menu anymore /* V_DrawPatchDirect(OptionsDef.x + 120, OptionsDef.y + LINEHEIGHT * messages, W_CacheLumpName(DEH_String(msgNames[showMessages]), PU_CACHE)); */ M_WriteText(OptionsDef.x + M_StringWidth("Messages: "), OptionsDef.y + LINEHEIGHT * messages + 8 - (M_StringHeight("OnOff")/2), showMessages ? "On" : "Off"); M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1), 9 + 3,screenSize); // [crispy] Crispy HUD } // [crispy] mouse sensitivity menu static void M_DrawMouse(void) { char mouse_menu_text[48]; V_DrawPatchDirect (60, LoadDef_y, W_CacheLumpName(DEH_String("M_MSENS"), PU_CACHE)); M_WriteText(MouseDef.x, MouseDef.y + LINEHEIGHT * mouse_horiz + 6, "HORIZONTAL: TURN"); M_DrawThermo(MouseDef.x, MouseDef.y + LINEHEIGHT * mouse_empty1, 21, mouseSensitivity); M_WriteText(MouseDef.x, MouseDef.y + LINEHEIGHT * mouse_horiz2 + 6, "HORIZONTAL: STRAFE"); M_DrawThermo(MouseDef.x, MouseDef.y + LINEHEIGHT * mouse_empty2, 21, mouseSensitivity_x2); M_WriteText(MouseDef.x, MouseDef.y + LINEHEIGHT * mouse_vert + 6, "VERTICAL"); M_DrawThermo(MouseDef.x, MouseDef.y + LINEHEIGHT * mouse_empty3, 21, mouseSensitivity_y); M_snprintf(mouse_menu_text, sizeof(mouse_menu_text), "%sInvert Vertical Axis: %s%s", crstr[CR_NONE], mouse_y_invert ? crstr[CR_GREEN] : crstr[CR_DARK], mouse_y_invert ? "On" : "Off"); M_WriteText(MouseDef.x, MouseDef.y + LINEHEIGHT * mouse_invert + 6, mouse_menu_text); dp_translation = NULL; } // [crispy] Crispness menu #include "m_background.h" static void M_DrawCrispnessBackground(void) { const byte *const src = crispness_background; pixel_t *dest; int x, y; dest = I_VideoBuffer; for (y = 0; y < SCREENHEIGHT; y++) { for (x = 0; x < SCREENWIDTH; x++) { #ifndef CRISPY_TRUECOLOR *dest++ = src[(y & 63) * 64 + (x & 63)]; #else *dest++ = colormaps[src[(y & 63) * 64 + (x & 63)]]; #endif } } inhelpscreens = true; } static char crispy_menu_text[48]; static void M_DrawCrispnessHeader(char *item) { M_snprintf(crispy_menu_text, sizeof(crispy_menu_text), "%s%s", crstr[CR_GOLD], item); M_WriteText(ORIGWIDTH/2 - M_StringWidth(item) / 2, 12, crispy_menu_text); } static void M_DrawCrispnessSeparator(int y, char *item) { M_snprintf(crispy_menu_text, sizeof(crispy_menu_text), "%s%s", crstr[CR_GOLD], item); M_WriteText(currentMenu->x - 8, currentMenu->y + CRISPY_LINEHEIGHT * y, crispy_menu_text); } static void M_DrawCrispnessItem(int y, char *item, int feat, boolean cond) { M_snprintf(crispy_menu_text, sizeof(crispy_menu_text), "%s%s: %s%s", cond ? crstr[CR_NONE] : crstr[CR_DARK], item, cond ? (feat ? crstr[CR_GREEN] : crstr[CR_DARK]) : crstr[CR_DARK], cond && feat ? "On" : "Off"); M_WriteText(currentMenu->x, currentMenu->y + CRISPY_LINEHEIGHT * y, crispy_menu_text); } static void M_DrawCrispnessMultiItem(int y, char *item, multiitem_t *multiitem, int feat, boolean cond) { M_snprintf(crispy_menu_text, sizeof(crispy_menu_text), "%s%s: %s%s", cond ? crstr[CR_NONE] : crstr[CR_DARK], item, cond ? (feat ? crstr[CR_GREEN] : crstr[CR_DARK]) : crstr[CR_DARK], cond && feat ? multiitem[feat].name : multiitem[0].name); M_WriteText(currentMenu->x, currentMenu->y + CRISPY_LINEHEIGHT * y, crispy_menu_text); } static void M_DrawCrispnessGoto(int y, char *item) { M_snprintf(crispy_menu_text, sizeof(crispy_menu_text), "%s%s", crstr[CR_GOLD], item); M_WriteText(currentMenu->x, currentMenu->y + CRISPY_LINEHEIGHT * y, crispy_menu_text); } static void M_DrawCrispness1(void) { M_DrawCrispnessBackground(); M_DrawCrispnessHeader("Crispness 1/4"); M_DrawCrispnessSeparator(crispness_sep_rendering, "Rendering"); M_DrawCrispnessItem(crispness_hires, "High Resolution Rendering", crispy->hires, true); M_DrawCrispnessItem(crispness_uncapped, "Uncapped Framerate", crispy->uncapped, true); M_DrawCrispnessItem(crispness_vsync, "Enable VSync", crispy->vsync, !force_software_renderer); M_DrawCrispnessItem(crispness_smoothscaling, "Smooth Pixel Scaling", crispy->smoothscaling, true); M_DrawCrispnessMultiItem(crispness_aspectratio, "Force Aspect Ratio", multiitem_aspectratio, aspect_ratio_correct, true); M_DrawCrispnessSeparator(crispness_sep_visual, "Visual"); M_DrawCrispnessMultiItem(crispness_coloredhud, "Colorize HUD Elements", multiitem_coloredhud, crispy->coloredhud, true); M_DrawCrispnessMultiItem(crispness_translucency, "Enable Translucency", multiitem_translucency, crispy->translucency, true); M_DrawCrispnessItem(crispness_smoothlight, "Smooth Diminishing Lighting", crispy->smoothlight, true); M_DrawCrispnessMultiItem(crispness_brightmaps, "Apply Brightmaps to", multiitem_brightmaps, crispy->brightmaps, true); M_DrawCrispnessItem(crispness_coloredblood, "Colored Blood and Corpses", crispy->coloredblood, gameversion != exe_chex); M_DrawCrispnessItem(crispness_flipcorpses, "Randomly Mirrored Corpses", crispy->flipcorpses, gameversion != exe_chex); M_DrawCrispnessGoto(crispness1_next, "Next Page >"); M_DrawCrispnessGoto(crispness1_prev, "< Last Page"); dp_translation = NULL; } static void M_DrawCrispness2(void) { M_DrawCrispnessBackground(); M_DrawCrispnessHeader("Crispness 2/4"); M_DrawCrispnessSeparator(crispness_sep_audible, "Audible"); M_DrawCrispnessItem(crispness_soundfull, "Play sounds in full length", crispy->soundfull, true); M_DrawCrispnessItem(crispness_soundfix, "Misc. Sound Fixes", crispy->soundfix, true); M_DrawCrispnessMultiItem(crispness_sndchannels, "Sound Channels", multiitem_sndchannels, snd_channels >> 4, snd_sfxdevice != SNDDEVICE_PCSPEAKER); M_DrawCrispnessItem(crispness_soundmono, "Mono SFX", crispy->soundmono, true); M_DrawCrispnessSeparator(crispness_sep_navigational, "Navigational"); M_DrawCrispnessItem(crispness_extautomap, "Extended Automap colors", crispy->extautomap, true); M_DrawCrispnessMultiItem(crispness_automapstats, "Show Level Stats", multiitem_widgets, crispy->automapstats, true); M_DrawCrispnessMultiItem(crispness_leveltime, "Show Level Time", multiitem_widgets, crispy->leveltime, true); M_DrawCrispnessMultiItem(crispness_playercoords, "Show Player Coords", multiitem_widgets, crispy->playercoords, true); M_DrawCrispnessMultiItem(crispness_secretmessage, "Show Revealed Secrets", multiitem_secretmessage, crispy->secretmessage, true); M_DrawCrispnessGoto(crispness2_next, "Next Page >"); M_DrawCrispnessGoto(crispness2_prev, "< Prev Page"); dp_translation = NULL; } static void M_DrawCrispness3(void) { M_DrawCrispnessBackground(); M_DrawCrispnessHeader("Crispness 3/4"); M_DrawCrispnessSeparator(crispness_sep_tactical, "Tactical"); M_DrawCrispnessMultiItem(crispness_freelook, "Allow Free Look", multiitem_freelook, crispy->freelook, true); M_DrawCrispnessItem(crispness_mouselook, "Permanent Mouse Look", crispy->mouselook, true); M_DrawCrispnessMultiItem(crispness_bobfactor, "Player View/Weapon Bobbing", multiitem_bobfactor, crispy->bobfactor, true); M_DrawCrispnessMultiItem(crispness_centerweapon, "Weapon Attack Alignment", multiitem_centerweapon, crispy->centerweapon, crispy->bobfactor != BOBFACTOR_OFF); M_DrawCrispnessItem(crispness_weaponsquat, "Squat weapon down on impact", crispy->weaponsquat, true); M_DrawCrispnessItem(crispness_pitch, "Weapon Recoil Pitch", crispy->pitch, true); M_DrawCrispnessItem(crispness_neghealth, "Negative Player Health", crispy->neghealth, true); // M_DrawCrispnessItem(crispness_extsaveg, "Extended Savegames", crispy->extsaveg, true); M_DrawCrispnessSeparator(crispness_sep_crosshair, "Crosshair"); M_DrawCrispnessMultiItem(crispness_crosshair, "Draw Crosshair", multiitem_crosshair, crispy->crosshair, true); M_DrawCrispnessMultiItem(crispness_crosshairtype, "Crosshair Shape", multiitem_crosshairtype, crispy->crosshairtype + 1, crispy->crosshair); M_DrawCrispnessItem(crispness_crosshairhealth, "Color indicates Health", crispy->crosshairhealth, crispy->crosshair); M_DrawCrispnessItem(crispness_crosshairtarget, "Highlight on target", crispy->crosshairtarget, crispy->crosshair); M_DrawCrispnessGoto(crispness3_next, "Next Page >"); M_DrawCrispnessGoto(crispness3_prev, "< Prev Page"); dp_translation = NULL; } static void M_DrawCrispness4(void) { M_DrawCrispnessBackground(); M_DrawCrispnessHeader("Crispness 4/4"); M_DrawCrispnessSeparator(crispness_sep_physical, "Physical"); M_DrawCrispnessMultiItem(crispness_freeaim, "Vertical Aiming", multiitem_freeaim, crispy->freeaim, crispy->singleplayer); M_DrawCrispnessMultiItem(crispness_jumping, "Allow Jumping", multiitem_jump, crispy->jump, crispy->singleplayer); M_DrawCrispnessItem(crispness_overunder, "Walk over/under Monsters", crispy->overunder, crispy->singleplayer); M_DrawCrispnessItem(crispness_recoil, "Weapon Recoil Thrust", crispy->recoil, crispy->singleplayer); M_DrawCrispnessSeparator(crispness_sep_demos, "Demos"); M_DrawCrispnessMultiItem(crispness_demotimer, "Show Demo Timer", multiitem_demotimer, crispy->demotimer, true); M_DrawCrispnessMultiItem(crispness_demotimerdir, "Playback Timer Direction", multiitem_demotimerdir, crispy->demotimerdir + 1, crispy->demotimer & DEMOTIMER_PLAYBACK); M_DrawCrispnessItem(crispness_demobar, "Show Demo Progress Bar", crispy->demobar, true); M_DrawCrispnessGoto(crispness4_next, "First Page >"); M_DrawCrispnessGoto(crispness4_prev, "< Prev Page"); dp_translation = NULL; } void M_Options(int choice) { M_SetupNextMenu(&OptionsDef); } // [crispy] correctly handle inverted y-axis static void M_Mouse(int choice) { if (mouseSensitivity_y < 0) { mouseSensitivity_y = -mouseSensitivity_y; mouse_y_invert = 1; } if (mouse_acceleration_y < 0) { mouse_acceleration_y = -mouse_acceleration_y; mouse_y_invert = 1; } M_SetupNextMenu(&MouseDef); } static void M_CrispnessCur(int choice) { M_SetupNextMenu(CrispnessMenus[crispness_cur]); } static void M_CrispnessNext(int choice) { if (++crispness_cur > arrlen(CrispnessMenus) - 1) { crispness_cur = 0; } M_CrispnessCur(0); } static void M_CrispnessPrev(int choice) { if (--crispness_cur < 0) { crispness_cur = arrlen(CrispnessMenus) - 1; } M_CrispnessCur(0); } // // Toggle messages on/off // void M_ChangeMessages(int choice) { // warning: unused parameter `int choice' choice = 0; showMessages = 1 - showMessages; if (!showMessages) players[consoleplayer].message = DEH_String(MSGOFF); else players[consoleplayer].message = DEH_String(MSGON); message_dontfuckwithme = true; } // // M_EndGame // void M_EndGameResponse(int key) { if (key != key_menu_confirm) return; // [crispy] killough 5/26/98: make endgame quit if recording or playing back demo if (demorecording || singledemo) G_CheckDemoStatus(); // [crispy] clear quicksave slot quickSaveSlot = -1; currentMenu->lastOn = itemOn; M_ClearMenus (); D_StartTitle (); } void M_EndGame(int choice) { choice = 0; if (!usergame) { S_StartSound(NULL,sfx_oof); return; } if (netgame) { M_StartMessage(DEH_String(NETEND),NULL,false); return; } M_StartMessage(DEH_String(ENDGAME),M_EndGameResponse,true); } // // M_ReadThis // void M_ReadThis(int choice) { choice = 0; M_SetupNextMenu(&ReadDef1); } void M_ReadThis2(int choice) { choice = 0; M_SetupNextMenu(&ReadDef2); } void M_FinishReadThis(int choice) { choice = 0; M_SetupNextMenu(&MainDef); } // // M_QuitDOOM // 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 }; void M_QuitResponse(int key) { extern int show_endoom; if (key != key_menu_confirm) return; // [crispy] play quit sound only if the ENDOOM screen is also shown if (!netgame && show_endoom) { if (gamemode == commercial) S_StartSound(NULL,quitsounds2[(gametic>>2)&7]); else S_StartSound(NULL,quitsounds[(gametic>>2)&7]); I_WaitVBL(105); } I_Quit (); } static const char *M_SelectEndMessage(void) { const char **endmsg; if (logical_gamemission == doom) { // Doom 1 endmsg = doom1_endmsg; } else { // Doom 2 endmsg = doom2_endmsg; } return endmsg[gametic % NUM_QUITMESSAGES]; } void M_QuitDOOM(int choice) { // [crispy] fast exit if "run" key is held down if (speedkeydown()) I_Quit(); DEH_snprintf(endstring, sizeof(endstring), "%s\n\n" DOSY, DEH_String(M_SelectEndMessage())); M_StartMessage(endstring,M_QuitResponse,true); } void M_ChangeSensitivity(int choice) { switch(choice) { case 0: if (mouseSensitivity) mouseSensitivity--; break; case 1: if (mouseSensitivity < 255) // [crispy] extended range mouseSensitivity++; break; } } void M_ChangeSensitivity_x2(int choice) { switch(choice) { case 0: if (mouseSensitivity_x2) mouseSensitivity_x2--; break; case 1: if (mouseSensitivity_x2 < 255) // [crispy] extended range mouseSensitivity_x2++; break; } } static void M_ChangeSensitivity_y(int choice) { switch(choice) { case 0: if (mouseSensitivity_y) mouseSensitivity_y--; break; case 1: if (mouseSensitivity_y < 255) // [crispy] extended range mouseSensitivity_y++; break; } } static void M_MouseInvert(int choice) { choice = 0; mouse_y_invert = !mouse_y_invert; } void M_ChangeDetail(int choice) { choice = 0; detailLevel = 1 - detailLevel; R_SetViewSize (screenblocks, detailLevel); if (!detailLevel) players[consoleplayer].message = DEH_String(DETAILHI); else players[consoleplayer].message = DEH_String(DETAILLO); } void M_SizeDisplay(int choice) { switch(choice) { case 0: if (screenSize > 0) { screenblocks--; screenSize--; } break; case 1: if (screenSize < 8 + 3) // [crispy] Crispy HUD { screenblocks++; screenSize++; } break; } R_SetViewSize (screenblocks, detailLevel); } // // Menu Functions // void M_DrawThermo ( int x, int y, int thermWidth, int thermDot ) { int xx; int i; char num[4]; if (!thermDot) { dp_translation = cr[CR_DARK]; } xx = x; V_DrawPatchDirect(xx, y, W_CacheLumpName(DEH_String("M_THERML"), PU_CACHE)); xx += 8; for (i=0;i= thermWidth) { thermDot = thermWidth - 1; dp_translation = cr[CR_DARK]; } V_DrawPatchDirect((x + 8) + thermDot * 8, y, W_CacheLumpName(DEH_String("M_THERMO"), PU_CACHE)); dp_translation = NULL; } void M_StartMessage ( const char *string, void* routine, boolean input ) { messageLastMenuActive = menuactive; messageToPrint = 1; messageString = string; messageRoutine = routine; messageNeedsInput = input; menuactive = true; // [crispy] entering menus while recording demos pauses the game if (demorecording && !paused) { sendpause = true; } return; } // // Find string width from hu_font chars // int M_StringWidth(const char *string) { size_t i; int w = 0; int c; for (i = 0;i < strlen(string);i++) { // [crispy] correctly center colorized strings if (string[i] == cr_esc) { if (string[i+1] >= '0' && string[i+1] <= '0' + CRMAX - 1) { i++; continue; } } c = toupper(string[i]) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE) w += 4; else w += SHORT (hu_font[c]->width); } return w; } // // Find string height from hu_font chars // int M_StringHeight(const char* string) { size_t i; int h; int height = SHORT(hu_font[0]->height); h = height; for (i = 0;i < strlen(string);i++) if (string[i] == '\n') h += height; return h; } // // Write a string using the hu_font // void M_WriteText ( int x, int y, const char *string) { int w; const char *ch; int c; int cx; int cy; ch = string; cx = x; cy = y; while(1) { c = *ch++; if (!c) break; if (c == '\n') { cx = x; cy += 12; continue; } // [crispy] support multi-colored text if (c == cr_esc) { if (*ch >= '0' && *ch <= '0' + CRMAX - 1) { c = *ch++; dp_translation = cr[(int) (c - '0')]; continue; } } c = toupper(c) - HU_FONTSTART; if (c < 0 || c>= HU_FONTSIZE) { cx += 4; continue; } w = SHORT (hu_font[c]->width); if (cx+w > ORIGWIDTH) break; V_DrawPatchDirect(cx, cy, hu_font[c]); cx+=w; } } // These keys evaluate to a "null" key in Vanilla Doom that allows weird // jumping in the menus. Preserve this behavior for accuracy. static boolean IsNullKey(int key) { return key == KEY_PAUSE || key == KEY_CAPSLOCK || key == KEY_SCRLCK || key == KEY_NUMLOCK; } // [crispy] reload current level / go to next level // adapted from prboom-plus/src/e6y.c:369-449 static int G_ReloadLevel(void) { int result = false; if (gamestate == GS_LEVEL) { // [crispy] restart demos from the map they were started if (demorecording) { gamemap = startmap; } G_DeferedInitNew(gameskill, gameepisode, gamemap); result = true; } return result; } static int G_GotoNextLevel(void) { static byte doom_next[5][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, 51, 43}, {52, 53, 54, 55, 56, 59, 58, 11, 57}, }; static byte doom2_next[33] = { 0, 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 nerve_next[9] = { 2, 3, 4, 9, 6, 7, 8, 1, 5 }; int changed = false; // [crispy] process only once if (!doom2_next[0]) { doom2_next[0] = 2; if (gamemode == commercial) { if (crispy->havemap33) doom2_next[1] = 33; if (W_CheckNumForName("map31") < 0) doom2_next[14] = 16; if (gamemission == pack_hacx) { doom2_next[30] = 16; doom2_next[20] = 1; } if (gamemission == pack_master) { doom2_next[1] = 3; doom2_next[14] = 16; doom2_next[20] = 1; } } else { if (gamemode == shareware) doom_next[0][7] = 11; if (gamemode == registered) doom_next[2][7] = 11; if (!crispy->haved1e5) doom_next[3][7] = 11; if (gameversion == exe_chex) { doom_next[0][2] = 14; doom_next[0][4] = 11; } } } if (gamestate == GS_LEVEL) { int epsd, map; if (gamemode == commercial) { epsd = gameepisode; if (gamemission == pack_nerve) map = nerve_next[gamemap-1]; else map = doom2_next[gamemap-1]; } else { epsd = doom_next[gameepisode-1][gamemap-1] / 10; map = doom_next[gameepisode-1][gamemap-1] % 10; } // [crispy] special-casing for E1M10 "Sewers" support if (crispy->havee1m10 && gameepisode == 1) { if (gamemap == 1) { map = 10; } else if (gamemap == 10) { epsd = 1; map = 2; } } G_DeferedInitNew(gameskill, epsd, map); changed = true; } return changed; } // // CONTROL PANEL // // // M_Responder // boolean M_Responder (event_t* ev) { int ch; int key; int i; static int mousewait = 0; static int mousey = 0; static int lasty = 0; static int mousex = 0; static int lastx = 0; // In testcontrols mode, none of the function keys should do anything // - the only key is escape to quit. if (testcontrols) { if (ev->type == ev_quit || (ev->type == ev_keydown && (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit))) { I_Quit(); return true; } return false; } // "close" button pressed on window? if (ev->type == ev_quit) { // First click on close button = bring up quit confirm message. // Second click on close button = confirm quit if (menuactive && messageToPrint && messageRoutine == M_QuitResponse) { M_QuitResponse(key_menu_confirm); } else { S_StartSound(NULL,sfx_swtchn); M_QuitDOOM(0); } return true; } // key is the key pressed, ch is the actual character typed ch = 0; key = -1; if (ev->type == ev_joystick) { // Simulate key presses from joystick events to interact with the menu. if (menuactive) { if (ev->data3 < 0) { key = key_menu_up; joywait = I_GetTime() + 5; } else if (ev->data3 > 0) { key = key_menu_down; joywait = I_GetTime() + 5; } if (ev->data2 < 0) { key = key_menu_left; joywait = I_GetTime() + 2; } else if (ev->data2 > 0) { key = key_menu_right; joywait = I_GetTime() + 2; } #define JOY_BUTTON_MAPPED(x) ((x) >= 0) #define JOY_BUTTON_PRESSED(x) (JOY_BUTTON_MAPPED(x) && (ev->data1 & (1 << (x))) != 0) if (JOY_BUTTON_PRESSED(joybfire)) { // Simulate a 'Y' keypress when Doom show a Y/N dialog with Fire button. if (messageToPrint && messageNeedsInput) { key = key_menu_confirm; } // Simulate pressing "Enter" when we are supplying a save slot name else if (saveStringEnter) { key = KEY_ENTER; } else { // if selecting a save slot via joypad, set a flag if (currentMenu == &SaveDef) { joypadSave = true; } key = key_menu_forward; } joywait = I_GetTime() + 5; } if (JOY_BUTTON_PRESSED(joybuse)) { // Simulate a 'N' keypress when Doom show a Y/N dialog with Use button. if (messageToPrint && messageNeedsInput) { key = key_menu_abort; } // If user was entering a save name, back out else if (saveStringEnter) { key = KEY_ESCAPE; } else { key = key_menu_back; } joywait = I_GetTime() + 5; } } if (JOY_BUTTON_PRESSED(joybmenu)) { key = key_menu_activate; joywait = I_GetTime() + 5; } } else { if (ev->type == ev_mouse && mousewait < I_GetTime()) { // [crispy] novert disables controlling the menus with the mouse if (!novert) { mousey += ev->data3; } if (mousey < lasty-30) { key = key_menu_down; mousewait = I_GetTime() + 5; mousey = lasty -= 30; } else if (mousey > lasty+30) { key = key_menu_up; mousewait = I_GetTime() + 5; mousey = lasty += 30; } mousex += ev->data2; if (mousex < lastx-30) { key = key_menu_left; mousewait = I_GetTime() + 5; mousex = lastx -= 30; } else if (mousex > lastx+30) { key = key_menu_right; mousewait = I_GetTime() + 5; mousex = lastx += 30; } if (ev->data1&1) { key = key_menu_forward; mousewait = I_GetTime() + 15; } if (ev->data1&2) { key = key_menu_back; mousewait = I_GetTime() + 15; } // [crispy] scroll menus with mouse wheel if (mousebprevweapon >= 0 && ev->data1 & (1 << mousebprevweapon)) { key = key_menu_down; mousewait = I_GetTime() + 5; } else if (mousebnextweapon >= 0 && ev->data1 & (1 << mousebnextweapon)) { key = key_menu_up; mousewait = I_GetTime() + 5; } } else { if (ev->type == ev_keydown) { key = ev->data1; ch = ev->data2; } } } if (key == -1) return false; // Save Game string input if (saveStringEnter) { switch(key) { case KEY_BACKSPACE: if (saveCharIndex > 0) { saveCharIndex--; savegamestrings[saveSlot][saveCharIndex] = 0; } break; case KEY_ESCAPE: saveStringEnter = 0; I_StopTextInput(); M_StringCopy(savegamestrings[saveSlot], saveOldString, SAVESTRINGSIZE); break; case KEY_ENTER: saveStringEnter = 0; I_StopTextInput(); if (savegamestrings[saveSlot][0]) M_DoSave(saveSlot); break; default: // Savegame name entry. This is complicated. // Vanilla has a bug where the shift key is ignored when entering // a savegame name. If vanilla_keyboard_mapping is on, we want // to emulate this bug by using ev->data1. But if it's turned off, // it implies the user doesn't care about Vanilla emulation: // instead, use ev->data3 which gives the fully-translated and // modified key input. if (ev->type != ev_keydown) { break; } if (vanilla_keyboard_mapping) { ch = ev->data1; } else { ch = ev->data3; } ch = toupper(ch); if (ch != ' ' && (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE)) { break; } if (ch >= 32 && ch <= 127 && saveCharIndex < SAVESTRINGSIZE-1 && M_StringWidth(savegamestrings[saveSlot]) < (SAVESTRINGSIZE-2)*8) { savegamestrings[saveSlot][saveCharIndex++] = ch; savegamestrings[saveSlot][saveCharIndex] = 0; } break; } return true; } // Take care of any messages that need input if (messageToPrint) { if (messageNeedsInput) { if (key != ' ' && key != KEY_ESCAPE && key != key_menu_confirm && key != key_menu_abort) { return false; } } menuactive = messageLastMenuActive; if (messageRoutine) messageRoutine(key); // [crispy] stay in menu if (messageToPrint < 2) { menuactive = false; } messageToPrint = 0; // [crispy] moved here S_StartSound(NULL,sfx_swtchx); return true; } // [crispy] take screen shot without weapons and HUD if (key != 0 && key == key_menu_cleanscreenshot) { crispy->cleanscreenshot = (screenblocks > 10) ? 2 : 1; } if ((devparm && key == key_menu_help) || (key != 0 && (key == key_menu_screenshot || key == key_menu_cleanscreenshot))) { G_ScreenShot (); return true; } // F-Keys if (!menuactive) { if (key == key_menu_decscreen) // Screen size down { if (automapactive || chat_on) return false; M_SizeDisplay(0); S_StartSound(NULL,sfx_stnmov); return true; } else if (key == key_menu_incscreen) // Screen size up { if (automapactive || chat_on) return false; M_SizeDisplay(1); S_StartSound(NULL,sfx_stnmov); return true; } else if (key == key_menu_help) // Help key { M_StartControlPanel (); if (gameversion >= exe_ultimate) currentMenu = &ReadDef2; else currentMenu = &ReadDef1; itemOn = 0; S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_save) // Save { M_StartControlPanel(); S_StartSound(NULL,sfx_swtchn); M_SaveGame(0); return true; } else if (key == key_menu_load) // Load { M_StartControlPanel(); S_StartSound(NULL,sfx_swtchn); M_LoadGame(0); return true; } else if (key == key_menu_volume) // Sound Volume { M_StartControlPanel (); currentMenu = &SoundDef; itemOn = sfx_vol; S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_detail) // Detail toggle { M_ChangeDetail(0); S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_qsave) // Quicksave { S_StartSound(NULL,sfx_swtchn); M_QuickSave(); return true; } else if (key == key_menu_endgame) // End game { S_StartSound(NULL,sfx_swtchn); M_EndGame(0); return true; } else if (key == key_menu_messages) // Toggle messages { M_ChangeMessages(0); S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_qload) // Quickload { S_StartSound(NULL,sfx_swtchn); M_QuickLoad(); return true; } else if (key == key_menu_quit) // Quit DOOM { S_StartSound(NULL,sfx_swtchn); M_QuitDOOM(0); return true; } else if (key == key_menu_gamma) // gamma toggle { usegamma++; if (usegamma > 4+4) // [crispy] intermediate gamma levels usegamma = 0; players[consoleplayer].message = DEH_String(gammamsg[usegamma]); #ifndef CRISPY_TRUECOLOR I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE)); #else { extern void R_InitColormaps (void); I_SetPalette (0); R_InitColormaps(); inhelpscreens = true; R_FillBackScreen(); viewactive = false; } #endif return true; } // [crispy] those two can be considered as shortcuts for the IDCLEV cheat // and should be treated as such, i.e. add "if (!netgame)" else if (!netgame && key != 0 && key == key_menu_reloadlevel) { if (G_ReloadLevel()) return true; } else if (!netgame && key != 0 && key == key_menu_nextlevel) { if (G_GotoNextLevel()) return true; } } // Pop-up menu? if (!menuactive) { if (key == key_menu_activate) { M_StartControlPanel (); S_StartSound(NULL,sfx_swtchn); return true; } return false; } // Keys usable within menu if (key == key_menu_down) { // Move down to next item do { if (itemOn+1 > currentMenu->numitems-1) itemOn = 0; else itemOn++; S_StartSound(NULL,sfx_pstop); } while(currentMenu->menuitems[itemOn].status==-1); return true; } else if (key == key_menu_up) { // Move back up to previous item do { if (!itemOn) itemOn = currentMenu->numitems-1; else itemOn--; S_StartSound(NULL,sfx_pstop); } while(currentMenu->menuitems[itemOn].status==-1); return true; } else if (key == key_menu_left) { // Slide slider left if (currentMenu->menuitems[itemOn].routine && currentMenu->menuitems[itemOn].status == 2) { S_StartSound(NULL,sfx_stnmov); currentMenu->menuitems[itemOn].routine(0); } return true; } else if (key == key_menu_right) { // Slide slider right if (currentMenu->menuitems[itemOn].routine && currentMenu->menuitems[itemOn].status == 2) { S_StartSound(NULL,sfx_stnmov); currentMenu->menuitems[itemOn].routine(1); } return true; } else if (key == key_menu_forward) { // Activate menu item 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); } } return true; } else if (key == key_menu_activate) { // Deactivate menu currentMenu->lastOn = itemOn; M_ClearMenus (); S_StartSound(NULL,sfx_swtchx); return true; } else if (key == key_menu_back) { // Go back to previous menu currentMenu->lastOn = itemOn; if (currentMenu->prevMenu) { currentMenu = currentMenu->prevMenu; itemOn = currentMenu->lastOn; S_StartSound(NULL,sfx_swtchn); } return true; } // [crispy] delete a savegame else if (key == key_menu_del) { if (currentMenu == &LoadDef || currentMenu == &SaveDef) { if (LoadMenu[itemOn].status) { currentMenu->lastOn = itemOn; M_ConfirmDeleteGame(); return true; } else { S_StartSound(NULL,sfx_oof); } } } // [crispy] next/prev Crispness menu else if (key == KEY_PGUP) { currentMenu->lastOn = itemOn; if (currentMenu == CrispnessMenus[crispness_cur]) { M_CrispnessPrev(0); S_StartSound(NULL,sfx_swtchn); return true; } } else if (key == KEY_PGDN) { currentMenu->lastOn = itemOn; if (currentMenu == CrispnessMenus[crispness_cur]) { M_CrispnessNext(0); S_StartSound(NULL,sfx_swtchn); return true; } } // Keyboard shortcut? // Vanilla Doom has a weird behavior where it jumps to the scroll bars // when the certain keys are pressed, so emulate this. else if (ch != 0 || IsNullKey(key)) { 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; } // // M_StartControlPanel // void M_StartControlPanel (void) { // intro might call this repeatedly if (menuactive) return; // [crispy] entering menus while recording demos pauses the game if (demorecording && !paused) sendpause = true; menuactive = 1; currentMenu = &MainDef; // JDC itemOn = currentMenu->lastOn; // JDC } // Display OPL debug messages - hack for GENMIDI development. static void M_DrawOPLDev(void) { extern void I_OPL_DevMessages(char *, size_t); char debug[1024]; char *curr, *p; int line; I_OPL_DevMessages(debug, sizeof(debug)); curr = debug; line = 0; for (;;) { p = strchr(curr, '\n'); if (p != NULL) { *p = '\0'; } M_WriteText(0, line * 8, curr); ++line; if (p == NULL) { break; } curr = p + 1; } } // // M_Drawer // Called after the view has been rendered, // but before it has been blitted. // void M_Drawer (void) { static short x; static short y; unsigned int i; unsigned int max; char string[80]; const char *name; int start; inhelpscreens = false; // Horiz. & Vertically center string and print it. if (messageToPrint) { // [crispy] draw a background for important questions if (messageToPrint == 2) { M_DrawCrispnessBackground(); } start = 0; y = ORIGHEIGHT/2 - M_StringHeight(messageString) / 2; while (messageString[start] != '\0') { boolean foundnewline = false; for (i = 0; messageString[start + i] != '\0'; i++) { if (messageString[start + i] == '\n') { M_StringCopy(string, messageString + start, sizeof(string)); if (i < sizeof(string)) { string[i] = '\0'; } foundnewline = true; start += i + 1; break; } } if (!foundnewline) { M_StringCopy(string, messageString + start, sizeof(string)); start += strlen(string); } x = ORIGWIDTH/2 - M_StringWidth(string) / 2; M_WriteText(x > 0 ? x : 0, y, string); // [crispy] prevent negative x-coords y += SHORT(hu_font[0]->height); } return; } if (opldev) { M_DrawOPLDev(); } if (!menuactive) return; if (currentMenu->routine) currentMenu->routine(); // call Draw routine // DRAW MENU x = currentMenu->x; y = currentMenu->y; max = currentMenu->numitems; for (i=0;imenuitems[i].name); if (name[0]) // && W_CheckNumForName(name) > 0) // [crispy] moved... { // [crispy] shade unavailable menu items if ((currentMenu == &MainDef && i == savegame && (!usergame || gamestate != GS_LEVEL)) || (currentMenu == &OptionsDef && i == endgame && (!usergame || netgame)) || (currentMenu == &MainDef && i == loadgame && (netgame && !demoplayback)) || (currentMenu == &MainDef && i == newgame && (demorecording || (netgame && !demoplayback)))) dp_translation = cr[CR_DARK]; if (currentMenu == &OptionsDef) { char *alttext = currentMenu->menuitems[i].alttext; if (alttext) M_WriteText(x, y+8-(M_StringHeight(alttext)/2), alttext); } else if (W_CheckNumForName(name) > 0) // [crispy] ...here V_DrawPatchDirect (x, y, W_CacheLumpName(name, PU_CACHE)); dp_translation = NULL; } y += LINEHEIGHT; } // DRAW SKULL if (currentMenu == CrispnessMenus[crispness_cur]) { char item[4]; M_snprintf(item, sizeof(item), "%s>", whichSkull ? crstr[CR_NONE] : crstr[CR_DARK]); M_WriteText(currentMenu->x - 8, currentMenu->y + CRISPY_LINEHEIGHT * itemOn, item); dp_translation = NULL; } else V_DrawPatchDirect(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT, W_CacheLumpName(DEH_String(skullName[whichSkull]), PU_CACHE)); } // // M_ClearMenus // void M_ClearMenus (void) { menuactive = 0; // [crispy] entering menus while recording demos pauses the game if (demorecording && paused) sendpause = true; // if (!netgame && usergame && paused) // sendpause = true; } // // M_SetupNextMenu // void M_SetupNextMenu(menu_t *menudef) { currentMenu = menudef; itemOn = currentMenu->lastOn; } // // M_Ticker // void M_Ticker (void) { if (--skullAnimCounter <= 0) { whichSkull ^= 1; skullAnimCounter = 8; } } // // M_Init // void M_Init (void) { currentMenu = &MainDef; menuactive = 0; 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. // The same hacks were used in the original Doom EXEs. if (gameversion >= exe_ultimate) { MainMenu[readthis].routine = M_ReadThis2; ReadDef2.prevMenu = NULL; } if (gameversion >= exe_final && gameversion <= exe_final2) { ReadDef2.routine = M_DrawReadThisCommercial; // [crispy] rearrange Skull in Final Doom HELP screen ReadDef2.y -= 10; } if (gamemode == commercial) { MainMenu[readthis] = MainMenu[quitdoom]; MainDef.numitems--; MainDef.y += 8; NewDef.prevMenu = nervewadfile ? &ExpDef : &MainDef; ReadDef1.routine = M_DrawReadThisCommercial; ReadDef1.x = 330; ReadDef1.y = 165; ReadMenu1[rdthsempty1].routine = M_FinishReadThis; } // [crispy] Sigil if (!crispy->haved1e5) { EpiDef.numitems = 4; } // Versions of doom.exe before the Ultimate Doom release only had // three episodes; if we're emulating one of those then don't try // to show episode four. If we are, then do show episode four // (should crash if missing). if (gameversion < exe_ultimate) { EpiDef.numitems--; } // chex.exe shows only one episode. else if (gameversion == exe_chex) { EpiDef.numitems = 1; // [crispy] never show the Episode menu NewDef.prevMenu = &MainDef; } // [crispy] rearrange Load Game and Save Game menus { const patch_t *patchl, *patchs, *patchm; short captionheight, vstep; patchl = W_CacheLumpName(DEH_String("M_LOADG"), PU_CACHE); patchs = W_CacheLumpName(DEH_String("M_SAVEG"), PU_CACHE); patchm = W_CacheLumpName(DEH_String("M_LSLEFT"), PU_CACHE); LoadDef_x = (ORIGWIDTH - SHORT(patchl->width)) / 2 + SHORT(patchl->leftoffset); SaveDef_x = (ORIGWIDTH - SHORT(patchs->width)) / 2 + SHORT(patchs->leftoffset); LoadDef.x = SaveDef.x = (ORIGWIDTH - 24 * 8) / 2 + SHORT(patchm->leftoffset); // [crispy] see M_DrawSaveLoadBorder() captionheight = MAX(SHORT(patchl->height), SHORT(patchs->height)); vstep = ORIGHEIGHT - 32; // [crispy] ST_HEIGHT vstep -= captionheight; vstep -= (load_end - 1) * LINEHEIGHT + SHORT(patchm->height); vstep /= 3; if (vstep > 0) { LoadDef_y = vstep + captionheight - SHORT(patchl->height) + SHORT(patchl->topoffset); SaveDef_y = vstep + captionheight - SHORT(patchs->height) + SHORT(patchs->topoffset); LoadDef.y = SaveDef.y = vstep + captionheight + vstep + SHORT(patchm->topoffset) - 7; // [crispy] see M_DrawSaveLoadBorder() MouseDef.y = LoadDef.y; } } // [crispy] remove DOS reference from the game quit confirmation dialogs if (!M_ParmExists("-nodeh")) { const char *string; char *replace; // [crispy] "i wouldn't leave if i were you.\ndos is much worse." string = doom1_endmsg[3]; if (!DEH_HasStringReplacement(string)) { replace = M_StringReplace(string, "dos", crispy->platform); DEH_AddStringReplacement(string, replace); free(replace); } // [crispy] "you're trying to say you like dos\nbetter than me, right?" string = doom1_endmsg[4]; if (!DEH_HasStringReplacement(string)) { replace = M_StringReplace(string, "dos", crispy->platform); DEH_AddStringReplacement(string, replace); free(replace); } // [crispy] "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!" string = doom2_endmsg[2]; if (!DEH_HasStringReplacement(string)) { replace = M_StringReplace(string, "dos", "command"); DEH_AddStringReplacement(string, replace); free(replace); } } opldev = M_CheckParm("-opldev") > 0; } // [crispy] extended savegames static char *savegwarning; static void M_ForceLoadGameResponse(int key) { free(savegwarning); free(savewadfilename); if (key != key_menu_confirm || !savemaplumpinfo) { // [crispy] no need to end game anymore when denied to load savegame //M_EndGameResponse(key_menu_confirm); savewadfilename = NULL; // [crispy] reload Load Game menu M_StartControlPanel(); M_LoadGame(0); return; } savewadfilename = (char *)W_WadNameForLump(savemaplumpinfo); gameaction = ga_loadgame; } void M_ForceLoadGame() { savegwarning = savemaplumpinfo ? M_StringJoin("This savegame requires the file\n", crstr[CR_GOLD], savewadfilename, crstr[CR_NONE], "\n", "to restore ", crstr[CR_GOLD], savemaplumpinfo->name, crstr[CR_NONE], " .\n\n", "Continue to restore from\n", crstr[CR_GOLD], W_WadNameForLump(savemaplumpinfo), crstr[CR_NONE], " ?\n\n", PRESSYN, NULL) : M_StringJoin("This savegame requires the file\n", crstr[CR_GOLD], savewadfilename, crstr[CR_NONE], "\n", "to restore a map that is\n", "currently not available!\n\n", PRESSKEY, NULL) ; M_StartMessage(savegwarning, M_ForceLoadGameResponse, savemaplumpinfo != NULL); messageToPrint = 2; S_StartSound(NULL,sfx_swtchn); } static void M_ConfirmDeleteGameResponse (int key) { free(savegwarning); if (key == key_menu_confirm) { char name[256]; M_StringCopy(name, P_SaveGameFile(itemOn), sizeof(name)); remove(name); M_ReadSaveStrings(); } } void M_ConfirmDeleteGame () { savegwarning = M_StringJoin("delete savegame\n\n", crstr[CR_GOLD], savegamestrings[itemOn], crstr[CR_NONE], " ?\n\n", PRESSYN, NULL); M_StartMessage(savegwarning, M_ConfirmDeleteGameResponse, true); messageToPrint = 2; S_StartSound(NULL,sfx_swtchn); } // [crispy] indicate game version mismatch void M_LoadGameVerMismatch () { M_StartMessage("Game Version Mismatch\n\n"PRESSKEY, NULL, false); messageToPrint = 2; S_StartSound(NULL,sfx_swtchn); } crispy-doom-crispy-doom-5.6.4/src/doom/m_menu.h000066400000000000000000000027011360717211000214120ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Menu widget stuff, episode selection and such. // #ifndef __M_MENU__ #define __M_MENU__ #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. boolean M_Responder (event_t *ev); // Called by main loop, // only used for menu (skull cursor) animation. void M_Ticker (void); // Called by main loop, // draws the menus directly into the screen buffer. void M_Drawer (void); // Called by D_DoomMain, // loads the config file. void M_Init (void); // Called by intro code to force menu up upon a keypress, // does nothing if menu is already up. void M_StartControlPanel (void); extern int detailLevel; extern int screenblocks; #endif crispy-doom-crispy-doom-5.6.4/src/doom/m_random.c000066400000000000000000000053771360717211000217350ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Random number LUT. // // // M_Random // Returns a 0-255 number // static const unsigned char rndtable[256] = { 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , 120, 163, 236, 249 }; int rndindex = 0; int prndindex = 0; int crndindex = 0; // Which one is deterministic? int P_Random (void) { prndindex = (prndindex+1)&0xff; return rndtable[prndindex]; } int M_Random (void) { rndindex = (rndindex+1)&0xff; return rndtable[rndindex]; } // [crispy] our own private random function int Crispy_Random (void) { crndindex = (crndindex+1)&0xff; return rndtable[crndindex]; } void M_ClearRandom (void) { rndindex = prndindex = 0; crndindex = 0; } // inspired by the same routine in Eternity, thanks haleyjd int P_SubRandom (void) { int r = P_Random(); return r - P_Random(); } int Crispy_SubRandom (void) { int r = Crispy_Random(); return r - Crispy_Random(); } crispy-doom-crispy-doom-5.6.4/src/doom/m_random.h000066400000000000000000000020661360717211000217320ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __M_RANDOM__ #define __M_RANDOM__ #include "doomtype.h" // Returns a number from 0 to 255, // from a lookup table. int M_Random (void); // As M_Random, but used only by the play simulation. int P_Random (void); // [crispy] our own private random function int Crispy_Random (void); // Fix randoms for demos. void M_ClearRandom (void); // Defined version of P_Random() - P_Random() int P_SubRandom (void); int Crispy_SubRandom (void); #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_bexptr.c000066400000000000000000000171071360717211000217560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1999 id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] additional BOOM and MBF code pointers // #include "p_local.h" #include "m_random.h" #include "s_sound.h" extern void A_Explode(); extern void A_FaceTarget(); extern boolean P_CheckMeleeRange (mobj_t *actor); extern void P_Thrust (player_t* player, angle_t angle, fixed_t move); // killough 11/98: kill an object void A_Die(mobj_t *actor) { 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) { 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 fixed_t misc1 = actor->state->misc1 ? actor->state->misc1 : FRACUNIT*4; fixed_t misc2 = actor->state->misc2 ? actor->state->misc2 : FRACUNIT/2; A_Explode(actor); // make normal explosion for (i = -n; i <= n; i += 8) // launch mushroom cloud 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 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 } } // // A_BetaSkullAttack() // killough 10/98: this emulates the beta version's lost soul attacks // void A_BetaSkullAttack(mobj_t *actor) { int damage; 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) { actor->momx = actor->momy = actor->momz = 0; } // // 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 (mo->state->misc1) { /* mobj_t *newmobj = */ P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z, mo->state->misc1 - 1); // newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); } } void A_Turn(mobj_t *mo) { mo->angle += (angle_t)(((uint64_t) mo->state->misc1 << 32) / 360); } void A_Face(mobj_t *mo) { mo->angle = (angle_t)(((uint64_t) mo->state->misc1 << 32) / 360); } void A_Scratch(mobj_t *mo) { 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) { S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1); } // [crispy] this is pretty much the only action pointer that makes sense for both mobj and pspr states void A_RandomJump(mobj_t *mo, player_t *player, pspdef_t *psp) { // [crispy] first, try to apply to pspr states if (player && psp) { if (Crispy_Random() < psp->state->misc2) { extern void P_SetPsprite (player_t *player, int position, statenum_t stnum); P_SetPsprite(player, psp - &player->psprites[0], psp->state->misc1); } } else // [crispy] second, apply to mobj states if (mo) { if (Crispy_Random() < 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) { //if (!(mo->intflags & MIF_LINEDONE)) // Unless already used up { line_t junk = *lines; // Fake linedef set to 1st if ((junk.special = (short)mo->state->misc1)) // Linedef type { player_t player, *oldplayer = mo->player; // Remember player status mo->player = &player; // Fake player player.health = 100; // Alive player junk.tag = (short)mo->state->misc2; // Sector tag for linedef if (!P_UseSpecialLine(mo, &junk, 0)) // Try using it P_CrossSpecialLinePtr(&junk, 0, mo); // Try crossing it // if (!junk.special) // If type cleared, // mo->intflags |= MIF_LINEDONE; // no more for this thing mo->player = oldplayer; // Restore player status } } } // // 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. void A_FireOldBFG(mobj_t *mobj, player_t *player, pspdef_t *psp) { int type = MT_PLASMA1; extern void P_CheckMissileSpawn (mobj_t* th); if (!player) return; // [crispy] let pspr action pointers get called from mobj states if (crispy->recoil && !(player->mo->flags & MF_NOCLIP)) P_Thrust(player, ANG180 + player->mo->angle, 512*20);//recoil_values[wp_plasma][0]); 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 int mask = 0;//MF_FRIEND; fixed_t slope; if (critical->freeaim == FREEAIM_DIRECT) slope = PLAYER_SLOPE(player); else 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 = (critical->freeaim == FREEAIM_BOTH) ? PLAYER_SLOPE(player) : 0, an = mo->angle; } while (mask && (mask=0, !linetarget)); // killough 8/2/98 an1 += an - mo->angle; // [crispy] consider negative slope if (slope < 0) an2 -= tantoangle[-slope >> DBITS]; else an2 += tantoangle[slope >> DBITS]; } th = P_SpawnMobj(mo->x, mo->y, mo->z + 62*FRACUNIT - player->psprites[ps_weapon].sy, type); th->target = mo; // 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; // [crispy] suppress interpolation of player missiles for the first tic th->interp = -1; P_CheckMissileSpawn(th); } while ((type != MT_PLASMA2) && (type = MT_PLASMA2)); //killough: obfuscated! } crispy-doom-crispy-doom-5.6.4/src/doom/p_blockmap.c000066400000000000000000000136311360717211000222400ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1999 id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] Create Blockmap // #include #include "i_system.h" #include "p_local.h" #include "z_zone.h" // [crispy] taken from mbfsrc/P_SETUP.C:547-707, slightly adapted void P_CreateBlockMap(void) { register int i; fixed_t minx = INT_MAX, miny = INT_MAX, maxx = INT_MIN, maxy = INT_MIN; // First find limits of map for (i=0; i> FRACBITS < minx) minx = vertexes[i].x >> FRACBITS; else if (vertexes[i].x >> FRACBITS > maxx) maxx = vertexes[i].x >> FRACBITS; if (vertexes[i].y >> FRACBITS < miny) miny = vertexes[i].y >> FRACBITS; else if (vertexes[i].y >> FRACBITS > maxy) maxy = vertexes[i].y >> FRACBITS; } // [crispy] doombsp/DRAWING.M:175-178 minx -= 8; miny -= 8; maxx += 8; maxy += 8; // Save blockmap parameters bmaporgx = minx << FRACBITS; bmaporgy = miny << FRACBITS; bmapwidth = ((maxx-minx) >> MAPBTOFRAC) + 1; bmapheight = ((maxy-miny) >> MAPBTOFRAC) + 1; // Compute blockmap, which is stored as a 2d array of variable-sized lists. // // Pseudocode: // // For each linedef: // // Map the starting and ending vertices to blocks. // // Starting in the starting vertex's block, do: // // Add linedef to current block's list, dynamically resizing it. // // If current block is the same as the ending vertex's block, exit loop. // // Move to an adjacent block by moving towards the ending block in // either the x or y direction, to the block which contains the linedef. { typedef struct { int n, nalloc, *list; } bmap_t; // blocklist structure unsigned tot = bmapwidth * bmapheight; // size of blockmap bmap_t *bmap = calloc(sizeof *bmap, tot); // array of blocklists int x, y, adx, ady, bend; for (i=0; i < numlines; i++) { int dx, dy, diff, b; // starting coordinates x = (lines[i].v1->x >> FRACBITS) - minx; y = (lines[i].v1->y >> FRACBITS) - miny; // x-y deltas adx = lines[i].dx >> FRACBITS, dx = adx < 0 ? -1 : 1; ady = lines[i].dy >> FRACBITS, dy = ady < 0 ? -1 : 1; // difference in preferring to move across y (>0) instead of x (<0) diff = !adx ? 1 : !ady ? -1 : (((x >> MAPBTOFRAC) << MAPBTOFRAC) + (dx > 0 ? MAPBLOCKUNITS-1 : 0) - x) * (ady = abs(ady)) * dx - (((y >> MAPBTOFRAC) << MAPBTOFRAC) + (dy > 0 ? MAPBLOCKUNITS-1 : 0) - y) * (adx = abs(adx)) * dy; // starting block, and pointer to its blocklist structure b = (y >> MAPBTOFRAC)*bmapwidth + (x >> MAPBTOFRAC); // ending block bend = (((lines[i].v2->y >> FRACBITS) - miny) >> MAPBTOFRAC) * bmapwidth + (((lines[i].v2->x >> FRACBITS) - minx) >> MAPBTOFRAC); // delta for pointer when moving across y dy *= bmapwidth; // deltas for diff inside the loop adx <<= MAPBTOFRAC; ady <<= MAPBTOFRAC; // Now we simply iterate block-by-block until we reach the end block. while ((unsigned) b < tot) // failsafe -- should ALWAYS be true { // Increase size of allocated list if necessary if (bmap[b].n >= bmap[b].nalloc) bmap[b].list = I_Realloc(bmap[b].list, (bmap[b].nalloc = bmap[b].nalloc ? bmap[b].nalloc*2 : 8)*sizeof*bmap->list); // Add linedef to end of list bmap[b].list[bmap[b].n++] = i; // If we have reached the last block, exit if (b == bend) break; // Move in either the x or y direction to the next block if (diff < 0) diff += ady, b += dx; else diff -= adx, b += dy; } } // Compute the total size of the blockmap. // // Compression of empty blocks is performed by reserving two offset words // at tot and tot+1. // // 4 words, unused if this routine is called, are reserved at the start. { int count = tot+6; // we need at least 1 word per block, plus reserved's for (i = 0; i < tot; i++) if (bmap[i].n) count += bmap[i].n + 2; // 1 header word + 1 trailer word + blocklist // Allocate blockmap lump with computed count blockmaplump = Z_Malloc(sizeof(*blockmaplump) * count, PU_LEVEL, 0); } // Now compress the blockmap. { int ndx = tot += 4; // Advance index to start of linedef lists bmap_t *bp = bmap; // Start of uncompressed blockmap blockmaplump[ndx++] = 0; // Store an empty blockmap list at start blockmaplump[ndx++] = -1; // (Used for compression) for (i = 4; i < tot; i++, bp++) if (bp->n) // Non-empty blocklist { blockmaplump[blockmaplump[i] = ndx++] = 0; // Store index & header do blockmaplump[ndx++] = bp->list[--bp->n]; // Copy linedef list while (bp->n); blockmaplump[ndx++] = -1; // Store trailer free(bp->list); // Free linedef list } else // Empty blocklist: point to reserved empty blocklist blockmaplump[i] = tot; free(bmap); // Free uncompressed blockmap } } // [crispy] copied over from P_LoadBlockMap() { int count = sizeof(*blocklinks) * bmapwidth * bmapheight; blocklinks = Z_Malloc(count, PU_LEVEL, 0); memset(blocklinks, 0, count); blockmap = blockmaplump+4; } fprintf(stderr, "+BLOCKMAP)\n"); } crispy-doom-crispy-doom-5.6.4/src/doom/p_ceilng.c000066400000000000000000000136451360717211000217160ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Ceiling aninmation (lowering, crushing, raising) // #include "z_zone.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // // CEILINGS // ceiling_t* activeceilings[MAXCEILINGS]; // // T_MoveCeiling // void T_MoveCeiling (ceiling_t* ceiling) { result_e res; switch(ceiling->direction) { case 0: // IN STASIS break; case 1: // UP res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false,1,ceiling->direction); if (!(leveltime&7)) { switch(ceiling->type) { case silentCrushAndRaise: break; default: S_StartSound(&ceiling->sector->soundorg, sfx_stnmov); // ? break; } } if (res == pastdest) { switch(ceiling->type) { case raiseToHighest: P_RemoveActiveCeiling(ceiling); break; case silentCrushAndRaise: S_StartSound(&ceiling->sector->soundorg, sfx_pstop); case fastCrushAndRaise: case crushAndRaise: ceiling->direction = -1; break; default: break; } } break; case -1: // DOWN res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush,1,ceiling->direction); if (!(leveltime&7)) { switch(ceiling->type) { case silentCrushAndRaise: break; default: S_StartSound(&ceiling->sector->soundorg, sfx_stnmov); } } if (res == pastdest) { switch(ceiling->type) { case silentCrushAndRaise: S_StartSound(&ceiling->sector->soundorg, sfx_pstop); case crushAndRaise: ceiling->speed = CEILSPEED; case fastCrushAndRaise: ceiling->direction = 1; break; case lowerAndCrush: case lowerToFloor: P_RemoveActiveCeiling(ceiling); break; default: break; } } else // ( res != pastdest ) { if (res == crushed) { switch(ceiling->type) { case silentCrushAndRaise: case crushAndRaise: case lowerAndCrush: ceiling->speed = CEILSPEED / 8; break; default: break; } } } break; } } // // EV_DoCeiling // Move a ceiling up/down and all around! // int EV_DoCeiling ( line_t* line, ceiling_e type ) { int secnum; int rtn; sector_t* sec; ceiling_t* ceiling; secnum = -1; rtn = 0; // Reactivate in-stasis ceilings...for certain types. switch(type) { case fastCrushAndRaise: case silentCrushAndRaise: case crushAndRaise: P_ActivateInStasisCeiling(line); default: break; } while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // new door thinker rtn = 1; ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); P_AddThinker (&ceiling->thinker); sec->specialdata = ceiling; ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; ceiling->sector = sec; ceiling->crush = false; switch(type) { case fastCrushAndRaise: ceiling->crush = true; ceiling->topheight = sec->ceilingheight; ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); ceiling->direction = -1; ceiling->speed = CEILSPEED * 2; break; case silentCrushAndRaise: case crushAndRaise: ceiling->crush = true; ceiling->topheight = sec->ceilingheight; case lowerAndCrush: case lowerToFloor: ceiling->bottomheight = sec->floorheight; if (type != lowerToFloor) ceiling->bottomheight += 8*FRACUNIT; ceiling->direction = -1; ceiling->speed = CEILSPEED; break; case raiseToHighest: ceiling->topheight = P_FindHighestCeilingSurrounding(sec); ceiling->direction = 1; ceiling->speed = CEILSPEED; break; } ceiling->tag = sec->tag; ceiling->type = type; P_AddActiveCeiling(ceiling); } return rtn; } // // Add an active ceiling // void P_AddActiveCeiling(ceiling_t* c) { int i; for (i = 0; i < MAXCEILINGS;i++) { if (activeceilings[i] == NULL) { activeceilings[i] = c; return; } } } // // Remove a ceiling's thinker // void P_RemoveActiveCeiling(ceiling_t* c) { int i; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] == c) { activeceilings[i]->sector->specialdata = NULL; P_RemoveThinker (&activeceilings[i]->thinker); activeceilings[i] = NULL; break; } } } // // Restart a ceiling that's in-stasis // void P_ActivateInStasisCeiling(line_t* line) { int i; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction == 0)) { activeceilings[i]->direction = activeceilings[i]->olddirection; activeceilings[i]->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; } } } // // EV_CeilingCrushStop // Stop a ceiling from crushing! // int EV_CeilingCrushStop(line_t *line) { int i; int rtn; rtn = 0; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction != 0)) { activeceilings[i]->olddirection = activeceilings[i]->direction; activeceilings[i]->thinker.function.acv = (actionf_v)NULL; activeceilings[i]->direction = 0; // in-stasis rtn = 1; } } return rtn; } crispy-doom-crispy-doom-5.6.4/src/doom/p_doors.c000066400000000000000000000445461360717211000216070ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Door animation code (opening/closing) // #include "z_zone.h" #include "doomdef.h" #include "deh_main.h" #include "p_local.h" #include "i_system.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "dstrings.h" #include "sounds.h" #if 0 // // Sliding door frame information // slidename_t slideFrameNames[MAXSLIDEDOORS] = { {"GDOORF1","GDOORF2","GDOORF3","GDOORF4", // front "GDOORB1","GDOORB2","GDOORB3","GDOORB4"}, // back {"\0","\0","\0","\0"} }; #endif // // VERTICAL DOORS // // // T_VerticalDoor // void T_VerticalDoor (vldoor_t* door) { result_e res; switch(door->direction) { case 0: // WAITING if (!--door->topcountdown) { switch(door->type) { case vld_blazeRaise: door->direction = -1; // time to go back down S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_normal: door->direction = -1; // time to go back down S_StartSound(&door->sector->soundorg, sfx_dorcls); break; case vld_close30ThenOpen: door->direction = 1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } } break; case 2: // INITIAL WAIT if (!--door->topcountdown) { switch(door->type) { case vld_raiseIn5Mins: door->direction = 1; door->type = vld_normal; S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } } break; case -1: // DOWN res = T_MovePlane(door->sector, door->speed, door->sector->floorheight, false,1,door->direction); if (res == pastdest) { switch(door->type) { case vld_blazeRaise: case vld_blazeClose: door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free // [crispy] fix "fast doors make two closing sounds" if (!crispy->soundfix) S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_normal: case vld_close: door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free break; case vld_close30ThenOpen: door->direction = 0; door->topcountdown = TICRATE*30; break; default: break; } } else if (res == crushed) { switch(door->type) { case vld_blazeClose: case vld_close: // DO NOT GO BACK UP! break; // [crispy] fix "fast doors reopening with wrong sound" case vld_blazeRaise: if (crispy->soundfix) { door->direction = 1; S_StartSound(&door->sector->soundorg, sfx_bdopn); break; } default: door->direction = 1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; } } break; case 1: // UP res = T_MovePlane(door->sector, door->speed, door->topheight, false,1,door->direction); if (res == pastdest) { switch(door->type) { case vld_blazeRaise: case vld_normal: door->direction = 0; // wait at top door->topcountdown = door->topwait; break; case vld_close30ThenOpen: case vld_blazeOpen: case vld_open: door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free break; default: break; } } break; } } // // EV_DoLockedDoor // Move a locked door up/down // int EV_DoLockedDoor ( line_t* line, vldoor_e type, mobj_t* thing ) { player_t* p; p = thing->player; if (!p) return 0; switch(line->special) { case 99: // Blue Lock case 133: if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) { p->message = DEH_String(PD_BLUEO); S_StartSound(crispy->soundfix ? p->mo : NULL,sfx_oof); // [crispy] blinking key or skull in the status bar p->tryopen[it_bluecard] = KEYBLINKTICS; return 0; } break; case 134: // Red Lock case 135: if (!p->cards[it_redcard] && !p->cards[it_redskull]) { p->message = DEH_String(PD_REDO); S_StartSound(crispy->soundfix ? p->mo : NULL,sfx_oof); // [crispy] blinking key or skull in the status bar p->tryopen[it_redcard] = KEYBLINKTICS; return 0; } break; case 136: // Yellow Lock case 137: if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) { p->message = DEH_String(PD_YELLOWO); S_StartSound(crispy->soundfix ? p->mo : NULL,sfx_oof); // [crispy] blinking key or skull in the status bar p->tryopen[it_yellowcard] = KEYBLINKTICS; return 0; } break; } return EV_DoDoor(line,type); } int EV_DoDoor ( line_t* line, vldoor_e type ) { int secnum,rtn; sector_t* sec; vldoor_t* door; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // new door thinker rtn = 1; door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->thinker.function.acp1 = (actionf_p1) T_VerticalDoor; door->sector = sec; door->type = type; door->topwait = VDOORWAIT; door->speed = VDOORSPEED; switch(type) { case vld_blazeClose: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; door->speed = VDOORSPEED * 4; // [crispy] fix door-closing sound playing, even when door is already closed (repeatable walkover trigger) if (door->sector->ceilingheight - door->sector->floorheight > 0 || !crispy->soundfix) S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_close: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; // [crispy] fix door-closing sound playing, even when door is already closed (repeatable walkover trigger) if (door->sector->ceilingheight - door->sector->floorheight > 0 || !crispy->soundfix) S_StartSound(&door->sector->soundorg, sfx_dorcls); break; case vld_close30ThenOpen: door->topheight = sec->ceilingheight; door->direction = -1; // [crispy] fix door-closing sound playing, even when door is already closed (repeatable walkover trigger) if (door->sector->ceilingheight - door->sector->floorheight > 0 || !crispy->soundfix) S_StartSound(&door->sector->soundorg, sfx_dorcls); break; case vld_blazeRaise: case vld_blazeOpen: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->speed = VDOORSPEED * 4; if (door->topheight != sec->ceilingheight) S_StartSound(&door->sector->soundorg, sfx_bdopn); break; case vld_normal: case vld_open: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; if (door->topheight != sec->ceilingheight) S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } } return rtn; } // // EV_VerticalDoor : open a door manually, no tag value // void EV_VerticalDoor ( line_t* line, mobj_t* thing ) { player_t* player; sector_t* sec; vldoor_t* door; int side; side = 0; // only front sides can be used // Check for locks player = thing->player; switch(line->special) { case 26: // Blue Lock case 32: if ( !player ) return; if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) { player->message = DEH_String(PD_BLUEK); S_StartSound(crispy->soundfix ? player->mo : NULL,sfx_oof); // [crispy] blinking key or skull in the status bar player->tryopen[it_bluecard] = KEYBLINKTICS; return; } break; case 27: // Yellow Lock case 34: if ( !player ) return; if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) { player->message = DEH_String(PD_YELLOWK); S_StartSound(crispy->soundfix ? player->mo : NULL,sfx_oof); // [crispy] blinking key or skull in the status bar player->tryopen[it_yellowcard] = KEYBLINKTICS; return; } break; case 28: // Red Lock case 33: if ( !player ) return; if (!player->cards[it_redcard] && !player->cards[it_redskull]) { player->message = DEH_String(PD_REDK); S_StartSound(crispy->soundfix ? player->mo : NULL,sfx_oof); // [crispy] blinking key or skull in the status bar player->tryopen[it_redcard] = KEYBLINKTICS; return; } break; } // if the sector has an active thinker, use it if (line->sidenum[side^1] == NO_INDEX) { // [crispy] do not crash if the wrong side of the door is pushed fprintf(stderr, "EV_VerticalDoor: DR special type on 1-sided linedef\n"); return; } sec = sides[ line->sidenum[side^1]] .sector; if (sec->specialdata) { door = sec->specialdata; switch(line->special) { case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s case 26: case 27: case 28: case 117: if (door->direction == -1) { door->direction = 1; // go back up // [crispy] play sound effect when the door is opened again while going down if (crispy->soundfix && door->thinker.function.acp1 == (actionf_p1) T_VerticalDoor) S_StartSound(&door->sector->soundorg, line->special == 117 ? sfx_bdopn : sfx_doropn); } else { if (!thing->player) return; // JDC: bad guys never close doors // When is a door not a door? // In Vanilla, door->direction is set, even though // "specialdata" might not actually point at a door. if (door->thinker.function.acp1 == (actionf_p1) T_VerticalDoor) { door->direction = -1; // start going down immediately // [crispy] play sound effect when the door is closed manually if (crispy->soundfix) S_StartSound(&door->sector->soundorg, line->special == 117 ? sfx_bdcls : sfx_dorcls); } else if (door->thinker.function.acp1 == (actionf_p1) T_PlatRaise) { // Erm, this is a plat, not a door. // This notably causes a problem in ep1-0500.lmp where // a plat and a door are cross-referenced; the door // doesn't open on 64-bit. // The direction field in vldoor_t corresponds to the wait // field in plat_t. Let's set that to -1 instead. plat_t *plat; plat = (plat_t *) door; plat->wait = -1; } else { // This isn't a door OR a plat. Now we're in trouble. fprintf(stderr, "EV_VerticalDoor: Tried to close " "something that wasn't a door.\n"); // Try closing it anyway. At least it will work on 32-bit // machines. door->direction = -1; } } return; } } // for proper sound switch(line->special) { case 117: // BLAZING DOOR RAISE case 118: // BLAZING DOOR OPEN S_StartSound(&sec->soundorg,sfx_bdopn); break; case 1: // NORMAL DOOR SOUND case 31: S_StartSound(&sec->soundorg,sfx_doropn); break; default: // LOCKED DOOR SOUND S_StartSound(&sec->soundorg,sfx_doropn); break; } // new door thinker door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->thinker.function.acp1 = (actionf_p1) T_VerticalDoor; door->sector = sec; door->direction = 1; door->speed = VDOORSPEED; door->topwait = VDOORWAIT; switch(line->special) { case 1: case 26: case 27: case 28: door->type = vld_normal; break; case 31: case 32: case 33: case 34: door->type = vld_open; line->special = 0; break; case 117: // blazing door raise door->type = vld_blazeRaise; door->speed = VDOORSPEED*4; break; case 118: // blazing door open door->type = vld_blazeOpen; line->special = 0; door->speed = VDOORSPEED*4; break; } // find the top and bottom of the movement range door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; } // // Spawn a door that closes after 30 seconds // void P_SpawnDoorCloseIn30 (sector_t* sec) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; door->sector = sec; door->direction = 0; door->type = vld_normal; door->speed = VDOORSPEED; door->topcountdown = 30 * TICRATE; } // // Spawn a door that opens after 5 minutes // void P_SpawnDoorRaiseIn5Mins ( sector_t* sec, int secnum ) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; door->sector = sec; door->direction = 2; door->type = vld_raiseIn5Mins; door->speed = VDOORSPEED; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->topwait = VDOORWAIT; door->topcountdown = 5 * 60 * TICRATE; } // UNUSED // Separate into p_slidoor.c? #if 0 // ABANDONED TO THE MISTS OF TIME!!! // // EV_SlidingDoor : slide a door horizontally // (animate midtexture, then set noblocking line) // slideframe_t slideFrames[MAXSLIDEDOORS]; void P_InitSlidingDoorFrames(void) { int i; int f1; int f2; int f3; int f4; // DOOM II ONLY... if ( gamemode != commercial) return; for (i = 0;i < MAXSLIDEDOORS; i++) { if (!slideFrameNames[i].frontFrame1[0]) break; f1 = R_TextureNumForName(slideFrameNames[i].frontFrame1); f2 = R_TextureNumForName(slideFrameNames[i].frontFrame2); f3 = R_TextureNumForName(slideFrameNames[i].frontFrame3); f4 = R_TextureNumForName(slideFrameNames[i].frontFrame4); slideFrames[i].frontFrames[0] = f1; slideFrames[i].frontFrames[1] = f2; slideFrames[i].frontFrames[2] = f3; slideFrames[i].frontFrames[3] = f4; f1 = R_TextureNumForName(slideFrameNames[i].backFrame1); f2 = R_TextureNumForName(slideFrameNames[i].backFrame2); f3 = R_TextureNumForName(slideFrameNames[i].backFrame3); f4 = R_TextureNumForName(slideFrameNames[i].backFrame4); slideFrames[i].backFrames[0] = f1; slideFrames[i].backFrames[1] = f2; slideFrames[i].backFrames[2] = f3; slideFrames[i].backFrames[3] = f4; } } // // Return index into "slideFrames" array // for which door type to use // int P_FindSlidingDoorType(line_t* line) { int i; int val; for (i = 0;i < MAXSLIDEDOORS;i++) { val = sides[line->sidenum[0]].midtexture; if (val == slideFrames[i].frontFrames[0]) return i; } return -1; } void T_SlidingDoor (slidedoor_t* door) { switch(door->status) { case sd_opening: if (!door->timer--) { if (++door->frame == SNUMFRAMES) { // IF DOOR IS DONE OPENING... sides[door->line->sidenum[0]].midtexture = 0; sides[door->line->sidenum[1]].midtexture = 0; door->line->flags &= ML_BLOCKING^0xff; if (door->type == sdt_openOnly) { door->frontsector->specialdata = NULL; P_RemoveThinker (&door->thinker); break; } door->timer = SDOORWAIT; door->status = sd_waiting; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door->timer = SWAITTICS; sides[door->line->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex]. frontFrames[door->frame]; sides[door->line->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex]. backFrames[door->frame]; } } break; case sd_waiting: // IF DOOR IS DONE WAITING... if (!door->timer--) { // CAN DOOR CLOSE? if (door->frontsector->thinglist != NULL || door->backsector->thinglist != NULL) { door->timer = SDOORWAIT; break; } //door->frame = SNUMFRAMES-1; door->status = sd_closing; door->timer = SWAITTICS; } break; case sd_closing: if (!door->timer--) { if (--door->frame < 0) { // IF DOOR IS DONE CLOSING... door->line->flags |= ML_BLOCKING; door->frontsector->specialdata = NULL; P_RemoveThinker (&door->thinker); break; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door->timer = SWAITTICS; sides[door->line->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex]. frontFrames[door->frame]; sides[door->line->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex]. backFrames[door->frame]; } } break; } } void EV_SlidingDoor ( line_t* line, mobj_t* thing ) { sector_t* sec; slidedoor_t* door; // DOOM II ONLY... if (gamemode != commercial) return; // Make sure door isn't already being animated sec = line->frontsector; door = NULL; if (sec->specialdata) { if (!thing->player) return; door = sec->specialdata; if (door->type == sdt_openAndClose) { if (door->status == sd_waiting) door->status = sd_closing; } else return; } // Init sliding door vars if (!door) { door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->type = sdt_openAndClose; door->status = sd_opening; door->whichDoorIndex = P_FindSlidingDoorType(line); if (door->whichDoorIndex < 0) I_Error("EV_SlidingDoor: Can't use texture for sliding door!"); door->frontsector = sec; door->backsector = line->backsector; door->thinker.function = T_SlidingDoor; door->timer = SWAITTICS; door->frame = 0; door->line = line; } } #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_enemy.c000066400000000000000000001213051360717211000215630ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Enemy thinking, AI. // Action Pointer Functions // that are associated with states/frames. // #include #include #include "m_random.h" #include "i_system.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "g_game.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" typedef enum { DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_NODIR, NUMDIRS } dirtype_t; // // P_NewChaseDir related LUT. // dirtype_t opposite[] = { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR }; dirtype_t diags[] = { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST }; void A_Fall (mobj_t *actor); // // 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. // mobj_t* soundtarget; void P_RecursiveSound ( sector_t* sec, int soundblocks ) { int i; line_t* check; sector_t* other; // wake up all monsters in this sector if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1) { return; // already flooded } sec->validcount = validcount; sec->soundtraversed = soundblocks+1; sec->soundtarget = soundtarget; for (i=0 ;ilinecount ; i++) { check = sec->lines[i]; if (! (check->flags & ML_TWOSIDED) ) continue; P_LineOpening (check); if (openrange <= 0) continue; // closed door if ( sides[ check->sidenum[0] ].sector == sec) other = sides[ check->sidenum[1] ] .sector; else other = sides[ check->sidenum[0] ].sector; if (check->flags & ML_SOUNDBLOCK) { if (!soundblocks) P_RecursiveSound (other, 1); } else P_RecursiveSound (other, soundblocks); } } // // P_NoiseAlert // If a monster yells at a player, // it will alert other monsters to the player. // void P_NoiseAlert ( mobj_t* target, mobj_t* emmiter ) { // [crispy] monsters are deaf with NOTARGET cheat if (target && target->player && (target->player->cheats & CF_NOTARGET)) return; soundtarget = target; validcount++; P_RecursiveSound (emmiter->subsector->sector, 0); } // // P_CheckMeleeRange // boolean P_CheckMeleeRange (mobj_t* actor) { mobj_t* pl; fixed_t dist; fixed_t range; if (!actor->target) return false; pl = actor->target; dist = P_AproxDistance (pl->x-actor->x, pl->y-actor->y); if (gameversion <= exe_doom_1_2) range = MELEERANGE; else range = MELEERANGE-20*FRACUNIT+pl->info->radius; if (dist >= range) return false; if (! P_CheckSight (actor, actor->target) ) return false; // [crispy] height check for melee attacks if (critical->overunder && pl->player) { if (pl->z >= actor->z + actor->height || actor->z >= pl->z + pl->height) { return false; } } return true; } // // P_CheckMissileRange // boolean P_CheckMissileRange (mobj_t* actor) { fixed_t dist; if (! P_CheckSight (actor, actor->target) ) return false; if ( actor->flags & MF_JUSTHIT ) { // the target just hit the enemy, // so fight back! actor->flags &= ~MF_JUSTHIT; return true; } if (actor->reactiontime) return false; // do not attack yet // OPTIMIZE: get this from a global checksight dist = P_AproxDistance ( actor->x-actor->target->x, actor->y-actor->target->y) - 64*FRACUNIT; if (!actor->info->meleestate) dist -= 128*FRACUNIT; // no melee attack, so fire more dist >>= FRACBITS; if (actor->type == MT_VILE) { if (dist > 14*64) return false; // too far away } if (actor->type == MT_UNDEAD) { if (dist < 196) return false; // close for fist attack dist >>= 1; } if (actor->type == MT_CYBORG || actor->type == MT_SPIDER || actor->type == MT_SKULL) { dist >>= 1; } if (dist > 200) dist = 200; if (actor->type == MT_CYBORG && dist > 160) dist = 160; if (P_Random () < dist) return false; return true; } // // P_Move // Move in the current direction, // returns false if the move is blocked. // fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; boolean P_Move (mobj_t* actor) { fixed_t tryx; fixed_t tryy; line_t* ld; // warning: 'catch', 'throw', and 'try' // are all C++ reserved words boolean try_ok; boolean good; if (actor->movedir == DI_NODIR) return false; if ((unsigned)actor->movedir >= 8) I_Error ("Weird actor->movedir!"); tryx = actor->x + actor->info->speed*xspeed[actor->movedir]; tryy = actor->y + actor->info->speed*yspeed[actor->movedir]; try_ok = P_TryMove (actor, tryx, tryy); if (!try_ok) { // open any specials if (actor->flags & MF_FLOAT && floatok) { // must adjust height if (actor->z < tmfloorz) actor->z += FLOATSPEED; else actor->z -= FLOATSPEED; actor->flags |= MF_INFLOAT; return true; } if (!numspechit) return false; actor->movedir = DI_NODIR; good = false; while (numspechit--) { ld = spechit[numspechit]; // if the special is not a door // that can be opened, // return false if (P_UseSpecialLine (actor, ld,0)) good = true; } return good; } else { actor->flags &= ~MF_INFLOAT; } if (! (actor->flags & MF_FLOAT) ) actor->z = actor->floorz; return true; } // // TryWalk // Attempts to move actor on // in its current (ob->moveangle) direction. // If blocked by either a wall or an actor // returns FALSE // If move is either clear or blocked only by a door, // returns TRUE and sets... // If a door is in the way, // an OpenDoor call is made to start it opening. // boolean P_TryWalk (mobj_t* actor) { if (!P_Move (actor)) { return false; } actor->movecount = P_Random()&15; return true; } void P_NewChaseDir (mobj_t* actor) { fixed_t deltax; fixed_t deltay; dirtype_t d[3]; int tdir; dirtype_t olddir; dirtype_t turnaround; if (!actor->target) I_Error ("P_NewChaseDir: called with no target"); olddir = actor->movedir; turnaround=opposite[olddir]; deltax = actor->target->x - actor->x; deltay = actor->target->y - actor->y; if (deltax>10*FRACUNIT) d[1]= DI_EAST; else if (deltax<-10*FRACUNIT) d[1]= DI_WEST; else d[1]=DI_NODIR; if (deltay<-10*FRACUNIT) d[2]= DI_SOUTH; else if (deltay>10*FRACUNIT) d[2]= DI_NORTH; else d[2]=DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { actor->movedir = diags[((deltay<0)<<1)+(deltax>0)]; if (actor->movedir != (int) turnaround && P_TryWalk(actor)) return; } // try other directions if (P_Random() > 200 || abs(deltay)>abs(deltax)) { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } if (d[1]==turnaround) d[1]=DI_NODIR; if (d[2]==turnaround) d[2]=DI_NODIR; if (d[1]!=DI_NODIR) { actor->movedir = d[1]; if (P_TryWalk(actor)) { // either moved forward or attacked return; } } if (d[2]!=DI_NODIR) { actor->movedir =d[2]; if (P_TryWalk(actor)) return; } // there is no direct path to the player, // so pick another direction. if (olddir!=DI_NODIR) { actor->movedir =olddir; if (P_TryWalk(actor)) return; } // randomly determine direction of search if (P_Random()&1) { for ( tdir=DI_EAST; tdir<=DI_SOUTHEAST; tdir++ ) { if (tdir != (int) turnaround) { actor->movedir =tdir; if ( P_TryWalk(actor) ) return; } } } else { for ( tdir=DI_SOUTHEAST; tdir != (DI_EAST-1); tdir-- ) { if (tdir != (int) turnaround) { actor->movedir = tdir; if ( P_TryWalk(actor) ) return; } } } if (turnaround != DI_NODIR) { actor->movedir =turnaround; if ( P_TryWalk(actor) ) return; } actor->movedir = DI_NODIR; // can not move } // // P_LookForPlayers // If allaround is false, only look 180 degrees in front. // Returns true if a player is targeted. // boolean P_LookForPlayers ( mobj_t* actor, boolean allaround ) { int c; int stop; player_t* player; angle_t an; fixed_t dist; c = 0; stop = (actor->lastlook-1)&3; for ( ; ; actor->lastlook = (actor->lastlook+1)&3 ) { if (!playeringame[actor->lastlook]) continue; if (c++ == 2 || actor->lastlook == stop) { // done looking return false; } player = &players[actor->lastlook]; // [crispy] monsters don't look for players with NOTARGET cheat if (player->cheats & CF_NOTARGET) continue; if (player->health <= 0) continue; // dead if (!P_CheckSight (actor, player->mo)) continue; // out of sight if (!allaround) { an = R_PointToAngle2 (actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; if (an > ANG90 && an < ANG270) { dist = P_AproxDistance (player->mo->x - actor->x, player->mo->y - actor->y); // if real close, react anyway if (dist > MELEERANGE) continue; // behind back } } actor->target = player->mo; return true; } return false; } // // A_KeenDie // DOOM II special, map 32. // Uses special tag 666. // void A_KeenDie (mobj_t* mo) { thinker_t* th; mobj_t* mo2; line_t junk; A_Fall (mo); // scan the remaining thinkers // to see if all Keens are dead for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 != (actionf_p1)P_MobjThinker) continue; mo2 = (mobj_t *)th; if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) { // other Keen not dead return; } } junk.tag = 666; EV_DoDoor(&junk, vld_open); } // // ACTION ROUTINES // // // A_Look // Stay in state until a player is sighted. // void A_Look (mobj_t* actor) { mobj_t* targ; actor->threshold = 0; // any shot will wake up targ = actor->subsector->sector->soundtarget; // [crispy] monsters don't look for players with NOTARGET cheat if (targ && targ->player && (targ->player->cheats & CF_NOTARGET)) return; if (targ && (targ->flags & MF_SHOOTABLE) ) { actor->target = targ; if ( actor->flags & MF_AMBUSH ) { if (P_CheckSight (actor, actor->target)) goto seeyou; } else goto seeyou; } if (!P_LookForPlayers (actor, false) ) return; // go into chase state seeyou: if (actor->info->seesound) { int sound; switch (actor->info->seesound) { case sfx_posit1: case sfx_posit2: case sfx_posit3: sound = sfx_posit1+P_Random()%3; break; case sfx_bgsit1: case sfx_bgsit2: sound = sfx_bgsit1+P_Random()%2; break; default: sound = actor->info->seesound; break; } if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) { // full volume // [crispy] prevent from adding up volume crispy->soundfull ? S_StartSoundOnce (NULL, sound) : S_StartSound (NULL, sound); } else S_StartSound (actor, sound); // [crispy] make seesounds uninterruptible if (crispy->soundfull) { S_UnlinkSound(actor); } } P_SetMobjState (actor, actor->info->seestate); } // // A_Chase // Actor has a melee attack, // so it tries to close as fast as possible // void A_Chase (mobj_t* actor) { int delta; if (actor->reactiontime) actor->reactiontime--; // modify target threshold if (actor->threshold) { if (gameversion > exe_doom_1_2 && (!actor->target || actor->target->health <= 0)) { actor->threshold = 0; } else actor->threshold--; } // turn towards movement direction if not there yet if (actor->movedir < 8) { actor->angle &= (7<<29); delta = actor->angle - (actor->movedir << 29); if (delta > 0) actor->angle -= ANG90/2; else if (delta < 0) actor->angle += ANG90/2; } if (!actor->target || !(actor->target->flags&MF_SHOOTABLE)) { // look for a new target if (P_LookForPlayers(actor,true)) return; // got a new target P_SetMobjState (actor, actor->info->spawnstate); return; } // do not attack twice in a row if (actor->flags & MF_JUSTATTACKED) { actor->flags &= ~MF_JUSTATTACKED; if (gameskill != sk_nightmare && !fastparm) P_NewChaseDir (actor); return; } // check for melee attack if (actor->info->meleestate && P_CheckMeleeRange (actor)) { if (actor->info->attacksound) S_StartSound (actor, actor->info->attacksound); P_SetMobjState (actor, actor->info->meleestate); return; } // check for missile attack if (actor->info->missilestate) { if (gameskill < sk_nightmare && !fastparm && actor->movecount) { goto nomissile; } if (!P_CheckMissileRange (actor)) goto nomissile; P_SetMobjState (actor, actor->info->missilestate); actor->flags |= MF_JUSTATTACKED; return; } // ? nomissile: // possibly choose another target if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) ) { if (P_LookForPlayers(actor,true)) return; // got a new target } // chase towards player if (--actor->movecount<0 || !P_Move (actor)) { P_NewChaseDir (actor); } // make active sound if (actor->info->activesound && P_Random () < 3) { S_StartSound (actor, actor->info->activesound); } } // // A_FaceTarget // void A_FaceTarget (mobj_t* actor) { if (!actor->target) return; actor->flags &= ~MF_AMBUSH; actor->angle = R_PointToAngle2 (actor->x, actor->y, actor->target->x, actor->target->y); if (actor->target->flags & MF_SHADOW) actor->angle += P_SubRandom() << 21; } // // A_PosAttack // void A_PosAttack (mobj_t* actor) { int angle; int damage; int slope; if (!actor->target) return; A_FaceTarget (actor); angle = actor->angle; slope = P_AimLineAttack (actor, angle, MISSILERANGE); S_StartSound (actor, sfx_pistol); angle += P_SubRandom() << 20; damage = ((P_Random()%5)+1)*3; P_LineAttack (actor, angle, MISSILERANGE, slope, damage); } void A_SPosAttack (mobj_t* actor) { int i; int angle; int bangle; int damage; int slope; if (!actor->target) return; S_StartSound (actor, sfx_shotgn); A_FaceTarget (actor); bangle = actor->angle; slope = P_AimLineAttack (actor, bangle, MISSILERANGE); for (i=0 ; i<3 ; i++) { angle = bangle + (P_SubRandom() << 20); damage = ((P_Random()%5)+1)*3; P_LineAttack (actor, angle, MISSILERANGE, slope, damage); } } void A_CPosAttack (mobj_t* actor) { int angle; int bangle; int damage; int slope; if (!actor->target) return; S_StartSound (actor, sfx_shotgn); A_FaceTarget (actor); bangle = actor->angle; slope = P_AimLineAttack (actor, bangle, MISSILERANGE); angle = bangle + (P_SubRandom() << 20); damage = ((P_Random()%5)+1)*3; P_LineAttack (actor, angle, MISSILERANGE, slope, damage); } void A_CPosRefire (mobj_t* actor) { // keep firing unless target got out of sight A_FaceTarget (actor); if (P_Random () < 40) return; if (!actor->target || actor->target->health <= 0 || !P_CheckSight (actor, actor->target) ) { P_SetMobjState (actor, actor->info->seestate); } } void A_SpidRefire (mobj_t* actor) { // keep firing unless target got out of sight A_FaceTarget (actor); if (P_Random () < 10) return; if (!actor->target || actor->target->health <= 0 || !P_CheckSight (actor, actor->target) ) { P_SetMobjState (actor, actor->info->seestate); } } void A_BspiAttack (mobj_t *actor) { if (!actor->target) return; A_FaceTarget (actor); // launch a missile P_SpawnMissile (actor, actor->target, MT_ARACHPLAZ); } // // A_TroopAttack // void A_TroopAttack (mobj_t* actor) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { S_StartSound (actor, sfx_claw); damage = (P_Random()%8+1)*3; P_DamageMobj (actor->target, actor, actor, damage); return; } // launch a missile P_SpawnMissile (actor, actor->target, MT_TROOPSHOT); } void A_SargAttack (mobj_t* actor) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (gameversion > exe_doom_1_2) { if (!P_CheckMeleeRange (actor)) return; } damage = ((P_Random()%10)+1)*4; if (gameversion <= exe_doom_1_2) P_LineAttack(actor, actor->angle, MELEERANGE, 0, damage); else P_DamageMobj (actor->target, actor, actor, damage); } void A_HeadAttack (mobj_t* actor) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { damage = (P_Random()%6+1)*10; P_DamageMobj (actor->target, actor, actor, damage); return; } // launch a missile P_SpawnMissile (actor, actor->target, MT_HEADSHOT); } void A_CyberAttack (mobj_t* actor) { if (!actor->target) return; A_FaceTarget (actor); P_SpawnMissile (actor, actor->target, MT_ROCKET); } void A_BruisAttack (mobj_t* actor) { int damage; if (!actor->target) return; // [crispy] face the enemy // A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { S_StartSound (actor, sfx_claw); damage = (P_Random()%8+1)*10; P_DamageMobj (actor->target, actor, actor, damage); return; } // launch a missile P_SpawnMissile (actor, actor->target, MT_BRUISERSHOT); } // // A_SkelMissile // void A_SkelMissile (mobj_t* actor) { mobj_t* mo; if (!actor->target) return; A_FaceTarget (actor); actor->z += 16*FRACUNIT; // so missile spawns higher mo = P_SpawnMissile (actor, actor->target, MT_TRACER); actor->z -= 16*FRACUNIT; // back to normal mo->x += mo->momx; mo->y += mo->momy; mo->tracer = actor->target; } int TRACEANGLE = 0xc000000; void A_Tracer (mobj_t* actor) { angle_t exact; fixed_t dist; fixed_t slope; mobj_t* dest; mobj_t* th; extern int demostarttic; if ((gametic - demostarttic) & 3) // [crispy] fix revenant internal demo bug return; // spawn a puff of smoke behind the rocket P_SpawnPuff (actor->x, actor->y, actor->z); th = P_SpawnMobj (actor->x-actor->momx, actor->y-actor->momy, actor->z, MT_SMOKE); th->momz = FRACUNIT; th->tics -= P_Random()&3; if (th->tics < 1) th->tics = 1; // adjust direction dest = actor->tracer; if (!dest || dest->health <= 0) return; // change angle exact = R_PointToAngle2 (actor->x, actor->y, dest->x, dest->y); if (exact != actor->angle) { if (exact - actor->angle > 0x80000000) { actor->angle -= TRACEANGLE; if (exact - actor->angle < 0x80000000) actor->angle = exact; } else { actor->angle += TRACEANGLE; if (exact - actor->angle > 0x80000000) actor->angle = exact; } } exact = actor->angle>>ANGLETOFINESHIFT; actor->momx = FixedMul (actor->info->speed, finecosine[exact]); actor->momy = FixedMul (actor->info->speed, finesine[exact]); // change slope dist = P_AproxDistance (dest->x - actor->x, dest->y - actor->y); dist = dist / actor->info->speed; if (dist < 1) dist = 1; slope = (dest->z+40*FRACUNIT - actor->z) / dist; if (slope < actor->momz) actor->momz -= FRACUNIT/8; else actor->momz += FRACUNIT/8; } void A_SkelWhoosh (mobj_t* actor) { if (!actor->target) return; A_FaceTarget (actor); S_StartSound (actor,sfx_skeswg); } void A_SkelFist (mobj_t* actor) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { damage = ((P_Random()%10)+1)*6; S_StartSound (actor, sfx_skepch); P_DamageMobj (actor->target, actor, actor, damage); } } // // PIT_VileCheck // Detect a corpse that could be raised. // mobj_t* corpsehit; mobj_t* vileobj; fixed_t viletryx; fixed_t viletryy; boolean PIT_VileCheck (mobj_t* thing) { int maxdist; boolean check; if (!(thing->flags & MF_CORPSE) ) return true; // not a monster if (thing->tics != -1) return true; // not lying still yet if (thing->info->raisestate == S_NULL) return true; // monster doesn't have a raise state maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; if ( abs(thing->x - viletryx) > maxdist || abs(thing->y - viletryy) > maxdist ) return true; // not actually touching corpsehit = thing; corpsehit->momx = corpsehit->momy = 0; corpsehit->height <<= 2; check = P_CheckPosition (corpsehit, corpsehit->x, corpsehit->y); corpsehit->height >>= 2; if (!check) return true; // doesn't fit here return false; // got one, so stop checking } // // A_VileChase // Check for ressurecting a body // void A_VileChase (mobj_t* actor) { int xl; int xh; int yl; int yh; int bx; int by; mobjinfo_t* info; mobj_t* temp; if (actor->movedir != DI_NODIR) { // check for corpses to raise viletryx = actor->x + actor->info->speed*xspeed[actor->movedir]; viletryy = actor->y + actor->info->speed*yspeed[actor->movedir]; xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT; xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT; yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT; yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT; vileobj = actor; for (bx=xl ; bx<=xh ; bx++) { for (by=yl ; by<=yh ; by++) { // Call PIT_VileCheck to check // whether object is a corpse // that canbe raised. if (!P_BlockThingsIterator(bx,by,PIT_VileCheck)) { // got one! temp = actor->target; actor->target = corpsehit; A_FaceTarget (actor); actor->target = temp; P_SetMobjState (actor, S_VILE_HEAL1); S_StartSound (corpsehit, sfx_slop); info = corpsehit->info; P_SetMobjState (corpsehit,info->raisestate); corpsehit->height <<= 2; corpsehit->flags = info->flags; corpsehit->health = info->spawnhealth; corpsehit->target = NULL; // [crispy] count resurrected monsters extrakills++; // [crispy] resurrected pools of gore ("ghost monsters") are translucent if (corpsehit->height == 0 && corpsehit->radius == 0) { corpsehit->flags |= MF_TRANSLUCENT; fprintf(stderr, "A_VileChase: Resurrected ghost monster (%d) at (%d/%d)!\n", corpsehit->type, corpsehit->x>>FRACBITS, corpsehit->y>>FRACBITS); } return; } } } } // Return to normal attack. A_Chase (actor); } // // A_VileStart // void A_VileStart (mobj_t* actor) { S_StartSound (actor, sfx_vilatk); } // // A_Fire // Keep fire in front of player unless out of sight // void A_Fire (mobj_t* actor); void A_StartFire (mobj_t* actor) { S_StartSound(actor,sfx_flamst); A_Fire(actor); } void A_FireCrackle (mobj_t* actor) { S_StartSound(actor,sfx_flame); A_Fire(actor); } void A_Fire (mobj_t* actor) { mobj_t* dest; mobj_t* target; unsigned an; 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); fog = P_SpawnMobj (actor->target->x, actor->target->x, actor->target->z, MT_FIRE); actor->tracer = fog; fog->target = actor; fog->tracer = actor->target; // [crispy] play DSFLAMST sound when Arch-Vile spawns fire attack if (crispy->soundfix && I_GetSfxLumpNum(&S_sfx[sfx_flamst]) != -1) S_StartSound(fog, sfx_flamst); 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; 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; 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; 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; } // // A_PainShootSkull // Spawn a lost soul and launch it at the target // void A_PainShootSkull ( mobj_t* actor, angle_t angle ) { fixed_t x; fixed_t y; fixed_t z; mobj_t* newmobj; angle_t an; int prestep; int count; thinker_t* currentthinker; // count total number of skull currently on the level count = 0; currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { if ( (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) && ((mobj_t *)currentthinker)->type == MT_SKULL) count++; currentthinker = currentthinker->next; } // if there are allready 20 skulls on the level, // don't spit another one if (count > 20) return; // okay, there's playe for another one an = angle >> ANGLETOFINESHIFT; prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2; x = actor->x + FixedMul (prestep, finecosine[an]); y = actor->y + FixedMul (prestep, finesine[an]); z = actor->z + 8*FRACUNIT; newmobj = P_SpawnMobj (x , y, z, MT_SKULL); // Check for movements. if (!P_TryMove (newmobj, newmobj->x, newmobj->y)) { // kill it immediately P_DamageMobj (newmobj,actor,actor,10000); return; } // [crispy] Lost Souls bleed Puffs if (crispy->coloredblood) newmobj->flags |= MF_NOBLOOD; newmobj->target = actor->target; A_SkullAttack (newmobj); } // // A_PainAttack // Spawn a lost soul and launch it at the target // void A_PainAttack (mobj_t* actor) { if (!actor->target) return; A_FaceTarget (actor); A_PainShootSkull (actor, actor->angle); } void A_PainDie (mobj_t* actor) { A_Fall (actor); A_PainShootSkull (actor, actor->angle+ANG90); A_PainShootSkull (actor, actor->angle+ANG180); A_PainShootSkull (actor, actor->angle+ANG270); } void A_Scream (mobj_t* actor) { int sound; switch (actor->info->deathsound) { case 0: return; case sfx_podth1: case sfx_podth2: case sfx_podth3: sound = sfx_podth1 + P_Random ()%3; break; case sfx_bgdth1: case sfx_bgdth2: sound = sfx_bgdth1 + P_Random ()%2; break; default: sound = actor->info->deathsound; break; } // Check for bosses. if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) { // full volume // [crispy] prevent from adding up volume crispy->soundfull ? S_StartSoundOnce (NULL, sound) : S_StartSound (NULL, sound); } else S_StartSound (actor, sound); } void A_XScream (mobj_t* actor) { S_StartSound (actor, sfx_slop); } void A_Pain (mobj_t* actor) { if (actor->info->painsound) S_StartSound (actor, actor->info->painsound); } void A_Fall (mobj_t *actor) { // actor is on ground, it can be walked over actor->flags &= ~MF_SOLID; // So change this if corpse objects // are meant to be obstacles. } // // A_Explode // void A_Explode (mobj_t* thingy) { P_RadiusAttack(thingy, thingy->target, 128); } // Check whether the death of the specified monster type is allowed // to trigger the end of episode special action. // // This behavior changed in v1.9, the most notable effect of which // was to break uac_dead.wad static boolean CheckBossEnd(mobjtype_t motype) { if (gameversion < exe_ultimate) { if (gamemap != 8) { return false; } // Baron death on later episodes is nothing special. if (motype == MT_BRUISER && gameepisode != 1) { return false; } return true; } else { // New logic that appeared in Ultimate Doom. // Looks like the logic was overhauled while adding in the // episode 4 support. Now bosses only trigger on their // specific episode. switch(gameepisode) { case 1: return gamemap == 8 && motype == MT_BRUISER; case 2: return gamemap == 8 && motype == MT_CYBORG; case 3: return gamemap == 8 && motype == MT_SPIDER; case 4: return (gamemap == 6 && motype == MT_CYBORG) || (gamemap == 8 && motype == MT_SPIDER); // [crispy] Sigil case 5: return false; default: return gamemap == 8; } } } // // A_BossDeath // Possibly trigger special effects // if on first boss level // void A_BossDeath (mobj_t* mo) { thinker_t* th; mobj_t* mo2; line_t junk; int i; if ( gamemode == commercial) { if (gamemap != 7 && // [crispy] Master Levels in PC slot 7 !(crispy->singleplayer && gamemission == pack_master && (gamemap == 14 || gamemap == 15 || gamemap == 16))) return; if ((mo->type != MT_FATSO) && (mo->type != MT_BABY)) return; } else { if (!CheckBossEnd(mo->type)) { 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 // scan the remaining thinkers to see // if all bosses are dead for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 != (actionf_p1)P_MobjThinker) continue; mo2 = (mobj_t *)th; if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) { // other boss not dead return; } } // victory! if ( gamemode == commercial) { if (gamemap == 7 || // [crispy] Master Levels in PC slot 7 (crispy->singleplayer && gamemission == pack_master && (gamemap == 14 || gamemap == 15 || gamemap == 16))) { 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, vld_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 ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->so, sfx_dbopn); // [crispy] weapon sound source } void A_LoadShotgun2 ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->so, sfx_dbload); // [crispy] weapon sound source } void A_ReFire ( mobj_t* mobj, player_t* player, pspdef_t* psp ); void A_CloseShotgun2 ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->so, sfx_dbcls); // [crispy] weapon sound source A_ReFire(NULL,player,psp); // [crispy] let pspr action pointers get called from mobj states } mobj_t** braintargets = NULL; int numbraintargets = 0; // [crispy] initialize int braintargeton = 0; static int maxbraintargets; // [crispy] remove braintargets limit void A_BrainAwake (mobj_t* mo) { thinker_t* thinker; mobj_t* m; // find all the target spots numbraintargets = 0; braintargeton = 0; thinker = thinkercap.next; for (thinker = thinkercap.next ; thinker != &thinkercap ; thinker = thinker->next) { if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) continue; // not a mobj m = (mobj_t *)thinker; if (m->type == MT_BOSSTARGET ) { // [crispy] remove braintargets limit if (numbraintargets == maxbraintargets) { maxbraintargets = maxbraintargets ? 2 * maxbraintargets : 32; braintargets = I_Realloc(braintargets, maxbraintargets * sizeof(*braintargets)); if (maxbraintargets > 32) fprintf(stderr, "R_BrainAwake: Raised braintargets limit to %d.\n", maxbraintargets); } braintargets[numbraintargets] = m; numbraintargets++; } } S_StartSound (NULL,sfx_bossit); // [crispy] prevent braintarget overflow // (e.g. in two subsequent maps featuring a brain spitter) if (braintargeton >= numbraintargets) { braintargeton = 0; } // [crispy] no spawn spots available if (numbraintargets == 0) numbraintargets = -1; } void A_BrainPain (mobj_t* mo) { // [crispy] prevent from adding up volume crispy->soundfull ? S_StartSoundOnce (NULL,sfx_bospn) : S_StartSound (NULL,sfx_bospn); } void A_BrainScream (mobj_t* mo) { int x; int y; int z; mobj_t* th; for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8) { y = mo->y - 320*FRACUNIT; z = 128 + P_Random()*2*FRACUNIT; th = P_SpawnMobj (x,y,z, MT_ROCKET); th->momz = P_Random()*512; P_SetMobjState (th, S_BRAINEXPLODE1); th->tics -= P_Random()&7; if (th->tics < 1) th->tics = 1; } S_StartSound (NULL,sfx_bosdth); } void A_BrainExplode (mobj_t* mo) { int x; int y; int z; mobj_t* th; x = mo->x + P_SubRandom() * 2048; y = mo->y; z = 128 + P_Random()*2*FRACUNIT; th = P_SpawnMobj (x,y,z, MT_ROCKET); th->momz = P_Random()*512; P_SetMobjState (th, S_BRAINEXPLODE1); th->tics -= P_Random()&7; if (th->tics < 1) th->tics = 1; // [crispy] brain explosions are translucent th->flags |= MF_TRANSLUCENT; } void A_BrainDie (mobj_t* mo) { G_ExitLevel (); } void A_BrainSpit (mobj_t* mo) { mobj_t* targ; mobj_t* newmobj; static int easy = 0; easy ^= 1; if (gameskill <= sk_easy && (!easy)) return; // [crispy] avoid division by zero by recalculating the number of spawn spots if (numbraintargets == 0) A_BrainAwake(NULL); // [crispy] still no spawn spots available if (numbraintargets == -1) return; // shoot a cube at current target targ = braintargets[braintargeton]; if (numbraintargets == 0) { I_Error("A_BrainSpit: numbraintargets was 0 (vanilla crashes here)"); } braintargeton = (braintargeton+1)%numbraintargets; // spawn brain missile newmobj = P_SpawnMissile (mo, targ, MT_SPAWNSHOT); newmobj->target = targ; newmobj->reactiontime = ((targ->y - mo->y)/newmobj->momy) / newmobj->state->tics; S_StartSound(NULL, sfx_bospit); } void A_SpawnFly (mobj_t* mo); // 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 (); // 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); // [crispy] count spawned monsters extrakills++; if (P_LookForPlayers (newmobj, true) ) P_SetMobjState (newmobj, newmobj->info->seestate); // telefrag anything in this spot P_TeleportMove (newmobj, newmobj->x, newmobj->y); // remove self (i.e., cube). P_RemoveMobj (mo); } void A_PlayerScream (mobj_t* mo) { // Default death sound. int sound = sfx_pldeth; if ( (gamemode == commercial) && (mo->health < -50)) { // IF THE PLAYER DIES // LESS THAN -50% WITHOUT GIBBING sound = sfx_pdiehi; } S_StartSound (mo, sound); } crispy-doom-crispy-doom-5.6.4/src/doom/p_extnodes.c000066400000000000000000000373541360717211000223110ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2015-2018 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] support maps with NODES in compressed or uncompressed ZDBSP // format or DeePBSP format and/or LINEDEFS and THINGS lumps in Hexen format // #include "m_bbox.h" #include "p_local.h" #include "i_swap.h" #include "i_system.h" #include "w_wad.h" #include "z_zone.h" // [crispy] support maps with compressed ZDBSP nodes #include "config.h" #ifdef HAVE_LIBZ #include #endif #include "p_extnodes.h" void P_SpawnMapThing (mapthing_t* mthing); fixed_t GetOffset(vertex_t *v1, vertex_t *v2); sector_t* GetSectorAtNullAddress(void); // [crispy] support maps with NODES in compressed or uncompressed ZDBSP // format or DeePBSP format and/or LINEDEFS and THINGS lumps in Hexen format mapformat_t P_CheckMapFormat (int lumpnum) { mapformat_t format = 0; byte *nodes = NULL; int b; if ((b = lumpnum+ML_BLOCKMAP+1) < numlumps && !strncasecmp(lumpinfo[b]->name, "BEHAVIOR", 8)) { fprintf(stderr, "Hexen ("); format |= MFMT_HEXEN; } else fprintf(stderr, "Doom ("); if (!((b = lumpnum+ML_NODES) < numlumps && (nodes = W_CacheLumpNum(b, PU_CACHE)) && W_LumpLength(b) > 0)) fprintf(stderr, "no nodes"); else if (!memcmp(nodes, "xNd4\0\0\0\0", 8)) { fprintf(stderr, "DeePBSP"); format |= MFMT_DEEPBSP; } else if (!memcmp(nodes, "XNOD", 4)) { fprintf(stderr, "ZDBSP"); format |= MFMT_ZDBSPX; } else if (!memcmp(nodes, "ZNOD", 4)) { fprintf(stderr, "compressed ZDBSP"); format |= MFMT_ZDBSPZ; } else fprintf(stderr, "BSP"); if (nodes) W_ReleaseLumpNum(b); return format; } // [crispy] support maps with DeePBSP nodes // adapted from prboom-plus/src/p_setup.c:633-752 void P_LoadSegs_DeePBSP (int lump) { int i; mapseg_deepbsp_t *data; numsegs = W_LumpLength(lump) / sizeof(mapseg_deepbsp_t); segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); data = (mapseg_deepbsp_t *)W_CacheLumpNum(lump, PU_STATIC); for (i = 0; i < numsegs; i++) { seg_t *li = segs + i; mapseg_deepbsp_t *ml = data + i; int side, linedef; line_t *ldef; li->v1 = &vertexes[ml->v1]; li->v2 = &vertexes[ml->v2]; li->angle = (SHORT(ml->angle))<offset = (SHORT(ml->offset))<linedef); ldef = &lines[linedef]; li->linedef = ldef; side = SHORT(ml->side); // 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]]; li->frontsector = sides[ldef->sidenum[side]].sector; // [crispy] recalculate li->offset = GetOffset(li->v1, (ml->side ? ldef->v2 : ldef->v1)); if (ldef->flags & ML_TWOSIDED) { int sidenum = ldef->sidenum[side ^ 1]; if (sidenum < 0 || sidenum >= numsides) { if (li->sidedef->midtexture) { li->backsector = 0; fprintf(stderr, "P_LoadSegs: Linedef %d has two-sided flag set, but no second sidedef\n", i); } else li->backsector = GetSectorAtNullAddress(); } else li->backsector = sides[sidenum].sector; } else li->backsector = 0; } W_ReleaseLumpNum(lump); } // [crispy] support maps with DeePBSP nodes // adapted from prboom-plus/src/p_setup.c:843-863 void P_LoadSubsectors_DeePBSP (int lump) { mapsubsector_deepbsp_t *data; int i; numsubsectors = W_LumpLength(lump) / sizeof(mapsubsector_deepbsp_t); subsectors = Z_Malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); data = (mapsubsector_deepbsp_t *)W_CacheLumpNum(lump, PU_STATIC); // [crispy] fail on missing subsectors if (!data || !numsubsectors) I_Error("P_LoadSubsectors: No subsectors in map!"); for (i = 0; i < numsubsectors; i++) { subsectors[i].numlines = (int)data[i].numsegs; subsectors[i].firstline = (int)data[i].firstseg; } W_ReleaseLumpNum(lump); } // [crispy] support maps with DeePBSP nodes // adapted from prboom-plus/src/p_setup.c:995-1038 void P_LoadNodes_DeePBSP (int lump) { const byte *data; int i; numnodes = (W_LumpLength (lump) - 8) / sizeof(mapnode_deepbsp_t); nodes = Z_Malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); data = W_CacheLumpNum (lump, PU_STATIC); // [crispy] warn about missing nodes if (!data || !numnodes) { if (numsubsectors == 1) fprintf(stderr, "P_LoadNodes: No nodes in map, but only one subsector.\n"); else I_Error("P_LoadNodes: No nodes in map!"); } // skip header data += 8; for (i = 0; i < numnodes; i++) { node_t *no = nodes + i; const mapnode_deepbsp_t *mn = (const mapnode_deepbsp_t *) data + i; int j; no->x = SHORT(mn->x)<y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = (unsigned int)(mn->children[j]); for (k = 0; k < 4; k++) no->bbox[j][k] = SHORT(mn->bbox[j][k])<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_LoadNodes: Error during ZDBSP 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 = I_Realloc(output, outlen); zstream->next_out = output + outlen_old; zstream->avail_out = outlen - outlen_old; } if (err != Z_STREAM_END) I_Error("P_LoadNodes: Error during ZDBSP nodes decompression!"); fprintf(stderr, "P_LoadNodes: ZDBSP nodes compression ratio %.3f\n", (float)zstream->total_out/zstream->total_in); data = output; if (inflateEnd(zstream) != Z_OK) I_Error("P_LoadNodes: Error during ZDBSP nodes decompression shut-down!"); // release the original data lump W_ReleaseLumpNum(lump); free(zstream); #else I_Error("P_LoadNodes: Compressed ZDBSP nodes are not supported!"); #endif } else { // skip header data += 4; } // 1. Load new vertices added during node building orgVerts = *((unsigned int*)data); data += sizeof(orgVerts); newVerts = *((unsigned int*)data); data += sizeof(newVerts); if (orgVerts + newVerts == (unsigned int)numvertexes) { newvertarray = vertexes; } else { newvertarray = Z_Malloc((orgVerts + newVerts) * sizeof(vertex_t), PU_LEVEL, 0); memcpy(newvertarray, vertexes, orgVerts * sizeof(vertex_t)); memset(newvertarray + orgVerts, 0, newVerts * sizeof(vertex_t)); } for (i = 0; i < newVerts; i++) { newvertarray[i + orgVerts].r_x = newvertarray[i + orgVerts].x = *((unsigned int*)data); data += sizeof(newvertarray[0].x); newvertarray[i + orgVerts].r_y = newvertarray[i + orgVerts].y = *((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; } Z_Free(vertexes); vertexes = newvertarray; numvertexes = orgVerts + newVerts; } // 2. Load subsectors numSubs = *((unsigned int*)data); data += sizeof(numSubs); if (numSubs < 1) I_Error("P_LoadNodes: No subsectors in map!"); numsubsectors = numSubs; subsectors = Z_Malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); for (i = currSeg = 0; i < numsubsectors; i++) { mapsubsector_zdbsp_t *mseg = (mapsubsector_zdbsp_t*) data + i; subsectors[i].firstline = currSeg; subsectors[i].numlines = mseg->numsegs; currSeg += mseg->numsegs; } data += numsubsectors * sizeof(mapsubsector_zdbsp_t); // 3. Load segs numSegs = *((unsigned int*)data); data += sizeof(numSegs); // The number of stored segs should match the number of segs used by subsectors if (numSegs != currSeg) { I_Error("P_LoadNodes: Incorrect number of segs in ZDBSP nodes!"); } numsegs = numSegs; segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); for (i = 0; i < numsegs; i++) { line_t *ldef; unsigned int linedef; unsigned char side; seg_t *li = segs + i; mapseg_zdbsp_t *ml = (mapseg_zdbsp_t *) data + i; li->v1 = &vertexes[ml->v1]; li->v2 = &vertexes[ml->v2]; linedef = (unsigned short)SHORT(ml->linedef); ldef = &lines[linedef]; li->linedef = ldef; side = ml->side; // 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]]; li->frontsector = sides[ldef->sidenum[side]].sector; // seg angle and offset are not included li->angle = R_PointToAngle2(segs[i].v1->x, segs[i].v1->y, segs[i].v2->x, segs[i].v2->y); li->offset = GetOffset(li->v1, (ml->side ? ldef->v2 : ldef->v1)); if (ldef->flags & ML_TWOSIDED) { int sidenum = ldef->sidenum[side ^ 1]; if (sidenum < 0 || sidenum >= numsides) { if (li->sidedef->midtexture) { li->backsector = 0; fprintf(stderr, "P_LoadSegs: Linedef %u has two-sided flag set, but no second sidedef\n", i); } else li->backsector = GetSectorAtNullAddress(); } else li->backsector = sides[sidenum].sector; } else li->backsector = 0; } data += numsegs * sizeof(mapseg_zdbsp_t); // 4. Load nodes numNodes = *((unsigned int*)data); data += sizeof(numNodes); numnodes = numNodes; nodes = Z_Malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); for (i = 0; i < numnodes; i++) { int j, k; node_t *no = nodes + i; mapnode_zdbsp_t *mn = (mapnode_zdbsp_t *) data + i; no->x = SHORT(mn->x)<y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = (unsigned int)(mn->children[j]); for (k = 0; k < 4; k++) no->bbox[j][k] = SHORT(mn->bbox[j][k])<tid); spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); // spawnthing.height = SHORT(mt->height); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); spawnthing.options = SHORT(mt->options); // spawnthing.special = mt->special; // spawnthing.arg1 = mt->arg1; // spawnthing.arg2 = mt->arg2; // spawnthing.arg3 = mt->arg3; // spawnthing.arg4 = mt->arg4; // spawnthing.arg5 = mt->arg5; P_SpawnMapThing(&spawnthing); } W_ReleaseLumpNum(lump); } // [crispy] allow loading of Hexen-format maps // adapted from chocolate-doom/src/hexen/p_setup.c:410-490 void P_LoadLineDefs_Hexen (int lump) { byte *data; int i; maplinedef_hexen_t *mld; line_t *ld; vertex_t *v1, *v2; int warn; // [crispy] warn about unknown linedef types numlines = W_LumpLength(lump) / sizeof(maplinedef_hexen_t); lines = Z_Malloc(numlines * sizeof(line_t), PU_LEVEL, 0); memset(lines, 0, numlines * sizeof(line_t)); data = W_CacheLumpNum(lump, PU_STATIC); mld = (maplinedef_hexen_t *) data; ld = lines; warn = 0; // [crispy] warn about unknown linedef types for (i = 0; i < numlines; i++, mld++, ld++) { ld->flags = (unsigned short)SHORT(mld->flags); ld->special = mld->special; // ld->arg1 = mld->arg1; // ld->arg2 = mld->arg2; // ld->arg3 = mld->arg3; // ld->arg4 = mld->arg4; // ld->arg5 = mld->arg5; // [crispy] warn about unknown linedef types if ((unsigned short) ld->special > 141) { fprintf(stderr, "P_LoadLineDefs: Unknown special %d at line %d\n", ld->special, i); warn++; } v1 = ld->v1 = &vertexes[(unsigned short)SHORT(mld->v1)]; v2 = ld->v2 = &vertexes[(unsigned short)SHORT(mld->v2)]; ld->dx = v2->x - v1->x; ld->dy = v2->y - v1->y; if (!ld->dx) ld->slopetype = ST_VERTICAL; else if (!ld->dy) ld->slopetype = ST_HORIZONTAL; else { if (FixedDiv(ld->dy, ld->dx) > 0) ld->slopetype = ST_POSITIVE; else ld->slopetype = ST_NEGATIVE; } if (v1->x < v2->x) { ld->bbox[BOXLEFT] = v1->x; ld->bbox[BOXRIGHT] = v2->x; } else { ld->bbox[BOXLEFT] = v2->x; ld->bbox[BOXRIGHT] = v1->x; } if (v1->y < v2->y) { ld->bbox[BOXBOTTOM] = v1->y; ld->bbox[BOXTOP] = v2->y; } else { ld->bbox[BOXBOTTOM] = v2->y; ld->bbox[BOXTOP] = v1->y; } // [crispy] calculate sound origin of line to be its midpoint ld->soundorg.x = ld->bbox[BOXLEFT] / 2 + ld->bbox[BOXRIGHT] / 2; ld->soundorg.y = ld->bbox[BOXTOP] / 2 + ld->bbox[BOXBOTTOM] / 2; ld->sidenum[0] = SHORT(mld->sidenum[0]); ld->sidenum[1] = SHORT(mld->sidenum[1]); // [crispy] substitute dummy sidedef for missing right side if (ld->sidenum[0] == NO_INDEX) { ld->sidenum[0] = 0; fprintf(stderr, "P_LoadLineDefs: linedef %d without first sidedef!\n", i); } if (ld->sidenum[0] != NO_INDEX) ld->frontsector = sides[ld->sidenum[0]].sector; else ld->frontsector = 0; if (ld->sidenum[1] != NO_INDEX) ld->backsector = sides[ld->sidenum[1]].sector; else ld->backsector = 0; } // [crispy] warn about unknown linedef types if (warn) { fprintf(stderr, "P_LoadLineDefs: Found %d line%s with unknown linedef type.\n" "THIS MAP MAY NOT WORK AS EXPECTED!\n", warn, (warn > 1) ? "s" : ""); } W_ReleaseLumpNum(lump); } crispy-doom-crispy-doom-5.6.4/src/doom/p_extnodes.h000066400000000000000000000025211360717211000223020ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2015-2018 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] support maps with NODES in compressed or uncompressed ZDBSP // format or DeePBSP format and/or LINEDEFS and THINGS lumps in Hexen format // #ifndef __P_EXTNODES__ #define __P_EXTNODES__ typedef enum { MFMT_DOOMBSP = 0x000, MFMT_DEEPBSP = 0x001, MFMT_ZDBSPX = 0x002, MFMT_ZDBSPZ = 0x004, MFMT_HEXEN = 0x100, } mapformat_t; extern mapformat_t P_CheckMapFormat (int lumpnum); extern void P_LoadSegs_DeePBSP (int lump); extern void P_LoadSubsectors_DeePBSP (int lump); extern void P_LoadNodes_DeePBSP (int lump); extern void P_LoadNodes_ZDBSP (int lump, boolean compressed); extern void P_LoadThings_Hexen (int lump); extern void P_LoadLineDefs_Hexen (int lump); #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_extsaveg.c000066400000000000000000000303031360717211000222710ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2016 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] Archiving: Extended SaveGame I/O. // #include #include #include "config.h" #include "doomstat.h" #include "doomtype.h" #include "m_misc.h" #include "p_extsaveg.h" #include "p_local.h" #include "p_saveg.h" #include "p_setup.h" #include "s_sound.h" #include "s_musinfo.h" #include "z_zone.h" #define MAX_LINE_LEN 260 #define MAX_STRING_LEN 80 static char *line, *string; static void P_WritePackageTarname (const char *key) { M_snprintf(line, MAX_LINE_LEN, "%s %s\n", key, PACKAGE_VERSION); fputs(line, save_stream); } // maplumpinfo->wad_file->basename char *savewadfilename = NULL; static void P_WriteWadFileName (const char *key) { M_snprintf(line, MAX_LINE_LEN, "%s %s\n", key, W_WadNameForLump(maplumpinfo)); fputs(line, save_stream); } static void P_ReadWadFileName (const char *key) { if (!savewadfilename && // [crispy] only check if loaded from the menu, // we have no chance to show a dialog otherwise startloadgame == -1) { if (sscanf(line, "%s", string) == 1 && !strncmp(string, key, MAX_STRING_LEN)) { if (sscanf(line, "%*s %s", string) == 1) { savewadfilename = strdup(string); } } } } // extrakills static void P_WriteExtraKills (const char *key) { if (extrakills) { M_snprintf(line, MAX_LINE_LEN, "%s %d\n", key, extrakills); fputs(line, save_stream); } } static void P_ReadExtraKills (const char *key) { int value; if (sscanf(line, "%s %d", string, &value) == 2 && !strncmp(string, key, MAX_STRING_LEN)) { extrakills = value; } } // totalleveltimes static void P_WriteTotalLevelTimes (const char *key) { if (totalleveltimes) { M_snprintf(line, MAX_LINE_LEN, "%s %d\n", key, totalleveltimes); fputs(line, save_stream); } } static void P_ReadTotalLevelTimes (const char *key) { int value; if (sscanf(line, "%s %d", string, &value) == 2 && !strncmp(string, key, MAX_STRING_LEN)) { totalleveltimes = value; } } // T_FireFlicker() extern void T_FireFlicker (fireflicker_t* flick); static void P_WriteFireFlicker (const char *key) { thinker_t* th; for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1)T_FireFlicker) { fireflicker_t *flick = (fireflicker_t *)th; M_snprintf(line, MAX_LINE_LEN, "%s %d %d %d %d\n", key, (int)(flick->sector - sectors), (int)flick->count, (int)flick->maxlight, (int)flick->minlight); fputs(line, save_stream); } } } static void P_ReadFireFlicker (const char *key) { int sector, count, maxlight, minlight; if (sscanf(line, "%s %d %d %d %d\n", string, §or, &count, &maxlight, &minlight) == 5 && !strncmp(string, key, MAX_STRING_LEN)) { fireflicker_t *flick; flick = Z_Malloc(sizeof(*flick), PU_LEVEL, NULL); flick->sector = §ors[sector]; flick->count = count; flick->maxlight = maxlight; flick->minlight = minlight; flick->thinker.function.acp1 = (actionf_p1)T_FireFlicker; P_AddThinker(&flick->thinker); } } // sector->soundtarget static void P_WriteSoundTarget (const char *key) { int i; sector_t *sector; for (i = 0, sector = sectors; i < numsectors; i++, sector++) { if (sector->soundtarget) { M_snprintf(line, MAX_LINE_LEN, "%s %d %d\n", key, i, P_ThinkerToIndex((thinker_t *) sector->soundtarget)); fputs(line, save_stream); } } } static void P_ReadSoundTarget (const char *key) { int sector, target; if (sscanf(line, "%s %d %d\n", string, §or, &target) == 3 && !strncmp(string, key, MAX_STRING_LEN)) { sectors[sector].soundtarget = (mobj_t *) P_IndexToThinker(target); } } // sector->oldspecial static void P_WriteOldSpecial (const char *key) { int i; sector_t *sector; for (i = 0, sector = sectors; i < numsectors; i++, sector++) { if (sector->oldspecial) { M_snprintf(line, MAX_LINE_LEN, "%s %d %d\n", key, i, sector->oldspecial); fputs(line, save_stream); } } } static void P_ReadOldSpecial (const char *key) { int sector, oldspecial; if (sscanf(line, "%s %d %d\n", string, §or, &oldspecial) == 3 && !strncmp(string, key, MAX_STRING_LEN)) { sectors[sector].oldspecial = oldspecial; } } // buttonlist[] extern void P_StartButton (line_t *line, bwhere_e w, int texture, int time); static void P_WriteButton (const char *key) { int i; for (i = 0; i < maxbuttons; i++) { button_t *button = &buttonlist[i]; if (button->btimer) { M_snprintf(line, MAX_LINE_LEN, "%s %d %d %d %d\n", key, (int)(button->line - lines), (int)button->where, (int)button->btexture, (int)button->btimer); fputs(line, save_stream); } } } static void P_ReadButton (const char *key) { int linedef, where, btexture, btimer; if (sscanf(line, "%s %d %d %d %d\n", string, &linedef, &where, &btexture, &btimer) == 5 && !strncmp(string, key, MAX_STRING_LEN)) { P_StartButton(&lines[linedef], where, btexture, btimer); } } // numbraintargets, braintargeton extern int numbraintargets, braintargeton; static void P_WriteBrainTarget (const char *key) { thinker_t *th; for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) { mobj_t *mo = (mobj_t *)th; if (mo->state == &states[S_BRAINEYE1]) { M_snprintf(line, MAX_LINE_LEN, "%s %d %d\n", key, numbraintargets, braintargeton); fputs(line, save_stream); // [crispy] return after the first brain spitter is found return; } } } } static void P_ReadBrainTarget (const char *key) { int numtargets, targeton; if (sscanf(line, "%s %d %d", string, &numtargets, &targeton) == 3 && !strncmp(string, key, MAX_STRING_LEN)) { numbraintargets = 0; // [crispy] force A_BrainAwake() braintargeton = targeton; } } // markpoints[] extern void AM_GetMarkPoints (int *n, long *p); extern void AM_SetMarkPoints (int n, long *p); static void P_WriteMarkPoints (const char *key) { int n; long p[20]; AM_GetMarkPoints(&n, p); if (p[0] != -1) { M_snprintf(line, MAX_LINE_LEN, "%s %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", key, n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15], p[16], p[17], p[18], p[19]); fputs(line, save_stream); } } static void P_ReadMarkPoints (const char *key) { int n; long p[20]; if (sscanf(line, "%s %d %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", string, &n, &p[0], &p[1], &p[2], &p[3], &p[4], &p[5], &p[6], &p[7], &p[8], &p[9], &p[10], &p[11], &p[12], &p[13], &p[14], &p[15], &p[16], &p[17], &p[18], &p[19]) == 22 && !strncmp(string, key, MAX_STRING_LEN)) { AM_SetMarkPoints(n, p); } } // players[]->lookdir static void P_WritePlayersLookdir (const char *key) { int i; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && players[i].lookdir) { M_snprintf(line, MAX_LINE_LEN, "%s %d %d\n", key, i, players[i].lookdir); fputs(line, save_stream); } } } static void P_ReadPlayersLookdir (const char *key) { int i, value; if (sscanf(line, "%s %d %d", string, &i, &value) == 3 && !strncmp(string, key, MAX_STRING_LEN) && i < MAXPLAYERS && (crispy->freelook || crispy->mouselook)) { players[i].lookdir = value; } } // musinfo.current_item static void P_WriteMusInfo (const char *key) { if (musinfo.current_item > 0 && musinfo.items[0] > 0) { char lump[9], orig[9]; strncpy(lump, lumpinfo[musinfo.current_item]->name, 8); strncpy(orig, lumpinfo[musinfo.items[0]]->name, 8); M_snprintf(line, MAX_LINE_LEN, "%s %s %s\n", key, lump, orig); fputs(line, save_stream); } } static void P_ReadMusInfo (const char *key) { int items; char lump[9] = {0}, orig[9] = {0}; items = sscanf(line, "%s %s %s", string, lump, orig); if (items >= 2 && !strncmp(string, key, MAX_STRING_LEN)) { int i; if ((i = W_CheckNumForName(lump)) > 0) { memset(&musinfo, 0, sizeof(musinfo)); musinfo.current_item = i; musinfo.from_savegame = true; S_ChangeMusInfoMusic(i, true); } if (items == 3 && (i = W_CheckNumForName(orig)) > 0) { musinfo.items[0] = i; } } } typedef struct { const char *key; void (* extsavegwritefn) (const char *key); void (* extsavegreadfn) (const char *key); const int pass; } extsavegdata_t; static const extsavegdata_t extsavegdata[] = { {PACKAGE_TARNAME, P_WritePackageTarname, NULL, 0}, {"wadfilename", P_WriteWadFileName, P_ReadWadFileName, 0}, {"extrakills", P_WriteExtraKills, P_ReadExtraKills, 1}, {"totalleveltimes", P_WriteTotalLevelTimes, P_ReadTotalLevelTimes, 1}, {"fireflicker", P_WriteFireFlicker, P_ReadFireFlicker, 1}, {"soundtarget", P_WriteSoundTarget, P_ReadSoundTarget, 1}, {"oldspecial", P_WriteOldSpecial, P_ReadOldSpecial, 1}, {"button", P_WriteButton, P_ReadButton, 1}, {"braintarget", P_WriteBrainTarget, P_ReadBrainTarget, 1}, {"markpoints", P_WriteMarkPoints, P_ReadMarkPoints, 1}, {"playerslookdir", P_WritePlayersLookdir, P_ReadPlayersLookdir, 1}, {"musinfo", P_WriteMusInfo, P_ReadMusInfo, 0}, }; void P_WriteExtendedSaveGameData (void) { int i; line = malloc(MAX_LINE_LEN); for (i = 0; i < arrlen(extsavegdata); i++) { extsavegdata[i].extsavegwritefn(extsavegdata[i].key); } free(line); } static void P_ReadKeyValuePairs (int pass) { while (fgets(line, MAX_LINE_LEN, save_stream)) { if (sscanf(line, "%s", string) == 1) { int i; for (i = 1; i < arrlen(extsavegdata); i++) { if (extsavegdata[i].extsavegreadfn && extsavegdata[i].pass == pass && !strncmp(string, extsavegdata[i].key, MAX_STRING_LEN)) { extsavegdata[i].extsavegreadfn(extsavegdata[i].key); } } } } } // [crispy] pointer to the info struct for the map lump about to load lumpinfo_t *savemaplumpinfo = NULL; void P_ReadExtendedSaveGameData (int pass) { long p, curpos, endpos; byte episode, map; int lumpnum = -1; line = malloc(MAX_LINE_LEN); string = malloc(MAX_STRING_LEN); // [crispy] two-pass reading of extended savegame data if (pass == 1) { P_ReadKeyValuePairs(1); free(line); free(string); return; } curpos = ftell(save_stream); // [crispy] check which map we would want to load fseek(save_stream, SAVESTRINGSIZE + VERSIONSIZE + 1, SEEK_SET); // [crispy] + 1 for "gameskill" if (fread(&episode, 1, 1, save_stream) == 1 && fread(&map, 1, 1, save_stream) == 1) { lumpnum = P_GetNumForMap ((int) episode, (int) map, false); } if (lumpnum >= 0) { savemaplumpinfo = lumpinfo[lumpnum]; } else { // [crispy] unavailable map! savemaplumpinfo = NULL; } // [crispy] read key/value pairs past the end of the regular savegame data fseek(save_stream, 0, SEEK_END); endpos = ftell(save_stream); for (p = endpos - 1; p > 0; p--) { byte curbyte; fseek(save_stream, p, SEEK_SET); if (fread(&curbyte, 1, 1, save_stream) < 1) { break; } if (curbyte == SAVEGAME_EOF) { if (!fgets(line, MAX_LINE_LEN, save_stream)) { continue; } if (sscanf(line, "%s", string) == 1 && !strncmp(string, extsavegdata[0].key, MAX_STRING_LEN)) { P_ReadKeyValuePairs(0); break; } } } free(line); free(string); // [crispy] back to where we started fseek(save_stream, curpos, SEEK_SET); } crispy-doom-crispy-doom-5.6.4/src/doom/p_extsaveg.h000066400000000000000000000021361360717211000223010ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2016 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] Archiving: Extended SaveGame I/O. // #ifndef __P_EXTSAVEG__ #define __P_EXTSAVEG__ /* p_extsaveg.c */ extern char *savewadfilename; extern void P_WriteExtendedSaveGameData (void); extern void P_ReadExtendedSaveGameData (int pass); /* p_saveg.c */ extern uint32_t P_ThinkerToIndex (thinker_t* thinker); extern thinker_t* P_IndexToThinker (uint32_t index); /* m_menu.c */ extern void M_ForceLoadGame (void); extern void M_ConfirmDeleteGame (void); #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_floor.c000066400000000000000000000341031360717211000215660ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Floor animation: raising stairs. // #include "z_zone.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" //e6y #define STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE 10 // // FLOORS // // // Move a plane (floor or ceiling) and check for crushing // result_e T_MovePlane ( sector_t* sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction ) { boolean flag; fixed_t lastpos; // [AM] Store old sector heights for interpolation. sector->oldfloorheight = sector->floorheight; sector->oldceilingheight = sector->ceilingheight; sector->oldgametic = gametic; switch(floorOrCeiling) { case 0: // FLOOR switch(direction) { case -1: // DOWN if (sector->floorheight - speed < dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->floorheight =lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { lastpos = sector->floorheight; sector->floorheight -= speed; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector,crush); return crushed; } } break; case 1: // UP if (sector->floorheight + speed > dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { // COULD GET CRUSHED lastpos = sector->floorheight; sector->floorheight += speed; flag = P_ChangeSector(sector,crush); if (flag == true) { if (crush == true) return crushed; sector->floorheight = lastpos; P_ChangeSector(sector,crush); return crushed; } } break; } break; case 1: // CEILING switch(direction) { case -1: // DOWN if (sector->ceilingheight - speed < dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { // COULD GET CRUSHED lastpos = sector->ceilingheight; sector->ceilingheight -= speed; flag = P_ChangeSector(sector,crush); if (flag == true) { if (crush == true) return crushed; sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); return crushed; } } break; case 1: // UP if (sector->ceilingheight + speed > dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { lastpos = sector->ceilingheight; sector->ceilingheight += speed; flag = P_ChangeSector(sector,crush); // UNUSED #if 0 if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); return crushed; } #endif } break; } break; } return ok; } // // MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) // void T_MoveFloor(floormove_t* floor) { result_e res; res = T_MovePlane(floor->sector, floor->speed, floor->floordestheight, floor->crush,0,floor->direction); if (!(leveltime&7)) S_StartSound(&floor->sector->soundorg, sfx_stnmov); if (res == pastdest) { floor->sector->specialdata = NULL; if (floor->direction == 1) { switch(floor->type) { case donutRaise: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } } else if (floor->direction == -1) { switch(floor->type) { case lowerAndChange: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } } P_RemoveThinker(&floor->thinker); S_StartSound(&floor->sector->soundorg, sfx_pstop); } } // [crispy] easter egg: homage to an old friend (thinker) void T_MoveGoobers (floormove_t *floor) { result_e res1, res2; // [crispy] one thinker for the floors ... res1 = T_MovePlane(floor->sector, 2 * FLOORSPEED, 0, true, 0, (floor->direction & 1) * 2 - 1); // [crispy] ... and one for the ceilings // * floordestheight is actually the ceiling destination height (either 0 or 128) // * the 5th argument is "floorOrCeiling" // * the actual direction is given by the second-lowest bit of the "direction" field res2 = T_MovePlane(floor->sector, 2 * FLOORSPEED, floor->floordestheight, true, 1, (floor->direction >> 1) * 2 - 1); if (!(leveltime & 7)) { S_StartSound(&floor->sector->soundorg, sfx_stnmov); } // [crispy] remove thinker once both the sector's floor and ceiling // have reached their respective destination heights if ((res1 & res2) == pastdest) { floor->sector->specialdata = NULL; P_RemoveThinker(&floor->thinker); S_StartSound(&floor->sector->soundorg, sfx_pstop); } } // [crispy] easter egg: homage to an old friend void EV_DoGoobers (void) { int i; for (i = 0; i < numsectors; i++) { sector_t* sec; floormove_t* floor; sec = §ors[i]; // [crispy] remove thinker for sectors that are already moving if (sec->specialdata) { floor = sec->specialdata; P_RemoveThinker(&floor->thinker); sec->specialdata = NULL; } floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveGoobers; floor->sector = sec; // [crispy] actually destination ceilingheight here (destination floorheight is always 0), // leave destination ceilingheight for untagged closed sectors (i.e. DR-type doors) at 0, // for all others set to 128 floor->floordestheight = (!sec->tag && sec->interpceilingheight == sec->interpfloorheight) ? 0 : 128 * FRACUNIT; // [crispy] the lowest bit determines floor direction (i.e. 1 means "up" for floorheight < 0), // the second-lowest bit determines ceiling direction (e.g. if ceiling height is below its destination height) floor->direction = (sec->floorheight < 0) | (sec->ceilingheight < floor->floordestheight) << 1; } } // // HANDLE FLOOR TYPES // int EV_DoFloor ( line_t* line, floor_e floortype ) { int secnum; int rtn; int i; sector_t* sec; floormove_t* floor; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // new floor thinker rtn = 1; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = floortype; floor->crush = false; switch(floortype) { case lowerFloor: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindHighestFloorSurrounding(sec); break; case lowerFloorToLowest: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); break; case turboLower: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED * 4; floor->floordestheight = P_FindHighestFloorSurrounding(sec); if (gameversion <= exe_doom_1_2 || floor->floordestheight != sec->floorheight) floor->floordestheight += 8*FRACUNIT; break; case raiseFloorCrush: floor->crush = true; case raiseFloor: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestCeilingSurrounding(sec); if (floor->floordestheight > sec->ceilingheight) floor->floordestheight = sec->ceilingheight; floor->floordestheight -= (8*FRACUNIT)* (floortype == raiseFloorCrush); break; case raiseFloorTurbo: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED*4; floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); break; case raiseFloorToNearest: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); break; case raiseFloor24: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; break; case raiseFloor512: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 512 * FRACUNIT; break; case raiseFloor24AndChange: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; sec->floorpic = line->frontsector->floorpic; sec->special = line->frontsector->special; break; case raiseToTexture: { int minsize = INT_MAX; side_t* side; floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; for (i = 0; i < sec->linecount; i++) { if (twoSided (secnum, i) ) { side = getSide(secnum,i,0); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; side = getSide(secnum,i,1); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; } } floor->floordestheight = floor->sector->floorheight + minsize; } break; case lowerAndChange: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); floor->texture = sec->floorpic; for (i = 0; i < sec->linecount; i++) { if ( twoSided(secnum, i) ) { if (getSide(secnum,i,0)->sector-sectors == secnum) { sec = getSector(secnum,i,1); if (sec->floorheight == floor->floordestheight) { floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } } else { sec = getSector(secnum,i,0); if (sec->floorheight == floor->floordestheight) { floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } } } } default: break; } } return rtn; } // // BUILD A STAIRCASE! // int EV_BuildStairs ( line_t* line, stair_e type ) { int secnum; int height; int i; int newsecnum; int texture; int ok; int rtn; sector_t* sec; sector_t* tsec; floormove_t* floor; fixed_t stairsize = 0; fixed_t speed = 0; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // new floor thinker rtn = 1; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->direction = 1; floor->sector = sec; switch(type) { case build8: speed = FLOORSPEED/4; stairsize = 8*FRACUNIT; break; case turbo16: speed = FLOORSPEED*4; stairsize = 16*FRACUNIT; break; } floor->speed = speed; height = sec->floorheight + stairsize; floor->floordestheight = height; // Initialize floor->type = lowerFloor; // e6y // Uninitialized crush field will not be equal to 0 or 1 (true) // with high probability. So, initialize it with any other value floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; texture = sec->floorpic; // Find next sector to raise // 1. Find 2-sided line with same sector side[0] // 2. Other side is the next sector to raise do { ok = 0; for (i = 0;i < sec->linecount;i++) { if ( !((sec->lines[i])->flags & ML_TWOSIDED) ) continue; tsec = (sec->lines[i])->frontsector; newsecnum = tsec-sectors; if (secnum != newsecnum) continue; tsec = (sec->lines[i])->backsector; newsecnum = tsec - sectors; if (tsec->floorpic != texture) continue; height += stairsize; if (tsec->specialdata) continue; sec = tsec; secnum = newsecnum; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->direction = 1; floor->sector = sec; floor->speed = speed; floor->floordestheight = height; // Initialize floor->type = lowerFloor; // e6y // Uninitialized crush field will not be equal to 0 or 1 (true) // with high probability. So, initialize it with any other value floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; ok = 1; break; } } while(ok); } return rtn; } crispy-doom-crispy-doom-5.6.4/src/doom/p_inter.c000066400000000000000000000516251360717211000215760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handling interactions (i.e., collisions). // // Data. #include "doomdef.h" #include "dstrings.h" #include "sounds.h" #include "deh_main.h" #include "deh_misc.h" #include "doomstat.h" #include "m_random.h" #include "i_system.h" #include "am_map.h" #include "p_local.h" #include "s_sound.h" #include "p_inter.h" #define BONUSADD 6 // a weapon is found with two clip loads, // a big item has five clip loads int maxammo[NUMAMMO] = {200, 50, 300, 50}; int clipammo[NUMAMMO] = {10, 4, 20, 1}; // // GET STUFF // // // P_GiveAmmo // Num is the number of clip loads, // not the individual count (0= 1/2 clip). // Returns false if the ammo can't be picked up at all // boolean P_GiveAmmo ( player_t* player, ammotype_t ammo, int num, boolean dropped ) // [NS] Dropped ammo/weapons give half as much. { int oldammo; if (ammo == am_noammo) return false; if (ammo > NUMAMMO) I_Error ("P_GiveAmmo: bad type %i", ammo); if ( player->ammo[ammo] == player->maxammo[ammo] ) return false; if (num) num *= clipammo[ammo]; else num = clipammo[ammo]/2; if (gameskill == sk_baby || gameskill == sk_nightmare) { // give double ammo in trainer mode, // you'll need in nightmare num <<= 1; } // [NS] Halve if needed. if (dropped) { num >>= 1; // Don't round down to 0. if (!num) 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; } // [crispy] show weapon pickup messages in multiplayer games const char *const WeaponPickupMessages[NUMWEAPONS] = { NULL, // wp_fist NULL, // wp_pistol GOTSHOTGUN, GOTCHAINGUN, GOTLAUNCHER, GOTPLASMA, GOTBFG9000, GOTCHAINSAW, GOTSHOTGUN2, }; // // P_GiveWeapon // The weapon name may have a MF_DROPPED flag ored in. // boolean P_GiveWeapon ( player_t* player, weapontype_t weapon, boolean dropped ) { boolean gaveammo; boolean gaveweapon; if (netgame && (deathmatch!=2) && !dropped ) { // leave placed weapons forever on net games if (player->weaponowned[weapon]) return false; player->bonuscount += BONUSADD; player->weaponowned[weapon] = true; if (deathmatch) P_GiveAmmo (player, weaponinfo[weapon].ammo, 5, false); else P_GiveAmmo (player, weaponinfo[weapon].ammo, 2, false); player->pendingweapon = weapon; // [crispy] show weapon pickup messages in multiplayer games player->message = DEH_String(WeaponPickupMessages[weapon]); if (player == &players[consoleplayer]) S_StartSound (NULL, sfx_wpnup); return false; } if (weaponinfo[weapon].ammo != am_noammo) { // give one clip with a dropped weapon, // two clips with a found weapon // [NS] Just need to pass that it's dropped. gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2, dropped); /* if (dropped) gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1); else gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); */ } else gaveammo = false; if (player->weaponowned[weapon]) gaveweapon = false; else { gaveweapon = true; player->weaponowned[weapon] = true; player->pendingweapon = weapon; } return (gaveweapon || gaveammo); } // // P_GiveBody // Returns false if the body isn't needed at all // boolean P_GiveBody ( player_t* player, int num ) { if (player->health >= MAXHEALTH) return false; player->health += num; if (player->health > MAXHEALTH) player->health = MAXHEALTH; player->mo->health = player->health; return true; } // // P_GiveArmor // Returns false if the armor is worse // than the current armor. // boolean P_GiveArmor ( player_t* player, int armortype ) { int hits; hits = armortype*100; if (player->armorpoints >= hits) return false; // don't pick up player->armortype = armortype; player->armorpoints = hits; return true; } // // P_GiveCard // void P_GiveCard ( player_t* player, card_t card ) { if (player->cards[card]) return; player->bonuscount += netgame ? BONUSADD : 0; // [crispy] Fix "Key pickup resets palette" player->cards[card] = 1; } // // P_GivePower // boolean P_GivePower ( player_t* player, int /*powertype_t*/ power ) { if (power == pw_invulnerability) { player->powers[power] = INVULNTICS; return true; } if (power == pw_invisibility) { player->powers[power] = INVISTICS; player->mo->flags |= MF_SHADOW; return true; } if (power == pw_infrared) { player->powers[power] = INFRATICS; return true; } if (power == pw_ironfeet) { player->powers[power] = IRONTICS; return true; } if (power == pw_strength) { P_GiveBody (player, 100); player->powers[power] = 1; return true; } if (player->powers[power]) return false; // already got it player->powers[power] = 1; return true; } // // P_TouchSpecialThing // void P_TouchSpecialThing ( mobj_t* special, mobj_t* toucher ) { player_t* player; int i; fixed_t delta; int sound; const boolean dropped = ((special->flags & MF_DROPPED) != 0); delta = special->z - toucher->z; if (delta > toucher->height || delta < -8*FRACUNIT) { // out of reach return; } sound = sfx_itemup; player = toucher->player; // Dead thing touching. // Can happen with a sliding player corpse. if (toucher->health <= 0) return; // Identify by sprite. switch (special->sprite) { // armor case SPR_ARM1: if (!P_GiveArmor (player, deh_green_armor_class)) return; player->message = DEH_String(GOTARMOR); break; case SPR_ARM2: if (!P_GiveArmor (player, deh_blue_armor_class)) return; player->message = DEH_String(GOTMEGA); break; // bonus items case SPR_BON1: player->health++; // can go over 100% if (player->health > deh_max_health) player->health = deh_max_health; player->mo->health = player->health; player->message = DEH_String(GOTHTHBONUS); break; case SPR_BON2: player->armorpoints++; // can go over 100% if (player->armorpoints > deh_max_armor && gameversion > exe_doom_1_2) player->armorpoints = deh_max_armor; // deh_green_armor_class only applies to the green armor shirt; // for the armor helmets, armortype 1 is always used. if (!player->armortype) player->armortype = 1; player->message = DEH_String(GOTARMBONUS); break; case SPR_SOUL: player->health += deh_soulsphere_health; if (player->health > deh_max_soulsphere) player->health = deh_max_soulsphere; player->mo->health = player->health; player->message = DEH_String(GOTSUPER); if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; case SPR_MEGA: if (gamemode != commercial) return; player->health = deh_megasphere_health; player->mo->health = player->health; // We always give armor type 2 for the megasphere; dehacked only // affects the MegaArmor. P_GiveArmor (player, 2); player->message = DEH_String(GOTMSPHERE); if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; // cards // leave cards for everyone case SPR_BKEY: if (!player->cards[it_bluecard]) player->message = DEH_String(GOTBLUECARD); P_GiveCard (player, it_bluecard); if (!netgame) break; return; case SPR_YKEY: if (!player->cards[it_yellowcard]) player->message = DEH_String(GOTYELWCARD); P_GiveCard (player, it_yellowcard); if (!netgame) break; return; case SPR_RKEY: if (!player->cards[it_redcard]) player->message = DEH_String(GOTREDCARD); P_GiveCard (player, it_redcard); if (!netgame) break; return; case SPR_BSKU: if (!player->cards[it_blueskull]) player->message = DEH_String(GOTBLUESKUL); P_GiveCard (player, it_blueskull); if (!netgame) break; return; case SPR_YSKU: if (!player->cards[it_yellowskull]) player->message = DEH_String(GOTYELWSKUL); P_GiveCard (player, it_yellowskull); if (!netgame) break; return; case SPR_RSKU: if (!player->cards[it_redskull]) player->message = DEH_String(GOTREDSKULL); P_GiveCard (player, it_redskull); if (!netgame) break; return; // medikits, heals case SPR_STIM: if (!P_GiveBody (player, 10)) return; player->message = DEH_String(GOTSTIM); break; case SPR_MEDI: if (!P_GiveBody (player, 25)) return; // [crispy] show "Picked up a Medikit that you really need" message as intended if (player->health < 50) player->message = DEH_String(GOTMEDINEED); else player->message = DEH_String(GOTMEDIKIT); break; // power ups case SPR_PINV: if (!P_GivePower (player, pw_invulnerability)) return; player->message = DEH_String(GOTINVUL); if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; case SPR_PSTR: if (!P_GivePower (player, pw_strength)) return; player->message = DEH_String(GOTBERSERK); if (player->readyweapon != wp_fist) player->pendingweapon = wp_fist; if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; case SPR_PINS: if (!P_GivePower (player, pw_invisibility)) return; player->message = DEH_String(GOTINVIS); if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; case SPR_SUIT: if (!P_GivePower (player, pw_ironfeet)) return; player->message = DEH_String(GOTSUIT); if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; case SPR_PMAP: if (!P_GivePower (player, pw_allmap)) return; player->message = DEH_String(GOTMAP); if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; case SPR_PVIS: if (!P_GivePower (player, pw_infrared)) return; player->message = DEH_String(GOTVISOR); if (gameversion > exe_doom_1_2) sound = sfx_getpow; break; // ammo // [NS] Give half ammo for drops of all types. 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; } */ if (!P_GiveAmmo (player,am_clip,1,dropped)) return; player->message = DEH_String(GOTCLIP); break; case SPR_AMMO: if (!P_GiveAmmo (player, am_clip,5,dropped)) return; player->message = DEH_String(GOTCLIPBOX); break; case SPR_ROCK: if (!P_GiveAmmo (player, am_misl,1,dropped)) return; player->message = DEH_String(GOTROCKET); break; case SPR_BROK: if (!P_GiveAmmo (player, am_misl,5,dropped)) return; player->message = DEH_String(GOTROCKBOX); break; case SPR_CELL: if (!P_GiveAmmo (player, am_cell,1,dropped)) return; player->message = DEH_String(GOTCELL); break; case SPR_CELP: if (!P_GiveAmmo (player, am_cell,5,dropped)) return; player->message = DEH_String(GOTCELLBOX); break; case SPR_SHEL: if (!P_GiveAmmo (player, am_shell,1,dropped)) return; player->message = DEH_String(GOTSHELLS); break; case SPR_SBOX: if (!P_GiveAmmo (player, am_shell,5,dropped)) return; player->message = DEH_String(GOTSHELLBOX); break; case SPR_BPAK: if (!player->backpack) { for (i=0 ; imaxammo[i] *= 2; player->backpack = true; } for (i=0 ; imessage = DEH_String(GOTBACKPACK); break; // weapons // [NS] Give half ammo for all dropped weapons. case SPR_BFUG: if (!P_GiveWeapon (player, wp_bfg, dropped) ) return; player->message = DEH_String(GOTBFG9000); sound = sfx_wpnup; break; case SPR_MGUN: if (!P_GiveWeapon(player, wp_chaingun, (special->flags & MF_DROPPED) != 0)) return; player->message = DEH_String(GOTCHAINGUN); sound = sfx_wpnup; break; case SPR_CSAW: if (!P_GiveWeapon (player, wp_chainsaw, dropped) ) return; player->message = DEH_String(GOTCHAINSAW); sound = sfx_wpnup; break; case SPR_LAUN: if (!P_GiveWeapon (player, wp_missile, dropped) ) return; player->message = DEH_String(GOTLAUNCHER); sound = sfx_wpnup; break; case SPR_PLAS: if (!P_GiveWeapon (player, wp_plasma, dropped) ) return; player->message = DEH_String(GOTPLASMA); sound = sfx_wpnup; break; case SPR_SHOT: if (!P_GiveWeapon(player, wp_shotgun, (special->flags & MF_DROPPED) != 0)) return; player->message = DEH_String(GOTSHOTGUN); sound = sfx_wpnup; break; case SPR_SGN2: if (!P_GiveWeapon(player, wp_supershotgun, (special->flags & MF_DROPPED) != 0)) return; player->message = DEH_String(GOTSHOTGUN2); sound = sfx_wpnup; break; default: I_Error ("P_SpecialThing: Unknown gettable thing"); } if (special->flags & MF_COUNTITEM) player->itemcount++; P_RemoveMobj (special); player->bonuscount += BONUSADD; if (player == &players[consoleplayer]) S_StartSound (NULL, sound); } // // KillMobj // void P_KillMobj ( mobj_t* source, mobj_t* target ) { mobjtype_t item; mobj_t* mo; target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); if (target->type != MT_SKULL) target->flags &= ~MF_NOGRAVITY; target->flags |= MF_CORPSE|MF_DROPOFF; target->height >>= 2; if (source && source->player) { // count for intermission if (target->flags & MF_COUNTKILL) source->player->killcount++; if (target->player) source->player->frags[target->player-players]++; } else if (!netgame && (target->flags & MF_COUNTKILL) ) { // count all monster deaths, // even those caused by other monsters players[0].killcount++; } if (target->player) { // count environment kills against you if (!source) target->player->frags[target->player-players]++; target->flags &= ~MF_SOLID; target->player->playerstate = PST_DEAD; P_DropWeapon (target->player); // [crispy] center view when dying target->player->centering = true; // [JN] & [crispy] Reset the yellow bonus palette when the player dies target->player->bonuscount = 0; // [JN] & [crispy] Remove the effect of the inverted palette when the player dies target->player->fixedcolormap = target->player->powers[pw_infrared] ? 1 : 0; if (target->player == &players[consoleplayer] && automapactive) { // don't die in auto map, // switch view prior to dying AM_Stop (); } } // [crispy] Lost Soul, Pain Elemental and Barrel explosions are translucent if (target->type == MT_SKULL || target->type == MT_PAIN || target->type == MT_BARREL) target->flags |= MF_TRANSLUCENT; if (target->health < -target->info->spawnhealth && target->info->xdeathstate) { P_SetMobjState (target, target->info->xdeathstate); } else P_SetMobjState (target, target->info->deathstate); target->tics -= P_Random()&3; // [crispy] randomly flip corpse, blood and death animation sprites if (target->flags & MF_FLIPPABLE) { target->health = (target->health & (int)~1) - (Crispy_Random() & 1); } if (target->tics < 1) target->tics = 1; // I_StartSound (&actor->r, actor->info->deathsound); // In Chex Quest, monsters don't drop items. if (gameversion == exe_chex) { return; } // Drop stuff. // This determines the kind of object spawned // during the death frame of a thing. switch (target->type) { case MT_WOLFSS: case MT_POSSESSED: item = MT_CLIP; break; case MT_SHOTGUY: item = MT_SHOTGUN; break; case MT_CHAINGUY: item = MT_CHAINGUN; break; default: return; } mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item); mo->flags |= MF_DROPPED; // special versions of items } // // P_DamageMobj // Damages both enemies and players // "inflictor" is the thing that caused the damage // creature or missile, can be 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 ) { unsigned ang; int saved; player_t* player; fixed_t thrust; int temp; if ( !(target->flags & MF_SHOOTABLE) ) return; // shouldn't happen... if (target->health <= 0) return; if ( target->flags & MF_SKULLFLY ) { target->momx = target->momy = target->momz = 0; } player = target->player; if (player && gameskill == sk_baby) damage >>= 1; // take half damage in trainer mode // Some close combat weapons should not // inflict thrust and push the victim out of reach, // thus kick away unless using the chainsaw. if (inflictor && !(target->flags & MF_NOCLIP) && (!source || !source->player || source->player->readyweapon != wp_chainsaw)) { ang = R_PointToAngle2 ( inflictor->x, inflictor->y, target->x, target->y); thrust = damage*(FRACUNIT>>3)*100/target->info->mass; // make fall forwards sometimes if ( damage < 40 && damage > target->health && target->z - inflictor->z > 64*FRACUNIT && (P_Random ()&1) ) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; target->momx += FixedMul (thrust, finecosine[ang]); target->momy += FixedMul (thrust, finesine[ang]); } // player specific if (player) { // end of game hell hack if (target->subsector->sector->special == 11 && damage >= target->health) { damage = target->health - 1; } // Below certain threshold, // ignore damage in GOD mode, or with INVUL power. if ( damage < 1000 && ( (player->cheats&CF_GODMODE) || player->powers[pw_invulnerability] ) ) { return; } if (player->armortype) { if (player->armortype == 1) saved = damage/3; else saved = damage/2; if (player->armorpoints <= saved) { // armor is used up saved = player->armorpoints; player->armortype = 0; } player->armorpoints -= saved; damage -= saved; } player->health -= damage; // mirror mobj health here for Dave // [crispy] negative player health if (player->health < -99) player->health = -99; if (!crispy->neghealth) { if (player->health < 0) player->health = 0; } player->attacker = source; player->damagecount += damage; // add damage after armor / invuln if (player->damagecount > 100) player->damagecount = 100; // teleport stomp does 10k points... temp = damage < 100 ? damage : 100; if (player == &players[consoleplayer]) I_Tactile (40,10,40+temp*2); } // do the damage target->health -= damage; if (target->health <= 0) { P_KillMobj (source, target); return; } if ( (P_Random () < target->info->painchance) && !(target->flags&MF_SKULLFLY) ) { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState (target, target->info->painstate); } target->reactiontime = 0; // we're awake now... if ( (!target->threshold || target->type == MT_VILE) && source && (source != target || gameversion <= exe_doom_1_2) && source->type != MT_VILE) { // if not intent on another player, // chase after this one target->target = source; target->threshold = BASETHRESHOLD; if (target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL) P_SetMobjState (target, target->info->seestate); } } crispy-doom-crispy-doom-5.6.4/src/doom/p_inter.h000066400000000000000000000013041360717211000215700ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __P_INTER__ #define __P_INTER__ boolean P_GivePower(player_t*, int); #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_lights.c000066400000000000000000000145601360717211000217440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handle Sector base lighting effects. // Muzzle flash? // #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" // State. #include "r_state.h" // // FIRELIGHT FLICKER // // // T_FireFlicker // void T_FireFlicker (fireflicker_t* flick) { int amount; if (--flick->count) return; amount = (P_Random()&3)*16; if (flick->sector->lightlevel - amount < flick->minlight) flick->sector->lightlevel = flick->minlight; else flick->sector->lightlevel = flick->maxlight - amount; flick->count = 4; } // // P_SpawnFireFlicker // void P_SpawnFireFlicker (sector_t* sector) { fireflicker_t* flick; // Note that we are resetting sector attributes. // Nothing special about it during gameplay. sector->special = 0; flick = Z_Malloc ( sizeof(*flick), PU_LEVSPEC, 0); P_AddThinker (&flick->thinker); flick->thinker.function.acp1 = (actionf_p1) T_FireFlicker; flick->sector = sector; flick->maxlight = sector->lightlevel; flick->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel)+16; flick->count = 4; } // // BROKEN LIGHT FLASHING // // // T_LightFlash // Do flashing lights. // void T_LightFlash (lightflash_t* flash) { if (--flash->count) return; if (flash->sector->lightlevel == flash->maxlight) { flash-> sector->lightlevel = flash->minlight; flash->count = (P_Random()&flash->mintime)+1; } else { flash-> sector->lightlevel = flash->maxlight; flash->count = (P_Random()&flash->maxtime)+1; } } // // P_SpawnLightFlash // After the map has been loaded, scan each sector // for specials that spawn thinkers // void P_SpawnLightFlash (sector_t* sector) { lightflash_t* flash; // nothing special about it during gameplay sector->special = 0; flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); P_AddThinker (&flash->thinker); flash->thinker.function.acp1 = (actionf_p1) T_LightFlash; flash->sector = sector; flash->maxlight = sector->lightlevel; flash->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); flash->maxtime = 64; flash->mintime = 7; flash->count = (P_Random()&flash->maxtime)+1; } // // STROBE LIGHT FLASHING // // // T_StrobeFlash // void T_StrobeFlash (strobe_t* flash) { if (--flash->count) return; if (flash->sector->lightlevel == flash->minlight) { flash-> sector->lightlevel = flash->maxlight; flash->count = flash->brighttime; } else { flash-> sector->lightlevel = flash->minlight; flash->count =flash->darktime; } } // // P_SpawnStrobeFlash // After the map has been loaded, scan each sector // for specials that spawn thinkers // void P_SpawnStrobeFlash ( sector_t* sector, int fastOrSlow, int inSync ) { strobe_t* flash; flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); P_AddThinker (&flash->thinker); flash->sector = sector; flash->darktime = fastOrSlow; flash->brighttime = STROBEBRIGHT; flash->thinker.function.acp1 = (actionf_p1) T_StrobeFlash; flash->maxlight = sector->lightlevel; flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); if (flash->minlight == flash->maxlight) flash->minlight = 0; // nothing special about it during gameplay sector->special = 0; if (!inSync) flash->count = (P_Random()&7)+1; else flash->count = 1; } // // Start strobing lights (usually from a trigger) // void EV_StartLightStrobing(line_t* line) { int secnum; sector_t* sec; secnum = -1; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; P_SpawnStrobeFlash (sec,SLOWDARK, 0); } } // // TURN LINE'S TAG LIGHTS OFF // void EV_TurnTagLightsOff(line_t* line) { int i; int j; int min; sector_t* sector; sector_t* tsec; line_t* templine; sector = sectors; for (j = 0;j < numsectors; j++, sector++) { if (sector->tag == line->tag) { min = sector->lightlevel; for (i = 0;i < sector->linecount; i++) { templine = sector->lines[i]; tsec = getNextSector(templine,sector); if (!tsec) continue; if (tsec->lightlevel < min) min = tsec->lightlevel; } sector->lightlevel = min; } } } // // TURN LINE'S TAG LIGHTS ON // void EV_LightTurnOn ( line_t* line, int bright ) { int i; int j; sector_t* sector; sector_t* temp; line_t* templine; sector = sectors; for (i=0;itag == line->tag) { // bright = 0 means to search // for highest light level // surrounding sector if (!bright) { for (j = 0;j < sector->linecount; j++) { templine = sector->lines[j]; temp = getNextSector(templine,sector); if (!temp) continue; if (temp->lightlevel > bright) bright = temp->lightlevel; } } sector-> lightlevel = bright; } } } // // Spawn glowing light // void T_Glow(glow_t* g) { switch(g->direction) { case -1: // DOWN g->sector->lightlevel -= GLOWSPEED; if (g->sector->lightlevel <= g->minlight) { g->sector->lightlevel += GLOWSPEED; g->direction = 1; } break; case 1: // UP g->sector->lightlevel += GLOWSPEED; if (g->sector->lightlevel >= g->maxlight) { g->sector->lightlevel -= GLOWSPEED; g->direction = -1; } break; } } void P_SpawnGlowingLight(sector_t* sector) { glow_t* g; g = Z_Malloc( sizeof(*g), PU_LEVSPEC, 0); P_AddThinker(&g->thinker); g->sector = sector; g->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); g->maxlight = sector->lightlevel; g->thinker.function.acp1 = (actionf_p1) T_Glow; g->direction = -1; sector->special = 0; } crispy-doom-crispy-doom-5.6.4/src/doom/p_local.h000066400000000000000000000157101360717211000215470ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Play functions, animation, global header. // #ifndef __P_LOCAL__ #define __P_LOCAL__ #ifndef __R_LOCAL__ #include "r_local.h" #endif #define TOCENTER -8 #define AFLAG_JUMP 0x80 #define FLOATSPEED (FRACUNIT*4) #define MAXHEALTH 100 #define VIEWHEIGHT (41*FRACUNIT) // mapblocks are used to check movement // against lines and things #define MAPBLOCKUNITS 128 #define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) #define MAPBLOCKSHIFT (FRACBITS+7) #define MAPBMASK (MAPBLOCKSIZE-1) #define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) // player radius for movement checking #define PLAYERRADIUS 16*FRACUNIT // MAXRADIUS is for precalculated sector block boxes // the spider demon is larger, // but we do not have any moving sectors nearby #define MAXRADIUS 32*FRACUNIT #define GRAVITY FRACUNIT #define MAXMOVE (30*FRACUNIT) #define USERANGE (64*FRACUNIT) #define MELEERANGE (64*FRACUNIT) #define MISSILERANGE (32*64*FRACUNIT) // follow a player exlusively for 3 seconds #define BASETHRESHOLD 100 // // P_TICK // // both the head and tail of the thinker list extern thinker_t thinkercap; void P_InitThinkers (void); void P_AddThinker (thinker_t* thinker); void P_RemoveThinker (thinker_t* thinker); // // P_PSPR // void P_SetupPsprites (player_t* curplayer); void P_MovePsprites (player_t* curplayer); void P_DropWeapon (player_t* player); // // P_USER // #define MLOOKUNIT 8 #define PLAYER_SLOPE(a) ((((a)->lookdir / MLOOKUNIT) << FRACBITS) / 173) void P_PlayerThink (player_t* player); // // P_MOBJ // #define ONFLOORZ INT_MIN #define ONCEILINGZ INT_MAX // Time interval for item respawning. #define ITEMQUESIZE 128 extern mapthing_t itemrespawnque[ITEMQUESIZE]; extern int itemrespawntime[ITEMQUESIZE]; extern int iquehead; extern int iquetail; void P_RespawnSpecials (void); mobj_t* P_SpawnMobj ( fixed_t x, fixed_t y, fixed_t z, mobjtype_t type ); void P_RemoveMobj (mobj_t* th); mobj_t* P_SubstNullMobj (mobj_t* th); boolean P_SetMobjState (mobj_t* mobj, statenum_t state); void P_MobjThinker (mobj_t* mobj); mobj_t *Crispy_PlayerSO (int p); // [crispy] weapon sound sources 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* target); mobj_t* P_SpawnMissile (mobj_t* source, mobj_t* dest, mobjtype_t type); void P_SpawnPlayerMissile (mobj_t* source, mobjtype_t type); void P_SpawnPuffSafe (fixed_t x, fixed_t y, fixed_t z, boolean safe); // // P_ENEMY // void P_NoiseAlert (mobj_t* target, mobj_t* emmiter); // // P_MAPUTL // typedef struct { fixed_t x; fixed_t y; fixed_t dx; fixed_t dy; } divline_t; typedef struct { fixed_t frac; // along trace line boolean isaline; union { mobj_t* thing; line_t* line; } d; } intercept_t; // Extended MAXINTERCEPTS, to allow for intercepts overrun emulation. #define MAXINTERCEPTS_ORIGINAL 128 #define MAXINTERCEPTS (MAXINTERCEPTS_ORIGINAL + 61) //extern intercept_t intercepts[MAXINTERCEPTS]; // [crispy] remove INTERCEPTS limit extern intercept_t* intercept_p; typedef boolean (*traverser_t) (intercept_t *in); fixed_t P_AproxDistance (fixed_t dx, fixed_t dy); int P_PointOnLineSide (fixed_t x, fixed_t y, line_t* line); int P_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t* line); void P_MakeDivline (line_t* li, divline_t* dl); fixed_t P_InterceptVector (divline_t* v2, divline_t* v1); int P_BoxOnLineSide (fixed_t* tmbox, line_t* ld); extern fixed_t opentop; extern fixed_t openbottom; extern fixed_t openrange; extern fixed_t lowfloor; void P_LineOpening (line_t* linedef); boolean P_BlockLinesIterator (int x, int y, boolean(*func)(line_t*) ); boolean P_BlockThingsIterator (int x, int y, boolean(*func)(mobj_t*) ); #define PT_ADDLINES 1 #define PT_ADDTHINGS 2 #define PT_EARLYOUT 4 extern divline_t trace; boolean P_PathTraverse ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean (*trav) (intercept_t *)); void P_UnsetThingPosition (mobj_t* thing); void P_SetThingPosition (mobj_t* thing); // // P_MAP // // If "floatok" true, move would be ok // if within "tmfloorz - tmceilingz". extern boolean floatok; extern fixed_t tmfloorz; extern fixed_t tmceilingz; extern line_t* ceilingline; // fraggle: I have increased the size of this buffer. In the original Doom, // overrunning past this limit caused other bits of memory to be overwritten, // affecting demo playback. However, in doing so, the limit was still // exceeded. So we have to support more than 8 specials. // // We keep the original limit, to detect what variables in memory were // overwritten (see SpechitOverrun()) #define MAXSPECIALCROSS 20 #define MAXSPECIALCROSS_ORIGINAL 8 extern line_t* spechit[MAXSPECIALCROSS]; extern int numspechit; boolean P_CheckPosition (mobj_t *thing, fixed_t x, fixed_t y); boolean P_TryMove (mobj_t* thing, fixed_t x, fixed_t y); boolean P_TeleportMove (mobj_t* thing, fixed_t x, fixed_t y); void P_SlideMove (mobj_t* mo); boolean P_CheckSight (mobj_t* t1, mobj_t* t2); void P_UseLines (player_t* player); boolean P_ChangeSector (sector_t* sector, boolean crunch); extern mobj_t* linetarget; // who got hit (or NULL) fixed_t P_AimLineAttack ( mobj_t* t1, angle_t angle, fixed_t distance ); void P_LineAttack ( mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage ); void P_RadiusAttack ( mobj_t* spot, mobj_t* source, int damage ); // // P_SETUP // extern byte* rejectmatrix; // for fast sight rejection extern int32_t* blockmaplump; // offsets in blockmap are from here // [crispy] BLOCKMAP limit extern int32_t* blockmap; // [crispy] BLOCKMAP limit 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 // [crispy] factor out map lump name and number finding into a separate function extern int P_GetNumForMap (int episode, int map, boolean critical); // [crispy] blinking key or skull in the status bar #define KEYBLINKMASK 0x8 #define KEYBLINKTICS (7*KEYBLINKMASK) extern int st_keyorskull[3]; // // P_INTER // extern int maxammo[NUMAMMO]; extern int clipammo[NUMAMMO]; void P_TouchSpecialThing ( mobj_t* special, mobj_t* toucher ); void P_DamageMobj ( mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage ); // // P_SPEC // #include "p_spec.h" #endif // __P_LOCAL__ crispy-doom-crispy-doom-5.6.4/src/doom/p_map.c000066400000000000000000001133101360717211000212200ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard, Andrey Budko // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Movement, collision handling. // Shooting and aiming. // #include #include #include "deh_misc.h" #include "m_bbox.h" #include "m_random.h" #include "i_system.h" #include "doomdef.h" #include "m_argv.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // Spechit overrun magic value. // // This is the value used by PrBoom-plus. I think the value below is // actually better and works with more demos. However, I think // it's better for the spechits emulation to be compatible with // PrBoom-plus, at least so that the big spechits emulation list // on Doomworld can also be used with Chocolate Doom. #define DEFAULT_SPECHIT_MAGIC 0x01C09C98 // This is from a post by myk on the Doomworld forums, // outputted from entryway's spechit_magic generator for // s205n546.lmp. The _exact_ value of this isn't too // important; as long as it is in the right general // range, it will usually work. Otherwise, we can use // the generator (hacked doom2.exe) and provide it // with -spechit. //#define DEFAULT_SPECHIT_MAGIC 0x84f968e8 fixed_t tmbbox[4]; mobj_t* tmthing; int tmflags; fixed_t tmx; fixed_t tmy; // If "floatok" true, move would be ok // if within "tmfloorz - tmceilingz". boolean floatok; fixed_t tmfloorz; fixed_t tmceilingz; fixed_t tmdropoffz; // keep track of the line that lowers the ceiling, // so missiles don't explode against sky hack walls line_t* ceilingline; // keep track of special lines as they are hit, // but don't process them until the move is proven valid line_t* spechit[MAXSPECIALCROSS]; int numspechit; // // TELEPORT MOVE // // // PIT_StompThing // boolean PIT_StompThing (mobj_t* thing) { fixed_t blockdist; if (!(thing->flags & MF_SHOOTABLE) ) return true; blockdist = thing->radius + tmthing->radius; if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; // monsters don't stomp things except on boss level if ( !tmthing->player && gamemap != 30) return false; P_DamageMobj (thing, tmthing, tmthing, 10000); return true; } // // P_TeleportMove // boolean P_TeleportMove ( mobj_t* thing, fixed_t x, fixed_t y ) { int xl; int xh; int yl; int yh; int bx; int by; subsector_t* newsubsec; // kill anything occupying the position tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector (x,y); ceilingline = NULL; // The base floor/ceiling is from the subsector // that contains the point. // Any contacted lines the step closer together // will adjust them. tmfloorz = tmdropoffz = newsubsec->sector->floorheight; tmceilingz = newsubsec->sector->ceilingheight; validcount++; numspechit = 0; // stomp on any things contacted xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!P_BlockThingsIterator(bx,by,PIT_StompThing)) return false; // the move is ok, // so link the thing into its new position P_UnsetThingPosition (thing); thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; // [AM] Don't interpolate mobjs that pass // through teleporters thing->interp = false; P_SetThingPosition (thing); return true; } // // MOVEMENT ITERATOR FUNCTIONS // static void SpechitOverrun(line_t *ld); // // PIT_CheckLine // Adjusts tmfloorz and tmceilingz as lines are contacted // boolean PIT_CheckLine (line_t* ld) { if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] ) return true; if (P_BoxOnLineSide (tmbbox, ld) != -1) return true; // A line has been hit // The moving thing's destination position will cross // the given line. // If this should not be allowed, return false. // If the line is special, keep track of it // to process later if the move is proven ok. // NOTE: specials are NOT sorted by order, // so two special lines that are only 8 pixels apart // could be crossed in either order. if (!ld->backsector) return false; // one sided line if (!(tmthing->flags & MF_MISSILE) ) { if ( ld->flags & ML_BLOCKING ) return false; // explicitly blocking everything if ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS ) return false; // block monsters only } // set openrange, opentop, openbottom P_LineOpening (ld); // adjust floor / ceiling heights if (opentop < tmceilingz) { tmceilingz = opentop; ceilingline = ld; } if (openbottom > tmfloorz) tmfloorz = openbottom; if (lowfloor < tmdropoffz) tmdropoffz = lowfloor; // if contacted a special line, add it to the list if (ld->special) { spechit[numspechit] = ld; numspechit++; // fraggle: spechits overrun emulation code from prboom-plus if (numspechit > MAXSPECIALCROSS_ORIGINAL) { // [crispy] print a warning if (numspechit == MAXSPECIALCROSS_ORIGINAL + 1) fprintf(stderr, "PIT_CheckLine: Triggered SPECHITS overflow!\n"); SpechitOverrun(ld); } } return true; } // // PIT_CheckThing // boolean PIT_CheckThing (mobj_t* thing) { fixed_t blockdist; boolean solid; boolean unblocking = false; int damage; if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) )) return true; blockdist = thing->radius + tmthing->radius; if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; // check for skulls slamming into things if (tmthing->flags & MF_SKULLFLY) { // [crispy] check if attacking skull flies over player if (critical->overunder && thing->player) { if (tmthing->z > thing->z + thing->height) { return true; } } damage = ((P_Random()%8)+1)*tmthing->info->damage; P_DamageMobj (thing, tmthing, tmthing, damage); tmthing->flags &= ~MF_SKULLFLY; tmthing->momx = tmthing->momy = tmthing->momz = 0; P_SetMobjState (tmthing, tmthing->info->spawnstate); return false; // stop moving } // missiles can hit other things if (tmthing->flags & MF_MISSILE) { // [crispy] mobj or actual sprite height const fixed_t thingheight = (tmthing->target && tmthing->target->player && critical->freeaim == FREEAIM_DIRECT) ? thing->info->actualheight : thing->height; // see if it went over / under if (tmthing->z > thing->z + thingheight) return true; // overhead if (tmthing->z+tmthing->height < thing->z) return true; // underneath if (tmthing->target && (tmthing->target->type == thing->type || (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)|| (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) ) { // Don't hit same species as originator. if (thing == tmthing->target) return true; // sdh: Add deh_species_infighting here. We can override the // "monsters of the same species cant hurt each other" behavior // through dehacked patches if (thing->type != MT_PLAYER && !deh_species_infighting) { // Explode, but do no damage. // Let players missile other players. return false; } } if (! (thing->flags & MF_SHOOTABLE) ) { // didn't do any damage return !(thing->flags & MF_SOLID); } // damage / explode damage = ((P_Random()%8)+1)*tmthing->info->damage; P_DamageMobj (thing, tmthing, tmthing->target, damage); // don't traverse any more return false; } // check for special pickup if (thing->flags & MF_SPECIAL) { solid = (thing->flags & MF_SOLID) != 0; if (tmflags&MF_PICKUP) { // can remove thing P_TouchSpecialThing (thing, tmthing); } return !solid; } if (critical->overunder) { // [crispy] a solid hanging body will allow sufficiently small things underneath it if (thing->flags & MF_SOLID && thing->flags & MF_SPAWNCEILING) { if (tmthing->z + tmthing->height <= thing->z) { if (thing->z < tmceilingz) { tmceilingz = thing->z; } return true; } } // [crispy] allow players to walk over/under shootable objects if (tmthing->player && thing->flags & MF_SHOOTABLE) { fixed_t newfloorz, newceilingz; // [crispy] allow the usual 24 units step-up even across monsters' heads, // only if the current height has not been reached by "low" jumping fixed_t step_up = tmthing->player->jumpTics > 7 ? 0 : 24*FRACUNIT; if (tmthing->z + step_up >= thing->z + thing->height) { // player walks over object if ((newfloorz = thing->z + thing->height) > tmfloorz) { tmfloorz = newfloorz; } if ((newceilingz = tmthing->z) < thing->ceilingz) { thing->ceilingz = newceilingz; } return true; } else if (tmthing->z + tmthing->height <= thing->z) { // player walks underneath object if ((newceilingz = thing->z) < tmceilingz) { tmceilingz = newceilingz; } if ((newfloorz = tmthing->z + tmthing->height) > thing->floorz) { thing->floorz = newfloorz; } return true; } // [crispy] check if things are stuck and allow them to move further apart // taken from doomretro/src/p_map.c:319-332 if (tmx == tmthing->x && tmy == tmthing->y) { unblocking = true; } else { fixed_t newdist = P_AproxDistance(thing->x - tmx, thing->y - tmy); fixed_t olddist = P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y); if (newdist > olddist) { unblocking = (tmthing->z < thing->z + thing->height && tmthing->z + tmthing->height > thing->z); } } } } return !(thing->flags & MF_SOLID) || unblocking; } // // 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 // boolean P_CheckPosition ( mobj_t* thing, fixed_t x, fixed_t y ) { int xl; int xh; int yl; int yh; int bx; int by; subsector_t* newsubsec; tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector (x,y); ceilingline = NULL; // The base floor / ceiling is from the subsector // that contains the point. // Any contacted lines the step closer together // will adjust them. tmfloorz = tmdropoffz = newsubsec->sector->floorheight; tmceilingz = newsubsec->sector->ceilingheight; validcount++; numspechit = 0; if ( tmflags & MF_NOCLIP ) return true; // Check things first, possibly picking things up. // The bounding box is extended by MAXRADIUS // because mobj_ts are grouped into mapblocks // based on their origin point, and can overlap // into adjacent blocks by up to MAXRADIUS units. xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!P_BlockThingsIterator(bx,by,PIT_CheckThing)) return false; // check lines xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!P_BlockLinesIterator (bx,by,PIT_CheckLine)) return false; return true; } // // P_TryMove // Attempt to move to a new position, // crossing special lines unless MF_TELEPORT is set. // boolean P_TryMove ( mobj_t* thing, fixed_t x, fixed_t y ) { fixed_t oldx; fixed_t oldy; int side; int oldside; line_t* ld; floatok = false; if (!P_CheckPosition (thing, x, y)) return false; // solid wall or thing if ( !(thing->flags & MF_NOCLIP) ) { if (tmceilingz - tmfloorz < thing->height) return false; // doesn't fit floatok = true; if ( !(thing->flags&MF_TELEPORT) &&tmceilingz - thing->z < thing->height) return false; // mobj must lower itself to fit if ( !(thing->flags&MF_TELEPORT) && tmfloorz - thing->z > 24*FRACUNIT ) return false; // too big a step up if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT)) && tmfloorz - tmdropoffz > 24*FRACUNIT ) return false; // don't stand over a dropoff } // the move is ok, // so link the thing into its new position P_UnsetThingPosition (thing); oldx = thing->x; oldy = thing->y; thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; P_SetThingPosition (thing); // if any special lines were hit, do the effect if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) ) { while (numspechit--) { // see if the line was crossed ld = spechit[numspechit]; side = P_PointOnLineSide (thing->x, thing->y, ld); oldside = P_PointOnLineSide (oldx, oldy, ld); if (side != oldside) { if (ld->special) P_CrossSpecialLine (ld-lines, oldside, thing); } } } return true; } // // P_ThingHeightClip // Takes a valid thing and adjusts the thing->floorz, // thing->ceilingz, and possibly thing->z. // This is called for all nearby monsters // whenever a sector changes height. // If the thing doesn't fit, // the z will be set to the lowest value // and false will be returned. // boolean P_ThingHeightClip (mobj_t* thing) { boolean onfloor; onfloor = (thing->z == thing->floorz); P_CheckPosition (thing, thing->x, thing->y); // what about stranding a monster partially off an edge? thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; if (onfloor) { // walking monsters rise and fall with the floor thing->z = thing->floorz; } else { // don't adjust a floating monster unless forced to if (thing->z+thing->height > thing->ceilingz) thing->z = thing->ceilingz - thing->height; } if (thing->ceilingz - thing->floorz < thing->height) return false; return true; } // // SLIDE MOVE // Allows the player to slide along any angled walls. // fixed_t bestslidefrac; fixed_t secondslidefrac; line_t* bestslideline; line_t* secondslideline; mobj_t* slidemo; fixed_t tmxmove; fixed_t tmymove; // // P_HitSlideLine // Adjusts the xmove / ymove // so that the next move will slide along the wall. // void P_HitSlideLine (line_t* ld) { int side; angle_t lineangle; angle_t moveangle; angle_t deltaangle; fixed_t movelen; fixed_t newlen; if (ld->slopetype == ST_HORIZONTAL) { tmymove = 0; return; } if (ld->slopetype == ST_VERTICAL) { tmxmove = 0; return; } side = P_PointOnLineSide (slidemo->x, slidemo->y, ld); lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy); if (side == 1) lineangle += ANG180; moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove); deltaangle = moveangle-lineangle; if (deltaangle > ANG180) deltaangle += ANG180; // I_Error ("SlideLine: ang>ANG180"); lineangle >>= ANGLETOFINESHIFT; deltaangle >>= ANGLETOFINESHIFT; movelen = P_AproxDistance (tmxmove, tmymove); newlen = FixedMul (movelen, finecosine[deltaangle]); tmxmove = FixedMul (newlen, finecosine[lineangle]); tmymove = FixedMul (newlen, finesine[lineangle]); } // // PTR_SlideTraverse // boolean 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)) { // don't hit the back side return true; } goto isblocking; } // set openrange, opentop, openbottom P_LineOpening (li); if (openrange < slidemo->height) goto isblocking; // doesn't fit if (opentop - slidemo->z < slidemo->height) goto isblocking; // mobj is too high if (openbottom - slidemo->z > 24*FRACUNIT ) goto isblocking; // too big a step up // this line doesn't block movement return true; // the line does block movement, // see if it is closer than best so far isblocking: if (in->frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in->frac; bestslideline = li; } return false; // stop } // // P_SlideMove // The momx / momy move is bad, so try to slide // along a wall. // Find the first line hit, move flush to it, // and slide along it // // This is a kludgy mess. // void P_SlideMove (mobj_t* mo) { fixed_t leadx; fixed_t leady; fixed_t trailx; fixed_t traily; fixed_t newx; fixed_t newy; int hitcount; slidemo = mo; hitcount = 0; retry: if (++hitcount == 3) goto stairstep; // don't loop forever // trace along the three leading corners if (mo->momx > 0) { leadx = mo->x + mo->radius; trailx = mo->x - mo->radius; } else { leadx = mo->x - mo->radius; trailx = mo->x + mo->radius; } if (mo->momy > 0) { leady = mo->y + mo->radius; traily = mo->y - mo->radius; } else { leady = mo->y - mo->radius; traily = mo->y + mo->radius; } bestslidefrac = FRACUNIT+1; P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy, PT_ADDLINES, PTR_SlideTraverse ); P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy, PT_ADDLINES, PTR_SlideTraverse ); P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy, PT_ADDLINES, PTR_SlideTraverse ); // move up to the wall if (bestslidefrac == FRACUNIT+1) { // the move most have hit the middle, so stairstep stairstep: if (!P_TryMove (mo, mo->x, mo->y + mo->momy)) P_TryMove (mo, mo->x + mo->momx, mo->y); return; } // fudge a bit to make sure it doesn't hit bestslidefrac -= 0x800; if (bestslidefrac > 0) { newx = FixedMul (mo->momx, bestslidefrac); newy = FixedMul (mo->momy, bestslidefrac); if (!P_TryMove (mo, mo->x+newx, mo->y+newy)) goto stairstep; } // Now continue along the wall. // First calculate remainder. bestslidefrac = FRACUNIT-(bestslidefrac+0x800); if (bestslidefrac > FRACUNIT) bestslidefrac = FRACUNIT; if (bestslidefrac <= 0) return; tmxmove = FixedMul (mo->momx, bestslidefrac); tmymove = FixedMul (mo->momy, bestslidefrac); P_HitSlideLine (bestslideline); // clip the moves mo->momx = tmxmove; mo->momy = tmymove; if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove)) { goto retry; } } // // P_LineAttack // mobj_t* linetarget; // who got hit (or NULL) mobj_t* shootthing; // Height if not aiming up or down // ???: use slope for monsters? fixed_t shootz; int la_damage; fixed_t attackrange; fixed_t aimslope; // slopes to top and bottom of target extern fixed_t topslope; extern fixed_t bottomslope; extern degenmobj_t *laserspot; // // PTR_AimTraverse // Sets linetaget and aimslope when a target is aimed at. // boolean PTR_AimTraverse (intercept_t* in) { line_t* li; mobj_t* th; fixed_t slope; fixed_t thingtopslope; fixed_t thingbottomslope; fixed_t dist; if (in->isaline) { li = in->d.line; if ( !(li->flags & ML_TWOSIDED) ) return false; // stop // Crosses a two sided line. // A two sided line will restrict // the possible target ranges. P_LineOpening (li); if (openbottom >= opentop) return false; // stop dist = FixedMul (attackrange, in->frac); if (li->backsector == NULL || li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > bottomslope) bottomslope = slope; } if (li->backsector == NULL || li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv (opentop - shootz , dist); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop return true; // shot continues } // shoot a thing th = in->d.thing; if (th == shootthing) return true; // can't shoot self if (!(th->flags&MF_SHOOTABLE)) return true; // corpse or something // check angles to see if the thing can be aimed at dist = FixedMul (attackrange, in->frac); thingtopslope = FixedDiv (th->z+th->height - shootz , dist); if (thingtopslope < bottomslope) return true; // shot over the thing thingbottomslope = FixedDiv (th->z - shootz, dist); if (thingbottomslope > topslope) return true; // shot under the thing // this thing can be hit! if (thingtopslope > topslope) thingtopslope = topslope; if (thingbottomslope < bottomslope) thingbottomslope = bottomslope; aimslope = (thingtopslope+thingbottomslope)/2; linetarget = th; return false; // don't go any farther } // // PTR_ShootTraverse // boolean PTR_ShootTraverse (intercept_t* in) { fixed_t x; fixed_t y; fixed_t z; fixed_t frac; line_t* li; mobj_t* th; fixed_t slope; fixed_t dist; fixed_t thingtopslope; fixed_t thingbottomslope; fixed_t thingheight; // [crispy] mobj or actual sprite height if (in->isaline) { boolean safe = false; li = in->d.line; // [crispy] laser spot does not shoot any line if (li->special && la_damage > INT_MIN) P_ShootSpecialLine (shootthing, li); if ( !(li->flags & ML_TWOSIDED) ) goto hitline; // crosses a two sided line P_LineOpening (li); dist = FixedMul (attackrange, in->frac); // e6y: emulation of missed back side on two-sided lines. // backsector can be NULL when emulating missing back side. if (li->backsector == NULL) { slope = FixedDiv (openbottom - shootz , dist); if (slope > aimslope) goto hitline; slope = FixedDiv (opentop - shootz , dist); if (slope < aimslope) goto hitline; } else { if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > aimslope) goto hitline; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv (opentop - shootz , dist); if (slope < aimslope) goto hitline; } } // shot continues return true; // hit line hitline: // position a bit closer frac = in->frac - FixedDiv (4*FRACUNIT,attackrange); x = trace.x + FixedMul (trace.dx, frac); y = trace.y + FixedMul (trace.dy, frac); z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); if (li->frontsector->ceilingpic == skyflatnum) { // don't shoot the sky! if (z > li->frontsector->ceilingheight) return false; // it's a sky hack wall if (li->backsector && li->backsector->ceilingpic == skyflatnum) { // [crispy] fix bullet puffs and laser spot not appearing in outdoor areas if (li->backsector->ceilingheight < z) return false; else safe = true; } } // [crispy] check if the pullet puff's z-coordinate is below of above // its spawning sector's floor or ceiling, respectively, and move its // coordinates to the point where the trajectory hits the plane if (aimslope) { const int lineside = P_PointOnLineSide(x, y, li); int side; if ((side = li->sidenum[lineside]) != NO_INDEX) { const sector_t *const sector = sides[side].sector; if (z < sector->floorheight || (z > sector->ceilingheight && sector->ceilingpic != skyflatnum)) { z = BETWEEN(sector->floorheight, sector->ceilingheight, z); frac = FixedDiv(z - shootz, FixedMul(aimslope, attackrange)); x = trace.x + FixedMul (trace.dx, frac); y = trace.y + FixedMul (trace.dy, frac); } } } // [crispy] update laser spot position and return if (la_damage == INT_MIN) { laserspot->thinker.function.acv = (actionf_v) (1); laserspot->x = x; laserspot->y = y; laserspot->z = z; return false; } // Spawn bullet puffs. P_SpawnPuffSafe (x, y, z, safe); // 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); // [crispy] mobj or actual sprite height thingheight = (shootthing->player && critical->freeaim == FREEAIM_DIRECT) ? th->info->actualheight : th->height; thingtopslope = FixedDiv (th->z+thingheight - 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)); // [crispy] update laser spot position and return if (la_damage == INT_MIN) { // [crispy] pass through Spectres if (th->flags & MF_SHADOW) return true; laserspot->thinker.function.acv = (actionf_v) (1); laserspot->x = th->x; laserspot->y = th->y; laserspot->z = z; return false; } // 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); // [crispy] pass thing type if (la_damage) P_DamageMobj (th, shootthing, shootthing, la_damage); // don't go any farther return false; } // // P_AimLineAttack // fixed_t P_AimLineAttack ( mobj_t* t1, angle_t angle, fixed_t distance ) { fixed_t x2; fixed_t y2; 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 = (SCREENHEIGHT/2)*FRACUNIT/(SCREENWIDTH/2); bottomslope = -(SCREENHEIGHT/2)*FRACUNIT/(SCREENWIDTH/2); attackrange = distance; linetarget = NULL; 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. // [crispy] if damage == INT_MIN, it is a trace // to update the laser spot position // void P_LineAttack ( mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage ) { // [crispy] smooth laser spot movement with uncapped framerate const fixed_t t1x = (damage == INT_MIN) ? viewx : t1->x; const fixed_t t1y = (damage == INT_MIN) ? viewy : t1->y; fixed_t x2; fixed_t y2; angle >>= ANGLETOFINESHIFT; shootthing = t1; la_damage = damage; x2 = t1x + (distance>>FRACBITS)*finecosine[angle]; y2 = t1y + (distance>>FRACBITS)*finesine[angle]; shootz = (damage == INT_MIN) ? viewz : t1->z + (t1->height>>1) + 8*FRACUNIT; attackrange = distance; aimslope = slope; P_PathTraverse ( t1x, t1y, x2, y2, PT_ADDLINES|PT_ADDTHINGS, PTR_ShootTraverse ); } // [crispy] update laser spot position // call P_AimLineAttack() to check if a target is aimed at (linetarget) // then call P_LineAttack() with either aimslope or the passed slope void P_LineLaser ( mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope ) { fixed_t lslope; laserspot->thinker.function.acv = (actionf_v) (0); // [crispy] intercepts overflow guard crispy->crosshair |= CROSSHAIR_INTERCEPT; // [crispy] set the linetarget pointer lslope = P_AimLineAttack(t1, angle, distance); if (critical->freeaim == FREEAIM_DIRECT) { lslope = slope; } else { // [crispy] increase accuracy if (!linetarget) { angle_t an = angle; an += 1<<26; lslope = P_AimLineAttack(t1, an, distance); if (!linetarget) { an -= 2<<26; lslope = P_AimLineAttack(t1, an, distance); if (!linetarget && critical->freeaim == FREEAIM_BOTH) { lslope = slope; } } } } if ((crispy->crosshair & ~CROSSHAIR_INTERCEPT) == CROSSHAIR_PROJECTED) { // [crispy] don't aim at Spectres if (linetarget && !(linetarget->flags & MF_SHADOW) && (crispy->freeaim != FREEAIM_DIRECT)) P_LineAttack(t1, angle, distance, aimslope, INT_MIN); else // [crispy] double the auto aim distance P_LineAttack(t1, angle, 2*distance, lslope, INT_MIN); } // [crispy] intercepts overflow guard crispy->crosshair &= ~CROSSHAIR_INTERCEPT; } // // USE LINES // mobj_t* usething; boolean PTR_UseTraverse (intercept_t* in) { int side; if (!in->d.line->special) { P_LineOpening (in->d.line); if (openrange <= 0) { S_StartSound (usething, sfx_noway); // can't use through a wall return false; } // not a special line, but keep checking return true ; } side = 0; if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) side = 1; // return false; // don't use back side P_UseSpecialLine (usething, in->d.line, side); // can't use for than one special line in a row return false; } // // P_UseLines // Looks for special lines in front of the player to activate. // void P_UseLines (player_t* player) { int angle; fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; usething = player->mo; angle = player->mo->angle >> ANGLETOFINESHIFT; x1 = player->mo->x; y1 = player->mo->y; x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); } // // RADIUS ATTACK // mobj_t* bombsource; mobj_t* bombspot; int bombdamage; // // PIT_RadiusAttack // "bombsource" is the creature // that caused the explosion at "bombspot". // boolean PIT_RadiusAttack (mobj_t* thing) { fixed_t dx; fixed_t dy; fixed_t dist; if (!(thing->flags & MF_SHOOTABLE) ) return true; // Boss spider and cyborg // take no damage from concussion. if (thing->type == MT_CYBORG || thing->type == MT_SPIDER) return true; dx = abs(thing->x - bombspot->x); dy = abs(thing->y - bombspot->y); dist = dx>dy ? dx : dy; dist = (dist - thing->radius) >> FRACBITS; if (dist < 0) dist = 0; if (dist >= bombdamage) return true; // out of range if ( P_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)>>MAPBLOCKSHIFT; yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT; xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT; xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT; bombspot = spot; bombsource = source; bombdamage = damage; for (y=yl ; y<=yh ; y++) for (x=xl ; x<=xh ; x++) P_BlockThingsIterator (x, y, PIT_RadiusAttack ); } // // SECTOR HEIGHT CHANGING // After modifying a sectors floor or ceiling height, // call this routine to adjust the positions // of all things that touch the sector. // // If anything doesn't fit anymore, true will be returned. // If crunch is true, they will take damage // as they are being crushed. // If Crunch is false, you should set the sector height back // the way it was and call P_ChangeSector again // to undo the changes. // boolean crushchange; boolean nofit; // // PIT_ChangeSector // boolean PIT_ChangeSector (mobj_t* thing) { mobj_t* mo; if (P_ThingHeightClip (thing)) { // keep checking return true; } // crunch bodies to giblets if (thing->health <= 0) { // [crispy] no blood, no giblets // S_GIBS should be a "safe" state, and so is S_NULL // TODO: Add a check for DEHACKED states P_SetMobjState (thing, (thing->flags & MF_NOBLOOD) ? S_NULL : S_GIBS); if (gameversion > exe_doom_1_2) thing->flags &= ~MF_SOLID; thing->height = 0; thing->radius = 0; // [crispy] connect giblet object with the crushed monster thing->target = thing; // keep checking return true; } // crunch dropped items if (thing->flags & MF_DROPPED) { P_RemoveMobj (thing); // keep checking return true; } if (! (thing->flags & MF_SHOOTABLE) ) { // assume it is bloody gibs or something return true; } nofit = true; if (crushchange && !(leveltime&3) ) { P_DamageMobj(thing,NULL,NULL,10); // spray blood in a random direction mo = P_SpawnMobj (thing->x, thing->y, // [crispy] Lost Souls and Barrels bleed Puffs thing->z + thing->height/2, (thing->flags & MF_NOBLOOD) ? MT_PUFF : MT_BLOOD); mo->momx = P_SubRandom() << 12; mo->momy = P_SubRandom() << 12; // [crispy] connect blood object with the monster that bleeds it mo->target = thing; // [crispy] Spectres bleed spectre blood if (crispy->coloredblood) mo->flags |= (thing->flags & MF_SHADOW); } // keep checking (crush other things) return true; } // // P_ChangeSector // boolean P_ChangeSector ( sector_t* sector, boolean crunch ) { int x; int y; nofit = false; crushchange = crunch; // re-check heights for all things near the moving sector for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++) for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++) P_BlockThingsIterator (x, y, PIT_ChangeSector); return nofit; } // Code to emulate the behavior of Vanilla Doom when encountering an overrun // of the spechit array. This is by Andrey Budko (e6y) and comes from his // PrBoom plus port. A big thanks to Andrey for this. static void SpechitOverrun(line_t *ld) { static unsigned int baseaddr = 0; unsigned int addr; if (baseaddr == 0) { int p; // This is the first time we have had an overrun. Work out // what base address we are going to use. // Allow a spechit value to be specified on the command line. //! // @category compat // @arg // // Use the specified magic value when emulating spechit overruns. // p = M_CheckParmWithArgs("-spechit", 1); if (p > 0) { M_StrToInt(myargv[p+1], (int *) &baseaddr); } else { baseaddr = DEFAULT_SPECHIT_MAGIC; } } // Calculate address used in doom2.exe addr = baseaddr + (ld - lines) * 0x3E; switch(numspechit) { case 9: case 10: case 11: case 12: tmbbox[numspechit-9] = addr; break; case 13: crushchange = addr; break; case 14: nofit = addr; break; default: fprintf(stderr, "SpechitOverrun: Warning: unable to emulate" "an overrun where numspechit=%i\n", numspechit); break; } } crispy-doom-crispy-doom-5.6.4/src/doom/p_maputl.c000066400000000000000000000517221360717211000217550ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2005, 2006 Andrey Budko // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Movement/collision utility functions, // as used by function in p_map.c. // BLOCKMAP Iterator functions, // and some PIT_* functions to use for iteration. // #include #include "i_system.h" // [crispy] I_Realloc() #include "m_bbox.h" #include "doomdef.h" #include "doomstat.h" #include "p_local.h" // State. #include "r_state.h" // // P_AproxDistance // Gives an estimation of distance (not exact) // fixed_t P_AproxDistance ( fixed_t dx, fixed_t dy ) { dx = abs(dx); dy = abs(dy); if (dx < dy) return dx+dy-(dx>>1); return dx+dy-(dy>>1); } // // P_PointOnLineSide // Returns 0 or 1 // int P_PointOnLineSide ( fixed_t x, fixed_t y, line_t* line ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!line->dx) { if (x <= line->v1->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->v1->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->v1->x); dy = (y - line->v1->y); left = FixedMul ( line->dy>>FRACBITS , dx ); right = FixedMul ( dy , line->dx>>FRACBITS ); if (right < left) return 0; // front side return 1; // back side } // // P_BoxOnLineSide // Considers the line to be infinite // Returns side 0 or 1, -1 if box crosses the line. // int P_BoxOnLineSide ( fixed_t* tmbox, line_t* ld ) { int p1 = 0; int p2 = 0; switch (ld->slopetype) { case ST_HORIZONTAL: p1 = tmbox[BOXTOP] > ld->v1->y; p2 = tmbox[BOXBOTTOM] > ld->v1->y; if (ld->dx < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_VERTICAL: p1 = tmbox[BOXRIGHT] < ld->v1->x; p2 = tmbox[BOXLEFT] < ld->v1->x; if (ld->dy < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_POSITIVE: p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); break; case ST_NEGATIVE: p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); break; } if (p1 == p2) return p1; return -1; } // // P_PointOnDivlineSide // Returns 0 or 1. // int P_PointOnDivlineSide ( fixed_t x, fixed_t y, divline_t* line ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!line->dx) { if (x <= line->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->x); dy = (y - line->y); // try to quickly decide by looking at sign bits if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 ) { if ( (line->dy ^ dx) & 0x80000000 ) return 1; // (left is negative) return 0; } left = FixedMul ( line->dy>>8, dx>>8 ); right = FixedMul ( dy>>8 , line->dx>>8 ); if (right < left) return 0; // front side return 1; // back side } // // P_MakeDivline // void P_MakeDivline ( line_t* li, divline_t* dl ) { dl->x = li->v1->x; dl->y = li->v1->y; dl->dx = li->dx; dl->dy = li->dy; } // // P_InterceptVector // Returns the fractional intercept point // along the first divline. // This is only called by the addthings // and addlines traversers. // fixed_t P_InterceptVector ( divline_t* v2, divline_t* v1 ) { #if 1 fixed_t frac; fixed_t num; fixed_t den; den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy ) +FixedMul ( (v2->y - v1->y)>>8, v1->dx ); frac = FixedDiv (num , den); return frac; #else // UNUSED, float debug. float frac; float num; float den; float v1x; float v1y; float v1dx; float v1dy; float v2x; float v2y; float v2dx; float v2dy; v1x = (float)v1->x/FRACUNIT; v1y = (float)v1->y/FRACUNIT; v1dx = (float)v1->dx/FRACUNIT; v1dy = (float)v1->dy/FRACUNIT; v2x = (float)v2->x/FRACUNIT; v2y = (float)v2->y/FRACUNIT; v2dx = (float)v2->dx/FRACUNIT; v2dy = (float)v2->dy/FRACUNIT; den = v1dy*v2dx - v1dx*v2dy; if (den == 0) return 0; // parallel num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; frac = num / den; return frac*FRACUNIT; #endif } // // P_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; void P_LineOpening (line_t* linedef) { sector_t* front; sector_t* back; if (linedef->sidenum[1] == NO_INDEX) // [crispy] extended nodes { // single sided line openrange = 0; return; } front = linedef->frontsector; back = linedef->backsector; if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; if (front->floorheight > back->floorheight) { openbottom = front->floorheight; lowfloor = back->floorheight; } else { openbottom = back->floorheight; lowfloor = front->floorheight; } openrange = opentop - openbottom; } // // THING POSITION SETTING // // // P_UnsetThingPosition // Unlinks a thing from block map and sectors. // On each position change, BLOCKMAP and other // lookups maintaining lists ot things inside // these structures need to be updated. // void P_UnsetThingPosition (mobj_t* thing) { int blockx; int blocky; if ( ! (thing->flags & MF_NOSECTOR) ) { // inert things don't need to be in blockmap? // unlink from subsector if (thing->snext) thing->snext->sprev = thing->sprev; if (thing->sprev) thing->sprev->snext = thing->snext; else thing->subsector->sector->thinglist = thing->snext; } if ( ! (thing->flags & MF_NOBLOCKMAP) ) { // inert things don't need to be in blockmap // unlink from block map if (thing->bnext) thing->bnext->bprev = thing->bprev; if (thing->bprev) thing->bprev->bnext = thing->bnext; else { blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky bnext; } } } } // // P_SetThingPosition // Links a thing into both a block and a subsector // based on it's x y. // Sets thing->subsector properly // void P_SetThingPosition (mobj_t* thing) { subsector_t* ss; sector_t* sec; int blockx; int blocky; mobj_t** link; // link into subsector ss = R_PointInSubsector (thing->x,thing->y); thing->subsector = ss; if ( ! (thing->flags & MF_NOSECTOR) ) { // invisible things don't go into the sector links sec = ss->sector; thing->sprev = NULL; thing->snext = sec->thinglist; if (sec->thinglist) sec->thinglist->sprev = thing; sec->thinglist = thing; } // link into blockmap if ( ! (thing->flags & MF_NOBLOCKMAP) ) { // inert things don't need to be in blockmap blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky < bmapheight) { link = &blocklinks[blocky*bmapwidth+blockx]; thing->bprev = NULL; thing->bnext = *link; if (*link) (*link)->bprev = thing; *link = thing; } else { // thing is off the map thing->bnext = thing->bprev = NULL; } } } // // BLOCK MAP ITERATORS // For each line/thing in the given mapblock, // call the passed PIT_* function. // If the function returns false, // exit with false without checking anything else. // // // P_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. // boolean P_BlockLinesIterator ( int x, int y, boolean(*func)(line_t*) ) { int offset; int32_t* list; // [crispy] BLOCKMAP limit line_t* ld; if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) { return true; } offset = y*bmapwidth+x; offset = *(blockmap+offset); for ( list = blockmaplump+offset ; *list != -1 ; list++) { ld = &lines[*list]; if (ld->validcount == validcount) continue; // line has already been checked ld->validcount = validcount; if ( !func(ld) ) return false; } return true; // everything was checked } // // P_BlockThingsIterator // boolean P_BlockThingsIterator ( int x, int y, boolean(*func)(mobj_t*) ) { mobj_t* mobj; if ( x<0 || y<0 || x>=bmapwidth || y>=bmapheight) { return true; } for (mobj = blocklinks[y*bmapwidth+x] ; mobj ; mobj = mobj->bnext) { if (!func( mobj ) ) return false; } return true; } // // INTERCEPT ROUTINES // static intercept_t* intercepts; // [crispy] remove INTERCEPTS limit intercept_t* intercept_p; // [crispy] remove INTERCEPTS limit // taken from PrBoom+/src/p_maputl.c:422-433 static void check_intercept(void) { static size_t num_intercepts; const size_t offset = intercept_p - intercepts; if (offset >= num_intercepts) { num_intercepts = num_intercepts ? num_intercepts * 2 : MAXINTERCEPTS_ORIGINAL; intercepts = I_Realloc(intercepts, sizeof(*intercepts) * num_intercepts); intercept_p = intercepts + offset; } } divline_t trace; boolean earlyout; int ptflags; static void InterceptsOverrun(int num_intercepts, intercept_t *intercept); // [crispy] show mapthing number in INTERCEPTS overflow warnings extern mobj_t* shootthing; // // PIT_AddLineIntercepts. // Looks for lines in the given block // that intercept the given trace // to add to the intercepts list. // // A line is crossed if its endpoints // are on opposite sides of the trace. // Returns true if earlyout and a solid line hit. // boolean PIT_AddLineIntercepts (line_t* ld) { int s1; int s2; fixed_t frac; divline_t dl; // avoid precision problems with two routines if ( trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16 || trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16) { s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); } else { s1 = P_PointOnLineSide (trace.x, trace.y, ld); s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld); } if (s1 == s2) return true; // line isn't crossed // hit the line P_MakeDivline (ld, &dl); frac = P_InterceptVector (&trace, &dl); if (frac < 0) return true; // behind source // try to early out the check if (earlyout && frac < FRACUNIT && !ld->backsector) { return false; // stop checking } check_intercept(); // [crispy] remove INTERCEPTS limit intercept_p->frac = frac; intercept_p->isaline = true; intercept_p->d.line = ld; InterceptsOverrun(intercept_p - intercepts, intercept_p); // [crispy] intercepts overflow guard if (intercept_p - intercepts == MAXINTERCEPTS_ORIGINAL + 1) { if (crispy->crosshair & CROSSHAIR_INTERCEPT) return false; else // [crispy] print a warning fprintf(stderr, "PIT_AddLineIntercepts: Triggered INTERCEPTS overflow!\n"); } intercept_p++; return true; // continue } // // PIT_AddThingIntercepts // boolean PIT_AddThingIntercepts (mobj_t* thing) { fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; int s1; int s2; boolean tracepositive; divline_t dl; fixed_t frac; tracepositive = (trace.dx ^ trace.dy)>0; // check a corner to corner crossection for hit if (tracepositive) { x1 = thing->x - thing->radius; y1 = thing->y + thing->radius; x2 = thing->x + thing->radius; y2 = thing->y - thing->radius; } else { x1 = thing->x - thing->radius; y1 = thing->y - thing->radius; x2 = thing->x + thing->radius; y2 = thing->y + thing->radius; } s1 = P_PointOnDivlineSide (x1, y1, &trace); s2 = P_PointOnDivlineSide (x2, y2, &trace); if (s1 == s2) return true; // line isn't crossed dl.x = x1; dl.y = y1; dl.dx = x2-x1; dl.dy = y2-y1; frac = P_InterceptVector (&trace, &dl); if (frac < 0) return true; // behind source check_intercept(); // [crispy] remove INTERCEPTS limit intercept_p->frac = frac; intercept_p->isaline = false; intercept_p->d.thing = thing; InterceptsOverrun(intercept_p - intercepts, intercept_p); // [crispy] intercepts overflow guard if (intercept_p - intercepts == MAXINTERCEPTS_ORIGINAL + 1) { if (crispy->crosshair & CROSSHAIR_INTERCEPT) return false; else // [crispy] print a warning fprintf(stderr, "PIT_AddThingIntercepts: Triggered INTERCEPTS overflow!\n"); } intercept_p++; return true; // keep going } // // P_TraverseIntercepts // Returns true if the traverser function returns true // for all lines. // boolean P_TraverseIntercepts ( traverser_t func, fixed_t maxfrac ) { int count; fixed_t dist; intercept_t* scan; intercept_t* in; count = intercept_p - intercepts; in = 0; // shut up compiler warning while (count--) { dist = INT_MAX; for (scan = intercepts ; scanfrac < dist) { dist = scan->frac; in = scan; } } if (dist > maxfrac) return true; // checked everything in range #if 0 // UNUSED { // don't check these yet, there may be others inserted in = scan = intercepts; for ( scan = intercepts ; scanfrac > maxfrac) *in++ = *scan; intercept_p = in; return false; } #endif if ( !func (in) ) return false; // don't bother going farther in->frac = INT_MAX; } return true; // everything was traversed } extern fixed_t bulletslope; // Intercepts Overrun emulation, from PrBoom-plus. // Thanks to Andrey Budko (entryway) for researching this and his // implementation of Intercepts Overrun emulation in PrBoom-plus // which this is based on. typedef struct { int len; void *addr; boolean int16_array; } intercepts_overrun_t; // Intercepts memory table. This is where various variables are located // in memory in Vanilla Doom. When the intercepts table overflows, we // need to write to them. // // Almost all of the values to overwrite are 32-bit integers, except for // playerstarts, which is effectively an array of 16-bit integers and // must be treated differently. static intercepts_overrun_t 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}, }; // 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; } } // Emulate overruns of the intercepts[] array. static void InterceptsOverrun(int num_intercepts, intercept_t *intercept) { int location; if (num_intercepts <= MAXINTERCEPTS_ORIGINAL) { // No overrun return; } 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, (intptr_t) intercept->d.thing); } // // 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. // boolean P_PathTraverse ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean (*trav) (intercept_t *)) { fixed_t xt1; fixed_t yt1; fixed_t xt2; fixed_t yt2; fixed_t xstep; fixed_t ystep; fixed_t partial; fixed_t xintercept; fixed_t yintercept; int mapx; int mapy; int mapxstep; int mapystep; int count; earlyout = (flags & PT_EARLYOUT) != 0; validcount++; intercept_p = intercepts; if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) y1 += FRACUNIT; // don't side exactly on a line trace.x = x1; trace.y = y1; trace.dx = x2 - x1; trace.dy = y2 - y1; x1 -= bmaporgx; y1 -= bmaporgy; xt1 = x1>>MAPBLOCKSHIFT; yt1 = y1>>MAPBLOCKSHIFT; x2 -= bmaporgx; y2 -= bmaporgy; xt2 = x2>>MAPBLOCKSHIFT; yt2 = y2>>MAPBLOCKSHIFT; if (xt2 > xt1) { mapxstep = 1; partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); ystep = 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 (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 ); } crispy-doom-crispy-doom-5.6.4/src/doom/p_mobj.c000066400000000000000000000670471360717211000214110ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Moving object handling. Spawn functions. // #include #include "i_system.h" #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" #include "sounds.h" #include "st_stuff.h" #include "hu_stuff.h" #include "s_sound.h" #include "s_musinfo.h" // [crispy] S_ParseMusInfo() #include "doomstat.h" void G_PlayerReborn (int player); void P_SpawnMapThing (mapthing_t* mthing); // // P_SetMobjState // Returns true if the mobj is still present. // int test; // Use a heuristic approach to detect infinite state cycles: Count the number // of times the loop in P_SetMobjState() executes and exit with an error once // an arbitrary very large limit is reached. #define MOBJ_CYCLE_LIMIT 1000000 boolean P_SetMobjState ( mobj_t* mobj, statenum_t state ) { state_t* st; int cycle_counter = 0; do { if (state == S_NULL) { mobj->state = (state_t *) S_NULL; P_RemoveMobj (mobj); return false; } st = &states[state]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // Modified handling. // Call action functions when the state is set if (st->action.acp3) st->action.acp3(mobj, NULL, NULL); // [crispy] let pspr action pointers get called from mobj states state = st->nextstate; if (cycle_counter++ > MOBJ_CYCLE_LIMIT) { I_Error("P_SetMobjState: Infinite state cycle detected!"); } } while (!mobj->tics); return true; } // [crispy] return the latest "safe" state in a state sequence, // so that no action pointer is ever called static statenum_t P_LatestSafeState(statenum_t state) { statenum_t safestate = S_NULL; static statenum_t laststate, lastsafestate; if (state == laststate) { return lastsafestate; } for (laststate = state; state != S_NULL; state = states[state].nextstate) { if (safestate == S_NULL) { safestate = state; } if (states[state].action.acp1) { safestate = S_NULL; } // [crispy] a state with -1 tics never changes if (states[state].tics == -1 || state == states[state].nextstate) { break; } } return lastsafestate = safestate; } // // P_ExplodeMissile // static void P_ExplodeMissileSafe (mobj_t* mo, boolean safe) { mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, safe ? P_LatestSafeState(mobjinfo[mo->type].deathstate) : mobjinfo[mo->type].deathstate); mo->tics -= safe ? Crispy_Random()&3 : P_Random()&3; if (mo->tics < 1) mo->tics = 1; mo->flags &= ~MF_MISSILE; // [crispy] missile explosions are translucent mo->flags |= MF_TRANSLUCENT; if (mo->info->deathsound) S_StartSound (mo, mo->info->deathsound); } void P_ExplodeMissile (mobj_t* mo) { return P_ExplodeMissileSafe(mo, false); } // // P_XYMovement // #define STOPSPEED 0x1000 #define FRICTION 0xe800 void P_XYMovement (mobj_t* mo) { fixed_t ptryx; fixed_t ptryy; player_t* player; fixed_t xmove; fixed_t ymove; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, mo->info->spawnstate); } return; } player = mo->player; if (mo->momx > MAXMOVE) mo->momx = MAXMOVE; else if (mo->momx < -MAXMOVE) mo->momx = -MAXMOVE; if (mo->momy > MAXMOVE) mo->momy = MAXMOVE; else if (mo->momy < -MAXMOVE) mo->momy = -MAXMOVE; xmove = mo->momx; ymove = mo->momy; do { if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2) { ptryx = mo->x + xmove/2; ptryy = mo->y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } if (!P_TryMove (mo, ptryx, ptryy)) { // blocked move if (mo->player) { // try to slide along it P_SlideMove (mo); } else if (mo->flags & MF_MISSILE) { boolean safe = false; // explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) { if (mo->z > ceilingline->backsector->ceilingheight) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. P_RemoveMobj (mo); return; } else { safe = true; } } P_ExplodeMissileSafe (mo, safe); } else mo->momx = mo->momy = 0; } } while (xmove || ymove); // slow down if (player && player->cheats & CF_NOMOMENTUM) { // debug option for no sliding at all mo->momx = mo->momy = 0; return; } if (mo->flags & (MF_MISSILE | MF_SKULLFLY) ) return; // no friction for missiles ever if (mo->z > mo->floorz) return; // no friction when airborne if (mo->flags & MF_CORPSE) { // do not stop sliding // if halfway off a step with some momentum if (mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 || mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) { if (mo->floorz != mo->subsector->sector->floorheight) return; } } if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && mo->momy > -STOPSPEED && mo->momy < STOPSPEED && (!player || (player->cmd.forwardmove== 0 && player->cmd.sidemove == 0 ) ) ) { // if in a walking frame, stop moving if ( player&&(unsigned)((player->mo->state - states)- S_PLAY_RUN1) < 4) P_SetMobjState (player->mo, S_PLAY); mo->momx = 0; mo->momy = 0; } else { mo->momx = FixedMul (mo->momx, FRICTION); mo->momy = FixedMul (mo->momy, FRICTION); } } // // P_ZMovement // void P_ZMovement (mobj_t* mo) { fixed_t dist; fixed_t delta; // check for smooth step up if (mo->player && mo->z < mo->floorz) { mo->player->viewheight -= mo->floorz-mo->z; mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; } // adjust height mo->z += mo->momz; if ( mo->flags & MF_FLOAT && mo->target) { // float down towards target if too close if ( !(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT) ) { dist = P_AproxDistance (mo->x - mo->target->x, mo->y - mo->target->y); delta =(mo->target->z + (mo->height>>1)) - mo->z; if (delta<0 && dist < -(delta*3) ) mo->z -= FLOATSPEED; else if (delta>0 && dist < (delta*3) ) mo->z += FLOATSPEED; } } // clip movement if (mo->z <= mo->floorz) { // hit the floor // Note (id): // somebody left this after the setting momz to 0, // kinda useless there. // // cph - This was the a bug in the linuxdoom-1.10 source which // caused it not to sync Doom 2 v1.9 demos. Someone // added the above comment and moved up the following code. So // demos would desync in close lost soul fights. // Note that this only applies to original Doom 1 or Doom2 demos - not // Final Doom and Ultimate Doom. So we test demo_compatibility *and* // gamemission. (Note we assume that Doom1 is always Ult Doom, which // seems to hold for most published demos.) // // fraggle - cph got the logic here slightly wrong. There are three // versions of Doom 1.9: // // * The version used in registered doom 1.9 + doom2 - no bounce // * The version used in ultimate doom - has bounce // * The version used in final doom - has bounce // // So we need to check that this is either retail or commercial // (but not doom2) int correct_lost_soul_bounce = gameversion >= exe_ultimate; if (correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } if (mo->momz < 0) { // [crispy] delay next jump if (mo->player) mo->player->jumpTics = 7; if (mo->player && mo->momz < -GRAVITY*8) { // Squat down. // Decrease viewheight for a moment // after hitting the ground (hard), // and utter appropriate sound. mo->player->deltaviewheight = mo->momz>>3; // [crispy] squat down weapon sprite as well if (crispy->weaponsquat) { mo->player->psp_dy_max = mo->momz>>2; } // [crispy] center view if not using permanent mouselook if (!crispy->mouselook) mo->player->centering = true; // [crispy] dead men don't say "oof" if (mo->health > 0 || !crispy->soundfix) { S_StartSound (mo, sfx_oof); } } mo->momz = 0; } mo->z = mo->floorz; // cph 2001/05/26 - // See lost soul bouncing comment above. We need this here for bug // compatibility with original Doom2 v1.9 - if a soul is charging and // hit by a raising floor this incorrectly reverses its Y momentum. // if (!correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) mo->momz = -mo->momz; if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) { P_ExplodeMissile (mo); return; } } else if (! (mo->flags & MF_NOGRAVITY) ) { if (mo->momz == 0) mo->momz = -GRAVITY*2; else mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // hit the ceiling if (mo->momz > 0) mo->momz = 0; { mo->z = mo->ceilingz - mo->height; } if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) { P_ExplodeMissile (mo); return; } } } // // P_NightmareRespawn // void P_NightmareRespawn (mobj_t* mobj) { fixed_t x; fixed_t y; fixed_t z; subsector_t* ss; mobj_t* mo; mapthing_t* mthing; x = mobj->spawnpoint.x << FRACBITS; y = mobj->spawnpoint.y << FRACBITS; // somthing is occupying it's position? if (!P_CheckPosition (mobj, x, y) ) return; // no respwan // spawn a teleport fog at old spot // because of removal of the body? mo = P_SpawnMobj (mobj->x, mobj->y, mobj->subsector->sector->floorheight , MT_TFOG); // initiate teleport sound S_StartSound (mo, sfx_telept); // spawn a teleport fog at the new spot ss = R_PointInSubsector (x,y); mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG); S_StartSound (mo, sfx_telept); // spawn the new monster mthing = &mobj->spawnpoint; // spawn it if (mobj->info->flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; // inherit attributes from deceased one mo = P_SpawnMobj (x,y,z, mobj->type); mo->spawnpoint = mobj->spawnpoint; mo->angle = ANG45 * (mthing->angle/45); // [crispy] count respawned monsters extrakills++; if (mthing->options & MTF_AMBUSH) mo->flags |= MF_AMBUSH; mo->reactiontime = 18; // remove the old monster, P_RemoveMobj (mobj); } // [crispy] support MUSINFO lump (dynamic music changing) static inline 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 = leveltime ? 30 : 0; } } // // P_MobjThinker // void P_MobjThinker (mobj_t* mobj) { // [crispy] support MUSINFO lump (dynamic music changing) if (mobj->type == MT_MUSICSOURCE) { return MusInfoThinker(mobj); } // [crispy] suppress interpolation of player missiles for the first tic if (mobj->interp == -1) { mobj->interp = false; } else // [AM] Handle interpolation unless we're an active player. if (!(mobj->player != NULL && mobj == mobj->player->mo)) { // Assume we can interpolate at the beginning // of the tic. mobj->interp = true; // Store starting position for mobj interpolation. mobj->oldx = mobj->x; mobj->oldy = mobj->y; mobj->oldz = mobj->z; mobj->oldangle = mobj->angle; } // momentum movement if (mobj->momx || mobj->momy || (mobj->flags&MF_SKULLFLY) ) { P_XYMovement (mobj); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj->thinker.function.acv == (actionf_v) (-1)) return; // mobj was removed } if ( (mobj->z != mobj->floorz) || mobj->momz ) { P_ZMovement (mobj); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj->thinker.function.acv == (actionf_v) (-1)) return; // mobj was removed } // cycle through states, // calling action functions at transitions if (mobj->tics != -1) { mobj->tics--; // you can cycle through multiple states in a tic if (!mobj->tics) if (!P_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*TICRATE) return; if ( leveltime&31 ) return; if (P_Random () > 4) return; P_NightmareRespawn (mobj); } } // // P_SpawnMobj // static mobj_t* P_SpawnMobjSafe ( fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, boolean safe ) { mobj_t* mobj; state_t* st; mobjinfo_t* info; mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL); memset (mobj, 0, sizeof (*mobj)); info = &mobjinfo[type]; mobj->type = type; mobj->info = info; mobj->x = x; mobj->y = y; mobj->radius = info->radius; mobj->height = info->height; mobj->flags = info->flags; mobj->health = info->spawnhealth; if (gameskill != sk_nightmare) mobj->reactiontime = info->reactiontime; mobj->lastlook = safe ? Crispy_Random () % MAXPLAYERS : P_Random () % MAXPLAYERS; // do not set the state with P_SetMobjState, // because action routines can not be called yet st = &states[safe ? P_LatestSafeState(info->spawnstate) : info->spawnstate]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // set subsector and/or block links P_SetThingPosition (mobj); mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; if (z == ONFLOORZ) mobj->z = mobj->floorz; else if (z == ONCEILINGZ) mobj->z = mobj->ceilingz - mobj->info->height; else mobj->z = z; // [crispy] randomly flip corpse, blood and death animation sprites if (mobj->flags & MF_FLIPPABLE && !(mobj->flags & MF_SHOOTABLE)) { mobj->health = (mobj->health & (int)~1) - (Crispy_Random() & 1); } // [AM] Do not interpolate on spawn. mobj->interp = false; // [AM] Just in case interpolation is attempted... mobj->oldx = mobj->x; mobj->oldy = mobj->y; mobj->oldz = mobj->z; mobj->oldangle = mobj->angle; mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; P_AddThinker (&mobj->thinker); return mobj; } mobj_t* P_SpawnMobj ( fixed_t x, fixed_t y, fixed_t z, mobjtype_t type ) { return P_SpawnMobjSafe(x, y, z, type, false); } // // P_RemoveMobj // mapthing_t itemrespawnque[ITEMQUESIZE]; int itemrespawntime[ITEMQUESIZE]; int iquehead; int iquetail; 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); // [crispy] removed map objects may finish their sounds if (crispy->soundfull) { S_UnlinkSound(mobj); } else { // stop any playing sound S_StopSound (mobj); } // free block P_RemoveThinker ((thinker_t*)mobj); } // // P_RespawnSpecials // void P_RespawnSpecials (void) { fixed_t x; fixed_t y; fixed_t z; subsector_t* ss; mobj_t* mo; mapthing_t* mthing; int i; // only respawn items in deathmatch // AX: deathmatch 3 is a Crispy-specific change if (deathmatch != 2 && deathmatch != 3) return; // // nothing left to respawn? if (iquehead == iquetail) return; // wait at least 30 seconds if (leveltime - itemrespawntime[iquetail] < 30*TICRATE) return; mthing = &itemrespawnque[iquetail]; x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; // spawn a teleport fog at the new spot ss = R_PointInSubsector (x,y); mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); S_StartSound (mo, sfx_itmbk); // find which type to spawn for (i=0 ; i< NUMMOBJTYPES ; i++) { if (mthing->type == mobjinfo[i].doomednum) break; } if (i >= NUMMOBJTYPES) { I_Error("P_RespawnSpecials: Failed to find mobj type with doomednum " "%d when respawning thing. This would cause a buffer overrun " "in vanilla Doom", mthing->type); } // spawn it if (mobjinfo[i].flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; mo = P_SpawnMobj (x,y,z, i); mo->spawnpoint = *mthing; mo->angle = ANG45 * (mthing->angle/45); // pull it from the que iquetail = (iquetail+1)&(ITEMQUESIZE-1); } // [crispy] weapon sound sources degenmobj_t muzzles[MAXPLAYERS]; mobj_t *Crispy_PlayerSO (int p) { return crispy->soundfull ? (mobj_t *) &muzzles[p] : players[p].mo; } // // P_SpawnPlayer // Called when a player is spawned on the level. // Most of the player structure stays unchanged // between levels. // void P_SpawnPlayer (mapthing_t* mthing) { player_t* p; fixed_t x; fixed_t y; fixed_t z; mobj_t* mobj; int i; if (mthing->type == 0) { return; } // not playing? if (!playeringame[mthing->type-1]) return; p = &players[mthing->type-1]; if (p->playerstate == PST_REBORN) G_PlayerReborn (mthing->type-1); x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; z = ONFLOORZ; mobj = P_SpawnMobj (x,y,z, MT_PLAYER); // set color translations for player sprites if (mthing->type > 1) mobj->flags |= (mthing->type-1)<angle = ANG45 * (mthing->angle/45); mobj->player = p; mobj->health = p->health; p->mo = mobj; p->playerstate = PST_LIVE; p->refire = 0; p->message = NULL; p->damagecount = 0; p->bonuscount = 0; p->extralight = 0; p->fixedcolormap = 0; p->viewheight = VIEWHEIGHT; // [crispy] weapon sound source p->so = Crispy_PlayerSO(mthing->type-1); // setup gun psprite P_SetupPsprites (p); // give all cards in death match mode if (deathmatch) for (i=0 ; icards[i] = true; if (mthing->type-1 == consoleplayer) { // wake up the status bar ST_Start (); // wake up the heads up text HU_Start (); } } // // P_SpawnMapThing // The fields of the mapthing should // already be in host byte order. // void P_SpawnMapThing (mapthing_t* mthing) { int i; int bit; mobj_t* mobj; fixed_t x; fixed_t y; fixed_t z; int musid = 0; // count deathmatch start positions if (mthing->type == 11) { if (deathmatch_p < &deathmatchstarts[10]) { memcpy (deathmatch_p, mthing, sizeof(*mthing)); deathmatch_p++; } return; } if (mthing->type <= 0) { // Thing type 0 is actually "player -1 start". // For some reason, Vanilla Doom accepts/ignores this. return; } // check for players specially if (mthing->type <= 4) { // save spots for respawning in network games playerstarts[mthing->type-1] = *mthing; playerstartsingame[mthing->type-1] = true; if (!deathmatch) P_SpawnPlayer (mthing); return; } // check for apropriate skill level if (!netgame && (mthing->options & 16) ) return; if (gameskill == sk_baby) bit = 1; else if (gameskill == sk_nightmare) bit = 4; else bit = 1<<(gameskill-1); // [crispy] warn about mapthings without any skill tag set if (!(mthing->options & (MTF_EASY|MTF_NORMAL|MTF_HARD))) { fprintf(stderr, "P_SpawnMapThing: Mapthing type %i without any skill tag at (%i, %i)\n", mthing->type, mthing->x, mthing->y); } if (!(mthing->options & bit) ) return; // [crispy] support MUSINFO lump (dynamic music changing) if (mthing->type >= 14100 && mthing->type <= 14164) { musid = mthing->type - 14100; mthing->type = mobjinfo[MT_MUSICSOURCE].doomednum; } // find which type to spawn for (i=0 ; i< NUMMOBJTYPES ; i++) if (mthing->type == mobjinfo[i].doomednum) break; if (i==NUMMOBJTYPES) { // [crispy] ignore unknown map things fprintf (stderr, "P_SpawnMapThing: Unknown type %i at (%i, %i)\n", mthing->type, mthing->x, mthing->y); return; } // don't spawn keycards and players in deathmatch if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) return; // don't spawn any monsters if -nomonsters if (nomonsters && ( i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL)) ) { return; } // spawn it x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; if (mobjinfo[i].flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; mobj = P_SpawnMobj (x,y,z, i); mobj->spawnpoint = *mthing; if (mobj->tics > 0) mobj->tics = 1 + (P_Random () % mobj->tics); if (mobj->flags & MF_COUNTKILL) totalkills++; if (mobj->flags & MF_COUNTITEM) totalitems++; mobj->angle = ANG45 * (mthing->angle/45); if (mthing->options & MTF_AMBUSH) mobj->flags |= MF_AMBUSH; // [crispy] support MUSINFO lump (dynamic music changing) if (i == MT_MUSICSOURCE) { mobj->health = 1000 + musid; } // [crispy] Lost Souls bleed Puffs if (crispy->coloredblood && i == MT_SKULL) mobj->flags |= MF_NOBLOOD; // [crispy] randomly colorize space marine corpse objects if (!netgame && crispy->coloredblood && (mobj->info->spawnstate == S_PLAY_DIE7 || mobj->info->spawnstate == S_PLAY_XDIE9)) { mobj->flags |= (Crispy_Random() & 3) << MF_TRANSSHIFT; } // [crispy] blinking key or skull in the status bar if (mobj->sprite == SPR_BSKU) st_keyorskull[it_bluecard] = 3; else if (mobj->sprite == SPR_RSKU) st_keyorskull[it_redcard] = 3; else if (mobj->sprite == SPR_YSKU) st_keyorskull[it_yellowcard] = 3; } // // GAME SPAWN FUNCTIONS // // // P_SpawnPuff // extern fixed_t attackrange; void P_SpawnPuff ( fixed_t x, fixed_t y, fixed_t z ) { return P_SpawnPuffSafe(x, y, z, false); } void P_SpawnPuffSafe ( fixed_t x, fixed_t y, fixed_t z, boolean safe ) { mobj_t* th; z += safe ? (Crispy_SubRandom() << 10) : (P_SubRandom() << 10); th = P_SpawnMobjSafe (x,y,z, MT_PUFF, safe); th->momz = FRACUNIT; th->tics -= safe ? Crispy_Random()&3 : P_Random()&3; if (th->tics < 1) th->tics = 1; // don't make punches spark on the wall if (attackrange == MELEERANGE) P_SetMobjState (th, safe ? P_LatestSafeState(S_PUFF3) : S_PUFF3); } // // P_SpawnBlood // void P_SpawnBlood ( fixed_t x, fixed_t y, fixed_t z, int damage, mobj_t* target ) // [crispy] pass thing type { mobj_t* th; z += (P_SubRandom() << 10); th = P_SpawnMobj (x,y,z, MT_BLOOD); th->momz = FRACUNIT*2; th->tics -= P_Random()&3; if (th->tics < 1) th->tics = 1; if (damage <= 12 && damage >= 9) P_SetMobjState (th,S_BLOOD2); else if (damage < 9) P_SetMobjState (th,S_BLOOD3); // [crispy] connect blood object with the monster that bleeds it th->target = target; // [crispy] Spectres bleed spectre blood if (crispy->coloredblood) th->flags |= (target->flags & MF_SHADOW); } // // P_CheckMissileSpawn // Moves the missile forward a bit // and possibly explodes it right there. // void P_CheckMissileSpawn (mobj_t* th) { th->tics -= P_Random()&3; if (th->tics < 1) th->tics = 1; // move a little forward so an angle can // be computed if it immediately explodes th->x += (th->momx>>1); th->y += (th->momy>>1); th->z += (th->momz>>1); if (!P_TryMove (th, th->x, th->y)) P_ExplodeMissile (th); } // 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_SpawnMissile // mobj_t* P_SpawnMissile ( mobj_t* source, mobj_t* dest, mobjtype_t type ) { mobj_t* th; angle_t an; int dist; th = P_SpawnMobj (source->x, source->y, source->z + 4*8*FRACUNIT, type); if (th->info->seesound) S_StartSound (th, th->info->seesound); th->target = source; // where it came from an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); // fuzzy player if (dest->flags & MF_SHADOW) an += P_SubRandom() << 20; th->angle = an; an >>= ANGLETOFINESHIFT; th->momx = FixedMul (th->info->speed, finecosine[an]); th->momy = FixedMul (th->info->speed, finesine[an]); dist = P_AproxDistance (dest->x - source->x, dest->y - source->y); dist = dist / th->info->speed; if (dist < 1) dist = 1; th->momz = (dest->z - source->z) / dist; P_CheckMissileSpawn (th); return th; } // // P_SpawnPlayerMissile // Tries to aim at a nearby monster // void P_SpawnPlayerMissile ( mobj_t* source, mobjtype_t type ) { mobj_t* th; angle_t an; fixed_t x; fixed_t y; fixed_t z; fixed_t slope; extern void A_Recoil (player_t* player); // see which target is to be aimed at an = source->angle; if (critical->freeaim == FREEAIM_DIRECT) { slope = PLAYER_SLOPE(source->player); } else { slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); } if (!linetarget) { an = source->angle; if (critical->freeaim == FREEAIM_BOTH) slope = PLAYER_SLOPE(source->player); else slope = 0; } } } x = source->x; y = source->y; z = source->z + 4*8*FRACUNIT; th = P_SpawnMobj (x,y,z, type); if (th->info->seesound) S_StartSound (th, th->info->seesound); th->target = source; th->angle = an; th->momx = FixedMul( th->info->speed, finecosine[an>>ANGLETOFINESHIFT]); th->momy = FixedMul( th->info->speed, finesine[an>>ANGLETOFINESHIFT]); th->momz = FixedMul( th->info->speed, slope); // [crispy] suppress interpolation of player missiles for the first tic th->interp = -1; P_CheckMissileSpawn (th); A_Recoil (source->player); } crispy-doom-crispy-doom-5.6.4/src/doom/p_mobj.h000066400000000000000000000216431360717211000214060ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Map Objects, MObj, definition and handling. // #ifndef __P_MOBJ__ #define __P_MOBJ__ // Basics. #include "tables.h" #include "m_fixed.h" // We need the thinker_t stuff. #include "d_think.h" // We need the WAD data structure for Map things, // from the THINGS lump. #include "doomdata.h" // States are tied to finite states are // tied to animation frames. // Needs precompiled tables/data structures. #include "info.h" // // NOTES: mobj_t // // mobj_ts are used to tell the refresh where to draw an image, // tell the world simulation when objects are contacted, // and tell the sound driver how to position a sound. // // The refresh uses the next and prev links to follow // lists of things in sectors as they are being drawn. // The sprite, frame, and angle elements determine which patch_t // is used to draw the sprite if it is visible. // The sprite and frame values are allmost allways set // from state_t structures. // The statescr.exe utility generates the states.h and states.c // files that contain the sprite/frame numbers from the // statescr.txt source file. // The xyz origin point represents a point at the bottom middle // of the sprite (between the feet of a biped). // This is the default origin position for patch_ts grabbed // with lumpy.exe. // A walking creature will have its z equal to the floor // it is standing on. // // The sound code uses the x,y, and subsector fields // to do stereo positioning of any sound effited by the mobj_t. // // The play simulation uses the blocklinks, x,y,z, radius, height // to determine when mobj_ts are touching each other, // touching lines in the map, or hit by trace lines (gunshots, // lines of sight, etc). // The mobj_t->flags element has various bit flags // used by the simulation. // // Every mobj_t is linked into a single sector // based on its origin coordinates. // The subsector_t is found with R_PointInSubsector(x,y), // and the sector_t can be found with subsector->sector. // The sector links are only used by the rendering code, // the play simulation does not care about them at all. // // Any mobj_t that needs to be acted upon by something else // in the play world (block movement, be shot, etc) will also // need to be linked into the blockmap. // If the thing has the MF_NOBLOCK flag set, it will not use // the block links. It can still interact with other things, // but only as the instigator (missiles will run into other // things, but nothing can run into a missile). // Each block in the grid is 128*128 units, and knows about // every line_t that it contains a piece of, and every // interactable mobj_t that has its origin contained. // // A valid mobj_t is a mobj_t that has the proper subsector_t // filled in for its xy coordinates and is linked into the // sector from which the subsector was made, or has the // MF_NOSECTOR flag set (the subsector_t needs to be valid // even if MF_NOSECTOR is set), and is linked into a blockmap // block or has the MF_NOBLOCKMAP flag set. // Links should only be modified by the P_[Un]SetThingPosition() // functions. // Do not change the MF_NO? flags while a thing is valid. // // Any questions? // // // Misc. mobj flags // typedef enum { // Call P_SpecialThing when touched. MF_SPECIAL = 1, // Blocks. MF_SOLID = 2, // Can be hit. MF_SHOOTABLE = 4, // Don't use the sector links (invisible but touchable). MF_NOSECTOR = 8, // Don't use the blocklinks (inert but displayable) MF_NOBLOCKMAP = 16, // Not to be activated by sound, deaf monster. MF_AMBUSH = 32, // Will try to attack right back. MF_JUSTHIT = 64, // Will take at least one step before attacking. MF_JUSTATTACKED = 128, // On level spawning (initial position), // hang from ceiling instead of stand on floor. MF_SPAWNCEILING = 256, // Don't apply gravity (every tic), // that is, object will float, keeping current height // or changing it actively. MF_NOGRAVITY = 512, // Movement flags. // This allows jumps from high places. MF_DROPOFF = 0x400, // For players, will pick up items. MF_PICKUP = 0x800, // Player cheat. ??? MF_NOCLIP = 0x1000, // Player: keep info about sliding along walls. MF_SLIDE = 0x2000, // Allow moves to any height, no gravity. // For active floaters, e.g. cacodemons, pain elementals. MF_FLOAT = 0x4000, // Don't cross lines // ??? or look at heights on teleport. MF_TELEPORT = 0x8000, // Don't hit same species, explode on block. // Player missiles as well as fireballs of various kinds. MF_MISSILE = 0x10000, // Dropped by a demon, not level spawned. // E.g. ammo clips dropped by dying former humans. MF_DROPPED = 0x20000, // Use fuzzy draw (shadow demons or spectres), // temporary player invisibility powerup. MF_SHADOW = 0x40000, // Flag: don't bleed when shot (use puff), // barrels and shootable furniture shall not bleed. MF_NOBLOOD = 0x80000, // Don't stop moving halfway off a step, // that is, have dead bodies slide down all the way. MF_CORPSE = 0x100000, // Floating to a height for a move, ??? // don't auto float to target's height. MF_INFLOAT = 0x200000, // On kill, count this enemy object // towards intermission kill total. // Happy gathering. MF_COUNTKILL = 0x400000, // On picking up, count this item object // towards intermission item total. MF_COUNTITEM = 0x800000, // Special handling: skull in flight. // Neither a cacodemon nor a missile. MF_SKULLFLY = 0x1000000, // Don't spawn this object // in death match mode (e.g. key cards). MF_NOTDMATCH = 0x2000000, // Player sprites in multiplayer modes are modified // using an internal color lookup table for re-indexing. // If 0x4 0x8 or 0xc, // use a translation table for player colormaps MF_TRANSLATION = 0xc000000, // Hmm ???. MF_TRANSSHIFT = 26, // [crispy] randomly flip corpse, blood and death animation sprites MF_FLIPPABLE = 0x40000000, // [crispy] translucent sprite MF_TRANSLUCENT = 0x80000000 } mobjflag_t; // Map Object definition. typedef struct mobj_s { // List: thinker links. thinker_t thinker; // Info for drawing: position. fixed_t x; fixed_t y; fixed_t z; // More list: links in sector (if needed) struct mobj_s* snext; struct mobj_s* sprev; //More drawing info: to determine current sprite. angle_t angle; // orientation spritenum_t sprite; // used to find patch_t and flip value int frame; // might be ORed with FF_FULLBRIGHT // Interaction info, by BLOCKMAP. // Links in blocks (if needed). struct mobj_s* bnext; struct mobj_s* bprev; struct subsector_s* subsector; // The closest interval over all contacted Sectors. fixed_t floorz; fixed_t ceilingz; // For movement checking. fixed_t radius; fixed_t height; // Momentums, used to update position. fixed_t momx; fixed_t momy; fixed_t momz; // If == validcount, already checked. int validcount; mobjtype_t type; mobjinfo_t* info; // &mobjinfo[mobj->type] int tics; // state tic counter state_t* state; int flags; int health; // Movement direction, movement generation (zig-zagging). int movedir; // 0-7 int movecount; // when 0, select a new dir // Thing being chased/attacked (or NULL), // also the originator for missiles. struct mobj_s* target; // Reaction time: if non 0, don't attack yet. // Used by player to freeze a bit after teleporting. int reactiontime; // If >0, the target will be chased // no matter what (even if shot) int threshold; // Additional info record for player avatars only. // Only valid if type == MT_PLAYER struct player_s* player; // Player number last looked for. int lastlook; // For nightmare respawn. mapthing_t spawnpoint; // Thing being chased/attacked for tracers. struct mobj_s* tracer; // [AM] If true, ok to interpolate this tic. int interp; // [AM] Previous position of mobj before think. // Used to interpolate between positions. fixed_t oldx; fixed_t oldy; fixed_t oldz; angle_t oldangle; } mobj_t; #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_plats.c000066400000000000000000000143471360717211000216000ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Plats (i.e. elevator platforms) code, raising/lowering. // #include #include "i_system.h" #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" plat_t* activeplats[MAXPLATS]; // // Move a plat up and down // void T_PlatRaise(plat_t* plat) { result_e res; switch(plat->status) { case up: res = T_MovePlane(plat->sector, plat->speed, plat->high, plat->crush,0,1); if (plat->type == raiseAndChange || plat->type == raiseToNearestAndChange) { if (!(leveltime&7)) S_StartSound(&plat->sector->soundorg, sfx_stnmov); } if (res == crushed && (!plat->crush)) { plat->count = plat->wait; plat->status = down; S_StartSound(&plat->sector->soundorg, sfx_pstart); } else { if (res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg, sfx_pstop); switch(plat->type) { case blazeDWUS: case downWaitUpStay: P_RemoveActivePlat(plat); break; case raiseAndChange: case raiseToNearestAndChange: P_RemoveActivePlat(plat); break; default: break; } } } break; case down: res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1); if (res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg,sfx_pstop); } break; case waiting: if (!--plat->count) { if (plat->sector->floorheight == plat->low) plat->status = up; else plat->status = down; S_StartSound(&plat->sector->soundorg,sfx_pstart); } case in_stasis: break; } } // // Do Platforms // "amount" is only used for SOME platforms. // int EV_DoPlat ( line_t* line, plattype_e type, int amount ) { plat_t* plat; int secnum; int rtn; sector_t* sec; secnum = -1; rtn = 0; // Activate all plats that are in_stasis switch(type) { case perpetualRaise: P_ActivateInStasis(line->tag); break; default: break; } while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // Find lowest & highest floors around sector rtn = 1; plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); P_AddThinker(&plat->thinker); plat->type = type; plat->sector = sec; plat->sector->specialdata = plat; plat->thinker.function.acp1 = (actionf_p1) T_PlatRaise; plat->crush = false; plat->tag = line->tag; switch(type) { case raiseToNearestAndChange: plat->speed = PLATSPEED/2; sec->floorpic = sides[line->sidenum[0]].sector->floorpic; plat->high = P_FindNextHighestFloor(sec,sec->floorheight); plat->wait = 0; plat->status = up; // NO MORE DAMAGE, IF APPLICABLE sec->special = 0; S_StartSound(&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(&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 = TICRATE*PLATWAIT; plat->status = down; S_StartSound(&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 = TICRATE*PLATWAIT; plat->status = down; S_StartSound(&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 = TICRATE*PLATWAIT; plat->status = P_Random()&1; S_StartSound(&sec->soundorg,sfx_pstart); break; } P_AddActivePlat(plat); } return rtn; } void P_ActivateInStasis(int tag) { int i; for (i = 0;i < MAXPLATS;i++) if (activeplats[i] && (activeplats[i])->tag == tag && (activeplats[i])->status == in_stasis) { (activeplats[i])->status = (activeplats[i])->oldstatus; (activeplats[i])->thinker.function.acp1 = (actionf_p1) T_PlatRaise; } } void EV_StopPlat(line_t* line) { int j; for (j = 0;j < MAXPLATS;j++) if (activeplats[j] && ((activeplats[j])->status != in_stasis) && ((activeplats[j])->tag == line->tag)) { (activeplats[j])->oldstatus = (activeplats[j])->status; (activeplats[j])->status = in_stasis; (activeplats[j])->thinker.function.acv = (actionf_v)NULL; } } void P_AddActivePlat(plat_t* plat) { int i; for (i = 0;i < MAXPLATS;i++) if (activeplats[i] == NULL) { activeplats[i] = plat; return; } I_Error ("P_AddActivePlat: no more plats!"); } void P_RemoveActivePlat(plat_t* plat) { int i; for (i = 0;i < MAXPLATS;i++) if (plat == activeplats[i]) { (activeplats[i])->sector->specialdata = NULL; P_RemoveThinker(&(activeplats[i])->thinker); activeplats[i] = NULL; return; } I_Error ("P_RemoveActivePlat: can't find plat!"); } crispy-doom-crispy-doom-5.6.4/src/doom/p_pspr.c000066400000000000000000000547221360717211000214420ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Weapon sprite animation, weapon objects. // Action functions for weapons. // #include "doomdef.h" #include "d_event.h" #include "deh_misc.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" // Data. #include "sounds.h" #include "p_pspr.h" #define LOWERSPEED FRACUNIT*6 #define RAISESPEED FRACUNIT*6 #define WEAPONBOTTOM 128*FRACUNIT #define WEAPONTOP 32*FRACUNIT // [crispy] weapon recoil {thrust, pitch} values // thrust values from prboom-plus/src/p_pspr.c:73-83 static const int recoil_values[][2] = { {10, 0}, // wp_fist {10, 4}, // wp_pistol {30, 8}, // wp_shotgun {10, 4}, // wp_chaingun {100, 16}, // wp_missile {20, 4}, // wp_plasma {100, 20}, // wp_bfg {0, -2}, // wp_chainsaw {80, 16}, // wp_supershotgun }; // [crispy] add weapon recoil // adapted from prboom-plus/src/p_pspr.c:484-495 (A_FireSomething ()) extern void P_Thrust (player_t* player, angle_t angle, fixed_t move); void A_Recoil (player_t* player) { if (player) { if (critical->recoil && !(player->mo->flags & MF_NOCLIP)) { P_Thrust(player, ANG180 + player->mo->angle, 2048 * recoil_values[player->readyweapon][0]); } if (crispy->pitch) { player->recoilpitch = recoil_values[player->readyweapon][1]; } } } // // P_SetPsprite // void P_SetPsprite ( player_t* player, int position, statenum_t stnum ) { pspdef_t* psp; state_t* state; psp = &player->psprites[position]; do { if (!stnum) { // object removed itself psp->state = 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; // [crispy] variable weapon sprite bob psp->sx2 = psp->sx; psp->sy2 = psp->sy; } // Call action routine. // Modified handling. if (state->action.acp3) { state->action.acp3(player->mo, player, psp); // [crispy] let mobj action pointers get called from pspr states if (!psp->state) break; } stnum = psp->state->nextstate; } while (!psp->tics); // an initial state of 0 could cycle through } // // P_CalcSwing // fixed_t swingx; fixed_t swingy; void P_CalcSwing (player_t* player) { fixed_t swing; int angle; // OPTIMIZE: tablify this. // A LUT would allow for different modes, // and add flexibility. swing = player->bob; angle = (FINEANGLES/70*leveltime)&FINEMASK; swingx = FixedMul ( swing, finesine[angle]); angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK; swingy = -FixedMul ( swingx, finesine[angle]); } // // P_BringUpWeapon // Starts bringing the pending weapon up // from the bottom of the screen. // Uses player // void P_BringUpWeapon (player_t* player) { statenum_t newstate; if (player->pendingweapon == wp_nochange) player->pendingweapon = player->readyweapon; if (player->pendingweapon == wp_chainsaw) S_StartSound (player->mo, sfx_sawup); // [crispy] intentionally not weapon sound source #if 0 // [crispy] play "power up" sound when selecting berserk fist... if (player->pendingweapon == wp_fist && player->powers[pw_strength]) { // [crispy] ...only if not playing already if (player == &players[consoleplayer]) { S_StartSoundOnce (NULL, sfx_getpow); } } #endif newstate = weaponinfo[player->pendingweapon].upstate; player->pendingweapon = wp_nochange; player->psprites[ps_weapon].sy = WEAPONBOTTOM; // [crispy] squat down weapon sprite player->psprites[ps_weapon].dy = 0; P_SetPsprite (player, ps_weapon, newstate); } // // P_CheckAmmo // Returns true if there is enough ammo to shoot. // If not, selects the next weapon to use. // boolean P_CheckAmmo (player_t* player) { ammotype_t ammo; int count; ammo = weaponinfo[player->readyweapon].ammo; // Minimal amount for one shot varies. if (player->readyweapon == wp_bfg) count = deh_bfg_cells_per_shot; else if (player->readyweapon == wp_supershotgun) count = 2; // Double barrel. else count = 1; // Regular. // [crispy] force weapon switch if weapon not owned // only relevant when removing current weapon with TNTWEAPx cheat if (!player->weaponowned[player->readyweapon]) { ammo = am_clip; // [crispy] at least not am_noammo, see below count = INT_MAX; } // Some do not need ammunition anyway. // Return if current ammunition sufficient. if (ammo == am_noammo || player->ammo[ammo] >= count) return true; // Out of ammo, pick a weapon to change to. // Preferences are set here. do { if (player->weaponowned[wp_plasma] && player->ammo[am_cell] && (gamemode != shareware) ) { player->pendingweapon = wp_plasma; } else if (player->weaponowned[wp_supershotgun] && player->ammo[am_shell]>2 && (crispy->havessg) ) { player->pendingweapon = wp_supershotgun; } else if (player->weaponowned[wp_chaingun] && player->ammo[am_clip]) { player->pendingweapon = wp_chaingun; } else if (player->weaponowned[wp_shotgun] && player->ammo[am_shell]) { player->pendingweapon = wp_shotgun; } // [crispy] allow to remove the pistol via TNTWEAP2 else if (player->ammo[am_clip] && player->weaponowned[wp_pistol]) { player->pendingweapon = wp_pistol; } else if (player->weaponowned[wp_chainsaw]) { player->pendingweapon = wp_chainsaw; } else if (player->weaponowned[wp_missile] && player->ammo[am_misl]) { player->pendingweapon = wp_missile; } else if (player->weaponowned[wp_bfg] && player->ammo[am_cell]>40 && (gamemode != shareware) ) { player->pendingweapon = wp_bfg; } else { // If everything fails. player->pendingweapon = wp_fist; } } while (player->pendingweapon == wp_nochange); // Now set appropriate weapon overlay. P_SetPsprite (player, ps_weapon, weaponinfo[player->readyweapon].downstate); return false; } // // P_FireWeapon. // void P_FireWeapon (player_t* player) { statenum_t newstate; if (!P_CheckAmmo (player)) return; P_SetMobjState (player->mo, S_PLAY_ATK1); newstate = weaponinfo[player->readyweapon].atkstate; P_SetPsprite (player, ps_weapon, newstate); P_NoiseAlert (player->mo, player->mo); } // // 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 ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { statenum_t newstate; int angle; if (!player) return; // [crispy] let pspr action pointers get called from mobj states // 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->so, sfx_sawidl); // [crispy] weapon sound source } // check for change // if player is dead, put the weapon away if (player->pendingweapon != wp_nochange || player->health <= 0) // [crispy] negative player health { // change weapon // (pending weapon should allready be validated) newstate = weaponinfo[player->readyweapon].downstate; P_SetPsprite (player, ps_weapon, newstate); return; } // check for fire // the missile launcher and bfg do not auto fire if (player->cmd.buttons & BT_ATTACK) { if ( !player->attackdown || (player->readyweapon != wp_missile && player->readyweapon != wp_bfg) ) { player->attackdown = true; P_FireWeapon (player); return; } } else player->attackdown = false; // bob the weapon based on movement speed angle = (128*leveltime)&FINEMASK; psp->sx = FRACUNIT + FixedMul (player->bob, finecosine[angle]); angle &= FINEANGLES/2-1; psp->sy = WEAPONTOP + FixedMul (player->bob, finesine[angle]); } // // A_ReFire // The player can re-fire the weapon // without lowering it entirely. // void A_ReFire ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states // 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 > 0) // [crispy] negative player health { player->refire++; P_FireWeapon (player); } else { player->refire = 0; P_CheckAmmo (player); } } void A_CheckReload ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states P_CheckAmmo (player); #if 0 if (player->ammo[am_shell]<2) P_SetPsprite (player, ps_weapon, S_DSNR1); #endif } // // A_Lower // Lowers current weapon, // and changes weapon at bottom. // void A_Lower ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states psp->sy += LOWERSPEED; // Is already down. if (psp->sy < WEAPONBOTTOM ) return; // Player is dead. if (player->playerstate == PST_DEAD) { psp->sy = WEAPONBOTTOM; // don't bring weapon back up return; } // The old weapon has been lowered off the screen, // so change the weapon and start raising it if (player->health <= 0) // [crispy] negative player health { // Player is dead, so keep the weapon off screen. P_SetPsprite (player, ps_weapon, S_NULL); return; } player->readyweapon = player->pendingweapon; P_BringUpWeapon (player); } // // A_Raise // void A_Raise ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { statenum_t newstate; if (!player) return; // [crispy] let pspr action pointers get called from mobj states psp->sy -= RAISESPEED; if (psp->sy > WEAPONTOP ) return; psp->sy = WEAPONTOP; // The weapon has been raised all the way, // so change to the ready state. newstate = weaponinfo[player->readyweapon].readystate; P_SetPsprite (player, ps_weapon, newstate); } // // A_GunFlash // void A_GunFlash ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states P_SetMobjState (player->mo, S_PLAY_ATK2); P_SetPsprite (player,ps_flash,weaponinfo[player->readyweapon].flashstate); } // // WEAPON ATTACKS // // // A_Punch // void A_Punch ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { angle_t angle; int damage; int slope; if (!player) return; // [crispy] let pspr action pointers get called from mobj states damage = (P_Random ()%10+1)<<1; if (player->powers[pw_strength]) damage *= 10; angle = player->mo->angle; angle += P_SubRandom() << 18; slope = P_AimLineAttack (player->mo, angle, MELEERANGE); P_LineAttack (player->mo, angle, MELEERANGE, slope, damage); // turn to face target if (linetarget) { S_StartSound (player->so, sfx_punch); // [crispy] weapon sound source player->mo->angle = R_PointToAngle2 (player->mo->x, player->mo->y, linetarget->x, linetarget->y); } } // // A_Saw // void A_Saw ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { angle_t angle; int damage; int slope; if (!player) return; // [crispy] let pspr action pointers get called from mobj states damage = 2*(P_Random ()%10+1); angle = player->mo->angle; angle += P_SubRandom() << 18; // use meleerange + 1 se the puff doesn't skip the flash slope = P_AimLineAttack (player->mo, angle, MELEERANGE+1); P_LineAttack (player->mo, angle, MELEERANGE+1, slope, damage); A_Recoil (player); if (!linetarget) { S_StartSound (player->so, sfx_sawful); // [crispy] weapon sound source return; } S_StartSound (player->so, sfx_sawhit); // [crispy] weapon sound source // turn to face target angle = R_PointToAngle2 (player->mo->x, player->mo->y, linetarget->x, linetarget->y); if (angle - player->mo->angle > ANG180) { if ((signed int) (angle - player->mo->angle) < -ANG90/20) player->mo->angle = angle + ANG90/21; else player->mo->angle -= ANG90/20; } else { if (angle - player->mo->angle > ANG90/20) player->mo->angle = angle - ANG90/21; else player->mo->angle += ANG90/20; } player->mo->flags |= MF_JUSTATTACKED; } // Doom does not check the bounds of the ammo array. As a result, // it is possible to use an ammo type > 4 that overflows into the // maxammo array and affects that instead. Through dehacked, for // example, it is possible to make a weapon that decreases the max // number of ammo for another weapon. Emulate this. static void DecreaseAmmo(player_t *player, int ammonum, int amount) { if (ammonum < NUMAMMO) { player->ammo[ammonum] -= amount; // [crispy] never allow less than zero ammo if (player->ammo[ammonum] < 0) { player->ammo[ammonum] = 0; } } else { player->maxammo[ammonum - NUMAMMO] -= amount; } } // // A_FireMissile // void A_FireMissile ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SpawnPlayerMissile (player->mo, MT_ROCKET); } // // A_FireBFG // void A_FireBFG ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, deh_bfg_cells_per_shot); P_SpawnPlayerMissile (player->mo, MT_BFG); } // // A_FirePlasma // void A_FirePlasma ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate+(P_Random ()&1) ); P_SpawnPlayerMissile (player->mo, MT_PLASMA); } // // P_BulletSlope // Sets a slope so a near miss is at aproximately // the height of the intended target // fixed_t bulletslope; void P_BulletSlope (mobj_t* mo) { angle_t an; if (critical->freeaim == FREEAIM_DIRECT) { bulletslope = PLAYER_SLOPE(mo->player); } else { // see which target is to be aimed at an = mo->angle; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget && critical->freeaim == FREEAIM_BOTH) { bulletslope = PLAYER_SLOPE(mo->player); } } } } } // // P_GunShot // void P_GunShot ( mobj_t* mo, boolean accurate ) { angle_t angle; int damage; damage = 5*(P_Random ()%3+1); angle = mo->angle; if (!accurate) angle += P_SubRandom() << 18; P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage); } // // A_FirePistol // void A_FirePistol ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->so, sfx_pistol); // [crispy] weapon sound source P_SetMobjState (player->mo, S_PLAY_ATK2); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate); P_BulletSlope (player->mo); P_GunShot (player->mo, !player->refire); A_Recoil (player); } // // A_FireShotgun // void A_FireShotgun ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { int i; if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->so, sfx_shotgn); // [crispy] weapon sound source P_SetMobjState (player->mo, S_PLAY_ATK2); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate); P_BulletSlope (player->mo); for (i=0 ; i<7 ; i++) P_GunShot (player->mo, false); A_Recoil (player); } // // A_FireShotgun2 // void A_FireShotgun2 ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { int i; angle_t angle; int damage; if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->so, sfx_dshtgn); // [crispy] weapon sound source P_SetMobjState (player->mo, S_PLAY_ATK2); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 2); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate); P_BulletSlope (player->mo); for (i=0 ; i<20 ; i++) { damage = 5*(P_Random ()%3+1); angle = player->mo->angle; angle += P_SubRandom() << ANGLETOFINESHIFT; P_LineAttack (player->mo, angle, MISSILERANGE, bulletslope + (P_SubRandom() << 5), damage); } A_Recoil (player); } // // A_FireCGun // void A_FireCGun ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->so, sfx_pistol); // [crispy] weapon sound source if (!player->ammo[weaponinfo[player->readyweapon].ammo]) return; P_SetMobjState (player->mo, S_PLAY_ATK2); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate + psp->state - &states[S_CHAIN1] ); P_BulletSlope (player->mo); P_GunShot (player->mo, !player->refire); A_Recoil (player); } // // ? // void A_Light0 (mobj_t *mobj, player_t *player, pspdef_t *psp) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states player->extralight = 0; } void A_Light1 (mobj_t *mobj, player_t *player, pspdef_t *psp) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states player->extralight = 1; } void A_Light2 (mobj_t *mobj, player_t *player, pspdef_t *psp) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states player->extralight = 2; } // // A_BFGSpray // Spawn a BFG explosion on every monster in view // void A_BFGSpray (mobj_t* mo) { int i; int j; int damage; angle_t an; // offset angles from its attack angle for (i=0 ; i<40 ; i++) { an = mo->angle - ANG90/2 + ANG90/40*i; // mo->target is the originator (player) // of the missile P_AimLineAttack (mo->target, an, 16*64*FRACUNIT); if (!linetarget) continue; P_SpawnMobj (linetarget->x, linetarget->y, linetarget->z + (linetarget->height>>2), MT_EXTRABFG); damage = 0; for (j=0;j<15;j++) damage += (P_Random()&7) + 1; P_DamageMobj (linetarget, mo->target,mo->target, damage); } } // // A_BFGsound // void A_BFGsound ( mobj_t* mobj, player_t* player, pspdef_t* psp ) { if (!player) return; // [crispy] let pspr action pointers get called from mobj states S_StartSound (player->mo, sfx_bfg); // [crispy] intentionally not weapon sound source } // // 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) { int i; pspdef_t* psp; state_t* state; psp = &player->psprites[0]; for (i=0 ; istate) ) { // drop tic count and possibly change state // a -1 tic count never changes if (psp->tics != -1) { psp->tics--; if (!psp->tics) P_SetPsprite (player, i, psp->state->nextstate); } } } player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; // [crispy] apply bobbing (or centering) to the player's weapon sprite psp = &player->psprites[0]; if (psp->state) { // [crispy] don't center vertically during lowering and raising states if (psp->state->misc1 || psp->state->action.acp3 == (actionf_p3)A_Lower || psp->state->action.acp3 == (actionf_p3)A_Raise) { psp->sx2 = psp->sx; psp->sy2 = psp->sy; } else if (psp->state->action.acp3 == (actionf_p3)A_WeaponReady || crispy->centerweapon == CENTERWEAPON_BOB) { angle_t angle = (128 * leveltime) & FINEMASK; psp->sx2 = FRACUNIT + FixedMul(player->bob2, finecosine[angle]); angle &= FINEANGLES / 2 - 1; psp->sy2 = WEAPONTOP + FixedMul(player->bob2, finesine[angle]); } else // [crispy] center the weapon sprite horizontally and push up vertically if (crispy->centerweapon == CENTERWEAPON_CENTER) { psp->sx2 = FRACUNIT; psp->sy2 = WEAPONTOP; } } else { psp->sx2 = psp->sx; psp->sy2 = psp->sy; } // [crispy] squat down weapon sprite a bit after hitting the ground if (player->psp_dy_max) { psp->dy -= FRACUNIT; if (psp->dy < player->psp_dy_max) { psp->dy = -psp->dy; } if (psp->dy == 0) { player->psp_dy_max = 0; } } player->psprites[ps_flash].dy = psp->dy; player->psprites[ps_flash].sx2 = psp->sx2; player->psprites[ps_flash].sy2 = psp->sy2; } crispy-doom-crispy-doom-5.6.4/src/doom/p_pspr.h000066400000000000000000000032221360717211000214340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Sprite animation. // #ifndef __P_PSPR__ #define __P_PSPR__ // Basic data types. // Needs fixed point, and BAM angles. #include "m_fixed.h" #include "tables.h" // // Needs to include the precompiled // sprite animation tables. // Header generated by multigen utility. // This includes all the data for thing animation, // i.e. the Thing Atrributes table // and the Frame Sequence table. #include "info.h" // // Frame flags: // handles maximum brightness (torches, muzzle flare, light sources) // #define FF_FULLBRIGHT 0x8000 // flag in thing->frame #define FF_FRAMEMASK 0x7fff // // Overlay psprites are scaled shapes // drawn directly on the view screen, // coordinates are given for a 320*200 view screen. // typedef enum { ps_weapon, ps_flash, NUMPSPRITES } psprnum_t; typedef struct { state_t* state; // a NULL state means not active int tics; fixed_t sx; fixed_t sy; // [crispy] squat down weapon sprite fixed_t dy; // [crispy] variable weapon sprite bob fixed_t sx2; fixed_t sy2; } pspdef_t; #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_saveg.c000066400000000000000000001143321360717211000215550ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Archiving: SaveGame I/O. // #include #include #include "dstrings.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "p_local.h" #include "p_saveg.h" // State. #include "doomstat.h" #include "g_game.h" #include "m_misc.h" #include "r_state.h" FILE *save_stream; int savegamelength; boolean savegame_error; static int restoretargets_fail; // Get the filename of a temporary file to write the savegame to. After // the file has been successfully saved, it will be renamed to the // real file. char *P_TempSaveGameFile(void) { static char *filename = NULL; if (filename == NULL) { filename = M_StringJoin(savegamedir, "temp.dsg", NULL); } return filename; } // Get the filename of the save game file to use for the specified slot. char *P_SaveGameFile(int slot) { static char *filename = NULL; static size_t filename_size = 0; char basename[32]; if (filename == NULL) { filename_size = strlen(savegamedir) + 32; filename = malloc(filename_size); } DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot); M_snprintf(filename, filename_size, "%s%s", savegamedir, basename); return filename; } // Endian-safe integer read/write functions static byte saveg_read8(void) { byte result = -1; if (fread(&result, 1, 1, save_stream) < 1) { if (!savegame_error) { fprintf(stderr, "saveg_read8: Unexpected end of file while " "reading save game\n"); savegame_error = true; } } return result; } static void saveg_write8(byte value) { if (fwrite(&value, 1, 1, save_stream) < 1) { if (!savegame_error) { fprintf(stderr, "saveg_write8: Error while writing save game\n"); savegame_error = true; } } } static short saveg_read16(void) { int result; result = saveg_read8(); result |= saveg_read8() << 8; return result; } static void saveg_write16(short value) { saveg_write8(value & 0xff); saveg_write8((value >> 8) & 0xff); } static int saveg_read32(void) { int result; result = saveg_read8(); result |= saveg_read8() << 8; result |= saveg_read8() << 16; result |= saveg_read8() << 24; return result; } static void saveg_write32(int value) { saveg_write8(value & 0xff); saveg_write8((value >> 8) & 0xff); saveg_write8((value >> 16) & 0xff); saveg_write8((value >> 24) & 0xff); } // Pad to 4-byte boundaries static void saveg_read_pad(void) { unsigned long pos; int padding; int i; pos = ftell(save_stream); padding = (4 - (pos & 3)) & 3; for (i=0; ix = saveg_read16(); // short y; str->y = saveg_read16(); // short angle; str->angle = saveg_read16(); // short type; str->type = saveg_read16(); // short options; str->options = saveg_read16(); } static void saveg_write_mapthing_t(mapthing_t *str) { // short x; saveg_write16(str->x); // short y; saveg_write16(str->y); // short angle; saveg_write16(str->angle); // short type; saveg_write16(str->type); // short options; saveg_write16(str->options); } // // actionf_t // static void saveg_read_actionf_t(actionf_t *str) { // actionf_p1 acp1; str->acp1 = saveg_readp(); } static void saveg_write_actionf_t(actionf_t *str) { // actionf_p1 acp1; saveg_writep(str->acp1); } // // think_t // // This is just an actionf_t. // #define saveg_read_think_t saveg_read_actionf_t #define saveg_write_think_t saveg_write_actionf_t // // thinker_t // static void saveg_read_thinker_t(thinker_t *str) { // struct thinker_s* prev; str->prev = saveg_readp(); // struct thinker_s* next; str->next = saveg_readp(); // think_t function; saveg_read_think_t(&str->function); } static void saveg_write_thinker_t(thinker_t *str) { // struct thinker_s* prev; saveg_writep(str->prev); // struct thinker_s* next; saveg_writep(str->next); // think_t function; saveg_write_think_t(&str->function); } // // mobj_t // static void saveg_read_mobj_t(mobj_t *str) { int pl; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // fixed_t x; str->x = saveg_read32(); // fixed_t y; str->y = saveg_read32(); // fixed_t z; str->z = saveg_read32(); // struct mobj_s* snext; str->snext = saveg_readp(); // struct mobj_s* sprev; str->sprev = saveg_readp(); // angle_t angle; str->angle = saveg_read32(); // spritenum_t sprite; str->sprite = saveg_read_enum(); // int frame; str->frame = saveg_read32(); // struct mobj_s* bnext; str->bnext = saveg_readp(); // struct mobj_s* bprev; str->bprev = saveg_readp(); // struct subsector_s* subsector; str->subsector = saveg_readp(); // fixed_t floorz; str->floorz = saveg_read32(); // fixed_t ceilingz; str->ceilingz = saveg_read32(); // fixed_t radius; str->radius = saveg_read32(); // fixed_t height; str->height = saveg_read32(); // fixed_t momx; str->momx = saveg_read32(); // fixed_t momy; str->momy = saveg_read32(); // fixed_t momz; str->momz = saveg_read32(); // int validcount; str->validcount = saveg_read32(); // mobjtype_t type; str->type = saveg_read_enum(); // mobjinfo_t* info; str->info = saveg_readp(); // int tics; str->tics = saveg_read32(); // state_t* state; str->state = &states[saveg_read32()]; // int flags; str->flags = saveg_read32(); // int health; str->health = saveg_read32(); // int movedir; str->movedir = saveg_read32(); // int movecount; str->movecount = saveg_read32(); // struct mobj_s* target; str->target = saveg_readp(); // int reactiontime; str->reactiontime = saveg_read32(); // int threshold; str->threshold = saveg_read32(); // struct player_s* player; pl = saveg_read32(); if (pl > 0) { str->player = &players[pl - 1]; str->player->mo = str; str->player->so = Crispy_PlayerSO(pl - 1); // [crispy] weapon sound sources } else { str->player = NULL; } // int lastlook; str->lastlook = saveg_read32(); // mapthing_t spawnpoint; saveg_read_mapthing_t(&str->spawnpoint); // struct mobj_s* tracer; str->tracer = saveg_readp(); } // [crispy] enumerate all thinker pointers uint32_t P_ThinkerToIndex (thinker_t* thinker) { thinker_t* th; uint32_t i; if (!thinker) return 0; for (th = thinkercap.next, i = 0; th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1) P_MobjThinker) { i++; if (th == thinker) return i; } } return 0; } // [crispy] replace indizes with corresponding pointers thinker_t* P_IndexToThinker (uint32_t index) { thinker_t* th; uint32_t i; if (!index) return NULL; for (th = thinkercap.next, i = 0; th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1) P_MobjThinker) { i++; if (i == index) return th; } } restoretargets_fail++; return NULL; } static void saveg_write_mobj_t(mobj_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // fixed_t x; saveg_write32(str->x); // fixed_t y; saveg_write32(str->y); // fixed_t z; saveg_write32(str->z); // struct mobj_s* snext; saveg_writep(str->snext); // struct mobj_s* sprev; saveg_writep(str->sprev); // angle_t angle; saveg_write32(str->angle); // spritenum_t sprite; saveg_write_enum(str->sprite); // int frame; saveg_write32(str->frame); // struct mobj_s* bnext; saveg_writep(str->bnext); // struct mobj_s* bprev; saveg_writep(str->bprev); // struct subsector_s* subsector; saveg_writep(str->subsector); // fixed_t floorz; saveg_write32(str->floorz); // fixed_t ceilingz; saveg_write32(str->ceilingz); // fixed_t radius; saveg_write32(str->radius); // fixed_t height; saveg_write32(str->height); // fixed_t momx; saveg_write32(str->momx); // fixed_t momy; saveg_write32(str->momy); // fixed_t momz; saveg_write32(str->momz); // int validcount; saveg_write32(str->validcount); // mobjtype_t type; saveg_write_enum(str->type); // mobjinfo_t* info; saveg_writep(str->info); // int tics; saveg_write32(str->tics); // state_t* state; saveg_write32(str->state - states); // int flags; saveg_write32(str->flags); // int health; saveg_write32(str->health); // int movedir; saveg_write32(str->movedir); // int movecount; saveg_write32(str->movecount); // struct mobj_s* target; // [crispy] instead of the actual pointer, store the // corresponding index in the mobj->target field saveg_writep((void *)(uintptr_t) P_ThinkerToIndex((thinker_t *) str->target)); // int reactiontime; saveg_write32(str->reactiontime); // int threshold; saveg_write32(str->threshold); // struct player_s* player; if (str->player) { saveg_write32(str->player - players + 1); } else { saveg_write32(0); } // int lastlook; saveg_write32(str->lastlook); // mapthing_t spawnpoint; saveg_write_mapthing_t(&str->spawnpoint); // struct mobj_s* tracer; // [crispy] instead of the actual pointer, store the // corresponding index in the mobj->tracers field saveg_writep((void *)(uintptr_t) P_ThinkerToIndex((thinker_t *) str->tracer)); } // // ticcmd_t // static void saveg_read_ticcmd_t(ticcmd_t *str) { // signed char forwardmove; str->forwardmove = saveg_read8(); // signed char sidemove; str->sidemove = saveg_read8(); // short angleturn; str->angleturn = saveg_read16(); // short consistancy; str->consistancy = saveg_read16(); // byte chatchar; str->chatchar = saveg_read8(); // byte buttons; str->buttons = saveg_read8(); } static void saveg_write_ticcmd_t(ticcmd_t *str) { // signed char forwardmove; saveg_write8(str->forwardmove); // signed char sidemove; saveg_write8(str->sidemove); // short angleturn; saveg_write16(str->angleturn); // short consistancy; saveg_write16(str->consistancy); // byte chatchar; saveg_write8(str->chatchar); // byte buttons; saveg_write8(str->buttons); } // // pspdef_t // static void saveg_read_pspdef_t(pspdef_t *str) { int state; // state_t* state; state = saveg_read32(); if (state > 0) { str->state = &states[state]; } else { str->state = NULL; } // int tics; str->tics = saveg_read32(); // fixed_t sx; str->sx = saveg_read32(); // fixed_t sy; str->sy = saveg_read32(); // [crispy] variable weapon sprite bob str->dy = 0; str->sx2 = str->sx; str->sy2 = str->sy; } static void saveg_write_pspdef_t(pspdef_t *str) { // state_t* state; if (str->state) { saveg_write32(str->state - states); } else { saveg_write32(0); } // int tics; saveg_write32(str->tics); // fixed_t sx; saveg_write32(str->sx); // fixed_t sy; saveg_write32(str->sy); } // // player_t // static void saveg_read_player_t(player_t *str) { int i; // mobj_t* mo; str->mo = saveg_readp(); // playerstate_t playerstate; str->playerstate = saveg_read_enum(); // ticcmd_t cmd; saveg_read_ticcmd_t(&str->cmd); // fixed_t viewz; str->viewz = saveg_read32(); // fixed_t viewheight; str->viewheight = saveg_read32(); // fixed_t deltaviewheight; str->deltaviewheight = saveg_read32(); // fixed_t bob; str->bob = saveg_read32(); // [crispy] variable player view bob str->bob2 = str->bob; // int health; str->health = saveg_read32(); // int armorpoints; str->armorpoints = saveg_read32(); // int armortype; str->armortype = saveg_read32(); // int powers[NUMPOWERS]; for (i=0; ipowers[i] = saveg_read32(); } // boolean cards[NUMCARDS]; for (i=0; icards[i] = saveg_read32(); } // boolean backpack; str->backpack = saveg_read32(); // int frags[MAXPLAYERS]; for (i=0; ifrags[i] = saveg_read32(); } // weapontype_t readyweapon; str->readyweapon = saveg_read_enum(); // weapontype_t pendingweapon; str->pendingweapon = saveg_read_enum(); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i] = saveg_read32(); } // int ammo[NUMAMMO]; for (i=0; iammo[i] = saveg_read32(); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i] = saveg_read32(); } // int attackdown; str->attackdown = saveg_read32(); // int usedown; str->usedown = saveg_read32(); // int cheats; str->cheats = saveg_read32(); // int refire; str->refire = saveg_read32(); // int killcount; str->killcount = saveg_read32(); // int itemcount; str->itemcount = saveg_read32(); // int secretcount; str->secretcount = saveg_read32(); // char* message; str->message = saveg_readp(); // int damagecount; str->damagecount = saveg_read32(); // int bonuscount; str->bonuscount = saveg_read32(); // mobj_t* attacker; str->attacker = saveg_readp(); // int extralight; str->extralight = saveg_read32(); // int fixedcolormap; str->fixedcolormap = saveg_read32(); // int colormap; str->colormap = saveg_read32(); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // boolean didsecret; str->didsecret = saveg_read32(); } static void saveg_write_player_t(player_t *str) { int i; // mobj_t* mo; saveg_writep(str->mo); // playerstate_t playerstate; saveg_write_enum(str->playerstate); // ticcmd_t cmd; saveg_write_ticcmd_t(&str->cmd); // fixed_t viewz; saveg_write32(str->viewz); // fixed_t viewheight; saveg_write32(str->viewheight); // fixed_t deltaviewheight; saveg_write32(str->deltaviewheight); // fixed_t bob; saveg_write32(str->bob); // int health; saveg_write32(str->health); // int armorpoints; saveg_write32(str->armorpoints); // int armortype; saveg_write32(str->armortype); // int powers[NUMPOWERS]; for (i=0; ipowers[i]); } // boolean cards[NUMCARDS]; for (i=0; icards[i]); } // boolean backpack; saveg_write32(str->backpack); // int frags[MAXPLAYERS]; for (i=0; ifrags[i]); } // weapontype_t readyweapon; saveg_write_enum(str->readyweapon); // weapontype_t pendingweapon; saveg_write_enum(str->pendingweapon); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i]); } // int ammo[NUMAMMO]; for (i=0; iammo[i]); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i]); } // int attackdown; saveg_write32(str->attackdown); // int usedown; saveg_write32(str->usedown); // int cheats; saveg_write32(str->cheats); // int refire; saveg_write32(str->refire); // int killcount; saveg_write32(str->killcount); // int itemcount; saveg_write32(str->itemcount); // int secretcount; saveg_write32(str->secretcount); // char* message; saveg_writep(str->message); // int damagecount; saveg_write32(str->damagecount); // int bonuscount; saveg_write32(str->bonuscount); // mobj_t* attacker; saveg_writep(str->attacker); // int extralight; saveg_write32(str->extralight); // int fixedcolormap; saveg_write32(str->fixedcolormap); // int colormap; saveg_write32(str->colormap); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // boolean didsecret; saveg_write32(str->didsecret); } // // ceiling_t // static void saveg_read_ceiling_t(ceiling_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // ceiling_e type; str->type = saveg_read_enum(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t bottomheight; str->bottomheight = saveg_read32(); // fixed_t topheight; str->topheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); // boolean crush; str->crush = saveg_read32(); // int direction; str->direction = saveg_read32(); // int tag; str->tag = saveg_read32(); // int olddirection; str->olddirection = saveg_read32(); } static void saveg_write_ceiling_t(ceiling_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // ceiling_e type; saveg_write_enum(str->type); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t bottomheight; saveg_write32(str->bottomheight); // fixed_t topheight; saveg_write32(str->topheight); // fixed_t speed; saveg_write32(str->speed); // boolean crush; saveg_write32(str->crush); // int direction; saveg_write32(str->direction); // int tag; saveg_write32(str->tag); // int olddirection; saveg_write32(str->olddirection); } // // vldoor_t // static void saveg_read_vldoor_t(vldoor_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // vldoor_e type; str->type = saveg_read_enum(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t topheight; str->topheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); // int direction; str->direction = saveg_read32(); // int topwait; str->topwait = saveg_read32(); // int topcountdown; str->topcountdown = saveg_read32(); } static void saveg_write_vldoor_t(vldoor_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // vldoor_e type; saveg_write_enum(str->type); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t topheight; saveg_write32(str->topheight); // fixed_t speed; saveg_write32(str->speed); // int direction; saveg_write32(str->direction); // int topwait; saveg_write32(str->topwait); // int topcountdown; saveg_write32(str->topcountdown); } // // floormove_t // static void saveg_read_floormove_t(floormove_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // floor_e type; str->type = saveg_read_enum(); // boolean crush; str->crush = saveg_read32(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int direction; str->direction = saveg_read32(); // int newspecial; str->newspecial = saveg_read32(); // short texture; str->texture = saveg_read16(); // fixed_t floordestheight; str->floordestheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); } static void saveg_write_floormove_t(floormove_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // floor_e type; saveg_write_enum(str->type); // boolean crush; saveg_write32(str->crush); // sector_t* sector; saveg_write32(str->sector - sectors); // int direction; saveg_write32(str->direction); // int newspecial; saveg_write32(str->newspecial); // short texture; saveg_write16(str->texture); // fixed_t floordestheight; saveg_write32(str->floordestheight); // fixed_t speed; saveg_write32(str->speed); } // // plat_t // static void saveg_read_plat_t(plat_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t speed; str->speed = saveg_read32(); // fixed_t low; str->low = saveg_read32(); // fixed_t high; str->high = saveg_read32(); // int wait; str->wait = saveg_read32(); // int count; str->count = saveg_read32(); // plat_e status; str->status = saveg_read_enum(); // plat_e oldstatus; str->oldstatus = saveg_read_enum(); // boolean crush; str->crush = saveg_read32(); // int tag; str->tag = saveg_read32(); // plattype_e type; str->type = saveg_read_enum(); } static void saveg_write_plat_t(plat_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t speed; saveg_write32(str->speed); // fixed_t low; saveg_write32(str->low); // fixed_t high; saveg_write32(str->high); // int wait; saveg_write32(str->wait); // int count; saveg_write32(str->count); // plat_e status; saveg_write_enum(str->status); // plat_e oldstatus; saveg_write_enum(str->oldstatus); // boolean crush; saveg_write32(str->crush); // int tag; saveg_write32(str->tag); // plattype_e type; saveg_write_enum(str->type); } // // lightflash_t // static void saveg_read_lightflash_t(lightflash_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int count; str->count = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int minlight; str->minlight = saveg_read32(); // int maxtime; str->maxtime = saveg_read32(); // int mintime; str->mintime = saveg_read32(); } static void saveg_write_lightflash_t(lightflash_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int count; saveg_write32(str->count); // int maxlight; saveg_write32(str->maxlight); // int minlight; saveg_write32(str->minlight); // int maxtime; saveg_write32(str->maxtime); // int mintime; saveg_write32(str->mintime); } // // strobe_t // static void saveg_read_strobe_t(strobe_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int count; str->count = saveg_read32(); // int minlight; str->minlight = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int darktime; str->darktime = saveg_read32(); // int brighttime; str->brighttime = saveg_read32(); } static void saveg_write_strobe_t(strobe_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int count; saveg_write32(str->count); // int minlight; saveg_write32(str->minlight); // int maxlight; saveg_write32(str->maxlight); // int darktime; saveg_write32(str->darktime); // int brighttime; saveg_write32(str->brighttime); } // // glow_t // static void saveg_read_glow_t(glow_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int minlight; str->minlight = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int direction; str->direction = saveg_read32(); } static void saveg_write_glow_t(glow_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int minlight; saveg_write32(str->minlight); // int maxlight; saveg_write32(str->maxlight); // int direction; saveg_write32(str->direction); } // // Write the header for a savegame // void P_WriteSaveGameHeader(char *description) { char name[VERSIONSIZE]; int i; for (i=0; description[i] != '\0'; ++i) saveg_write8(description[i]); for (; i> 16) & 0xff); saveg_write8((leveltime >> 8) & 0xff); saveg_write8(leveltime & 0xff); } // // Read the header for a savegame // boolean P_ReadSaveGameHeader(void) { int i; byte a, b, c; char vcheck[VERSIONSIZE]; char read_vcheck[VERSIONSIZE]; // skip the description field for (i=0; ifloorheight >> FRACBITS); saveg_write16(sec->ceilingheight >> FRACBITS); saveg_write16(sec->floorpic); saveg_write16(sec->ceilingpic); saveg_write16(sec->lightlevel); saveg_write16(sec->special); // needed? saveg_write16(sec->tag); // needed? } // do lines for (i=0, li = lines ; iflags); saveg_write16(li->special); saveg_write16(li->tag); for (j=0 ; j<2 ; j++) { if (li->sidenum[j] == NO_INDEX) // [crispy] extended nodes continue; si = &sides[li->sidenum[j]]; saveg_write16(si->textureoffset >> FRACBITS); saveg_write16(si->rowoffset >> FRACBITS); saveg_write16(si->toptexture); saveg_write16(si->bottomtexture); saveg_write16(si->midtexture); } } } // // P_UnArchiveWorld // void P_UnArchiveWorld (void) { int i; int j; sector_t* sec; line_t* li; side_t* si; // do sectors for (i=0, sec = sectors ; ifloorheight = saveg_read16() << FRACBITS; sec->ceilingheight = saveg_read16() << FRACBITS; floorpic = saveg_read16(); ceilingpic = saveg_read16(); sec->lightlevel = saveg_read16(); sec->special = saveg_read16(); // needed? sec->tag = saveg_read16(); // needed? sec->specialdata = 0; sec->soundtarget = 0; // [crispy] add overflow guard for the flattranslation[] array if (floorpic >= 0 && floorpic < numflats) { sec->floorpic = floorpic; } if (ceilingpic >= 0 && ceilingpic < numflats) { sec->ceilingpic = ceilingpic; } } // do lines for (i=0, li = lines ; iflags = saveg_read16(); li->special = saveg_read16(); li->tag = saveg_read16(); for (j=0 ; j<2 ; j++) { if (li->sidenum[j] == NO_INDEX) // [crispy] extended nodes continue; si = &sides[li->sidenum[j]]; si->textureoffset = saveg_read16() << FRACBITS; si->rowoffset = saveg_read16() << FRACBITS; si->toptexture = saveg_read16(); si->bottomtexture = saveg_read16(); si->midtexture = saveg_read16(); } } } // // Thinkers // typedef enum { tc_end, tc_mobj } thinkerclass_t; // // P_ArchiveThinkers // void P_ArchiveThinkers (void) { thinker_t* th; // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) { saveg_write8(tc_mobj); saveg_write_pad(); saveg_write_mobj_t((mobj_t *) th); continue; } // I_Error ("P_ArchiveThinkers: Unknown thinker function"); } // add a terminating marker saveg_write8(tc_end); } // // P_UnArchiveThinkers // void P_UnArchiveThinkers (void) { byte tclass; thinker_t* currentthinker; thinker_t* next; mobj_t* mobj; // remove all the current thinkers currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { next = currentthinker->next; if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) P_RemoveMobj ((mobj_t *)currentthinker); else Z_Free (currentthinker); currentthinker = next; } P_InitThinkers (); // read in saved thinkers while (1) { tclass = saveg_read8(); switch (tclass) { case tc_end: return; // end of list case tc_mobj: saveg_read_pad(); mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL); saveg_read_mobj_t(mobj); // [crispy] restore mobj->target and mobj->tracer fields //mobj->target = NULL; //mobj->tracer = NULL; P_SetThingPosition (mobj); mobj->info = &mobjinfo[mobj->type]; // [crispy] 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.acp1 = (actionf_p1)P_MobjThinker; P_AddThinker (&mobj->thinker); break; default: I_Error ("Unknown tclass %i in savegame",tclass); } } } // [crispy] after all the thinkers have been restored, replace all indices in // the mobj->target and mobj->tracers fields by the corresponding current pointers again void P_RestoreTargets (void) { mobj_t* mo; thinker_t* th; for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1) P_MobjThinker) { mo = (mobj_t*) th; mo->target = (mobj_t*) P_IndexToThinker((uintptr_t) mo->target); mo->tracer = (mobj_t*) P_IndexToThinker((uintptr_t) mo->tracer); } } if (restoretargets_fail) { fprintf (stderr, "P_RestoreTargets: Failed to restore %d target pointers.\n", restoretargets_fail); restoretargets_fail = 0; } } // // P_ArchiveSpecials // enum { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, tc_endspecials } specials_e; // // Things to handle: // // T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list // T_VerticalDoor, (vldoor_t: sector_t * swizzle), // T_MoveFloor, (floormove_t: sector_t * swizzle), // T_LightFlash, (lightflash_t: sector_t * swizzle), // T_StrobeFlash, (strobe_t: sector_t *), // T_Glow, (glow_t: sector_t *), // T_PlatRaise, (plat_t: sector_t *), - active list // void P_ArchiveSpecials (void) { thinker_t* th; int i; // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acv == (actionf_v)NULL) { for (i = 0; i < MAXCEILINGS;i++) if (activeceilings[i] == (ceiling_t *)th) break; if (ifunction.acp1 == (actionf_p1)T_MoveCeiling) { saveg_write8(tc_ceiling); saveg_write_pad(); saveg_write_ceiling_t((ceiling_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_VerticalDoor) { saveg_write8(tc_door); saveg_write_pad(); saveg_write_vldoor_t((vldoor_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_MoveFloor) { saveg_write8(tc_floor); saveg_write_pad(); saveg_write_floormove_t((floormove_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_PlatRaise) { saveg_write8(tc_plat); saveg_write_pad(); saveg_write_plat_t((plat_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_LightFlash) { saveg_write8(tc_flash); saveg_write_pad(); saveg_write_lightflash_t((lightflash_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_StrobeFlash) { saveg_write8(tc_strobe); saveg_write_pad(); saveg_write_strobe_t((strobe_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_Glow) { saveg_write8(tc_glow); saveg_write_pad(); saveg_write_glow_t((glow_t *) th); continue; } } // add a terminating marker saveg_write8(tc_endspecials); } // // P_UnArchiveSpecials // void P_UnArchiveSpecials (void) { byte tclass; ceiling_t* ceiling; vldoor_t* door; floormove_t* floor; plat_t* plat; lightflash_t* flash; strobe_t* strobe; glow_t* glow; // read in saved thinkers while (1) { tclass = saveg_read8(); switch (tclass) { case tc_endspecials: return; // end of list case tc_ceiling: saveg_read_pad(); ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); saveg_read_ceiling_t(ceiling); ceiling->sector->specialdata = ceiling; if (ceiling->thinker.function.acp1) ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; P_AddThinker (&ceiling->thinker); P_AddActiveCeiling(ceiling); break; case tc_door: saveg_read_pad(); door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); saveg_read_vldoor_t(door); door->sector->specialdata = door; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; P_AddThinker (&door->thinker); break; case tc_floor: saveg_read_pad(); floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); saveg_read_floormove_t(floor); floor->sector->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; P_AddThinker (&floor->thinker); break; case tc_plat: saveg_read_pad(); plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); saveg_read_plat_t(plat); plat->sector->specialdata = plat; if (plat->thinker.function.acp1) plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; P_AddThinker (&plat->thinker); P_AddActivePlat(plat); break; case tc_flash: saveg_read_pad(); flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); saveg_read_lightflash_t(flash); flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; P_AddThinker (&flash->thinker); break; case tc_strobe: saveg_read_pad(); strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); saveg_read_strobe_t(strobe); strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; P_AddThinker (&strobe->thinker); break; case tc_glow: saveg_read_pad(); glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); saveg_read_glow_t(glow); glow->thinker.function.acp1 = (actionf_p1)T_Glow; P_AddThinker (&glow->thinker); break; default: I_Error ("P_UnarchiveSpecials:Unknown tclass %i " "in savegame",tclass); } } } crispy-doom-crispy-doom-5.6.4/src/doom/p_saveg.h000066400000000000000000000031611360717211000215570ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Savegame I/O, archiving, persistence. // #ifndef __P_SAVEG__ #define __P_SAVEG__ #include #define SAVEGAME_EOF 0x1d #define VERSIONSIZE 16 // maximum size of a savegame description #define SAVESTRINGSIZE 24 // temporary filename to use while saving. char *P_TempSaveGameFile(void); // filename to use for a savegame slot char *P_SaveGameFile(int slot); // Savegame file header read/write functions boolean P_ReadSaveGameHeader(void); void P_WriteSaveGameHeader(char *description); // Savegame end-of-file read/write functions boolean P_ReadSaveGameEOF(void); void P_WriteSaveGameEOF(void); // 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_RestoreTargets (void); extern FILE *save_stream; extern boolean savegame_error; #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_setup.c000066400000000000000000001010331360717211000216020ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Do all the WAD I/O, get map description, // set up initial state and misc. LUTs. // #include #include "z_zone.h" #include "deh_main.h" #include "i_swap.h" #include "m_argv.h" #include "m_bbox.h" #include "m_misc.h" // [crispy] M_StringJoin() #include "g_game.h" #include "i_system.h" #include "w_wad.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "s_musinfo.h" // [crispy] S_ParseMusInfo() #include "doomstat.h" #include "p_extnodes.h" // [crispy] support extended node formats void P_SpawnMapThing (mapthing_t* mthing); // // 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; static int totallines; // BLOCKMAP // Created from axis aligned bounding box // of the map, a rectangular array of // blocks of size ... // Used to speed up collision detection // by spatial subdivision in 2D. // // Blockmap size. int bmapwidth; int bmapheight; // size in mapblocks int32_t* blockmap; // int for larger maps // [crispy] BLOCKMAP limit // offsets in blockmap are from here int32_t* blockmaplump; // [crispy] BLOCKMAP limit // origin of block map fixed_t bmaporgx; fixed_t bmaporgy; // for thing chains mobj_t** blocklinks; // REJECT // For fast sight rejection. // Speeds up enemy AI by skipping detailed // LineOf Sight calculation. // Without special effect, this could be // used as a PVS lookup as well. // byte* rejectmatrix; // Maintain single and multi player starting spots. #define MAX_DEATHMATCH_STARTS 10 mapthing_t deathmatchstarts[MAX_DEATHMATCH_STARTS]; mapthing_t* deathmatch_p; mapthing_t playerstarts[MAXPLAYERS]; boolean playerstartsingame[MAXPLAYERS]; // [crispy] recalculate seg offsets // adapted from prboom-plus/src/p_setup.c:474-482 fixed_t GetOffset(vertex_t *v1, vertex_t *v2) { fixed_t dx, dy; fixed_t r; dx = (v1->x - v2->x)>>FRACBITS; dy = (v1->y - v2->y)>>FRACBITS; r = (fixed_t)(sqrt(dx*dx + dy*dy))<x = SHORT(ml->x)<y = SHORT(ml->y)<r_x = li->x; li->r_y = li->y; li->moved = false; } // Free buffer memory. W_ReleaseLumpNum(lump); } // // GetSectorAtNullAddress // sector_t* GetSectorAtNullAddress(void) { static boolean null_sector_is_initialized = false; static sector_t null_sector; if (!null_sector_is_initialized) { memset(&null_sector, 0, sizeof(null_sector)); I_GetMemoryValue(0, &null_sector.floorheight, 4); I_GetMemoryValue(4, &null_sector.ceilingheight, 4); null_sector_is_initialized = true; } return &null_sector; } // // P_LoadSegs // void P_LoadSegs (int lump) { byte* data; int i; mapseg_t* ml; seg_t* li; line_t* ldef; int linedef; int side; int sidenum; numsegs = W_LumpLength (lump) / sizeof(mapseg_t); segs = Z_Malloc (numsegs*sizeof(seg_t),PU_LEVEL,0); memset (segs, 0, numsegs*sizeof(seg_t)); data = W_CacheLumpNum (lump,PU_STATIC); ml = (mapseg_t *)data; li = segs; for (i=0 ; iv1 = &vertexes[(unsigned short)SHORT(ml->v1)]; // [crispy] extended nodes li->v2 = &vertexes[(unsigned short)SHORT(ml->v2)]; // [crispy] extended nodes li->angle = (SHORT(ml->angle))<offset = (SHORT(ml->offset))<linedef); // [crispy] extended nodes ldef = &lines[linedef]; li->linedef = ldef; side = SHORT(ml->side); // 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]]; li->frontsector = sides[ldef->sidenum[side]].sector; // [crispy] recalculate li->offset = GetOffset(li->v1, (ml->side ? ldef->v2 : ldef->v1)); if (ldef-> flags & ML_TWOSIDED) { sidenum = ldef->sidenum[side ^ 1]; // If the sidenum is out of range, this may be a "glass hack" // impassible window. Point at side #0 (this may not be // the correct Vanilla behavior; however, it seems to work for // OTTAWAU.WAD, which is the one place I've seen this trick // used). if (sidenum < 0 || sidenum >= numsides) { // [crispy] linedef has two-sided flag set, but no valid second sidedef; // but since it has a midtexture, it is supposed to be rendered just // like a regular one-sided linedef if (li->sidedef->midtexture) { li->backsector = 0; fprintf(stderr, "P_LoadSegs: Linedef %d has two-sided flag set, but no second sidedef\n", i); } else li->backsector = GetSectorAtNullAddress(); } else { li->backsector = sides[sidenum].sector; } } else { li->backsector = 0; } } W_ReleaseLumpNum(lump); } // [crispy] fix long wall wobble void P_SegLengths (boolean contrast_only) { int i; const int rightangle = abs(finesine[(ANG60/2) >> ANGLETOFINESHIFT]); for (i = 0; i < numsegs; i++) { seg_t *const li = &segs[i]; int64_t dx, dy; dx = li->v2->r_x - li->v1->r_x; dy = li->v2->r_y - li->v1->r_y; if (!contrast_only) { li->length = (uint32_t)(sqrt((double)dx*dx + (double)dy*dy)/2); // [crispy] re-calculate angle used for rendering viewx = li->v1->r_x; viewy = li->v1->r_y; li->r_angle = R_PointToAngleCrispy(li->v2->r_x, li->v2->r_y); } // [crispy] smoother fake contrast if (!dy) li->fakecontrast = -LIGHTBRIGHT; else if (abs(finesine[li->r_angle >> ANGLETOFINESHIFT]) < rightangle) li->fakecontrast = -(LIGHTBRIGHT >> 1); else if (!dx) li->fakecontrast = LIGHTBRIGHT; else if (abs(finecosine[li->r_angle >> ANGLETOFINESHIFT]) < rightangle) li->fakecontrast = LIGHTBRIGHT >> 1; else li->fakecontrast = 0; } } // // P_LoadSubsectors // void P_LoadSubsectors (int lump) { byte* data; int i; mapsubsector_t* ms; subsector_t* ss; numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t); subsectors = Z_Malloc (numsubsectors*sizeof(subsector_t),PU_LEVEL,0); data = W_CacheLumpNum (lump,PU_STATIC); // [crispy] fail on missing subsectors if (!data || !numsubsectors) I_Error("P_LoadSubsectors: No subsectors in map!"); ms = (mapsubsector_t *)data; memset (subsectors,0, numsubsectors*sizeof(subsector_t)); ss = subsectors; for (i=0 ; inumlines = (unsigned short)SHORT(ms->numsegs); // [crispy] extended nodes ss->firstline = (unsigned short)SHORT(ms->firstseg); // [crispy] extended nodes } W_ReleaseLumpNum(lump); } // // P_LoadSectors // void P_LoadSectors (int lump) { byte* data; int i; mapsector_t* ms; sector_t* ss; // [crispy] fail on missing sectors if (lump >= numlumps) I_Error("P_LoadSectors: No sectors in map!"); numsectors = W_LumpLength (lump) / sizeof(mapsector_t); sectors = Z_Malloc (numsectors*sizeof(sector_t),PU_LEVEL,0); memset (sectors, 0, numsectors*sizeof(sector_t)); data = W_CacheLumpNum (lump,PU_STATIC); // [crispy] fail on missing sectors if (!data || !numsectors) I_Error("P_LoadSectors: No sectors in map!"); ms = (mapsector_t *)data; ss = sectors; for (i=0 ; ifloorheight = SHORT(ms->floorheight)<ceilingheight = SHORT(ms->ceilingheight)<floorpic = R_FlatNumForName(ms->floorpic); ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); ss->lightlevel = SHORT(ms->lightlevel); ss->special = SHORT(ms->special); ss->tag = SHORT(ms->tag); ss->thinglist = NULL; // [crispy] WiggleFix: [kb] for R_FixWiggle() ss->cachedheight = 0; // [AM] Sector interpolation. Even if we're // not running uncapped, the renderer still // uses this data. ss->oldfloorheight = ss->floorheight; ss->interpfloorheight = ss->floorheight; ss->oldceilingheight = ss->ceilingheight; ss->interpceilingheight = ss->ceilingheight; // [crispy] inhibit sector interpolation during the 0th gametic ss->oldgametic = -1; } W_ReleaseLumpNum(lump); } // // P_LoadNodes // void P_LoadNodes (int lump) { byte* data; int i; int j; int k; mapnode_t* mn; node_t* no; numnodes = W_LumpLength (lump) / sizeof(mapnode_t); nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0); data = W_CacheLumpNum (lump,PU_STATIC); // [crispy] warn about missing nodes if (!data || !numnodes) { if (numsubsectors == 1) fprintf(stderr, "P_LoadNodes: No nodes in map, but only one subsector.\n"); else I_Error("P_LoadNodes: No nodes in map!"); } mn = (mapnode_t *)data; no = nodes; for (i=0 ; ix = SHORT(mn->x)<y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = (unsigned short)SHORT(mn->children[j]); // [crispy] extended nodes // [crispy] add support for extended nodes // from prboom-plus/src/p_setup.c:937-957 if (no->children[j] == 0xFFFF) no->children[j] = -1; else if (no->children[j] & 0x8000) { no->children[j] &= ~0x8000; if (no->children[j] >= numsubsectors) no->children[j] = 0; no->children[j] |= NF_SUBSECTOR; } for (k=0 ; k<4 ; k++) no->bbox[j][k] = SHORT(mn->bbox[j][k])<type)) { case 68: // Arachnotron case 64: // Archvile case 88: // Boss Brain case 89: // Boss Shooter case 69: // Hell Knight case 67: // Mancubus case 71: // Pain Elemental case 65: // Former Human Commando case 66: // Revenant case 84: // Wolf SS spawn = false; break; } } if (spawn == false) break; // Do spawn all other stuff. spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); spawnthing.options = SHORT(mt->options); P_SpawnMapThing(&spawnthing); } if (!deathmatch) { for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && !playerstartsingame[i]) { I_Error("P_LoadThings: Player %d start missing (vanilla crashes here)", i + 1); } playerstartsingame[i] = false; } } W_ReleaseLumpNum(lump); } // // P_LoadLineDefs // Also counts secret lines for intermissions. // void P_LoadLineDefs (int lump) { byte* data; int i; maplinedef_t* mld; line_t* ld; vertex_t* v1; vertex_t* v2; int warn, warn2; // [crispy] warn about invalid linedefs numlines = W_LumpLength (lump) / sizeof(maplinedef_t); lines = Z_Malloc (numlines*sizeof(line_t),PU_LEVEL,0); memset (lines, 0, numlines*sizeof(line_t)); data = W_CacheLumpNum (lump,PU_STATIC); mld = (maplinedef_t *)data; ld = lines; warn = warn2 = 0; // [crispy] warn about invalid linedefs for (i=0 ; iflags = (unsigned short)SHORT(mld->flags); // [crispy] extended nodes ld->special = SHORT(mld->special); // [crispy] warn about unknown linedef types if ((unsigned short) ld->special > 141) { fprintf(stderr, "P_LoadLineDefs: Unknown special %d at line %d.\n", ld->special, i); warn++; } ld->tag = SHORT(mld->tag); // [crispy] warn about special linedefs without tag if (ld->special && !ld->tag) { switch (ld->special) { 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 case 271: // MBF sky transfers case 272: case 48: // Scroll Wall Left case 85: // [crispy] [JN] (Boom) Scroll Texture Right case 11: // s1 Exit level case 51: // s1 Secret exit case 52: // w1 Exit level case 124: // w1 Secret exit break; default: fprintf(stderr, "P_LoadLineDefs: Special linedef %d without tag.\n", i); warn2++; break; } } v1 = ld->v1 = &vertexes[(unsigned short)SHORT(mld->v1)]; // [crispy] extended nodes v2 = ld->v2 = &vertexes[(unsigned short)SHORT(mld->v2)]; // [crispy] extended nodes ld->dx = v2->x - v1->x; ld->dy = v2->y - v1->y; if (!ld->dx) ld->slopetype = ST_VERTICAL; else if (!ld->dy) ld->slopetype = ST_HORIZONTAL; else { if (FixedDiv (ld->dy , ld->dx) > 0) ld->slopetype = ST_POSITIVE; else ld->slopetype = ST_NEGATIVE; } if (v1->x < v2->x) { ld->bbox[BOXLEFT] = v1->x; ld->bbox[BOXRIGHT] = v2->x; } else { ld->bbox[BOXLEFT] = v2->x; ld->bbox[BOXRIGHT] = v1->x; } if (v1->y < v2->y) { ld->bbox[BOXBOTTOM] = v1->y; ld->bbox[BOXTOP] = v2->y; } else { ld->bbox[BOXBOTTOM] = v2->y; ld->bbox[BOXTOP] = v1->y; } // [crispy] calculate sound origin of line to be its midpoint ld->soundorg.x = ld->bbox[BOXLEFT] / 2 + ld->bbox[BOXRIGHT] / 2; ld->soundorg.y = ld->bbox[BOXTOP] / 2 + ld->bbox[BOXBOTTOM] / 2; ld->sidenum[0] = SHORT(mld->sidenum[0]); ld->sidenum[1] = SHORT(mld->sidenum[1]); // [crispy] substitute dummy sidedef for missing right side if (ld->sidenum[0] == NO_INDEX) { ld->sidenum[0] = 0; fprintf(stderr, "P_LoadLineDefs: linedef %d without first sidedef!\n", i); } if (ld->sidenum[0] != NO_INDEX) // [crispy] extended nodes ld->frontsector = sides[ld->sidenum[0]].sector; else ld->frontsector = 0; if (ld->sidenum[1] != NO_INDEX) // [crispy] extended nodes ld->backsector = sides[ld->sidenum[1]].sector; else ld->backsector = 0; } // [crispy] warn about unknown linedef types if (warn) { fprintf(stderr, "P_LoadLineDefs: Found %d line%s with unknown linedef type.\n", warn, (warn > 1) ? "s" : ""); } // [crispy] warn about special linedefs without tag if (warn2) { fprintf(stderr, "P_LoadLineDefs: Found %d special linedef%s without tag.\n", warn2, (warn2 > 1) ? "s" : ""); } if (warn || warn2) { fprintf(stderr, "THIS MAP MAY NOT WORK AS EXPECTED!\n"); } W_ReleaseLumpNum(lump); } // // P_LoadSideDefs // void P_LoadSideDefs (int lump) { byte* data; int i; mapsidedef_t* msd; side_t* sd; numsides = W_LumpLength (lump) / sizeof(mapsidedef_t); sides = Z_Malloc (numsides*sizeof(side_t),PU_LEVEL,0); memset (sides, 0, numsides*sizeof(side_t)); data = W_CacheLumpNum (lump,PU_STATIC); msd = (mapsidedef_t *)data; sd = sides; for (i=0 ; itextureoffset = SHORT(msd->textureoffset)<rowoffset = SHORT(msd->rowoffset)<toptexture = R_TextureNumForName(msd->toptexture); sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); sd->midtexture = R_TextureNumForName(msd->midtexture); sd->sector = §ors[SHORT(msd->sector)]; // [crispy] smooth texture scrolling sd->basetextureoffset = sd->textureoffset; } W_ReleaseLumpNum(lump); } // // P_LoadBlockMap // boolean P_LoadBlockMap (int lump) { int i; int count; int lumplen; short *wadblockmaplump; // [crispy] (re-)create BLOCKMAP if necessary if (M_CheckParm("-blockmap") || lump >= numlumps || (lumplen = W_LumpLength(lump)) < 8 || (count = lumplen / 2) >= 0x10000) { return false; } // [crispy] remove BLOCKMAP limit // adapted from boom202s/P_SETUP.C:1025-1076 wadblockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL); W_ReadLump(lump, wadblockmaplump); blockmaplump = Z_Malloc(sizeof(*blockmaplump) * count, PU_LEVEL, NULL); blockmap = blockmaplump + 4; blockmaplump[0] = SHORT(wadblockmaplump[0]); blockmaplump[1] = SHORT(wadblockmaplump[1]); blockmaplump[2] = (int32_t)(SHORT(wadblockmaplump[2])) & 0xffff; blockmaplump[3] = (int32_t)(SHORT(wadblockmaplump[3])) & 0xffff; // Swap all short integers to native byte ordering. for (i=4; ifirstline]; ss->sector = seg->sidedef->sector; } // count number of lines in each sector li = lines; totallines = 0; for (i=0 ; ifrontsector->linecount++; if (li->backsector && li->backsector != li->frontsector) { li->backsector->linecount++; totallines++; } } // build line tables for each sector linebuffer = Z_Malloc (totallines*sizeof(line_t *), PU_LEVEL, 0); for (i=0; ifrontsector != NULL) { sector = li->frontsector; sector->lines[sector->linecount] = li; ++sector->linecount; } if (li->backsector != NULL && li->frontsector != li->backsector) { sector = li->backsector; sector->lines[sector->linecount] = li; ++sector->linecount; } } // Generate bounding boxes for sectors sector = sectors; for (i=0 ; ilinecount; j++) { li = sector->lines[j]; M_AddToBox (bbox, li->v1->x, li->v1->y); M_AddToBox (bbox, li->v2->x, li->v2->y); } // set the degenmobj_t to the middle of the bounding box sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2; sector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2; // adjust bounding box to map blocks block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT; block = block >= bmapheight ? bmapheight-1 : block; sector->blockbox[BOXTOP]=block; block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXBOTTOM]=block; block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT; block = block >= bmapwidth ? bmapwidth-1 : block; sector->blockbox[BOXRIGHT]=block; block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXLEFT]=block; } } // [crispy] remove slime trails // mostly taken from Lee Killough's implementation in mbfsrc/P_SETUP.C:849-924, // with the exception that not the actual vertex coordinates are modified, // but separate coordinates that are *only* used in rendering, // i.e. r_bsp.c:R_AddLine() static void P_RemoveSlimeTrails(void) { int i; for (i = 0; i < numsegs; i++) { const line_t *l = segs[i].linedef; vertex_t *v = segs[i].v1; // [crispy] ignore exactly vertical or horizontal linedefs if (l->dx && l->dy) { do { // [crispy] vertex wasn't already moved if (!v->moved) { v->moved = true; // [crispy] ignore endpoints of linedefs if (v != l->v1 && v != l->v2) { // [crispy] move the vertex towards the linedef // by projecting it using the law of cosines int64_t dx2 = (l->dx >> FRACBITS) * (l->dx >> FRACBITS); int64_t dy2 = (l->dy >> FRACBITS) * (l->dy >> FRACBITS); int64_t dxy = (l->dx >> FRACBITS) * (l->dy >> FRACBITS); int64_t s = dx2 + dy2; // [crispy] MBF actually overrides v->x and v->y here v->r_x = (fixed_t)((dx2 * v->x + dy2 * l->v1->x + dxy * (v->y - l->v1->y)) / s); v->r_y = (fixed_t)((dy2 * v->y + dx2 * l->v1->y + dxy * (v->x - l->v1->x)) / s); // [crispy] wait a minute... moved more than 8 map units? // maybe that's a linguortal then, back to the original coordinates if (abs(v->r_x - v->x) > 8*FRACUNIT || abs(v->r_y - v->y) > 8*FRACUNIT) { v->r_x = v->x; v->r_y = v->y; } } } // [crispy] if v doesn't point to the second vertex of the seg already, point it there } while ((v != segs[i].v2) && (v = segs[i].v2)); } } } // Pad the REJECT lump with extra data when the lump is too small, // to simulate a REJECT buffer overflow in Vanilla Doom. static void PadRejectArray(byte *array, unsigned int len) { unsigned int i; unsigned int byte_num; byte *dest; unsigned int padvalue; // Values to pad the REJECT array with: unsigned int rejectpad[4] = { 0, // Size 0, // Part of z_zone block header 50, // PU_LEVEL 0x1d4a11 // DOOM_CONST_ZONEID }; rejectpad[0] = ((totallines * 4 + 3) & ~3) + 24; // Copy values from rejectpad into the destination array. dest = array; for (i=0; i> (byte_num * 8)) & 0xff; ++dest; } // We only have a limited pad size. Print a warning if the // REJECT lump is too small. if (len > sizeof(rejectpad)) { fprintf(stderr, "PadRejectArray: REJECT lump too short to pad! (%u > %i)\n", len, (int) sizeof(rejectpad)); // Pad remaining space with 0 (or 0xff, if specified on command line). if (M_CheckParm("-reject_pad_with_ff")) { padvalue = 0xff; } else { padvalue = 0xf00; } memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad)); } } static void P_LoadReject(int lumpnum) { int minlength; int lumplen; // Calculate the size that the REJECT lump *should* be. minlength = (numsectors * numsectors + 7) / 8; // If the lump meets the minimum length, it can be loaded directly. // Otherwise, we need to allocate a buffer of the correct size // and pad it with appropriate data. lumplen = W_LumpLength(lumpnum); if (lumplen >= minlength) { rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL); } else { rejectmatrix = Z_Malloc(minlength, PU_LEVEL, &rejectmatrix); W_ReadLump(lumpnum, rejectmatrix); PadRejectArray(rejectmatrix + lumplen, minlength - lumplen); } } // [crispy] log game skill in plain text const char *skilltable[] = { "Nothing", "Baby", "Easy", "Normal", "Hard", "Nightmare" }; // [crispy] factor out map lump name and number finding into a separate function int P_GetNumForMap (int episode, int map, boolean critical) { char lumpname[9]; int lumpnum; // find map name if ( gamemode == commercial) { if (map<10) DEH_snprintf(lumpname, 9, "map0%i", map); else DEH_snprintf(lumpname, 9, "map%i", map); } else { lumpname[0] = 'E'; lumpname[1] = '0' + episode; lumpname[2] = 'M'; lumpname[3] = '0' + map; lumpname[4] = 0; } // [crispy] special-casing for E1M10 "Sewers" support if (crispy->havee1m10 && episode == 1 && map == 10) DEH_snprintf(lumpname, 9, "E1M10"); lumpnum = critical ? W_GetNumForName (lumpname) : W_CheckNumForName (lumpname); if (nervewadfile && episode != 2 && map <= 9) { lumpnum = W_CheckNumForNameFromTo (lumpname, lumpnum - 1, 0); } return lumpnum; } // pointer to the current map lump info struct lumpinfo_t *maplumpinfo; // // P_SetupLevel // void P_SetupLevel ( int episode, int map, int playermask, skill_t skill) { int i; char lumpname[9]; int lumpnum; boolean crispy_validblockmap; mapformat_t crispy_mapformat; totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; // [crispy] count spawned monsters extrakills = 0; wminfo.partime = 180; for (i=0 ; idemowarp == map) { crispy->demowarp = 0; nodrawers = false; singletics = false; } // [crispy] don't load map's default music if loaded from a savegame with MUSINFO data if (!musinfo.from_savegame) { // Make sure all sounds are stopped before Z_FreeTags. S_Start (); } musinfo.from_savegame = false; Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1); // UNUSED W_Profile (); P_InitThinkers (); // if working with a devlopment map, reload it W_Reload (); // [crispy] factor out map lump name and number finding into a separate function /* // find map name if ( gamemode == commercial) { if (map<10) DEH_snprintf(lumpname, 9, "map0%i", map); else DEH_snprintf(lumpname, 9, "map%i", map); } else { lumpname[0] = 'E'; lumpname[1] = '0' + episode; lumpname[2] = 'M'; lumpname[3] = '0' + map; lumpname[4] = 0; } lumpnum = W_GetNumForName (lumpname); */ lumpnum = P_GetNumForMap (episode, map, true); maplumpinfo = lumpinfo[lumpnum]; strncpy(lumpname, maplumpinfo->name, 8); leveltime = 0; oldleveltime = 0; // [crispy] better logging { extern int savedleveltime; const int ltime = savedleveltime / TICRATE, ttime = (totalleveltimes + savedleveltime) / TICRATE; char *rfn_str; rfn_str = M_StringJoin( respawnparm ? " -respawn" : "", fastparm ? " -fast" : "", nomonsters ? " -nomonsters" : "", NULL); fprintf(stderr, "P_SetupLevel: %s (%s) %s%s %d:%02d:%02d/%d:%02d:%02d ", maplumpinfo->name, W_WadNameForLump(maplumpinfo), skilltable[BETWEEN(0,5,(int) skill+1)], rfn_str, ltime/3600, (ltime%3600)/60, ltime%60, ttime/3600, (ttime%3600)/60, ttime%60); free(rfn_str); } // [crispy] check and log map and nodes format crispy_mapformat = P_CheckMapFormat(lumpnum); // note: most of this ordering is important crispy_validblockmap = P_LoadBlockMap (lumpnum+ML_BLOCKMAP); // [crispy] (re-)create BLOCKMAP if necessary P_LoadVertexes (lumpnum+ML_VERTEXES); P_LoadSectors (lumpnum+ML_SECTORS); P_LoadSideDefs (lumpnum+ML_SIDEDEFS); if (crispy_mapformat & MFMT_HEXEN) P_LoadLineDefs_Hexen (lumpnum+ML_LINEDEFS); else P_LoadLineDefs (lumpnum+ML_LINEDEFS); // [crispy] (re-)create BLOCKMAP if necessary if (!crispy_validblockmap) { extern void P_CreateBlockMap (void); P_CreateBlockMap(); } if (crispy_mapformat & (MFMT_ZDBSPX | MFMT_ZDBSPZ)) P_LoadNodes_ZDBSP (lumpnum+ML_NODES, crispy_mapformat & MFMT_ZDBSPZ); else if (crispy_mapformat & MFMT_DEEPBSP) { P_LoadSubsectors_DeePBSP (lumpnum+ML_SSECTORS); P_LoadNodes_DeePBSP (lumpnum+ML_NODES); P_LoadSegs_DeePBSP (lumpnum+ML_SEGS); } else { P_LoadSubsectors (lumpnum+ML_SSECTORS); P_LoadNodes (lumpnum+ML_NODES); P_LoadSegs (lumpnum+ML_SEGS); } P_GroupLines (); P_LoadReject (lumpnum+ML_REJECT); // [crispy] remove slime trails P_RemoveSlimeTrails(); // [crispy] fix long wall wobble P_SegLengths(false); // [crispy] blinking key or skull in the status bar memset(st_keyorskull, 0, sizeof(st_keyorskull)); bodyqueslot = 0; deathmatch_p = deathmatchstarts; if (crispy_mapformat & MFMT_HEXEN) P_LoadThings_Hexen (lumpnum+ML_THINGS); else P_LoadThings (lumpnum+ML_THINGS); // if deathmatch, randomly spawn the active players if (deathmatch) { for (i=0 ; isprite]; if (!sprdef->numframes || !(mobjinfo[i].flags & (MF_SOLID|MF_SHOOTABLE))) { mobjinfo[i].actualheight = mobjinfo[i].height; continue; } sprframe = &sprdef->spriteframes[state->frame & FF_FRAMEMASK]; lump = sprframe->lump[0]; patch = W_CacheLumpNum (lump + firstspritelump, PU_CACHE); // [crispy] round to the next integer multiple of 8 mobjinfo[i].actualheight = ((patch->height + 7) & (~7)) << FRACBITS; } } // // P_Init // void P_Init (void) { P_InitSwitchList (); P_InitPicAnims (); R_InitSprites (sprnames); P_InitActualHeights(); } crispy-doom-crispy-doom-5.6.4/src/doom/p_setup.h000066400000000000000000000017521360717211000216160ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Setup a game, startup stuff. // #ifndef __P_SETUP__ #define __P_SETUP__ #include "w_wad.h" extern lumpinfo_t *maplumpinfo; // [crispy] pointer to the map lump about to load extern lumpinfo_t *savemaplumpinfo; // NOT called by W_Ticker. Fixme. void P_SetupLevel ( int episode, int map, int playermask, skill_t skill); // Called by startup code. void P_Init (void); #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_sight.c000066400000000000000000000201331360717211000215610ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // LineOfSight/Visibility checks, uses REJECT Lookup Table. // #include "doomdef.h" #include "doomstat.h" #include "i_system.h" #include "p_local.h" // State. #include "r_state.h" // // P_CheckSight // fixed_t sightzstart; // eye z of looker fixed_t topslope; fixed_t bottomslope; // slopes to top and bottom of target divline_t strace; // from t1 to t2 fixed_t t2x; fixed_t t2y; int sightcounts[2]; // PTR_SightTraverse() for Doom 1.2 sight calculations // taken from prboom-plus/src/p_sight.c:69-102 boolean 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_DivlineSide // Returns side 0 (front), 1 (back), or 2 (on). // int P_DivlineSide ( fixed_t x, fixed_t y, divline_t* node ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!node->dx) { if (x==node->x) return 2; if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (x==node->y) return 2; if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); left = (node->dy>>FRACBITS) * (dx>>FRACBITS); right = (dy>>FRACBITS) * (node->dx>>FRACBITS); if (right < left) return 0; // front side if (left == right) return 2; return 1; // back side } // // P_InterceptVector2 // Returns the fractional intercept point // along the first divline. // This is only called by the addthings and addlines traversers. // fixed_t P_InterceptVector2 ( divline_t* v2, divline_t* v1 ) { fixed_t frac; fixed_t num; fixed_t den; den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy) + FixedMul ( (v2->y - v1->y)>>8 , v1->dx); frac = FixedDiv (num , den); return frac; } // // P_CrossSubsector // Returns true // if strace crosses the given subsector successfully. // boolean P_CrossSubsector (int num) { seg_t* seg; line_t* line; int s1; int s2; int count; subsector_t* sub; sector_t* front; sector_t* back; fixed_t opentop; fixed_t openbottom; divline_t divl; vertex_t* v1; vertex_t* v2; fixed_t frac; fixed_t slope; #ifdef RANGECHECK if (num>=numsubsectors) I_Error ("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors); #endif sub = &subsectors[num]; // check lines count = sub->numlines; seg = &segs[sub->firstline]; for ( ; count ; seg++, count--) { line = seg->linedef; // allready checked other side? if (line->validcount == validcount) continue; line->validcount = validcount; v1 = line->v1; v2 = line->v2; s1 = P_DivlineSide (v1->x,v1->y, &strace); s2 = P_DivlineSide (v2->x, v2->y, &strace); // line isn't crossed? if (s1 == s2) continue; divl.x = v1->x; divl.y = v1->y; divl.dx = v2->x - v1->x; divl.dy = v2->y - v1->y; s1 = P_DivlineSide (strace.x, strace.y, &divl); s2 = P_DivlineSide (t2x, t2y, &divl); // line isn't crossed? if (s1 == s2) continue; // Backsector may be NULL if this is an "impassible // glass" hack line. if (line->backsector == NULL) { return false; } // stop because it is not two sided anyway // might do this after updating validcount? if ( !(line->flags & ML_TWOSIDED) ) return false; // crosses a two sided line front = seg->frontsector; back = seg->backsector; // no wall to block sight with? if (front->floorheight == back->floorheight && front->ceilingheight == back->ceilingheight) continue; // possible occluder // because of ceiling height differences if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; // because of ceiling height differences if (front->floorheight > back->floorheight) openbottom = front->floorheight; else openbottom = back->floorheight; // quick test for totally closed doors if (openbottom >= opentop) return false; // stop frac = P_InterceptVector2 (&strace, &divl); if (front->floorheight != back->floorheight) { slope = FixedDiv (openbottom - sightzstart , frac); if (slope > bottomslope) bottomslope = slope; } if (front->ceilingheight != back->ceilingheight) { slope = FixedDiv (opentop - sightzstart , frac); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop } // passed the subsector ok return true; } // // P_CrossBSPNode // Returns true // if strace crosses the given node successfully. // boolean P_CrossBSPNode (int bspnum) { node_t* bsp; int side; if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) return P_CrossSubsector (0); else return P_CrossSubsector (bspnum&(~NF_SUBSECTOR)); } bsp = &nodes[bspnum]; // decide which side the start point is on side = P_DivlineSide (strace.x, strace.y, (divline_t *)bsp); if (side == 2) side = 0; // an "on" should cross both sides // cross the starting side if (!P_CrossBSPNode (bsp->children[side]) ) return false; // the partition plane is crossed here if (side == P_DivlineSide (t2x, t2y,(divline_t *)bsp)) { // the line doesn't touch the other side return true; } // cross the ending side return P_CrossBSPNode (bsp->children[side^1]); } // // P_CheckSight // Returns true // if a straight line between t1 and t2 is unobstructed. // Uses REJECT. // boolean P_CheckSight ( mobj_t* t1, mobj_t* t2 ) { int s1; int s2; int pnum; int bytenum; int bitnum; // First check for trivial rejection. // Determine subsector entries in REJECT table. s1 = (t1->subsector->sector - sectors); s2 = (t2->subsector->sector - sectors); pnum = s1*numsectors + s2; bytenum = pnum>>3; bitnum = 1 << (pnum&7); // Check in REJECT table. if (rejectmatrix[bytenum]&bitnum) { sightcounts[0]++; // can't possibly be connected return false; } // An unobstructed LOS is possible. // Now look from eyes of t1 to any part of t2. sightcounts[1]++; validcount++; sightzstart = t1->z + t1->height - (t1->height>>2); topslope = (t2->z+t2->height) - sightzstart; bottomslope = (t2->z) - sightzstart; if (gameversion <= exe_doom_1_2) { return P_PathTraverse(t1->x, t1->y, t2->x, t2->y, PT_EARLYOUT | PT_ADDLINES, PTR_SightTraverse); } strace.x = t1->x; strace.y = t1->y; t2x = t2->x; t2y = t2->y; strace.dx = t2->x - t1->x; strace.dy = t2->y - t1->y; // the head node is the last node output return P_CrossBSPNode (numnodes-1); } crispy-doom-crispy-doom-5.6.4/src/doom/p_spec.c000066400000000000000000001046771360717211000214150ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Implements special effects: // Texture animation, height or lighting changes // according to adjacent sectors, respective // utility functions, etc. // Line Tag handling. Line and Sector triggers. // #include #include "doomdef.h" #include "doomstat.h" #include "deh_main.h" #include "i_system.h" #include "i_swap.h" // [crispy] LONG() #include "z_zone.h" #include "m_argv.h" #include "m_misc.h" #include "m_random.h" #include "w_wad.h" #include "r_swirl.h" // [crispy] R_InitDistortedFlats() #include "r_local.h" #include "p_local.h" #include "g_game.h" #include "s_sound.h" // State. #include "r_state.h" // Data. #include "sounds.h" #define HUSTR_SECRETFOUND "A secret is revealed!" // // Animating textures and planes // There is another anim_t used in wi_stuff, unrelated. // typedef struct { boolean istexture; int picnum; int basepic; int numpics; int speed; } anim_t; // // source animation definition // // [crispy] change istexture type from int to char and // add PACKEDATTR for reading ANIMATED lumps from memory typedef PACKED_STRUCT ( { signed char istexture; // if false, it is a flat char endname[9]; char startname[9]; int speed; }) animdef_t; #define MAXANIMS 32 // [crispy] remove MAXANIMS limit extern anim_t* anims; extern anim_t* lastanim; // // P_InitPicAnims // // Floor/ceiling animation sequences, // defined by first and last frame, // i.e. the flat (64x64 tile) name to // be used. // The full animation sequence is given // using all the flats between the start // and end entry, in the order found in // the WAD file. // // [crispy] add support for ANIMATED lumps animdef_t animdefs_vanilla[] = { {false, "NUKAGE3", "NUKAGE1", 8}, {false, "FWATER4", "FWATER1", 8}, {false, "SWATER4", "SWATER1", 8}, {false, "LAVA4", "LAVA1", 8}, {false, "BLOOD3", "BLOOD1", 8}, // DOOM II flat animations. {false, "RROCK08", "RROCK05", 8}, {false, "SLIME04", "SLIME01", 8}, {false, "SLIME08", "SLIME05", 8}, {false, "SLIME12", "SLIME09", 8}, {true, "BLODGR4", "BLODGR1", 8}, {true, "SLADRIP3", "SLADRIP1", 8}, {true, "BLODRIP4", "BLODRIP1", 8}, {true, "FIREWALL", "FIREWALA", 8}, {true, "GSTFONT3", "GSTFONT1", 8}, {true, "FIRELAVA", "FIRELAV3", 8}, {true, "FIREMAG3", "FIREMAG1", 8}, {true, "FIREBLU2", "FIREBLU1", 8}, {true, "ROCKRED3", "ROCKRED1", 8}, {true, "BFALL4", "BFALL1", 8}, {true, "SFALL4", "SFALL1", 8}, {true, "WFALL4", "WFALL1", 8}, {true, "DBRAIN4", "DBRAIN1", 8}, {-1, "", "", 0}, }; // [crispy] remove MAXANIMS limit anim_t* anims; anim_t* lastanim; static size_t maxanims; // // Animating line specials // #define MAXLINEANIMS 64*256 extern short numlinespecials; extern line_t* linespeciallist[MAXLINEANIMS]; void P_InitPicAnims (void) { int i; boolean init_swirl = false; // [crispy] add support for ANIMATED lumps animdef_t *animdefs; const boolean from_lump = (W_CheckNumForName("ANIMATED") != -1); if (from_lump) { animdefs = W_CacheLumpName("ANIMATED", PU_STATIC); } else { animdefs = animdefs_vanilla; } // Init animation lastanim = anims; for (i=0 ; animdefs[i].istexture != -1 ; i++) { const char *startname, *endname; // [crispy] remove MAXANIMS limit if (lastanim >= anims + maxanims) { size_t newmax = maxanims ? 2 * maxanims : MAXANIMS; anims = I_Realloc(anims, newmax * sizeof(*anims)); lastanim = anims + maxanims; maxanims = newmax; } startname = DEH_String(animdefs[i].startname); endname = DEH_String(animdefs[i].endname); if (animdefs[i].istexture) { // different episode ? if (R_CheckTextureNumForName(startname) == -1) continue; lastanim->picnum = R_TextureNumForName(endname); lastanim->basepic = R_TextureNumForName(startname); } else { if (W_CheckNumForName(startname) == -1) continue; lastanim->picnum = R_FlatNumForName(endname); lastanim->basepic = R_FlatNumForName(startname); } lastanim->istexture = animdefs[i].istexture; lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; lastanim->speed = from_lump ? LONG(animdefs[i].speed) : animdefs[i].speed; // [crispy] add support for SMMU swirling flats if (lastanim->speed > 65535 || lastanim->numpics == 1) { init_swirl = true; } else if (lastanim->numpics < 2) I_Error ("P_InitPicAnims: bad cycle from %s to %s", startname, endname); lastanim++; } if (from_lump) { W_ReleaseLumpName("ANIMATED"); } if (init_swirl) { R_InitDistortedFlats(); } } // // UTILITIES // // // getSide() // Will return a side_t* // given the number of the current sector, // the line number, and the side (0/1) that you want. // side_t* getSide ( int currentSector, int line, int side ) { return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ]; } // // getSector() // Will return a sector_t* // given the number of the current sector, // the line number and the side (0/1) that you want. // sector_t* getSector ( int currentSector, int line, int side ) { return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector; } // // twoSided() // Given the sector number and the line number, // it will tell you whether the line is two-sided or not. // int twoSided ( int sector, int line ) { return (sectors[sector].lines[line])->flags & ML_TWOSIDED; } // // getNextSector() // Return sector_t * of sector next to current. // NULL if not two-sided line // sector_t* getNextSector ( line_t* line, sector_t* sec ) { if (!(line->flags & ML_TWOSIDED)) return NULL; if (line->frontsector == sec) return line->backsector; return line->frontsector; } // // P_FindLowestFloorSurrounding() // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS // fixed_t P_FindLowestFloorSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t floor = sec->floorheight; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight < floor) floor = other->floorheight; } return floor; } // // P_FindHighestFloorSurrounding() // FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS // fixed_t P_FindHighestFloorSurrounding(sector_t *sec) { int i; line_t* check; sector_t* other; fixed_t floor = -500*FRACUNIT; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight > floor) floor = other->floorheight; } return floor; } // // P_FindNextHighestFloor // FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS // Note: this should be doable w/o a fixed array. // Thanks to entryway for the Vanilla overflow emulation. // 20 adjoining sectors max! #define MAX_ADJOINING_SECTORS 20 fixed_t P_FindNextHighestFloor ( sector_t* sec, int currentheight ) { int i; int h; int min; line_t* check; sector_t* other; fixed_t height = currentheight; static fixed_t *heightlist = NULL; static int heightlist_size = 0; // [crispy] remove MAX_ADJOINING_SECTORS Vanilla limit // from prboom-plus/src/p_spec.c:404-411 if (sec->linecount > heightlist_size) { do { heightlist_size = heightlist_size ? 2 * heightlist_size : MAX_ADJOINING_SECTORS; } while (sec->linecount > heightlist_size); heightlist = I_Realloc(heightlist, heightlist_size * sizeof(*heightlist)); } for (i=0, h=0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight > height) { // Emulation of memory (stack) overflow if (h == MAX_ADJOINING_SECTORS + 1) { height = other->floorheight; } else if (h == MAX_ADJOINING_SECTORS + 2) { // Fatal overflow: game crashes at 22 sectors fprintf(stderr, "Sector with more than 22 adjoining sectors. " "Vanilla will crash here\n"); } heightlist[h++] = other->floorheight; } } // Find lowest height in list if (!h) { return currentheight; } min = heightlist[0]; // Range checking? for (i = 1; i < h; i++) { if (heightlist[i] < min) { min = heightlist[i]; } } return min; } // // FIND LOWEST CEILING IN THE SURROUNDING SECTORS // fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t height = INT_MAX; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->ceilingheight < height) height = other->ceilingheight; } return height; } // // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS // fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t height = 0; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->ceilingheight > height) height = other->ceilingheight; } return height; } // // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO // int P_FindSectorFromLineTag ( line_t* line, int start ) { int i; #if 0 // [crispy] linedefs without tags apply locally if (crispy->singleplayer && !line->tag) { for (i=start+1;ibacksector) { const long linedef = line - lines; fprintf(stderr, "P_FindSectorFromLineTag: Linedef %ld without tag applied to sector %d\n", linedef, i); return i; } } else #else // [crispy] emit a warning for linedefs without tags if (!line->tag) { const long linedef = line - lines; fprintf(stderr, "P_FindSectorFromLineTag: Linedef %ld without tag\n", linedef); } #endif for (i=start+1;itag) return i; return -1; } // // Find minimum light from an adjacent sector // int P_FindMinSurroundingLight ( sector_t* sector, int max ) { int i; int min; line_t* line; sector_t* check; min = max; for (i=0 ; i < sector->linecount ; i++) { line = sector->lines[i]; check = getNextSector(line,sector); if (!check) continue; if (check->lightlevel < min) min = check->lightlevel; } return min; } // // EVENTS // Events are operations triggered by using, crossing, // or shooting special lines, or by timed thinkers. // // // P_CrossSpecialLine - TRIGGER // Called every time a thing origin is about // to cross a line with a non 0 special. // void P_CrossSpecialLine ( int linenum, int side, mobj_t* thing ) { return P_CrossSpecialLinePtr(&lines[linenum], side, thing); } // [crispy] more MBF code pointers void P_CrossSpecialLinePtr ( line_t* line, int side, mobj_t* thing ) { // line_t* line; int ok; // line = &lines[linenum]; if (gameversion <= exe_doom_1_2) { if (line->special > 98 && line->special != 104) { return; } } else { // Triggers that other things can activate if (!thing->player) { // Things that should NOT trigger specials... switch(thing->type) { case MT_ROCKET: case MT_PLASMA: case MT_BFG: case MT_TROOPSHOT: case MT_HEADSHOT: case MT_BRUISERSHOT: return; default: break; } } } if (!thing->player) { ok = 0; switch(line->special) { case 39: // TELEPORT TRIGGER case 97: // TELEPORT RETRIGGER case 125: // TELEPORT MONSTERONLY TRIGGER case 126: // TELEPORT MONSTERONLY RETRIGGER case 4: // RAISE DOOR case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER ok = 1; break; } if (!ok) return; } // Note: could use some const's here. switch (line->special) { // TRIGGERS. // All from here to RETRIGGERS. case 2: // Open Door EV_DoDoor(line,vld_open); line->special = 0; break; case 3: // Close Door EV_DoDoor(line,vld_close); line->special = 0; break; case 4: // Raise Door EV_DoDoor(line,vld_normal); line->special = 0; break; case 5: // Raise Floor EV_DoFloor(line,raiseFloor); line->special = 0; break; case 6: // Fast Ceiling Crush & Raise EV_DoCeiling(line,fastCrushAndRaise); line->special = 0; break; case 8: // Build Stairs EV_BuildStairs(line,build8); line->special = 0; break; case 10: // PlatDownWaitUp EV_DoPlat(line,downWaitUpStay,0); line->special = 0; break; case 12: // Light Turn On - brightest near EV_LightTurnOn(line,0); line->special = 0; break; case 13: // Light Turn On 255 EV_LightTurnOn(line,255); line->special = 0; break; case 16: // Close Door 30 EV_DoDoor(line,vld_close30ThenOpen); line->special = 0; break; case 17: // Start Light Strobing EV_StartLightStrobing(line); line->special = 0; break; case 19: // Lower Floor EV_DoFloor(line,lowerFloor); line->special = 0; break; case 22: // Raise floor to nearest height and change texture EV_DoPlat(line,raiseToNearestAndChange,0); line->special = 0; break; case 25: // Ceiling Crush and Raise EV_DoCeiling(line,crushAndRaise); line->special = 0; break; case 30: // Raise floor to shortest texture height // on either side of lines. EV_DoFloor(line,raiseToTexture); line->special = 0; break; case 35: // Lights Very Dark EV_LightTurnOn(line,35); line->special = 0; break; case 36: // Lower Floor (TURBO) EV_DoFloor(line,turboLower); line->special = 0; break; case 37: // LowerAndChange EV_DoFloor(line,lowerAndChange); line->special = 0; break; case 38: // Lower Floor To Lowest EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break; case 39: // TELEPORT! EV_Teleport( line, side, thing ); line->special = 0; break; case 40: // RaiseCeilingLowerFloor EV_DoCeiling( line, raiseToHighest ); EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break; case 44: // Ceiling Crush EV_DoCeiling( line, lowerAndCrush ); line->special = 0; break; case 52: // EXIT! G_ExitLevel (); break; case 53: // Perpetual Platform Raise EV_DoPlat(line,perpetualRaise,0); line->special = 0; break; case 54: // Platform Stop EV_StopPlat(line); line->special = 0; break; case 56: // Raise Floor Crush EV_DoFloor(line,raiseFloorCrush); line->special = 0; break; case 57: // Ceiling Crush Stop EV_CeilingCrushStop(line); line->special = 0; break; case 58: // Raise Floor 24 EV_DoFloor(line,raiseFloor24); line->special = 0; break; case 59: // Raise Floor 24 And Change EV_DoFloor(line,raiseFloor24AndChange); line->special = 0; break; case 104: // Turn lights off in sector(tag) EV_TurnTagLightsOff(line); line->special = 0; break; case 108: // Blazing Door Raise (faster than TURBO!) EV_DoDoor (line,vld_blazeRaise); line->special = 0; break; case 109: // Blazing Door Open (faster than TURBO!) EV_DoDoor (line,vld_blazeOpen); line->special = 0; break; case 100: // Build Stairs Turbo 16 EV_BuildStairs(line,turbo16); line->special = 0; break; case 110: // Blazing Door Close (faster than TURBO!) EV_DoDoor (line,vld_blazeClose); line->special = 0; break; case 119: // Raise floor to nearest surr. floor EV_DoFloor(line,raiseFloorToNearest); line->special = 0; break; case 121: // Blazing PlatDownWaitUpStay EV_DoPlat(line,blazeDWUS,0); line->special = 0; break; case 124: // Secret EXIT G_SecretExitLevel (); break; case 125: // TELEPORT MonsterONLY if (!thing->player) { EV_Teleport( line, side, thing ); line->special = 0; } break; case 130: // Raise Floor Turbo EV_DoFloor(line,raiseFloorTurbo); line->special = 0; break; case 141: // Silent Ceiling Crush & Raise EV_DoCeiling(line,silentCrushAndRaise); line->special = 0; break; // RETRIGGERS. All from here till end. case 72: // Ceiling Crush EV_DoCeiling( line, lowerAndCrush ); break; case 73: // Ceiling Crush and Raise EV_DoCeiling(line,crushAndRaise); break; case 74: // Ceiling Crush Stop EV_CeilingCrushStop(line); break; case 75: // Close Door EV_DoDoor(line,vld_close); break; case 76: // Close Door 30 EV_DoDoor(line,vld_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,vld_open); break; case 87: // Perpetual Platform Raise EV_DoPlat(line,perpetualRaise,0); break; case 88: // PlatDownWaitUp EV_DoPlat(line,downWaitUpStay,0); break; case 89: // Platform Stop EV_StopPlat(line); break; case 90: // Raise Door EV_DoDoor(line,vld_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,vld_blazeRaise); break; case 106: // Blazing Door Open (faster than TURBO!) EV_DoDoor (line,vld_blazeOpen); break; case 107: // Blazing Door Close (faster than TURBO!) EV_DoDoor (line,vld_blazeClose); break; case 120: // Blazing PlatDownWaitUpStay. EV_DoPlat(line,blazeDWUS,0); break; case 126: // TELEPORT MonsterONLY. if (!thing->player) EV_Teleport( line, side, thing ); break; case 128: // Raise To Nearest Floor EV_DoFloor(line,raiseFloorToNearest); break; case 129: // Raise Floor Turbo EV_DoFloor(line,raiseFloorTurbo); break; } } // // P_ShootSpecialLine - IMPACT SPECIALS // Called when a thing shoots a special line. // void P_ShootSpecialLine ( mobj_t* thing, line_t* line ) { int ok; // Impacts that other things can activate. if (!thing->player) { ok = 0; switch(line->special) { case 46: // OPEN DOOR IMPACT ok = 1; break; } if (!ok) return; } switch(line->special) { case 24: // RAISE FLOOR EV_DoFloor(line,raiseFloor); P_ChangeSwitchTexture(line,0); break; case 46: // OPEN DOOR EV_DoDoor(line,vld_open); P_ChangeSwitchTexture(line,1); break; case 47: // RAISE FLOOR NEAR AND CHANGE EV_DoPlat(line,raiseToNearestAndChange,0); P_ChangeSwitchTexture(line,0); break; } } // // P_PlayerInSpecialSector // Called every tic frame // that the player origin is in a special sector // void P_PlayerInSpecialSector (player_t* player) { sector_t* sector; extern int showMessages; static sector_t* error; sector = player->mo->subsector->sector; // Falling, not all the way down yet? if (player->mo->z != sector->floorheight) return; // Has hitten ground. switch (sector->special) { case 5: // HELLSLIME DAMAGE // [crispy] no nukage damage with NOCLIP cheat if (!player->powers[pw_ironfeet] && !(player->mo->flags & MF_NOCLIP)) if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 10); break; case 7: // NUKAGE DAMAGE // [crispy] no nukage damage with NOCLIP cheat if (!player->powers[pw_ironfeet] && !(player->mo->flags & MF_NOCLIP)) if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 5); break; case 16: // SUPER HELLSLIME DAMAGE case 4: // STROBE HURT // [crispy] no nukage damage with NOCLIP cheat if ((!player->powers[pw_ironfeet] || (P_Random()<5) ) && !(player->mo->flags & MF_NOCLIP)) { if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 20); } break; case 9: // SECRET SECTOR player->secretcount++; // [crispy] show centered "Secret Revealed!" message if (showMessages && crispy->secretmessage && player == &players[consoleplayer]) { int sfx_id; static char str_count[32]; M_snprintf(str_count, sizeof(str_count), "Secret %d of %d revealed!", player->secretcount, totalsecret); // [crispy] play DSSECRET if available sfx_id = I_GetSfxLumpNum(&S_sfx[sfx_secret]) != -1 ? sfx_secret : sfx_itmbk; player->centermessage = (crispy->secretmessage == SECRETMESSAGE_COUNT) ? str_count : HUSTR_SECRETFOUND; S_StartSound(NULL, sfx_id); } // [crispy] remember revealed secrets sector->oldspecial = sector->special; sector->special = 0; break; case 11: // EXIT SUPER DAMAGE! (for E1M8 finale) player->cheats &= ~CF_GODMODE; if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 20); if (player->health <= 10) G_ExitLevel(); break; default: // [crispy] ignore unknown special sectors if (error != sector) { error = sector; fprintf (stderr, "P_PlayerInSpecialSector: " "unknown special %i\n", sector->special); } break; }; } // // P_UpdateSpecials // Animate planes, scroll walls, etc. // boolean levelTimer; int levelTimeCount; void P_UpdateSpecials (void) { anim_t* anim; int pic; int i; line_t* line; // LEVEL TIMER if (levelTimer == true) { levelTimeCount--; if (!levelTimeCount) G_ExitLevel(); } // ANIMATE FLATS AND TEXTURES GLOBALLY for (anim = anims ; anim < lastanim ; anim++) { for (i=anim->basepic ; ibasepic+anim->numpics ; i++) { pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics ); if (anim->istexture) texturetranslation[i] = pic; else { // [crispy] add support for SMMU swirling flats if (anim->speed > 65535 || anim->numpics == 1) { flattranslation[i] = -1; } else flattranslation[i] = pic; } } } // ANIMATE LINE SPECIALS for (i = 0; i < numlinespecials; i++) { line = linespeciallist[i]; switch(line->special) { case 48: // EFFECT FIRSTCOL SCROLL + // [crispy] smooth texture scrolling sides[line->sidenum[0]].basetextureoffset += FRACUNIT; sides[line->sidenum[0]].textureoffset = sides[line->sidenum[0]].basetextureoffset; break; case 85: // [JN] (Boom) Scroll Texture Right // [crispy] smooth texture scrolling sides[line->sidenum[0]].basetextureoffset -= FRACUNIT; sides[line->sidenum[0]].textureoffset = sides[line->sidenum[0]].basetextureoffset; break; } } // DO BUTTONS for (i = 0; i < maxbuttons; i++) if (buttonlist[i].btimer) { buttonlist[i].btimer--; if (!buttonlist[i].btimer) { switch(buttonlist[i].where) { case top: sides[buttonlist[i].line->sidenum[0]].toptexture = buttonlist[i].btexture; break; case middle: sides[buttonlist[i].line->sidenum[0]].midtexture = buttonlist[i].btexture; break; case bottom: sides[buttonlist[i].line->sidenum[0]].bottomtexture = buttonlist[i].btexture; break; } // [crispy] & [JN] Logically proper sound behavior. // Do not play second "sfx_swtchn" on two-sided linedefs that attached to special sectors, // and always play second sound on single-sided linedefs. if (crispy->soundfix) { if (!buttonlist[i].line->backsector || !buttonlist[i].line->backsector->specialdata) { S_StartSoundOnce(buttonlist[i].soundorg,sfx_swtchn); } } else { S_StartSoundOnce(&buttonlist[i].soundorg,sfx_swtchn); } memset(&buttonlist[i],0,sizeof(button_t)); } } // [crispy] draw fuzz effect independent of rendering frame rate R_SetFuzzPosTic(); } // [crispy] smooth texture scrolling void R_InterpolateTextureOffsets (void) { if (crispy->uncapped && leveltime > oldleveltime) { int i; for (i = 0; i < numlinespecials; i++) { const line_t *const line = linespeciallist[i]; side_t *const side = &sides[line->sidenum[0]]; if (line->special == 48) { side->textureoffset = side->basetextureoffset + fractionaltic; } else if (line->special == 85) { side->textureoffset = side->basetextureoffset - fractionaltic; } } } } // // Donut overrun emulation // // Derived from the code from PrBoom+. Thanks go to Andrey Budko (entryway) // as usual :-) // #define DONUT_FLOORHEIGHT_DEFAULT 0x00000000 #define DONUT_FLOORPIC_DEFAULT 0x16 static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic, line_t *line, sector_t *pillar_sector) { static int first = 1; static int tmp_s3_floorheight; static int tmp_s3_floorpic; extern int numflats; if (first) { int p; // This is the first time we have had an overrun. first = 0; // Default values tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT; tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; //! // @category compat // @arg // // Use the specified magic values when emulating behavior caused // by memory overruns from improperly constructed donuts. // In Vanilla Doom this can differ depending on the operating // system. The default (if this option is not specified) is to // emulate the behavior when running under Windows 98. p = M_CheckParmWithArgs("-donut", 2); if (p > 0) { // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008 // // C:\>debug // -d 0:0 // // DOS 6.22: // 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) // DOS 7.1: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // Win98: // 0000:0000 (00 00 00 00) 65 04 70 00-(16 00) // DOSBox under XP: // 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) M_StrToInt(myargv[p + 1], &tmp_s3_floorheight); M_StrToInt(myargv[p + 2], &tmp_s3_floorpic); if (tmp_s3_floorpic >= numflats) { fprintf(stderr, "DonutOverrun: The second parameter for \"-donut\" " "switch should be greater than 0 and less than number " "of flats (%d). Using default value (%d) instead. \n", numflats, DONUT_FLOORPIC_DEFAULT); tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; } } } /* fprintf(stderr, "Linedef: %d; Sector: %d; " "New floor height: %d; New floor pic: %d\n", line->iLineID, pillar_sector->iSectorID, tmp_s3_floorheight >> 16, tmp_s3_floorpic); */ *s3_floorheight = (fixed_t) tmp_s3_floorheight; *s3_floorpic = (short) tmp_s3_floorpic; } // // Special Stuff that can not be categorized // int EV_DoDonut(line_t* line) { sector_t* s1; sector_t* s2; sector_t* s3; int secnum; int rtn; int i; floormove_t* floor; fixed_t s3_floorheight; short s3_floorpic; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { s1 = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (s1->specialdata) continue; rtn = 1; s2 = getNextSector(s1->lines[0],s1); // Vanilla Doom does not check if the linedef is one sided. The // game does not crash, but reads invalid memory and causes the // sector floor to move "down" to some unknown height. // DOSbox prints a warning about an invalid memory access. // // I'm not sure exactly what invalid memory is being read. This // isn't something that should be done, anyway. // Just print a warning and return. if (s2 == NULL) { fprintf(stderr, "EV_DoDonut: linedef had no second sidedef! " "Unexpected behavior may occur in Vanilla Doom. \n"); break; } for (i = 0; i < s2->linecount; i++) { s3 = s2->lines[i]->backsector; if (s3 == s1) continue; if (s3 == NULL) { // e6y // s3 is NULL, so // s3->floorheight is an int at 0000:0000 // s3->floorpic is a short at 0000:0008 // Trying to emulate fprintf(stderr, "EV_DoDonut: WARNING: emulating buffer overrun due to " "NULL back sector. " "Unexpected behavior may occur in Vanilla Doom.\n"); DonutOverrun(&s3_floorheight, &s3_floorpic, line, s1); } else { s3_floorheight = s3->floorheight; s3_floorpic = s3->floorpic; } // Spawn rising slime floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s2->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = donutRaise; floor->crush = false; floor->direction = 1; floor->sector = s2; floor->speed = FLOORSPEED / 2; floor->texture = s3_floorpic; floor->newspecial = 0; floor->floordestheight = s3_floorheight; // Spawn lowering donut-hole floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s1->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = lowerFloor; floor->crush = false; floor->direction = -1; floor->sector = s1; floor->speed = FLOORSPEED / 2; floor->floordestheight = s3_floorheight; break; } } return rtn; } // // SPECIAL SPAWNING // // // P_SpawnSpecials // After the map has been loaded, scan for specials // that spawn thinkers // short numlinespecials; line_t* linespeciallist[MAXLINEANIMS]; static unsigned int NumScrollers() { unsigned int i, scrollers = 0; for (i = 0; i < numlines; i++) { if (48 == lines[i].special) { scrollers++; } } return scrollers; } // Parses command line parameters. void P_SpawnSpecials (void) { sector_t* sector; int i; // See if -TIMER was specified. if (timelimit > 0 && deathmatch) { levelTimer = true; levelTimeCount = timelimit * 60 * TICRATE; } else { levelTimer = false; } // Init special SECTORs. sector = sectors; for (i=0 ; ispecial) continue; switch (sector->special) { case 1: // FLICKERING LIGHTS P_SpawnLightFlash (sector); break; case 2: // STROBE FAST P_SpawnStrobeFlash(sector,FASTDARK,0); break; case 3: // STROBE SLOW P_SpawnStrobeFlash(sector,SLOWDARK,0); break; case 4: // STROBE FAST/DEATH SLIME P_SpawnStrobeFlash(sector,FASTDARK,0); sector->special = 4; break; case 8: // GLOWING LIGHT P_SpawnGlowingLight(sector); break; case 9: // SECRET SECTOR totalsecret++; break; case 10: // DOOR CLOSE IN 30 SECONDS P_SpawnDoorCloseIn30 (sector); break; case 12: // SYNC STROBE SLOW P_SpawnStrobeFlash (sector, SLOWDARK, 1); break; case 13: // SYNC STROBE FAST P_SpawnStrobeFlash (sector, FASTDARK, 1); break; case 14: // DOOR RAISE IN 5 MINUTES P_SpawnDoorRaiseIn5Mins (sector, i); break; case 17: P_SpawnFireFlicker(sector); break; } } // Init line EFFECTs numlinespecials = 0; for (i = 0;i < numlines; i++) { switch(lines[i].special) { case 48: case 85: // [crispy] [JN] (Boom) Scroll Texture Right if (numlinespecials >= MAXLINEANIMS) { I_Error("Too many scrolling wall linedefs (%d)! " "(Vanilla limit is 64)", NumScrollers()); } // EFFECT FIRSTCOL SCROLL+ linespeciallist[numlinespecials] = &lines[i]; numlinespecials++; break; // [crispy] add support for MBF sky tranfers case 271: case 272: { int secnum; for (secnum = 0; secnum < numsectors; secnum++) { if (sectors[secnum].tag == lines[i].tag) { sectors[secnum].sky = i | PL_SKYFLAT; } } } break; } } // Init other misc stuff for (i = 0;i < MAXCEILINGS;i++) activeceilings[i] = NULL; for (i = 0;i < MAXPLATS;i++) activeplats[i] = NULL; for (i = 0;i < maxbuttons;i++) memset(&buttonlist[i],0,sizeof(button_t)); // UNUSED: no horizonal sliders. // P_InitSlidingDoorFrames(); } crispy-doom-crispy-doom-5.6.4/src/doom/p_spec.h000066400000000000000000000225361360717211000214130ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // Implements special effects: // Texture animation, height or lighting changes // according to adjacent sectors, respective // utility functions, etc. // #ifndef __P_SPEC__ #define __P_SPEC__ // // End-level timer (-TIMER option) // extern boolean levelTimer; extern int levelTimeCount; // Define values for map objects #define MO_TELEPORTMAN 14 // at game start void P_InitPicAnims (void); // at map load void P_SpawnSpecials (void); // every tic void P_UpdateSpecials (void); // when needed boolean P_UseSpecialLine ( mobj_t* thing, line_t* line, int side ); void P_ShootSpecialLine ( mobj_t* thing, line_t* line ); void P_CrossSpecialLine ( int linenum, int side, mobj_t* thing ); // [crispy] more MBF code pointers void P_CrossSpecialLinePtr ( line_t* line, int side, mobj_t* thing ); void P_PlayerInSpecialSector (player_t* player); int twoSided ( int sector, int line ); sector_t* getSector ( int currentSector, int line, int side ); side_t* getSide ( int currentSector, int line, int side ); fixed_t P_FindLowestFloorSurrounding(sector_t* sec); fixed_t P_FindHighestFloorSurrounding(sector_t* sec); fixed_t P_FindNextHighestFloor ( sector_t* sec, int currentheight ); fixed_t P_FindLowestCeilingSurrounding(sector_t* sec); fixed_t P_FindHighestCeilingSurrounding(sector_t* sec); int P_FindSectorFromLineTag ( line_t* line, int start ); int P_FindMinSurroundingLight ( sector_t* sector, int max ); sector_t* getNextSector ( line_t* line, sector_t* sec ); // // SPECIAL // int EV_DoDonut(line_t* line); // // P_LIGHTS // typedef struct { thinker_t thinker; sector_t* sector; int count; int maxlight; int minlight; } fireflicker_t; typedef struct { thinker_t thinker; sector_t* sector; int count; int maxlight; int minlight; int maxtime; int mintime; } lightflash_t; typedef struct { thinker_t thinker; sector_t* sector; int count; int minlight; int maxlight; int darktime; int brighttime; } strobe_t; typedef struct { thinker_t thinker; sector_t* sector; int minlight; int maxlight; int direction; } glow_t; #define GLOWSPEED 8 #define STROBEBRIGHT 5 #define FASTDARK 15 #define SLOWDARK 35 void P_SpawnFireFlicker (sector_t* sector); void T_LightFlash (lightflash_t* flash); void P_SpawnLightFlash (sector_t* sector); void T_StrobeFlash (strobe_t* flash); void P_SpawnStrobeFlash ( sector_t* sector, int fastOrSlow, int inSync ); void EV_StartLightStrobing(line_t* line); void EV_TurnTagLightsOff(line_t* line); void EV_LightTurnOn ( line_t* line, int bright ); void T_Glow(glow_t* g); void P_SpawnGlowingLight(sector_t* sector); // // P_SWITCH // // [crispy] add PACKEDATTR for reading SWITCHES lumps from memory typedef PACKED_STRUCT ( { char name1[9]; char name2[9]; short episode; }) switchlist_t; typedef enum { top, middle, bottom } bwhere_e; typedef struct { line_t* line; bwhere_e where; int btexture; int btimer; degenmobj_t *soundorg; } button_t; // max # of wall switches in a level #define MAXSWITCHES 50 // 4 players, 4 buttons each at once, max. #define MAXBUTTONS 16 // 1 second, in ticks. #define BUTTONTIME 35 extern button_t *buttonlist; extern int maxbuttons; void P_ChangeSwitchTexture ( line_t* line, int useAgain ); void P_InitSwitchList(void); // // P_PLATS // typedef enum { up, down, waiting, in_stasis } plat_e; typedef enum { perpetualRaise, downWaitUpStay, raiseAndChange, raiseToNearestAndChange, blazeDWUS } plattype_e; typedef struct { thinker_t thinker; sector_t* sector; fixed_t speed; fixed_t low; fixed_t high; int wait; int count; plat_e status; plat_e oldstatus; boolean crush; int tag; plattype_e type; } plat_t; #define PLATWAIT 3 #define PLATSPEED FRACUNIT #define MAXPLATS 30*256 extern plat_t* activeplats[MAXPLATS]; void T_PlatRaise(plat_t* plat); int EV_DoPlat ( line_t* line, plattype_e type, int amount ); void P_AddActivePlat(plat_t* plat); void P_RemoveActivePlat(plat_t* plat); void EV_StopPlat(line_t* line); void P_ActivateInStasis(int tag); // // P_DOORS // typedef enum { vld_normal, vld_close30ThenOpen, vld_close, vld_open, vld_raiseIn5Mins, vld_blazeRaise, vld_blazeOpen, vld_blazeClose } vldoor_e; typedef struct { thinker_t thinker; vldoor_e type; sector_t* sector; fixed_t topheight; fixed_t speed; // 1 = up, 0 = waiting at top, -1 = down int direction; // tics to wait at the top int topwait; // (keep in case a door going down is reset) // when it reaches 0, start going down int topcountdown; } vldoor_t; #define VDOORSPEED FRACUNIT*2 #define VDOORWAIT 150 void EV_VerticalDoor ( line_t* line, mobj_t* thing ); int EV_DoDoor ( line_t* line, vldoor_e type ); int EV_DoLockedDoor ( line_t* line, vldoor_e type, mobj_t* thing ); void T_VerticalDoor (vldoor_t* door); void P_SpawnDoorCloseIn30 (sector_t* sec); void P_SpawnDoorRaiseIn5Mins ( sector_t* sec, int secnum ); #if 0 // UNUSED // // Sliding doors... // typedef enum { sd_opening, sd_waiting, sd_closing } sd_e; typedef enum { sdt_openOnly, sdt_closeOnly, sdt_openAndClose } sdt_e; typedef struct { thinker_t thinker; sdt_e type; line_t* line; int frame; int whichDoorIndex; int timer; sector_t* frontsector; sector_t* backsector; sd_e status; } slidedoor_t; typedef struct { char frontFrame1[9]; char frontFrame2[9]; char frontFrame3[9]; char frontFrame4[9]; char backFrame1[9]; char backFrame2[9]; char backFrame3[9]; char backFrame4[9]; } slidename_t; typedef struct { int frontFrames[4]; int backFrames[4]; } slideframe_t; // how many frames of animation #define SNUMFRAMES 4 #define SDOORWAIT 35*3 #define SWAITTICS 4 // how many diff. types of anims #define MAXSLIDEDOORS 5 void P_InitSlidingDoorFrames(void); void EV_SlidingDoor ( line_t* line, mobj_t* thing ); #endif // // P_CEILNG // typedef enum { lowerToFloor, raiseToHighest, lowerAndCrush, crushAndRaise, fastCrushAndRaise, silentCrushAndRaise } ceiling_e; typedef struct { thinker_t thinker; ceiling_e type; sector_t* sector; fixed_t bottomheight; fixed_t topheight; fixed_t speed; boolean crush; // 1 = up, 0 = waiting, -1 = down int direction; // ID int tag; int olddirection; } ceiling_t; #define CEILSPEED FRACUNIT #define CEILWAIT 150 #define MAXCEILINGS 30 extern ceiling_t* activeceilings[MAXCEILINGS]; int EV_DoCeiling ( line_t* line, ceiling_e type ); void T_MoveCeiling (ceiling_t* ceiling); void P_AddActiveCeiling(ceiling_t* c); void P_RemoveActiveCeiling(ceiling_t* c); int EV_CeilingCrushStop(line_t* line); void P_ActivateInStasisCeiling(line_t* line); // // P_FLOOR // typedef enum { // lower floor to highest surrounding floor lowerFloor, // lower floor to lowest surrounding floor lowerFloorToLowest, // lower floor to highest surrounding floor VERY FAST turboLower, // raise floor to lowest surrounding CEILING raiseFloor, // raise floor to next highest surrounding floor raiseFloorToNearest, // raise floor to shortest height texture around it raiseToTexture, // lower floor to lowest surrounding floor // and change floorpic lowerAndChange, raiseFloor24, raiseFloor24AndChange, raiseFloorCrush, // raise to next highest floor, turbo-speed raiseFloorTurbo, donutRaise, raiseFloor512 } floor_e; typedef enum { build8, // slowly build by 8 turbo16 // quickly build by 16 } stair_e; typedef struct { thinker_t thinker; floor_e type; boolean crush; sector_t* sector; int direction; int newspecial; short texture; fixed_t floordestheight; fixed_t speed; } floormove_t; #define FLOORSPEED FRACUNIT typedef enum { ok, crushed, pastdest } result_e; result_e T_MovePlane ( sector_t* sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction ); int EV_BuildStairs ( line_t* line, stair_e type ); int EV_DoFloor ( line_t* line, floor_e floortype ); void T_MoveFloor( floormove_t* floor); // // P_TELEPT // int EV_Teleport ( line_t* line, int side, mobj_t* thing ); #endif crispy-doom-crispy-doom-5.6.4/src/doom/p_switch.c000066400000000000000000000372601360717211000217550ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // DESCRIPTION: // Switches, buttons. Two-state animation. Exits. // #include #include "i_system.h" #include "deh_main.h" #include "doomdef.h" #include "p_local.h" #include "i_swap.h" // [crispy] SHORT() #include "w_wad.h" // [crispy] W_CheckNumForName() #include "z_zone.h" // [crispy] PU_STATIC #include "g_game.h" #include "s_sound.h" // Data. #include "sounds.h" // State. #include "doomstat.h" #include "r_state.h" // // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE // // [crispy] add support for SWITCHES lumps switchlist_t alphSwitchList_vanilla[] = { // Doom shareware episode 1 switches {"SW1BRCOM", "SW2BRCOM", 1}, {"SW1BRN1", "SW2BRN1", 1}, {"SW1BRN2", "SW2BRN2", 1}, {"SW1BRNGN", "SW2BRNGN", 1}, {"SW1BROWN", "SW2BROWN", 1}, {"SW1COMM", "SW2COMM", 1}, {"SW1COMP", "SW2COMP", 1}, {"SW1DIRT", "SW2DIRT", 1}, {"SW1EXIT", "SW2EXIT", 1}, {"SW1GRAY", "SW2GRAY", 1}, {"SW1GRAY1", "SW2GRAY1", 1}, {"SW1METAL", "SW2METAL", 1}, {"SW1PIPE", "SW2PIPE", 1}, {"SW1SLAD", "SW2SLAD", 1}, {"SW1STARG", "SW2STARG", 1}, {"SW1STON1", "SW2STON1", 1}, {"SW1STON2", "SW2STON2", 1}, {"SW1STONE", "SW2STONE", 1}, {"SW1STRTN", "SW2STRTN", 1}, // Doom registered episodes 2&3 switches {"SW1BLUE", "SW2BLUE", 2}, {"SW1CMT", "SW2CMT", 2}, {"SW1GARG", "SW2GARG", 2}, {"SW1GSTON", "SW2GSTON", 2}, {"SW1HOT", "SW2HOT", 2}, {"SW1LION", "SW2LION", 2}, {"SW1SATYR", "SW2SATYR", 2}, {"SW1SKIN", "SW2SKIN", 2}, {"SW1VINE", "SW2VINE", 2}, {"SW1WOOD", "SW2WOOD", 2}, // Doom II switches {"SW1PANEL", "SW2PANEL", 3}, {"SW1ROCK", "SW2ROCK", 3}, {"SW1MET2", "SW2MET2", 3}, {"SW1WDMET", "SW2WDMET", 3}, {"SW1BRIK", "SW2BRIK", 3}, {"SW1MOD1", "SW2MOD1", 3}, {"SW1ZIM", "SW2ZIM", 3}, {"SW1STON6", "SW2STON6", 3}, {"SW1TEK", "SW2TEK", 3}, {"SW1MARB", "SW2MARB", 3}, {"SW1SKULL", "SW2SKULL", 3}, // [crispy] SWITCHES lumps are supposed to end like this {"\0", "\0", 0} }; // [crispy] remove MAXSWITCHES limit int *switchlist; int numswitches; static size_t maxswitches; button_t *buttonlist; // [crispy] remove MAXBUTTONS limit int maxbuttons; // [crispy] remove MAXBUTTONS limit // // P_InitSwitchList // Only called at game initialization. // void P_InitSwitchList(void) { int i, slindex, episode; // [crispy] add support for SWITCHES lumps switchlist_t *alphSwitchList; boolean from_lump; if ((from_lump = (W_CheckNumForName("SWITCHES") != -1))) { alphSwitchList = W_CacheLumpName("SWITCHES", PU_STATIC); } else { alphSwitchList = alphSwitchList_vanilla; } // Note that this is called "episode" here but it's actually something // quite different. As we progress from Shareware->Registered->Doom II // we support more switch textures. switch (gamemode) { case registered: case retail: episode = 2; break; case commercial: episode = 3; break; default: episode = 1; break; } slindex = 0; for (i = 0; alphSwitchList[i].episode; i++) { const short alphSwitchList_episode = from_lump ? SHORT(alphSwitchList[i].episode) : alphSwitchList[i].episode; // [crispy] remove MAXSWITCHES limit if (slindex + 1 >= maxswitches) { size_t newmax = maxswitches ? 2 * maxswitches : MAXSWITCHES; switchlist = I_Realloc(switchlist, newmax * sizeof(*switchlist)); maxswitches = newmax; } // [crispy] ignore switches referencing unknown texture names, // warn if either one is missing, but only add if both are valid if (alphSwitchList_episode <= episode) { int texture1, texture2; const char *name1 = DEH_String(alphSwitchList[i].name1); const char *name2 = DEH_String(alphSwitchList[i].name2); texture1 = R_CheckTextureNumForName(name1); texture2 = R_CheckTextureNumForName(name2); if (texture1 == -1 || texture2 == -1) { fprintf(stderr, "P_InitSwitchList: could not add %s(%d)/%s(%d)\n", name1, texture1, name2, texture2); } else { switchlist[slindex++] = texture1; switchlist[slindex++] = texture2; } } } numswitches = slindex / 2; switchlist[slindex] = -1; // [crispy] add support for SWITCHES lumps if (from_lump) { W_ReleaseLumpName("SWITCHES"); } // [crispy] pre-allocate some memory for the buttonlist[] array buttonlist = I_Realloc(NULL, sizeof(*buttonlist) * (maxbuttons = MAXBUTTONS)); memset(buttonlist, 0, sizeof(*buttonlist) * maxbuttons); } // // Start a button counting down till it turns off. // void P_StartButton ( line_t* line, bwhere_e w, int texture, int time ) { int i; // See if button is already pressed for (i = 0;i < maxbuttons;i++) { if (buttonlist[i].btimer && buttonlist[i].line == line) { // [crispy] register up to three buttons at once for lines with more than one switch texture if (buttonlist[i].where == w) { return; } } } for (i = 0;i < maxbuttons;i++) { if (!buttonlist[i].btimer) { buttonlist[i].line = line; buttonlist[i].where = w; buttonlist[i].btexture = texture; buttonlist[i].btimer = time; buttonlist[i].soundorg = crispy->soundfix ? &line->soundorg : &line->frontsector->soundorg; // [crispy] corrected sound source return; } } // [crispy] remove MAXBUTTONS limit { maxbuttons = 2 * maxbuttons; buttonlist = I_Realloc(buttonlist, sizeof(*buttonlist) * maxbuttons); memset(buttonlist + maxbuttons/2, 0, sizeof(*buttonlist) * maxbuttons/2); return P_StartButton(line, w, texture, time); } I_Error("P_StartButton: no button slots left!"); } // // Function that changes wall texture. // Tell it if switch is ok to use again (1=yes, it's a button). // void P_ChangeSwitchTexture ( line_t* line, int useAgain ) { int texTop; int texMid; int texBot; int i; int sound; boolean playsound = false; if (!useAgain) line->special = 0; texTop = sides[line->sidenum[0]].toptexture; texMid = sides[line->sidenum[0]].midtexture; texBot = sides[line->sidenum[0]].bottomtexture; sound = sfx_swtchn; // EXIT SWITCH? if (line->special == 11) sound = sfx_swtchx; for (i = 0;i < numswitches*2;i++) { if (switchlist[i] == texTop) { // S_StartSound(buttonlist->soundorg,sound); playsound = true; sides[line->sidenum[0]].toptexture = switchlist[i^1]; if (useAgain) P_StartButton(line,top,switchlist[i],BUTTONTIME); // return; } // [crispy] register up to three buttons at once for lines with more than one switch texture // else { if (switchlist[i] == texMid) { // S_StartSound(buttonlist->soundorg,sound); playsound = true; sides[line->sidenum[0]].midtexture = switchlist[i^1]; if (useAgain) P_StartButton(line, middle,switchlist[i],BUTTONTIME); // return; } // [crispy] register up to three buttons at once for lines with more than one switch texture // else { if (switchlist[i] == texBot) { // S_StartSound(buttonlist->soundorg,sound); playsound = true; sides[line->sidenum[0]].bottomtexture = switchlist[i^1]; if (useAgain) P_StartButton(line, bottom,switchlist[i],BUTTONTIME); // return; } } } } // [crispy] corrected sound source if (playsound) { S_StartSound(crispy->soundfix ? &line->soundorg : buttonlist->soundorg,sound); } } // // P_UseSpecialLine // Called when a thing uses a special line. // Only the front sides of lines are usable. // boolean P_UseSpecialLine ( mobj_t* thing, line_t* line, int side ) { // Err... // Use the back sides of VERY SPECIAL lines... if (side) { switch(line->special) { case 124: // Sliding door open&close // UNUSED? break; default: return false; break; } } // Switches that other things can activate. if (!thing->player) { // never open secret doors if (line->flags & ML_SECRET) return false; switch(line->special) { case 1: // MANUAL DOOR RAISE case 32: // MANUAL BLUE case 33: // MANUAL RED case 34: // MANUAL YELLOW break; default: return false; break; } } // do something switch (line->special) { // MANUALS case 1: // Vertical Door case 26: // Blue Door/Locked case 27: // Yellow Door /Locked case 28: // Red Door /Locked case 31: // Manual door open case 32: // Blue locked door open case 33: // Red locked door open case 34: // Yellow locked door open case 117: // Blazing door raise case 118: // Blazing door open EV_VerticalDoor (line, thing); break; //UNUSED - Door Slide Open&Close // case 124: // EV_SlidingDoor (line, thing); // break; // SWITCHES case 7: // Build Stairs if (EV_BuildStairs(line,build8)) P_ChangeSwitchTexture(line,0); break; case 9: // Change Donut if (EV_DoDonut(line)) P_ChangeSwitchTexture(line,0); break; case 11: // Exit level P_ChangeSwitchTexture(line,0); G_ExitLevel (); break; case 14: // Raise Floor 32 and change texture if (EV_DoPlat(line,raiseAndChange,32)) P_ChangeSwitchTexture(line,0); break; case 15: // Raise Floor 24 and change texture if (EV_DoPlat(line,raiseAndChange,24)) P_ChangeSwitchTexture(line,0); break; case 18: // Raise Floor to next highest floor if (EV_DoFloor(line, raiseFloorToNearest)) P_ChangeSwitchTexture(line,0); break; case 20: // Raise Plat next highest floor and change texture if (EV_DoPlat(line,raiseToNearestAndChange,0)) P_ChangeSwitchTexture(line,0); break; case 21: // PlatDownWaitUpStay if (EV_DoPlat(line,downWaitUpStay,0)) P_ChangeSwitchTexture(line,0); break; case 23: // Lower Floor to Lowest if (EV_DoFloor(line,lowerFloorToLowest)) P_ChangeSwitchTexture(line,0); break; case 29: // Raise Door if (EV_DoDoor(line,vld_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,vld_close)) P_ChangeSwitchTexture(line,0); break; case 51: // Secret EXIT P_ChangeSwitchTexture(line,0); G_SecretExitLevel (); break; case 55: // Raise Floor Crush if (EV_DoFloor(line,raiseFloorCrush)) P_ChangeSwitchTexture(line,0); break; case 101: // Raise Floor if (EV_DoFloor(line,raiseFloor)) P_ChangeSwitchTexture(line,0); break; case 102: // Lower Floor to Surrounding floor height if (EV_DoFloor(line,lowerFloor)) P_ChangeSwitchTexture(line,0); break; case 103: // Open Door if (EV_DoDoor(line,vld_open)) P_ChangeSwitchTexture(line,0); break; case 111: // Blazing Door Raise (faster than TURBO!) if (EV_DoDoor (line,vld_blazeRaise)) P_ChangeSwitchTexture(line,0); break; case 112: // Blazing Door Open (faster than TURBO!) if (EV_DoDoor (line,vld_blazeOpen)) P_ChangeSwitchTexture(line,0); break; case 113: // Blazing Door Close (faster than TURBO!) if (EV_DoDoor (line,vld_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,vld_blazeOpen,thing)) P_ChangeSwitchTexture(line,0); break; case 140: // Raise Floor 512 if (EV_DoFloor(line,raiseFloor512)) P_ChangeSwitchTexture(line,0); break; // BUTTONS case 42: // Close Door if (EV_DoDoor(line,vld_close)) P_ChangeSwitchTexture(line,1); break; case 43: // Lower Ceiling to Floor if (EV_DoCeiling(line,lowerToFloor)) P_ChangeSwitchTexture(line,1); break; case 45: // Lower Floor to Surrounding floor height if (EV_DoFloor(line,lowerFloor)) P_ChangeSwitchTexture(line,1); break; case 60: // Lower Floor to Lowest if (EV_DoFloor(line,lowerFloorToLowest)) P_ChangeSwitchTexture(line,1); break; case 61: // Open Door if (EV_DoDoor(line,vld_open)) P_ChangeSwitchTexture(line,1); break; case 62: // PlatDownWaitUpStay if (EV_DoPlat(line,downWaitUpStay,1)) P_ChangeSwitchTexture(line,1); break; case 63: // Raise Door if (EV_DoDoor(line,vld_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,vld_blazeRaise)) P_ChangeSwitchTexture(line,1); break; case 115: // Blazing Door Open (faster than TURBO!) if (EV_DoDoor (line,vld_blazeOpen)) P_ChangeSwitchTexture(line,1); break; case 116: // Blazing Door Close (faster than TURBO!) if (EV_DoDoor (line,vld_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,vld_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; } crispy-doom-crispy-doom-5.6.4/src/doom/p_telept.c000066400000000000000000000056231360717211000217470ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Teleportation. // #include "doomdef.h" #include "doomstat.h" #include "s_sound.h" #include "p_local.h" // Data. #include "sounds.h" // State. #include "r_state.h" // // TELEPORTATION // int EV_Teleport ( line_t* line, int side, mobj_t* thing ) { int i; int tag; mobj_t* m; mobj_t* fog; unsigned an; thinker_t* thinker; sector_t* sector; fixed_t oldx; fixed_t oldy; fixed_t oldz; // don't teleport missiles if (thing->flags & MF_MISSILE) return 0; // Don't teleport if hit back of line, // so you can get out of teleporter. if (side == 1) return 0; tag = line->tag; for (i = 0; i < numsectors; i++) { if (sectors[ i ].tag == tag ) { thinker = thinkercap.next; for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { // not a mobj if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) continue; m = (mobj_t *)thinker; // not a teleportman if (m->type != MT_TELEPORTMAN ) continue; sector = m->subsector->sector; // wrong sector if (sector-sectors != i ) continue; oldx = thing->x; oldy = thing->y; oldz = thing->z; if (!P_TeleportMove (thing, m->x, m->y)) return 0; // The first Final Doom executable does not set thing->z // when teleporting. This quirk is unique to this // particular version; the later version included in // some versions of the Id Anthology fixed this. if (gameversion != exe_final) thing->z = thing->floorz; if (thing->player) { thing->player->viewz = thing->z+thing->player->viewheight; // [crispy] center view after teleporting thing->player->centering = true; } // spawn teleport fog at source and destination fog = P_SpawnMobj (oldx, oldy, oldz, MT_TFOG); S_StartSound (fog, sfx_telept); an = m->angle >> ANGLETOFINESHIFT; fog = P_SpawnMobj (m->x+20*finecosine[an], m->y+20*finesine[an] , thing->z, MT_TFOG); // emit sound, where? S_StartSound (fog, sfx_telept); // don't move for a bit if (thing->player) thing->reactiontime = 18; thing->angle = m->angle; thing->momx = thing->momy = thing->momz = 0; return 1; } } } return 0; } crispy-doom-crispy-doom-5.6.4/src/doom/p_tick.c000066400000000000000000000057231360717211000214050ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Archiving: SaveGame I/O. // Thinker, Ticker. // #include "z_zone.h" #include "p_local.h" #include "s_musinfo.h" // [crispy] T_MAPMusic() #include "doomstat.h" int leveltime; // // THINKERS // All thinkers should be allocated by Z_Malloc // so they can be operated on uniformly. // The actual structures will vary in size, // but the first element must be thinker_t. // // Both the head and tail of the thinker list. thinker_t thinkercap; // // P_InitThinkers // void P_InitThinkers (void) { thinkercap.prev = thinkercap.next = &thinkercap; } // // P_AddThinker // Adds a new thinker at the end of the list. // void P_AddThinker (thinker_t* thinker) { thinkercap.prev->next = thinker; thinker->next = &thinkercap; thinker->prev = thinkercap.prev; thinkercap.prev = thinker; } // // P_RemoveThinker // Deallocation is lazy -- it will not actually be freed // until its thinking turn comes up. // void P_RemoveThinker (thinker_t* thinker) { // FIXME: NOP. thinker->function.acv = (actionf_v)(-1); } // // P_AllocateThinker // Allocates memory and adds a new thinker at the end of the list. // void P_AllocateThinker (thinker_t* thinker) { } // // P_RunThinkers // void P_RunThinkers (void) { thinker_t *currentthinker, *nextthinker; currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { if ( currentthinker->function.acv == (actionf_v)(-1) ) { // time to remove it nextthinker = currentthinker->next; currentthinker->next->prev = currentthinker->prev; currentthinker->prev->next = currentthinker->next; Z_Free(currentthinker); } else { if (currentthinker->function.acp1) currentthinker->function.acp1 (currentthinker); nextthinker = currentthinker->next; } currentthinker = nextthinker; } // [crispy] support MUSINFO lump (dynamic music changing) T_MusInfo(); } // // P_Ticker // void P_Ticker (void) { int i; // run the tic if (paused) return; // pause if in menu and at least one tic has been run if ( !netgame && menuactive && !demoplayback && players[consoleplayer].viewz != 1) { return; } for (i=0 ; i // [crispy] abs() #include "doomdef.h" #include "d_event.h" #include "p_local.h" #include "doomstat.h" // Index of the special effects (INVUL inverse) map. #define INVERSECOLORMAP 32 // // Movement. // // 16 pixels of bob #define MAXBOB 0x100000 // [crispy] variable player view bob static const fixed_t crispy_bobfactor[3] = {4, 3, 0}; boolean onground; // // P_Thrust // Moves the given origin along a given angle. // void P_Thrust ( player_t* player, angle_t angle, fixed_t move ) { angle >>= ANGLETOFINESHIFT; player->mo->momx += FixedMul(move,finecosine[angle]); player->mo->momy += FixedMul(move,finesine[angle]); } // // P_CalcHeight // Calculate the walking / running height adjustment // void P_CalcHeight (player_t* player) { int angle; fixed_t bob; // Regular movement bobbing // (needs to be calculated for gun swing // even if not on ground) // OPTIMIZE: tablify angle // Note: a LUT allows for effects // like a ramp with low health. player->bob = FixedMul (player->mo->momx, player->mo->momx) + FixedMul (player->mo->momy,player->mo->momy); player->bob >>= 2; if (player->bob>MAXBOB) player->bob = MAXBOB; // [crispy] variable player view bob player->bob2 = crispy_bobfactor[crispy->bobfactor] * player->bob / 4; if ((player->cheats & CF_NOMOMENTUM) || !onground) { player->viewz = player->mo->z + VIEWHEIGHT; if (player->viewz > player->mo->ceilingz-4*FRACUNIT) player->viewz = player->mo->ceilingz-4*FRACUNIT; // [crispy] fix player viewheight in NOMOMENTUM mode //player->viewz = player->mo->z + player->viewheight; return; } angle = (FINEANGLES/20*leveltime)&FINEMASK; bob = FixedMul ( player->bob2/2, finesine[angle]); // [crispy] variable player view bob // move viewheight if (player->playerstate == PST_LIVE) { player->viewheight += player->deltaviewheight; if (player->viewheight > VIEWHEIGHT) { player->viewheight = VIEWHEIGHT; player->deltaviewheight = 0; } if (player->viewheight < VIEWHEIGHT/2) { player->viewheight = VIEWHEIGHT/2; if (player->deltaviewheight <= 0) player->deltaviewheight = 1; } if (player->deltaviewheight) { player->deltaviewheight += FRACUNIT/4; if (!player->deltaviewheight) player->deltaviewheight = 1; } } player->viewz = player->mo->z + player->viewheight + bob; if (player->viewz > player->mo->ceilingz-4*FRACUNIT) player->viewz = player->mo->ceilingz-4*FRACUNIT; } // // P_MovePlayer // void P_MovePlayer (player_t* player) { ticcmd_t* cmd; int look; cmd = &player->cmd; player->mo->angle += (cmd->angleturn<mo->z <= player->mo->floorz); // [crispy] give full control in no-clipping mode onground |= (player->mo->flags & MF_NOCLIP); if (cmd->forwardmove && onground) P_Thrust (player, player->mo->angle, cmd->forwardmove*2048); else // [crispy] in-air movement is only possible with jumping enabled if (cmd->forwardmove && critical->jump) P_Thrust (player, player->mo->angle, FRACUNIT >> 8); if (cmd->sidemove && onground) P_Thrust (player, player->mo->angle-ANG90, cmd->sidemove*2048); else // [crispy] in-air movement is only possible with jumping enabled if (cmd->sidemove && critical->jump) P_Thrust(player, player->mo->angle, FRACUNIT >> 8); if ( (cmd->forwardmove || cmd->sidemove) && player->mo->state == &states[S_PLAY] ) { P_SetMobjState (player->mo, S_PLAY_RUN1); } // [crispy] apply lookdir delta look = cmd->lookfly & 15; if (look > 7) { look -= 16; } if (look) { if (look == TOCENTER) { player->centering = true; } else { cmd->lookdir = MLOOKUNIT * 5 * look; } } if (!menuactive && !demoplayback) { player->lookdir = BETWEEN(-LOOKDIRMIN * MLOOKUNIT, LOOKDIRMAX * MLOOKUNIT, player->lookdir + cmd->lookdir); } } // // P_DeathThink // Fall on your face when dying. // Decrease POV height to floor height. // #define ANG5 (ANG90/18) void P_DeathThink (player_t* player) { angle_t angle; angle_t delta; P_MovePsprites (player); // fall to the ground if (player->viewheight > 6*FRACUNIT) player->viewheight -= FRACUNIT; if (player->viewheight < 6*FRACUNIT) player->viewheight = 6*FRACUNIT; player->deltaviewheight = 0; onground = (player->mo->z <= player->mo->floorz); P_CalcHeight (player); if (player->attacker && player->attacker != player->mo) { angle = R_PointToAngle2 (player->mo->x, player->mo->y, player->attacker->x, player->attacker->y); delta = angle - player->mo->angle; if (delta < ANG5 || delta > (unsigned)-ANG5) { // Looking at killer, // so fade damage flash down. player->mo->angle = angle; if (player->damagecount) player->damagecount--; } else if (delta < ANG180) player->mo->angle += ANG5; else player->mo->angle -= ANG5; } else if (player->damagecount) player->damagecount--; if (player->cmd.buttons & BT_USE) player->playerstate = PST_REBORN; } // // P_PlayerThink // void P_PlayerThink (player_t* player) { ticcmd_t* cmd; weapontype_t newweapon; // [AM] Assume we can interpolate at the beginning // of the tic. player->mo->interp = true; // [AM] Store starting position for player interpolation. player->mo->oldx = player->mo->x; player->mo->oldy = player->mo->y; player->mo->oldz = player->mo->z; player->mo->oldangle = player->mo->angle; player->oldviewz = player->viewz; player->oldlookdir = player->lookdir; player->oldrecoilpitch = player->recoilpitch; // [crispy] update weapon sound source coordinates if (player->so != player->mo) { memcpy(player->so, player->mo, sizeof(degenmobj_t)); } // fixme: do this in the cheat code if (player->cheats & CF_NOCLIP) player->mo->flags |= MF_NOCLIP; else player->mo->flags &= ~MF_NOCLIP; // chain saw run forward cmd = &player->cmd; if (player->mo->flags & MF_JUSTATTACKED) { cmd->angleturn = 0; cmd->forwardmove = 0xc800/512; cmd->sidemove = 0; player->mo->flags &= ~MF_JUSTATTACKED; } // [crispy] center view // e.g. after teleporting, dying, jumping and on demand if (player->centering) { if (player->lookdir > 0) { player->lookdir -= 8 * MLOOKUNIT; } else if (player->lookdir < 0) { player->lookdir += 8 * MLOOKUNIT; } if (abs(player->lookdir) < 8 * MLOOKUNIT) { player->lookdir = 0; player->centering = false; } } // [crispy] weapon recoil pitch if (player->recoilpitch) { if (player->recoilpitch > 0) { player->recoilpitch -= 1; } else if (player->recoilpitch < 0) { player->recoilpitch += 1; } } if (player->playerstate == PST_DEAD) { P_DeathThink (player); return; } // [crispy] delay next possible jump 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_CalcHeight (player); if (player->mo->subsector->sector->special) P_PlayerInSpecialSector (player); // [crispy] jumping: apply vertical momentum if (cmd->arti) { if ((cmd->arti & AFLAG_JUMP) && onground && !player->jumpTics) { // [crispy] Hexen sets 9; Strife adds 8 player->mo->momz = (7 + crispy->jump) * FRACUNIT; player->jumpTics = 18; // [crispy] squat down weapon sprite a bit if (crispy->weaponsquat) { player->psp_dy_max = -player->mo->momz>>2; } } } // Check for weapon change. // A special event has no other buttons. if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; if (cmd->buttons & BT_CHANGE) { // The actual changing of the weapon is done // when the weapon psprite can do it // (read: not in the middle of an attack). newweapon = (cmd->buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT; if (newweapon == wp_fist && player->weaponowned[wp_chainsaw] && !(player->readyweapon == wp_chainsaw && player->powers[pw_strength])) { newweapon = wp_chainsaw; } if ( (crispy->havessg) && newweapon == wp_shotgun && player->weaponowned[wp_supershotgun] && player->readyweapon != wp_supershotgun) { newweapon = wp_supershotgun; } if (player->weaponowned[newweapon] && newweapon != player->readyweapon) { // Do not go to plasma or BFG in shareware, // even if cheated. if ((newweapon != wp_plasma && newweapon != wp_bfg) || (gamemode != shareware) ) { player->pendingweapon = newweapon; } } } // check for use if (cmd->buttons & BT_USE) { if (!player->usedown) { P_UseLines (player); player->usedown = true; } } else player->usedown = false; // cycle psprites P_MovePsprites (player); // Counters, time dependend power ups. // Strength counts up to diminish fade. if (player->powers[pw_strength]) player->powers[pw_strength]++; if (player->powers[pw_invulnerability]) player->powers[pw_invulnerability]--; if (player->powers[pw_invisibility]) if (! --player->powers[pw_invisibility] ) player->mo->flags &= ~MF_SHADOW; if (player->powers[pw_infrared]) player->powers[pw_infrared]--; if (player->powers[pw_ironfeet]) player->powers[pw_ironfeet]--; if (player->damagecount) player->damagecount--; if (player->bonuscount) player->bonuscount--; // Handling colormaps. if (player->powers[pw_invulnerability]) { if (player->powers[pw_invulnerability] > 4*32 || (player->powers[pw_invulnerability]&8) ) player->fixedcolormap = INVERSECOLORMAP; else // [crispy] Visor effect when Invulnerability is fading out player->fixedcolormap = player->powers[pw_infrared] ? 1 : 0; } else if (player->powers[pw_infrared]) { if (player->powers[pw_infrared] > 4*32 || (player->powers[pw_infrared]&8) ) { // almost full bright player->fixedcolormap = 1; } else player->fixedcolormap = 0; } else player->fixedcolormap = 0; } crispy-doom-crispy-doom-5.6.4/src/doom/r_bmaps.c000066400000000000000000000643511360717211000215610ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2013-2017 Brad Harding // Copyright(C) 2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Brightmaps for wall textures // Adapted from doomretro/src/r_data.c:97-209 // #include "doomtype.h" #include "doomstat.h" #include "r_data.h" #include "w_wad.h" // [crispy] brightmap data static byte nobrightmap[256] = {0}; static byte notgray[256] = { 0, 0, 0, 0, 1, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; static byte notgrayorbrown[256] = { 0, 0, 0, 0, 1, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; static byte redonly[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static byte greenonly1[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static byte greenonly2[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static byte greenonly3[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static byte yellowonly[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, }; static byte redandgreen[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static byte blueandgreen[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static byte brighttan[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // [crispy] Chex Quest's "locked" door switches static byte chexred[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // [crispy] Chex Quest's "open" door switches static byte chexgreen[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // [crispy] Chex Quest's "lock"/"open" knobs static byte chexredgreen[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static byte hacxlightning[256] = { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; byte *dc_brightmap = nobrightmap; // [crispy] brightmaps for textures enum { DOOM1AND2, DOOM1ONLY, DOOM2ONLY, }; typedef struct { const char *const texture; const int game; byte *colormask; } fullbright_t; static const fullbright_t fullbright_doom[] = { // [crispy] common textures {"COMP2", DOOM1AND2, blueandgreen}, {"COMPSTA1", DOOM1AND2, notgray}, {"COMPSTA2", DOOM1AND2, notgray}, {"COMPUTE1", DOOM1AND2, notgrayorbrown}, {"COMPUTE2", DOOM1AND2, notgrayorbrown}, {"COMPUTE3", DOOM1AND2, notgrayorbrown}, {"EXITSIGN", DOOM1AND2, notgray}, {"EXITSTON", DOOM1AND2, redonly}, {"PLANET1", DOOM1AND2, notgray}, {"SILVER2", DOOM1AND2, notgray}, {"SILVER3", DOOM1AND2, notgrayorbrown}, {"SLADSKUL", DOOM1AND2, redonly}, {"SW1BRCOM", DOOM1AND2, redonly}, {"SW1BRIK", DOOM1AND2, redonly}, {"SW1BRN1", DOOM2ONLY, redonly}, {"SW1COMM", DOOM1AND2, redonly}, {"SW1DIRT", DOOM1AND2, redonly}, {"SW1MET2", DOOM1AND2, redonly}, {"SW1STARG", DOOM2ONLY, redonly}, {"SW1STON1", DOOM1AND2, redonly}, {"SW1STON2", DOOM2ONLY, redonly}, {"SW1STONE", DOOM1AND2, redonly}, {"SW1STRTN", DOOM1AND2, redonly}, {"SW2BLUE", DOOM1AND2, redonly}, {"SW2BRCOM", DOOM1AND2, greenonly2}, {"SW2BRIK", DOOM1AND2, greenonly1}, {"SW2BRN1", DOOM1AND2, greenonly2}, {"SW2BRN2", DOOM1AND2, greenonly1}, {"SW2BRNGN", DOOM1AND2, greenonly3}, {"SW2COMM", DOOM1AND2, greenonly1}, {"SW2COMP", DOOM1AND2, redonly}, {"SW2DIRT", DOOM1AND2, greenonly2}, {"SW2EXIT", DOOM1AND2, notgray}, {"SW2GRAY", DOOM1AND2, notgray}, {"SW2GRAY1", DOOM1AND2, notgray}, {"SW2GSTON", DOOM1AND2, redonly}, {"SW2MARB", DOOM2ONLY, redonly}, {"SW2MET2", DOOM1AND2, greenonly1}, {"SW2METAL", DOOM1AND2, greenonly3}, {"SW2MOD1", DOOM1AND2, greenonly1}, {"SW2PANEL", DOOM1AND2, redonly}, {"SW2ROCK", DOOM1AND2, redonly}, {"SW2SLAD", DOOM1AND2, redonly}, {"SW2STARG", DOOM2ONLY, greenonly2}, {"SW2STON1", DOOM1AND2, greenonly3}, // [crispy] beware! {"SW2STON2", DOOM1ONLY, redonly}, {"SW2STON2", DOOM2ONLY, greenonly2}, {"SW2STON6", DOOM1AND2, redonly}, {"SW2STONE", DOOM1AND2, greenonly2}, {"SW2STRTN", DOOM1AND2, greenonly1}, {"SW2TEK", DOOM1AND2, greenonly1}, {"SW2VINE", DOOM1AND2, greenonly1}, {"SW2WOOD", DOOM1AND2, redonly}, {"SW2ZIM", DOOM1AND2, redonly}, {"WOOD4", DOOM1AND2, redonly}, {"WOODGARG", DOOM1AND2, redonly}, {"WOODSKUL", DOOM1AND2, redonly}, // {"ZELDOOR", DOOM1AND2, redonly}, {"LITEBLU1", DOOM1AND2, notgray}, {"LITEBLU2", DOOM1AND2, notgray}, {"SPCDOOR3", DOOM2ONLY, greenonly1}, {"PIPEWAL1", DOOM2ONLY, greenonly1}, {"TEKLITE2", DOOM2ONLY, greenonly1}, {"TEKBRON2", DOOM2ONLY, yellowonly}, // {"SW2SKULL", DOOM2ONLY, greenonly2}, {"SW2SATYR", DOOM1AND2, brighttan}, {"SW2LION", DOOM1AND2, brighttan}, {"SW2GARG", DOOM1AND2, brighttan}, // [crispy] Final Doom textures // TNT - Evilution exclusive {"PNK4EXIT", DOOM2ONLY, redonly}, {"SLAD2", DOOM2ONLY, notgrayorbrown}, {"SLAD3", DOOM2ONLY, notgrayorbrown}, {"SLAD4", DOOM2ONLY, notgrayorbrown}, {"SLAD5", DOOM2ONLY, notgrayorbrown}, {"SLAD6", DOOM2ONLY, notgrayorbrown}, {"SLAD7", DOOM2ONLY, notgrayorbrown}, {"SLAD8", DOOM2ONLY, notgrayorbrown}, {"SLAD9", DOOM2ONLY, notgrayorbrown}, {"SLAD10", DOOM2ONLY, notgrayorbrown}, {"SLAD11", DOOM2ONLY, notgrayorbrown}, {"SLADRIP1", DOOM2ONLY, notgrayorbrown}, {"SLADRIP3", DOOM2ONLY, notgrayorbrown}, {"M_TEC", DOOM2ONLY, greenonly2}, {"LITERED2", DOOM2ONLY, redonly}, {"BTNTMETL", DOOM2ONLY, notgrayorbrown}, {"BTNTSLVR", DOOM2ONLY, notgrayorbrown}, {"LITEYEL2", DOOM2ONLY, yellowonly}, {"LITEYEL3", DOOM2ONLY, yellowonly}, {"YELMETAL", DOOM2ONLY, yellowonly}, // Plutonia exclusive // {"SW2SKULL", DOOM2ONLY, redonly}, }; static const fullbright_t fullbright_chex[] = { {"BIGDOOR1", DOOM1AND2, greenonly3}, // {"BIGDOOR4", DOOM1AND2, greenonly3}, // C1: some stray green pixels, C2: many stray green pixels // {"BRNBIGL", DOOM1AND2, greenonly3}, // {"BRNBIGR", DOOM1AND2, greenonly3}, // C1, C2: one stray green pixel // {"BRNSMAL2", DOOM1AND2, greenonly3}, // C1, C2: many stray green pixels {"COMP2", DOOM1AND2, notgray}, // {"COMPTALL", DOOM1ONLY, notgray}, // {"COMPTALL", DOOM2ONLY, greenonly3}, // C2: many stray green pixels {"COMPUTE2", DOOM1AND2, notgray}, {"LITE5", DOOM1ONLY, greenonly2}, {"STARTAN3", DOOM1AND2, greenonly2}, {"SW1BRCOM", DOOM1AND2, chexred}, {"SW1BRN1", DOOM1AND2, chexgreen}, {"SW1BRN2", DOOM1AND2, chexred}, {"SW1BRNGN", DOOM1AND2, chexred}, {"SW1BROWN", DOOM1AND2, chexred}, {"SW1COMM", DOOM1AND2, chexred}, {"SW1COMP", DOOM1AND2, chexred}, {"SW1DIRT", DOOM1AND2, chexgreen}, {"SW1METAL", DOOM1AND2, chexredgreen}, {"SW1PIPE", DOOM1AND2, chexgreen}, {"SW1STARG", DOOM1AND2, chexred}, {"SW1STON1", DOOM1AND2, chexred}, {"SW1STRTN", DOOM1AND2, chexred}, {"SW2BRCOM", DOOM1AND2, chexgreen}, {"SW2BRN1", DOOM1AND2, chexred}, {"SW2BRN2", DOOM1AND2, chexgreen}, {"SW2BRNGN", DOOM1AND2, chexgreen}, {"SW2BROWN", DOOM1AND2, chexgreen}, {"SW2COMM", DOOM1AND2, chexgreen}, {"SW2COMP", DOOM1AND2, chexgreen}, {"SW2DIRT", DOOM1AND2, chexred}, {"SW2METAL", DOOM1AND2, chexredgreen}, {"SW2PIPE", DOOM1AND2, chexred}, {"SW2STARG", DOOM1AND2, chexgreen}, {"SW2STON1", DOOM1AND2, chexgreen}, {"SW2STONE", DOOM1AND2, chexgreen}, {"SW2STRTN", DOOM1AND2, chexgreen}, // {"BIGDOOR5", DOOM1AND2, greenonly1}, // C1, C2: some stray green pixels // {"BIGDOOR6", DOOM1AND2, greenonly1}, // C1, C2: some stray green pixels {"CEMENT3", DOOM1AND2, greenonly3}, {"SKINFACE", DOOM1AND2, greenonly1}, {"SKINTEK1", DOOM1ONLY, greenonly1}, {"SKSPINE2", DOOM1AND2, greenonly3}, {"SW1BLUE", DOOM1AND2, chexgreen}, {"SW1HOT", DOOM1AND2, chexgreen}, {"SW1SKIN", DOOM1AND2, chexgreen}, {"SW1VINE", DOOM1ONLY, chexgreen}, // C1: some stray green pixels in the vines {"SW1WOOD", DOOM1AND2, chexgreen}, {"SW2BLUE", DOOM1AND2, chexred}, {"SW2CMT", DOOM1AND2, chexgreen}, {"SW2GSTON", DOOM1AND2, chexred}, {"SW2HOT", DOOM1AND2, chexred}, {"SW2SKIN", DOOM1AND2, chexred}, {"SW2VINE", DOOM1ONLY, chexred}, {"SW2WOOD", DOOM1AND2, chexred}, {"WOOD4", DOOM1AND2, chexredgreen}, {"WOODGARG", DOOM1AND2, chexred}, {"WOODSKUL", DOOM1AND2, chexredgreen}, }; static const fullbright_t fullbright_hacx[] = { // {"BFALL1", DOOM2ONLY, redandgreen}, // {"BFALL2", DOOM2ONLY, redandgreen}, // {"BFALL3", DOOM2ONLY, redandgreen}, // {"BFALL4", DOOM2ONLY, redandgreen}, {"BRNSMALR", DOOM2ONLY, greenonly1}, {"DOORRED", DOOM2ONLY, redandgreen}, {"SLADWALL", DOOM2ONLY, chexred}, // {"SW1BRCOM", DOOM2ONLY, redonly}, // {"SW1BRN1", DOOM2ONLY, redandgreen}, {"SW1BRN2", DOOM2ONLY, notgrayorbrown}, {"SW1BRNGN", DOOM2ONLY, notgrayorbrown}, // {"SW1BROWN", DOOM2ONLY, notgrayorbrown}, // {"SW2BRCOM", DOOM2ONLY, greenonly1}, // {"SW2BRN1", DOOM2ONLY, redandgreen}, {"SW2BRN2", DOOM2ONLY, notgrayorbrown}, // {"SW2BROWN", DOOM2ONLY, notgrayorbrown}, {"COMPSPAN", DOOM2ONLY, greenonly1}, {"COMPSTA1", DOOM2ONLY, notgrayorbrown}, // {"COMPSTA2", DOOM2ONLY, notgrayorbrown}, {"HD5", DOOM2ONLY, redandgreen}, // {"HD8", DOOM2ONLY, redandgreen}, // {"HD9", DOOM2ONLY, redandgreen}, {"BLAKWAL2", DOOM2ONLY, redandgreen}, {"CEMENT7", DOOM2ONLY, greenonly1}, {"ROCK4", DOOM2ONLY, redonly}, // {"SLOPPY1", DOOM2ONLY, notgrayorbrown}, // {"SPCDOOR4", DOOM2ONLY, notgrayorbrown}, {"ZZZFACE1", DOOM2ONLY, greenonly1}, {"ZZZFACE2", DOOM2ONLY, redandgreen}, {"HW166", DOOM2ONLY, redandgreen}, {"HW510", DOOM2ONLY, notgrayorbrown}, {"HW511", DOOM2ONLY, notgrayorbrown}, {"HW512", DOOM2ONLY, notgrayorbrown}, }; static byte *R_BrightmapForTexName_Doom (const char *texname) { int i; for (i = 0; i < arrlen(fullbright_doom); i++) { const fullbright_t *fullbright = &fullbright_doom[i]; if ((gamemission == doom && fullbright->game == DOOM2ONLY) || (gamemission != doom && fullbright->game == DOOM1ONLY)) { continue; } if (!strncasecmp(fullbright->texture, texname, 8)) { return fullbright->colormask; } } return nobrightmap; } static boolean chex2 = false; static byte *R_BrightmapForTexName_Chex (const char *texname) { int i; for (i = 0; i < arrlen(fullbright_chex); i++) { const fullbright_t *fullbright = &fullbright_chex[i]; if ((chex2 && fullbright->game == DOOM1ONLY) || (!chex2 && fullbright->game == DOOM2ONLY)) { continue; } if (!strncasecmp(fullbright->texture, texname, 8)) { return fullbright->colormask; } } return nobrightmap; } static byte *R_BrightmapForTexName_Hacx (const char *texname) { int i; for (i = 0; i < arrlen(fullbright_hacx); i++) { const fullbright_t *fullbright = &fullbright_hacx[i]; if (!strncasecmp(fullbright->texture, texname, 8)) { return fullbright->colormask; } } return nobrightmap; } // [crispy] brightmaps for sprites // [crispy] adapted from russian-doom/src/doom/r_things.c:617-639 static byte *R_BrightmapForSprite_Doom (const int type) { if (crispy->brightmaps & BRIGHTMAPS_SPRITES) { switch (type) { // Armor Bonus case SPR_BON2: // Cell Charge case SPR_CELL: { return greenonly1; break; } // Barrel case SPR_BAR1: { return greenonly3; break; } // Cell Charge Pack case SPR_CELP: { return yellowonly; break; } // BFG9000 case SPR_BFUG: // Plasmagun case SPR_PLAS: { return redonly; break; } } } return nobrightmap; } static byte *R_BrightmapForSprite_Chex (const int type) { // [crispy] TODO /* if (crispy->brightmaps & BRIGHTMAPS_SPRITES) { switch (type) { // Chainsaw case SPR_CSAW: // Shotgun case SPR_SHOT: // Chaingun case SPR_MGUN: // Rocket launcher case SPR_LAUN: // Plasmagun case SPR_PLAS: // BFG9000 case SPR_BFUG: { return redandgreen; break; } } } */ return nobrightmap; } static byte *R_BrightmapForSprite_Hacx (const int type) { if (crispy->brightmaps & BRIGHTMAPS_SPRITES) { switch (type) { // Chainsaw case SPR_CSAW: // Plasmagun case SPR_PLAS: // Cell Charge case SPR_CELL: // Cell Charge Pack case SPR_CELP: { return redonly; break; } // Rocket launcher case SPR_LAUN: // Medikit case SPR_MEDI: { return redandgreen; break; } // Rocket case SPR_ROCK: // Box of rockets case SPR_BROK: { return greenonly1; break; } // Health Bonus case SPR_BON1: // Stimpack case SPR_STIM: { return notgrayorbrown; break; } } } return nobrightmap; } // [crispy] brightmaps for flats static int bmapflatnum[12]; static byte *R_BrightmapForFlatNum_Doom (const int num) { if (crispy->brightmaps & BRIGHTMAPS_TEXTURES) { if (num == bmapflatnum[0] || num == bmapflatnum[1] || num == bmapflatnum[2]) { return notgrayorbrown; } } return nobrightmap; } static byte *R_BrightmapForFlatNum_Hacx (const int num) { if (crispy->brightmaps & BRIGHTMAPS_TEXTURES) { if (num == bmapflatnum[0] || num == bmapflatnum[1] || num == bmapflatnum[2] || num == bmapflatnum[3] || num == bmapflatnum[4] || num == bmapflatnum[5] || num == bmapflatnum[9] || num == bmapflatnum[10] || num == bmapflatnum[11]) { return notgrayorbrown; } if (num == bmapflatnum[6] || num == bmapflatnum[7] || num == bmapflatnum[8]) { return greenonly1; } } return nobrightmap; } static byte *R_BrightmapForFlatNum_None (const int num) { return nobrightmap; } // [crispy] brightmaps for states static byte *R_BrightmapForState_Doom (const int state) { if (crispy->brightmaps & BRIGHTMAPS_SPRITES) { switch (state) { case S_BFG1: case S_BFG2: case S_BFG3: case S_BFG4: { return redonly; break; } } } return nobrightmap; } static byte *R_BrightmapForState_Hacx (const int state) { if (crispy->brightmaps & BRIGHTMAPS_SPRITES) { switch (state) { case S_SAW2: case S_SAW3: { return hacxlightning; break; } case S_MISSILE: { return redandgreen; break; } case S_SAW: case S_SAWB: case S_PLASMA: case S_PLASMA2: { return redonly; break; } } } return nobrightmap; } static byte *R_BrightmapForState_None (const int state) { return nobrightmap; } // [crispy] initialize brightmaps byte *(*R_BrightmapForTexName) (const char *texname); byte *(*R_BrightmapForSprite) (const int type); byte *(*R_BrightmapForFlatNum) (const int num); byte *(*R_BrightmapForState) (const int state); void R_InitBrightmaps () { if (gameversion == exe_hacx) { bmapflatnum[0] = R_FlatNumForName("FLOOR1_1"); bmapflatnum[1] = R_FlatNumForName("FLOOR1_7"); bmapflatnum[2] = R_FlatNumForName("FLOOR3_3"); bmapflatnum[3] = R_FlatNumForName("NUKAGE1"); bmapflatnum[4] = R_FlatNumForName("NUKAGE2"); bmapflatnum[5] = R_FlatNumForName("NUKAGE3"); bmapflatnum[6] = R_FlatNumForName("BLOOD1"); bmapflatnum[7] = R_FlatNumForName("BLOOD2"); bmapflatnum[8] = R_FlatNumForName("BLOOD2"); bmapflatnum[9] = R_FlatNumForName("SLIME13"); bmapflatnum[10] = R_FlatNumForName("SLIME14"); bmapflatnum[11] = R_FlatNumForName("SLIME15"); R_BrightmapForTexName = R_BrightmapForTexName_Hacx; R_BrightmapForSprite = R_BrightmapForSprite_Hacx; R_BrightmapForFlatNum = R_BrightmapForFlatNum_Hacx; R_BrightmapForState = R_BrightmapForState_Hacx; } else if (gameversion == exe_chex) { int lump; // [crispy] detect Chex Quest 2 lump = W_CheckNumForName("INTERPIC"); if (!strcasecmp(W_WadNameForLump(lumpinfo[lump]), "chex2.wad")) { chex2 = true; } R_BrightmapForTexName = R_BrightmapForTexName_Chex; R_BrightmapForSprite = R_BrightmapForSprite_Chex; R_BrightmapForFlatNum = R_BrightmapForFlatNum_None; R_BrightmapForState = R_BrightmapForState_None; } else { // [crispy] only three select brightmapped flats bmapflatnum[0] = R_FlatNumForName("CONS1_1"); bmapflatnum[1] = R_FlatNumForName("CONS1_5"); bmapflatnum[2] = R_FlatNumForName("CONS1_7"); R_BrightmapForTexName = R_BrightmapForTexName_Doom; R_BrightmapForSprite = R_BrightmapForSprite_Doom; R_BrightmapForFlatNum = R_BrightmapForFlatNum_Doom; R_BrightmapForState = R_BrightmapForState_Doom; } } crispy-doom-crispy-doom-5.6.4/src/doom/r_bmaps.h000066400000000000000000000020271360717211000215560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Brightmaps for wall textures // #ifndef __R_BMAPS__ #define __R_BMAPS__ #include "doomtype.h" extern void R_InitBrightmaps (); extern byte *(*R_BrightmapForTexName) (const char *texname); extern byte *(*R_BrightmapForSprite) (const int type); extern byte *(*R_BrightmapForFlatNum) (const int num); extern byte *(*R_BrightmapForState) (const int state); extern byte **texturebrightmap; #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_bsp.c000066400000000000000000000322211360717211000212320ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // BSP traversal, handling of LineSegs for rendering. // #include "doomdef.h" #include "m_bbox.h" #include "i_system.h" #include "r_main.h" #include "r_plane.h" #include "r_things.h" // State. #include "doomstat.h" #include "r_state.h" //#include "r_local.h" seg_t* curline; side_t* sidedef; line_t* linedef; sector_t* frontsector; sector_t* backsector; drawseg_t* drawsegs = NULL; drawseg_t* ds_p; int numdrawsegs = 0; void R_StoreWallRange ( int start, int stop ); // // R_ClearDrawSegs // void R_ClearDrawSegs (void) { ds_p = drawsegs; } // // ClipWallSegment // Clips the given range of columns // and includes it in the new clip list. // typedef struct { int first; int last; } cliprange_t; // We must expand MAXSEGS to the theoretical limit of the number of solidsegs // that can be generated in a scene by the DOOM engine. This was determined by // Lee Killough during BOOM development to be a function of the screensize. // The simplest thing we can do, other than fix this bug, is to let the game // render overage and then bomb out by detecting the overflow after the // fact. -haleyjd //#define MAXSEGS 32 #define MAXSEGS (MAXWIDTH / 2 + 1) // newend is one past the last valid seg cliprange_t* newend; cliprange_t solidsegs[MAXSEGS]; // // R_ClipSolidWallSegment // Does handle solid walls, // e.g. single sided LineDefs (middle texture) // that entirely block the view. // void R_ClipSolidWallSegment ( int first, int last ) { cliprange_t* next; cliprange_t* start; // Find the first range that touches the range // (adjacent pixels are touching). start = solidsegs; while (start->last < first-1) start++; if (first < start->first) { if (last < start->first-1) { // Post is entirely visible (above start), // so insert a new clippost. R_StoreWallRange (first, last); next = newend; newend++; while (next != start) { *next = *(next-1); next--; } next->first = first; next->last = last; return; } // There is a fragment above *start. R_StoreWallRange (first, start->first - 1); // Now adjust the clip size. start->first = first; } // Bottom contained in start? if (last <= start->last) return; next = start; while (last >= (next+1)->first-1) { // There is a fragment between two posts. R_StoreWallRange (next->last + 1, (next+1)->first - 1); next++; if (last <= next->last) { // Bottom is contained in next. // Adjust the clip size. start->last = next->last; goto crunch; } } // There is a fragment after *next. R_StoreWallRange (next->last + 1, last); // Adjust the clip size. start->last = last; // Remove start+1 to next from the clip list, // because start now covers their area. crunch: if (next == start) { // Post just extended past the bottom of one post. return; } while (next++ != newend) { // Remove a post. *++start = *next; } newend = start+1; } // // R_ClipPassWallSegment // Clips the given range of columns, // but does not includes it in the clip list. // Does handle windows, // e.g. LineDefs with upper and lower texture. // void R_ClipPassWallSegment ( int first, int last ) { cliprange_t* start; // Find the first range that touches the range // (adjacent pixels are touching). start = solidsegs; while (start->last < first-1) start++; if (first < start->first) { if (last < start->first-1) { // Post is entirely visible (above start). R_StoreWallRange (first, last); return; } // There is a fragment above *start. R_StoreWallRange (first, start->first - 1); } // Bottom contained in start? if (last <= start->last) return; while (last >= (start+1)->first-1) { // There is a fragment between two posts. R_StoreWallRange (start->last + 1, (start+1)->first - 1); start++; if (last <= start->last) return; } // There is a fragment after *next. R_StoreWallRange (start->last + 1, last); } // // R_ClearClipSegs // void R_ClearClipSegs (void) { solidsegs[0].first = -0x7fffffff; solidsegs[0].last = -1; solidsegs[1].first = viewwidth; solidsegs[1].last = 0x7fffffff; newend = solidsegs+2; } // [AM] Interpolate the passed sector, if prudent. void R_MaybeInterpolateSector(sector_t* sector) { if (crispy->uncapped && // Only if we moved the sector last tic. sector->oldgametic == gametic - 1) { // Interpolate between current and last floor/ceiling position. if (sector->floorheight != sector->oldfloorheight) sector->interpfloorheight = sector->oldfloorheight + FixedMul(sector->floorheight - sector->oldfloorheight, fractionaltic); else sector->interpfloorheight = sector->floorheight; if (sector->ceilingheight != sector->oldceilingheight) sector->interpceilingheight = sector->oldceilingheight + FixedMul(sector->ceilingheight - sector->oldceilingheight, fractionaltic); else sector->interpceilingheight = sector->ceilingheight; } else { sector->interpfloorheight = sector->floorheight; sector->interpceilingheight = sector->ceilingheight; } } // // R_AddLine // Clips the given segment // and adds any visible pieces to the line list. // void R_AddLine (seg_t* line) { int x1; int x2; angle_t angle1; angle_t angle2; angle_t span; angle_t tspan; curline = line; // OPTIMIZE: quickly reject orthogonal back sides. // [crispy] remove slime trails angle1 = R_PointToAngleCrispy (line->v1->r_x, line->v1->r_y); angle2 = R_PointToAngleCrispy (line->v2->r_x, line->v2->r_y); // Clip to view edges. // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). span = angle1 - angle2; // Back side? I.e. backface culling? if (span >= ANG180) return; // Global angle needed by segcalc. rw_angle1 = angle1; angle1 -= viewangle; angle2 -= viewangle; tspan = angle1 + clipangle; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return; angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return; angle2 = -clipangle; } // The seg is in the view range, // but not necessarily visible. angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; x1 = viewangletox[angle1]; x2 = viewangletox[angle2]; // Does not cross a pixel? if (x1 == x2) return; backsector = line->backsector; // Single sided line? if (!backsector) goto clipsolid; // [AM] Interpolate sector movement before // running clipping tests. Frontsector // should already be interpolated. R_MaybeInterpolateSector(backsector); // Closed door. if (backsector->interpceilingheight <= frontsector->interpfloorheight || backsector->interpfloorheight >= frontsector->interpceilingheight) goto clipsolid; // Window. if (backsector->interpceilingheight != frontsector->interpceilingheight || backsector->interpfloorheight != frontsector->interpfloorheight) goto clippass; // Reject empty lines used for triggers // and special events. // Identical floor and ceiling on both sides, // identical light levels on both sides, // and no middle texture. if (backsector->ceilingpic == frontsector->ceilingpic && backsector->floorpic == frontsector->floorpic && backsector->lightlevel == frontsector->lightlevel && curline->sidedef->midtexture == 0) { return; } clippass: R_ClipPassWallSegment (x1, x2-1); return; clipsolid: R_ClipSolidWallSegment (x1, x2-1); } // // R_CheckBBox // Checks BSP node/subtree bounding box. // Returns true // if some part of the bbox might be visible. // int checkcoord[12][4] = { {3,0,2,1}, {3,0,2,0}, {3,1,2,0}, {0}, {2,0,2,1}, {0,0,0,0}, {3,1,3,0}, {0}, {2,0,3,1}, {2,1,3,1}, {2,1,3,0} }; boolean R_CheckBBox (fixed_t* bspcoord) { int boxx; int boxy; int boxpos; fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; angle_t angle1; angle_t angle2; angle_t span; angle_t tspan; cliprange_t* start; int sx1; int sx2; // Find the corners of the box // that define the edges from current viewpoint. if (viewx <= bspcoord[BOXLEFT]) boxx = 0; else if (viewx < bspcoord[BOXRIGHT]) boxx = 1; else boxx = 2; if (viewy >= bspcoord[BOXTOP]) boxy = 0; else if (viewy > bspcoord[BOXBOTTOM]) boxy = 1; else boxy = 2; boxpos = (boxy<<2)+boxx; if (boxpos == 5) return true; x1 = bspcoord[checkcoord[boxpos][0]]; y1 = bspcoord[checkcoord[boxpos][1]]; x2 = bspcoord[checkcoord[boxpos][2]]; y2 = bspcoord[checkcoord[boxpos][3]]; // check clip list for an open space angle1 = R_PointToAngleCrispy (x1, y1) - viewangle; angle2 = R_PointToAngleCrispy (x2, y2) - viewangle; span = angle1 - angle2; // Sitting on a line? if (span >= ANG180) return true; tspan = angle1 + clipangle; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return false; angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return false; angle2 = -clipangle; } // Find the first clippost // that touches the source post // (adjacent pixels are touching). angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; sx1 = viewangletox[angle1]; sx2 = viewangletox[angle2]; // Does not cross a pixel. if (sx1 == sx2) return false; sx2--; start = solidsegs; while (start->last < sx2) start++; if (sx1 >= start->first && sx2 <= start->last) { // The clippost contains the new span. return false; } return true; } // // R_Subsector // Determine floor/ceiling planes. // Add sprites of things in sector. // Draw one or more line segments. // void R_Subsector (int num) { int count; seg_t* line; subsector_t* sub; #ifdef RANGECHECK if (num>=numsubsectors) I_Error ("R_Subsector: ss %i with numss = %i", num, numsubsectors); #endif sscount++; sub = &subsectors[num]; frontsector = sub->sector; count = sub->numlines; line = &segs[sub->firstline]; // [AM] Interpolate sector movement. Usually only needed // when you're standing inside the sector. R_MaybeInterpolateSector(frontsector); if (frontsector->interpfloorheight < viewz) { floorplane = R_FindPlane(frontsector->interpfloorheight, // [crispy] add support for MBF sky tranfers frontsector->floorpic == skyflatnum && frontsector->sky & PL_SKYFLAT ? frontsector->sky : frontsector->floorpic, frontsector->lightlevel); } else floorplane = NULL; if (frontsector->interpceilingheight > viewz || frontsector->ceilingpic == skyflatnum) { ceilingplane = R_FindPlane(frontsector->interpceilingheight, // [crispy] add support for MBF sky tranfers frontsector->ceilingpic == skyflatnum && frontsector->sky & PL_SKYFLAT ? frontsector->sky : frontsector->ceilingpic, frontsector->lightlevel); } else ceilingplane = NULL; R_AddSprites (frontsector); while (count--) { R_AddLine (line); line++; } // check for solidsegs overflow - extremely unsatisfactory! if(newend > &solidsegs[32] && false) I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n"); } // // RenderBSPNode // Renders all subsectors below a given node, // traversing subtree recursively. // Just call with BSP root. void R_RenderBSPNode (int bspnum) { node_t* bsp; int side; // Found a subsector? if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) R_Subsector (0); else R_Subsector (bspnum&(~NF_SUBSECTOR)); return; } bsp = &nodes[bspnum]; // Decide which side the view point is on. side = R_PointOnSide (viewx, viewy, bsp); // Recursively divide front space. R_RenderBSPNode (bsp->children[side]); // Possibly divide back space. if (R_CheckBBox (bsp->bbox[side^1])) R_RenderBSPNode (bsp->children[side^1]); } crispy-doom-crispy-doom-5.6.4/src/doom/r_bsp.h000066400000000000000000000025461360717211000212460ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh module, BSP traversal and handling. // #ifndef __R_BSP__ #define __R_BSP__ extern seg_t* curline; extern side_t* sidedef; extern line_t* linedef; extern sector_t* frontsector; extern sector_t* backsector; extern int rw_x; extern int rw_stopx; extern boolean segtextured; // false if the back side is the same plane extern boolean markfloor; extern boolean markceiling; extern boolean skymap; extern drawseg_t* drawsegs; extern drawseg_t* ds_p; extern int numdrawsegs; extern lighttable_t** hscalelight; extern lighttable_t** vscalelight; extern lighttable_t** dscalelight; typedef void (*drawfunc_t) (int start, int stop); // BSP? void R_ClearClipSegs (void); void R_ClearDrawSegs (void); void R_RenderBSPNode (int bspnum); #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_data.c000066400000000000000000001135571360717211000213730ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Preparation of data for rendering, // generation of lookups, caching, retrieval by name. // #include #include // [crispy] calloc() #include "deh_main.h" #include "i_swap.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "doomdef.h" #include "m_misc.h" #include "r_local.h" #include "p_local.h" #include "doomstat.h" #include "r_sky.h" #include "r_data.h" #include "v_trans.h" // [crispy] tranmap, CRMAX #include "r_bmaps.h" // [crispy] R_BrightmapForTexName() // // 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 PACKED_STRUCT ( { short originx; short originy; short patch; short stepdir; short colormap; }) mappatch_t; // // Texture definition. // A DOOM wall texture is a list of patches // which are to be combined in a predefined order. // typedef PACKED_STRUCT ( { char name[8]; int masked; short width; short height; int obsolete; short patchcount; mappatch_t patches[1]; }) maptexture_t; // A single patch from a texture definition, // basically a rectangular area within // the texture rectangle. typedef struct { // Block origin (allways UL), // which has allready accounted // for the internal origin of the patch. short originx; short originy; int patch; } texpatch_t; // A maptexturedef_t describes a rectangular texture, // which is composed of one or more mappatch_t structures // that arrange graphic patches. typedef struct texture_s texture_t; struct texture_s { // Keep name for switch changing, etc. char name[8]; short width; short height; // Index in textures list int index; // Next in hash table chain texture_t *next; // All the patches[patchcount] // are drawn back to front into the cached texture. short patchcount; texpatch_t patches[1]; }; int firstflat; int lastflat; int numflats; int firstpatch; int lastpatch; int numpatches; int firstspritelump; int lastspritelump; int numspritelumps; int numtextures; texture_t** textures; texture_t** textures_hashtable; int* texturewidthmask; // needed for texture pegging fixed_t* textureheight; int* texturecompositesize; short** texturecolumnlump; unsigned** texturecolumnofs; // killough 4/9/98: make 32-bit unsigned** texturecolumnofs2; // [crispy] original column offsets for single-patched textures byte** texturecomposite; byte** texturebrightmap; // [crispy] brightmaps // for global animation int* flattranslation; int* texturetranslation; // needed for pre rendering fixed_t* spritewidth; fixed_t* spriteoffset; fixed_t* spritetopoffset; lighttable_t *colormaps; // // MAPTEXTURE_T CACHING // When a texture is first needed, // it counts the number of composite columns // required in the texture and allocates space // for a column directory and any new columns. // The directory will simply point inside other patches // if there is only one patch in a given column, // but any columns with multiple patches // will have new column_ts generated. // // [crispy] replace R_DrawColumnInCache(), R_GenerateComposite() and R_GenerateLookup() // with Lee Killough's implementations found in MBF to fix Medusa bug // taken from mbfsrc/R_DATA.C:136-425 // // R_DrawColumnInCache // Clip and draw a column // from a patch into a cached post. // // Rewritten by Lee Killough for performance and to fix Medusa bug // void R_DrawColumnInCache ( column_t* patch, byte* cache, int originy, int cacheheight, byte* marks ) { int count; int position; byte* source; int top = -1; while (patch->topdelta != 0xff) { // [crispy] support for DeePsea tall patches if (patch->topdelta <= top) { top += patch->topdelta; } else { top = patch->topdelta; } source = (byte *)patch + 3; count = patch->length; position = originy + top; if (position < 0) { count += position; position = 0; } if (position + count > cacheheight) count = cacheheight - position; if (count > 0) { memcpy (cache + position, source, count); // killough 4/9/98: remember which cells in column have been drawn, // so that column can later be converted into a series of posts, to // fix the Medusa bug. memset (marks + position, 0xff, count); } patch = (column_t *)( (byte *)patch + patch->length + 4); } } // // R_GenerateComposite // Using the texture definition, // the composite texture is created from the patches, // and each column is cached. // // Rewritten by Lee Killough for performance and to fix Medusa bug void R_GenerateComposite (int texnum) { byte* block; texture_t* texture; texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; column_t* patchcol; short* collump; unsigned* colofs; // killough 4/9/98: make 32-bit byte* marks; // killough 4/9/98: transparency marks byte* source; // killough 4/9/98: temporary column texture = textures[texnum]; block = Z_Malloc (texturecompositesize[texnum], PU_STATIC, &texturecomposite[texnum]); collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // Composite the columns together. patch = texture->patches; // killough 4/9/98: marks to identify transparent regions in merged textures marks = calloc(texture->width, texture->height); // [crispy] initialize composite background to black (index 0) memset(block, 0, texturecompositesize[texnum]); for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1<0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; x= 0) continue; */ patchcol = (column_t *)((byte *)realpatch + LONG(realpatch->columnofs[x-x1])); R_DrawColumnInCache (patchcol, block + colofs[x], // [crispy] single-patched columns are normally not composited // but directly read from the patch lump ignoring their originy collump[x] >= 0 ? 0 : patch->originy, texture->height, marks + x * texture->height); } } // killough 4/9/98: Next, convert multipatched columns into true columns, // to fix Medusa bug while still allowing for transparent regions. source = I_Realloc(NULL, texture->height); // temporary column for (i = 0; i < texture->width; i++) { if (collump[i] == -1) // process only multipatched columns { column_t *col = (column_t *)(block + colofs[i] - 3); // cached column const byte *mark = marks + i * texture->height; int j = 0; // save column in temporary so we can shuffle it around memcpy(source, (byte *) col + 3, texture->height); for ( ; ; ) // reconstruct the column by scanning transparency marks { unsigned len; // killough 12/98 while (j < texture->height && !mark[j]) // skip transparent cells j++; if (j >= texture->height) // if at end of column { col->topdelta = -1; // end-of-column marker break; } col->topdelta = j; // starting offset of post // killough 12/98: // Use 32-bit len counter, to support tall 1s multipatched textures for (len = 0; j < texture->height && mark[j]; j++) len++; // count opaque cells col->length = len; // killough 12/98: intentionally truncate length // copy opaque cells from the temporary back into the column memcpy((byte *) col + 3, source + col->topdelta, len); col = (column_t *)((byte *) col + len + 4); // next post } } } free(source); // free temporary column free(marks); // free transparency marks // Now that the texture has been built in column cache, // it is purgable from zone memory. Z_ChangeTag (block, PU_CACHE); } // // R_GenerateLookup // // Rewritten by Lee Killough for performance and to fix Medusa bug // void R_GenerateLookup (int texnum) { texture_t* texture; byte* patchcount; // patchcount[texture->width] byte* postcount; // killough 4/9/98: keep count of posts in addition to patches. texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; short* collump; unsigned* colofs; // killough 4/9/98: make 32-bit unsigned* colofs2; // [crispy] original column offsets int csize = 0; // killough 10/98 int err = 0; // killough 10/98 texture = textures[texnum]; // Composited texture not created yet. texturecomposite[texnum] = 0; texturecompositesize[texnum] = 0; collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; colofs2 = texturecolumnofs2[texnum]; // [crispy] original column offsets // Now count the number of columns // that are covered by more than one patch. // Fill in the lump / offset, so columns // with only a single patch are all done. patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount); postcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &postcount); memset (patchcount, 0, texture->width); memset (postcount, 0, texture->width); patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; xpatch; colofs[x] = colofs2[x] = LONG(realpatch->columnofs[x-x1])+3; // [crispy] original column offsets } } // killough 4/9/98: keep a count of the number of posts in column, // to fix Medusa bug while allowing for transparent multipatches. // // killough 12/98: // Post counts are only necessary if column is multipatched, // so skip counting posts if column comes from a single patch. // This allows arbitrarily tall textures for 1s walls. // // If texture is >= 256 tall, assume it's 1s, and hence it has // only one post per column. This avoids crashes while allowing // for arbitrarily tall multipatched 1s textures. if (texture->patchcount > 1 && texture->height < 256) { // killough 12/98: Warn about a common column construction bug unsigned limit = texture->height * 3 + 3; // absolute column size limit for (i = texture->patchcount, patch = texture->patches; --i >= 0; ) { int pat = patch->patch; const patch_t *realpatch = W_CacheLumpNum(pat, PU_CACHE); int x, x1 = patch++->originx, x2 = x1 + SHORT(realpatch->width); const int *cofs = realpatch->columnofs - x1; if (x2 > texture->width) x2 = texture->width; if (x1 < 0) x1 = 0; for (x = x1 ; x < x2 ; x++) { if (patchcount[x] > 1) // Only multipatched columns { const column_t *col = (const column_t*)((const byte*) realpatch + LONG(cofs[x])); const byte *base = (const byte *) col; // count posts for ( ; col->topdelta != 0xff; postcount[x]++) { if ((unsigned)((const byte *) col - base) <= limit) col = (const column_t *)((const byte *) col + col->length + 4); else break; } } } } } // Now count the number of columns // that are covered by more than one patch. // Fill in the lump / offset, so columns // with only a single patch are all done. for (x=0 ; xwidth ; x++) { if (!patchcount[x] && !err++) // killough 10/98: non-verbose output { // [crispy] fix absurd texture name in error message char namet[9]; namet[8] = 0; memcpy (namet, texture->name, 8); printf ("R_GenerateLookup: column without a patch (%s)\n", namet); // [crispy] do not return yet /* return; */ } // I_Error ("R_GenerateLookup: column without a patch"); // [crispy] treat patch-less columns the same as multi-patched if (patchcount[x] > 1 || !patchcount[x]) { // Use the cached block. // [crispy] moved up here, the rest in this loop // applies to single-patched textures as well collump[x] = -1; } // killough 1/25/98, 4/9/98: // // Fix Medusa bug, by adding room for column header // and trailer bytes for each post in merged column. // For now, just allocate conservatively 4 bytes // per post per patch per column, since we don't // yet know how many posts the merged column will // require, and it's bounded above by this limit. colofs[x] = csize + 3; // three header bytes in a column // killough 12/98: add room for one extra post csize += 4 * postcount[x] + 5; // 1 stop byte plus 4 bytes per post // [crispy] remove limit /* if (texturecompositesize[texnum] > 0x10000-texture->height) { I_Error ("R_GenerateLookup: texture %i is >64k", texnum); } */ csize += texture->height; // height bytes of texture data } texturecompositesize[texnum] = csize; Z_Free(patchcount); Z_Free(postcount); } // // R_GetColumn // byte* R_GetColumn ( int tex, int col, boolean opaque ) { int lump; int ofs; int ofs2; col &= texturewidthmask[tex]; lump = texturecolumnlump[tex][col]; ofs = texturecolumnofs[tex][col]; ofs2 = texturecolumnofs2[tex][col]; // [crispy] single-patched mid-textures on two-sided walls if (lump > 0 && !opaque) return (byte *)W_CacheLumpNum(lump,PU_CACHE)+ofs2; if (!texturecomposite[tex]) R_GenerateComposite (tex); return texturecomposite[tex] + ofs; } static void GenerateTextureHashTable(void) { texture_t **rover; int i; int key; textures_hashtable = Z_Malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0); memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures); // Add all textures to hash table for (i=0; iindex = i; // Vanilla Doom does a linear search of the texures array // and stops at the first entry it finds. If there are two // entries with the same name, the first one in the array // wins. The new entry must therefore be added at the end // of the hash chain, so that earlier entries win. key = W_LumpNameHash(textures[i]->name) % numtextures; rover = &textures_hashtable[key]; while (*rover != NULL) { rover = &(*rover)->next; } // Hook into hash table textures[i]->next = NULL; *rover = textures[i]; } } // // R_InitTextures // Initializes the texture list // with the textures from the world map. // // [crispy] partly rewritten to merge PNAMES and TEXTURE1/2 lumps void R_InitTextures (void) { maptexture_t* mtexture; texture_t* texture; mappatch_t* mpatch; texpatch_t* patch; int i; int j; int k; int* maptex = NULL; char name[9]; int* patchlookup; int totalwidth; int nummappatches; int offset; int maxoff = 0; int* directory = NULL; int temp1; int temp2; int temp3; typedef struct { int lumpnum; void *names; short nummappatches; short summappatches; char *name_p; } pnameslump_t; typedef struct { int lumpnum; int *maptex; int maxoff; short numtextures; short sumtextures; short pnamesoffset; } texturelump_t; pnameslump_t *pnameslumps = NULL; texturelump_t *texturelumps = NULL, *texturelump; int maxpnameslumps = 1; // PNAMES int maxtexturelumps = 2; // TEXTURE1, TEXTURE2 int numpnameslumps = 0; int numtexturelumps = 0; // [crispy] allocate memory for the pnameslumps and texturelumps arrays pnameslumps = I_Realloc(pnameslumps, maxpnameslumps * sizeof(*pnameslumps)); texturelumps = I_Realloc(texturelumps, maxtexturelumps * sizeof(*texturelumps)); // [crispy] make sure the first available TEXTURE1/2 lumps // are always processed first texturelumps[numtexturelumps++].lumpnum = W_GetNumForName(DEH_String("TEXTURE1")); if ((i = W_CheckNumForName(DEH_String("TEXTURE2"))) != -1) texturelumps[numtexturelumps++].lumpnum = i; else texturelumps[numtexturelumps].lumpnum = -1; // [crispy] fill the arrays with all available PNAMES lumps // and the remaining available TEXTURE1/2 lumps nummappatches = 0; for (i = numlumps - 1; i >= 0; i--) { if (!strncasecmp(lumpinfo[i]->name, DEH_String("PNAMES"), 6)) { if (numpnameslumps == maxpnameslumps) { maxpnameslumps++; pnameslumps = I_Realloc(pnameslumps, maxpnameslumps * sizeof(*pnameslumps)); } pnameslumps[numpnameslumps].lumpnum = i; pnameslumps[numpnameslumps].names = W_CacheLumpNum(pnameslumps[numpnameslumps].lumpnum, PU_STATIC); pnameslumps[numpnameslumps].nummappatches = LONG(*((int *) pnameslumps[numpnameslumps].names)); // [crispy] accumulated number of patches in the lookup tables // excluding the current one pnameslumps[numpnameslumps].summappatches = nummappatches; pnameslumps[numpnameslumps].name_p = (char*)pnameslumps[numpnameslumps].names + 4; // [crispy] calculate total number of patches nummappatches += pnameslumps[numpnameslumps].nummappatches; numpnameslumps++; } else if (!strncasecmp(lumpinfo[i]->name, DEH_String("TEXTURE"), 7)) { // [crispy] support only TEXTURE1/2 lumps, not TEXTURE3 etc. if (lumpinfo[i]->name[7] != '1' && lumpinfo[i]->name[7] != '2') continue; // [crispy] make sure the first available TEXTURE1/2 lumps // are not processed again if (i == texturelumps[0].lumpnum || i == texturelumps[1].lumpnum) // [crispy] may still be -1 continue; if (numtexturelumps == maxtexturelumps) { maxtexturelumps++; texturelumps = I_Realloc(texturelumps, maxtexturelumps * sizeof(*texturelumps)); } // [crispy] do not proceed any further, yet // we first need a complete pnameslumps[] array and need // to process texturelumps[0] (and also texturelumps[1]) as well texturelumps[numtexturelumps].lumpnum = i; numtexturelumps++; } } // [crispy] fill up the patch lookup table name[8] = 0; patchlookup = Z_Malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL); for (i = 0, k = 0; i < numpnameslumps; i++) { for (j = 0; j < pnameslumps[i].nummappatches; j++) { int p, po; M_StringCopy(name, pnameslumps[i].name_p + j * 8, sizeof(name)); p = po = W_CheckNumForName(name); // [crispy] prevent flat lumps from being mistaken as patches while (p >= firstflat && p <= lastflat) { p = W_CheckNumForNameFromTo (name, p - 1, 0); } // [crispy] if the name is unambiguous, use the lump we found patchlookup[k++] = (p == -1) ? po : p; } } // [crispy] calculate total number of textures numtextures = 0; for (i = 0; i < numtexturelumps; i++) { texturelumps[i].maptex = W_CacheLumpNum(texturelumps[i].lumpnum, PU_STATIC); texturelumps[i].maxoff = W_LumpLength(texturelumps[i].lumpnum); texturelumps[i].numtextures = LONG(*texturelumps[i].maptex); // [crispy] accumulated number of textures in the texture files // including the current one numtextures += texturelumps[i].numtextures; texturelumps[i].sumtextures = numtextures; // [crispy] link textures to their own WAD's patch lookup table (if any) texturelumps[i].pnamesoffset = 0; for (j = 0; j < numpnameslumps; j++) { // [crispy] both are from the same WAD? if (lumpinfo[texturelumps[i].lumpnum]->wad_file == lumpinfo[pnameslumps[j].lumpnum]->wad_file) { texturelumps[i].pnamesoffset = pnameslumps[j].summappatches; break; } } } // [crispy] release memory allocated for patch lookup tables for (i = 0; i < numpnameslumps; i++) { W_ReleaseLumpNum(pnameslumps[i].lumpnum); } free(pnameslumps); // [crispy] pointer to (i.e. actually before) the first texture file texturelump = texturelumps - 1; // [crispy] gets immediately increased below textures = Z_Malloc (numtextures * sizeof(*textures), PU_STATIC, 0); texturecolumnlump = Z_Malloc (numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0); texturecolumnofs = Z_Malloc (numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0); texturecolumnofs2 = Z_Malloc (numtextures * sizeof(*texturecolumnofs2), PU_STATIC, 0); texturecomposite = Z_Malloc (numtextures * sizeof(*texturecomposite), PU_STATIC, 0); texturecompositesize = Z_Malloc (numtextures * sizeof(*texturecompositesize), PU_STATIC, 0); texturewidthmask = Z_Malloc (numtextures * sizeof(*texturewidthmask), PU_STATIC, 0); textureheight = Z_Malloc (numtextures * sizeof(*textureheight), PU_STATIC, 0); texturebrightmap = Z_Malloc (numtextures * sizeof(*texturebrightmap), PU_STATIC, 0); totalwidth = 0; // Really complex printing shit... temp1 = W_GetNumForName (DEH_String("S_START")); // P_??????? temp2 = W_GetNumForName (DEH_String("S_END")) - 1; temp3 = ((temp2-temp1+63)/64) + ((numtextures+63)/64); // If stdout is a real console, use the classic vanilla "filling // up the box" effect, which uses backspace to "step back" inside // the box. If stdout is a file, don't draw the box. if (I_ConsoleStdout()) { printf("["); #ifndef CRISPY_TRUECOLOR for (i = 0; i < temp3 + 9 + 1; i++) // [crispy] one more for R_InitTranMap() #else for (i = 0; i < temp3 + 9; i++) #endif printf(" "); printf("]"); #ifndef CRISPY_TRUECOLOR for (i = 0; i < temp3 + 10 + 1; i++) // [crispy] one more for R_InitTranMap() #else for (i = 0; i < temp3 + 10; i++) #endif printf("\b"); } for (i=0 ; isumtextures) { // [crispy] start looking in next texture file texturelump++; maptex = texturelump->maptex; maxoff = texturelump->maxoff; directory = maptex+1; } offset = LONG(*directory); if (offset > maxoff) I_Error ("R_InitTextures: bad texture directory"); mtexture = (maptexture_t *) ( (byte *)maptex + offset); texture = textures[i] = Z_Malloc (sizeof(texture_t) + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1), PU_STATIC, 0); texture->width = SHORT(mtexture->width); texture->height = SHORT(mtexture->height); texture->patchcount = SHORT(mtexture->patchcount); memcpy (texture->name, mtexture->name, sizeof(texture->name)); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; // [crispy] initialize brightmaps texturebrightmap[i] = R_BrightmapForTexName(texture->name); for (j=0 ; jpatchcount ; j++, mpatch++, patch++) { short p; patch->originx = SHORT(mpatch->originx); patch->originy = SHORT(mpatch->originy); // [crispy] apply offset for patches not in the // first available patch offset table p = SHORT(mpatch->patch) + texturelump->pnamesoffset; // [crispy] catch out-of-range patches if (p < nummappatches) patch->patch = patchlookup[p]; if (patch->patch == -1 || p >= nummappatches) { char texturename[9]; texturename[8] = '\0'; memcpy (texturename, texture->name, 8); // [crispy] make non-fatal fprintf (stderr, "R_InitTextures: Missing patch in texture %s\n", texturename); patch->patch = 0; } } texturecolumnlump[i] = Z_Malloc (texture->width*sizeof(**texturecolumnlump), PU_STATIC,0); texturecolumnofs[i] = Z_Malloc (texture->width*sizeof(**texturecolumnofs), PU_STATIC,0); texturecolumnofs2[i] = Z_Malloc (texture->width*sizeof(**texturecolumnofs2), PU_STATIC,0); j = 1; while (j*2 <= texture->width) j<<=1; texturewidthmask[i] = j-1; textureheight[i] = texture->height<width; } Z_Free(patchlookup); // [crispy] release memory allocated for texture files for (i = 0; i < numtexturelumps; i++) { W_ReleaseLumpNum(texturelumps[i].lumpnum); } free(texturelumps); // Precalculate whatever possible. for (i=0 ; iwidth)<leftoffset)<topoffset)<truecolor) { for (c = 0; c < NUMCOLORMAPS; c++) { const float scale = 1. * c / NUMCOLORMAPS; for (i = 0; i < 256; i++) { r = gamma2table[usegamma][playpal[3 * i + 0]] * (1. - scale) + gamma2table[usegamma][0] * scale; g = gamma2table[usegamma][playpal[3 * i + 1]] * (1. - scale) + gamma2table[usegamma][0] * scale; b = gamma2table[usegamma][playpal[3 * i + 2]] * (1. - scale) + gamma2table[usegamma][0] * scale; colormaps[j++] = 0xff000000 | (r << 16) | (g << 8) | b; } } // [crispy] Invulnerability (c == COLORMAPS) for (i = 0; i < 256; i++) { const byte gray = 0xff - (byte) (0.299 * playpal[3 * i + 0] + 0.587 * playpal[3 * i + 1] + 0.114 * playpal[3 * i + 2]); r = g = b = gamma2table[usegamma][gray]; colormaps[j++] = 0xff000000 | (r << 16) | (g << 8) | b; } } else { byte *const colormap = W_CacheLumpName("COLORMAP", PU_STATIC); for (c = 0; c <= NUMCOLORMAPS; c++) { for (i = 0; i < 256; i++) { r = gamma2table[usegamma][playpal[3 * colormap[c * 256 + i] + 0]] & ~3; g = gamma2table[usegamma][playpal[3 * colormap[c * 256 + i] + 1]] & ~3; b = gamma2table[usegamma][playpal[3 * colormap[c * 256 + i] + 2]] & ~3; colormaps[j++] = 0xff000000 | (r << 16) | (g << 8) | b; } } W_ReleaseLumpName("COLORMAP"); } #endif // [crispy] initialize color translation and color strings tables { byte *playpal = W_CacheLumpName("PLAYPAL", PU_STATIC); char c[3]; int i, j; boolean keepgray = false; extern byte V_Colorize (byte *playpal, int cr, byte source, boolean keepgray109); if (!crstr) crstr = I_Realloc(NULL, CRMAX * sizeof(*crstr)); // [crispy] check for status bar graphics replacements i = W_CheckNumForName(DEH_String("sttnum0")); // [crispy] Status Bar '0' keepgray = (i >= 0 && W_IsIWADLump(lumpinfo[i])); // [crispy] CRMAX - 2: don't override the original GREN and BLUE2 Boom tables for (i = 0; i < CRMAX - 2; i++) { for (j = 0; j < 256; j++) { cr[i][j] = V_Colorize(playpal, i, j, keepgray); } M_snprintf(c, sizeof(c), "%c%c", cr_esc, '0' + i); crstr[i] = M_StringDuplicate(c); } W_ReleaseLumpName("PLAYPAL"); } } // // R_InitData // Locates all the lumps // that will be used by all views // Must be called after W_Init. // void R_InitData (void) { // [crispy] Moved R_InitFlats() to the top, because it sets firstflat/lastflat // which are required by R_InitTextures() to prevent flat lumps from being // mistaken as patches and by R_InitBrightmaps() to set brightmaps for flats. // R_InitBrightmaps() comes next, because it sets R_BrightmapForTexName() // to initialize brightmaps depending on gameversion in R_InitTextures(). R_InitFlats (); R_InitBrightmaps (); R_InitTextures (); printf ("."); // R_InitFlats (); [crispy] moved ... printf ("."); R_InitSpriteLumps (); printf ("."); R_InitColormaps (); #ifndef CRISPY_TRUECOLOR R_InitTranMap(); // [crispy] prints a mark itself #endif } // // R_FlatNumForName // Retrieval, get a flat number for a flat name. // int R_FlatNumForName(const char *name) { int i; char namet[9]; i = W_CheckNumForNameFromTo (name, lastflat, firstflat); if (i == -1) { namet[8] = 0; memcpy (namet, name,8); // [crispy] make non-fatal fprintf (stderr, "R_FlatNumForName: %s not found\n", namet); // [crispy] since there is no "No Flat" marker, // render missing flats as SKY return skyflatnum; } return i - firstflat; } // // R_CheckTextureNumForName // Check whether texture is available. // Filter out NoTexture indicator. // int R_CheckTextureNumForName(const char *name) { texture_t *texture; int key; // "NoTexture" marker. if (name[0] == '-') return 0; key = W_LumpNameHash(name) % numtextures; texture=textures_hashtable[key]; while (texture != NULL) { if (!strncasecmp (texture->name, name, 8) ) return texture->index; texture = texture->next; } return -1; } // // R_TextureNumForName // Calls R_CheckTextureNumForName, // aborts with error message. // int R_TextureNumForName(const char *name) { int i; i = R_CheckTextureNumForName (name); if (i==-1) { // [crispy] fix absurd texture name in error message char namet[9]; namet[8] = '\0'; memcpy (namet, name, 8); // [crispy] make non-fatal fprintf (stderr, "R_TextureNumForName: %s not found\n", namet); return 0; } return i; } // // R_PrecacheLevel // Preloads all relevant graphics for the level. // int flatmemory; int texturememory; int spritememory; void R_PrecacheLevel (void) { char* flatpresent; char* texturepresent; char* spritepresent; int i; int j; int k; int lump; texture_t* texture; thinker_t* th; spriteframe_t* sf; if (demoplayback) return; // Precache flats. flatpresent = Z_Malloc(numflats, PU_STATIC, NULL); memset (flatpresent,0,numflats); for (i=0 ; isize; W_CacheLumpNum(lump, PU_CACHE); } } Z_Free(flatpresent); // Precache textures. texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL); memset (texturepresent,0, numtextures); for (i=0 ; ipatchcount ; j++) { lump = texture->patches[j].patch; texturememory += lumpinfo[lump]->size; W_CacheLumpNum(lump , PU_CACHE); } } Z_Free(texturepresent); // Precache sprites. spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL); memset (spritepresent,0, numsprites); for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) spritepresent[((mobj_t *)th)->sprite] = 1; } spritememory = 0; for (i=0 ; ilump[k]; spritememory += lumpinfo[lump]->size; W_CacheLumpNum(lump , PU_CACHE); } } } Z_Free(spritepresent); } crispy-doom-crispy-doom-5.6.4/src/doom/r_data.h000066400000000000000000000026471360717211000213750ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh module, data I/O, caching, retrieval of graphics // by name. // #ifndef __R_DATA__ #define __R_DATA__ #include "r_defs.h" #include "r_state.h" #define LOOKDIRMIN 110 // [crispy] -110, actually #define LOOKDIRMAX 90 #define LOOKDIRS (LOOKDIRMIN+1+LOOKDIRMAX) // [crispy] lookdir range: -110..0..90 // Retrieve column data for span blitting. byte* R_GetColumn ( int tex, int col, boolean opaque ); // 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); // Called by P_Ticker for switches and animations, // returns the texture number for the texture name. int R_TextureNumForName(const char *name); int R_CheckTextureNumForName(const char *name); #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_defs.h000066400000000000000000000246561360717211000214110ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh/rendering module, shared data struct definitions. // #ifndef __R_DEFS__ #define __R_DEFS__ // Screenwidth. #include "doomdef.h" // Some more or less basic data types // we depend on. #include "m_fixed.h" // We rely on the thinker data struct // to handle sound origins in sectors. #include "d_think.h" // SECTORS do store MObjs anyway. #include "p_mobj.h" #include "i_video.h" #include "v_patch.h" // Silhouette, needed for clipping Segs (mainly) // and sprites representing things. #define SIL_NONE 0 #define SIL_BOTTOM 1 #define SIL_TOP 2 #define SIL_BOTH 3 #define MAXDRAWSEGS 256 // // INTERNAL MAP TYPES // used by play and refresh // // // Your plain vanilla vertex. // Note: transformed values not buffered locally, // like some DOOM-alikes ("wt", "WebView") did. // typedef struct { fixed_t x; fixed_t y; // [crispy] remove slime trails // vertex coordinates *only* used in rendering that have been // moved towards the linedef associated with their seg by projecting them // using the law of cosines in p_setup.c:P_RemoveSlimeTrails(); fixed_t r_x; fixed_t r_y; boolean moved; } vertex_t; // Forward of LineDefs, for Sectors. struct line_s; // Each sector has a degenmobj_t in its center // for sound origin purposes. // I suppose this does not handle sound from // moving objects (doppler), because // position is prolly just buffered, not // updated. typedef struct { thinker_t thinker; // not used for anything fixed_t x; fixed_t y; fixed_t z; } degenmobj_t; // // The SECTORS record, at runtime. // Stores things/mobjs. // typedef struct { fixed_t floorheight; fixed_t ceilingheight; short floorpic; short ceilingpic; short lightlevel; short special; short tag; // 0 = untraversed, 1,2 = sndlines -1 int soundtraversed; // thing that made a sound (or null) mobj_t* soundtarget; // mapblock bounding box for height changes int blockbox[4]; // origin for any sounds played by the sector degenmobj_t soundorg; // if == validcount, already checked int validcount; // list of mobjs in sector mobj_t* thinglist; // thinker_t for reversable actions void* specialdata; int linecount; struct line_s** lines; // [linecount] size // [crispy] WiggleFix: [kb] for R_FixWiggle() int cachedheight; int scaleindex; // [crispy] add support for MBF sky tranfers int sky; // [AM] Previous position of floor and ceiling before // think. Used to interpolate between positions. fixed_t oldfloorheight; fixed_t oldceilingheight; // [AM] Gametic when the old positions were recorded. // Has a dual purpose; it prevents movement thinkers // from storing old positions twice in a tic, and // prevents the renderer from attempting to interpolate // if old values were not updated recently. int oldgametic; // [AM] Interpolated floor and ceiling height. // Calculated once per tic and used inside // the renderer. fixed_t interpfloorheight; fixed_t interpceilingheight; // [crispy] revealed secrets short oldspecial; } sector_t; // // The SideDef. // typedef struct { // add this to the calculated texture column fixed_t textureoffset; // add this to the calculated texture top fixed_t rowoffset; // Texture indices. // We do not maintain names here. short toptexture; short bottomtexture; short midtexture; // Sector the SideDef is facing. sector_t* sector; // [crispy] smooth texture scrolling fixed_t basetextureoffset; } side_t; // // Move clipping aid for LineDefs. // typedef enum { ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE } slopetype_t; typedef struct line_s { // Vertices, from v1 to v2. vertex_t* v1; vertex_t* v2; // Precalculated v2 - v1 for side checking. fixed_t dx; fixed_t dy; // Animation related. unsigned short flags; // [crispy] extended nodes short special; short tag; // Visual appearance: SideDefs. // sidenum[1] will be -1 (NO_INDEX) if one sided unsigned short sidenum[2]; // [crispy] extended nodes // Neat. Another bounding box, for the extent // of the LineDef. fixed_t bbox[4]; // To aid move clipping. slopetype_t slopetype; // Front and back sector. // Note: redundant? Can be retrieved from SideDefs. sector_t* frontsector; sector_t* backsector; // if == validcount, already checked int validcount; // thinker_t for reversable actions void* specialdata; // [crispy] calculate sound origin of line to be its midpoint degenmobj_t soundorg; } line_t; // // A SubSector. // References a Sector. // Basically, this is a list of LineSegs, // indicating the visible walls that define // (all or some) sides of a convex BSP leaf. // typedef struct subsector_s { sector_t* sector; int numlines; // [crispy] extended nodes int firstline; // [crispy] extended nodes } subsector_t; // // The LineSeg. // typedef struct { vertex_t* v1; vertex_t* v2; fixed_t offset; angle_t angle; side_t* sidedef; line_t* linedef; // Sector references. // Could be retrieved from linedef, too. // backsector is NULL for one sided lines sector_t* frontsector; sector_t* backsector; uint32_t length; // [crispy] fix long wall wobble angle_t r_angle; // [crispy] re-calculated angle used for rendering int fakecontrast; } seg_t; // // BSP node. // typedef struct { // Partition line. fixed_t x; fixed_t y; fixed_t dx; fixed_t dy; // Bounding box for each child. fixed_t bbox[2][4]; // If NF_SUBSECTOR its a subsector. int children[2]; // [crispy] extended nodes } node_t; // PC direct to screen pointers //B UNUSED - keep till detailshift in r_draw.c resolved //extern byte* destview; //extern byte* destscreen; // // OTHER TYPES // // This could be wider for >8 bit display. // Indeed, true color support is posibble // precalculating 24bpp lightmap/colormap LUT. // from darkening PLAYPAL to all black. // Could even us emore than 32 levels. typedef pixel_t lighttable_t; // // ? // typedef struct drawseg_s { seg_t* curline; int x1; int x2; fixed_t scale1; fixed_t scale2; fixed_t scalestep; // 0=none, 1=bottom, 2=top, 3=both int silhouette; // do not clip sprites above this fixed_t bsilheight; // do not clip sprites below this fixed_t tsilheight; // Pointers to lists for sprite clipping, // all three adjusted so [x1] is first value. int* sprtopclip; // [crispy] 32-bit integer math int* sprbottomclip; // [crispy] 32-bit integer math int* maskedtexturecol; // [crispy] 32-bit integer math } drawseg_t; // A vissprite_t is a thing // that will be drawn during a refresh. // I.e. a sprite object that is partly visible. typedef struct vissprite_s { // Doubly linked list. struct vissprite_s* prev; struct vissprite_s* next; int x1; int x2; // for line side calculation fixed_t gx; fixed_t gy; // global bottom / top for silhouette clipping fixed_t gz; fixed_t gzt; // horizontal position of x1 fixed_t startfrac; fixed_t scale; // negative if flipped fixed_t xiscale; fixed_t texturemid; int patch; // for color translation and shadow draw, // maxbright frames as well // [crispy] brightmaps for select sprites lighttable_t* colormap[2]; byte *brightmap; int mobjflags; // [crispy] color translation table for blood colored by monster class byte* translation; #ifdef CRISPY_TRUECOLOR const pixel_t (*blendfunc)(const pixel_t fg, const pixel_t bg); #endif } 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; // [crispy] we use a value of 2 for 16 sprite rotations // Lump to use for view angles 0-7. short lump[16]; // [crispy] support 16 sprite rotations // Flip bit (1 = flip) to use for view angles 0-7. byte flip[16]; // [crispy] support 16 sprite rotations } spriteframe_t; // // A sprite definition: // a number of animation frames. // typedef struct { int numframes; spriteframe_t* spriteframes; } spritedef_t; // // Now what is a visplane, anyway? // typedef struct { fixed_t height; int picnum; int lightlevel; int minx; int maxx; // leave pads for [minx-1]/[maxx+1] unsigned int pad1; // [crispy] hires / 32-bit integer math // Here lies the rub for all // dynamic resize/change of resolution. unsigned int top[MAXWIDTH]; // [crispy] hires / 32-bit integer math unsigned int pad2; // [crispy] hires / 32-bit integer math unsigned int pad3; // [crispy] hires / 32-bit integer math // See above. unsigned int bottom[MAXWIDTH]; // [crispy] hires / 32-bit integer math unsigned int pad4; // [crispy] hires / 32-bit integer math } visplane_t; typedef struct { char c; char a[9]; int l, w, h; } laserpatch_t; extern laserpatch_t *laserpatch; #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_draw.c000066400000000000000000000675011360717211000214140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The actual span/column drawing functions. // Here find the main potential for optimization, // e.g. inline assembly, different algorithms. // #include "doomdef.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "r_local.h" // Needs access to LFB (guess what). #include "v_video.h" #include "v_trans.h" // State. #include "doomstat.h" // ? //#define MAXWIDTH 1120 //#define MAXHEIGHT 832 // status bar height at bottom of screen #define SBARHEIGHT (32 << crispy->hires) // // 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; pixel_t* ylookup[MAXHEIGHT]; int columnofs[MAXWIDTH]; // Color tables for different players, // translate a limited part to another // (color ramps used for suit colors). // byte translations[3][256]; // Backing buffer containing the bezel drawn around the screen and // surrounding background. static pixel_t *background_buffer = NULL; // // R_DrawColumn // Source is the top of the column to scale. // lighttable_t* dc_colormap[2]; // [crispy] brightmaps int dc_x; int dc_yl; int dc_yh; fixed_t dc_iscale; fixed_t dc_texturemid; int dc_texheight; // [crispy] Tutti-Frutti fix // first pixel in a column (possibly virtual) byte* dc_source; // just for profiling int dccount; // // 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. // // [crispy] replace R_DrawColumn() with Lee Killough's implementation // found in MBF to fix Tutti-Frutti, taken from mbfsrc/R_DRAW.C:99-1979 void R_DrawColumn (void) { int count; pixel_t* dest; fixed_t frac; fixed_t fracstep; int heightmask = dc_texheight - 1; count = dc_yh - dc_yl; // Zero length, column does not exceed a pixel. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = ylookup[dc_yl] + columnofs[flipviewwidth[dc_x]]; // Determine scaling, // which is the only mapping to be done. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. // heightmask is the Tutti-Frutti fix -- killough if (dc_texheight & heightmask) // not a power of 2 -- killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0); else while (frac >= heightmask) frac -= heightmask; do { // [crispy] brightmaps const byte source = dc_source[frac>>FRACBITS]; *dest = dc_colormap[dc_brightmap[source]][source]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (count--); } else // texture height is a power of 2 -- killough { do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // [crispy] brightmaps const byte source = dc_source[(frac>>FRACBITS)&heightmask]; *dest = dc_colormap[dc_brightmap[source]][source]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } } // UNUSED. // Loop unrolled. #if 0 void R_DrawColumn (void) { int count; byte* source; byte* dest; byte* colormap; unsigned frac; unsigned fracstep; unsigned fracstep2; unsigned fracstep3; unsigned fracstep4; count = dc_yh - dc_yl + 1; source = dc_source; colormap = dc_colormap; dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale<<9; frac = (dc_texturemid + (dc_yl-centery)*dc_iscale)<<9; fracstep2 = fracstep+fracstep; fracstep3 = fracstep2+fracstep; fracstep4 = fracstep3+fracstep; while (count >= 8) { dest[0] = colormap[source[frac>>25]]; dest[SCREENWIDTH] = colormap[source[(frac+fracstep)>>25]]; dest[SCREENWIDTH*2] = colormap[source[(frac+fracstep2)>>25]]; dest[SCREENWIDTH*3] = colormap[source[(frac+fracstep3)>>25]]; frac += fracstep4; dest[SCREENWIDTH*4] = colormap[source[frac>>25]]; dest[SCREENWIDTH*5] = colormap[source[(frac+fracstep)>>25]]; dest[SCREENWIDTH*6] = colormap[source[(frac+fracstep2)>>25]]; dest[SCREENWIDTH*7] = colormap[source[(frac+fracstep3)>>25]]; frac += fracstep4; dest += SCREENWIDTH*8; count -= 8; } while (count > 0) { *dest = colormap[source[frac>>25]]; dest += SCREENWIDTH; frac += fracstep; count--; } } #endif void R_DrawColumnLow (void) { int count; pixel_t* dest; pixel_t* dest2; fixed_t frac; fixed_t fracstep; int x; int heightmask = dc_texheight - 1; count = dc_yh - dc_yl; // Zero length. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } // dccount++; #endif // Blocky mode, need to multiply by 2. x = dc_x << 1; dest = ylookup[dc_yl] + columnofs[flipviewwidth[x]]; dest2 = ylookup[dc_yl] + columnofs[flipviewwidth[x+1]]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // heightmask is the Tutti-Frutti fix -- killough if (dc_texheight & heightmask) // not a power of 2 -- killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0); else while (frac >= heightmask) frac -= heightmask; do { // [crispy] brightmaps const byte source = dc_source[frac>>FRACBITS]; *dest2 = *dest = dc_colormap[dc_brightmap[source]][source]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (count--); } else // texture height is a power of 2 -- killough { do { // Hack. Does not work corretly. // [crispy] brightmaps const byte source = dc_source[(frac>>FRACBITS)&heightmask]; *dest2 = *dest = dc_colormap[dc_brightmap[source]][source]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count--); } } // // Spectre/Invisibility. // #define FUZZTABLE 50 #define FUZZOFF (1) int fuzzoffset[FUZZTABLE] = { FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF }; int fuzzpos = 0; // [crispy] draw fuzz effect independent of rendering frame rate static int fuzzpos_tic; void R_SetFuzzPosTic (void) { // [crispy] prevent the animation from remaining static if (fuzzpos == fuzzpos_tic) { fuzzpos = (fuzzpos + 1) % FUZZTABLE; } fuzzpos_tic = fuzzpos; } void R_SetFuzzPosDraw (void) { fuzzpos = fuzzpos_tic; } // // Framebuffer postprocessing. // Creates a fuzzy image by copying pixels // from adjacent ones to left and right. // Used with an all black colormap, this // could create the SHADOW effect, // i.e. spectres and invisible players. // void R_DrawFuzzColumn (void) { int count; pixel_t* dest; fixed_t frac; fixed_t fracstep; boolean cutoff = false; // Adjust borders. Low... if (!dc_yl) dc_yl = 1; // .. and high. if (dc_yh == viewheight-1) { dc_yh = viewheight - 2; cutoff = true; } count = dc_yh - dc_yl; // Zero length. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawFuzzColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[flipviewwidth[dc_x]]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. #ifndef CRISPY_TRUECOLOR *dest = colormaps[6*256+dest[SCREENWIDTH*fuzzoffset[fuzzpos]]]; #else *dest = I_BlendDark(dest[fuzzoffset[fuzzpos]], 0xc0); #endif // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; frac += fracstep; } while (count--); // [crispy] if the line at the bottom had to be cut off, // draw one extra line using only pixels of that line and the one above if (cutoff) { #ifndef CRISPY_TRUECOLOR *dest = colormaps[6*256+dest[SCREENWIDTH*(fuzzoffset[fuzzpos]-FUZZOFF)/2]]; #else *dest = I_BlendDark(dest[(fuzzoffset[fuzzpos]-FUZZOFF)/2], 0xc0); #endif } } // low detail mode version void R_DrawFuzzColumnLow (void) { int count; pixel_t* dest; pixel_t* dest2; fixed_t frac; fixed_t fracstep; int x; boolean cutoff = false; // Adjust borders. Low... if (!dc_yl) dc_yl = 1; // .. and high. if (dc_yh == viewheight-1) { dc_yh = viewheight - 2; cutoff = true; } count = dc_yh - dc_yl; // Zero length. if (count < 0) return; // low detail mode, need to multiply by 2 x = dc_x << 1; #ifdef RANGECHECK if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawFuzzColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[flipviewwidth[x]]; dest2 = ylookup[dc_yl] + columnofs[flipviewwidth[x+1]]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. #ifndef CRISPY_TRUECOLOR *dest = colormaps[6*256+dest[SCREENWIDTH*fuzzoffset[fuzzpos]]]; *dest2 = colormaps[6*256+dest2[SCREENWIDTH*fuzzoffset[fuzzpos]]]; #else *dest = I_BlendDark(dest[fuzzoffset[fuzzpos]], 0xc0); *dest2 = I_BlendDark(dest2[fuzzoffset[fuzzpos]], 0xc0); #endif // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count--); // [crispy] if the line at the bottom had to be cut off, // draw one extra line using only pixels of that line and the one above if (cutoff) { #ifndef CRISPY_TRUECOLOR *dest = colormaps[6*256+dest[SCREENWIDTH*(fuzzoffset[fuzzpos]-FUZZOFF)/2]]; *dest2 = colormaps[6*256+dest2[SCREENWIDTH*(fuzzoffset[fuzzpos]-FUZZOFF)/2]]; #else *dest = I_BlendDark(dest[(fuzzoffset[fuzzpos]-FUZZOFF)/2], 0xc0); *dest2 = I_BlendDark(dest2[(fuzzoffset[fuzzpos]-FUZZOFF)/2], 0xc0); #endif } } // // 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. // byte* dc_translation; byte* translationtables; void R_DrawTranslatedColumn (void) { int count; pixel_t* dest; fixed_t frac; fixed_t fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[flipviewwidth[dc_x]]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Here we do an additional index re-mapping. do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. *dest = dc_colormap[0][dc_translation[dc_source[frac>>FRACBITS]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } void R_DrawTranslatedColumnLow (void) { int count; pixel_t* dest; pixel_t* dest2; fixed_t frac; fixed_t fracstep; int x; count = dc_yh - dc_yl; if (count < 0) return; // low detail, need to scale by 2 x = dc_x << 1; #ifdef RANGECHECK if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, x); } #endif dest = ylookup[dc_yl] + columnofs[flipviewwidth[x]]; dest2 = ylookup[dc_yl] + columnofs[flipviewwidth[x+1]]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Here we do an additional index re-mapping. do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. *dest = dc_colormap[0][dc_translation[dc_source[frac>>FRACBITS]]]; *dest2 = dc_colormap[0][dc_translation[dc_source[frac>>FRACBITS]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count--); } void R_DrawTLColumn (void) { int count; pixel_t* dest; fixed_t frac; fixed_t fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[flipviewwidth[dc_x]]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; do { #ifndef CRISPY_TRUECOLOR // actual translucency map lookup taken from boom202s/R_DRAW.C:255 *dest = tranmap[(*dest<<8)+dc_colormap[0][dc_source[frac>>FRACBITS]]]; #else const pixel_t destrgb = dc_colormap[0][dc_source[frac>>FRACBITS]]; *dest = blendfunc(*dest, destrgb); #endif dest += SCREENWIDTH; frac += fracstep; } while (count--); } // [crispy] draw translucent column, low-resolution version void R_DrawTLColumnLow (void) { int count; pixel_t* dest; pixel_t* dest2; fixed_t frac; fixed_t fracstep; int x; count = dc_yh - dc_yl; if (count < 0) return; x = dc_x << 1; #ifdef RANGECHECK if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, x); } #endif dest = ylookup[dc_yl] + columnofs[flipviewwidth[x]]; dest2 = ylookup[dc_yl] + columnofs[flipviewwidth[x+1]]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; do { #ifndef CRISPY_TRUECOLOR *dest = tranmap[(*dest<<8)+dc_colormap[0][dc_source[frac>>FRACBITS]]]; *dest2 = tranmap[(*dest2<<8)+dc_colormap[0][dc_source[frac>>FRACBITS]]]; #else const pixel_t destrgb = dc_colormap[0][dc_source[frac>>FRACBITS]]; *dest = blendfunc(*dest, destrgb); *dest2 = blendfunc(*dest2, destrgb); #endif dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count--); } // // R_InitTranslationTables // Creates the translation tables to map // the green color ramp to gray, brown, red. // Assumes a given structure of the PLAYPAL. // Could be read from a lump instead. // void R_InitTranslationTables (void) { int i; translationtables = Z_Malloc (256*3, PU_STATIC, 0); // translate just the 16 green colors for (i=0 ; i<256 ; i++) { if (i >= 0x70 && i<= 0x7f) { // map green ramp to gray, brown, red translationtables[i] = 0x60 + (i&0xf); translationtables [i+256] = 0x40 + (i&0xf); translationtables [i+512] = 0x20 + (i&0xf); } else { // Keep all other colors as is. translationtables[i] = translationtables[i+256] = translationtables[i+512] = i; } } } // // R_DrawSpan // With DOOM style restrictions on view orientation, // the floors and ceilings consist of horizontal slices // or spans with constant z depth. // However, rotation around the world z axis is possible, // thus this mapping, while simpler and faster than // perspective correct texture mapping, has to traverse // the texture at an angle in all but a few cases. // In consequence, flats are not stored by column (like walls), // and the inner loop has to step in texture space u and v. // int ds_y; int ds_x1; int ds_x2; lighttable_t* ds_colormap[2]; byte* ds_brightmap; fixed_t ds_xfrac; fixed_t ds_yfrac; fixed_t ds_xstep; fixed_t ds_ystep; // start of a 64*64 tile image byte* ds_source; // just for profiling int dscount; // // Draws the actual span. void R_DrawSpan (void) { // unsigned int position, step; pixel_t *dest; int count; int spot; unsigned int xtemp, ytemp; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH || (unsigned)ds_y>SCREENHEIGHT) { I_Error( "R_DrawSpan: %i to %i at %i", ds_x1,ds_x2,ds_y); } // dscount++; #endif // Pack position and step variables into a single 32-bit integer, // with x in the top 16 bits and y in the bottom 16 bits. For // each 16-bit part, the top 6 bits are the integer part and the // bottom 10 bits are the fractional part of the pixel position. /* position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0x0000ffff); step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); */ // dest = ylookup[ds_y] + columnofs[ds_x1]; // We do not check for zero spans here? count = ds_x2 - ds_x1; do { byte source; // Calculate current texture index in u,v. // [crispy] fix flats getting more distorted the closer they are to the right ytemp = (ds_yfrac >> 10) & 0x0fc0; xtemp = (ds_xfrac >> 16) & 0x3f; spot = xtemp | ytemp; // Lookup pixel from flat texture tile, // re-index using light/colormap. source = ds_source[spot]; dest = ylookup[ds_y] + columnofs[flipviewwidth[ds_x1++]]; *dest = ds_colormap[ds_brightmap[source]][source]; // position += step; ds_xfrac += ds_xstep; ds_yfrac += ds_ystep; } while (count--); } // UNUSED. // Loop unrolled by 4. #if 0 void R_DrawSpan (void) { unsigned position, step; byte* source; byte* colormap; pixel_t* dest; unsigned count; usingned spot; unsigned value; unsigned temp; unsigned xtemp; unsigned ytemp; position = ((ds_xfrac<<10)&0xffff0000) | ((ds_yfrac>>6)&0xffff); step = ((ds_xstep<<10)&0xffff0000) | ((ds_ystep>>6)&0xffff); source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; count = ds_x2 - ds_x1 + 1; while (count >= 4) { ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[0] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[1] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[2] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[3] = colormap[source[spot]]; count -= 4; dest += 4; } while (count > 0) { ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; *dest++ = colormap[source[spot]]; count--; } } #endif // // Again.. // void R_DrawSpanLow (void) { // unsigned int position, step; unsigned int xtemp, ytemp; pixel_t *dest; int count; int spot; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH || (unsigned)ds_y>SCREENHEIGHT) { I_Error( "R_DrawSpan: %i to %i at %i", ds_x1,ds_x2,ds_y); } // dscount++; #endif /* position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0x0000ffff); step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); */ count = (ds_x2 - ds_x1); // Blocky mode, need to multiply by 2. ds_x1 <<= 1; ds_x2 <<= 1; // dest = ylookup[ds_y] + columnofs[ds_x1]; do { byte source; // Calculate current texture index in u,v. // [crispy] fix flats getting more distorted the closer they are to the right ytemp = (ds_yfrac >> 10) & 0x0fc0; xtemp = (ds_xfrac >> 16) & 0x3f; spot = xtemp | ytemp; // Lowres/blocky mode does it twice, // while scale is adjusted appropriately. source = ds_source[spot]; dest = ylookup[ds_y] + columnofs[flipviewwidth[ds_x1++]]; *dest = ds_colormap[ds_brightmap[source]][source]; dest = ylookup[ds_y] + columnofs[flipviewwidth[ds_x1++]]; *dest = ds_colormap[ds_brightmap[source]][source]; // position += step; ds_xfrac += ds_xstep; ds_yfrac += ds_ystep; } while (count--); } // // R_InitBuffer // Creats lookup tables that avoid // multiplies and other hazzles // for getting the framebuffer address // of a pixel to draw. // void R_InitBuffer ( int width, int height ) { int i; // Handle resize, // e.g. smaller view windows // with border and/or status bar. viewwindowx = (SCREENWIDTH-width) >> 1; // Column offset. For windows. for (i=0 ; i> 1; // Preclaculate all row offsets. for (i=0 ; i> crispy->hires) ; x+=8) V_DrawPatch((viewwindowx >> crispy->hires)+x, (viewwindowy >> crispy->hires)-8, patch); patch = W_CacheLumpName(DEH_String("brdr_b"),PU_CACHE); for (x=0 ; x<(scaledviewwidth >> crispy->hires) ; x+=8) V_DrawPatch((viewwindowx >> crispy->hires)+x, (viewwindowy >> crispy->hires)+(viewheight >> crispy->hires), patch); patch = W_CacheLumpName(DEH_String("brdr_l"),PU_CACHE); for (y=0 ; y<(viewheight >> crispy->hires) ; y+=8) V_DrawPatch((viewwindowx >> crispy->hires)-8, (viewwindowy >> crispy->hires)+y, patch); patch = W_CacheLumpName(DEH_String("brdr_r"),PU_CACHE); for (y=0 ; y<(viewheight >> crispy->hires) ; y+=8) V_DrawPatch((viewwindowx >> crispy->hires)+(scaledviewwidth >> crispy->hires), (viewwindowy >> crispy->hires)+y, patch); // Draw beveled edge. V_DrawPatch((viewwindowx >> crispy->hires)-8, (viewwindowy >> crispy->hires)-8, W_CacheLumpName(DEH_String("brdr_tl"),PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires)+(scaledviewwidth >> crispy->hires), (viewwindowy >> crispy->hires)-8, W_CacheLumpName(DEH_String("brdr_tr"),PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires)-8, (viewwindowy >> crispy->hires)+(viewheight >> crispy->hires), W_CacheLumpName(DEH_String("brdr_bl"),PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires)+(scaledviewwidth >> crispy->hires), (viewwindowy >> crispy->hires)+(viewheight >> crispy->hires), W_CacheLumpName(DEH_String("brdr_br"),PU_CACHE)); V_RestoreBuffer(); } // // Copy a screen buffer. // void R_VideoErase ( unsigned ofs, int count ) { // LFB copy. // This might not be a good idea if memcpy // is not optiomal, e.g. byte by byte on // a 32bit CPU, as GNU GCC/Linux libc did // at one point. if (background_buffer != NULL) { memcpy(I_VideoBuffer + ofs, background_buffer + ofs, count * sizeof(*I_VideoBuffer)); } } // // R_DrawViewBorder // Draws the border around the view // for different size windows? // void R_DrawViewBorder (void) { int top; int side; int ofs; int i; if (scaledviewwidth == SCREENWIDTH) return; top = ((SCREENHEIGHT-SBARHEIGHT)-viewheight)/2; side = (SCREENWIDTH-scaledviewwidth)/2; // copy top and one line of left side R_VideoErase (0, top*SCREENWIDTH+side); // copy one line of right side and bottom ofs = (viewheight+top)*SCREENWIDTH-side; R_VideoErase (ofs, top*SCREENWIDTH+side); // copy sides using wraparound ofs = top*SCREENWIDTH + SCREENWIDTH-side; side <<= 1; for (i=1 ; i #include #include "doomdef.h" #include "doomstat.h" // [AM] leveltime, paused, menuactive #include "d_loop.h" #include "m_bbox.h" #include "m_menu.h" #include "i_system.h" // [crispy] I_Realloc() #include "p_local.h" // [crispy] MLOOKUNIT #include "r_local.h" #include "r_sky.h" #include "st_stuff.h" // [crispy] ST_refreshBackground() // Fineangles in the SCREENWIDTH wide window. #define FIELDOFVIEW 2048 int viewangleoffset; // increment every time a check is made int validcount = 1; lighttable_t* fixedcolormap; extern lighttable_t** walllights; int centerx; int centery; fixed_t centerxfrac; fixed_t centeryfrac; fixed_t projection; // just for profiling purposes int framecount; int sscount; int linecount; int loopcount; fixed_t viewx; fixed_t viewy; fixed_t viewz; angle_t viewangle; fixed_t viewcos; fixed_t viewsin; player_t* viewplayer; // 0 = high, 1 = low int detailshift; // // precalculated math tables // angle_t clipangle; // The viewangletox[viewangle + FINEANGLES/4] lookup // maps the visible view angles to screen X coordinates, // flattening the arc to a flat projection plane. // There will be many angles mapped to the same X. int viewangletox[FINEANGLES/2]; // The xtoviewangleangle[] table maps a screen pixel // to the lowest viewangle that maps back to x ranges // from clipangle to -clipangle. angle_t xtoviewangle[MAXWIDTH+1]; // [crispy] parameterized for smooth diminishing lighting lighttable_t*** scalelight = NULL; lighttable_t** scalelightfixed = NULL; lighttable_t*** zlight = NULL; // bumped light from gun blasts int extralight; // [crispy] parameterized for smooth diminishing lighting int LIGHTLEVELS; int LIGHTSEGSHIFT; int LIGHTBRIGHT; int MAXLIGHTSCALE; int LIGHTSCALESHIFT; int MAXLIGHTZ; int LIGHTZSHIFT; void (*colfunc) (void); void (*basecolfunc) (void); void (*fuzzcolfunc) (void); void (*transcolfunc) (void); void (*tlcolfunc) (void); void (*spanfunc) (void); // // R_AddPointToBox // Expand a given bbox // so that it encloses a given point. // void R_AddPointToBox ( int x, int y, fixed_t* box ) { if (x< box[BOXLEFT]) box[BOXLEFT] = x; if (x> box[BOXRIGHT]) box[BOXRIGHT] = x; if (y< box[BOXBOTTOM]) box[BOXBOTTOM] = y; if (y> box[BOXTOP]) box[BOXTOP] = y; } // // R_PointOnSide // Traverse BSP (sub) tree, // check point against partition plane. // Returns side 0 (front) or 1 (back). // int R_PointOnSide ( fixed_t x, fixed_t y, node_t* node ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!node->dx) { if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); // Try to quickly decide by looking at sign bits. if ( (node->dy ^ node->dx ^ dx ^ dy)&0x80000000 ) { if ( (node->dy ^ dx) & 0x80000000 ) { // (left is negative) return 1; } return 0; } left = FixedMul ( node->dy>>FRACBITS , dx ); right = FixedMul ( dy , node->dx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } int R_PointOnSegSide ( fixed_t x, fixed_t y, seg_t* line ) { fixed_t lx; fixed_t ly; fixed_t ldx; fixed_t ldy; fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; lx = line->v1->x; ly = line->v1->y; ldx = line->v2->x - lx; ldy = line->v2->y - ly; if (!ldx) { if (x <= lx) return ldy > 0; return ldy < 0; } if (!ldy) { if (y <= ly) return ldx < 0; return ldx > 0; } dx = (x - lx); dy = (y - ly); // Try to quickly decide by looking at sign bits. if ( (ldy ^ ldx ^ dx ^ dy)&0x80000000 ) { if ( (ldy ^ dx) & 0x80000000 ) { // (left is negative) return 1; } return 0; } left = FixedMul ( ldy>>FRACBITS , dx ); right = FixedMul ( dy , ldx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } // // R_PointToAngle // To get a global angle from cartesian coordinates, // the coordinates are flipped until they are in // the first octant of the coordinate system, then // the y (<=x) is scaled and divided by x to get a // tangent (slope) value which is looked up in the // tantoangle[] table. // // [crispy] turned into a general R_PointToAngle() flavor // called with either slope_div = SlopeDivCrispy() from R_PointToAngleCrispy() // or slope_div = SlopeDiv() else angle_t R_PointToAngleSlope ( fixed_t x, fixed_t y, int (*slope_div) (unsigned int num, unsigned int den)) { x -= viewx; y -= viewy; if ( (!x) && (!y) ) return 0; if (x>= 0) { // x >=0 if (y>= 0) { // y>= 0 if (x>y) { // octant 0 return tantoangle[slope_div(y,x)]; } else { // octant 1 return ANG90-1-tantoangle[slope_div(x,y)]; } } else { // y<0 y = -y; if (x>y) { // octant 8 return -tantoangle[slope_div(y,x)]; } else { // octant 7 return ANG270+tantoangle[slope_div(x,y)]; } } } else { // x<0 x = -x; if (y>= 0) { // y>= 0 if (x>y) { // octant 3 return ANG180-1-tantoangle[slope_div(y,x)]; } else { // octant 2 return ANG90+ tantoangle[slope_div(x,y)]; } } else { // y<0 y = -y; if (x>y) { // octant 4 return ANG180+tantoangle[slope_div(y,x)]; } else { // octant 5 return ANG270-1-tantoangle[slope_div(x,y)]; } } } return 0; } angle_t R_PointToAngle ( fixed_t x, fixed_t y ) { return R_PointToAngleSlope (x, y, SlopeDiv); } // [crispy] overflow-safe R_PointToAngle() flavor // called only from R_CheckBBox(), R_AddLine() and P_SegLengths() angle_t R_PointToAngleCrispy ( fixed_t x, fixed_t y ) { // [crispy] fix overflows for very long distances int64_t y_viewy = (int64_t)y - viewy; int64_t x_viewx = (int64_t)x - viewx; // [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 = x_viewx / 2 + viewx; y = y_viewy / 2 + viewy; } return R_PointToAngleSlope (x, y, SlopeDivCrispy); } angle_t R_PointToAngle2 ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2 ) { viewx = x1; viewy = y1; // [crispy] R_PointToAngle2() is never called during rendering return R_PointToAngleSlope (x2, y2, SlopeDiv); } fixed_t R_PointToDist ( fixed_t x, fixed_t y ) { int angle; fixed_t dx; fixed_t dy; fixed_t temp; fixed_t dist; fixed_t frac; dx = abs(x - viewx); dy = abs(y - viewy); if (dy>dx) { temp = dx; dx = dy; dy = temp; } // Fix crashes in udm1.wad if (dx != 0) { frac = FixedDiv(dy, dx); } else { frac = 0; } angle = (tantoangle[frac>>DBITS]+ANG90) >> ANGLETOFINESHIFT; // use as cosine dist = FixedDiv (dx, finesine[angle] ); return dist; } // // R_InitPointToAngle // void R_InitPointToAngle (void) { // UNUSED - now getting from tables.c #if 0 int i; long t; float f; // // slope (tangent) to angle lookup // for (i=0 ; i<=SLOPERANGE ; i++) { f = atan( (float)i/SLOPERANGE )/(3.141592657*2); t = 0xffffffff*f; tantoangle[i] = t; } #endif } // [crispy] WiggleFix: move R_ScaleFromGlobalAngle function to r_segs.c, // above R_StoreWallRange #if 0 // // R_ScaleFromGlobalAngle // Returns the texture mapping scale // for the current line (horizontal span) // at the given angle. // rw_distance must be calculated first. // fixed_t R_ScaleFromGlobalAngle (angle_t visangle) { fixed_t scale; angle_t anglea; angle_t angleb; int sinea; int sineb; fixed_t num; int den; // UNUSED #if 0 { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv = finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist = FixedDiv (rw_distance, sinv); cosv = finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z = abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z); return scale; } #endif anglea = ANG90 + (visangle-viewangle); angleb = ANG90 + (visangle-rw_normalangle); // both sines are allways positive sinea = finesine[anglea>>ANGLETOFINESHIFT]; sineb = finesine[angleb>>ANGLETOFINESHIFT]; num = FixedMul(projection,sineb)< num>>FRACBITS) { scale = FixedDiv (num, den); if (scale > 64*FRACUNIT) scale = 64*FRACUNIT; else if (scale < 256) scale = 256; } else scale = 64*FRACUNIT; return scale; } #endif // [AM] Interpolate between two angles. angle_t R_InterpolateAngle(angle_t oangle, angle_t nangle, fixed_t scale) { if (nangle == oangle) return nangle; else if (nangle > oangle) { if (nangle - oangle < ANG270) return oangle + (angle_t)((nangle - oangle) * FIXED2DOUBLE(scale)); else // Wrapped around return oangle - (angle_t)((oangle - nangle) * FIXED2DOUBLE(scale)); } else // nangle < oangle { if (oangle - nangle < ANG270) return oangle - (angle_t)((oangle - nangle) * FIXED2DOUBLE(scale)); else // Wrapped around return oangle + (angle_t)((nangle - oangle) * FIXED2DOUBLE(scale)); } } // // R_InitTables // void R_InitTables (void) { // UNUSED: now getting from tables.c #if 0 int i; float a; float fv; int t; // viewangle tangent table for (i=0 ; i FRACUNIT*2) t = -1; else if (finetangent[i] < -FRACUNIT*2) t = viewwidth+1; else { t = FixedMul (finetangent[i], focallength); t = (centerxfrac - t+FRACUNIT-1)>>FRACBITS; if (t < -1) t = -1; else if (t>viewwidth+1) t = viewwidth+1; } viewangletox[i] = t; } // Scan viewangletox[] to generate xtoviewangle[]: // xtoviewangle will give the smallest view angle // that maps to x. for (x=0;x<=viewwidth;x++) { i = 0; while (viewangletox[i]>x) i++; xtoviewangle[x] = (i<smoothlight) { LIGHTLEVELS = 32; LIGHTSEGSHIFT = 3; LIGHTBRIGHT = 2; MAXLIGHTSCALE = 48; LIGHTSCALESHIFT = 12; MAXLIGHTZ = 1024; LIGHTZSHIFT = 17; } else { LIGHTLEVELS = 16; LIGHTSEGSHIFT = 4; LIGHTBRIGHT = 1; MAXLIGHTSCALE = 48; LIGHTSCALESHIFT = 12; MAXLIGHTZ = 128; LIGHTZSHIFT = 20; } scalelight = malloc(LIGHTLEVELS * sizeof(*scalelight)); scalelightfixed = malloc(MAXLIGHTSCALE * sizeof(*scalelightfixed)); zlight = malloc(LIGHTLEVELS * sizeof(*zlight)); // Calculate the light levels to use // for each level / distance combination. for (i=0 ; i< LIGHTLEVELS ; i++) { zlight[i] = malloc(MAXLIGHTZ * sizeof(**zlight)); startmap = ((LIGHTLEVELS-LIGHTBRIGHT-i)*2)*NUMCOLORMAPS/LIGHTLEVELS; for (j=0 ; j>= LIGHTSCALESHIFT; level = startmap - scale/DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS-1; zlight[i][j] = colormaps + level*256; } } } // // R_SetViewSize // Do not really change anything here, // because it might be in the middle of a refresh. // The change will take effect next refresh. // boolean setsizeneeded; int setblocks; int setdetail; // [crispy] lookup table for horizontal screen coordinates int flipscreenwidth[MAXWIDTH]; int *flipviewwidth; void R_SetViewSize ( int blocks, int detail ) { setsizeneeded = true; setblocks = blocks; setdetail = detail; } // // R_ExecuteSetViewSize // void R_ExecuteSetViewSize (void) { fixed_t cosadj; fixed_t dy; int i; int j; int level; int startmap; setsizeneeded = false; if (setblocks >= 11) // [crispy] Crispy HUD { scaledviewwidth = SCREENWIDTH; viewheight = SCREENHEIGHT; } else { scaledviewwidth = (setblocks*32)<hires; viewheight = ((setblocks*168/10)&~7)<hires; } detailshift = setdetail; viewwidth = scaledviewwidth>>detailshift; centery = viewheight/2; centerx = viewwidth/2; centerxfrac = centerx<hires)) * (screenblocks < 11 ? screenblocks : 11) / 10))<>ANGLETOFINESHIFT]); distscale[i] = FixedDiv (FRACUNIT,cosadj); } // Calculate the light levels to use // for each level / scale combination. for (i=0 ; i< LIGHTLEVELS ; i++) { scalelight[i] = malloc(MAXLIGHTSCALE * sizeof(**scalelight)); startmap = ((LIGHTLEVELS-LIGHTBRIGHT-i)*2)*NUMCOLORMAPS/LIGHTLEVELS; for (j=0 ; j= NUMCOLORMAPS) level = NUMCOLORMAPS-1; scalelight[i][j] = colormaps + level*256; } } // [crispy] lookup table for horizontal screen coordinates for (i = 0, j = SCREENWIDTH - 1; i < SCREENWIDTH; i++, j--) { flipscreenwidth[i] = crispy->fliplevels ? j : i; } flipviewwidth = flipscreenwidth + (crispy->fliplevels ? (SCREENWIDTH - scaledviewwidth) : 0); // [crispy] forcefully initialize the status bar backing screen ST_refreshBackground(true); } // // R_Init // void R_Init (void) { R_InitData (); printf ("."); R_InitPointToAngle (); printf ("."); R_InitTables (); // viewwidth / viewheight / detailLevel are set by the defaults printf ("."); R_SetViewSize (screenblocks, detailLevel); R_InitPlanes (); printf ("."); R_InitLightTables (); printf ("."); R_InitSkyMap (); R_InitTranslationTables (); printf ("."); framecount = 0; } // // R_PointInSubsector // subsector_t* R_PointInSubsector ( fixed_t x, fixed_t y ) { node_t* node; int side; int nodenum; // single subsector is a special case if (!numnodes) return subsectors; nodenum = numnodes-1; while (! (nodenum & NF_SUBSECTOR) ) { node = &nodes[nodenum]; side = R_PointOnSide (x, y, node); nodenum = node->children[side]; } return &subsectors[nodenum & ~NF_SUBSECTOR]; } // // R_SetupFrame // void R_SetupFrame (player_t* player) { int i; int tempCentery; int pitch; viewplayer = player; // [AM] Interpolate the player camera if the feature is enabled. if (crispy->uncapped && // Don't interpolate on the first tic of a level, // otherwise oldviewz might be garbage. leveltime > 1 && // Don't interpolate if the player did something // that would necessitate turning it off for a tic. player->mo->interp == true && // Don't interpolate during a paused state leveltime > oldleveltime) { // Interpolate player camera from their old position to their current one. viewx = player->mo->oldx + FixedMul(player->mo->x - player->mo->oldx, fractionaltic); viewy = player->mo->oldy + FixedMul(player->mo->y - player->mo->oldy, fractionaltic); viewz = player->oldviewz + FixedMul(player->viewz - player->oldviewz, fractionaltic); viewangle = R_InterpolateAngle(player->mo->oldangle, player->mo->angle, fractionaltic) + viewangleoffset; pitch = (player->oldlookdir + (player->lookdir - player->oldlookdir) * FIXED2DOUBLE(fractionaltic)) / MLOOKUNIT + (player->oldrecoilpitch + FixedMul(player->recoilpitch - player->oldrecoilpitch, fractionaltic)); } else { viewx = player->mo->x; viewy = player->mo->y; viewz = player->viewz; viewangle = player->mo->angle + viewangleoffset; // [crispy] pitch is actual lookdir and weapon pitch pitch = player->lookdir / MLOOKUNIT + player->recoilpitch; } extralight = player->extralight; if (pitch > LOOKDIRMAX) pitch = LOOKDIRMAX; else if (pitch < -LOOKDIRMIN) pitch = -LOOKDIRMIN; // apply new yslope[] whenever "lookdir", "detailshift" or "screenblocks" change tempCentery = viewheight/2 + (pitch * (1 << crispy->hires)) * (screenblocks < 11 ? screenblocks : 11) / 10; if (centery != tempCentery) { centery = tempCentery; centeryfrac = centery << FRACBITS; yslope = yslopes[LOOKDIRMIN + pitch]; } viewsin = finesine[viewangle>>ANGLETOFINESHIFT]; viewcos = finecosine[viewangle>>ANGLETOFINESHIFT]; sscount = 0; if (player->fixedcolormap) { fixedcolormap = colormaps + player->fixedcolormap*256; walllights = scalelightfixed; for (i=0 ; iautomapoverlay) { R_RenderBSPNode (numnodes-1); return; } // [crispy] flashing HOM indicator V_DrawFilledBox(viewwindowx, viewwindowy, scaledviewwidth, viewheight, #ifndef CRISPY_TRUECOLOR crispy->flashinghom ? (176 + (gametic % 16)) : 0); #else colormaps[crispy->flashinghom ? (176 + (gametic % 16)) : 0]); #endif // check for new console commands. NetUpdate (); // [crispy] smooth texture scrolling R_InterpolateTextureOffsets(); // The head node is the last node output. R_RenderBSPNode (numnodes-1); // Check for new console commands. NetUpdate (); R_DrawPlanes (); // Check for new console commands. NetUpdate (); // [crispy] draw fuzz effect independent of rendering frame rate R_SetFuzzPosDraw(); R_DrawMasked (); // Check for new console commands. NetUpdate (); } crispy-doom-crispy-doom-5.6.4/src/doom/r_main.h000066400000000000000000000063271360717211000214070ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System specific interface stuff. // #ifndef __R_MAIN__ #define __R_MAIN__ #include "d_player.h" #include "r_data.h" // // POV related. // extern fixed_t viewcos; extern fixed_t viewsin; extern int viewwindowx; extern int viewwindowy; extern int centerx; extern int centery; extern fixed_t centerxfrac; extern fixed_t centeryfrac; extern fixed_t projection; extern int validcount; extern int linecount; extern int loopcount; // // Lighting LUT. // Used for z-depth cuing per column/row, // and other lighting effects (sector ambient, flash). // // Lighting constants. // Now why not 32 levels here? // [crispy] parameterized for smooth diminishing lighting extern int LIGHTLEVELS; extern int LIGHTSEGSHIFT; extern int LIGHTBRIGHT; extern int MAXLIGHTSCALE; extern int LIGHTSCALESHIFT; extern int MAXLIGHTZ; extern int LIGHTZSHIFT; extern lighttable_t*** scalelight; extern lighttable_t** scalelightfixed; extern lighttable_t*** zlight; extern int extralight; extern lighttable_t* fixedcolormap; // Number of diminishing brightness levels. // There a 0-31, i.e. 32 LUT in the COLORMAP lump. #define NUMCOLORMAPS 32 // [AM] Fractional part of the current tic, in the half-open // range of [0.0, 1.0). Used for interpolation. extern fixed_t fractionaltic; // Blocky/low detail mode. //B remove this? // 0 = high, 1 = low extern int detailshift; // // Function pointers to switch refresh/drawing functions. // Used to select shadow mode etc. // extern void (*colfunc) (void); extern void (*transcolfunc) (void); extern void (*basecolfunc) (void); extern void (*fuzzcolfunc) (void); extern void (*tlcolfunc) (void); // No shadow effects on floors. extern void (*spanfunc) (void); // // Utility functions. int R_PointOnSide ( fixed_t x, fixed_t y, node_t* node ); int R_PointOnSegSide ( fixed_t x, fixed_t y, seg_t* line ); angle_t R_PointToAngle ( fixed_t x, fixed_t y ); angle_t R_PointToAngleCrispy ( fixed_t x, fixed_t y ); angle_t R_PointToAngle2 ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2 ); fixed_t R_PointToDist ( fixed_t x, fixed_t y ); fixed_t R_ScaleFromGlobalAngle (angle_t visangle); subsector_t* R_PointInSubsector ( fixed_t x, fixed_t y ); void R_AddPointToBox ( int x, int y, fixed_t* box ); // [AM] Interpolate between two angles. angle_t R_InterpolateAngle(angle_t oangle, angle_t nangle, fixed_t scale); // // REFRESH - the actual rendering functions. // // Called by G_Drawer. void R_RenderPlayerView (player_t *player); // Called by startup code. void R_Init (void); // Called by M_Responder. void R_SetViewSize (int blocks, int detail); #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_plane.c000066400000000000000000000267231360717211000215570ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Here is a core component: drawing the floors and ceilings, // while maintaining a per column clipping list only. // Moreover, the sky areas have to be determined. // #include #include #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "doomdef.h" #include "doomstat.h" #include "r_local.h" #include "r_sky.h" #include "r_bmaps.h" // [crispy] R_BrightmapForTexName() #include "r_swirl.h" // [crispy] R_DistortedFlat() planefunction_t floorfunc; planefunction_t ceilingfunc; // // opening // // Here comes the obnoxious "visplane". #define MAXVISPLANES 128 visplane_t* visplanes = NULL; visplane_t* lastvisplane; visplane_t* floorplane; visplane_t* ceilingplane; static int numvisplanes; // ? #define MAXOPENINGS MAXWIDTH*64*4 int openings[MAXOPENINGS]; // [crispy] 32-bit integer math int* lastopening; // [crispy] 32-bit integer math // // Clip values are the solid pixel bounding the range. // floorclip starts out SCREENHEIGHT // ceilingclip starts out -1 // int floorclip[MAXWIDTH]; // [crispy] 32-bit integer math int ceilingclip[MAXWIDTH]; // [crispy] 32-bit integer math // // spanstart holds the start of a plane span // initialized to 0 at start // int spanstart[MAXHEIGHT]; int spanstop[MAXHEIGHT]; // // texture mapping // lighttable_t** planezlight; fixed_t planeheight; fixed_t* yslope; fixed_t yslopes[LOOKDIRS][MAXHEIGHT]; fixed_t distscale[MAXWIDTH]; fixed_t basexscale; fixed_t baseyscale; fixed_t cachedheight[MAXHEIGHT]; fixed_t cacheddistance[MAXHEIGHT]; fixed_t cachedxstep[MAXHEIGHT]; fixed_t cachedystep[MAXHEIGHT]; // // R_InitPlanes // Only at game startup. // void R_InitPlanes (void) { // Doh! } // // R_MapPlane // // Uses global vars: // planeheight // ds_source // basexscale // baseyscale // viewx // viewy // // BASIC PRIMITIVE // void R_MapPlane ( int y, int x1, int x2 ) { // [crispy] see below // angle_t angle; fixed_t distance; // fixed_t length; unsigned index; int dx, dy; #ifdef RANGECHECK if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) { I_Error ("R_MapPlane: %i, %i at %i",x1,x2,y); } #endif // [crispy] visplanes with the same flats now match up far better than before // adapted from prboom-plus/src/r_plane.c:191-239, translated to fixed-point math if (!(dy = abs(centery - y))) { return; } if (planeheight != cachedheight[y]) { cachedheight[y] = planeheight; distance = cacheddistance[y] = FixedMul (planeheight, yslope[y]); ds_xstep = cachedxstep[y] = (FixedMul (viewsin, planeheight) / dy) << detailshift; ds_ystep = cachedystep[y] = (FixedMul (viewcos, planeheight) / dy) << detailshift; } else { distance = cacheddistance[y]; ds_xstep = cachedxstep[y]; ds_ystep = cachedystep[y]; } dx = x1 - centerx; ds_xfrac = viewx + FixedMul(viewcos, distance) + dx * ds_xstep; ds_yfrac = -viewy - FixedMul(viewsin, distance) + dx * ds_ystep; if (fixedcolormap) ds_colormap[0] = ds_colormap[1] = fixedcolormap; else { index = distance >> LIGHTZSHIFT; if (index >= MAXLIGHTZ ) index = MAXLIGHTZ-1; ds_colormap[0] = planezlight[index]; ds_colormap[1] = zlight[LIGHTLEVELS-1][MAXLIGHTZ-1]; } ds_y = y; ds_x1 = x1; ds_x2 = x2; // high or low detail spanfunc (); } // // R_ClearPlanes // At begining of frame. // void R_ClearPlanes (void) { int i; angle_t angle; // opening / clipping determination for (i=0 ; i>ANGLETOFINESHIFT; // scale will be unit scale at SCREENWIDTH/2 distance basexscale = FixedDiv (finecosine[angle],centerxfrac); baseyscale = -FixedDiv (finesine[angle],centerxfrac); } // [crispy] remove MAXVISPLANES Vanilla limit static void R_RaiseVisplanes (visplane_t** vp) { if (lastvisplane - visplanes == numvisplanes) { int numvisplanes_old = numvisplanes; visplane_t* visplanes_old = visplanes; numvisplanes = numvisplanes ? 2 * numvisplanes : MAXVISPLANES; visplanes = I_Realloc(visplanes, numvisplanes * sizeof(*visplanes)); memset(visplanes + numvisplanes_old, 0, (numvisplanes - numvisplanes_old) * sizeof(*visplanes)); lastvisplane = visplanes + numvisplanes_old; floorplane = visplanes + (floorplane - visplanes_old); ceilingplane = visplanes + (ceilingplane - visplanes_old); if (numvisplanes_old) fprintf(stderr, "R_FindPlane: Hit MAXVISPLANES limit at %d, raised to %d.\n", numvisplanes_old, numvisplanes); // keep the pointer passed as argument in relation to the visplanes pointer if (vp) *vp = visplanes + (*vp - visplanes_old); } } // // R_FindPlane // visplane_t* R_FindPlane ( fixed_t height, int picnum, int lightlevel ) { visplane_t* check; // [crispy] add support for MBF sky tranfers if (picnum == skyflatnum || picnum & PL_SKYFLAT) { height = 0; // all skys map together lightlevel = 0; } for (check=visplanes; checkheight && picnum == check->picnum && lightlevel == check->lightlevel) { break; } } if (check < lastvisplane) return check; R_RaiseVisplanes(&check); // [crispy] remove VISPLANES limit if (lastvisplane - visplanes == MAXVISPLANES && false) I_Error ("R_FindPlane: no more visplanes"); lastvisplane++; check->height = height; check->picnum = picnum; check->lightlevel = lightlevel; check->minx = SCREENWIDTH; check->maxx = -1; memset (check->top,0xff,sizeof(check->top)); return check; } // // R_CheckPlane // visplane_t* R_CheckPlane ( visplane_t* pl, int start, int stop ) { int intrl; int intrh; int unionl; int unionh; int x; if (start < pl->minx) { intrl = pl->minx; unionl = start; } else { unionl = pl->minx; intrl = start; } if (stop > pl->maxx) { intrh = pl->maxx; unionh = stop; } else { unionh = pl->maxx; intrh = stop; } for (x=intrl ; x<= intrh ; x++) if (pl->top[x] != 0xffffffffu) // [crispy] hires / 32-bit integer math break; // [crispy] fix HOM if ceilingplane and floorplane are the same // visplane (e.g. both are skies) if (!(pl == floorplane && markceiling && floorplane == ceilingplane)) { if (x > intrh) { pl->minx = unionl; pl->maxx = unionh; // use the same one return pl; } } // make a new visplane R_RaiseVisplanes(&pl); // [crispy] remove VISPLANES limit lastvisplane->height = pl->height; lastvisplane->picnum = pl->picnum; lastvisplane->lightlevel = pl->lightlevel; if (lastvisplane - visplanes == MAXVISPLANES && false) // [crispy] remove VISPLANES limit I_Error ("R_CheckPlane: no more visplanes"); pl = lastvisplane++; pl->minx = start; pl->maxx = stop; memset (pl->top,0xff,sizeof(pl->top)); return pl; } // // R_MakeSpans // void R_MakeSpans ( int x, unsigned int t1, // [crispy] 32-bit integer math unsigned int b1, // [crispy] 32-bit integer math unsigned int t2, // [crispy] 32-bit integer math unsigned int b2 ) // [crispy] 32-bit integer math { while (t1 < t2 && t1<=b1) { R_MapPlane (t1,spanstart[t1],x-1); t1++; } while (b1 > b2 && b1>=t1) { R_MapPlane (b1,spanstart[b1],x-1); b1--; } while (t2 < t1 && t2<=b2) { spanstart[t2] = x; t2++; } while (b2 > b1 && b2>=t2) { spanstart[b2] = x; b2--; } } // // R_DrawPlanes // At the end of each frame. // void R_DrawPlanes (void) { visplane_t* pl; int light; int x; int stop; int angle; int lumpnum; #ifdef RANGECHECK if (ds_p - drawsegs > numdrawsegs) I_Error ("R_DrawPlanes: drawsegs overflow (%" PRIiPTR ")", ds_p - drawsegs); if (lastvisplane - visplanes > numvisplanes) I_Error ("R_DrawPlanes: visplane overflow (%" PRIiPTR ")", lastvisplane - visplanes); if (lastopening - openings > MAXOPENINGS) I_Error ("R_DrawPlanes: opening overflow (%" PRIiPTR ")", lastopening - openings); #endif for (pl = visplanes ; pl < lastvisplane ; pl++) { const boolean swirling = (flattranslation[pl->picnum] == -1); if (pl->minx > pl->maxx) continue; // sky flat // [crispy] add support for MBF sky tranfers if (pl->picnum == skyflatnum || pl->picnum & PL_SKYFLAT) { int texture; angle_t an = viewangle, flip; if (pl->picnum & PL_SKYFLAT) { const line_t *l = &lines[pl->picnum & ~PL_SKYFLAT]; const side_t *s = *l->sidenum + sides; texture = texturetranslation[s->toptexture]; dc_texturemid = s->rowoffset - 28*FRACUNIT; // [crispy] stretch sky if (crispy->stretchsky) { dc_texturemid = dc_texturemid * (textureheight[texture]>>FRACBITS) / SKYSTRETCH_HEIGHT; } flip = (l->special == 272) ? 0u : ~0u; an += s->textureoffset; } else { texture = skytexture; dc_texturemid = skytexturemid; flip = 0; } dc_iscale = pspriteiscale>>detailshift; // Sky is allways drawn full bright, // i.e. colormaps[0] is used. // Because of this hack, sky is not affected // by INVUL inverse mapping. // [crispy] no brightmaps for sky dc_colormap[0] = dc_colormap[1] = colormaps; // dc_texturemid = skytexturemid; dc_texheight = textureheight[texture]>>FRACBITS; // [crispy] Tutti-Frutti fix // [crispy] stretch sky if (crispy->stretchsky) dc_iscale = dc_iscale * dc_texheight / SKYSTRETCH_HEIGHT; for (x=pl->minx ; x <= pl->maxx ; x++) { dc_yl = pl->top[x]; dc_yh = pl->bottom[x]; if ((unsigned) dc_yl <= dc_yh) // [crispy] 32-bit integer math { angle = ((an + xtoviewangle[x])^flip)>>ANGLETOSKYSHIFT; dc_x = x; dc_source = R_GetColumn(texture, angle, false); colfunc (); } } continue; } // regular flat lumpnum = firstflat + (swirling ? pl->picnum : flattranslation[pl->picnum]); // [crispy] add support for SMMU swirling flats ds_source = swirling ? R_DistortedFlat(lumpnum) : W_CacheLumpNum(lumpnum, PU_STATIC); ds_brightmap = R_BrightmapForFlatNum(lumpnum-firstflat); planeheight = abs(pl->height-viewz); light = (pl->lightlevel >> LIGHTSEGSHIFT)+(extralight * LIGHTBRIGHT); if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; if (light < 0) light = 0; planezlight = zlight[light]; pl->top[pl->maxx+1] = 0xffffffffu; // [crispy] hires / 32-bit integer math pl->top[pl->minx-1] = 0xffffffffu; // [crispy] hires / 32-bit integer math stop = pl->maxx + 1; for (x=pl->minx ; x<= stop ; x++) { R_MakeSpans(x,pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } W_ReleaseLumpNum(lumpnum); } } crispy-doom-crispy-doom-5.6.4/src/doom/r_plane.h000066400000000000000000000033141360717211000215530ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh, visplane stuff (floor, ceilings). // #ifndef __R_PLANE__ #define __R_PLANE__ #include "r_data.h" #define PL_SKYFLAT (0x80000000) // Visplane related. extern int* lastopening; // [crispy] 32-bit integer math typedef void (*planefunction_t) (int top, int bottom); extern planefunction_t floorfunc; extern planefunction_t ceilingfunc_t; extern int floorclip[MAXWIDTH]; // [crispy] 32-bit integer math extern int ceilingclip[MAXWIDTH]; // [crispy] 32-bit integer math extern fixed_t* yslope; extern fixed_t yslopes[LOOKDIRS][MAXHEIGHT]; extern fixed_t distscale[MAXWIDTH]; void R_InitPlanes (void); void R_ClearPlanes (void); void R_MapPlane ( int y, int x1, int x2 ); void R_MakeSpans ( int x, unsigned int t1, // [crispy] 32-bit integer math unsigned int b1, // [crispy] 32-bit integer math unsigned int t2, // [crispy] 32-bit integer math unsigned int b2 ); // [crispy] 32-bit integer math void R_DrawPlanes (void); visplane_t* R_FindPlane ( fixed_t height, int picnum, int lightlevel ); visplane_t* R_CheckPlane ( visplane_t* pl, int start, int stop ); #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_segs.c000066400000000000000000000611641360717211000214170ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // All the clipping: columns, horizontal spans, sky columns. // #include #include #include "i_system.h" #include "doomdef.h" #include "doomstat.h" #include "r_local.h" #include "r_sky.h" #include "r_bmaps.h" // [crispy] brightmaps // OPTIMIZE: closed two sided lines as single sided // True if any of the segs textures might be visible. boolean segtextured; // False if the back side is the same plane. boolean markfloor; boolean markceiling; boolean maskedtexture; int toptexture; int bottomtexture; int midtexture; angle_t rw_normalangle; // angle to line origin int rw_angle1; // // regular wall // int rw_x; int rw_stopx; angle_t rw_centerangle; fixed_t rw_offset; fixed_t rw_distance; fixed_t rw_scale; fixed_t rw_scalestep; fixed_t rw_midtexturemid; fixed_t rw_toptexturemid; fixed_t rw_bottomtexturemid; int worldtop; int worldbottom; int worldhigh; int worldlow; int64_t pixhigh; // [crispy] WiggleFix int64_t pixlow; // [crispy] WiggleFix fixed_t pixhighstep; fixed_t pixlowstep; int64_t topfrac; // [crispy] WiggleFix fixed_t topstep; int64_t bottomfrac; // [crispy] WiggleFix fixed_t bottomstep; lighttable_t** walllights; int* maskedtexturecol; // [crispy] 32-bit integer math // [crispy] WiggleFix: add this code block near the top of r_segs.c // // R_FixWiggle() // Dynamic wall/texture rescaler, AKA "WiggleHack II" // by Kurt "kb1" Baumgardner ("kb") and Andrey "Entryway" Budko ("e6y") // // [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, // causing full-screen HOM, and possible program crashes. // // 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". Not a crash, but still ugly. // // 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. // // This code 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 no longer possible, even in levels with maximum // height sectors (65535 is the theoretical height, though Doom // cannot handle sectors > 32767 units in height. // // The code is not perfect across all situations. Some floor wiggle can // still be seen, and some texture strips may be slightly misaligned in // extreme cases. These effects cannot be corrected further, without // increasing the precision of various renderer variables, and, // possibly, creating a noticable performance penalty. // static int max_rwscale = 64 * FRACUNIT; static int heightbits = 12; static int heightunit = (1 << 12); static int invhgtbits = 4; static const struct { int clamp; int heightbits; } scale_values[8] = { {2048 * FRACUNIT, 12}, {1024 * FRACUNIT, 12}, {1024 * FRACUNIT, 11}, { 512 * FRACUNIT, 11}, { 512 * FRACUNIT, 10}, { 256 * FRACUNIT, 10}, { 256 * FRACUNIT, 9}, { 128 * FRACUNIT, 9} }; void R_FixWiggle (sector_t *sector) { static int lastheight = 0; int height = (sector->interpceilingheight - sector->interpfloorheight) >> FRACBITS; // disallow negative heights. using 1 forces cache initialization if (height < 1) height = 1; // early out? if (height != lastheight) { lastheight = height; // initialize, or handle moving sector if (height != sector->cachedheight) { sector->cachedheight = height; sector->scaleindex = 0; height >>= 7; // calculate adjustment while (height >>= 1) sector->scaleindex++; } // fine-tune renderer for this wall max_rwscale = scale_values[sector->scaleindex].clamp; heightbits = scale_values[sector->scaleindex].heightbits; heightunit = (1 << heightbits); invhgtbits = FRACBITS - heightbits; } } // // R_RenderMaskedSegRange // void R_RenderMaskedSegRange ( drawseg_t* ds, int x1, int x2 ) { unsigned index; column_t* col; int lightnum; int texnum; // Calculate light table. // Use different light tables // for horizontal / vertical / diagonal. Diagonal? // OPTIMIZE: get rid of LIGHTSEGSHIFT globally curline = ds->curline; frontsector = curline->frontsector; backsector = curline->backsector; texnum = texturetranslation[curline->sidedef->midtexture]; lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+(extralight * LIGHTBRIGHT); // [crispy] smoother fake contrast lightnum += curline->fakecontrast; /* if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; */ if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS-1]; else walllights = scalelight[lightnum]; maskedtexturecol = ds->maskedtexturecol; rw_scalestep = ds->scalestep; spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; mfloorclip = ds->sprbottomclip; mceilingclip = ds->sprtopclip; // find positioning if (curline->linedef->flags & ML_DONTPEGBOTTOM) { dc_texturemid = frontsector->interpfloorheight > backsector->interpfloorheight ? frontsector->interpfloorheight : backsector->interpfloorheight; dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; } else { dc_texturemid =frontsector->interpceilingheightinterpceilingheight ? frontsector->interpceilingheight : backsector->interpceilingheight; dc_texturemid = dc_texturemid - viewz; } dc_texturemid += curline->sidedef->rowoffset; if (fixedcolormap) dc_colormap[0] = dc_colormap[1] = fixedcolormap; // draw the columns for (dc_x = x1 ; dc_x <= x2 ; dc_x++) { // calculate lighting if (maskedtexturecol[dc_x] != INT_MAX) // [crispy] 32-bit integer math { if (!fixedcolormap) { index = spryscale>>(LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE ) index = MAXLIGHTSCALE-1; // [crispy] no brightmaps for mid-textures dc_colormap[0] = dc_colormap[1] = walllights[index]; } // [crispy] apply Killough's int64 sprtopscreen overflow fix // from winmbf/Source/r_segs.c:174-191 // killough 3/2/98: // // This calculation used to overflow and cause crashes in Doom: // // sprtopscreen = centeryfrac - FixedMul(dc_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: { int64_t t = ((int64_t) centeryfrac << FRACBITS) - (int64_t) dc_texturemid * spryscale; if (t + (int64_t) textureheight[texnum] * spryscale < 0 || t > (int64_t) SCREENHEIGHT << FRACBITS*2) { spryscale += rw_scalestep; // [crispy] MBF had this in the for-loop iterator continue; // skip if the texture is out of screen's range } sprtopscreen = (int64_t)(t >> FRACBITS); // [crispy] WiggleFix } dc_iscale = 0xffffffffu / (unsigned)spryscale; // draw the texture col = (column_t *)( (byte *)R_GetColumn(texnum,maskedtexturecol[dc_x], false) -3); R_DrawMaskedColumn (col); maskedtexturecol[dc_x] = INT_MAX; // [crispy] 32-bit integer math } spryscale += rw_scalestep; } } // // R_RenderSegLoop // Draws zero, one, or two textures (and possibly a masked // texture) for walls. // Can draw or mark the starting pixel of floor and ceiling // textures. // CALLED: CORE LOOPING ROUTINE. // #define HEIGHTBITS 12 #define HEIGHTUNIT (1<>heightbits); // [crispy] WiggleFix // no space above wall? if (yl < ceilingclip[rw_x]+1) yl = ceilingclip[rw_x]+1; if (markceiling) { top = ceilingclip[rw_x]+1; bottom = yl-1; if (bottom >= floorclip[rw_x]) bottom = floorclip[rw_x]-1; if (top <= bottom) { ceilingplane->top[rw_x] = top; ceilingplane->bottom[rw_x] = bottom; } } yh = (int)(bottomfrac>>heightbits); // [crispy] WiggleFix if (yh >= floorclip[rw_x]) yh = floorclip[rw_x]-1; if (markfloor) { top = yh+1; bottom = floorclip[rw_x]-1; if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x]+1; if (top <= bottom) { floorplane->top[rw_x] = top; floorplane->bottom[rw_x] = bottom; } } // texturecolumn and lighting are independent of wall tiers if (segtextured) { // calculate texture offset angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT; texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale>>(LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE ) index = MAXLIGHTSCALE-1; // [crispy] optional brightmaps dc_colormap[0] = walllights[index]; dc_colormap[1] = (!fixedcolormap && (crispy->brightmaps & BRIGHTMAPS_TEXTURES)) ? scalelight[LIGHTLEVELS-1][MAXLIGHTSCALE-1] : dc_colormap[0]; dc_x = rw_x; dc_iscale = 0xffffffffu / (unsigned)rw_scale; } else { // purely to shut up the compiler texturecolumn = 0; } // draw the wall tiers if (midtexture) { // single sided line dc_yl = yl; dc_yh = yh; dc_texturemid = rw_midtexturemid; dc_source = R_GetColumn(midtexture,texturecolumn,true); dc_texheight = textureheight[midtexture]>>FRACBITS; // [crispy] Tutti-Frutti fix dc_brightmap = texturebrightmap[midtexture]; colfunc (); ceilingclip[rw_x] = viewheight; floorclip[rw_x] = -1; } else { // two sided line if (toptexture) { // top wall mid = (int)(pixhigh>>heightbits); // [crispy] WiggleFix pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x]-1; if (mid >= yl) { dc_yl = yl; dc_yh = mid; dc_texturemid = rw_toptexturemid; dc_source = R_GetColumn(toptexture,texturecolumn,true); dc_texheight = textureheight[toptexture]>>FRACBITS; // [crispy] Tutti-Frutti fix dc_brightmap = texturebrightmap[toptexture]; colfunc (); ceilingclip[rw_x] = mid; } else ceilingclip[rw_x] = yl-1; } else { // no top wall if (markceiling) ceilingclip[rw_x] = yl-1; } if (bottomtexture) { // bottom wall mid = (int)((pixlow+heightunit-1)>>heightbits); // [crispy] WiggleFix pixlow += pixlowstep; // no space above wall? if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x]+1; if (mid <= yh) { dc_yl = mid; dc_yh = yh; dc_texturemid = rw_bottomtexturemid; dc_source = R_GetColumn(bottomtexture, texturecolumn,true); dc_texheight = textureheight[bottomtexture]>>FRACBITS; // [crispy] Tutti-Frutti fix dc_brightmap = texturebrightmap[bottomtexture]; colfunc (); floorclip[rw_x] = mid; } else floorclip[rw_x] = yh+1; } else { // no bottom wall if (markfloor) floorclip[rw_x] = yh+1; } if (maskedtexture) { // save texturecol // for backdrawing of masked mid texture maskedtexturecol[rw_x] = texturecolumn; } } rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } } // [crispy] WiggleFix: move R_ScaleFromGlobalAngle function to r_segs.c, // above R_StoreWallRange 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]); fixed_t num = FixedMul(projection, finesine[angleb >> ANGLETOFINESHIFT])< (num >> 16)) { scale = FixedDiv(num, den); // [kb] When this evaluates True, the scale is clamped, // and there will be some wiggling. if (scale > max_rwscale) scale = max_rwscale; else if (scale < 256) scale = 256; } else scale = max_rwscale; return scale; } // // R_StoreWallRange // A wall segment will be drawn // between start and stop pixels (inclusive). // void R_StoreWallRange ( int start, int stop ) { fixed_t vtop; int lightnum; int64_t dx, dy, dx1, dy1, dist; // [crispy] fix long wall wobble const uint32_t len = curline->length; // [crispy] remove MAXDRAWSEGS Vanilla limit if (ds_p == &drawsegs[numdrawsegs]) { int numdrawsegs_old = numdrawsegs; numdrawsegs = numdrawsegs ? 2 * numdrawsegs : MAXDRAWSEGS; drawsegs = I_Realloc(drawsegs, numdrawsegs * sizeof(*drawsegs)); memset(drawsegs + numdrawsegs_old, 0, (numdrawsegs - numdrawsegs_old) * sizeof(*drawsegs)); ds_p = drawsegs + numdrawsegs_old; if (numdrawsegs_old) fprintf(stderr, "R_StoreWallRange: Hit MAXDRAWSEGS limit at %d, raised to %d.\n", numdrawsegs_old, numdrawsegs); } #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; // [crispy] (flags & ML_MAPPED) is all we need to know for automap if (automapactive && !crispy->automapoverlay) return; // calculate rw_distance for scale calculation rw_normalangle = curline->r_angle + ANG90; // [crispy] use re-calculated angle // [crispy] fix long wall wobble // thank you very much Linguica, e6y and kb1 // http://www.doomworld.com/vb/post/1340718 // shift right to avoid possibility of int64 overflow in rw_distance calculation dx = ((int64_t)curline->v2->r_x - curline->v1->r_x) >> 1; dy = ((int64_t)curline->v2->r_y - curline->v1->r_y) >> 1; dx1 = ((int64_t)viewx - curline->v1->r_x) >> 1; dy1 = ((int64_t)viewy - curline->v1->r_y) >> 1; dist = ((dy * dx1 - dx * dy1) / len) << 1; 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; // [crispy] WiggleFix: add this line, in r_segs.c:R_StoreWallRange, // right before calls to R_ScaleFromGlobalAngle: 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 { // UNUSED: try to fix the stretched line bug #if 0 if (rw_distance < FRACUNIT/2) { fixed_t trx,try; fixed_t gxt,gyt; trx = curline->v1->x - viewx; try = curline->v1->y - viewy; gxt = FixedMul(trx,viewcos); gyt = -FixedMul(try,viewsin); ds_p->scale1 = FixedDiv(projection, gxt-gyt)<scale2 = ds_p->scale1; } // calculate texture boundaries // and decide if floor / ceiling marks are needed worldtop = frontsector->interpceilingheight - viewz; worldbottom = frontsector->interpfloorheight - viewz; midtexture = toptexture = bottomtexture = maskedtexture = 0; ds_p->maskedtexturecol = NULL; if (!backsector) { // single sided line midtexture = texturetranslation[sidedef->midtexture]; // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; if (linedef->flags & ML_DONTPEGBOTTOM) { vtop = frontsector->interpfloorheight + textureheight[sidedef->midtexture]; // bottom of texture at bottom rw_midtexturemid = vtop - viewz; } else { // top of texture at top rw_midtexturemid = worldtop; } rw_midtexturemid += sidedef->rowoffset; ds_p->silhouette = SIL_BOTH; ds_p->sprtopclip = screenheightarray; ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->tsilheight = INT_MIN; } else { // [crispy] fix sprites being visible behind closed doors // adapted from mbfsrc/R_BSP.C:234-257 const boolean doorclosed = // if door is closed because back is shut: backsector->interpceilingheight <= backsector->interpfloorheight // preserve a kind of transparent door/lift special effect: && (backsector->interpceilingheight >= frontsector->interpceilingheight || curline->sidedef->toptexture) && (backsector->interpfloorheight <= frontsector->interpfloorheight || curline->sidedef->bottomtexture) // properly render skies (consider door "open" if both ceilings are sky): && (backsector->ceilingpic != skyflatnum || frontsector->ceilingpic != skyflatnum); // two sided line ds_p->sprtopclip = ds_p->sprbottomclip = NULL; ds_p->silhouette = 0; if (frontsector->interpfloorheight > backsector->interpfloorheight) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = frontsector->interpfloorheight; } else if (backsector->interpfloorheight > viewz) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = INT_MAX; // ds_p->sprbottomclip = negonearray; } if (frontsector->interpceilingheight < backsector->interpceilingheight) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = frontsector->interpceilingheight; } else if (backsector->interpceilingheight < viewz) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT_MIN; // ds_p->sprtopclip = screenheightarray; } if (backsector->interpceilingheight <= frontsector->interpfloorheight || doorclosed) { ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->silhouette |= SIL_BOTTOM; } if (backsector->interpfloorheight >= frontsector->interpceilingheight || doorclosed) { ds_p->sprtopclip = screenheightarray; ds_p->tsilheight = INT_MIN; ds_p->silhouette |= SIL_TOP; } worldhigh = backsector->interpceilingheight - viewz; worldlow = backsector->interpfloorheight - viewz; // hack to allow height changes in outdoor areas if (frontsector->ceilingpic == skyflatnum && backsector->ceilingpic == skyflatnum) { worldtop = worldhigh; } if (worldlow != worldbottom || backsector->floorpic != frontsector->floorpic || backsector->lightlevel != frontsector->lightlevel) { markfloor = true; } else { // same plane on both sides markfloor = false; } if (worldhigh != worldtop || backsector->ceilingpic != frontsector->ceilingpic || backsector->lightlevel != frontsector->lightlevel) { markceiling = true; } else { // same plane on both sides markceiling = false; } if (backsector->interpceilingheight <= frontsector->interpfloorheight || backsector->interpfloorheight >= frontsector->interpceilingheight) { // closed door markceiling = markfloor = true; } if (worldhigh < worldtop) { // top texture toptexture = texturetranslation[sidedef->toptexture]; if (linedef->flags & ML_DONTPEGTOP) { // top of texture at top rw_toptexturemid = worldtop; } else { vtop = backsector->interpceilingheight + textureheight[sidedef->toptexture]; // bottom of texture rw_toptexturemid = vtop - viewz; } } if (worldlow > worldbottom) { // bottom texture bottomtexture = texturetranslation[sidedef->bottomtexture]; if (linedef->flags & ML_DONTPEGBOTTOM ) { // bottom of texture at bottom // top of texture at top rw_bottomtexturemid = worldtop; } else // top of texture at top rw_bottomtexturemid = worldlow; } rw_toptexturemid += sidedef->rowoffset; rw_bottomtexturemid += sidedef->rowoffset; // allocate space for masked texture tables if (sidedef->midtexture) { // masked midtexture maskedtexture = true; ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; lastopening += rw_stopx - rw_x; } } // calculate rw_offset (only needed for textured lines) segtextured = midtexture | toptexture | bottomtexture | maskedtexture; if (segtextured) { // [crispy] fix long wall wobble rw_offset = (fixed_t)(((dx*dx1 + dy*dy1) / len) << 1); rw_offset += sidedef->textureoffset + curline->offset; rw_centerangle = ANG90 + viewangle - rw_normalangle; // calculate light table // use different light tables // for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally if (!fixedcolormap) { lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+(extralight * LIGHTBRIGHT); // [crispy] smoother fake contrast lightnum += curline->fakecontrast; /* if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; */ if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS-1]; else walllights = scalelight[lightnum]; } } // if a floor / ceiling plane is on the wrong side // of the view plane, it is definitely invisible // and doesn't need to be marked. if (frontsector->interpfloorheight >= viewz) { // above view plane markfloor = false; } if (frontsector->interpceilingheight <= 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 = ((int64_t)centeryfrac>>invhgtbits) - (((int64_t)worldtop * rw_scale)>>FRACBITS); // [crispy] WiggleFix bottomstep = -FixedMul (rw_scalestep,worldbottom); bottomfrac = ((int64_t)centeryfrac>>invhgtbits) - (((int64_t)worldbottom * rw_scale)>>FRACBITS); // [crispy] WiggleFix if (backsector) { worldhigh >>= invhgtbits; worldlow >>= invhgtbits; if (worldhigh < worldtop) { pixhigh = ((int64_t)centeryfrac>>invhgtbits) - (((int64_t)worldhigh * rw_scale)>>FRACBITS); // [crispy] WiggleFix pixhighstep = -FixedMul (rw_scalestep,worldhigh); } if (worldlow > worldbottom) { pixlow = ((int64_t)centeryfrac>>invhgtbits) - (((int64_t)worldlow * rw_scale)>>FRACBITS); // [crispy] WiggleFix pixlowstep = -FixedMul (rw_scalestep,worldlow); } } // render it if (markceiling) ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1); if (markfloor) floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1); R_RenderSegLoop (); // save sprite clipping info if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) { memcpy (lastopening, ceilingclip+start, sizeof(*lastopening)*(rw_stopx-start)); ds_p->sprtopclip = lastopening - start; lastopening += rw_stopx - start; } if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && !ds_p->sprbottomclip) { memcpy (lastopening, floorclip+start, sizeof(*lastopening)*(rw_stopx-start)); ds_p->sprbottomclip = lastopening - start; lastopening += rw_stopx - start; } if (maskedtexture && !(ds_p->silhouette&SIL_TOP)) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT_MIN; } if (maskedtexture && !(ds_p->silhouette&SIL_BOTTOM)) { ds_p->silhouette |= SIL_BOTTOM; ds_p->bsilheight = INT_MAX; } ds_p++; } crispy-doom-crispy-doom-5.6.4/src/doom/r_segs.h000066400000000000000000000014141360717211000214140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh module, drawing LineSegs from BSP. // #ifndef __R_SEGS__ #define __R_SEGS__ void R_RenderMaskedSegRange ( drawseg_t* ds, int x1, int x2 ); #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_sky.c000066400000000000000000000027651360717211000212660ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Sky rendering. The DOOM sky is a texture map like any // wall, wrapping around. A 1024 columns equal 360 degrees. // The default sky map is 256 columns and repeats 4 times // on a 320 screen? // // // Needed for FRACUNIT. #include "m_fixed.h" // Needed for Flat retrieval. #include "r_data.h" #include "r_sky.h" // // sky mapping // int skyflatnum; int skytexture = -1; // [crispy] initialize int skytexturemid; // // R_InitSkyMap // Called whenever the view size changes. // void R_InitSkyMap (void) { // skyflatnum = R_FlatNumForName ( SKYFLATNAME ); // [crispy] stretch sky if (skytexture == -1) { return; } if ((crispy->stretchsky = crispy->freelook || crispy->mouselook || crispy->pitch)) { skytexturemid = -28*FRACUNIT * (textureheight[skytexture] >> FRACBITS) / SKYSTRETCH_HEIGHT; } else skytexturemid = ORIGHEIGHT/2*FRACUNIT; } crispy-doom-crispy-doom-5.6.4/src/doom/r_sky.h000066400000000000000000000017321360717211000212640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Sky rendering. // #ifndef __R_SKY__ #define __R_SKY__ // SKY, store the number for name. #define SKYFLATNAME "F_SKY1" // The sky map is 256*128*4 maps. #define ANGLETOSKYSHIFT 22 // [crispy] stretch sky #define SKYSTRETCH_HEIGHT 228 extern int skytexture; extern int skytexturemid; // Called whenever the view size changes. void R_InitSkyMap (void); #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_state.h000066400000000000000000000046501360717211000216000ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh/render internal state variables (global). // #ifndef __R_STATE__ #define __R_STATE__ // Need data structure definitions. #include "d_player.h" #include "r_data.h" // // Refresh internal data structures, // for rendering. // // needed for texture pegging extern fixed_t* textureheight; // needed for pre rendering (fracs) extern fixed_t* spritewidth; extern fixed_t* spriteoffset; extern fixed_t* spritetopoffset; extern lighttable_t* colormaps; extern int viewwidth; extern int scaledviewwidth; extern int viewheight; // [crispy] lookup table for horizontal screen coordinates extern int flipscreenwidth[MAXWIDTH]; extern int *flipviewwidth; extern int firstflat; // for global animation extern int* flattranslation; extern int* texturetranslation; // Sprite.... extern int firstspritelump; extern int lastspritelump; extern int numspritelumps; // // Lookup tables for map data. // extern int numsprites; extern spritedef_t* sprites; extern int numvertexes; extern vertex_t* vertexes; extern int numsegs; extern seg_t* segs; extern int numsectors; extern sector_t* sectors; extern int numsubsectors; extern subsector_t* subsectors; extern int numnodes; extern node_t* nodes; extern int numlines; extern line_t* lines; extern int numsides; extern side_t* sides; // // POV data. // extern fixed_t viewx; extern fixed_t viewy; extern fixed_t viewz; extern angle_t viewangle; extern player_t* viewplayer; // ? extern angle_t clipangle; extern int viewangletox[FINEANGLES/2]; extern angle_t xtoviewangle[MAXWIDTH+1]; //extern fixed_t finetangent[FINEANGLES/2]; extern fixed_t rw_distance; extern angle_t rw_normalangle; // angle to line origin extern int rw_angle1; // Segs count? extern int sscount; extern visplane_t* floorplane; extern visplane_t* ceilingplane; #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_swirl.c000066400000000000000000000052031360717211000216060ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2000, 2005-2014 Simon Howard // Copyright(C) 2019 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] add support for SMMU swirling flats // // [crispy] adapted from smmu/r_ripple.c, by Simon Howard #include #include #include #include #include "doomstat.h" // swirl factors determine the number of waves per flat width // 1 cycle per 64 units #define swirlfactor (8192/64) // 1 cycle per 32 units (2 in 64) #define swirlfactor2 (8192/32) #define SEQUENCE 1024 #define FLATSIZE (64 * 64) static int *offsets; static int *offset; #define AMP 2 #define AMP2 2 #define SPEED 40 void R_InitDistortedFlats() { if (!offsets) { int i; offsets = I_Realloc(NULL, SEQUENCE * FLATSIZE * sizeof(*offsets)); offset = offsets; for (i = 0; i < SEQUENCE; i++) { int x, y; for (x = 0; x < 64; x++) { for (y = 0; y < 64; y++) { int x1, y1; int sinvalue, sinvalue2; sinvalue = (y * swirlfactor + i * SPEED * 5 + 900) & 8191; sinvalue2 = (x * swirlfactor2 + i * SPEED * 4 + 300) & 8191; x1 = x + 128 + ((finesine[sinvalue] * AMP) >> FRACBITS) + ((finesine[sinvalue2] * AMP2) >> FRACBITS); sinvalue = (x * swirlfactor + i * SPEED * 3 + 700) & 8191; sinvalue2 = (y * swirlfactor2 + i * SPEED * 4 + 1200) & 8191; y1 = y + 128 + ((finesine[sinvalue] * AMP) >> FRACBITS) + ((finesine[sinvalue2] * AMP2) >> FRACBITS); x1 &= 63; y1 &= 63; offset[(y << 6) + x] = (y1 << 6) + x1; } } offset += FLATSIZE; } } } char *R_DistortedFlat(int flatnum) { static int swirltic = -1; static int swirlflat = -1; static char distortedflat[FLATSIZE]; if (swirltic != leveltime) { offset = offsets + ((leveltime & (SEQUENCE - 1)) * FLATSIZE); swirltic = leveltime; swirlflat = -1; } if (swirlflat != flatnum) { char *normalflat; int i; normalflat = W_CacheLumpNum(flatnum, PU_STATIC); for (i = 0; i < FLATSIZE; i++) { distortedflat[i] = normalflat[offset[i]]; } W_ReleaseLumpNum(flatnum); swirlflat = flatnum; } return distortedflat; } crispy-doom-crispy-doom-5.6.4/src/doom/r_swirl.h000066400000000000000000000014631360717211000216170ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2000, 2005-2014 Simon Howard // Copyright(C) 2019 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] add support for SMMU swirling flats // #ifndef __R_SWIRL__ #define __R_SWIRL__ void R_InitDistortedFlats(); char *R_DistortedFlat(int flatnum); #endif crispy-doom-crispy-doom-5.6.4/src/doom/r_things.c000066400000000000000000000776451360717211000217650ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh of things, i.e. objects represented by sprites. // #include #include #include "deh_main.h" #include "doomdef.h" #include "i_swap.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "r_local.h" #include "doomstat.h" #include "v_trans.h" // [crispy] colored blood sprites #include "p_local.h" // [crispy] MLOOKUNIT #include "r_bmaps.h" // [crispy] R_BrightmapForTexName() #define MINZ (FRACUNIT*4) #define BASEYCENTER (ORIGHEIGHT/2) //void R_DrawColumn (void); //void R_DrawFuzzColumn (void); typedef struct { int x1; int x2; int column; int topclip; int bottomclip; } maskdraw_t; static degenmobj_t laserspot_m = {{0}}; degenmobj_t *laserspot = &laserspot_m; // [crispy] extendable, but the last char element must be zero, // keep in sync with multiitem_t multiitem_crosshairtype[] in m_menu.c static laserpatch_t laserpatch_m[] = { {'+', "cross1", 0, 0, 0}, {'^', "cross2", 0, 0, 0}, {'.', "cross3", 0, 0, 0}, {0, "", 0, 0, 0}, }; laserpatch_t *laserpatch = laserpatch_m; // // Sprite rotation 0 is facing the viewer, // rotation 1 is one angle turn CLOCKWISE around the axis. // This is not the same as the angle, // which increases counter clockwise (protractor). // There was a lot of stuff grabbed wrong, so I changed it... // fixed_t pspritescale; fixed_t pspriteiscale; lighttable_t** spritelights; // constant arrays // used for psprite clipping and initializing clipping int negonearray[MAXWIDTH]; // [crispy] 32-bit integer math int screenheightarray[MAXWIDTH]; // [crispy] 32-bit integer math // // INITIALIZATION FUNCTIONS // // variables used to look up // and range check thing_t sprites patches spritedef_t* sprites; int numsprites; spriteframe_t sprtemp[29]; int maxframe; const char *spritename; // // R_InstallSpriteLump // Local function for R_InitSprites. // void R_InstallSpriteLump ( int lump, unsigned frame, char rot, boolean flipped ) { int r; // [crispy] support 16 sprite rotations unsigned rotation = (rot >= 'A') ? rot - 'A' + 10 : (rot >= '0') ? rot - '0' : 17; if (frame >= 29 || rotation > 16) // [crispy] support 16 sprite rotations 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 // [crispy] make non-fatal if (sprtemp[frame].rotate == false) fprintf (stderr, "R_InitSprites: Sprite %s frame %c has " "multip rot=0 lump\n", spritename, 'A'+frame); // [crispy] make non-fatal if (sprtemp[frame].rotate == true) fprintf (stderr, "R_InitSprites: Sprite %s frame %c has rotations " "and a rot=0 lump\n", spritename, 'A'+frame); // [crispy] moved ... // sprtemp[frame].rotate = false; for (r=0 ; r<8 ; r++) { // [crispy] only if not yet substituted if (sprtemp[frame].lump[r] == -1) { sprtemp[frame].lump[r] = lump - firstspritelump; sprtemp[frame].flip[r] = (byte)flipped; // [crispy] ... here sprtemp[frame].rotate = false; } } return; } // the lump is only used for one rotation // [crispy] make non-fatal if (sprtemp[frame].rotate == false) fprintf (stderr, "R_InitSprites: Sprite %s frame %c has rotations " "and a rot=0 lump\n", spritename, 'A'+frame); // [crispy] moved ... // sprtemp[frame].rotate = true; // make 0 based rotation--; if (sprtemp[frame].lump[rotation] != -1) { // [crispy] make non-fatal fprintf (stderr, "R_InitSprites: Sprite %s : %c : %c " "has two lumps mapped to it\n", spritename, 'A'+frame, '1'+rotation); return; } sprtemp[frame].lump[rotation] = lump - firstspritelump; sprtemp[frame].flip[rotation] = (byte)flipped; // [crispy] ... here sprtemp[frame].rotate = true; } // // R_InitSpriteDefs // Pass a null terminated list of sprite names // (4 chars exactly) to be used. // Builds the sprite rotation matrixes to account // for horizontally flipped sprites. // Will report an error if the lumps are inconsistant. // Only called at startup. // // Sprite lump names are 4 characters for the actor, // a letter for the frame, and a number for the rotation. // A sprite that is flippable will have an additional // letter/number appended. // The rotation character can be 0 to signify no rotations. // void R_InitSpriteDefs(const char **namelist) { const char **check; int i; int l; int frame; int rotation; int start; int end; int patched; // count the number of sprite names check = namelist; while (*check != NULL) check++; numsprites = check-namelist; if (!numsprites) return; sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL); start = firstspritelump-1; end = lastspritelump+1; // scan all the lump names for each of the names, // noting the highest frame letter. // Just compare 4 characters as ints for (i=0 ; iname, spritename, 4)) { frame = lumpinfo[l]->name[4] - 'A'; rotation = lumpinfo[l]->name[5]; if (modifiedgame) patched = W_GetNumForName (lumpinfo[l]->name); else patched = l; R_InstallSpriteLump (patched, frame, rotation, false); if (lumpinfo[l]->name[6]) { frame = lumpinfo[l]->name[6] - 'A'; rotation = lumpinfo[l]->name[7]; R_InstallSpriteLump (l, frame, rotation, true); } } } // check the frames that were found for completeness if (maxframe == -1) { sprites[i].numframes = 0; continue; } maxframe++; for (frame = 0 ; frame < maxframe ; frame++) { switch ((int)sprtemp[frame].rotate) { case -1: // no rotations were found for that frame at all // [crispy] make non-fatal fprintf (stderr, "R_InitSprites: No patches found " "for %s frame %c\n", spritename, frame+'A'); break; case 0: // only the first rotation is needed break; case 1: // must have all 8 frames for (rotation=0 ; rotation<8 ; rotation++) if (sprtemp[frame].lump[rotation] == -1) I_Error ("R_InitSprites: Sprite %s frame %c " "is missing rotations", spritename, frame+'A'); // [crispy] support 16 sprite rotations sprtemp[frame].rotate = 2; for ( ; rotation<16 ; rotation++) if (sprtemp[frame].lump[rotation] == -1) { sprtemp[frame].rotate = 1; break; } break; } } // allocate space for the frames present and copy sprtemp to it sprites[i].numframes = maxframe; sprites[i].spriteframes = Z_Malloc (maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); memcpy (sprites[i].spriteframes, sprtemp, maxframe*sizeof(spriteframe_t)); } } // // GAME FUNCTIONS // vissprite_t* vissprites = NULL; vissprite_t* vissprite_p; int newvissprite; static int numvissprites; // // R_InitSprites // Called at program start. // void R_InitSprites(const char **namelist) { int i; for (i=0 ; itopdelta != 0xff ; ) { // [crispy] support for DeePsea tall patches if (column->topdelta <= top) { top += column->topdelta; } else { top = column->topdelta; } // calculate unclipped screen coordinates // for post topscreen = sprtopscreen + spryscale*top; bottomscreen = topscreen + spryscale*column->length; dc_yl = (int)((topscreen+FRACUNIT-1)>>FRACBITS); // [crispy] WiggleFix dc_yh = (int)((bottomscreen-1)>>FRACBITS); // [crispy] WiggleFix if (dc_yh >= mfloorclip[dc_x]) dc_yh = mfloorclip[dc_x]-1; if (dc_yl <= mceilingclip[dc_x]) dc_yl = mceilingclip[dc_x]+1; if (dc_yl <= dc_yh) { dc_source = (byte *)column + 3; dc_texturemid = basetexturemid - (top<length + 4); } dc_texturemid = basetexturemid; } // // R_DrawVisSprite // mfloorclip and mceilingclip should also be set. // void R_DrawVisSprite ( vissprite_t* vis, int x1, int x2 ) { column_t* column; int texturecolumn; fixed_t frac; patch_t* patch; patch = W_CacheLumpNum (vis->patch+firstspritelump, PU_CACHE); // [crispy] brightmaps for select sprites dc_colormap[0] = vis->colormap[0]; dc_colormap[1] = vis->colormap[1]; dc_brightmap = vis->brightmap; if (!dc_colormap[0]) { // NULL colormap = shadow draw colfunc = fuzzcolfunc; } else if (vis->mobjflags & MF_TRANSLATION) { colfunc = transcolfunc; dc_translation = translationtables - 256 + ( (vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8) ); } // [crispy] color-translated sprites (i.e. blood) else if (vis->translation) { colfunc = transcolfunc; dc_translation = vis->translation; } // [crispy] translucent sprites else if (crispy->translucency && vis->mobjflags & MF_TRANSLUCENT) { if (!(vis->mobjflags & (MF_NOGRAVITY | MF_COUNTITEM)) || (vis->mobjflags & MF_NOGRAVITY && crispy->translucency & TRANSLUCENCY_MISSILE) || (vis->mobjflags & MF_COUNTITEM && crispy->translucency & TRANSLUCENCY_ITEM)) { colfunc = tlcolfunc; } #ifdef CRISPY_TRUECOLOR blendfunc = vis->blendfunc; #endif } dc_iscale = abs(vis->xiscale)>>detailshift; dc_texturemid = vis->texturemid; frac = vis->startfrac; spryscale = vis->scale; sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale); for (dc_x=vis->x1 ; dc_x<=vis->x2 ; dc_x++, frac += vis->xiscale) { static boolean error = false; texturecolumn = frac>>FRACBITS; #ifdef RANGECHECK if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) { // [crispy] make non-fatal if (!error) { fprintf (stderr, "R_DrawSpriteRange: bad texturecolumn\n"); error = true; } continue; } #endif column = (column_t *) ((byte *)patch + LONG(patch->columnofs[texturecolumn])); R_DrawMaskedColumn (column); } colfunc = basecolfunc; #ifdef CRISPY_TRUECOLOR blendfunc = I_BlendOver; #endif } // // R_ProjectSprite // Generates a vissprite for a thing // if it might be visible. // void R_ProjectSprite (mobj_t* thing) { fixed_t tr_x; fixed_t tr_y; fixed_t gxt; fixed_t gyt; fixed_t gzt; // [JN] killough 3/27/98 fixed_t tx; fixed_t tz; fixed_t xscale; int x1; int x2; spritedef_t* sprdef; spriteframe_t* sprframe; int lump; unsigned rot; boolean flip; int index; vissprite_t* vis; angle_t ang; fixed_t iscale; fixed_t interpx; fixed_t interpy; fixed_t interpz; fixed_t interpangle; // [AM] Interpolate between current and last position, // if prudent. if (crispy->uncapped && // Don't interpolate if the mobj did something // that would necessitate turning it off for a tic. thing->interp == true && // Don't interpolate during a paused state. leveltime > oldleveltime) { interpx = thing->oldx + FixedMul(thing->x - thing->oldx, fractionaltic); interpy = thing->oldy + FixedMul(thing->y - thing->oldy, fractionaltic); interpz = thing->oldz + FixedMul(thing->z - thing->oldz, fractionaltic); interpangle = R_InterpolateAngle(thing->oldangle, thing->angle, fractionaltic); } else { interpx = thing->x; interpy = thing->y; interpz = thing->z; interpangle = thing->angle; } // transform the origin point tr_x = interpx - viewx; tr_y = interpy - viewy; gxt = FixedMul(tr_x,viewcos); gyt = -FixedMul(tr_y,viewsin); tz = gxt-gyt; // thing is behind view plane? if (tz < MINZ) return; xscale = FixedDiv(projection, tz); gxt = -FixedMul(tr_x,viewsin); gyt = FixedMul(tr_y,viewcos); tx = -(gyt+gxt); // too far off the side? if (abs(tx)>(tz<<2)) return; // decide which patch to use for sprite relative to player #ifdef RANGECHECK if ((unsigned int) thing->sprite >= (unsigned int) numsprites) I_Error ("R_ProjectSprite: invalid sprite number %i ", thing->sprite); #endif sprdef = &sprites[thing->sprite]; // [crispy] the TNT1 sprite is not supposed to be rendered anyway if (!sprdef->numframes && thing->sprite == SPR_TNT1) { return; } #ifdef RANGECHECK if ( (thing->frame&FF_FRAMEMASK) >= sprdef->numframes ) I_Error ("R_ProjectSprite: invalid sprite frame %i : %i ", thing->sprite, thing->frame); #endif sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK]; if (sprframe->rotate) { // choose a different rotation based on player view ang = R_PointToAngle (interpx, interpy); // [crispy] now made non-fatal if (sprframe->rotate == -1) { return; } else // [crispy] support 16 sprite rotations if (sprframe->rotate == 2) { const unsigned rot2 = (ang-interpangle+(unsigned)(ANG45/4)*17); rot = (rot2>>29) + ((rot2>>25)&8); } else { rot = (ang-interpangle+(unsigned)(ANG45/2)*9)>>29; } lump = sprframe->lump[rot]; flip = (boolean)sprframe->flip[rot]; } else { // use single rotation for all views lump = sprframe->lump[0]; flip = (boolean)sprframe->flip[0]; } // [crispy] randomly flip corpse, blood and death animation sprites if (crispy->flipcorpses && (thing->flags & MF_FLIPPABLE) && !(thing->flags & MF_SHOOTABLE) && (thing->health & 1)) { flip = !flip; } // calculate edges of the shape // [crispy] fix sprite offsets for mirrored sprites tx -= flip ? spritewidth[lump] - spriteoffset[lump] : spriteoffset[lump]; x1 = (centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS; // off the right side? if (x1 > viewwidth) return; tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS) - 1; // off the left side if (x2 < 0) return; // [JN] killough 4/9/98: clip things which are out of view due to height gzt = interpz + spritetopoffset[lump]; if (interpz > viewz + FixedDiv(viewheight << FRACBITS, xscale) || gzt < viewz - FixedDiv((viewheight << FRACBITS)-viewheight, xscale)) { return; } // [JN] quickly reject sprites with bad x ranges if (x1 >= x2) { return; } // store information in a vissprite vis = R_NewVisSprite (); vis->translation = NULL; // [crispy] no color translation vis->mobjflags = thing->flags; vis->scale = xscale<gx = interpx; vis->gy = interpy; vis->gz = interpz; vis->gzt = gzt; // [JN] killough 3/27/98 vis->texturemid = gzt - viewz; vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; iscale = FixedDiv (FRACUNIT, xscale); if (flip) { vis->startfrac = spritewidth[lump]-1; vis->xiscale = -iscale; } else { vis->startfrac = 0; vis->xiscale = iscale; } if (vis->x1 > x1) vis->startfrac += vis->xiscale*(vis->x1-x1); vis->patch = lump; // get light level if (thing->flags & MF_SHADOW) { // shadow draw vis->colormap[0] = vis->colormap[1] = NULL; } else if (fixedcolormap) { // fixed map vis->colormap[0] = vis->colormap[1] = fixedcolormap; } else if (thing->frame & FF_FULLBRIGHT) { // full bright vis->colormap[0] = vis->colormap[1] = colormaps; } else { // diminished light index = xscale>>(LIGHTSCALESHIFT-detailshift+crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE-1; // [crispy] brightmaps for select sprites vis->colormap[0] = spritelights[index]; vis->colormap[1] = scalelight[LIGHTLEVELS-1][MAXLIGHTSCALE-1]; } vis->brightmap = R_BrightmapForSprite(thing->sprite); // [crispy] colored blood if (crispy->coloredblood && (thing->type == MT_BLOOD || thing->state - states == S_GIBS) && thing->target) { // [crispy] Thorn Things in Hacx bleed green blood if (gamemission == pack_hacx) { if (thing->target->type == MT_BABY) { vis->translation = cr[CR_RED2GREEN]; } } else { // [crispy] Barons of Hell and Hell Knights bleed green blood if (thing->target->type == MT_BRUISER || thing->target->type == MT_KNIGHT) { vis->translation = cr[CR_RED2GREEN]; } else // [crispy] Cacodemons bleed blue blood if (thing->target->type == MT_HEAD) { vis->translation = cr[CR_RED2BLUE]; } } } #ifdef CRISPY_TRUECOLOR // [crispy] translucent sprites if (thing->flags & MF_TRANSLUCENT) { vis->blendfunc = (thing->frame & FF_FULLBRIGHT) ? I_BlendAdd : I_BlendOver; } #endif } extern void P_LineLaser (mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope); byte *R_LaserspotColor (void) { if (crispy->crosshairtarget) { // [crispy] the projected crosshair code calls P_LineLaser() itself if (crispy->crosshair == CROSSHAIR_STATIC) { P_LineLaser(viewplayer->mo, viewangle, 16*64*FRACUNIT, PLAYER_SLOPE(viewplayer)); } if (linetarget) { return cr[CR_GRAY]; } } // [crispy] keep in sync with st_stuff.c:ST_WidgetColor(hudcolor_health) if (crispy->crosshairhealth) { const int health = viewplayer->health; // [crispy] Invulnerability powerup and God Mode cheat turn Health values gray if (viewplayer->cheats & CF_GODMODE || viewplayer->powers[pw_invulnerability]) return cr[CR_GRAY]; else if (health < 25) return cr[CR_RED]; else if (health < 50) return cr[CR_GOLD]; else if (health <= 100) return cr[CR_GREEN]; else return cr[CR_BLUE]; } return NULL; } // [crispy] generate a vissprite for the laser spot static void R_DrawLSprite (void) { fixed_t xscale; fixed_t tx, tz; vissprite_t* vis; static int lump; static patch_t* patch; if (weaponinfo[viewplayer->readyweapon].ammo == am_noammo || viewplayer->playerstate != PST_LIVE) return; if (lump != laserpatch[crispy->crosshairtype].l) { lump = laserpatch[crispy->crosshairtype].l; patch = W_CacheLumpNum(lump, PU_STATIC); } P_LineLaser(viewplayer->mo, viewangle, 16*64*FRACUNIT, PLAYER_SLOPE(viewplayer)); if (!laserspot->thinker.function.acv) return; tz = FixedMul(laserspot->x - viewx, viewcos) + FixedMul(laserspot->y - viewy, viewsin); if (tz < MINZ) return; xscale = FixedDiv(projection, tz); // [crispy] the original patch has 5x5 pixels, cap the projection at 20x20 xscale = (xscale > 4*FRACUNIT) ? 4*FRACUNIT : xscale; tx = -(FixedMul(laserspot->y - viewy, viewcos) - FixedMul(laserspot->x - viewx, viewsin)); if (abs(tx) > (tz<<2)) return; vis = R_NewVisSprite(); memset(vis, 0, sizeof(*vis)); // [crispy] set all fields to NULL, except ... vis->patch = lump - firstspritelump; // [crispy] not a sprite patch vis->colormap[0] = vis->colormap[1] = fixedcolormap ? fixedcolormap : colormaps; // [crispy] always full brightness vis->brightmap = dc_brightmap; vis->translation = R_LaserspotColor(); #ifdef CRISPY_TRUECOLOR vis->mobjflags |= MF_TRANSLUCENT; vis->blendfunc = I_BlendAdd; #endif vis->xiscale = FixedDiv (FRACUNIT, xscale); vis->texturemid = laserspot->z - viewz; vis->scale = xscale<width/2)<x1 = (centerxfrac + FixedMul(tx, xscale))>>FRACBITS; tx += SHORT(patch->width)<x2 = ((centerxfrac + FixedMul(tx, xscale))>>FRACBITS) - 1; if (vis->x1 < 0 || vis->x1 >= viewwidth || vis->x2 < 0 || vis->x2 >= viewwidth) return; R_DrawVisSprite (vis, vis->x1, vis->x2); } // // R_AddSprites // During BSP traversal, this adds sprites by sector. // void R_AddSprites (sector_t* sec) { mobj_t* thing; int lightnum; // BSP is traversed by subsector. // A sector might have been split into several // subsectors during BSP building. // Thus we check whether its already added. if (sec->validcount == validcount) return; // Well, now it will be done. sec->validcount = validcount; lightnum = (sec->lightlevel >> LIGHTSEGSHIFT)+(extralight * LIGHTBRIGHT); if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; // Handle all things in sector. for (thing = sec->thinglist ; thing ; thing = thing->snext) R_ProjectSprite (thing); } // // R_DrawPSprite // void R_DrawPSprite (pspdef_t* psp, psprnum_t psprnum) // [crispy] differentiate gun from flash sprites { fixed_t tx; int x1; int x2; spritedef_t* sprdef; spriteframe_t* sprframe; int lump; boolean flip; vissprite_t* vis; vissprite_t avis; // decide which patch to use #ifdef RANGECHECK if ( (unsigned)psp->state->sprite >= (unsigned int) numsprites) I_Error ("R_ProjectSprite: invalid sprite number %i ", psp->state->sprite); #endif sprdef = &sprites[psp->state->sprite]; // [crispy] the TNT1 sprite is not supposed to be rendered anyway if (!sprdef->numframes && psp->state->sprite == SPR_TNT1) { return; } #ifdef RANGECHECK if ( (psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) I_Error ("R_ProjectSprite: invalid sprite frame %i : %i ", psp->state->sprite, psp->state->frame); #endif sprframe = &sprdef->spriteframes[ psp->state->frame & FF_FRAMEMASK ]; lump = sprframe->lump[0]; flip = (boolean)sprframe->flip[0] ^ crispy->flipweapons; // calculate edges of the shape tx = psp->sx2-(ORIGWIDTH/2)*FRACUNIT; // [crispy] fix sprite offsets for mirrored sprites tx -= flip ? 2 * tx - spriteoffset[lump] + spritewidth[lump] : spriteoffset[lump]; x1 = (centerxfrac + FixedMul (tx,pspritescale) ) >>FRACBITS; // off the right side if (x1 > viewwidth) return; tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx, pspritescale) ) >>FRACBITS) - 1; // off the left side if (x2 < 0) return; // store information in a vissprite vis = &avis; vis->translation = NULL; // [crispy] no color translation vis->mobjflags = 0; // [crispy] weapons drawn 1 pixel too high when player is idle vis->texturemid = (BASEYCENTER<sy2+abs(psp->dy)-spritetopoffset[lump]); vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; vis->scale = pspritescale<xiscale = -pspriteiscale; vis->startfrac = spritewidth[lump]-1; } else { vis->xiscale = pspriteiscale; vis->startfrac = 0; } // [crispy] free look vis->texturemid += FixedMul(((centery - viewheight / 2) << FRACBITS), pspriteiscale) >> detailshift; if (vis->x1 > x1) vis->startfrac += vis->xiscale*(vis->x1-x1); vis->patch = lump; if (viewplayer->powers[pw_invisibility] > 4*32 || viewplayer->powers[pw_invisibility] & 8) { // shadow draw vis->colormap[0] = vis->colormap[1] = NULL; } else if (fixedcolormap) { // fixed color vis->colormap[0] = vis->colormap[1] = fixedcolormap; } else if (psp->state->frame & FF_FULLBRIGHT) { // full bright vis->colormap[0] = vis->colormap[1] = colormaps; } else { // local light vis->colormap[0] = spritelights[MAXLIGHTSCALE-1]; vis->colormap[1] = scalelight[LIGHTLEVELS-1][MAXLIGHTSCALE-1]; } vis->brightmap = R_BrightmapForState(psp->state - states); // [crispy] translucent gun flash sprites if (psprnum == ps_flash) { vis->mobjflags |= MF_TRANSLUCENT; #ifdef CRISPY_TRUECOLOR vis->blendfunc = I_BlendOver; // I_BlendAdd; #endif } R_DrawVisSprite (vis, vis->x1, vis->x2); } // // R_DrawPlayerSprites // void R_DrawPlayerSprites (void) { int i; int lightnum; pspdef_t* psp; // get light level lightnum = (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) +(extralight * LIGHTBRIGHT); if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; // clip to screen bounds mfloorclip = screenheightarray; mceilingclip = negonearray; if (crispy->crosshair == CROSSHAIR_PROJECTED) R_DrawLSprite(); // add all active psprites for (i=0, psp=viewplayer->psprites; istate) R_DrawPSprite (psp, i); // [crispy] pass gun or flash sprite } } // // R_SortVisSprites // #ifdef HAVE_QSORT // [crispy] use stdlib's qsort() function for sorting the vissprites[] array static inline int cmp_vissprites (const void *a, const void *b) { const vissprite_t *vsa = (const vissprite_t *) a; const vissprite_t *vsb = (const vissprite_t *) b; const int ret = vsa->scale - vsb->scale; return ret ? ret : vsa->next - vsb->next; } void R_SortVisSprites (void) { int count; vissprite_t *ds; count = vissprite_p - vissprites; if (!count) return; // [crispy] maintain a stable sort for deliberately overlaid sprites for (ds = vissprites; ds < vissprite_p; ds++) { ds->next = ds + 1; } qsort(vissprites, count, sizeof(*vissprites), cmp_vissprites); } #else vissprite_t vsprsortedhead; void R_SortVisSprites (void) { int i; int count; vissprite_t* ds; vissprite_t* best; vissprite_t unsorted; fixed_t bestscale; count = vissprite_p - vissprites; unsorted.next = unsorted.prev = &unsorted; if (!count) return; for (ds=vissprites ; dsnext = ds+1; ds->prev = ds-1; } vissprites[0].prev = &unsorted; unsorted.next = &vissprites[0]; (vissprite_p-1)->next = &unsorted; unsorted.prev = vissprite_p-1; // pull the vissprites out by scale vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; for (i=0 ; inext) { if (ds->scale < bestscale) { bestscale = ds->scale; best = ds; } } best->next->prev = best->prev; best->prev->next = best->next; best->next = &vsprsortedhead; best->prev = vsprsortedhead.prev; vsprsortedhead.prev->next = best; vsprsortedhead.prev = best; } } #endif // // R_DrawSprite // void R_DrawSprite (vissprite_t* spr) { drawseg_t* ds; int clipbot[MAXWIDTH]; // [crispy] 32-bit integer math int cliptop[MAXWIDTH]; // [crispy] 32-bit integer math int x; int r1; int r2; fixed_t scale; fixed_t lowscale; int silhouette; for (x = spr->x1 ; x<=spr->x2 ; x++) clipbot[x] = cliptop[x] = -2; // Scan drawsegs from end to start for obscuring segs. // The first drawseg that has a greater scale // is the clip seg. for (ds=ds_p-1 ; ds >= drawsegs ; ds--) { // determine if the drawseg obscures the sprite if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || (!ds->silhouette && !ds->maskedtexturecol) ) { // does not cover sprite continue; } r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; if (ds->scale1 > ds->scale2) { lowscale = ds->scale2; scale = ds->scale1; } else { lowscale = ds->scale1; scale = ds->scale2; } if (scale < spr->scale || ( lowscale < spr->scale && !R_PointOnSegSide (spr->gx, spr->gy, ds->curline) ) ) { // masked mid texture? if (ds->maskedtexturecol) R_RenderMaskedSegRange (ds, r1, r2); // seg is behind sprite continue; } // clip this piece of the sprite silhouette = ds->silhouette; if (spr->gz >= ds->bsilheight) silhouette &= ~SIL_BOTTOM; if (spr->gzt <= ds->tsilheight) silhouette &= ~SIL_TOP; if (silhouette == 1) { // bottom sil for (x=r1 ; x<=r2 ; x++) if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; } else if (silhouette == 2) { // top sil for (x=r1 ; x<=r2 ; x++) if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } else if (silhouette == 3) { // both for (x=r1 ; x<=r2 ; x++) { if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } } } // all clipping has been performed, so draw the sprite // check for unclipped columns for (x = spr->x1 ; x<=spr->x2 ; x++) { if (clipbot[x] == -2) clipbot[x] = viewheight; if (cliptop[x] == -2) cliptop[x] = -1; } mfloorclip = clipbot; mceilingclip = cliptop; R_DrawVisSprite (spr, spr->x1, spr->x2); } // // R_DrawMasked // void R_DrawMasked (void) { vissprite_t* spr; drawseg_t* ds; R_SortVisSprites (); if (vissprite_p > vissprites) { // draw all vissprites back to front #ifdef HAVE_QSORT for (spr = vissprites; spr < vissprite_p; spr++) #else for (spr = vsprsortedhead.next ; spr != &vsprsortedhead ; spr=spr->next) #endif { R_DrawSprite (spr); } } // render any remaining masked mid textures for (ds=ds_p-1 ; ds >= drawsegs ; ds--) if (ds->maskedtexturecol) R_RenderMaskedSegRange (ds, ds->x1, ds->x2); if (crispy->cleanscreenshot == 2) return; // draw the psprites on top of everything // but does not draw on side views if (!viewangleoffset) R_DrawPlayerSprites (); } crispy-doom-crispy-doom-5.6.4/src/doom/r_things.h000066400000000000000000000032161360717211000217510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Rendering of moving objects, sprites. // #ifndef __R_THINGS__ #define __R_THINGS__ #define MAXVISSPRITES 128 extern vissprite_t* vissprites; extern vissprite_t* vissprite_p; extern vissprite_t vsprsortedhead; // Constant arrays used for psprite clipping // and initializing clipping. extern int negonearray[MAXWIDTH]; // [crispy] 32-bit integer math extern int screenheightarray[MAXWIDTH]; // [crispy] 32-bit integer math // vars for R_DrawMaskedColumn extern int* mfloorclip; // [crispy] 32-bit integer math extern int* mceilingclip; // [crispy] 32-bit integer math extern fixed_t spryscale; extern int64_t sprtopscreen; // [crispy] WiggleFix extern fixed_t pspritescale; extern fixed_t pspriteiscale; void R_DrawMaskedColumn (column_t* column); void R_SortVisSprites (void); void R_AddSprites (sector_t* sec); void R_AddPSprites (void); void R_DrawSprites (void); void R_InitSprites(const char **namelist); void R_ClearSprites (void); void R_DrawMasked (void); void R_ClipVisSprite ( vissprite_t* vis, int xl, int xh ); #endif crispy-doom-crispy-doom-5.6.4/src/doom/s_musinfo.c000066400000000000000000000213771360717211000221410ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2005-2006 Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko // Copyright(C) 2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] support MUSINFO lump (dynamic music changing) // // [crispy] adapted from chocolate-doom/src/hexen/sc_man.c:18-470 // HEADER FILES ------------------------------------------------------------ #include #include #include "doomstat.h" #include "i_system.h" #include "m_misc.h" #include "r_defs.h" #include "s_sound.h" #include "w_wad.h" #include "z_zone.h" #include "s_musinfo.h" // MACROS ------------------------------------------------------------------ #define MAX_STRING_SIZE 64 #define ASCII_COMMENT (';') #define ASCII_QUOTE (34) #define LUMP_SCRIPT 1 #define FILE_ZONE_SCRIPT 2 // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void CheckOpen(void); static void OpenScript(char *name, int type); static void SC_OpenLump(char *name); static void SC_Close(void); static boolean SC_Compare(const char *text); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- static char *sc_String; static int sc_Line; static boolean sc_End; static boolean sc_Crossed; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static char ScriptName[16]; static char *ScriptBuffer; static char *ScriptPtr; static char *ScriptEndPtr; static char StringBuffer[MAX_STRING_SIZE]; static int ScriptLumpNum; static boolean ScriptOpen = false; static int ScriptSize; static boolean AlreadyGot = false; // CODE -------------------------------------------------------------------- //========================================================================== // // SC_OpenLump // // Loads a script (from the WAD files) and prepares it for parsing. // //========================================================================== static void SC_OpenLump(char *name) { OpenScript(name, LUMP_SCRIPT); } //========================================================================== // // OpenScript // //========================================================================== static void OpenScript(char *name, int type) { SC_Close(); if (type == LUMP_SCRIPT) { // Lump script ScriptLumpNum = W_GetNumForName(name); ScriptBuffer = (char *) W_CacheLumpNum(ScriptLumpNum, PU_STATIC); ScriptSize = W_LumpLength(ScriptLumpNum); M_StringCopy(ScriptName, name, sizeof(ScriptName)); } else if (type == FILE_ZONE_SCRIPT) { // File script - zone ScriptLumpNum = -1; ScriptSize = M_ReadFile(name, (byte **) & ScriptBuffer); M_ExtractFileBase(name, ScriptName); } ScriptPtr = ScriptBuffer; ScriptEndPtr = ScriptPtr + ScriptSize; sc_Line = 1; sc_End = false; ScriptOpen = true; sc_String = StringBuffer; AlreadyGot = false; } //========================================================================== // // SC_Close // //========================================================================== static void SC_Close(void) { if (ScriptOpen) { if (ScriptLumpNum >= 0) { W_ReleaseLumpNum(ScriptLumpNum); } else { Z_Free(ScriptBuffer); } ScriptOpen = false; } } //========================================================================== // // SC_GetString // //========================================================================== static boolean SC_GetString(void) { char *text; boolean 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 < ScriptEndPtr && *ScriptPtr <= 32) { 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_Compare // //========================================================================== static boolean SC_Compare(const char *text) { if (strcasecmp(text, sc_String) == 0) { return true; } return false; } //========================================================================== // // CheckOpen // //========================================================================== static void CheckOpen(void) { if (ScriptOpen == false) { I_Error("SC_ call before SC_Open()."); } } // [crispy] adapted from prboom-plus/src/s_advsound.c:54-159 musinfo_t musinfo = {0}; // // S_ParseMusInfo // Parses MUSINFO lump. // void S_ParseMusInfo (const char *mapid) { if (W_CheckNumForName("MUSINFO") != -1) { int num, lumpnum; int inMap = false; 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; // printf("S_ParseMusInfo: (%d) %s\n", num, sc_String); } else { fprintf(stderr, "S_ParseMusInfo: Unknown MUS lump %s\n", sc_String); } } } else { fprintf(stderr, "S_ParseMusInfo: Number not in range 1 to %d\n", MAX_MUS_ENTRIES - 1); } } } SC_Close(); } } void T_MusInfo (void) { if (musinfo.tics < 0 || !musinfo.mapthing) { return; } if (musinfo.tics > 0) { musinfo.tics--; } else { if (!musinfo.tics && musinfo.lastmapthing != musinfo.mapthing) { // [crispy] encode music lump number in mapthing health int arraypt = musinfo.mapthing->health - 1000; if (arraypt >= 0 && arraypt < MAX_MUS_ENTRIES) { int lumpnum = musinfo.items[arraypt]; if (lumpnum > 0 && lumpnum < numlumps) { S_ChangeMusInfoMusic(lumpnum, true); } } musinfo.tics = -1; } } } crispy-doom-crispy-doom-5.6.4/src/doom/s_musinfo.h000066400000000000000000000023251360717211000221360ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2005-2006 Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko // Copyright(C) 2017 Fabian Greffrath // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // [crispy] Support MUSINFO lump (dynamic music changing) // #ifndef __S_MUSINFO__ #define __S_MUSINFO__ #include "p_mobj.h" #define MAX_MUS_ENTRIES 65 // [crispy] 0 to 64 inclusive typedef struct musinfo_s { mobj_t *mapthing; mobj_t *lastmapthing; int tics; int current_item; int items[MAX_MUS_ENTRIES]; boolean from_savegame; } musinfo_t; extern musinfo_t musinfo; extern void S_ParseMusInfo (const char *mapid); extern void T_MusInfo (void); #endif crispy-doom-crispy-doom-5.6.4/src/doom/s_sound.c000066400000000000000000000637421360717211000216130ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // #include #include #include "i_sound.h" #include "i_system.h" #include "deh_str.h" #include "doomstat.h" #include "doomtype.h" #include "sounds.h" #include "s_sound.h" #include "s_musinfo.h" // [crispy] struct musinfo #include "m_misc.h" #include "m_random.h" #include "m_argv.h" #include "p_local.h" #include "w_wad.h" #include "z_zone.h" // when to clip out sounds // Does not fit the large outdoor areas. #define S_CLIPPING_DIST (1200 * FRACUNIT) // Distance tp origin when sounds should be maxed out. // This should relate to movement clipping resolution // (see BLOCKMAP handling). // In the source code release: (160*FRACUNIT). Changed back to the // Vanilla value of 200 (why was this changed?) #define S_CLOSE_DIST (200 * FRACUNIT) // The range over which sound attenuates #define S_ATTENUATOR ((S_CLIPPING_DIST - S_CLOSE_DIST) >> FRACBITS) // Stereo separation #define S_STEREO_SWING (96 * FRACUNIT) static int stereo_swing; #define NORM_PRIORITY 64 #define NORM_SEP 128 typedef struct { // sound information (if null, channel avail.) sfxinfo_t *sfxinfo; // origin of sound mobj_t *origin; // handle of the sound being played int handle; int pitch; } channel_t; // The set of channels available static channel_t *channels; static degenmobj_t *sobjs; // Maximum volume of a sound effect. // Internal default is max out of 0-15. int sfxVolume = 8; // Maximum volume of music. int musicVolume = 8; // Internal volume level, ranging from 0-127 static int snd_SfxVolume; // Whether songs are mus_paused static boolean mus_paused; // Music currently being played static musicinfo_t *mus_playing = NULL; // Number of channels to use int snd_channels = 8; // [crispy] add support for alternative music tracks for Final Doom's // TNT and Plutonia as introduced in DoomMetalVol5.wad typedef struct { const char *const from; const char *const to; } altmusic_t; static const altmusic_t altmusic_tnt[] = { {"runnin", "sadist"}, // MAP01 {"stalks", "burn"}, // MAP02 {"countd", "messag"}, // MAP03 {"betwee", "bells"}, // MAP04 {"doom", "more"}, // MAP05 {"the_da", "agony"}, // MAP06 {"shawn", "chaos"}, // MAP07 {"ddtblu", "beast"}, // MAP08 {"in_cit", "sadist"}, // MAP09 {"dead", "infini"}, // MAP10 {"stlks2", "kill"}, // MAP11 {"theda2", "ddtbl3"}, // MAP12 {"doom2", "bells"}, // MAP13 {"ddtbl2", "cold"}, // MAP14 {"runni2", "burn2"}, // MAP15 {"dead2", "blood"}, // MAP16 {"stlks3", "more"}, // MAP17 {"romero", "infini"}, // MAP18 {"shawn2", "countd"}, // MAP19 {"messag", "horizo"}, // MAP20 {"count2", "in_cit"}, // MAP21 {"ddtbl3", "aim"}, // MAP22 // {"ampie", "ampie"}, // MAP23 {"theda3", "betwee"}, // MAP24 {"adrian", "doom"}, // MAP25 {"messg2", "blood"}, // MAP26 {"romer2", "beast"}, // MAP27 {"tense", "aim"}, // MAP28 {"shawn3", "bells"}, // MAP29 {"openin", "beast"}, // MAP30 // {"evil", "evil"}, // MAP31 {"ultima", "in_cit"}, // MAP32 {NULL, NULL}, }; // Plutonia music is completely taken from Doom 1 and 2, but re-arranged. // That is, Plutonia's D_RUNNIN (for MAP01) is the renamed D_E1M2. So, // it makes sense to play the D_E1M2 replacement from DoomMetal in Plutonia. static const altmusic_t altmusic_plut[] = { {"runnin", "e1m2"}, // MAP01 {"stalks", "e1m3"}, // MAP02 {"countd", "e1m6"}, // MAP03 {"betwee", "e1m4"}, // MAP04 {"doom", "e1m9"}, // MAP05 {"the_da", "e1m8"}, // MAP06 {"shawn", "e2m1"}, // MAP07 {"ddtblu", "e2m2"}, // MAP08 {"in_cit", "e3m3"}, // MAP09 {"dead", "e1m7"}, // MAP10 {"stlks2", "bunny"}, // MAP11 {"theda2", "e3m8"}, // MAP12 {"doom2", "e3m2"}, // MAP13 {"ddtbl2", "e2m8"}, // MAP14 {"runni2", "e2m7"}, // MAP15 {"dead2", "e3m1"}, // MAP16 {"stlks3", "e1m1"}, // MAP17 {"romero", "e2m5"}, // MAP18 {"shawn2", "e1m5"}, // MAP19 // {"messag", "messag"}, // MAP20 // {"count2", "count2"}, // MAP21 (d_read_m has no instumental cover in Doom Metal) // {"ddtbl3", "ddtbl3"}, // MAP22 // {"ampie", "ampie"}, // MAP23 // {"theda3", "theda3"}, // MAP24 // {"adrian", "adrian"}, // MAP25 // {"messg2", "messg2"}, // MAP26 {"romer2", "e2m1"}, // MAP27 {"tense", "e2m2"}, // MAP28 {"shawn3", "e1m1"}, // MAP29 // {"openin", "openin"}, // MAP30 (d_victor has no instumental cover in Doom Metal) {"evil", "e3m4"}, // MAP31 {"ultima", "e2m8"}, // MAP32 {NULL, NULL}, }; static void S_RegisterAltMusic() { const altmusic_t *altmusic_fromto, *altmusic; if (gamemission == pack_tnt) { altmusic_fromto = altmusic_tnt; } else if (gamemission == pack_plut) { altmusic_fromto = altmusic_plut; } else { return; } // [crispy] chicken-out if only one lump is missing, something must be wrong for (altmusic = altmusic_fromto; altmusic->from; altmusic++) { char name[9]; M_snprintf(name, sizeof(name), "d_%s", altmusic->to); if (W_CheckNumForName(name) == -1) { return; } } for (altmusic = altmusic_fromto; altmusic->from; altmusic++) { DEH_AddStringReplacement(altmusic->from, altmusic->to); } } // // Initializes sound stuff, including volume // Sets channels, SFX and music volume, // allocates channel buffer, sets S_sfx lookup. // void S_Init(int sfxVolume, int musicVolume) { int i; if (gameversion == exe_doom_1_666) { if (logical_gamemission == doom) { I_SetOPLDriverVer(opl_doom1_1_666); } else { I_SetOPLDriverVer(opl_doom2_1_666); } } else { I_SetOPLDriverVer(opl_doom_1_9); } I_PrecacheSounds(S_sfx, NUMSFX); S_SetSfxVolume(sfxVolume); S_SetMusicVolume(musicVolume); // Allocating the internal channels for mixing // (the maximum numer of sounds rendered // simultaneously) within zone memory. // [crispy] variable number of sound channels channels = I_Realloc(NULL, snd_channels*sizeof(channel_t)); sobjs = I_Realloc(NULL, snd_channels*sizeof(degenmobj_t)); // Free all channels for use for (i=0 ; iname)); music->lumpnum = W_CheckNumForName(namebuf); } // [crispy] add support for alternative music tracks for Final Doom's // TNT and Plutonia as introduced in DoomMetalVol5.wad S_RegisterAltMusic(); // [crispy] handle stereo separation for mono-sfx and flipped levels S_UpdateStereoSeparation(); } void S_Shutdown(void) { I_ShutdownSound(); I_ShutdownMusic(); } static void S_StopChannel(int cnum) { int i; channel_t *c; c = &channels[cnum]; 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; } } // degrade usefulness of sound data c->sfxinfo->usefulness--; c->sfxinfo = NULL; c->origin = NULL; } } // // Per level startup code. // Kills playing sounds at start of level, // determines music if any, changes music. // static short prevmap = -1; void S_Start(void) { int cnum; int mnum; // kill all playing sounds at start of level // (trust me - a good idea) for (cnum=0 ; cnum 0) { mnum = sp_mnum; } } } } // [crispy] do not change music if not changing map (preserves IDMUS choice) { const short curmap = (gameepisode << 8) + gamemap; if (prevmap == curmap || (nodrawers && singletics)) return; prevmap = curmap; } // [crispy] reset musinfo data at the start of a new map memset(&musinfo, 0, sizeof(musinfo)); S_ChangeMusic(mnum, true); } void S_StopSound(mobj_t *origin) { int cnum; for (cnum=0 ; cnumx = origin->x; sobj->y = origin->y; sobj->z = origin->z; channels[cnum].origin = (mobj_t *) sobj; break; } } } } // // S_GetChannel : // If none available, return -1. Otherwise channel #. // static int S_GetChannel(mobj_t *origin, sfxinfo_t *sfxinfo) { // channel number to use int cnum; channel_t* c; // Find an open channel for (cnum=0 ; cnumpriority >= sfxinfo->priority) { break; } } if (cnum == snd_channels) { // FUCK! No lower priority. Sorry, Charlie. return -1; } else { // Otherwise, kick out lower priority. S_StopChannel(cnum); } } c = &channels[cnum]; // channel is decided to be cnum. c->sfxinfo = sfxinfo; c->origin = origin; return cnum; } // // Changes volume and stereo-separation variables // from the norm of a sound effect to be played. // If the sound is not audible, returns a 0. // Otherwise, modifies parameters and returns 1. // static int S_AdjustSoundParams(mobj_t *listener, mobj_t *source, int *vol, int *sep) { fixed_t approx_dist; fixed_t adx; fixed_t ady; angle_t angle; // [crispy] proper sound clipping in Doom 2 MAP08 and The Ultimate Doom E4M8 / Sigil E5M8 const boolean doom1map8 = (gamemap == 8 && ((gamemode != commercial && gameepisode < 4) || !crispy->soundfix)); // calculate the distance to sound origin // and clip it if necessary adx = abs(listener->x - source->x); ady = abs(listener->y - source->y); // From _GG1_ p.428. Appox. eucledian distance fast. approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); if (!doom1map8 && approx_dist > S_CLIPPING_DIST) { return 0; } // angle of source to listener angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y); if (angle > listener->angle) { angle = angle - listener->angle; } else { angle = angle + (0xffffffff - listener->angle); } angle >>= ANGLETOFINESHIFT; // stereo separation *sep = 128 - (FixedMul(stereo_swing, finesine[angle]) >> FRACBITS); // volume calculation if (approx_dist < S_CLOSE_DIST) { *vol = snd_SfxVolume; } else if (doom1map8) { if (approx_dist > S_CLIPPING_DIST) { approx_dist = S_CLIPPING_DIST; } *vol = 15+ ((snd_SfxVolume-15) *((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } else { // distance effect *vol = (snd_SfxVolume * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } return (*vol > 0); } // clamp supplied integer to the range 0 <= x <= 255. static int Clamp(int x) { if (x < 0) { return 0; } else if (x > 255) { return 255; } return x; } void S_StartSound(void *origin_p, int sfx_id) { sfxinfo_t *sfx; mobj_t *origin; int rc; int sep; int pitch; int cnum; int volume; origin = (mobj_t *) origin_p; volume = snd_SfxVolume; // [crispy] make non-fatal, consider zero volume if (sfx_id == sfx_None || !snd_SfxVolume || (nodrawers && singletics)) { return; } // check for bogus sound # if (sfx_id < 1 || sfx_id > NUMSFX) { I_Error("Bad sfx #: %d", sfx_id); } sfx = &S_sfx[sfx_id]; // Initialize sound parameters pitch = NORM_PITCH; if (sfx->link) { volume += sfx->volume; pitch = sfx->pitch; if (volume < 1) { return; } if (volume > snd_SfxVolume) { volume = snd_SfxVolume; } } // Check to see if it is audible, // and if not, modify the params if (origin && origin != players[consoleplayer].mo && origin != players[consoleplayer].so) // [crispy] weapon sound source { rc = S_AdjustSoundParams(players[consoleplayer].mo, origin, &volume, &sep); if (origin->x == players[consoleplayer].mo->x && origin->y == players[consoleplayer].mo->y) { sep = NORM_SEP; } if (!rc) { return; } } else { sep = NORM_SEP; } // hacks to vary the sfx pitches if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit) { pitch += 8 - (M_Random()&15); } else if (sfx_id != sfx_itemup && sfx_id != sfx_tink) { pitch += 16 - (M_Random()&31); } pitch = Clamp(pitch); // kill old sound if (!crispy->soundfull || origin || gamestate != GS_LEVEL) { S_StopSound(origin); } // try to find a channel cnum = S_GetChannel(origin, sfx); if (cnum < 0) { return; } // increase the usefulness if (sfx->usefulness++ < 0) { sfx->usefulness = 1; } if (sfx->lumpnum < 0) { sfx->lumpnum = I_GetSfxLumpNum(sfx); } channels[cnum].pitch = pitch; channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep, channels[cnum].pitch); } void S_StartSoundOnce (void *origin_p, int sfx_id) { int cnum; const sfxinfo_t *const sfx = &S_sfx[sfx_id]; for (cnum = 0; cnum < snd_channels; cnum++) { if (channels[cnum].sfxinfo == sfx && channels[cnum].origin == origin_p) { return; } } S_StartSound(origin_p, sfx_id); } // // Stop and resume music, during game PAUSE. // void S_PauseSound(void) { if (mus_playing && !mus_paused) { I_PauseSong(); mus_paused = true; } } void S_ResumeSound(void) { if (mus_playing && mus_paused) { I_ResumeSong(); mus_paused = false; } } // // Updates music & sounds // void S_UpdateSounds(mobj_t *listener) { int audible; int cnum; int volume; int sep; sfxinfo_t* sfx; channel_t* c; I_UpdateSound(); for (cnum=0; cnumsfxinfo; if (c->sfxinfo) { if (I_SoundIsPlaying(c->handle)) { // initialize parameters volume = snd_SfxVolume; sep = NORM_SEP; if (sfx->link) { 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 != c->origin && c->origin != players[consoleplayer].so) // [crispy] weapon sound source { audible = S_AdjustSoundParams(listener, c->origin, &volume, &sep); if (!audible) { S_StopChannel(cnum); } else { I_UpdateSoundParams(c->handle, volume, sep); } } } else { // if channel is allocated but sound has stopped, // free it S_StopChannel(cnum); } } } } void S_SetMusicVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("Attempt to set music volume at %d", volume); } // [crispy] [JN] Fixed bug when music was hearable with zero volume if (!musicVolume) { S_PauseSound(); } else if (!paused) { S_ResumeSound(); } I_SetMusicVolume(volume); } void S_SetSfxVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("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 = NULL; char namebuf[9]; void *handle; if (gamestate != GS_LEVEL) { prevmap = -1; } musinfo.current_item = -1; // [crispy] play no music if this is not the right map if (nodrawers && singletics) return; // [crispy] restart current music if IDMUS00 is entered if (looping == 2) { music = mus_playing; } // The Doom IWAD file has two versions of the intro music: d_intro // and d_introa. The latter is used for OPL playback. if (musicnum == mus_intro && (snd_musicdevice == SNDDEVICE_ADLIB || snd_musicdevice == SNDDEVICE_SB) && W_CheckNumForName("D_INTROA") >= 0) { const int intro = W_GetNumForName("D_INTRO"), introa = W_GetNumForName("D_INTROA"); // [crispy] if D_INTRO is from a PWAD, and D_INTROA is from a different WAD file, play the former if (W_IsIWADLump(lumpinfo[intro]) || (lumpinfo[intro]->wad_file == lumpinfo[introa]->wad_file)) { musicnum = mus_introa; } } // [crispy] prevent music number under- and overflows if (musicnum <= mus_None || (gamemode == commercial && musicnum < mus_runnin) || musicnum >= NUMMUSIC || (gamemode != commercial && musicnum >= mus_runnin) || S_music[musicnum].lumpnum == -1) { const unsigned int umusicnum = (unsigned int) musicnum; if (gamemode == commercial) { musicnum = mus_runnin + (umusicnum % (NUMMUSIC - mus_runnin)); } else { musicnum = mus_e1m1 + (umusicnum % (mus_e4m1 - mus_e1m1)); } } if (musicnum <= mus_None || musicnum >= NUMMUSIC) { I_Error("Bad music number %d", musicnum); } else { if (!music) // [crispy] restart current music if IDMUS00 is entered music = &S_music[musicnum]; } if (mus_playing == music) { if (looping != 2) // [crispy] restart current music if IDMUS00 is entered return; } // shutdown old music S_StopMusic(); // get lumpnum if neccessary if (!music->lumpnum) { M_snprintf(namebuf, sizeof(namebuf), "d_%s", DEH_String(music->name)); music->lumpnum = W_GetNumForName(namebuf); } music->data = W_CacheLumpNum(music->lumpnum, PU_STATIC); handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); music->handle = handle; I_PlaySong(handle, looping); // [crispy] log played music { char name[9]; M_snprintf(name, sizeof(name), "%s", lumpinfo[music->lumpnum]->name); fprintf(stderr, "S_ChangeMusic: %s (%s)\n", name, W_WadNameForLump(lumpinfo[music->lumpnum])); } mus_playing = music; // [crispy] musinfo.items[0] is reserved for the map's default music if (!musinfo.items[0]) { musinfo.items[0] = music->lumpnum; S_music[mus_musinfo].lumpnum = -1; } } // [crispy] adapted from prboom-plus/src/s_sound.c:552-590 void S_ChangeMusInfoMusic (int lumpnum, int looping) { musicinfo_t *music; // [crispy] restarting the map plays the original music prevmap = -1; // [crispy] play no music if this is not the right map if (nodrawers && singletics) { musinfo.current_item = lumpnum; return; } if (mus_playing && mus_playing->lumpnum == lumpnum) { return; } music = &S_music[mus_musinfo]; if (music->lumpnum == lumpnum) { return; } S_StopMusic(); music->lumpnum = lumpnum; music->data = W_CacheLumpNum(music->lumpnum, PU_STATIC); music->handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); I_PlaySong(music->handle, looping); // [crispy] log played music { char name[9]; M_snprintf(name, sizeof(name), "%s", lumpinfo[music->lumpnum]->name); fprintf(stderr, "S_ChangeMusInfoMusic: %s (%s)\n", name, W_WadNameForLump(lumpinfo[music->lumpnum])); } mus_playing = music; musinfo.current_item = lumpnum; } boolean S_MusicPlaying(void) { return I_MusicIsPlaying(); } void S_StopMusic(void) { if (mus_playing) { if (mus_paused) { I_ResumeSong(); } I_StopSong(); I_UnRegisterSong(mus_playing->handle); W_ReleaseLumpNum(mus_playing->lumpnum); mus_playing->data = NULL; mus_playing = NULL; } } // [crispy] variable number of sound channels void S_UpdateSndChannels (void) { int i; for (i = 0; i < snd_channels; i++) { if (channels[i].sfxinfo) { S_StopChannel(i); } } snd_channels <<= 1; if (snd_channels > 32) { snd_channels = 8; } channels = I_Realloc(channels, snd_channels * sizeof(channel_t)); sobjs = I_Realloc(sobjs, snd_channels * sizeof(degenmobj_t)); for (i = 0; i < snd_channels; i++) { channels[i].sfxinfo = 0; } } void S_UpdateStereoSeparation (void) { // [crispy] play all sound effects in mono if (crispy->soundmono) { stereo_swing = 0; } else if (crispy->fliplevels) { stereo_swing = -S_STEREO_SWING; } else { stereo_swing = S_STEREO_SWING; } } crispy-doom-crispy-doom-5.6.4/src/doom/s_sound.h000066400000000000000000000041111360717211000216010ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The not so system specific sound interface. // #ifndef __S_SOUND__ #define __S_SOUND__ #include "p_mobj.h" #include "sounds.h" // // 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); // Shut down sound void S_Shutdown(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); void S_StartSoundOnce(void *origin, int sound_id); // Stop sound for thing at void S_StopSound(mobj_t *origin); void S_UnlinkSound(mobj_t *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); // query if music is playing boolean S_MusicPlaying(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(mobj_t *listener); void S_SetMusicVolume(int volume); void S_SetSfxVolume(int volume); extern int snd_channels; void S_UpdateSndChannels (void); void S_UpdateStereoSeparation (void); #endif crispy-doom-crispy-doom-5.6.4/src/doom/sounds.c000066400000000000000000000133371360717211000214470ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Created by a sound utility. // Kept as a sample, DOOM2 sounds. // #include #include "doomtype.h" #include "sounds.h" // // Information about all the music // #define MUSIC(name) \ { name, 0, NULL, NULL } musicinfo_t S_music[] = { MUSIC(NULL), MUSIC("e1m1"), MUSIC("e1m2"), MUSIC("e1m3"), MUSIC("e1m4"), MUSIC("e1m5"), MUSIC("e1m6"), MUSIC("e1m7"), MUSIC("e1m8"), MUSIC("e1m9"), MUSIC("e2m1"), MUSIC("e2m2"), MUSIC("e2m3"), MUSIC("e2m4"), MUSIC("e2m5"), MUSIC("e2m6"), MUSIC("e2m7"), MUSIC("e2m8"), MUSIC("e2m9"), MUSIC("e3m1"), MUSIC("e3m2"), MUSIC("e3m3"), MUSIC("e3m4"), MUSIC("e3m5"), MUSIC("e3m6"), MUSIC("e3m7"), MUSIC("e3m8"), MUSIC("e3m9"), // [crispy] support dedicated music tracks for the 4th episode MUSIC("e4m1"), MUSIC("e4m2"), MUSIC("e4m3"), MUSIC("e4m4"), MUSIC("e4m5"), MUSIC("e4m6"), MUSIC("e4m7"), MUSIC("e4m8"), MUSIC("e4m9"), // [crispy] Sigil MUSIC("e5m1"), MUSIC("e5m2"), MUSIC("e5m3"), MUSIC("e5m4"), MUSIC("e5m5"), MUSIC("e5m6"), MUSIC("e5m7"), MUSIC("e5m8"), MUSIC("e5m9"), MUSIC("sigint"), MUSIC("inter"), MUSIC("intro"), MUSIC("bunny"), MUSIC("victor"), MUSIC("introa"), MUSIC("runnin"), MUSIC("stalks"), MUSIC("countd"), MUSIC("betwee"), MUSIC("doom"), MUSIC("the_da"), MUSIC("shawn"), MUSIC("ddtblu"), MUSIC("in_cit"), MUSIC("dead"), MUSIC("stlks2"), MUSIC("theda2"), MUSIC("doom2"), MUSIC("ddtbl2"), MUSIC("runni2"), MUSIC("dead2"), MUSIC("stlks3"), MUSIC("romero"), MUSIC("shawn2"), MUSIC("messag"), MUSIC("count2"), MUSIC("ddtbl3"), MUSIC("ampie"), MUSIC("theda3"), MUSIC("adrian"), MUSIC("messg2"), MUSIC("romer2"), MUSIC("tense"), MUSIC("shawn3"), MUSIC("openin"), MUSIC("evil"), MUSIC("ultima"), MUSIC("read_m"), MUSIC("dm2ttl"), MUSIC("dm2int"), MUSIC(NULL), MUSIC("musinfo") }; // // Information about all the sfx // #define SOUND(name, priority) \ { NULL, name, priority, NULL, -1, -1, 0, 0, -1, NULL } #define SOUND_LINK(name, priority, link_id, pitch, volume) \ { NULL, name, priority, &S_sfx[link_id], pitch, volume, 0, 0, -1, NULL } sfxinfo_t S_sfx[] = { // S_sfx[0] needs to be a dummy for odd reasons. SOUND("none", 0), SOUND("pistol", 64), SOUND("shotgn", 64), SOUND("sgcock", 64), SOUND("dshtgn", 64), SOUND("dbopn", 64), SOUND("dbcls", 64), SOUND("dbload", 64), SOUND("plasma", 64), SOUND("bfg", 64), SOUND("sawup", 64), SOUND("sawidl", 118), SOUND("sawful", 64), SOUND("sawhit", 64), SOUND("rlaunc", 64), SOUND("rxplod", 70), SOUND("firsht", 70), SOUND("firxpl", 70), SOUND("pstart", 100), SOUND("pstop", 100), SOUND("doropn", 100), SOUND("dorcls", 100), SOUND("stnmov", 119), SOUND("swtchn", 78), SOUND("swtchx", 78), SOUND("plpain", 96), SOUND("dmpain", 96), SOUND("popain", 96), SOUND("vipain", 96), SOUND("mnpain", 96), SOUND("pepain", 96), SOUND("slop", 78), SOUND("itemup", 78), SOUND("wpnup", 78), SOUND("oof", 96), SOUND("telept", 32), SOUND("posit1", 98), SOUND("posit2", 98), SOUND("posit3", 98), SOUND("bgsit1", 98), SOUND("bgsit2", 98), SOUND("sgtsit", 98), SOUND("cacsit", 98), SOUND("brssit", 94), SOUND("cybsit", 92), SOUND("spisit", 90), SOUND("bspsit", 90), SOUND("kntsit", 90), SOUND("vilsit", 90), SOUND("mansit", 90), SOUND("pesit", 90), SOUND("sklatk", 70), SOUND("sgtatk", 70), SOUND("skepch", 70), SOUND("vilatk", 70), SOUND("claw", 70), SOUND("skeswg", 70), SOUND("pldeth", 32), SOUND("pdiehi", 32), SOUND("podth1", 70), SOUND("podth2", 70), SOUND("podth3", 70), SOUND("bgdth1", 70), SOUND("bgdth2", 70), SOUND("sgtdth", 70), SOUND("cacdth", 70), SOUND("skldth", 70), SOUND("brsdth", 32), SOUND("cybdth", 32), SOUND("spidth", 32), SOUND("bspdth", 32), SOUND("vildth", 32), SOUND("kntdth", 32), SOUND("pedth", 32), SOUND("skedth", 32), SOUND("posact", 120), SOUND("bgact", 120), SOUND("dmact", 120), SOUND("bspact", 100), SOUND("bspwlk", 100), SOUND("vilact", 100), SOUND("noway", 78), SOUND("barexp", 60), SOUND("punch", 64), SOUND("hoof", 70), SOUND("metal", 70), SOUND_LINK("chgun", 64, sfx_pistol, 150, 0), SOUND("tink", 60), SOUND("bdopn", 100), SOUND("bdcls", 100), SOUND("itmbk", 100), SOUND("flame", 32), SOUND("flamst", 32), SOUND("getpow", 60), SOUND("bospit", 70), SOUND("boscub", 70), SOUND("bossit", 70), SOUND("bospn", 70), SOUND("bosdth", 70), SOUND("manatk", 70), SOUND("mandth", 70), SOUND("sssit", 70), SOUND("ssdth", 70), SOUND("keenpn", 70), SOUND("keendt", 70), SOUND("skeact", 70), SOUND("skesit", 70), SOUND("skeatk", 70), SOUND("radio", 60), // [crispy] additional BOOM and MBF states, sprites and code pointers SOUND("dgsit", 98), SOUND("dgatk", 70), SOUND("dgact", 120), SOUND("dgdth", 70), SOUND("dgpain", 96), // [crispy] play DSSECRET if available SOUND("secret", 60), }; crispy-doom-crispy-doom-5.6.4/src/doom/sounds.h000066400000000000000000000104421360717211000214460ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Created by the sound utility written by Dave Taylor. // Kept as a sample, DOOM2 sounds. Frozen. // #ifndef __SOUNDS__ #define __SOUNDS__ #include "i_sound.h" // 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, // [crispy] support dedicated music tracks for the 4th episode mus_e4m1, mus_e4m2, mus_e4m3, mus_e4m4, mus_e4m5, mus_e4m6, mus_e4m7, mus_e4m8, mus_e4m9, // [crispy] Sigil mus_e5m1, mus_e5m2, mus_e5m3, mus_e5m4, mus_e5m5, mus_e5m6, mus_e5m7, mus_e5m8, mus_e5m9, mus_sigint, 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, mus_musinfo } 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, // [crispy] additional BOOM and MBF states, sprites and code pointers sfx_dgsit, sfx_dgatk, sfx_dgact, sfx_dgdth, sfx_dgpain, // [crispy] play DSSECRET if available sfx_secret, NUMSFX } sfxenum_t; #endif crispy-doom-crispy-doom-5.6.4/src/doom/st_lib.c000066400000000000000000000123601360717211000214030ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The status bar widget code. // #include #include #include "deh_main.h" #include "doomdef.h" #include "z_zone.h" #include "v_video.h" #include "i_swap.h" #include "i_system.h" #include "w_wad.h" #include "st_stuff.h" #include "st_lib.h" #include "r_local.h" #include "v_trans.h" // [crispy] colored status bar widgets // in AM_map.c extern boolean automapactive; extern int screenblocks; // // Hack display negative frags. // Loads and store the stminus lump. // patch_t* sttminus; void STlib_init(void) { if (W_CheckNumForName(DEH_String("STTMINUS")) >= 0) sttminus = (patch_t *) W_CacheLumpName(DEH_String("STTMINUS"), PU_STATIC); else sttminus = NULL; } // ? void STlib_initNum ( st_number_t* n, int x, int y, patch_t** pl, int* num, boolean* on, int width ) { n->x = x; n->y = y; n->oldnum = 0; n->width = width; n->num = num; n->on = on; n->p = pl; } // // A fairly efficient way to draw a number // based on differences from the old number. // Note: worth the trouble? // void STlib_drawNum ( st_number_t* n, boolean refresh ) { int numdigits = n->width; int num = *n->num; int w = SHORT(n->p[0]->width); int h = SHORT(n->p[0]->height); int x = n->x; int neg; // [crispy] redraw only if necessary if (n->oldnum == num && !refresh) { return; } n->oldnum = *n->num; neg = num < 0; if (neg) { if (numdigits == 2 && num < -9) num = -9; else if (numdigits == 3 && num < -99) num = -99; num = -num; } // clear the area x = n->x - numdigits*w; if (n->y - ST_Y < 0) I_Error("drawNum: n->y - ST_Y < 0"); if (screenblocks < CRISPY_HUD || (automapactive && !crispy->automapoverlay)) V_CopyRect(x, n->y - ST_Y, st_backing_screen, w*numdigits, h, x, n->y); // if non-number, do not draw it if (num == 1994) return; x = n->x; // in the special case of 0, you draw 0 if (!num) V_DrawPatch(x - w, n->y, n->p[ 0 ]); // draw the new number while (num && numdigits--) { x -= w; V_DrawPatch(x, n->y, n->p[ num % 10 ]); num /= 10; } // draw a minus sign if necessary if (neg && sttminus) V_DrawPatch(x - 8, n->y, sttminus); } // void STlib_updateNum ( st_number_t* n, boolean refresh ) { if (*n->on) STlib_drawNum(n, refresh); } // void STlib_initPercent ( st_percent_t* p, int x, int y, patch_t** pl, int* num, boolean* on, patch_t* percent ) { STlib_initNum(&p->n, x, y, pl, num, on, 3); p->p = percent; } void STlib_updatePercent ( st_percent_t* per, int refresh ) { STlib_updateNum(&per->n, refresh); // [crispy] moved here if (crispy->coloredhud & COLOREDHUD_BAR) dp_translation = cr[CR_GRAY]; if (refresh && *per->n.on) V_DrawPatch(per->n.x, per->n.y, per->p); dp_translation = NULL; } void STlib_initMultIcon ( st_multicon_t* i, int x, int y, patch_t** il, int* inum, boolean* on ) { i->x = x; i->y = y; i->oldinum = -1; i->inum = inum; i->on = on; i->p = il; } void STlib_updateMultIcon ( st_multicon_t* mi, boolean refresh ) { int w; int h; int x; int y; if (*mi->on && (mi->oldinum != *mi->inum || refresh) && (*mi->inum!=-1)) { if (mi->oldinum != -1) { x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); w = SHORT(mi->p[mi->oldinum]->width); h = SHORT(mi->p[mi->oldinum]->height); if (y - ST_Y < 0) I_Error("updateMultIcon: y - ST_Y < 0"); if (screenblocks < CRISPY_HUD || (automapactive && !crispy->automapoverlay)) V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y); } V_DrawPatch(mi->x, mi->y, mi->p[*mi->inum]); mi->oldinum = *mi->inum; } } void STlib_initBinIcon ( st_binicon_t* b, int x, int y, patch_t* i, boolean* val, boolean* on ) { b->x = x; b->y = y; b->oldval = false; b->val = val; b->on = on; b->p = i; } void STlib_updateBinIcon ( st_binicon_t* bi, boolean refresh ) { int x; int y; int w; int h; if (*bi->on && (bi->oldval != *bi->val || refresh)) { x = bi->x - SHORT(bi->p->leftoffset); y = bi->y - SHORT(bi->p->topoffset); w = SHORT(bi->p->width); h = SHORT(bi->p->height); if (y - ST_Y < 0) I_Error("updateBinIcon: y - ST_Y < 0"); if (*bi->val) V_DrawPatch(bi->x, bi->y, bi->p); else if (screenblocks < CRISPY_HUD || (automapactive && !crispy->automapoverlay)) V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y); bi->oldval = *bi->val; } } crispy-doom-crispy-doom-5.6.4/src/doom/st_lib.h000066400000000000000000000063701360717211000214140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The status bar widget code. // #ifndef __STLIB__ #define __STLIB__ // We are referring to patches. #include "r_defs.h" // // Typedefs of widgets // // Number widget typedef struct { // upper right-hand corner // of the number (right-justified) int x; int y; // max # of digits in number int width; // last number value int oldnum; // pointer to current value int* num; // pointer to boolean stating // whether to update number boolean* on; // list of patches for 0-9 patch_t** p; // user data int data; } st_number_t; // Percent widget ("child" of number widget, // or, more precisely, contains a number widget.) typedef struct { // number information st_number_t n; // percent sign graphic patch_t* p; } st_percent_t; // Multiple Icon widget typedef struct { // center-justified location of icons int x; int y; // last icon number int oldinum; // pointer to current icon int* inum; // pointer to boolean stating // whether to update icon boolean* on; // list of icons patch_t** p; // user data int data; } st_multicon_t; // Binary Icon widget typedef struct { // center-justified location of icon int x; int y; // last icon value boolean oldval; // pointer to current icon status boolean* val; // pointer to boolean // stating whether to update icon boolean* on; patch_t* p; // icon int data; // user data } st_binicon_t; // // Widget creation, access, and update routines // // Initializes widget library. // More precisely, initialize STMINUS, // everything else is done somewhere else. // void STlib_init(void); // Number widget routines void STlib_initNum ( st_number_t* n, int x, int y, patch_t** pl, int* num, boolean* on, int width ); void STlib_updateNum ( st_number_t* n, boolean refresh ); // Percent widget routines void STlib_initPercent ( st_percent_t* p, int x, int y, patch_t** pl, int* num, boolean* on, patch_t* percent ); void STlib_updatePercent ( st_percent_t* per, int refresh ); // Multiple Icon widget routines void STlib_initMultIcon ( st_multicon_t* mi, int x, int y, patch_t** il, int* inum, boolean* on ); void STlib_updateMultIcon ( st_multicon_t* mi, boolean refresh ); // Binary Icon widget routines void STlib_initBinIcon ( st_binicon_t* b, int x, int y, patch_t* i, boolean* val, boolean* on ); void STlib_updateBinIcon ( st_binicon_t* bi, boolean refresh ); #endif crispy-doom-crispy-doom-5.6.4/src/doom/st_stuff.c000066400000000000000000001577671360717211000220110ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Status bar code. // Does the face/direction indicator animatin. // Does palette indicators as well (red pain/berserk, bright pickup) // #include #include "i_swap.h" // [crispy] SHORT() #include "i_system.h" #include "i_video.h" #include "z_zone.h" #include "m_argv.h" // [crispy] M_ParmExists() #include "m_misc.h" #include "m_random.h" #include "w_wad.h" #include "deh_main.h" #include "deh_misc.h" #include "doomdef.h" #include "doomkeys.h" #include "g_game.h" #include "st_stuff.h" #include "st_lib.h" #include "r_local.h" #include "p_local.h" #include "p_inter.h" #include "am_map.h" #include "m_cheat.h" #include "s_sound.h" // Needs access to LFB. #include "v_video.h" // State. #include "doomstat.h" // Data. #include "dstrings.h" #include "sounds.h" #include "v_trans.h" // [crispy] colored cheat messages extern int screenblocks; // [crispy] for the Crispy HUD extern boolean inhelpscreens; // [crispy] prevent palette changes // // STATUS BAR DATA // // Palette indices. // For damage/bonus red-/gold-shifts #define STARTREDPALS 1 #define STARTBONUSPALS 9 #define NUMREDPALS 8 #define NUMBONUSPALS 4 // Radiation suit, green shift. #define RADIATIONPAL 13 // N/256*100% probability // that the normal face state will change #define ST_FACEPROBABILITY 96 // For Responder #define ST_TOGGLECHAT KEY_ENTER // Location of status bar #define ST_X 0 #define ST_X2 104 #define ST_FX 143 #define ST_FY 169 // Should be set to patch width // for tall numbers later on #define ST_TALLNUMWIDTH (tallnum[0]->width) // Number of status faces. #define ST_NUMPAINFACES 5 #define ST_NUMSTRAIGHTFACES 3 #define ST_NUMTURNFACES 2 #define ST_NUMSPECIALFACES 3 #define ST_FACESTRIDE \ (ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES) #define ST_NUMEXTRAFACES 2 #define ST_NUMFACES \ (ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES) #define ST_TURNOFFSET (ST_NUMSTRAIGHTFACES) #define ST_OUCHOFFSET (ST_TURNOFFSET + ST_NUMTURNFACES) #define ST_EVILGRINOFFSET (ST_OUCHOFFSET + 1) #define ST_RAMPAGEOFFSET (ST_EVILGRINOFFSET + 1) #define ST_GODFACE (ST_NUMPAINFACES*ST_FACESTRIDE) #define ST_DEADFACE (ST_GODFACE+1) #define ST_FACESX 143 #define ST_FACESY 168 #define ST_EVILGRINCOUNT (2*TICRATE) #define ST_STRAIGHTFACECOUNT (TICRATE/2) #define ST_TURNCOUNT (1*TICRATE) #define ST_OUCHCOUNT (1*TICRATE) #define ST_RAMPAGEDELAY (2*TICRATE) #define ST_MUCHPAIN 20 // Location and size of statistics, // justified according to widget type. // Problem is, within which space? STbar? Screen? // Note: this could be read in by a lump. // Problem is, is the stuff rendered // into a buffer, // or into the frame buffer? // AMMO number pos. #define ST_AMMOWIDTH 3 #define ST_AMMOX 44 #define ST_AMMOY 171 // HEALTH number pos. #define ST_HEALTHWIDTH 3 #define ST_HEALTHX 90 #define ST_HEALTHY 171 // Weapon pos. #define ST_ARMSX 111 #define ST_ARMSY 172 #define ST_ARMSBGX 104 #define ST_ARMSBGY 168 #define ST_ARMSXSPACE 12 #define ST_ARMSYSPACE 10 // Frags pos. #define ST_FRAGSX 138 #define ST_FRAGSY 171 #define ST_FRAGSWIDTH 2 // ARMOR number pos. #define ST_ARMORWIDTH 3 #define ST_ARMORX 221 #define ST_ARMORY 171 // Key icon positions. #define ST_KEY0WIDTH 8 #define ST_KEY0HEIGHT 5 #define ST_KEY0X 239 #define ST_KEY0Y 171 #define ST_KEY1WIDTH ST_KEY0WIDTH #define ST_KEY1X 239 #define ST_KEY1Y 181 #define ST_KEY2WIDTH ST_KEY0WIDTH #define ST_KEY2X 239 #define ST_KEY2Y 191 // Ammunition counter. #define ST_AMMO0WIDTH 3 #define ST_AMMO0HEIGHT 6 #define ST_AMMO0X 288 #define ST_AMMO0Y 173 #define ST_AMMO1WIDTH ST_AMMO0WIDTH #define ST_AMMO1X 288 #define ST_AMMO1Y 179 #define ST_AMMO2WIDTH ST_AMMO0WIDTH #define ST_AMMO2X 288 #define ST_AMMO2Y 191 #define ST_AMMO3WIDTH ST_AMMO0WIDTH #define ST_AMMO3X 288 #define ST_AMMO3Y 185 // Indicate maximum ammunition. // Only needed because backpack exists. #define ST_MAXAMMO0WIDTH 3 #define ST_MAXAMMO0HEIGHT 5 #define ST_MAXAMMO0X 314 #define ST_MAXAMMO0Y 173 #define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH #define ST_MAXAMMO1X 314 #define ST_MAXAMMO1Y 179 #define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH #define ST_MAXAMMO2X 314 #define ST_MAXAMMO2Y 191 #define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH #define ST_MAXAMMO3X 314 #define ST_MAXAMMO3Y 185 // pistol #define ST_WEAPON0X 110 #define ST_WEAPON0Y 172 // shotgun #define ST_WEAPON1X 122 #define ST_WEAPON1Y 172 // chain gun #define ST_WEAPON2X 134 #define ST_WEAPON2Y 172 // missile launcher #define ST_WEAPON3X 110 #define ST_WEAPON3Y 181 // plasma gun #define ST_WEAPON4X 122 #define ST_WEAPON4Y 181 // bfg #define ST_WEAPON5X 134 #define ST_WEAPON5Y 181 // WPNS title #define ST_WPNSX 109 #define ST_WPNSY 191 // DETH title #define ST_DETHX 109 #define ST_DETHY 191 //Incoming messages window location //UNUSED // #define ST_MSGTEXTX (viewwindowx) // #define ST_MSGTEXTY (viewwindowy+viewheight-18) #define ST_MSGTEXTX 0 #define ST_MSGTEXTY 0 // Dimensions given in characters. #define ST_MSGWIDTH 52 // Or shall I say, in lines? #define ST_MSGHEIGHT 1 #define ST_OUTTEXTX 0 #define ST_OUTTEXTY 6 // Width, in characters again. #define ST_OUTWIDTH 52 // Height, in lines. #define ST_OUTHEIGHT 1 #define ST_MAPTITLEX \ (ORIGWIDTH - ST_MAPWIDTH * ST_CHATFONTWIDTH) #define ST_MAPTITLEY 0 #define ST_MAPHEIGHT 1 // graphics are drawn to a backing screen and blitted to the real screen pixel_t *st_backing_screen; // main player in game static player_t* plyr; // ST_Start() has just been called static boolean st_firsttime; // lump number for PLAYPAL static int lu_palette; // used for 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 boolean st_statusbaron; // [crispy] distinguish classic status bar with background and player face from Crispy HUD static boolean st_crispyhud; static boolean st_classicstatusbar; static boolean st_statusbarface; // whether status bar chat is active static boolean st_chat; // value of st_chat before message popped up static boolean st_oldchat; // whether chat window has the cursor on static boolean st_cursoron; // !deathmatch static boolean st_notdeathmatch; // !deathmatch && st_statusbaron static boolean st_armson; // !deathmatch static boolean st_fragson; // main bar left static patch_t* sbar; // main bar right, for doom 1.0 static patch_t* sbarr; // 0-9, tall numbers static patch_t* tallnum[10]; // tall % sign static patch_t* tallpercent; // 0-9, short, yellow (,different!) numbers static patch_t* shortnum[10]; // 3 key-cards, 3 skulls static patch_t* keys[NUMCARDS+3]; // [crispy] support combined card and skull keys // face status patches static patch_t* faces[ST_NUMFACES]; // face background static patch_t* faceback; // main bar right static patch_t* armsbg; // weapon ownership patches static patch_t* arms[6][2]; // ready-weapon widget static st_number_t w_ready; // in deathmatch only, summary of frags stats static st_number_t w_frags; // health widget static st_percent_t w_health; // arms background static st_binicon_t w_armsbg; // weapon ownership widgets static st_multicon_t w_arms[6]; // [crispy] show SSG availability in the Shotgun slot of the arms widget static int st_shotguns; // 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 boolean oldweaponsowned[NUMWEAPONS]; // count until face changes static int st_facecount = 0; // current face index, used by w_faces static int st_faceindex = 0; // holds key-type for each key box on bar static int keyboxes[3]; // [crispy] blinking key or skull in the status bar int st_keyorskull[3]; // a random number per tick static int st_randomnumber; cheatseq_t cheat_mus = CHEAT("idmus", 2); cheatseq_t cheat_god = CHEAT("iddqd", 0); cheatseq_t cheat_ammo = CHEAT("idkfa", 0); cheatseq_t cheat_ammonokey = CHEAT("idfa", 0); cheatseq_t cheat_noclip = CHEAT("idspispopd", 0); cheatseq_t cheat_commercial_noclip = CHEAT("idclip", 0); cheatseq_t cheat_powerup[8] = // [crispy] idbehold0 { CHEAT("idbeholdv", 0), CHEAT("idbeholds", 0), CHEAT("idbeholdi", 0), CHEAT("idbeholdr", 0), CHEAT("idbeholda", 0), CHEAT("idbeholdl", 0), CHEAT("idbehold", 0), CHEAT("idbehold0", 0), // [crispy] idbehold0 }; cheatseq_t cheat_choppers = CHEAT("idchoppers", 0); cheatseq_t cheat_clev = CHEAT("idclev", 2); cheatseq_t cheat_mypos = CHEAT("idmypos", 0); // [crispy] pseudo cheats to eat up the first digit typed after a cheat expecting two parameters cheatseq_t cheat_mus1 = CHEAT("idmus", 1); cheatseq_t cheat_clev1 = CHEAT("idclev", 1); // [crispy] new cheats cheatseq_t cheat_weapon = CHEAT("tntweap", 1); cheatseq_t cheat_massacre = CHEAT("tntem", 0); // [crispy] PrBoom+ cheatseq_t cheat_massacre2 = CHEAT("killem", 0); // [crispy] MBF cheatseq_t cheat_massacre3 = CHEAT("fhhall", 0); // [crispy] Doom95 cheatseq_t cheat_hom = CHEAT("tnthom", 0); cheatseq_t cheat_notarget = CHEAT("notarget", 0); // [crispy] PrBoom+ cheatseq_t cheat_notarget2 = CHEAT("fhshh", 0); // [crispy] Doom95 cheatseq_t cheat_spechits = CHEAT("spechits", 0); cheatseq_t cheat_nomomentum = CHEAT("nomomentum", 0); cheatseq_t cheat_showfps = CHEAT("showfps", 0); cheatseq_t cheat_showfps2 = CHEAT("idrate", 0); // [crispy] PrBoom+ cheatseq_t cheat_goobers = CHEAT("goobers", 0); cheatseq_t cheat_version = CHEAT("version", 0); // [crispy] Russian Doom cheatseq_t cheat_skill = CHEAT("skill", 0); static char msg[ST_MSGWIDTH]; // [crispy] restrict cheat usage static inline int cht_CheckCheatSP (cheatseq_t *cht, char key) { if (!cht_CheckCheat(cht, key)) { return false; } else if (!crispy->singleplayer) { plyr->message = "Cheater!"; return false; } return true; } // // STATUS BAR CODE // void ST_Stop(void); void ST_refreshBackground(boolean force) { if (st_classicstatusbar || force) { V_UseBuffer(st_backing_screen); V_DrawPatch(ST_X, 0, sbar); // draw right side of bar if needed (Doom 1.0) if (sbarr) V_DrawPatch(ST_ARMSBGX, 0, sbarr); // [crispy] back up arms widget background if (!deathmatch) V_DrawPatch(ST_ARMSBGX, 0, armsbg); if (netgame) V_DrawPatch(ST_FX, 0, faceback); V_RestoreBuffer(); if (!force) V_CopyRect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y); } } // [crispy] adapted from boom202s/M_CHEAT.C:467-498 static int ST_cheat_massacre() { int killcount = 0; thinker_t *th; extern int numbraintargets; extern void A_PainDie(mobj_t *); for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) { mobj_t *mo = (mobj_t *)th; if (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL) { if (mo->health > 0) { P_DamageMobj(mo, NULL, NULL, 10000); killcount++; } if (mo->type == MT_PAIN) { A_PainDie(mo); P_SetMobjState(mo, S_PAIN_DIE6); } } } } // [crispy] disable brain spitters numbraintargets = -1; return killcount; } // [crispy] trigger all special lines available on the map static int ST_cheat_spechits() { int i, speciallines = 0; boolean origcards[NUMCARDS]; line_t dummy; // [crispy] temporarily give all keys for (i = 0; i < NUMCARDS; i++) { origcards[i] = plyr->cards[i]; plyr->cards[i] = true; } for (i = 0; i < numlines; i++) { if (lines[i].special) { // [crispy] do not trigger level exit switches/lines or teleporters if (lines[i].special == 11 || lines[i].special == 51 || lines[i].special == 52 || lines[i].special == 124 || lines[i].special == 39 || lines[i].special == 97) { continue; } // [crispy] special without tag --> DR linedef type // do not change door direction if it is already moving if (lines[i].tag == 0 && lines[i].sidenum[1] != NO_INDEX && sides[lines[i].sidenum[1]].sector->specialdata) { continue; } P_CrossSpecialLine(i, 0, plyr->mo); P_ShootSpecialLine(plyr->mo, &lines[i]); P_UseSpecialLine(plyr->mo, &lines[i], 0); speciallines++; } } for (i = 0; i < NUMCARDS; i++) { plyr->cards[i] = origcards[i]; } // [crispy] trigger tag 666/667 events dummy.tag = 666; if (gamemode == commercial) { if (gamemap == 7 || // [crispy] Master Levels in PC slot 7 (gamemission == pack_master && (gamemap == 14 || gamemap == 15 || gamemap == 16))) { // Mancubi speciallines += EV_DoFloor(&dummy, lowerFloorToLowest); // Arachnotrons dummy.tag = 667; speciallines += EV_DoFloor(&dummy, raiseToTexture); dummy.tag = 666; } } else { if (gameepisode == 1) // Barons of Hell speciallines += EV_DoFloor(&dummy, lowerFloorToLowest); else if (gameepisode == 4) { if (gamemap == 6) // Cyberdemons speciallines += EV_DoDoor(&dummy, vld_blazeOpen); else if (gamemap == 8) // Spider Masterminds speciallines += EV_DoFloor(&dummy, lowerFloorToLowest); } } // Keens (no matter which level they are on) // this call will be ignored if the tagged sector is already moving // so actions triggered in the condition above will have precedence speciallines += EV_DoDoor(&dummy, vld_open); return (speciallines); } // [crispy] only give available weapons static boolean WeaponAvailable (int w) { if (w < 0 || w >= NUMWEAPONS) return false; if (w == wp_supershotgun && !crispy->havessg) return false; if ((w == wp_bfg || w == wp_plasma) && gamemode == shareware) return false; return true; } // [crispy] give or take backpack static void GiveBackpack (boolean give) { int i; if (give && !plyr->backpack) { for (i = 0; i < NUMAMMO; i++) { plyr->maxammo[i] *= 2; } plyr->backpack = true; } else if (!give && plyr->backpack) { for (i = 0; i < NUMAMMO; i++) { plyr->maxammo[i] /= 2; } plyr->backpack = false; } } // Respond to keyboard input events, // intercept cheats. boolean ST_Responder (event_t* ev) { int i; // Filter automap on/off. if (ev->type == ev_keyup && ((ev->data1 & 0xffff0000) == AM_MSGHEADER)) { switch(ev->data1) { case AM_MSGENTERED: st_gamestate = AutomapState; st_firsttime = true; break; case AM_MSGEXITED: // fprintf(stderr, "AM exited\n"); st_gamestate = FirstPersonState; break; } } // if a user keypress... else if (ev->type == ev_keydown) { if (!netgame && gameskill != sk_nightmare) { // 'dqd' cheat for toggleable god mode if (cht_CheckCheatSP(&cheat_god, ev->data2)) { // [crispy] dead players are first respawned at the current position mapthing_t mt = {0}; if (plyr->playerstate == PST_DEAD) { signed int an; extern void P_SpawnPlayer (mapthing_t* mthing); mt.x = plyr->mo->x >> FRACBITS; mt.y = plyr->mo->y >> FRACBITS; mt.angle = (plyr->mo->angle + ANG45/2)*(uint64_t)45/ANG45; mt.type = consoleplayer + 1; P_SpawnPlayer(&mt); // [crispy] 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); } plyr->cheats ^= CF_GODMODE; if (plyr->cheats & CF_GODMODE) { if (plyr->mo) plyr->mo->health = 100; plyr->health = deh_god_mode_health; plyr->message = DEH_String(STSTR_DQDON); } else plyr->message = DEH_String(STSTR_DQDOFF); // [crispy] eat key press when respawning if (mt.type) return true; } // 'fa' cheat for killer fucking arsenal else if (cht_CheckCheatSP(&cheat_ammonokey, ev->data2)) { plyr->armorpoints = deh_idfa_armor; plyr->armortype = deh_idfa_armor_class; // [crispy] give backpack GiveBackpack(true); for (i=0;iweaponowned[i] = true; for (i=0;iammo[i] = plyr->maxammo[i]; // [crispy] trigger evil grin now plyr->bonuscount += 2; plyr->message = DEH_String(STSTR_FAADDED); } // 'kfa' cheat for key full ammo else if (cht_CheckCheatSP(&cheat_ammo, ev->data2)) { plyr->armorpoints = deh_idkfa_armor; plyr->armortype = deh_idkfa_armor_class; // [crispy] give backpack GiveBackpack(true); for (i=0;iweaponowned[i] = true; for (i=0;iammo[i] = plyr->maxammo[i]; for (i=0;icards[i] = true; // [crispy] trigger evil grin now plyr->bonuscount += 2; plyr->message = DEH_String(STSTR_KFAADDED); } // 'mus' cheat for changing music else if (cht_CheckCheat(&cheat_mus, ev->data2)) { char buf[3]; int musnum; plyr->message = DEH_String(STSTR_MUS); cht_GetParam(&cheat_mus, buf); // Note: The original v1.9 had a bug that tried to play back // the Doom II music regardless of gamemode. This was fixed // in the Ultimate Doom executable so that it would work for // the Doom 1 music as well. // [crispy] restart current music if IDMUS00 is entered if (buf[0] == '0' && buf[1] == '0') { S_ChangeMusic(0, 2); // [crispy] eat key press, i.e. don't change weapon upon music change return true; } else // [JN] Fixed: using a proper IDMUS selection for shareware // and registered game versions. if (gamemode == commercial /* || gameversion < exe_ultimate */ ) { musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1; /* if (((buf[0]-'0')*10 + buf[1]-'0') > 35 && gameversion >= exe_doom_1_8) */ // [crispy] prevent crash with IDMUS00 if (musnum < mus_runnin || musnum >= NUMMUSIC) plyr->message = DEH_String(STSTR_NOMUS); else { S_ChangeMusic(musnum, 1); // [crispy] eat key press, i.e. don't change weapon upon music change return true; } } else { musnum = mus_e1m1 + (buf[0]-'1')*9 + (buf[1]-'1'); /* if (((buf[0]-'1')*9 + buf[1]-'1') > 31) */ // [crispy] prevent crash with IDMUS0x or IDMUSx0 if (musnum < mus_e1m1 || musnum >= mus_runnin || // [crispy] support dedicated music tracks for the 4th episode S_music[musnum].lumpnum == -1) plyr->message = DEH_String(STSTR_NOMUS); else { S_ChangeMusic(musnum, 1); // [crispy] eat key press, i.e. don't change weapon upon music change return true; } } } // [crispy] eat up the first digit typed after a cheat expecting two parameters else if (cht_CheckCheat(&cheat_mus1, ev->data2)) { char buf[2]; cht_GetParam(&cheat_mus1, buf); return isdigit(buf[0]); } // [crispy] allow both idspispopd and idclip cheats in all gamemissions else if ( ( /* logical_gamemission == doom && */ cht_CheckCheatSP(&cheat_noclip, ev->data2)) || ( /* logical_gamemission != doom && */ cht_CheckCheatSP(&cheat_commercial_noclip,ev->data2))) { // Noclip cheat. // For Doom 1, use the idspipsopd cheat; for all others, use // idclip plyr->cheats ^= CF_NOCLIP; if (plyr->cheats & CF_NOCLIP) plyr->message = DEH_String(STSTR_NCON); else plyr->message = DEH_String(STSTR_NCOFF); } // 'behold?' power-up cheats for (i=0;i<6;i++) { if (i < 4 ? cht_CheckCheatSP(&cheat_powerup[i], ev->data2) : cht_CheckCheat(&cheat_powerup[i], ev->data2)) { if (!plyr->powers[i]) P_GivePower( plyr, i); else if (i!=pw_strength && i!=pw_allmap) // [crispy] disable full Automap plyr->powers[i] = 1; else plyr->powers[i] = 0; plyr->message = DEH_String(STSTR_BEHOLDX); } } // [crispy] idbehold0 if (cht_CheckCheatSP(&cheat_powerup[7], ev->data2)) { memset(plyr->powers, 0, sizeof(plyr->powers)); plyr->mo->flags &= ~MF_SHADOW; // [crispy] cancel invisibility plyr->message = DEH_String(STSTR_BEHOLDX); } // 'behold' power-up menu if (cht_CheckCheat(&cheat_powerup[6], ev->data2)) { plyr->message = DEH_String(STSTR_BEHOLD); } // 'choppers' invulnerability & chainsaw else if (cht_CheckCheatSP(&cheat_choppers, ev->data2)) { plyr->weaponowned[wp_chainsaw] = true; plyr->powers[pw_invulnerability] = true; plyr->message = DEH_String(STSTR_CHOPPERS); } // 'mypos' for player position else if (cht_CheckCheat(&cheat_mypos, ev->data2)) { /* static char buf[ST_MSGWIDTH]; M_snprintf(buf, sizeof(buf), "ang=0x%x;x,y=(0x%x,0x%x)", players[consoleplayer].mo->angle, players[consoleplayer].mo->x, players[consoleplayer].mo->y); plyr->message = buf; */ // [crispy] extra high precision IDMYPOS variant, updates for 10 seconds plyr->powers[pw_mapcoords] = 10*TICRATE; } // [crispy] now follow "critical" Crispy Doom specific cheats // [crispy] implement Boom's "tntem" cheat else if (cht_CheckCheatSP(&cheat_massacre, ev->data2) || cht_CheckCheatSP(&cheat_massacre2, ev->data2) || cht_CheckCheatSP(&cheat_massacre3, ev->data2)) { int killcount = ST_cheat_massacre(); const char *const monster = (gameversion == exe_chex) ? "Flemoid" : "Monster"; const char *const killed = (gameversion == exe_chex) ? "returned" : "killed"; M_snprintf(msg, sizeof(msg), "%s%d %s%s%s %s", crstr[CR_GOLD], killcount, crstr[CR_NONE], monster, (killcount == 1) ? "" : "s", killed); plyr->message = msg; } // [crispy] implement Crispy Doom's "spechits" cheat else if (cht_CheckCheatSP(&cheat_spechits, ev->data2)) { int triggeredlines = ST_cheat_spechits(); M_snprintf(msg, sizeof(msg), "%s%d %sSpecial Line%s Triggered", crstr[CR_GOLD], triggeredlines, crstr[CR_NONE], (triggeredlines == 1) ? "" : "s"); plyr->message = msg; } // [crispy] implement PrBoom+'s "notarget" cheat else if (cht_CheckCheatSP(&cheat_notarget, ev->data2) || cht_CheckCheatSP(&cheat_notarget2, ev->data2)) { plyr->cheats ^= CF_NOTARGET; if (plyr->cheats & CF_NOTARGET) { int i; thinker_t *th; // [crispy] let mobjs forget their target and tracer for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) { mobj_t *const mo = (mobj_t *)th; if (mo->target && mo->target->player) { mo->target = NULL; } if (mo->tracer && mo->tracer->player) { mo->tracer = NULL; } } } // [crispy] let sectors forget their soundtarget for (i = 0; i < numsectors; i++) { sector_t *const sector = §ors[i]; sector->soundtarget = NULL; } } M_snprintf(msg, sizeof(msg), "Notarget Mode %s%s", crstr[CR_GREEN], (plyr->cheats & CF_NOTARGET) ? "ON" : "OFF"); plyr->message = msg; } // [crispy] implement "nomomentum" cheat, ne debug aid -- pretty useless, though else if (cht_CheckCheatSP(&cheat_nomomentum, ev->data2)) { plyr->cheats ^= CF_NOMOMENTUM; M_snprintf(msg, sizeof(msg), "Nomomentum Mode %s%s", crstr[CR_GREEN], (plyr->cheats & CF_NOMOMENTUM) ? "ON" : "OFF"); plyr->message = msg; } // [crispy] implement Crispy Doom's "goobers" cheat, ne easter egg else if (cht_CheckCheatSP(&cheat_goobers, ev->data2)) { extern void EV_DoGoobers (void); EV_DoGoobers(); M_snprintf(msg, sizeof(msg), "Get Psyched!"); plyr->message = msg; } // [crispy] implement Boom's "tntweap?" weapon cheats else if (cht_CheckCheatSP(&cheat_weapon, ev->data2)) { char buf[2]; int w; cht_GetParam(&cheat_weapon, buf); w = *buf - '1'; // [crispy] TNTWEAP0 takes away all weapons and ammo except for the pistol and 50 bullets if (w == -1) { GiveBackpack(false); plyr->powers[pw_strength] = 0; for (i = 0; i < NUMWEAPONS; i++) { oldweaponsowned[i] = plyr->weaponowned[i] = false; } oldweaponsowned[wp_fist] = plyr->weaponowned[wp_fist] = true; oldweaponsowned[wp_pistol] = plyr->weaponowned[wp_pistol] = true; for (i = 0; i < NUMAMMO; i++) { plyr->ammo[i] = 0; } plyr->ammo[am_clip] = deh_initial_bullets; if (plyr->readyweapon > wp_pistol) { plyr->pendingweapon = wp_pistol; } plyr->message = "All weapons removed!"; return true; } // [crispy] only give available weapons if (!WeaponAvailable(w)) return false; // make '1' apply beserker strength toggle if (w == wp_fist) { if (!plyr->powers[pw_strength]) { P_GivePower(plyr, pw_strength); S_StartSound(NULL, sfx_getpow); plyr->message = DEH_String(GOTBERSERK); } else { plyr->powers[pw_strength] = 0; plyr->message = DEH_String(STSTR_BEHOLDX); } } else { if (!plyr->weaponowned[w]) { extern boolean P_GiveWeapon (player_t* player, weapontype_t weapon, boolean dropped); extern const char *const WeaponPickupMessages[NUMWEAPONS]; P_GiveWeapon(plyr, w, false); S_StartSound(NULL, sfx_wpnup); if (w > 1) { plyr->message = DEH_String(WeaponPickupMessages[w]); } // [crispy] trigger evil grin now plyr->bonuscount += 2; } else { // [crispy] no reason for evil grin oldweaponsowned[w] = plyr->weaponowned[w] = false; // [crispy] removed current weapon, select another one if (w == plyr->readyweapon) { extern boolean P_CheckAmmo (player_t* player); P_CheckAmmo(plyr); } } } if (!plyr->message) { M_snprintf(msg, sizeof(msg), "Weapon %s%d%s %s", crstr[CR_GOLD], w + 1, crstr[CR_NONE], plyr->weaponowned[w] ? "added" : "removed"); plyr->message = msg; } } } // [crispy] now follow "harmless" Crispy Doom specific cheats // [crispy] implement Crispy Doom's "showfps" cheat, ne debug aid if (cht_CheckCheat(&cheat_showfps, ev->data2) || cht_CheckCheat(&cheat_showfps2, ev->data2)) { plyr->powers[pw_showfps] ^= 1; } // [crispy] implement Boom's "tnthom" cheat else if (cht_CheckCheat(&cheat_hom, ev->data2)) { crispy->flashinghom = !crispy->flashinghom; M_snprintf(msg, sizeof(msg), "HOM Detection %s%s", crstr[CR_GREEN], (crispy->flashinghom) ? "ON" : "OFF"); plyr->message = msg; } // [crispy] Show engine version, build date and SDL version else if (cht_CheckCheat(&cheat_version, ev->data2)) { #ifndef BUILD_DATE #define BUILD_DATE __DATE__ #endif M_snprintf(msg, sizeof(msg), "%s (%s) x%ld SDL%s", PACKAGE_STRING, BUILD_DATE, (long) sizeof(void *) * CHAR_BIT, crispy->sdlversion); #undef BUILD_DATE plyr->message = msg; fprintf(stderr, "%s\n", msg); } // [crispy] Show skill level else if (cht_CheckCheat(&cheat_skill, ev->data2)) { extern const char *skilltable[]; M_snprintf(msg, sizeof(msg), "Skill: %s", skilltable[BETWEEN(0,5,(int) gameskill+1)]); plyr->message = msg; } // 'clev' change-level cheat if (!netgame && cht_CheckCheat(&cheat_clev, ev->data2) && !menuactive) // [crispy] prevent only half the screen being updated { char buf[3]; int epsd; int map; cht_GetParam(&cheat_clev, buf); if (gamemode == commercial) { if (gamemission == pack_nerve) epsd = 2; else epsd = 0; map = (buf[0] - '0')*10 + buf[1] - '0'; } else { epsd = buf[0] - '0'; map = buf[1] - '0'; // Chex.exe always warps to episode 1. if (gameversion == exe_chex) { if (epsd > 1) { epsd = 1; } if (map > 5) { map = 5; } } } // [crispy] only fix episode/map if it doesn't exist if (P_GetNumForMap(epsd, map, false) < 0) { // Catch invalid maps. if (gamemode != commercial) { // [crispy] allow IDCLEV0x to work in Doom 1 if (epsd == 0) { epsd = gameepisode; } if (epsd < 1) { return false; } if (epsd > 4) { // [crispy] Sigil if (!(crispy->haved1e5 && epsd == 5)) return false; } if (epsd == 4 && gameversion < exe_ultimate) { return false; } // [crispy] IDCLEV00 restarts current map if ((map == 0) && (buf[0] - '0' == 0)) { map = gamemap; } // [crispy] support E1M10 "Sewers" if ((map == 0 || map > 9) && crispy->havee1m10 && epsd == 1) { map = 10; } if (map < 1) { return false; } if (map > 9) { // [crispy] support E1M10 "Sewers" if (!(crispy->havee1m10 && epsd == 1 && map == 10)) return false; } } else { // [crispy] IDCLEV00 restarts current map if ((map == 0) && (buf[0] - '0' == 0)) { map = gamemap; } if (map < 1) { return false; } if (map > 40) { return false; } if (map > 9 && gamemission == pack_nerve) { return false; } if (map > 21 && gamemission == pack_master) { return false; } } } // [crispy] prevent idclev to nonexistent levels exiting the game if (P_GetNumForMap(epsd, map, false) >= 0) { // So be it. plyr->message = DEH_String(STSTR_CLEV); // [crisp] allow IDCLEV during demo playback and warp to the requested map if (demoplayback) { if (map > gamemap) { crispy->demowarp = map; nodrawers = true; singletics = true; return true; } else { return false; } } else G_DeferedInitNew(gameskill, epsd, map); // [crispy] eat key press, i.e. don't change weapon upon level change return true; } } // [crispy] eat up the first digit typed after a cheat expecting two parameters else if (!netgame && cht_CheckCheat(&cheat_clev1, ev->data2) && !menuactive) { char buf[2]; cht_GetParam(&cheat_clev1, buf); return isdigit(buf[0]); } } return false; } int ST_calcPainOffset(void) { int health; static int lastcalc; static int oldhealth = -1; health = plyr->health > 100 ? 100 : plyr->health; if (health != oldhealth) { lastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101); oldhealth = health; } return lastcalc; } // // This is a not-very-pretty routine which handles // the face states and their timing. // the precedence of expressions is: // dead > evil grin > turned head > straight ahead // // [crispy] fix status bar face hysteresis static int faceindex; void ST_updateFaceWidget(void) { int i; angle_t badguyangle; angle_t diffang; static int lastattackdown = -1; static int priority = 0; boolean doevilgrin; // [crispy] fix status bar face hysteresis int painoffset; // [crispy] no evil grin or rampage face in god mode const boolean invul = (plyr->cheats & CF_GODMODE) || plyr->powers[pw_invulnerability]; painoffset = ST_calcPainOffset(); if (priority < 10) { // dead // [crispy] negative player health if (plyr->health <= 0) { priority = 9; painoffset = 0; 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]; } } // [crispy] no evil grin in god mode if (doevilgrin && !invul) { // evil grin if just picked up weapon priority = 8; st_facecount = ST_EVILGRINCOUNT; faceindex = ST_EVILGRINOFFSET; } } } if (priority < 8) { if (plyr->damagecount && plyr->attacker && plyr->attacker != plyr->mo) { // being attacked priority = 7; // [crispy] show "Ouch Face" as intended if (st_oldhealth - plyr->health > ST_MUCHPAIN) { // [crispy] raise "Ouch Face" priority priority = 8; st_facecount = ST_TURNCOUNT; faceindex = 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; if (diffang < ANG45) { // head-on faceindex = ST_RAMPAGEOFFSET; } else if (i) { // turn face right faceindex = ST_TURNOFFSET; } else { // turn face left faceindex = ST_TURNOFFSET+1; } } } } if (priority < 7) { // getting hurt because of your own damn stupidity if (plyr->damagecount) { // [crispy] show "Ouch Face" as intended if (st_oldhealth - plyr->health > ST_MUCHPAIN) { priority = 7; st_facecount = ST_TURNCOUNT; faceindex = ST_OUCHOFFSET; } else { priority = 6; st_facecount = ST_TURNCOUNT; faceindex = ST_RAMPAGEOFFSET; } } } if (priority < 6) { // rapid firing if (plyr->attackdown) { if (lastattackdown==-1) lastattackdown = ST_RAMPAGEDELAY; // [crispy] no rampage face in god mode else if (!--lastattackdown && !invul) { priority = 5; faceindex = ST_RAMPAGEOFFSET; st_facecount = 1; lastattackdown = 1; } } else lastattackdown = -1; } if (priority < 5) { // invulnerability if (invul) { priority = 4; painoffset = 0; faceindex = ST_GODFACE; st_facecount = 1; } } // look left or look right if the facecount has timed out if (!st_facecount) { faceindex = st_randomnumber % 3; st_facecount = ST_STRAIGHTFACECOUNT; priority = 0; } st_facecount--; // [crispy] fix status bar face hysteresis st_faceindex = painoffset + faceindex; } 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; if (plyr->cards[i+3]) keyboxes[i] = (keyboxes[i] == -1) ? i+3 : i+6; // [crispy] support combined card and skull keys // [crispy] blinking key or skull in the status bar if (plyr->tryopen[i]) { #if defined(CRISPY_KEYBLINK_WITH_SOUND) if (!(plyr->tryopen[i] & (2*KEYBLINKMASK-1))) { S_StartSound(NULL, sfx_itemup); } #endif #if defined(CRISPY_KEYBLINK_IN_CLASSIC_HUD) if (st_classicstatusbar && !(plyr->tryopen[i] & (KEYBLINKMASK-1))) { st_firsttime = true; } #endif plyr->tryopen[i]--; #if !defined(CRISPY_KEYBLINK_IN_CLASSIC_HUD) if (st_crispyhud) #endif { keyboxes[i] = (plyr->tryopen[i] & KEYBLINKMASK) ? i + st_keyorskull[i] : -1; } if (!plyr->tryopen[i]) { w_keyboxes[i].oldinum = -1; } } } // 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; } static int st_palette = 0; void ST_doPaletteStuff(void) { int palette; #ifndef CRISPY_TRUECOLOR byte* pal; #endif int cnt; int bzc; cnt = plyr->damagecount; if (plyr->powers[pw_strength]) { // slowly fade the berzerk out bzc = 12 - (plyr->powers[pw_strength]>>6); if (bzc > cnt) cnt = bzc; } if (cnt) { palette = (cnt+7)>>3; if (palette >= NUMREDPALS) palette = NUMREDPALS-1; // [crispy] tune down a bit so the menu remains legible if (menuactive || paused) palette >>= 1; palette += STARTREDPALS; } else if (plyr->bonuscount && plyr->health > 0) // [crispy] never show the yellow bonus palette for a dead player { palette = (plyr->bonuscount+7)>>3; if (palette >= NUMBONUSPALS) palette = NUMBONUSPALS-1; palette += STARTBONUSPALS; } else if ( plyr->powers[pw_ironfeet] > 4*32 || plyr->powers[pw_ironfeet]&8) palette = RADIATIONPAL; else palette = 0; // In Chex Quest, the player never sees red. Instead, the // radiation suit palette is used to tint the screen green, // as though the player is being covered in goo by an // attacking flemoid. if (gameversion == exe_chex && palette >= STARTREDPALS && palette < STARTREDPALS + NUMREDPALS) { palette = RADIATIONPAL; } // [crispy] prevent palette changes when in help screen or Crispness menu if (inhelpscreens) { palette = 0; } if (palette != st_palette) { st_palette = palette; #ifndef CRISPY_TRUECOLOR pal = (byte *) W_CacheLumpNum (lu_palette, PU_CACHE)+palette*768; I_SetPalette (pal); #else I_SetPalette (palette); #endif } } enum { hudcolor_ammo, hudcolor_health, hudcolor_frags, hudcolor_armor } hudcolor_t; // [crispy] return ammo/health/armor widget color static byte* ST_WidgetColor(int i) { if (!(crispy->coloredhud & COLOREDHUD_BAR)) return NULL; switch (i) { case hudcolor_ammo: { if (weaponinfo[plyr->readyweapon].ammo == am_noammo) { return NULL; } else { int ammo = plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; int fullammo = maxammo[weaponinfo[plyr->readyweapon].ammo]; if (ammo < fullammo/4) return cr[CR_RED]; else if (ammo < fullammo/2) return cr[CR_GOLD]; else if (ammo <= fullammo) return cr[CR_GREEN]; else return cr[CR_BLUE]; } break; } case hudcolor_health: { int health = plyr->health; // [crispy] Invulnerability powerup and God Mode cheat turn Health values gray if (plyr->cheats & CF_GODMODE || plyr->powers[pw_invulnerability]) return cr[CR_GRAY]; else if (health < 25) return cr[CR_RED]; else if (health < 50) return cr[CR_GOLD]; else if (health <= 100) return cr[CR_GREEN]; else return cr[CR_BLUE]; break; } case hudcolor_frags: { int frags = st_fragscount; if (frags < 0) return cr[CR_RED]; else if (frags == 0) return cr[CR_GOLD]; else return cr[CR_GREEN]; break; } case hudcolor_armor: { // [crispy] Invulnerability powerup and God Mode cheat turn Armor values gray if (plyr->cheats & CF_GODMODE || plyr->powers[pw_invulnerability]) return cr[CR_GRAY]; // [crispy] color by armor type else if (plyr->armortype >= 2) return cr[CR_BLUE]; else if (plyr->armortype == 1) return cr[CR_GREEN]; else if (plyr->armortype == 0) return cr[CR_RED]; /* // [crispy] alternatively, color by armor points int armor = plyr->armorpoints; if (armor < 25) return cr[CR_RED]; else if (armor < 50) return cr[CR_GOLD]; else if (armor <= 100) return cr[CR_GREEN]; else return cr[CR_BLUE]; */ break; } } return NULL; } // [crispy] draw the gibbed death state frames in the Health widget // in sync with the actual player sprite static inline void ST_DrawGibbedPlayerSprites (void) { state_t const *state = plyr->mo->state; spritedef_t *sprdef; spriteframe_t *sprframe; patch_t *patch; sprdef = &sprites[state->sprite]; // [crispy] the TNT1 sprite is not supposed to be rendered anyway if (!sprdef->numframes && plyr->mo->sprite == SPR_TNT1) { return; } sprframe = &sprdef->spriteframes[state->frame & FF_FRAMEMASK]; patch = W_CacheLumpNum(sprframe->lump[0] + firstspritelump, PU_CACHE); if (plyr->mo->flags & MF_TRANSLATION) { dp_translation = translationtables - 256 + ((plyr->mo->flags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); } V_DrawPatch(73, 186, patch); dp_translation = NULL; } void ST_drawWidgets(boolean refresh) { int i; boolean gibbed = false; // used by w_arms[] widgets st_armson = st_statusbaron && !deathmatch; // used by w_frags widget st_fragson = deathmatch && st_statusbaron; dp_translation = ST_WidgetColor(hudcolor_ammo); STlib_updateNum(&w_ready, refresh); dp_translation = NULL; // [crispy] draw "special widgets" in the Crispy HUD if (st_crispyhud) { // [crispy] draw berserk pack instead of no ammo if appropriate if (plyr->readyweapon == wp_fist && plyr->powers[pw_strength]) { static int lump = -1; patch_t *patch; if (lump == -1) { lump = W_CheckNumForName(DEH_String("PSTRA0")); if (lump == -1) { lump = W_CheckNumForName(DEH_String("MEDIA0")); } } patch = W_CacheLumpNum(lump, PU_CACHE); // [crispy] (23,179) is the center of the Ammo widget V_DrawPatch(23 - SHORT(patch->width)/2 + SHORT(patch->leftoffset), 179 - SHORT(patch->height)/2 + SHORT(patch->topoffset), patch); } // [crispy] draw the gibbed death state frames in the Health widget // in sync with the actual player sprite if (plyr->health <= 0 && plyr->mo->state - states >= mobjinfo[plyr->mo->type].xdeathstate) { ST_DrawGibbedPlayerSprites(); gibbed = true; } } for (i=0;i<4;i++) { STlib_updateNum(&w_ammo[i], refresh); STlib_updateNum(&w_maxammo[i], refresh); } if (!gibbed) { dp_translation = ST_WidgetColor(hudcolor_health); STlib_updatePercent(&w_health, refresh); } dp_translation = ST_WidgetColor(hudcolor_armor); STlib_updatePercent(&w_armor, refresh); dp_translation = NULL; STlib_updateBinIcon(&w_armsbg, refresh); // [crispy] show SSG availability in the Shotgun slot of the arms widget st_shotguns = plyr->weaponowned[wp_shotgun] | plyr->weaponowned[wp_supershotgun]; for (i=0;i<6;i++) STlib_updateMultIcon(&w_arms[i], refresh); // [crispy] draw the actual face widget background if (st_crispyhud && screenblocks == CRISPY_HUD) { V_CopyRect(ST_FX, 1, st_backing_screen, SHORT(faceback->width), ST_HEIGHT - 1, ST_FX, ST_Y + 1); } STlib_updateMultIcon(&w_faces, refresh); for (i=0;i<3;i++) STlib_updateMultIcon(&w_keyboxes[i], refresh); dp_translation = ST_WidgetColor(hudcolor_frags); STlib_updateNum(&w_frags, refresh); dp_translation = NULL; } void ST_doRefresh(void) { st_firsttime = false; // draw status bar background to off-screen buff ST_refreshBackground(false); // and refresh all widgets ST_drawWidgets(true); } void ST_diffDraw(void) { // update all widgets ST_drawWidgets(false); } void ST_Drawer (boolean fullscreen, boolean refresh) { st_statusbaron = (!fullscreen) || (automapactive && !crispy->automapoverlay); // [crispy] immediately redraw status bar after help screens have been shown st_firsttime = st_firsttime || refresh || inhelpscreens; // [crispy] distinguish classic status bar with background and player face from Crispy HUD st_crispyhud = screenblocks >= CRISPY_HUD && (!automapactive || crispy->automapoverlay); st_classicstatusbar = st_statusbaron && !st_crispyhud; st_statusbarface = st_classicstatusbar || (st_crispyhud && screenblocks == CRISPY_HUD); if (crispy->cleanscreenshot == 2) return; // Do red-/gold-shifts from damage/items ST_doPaletteStuff(); // [crispy] translucent HUD if (st_crispyhud && screenblocks > CRISPY_HUD + 1) dp_translucent = true; // If just after ST_Start(), refresh all if (st_firsttime) ST_doRefresh(); // Otherwise, update as little as possible else ST_diffDraw(); dp_translucent = false; } typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); // Iterates through all graphics to be loaded or unloaded, along with // the variable they use, invoking the specified callback function. static void ST_loadUnloadGraphics(load_callback_t callback) { int i; int j; int facenum; char namebuf[9]; // Load the numbers, tall and short for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "STTNUM%d", i); callback(namebuf, &tallnum[i]); DEH_snprintf(namebuf, 9, "STYSNUM%d", i); callback(namebuf, &shortnum[i]); } // Load percent key. //Note: why not load STMINUS here, too? callback(DEH_String("STTPRCNT"), &tallpercent); // key cards for (i=0;i= 0) { callback(DEH_String("STBAR"), &sbar); sbarr = NULL; } else { callback(DEH_String("STMBARL"), &sbar); callback(DEH_String("STMBARR"), &sbarr); } // face states facenum = 0; for (i=0; iweaponowned[i]; for (i=0;i<3;i++) keyboxes[i] = -1; STlib_init(); } void ST_createWidgets(void) { int i; // ready weapon ammo STlib_initNum(&w_ready, ST_AMMOX, ST_AMMOY, tallnum, &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], &st_statusbaron, ST_AMMOWIDTH ); // the last weapon type w_ready.data = plyr->readyweapon; // health percentage STlib_initPercent(&w_health, ST_HEALTHX, ST_HEALTHY, tallnum, &plyr->health, &st_statusbaron, tallpercent); // arms background STlib_initBinIcon(&w_armsbg, ST_ARMSBGX, ST_ARMSBGY, armsbg, &st_notdeathmatch, &st_classicstatusbar); // 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], &plyr->weaponowned[i+1], &st_armson); } // [crispy] show SSG availability in the Shotgun slot of the arms widget w_arms[1].inum = &st_shotguns; // 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_statusbarface); // 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 boolean st_stopped = true; void ST_Start (void) { if (!st_stopped) ST_Stop(); ST_initData(); ST_createWidgets(); st_stopped = false; // [crispy] correctly color the status bar face background in multiplayer // demos recorded by another player than player 1 if (netgame && consoleplayer) { char namebuf[8]; DEH_snprintf(namebuf, 7, "STFB%d", consoleplayer); faceback = W_CacheLumpName(namebuf, PU_STATIC); } } void ST_Stop (void) { if (st_stopped) return; #ifndef CRISPY_TRUECOLOR I_SetPalette (W_CacheLumpNum (lu_palette, PU_CACHE)); #else I_SetPalette (0); #endif st_stopped = true; } void ST_Init (void) { // [crispy] colorize the confusing 'behold' power-up menu if (!DEH_HasStringReplacement(STSTR_BEHOLD) && !M_ParmExists("-nodeh")) { char str_behold[80]; M_snprintf(str_behold, sizeof(str_behold), "in%sV%suln, %sS%str, %sI%snviso, %sR%sad, %sA%sllmap, or %sL%site-amp", crstr[CR_GOLD], crstr[CR_NONE], crstr[CR_GOLD], crstr[CR_NONE], crstr[CR_GOLD], crstr[CR_NONE], crstr[CR_GOLD], crstr[CR_NONE], crstr[CR_GOLD], crstr[CR_NONE], crstr[CR_GOLD], crstr[CR_NONE]); DEH_AddStringReplacement(STSTR_BEHOLD, str_behold); } ST_loadData(); st_backing_screen = (pixel_t *) Z_Malloc((ST_WIDTH << 1) * (ST_HEIGHT << 1) * sizeof(*st_backing_screen), PU_STATIC, 0); } // [crispy] Demo Timer widget void ST_DrawDemoTimer (const int time) { char buffer[16]; const int secs = time / TICRATE; const int w = shortnum[0]->width; int n, x; n = M_snprintf(buffer, sizeof(buffer), "%02i %02i %02i", secs / 60, secs % 60, time % TICRATE); x = (viewwindowx >> crispy->hires) + (scaledviewwidth >> crispy->hires); // [crispy] draw the Demo Timer widget with gray numbers dp_translation = cr[CR_GRAY]; dp_translucent = (gamestate == GS_LEVEL); while (n-- > 0) { const int c = buffer[n] - '0'; x -= w; if (c >= 0 && c <= 9) { V_DrawPatch(x, viewwindowy >> crispy->hires, shortnum[c]); } } dp_translation = NULL; dp_translucent = false; } crispy-doom-crispy-doom-5.6.4/src/doom/st_stuff.h000066400000000000000000000042711360717211000217730ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Status bar code. // Does the face/direction indicator 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 "m_cheat.h" // Size of statusbar. // Now sensitive for scaling. #define ST_HEIGHT 32 #define ST_WIDTH ORIGWIDTH #define ST_Y (ORIGHEIGHT - ST_HEIGHT) #define CRISPY_HUD 12 // [crispy] Demo Timer widget extern void ST_DrawDemoTimer (const int time); extern int defdemotics, deftotaldemotics; // // STATUS BAR // // Called by main loop. boolean ST_Responder (event_t* ev); // Called by main loop. void ST_Ticker (void); // Called by main loop. void ST_Drawer (boolean fullscreen, boolean refresh); // Called when the console player is spawned on each level. void ST_Start (void); // Called by startup code. void ST_Init (void); // [crispy] forcefully initialize the status bar backing screen extern void ST_refreshBackground(boolean force); // 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; extern pixel_t *st_backing_screen; extern cheatseq_t cheat_mus; extern cheatseq_t cheat_god; extern cheatseq_t cheat_ammo; extern cheatseq_t cheat_ammonokey; extern cheatseq_t cheat_noclip; extern cheatseq_t cheat_commercial_noclip; extern cheatseq_t cheat_powerup[8]; // [crispy] idbehold0 extern cheatseq_t cheat_choppers; extern cheatseq_t cheat_clev; extern cheatseq_t cheat_mypos; #endif crispy-doom-crispy-doom-5.6.4/src/doom/statdump.c000066400000000000000000000200121360717211000217610ustar00rootroot00000000000000 /* Copyright(C) 2005-2014 Simon Howard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT 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 "d_player.h" #include "d_mode.h" #include "m_argv.h" #include "statdump.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(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(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, wbstartstruct_t *stats, int player_num) { 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, 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, 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(wbstartstruct_t *stats) { if (M_ParmExists("-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_CheckParmWithArgs("-statdump", 1); if (i > 0) { printf("Statistics captured for %i level(s)\n", num_captured_stats); // We actually know what the real gamemission is, but this has // to match the output from statdump.exe. DiscoverGamemode(captured_stats, num_captured_stats); // Allow "-" as output file, for stdout. if (strcmp(myargv[i + 1], "-") != 0) { dumpfile = fopen(myargv[i + 1], "w"); } else { dumpfile = NULL; } for (i = 0; i < num_captured_stats; ++i) { PrintStats(dumpfile, &captured_stats[i]); } if (dumpfile != NULL) { fclose(dumpfile); } } } crispy-doom-crispy-doom-5.6.4/src/doom/statdump.h000066400000000000000000000012471360717211000217770ustar00rootroot00000000000000 /* Copyright(C) 2005-2014 Simon Howard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT 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(wbstartstruct_t *stats); void StatDump(void); #endif /* #ifndef DOOM_STATDUMP_H */ crispy-doom-crispy-doom-5.6.4/src/doom/wi_stuff.c000066400000000000000000001150011360717211000217510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Intermission screens. // #include #include "z_zone.h" #include "m_misc.h" #include "m_random.h" #include "deh_main.h" #include "deh_bexpars.h" // [crispy] bex_pars[] #include "i_swap.h" #include "i_system.h" #include "w_wad.h" #include "g_game.h" #include "r_local.h" #include "s_sound.h" #include "doomstat.h" // Data. #include "sounds.h" // Needs access to LFB. #include "v_video.h" #include "st_stuff.h" // [crispy] ST_DrawDemoTimer() #include "wi_stuff.h" // // Data needed to add patches to full screen intermission pics. // Patches are statistics messages, and animations. // Loads of by-pixel layout and placement, offsets etc. // // // Different vetween registered DOOM (1994) and // Ultimate DOOM - Final edition (retail, 1995?). // This is supposedly ignored for commercial // release (aka DOOM II), which had 34 maps // in one episode. So there. #define NUMEPISODES 4 #define NUMMAPS 9 // 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 // SINGPLE-PLAYER STUFF #define SP_STATSX 50 #define SP_STATSY 50 #define SP_TIMEX 16 #define SP_TIMEY (ORIGHEIGHT-32) // NET GAME STUFF #define NG_STATSY 50 #define NG_STATSX (32 + SHORT(star->width)/2 + 32*!dofrags) #define NG_SPACINGX 64 // DEATHMATCH STUFF #define DM_MATRIXX 42 #define DM_MATRIXY 68 #define DM_SPACINGX 40 #define DM_TOTALSX 269 #define DM_KILLERSX 10 #define DM_KILLERSY 100 #define DM_VICTIMSX 5 #define DM_VICTIMSY 50 typedef enum { ANIM_ALWAYS, ANIM_RANDOM, ANIM_LEVEL } animenum_t; typedef struct { int x; int y; } point_t; // // Animation. // There is another anim_t used in p_spec. // typedef struct { animenum_t type; // period in tics between animations int period; // number of animation frames int nanims; // location of animation point_t loc; // ALWAYS: n/a, // RANDOM: period deviation (<256), // LEVEL: level int data1; // ALWAYS: n/a, // RANDOM: random base period, // LEVEL: n/a int data2; // actual graphics for frames of animations patch_t* p[3]; // following must be initialized to zero before use! // next value of bcnt (used in conjunction with period) int nexttic; // last drawn animation frame int lastdrawn; // next frame number to animate int ctr; // used by RANDOM and LEVEL when animating int state; } anim_t; 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. // #define ANIM(type, period, nanims, x, y, nexttic) \ { (type), (period), (nanims), { (x), (y) }, (nexttic), \ 0, { NULL, NULL, NULL }, 0, 0, 0, 0 } static anim_t epsd0animinfo[] = { ANIM(ANIM_ALWAYS, TICRATE/3, 3, 224, 104, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 184, 160, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 112, 136, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 72, 112, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 88, 96, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 48, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 192, 40, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 136, 16, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 80, 16, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 24, 0), }; static anim_t epsd1animinfo[] = { ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 1), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 2), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 3), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 4), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 5), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 6), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 7), ANIM(ANIM_LEVEL, TICRATE/3, 3, 192, 144, 8), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 8), }; static anim_t epsd2animinfo[] = { ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 168, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 40, 136, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 160, 96, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 80, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 120, 32, 0), ANIM(ANIM_ALWAYS, TICRATE/4, 3, 40, 0, 0), }; static int NUMANIMS[NUMEPISODES] = { arrlen(epsd0animinfo), arrlen(epsd1animinfo), arrlen(epsd2animinfo), }; static anim_t *anims[NUMEPISODES] = { epsd0animinfo, epsd1animinfo, epsd2animinfo }; // // GENERAL DATA // // // Locally used stuff. // // States for single-player #define SP_KILLS 0 #define SP_ITEMS 2 #define SP_SECRET 4 #define SP_FRAGS 6 #define SP_TIME 8 #define SP_PAR ST_TIME #define SP_PAUSE 1 // in seconds #define SHOWNEXTLOCDELAY 4 //#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY // used to accelerate or skip a stage static int acceleratestage; // wbs->pnum static int me; // specifies current state static stateenum_t state; // contains information passed into intermission static wbstartstruct_t* wbs; static wbplayerstruct_t* plrs; // wbs->plyr[] // used for general timing static int cnt; // used for timing of background animation static int bcnt; // signals to refresh everything for one frame static int firstrefresh; static int cnt_kills[MAXPLAYERS]; static int cnt_items[MAXPLAYERS]; static int cnt_secret[MAXPLAYERS]; static int cnt_time; static int cnt_par; static int cnt_pause; // # of commercial levels static int NUMCMAPS = 32; // // GRAPHICS // // You Are Here graphic static patch_t* yah[3] = { NULL, NULL, NULL }; // splat static patch_t* splat[2] = { NULL, NULL }; // %, : graphics static patch_t* percent; static patch_t* colon; // 0-9 graphic static patch_t* num[10]; // minus sign static patch_t* wiminus; // "Finished!" graphics static patch_t* finished; // "Entering" graphic static patch_t* entering; // "secret" static patch_t* sp_secret; // "Kills", "Scrt", "Items", "Frags" static patch_t* kills; static patch_t* secret; static patch_t* items; static patch_t* frags; // Time sucks. static patch_t* timepatch; static patch_t* par; static patch_t* sucks; // "killers", "victims" static patch_t* killers; static patch_t* victims; // "Total", your face, your dead face static patch_t* total; static patch_t* star; static patch_t* bstar; // "red P[1..MAXPLAYERS]" static patch_t* p[MAXPLAYERS]; // "gray P[1..MAXPLAYERS]" static patch_t* bp[MAXPLAYERS]; // Name graphics of each level (centered) static patch_t** lnames; // [crispy] prevent crashes with maps without map title graphics lump static unsigned int num_lnames; // Buffer storing the backdrop static patch_t *background; // // CODE // // slam background void WI_slamBackground(void) { V_DrawPatchFullScreen(background, false); } // The ticker is used to detect keys // because of timing issues in netgames. boolean WI_Responder(event_t* ev) { return false; } // Draws " Finished!" void WI_drawLF(void) { int y = WI_TITLEY; // [crispy] prevent crashes with maps without map title graphics lump if (wbs->last >= num_lnames || lnames[wbs->last] == NULL) { V_DrawPatch((ORIGWIDTH - SHORT(finished->width)) / 2, y, finished); return; } if (gamemode != commercial || wbs->last < NUMCMAPS) { // draw V_DrawPatch((ORIGWIDTH - SHORT(lnames[wbs->last]->width))/2, y, lnames[wbs->last]); // draw "Finished!" y += (5*SHORT(lnames[wbs->last]->height))/4; V_DrawPatch((ORIGWIDTH - SHORT(finished->width)) / 2, y, finished); } else if (wbs->last == NUMCMAPS) { // MAP33 - draw "Finished!" only V_DrawPatch((ORIGWIDTH - SHORT(finished->width)) / 2, y, finished); } else if (wbs->last > NUMCMAPS) { // > MAP33. Doom bombs out here with a Bad V_DrawPatch error. // I'm pretty sure that doom2.exe is just reading into random // bits of memory at this point, but let's try to be accurate // anyway. This deliberately triggers a V_DrawPatch error. patch_t tmp = { ORIGWIDTH, ORIGHEIGHT, 1, 1, { 0, 0, 0, 0, 0, 0, 0, 0 } }; V_DrawPatch(0, y, &tmp); } } // Draws "Entering " void WI_drawEL(void) { int y = WI_TITLEY; // [crispy] prevent crashes with maps without map title graphics lump if (wbs->next >= num_lnames || lnames[wbs->next] == NULL) { return; } // draw "Entering" V_DrawPatch((ORIGWIDTH - SHORT(entering->width))/2, y, entering); // draw level y += (5*SHORT(lnames[wbs->next]->height))/4; V_DrawPatch((ORIGWIDTH - SHORT(lnames[wbs->next]->width))/2, y, lnames[wbs->next]); } void WI_drawOnLnode ( int n, patch_t* c[] ) { int i; int left; int top; int right; int bottom; boolean fits = false; i = 0; do { left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset); top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset); right = left + SHORT(c[i]->width); bottom = top + SHORT(c[i]->height); if (left >= 0 && right < ORIGWIDTH && top >= 0 && bottom < ORIGHEIGHT) { fits = true; } else { i++; } } while (!fits && i!=2 && c[i] != NULL); if (fits && i<2) { V_DrawPatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, c[i]); } else { // DEBUG printf("Could not place patch on level %d", n+1); } } void WI_initAnimatedBack(void) { int i; anim_t* a; if (gamemode == commercial) return; if (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; } } void WI_updateAnimatedBack(void) { int i; anim_t* a; if (gamemode == commercial) return; if (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; } } } } void WI_drawAnimatedBack(void) { int i; anim_t* a; if (gamemode == commercial) return; if (wbs->epsd > 2) return; for (i=0 ; iepsd] ; i++) { a = &anims[wbs->epsd][i]; if (a->ctr >= 0) V_DrawPatch(a->loc.x, a->loc.y, a->p[a->ctr]); } // [crispy] show Fortress of Mystery if it has been completed if (wbs->epsd == 1 && wbs->didsecret) { a = &anims[wbs->epsd][7]; V_DrawPatch(a->loc.x, a->loc.y, a->p[a->nanims - 1]); } } // // Draws a number. // If digits > 0, then use that many digits minimum, // otherwise only use as many as necessary. // Returns new x position. // int WI_drawNum ( int x, int y, int n, int digits ) { int fontwidth = SHORT(num[0]->width); int neg; int temp; if (digits < 0) { if (!n) { // make variable-length zeros 1 digit long digits = 1; } else { // figure out # of digits in # digits = 0; temp = n; while (temp) { temp /= 10; digits++; } } } neg = n < 0; if (neg) n = -n; // if non-number, do not draw it if (n == 1994) return 0; // draw the new number while (digits--) { x -= fontwidth; V_DrawPatch(x, y, num[ n % 10 ]); n /= 10; } // draw a minus sign if necessary if (neg && wiminus) V_DrawPatch(x-=8, y, wiminus); return x; } void WI_drawPercent ( int x, int y, int p ) { if (p < 0) return; V_DrawPatch(x, y, percent); WI_drawNum(x, y, p, -1); } // // Display level completion time and par, // or "sucks" message if overflow. // void WI_drawTime ( int x, int y, int t, boolean suck ) { int div; int n; if (t<0) return; if (t <= 61*59 || !suck) { div = 1; do { n = (t / div) % 60; x = WI_drawNum(x, y, n, 2) - SHORT(colon->width); div *= 60; // draw if (div==60 || t / div) V_DrawPatch(x, y, colon); } while (t / div && div < 3600); // [crispy] print at most in hhhh:mm:ss format if ((n = (t / div))) { x = WI_drawNum(x, y, n, -1); } } else { // "sucks" V_DrawPatch(x - SHORT(sucks->width), y, sucks); } } void WI_End(void) { void WI_unloadData(void); WI_unloadData(); } void WI_initNoState(void) { state = NoState; acceleratestage = 0; cnt = 10; } void WI_updateNoState(void) { WI_updateAnimatedBack(); if (!--cnt) { // Don't call WI_End yet. G_WorldDone doesnt immediately // change gamestate, so WI_Drawer is still going to get // run until that happens. If we do that after WI_End // (which unloads all the graphics), we're in trouble. //WI_End(); G_WorldDone(); } } static boolean snl_pointeron = false; void WI_initShowNextLoc(void) { // [crispy] display tally screen after ExM8 if ((gamemode != commercial) && (gamemap == 8)) { G_WorldDone(); return; } state = ShowNextLoc; acceleratestage = 0; cnt = SHOWNEXTLOCDELAY * TICRATE; WI_initAnimatedBack(); } void WI_updateShowNextLoc(void) { WI_updateAnimatedBack(); if (!--cnt || acceleratestage) WI_initNoState(); else snl_pointeron = (cnt & 31) < 20; } void WI_drawShowNextLoc(void) { int i; int last; extern boolean secretexit; // [crispy] Master Level support WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); if ( gamemode != commercial) { if (wbs->epsd > 2) { WI_drawEL(); return; } last = (wbs->last == 8 || wbs->last == 9) ? wbs->next - 1 : wbs->last; // [crispy] support E1M10 "Sewers" // 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); // [crispy] the splat for E1M10 "Sewers" is drawn only once, // i.e. now, when returning from the level // (and this is not going to change) if (crispy->havee1m10 && wbs->epsd == 0 && wbs->last == 9) WI_drawOnLnode(9, splat); // draw flashing ptr if (snl_pointeron) WI_drawOnLnode(wbs->next, yah); } if (crispy->singleplayer && ( (gamemission == pack_nerve && wbs->last == 7) || (gamemission == pack_master && wbs->last == 19 && !secretexit) || (gamemission == pack_master && wbs->last == 20))) return; // draws which level you are entering.. if ( (gamemode != commercial) || wbs->next != 30) WI_drawEL(); } void WI_drawNoState(void) { snl_pointeron = true; WI_drawShowNextLoc(); } int WI_fragSum(int playernum) { int i; int frags = 0; for (i=0 ; i 99) dm_frags[i][j] = 99; if (dm_frags[i][j] < -99) dm_frags[i][j] = -99; stillticking = true; } } dm_totals[i] = WI_fragSum(i); if (dm_totals[i] > 99) dm_totals[i] = 99; if (dm_totals[i] < -99) dm_totals[i] = -99; } } if (!stillticking) { S_StartSound(0, sfx_barexp); dm_state++; } } else if (dm_state == 4) { if (acceleratestage) { S_StartSound(0, sfx_slop); if ( gamemode == commercial) WI_initNoState(); else WI_initShowNextLoc(); } } else if (dm_state & 1) { if (!--cnt_pause) { dm_state++; cnt_pause = TICRATE; } } } void WI_drawDeathmatchStats(void) { int i; int j; int x; int y; int w; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); // draw stat titles (top line) V_DrawPatch(DM_TOTALSX-SHORT(total->width)/2, DM_MATRIXY-WI_SPACINGY+10, total); V_DrawPatch(DM_KILLERSX, DM_KILLERSY, killers); V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, victims); // draw P? x = DM_MATRIXX + DM_SPACINGX; y = DM_MATRIXY; for (i=0 ; iwidth)/2, DM_MATRIXY - WI_SPACINGY, p[i]); V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2, y, p[i]); if (i == me) { V_DrawPatch(x-SHORT(p[i]->width)/2, DM_MATRIXY - WI_SPACINGY, bstar); V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2, y, star); } } else { // V_DrawPatch(x-SHORT(bp[i]->width)/2, // DM_MATRIXY - WI_SPACINGY, bp[i]); // V_DrawPatch(DM_MATRIXX-SHORT(bp[i]->width)/2, // y, bp[i]); } x += DM_SPACINGX; y += WI_SPACINGY; } // draw stats y = DM_MATRIXY+10; w = SHORT(num[0]->width); for (i=0 ; imaxkills; cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; if (dofrags) cnt_frags[i] = WI_fragSum(i); } S_StartSound(0, sfx_barexp); ng_state = 10; } if (ng_state == 2) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); stillticking = false; for (i=0 ; i= (plrs[i].skills * 100) / wbs->maxkills) cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; else stillticking = true; } if (!stillticking) { S_StartSound(0, sfx_barexp); ng_state++; } } else if (ng_state == 4) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); stillticking = false; for (i=0 ; i= (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= (plrs[i].ssecret * 100) / wbs->maxsecret) cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; else stillticking = true; } if (!stillticking) { S_StartSound(0, sfx_barexp); ng_state += 1 + 2*!dofrags; } } else if (ng_state == 8) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); stillticking = false; for (i=0 ; i= (fsum = WI_fragSum(i))) cnt_frags[i] = fsum; else stillticking = true; } if (!stillticking) { S_StartSound(0, sfx_pldeth); ng_state++; } } else if (ng_state == 10) { if (acceleratestage) { S_StartSound(0, sfx_sgcock); if ( gamemode == commercial ) WI_initNoState(); else WI_initShowNextLoc(); } } else if (ng_state & 1) { if (!--cnt_pause) { ng_state++; cnt_pause = TICRATE; } } } void WI_drawNetgameStats(void) { int i; int x; int y; int pwidth = SHORT(percent->width); WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); // draw stat titles (top line) V_DrawPatch(NG_STATSX+NG_SPACINGX-SHORT(kills->width), NG_STATSY, kills); V_DrawPatch(NG_STATSX+2*NG_SPACINGX-SHORT(items->width), NG_STATSY, items); V_DrawPatch(NG_STATSX+3*NG_SPACINGX-SHORT(secret->width), NG_STATSY, secret); if (dofrags) V_DrawPatch(NG_STATSX+4*NG_SPACINGX-SHORT(frags->width), NG_STATSY, frags); // draw stats y = NG_STATSY + SHORT(kills->height); for (i=0 ; iwidth), y, p[i]); if (i == me) V_DrawPatch(x-SHORT(p[i]->width), y, star); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_kills[i]); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_items[i]); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_secret[i]); x += NG_SPACINGX; if (dofrags) WI_drawNum(x, y+10, cnt_frags[i], -1); y += WI_SPACINGY; } } static int sp_state; void WI_initStats(void) { state = StatCount; acceleratestage = 0; sp_state = 1; cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; cnt_time = cnt_par = -1; cnt_pause = TICRATE; WI_initAnimatedBack(); } void WI_updateStats(void) { WI_updateAnimatedBack(); if (acceleratestage && sp_state != 10) { acceleratestage = 0; cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; cnt_time = plrs[me].stime / TICRATE; cnt_par = wbs->partime / TICRATE; S_StartSound(0, sfx_barexp); sp_state = 10; } if (sp_state == 2) { cnt_kills[0] += 2; if (!(bcnt&3)) S_StartSound(0, sfx_pistol); if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) { cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; S_StartSound(0, sfx_barexp); sp_state++; } } else if (sp_state == 4) { cnt_items[0] += 2; if (!(bcnt&3)) S_StartSound(0, sfx_pistol); if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) { cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; S_StartSound(0, sfx_barexp); sp_state++; } } else if (sp_state == 6) { cnt_secret[0] += 2; if (!(bcnt&3)) S_StartSound(0, sfx_pistol); if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret) { cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; S_StartSound(0, sfx_barexp); sp_state++; } } else if (sp_state == 8) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); cnt_time += 3; if (cnt_time >= plrs[me].stime / TICRATE) cnt_time = plrs[me].stime / TICRATE; cnt_par += 3; if (cnt_par >= wbs->partime / TICRATE) { cnt_par = wbs->partime / TICRATE; if (cnt_time >= plrs[me].stime / TICRATE) { S_StartSound(0, sfx_barexp); sp_state++; } } } else if (sp_state == 10) { if (acceleratestage) { S_StartSound(0, sfx_sgcock); if (gamemode == commercial) WI_initNoState(); else WI_initShowNextLoc(); } } else if (sp_state & 1) { if (!--cnt_pause) { sp_state++; cnt_pause = TICRATE; } } } // [crispy] conditionally draw par times on intermission screen static boolean WI_drawParTime (void) { extern lumpinfo_t *maplumpinfo; boolean result = true; // [crispy] PWADs have no par times (including The Master Levels) if (!W_IsIWADLump(maplumpinfo)) { result = false; } if (gamemode == commercial) { // [crispy] IWAD: Final Doom has no par times if (gamemission == pack_tnt || gamemission == pack_plut) { result = false; } // [crispy] PWAD: NRFTL has par times (for singleplayer games) if (gamemission == pack_nerve && crispy->singleplayer) { result = true; } // [crispy] IWAD/PWAD: BEX patch provided par times if (bex_cpars[wbs->last]) { result = true; } } else { // [crispy] IWAD: Episode 4 has no par times // (but we have for singleplayer games) if (wbs->epsd == 3 && !crispy->singleplayer) { result = false; } // [crispy] IWAD/PWAD: BEX patch provided par times for Episode 4 // (disguised as par times for Doom II MAP02 to MAP10) if (wbs->epsd == 3 && bex_cpars[wbs->last + 1]) { result = true; } // [crispy] IWAD/PWAD: BEX patch provided par times for Episodes 1-4 if (wbs->epsd <= 3 && bex_pars[wbs->epsd + 1][wbs->last + 1]) { result = true; } // [crispy] PWAD: par times for Sigil if (wbs->epsd == 4) { result = true; } } return result; } void WI_drawStats(void) { // line height int lh; lh = (3*SHORT(num[0]->height))/2; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); V_DrawPatch(SP_STATSX, SP_STATSY, kills); WI_drawPercent(ORIGWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); V_DrawPatch(SP_STATSX, SP_STATSY+lh, items); WI_drawPercent(ORIGWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]); V_DrawPatch(SP_STATSX, SP_STATSY+2*lh, sp_secret); WI_drawPercent(ORIGWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]); V_DrawPatch(SP_TIMEX, SP_TIMEY, timepatch); WI_drawTime(ORIGWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time, true); // [crispy] conditionally draw par times on intermission screen if (WI_drawParTime()) { V_DrawPatch(ORIGWIDTH/2 + SP_TIMEX, SP_TIMEY, par); WI_drawTime(ORIGWIDTH - SP_TIMEX, SP_TIMEY, cnt_par, true); } // [crispy] draw total time after level time and par time if (sp_state > 8) { const int ttime = wbs->totaltimes / TICRATE; const boolean wide = (ttime > 61*59) || (SP_TIMEX + SHORT(total->width) >= ORIGWIDTH/4); V_DrawPatch(SP_TIMEX, SP_TIMEY + 16, total); // [crispy] choose x-position depending on width of time string WI_drawTime((wide ? ORIGWIDTH : ORIGWIDTH/2) - SP_TIMEX, SP_TIMEY + 16, ttime, false); } // [crispy] exit early from the tally screen after ExM8 if (sp_state == 10 && gamemode != commercial && gamemap == 8) { acceleratestage = 1; } // [crispy] demo timer widget if ((demoplayback && (crispy->demotimer & DEMOTIMER_PLAYBACK)) || (demorecording && (crispy->demotimer & DEMOTIMER_RECORD))) { ST_DrawDemoTimer(leveltime); } // [crispy] demo progress bar if (demoplayback && crispy->demobar) { extern void HU_DemoProgressBar (void); HU_DemoProgressBar(); } } 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; } } } // Updates stuff each tick void WI_Ticker(void) { // counter for general background animation bcnt++; if (bcnt == 1) { // intermission music if ( gamemode == commercial ) S_ChangeMusic(mus_dm2int, true); // [crispy] Sigil else if (crispy->haved1e5 && wbs->epsd == 4 && W_CheckNumForName(DEH_String("D_SIGINT")) != -1) S_ChangeMusic(mus_sigint, 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; } } typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); // Common load/unload function. Iterates over all the graphics // lumps to be loaded/unloaded into memory. static void WI_loadUnloadData(load_callback_t callback) { int i, j; char name[9]; anim_t *a; if (nervewadfile && gamemission == pack_nerve) { for (i=0 ; i<9 ; i++) { DEH_snprintf(name, 9, "NWILV%2.2d", i); callback(name, &lnames[i]); } for ( ; iepsd, i); callback(name, &lnames[i]); } // [crispy] special-casing for E1M10 "Sewers" support if (crispy->havee1m10) { DEH_snprintf(name, 9, "SEWERS"); callback(name, &lnames[i]); } // you are here callback(DEH_String("WIURH0"), &yah[0]); // you are here (alt.) callback(DEH_String("WIURH1"), &yah[1]); // splat callback(DEH_String("WISPLAT"), &splat[0]); 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 DEH_snprintf(name, 9, "WIA%d%.2d%.2d", wbs->epsd, j, i); callback(name, &a->p[i]); } else { // HACK ALERT! a->p[i] = anims[1][4].p[i]; } } } } } // More hacks on minus sign. if (W_CheckNumForName(DEH_String("WIMINUS")) > 0) callback(DEH_String("WIMINUS"), &wiminus); else wiminus = NULL; for (i=0;i<10;i++) { // numbers 0-9 DEH_snprintf(name, 9, "WINUM%d", i); callback(name, &num[i]); } // percent sign callback(DEH_String("WIPCNT"), &percent); // "finished" callback(DEH_String("WIF"), &finished); // "entering" callback(DEH_String("WIENTER"), &entering); // "kills" callback(DEH_String("WIOSTK"), &kills); // "scrt" callback(DEH_String("WIOSTS"), &secret); // "secret" callback(DEH_String("WISCRT2"), &sp_secret); // french wad uses WIOBJ (?) if (W_CheckNumForName(DEH_String("WIOBJ")) >= 0) { // "items" if (netgame && !deathmatch) callback(DEH_String("WIOBJ"), &items); else callback(DEH_String("WIOSTI"), &items); } else { callback(DEH_String("WIOSTI"), &items); } // "frgs" callback(DEH_String("WIFRGS"), &frags); // ":" callback(DEH_String("WICOLON"), &colon); // "time" callback(DEH_String("WITIME"), &timepatch); // "sucks" callback(DEH_String("WISUCKS"), &sucks); // "par" callback(DEH_String("WIPAR"), &par); // "killers" (vertical) callback(DEH_String("WIKILRS"), &killers); // "victims" (horiz) callback(DEH_String("WIVCTMS"), &victims); // "total" callback(DEH_String("WIMSTT"), &total); for (i=0 ; i= exe_ultimate && wbs->epsd == 3) { M_StringCopy(name, DEH_String("INTERPIC"), sizeof(name)); } else if (crispy->haved1e5 && wbs->epsd == 4 && W_CheckNumForName(DEH_String("SIGILINT")) != -1) // [crispy] Sigil { M_StringCopy(name, DEH_String("SIGILINT"), sizeof(name)); } else { DEH_snprintf(name, sizeof(name), "WIMAP%d", wbs->epsd); } // [crispy] if still in doubt, use INTERPIC if (W_CheckNumForName(name) == -1) { M_StringCopy(name, DEH_String("INTERPIC"), sizeof(name)); } // Draw backdrop and save to a temporary buffer callback(name, &background); } static void WI_loadCallback(const char *name, patch_t **variable) { // [crispy] prevent crashes with maps without map title graphics lump if (W_CheckNumForName(name) != -1) *variable = W_CacheLumpName(name, PU_STATIC); else *variable = NULL; } void WI_loadData(void) { if (gamemode == commercial) { NUMCMAPS = (crispy->havemap33) ? 33 : 32; lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS, PU_STATIC, NULL); num_lnames = NUMCMAPS; } else { // [crispy] support E1M10 "Sewers" int nummaps = crispy->havee1m10 ? NUMMAPS + 1 : NUMMAPS; lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * nummaps, PU_STATIC, NULL); num_lnames = nummaps; } WI_loadUnloadData(WI_loadCallback); // These two graphics are special cased because we're sharing // them with the status bar code // your face star = W_CacheLumpName(DEH_String("STFST01"), PU_STATIC); // dead face bstar = W_CacheLumpName(DEH_String("STFDEAD0"), PU_STATIC); } static void WI_unloadCallback(const char *name, patch_t **variable) { W_ReleaseLumpName(name); *variable = NULL; } void WI_unloadData(void) { WI_loadUnloadData(WI_unloadCallback); // We do not free these lumps as they are shared with the status // bar code. // W_ReleaseLumpName("STFST01"); // W_ReleaseLumpName("STFDEAD0"); } void WI_Drawer (void) { switch (state) { case StatCount: if (deathmatch) WI_drawDeathmatchStats(); else if (netgame) WI_drawNetgameStats(); else WI_drawStats(); break; case ShowNextLoc: WI_drawShowNextLoc(); break; case NoState: WI_drawNoState(); break; } } void WI_initVariables(wbstartstruct_t* wbstartstruct) { wbs = wbstartstruct; #ifdef RANGECHECKING if (gamemode != commercial) { if (gameversion >= exe_ultimate) RNGCHECK(wbs->epsd, 0, 3); else RNGCHECK(wbs->epsd, 0, 2); } else { RNGCHECK(wbs->last, 0, 8); RNGCHECK(wbs->next, 0, 8); } RNGCHECK(wbs->pnum, 0, MAXPLAYERS); RNGCHECK(wbs->pnum, 0, MAXPLAYERS); #endif acceleratestage = 0; cnt = bcnt = 0; firstrefresh = 1; me = wbs->pnum; plrs = wbs->plyr; if (!wbs->maxkills) wbs->maxkills = 1; if (!wbs->maxitems) wbs->maxitems = 1; if (!wbs->maxsecret) wbs->maxsecret = 1; if ( gameversion < exe_ultimate ) if (wbs->epsd > 2) wbs->epsd -= 3; } void WI_Start(wbstartstruct_t* wbstartstruct) { WI_initVariables(wbstartstruct); WI_loadData(); if (deathmatch) WI_initDeathmatchStats(); else if (netgame) WI_initNetgameStats(); else WI_initStats(); } crispy-doom-crispy-doom-5.6.4/src/doom/wi_stuff.h000066400000000000000000000022141360717211000217570ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Intermission. // #ifndef __WI_STUFF__ #define __WI_STUFF__ //#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); // Shut down the intermission screen void WI_End(void); #endif crispy-doom-crispy-doom-5.6.4/src/doomkeys.h000066400000000000000000000150641360717211000210340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Key definitions // #ifndef __DOOMKEYS__ #define __DOOMKEYS__ // // DOOM keyboard definition. // This is the stuff configured by Setup.Exe. // Most key data are simple ascii (uppercased). // #define KEY_RIGHTARROW 0xae #define KEY_LEFTARROW 0xac #define KEY_UPARROW 0xad #define KEY_DOWNARROW 0xaf #define KEY_ESCAPE 27 #define KEY_ENTER 13 #define KEY_TAB 9 #define KEY_F1 (0x80+0x3b) #define KEY_F2 (0x80+0x3c) #define KEY_F3 (0x80+0x3d) #define KEY_F4 (0x80+0x3e) #define KEY_F5 (0x80+0x3f) #define KEY_F6 (0x80+0x40) #define KEY_F7 (0x80+0x41) #define KEY_F8 (0x80+0x42) #define KEY_F9 (0x80+0x43) #define KEY_F10 (0x80+0x44) #define KEY_F11 (0x80+0x57) #define KEY_F12 (0x80+0x58) #define KEY_BACKSPACE 0x7f #define KEY_PAUSE 0xff #define KEY_EQUALS 0x3d #define KEY_MINUS 0x2d #define KEY_RSHIFT (0x80+0x36) #define KEY_RCTRL (0x80+0x1d) #define KEY_RALT (0x80+0x38) #define KEY_LALT KEY_RALT // new keys: #define KEY_CAPSLOCK (0x80+0x3a) #define KEY_NUMLOCK (0x80+0x45) #define KEY_SCRLCK (0x80+0x46) #define KEY_PRTSCR (0x80+0x59) #define KEY_HOME (0x80+0x47) #define KEY_END (0x80+0x4f) #define KEY_PGUP (0x80+0x49) #define KEY_PGDN (0x80+0x51) #define KEY_INS (0x80+0x52) #define KEY_DEL (0x80+0x53) #define KEYP_0 KEY_INS #define KEYP_1 KEY_END #define KEYP_2 KEY_DOWNARROW #define KEYP_3 KEY_PGDN #define KEYP_4 KEY_LEFTARROW #define KEYP_5 (0x80+0x4c) #define KEYP_6 KEY_RIGHTARROW #define KEYP_7 KEY_HOME #define KEYP_8 KEY_UPARROW #define KEYP_9 KEY_PGUP #define KEYP_DIVIDE '/' #define KEYP_PLUS '+' #define KEYP_MINUS '-' #define KEYP_MULTIPLY '*' #define KEYP_PERIOD 0 #define KEYP_EQUALS KEY_EQUALS #define KEYP_ENTER KEY_ENTER #define SCANCODE_TO_KEYS_ARRAY { \ 0, 0, 0, 0, 'a', /* 0-9 */ \ 'b', 'c', 'd', 'e', 'f', \ 'g', 'h', 'i', 'j', 'k', /* 10-19 */ \ 'l', 'm', 'n', 'o', 'p', \ 'q', 'r', 's', 't', 'u', /* 20-29 */ \ 'v', 'w', 'x', 'y', 'z', \ '1', '2', '3', '4', '5', /* 30-39 */ \ '6', '7', '8', '9', '0', \ KEY_ENTER, KEY_ESCAPE, KEY_BACKSPACE, KEY_TAB, ' ', /* 40-49 */ \ KEY_MINUS, KEY_EQUALS, '[', ']', '\\', \ 0, ';', '\'', '`', ',', /* 50-59 */ \ '.', '/', KEY_CAPSLOCK, KEY_F1, KEY_F2, \ KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* 60-69 */ \ KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, \ KEY_PRTSCR, KEY_SCRLCK, KEY_PAUSE, KEY_INS, KEY_HOME, /* 70-79 */ \ KEY_PGUP, KEY_DEL, KEY_END, KEY_PGDN, KEY_RIGHTARROW, \ KEY_LEFTARROW, KEY_DOWNARROW, KEY_UPARROW, /* 80-89 */ \ KEY_NUMLOCK, KEYP_DIVIDE, \ KEYP_MULTIPLY, KEYP_MINUS, KEYP_PLUS, KEYP_ENTER, KEYP_1, \ KEYP_2, KEYP_3, KEYP_4, KEYP_5, KEYP_6, /* 90-99 */ \ KEYP_7, KEYP_8, KEYP_9, KEYP_0, KEYP_PERIOD, \ 0, 0, 0, KEYP_EQUALS, /* 100-103 */ \ } // Default names for keys, to use in English or as fallback. #define KEY_NAMES_ARRAY { \ { KEY_BACKSPACE, "BACKSP" }, { KEY_TAB, "TAB" }, \ { KEY_INS, "INS" }, { KEY_DEL, "DEL" }, \ { KEY_PGUP, "PGUP" }, { KEY_PGDN, "PGDN" }, \ { KEY_ENTER, "ENTER" }, { KEY_ESCAPE, "ESC" }, \ { KEY_F1, "F1" }, { KEY_F2, "F2" }, \ { KEY_F3, "F3" }, { KEY_F4, "F4" }, \ { KEY_F5, "F5" }, { KEY_F6, "F6" }, \ { KEY_F7, "F7" }, { KEY_F8, "F8" }, \ { KEY_F9, "F9" }, { KEY_F10, "F10" }, \ { KEY_F11, "F11" }, { KEY_F12, "F12" }, \ { KEY_HOME, "HOME" }, { KEY_END, "END" }, \ { KEY_MINUS, "-" }, { KEY_EQUALS, "=" }, \ { KEY_NUMLOCK, "NUMLCK" }, { KEY_SCRLCK, "SCRLCK" }, \ { KEY_PAUSE, "PAUSE" }, { KEY_PRTSCR, "PRTSC" }, \ { KEY_UPARROW, "UP" }, { KEY_DOWNARROW, "DOWN" }, \ { KEY_LEFTARROW, "LEFT" }, { KEY_RIGHTARROW, "RIGHT" }, \ { KEY_RALT, "ALT" }, { KEY_LALT, "ALT" }, \ { KEY_RSHIFT, "SHIFT" }, { KEY_CAPSLOCK, "CAPS" }, \ { KEY_RCTRL, "CTRL" }, { KEYP_5, "NUM5" }, \ { ' ', "SPACE" }, \ { 'a', "A" }, { 'b', "B" }, { 'c', "C" }, { 'd', "D" }, \ { 'e', "E" }, { 'f', "F" }, { 'g', "G" }, { 'h', "H" }, \ { 'i', "I" }, { 'j', "J" }, { 'k', "K" }, { 'l', "L" }, \ { 'm', "M" }, { 'n', "N" }, { 'o', "O" }, { 'p', "P" }, \ { 'q', "Q" }, { 'r', "R" }, { 's', "S" }, { 't', "T" }, \ { 'u', "U" }, { 'v', "V" }, { 'w', "W" }, { 'x', "X" }, \ { 'y', "Y" }, { 'z', "Z" }, { '0', "0" }, { '1', "1" }, \ { '2', "2" }, { '3', "3" }, { '4', "4" }, { '5', "5" }, \ { '6', "6" }, { '7', "7" }, { '8', "8" }, { '9', "9" }, \ { '[', "[" }, { ']', "]" }, { ';', ";" }, { '`', "`" }, \ { ',', "," }, { '.', "." }, { '/', "/" }, { '\\', "\\" }, \ { '\'', "\'" }, \ } #endif // __DOOMKEYS__ crispy-doom-crispy-doom-5.6.4/src/doomtype.h000066400000000000000000000064771360717211000210520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Simple basic typedefs, isolated here to make it easier // separating modules. // #ifndef __DOOMTYPE__ #define __DOOMTYPE__ #include "config.h" #if defined(_MSC_VER) && !defined(__cplusplus) #define inline __inline #endif // #define macros to provide functions missing in Windows. // Outside Windows, we use strings.h for str[n]casecmp. #if !HAVE_DECL_STRCASECMP || !HAVE_DECL_STRNCASECMP #include #if !HAVE_DECL_STRCASECMP #define strcasecmp stricmp #endif #if !HAVE_DECL_STRNCASECMP #define strncasecmp strnicmp #endif #else #include #endif // // The packed attribute forces structures to be packed into the minimum // space necessary. If this is not done, the compiler may align structure // fields differently to optimize memory access, inflating the overall // structure size. It is important to use the packed attribute on certain // structures where alignment is important, particularly data read/written // to disk. // #ifdef __GNUC__ #if defined(_WIN32) && !defined(__clang__) #define PACKEDATTR __attribute__((packed,gcc_struct)) #else #define PACKEDATTR __attribute__((packed)) #endif #define PRINTF_ATTR(fmt, first) __attribute__((format(printf, fmt, first))) #define PRINTF_ARG_ATTR(x) __attribute__((format_arg(x))) #define NORETURN __attribute__((noreturn)) #else #if defined(_MSC_VER) #define PACKEDATTR __pragma(pack(pop)) #else #define PACKEDATTR #endif #define PRINTF_ATTR(fmt, first) #define PRINTF_ARG_ATTR(x) #define NORETURN #endif #ifdef __WATCOMC__ #define PACKEDPREFIX _Packed #elif defined(_MSC_VER) #define PACKEDPREFIX __pragma(pack(push,1)) #else #define PACKEDPREFIX #endif #define PACKED_STRUCT(...) PACKEDPREFIX struct __VA_ARGS__ PACKEDATTR // C99 integer types; with gcc we just use this. Other compilers // should add conditional statements that define the C99 types. // What is really wanted here is stdint.h; however, some old versions // of Solaris don't have stdint.h and only have inttypes.h (the // pre-standardisation version). inttypes.h is also in the C99 // standard and defined to include stdint.h, so include this. #include #if defined(__cplusplus) || defined(__bool_true_false_are_defined) // Use builtin bool type with C++. typedef bool boolean; #else typedef enum { false, true } boolean; #endif typedef uint8_t byte; #ifndef CRISPY_TRUECOLOR typedef uint8_t pixel_t; typedef int16_t dpixel_t; #else typedef uint32_t pixel_t; typedef int64_t dpixel_t; #endif #include #ifdef _WIN32 #define DIR_SEPARATOR '\\' #define DIR_SEPARATOR_S "\\" #define PATH_SEPARATOR ';' #else #define DIR_SEPARATOR '/' #define DIR_SEPARATOR_S "/" #define PATH_SEPARATOR ':' #endif #define arrlen(array) (sizeof(array) / sizeof(*array)) #endif crispy-doom-crispy-doom-5.6.4/src/gusconf.c000066400000000000000000000144161360717211000206410ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // GUS emulation code. // // Actually emulating a GUS is far too much work; fortunately // GUS "emulation" already exists in the form of Timidity, which // supports GUS patch files. This code therefore converts Doom's // DMXGUS lump into an equivalent Timidity configuration file. // #include #include #include #include #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #define MAX_INSTRUMENTS 256 typedef struct { char *patch_names[MAX_INSTRUMENTS]; int used[MAX_INSTRUMENTS]; int mapping[MAX_INSTRUMENTS]; unsigned int count; } gus_config_t; char *gus_patch_path = ""; int gus_ram_kb = 1024; static unsigned int MappingIndex(void) { unsigned int result = gus_ram_kb / 256; if (result < 1) { return 1; } else if (result > 4) { return 4; } else { return result; } } static int SplitLine(char *line, char **fields, unsigned int max_fields) { unsigned int num_fields; char *p; fields[0] = line; num_fields = 1; for (p = line; *p != '\0'; ++p) { if (*p == ',') { *p = '\0'; // Skip spaces following the comma. do { ++p; } while (*p != '\0' && isspace(*p)); fields[num_fields] = p; ++num_fields; --p; if (num_fields >= max_fields) { break; } } else if (*p == '#') { *p = '\0'; break; } } // Strip off trailing whitespace from the end of the line. p = fields[num_fields - 1] + strlen(fields[num_fields - 1]); while (p > fields[num_fields - 1] && isspace(*(p - 1))) { --p; *p = '\0'; } return num_fields; } static void ParseLine(gus_config_t *config, char *line) { char *fields[6]; unsigned int i; unsigned int num_fields; unsigned int instr_id, mapped_id; num_fields = SplitLine(line, fields, 6); if (num_fields < 6) { return; } instr_id = atoi(fields[0]); // Skip non GM percussions. if ((instr_id >= 128 && instr_id < 128 + 35) || instr_id > 128 + 81) { return; } mapped_id = atoi(fields[MappingIndex()]); for (i = 0; i < config->count; i++) { if (config->used[i] == mapped_id) { break; } } if (i == config->count) { // DMX uses wrong patch name (we should use name of 'mapped_id' // instrument, but DMX uses name of 'instr_id' instead). free(config->patch_names[i]); config->patch_names[i] = M_StringDuplicate(fields[5]); config->used[i] = mapped_id; config->count++; } config->mapping[instr_id] = i; } static void ParseDMXConfig(char *dmxconf, gus_config_t *config) { char *p, *newline; unsigned int i; memset(config, 0, sizeof(gus_config_t)); for (i = 0; i < MAX_INSTRUMENTS; ++i) { config->mapping[i] = -1; config->used[i] = -1; } config->count = 0; p = dmxconf; for (;;) { newline = strchr(p, '\n'); if (newline != NULL) { *newline = '\0'; } ParseLine(config, p); if (newline == NULL) { break; } else { p = newline + 1; } } } static void FreeDMXConfig(gus_config_t *config) { unsigned int i; for (i = 0; i < MAX_INSTRUMENTS; ++i) { free(config->patch_names[i]); } } static char *ReadDMXConfig(void) { int lumpnum; unsigned int len; char *data; // TODO: This should be chosen based on gamemode == commercial: lumpnum = W_CheckNumForName("DMXGUS"); if (lumpnum < 0) { lumpnum = W_GetNumForName("DMXGUSC"); } len = W_LumpLength(lumpnum); data = Z_Malloc(len + 1, PU_STATIC, NULL); W_ReadLump(lumpnum, data); data[len] = '\0'; return data; } static boolean WriteTimidityConfig(char *path, gus_config_t *config) { FILE *fstream; unsigned int i; fstream = fopen(path, "w"); if (fstream == NULL) { return false; } fprintf(fstream, "# Autogenerated Timidity config.\n\n"); fprintf(fstream, "dir %s\n", gus_patch_path); fprintf(fstream, "\nbank 0\n\n"); for (i = 0; i < 128; ++i) { if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && config->patch_names[config->mapping[i]] != NULL) { fprintf(fstream, "%u %s\n", i, config->patch_names[config->mapping[i]]); } } fprintf(fstream, "\ndrumset 0\n\n"); for (i = 128 + 35; i <= 128 + 81; ++i) { if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && config->patch_names[config->mapping[i]] != NULL) { fprintf(fstream, "%u %s\n", i - 128, config->patch_names[config->mapping[i]]); } } fprintf(fstream, "\n"); fclose(fstream); return true; } boolean GUS_WriteConfig(char *path) { boolean result; char *dmxconf; gus_config_t config; if (!strcmp(gus_patch_path, "")) { printf("You haven't configured gus_patch_path.\n"); printf("gus_patch_path needs to point to the location of " "your GUS patch set.\n" "To get a copy of the \"standard\" GUS patches, " "download a copy of dgguspat.zip.\n"); return false; } dmxconf = ReadDMXConfig(); ParseDMXConfig(dmxconf, &config); result = WriteTimidityConfig(path, &config); FreeDMXConfig(&config); Z_Free(dmxconf); return result; } crispy-doom-crispy-doom-5.6.4/src/gusconf.h000066400000000000000000000014271360717211000206440ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // GUS emulation code. // #ifndef __GUSCONF_H__ #define __GUSCONF_H__ #include "doomtype.h" extern char *gus_patch_path; extern int gus_ram_kb; boolean GUS_WriteConfig(char *path); #endif /* #ifndef __GUSCONF_H__ */ crispy-doom-crispy-doom-5.6.4/src/heretic/000077500000000000000000000000001360717211000204465ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/src/heretic/.gitignore000066400000000000000000000000461360717211000224360ustar00rootroot00000000000000Makefile Makefile.in .deps tags TAGS crispy-doom-crispy-doom-5.6.4/src/heretic/CMakeLists.txt000066400000000000000000000031221360717211000232040ustar00rootroot00000000000000add_library(heretic STATIC am_data.h am_map.c am_map.h ct_chat.c ct_chat.h deh_ammo.c deh_frame.c deh_htext.c deh_htic.c deh_htic.h deh_sound.c deh_thing.c deh_weapon.c d_main.c d_net.c doomdata.h doomdef.h dstrings.h f_finale.c g_game.c info.c info.h in_lude.c m_random.c m_random.h mn_menu.c p_action.h p_ceilng.c p_doors.c p_enemy.c p_floor.c p_inter.c p_lights.c p_local.h p_map.c p_maputl.c p_mobj.c p_plats.c p_pspr.c p_saveg.c p_setup.c p_sight.c p_spec.c p_spec.h p_switch.c p_telept.c p_tick.c p_user.c r_bsp.c r_data.c r_draw.c r_local.h r_main.c r_plane.c r_segs.c r_things.c sb_bar.c sounds.c sounds.h s_sound.c s_sound.h) target_include_directories(heretic PRIVATE "../" "${CMAKE_CURRENT_BINARY_DIR}/../../") target_link_libraries(heretic textscreen SDL2::SDL2 SDL2::mixer SDL2::net) crispy-doom-crispy-doom-5.6.4/src/heretic/Makefile.am000066400000000000000000000061601360717211000225050ustar00rootroot00000000000000 AM_CFLAGS=-I$(top_srcdir)/src \ -I$(top_srcdir)/textscreen \ @SDL_CFLAGS@ @SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@ EXTRA_DIST = CMakeLists.txt noinst_LIBRARIES=libheretic.a libheretic_a_SOURCES = \ am_data.h \ am_map.c am_map.h \ ct_chat.c ct_chat.h \ deh_ammo.c \ deh_frame.c \ deh_htext.c \ deh_htic.c deh_htic.h \ deh_sound.c \ deh_thing.c \ deh_weapon.c \ d_main.c \ d_net.c \ doomdata.h \ doomdef.h \ dstrings.h \ f_finale.c \ g_game.c \ info.c info.h \ in_lude.c \ m_random.c m_random.h \ mn_menu.c \ p_action.h \ p_ceilng.c \ p_doors.c \ p_enemy.c \ p_floor.c \ p_inter.c \ p_lights.c \ p_local.h \ p_map.c \ p_maputl.c \ p_mobj.c \ p_plats.c \ p_pspr.c \ p_saveg.c \ p_setup.c \ p_sight.c \ p_spec.c p_spec.h \ p_switch.c \ p_telept.c \ p_tick.c \ p_user.c \ r_bsp.c \ r_data.c \ r_draw.c \ r_local.h \ r_main.c \ r_plane.c \ r_segs.c \ r_things.c \ sb_bar.c \ sounds.c sounds.h \ s_sound.c s_sound.h crispy-doom-crispy-doom-5.6.4/src/heretic/am_data.h000066400000000000000000000072621360717211000222140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // AM_data.h : The vector graphics for the automap #ifndef __AMDATA_H__ #define __AMDATA_H__ // a line drawing of the player pointing right, starting from the middle. #define R ((8*PLAYERRADIUS)/7) mline_t player_arrow[] = { { { -R+R/4, 0 }, { 0, 0} }, // center line. { { -R+R/4, R/8 }, { R, 0} }, // blade { { -R+R/4, -R/8 }, { R, 0 } }, { { -R+R/4, -R/4 }, { -R+R/4, R/4 } }, // crosspiece { { -R+R/8, -R/4 }, { -R+R/8, R/4 } }, { { -R+R/8, -R/4 }, { -R+R/4, -R/4} }, //crosspiece connectors { { -R+R/8, R/4 }, { -R+R/4, R/4} }, { { -R-R/4, R/8 }, { -R-R/4, -R/8 } }, //pommel { { -R-R/4, R/8 }, { -R+R/8, R/8 } }, { { -R-R/4, -R/8}, { -R+R/8, -R/8 } } }; mline_t keysquare[] = { { { 0, 0 }, { R/4, -R/2 } }, { { R/4, -R/2 }, { R/2, -R/2 } }, { { R/2, -R/2 }, { R/2, R/2 } }, { { R/2, R/2 }, { R/4, R/2 } }, { { R/4, R/2 }, { 0, 0 } }, // handle part type thing { { 0, 0 }, { -R, 0 } }, // stem { { -R, 0 }, { -R, -R/2 } }, // end lockpick part { { -3*R/4, 0 }, { -3*R/4, -R/4 } } }; /*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 NUMKEYSQUARELINES (sizeof(keysquare)/sizeof(mline_t)) #define R ((8*PLAYERRADIUS)/7) mline_t cheat_player_arrow[] = { { { -R+R/8, 0 }, { R, 0 } }, // ----- { { R, 0 }, { R-R/2, R/6 } }, // -----> { { R, 0 }, { R-R/2, -R/6 } }, { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >-----> { { -R+R/8, 0 }, { -R-R/8, -R/6 } }, { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>-----> { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } }, { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d---> { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } }, { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } }, { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd--> { { -R/6, -R/6 }, { 0, -R/6 } }, { { 0, -R/6 }, { 0, R/4 } }, { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt-> { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } }, { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } } }; #undef R #define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) #define R (FRACUNIT) mline_t triangle_guy[] = { { { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } }, { { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)(R ) } }, { { (fixed_t)(0 ), (fixed_t)(R ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } } }; #undef R #define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) #define R (FRACUNIT) mline_t thintriangle_guy[] = { { { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (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)) #endif crispy-doom-crispy-doom-5.6.4/src/heretic/am_map.c000066400000000000000000001225571360717211000220600ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // AM_map.c #include #include "doomdef.h" #include "deh_str.h" #include "i_timer.h" #include "i_video.h" #include "m_controls.h" #include "p_local.h" #include "am_map.h" #include "am_data.h" #include "doomkeys.h" #include "v_video.h" vertex_t KeyPoints[NUMKEYS]; #define NUMALIAS 3 // Number of antialiased lines. const char *LevelNames[] = { // EPISODE 1 - THE CITY OF THE DAMNED "E1M1: THE DOCKS", "E1M2: THE DUNGEONS", "E1M3: THE GATEHOUSE", "E1M4: THE GUARD TOWER", "E1M5: THE CITADEL", "E1M6: THE CATHEDRAL", "E1M7: THE CRYPTS", "E1M8: HELL'S MAW", "E1M9: THE GRAVEYARD", // EPISODE 2 - HELL'S MAW "E2M1: THE CRATER", "E2M2: THE LAVA PITS", "E2M3: THE RIVER OF FIRE", "E2M4: THE ICE GROTTO", "E2M5: THE CATACOMBS", "E2M6: THE LABYRINTH", "E2M7: THE GREAT HALL", "E2M8: THE PORTALS OF CHAOS", "E2M9: THE GLACIER", // EPISODE 3 - THE DOME OF D'SPARIL "E3M1: THE STOREHOUSE", "E3M2: THE CESSPOOL", "E3M3: THE CONFLUENCE", "E3M4: THE AZURE FORTRESS", "E3M5: THE OPHIDIAN LAIR", "E3M6: THE HALLS OF FEAR", "E3M7: THE CHASM", "E3M8: D'SPARIL'S KEEP", "E3M9: THE AQUIFER", // EPISODE 4: THE OSSUARY "E4M1: CATAFALQUE", "E4M2: BLOCKHOUSE", "E4M3: AMBULATORY", "E4M4: SEPULCHER", "E4M5: GREAT STAIR", "E4M6: HALLS OF THE APOSTATE", "E4M7: RAMPARTS OF PERDITION", "E4M8: SHATTERED BRIDGE", "E4M9: MAUSOLEUM", // EPISODE 5: THE STAGNANT DEMESNE "E5M1: OCHRE CLIFFS", "E5M2: RAPIDS", "E5M3: QUAY", "E5M4: COURTYARD", "E5M5: HYDRATYR", "E5M6: COLONNADE", "E5M7: FOETID MANSE", "E5M8: FIELD OF JUDGEMENT", "E5M9: SKEIN OF D'SPARIL", // EPISODE 6: unnamed "E6M1: ", "E6M2: ", "E6M3: ", }; static int cheating = 0; static int grid = 0; static int leveljuststarted = 1; // kluge until AM_LevelInit() is called boolean automapactive = false; static int finit_width;// = SCREENWIDTH; static int finit_height;// = SCREENHEIGHT - (42 << crispy->hires); static int f_x, f_y; // location of window on screen static int f_w, f_h; // size of window on screen static int lightlev; // used for funky strobing effect static byte *fb; // pseudo-frame buffer static int amclock; static mpoint_t m_paninc; // how far the window pans each tic (map coords) static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords) static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords) // width/height of window on map (map coords) static fixed_t m_w, m_h; static fixed_t min_x, min_y; // based on level size static fixed_t max_x, max_y; // based on level size static fixed_t max_w, max_h; // max_x-min_x, max_y-min_y static fixed_t min_w, min_h; // based on player size static fixed_t min_scale_mtof; // used to tell when to stop zooming out static fixed_t max_scale_mtof; // used to tell when to stop zooming in // old stuff for recovery later static fixed_t old_m_w, old_m_h; static fixed_t old_m_x, old_m_y; // old location used by the Follower routine static mpoint_t f_oldloc; // used by MTOF to scale from map-to-frame-buffer coords static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) static fixed_t scale_ftom; static player_t *plr; // the player represented by an arrow static vertex_t oldplr; //static patch_t *marknums[10]; // numbers used for marking by the automap //static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are //static int markpointnum = 0; // next point to be assigned static int followplayer = 1; // specifies whether to follow the player around static char cheat_amap[] = { 'r', 'a', 'v', 'm', 'a', 'p' }; static byte cheatcount = 0; extern boolean viewactive; static byte antialias[NUMALIAS][8] = { {96, 97, 98, 99, 100, 101, 102, 103}, {110, 109, 108, 107, 106, 105, 104, 103}, {75, 76, 77, 78, 79, 80, 81, 103} }; /* static byte *aliasmax[NUMALIAS] = { &antialias[0][7], &antialias[1][7], &antialias[2][7] };*/ static byte *maplump; // pointer to the raw data for the automap background. static short mapystart = 0; // y-value for the start of the map bitmap...used in the paralax stuff. static short mapxstart = 0; //x-value for the bitmap. //byte screens[][SCREENWIDTH*SCREENHEIGHT]; //void V_MarkRect (int x, int y, int width, int height); // Functions void DrawWuLine(int X0, int Y0, int X1, int Y1, byte * BaseColor, int NumLevels, unsigned short IntensityBits); // Calculates the slope and slope according to the x-axis of a line // segment in map coordinates (with the upright y-axis n' all) so // that it can be used with the brain-dead drawing stuff. // Ripped out for Heretic /* void AM_getIslope(mline_t *ml, islope_t *is) { int dx, dy; dy = ml->a.y - ml->b.y; dx = ml->b.x - ml->a.x; if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX); else is->islp = FixedDiv(dx, dy); if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX); else is->slp = FixedDiv(dy, dx); } */ void AM_activateNewScale(void) { m_x += m_w / 2; m_y += m_h / 2; m_w = FTOM(f_w); m_h = FTOM(f_h); m_x -= m_w / 2; m_y -= m_h / 2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; } void AM_saveScaleAndLoc(void) { old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; } void AM_restoreScaleAndLoc(void) { m_w = old_m_w; m_h = old_m_h; if (!followplayer) { m_x = old_m_x; m_y = old_m_y; } else { m_x = plr->mo->x - m_w / 2; m_y = plr->mo->y - m_h / 2; } m_x2 = m_x + m_w; m_y2 = m_y + m_h; // Change the scaling multipliers scale_mtof = FixedDiv(f_w << FRACBITS, m_w); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } // adds a marker at the current location /* void AM_addMark(void) { markpoints[markpointnum].x = m_x + m_w/2; markpoints[markpointnum].y = m_y + m_h/2; markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; } */ void AM_findMinMaxBoundaries(void) { int i; fixed_t a, b; min_x = min_y = INT_MAX; max_x = max_y = -INT_MAX; for (i = 0; i < numvertexes; i++) { if (vertexes[i].x < min_x) min_x = vertexes[i].x; else if (vertexes[i].x > max_x) max_x = vertexes[i].x; if (vertexes[i].y < min_y) min_y = vertexes[i].y; else if (vertexes[i].y > max_y) max_y = vertexes[i].y; } max_w = max_x - min_x; max_h = max_y - min_y; min_w = 2 * PLAYERRADIUS; min_h = 2 * PLAYERRADIUS; a = FixedDiv(f_w << FRACBITS, max_w); b = FixedDiv(f_h << FRACBITS, max_h); min_scale_mtof = a < b ? a : b; max_scale_mtof = FixedDiv(f_h << FRACBITS, 2 * PLAYERRADIUS); } void AM_changeWindowLoc(void) { if (m_paninc.x || m_paninc.y) { followplayer = 0; f_oldloc.x = INT_MAX; } m_x += m_paninc.x; m_y += m_paninc.y; if (m_x + m_w / 2 > max_x) { m_x = max_x - m_w / 2; m_paninc.x = 0; } else if (m_x + m_w / 2 < min_x) { m_x = min_x - m_w / 2; m_paninc.x = 0; } if (m_y + m_h / 2 > max_y) { m_y = max_y - m_h / 2; m_paninc.y = 0; } else if (m_y + m_h / 2 < min_y) { m_y = min_y - m_h / 2; m_paninc.y = 0; } // The following code was commented out in the released Heretic source, // but I believe we need to do this here to stop the background moving // when we reach the map boundaries. (In the released source it's done // in AM_clearFB). mapxstart += MTOF(m_paninc.x+FRACUNIT/2); mapystart -= MTOF(m_paninc.y+FRACUNIT/2); if(mapxstart >= (finit_width >> crispy->hires)) mapxstart -= (finit_width >> crispy->hires); if(mapxstart < 0) mapxstart += (finit_width >> crispy->hires); if(mapystart >= (finit_height >> crispy->hires)) mapystart -= (finit_height >> crispy->hires); if(mapystart < 0) mapystart += (finit_height >> crispy->hires); // - end of code that was commented-out m_x2 = m_x + m_w; m_y2 = m_y + m_h; } void AM_initVariables(void) { int pnum; thinker_t *think; mobj_t *mo; //static event_t st_notify = { ev_keyup, AM_MSGENTERED }; automapactive = true; fb = I_VideoBuffer; f_oldloc.x = INT_MAX; amclock = 0; lightlev = 0; m_paninc.x = m_paninc.y = 0; ftom_zoommul = FRACUNIT; mtof_zoommul = FRACUNIT; m_w = FTOM(f_w); m_h = FTOM(f_h); // find player to center on initially if (!playeringame[pnum = consoleplayer]) for (pnum = 0; pnum < MAXPLAYERS; pnum++) if (playeringame[pnum]) break; plr = &players[pnum]; oldplr.x = plr->mo->x; oldplr.y = plr->mo->y; m_x = plr->mo->x - m_w / 2; m_y = plr->mo->y - m_h / 2; AM_changeWindowLoc(); // for saving & restoring old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; // load in the location of keys, if in baby mode memset(KeyPoints, 0, sizeof(vertex_t) * 3); if (gameskill == sk_baby) { for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { //not a mobj continue; } mo = (mobj_t *) think; if (mo->type == MT_CKEY) { KeyPoints[0].x = mo->x; KeyPoints[0].y = mo->y; } else if (mo->type == MT_AKYY) { KeyPoints[1].x = mo->x; KeyPoints[1].y = mo->y; } else if (mo->type == MT_BKYY) { KeyPoints[2].x = mo->x; KeyPoints[2].y = mo->y; } } } // inform the status bar of the change //c ST_Responder(&st_notify); } void AM_loadPics(void) { //int i; //char namebuf[9]; /* for (i=0;i<10;i++) { M_snprintf(namebuf, sizeof(namebuf), "AMMNUM%d", i); marknums[i] = W_CacheLumpName(namebuf, PU_STATIC); }*/ maplump = W_CacheLumpName(DEH_String("AUTOPAGE"), PU_STATIC); } /*void AM_unloadPics(void) { int i; for (i=0;i<10;i++) Z_ChangeTag(marknums[i], PU_CACHE); }*/ /* void AM_clearMarks(void) { int i; for (i=0;ihires); f_x = f_y = 0; f_w = finit_width; f_h = finit_height; mapxstart = mapystart = 0; // AM_clearMarks(); AM_findMinMaxBoundaries(); scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7 * FRACUNIT)); if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } static boolean stopped = true; void AM_Stop(void) { //static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED }; // AM_unloadPics(); automapactive = false; // ST_Responder(&st_notify); stopped = true; BorderNeedRefresh = true; } void AM_Start(void) { static int lastlevel = -1, lastepisode = -1; if (!stopped) AM_Stop(); stopped = false; if (gamestate != GS_LEVEL) { return; // don't show automap if we aren't in a game! } if (lastlevel != gamemap || lastepisode != gameepisode) { AM_LevelInit(); lastlevel = gamemap; lastepisode = gameepisode; } AM_initVariables(); AM_loadPics(); } // set the window scale to the maximum size void AM_minOutWindowScale(void) { scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // set the window scale to the minimum size void AM_maxOutWindowScale(void) { scale_mtof = max_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } boolean AM_Responder(event_t * ev) { int rc; int key; static int bigstate = 0; static int joywait = 0; key = ev->data1; rc = false; if (ev->type == ev_joystick && joybautomap >= 0 && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime()) { joywait = I_GetTime() + 5; if (!automapactive) { AM_Start (); viewactive = false; } else { bigstate = 0; viewactive = true; AM_Stop (); } } if (!automapactive) { if (ev->type == ev_keydown && key == key_map_toggle && gamestate == GS_LEVEL) { AM_Start(); viewactive = false; // viewactive = true; rc = true; } } else if (ev->type == ev_keydown) { rc = true; if (key == key_map_east) // pan right { if (!followplayer) m_paninc.x = FTOM(F_PANINC); else rc = false; } else if (key == key_map_west) // pan left { if (!followplayer) m_paninc.x = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_north) // pan up { if (!followplayer) m_paninc.y = FTOM(F_PANINC); else rc = false; } else if (key == key_map_south) // pan down { if (!followplayer) m_paninc.y = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_zoomout) // zoom out { mtof_zoommul = M_ZOOMOUT; ftom_zoommul = M_ZOOMIN; } else if (key == key_map_zoomin) // zoom in { mtof_zoommul = M_ZOOMIN; ftom_zoommul = M_ZOOMOUT; } else if (key == key_map_toggle) // toggle map (tab) { bigstate = 0; viewactive = true; AM_Stop(); } else if (key == key_map_maxzoom) { bigstate = !bigstate; if (bigstate) { AM_saveScaleAndLoc(); AM_minOutWindowScale(); } else AM_restoreScaleAndLoc(); } else if (key == key_map_follow) { followplayer = !followplayer; f_oldloc.x = INT_MAX; P_SetMessage(plr, followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF, true); } /* else if (key == key_map_grid) { grid = !grid; plr->message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF; } else if (key == key_map_mark) { M_snprintf(buffer, sizeof(buffer), "%s %d", AMSTR_MARKEDSPOT, markpointnum); plr->message = buffer; AM_addMark(); } else if (key == key_map_clearmark) { AM_clearMarks(); plr->message = AMSTR_MARKSCLEARED; } */ else { rc = false; } if (cheat_amap[cheatcount] == ev->data1 && !netgame) cheatcount++; else cheatcount = 0; if (cheatcount == 6) { cheatcount = 0; rc = false; cheating = (cheating + 1) % 3; } } else if (ev->type == ev_keyup) { rc = false; if (key == key_map_east) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_west) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_north) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_south) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_zoomout || key == key_map_zoomin) { mtof_zoommul = FRACUNIT; ftom_zoommul = FRACUNIT; } } return rc; } void AM_changeWindowScale(void) { // Change the scaling multipliers scale_mtof = FixedMul(scale_mtof, mtof_zoommul); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); if (scale_mtof < min_scale_mtof) AM_minOutWindowScale(); else if (scale_mtof > max_scale_mtof) AM_maxOutWindowScale(); else AM_activateNewScale(); } void AM_doFollowPlayer(void) { if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) { // m_x = FTOM(MTOF(plr->mo->x - m_w/2)); // m_y = FTOM(MTOF(plr->mo->y - m_h/2)); // m_x = plr->mo->x - m_w/2; // m_y = plr->mo->y - m_h/2; m_x = FTOM(MTOF(plr->mo->x)) - m_w / 2; m_y = FTOM(MTOF(plr->mo->y)) - m_h / 2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; // do the parallax parchment scrolling. /* dmapx = (MTOF(plr->mo->x)-MTOF(f_oldloc.x)); //fixed point dmapy = (MTOF(f_oldloc.y)-MTOF(plr->mo->y)); if(f_oldloc.x == INT_MAX) //to eliminate an error when the user first dmapx=0; //goes into the automap. mapxstart += dmapx; mapystart += dmapy; while(mapxstart >= finit_width) mapxstart -= finit_width; while(mapxstart < 0) mapxstart += finit_width; while(mapystart >= finit_height) mapystart -= finit_height; while(mapystart < 0) mapystart += finit_height; */ f_oldloc.x = plr->mo->x; f_oldloc.y = plr->mo->y; } } // Ripped out for Heretic /* void AM_updateLightLev(void) { static nexttic = 0; //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; static int litelevelscnt = 0; // Change light level if (amclock>nexttic) { lightlev = litelevels[litelevelscnt++]; if (litelevelscnt == sizeof(litelevels)/sizeof(int)) litelevelscnt = 0; nexttic = amclock + 6 - (amclock % 6); } } */ void AM_Ticker(void) { if (!automapactive) return; amclock++; if (followplayer) AM_doFollowPlayer(); // Change the zoom if necessary if (ftom_zoommul != FRACUNIT) AM_changeWindowScale(); // Change x,y location if (m_paninc.x || m_paninc.y) AM_changeWindowLoc(); // Update light level // AM_updateLightLev(); } void AM_clearFB(int color) { int i, j; int dmapx; int dmapy; if (followplayer) { dmapx = (MTOF(plr->mo->x) - MTOF(oldplr.x)); //fixed point dmapy = (MTOF(oldplr.y) - MTOF(plr->mo->y)); oldplr.x = plr->mo->x; oldplr.y = plr->mo->y; // if(f_oldloc.x == INT_MAX) //to eliminate an error when the user first // dmapx=0; //goes into the automap. mapxstart += dmapx >> 1; mapystart += dmapy >> 1; while (mapxstart >= (finit_width >> crispy->hires)) mapxstart -= (finit_width >> crispy->hires); while (mapxstart < 0) mapxstart += (finit_width >> crispy->hires); while (mapystart >= (finit_height >> crispy->hires)) mapystart -= (finit_height >> crispy->hires); while (mapystart < 0) mapystart += (finit_height >> crispy->hires); } else { // The released Heretic source does this here, but this causes a bug // where the map background keeps moving when we reach the map // boundaries. This is instead done in AM_changeWindowLoc. /* mapxstart += (MTOF(m_paninc.x) >> 1); mapystart -= (MTOF(m_paninc.y) >> 1); if (mapxstart >= (finit_width >> crispy->hires)) mapxstart -= (finit_width >> crispy->hires); if (mapxstart < 0) mapxstart += (finit_width >> crispy->hires); if (mapystart >= (finit_height >> crispy->hires)) mapystart -= (finit_height >> crispy->hires); if (mapystart < 0) mapystart += (finit_height >> crispy->hires); */ } //blit the automap background to the screen. j = (mapystart & ~crispy->hires) * (finit_width >> crispy->hires); for (i = 0; i < finit_height; i++) { memcpy(I_VideoBuffer + i * finit_width, maplump + j + mapxstart, finit_width - mapxstart); memcpy(I_VideoBuffer + i * finit_width + finit_width - mapxstart, maplump + j, mapxstart); j += finit_width; if (j >= (finit_height >> crispy->hires) * (finit_width >> crispy->hires)) j = 0; } // memcpy(I_VideoBuffer, maplump, finit_width*finit_height); // memset(fb, color, f_w*f_h); } // Based on Cohen-Sutherland clipping algorithm but with a slightly // faster reject and precalculated slopes. If I need the speed, will // hash algorithm to the common cases. boolean AM_clipMline(mline_t * ml, fline_t * fl) { enum { LEFT = 1, RIGHT = 2, BOTTOM = 4, TOP = 8 }; int outcode1 = 0, outcode2 = 0, outside; fpoint_t tmp = { 0, 0 }; int dx, dy; #define DOOUTCODE(oc, mx, my) \ (oc) = 0; \ if ((my) < 0) (oc) |= TOP; \ else if ((my) >= f_h) (oc) |= BOTTOM; \ if ((mx) < 0) (oc) |= LEFT; \ else if ((mx) >= f_w) (oc) |= RIGHT // do trivial rejects and outcodes if (ml->a.y > m_y2) outcode1 = TOP; else if (ml->a.y < m_y) outcode1 = BOTTOM; if (ml->b.y > m_y2) outcode2 = TOP; else if (ml->b.y < m_y) outcode2 = BOTTOM; if (outcode1 & outcode2) return false; // trivially outside if (ml->a.x < m_x) outcode1 |= LEFT; else if (ml->a.x > m_x2) outcode1 |= RIGHT; if (ml->b.x < m_x) outcode2 |= LEFT; else if (ml->b.x > m_x2) outcode2 |= RIGHT; if (outcode1 & outcode2) return false; // trivially outside // transform to frame-buffer coordinates. fl->a.x = CXMTOF(ml->a.x); fl->a.y = CYMTOF(ml->a.y); fl->b.x = CXMTOF(ml->b.x); fl->b.y = CYMTOF(ml->b.y); DOOUTCODE(outcode1, fl->a.x, fl->a.y); DOOUTCODE(outcode2, fl->b.x, fl->b.y); if (outcode1 & outcode2) return false; while (outcode1 | outcode2) { // may be partially inside box // find an outside point if (outcode1) outside = outcode1; else outside = outcode2; // clip to each side if (outside & TOP) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx * (fl->a.y)) / dy; tmp.y = 0; } else if (outside & BOTTOM) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx * (fl->a.y - f_h)) / dy; tmp.y = f_h - 1; } else if (outside & RIGHT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy * (f_w - 1 - fl->a.x)) / dx; tmp.x = f_w - 1; } else if (outside & LEFT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy * (-fl->a.x)) / dx; tmp.x = 0; } if (outside == outcode1) { fl->a = tmp; DOOUTCODE(outcode1, fl->a.x, fl->a.y); } else { fl->b = tmp; DOOUTCODE(outcode2, fl->b.x, fl->b.y); } if (outcode1 & outcode2) return false; // trivially outside } return true; } #undef DOOUTCODE // Classic Bresenham w/ whatever optimizations I need for speed void AM_drawFline(fline_t * fl, int color) { register int x, y, dx, dy, sx, sy, ax, ay, d; static int fuck = 0; switch (color) { case WALLCOLORS: DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[0][0], 8, 3); break; case FDWALLCOLORS: DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[1][0], 8, 3); break; case CDWALLCOLORS: DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[2][0], 8, 3); break; default: { // For debugging only if (fl->a.x < 0 || fl->a.x >= f_w || fl->a.y < 0 || fl->a.y >= f_h || fl->b.x < 0 || fl->b.x >= f_w || fl->b.y < 0 || fl->b.y >= f_h) { fprintf(stderr, "fuck %d \r", fuck++); return; } #define DOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) //the MACRO! 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) { DOT(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) { DOT(x, y, color); if (y == fl->b.y) return; if (d >= 0) { x += sx; d -= ay; } y += sy; d += ax; } } } } } /* Wu antialiased line drawer. * (X0,Y0),(X1,Y1) = line to draw * BaseColor = color # of first color in block used for antialiasing, the * 100% intensity version of the drawing color * NumLevels = size of color block, with BaseColor+NumLevels-1 being the * 0% intensity version of the drawing color * IntensityBits = log base 2 of NumLevels; the # of bits used to describe * the intensity of the drawing color. 2**IntensityBits==NumLevels */ void PUTDOT(short xx, short yy, byte * cc, byte * cm) { static int oldyy; static int oldyyshifted; byte *oldcc = cc; if (xx < 32) cc += 7 - (xx >> 2); else if (xx > (finit_width - 32)) cc += 7 - ((finit_width - xx) >> 2); // if(cc==oldcc) //make sure that we don't double fade the corners. // { if (yy < 32) cc += 7 - (yy >> 2); else if (yy > (finit_height - 32)) cc += 7 - ((finit_height - yy) >> 2); // } if (cc > cm && cm != NULL) { cc = cm; } else if (cc > oldcc + 6) // don't let the color escape from the fade table... { cc = oldcc + 6; } if (yy == oldyy + 1) { oldyy++; oldyyshifted += (320 << crispy->hires); } else if (yy == oldyy - 1) { oldyy--; oldyyshifted -= (320 << crispy->hires); } else if (yy != oldyy) { oldyy = yy; oldyyshifted = yy * (320 << crispy->hires); } fb[oldyyshifted + xx] = *(cc); // fb[(yy)*f_w+(xx)]=*(cc); } void DrawWuLine(int X0, int Y0, int X1, int Y1, byte * BaseColor, int NumLevels, unsigned short IntensityBits) { unsigned short IntensityShift, ErrorAdj, ErrorAcc; unsigned short ErrorAccTemp, Weighting, WeightingComplementMask; short DeltaX, DeltaY, Temp, XDir; /* Make sure the line runs top to bottom */ if (Y0 > Y1) { Temp = Y0; Y0 = Y1; Y1 = Temp; Temp = X0; X0 = X1; X1 = Temp; } /* Draw the initial pixel, which is always exactly intersected by the line and so needs no weighting */ PUTDOT(X0, Y0, &BaseColor[0], NULL); if ((DeltaX = X1 - X0) >= 0) { XDir = 1; } else { XDir = -1; DeltaX = -DeltaX; /* make DeltaX positive */ } /* Special-case horizontal, vertical, and diagonal lines, which require no weighting because they go right through the center of every pixel */ if ((DeltaY = Y1 - Y0) == 0) { /* Horizontal line */ while (DeltaX-- != 0) { X0 += XDir; PUTDOT(X0, Y0, &BaseColor[0], NULL); } return; } if (DeltaX == 0) { /* Vertical line */ do { Y0++; PUTDOT(X0, Y0, &BaseColor[0], NULL); } while (--DeltaY != 0); return; } //diagonal line. if (DeltaX == DeltaY) { do { X0 += XDir; Y0++; PUTDOT(X0, Y0, &BaseColor[0], NULL); } while (--DeltaY != 0); return; } /* Line is not horizontal, diagonal, or vertical */ ErrorAcc = 0; /* initialize the line error accumulator to 0 */ /* # of bits by which to shift ErrorAcc to get intensity level */ IntensityShift = 16 - IntensityBits; /* Mask used to flip all bits in an intensity weighting, producing the result (1 - intensity weighting) */ WeightingComplementMask = NumLevels - 1; /* Is this an X-major or Y-major line? */ if (DeltaY > DeltaX) { /* Y-major line; calculate 16-bit fixed-point fractional part of a pixel that X advances each time Y advances 1 pixel, truncating the result so that we won't overrun the endpoint along the X axis */ ErrorAdj = ((unsigned int) DeltaX << 16) / (unsigned int) DeltaY; /* Draw all pixels other than the first and last */ while (--DeltaY) { ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */ ErrorAcc += ErrorAdj; /* calculate error for next pixel */ if (ErrorAcc <= ErrorAccTemp) { /* The error accumulator turned over, so advance the X coord */ X0 += XDir; } Y0++; /* Y-major, so always advance Y */ /* The IntensityBits most significant bits of ErrorAcc give us the intensity weighting for this pixel, and the complement of the weighting for the paired pixel */ Weighting = ErrorAcc >> IntensityShift; PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]); PUTDOT(X0 + XDir, Y0, &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]); } /* Draw the final pixel, which is always exactly intersected by the line and so needs no weighting */ PUTDOT(X1, Y1, &BaseColor[0], NULL); return; } /* It's an X-major line; calculate 16-bit fixed-point fractional part of a pixel that Y advances each time X advances 1 pixel, truncating the result to avoid overrunning the endpoint along the X axis */ ErrorAdj = ((unsigned int) DeltaY << 16) / (unsigned int) DeltaX; /* Draw all pixels other than the first and last */ while (--DeltaX) { ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */ ErrorAcc += ErrorAdj; /* calculate error for next pixel */ if (ErrorAcc <= ErrorAccTemp) { /* The error accumulator turned over, so advance the Y coord */ Y0++; } X0 += XDir; /* X-major, so always advance X */ /* The IntensityBits most significant bits of ErrorAcc give us the intensity weighting for this pixel, and the complement of the weighting for the paired pixel */ Weighting = ErrorAcc >> IntensityShift; PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]); PUTDOT(X0, Y0 + 1, &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]); } /* Draw the final pixel, which is always exactly intersected by the line and so needs no weighting */ PUTDOT(X1, Y1, &BaseColor[0], NULL); } void AM_drawMline(mline_t * ml, int color) { static fline_t fl; if (AM_clipMline(ml, &fl)) AM_drawFline(&fl, color); // draws it on frame buffer using fb coords } void AM_drawGrid(int color) { fixed_t x, y; fixed_t start, end; mline_t ml; // Figure out start of vertical gridlines start = m_x; if ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)) start += (MAPBLOCKUNITS << FRACBITS) - ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)); end = m_x + m_w; // draw vertical gridlines ml.a.y = m_y; ml.b.y = m_y + m_h; for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS)) { ml.a.x = x; ml.b.x = x; AM_drawMline(&ml, color); } // Figure out start of horizontal gridlines start = m_y; if ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)) start += (MAPBLOCKUNITS << FRACBITS) - ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)); end = m_y + m_h; // draw horizontal gridlines ml.a.x = m_x; ml.b.x = m_x + m_w; for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS)) { ml.a.y = y; ml.b.y = y; AM_drawMline(&ml, color); } } void AM_drawWalls(void) { int i; static mline_t l; for (i = 0; i < numlines; i++) { l.a.x = lines[i].v1->x; l.a.y = lines[i].v1->y; l.b.x = lines[i].v2->x; l.b.y = lines[i].v2->y; if (cheating || (lines[i].flags & ML_MAPPED)) { if ((lines[i].flags & LINE_NEVERSEE) && !cheating) continue; if (!lines[i].backsector) { AM_drawMline(&l, WALLCOLORS + lightlev); } else { if (lines[i].special == 39) { // teleporters AM_drawMline(&l, WALLCOLORS + WALLRANGE / 2); } else if (lines[i].flags & ML_SECRET) // secret door { if (cheating) AM_drawMline(&l, 0); else AM_drawMline(&l, WALLCOLORS + lightlev); } else if (lines[i].special > 25 && lines[i].special < 35) { switch (lines[i].special) { case 26: case 32: AM_drawMline(&l, BLUEKEY); break; case 27: case 34: AM_drawMline(&l, YELLOWKEY); break; case 28: case 33: AM_drawMline(&l, GREENKEY); break; default: break; } } else if (lines[i].backsector->floorheight != lines[i].frontsector->floorheight) { AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change } else if (lines[i].backsector->ceilingheight != lines[i].frontsector->ceilingheight) { AM_drawMline(&l, CDWALLCOLORS + lightlev); // ceiling level change } else if (cheating) { AM_drawMline(&l, TSWALLCOLORS + lightlev); } } } else if (plr->powers[pw_allmap]) { if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS + 3); } } } void AM_rotate(fixed_t * x, fixed_t * y, angle_t a) { fixed_t tmpx; tmpx = FixedMul(*x, finecosine[a >> ANGLETOFINESHIFT]) - FixedMul(*y, finesine[a >> ANGLETOFINESHIFT]); *y = FixedMul(*x, finesine[a >> ANGLETOFINESHIFT]) + FixedMul(*y, finecosine[a >> ANGLETOFINESHIFT]); *x = tmpx; } void AM_drawLineCharacter(mline_t * lineguy, int lineguylines, fixed_t scale, angle_t angle, int color, fixed_t x, fixed_t y) { int i; mline_t l; for (i = 0; i < lineguylines; i++) { l.a.x = lineguy[i].a.x; l.a.y = lineguy[i].a.y; if (scale) { l.a.x = FixedMul(scale, l.a.x); l.a.y = FixedMul(scale, l.a.y); } if (angle) AM_rotate(&l.a.x, &l.a.y, angle); l.a.x += x; l.a.y += y; l.b.x = lineguy[i].b.x; l.b.y = lineguy[i].b.y; if (scale) { l.b.x = FixedMul(scale, l.b.x); l.b.y = FixedMul(scale, l.b.y); } if (angle) AM_rotate(&l.b.x, &l.b.y, angle); l.b.x += x; l.b.y += y; AM_drawMline(&l, color); } } void AM_drawPlayers(void) { int i; player_t *p; static int their_colors[] = { GREENKEY, YELLOWKEY, BLOODRED, BLUEKEY }; int their_color = -1; int color; if (!netgame) { /* if (cheating) AM_drawLineCharacter(cheat_player_arrow, NUMCHEATPLYRLINES, 0, plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); *///cheat key player pointer is the same as non-cheat pointer.. AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); return; } for (i = 0; i < MAXPLAYERS; i++) { their_color++; p = &players[i]; if (deathmatch && !singledemo && p != plr) { continue; } if (!playeringame[i]) continue; if (p->powers[pw_invisibility]) color = 102; // *close* to the automap color else color = their_colors[their_color]; AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle, color, p->mo->x, p->mo->y); } } void AM_drawThings(int colors, int colorrange) { int i; mobj_t *t; for (i = 0; i < numsectors; i++) { t = sectors[i].thinglist; while (t) { AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16 << FRACBITS, t->angle, colors + lightlev, t->x, t->y); t = t->snext; } } } /* void AM_drawMarks(void) { int i, fx, fy, w, h; for (i=0;iwidth); h = SHORT(marknums[i]->height); fx = CXMTOF(markpoints[i].x); fy = CYMTOF(markpoints[i].y); if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) V_DrawPatch(fx, fy, marknums[i]); } } } */ void AM_drawkeys(void) { if (KeyPoints[0].x != 0 || KeyPoints[0].y != 0) { AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, YELLOWKEY, KeyPoints[0].x, KeyPoints[0].y); } if (KeyPoints[1].x != 0 || KeyPoints[1].y != 0) { AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, GREENKEY, KeyPoints[1].x, KeyPoints[1].y); } if (KeyPoints[2].x != 0 || KeyPoints[2].y != 0) { AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, BLUEKEY, KeyPoints[2].x, KeyPoints[2].y); } } void AM_drawCrosshair(int color) { fb[(f_w * (f_h + 1)) / 2] = color; // single point for now } void AM_Drawer(void) { const char *level_name; int numepisodes; if (!automapactive) return; UpdateState |= I_FULLSCRN; AM_clearFB(BACKGROUND); if (grid) AM_drawGrid(GRIDCOLORS); AM_drawWalls(); AM_drawPlayers(); if (cheating == 2) AM_drawThings(THINGCOLORS, THINGRANGE); // AM_drawCrosshair(XHAIRCOLORS); // AM_drawMarks(); if (gameskill == sk_baby) { AM_drawkeys(); } if (gamemode == retail) { numepisodes = 5; } else { numepisodes = 3; } if (gameepisode <= numepisodes && gamemap < 10) { level_name = LevelNames[(gameepisode - 1) * 9 + gamemap - 1]; MN_DrTextA(DEH_String(level_name), 20, 145); } // I_Update(); // V_MarkRect(f_x, f_y, f_w, f_h); } crispy-doom-crispy-doom-5.6.4/src/heretic/am_map.h000066400000000000000000000055641360717211000220630ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __AMMAP_H__ #define __AMMAP_H__ // For use if I do walls with outsides/insides #define REDS 12*8 #define REDRANGE 1 //16 #define BLUES (256-4*16+8) #define BLUERANGE 1 //8 #define GREENS (33*8) #define GREENRANGE 1 //16 #define GRAYS (5*8) #define GRAYSRANGE 1 //16 #define BROWNS (14*8) #define BROWNRANGE 1 //16 #define YELLOWS 10*8 #define YELLOWRANGE 1 #define BLACK 0 #define WHITE 4*8 #define PARCH 13*8-1 #define BLOODRED 150 #define BLUEKEY 197 #define YELLOWKEY 144 #define GREENKEY 220 // Automap colors #define BACKGROUND PARCH #define YOURCOLORS WHITE #define YOURRANGE 0 #define WALLCOLORS REDS #define WALLRANGE REDRANGE #define TSWALLCOLORS GRAYS #define TSWALLRANGE GRAYSRANGE #define FDWALLCOLORS BROWNS #define FDWALLRANGE BROWNRANGE #define CDWALLCOLORS YELLOWS #define CDWALLRANGE YELLOWRANGE #define THINGCOLORS GREENS #define THINGRANGE GREENRANGE #define SECRETWALLCOLORS WALLCOLORS #define SECRETWALLRANGE WALLRANGE #define GRIDCOLORS (GRAYS + GRAYSRANGE/2) #define GRIDRANGE 0 #define XHAIRCOLORS GRAYS // drawing stuff #define FB 0 #define AM_NUMMARKPOINTS 10 #define AM_MSGHEADER (('a'<<24)+('m'<<16)) #define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) #define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) #define INITSCALEMTOF (.2*FRACUNIT) // scale on entry // how much the automap moves window per tic in frame-buffer coordinates #define F_PANINC 4 // moves 140 pixels in 1 second // how much zoom-in per tic #define M_ZOOMIN ((int) (1.02*FRACUNIT)) // goes to 2x in 1 second // how much zoom-out per tic #define M_ZOOMOUT ((int) (FRACUNIT/1.02)) // pulls out to 0.5x in 1 second // translates between frame-buffer and map distances #define FTOM(x) FixedMul(((x)<<16),scale_ftom) #define MTOF(x) (FixedMul((x),scale_mtof)>>16) // translates between frame-buffer and map coordinates #define CXMTOF(x) (f_x + MTOF((x)-m_x)) #define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) // the following is crap #define LINE_NEVERSEE ML_DONTDRAW typedef struct { int x, y; } fpoint_t; typedef struct { fpoint_t a, b; } fline_t; typedef vertex_t mpoint_t; typedef struct { mpoint_t a, b; } mline_t; typedef struct { fixed_t slp, islp; } islope_t; // extern int f_x, f_y, f_w, f_h; #endif crispy-doom-crispy-doom-5.6.4/src/heretic/ct_chat.c000066400000000000000000000275511360717211000222310ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Chat mode // #include #include #include "doomdef.h" #include "doomkeys.h" #include "deh_str.h" #include "i_input.h" #include "m_controls.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" #define QUEUESIZE 128 #define MESSAGESIZE 128 #define MESSAGELEN 265 #define CT_PLR_GREEN 1 #define CT_PLR_YELLOW 2 #define CT_PLR_RED 3 #define CT_PLR_BLUE 4 #define CT_PLR_ALL 5 #define CT_ESCAPE 6 // Public data boolean chatmodeon; // Private data void CT_queueChatChar(char ch); void CT_ClearChatMessage(int player); void CT_AddChar(int player, char c); void CT_BackSpace(int player); int head; int tail; byte ChatQueue[QUEUESIZE]; int chat_dest[MAXPLAYERS]; char chat_msg[MAXPLAYERS][MESSAGESIZE]; char plr_lastmsg[MAXPLAYERS][MESSAGESIZE + 9]; // add in the length of the pre-string int msgptr[MAXPLAYERS]; int msglen[MAXPLAYERS]; boolean cheated; static int FontABaseLump; const char *CT_FromPlrText[MAXPLAYERS] = { "GREEN: ", "YELLOW: ", "RED: ", "BLUE: " }; char *chat_macros[10]; boolean altdown; boolean shiftdown; //=========================================================================== // // CT_Init // // Initialize chat mode data //=========================================================================== void CT_Init(void) { int i; head = 0; //initialize the queue index tail = 0; chatmodeon = false; memset(ChatQueue, 0, QUEUESIZE); for (i = 0; i < MAXPLAYERS; i++) { chat_dest[i] = 0; msgptr[i] = 0; memset(plr_lastmsg[i], 0, MESSAGESIZE); memset(chat_msg[i], 0, MESSAGESIZE); } FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1; return; } //=========================================================================== // // CT_Stop // //=========================================================================== void CT_Stop(void) { chatmodeon = false; I_StopTextInput(); return; } // These keys are allowed by Vanilla Heretic: static boolean ValidChatChar(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '!' || c == '?' || c == ' ' || c == '\'' || c == ',' || c == '.' || c == '-' || c == '='; } //=========================================================================== // // CT_Responder // //=========================================================================== boolean CT_Responder(event_t * ev) { char *macro; int sendto; if (!netgame) { return false; } if (ev->data1 == KEY_LALT || ev->data2 == KEY_RALT) { altdown = (ev->type == ev_keydown); return false; } if (ev->data1 == KEY_RSHIFT) { shiftdown = (ev->type == ev_keydown); return false; } if (ev->type != ev_keydown) { return false; } if (!chatmodeon) { sendto = 0; if (ev->data1 == key_multi_msg) { sendto = CT_PLR_ALL; } else if (ev->data1 == key_multi_msgplayer[0]) { sendto = CT_PLR_GREEN; } else if (ev->data1 == key_multi_msgplayer[1]) { sendto = CT_PLR_YELLOW; } else if (ev->data1 == key_multi_msgplayer[2]) { sendto = CT_PLR_RED; } else if (ev->data1 == key_multi_msgplayer[3]) { sendto = CT_PLR_BLUE; } if (sendto == 0 || (sendto != CT_PLR_ALL && !playeringame[sendto - 1]) || sendto == consoleplayer + 1) { return false; } CT_queueChatChar(sendto); chatmodeon = true; I_StartTextInput(25, 10, SCREENWIDTH, 18); return true; } else { if (altdown) { if (ev->data1 >= '0' && ev->data1 <= '9') { if (ev->data1 == '0') { // macro 0 comes after macro 9 ev->data1 = '9' + 1; } macro = chat_macros[ev->data1 - '1']; CT_queueChatChar(KEY_ENTER); //send old message CT_queueChatChar(chat_dest[consoleplayer]); // chose the dest. while (*macro) { CT_queueChatChar(toupper(*macro++)); } CT_queueChatChar(KEY_ENTER); //send it off... CT_Stop(); return true; } } if (ev->data1 == KEY_ENTER) { CT_queueChatChar(KEY_ENTER); CT_Stop(); return true; } else if (ev->data1 == KEY_ESCAPE) { CT_queueChatChar(CT_ESCAPE); CT_Stop(); return true; } else if (ev->data1 == KEY_BACKSPACE) { CT_queueChatChar(KEY_BACKSPACE); return true; } else if (ValidChatChar(ev->data3)) { CT_queueChatChar(toupper(ev->data3)); return true; } } return false; } //=========================================================================== // // CT_Ticker // //=========================================================================== void CT_Ticker(void) { int i; int j; char c; int numplayers; for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) { continue; } if ((c = players[i].cmd.chatchar) != 0) { if (c <= 5) { chat_dest[i] = c; continue; } else if (c == CT_ESCAPE) { CT_ClearChatMessage(i); } else if (c == KEY_ENTER) { numplayers = 0; for (j = 0; j < MAXPLAYERS; j++) { numplayers += playeringame[j]; } CT_AddChar(i, 0); // set the end of message character if (numplayers > 2) { M_StringCopy(plr_lastmsg[i], DEH_String(CT_FromPlrText[i]), sizeof(plr_lastmsg[i])); M_StringConcat(plr_lastmsg[i], chat_msg[i], sizeof(plr_lastmsg[i])); } else { M_StringCopy(plr_lastmsg[i], chat_msg[i], sizeof(plr_lastmsg[i])); } if (i != consoleplayer && (chat_dest[i] == consoleplayer + 1 || chat_dest[i] == CT_PLR_ALL) && *chat_msg[i]) { P_SetMessage(&players[consoleplayer], plr_lastmsg[i], true); S_StartSound(NULL, sfx_chat); } else if (i == consoleplayer && (*chat_msg[i])) { if (numplayers > 1) { P_SetMessage(&players[consoleplayer], DEH_String("-MESSAGE SENT-"), true); S_StartSound(NULL, sfx_chat); } else { P_SetMessage(&players[consoleplayer], DEH_String("THERE ARE NO OTHER PLAYERS IN THE GAME!"), true); S_StartSound(NULL, sfx_chat); } } CT_ClearChatMessage(i); } else if (c == KEY_BACKSPACE) { CT_BackSpace(i); } else { CT_AddChar(i, c); } } } return; } //=========================================================================== // // CT_Drawer // //=========================================================================== void CT_Drawer(void) { int i; int x; patch_t *patch; if (chatmodeon) { x = 25; for (i = 0; i < msgptr[consoleplayer]; i++) { if (chat_msg[consoleplayer][i] < 33) { x += 6; } else { patch = W_CacheLumpNum(FontABaseLump + chat_msg[consoleplayer][i] - 33, PU_CACHE); V_DrawPatch(x, 10, patch); x += patch->width; } } V_DrawPatch(x, 10, W_CacheLumpName(DEH_String("FONTA59"), PU_CACHE)); BorderTopRefresh = true; UpdateState |= I_MESSAGES; } } //=========================================================================== // // CT_queueChatChar // //=========================================================================== void CT_queueChatChar(char ch) { if (((tail + 1) & (QUEUESIZE - 1)) == head) { // the queue is full return; } ChatQueue[tail] = ch; tail = (tail + 1) & (QUEUESIZE - 1); } //=========================================================================== // // CT_dequeueChatChar // //=========================================================================== char CT_dequeueChatChar(void) { byte temp; if (head == tail) { // queue is empty return 0; } temp = ChatQueue[head]; head = (head + 1) & (QUEUESIZE - 1); return temp; } //=========================================================================== // // CT_AddChar // //=========================================================================== void CT_AddChar(int player, char c) { patch_t *patch; if (msgptr[player] + 1 >= MESSAGESIZE || msglen[player] >= MESSAGELEN) { // full. return; } chat_msg[player][msgptr[player]] = c; msgptr[player]++; if (c < 33) { msglen[player] += 6; } else { patch = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); msglen[player] += patch->width; } } //=========================================================================== // // CT_BackSpace // // Backs up a space, when the user hits (obviously) backspace //=========================================================================== void CT_BackSpace(int player) { patch_t *patch; char c; if (msgptr[player] == 0) { // message is already blank return; } msgptr[player]--; c = chat_msg[player][msgptr[player]]; if (c < 33) { msglen[player] -= 6; } else { patch = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); msglen[player] -= patch->width; } chat_msg[player][msgptr[player]] = 0; } //=========================================================================== // // CT_ClearChatMessage // // Clears out the data for the chat message, but the player's message // is still saved in plrmsg. //=========================================================================== void CT_ClearChatMessage(int player) { memset(chat_msg[player], 0, MESSAGESIZE); msgptr[player] = 0; msglen[player] = 0; } crispy-doom-crispy-doom-5.6.4/src/heretic/ct_chat.h000066400000000000000000000020001360717211000222140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Chat mode stuff // #ifndef HERETIC_CT_CHAT_H #define HERETIC_CT_CHAT_H #define CT_PLR_GREEN 1 #define CT_PLR_YELLOW 2 #define CT_PLR_RED 3 #define CT_PLR_BLUE 4 #define CT_PLR_ALL 5 #define CT_KEY_GREEN 'g' #define CT_KEY_YELLOW 'y' #define CT_KEY_RED 'r' #define CT_KEY_BLUE 'b' #define CT_KEY_ALL 't' extern char *chat_macros[10]; #endif /* #ifndef HERETIC_CT_CHAT_H */ crispy-doom-crispy-doom-5.6.4/src/heretic/d_main.c000066400000000000000000000641221360717211000220460ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // D_main.c #include #include #include "txt_main.h" #include "txt_io.h" #include "net_client.h" #include "config.h" #include "ct_chat.h" #include "doomdef.h" #include "deh_main.h" #include "d_iwad.h" #include "i_endoom.h" #include "i_input.h" #include "i_joystick.h" #include "i_sound.h" #include "i_swap.h" // [crispy] SHORT() #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "m_argv.h" #include "m_config.h" #include "m_controls.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" #include "w_main.h" #include "v_video.h" #define CT_KEY_GREEN 'g' #define CT_KEY_YELLOW 'y' #define CT_KEY_RED 'r' #define CT_KEY_BLUE 'b' #define STARTUP_WINDOW_X 17 #define STARTUP_WINDOW_Y 7 GameMode_t gamemode = indetermined; const char *gamedescription = "unknown"; boolean nomonsters; // checkparm of -nomonsters boolean respawnparm; // checkparm of -respawn boolean debugmode; // checkparm of -debug boolean ravpic; // checkparm of -ravpic boolean cdrom; // true if cd-rom mode active boolean noartiskip; // whether shift-enter skips an artifact skill_t startskill; int startepisode; int startmap; int UpdateState; static int graphical_startup = 0; static boolean using_graphical_startup; static boolean main_loop_started = false; boolean autostart; extern boolean automapactive; boolean advancedemo; FILE *debugfile; static int show_endoom = 0; void D_ConnectNetGame(void); void D_CheckNetGame(void); void D_PageDrawer(void); void D_AdvanceDemo(void); boolean F_Responder(event_t * ev); //--------------------------------------------------------------------------- // // PROC D_ProcessEvents // // Send all the events of the given timestamp down the responder chain. // //--------------------------------------------------------------------------- void D_ProcessEvents(void) { event_t *ev; while ((ev = D_PopEvent()) != NULL) { if (F_Responder(ev)) { continue; } if (MN_Responder(ev)) { continue; } G_Responder(ev); } } //--------------------------------------------------------------------------- // // PROC DrawMessage // //--------------------------------------------------------------------------- void DrawMessage(void) { player_t *player; player = &players[consoleplayer]; if (player->messageTics <= 0 || !player->message) { // No message return; } MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message) / 2, 1); } //--------------------------------------------------------------------------- // // PROC D_Display // // Draw current display, possibly wiping it from the previous. // //--------------------------------------------------------------------------- static void CrispyDrawStats (void) { static short height, coord_x; char str[32]; player_t *const player = &players[consoleplayer]; if (!height || !coord_x) { const int FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1; const patch_t *const p = W_CacheLumpNum(FontABaseLump + 'A' - 33, PU_CACHE); height = SHORT(p->height) + 1; coord_x = ORIGWIDTH - 7 * SHORT(p->width); } if (crispy->automapstats == WIDGETS_ALWAYS || (automapactive && crispy->automapstats == WIDGETS_AUTOMAP)) { M_snprintf(str, sizeof(str), "K %d/%d", player->killcount, totalkills); MN_DrTextA(str, 0, 1*height); M_snprintf(str, sizeof(str), "I %d/%d", player->itemcount, totalitems); MN_DrTextA(str, 0, 2*height); M_snprintf(str, sizeof(str), "S %d/%d", player->secretcount, totalsecret); MN_DrTextA(str, 0, 3*height); } if (crispy->leveltime == WIDGETS_ALWAYS || (automapactive && crispy->leveltime == WIDGETS_AUTOMAP)) { const int time = leveltime / TICRATE; M_snprintf(str, sizeof(str), "%02d:%02d", time/60, time%60); MN_DrTextA(str, 0, 4*height); } if (crispy->playercoords == WIDGETS_ALWAYS || (automapactive && crispy->playercoords == WIDGETS_AUTOMAP)) { M_snprintf(str, sizeof(str), "X %-5d", player->mo->x>>FRACBITS); MN_DrTextA(str, coord_x, 1*height); M_snprintf(str, sizeof(str), "Y %-5d", player->mo->y>>FRACBITS); MN_DrTextA(str, coord_x, 2*height); M_snprintf(str, sizeof(str), "A %-5d", player->mo->angle/ANG1); MN_DrTextA(str, coord_x, 3*height); } } void R_ExecuteSetViewSize(void); extern boolean finalestage; void D_Display(void) { extern boolean askforquit; // Change the view size if needed if (setsizeneeded) { R_ExecuteSetViewSize(); } // // do buffered drawing // switch (gamestate) { case GS_LEVEL: if (!gametic) break; if (automapactive) AM_Drawer(); else R_RenderPlayerView(&players[displayplayer]); CT_Drawer(); UpdateState |= I_FULLVIEW; SB_Drawer(); CrispyDrawStats(); break; case GS_INTERMISSION: IN_Drawer(); break; case GS_FINALE: F_Drawer(); break; case GS_DEMOSCREEN: D_PageDrawer(); break; } if (testcontrols) { V_DrawMouseSpeedBox(testcontrols_mousespeed); } if (paused && !MenuActive && !askforquit) { if (!netgame) { V_DrawPatch(160, (viewwindowy >> crispy->hires) + 5, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE)); } else { V_DrawPatch(160, 70, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE)); } } // Handle player messages DrawMessage(); // Menu drawing MN_Drawer(); // Send out any new accumulation NetUpdate(); // Flush buffered stuff to screen I_FinishUpdate(); } // // D_GrabMouseCallback // // Called to determine whether to grab the mouse pointer // boolean D_GrabMouseCallback(void) { // 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 && !advancedemo; } //--------------------------------------------------------------------------- // // PROC D_DoomLoop // //--------------------------------------------------------------------------- void D_DoomLoop(void) { if (M_CheckParm("-debugfile")) { char filename[20]; M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer); debugfile = fopen(filename, "w"); } I_GraphicsCheckCommandLine(); I_SetGrabMouseCallback(D_GrabMouseCallback); I_InitGraphics(); main_loop_started = true; while (1) { // Frame syncronous IO operations I_StartFrame(); // Process one or more tics // Will run at least one tic TryRunTics(); // Move positional sounds S_UpdateSounds(players[consoleplayer].mo); D_Display(); } } /* =============================================================================== DEMO LOOP =============================================================================== */ static int demosequence; static int pagetic; static const char *pagename; /* ================ = = D_PageTicker = = Handles timing for warped projection = ================ */ void D_PageTicker(void) { if (--pagetic < 0) D_AdvanceDemo(); } /* ================ = = D_PageDrawer = ================ */ void D_PageDrawer(void) { V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE)); if (demosequence == 1) { V_DrawPatch(4, 160, W_CacheLumpName(DEH_String("ADVISOR"), PU_CACHE)); } UpdateState |= I_FULLSCRN; } /* ================= = = D_AdvanceDemo = = Called after each demo or intro demosequence finishes ================= */ void D_AdvanceDemo(void) { advancedemo = true; } void D_DoAdvanceDemo(void) { players[consoleplayer].playerstate = PST_LIVE; // don't reborn advancedemo = false; usergame = false; // can't save / end game here paused = false; gameaction = ga_nothing; demosequence = (demosequence + 1) % 7; switch (demosequence) { case 0: pagetic = 210; gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLE"); S_StartSong(mus_titl, false); break; case 1: pagetic = 140; gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLE"); break; case 2: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; G_DeferedPlayDemo(DEH_String("demo1")); break; case 3: pagetic = 200; gamestate = GS_DEMOSCREEN; pagename = DEH_String("CREDIT"); break; case 4: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; G_DeferedPlayDemo(DEH_String("demo2")); break; case 5: pagetic = 200; gamestate = GS_DEMOSCREEN; if (gamemode == shareware) { pagename = DEH_String("ORDER"); } else { pagename = DEH_String("CREDIT"); } break; case 6: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; G_DeferedPlayDemo(DEH_String("demo3")); break; } } /* ================= = = D_StartTitle = ================= */ void D_StartTitle(void) { gameaction = ga_nothing; demosequence = -1; D_AdvanceDemo(); } /* ============== = = D_CheckRecordFrom = = -recordfrom ============== */ void D_CheckRecordFrom(void) { int p; char *filename; //! // @vanilla // @category demo // @arg // // Record a demo, loading from the given filename. Equivalent // to -loadgame -record . p = M_CheckParmWithArgs("-recordfrom", 2); if (!p) return; filename = SV_Filename(myargv[p + 1][0] - '0'); G_LoadGame(filename); G_DoLoadGame(); // load the gameskill etc info from savegame G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]); D_DoomLoop(); // never returns free(filename); } /* =============== = = D_AddFile = =============== */ // MAPDIR should be defined as the directory that holds development maps // for the -wart # # command #define MAPDIR "\\data\\" #define SHAREWAREWADNAME "heretic1.wad" char *iwadfile; void wadprintf(void) { if (debugmode) { return; } } boolean D_AddFile(char *file) { wad_file_t *handle; printf(" adding %s\n", file); handle = W_AddFile(file); return handle != NULL; } //========================================================== // // Startup Thermo code // //========================================================== #define MSG_Y 9 #define THERM_X 14 #define THERM_Y 14 int thermMax; int thermCurrent; char smsg[80]; // status bar line // // Heretic startup screen shit // static int startup_line = STARTUP_WINDOW_Y; void hprintf(const char *string) { if (using_graphical_startup) { TXT_BGColor(TXT_COLOR_CYAN, 0); TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); TXT_GotoXY(STARTUP_WINDOW_X, startup_line); ++startup_line; TXT_Puts(string); TXT_UpdateScreen(); } // haleyjd: shouldn't be WATCOMC-only if (debugmode) puts(string); } void drawstatus(void) { int i; TXT_GotoXY(1, 24); TXT_BGColor(TXT_COLOR_BLUE, 0); TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); for (i=0; smsg[i] != '\0'; ++i) { TXT_PutChar(smsg[i]); } } static void status(const char *string) { if (using_graphical_startup) { M_StringConcat(smsg, string, sizeof(smsg)); drawstatus(); } } void DrawThermo(void) { static int last_progress = -1; int progress; int i; if (!using_graphical_startup) { return; } // No progress? Don't update the screen. progress = (50 * thermCurrent) / thermMax + 2; if (last_progress == progress) { return; } last_progress = progress; TXT_GotoXY(THERM_X, THERM_Y); TXT_FGColor(TXT_COLOR_BRIGHT_GREEN); TXT_BGColor(TXT_COLOR_GREEN, 0); for (i = 0; i < progress; i++) { TXT_PutChar(0xdb); } TXT_UpdateScreen(); } void initStartup(void) { byte *textScreen; byte *loading; if (!graphical_startup || debugmode || testcontrols) { using_graphical_startup = false; return; } if (!TXT_Init()) { using_graphical_startup = false; return; } I_InitWindowTitle(); I_InitWindowIcon(); // Blit main screen textScreen = TXT_GetScreenData(); loading = W_CacheLumpName(DEH_String("LOADING"), PU_CACHE); memcpy(textScreen, loading, 4000); // Print version string TXT_BGColor(TXT_COLOR_RED, 0); TXT_FGColor(TXT_COLOR_YELLOW); TXT_GotoXY(46, 2); TXT_Puts(HERETIC_VERSION_TEXT); TXT_UpdateScreen(); using_graphical_startup = true; } static void finishStartup(void) { if (using_graphical_startup) { TXT_Shutdown(); } } char tmsg[300]; void tprintf(const char *msg, int initflag) { printf("%s", msg); } // haleyjd: moved up, removed WATCOMC code void CleanExit(void) { DEH_printf("Exited from HERETIC.\n"); exit(1); } void CheckAbortStartup(void) { // haleyjd: removed WATCOMC // haleyjd FIXME: this should actually work in text mode too, but how to // get input before SDL video init? if(using_graphical_startup) { if(TXT_GetChar() == 27) CleanExit(); } } void IncThermo(void) { thermCurrent++; DrawThermo(); CheckAbortStartup(); } void InitThermo(int max) { thermMax = max; thermCurrent = 0; } // // Add configuration file variable bindings. // void D_BindVariables(void) { extern int screenblocks; extern int snd_Channels; int i; M_ApplyPlatformDefaults(); I_BindInputVariables(); I_BindVideoVariables(); I_BindJoystickVariables(); I_BindSoundVariables(); M_BindBaseControls(); M_BindHereticControls(); M_BindWeaponControls(); M_BindChatControls(MAXPLAYERS); key_multi_msgplayer[0] = CT_KEY_GREEN; key_multi_msgplayer[1] = CT_KEY_YELLOW; key_multi_msgplayer[2] = CT_KEY_RED; key_multi_msgplayer[3] = CT_KEY_BLUE; M_BindMenuControls(); M_BindMapControls(); NET_BindVariables(); M_BindIntVariable("mouse_sensitivity", &mouseSensitivity); M_BindIntVariable("sfx_volume", &snd_MaxVolume); M_BindIntVariable("music_volume", &snd_MusicVolume); M_BindIntVariable("screenblocks", &screenblocks); M_BindIntVariable("snd_channels", &snd_Channels); M_BindIntVariable("vanilla_savegame_limit", &vanilla_savegame_limit); M_BindIntVariable("vanilla_demo_limit", &vanilla_demo_limit); M_BindIntVariable("show_endoom", &show_endoom); M_BindIntVariable("graphical_startup", &graphical_startup); for (i=0; i<10; ++i) { char buf[12]; M_snprintf(buf, sizeof(buf), "chatmacro%i", i); M_BindStringVariable(buf, &chat_macros[i]); } // [crispy] bind "crispness" config variables M_BindIntVariable("crispy_automapstats", &crispy->automapstats); M_BindIntVariable("crispy_leveltime", &crispy->leveltime); M_BindIntVariable("crispy_playercoords", &crispy->playercoords); } // // Called at exit to display the ENDOOM screen (ENDTEXT in Heretic) // static void D_Endoom(void) { byte *endoom_data; // Disable ENDOOM? if (!show_endoom || testcontrols || !main_loop_started) { return; } endoom_data = W_CacheLumpName(DEH_String("ENDTEXT"), PU_STATIC); I_Endoom(endoom_data); } //--------------------------------------------------------------------------- // // PROC D_DoomMain // //--------------------------------------------------------------------------- void D_DoomMain(void) { GameMission_t gamemission; int p; char file[256]; char demolumpname[9]; I_PrintBanner(PACKAGE_STRING); I_AtExit(D_Endoom, false); //! // @category game // @vanilla // // Disable monsters. // nomonsters = M_ParmExists("-nomonsters"); //! // @category game // @vanilla // // Monsters respawn after being killed. // respawnparm = M_ParmExists("-respawn"); //! // @vanilla // // Take screenshots when F1 is pressed. // ravpic = M_ParmExists("-ravpic"); //! // @category obscure // @vanilla // // Allow artifacts to be used when the run key is held down. // noartiskip = M_ParmExists("-noartiskip"); debugmode = M_ParmExists("-debug"); startskill = sk_medium; startepisode = 1; startmap = 1; autostart = false; // // get skill / episode / map from parms // //! // @vanilla // @category net // // Start a deathmatch game. // if (M_ParmExists("-deathmatch")) { deathmatch = true; } //! // @category game // @arg // @vanilla // // Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of // 0 disables all monsters. // p = M_CheckParmWithArgs("-skill", 1); if (p) { startskill = myargv[p + 1][0] - '1'; autostart = true; } //! // @category game // @arg // @vanilla // // Start playing on episode n (1-4) // p = M_CheckParmWithArgs("-episode", 1); if (p) { startepisode = myargv[p + 1][0] - '0'; startmap = 1; autostart = true; } //! // @category game // @arg // @vanilla // // Start a game immediately, warping to level ExMy. // p = M_CheckParmWithArgs("-warp", 2); if (p && p < myargc - 2) { startepisode = myargv[p + 1][0] - '0'; startmap = myargv[p + 2][0] - '0'; autostart = true; } // // init subsystems // DEH_printf("V_Init: allocate screens.\n"); V_Init(); // Check for -CDROM cdrom = false; #ifdef _WIN32 //! // @category obscure // @platform windows // @vanilla // // Save configuration data and savegames in c:\heretic.cd, // allowing play from CD. // if (M_CheckParm("-cdrom")) { cdrom = true; } #endif if (cdrom) { M_SetConfigDir(DEH_String("c:\\heretic.cd")); } else { M_SetConfigDir(NULL); } // Load defaults before initing other systems DEH_printf("M_LoadDefaults: Load system defaults.\n"); D_BindVariables(); M_SetConfigFilenames("heretic.cfg", PROGRAM_PREFIX "heretic.cfg"); M_LoadDefaults(); I_AtExit(M_SaveDefaults, false); DEH_printf("Z_Init: Init zone memory allocation daemon.\n"); Z_Init(); DEH_printf("W_Init: Init WADfiles.\n"); iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission); if (iwadfile == NULL) { I_Error("Game mode indeterminate. No IWAD was found. Try specifying\n" "one with the '-iwad' command line parameter."); } D_AddFile(iwadfile); W_CheckCorrectIWAD(heretic); //! // @category mod // // Disable auto-loading of .wad files. // if (!M_ParmExists("-noautoload")) { char *autoload_dir; autoload_dir = M_GetAutoloadDir("heretic.wad"); DEH_AutoLoadPatches(autoload_dir); W_AutoLoadWADs(autoload_dir); free(autoload_dir); } // Load dehacked patches specified on the command line. DEH_ParseCommandLine(); // Load PWAD files. W_ParseCommandLine(); //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp. // p = M_CheckParmWithArgs("-playdemo", 1); if (!p) { //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp, determining the framerate // of the screen. // p = M_CheckParmWithArgs("-timedemo", 1); } if (p) { char *uc_filename = strdup(myargv[p + 1]); M_ForceUppercase(uc_filename); // In Vanilla, the filename must be specified without .lmp, // but make that optional. if (M_StringEndsWith(uc_filename, ".LMP")) { M_StringCopy(file, myargv[p + 1], sizeof(file)); } else { DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]); } free(uc_filename); if (D_AddFile(file)) { M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name, sizeof(demolumpname)); } else { // The file failed to load, but copy the original arg as a // demo name to make tricks like -playdemo demo1 possible. M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname)); } printf("Playing demo %s.\n", file); } // Generate the WAD hash table. Speed things up a bit. W_GenerateHashTable(); //! // @category demo // // Record or playback a demo without automatically quitting // after either level exit or player respawn. // demoextend = M_ParmExists("-demoextend"); if (W_CheckNumForName(DEH_String("E2M1")) == -1) { gamemode = shareware; gamedescription = "Heretic (shareware)"; } else if (W_CheckNumForName("EXTENDED") != -1) { // Presence of the EXTENDED lump indicates the retail version gamemode = retail; gamedescription = "Heretic: Shadow of the Serpent Riders"; } else { gamemode = registered; gamedescription = "Heretic (registered)"; } I_SetWindowTitle(gamedescription); savegamedir = M_GetSaveGameDir("heretic.wad"); I_PrintStartupBanner(gamedescription); if (M_ParmExists("-testcontrols")) { startepisode = 1; startmap = 1; autostart = true; testcontrols = true; } I_InitTimer(); I_InitSound(false); I_InitMusic(); tprintf("NET_Init: Init network subsystem.\n", 1); NET_Init (); D_ConnectNetGame(); // haleyjd: removed WATCOMC initStartup(); // // Build status bar line! // smsg[0] = 0; if (deathmatch) status(DEH_String("DeathMatch...")); if (nomonsters) status(DEH_String("No Monsters...")); if (respawnparm) status(DEH_String("Respawning...")); if (autostart) { char temp[64]; DEH_snprintf(temp, sizeof(temp), "Warp to Episode %d, Map %d, Skill %d ", startepisode, startmap, startskill + 1); status(temp); } wadprintf(); // print the added wadfiles tprintf(DEH_String("MN_Init: Init menu system.\n"), 1); MN_Init(); CT_Init(); tprintf(DEH_String("R_Init: Init Heretic refresh daemon."), 1); hprintf(DEH_String("Loading graphics")); R_Init(); tprintf("\n", 0); tprintf(DEH_String("P_Init: Init Playloop state.\n"), 1); hprintf(DEH_String("Init game engine.")); P_Init(); IncThermo(); tprintf(DEH_String("I_Init: Setting up machine state.\n"), 1); I_CheckIsScreensaver(); I_InitJoystick(); IncThermo(); tprintf(DEH_String("S_Init: Setting up sound.\n"), 1); S_Init(); //IO_StartupTimer(); S_Start(); tprintf(DEH_String("D_CheckNetGame: Checking network game status.\n"), 1); hprintf(DEH_String("Checking network game status.")); D_CheckNetGame(); IncThermo(); // haleyjd: removed WATCOMC tprintf(DEH_String("SB_Init: Loading patches.\n"), 1); SB_Init(); IncThermo(); // // start the apropriate game based on parms // D_CheckRecordFrom(); //! // @arg // @category demo // @vanilla // // Record a demo named x.lmp. // p = M_CheckParmWithArgs("-record", 1); if (p) { G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p + 1]); D_DoomLoop(); // Never returns } p = M_CheckParmWithArgs("-playdemo", 1); if (p) { singledemo = true; // Quit after one demo G_DeferedPlayDemo(demolumpname); D_DoomLoop(); // Never returns } p = M_CheckParmWithArgs("-timedemo", 1); if (p) { G_TimeDemo(demolumpname); D_DoomLoop(); // Never returns } //! // @category game // @arg // @vanilla // // Load the game in savegame slot s. // p = M_CheckParmWithArgs("-loadgame", 1); if (p && p < myargc - 1) { char *filename; filename = SV_Filename(myargv[p + 1][0] - '0'); G_LoadGame(filename); free(filename); } // Check valid episode and map if (autostart || netgame) { if (!D_ValidEpisodeMap(heretic, gamemode, startepisode, startmap)) { startepisode = 1; startmap = 1; } } if (gameaction != ga_loadgame) { UpdateState |= I_FULLSCRN; BorderNeedRefresh = true; if (autostart || netgame) { G_InitNew(startskill, startepisode, startmap); } else { D_StartTitle(); } } finishStartup(); D_DoomLoop(); // Never returns } crispy-doom-crispy-doom-5.6.4/src/heretic/d_net.c000066400000000000000000000123561360717211000217120ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM Network game communication and protocol, // all OS independend parts. // #include #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "doomdef.h" #include "m_argv.h" #include "m_misc.h" #include "w_checksum.h" #include "deh_main.h" #include "d_loop.h" ticcmd_t *netcmds; extern void D_DoAdvanceDemo(void); extern void D_ProcessEvents(void); extern void G_BuildTiccmd(ticcmd_t *cmd, int maketic); extern boolean G_CheckDemoStatus(void); // Called when a player leaves the game static void PlayerQuitGame(player_t *player) { static char exitmsg[80]; unsigned int player_num; player_num = player - players; // Note: // The Heretic source code does this, which doesn't actually work. // As a result, the exit message is never seen. M_StringCopy(exitmsg, "PLAYER 1 LEFT THE GAME", sizeof(exitmsg)); exitmsg[7] += player_num; players[consoleplayer].message = exitmsg; playeringame[player_num] = false; players[consoleplayer].message = exitmsg; // TODO: check if it is sensible to do this: if (demorecording) { G_CheckDemoStatus (); } } static void RunTic(ticcmd_t *cmds, boolean *ingame) { extern boolean advancedemo; unsigned int i; // Check for player quits. for (i = 0; i < MAXPLAYERS; ++i) { if (!demoplayback && playeringame[i] && !ingame[i]) { PlayerQuitGame(&players[i]); } } netcmds = cmds; // check that there are players in the game. if not, we cannot // run a tic. if (advancedemo) D_DoAdvanceDemo (); G_Ticker (); } static loop_interface_t doom_loop_interface = { D_ProcessEvents, G_BuildTiccmd, RunTic, MN_Ticker }; // Load game settings from the specified structure and // set global variables. static void LoadGameSettings(net_gamesettings_t *settings) { unsigned int i; deathmatch = settings->deathmatch; ticdup = settings->ticdup; startepisode = settings->episode; startmap = settings->map; startskill = settings->skill; // TODO startloadgame = settings->loadgame; lowres_turn = settings->lowres_turn; nomonsters = settings->nomonsters; respawnparm = settings->respawn_monsters; consoleplayer = settings->consoleplayer; if (lowres_turn) { printf("NOTE: Turning resolution is reduced; this is probably " "because there is a client recording a Vanilla demo.\n"); } for (i = 0; i < MAXPLAYERS; ++i) { playeringame[i] = i < settings->num_players; } } // Save the game settings from global variables to the specified // game settings structure. static void SaveGameSettings(net_gamesettings_t *settings) { // Fill in game settings structure with appropriate parameters // for the new game settings->deathmatch = deathmatch; settings->episode = startepisode; settings->map = startmap; settings->skill = startskill; // TODO settings->loadgame = startloadgame; settings->gameversion = exe_heretic_1_3; settings->nomonsters = nomonsters; settings->respawn_monsters = respawnparm; settings->timelimit = 0; settings->lowres_turn = M_ParmExists("-record") && !M_ParmExists("-longtics"); } static void InitConnectData(net_connect_data_t *connect_data) { connect_data->drone = false; connect_data->max_players = MAXPLAYERS; // // Connect data // // Game type fields: connect_data->gamemode = gamemode; connect_data->gamemission = heretic; // Are we recording a demo? Possibly set lowres turn mode connect_data->lowres_turn = M_ParmExists("-record") && !M_ParmExists("-longtics"); // Read checksums of our WAD directory and dehacked information W_Checksum(connect_data->wad_sha1sum); DEH_Checksum(connect_data->deh_sha1sum); connect_data->is_freedoom = 0; } void D_ConnectNetGame(void) { net_connect_data_t connect_data; InitConnectData(&connect_data); netgame = D_InitNetGame(&connect_data); //! // @category net // // Start the game playing as though in a netgame with a single // player. This can also be used to play back single player netgame // demos. // if (M_CheckParm("-solo-net") > 0) { netgame = true; } } // // D_CheckNetGame // Works out player numbers among the net participants // void D_CheckNetGame (void) { net_gamesettings_t settings; D_RegisterLoopCallbacks(&doom_loop_interface); if (netgame) { autostart = true; } SaveGameSettings(&settings); D_StartNetGame(&settings, NULL); LoadGameSettings(&settings); } crispy-doom-crispy-doom-5.6.4/src/heretic/deh_ammo.c000066400000000000000000000050121360717211000223610ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Ammo" sections in dehacked files // #include #include #include #include "doomdef.h" #include "doomtype.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "p_local.h" static void *DEH_AmmoStart(deh_context_t *context, char *line) { int ammo_number = 0; if (sscanf(line, "Ammo %i", &ammo_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (ammo_number < 0 || ammo_number >= NUMAMMO) { DEH_Warning(context, "Invalid ammo number: %i", ammo_number); return NULL; } return &maxammo[ammo_number]; } static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; int ivalue; int ammo_number; if (tag == NULL) return; ammo_number = ((int *) tag) - maxammo; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); if (!strcasecmp(variable_name, "Per ammo")) { // Heretic doesn't have a "per clip" ammo array, instead // it is per weapon. However, the weapon number lines // up with the ammo number if we add one. GetWeaponAmmo[ammo_number + 1] = ivalue; } else if (!strcasecmp(variable_name, "Max ammo")) { maxammo[ammo_number] = ivalue; } else { DEH_Warning(context, "Field named '%s' not found", variable_name); } } static void DEH_AmmoSHA1Hash(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "info.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "deh_mapping.h" #include "deh_htic.h" #include "p_action.h" typedef struct { int offsets[deh_hhe_num_versions]; void (*func)(); } hhe_action_pointer_t; // Offsets of action pointers within the Heretic executables. // Different versions have different offsets. // (Seriously Greg, was this really necessary? What was wrong with the // "copying action pointer from another frame" technique used in dehacked?) // Offset Action function // v1.0 v1.2 v1.3 static const hhe_action_pointer_t action_pointers[] = { { { 77680, 80144, 80208 }, A_AccTeleGlitter }, { { 78608, 81104, 81168 }, A_AddPlayerCorpse }, { { 115808, 118000, 118240 }, A_AddPlayerRain }, { { 112272, 114480, 114720 }, A_BeakAttackPL1 }, { { 112448, 114656, 114896 }, A_BeakAttackPL2 }, { { 111856, 114176, 114416 }, A_BeakRaise }, { { 111568, 113888, 114128 }, A_BeakReady }, { { 74640, 77120, 77184 }, A_BeastAttack }, { { 70480, 72992, 73056 }, A_BeastPuff }, { { 73120, 75600, 75664 }, A_BlueSpark }, { { 115456, 117648, 117888 }, A_BoltSpark }, { { 77344, 79808, 79872 }, A_BossDeath }, { { 69328, 71856, 71920 }, A_Chase }, { { 0, 80976, 81040 }, A_CheckBurnGone }, { { 78480, 80944, 81008 }, A_CheckSkullDone }, { { 78448, 80912, 80976 }, A_CheckSkullFloor }, { { 71376, 73888, 73952 }, A_ChicAttack }, { { 71488, 74000, 74064 }, A_ChicChase }, { { 71456, 73968, 74032 }, A_ChicLook }, { { 71520, 74032, 74096 }, A_ChicPain }, { { 75792, 78208, 78272 }, A_ClinkAttack }, { { 108432, 110816, 111056 }, A_ContMobjSound }, { { 114752, 116944, 117184 }, A_DeathBallImpact }, { { 70016, 72528, 72592 }, A_DripBlood }, { { 77472, 79936, 80000 }, A_ESound }, { { 76784, 79248, 79312 }, A_Explode }, { { 69872, 72400, 72464 }, A_FaceTarget }, { { 71568, 74080, 74144 }, A_Feathers }, { { 112928, 115136, 115376 }, A_FireBlasterPL1 }, { { 113072, 115280, 115520 }, A_FireBlasterPL2 }, { { 115232, 117424, 117664 }, A_FireCrossbowPL1 }, { { 115312, 117504, 117744 }, A_FireCrossbowPL2 }, { { 113152, 115360, 115600 }, A_FireGoldWandPL1 }, { { 113296, 115504, 115744 }, A_FireGoldWandPL2 }, { { 113760, 115968, 116208 }, A_FireMacePL1 }, { { 114624, 116816, 117056 }, A_FireMacePL2 }, { { 116368, 118544, 118784 }, A_FirePhoenixPL1 }, { { 116736, 118896, 119136 }, A_FirePhoenixPL2 }, { { 115568, 117760, 118000 }, A_FireSkullRodPL1 }, { { 115648, 117840, 118080 }, A_FireSkullRodPL2 }, { { 117120, 119280, 119520 }, A_FlameEnd }, { { 78704, 81200, 81264 }, A_FlameSnd }, { { 117152, 119312, 119552 }, A_FloatPuff }, { { 78512, 81008, 81072 }, A_FreeTargMobj }, { { 117184, 119344, 119584 }, A_GauntletAttack }, { { 73232, 75712, 75776 }, A_GenWizard }, { { 75872, 78304, 78368 }, A_GhostOff }, { { 74752, 77232, 77296 }, A_HeadAttack }, { { 75488, 77984, 78048 }, A_HeadFireGrow }, { { 75328, 77824, 77888 }, A_HeadIceImpact }, { { 116336, 118512, 118752 }, A_HideInCeiling }, { { 78736, 81232, 81296 }, A_HideThing }, { { 70976, 73488, 73552 }, A_ImpDeath }, { { 70304, 72816, 72880 }, A_ImpExplode }, { { 70592, 73104, 73168 }, A_ImpMeAttack }, { { 70672, 73184, 73248 }, A_ImpMsAttack }, { { 70880, 73392, 73456 }, A_ImpMsAttack2 }, { { 71024, 73536, 73600 }, A_ImpXDeath1 }, { { 71072, 73584, 73648 }, A_ImpXDeath2 }, { { 77728, 80192, 80256 }, A_InitKeyGizmo }, { { 116720, 118880, 119120 }, A_InitPhoenixPL2 }, { { 70160, 72672, 72736 }, A_KnightAttack }, { { 117648, 119824, 120064 }, A_Light0 }, { { 69200, 71728, 71792 }, A_Look }, { { 111760, 114080, 114320 }, A_Lower }, { { 114032, 116224, 116464 }, A_MaceBallImpact }, { { 114192, 116384, 116624 }, A_MaceBallImpact2 }, { { 113904, 116112, 116352 }, A_MacePL1Check }, { { 77104, 79568, 79632 }, A_MakePod }, { { 73648, 76128, 76192 }, A_MinotaurAtk1 }, { { 74112, 76592, 76656 }, A_MinotaurAtk2 }, { { 74352, 76832, 76896 }, A_MinotaurAtk3 }, { { 74032, 76512, 76576 }, A_MinotaurCharge }, { { 73760, 76240, 76304 }, A_MinotaurDecide }, { { 74528, 77008, 77072 }, A_MntrFloorFire }, { { 71808, 74288, 74352 }, A_MummyAttack }, { { 71920, 74400, 74464 }, A_MummyAttack2 }, { { 72016, 74496, 74560 }, A_MummyFX1Seek }, { { 72048, 74528, 74592 }, A_MummySoul }, { { 76400, 78832, 78896 }, A_NoBlocking }, { { 69984, 72496, 72560 }, A_Pain }, { { 116496, 118656, 118896 }, A_PhoenixPuff }, { { 76896, 79360, 79424 }, A_PodPain }, { { 116272, 118448, 118688 }, A_RainImpact }, { { 111920, 114240, 114480 }, A_Raise }, { { 111696, 114016, 114256 }, A_ReFire }, { { 77056, 79520, 79584 }, A_RemovePod }, { { 116480, 0, 0 }, A_RemovedPhoenixFunc }, { { 81952, 84464, 84528 }, A_RestoreArtifact }, { { 82048, 84544, 84608 }, A_RestoreSpecialThing1 }, { { 82128, 84592, 84656 }, A_RestoreSpecialThing2 }, { { 76144, 78576, 78640 }, A_Scream }, { { 117104, 119264, 119504 }, A_ShutdownPhoenixPL2 }, { { 78288, 80752, 80816 }, A_SkullPop }, { { 115776, 117968, 118208 }, A_SkullRodPL2Seek }, { { 115984, 118176, 118416 }, A_SkullRodStorm }, { { 75632, 78048, 78112 }, A_SnakeAttack }, { { 75712, 78128, 78192 }, A_SnakeAttack2 }, { { 72144, 74624, 74688 }, A_Sor1Chase }, { { 72096, 74576, 74640 }, A_Sor1Pain }, { { 73392, 75872, 75936 }, A_Sor2DthInit }, { { 73424, 75904, 75968 }, A_Sor2DthLoop }, { { 73584, 76064, 76128 }, A_SorDBon }, { { 73552, 76032, 76096 }, A_SorDExp }, { { 73520, 76000, 76064 }, A_SorDSph }, { { 73488, 75968, 76032 }, A_SorRise }, { { 73616, 76096, 76160 }, A_SorSightSnd }, { { 73456, 75936, 76000 }, A_SorZap }, { { 72480, 74960, 75024 }, A_SorcererRise }, { { 115088, 117280, 117520 }, A_SpawnRippers }, { { 77520, 79984, 80048 }, A_SpawnTeleGlitter }, { { 77600, 80064, 80128 }, A_SpawnTeleGlitter2 }, { { 72192, 74672, 74736 }, A_Srcr1Attack }, { { 72896, 75376, 75440 }, A_Srcr2Attack }, { { 72816, 75296, 75360 }, A_Srcr2Decide }, { { 112640, 114848, 115088 }, A_StaffAttackPL1 }, { { 112784, 114992, 115232 }, A_StaffAttackPL2 }, { { 78752, 81248, 81312 }, A_UnHideThing }, { { 78080, 80544, 80608 }, A_VolcBallImpact }, { { 77856, 80320, 80384 }, A_VolcanoBlast }, { { 77824, 80288, 80352 }, A_VolcanoSet }, { { 111168, 113488, 113728 }, A_WeaponReady }, { { 75168, 77664, 77728 }, A_WhirlwindSeek }, { { 75888, 78320, 78384 }, A_WizAtk1 }, { { 75920, 78352, 78416 }, A_WizAtk2 }, { { 75952, 78384, 78448 }, A_WizAtk3 }, }; DEH_BEGIN_MAPPING(state_mapping, state_t) DEH_MAPPING("Sprite number", sprite) DEH_MAPPING("Sprite subnumber", frame) DEH_MAPPING("Duration", tics) DEH_MAPPING("Next frame", nextstate) DEH_MAPPING("Unknown 1", misc1) DEH_MAPPING("Unknown 2", misc2) DEH_END_MAPPING static void DEH_FrameInit(void) { // Bit of a hack here: DEH_HereticInit(); } static void *DEH_FrameStart(deh_context_t *context, char *line) { int frame_number = 0; int mapped_frame_number; state_t *state; if (sscanf(line, "Frame %i", &frame_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } // Map the HHE frame number (which assumes a Heretic 1.0 state table) // to the internal frame number (which is is the Heretic 1.3 state table): mapped_frame_number = DEH_MapHereticFrameNumber(frame_number); if (mapped_frame_number < 0 || mapped_frame_number >= DEH_HERETIC_NUMSTATES) { DEH_Warning(context, "Invalid frame number: %i", frame_number); return NULL; } state = &states[mapped_frame_number]; return state; } static boolean GetActionPointerForOffset(int offset, void **result) { int i; // Special case. if (offset == 0) { *result = NULL; return true; } for (i=0; iaction = func; } else { // "Next frame" numbers need to undergo mapping. if (!strcasecmp(variable_name, "Next frame")) { ivalue = DEH_MapHereticFrameNumber(ivalue); } DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue); } } static void DEH_FrameSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include #include "doomtype.h" #include "dstrings.h" #include "z_zone.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_htic.h" #include "deh_main.h" // // Ok, Greg, the action pointers thing was bad enough, but this really // takes the biscuit. Why does HHE's text replacement address strings // by offset??!! The dehacked way was much nicer, why change it? // typedef struct { unsigned int offsets[deh_hhe_num_versions]; const char *string; } hhe_string_t; // Offsets String // v1.0 v1.2 v1.3 static const hhe_string_t strings[] = { { { 228, 228, 228 }, "PLAYPAL" }, { { 1240, 1252, 1252 }, "E1M1: THE DOCKS" }, { { 1260, 1272, 1272 }, "E1M2: THE DUNGEONS" }, { { 1280, 1292, 1292 }, "E1M3: THE GATEHOUSE" }, { { 1304, 1316, 1316 }, "E1M4: THE GUARD TOWER" }, { { 1328, 1340, 1340 }, "E1M5: THE CITADEL" }, { { 1348, 1360, 1360 }, "E1M6: THE CATHEDRAL" }, { { 1372, 1384, 1384 }, "E1M7: THE CRYPTS" }, { { 1392, 1404, 1404 }, "E1M8: HELL'S MAW" }, { { 1412, 1424, 1424 }, "E1M9: THE GRAVEYARD" }, { { 1436, 1448, 1448 }, "E2M1: THE CRATER" }, { { 1456, 1468, 1468 }, "E2M2: THE LAVA PITS" }, { { 1480, 1492, 1492 }, "E2M3: THE RIVER OF FIRE" }, { { 1508, 1520, 1520 }, "E2M4: THE ICE GROTTO" }, { { 1532, 1544, 1544 }, "E2M5: THE CATACOMBS" }, { { 1556, 1568, 1568 }, "E2M6: THE LABYRINTH" }, { { 1580, 1592, 1592 }, "E2M7: THE GREAT HALL" }, { { 1604, 1616, 1616 }, "E2M8: THE PORTALS OF CHAOS" }, { { 1632, 1644, 1644 }, "E2M9: THE GLACIER" }, { { 1652, 1664, 1664 }, "E3M1: THE STOREHOUSE" }, { { 1676, 1688, 1688 }, "E3M2: THE CESSPOOL" }, { { 1696, 1708, 1708 }, "E3M3: THE CONFLUENCE" }, { { 1720, 1732, 1732 }, "E3M4: THE AZURE FORTRESS" }, { { 1748, 1760, 1760 }, "E3M5: THE OPHIDIAN LAIR" }, { { 1776, 1788, 1788 }, "E3M6: THE HALLS OF FEAR" }, { { 1804, 1816, 1816 }, "E3M7: THE CHASM" }, { { 1824, 1836, 1836 }, "E3M8: D'SPARIL'S KEEP" }, { { 1848, 1860, 1860 }, "E3M9: THE AQUIFER" }, { { 0, 1880, 1880 }, "E4M1: CATAFALQUE" }, { { 0, 1900, 1900 }, "E4M2: BLOCKHOUSE" }, { { 0, 1920, 1920 }, "E4M3: AMBULATORY" }, { { 0, 1940, 1940 }, "E4M4: SEPULCHER" }, { { 0, 1960, 1960 }, "E4M5: GREAT STAIR" }, { { 0, 1980, 1980 }, "E4M6: HALLS OF THE APOSTATE" }, { { 0, 2012, 2012 }, "E4M7: RAMPARTS OF PERDITION" }, { { 0, 2044, 2044 }, "E4M8: SHATTERED BRIDGE" }, { { 0, 2068, 2068 }, "E4M9: MAUSOLEUM" }, { { 0, 2088, 2088 }, "E5M1: OCHRE CLIFFS" }, { { 0, 2108, 2108 }, "E5M2: RAPIDS" }, { { 0, 2124, 2124 }, "E5M3: QUAY" }, { { 0, 2136, 2136 }, "E5M4: COURTYARD" }, { { 0, 2156, 2156 }, "E5M5: HYDRATYR" }, { { 0, 2172, 2172 }, "E5M6: COLONNADE" }, { { 0, 2192, 2192 }, "E5M7: FOETID MANSE" }, { { 0, 2212, 2212 }, "E5M8: FIELD OF JUDGEMENT" }, { { 0, 2240, 2240 }, "E5M9: SKEIN OF D'SPARIL" }, { { 1868, 2268, 2268 }, "AUTOPAGE" }, { { 1880, 2280, 2280 }, "FOLLOW MODE ON" }, { { 1896, 2296, 2296 }, "FOLLOW MODE OFF" }, { { 1924, 2324, 2324 }, "GREEN: " }, { { 1936, 2336, 2336 }, "YELLOW: " }, { { 1948, 2348, 2348 }, "RED: " }, { { 1956, 2356, 2356 }, "BLUE: " }, { { 1964, 2364, 2364 }, "FONTA_S" }, { { 1972, 2372, 2372 }, "-MESSAGE SENT-" }, { { 1988, 2388, 2388 }, "THERE ARE NO OTHER PLAYERS IN THE GAME!" }, { { 2028, 2428, 2428 }, "FONTA59" }, { { 2036, 2504, 2504 }, "PAUSED" }, { { 2072, 2540, 2540 }, "ADVISOR" }, { { 2080, 2548, 2548 }, "TITLE" }, { { 2088, 2556, 2556 }, "demo1" }, { { 2096, 2564, 2564 }, "CREDIT" }, { { 2104, 2572, 2572 }, "demo2" }, { { 2112, 2580, 2580 }, "ORDER" }, { { 2120, 2588, 2588 }, "demo3" }, { { 2304, 2696, 2696 }, "Exited from HERETIC.\n" }, { { 2412, 2800, 2800 }, "c:\\heretic.cd" }, { { 2528, 2916, 2916 }, "Playing demo %s.lmp.\n" }, { { 2592, 2980, 2980 }, "V_Init: allocate screens.\n" }, { { 2620, 3008, 3008 }, "M_LoadDefaults: Load system defaults.\n" }, { { 2660, 3048, 3048 }, "Z_Init: Init zone memory allocation daemon.\n" }, { { 2708, 3096, 3096 }, "W_Init: Init WADfiles.\n" }, { { 2732, 3120, 3120 }, "E2M1" }, { { 0, 3128, 3128 }, "EXTENDED" }, { { 2740, 3140, 3140 }, "LOADING" }, { { 2748, 3148, 3148 }, "DeathMatch..." }, { { 2764, 3164, 3164 }, "No Monsters..." }, { { 2780, 3180, 3180 }, "Respawning..." }, { { 2796, 3196, 3196 }, "Warp to Episode %d, Map %d, Skill %d " }, { { 2836, 3236, 3236 }, "MN_Init: Init menu system.\n" }, { { 2864, 3264, 3264 }, "R_Init: Init Heretic refresh daemon." }, { { 2904, 3304, 3304 }, "Loading graphics" }, { { 2924, 3324, 3324 }, "P_Init: Init Playloop state." }, { { 2956, 3356, 3356 }, "Init game engine." }, { { 2976, 3376, 3376 }, "I_Init: Setting up machine state.\n" }, { { 3012, 3412, 3412 }, "D_CheckNetGame: Checking network game status.\n" }, { { 3060, 3460, 3460 }, "Checking network game status." }, { { 3092, 3492, 3492 }, "SB_Init: Loading patches.\n" }, { { 0, 3752, 3752 }, "PLAYER 1 LEFT THE GAME" }, { { 3508, 3932, 3932 }, "Network game synchronization aborted." }, { { 0, 3972, 3972 }, "Different DOOM versions cannot play a net game!" }, { { 3908, 4132, 4132 }, "SKY1" }, { { 3916, 4140, 4140 }, "SKY2" }, { { 3924, 4148, 4148 }, "SKY3" }, { { 3736, 4196, 4196 }, "NET GAME" }, { { 3748, 4208, 4208 }, "SAVE GAME" }, { { 3760, 4220, 4220 }, "Only %i deathmatch spots, 4 required" }, { { 3800, 4260, 4260 }, "version %i" }, { { 3828, 4372, 4372 }, "c:\\heretic.cd\\hticsav%d.hsg" }, { { 3856, 4400, 4400 }, "hticsav%d.hsg" }, { { 3896, 4416, 4416 }, "GAME SAVED" }, { { 4016, 4456, 4456 }, E1TEXT }, { { 4536, 4976, 4976 }, E2TEXT }, { { 5068, 5508, 5508 }, E3TEXT }, { { 0, 6072, 6072 }, E4TEXT }, { { 0, 6780, 6780 }, E5TEXT }, { { 5632, 7468, 7468 }, "FLOOR25" }, { { 5640, 7476, 7476 }, "FLATHUH1" }, { { 5652, 7488, 7488 }, "FLTWAWA2" }, { { 0, 7500, 7500 }, "FLOOR28" }, { { 0, 7508, 7508 }, "FLOOR08" }, { { 5664, 7516, 7516 }, "FONTA_S" }, { { 5704, 7524, 7524 }, "PLAYPAL" }, { { 5672, 7532, 7532 }, "FINAL1" }, { { 5680, 7540, 7540 }, "FINAL2" }, { { 5688, 7548, 7548 }, "E2PAL" }, { { 5696, 7556, 7556 }, "E2END" }, { { 7884, 7564, 7564 }, "TITLE" }, { { 5712, 7572, 7572 }, "ORDER" }, { { 0, 7580, 7580 }, "CREDIT" }, { { 5720, 7588, 7588 }, "IMPX" }, { { 5728, 7596, 7596 }, "ACLO" }, { { 5736, 7604, 7604 }, "PTN1" }, { { 5744, 7612, 7612 }, "SHLD" }, { { 5752, 7620, 7620 }, "SHD2" }, { { 5760, 7628, 7628 }, "BAGH" }, { { 5768, 7636, 7636 }, "SPMP" }, { { 5776, 7644, 7644 }, "INVS" }, { { 5784, 7652, 7652 }, "PTN2" }, { { 5792, 7660, 7660 }, "SOAR" }, { { 5800, 7668, 7668 }, "INVU" }, { { 5808, 7676, 7676 }, "PWBK" }, { { 5816, 7684, 7684 }, "EGGC" }, { { 5824, 7692, 7692 }, "EGGM" }, { { 5832, 7700, 7700 }, "FX01" }, { { 5840, 7708, 7708 }, "SPHL" }, { { 5848, 7716, 7716 }, "TRCH" }, { { 5856, 7724, 7724 }, "FBMB" }, { { 5864, 7732, 7732 }, "XPL1" }, { { 5872, 7740, 7740 }, "ATLP" }, { { 5880, 7748, 7748 }, "PPOD" }, { { 5888, 7756, 7756 }, "AMG1" }, { { 5896, 7764, 7764 }, "SPSH" }, { { 5904, 7772, 7772 }, "LVAS" }, { { 5912, 7780, 7780 }, "SLDG" }, { { 5920, 7788, 7788 }, "SKH1" }, { { 5928, 7796, 7796 }, "SKH2" }, { { 5936, 7804, 7804 }, "SKH3" }, { { 5944, 7812, 7812 }, "SKH4" }, { { 5952, 7820, 7820 }, "CHDL" }, { { 5960, 7828, 7828 }, "SRTC" }, { { 5968, 7836, 7836 }, "SMPL" }, { { 5976, 7844, 7844 }, "STGS" }, { { 5984, 7852, 7852 }, "STGL" }, { { 5992, 7860, 7860 }, "STCS" }, { { 6000, 7868, 7868 }, "STCL" }, { { 6008, 7876, 7876 }, "KFR1" }, { { 6016, 7884, 7884 }, "BARL" }, { { 6024, 7892, 7892 }, "BRPL" }, { { 6032, 7900, 7900 }, "MOS1" }, { { 6040, 7908, 7908 }, "MOS2" }, { { 6048, 7916, 7916 }, "WTRH" }, { { 6056, 7924, 7924 }, "HCOR" }, { { 6064, 7932, 7932 }, "KGZ1" }, { { 6072, 7940, 7940 }, "KGZB" }, { { 6080, 7948, 7948 }, "KGZG" }, { { 6088, 7956, 7956 }, "KGZY" }, { { 6096, 7964, 7964 }, "VLCO" }, { { 6104, 7972, 7972 }, "VFBL" }, { { 6112, 7980, 7980 }, "VTFB" }, { { 6120, 7988, 7988 }, "SFFI" }, { { 6128, 7996, 7996 }, "TGLT" }, { { 6136, 8004, 8004 }, "TELE" }, { { 6144, 8012, 8012 }, "STFF" }, { { 6152, 8020, 8020 }, "PUF3" }, { { 6160, 8028, 8028 }, "PUF4" }, { { 6168, 8036, 8036 }, "BEAK" }, { { 6176, 8044, 8044 }, "WGNT" }, { { 6184, 8052, 8052 }, "GAUN" }, { { 6192, 8060, 8060 }, "PUF1" }, { { 6200, 8068, 8068 }, "WBLS" }, { { 6208, 8076, 8076 }, "BLSR" }, { { 6216, 8084, 8084 }, "FX18" }, { { 6224, 8092, 8092 }, "FX17" }, { { 6232, 8100, 8100 }, "WMCE" }, { { 6240, 8108, 8108 }, "MACE" }, { { 6248, 8116, 8116 }, "FX02" }, { { 6256, 8124, 8124 }, "WSKL" }, { { 6264, 8132, 8132 }, "HROD" }, { { 6272, 8140, 8140 }, "FX00" }, { { 6280, 8148, 8148 }, "FX20" }, { { 6288, 8156, 8156 }, "FX21" }, { { 6296, 8164, 8164 }, "FX22" }, { { 6304, 8172, 8172 }, "FX23" }, { { 6312, 8180, 8180 }, "GWND" }, { { 6320, 8188, 8188 }, "PUF2" }, { { 6328, 8196, 8196 }, "WPHX" }, { { 6336, 8204, 8204 }, "PHNX" }, { { 6344, 8212, 8212 }, "FX04" }, { { 6352, 8220, 8220 }, "FX08" }, { { 6360, 8228, 8228 }, "FX09" }, { { 6368, 8236, 8236 }, "WBOW" }, { { 6376, 8244, 8244 }, "CRBW" }, { { 6384, 8252, 8252 }, "FX03" }, { { 6392, 8260, 8260 }, "BLOD" }, { { 6400, 8268, 8268 }, "PLAY" }, { { 6408, 8276, 8276 }, "FDTH" }, { { 6416, 8284, 8284 }, "BSKL" }, { { 6424, 8292, 8292 }, "CHKN" }, { { 6432, 8300, 8300 }, "MUMM" }, { { 6440, 8308, 8308 }, "FX15" }, { { 6448, 8316, 8316 }, "BEAS" }, { { 6456, 8324, 8324 }, "FRB1" }, { { 6464, 8332, 8332 }, "SNKE" }, { { 6472, 8340, 8340 }, "SNFX" }, { { 6480, 8348, 8348 }, "HEAD" }, { { 6488, 8356, 8356 }, "FX05" }, { { 6496, 8364, 8364 }, "FX06" }, { { 6504, 8372, 8372 }, "FX07" }, { { 6512, 8380, 8380 }, "CLNK" }, { { 6520, 8388, 8388 }, "WZRD" }, { { 6528, 8396, 8396 }, "FX11" }, { { 6536, 8404, 8404 }, "FX10" }, { { 6544, 8412, 8412 }, "KNIG" }, { { 6552, 8420, 8420 }, "SPAX" }, { { 6560, 8428, 8428 }, "RAXE" }, { { 6568, 8436, 8436 }, "SRCR" }, { { 6576, 8444, 8444 }, "FX14" }, { { 6584, 8452, 8452 }, "SOR2" }, { { 6592, 8460, 8460 }, "SDTH" }, { { 6600, 8468, 8468 }, "FX16" }, { { 6608, 8476, 8476 }, "MNTR" }, { { 6616, 8484, 8484 }, "FX12" }, { { 6624, 8492, 8492 }, "FX13" }, { { 6632, 8500, 8500 }, "AKYY" }, { { 6640, 8508, 8508 }, "BKYY" }, { { 6648, 8516, 8516 }, "CKYY" }, { { 6656, 8524, 8524 }, "AMG2" }, { { 6664, 8532, 8532 }, "AMM1" }, { { 6672, 8540, 8540 }, "AMM2" }, { { 6680, 8548, 8548 }, "AMC1" }, { { 6688, 8556, 8556 }, "AMC2" }, { { 6696, 8564, 8564 }, "AMS1" }, { { 6704, 8572, 8572 }, "AMS2" }, { { 6712, 8580, 8580 }, "AMP1" }, { { 6720, 8588, 8588 }, "AMP2" }, { { 6728, 8596, 8596 }, "AMB1" }, { { 6736, 8604, 8604 }, "AMB2" }, { { 6744, 8612, 8612 }, "K" }, { { 6748, 8616, 8616 }, "I" }, { { 6752, 8620, 8620 }, "L" }, { { 6756, 8624, 8624 }, "E" }, { { 6760, 8628, 8628 }, "R" }, { { 6764, 8632, 8632 }, "S" }, { { 6768, 8636, 8636 }, "PLAYPAL" }, { { 6776, 8644, 8644 }, "MAPE1" }, { { 6784, 8652, 8652 }, "MAPE2" }, { { 6792, 8660, 8660 }, "MAPE3" }, { { 6800, 8668, 8668 }, "IN_X" }, { { 6808, 8676, 8676 }, "IN_YAH" }, { { 6816, 8684, 8684 }, "FONTB16" }, { { 6824, 8692, 8692 }, "FONTB_S" }, { { 6832, 8700, 8700 }, "FONTB13" }, { { 6840, 8708, 8708 }, "FONTB15" }, { { 6848, 8716, 8716 }, "FONTB05" }, { { 6856, 8724, 8724 }, "FACEA0" }, { { 6864, 8732, 8732 }, "FACEB0" }, { { 6940, 8808, 8808 }, "FLOOR16" }, { { 6948, 8816, 8816 }, "FINISHED" }, { { 6960, 8828, 8828 }, "NOW ENTERING:" }, { { 6976, 8844, 8844 }, "KILLS" }, { { 6984, 8852, 8852 }, "ITEMS" }, { { 6992, 8860, 8860 }, "SECRETS" }, { { 7000, 8868, 8868 }, "TIME" }, { { 7008, 8876, 8876 }, "BONUS" }, { { 7016, 8884, 8884 }, "SECRET" }, { { 7024, 8892, 8892 }, "TOTAL" }, { { 7032, 8900, 8900 }, "VICTIMS" }, { { 7040, 8908, 8908 }, ":" }, { { 7044, 8912, 8912 }, "NEW GAME" }, { { 7056, 8924, 8924 }, "OPTIONS" }, { { 7064, 8932, 8932 }, "GAME FILES" }, { { 7076, 8944, 8944 }, "INFO" }, { { 7084, 8952, 8952 }, "QUIT GAME" }, { { 7096, 8964, 8964 }, "CITY OF THE DAMNED" }, { { 7116, 8984, 8984 }, "HELL'S MAW" }, { { 7128, 8996, 8996 }, "THE DOME OF D'SPARIL" }, { { 0, 9020, 9020 }, "THE OSSUARY" }, { { 0, 9032, 9032 }, "THE STAGNANT DEMESNE" }, { { 7152, 9056, 9056 }, "LOAD GAME" }, { { 7164, 9068, 9068 }, "SAVE GAME" }, { { 7176, 9080, 9080 }, "THOU NEEDETH A WET-NURSE" }, { { 7204, 9108, 9108 }, "YELLOWBELLIES-R-US" }, { { 7224, 9128, 9128 }, "BRINGEST THEM ONETH" }, { { 7244, 9148, 9148 }, "THOU ART A SMITE-MEISTER" }, { { 7272, 9176, 9176 }, "BLACK PLAGUE POSSESSES THEE" }, { { 7300, 9204, 9204 }, "END GAME" }, { { 7312, 9216, 9216 }, "MESSAGES : " }, { { 7324, 9228, 9228 }, "MOUSE SENSITIVITY" }, { { 7344, 9248, 9248 }, "MORE..." }, { { 7352, 9256, 9256 }, "SCREEN SIZE" }, { { 7364, 9268, 9268 }, "SFX VOLUME" }, { { 7376, 9280, 9280 }, "MUSIC VOLUME" }, { { 7416, 9296, 9296 }, "ARE YOU SURE YOU WANT TO QUIT?" }, { { 7448, 9328, 9328 }, "ARE YOU SURE YOU WANT TO END THE GAME?" }, { { 7488, 9368, 9368 }, "DO YOU WANT TO QUICKSAVE THE GAME NAMED" }, { { 7528, 9408, 9408 }, "DO YOU WANT TO QUICKLOAD THE GAME NAMED" }, { { 7392, 9448, 9448 }, "M_SKL00" }, { { 7400, 9456, 9456 }, "FONTA_S" }, { { 7408, 9464, 9464 }, "FONTB_S" }, { { 7568, 9472, 9472 }, "?" }, { { 7572, 9476, 9476 }, "M_SLCTR1" }, { { 7584, 9488, 9488 }, "M_SLCTR2" }, { { 7596, 9500, 9500 }, "M_HTIC" }, { { 7604, 9508, 9508 }, "c:\\heretic.cd\\hticsav%d.hsg" }, { { 7632, 9536, 9536 }, "hticsav%d.hsg" }, { { 7652, 9556, 9556 }, "M_FSLOT" }, { { 7660, 9564, 9564 }, "ON" }, { { 7664, 9568, 9568 }, "OFF" }, { { 0, 9572, 9572 }, "YOU CAN'T START A NEW GAME IN NETPLAY!" }, { { 0, 9612, 9612 }, "YOU CAN'T LOAD A GAME IN NETPLAY!" }, { { 7668, 9648, 9648 }, "MESSAGES ON" }, { { 7680, 9660, 9660 }, "MESSAGES OFF" }, { { 7748, 9676, 9676 }, "ONLY AVAILABLE IN THE REGISTERED VERSION" }, { { 7792, 9720, 9720 }, "PLAYPAL" }, { { 7800, 9728, 9728 }, "QUICKSAVING...." }, { { 7816, 9744, 9744 }, "QUICKLOADING...." }, { { 7836, 9764, 9764 }, "CHOOSE A QUICKSAVE SLOT" }, { { 7860, 9788, 9788 }, "CHOOSE A QUICKLOAD SLOT" }, { { 0, 9812, 9812 }, "TITLE" }, { { 7892, 9820, 9820 }, "M_SLDLT" }, { { 7900, 9828, 9828 }, "M_SLDMD1" }, { { 7912, 9840, 9840 }, "M_SLDMD2" }, { { 7924, 9852, 9852 }, "M_SLDRT" }, { { 7932, 9860, 9860 }, "M_SLDKB" }, { { 9016, 10944, 10944 }, "SCREEN SHOT" }, { { 9028, 10956, 10956 }, "YOU NEED A BLUE KEY TO OPEN THIS DOOR" }, { { 9068, 10996, 10996 }, "YOU NEED A YELLOW KEY TO OPEN THIS DOOR" }, { { 9108, 11036, 11036 }, "YOU NEED A GREEN KEY TO OPEN THIS DOOR" }, { { 9244, 11172, 11172 }, "CRYSTAL VIAL" }, { { 9260, 11188, 11188 }, "SILVER SHIELD" }, { { 9276, 11204, 11204 }, "ENCHANTED SHIELD" }, { { 9296, 11224, 11224 }, "BAG OF HOLDING" }, { { 9312, 11240, 11240 }, "MAP SCROLL" }, { { 9324, 11252, 11252 }, "BLUE KEY" }, { { 9336, 11264, 11264 }, "YELLOW KEY" }, { { 9348, 11276, 11276 }, "GREEN KEY" }, { { 9360, 11288, 11288 }, "QUARTZ FLASK" }, { { 9376, 11304, 11304 }, "WINGS OF WRATH" }, { { 9392, 11320, 11320 }, "RING OF INVINCIBILITY" }, { { 9416, 11344, 11344 }, "TOME OF POWER" }, { { 9432, 11360, 11360 }, "SHADOWSPHERE" }, { { 9448, 11376, 11376 }, "MORPH OVUM" }, { { 9460, 11388, 11388 }, "MYSTIC URN" }, { { 9472, 11400, 11400 }, "TORCH" }, { { 9480, 11408, 11408 }, "TIME BOMB OF THE ANCIENTS" }, { { 9508, 11436, 11436 }, "CHAOS DEVICE" }, { { 9524, 11452, 11452 }, "WAND CRYSTAL" }, { { 9540, 11468, 11468 }, "CRYSTAL GEODE" }, { { 9556, 11484, 11484 }, "MACE SPHERES" }, { { 9572, 11500, 11500 }, "PILE OF MACE SPHERES" }, { { 9596, 11524, 11524 }, "ETHEREAL ARROWS" }, { { 9612, 11540, 11540 }, "QUIVER OF ETHEREAL ARROWS" }, { { 9640, 11568, 11568 }, "CLAW ORB" }, { { 9652, 11580, 11580 }, "ENERGY ORB" }, { { 9664, 11592, 11592 }, "LESSER RUNES" }, { { 9680, 11608, 11608 }, "GREATER RUNES" }, { { 9696, 11624, 11624 }, "FLAME ORB" }, { { 9708, 11636, 11636 }, "INFERNO ORB" }, { { 9720, 11648, 11648 }, "FIREMACE" }, { { 9732, 11660, 11660 }, "ETHEREAL CROSSBOW" }, { { 9752, 11680, 11680 }, "DRAGON CLAW" }, { { 9764, 11692, 11692 }, "HELLSTAFF" }, { { 9776, 11704, 11704 }, "PHOENIX ROD" }, { { 9788, 11716, 11716 }, "GAUNTLETS OF THE NECROMANCER" }, { { 10088, 12016, 12016 }, "FLTWAWA1" }, { { 10100, 12028, 12028 }, "FLTFLWW1" }, { { 10112, 12040, 12040 }, "FLTLAVA1" }, { { 10124, 12052, 12052 }, "FLATHUH1" }, { { 10136, 12064, 12064 }, "FLTSLUD1" }, { { 10148, 12076, 12076 }, "END" }, { { 10236, 12164, 12164 }, "texture2" }, { { 10444, 12372, 12372 }, "PLAYPAL" }, { { 10596, 12488, 12488 }, "PNAMES" }, { { 10604, 12496, 12496 }, "TEXTURE1" }, { { 10616, 12508, 12508 }, "TEXTURE2" }, { { 10628, 12520, 12520 }, "S_END" }, { { 10636, 12528, 12528 }, "S_START" }, { { 10728, 12620, 12620 }, "F_START" }, { { 10736, 12628, 12628 }, "F_END" }, { { 10744, 12636, 12636 }, "COLORMAP" }, { { 10756, 12648, 12648 }, "\nR_InitTextures " }, { { 10776, 12668, 12668 }, "R_InitFlats\n" }, { { 10792, 12684, 12684 }, "R_InitSpriteLumps " }, { { 10948, 12772, 12772 }, "TINTTAB" }, { { 10984, 12780, 12780 }, "FLOOR04" }, { { 10992, 12788, 12788 }, "FLAT513" }, { { 11000, 12796, 12796 }, "bordt" }, { { 11008, 12804, 12804 }, "bordb" }, { { 11016, 12812, 12812 }, "bordl" }, { { 11024, 12820, 12820 }, "bordr" }, { { 11032, 12828, 12828 }, "bordtl" }, { { 11040, 12836, 12836 }, "bordtr" }, { { 11048, 12844, 12844 }, "bordbr" }, { { 11056, 12852, 12852 }, "bordbl" }, { { 11064, 12860, 12860 }, "R_InitData " }, { { 11076, 12872, 12872 }, "R_InitPointToAngle\n" }, { { 11096, 12892, 12892 }, "R_InitTables " }, { { 11112, 12908, 12908 }, "R_InitPlanes\n" }, { { 11128, 12924, 12924 }, "R_InitLightTables " }, { { 11148, 12944, 12944 }, "R_InitSkyMap\n" }, { { 11164, 12960, 12960 }, "F_SKY1" }, { { 12120, 13484, 13484 }, "LTFACE" }, { { 12128, 13492, 13492 }, "RTFACE" }, { { 12136, 13500, 13500 }, "BARBACK" }, { { 12144, 13508, 13508 }, "INVBAR" }, { { 12152, 13516, 13516 }, "CHAIN" }, { { 12160, 13524, 13524 }, "STATBAR" }, { { 12168, 13532, 13532 }, "LIFEBAR" }, { { 12176, 13540, 13540 }, "LIFEGEM2" }, { { 12188, 13552, 13552 }, "LIFEGEM0" }, { { 12200, 13564, 13564 }, "LTFCTOP" }, { { 12208, 13572, 13572 }, "RTFCTOP" }, { { 12224, 13580, 13580 }, "SELECTBOX" }, { { 12236, 13592, 13592 }, "INVGEML1" }, { { 12248, 13604, 13604 }, "INVGEML2" }, { { 12260, 13616, 13616 }, "INVGEMR1" }, { { 12272, 13628, 13628 }, "INVGEMR2" }, { { 12284, 13640, 13640 }, "BLACKSQ" }, { { 12292, 13648, 13648 }, "ARMCLEAR" }, { { 12304, 13660, 13660 }, "CHAINBACK" }, { { 12316, 13672, 13672 }, "IN0" }, { { 12320, 13676, 13676 }, "NEGNUM" }, { { 12328, 13684, 13684 }, "FONTB16" }, { { 12336, 13692, 13692 }, "SMALLIN0" }, { { 12348, 13704, 13704 }, "PLAYPAL" }, { { 12356, 13712, 13712 }, "SPINBK0" }, { { 12364, 13720, 13720 }, "SPFLY0" }, { { 12372, 13728, 13728 }, "LAME" }, { { 12380, 13736, 13736 }, "*** SOUND DEBUG INFO ***" }, { { 12408, 13764, 13764 }, "NAME" }, { { 12416, 13772, 13772 }, "MO.T" }, { { 12424, 13780, 13780 }, "MO.X" }, { { 12432, 13788, 13788 }, "MO.Y" }, { { 12440, 13796, 13796 }, "ID" }, { { 12444, 13800, 13800 }, "PRI" }, { { 12448, 13804, 13804 }, "DIST" }, { { 12456, 13812, 13812 }, "------" }, { { 12464, 13820, 13820 }, "%s" }, { { 12468, 13824, 13824 }, "%d" }, { { 12472, 13828, 13828 }, "GOD1" }, { { 12480, 13836, 13836 }, "GOD2" }, { { 12488, 13844, 13844 }, "useartia" }, { { 12500, 13856, 13856 }, "ykeyicon" }, { { 12512, 13868, 13868 }, "gkeyicon" }, { { 12524, 13880, 13880 }, "bkeyicon" }, { { 12216, 13892, 13892 }, "ARTIBOX" }, { { 12536, 13900, 13900 }, "GOD MODE ON" }, { { 12548, 13912, 13912 }, "GOD MODE OFF" }, { { 12564, 13928, 13928 }, "NO CLIPPING ON" }, { { 12580, 13944, 13944 }, "NO CLIPPING OFF" }, { { 12596, 13960, 13960 }, "ALL WEAPONS" }, { { 12608, 13972, 13972 }, "POWER OFF" }, { { 12620, 13984, 13984 }, "POWER ON" }, { { 12632, 13996, 13996 }, "FULL HEALTH" }, { { 12644, 14008, 14008 }, "ALL KEYS" }, { { 12656, 14020, 14020 }, "SOUND DEBUG ON" }, { { 12672, 14036, 14036 }, "SOUND DEBUG OFF" }, { { 12688, 14052, 14052 }, "TICKER ON" }, { { 12700, 14064, 14064 }, "TICKER OFF" }, { { 12712, 14076, 14076 }, "CHOOSE AN ARTIFACT ( A - J )" }, { { 12744, 14108, 14108 }, "HOW MANY ( 1 - 9 )" }, { { 12764, 14128, 14128 }, "YOU GOT IT" }, { { 12776, 14140, 14140 }, "BAD INPUT" }, { { 12788, 14152, 14152 }, "LEVEL WARP" }, { { 12800, 14164, 14164 }, "CHICKEN OFF" }, { { 12812, 14176, 14176 }, "CHICKEN ON" }, { { 12824, 14188, 14188 }, "MASSACRE" }, { { 12836, 14200, 14200 }, "CHEATER - YOU DON'T DESERVE WEAPONS" }, { { 12872, 14236, 14236 }, "TRYING TO CHEAT, EH? NOW YOU DIE!" }, }; // String offsets that are valid but we don't support. static const int unsupported_strings_1_0[] = { 0, 4, 64, 104, 160, 200, 220, 236, 244, 252, 272, 288, 296, 316, 332, 372, 436, 500, 504, 536, 544, 560, 576, 584, 592, 612, 640, 664, 708, 712, 744, 764, 808, 820, 828, 840, 876, 884, 908, 952, 992, 1028, 1036, 1048, 1088, 1128, 1160, 1192, 1212, 1912, 2044, 2056, 2068, 2128, 2140, 2168, 2184, 2196, 2212, 2228, 2240, 2252, 2260, 2264, 2284, 2292, 2296, 2300, 2328, 2340, 2352, 2364, 2372, 2384, 2388, 2404, 2428, 2436, 2444, 2464, 2496, 2508, 2520, 2552, 2564, 2572, 2584, 3120, 3128, 3140, 3184, 3220, 3248, 3252, 3256, 3280, 3304, 3320, 3352, 3380, 3400, 3432, 3464, 3548, 3600, 3624, 3664, 3696, 3812, 3872, 3932, 3940, 3976, 3996, 6872, 6896, 7648, 7696, 7940, 7964, 7968, 7992, 8020, 8028, 8052, 8056, 8076, 8088, 8104, 8116, 8128, 8136, 8148, 8164, 8180, 8192, 8204, 8220, 8232, 8248, 8264, 8276, 8292, 8308, 8320, 8328, 8340, 8352, 8364, 8376, 8392, 8408, 8424, 8436, 8448, 8460, 8472, 8488, 8504, 8520, 8536, 8548, 8560, 8572, 8584, 8596, 8608, 8612, 8624, 8648, 8660, 8668, 8680, 8708, 8720, 8728, 8740, 8752, 8764, 8788, 8800, 8812, 8824, 8848, 8860, 8864, 8868, 8876, 8888, 8896, 8916, 8944, 8948, 8960, 8964, 8968, 8980, 9148, 9172, 9212, 9216, 9220, 9820, 9860, 9892, 9940, 9972, 10012, 10036, 10040, 10052, 10080, 10152, 10192, 10248, 10284, 10320, 10360, 10392, 10452, 10488, 10508, 10556, 10644, 10684, 10812, 10844, 10880, 10912, 10956, 11172, 11200, 11232, 11272, 11312, 11348, 11380, 11404, 11436, 11492, 11548, 11616, 11684, 11748, 11792, 11840, 11896, 11936, 11980, 12028, 12072, 12908, 12924, 12956, 12960, 12968, 12976, 13020, 13048, 13076, 13104, 13136, 13168, 13196, 13240, 13272, 13292, 13296, 13308, 13312, 13320, 13324, 13364, 13408, 13460, 13492, 13516, 13560, 13612, 13664, 13700, 13744, 13796, 13848, 13884, 13940, 13996, 14040, 14084, 14140, 14148, 14156, 14164, 14184, 14192, 14204, 14208, 14212, 14256, 14272, 14284, 14296, 14300, 14312, 14320, 14324, 14348, 14356, 14360, 14372, 14380, 14392, 14432, 14440, 14444, 14472, 14496, 14516, 14536, 14548, 14560, 14572, 14580, 14588, 14596, 14604, 14612, 14620, 14636, 14660, 14704, 14740, 14748, 14756, 14760, 14768, -1, }; static const int unsupported_strings_1_2[] = { 0, 4, 64, 104, 160, 200, 220, 236, 244, 252, 272, 288, 296, 316, 332, 372, 436, 500, 504, 536, 544, 560, 576, 584, 592, 612, 640, 664, 708, 712, 744, 756, 776, 820, 832, 840, 852, 888, 896, 920, 964, 1004, 1040, 1048, 1060, 1100, 1140, 1172, 1204, 1224, 2312, 2436, 2448, 2464, 2480, 2492, 2512, 2524, 2536, 2596, 2608, 2636, 2652, 2656, 2676, 2684, 2688, 2720, 2732, 2744, 2752, 2764, 2772, 2776, 2792, 2816, 2824, 2832, 2852, 2884, 2896, 2908, 2940, 2952, 2960, 2972, 3520, 3528, 3540, 3584, 3620, 3648, 3652, 3656, 3680, 3704, 3720, 3776, 3804, 3824, 3856, 3888, 4020, 4044, 4084, 4116, 4156, 4272, 4288, 4296, 4332, 4352, 4428, 4432, 8740, 8764, 9552, 9868, 9888, 9900, 9916, 9928, 9940, 9948, 9960, 9976, 9992, 10004, 10016, 10032, 10044, 10060, 10076, 10088, 10104, 10120, 10132, 10140, 10152, 10164, 10176, 10188, 10204, 10220, 10236, 10248, 10260, 10272, 10284, 10300, 10316, 10332, 10348, 10360, 10372, 10384, 10396, 10408, 10420, 10424, 10436, 10460, 10472, 10480, 10492, 10520, 10532, 10540, 10552, 10564, 10576, 10600, 10612, 10624, 10636, 10660, 10672, 10676, 10700, 10704, 10728, 10756, 10764, 10788, 10792, 10796, 10804, 10816, 10824, 10844, 10872, 10876, 10888, 10892, 10896, 10908, 11076, 11100, 11140, 11144, 11148, 11748, 11788, 11820, 11868, 11900, 11940, 11964, 11968, 11980, 12008, 12080, 12120, 12176, 12212, 12248, 12288, 12320, 12380, 12400, 12448, 12536, 12576, 12704, 12736, 12968, 13000, 13024, 13080, 13136, 13204, 13272, 13336, 13380, 13428, 14272, 14288, 14320, 14324, 14332, 14340, 14384, 14412, 14440, 14468, 14500, 14532, 14560, 14604, 14636, 14656, 14696, 14740, 14792, 14824, 14848, 14892, 14944, 14996, 15032, 15076, 15128, 15180, 15216, 15272, 15328, 15372, 15416, 15472, 15480, 15488, 15496, 15516, 15524, 15536, 15540, 15544, 15588, 15604, 15616, 15628, 15632, 15644, 15652, 15656, 15680, 15688, 15692, 15704, 15712, 15724, 15764, 15772, 15776, 15804, 15828, 15848, 15868, 15880, 15892, 15904, 15912, 15920, 15928, 15936, -1, }; static const int unsupported_strings_1_3[] = { 0, 4, 64, 104, 160, 200, 220, 236, 244, 252, 272, 288, 296, 316, 332, 372, 436, 500, 504, 536, 544, 560, 576, 584, 592, 612, 640, 664, 708, 712, 744, 756, 776, 820, 832, 840, 852, 888, 896, 920, 964, 1004, 1040, 1048, 1060, 1100, 1140, 1172, 1204, 1224, 2312, 2436, 2448, 2464, 2480, 2492, 2512, 2524, 2536, 2596, 2608, 2636, 2652, 2656, 2676, 2684, 2688, 2720, 2732, 2744, 2752, 2764, 2772, 2776, 2792, 2816, 2824, 2832, 2852, 2884, 2896, 2908, 2940, 2952, 2960, 2972, 3520, 3528, 3540, 3584, 3620, 3648, 3652, 3656, 3680, 3704, 3720, 3776, 3804, 3824, 3856, 3888, 4020, 4044, 4084, 4116, 4156, 4272, 4288, 4296, 4332, 4352, 4428, 4432, 8740, 8764, 9552, 9868, 9888, 9900, 9916, 9928, 9940, 9948, 9960, 9976, 9992, 10004, 10016, 10032, 10044, 10060, 10076, 10088, 10104, 10120, 10132, 10140, 10152, 10164, 10176, 10188, 10204, 10220, 10236, 10248, 10260, 10272, 10284, 10300, 10316, 10332, 10348, 10360, 10372, 10384, 10396, 10408, 10420, 10424, 10436, 10460, 10472, 10480, 10492, 10520, 10532, 10540, 10552, 10564, 10576, 10600, 10612, 10624, 10636, 10660, 10672, 10676, 10700, 10704, 10728, 10756, 10764, 10788, 10792, 10796, 10804, 10816, 10824, 10844, 10872, 10876, 10888, 10892, 10896, 10908, 11076, 11100, 11140, 11144, 11148, 11748, 11788, 11820, 11868, 11900, 11940, 11964, 11968, 11980, 12008, 12080, 12120, 12176, 12212, 12248, 12288, 12320, 12380, 12400, 12448, 12536, 12576, 12704, 12736, 12968, 13000, 13024, 13080, 13136, 13204, 13272, 13336, 13380, 13428, 14272, 14288, 14320, 14324, 14332, 14340, 14384, 14412, 14440, 14468, 14500, 14532, 14560, 14604, 14636, 14656, 14696, 14740, 14792, 14824, 14848, 14892, 14944, 14996, 15032, 15076, 15128, 15180, 15216, 15272, 15328, 15372, 15416, 15472, 15480, 15488, 15496, 15516, 15524, 15536, 15540, 15544, 15588, 15604, 15616, 15628, 15632, 15644, 15652, 15656, 15680, 15688, 15692, 15704, 15712, 15724, 15764, 15772, 15776, 15804, 15828, 15848, 15868, 15880, 15892, 15904, 15912, 15920, 15928, 15936, -1, }; static const int *unsupported_strings[] = { unsupported_strings_1_0, unsupported_strings_1_2, unsupported_strings_1_3, }; static boolean StringIsUnsupported(unsigned int offset) { const int *string_list; int i; string_list = unsupported_strings[deh_hhe_version]; for (i=0; string_list[i] >= 0; ++i) { if ((unsigned int) string_list[i] == offset) { return true; } } return false; } static boolean GetStringByOffset(unsigned int offset, const char **result) { int i; for (i=0; i= 0; ++i) { if (string_list[i] == offset) { DEH_SuggestHereticVersion(v); } } } } static void *DEH_TextStart(deh_context_t *context, char *line) { char *repl_text; const char *orig_text; int orig_offset, repl_len; int i; if (sscanf(line, "Text %i %i", &orig_offset, &repl_len) != 2) { DEH_Warning(context, "Parse error on section start"); return NULL; } repl_text = malloc(repl_len + 1); // read in the "to" text for (i=0; i MaxStringLength(strlen(orig_text))) { DEH_Error(context, "Replacement string is longer than the maximum " "possible in heretic.exe"); } else { // Success. DEH_AddStringReplacement(orig_text, repl_text); } // We must always free the replacement text. free(repl_text); return NULL; } static void DEH_TextParseLine(deh_context_t *context, char *line, void *tag) { // not used } deh_section_t deh_section_heretic_text = { "Text", NULL, DEH_TextStart, DEH_TextParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/heretic/deh_htic.c000066400000000000000000000115511360717211000223640ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Top-level dehacked definitions for Heretic dehacked (HHE). // #include #include #include #include "deh_defs.h" #include "deh_main.h" #include "deh_htic.h" #include "info.h" #include "m_argv.h" const char *deh_signatures[] = { "Patch File for HHE v1.0", "Patch File for HHE v1.1", NULL }; static const char *hhe_versions[] = { "1.0", "1.2", "1.3" }; // Version number for patches. deh_hhe_version_t deh_hhe_version = deh_hhe_1_0; // deh_ammo.c: extern deh_section_t deh_section_ammo; // deh_frame.c: extern deh_section_t deh_section_frame; // deh_ptr.c: extern deh_section_t deh_section_pointer; // deh_sound.c extern deh_section_t deh_section_sound; // deh_htext.c: extern deh_section_t deh_section_heretic_text; // deh_thing.c: extern deh_section_t deh_section_thing; // deh_weapon.c: extern deh_section_t deh_section_weapon; // // List of section types: // deh_section_t *deh_section_types[] = { &deh_section_ammo, &deh_section_frame, // &deh_section_pointer, TODO &deh_section_sound, &deh_section_heretic_text, &deh_section_thing, &deh_section_weapon, NULL }; static void SetHHEVersionByName(char *name) { int i; for (i=0; i // @category mod // // Select the Heretic version number that was used to generate the // HHE patch to be loaded. Patches for each of the Vanilla // Heretic versions (1.0, 1.2, 1.3) can be loaded, but the correct // version number must be specified. i = M_CheckParm("-hhever"); if (i > 0) { SetHHEVersionByName(myargv[i + 1]); } // For v1.0 patches, we must apply a slight change to the states[] // table. The table was changed between 1.0 and 1.3 to add two extra // frames to the player "burning death" animation. // // If we are using a v1.0 patch, we must change the table to cut // these out again. if (deh_hhe_version < deh_hhe_1_2) { states[S_PLAY_FDTH18].nextstate = S_NULL; } } int DEH_MapHereticThingType(int type) { // Heretic 1.0 had an extra entry in the mobjinfo table that was removed // in later versions. This has been added back into the table for // compatibility. However, it also means that if we're loading a patch // for a later version, we need to translate to the index used internally. if (deh_hhe_version > deh_hhe_1_0) { if (type >= MT_PHOENIXFX_REMOVED) { ++type; } } return type; } int DEH_MapHereticFrameNumber(int frame) { if (deh_hhe_version < deh_hhe_1_2) { // Between Heretic 1.0 and 1.2, two new frames // were added to the "states" table, to extend the "flame death" // animation displayed when the player is killed by fire. Therefore, // we must map Heretic 1.0 frame numbers to corresponding indexes // for our state table. if (frame >= S_PLAY_FDTH19) { frame = (frame - S_PLAY_FDTH19) + S_BLOODYSKULL1; } } else { // After Heretic 1.2, three unused frames were removed from the // states table, unused phoenix rod frames. Our state table includes // these missing states for backwards compatibility. We must therefore // adjust frame numbers for v1.2/v1.3 to corresponding indexes for // our state table. if (frame >= S_PHOENIXFXIX_1) { frame = (frame - S_PHOENIXFXIX_1) + S_PHOENIXPUFF1; } } return frame; } void DEH_SuggestHereticVersion(deh_hhe_version_t version) { fprintf(stderr, "\n" "This patch may be for version %s. You are currently running in\n" "Heretic %s mode. For %s mode, add this to your command line:\n" "\n" "\t-hhever %s\n" "\n", hhe_versions[version], hhe_versions[deh_hhe_version], hhe_versions[version], hhe_versions[version]); } crispy-doom-crispy-doom-5.6.4/src/heretic/deh_htic.h000066400000000000000000000027141360717211000223720ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Common header for Heretic dehacked (HHE) support. // #ifndef DEH_HTIC_H #define DEH_HTIC_H #include "info.h" // HHE executable version. Loading HHE patches is (unfortunately) // dependent on the version of the Heretic executable used to make them. typedef enum { deh_hhe_1_0, deh_hhe_1_2, deh_hhe_1_3, deh_hhe_num_versions } deh_hhe_version_t; // HHE doesn't know about the last two states in the state table, so // these are considered invalid. #define DEH_HERETIC_NUMSTATES (NUMSTATES - 2) // It also doesn't know about the last two things in the mobjinfo table // (which correspond to the states above) #define DEH_HERETIC_NUMMOBJTYPES (NUMMOBJTYPES - 2) void DEH_HereticInit(void); int DEH_MapHereticThingType(int type); int DEH_MapHereticFrameNumber(int frame); void DEH_SuggestHereticVersion(deh_hhe_version_t version); extern deh_hhe_version_t deh_hhe_version; #endif /* #ifndef DEH_HTIC_H */ crispy-doom-crispy-doom-5.6.4/src/heretic/deh_sound.c000066400000000000000000000051211360717211000225610ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Sound" sections in dehacked files // #include #include #include "doomtype.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" #include "doomdef.h" #include "i_sound.h" #include "sounds.h" DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t) DEH_MAPPING_STRING("Name", name) DEH_UNSUPPORTED_MAPPING("Special") DEH_MAPPING("Value", priority) DEH_MAPPING("Unknown 1", usefulness) DEH_UNSUPPORTED_MAPPING("Unknown 2") DEH_UNSUPPORTED_MAPPING("Unknown 3") DEH_MAPPING("One/Two", numchannels) DEH_END_MAPPING static void *DEH_SoundStart(deh_context_t *context, char *line) { int sound_number = 0; if (sscanf(line, "Sound %i", &sound_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (sound_number < 0 || sound_number >= NUMSFX) { DEH_Warning(context, "Invalid sound number: %i", sound_number); return NULL; } if (sound_number >= DEH_VANILLA_NUMSFX) { DEH_Warning(context, "Attempt to modify SFX %i. This will cause " "problems in Vanilla dehacked.", sound_number); } return &S_sfx[sound_number]; } static void DEH_SoundParseLine(deh_context_t *context, char *line, void *tag) { sfxinfo_t *sfx; char *variable_name, *value; if (tag == NULL) return; sfx = (sfxinfo_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // Set the field value: if (!strcasecmp(variable_name, "Name")) { DEH_SetStringMapping(context, &sound_mapping, sfx, variable_name, value); } else { DEH_SetMapping(context, &sound_mapping, sfx, variable_name, atoi(value)); } } deh_section_t deh_section_sound = { "Sound", NULL, DEH_SoundStart, DEH_SoundParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/heretic/deh_thing.c000066400000000000000000000075621360717211000225550ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Thing" sections in dehacked files // #include #include #include "doomtype.h" #include "m_misc.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" #include "deh_htic.h" #include "info.h" DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t) DEH_MAPPING("ID #", doomednum) DEH_MAPPING("Initial frame", spawnstate) DEH_MAPPING("Hit points", spawnhealth) DEH_MAPPING("First moving frame", seestate) DEH_MAPPING("Alert sound", seesound) DEH_MAPPING("Reaction time", reactiontime) DEH_MAPPING("Attack sound", attacksound) DEH_MAPPING("Injury frame", painstate) DEH_MAPPING("Pain chance", painchance) DEH_MAPPING("Pain sound", painsound) DEH_MAPPING("Close attack frame", meleestate) DEH_MAPPING("Far attack frame", missilestate) DEH_MAPPING("Burning frame", crashstate) DEH_MAPPING("Death frame", deathstate) DEH_MAPPING("Exploding frame", xdeathstate) DEH_MAPPING("Death sound", deathsound) DEH_MAPPING("Speed", speed) DEH_MAPPING("Width", radius) DEH_MAPPING("Height", height) DEH_MAPPING("Mass", mass) DEH_MAPPING("Missile damage", damage) DEH_MAPPING("Action sound", activesound) DEH_MAPPING("Bits 1", flags) DEH_MAPPING("Bits 2", flags2) DEH_END_MAPPING static void *DEH_ThingStart(deh_context_t *context, char *line) { int orig_thing_number = 0, thing_number = 0; mobjinfo_t *mobj; if (sscanf(line, "Thing %i", &orig_thing_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } // Translate to the correct thing number based on the exe version this // patch was made for. Subtract one because HHE thing numbers are // indexed from 1. thing_number = DEH_MapHereticThingType(orig_thing_number - 1); if (thing_number < 0 || thing_number >= DEH_HERETIC_NUMMOBJTYPES) { DEH_Warning(context, "Invalid thing number: %i", orig_thing_number); return NULL; } mobj = &mobjinfo[thing_number]; return mobj; } static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag) { mobjinfo_t *mobj; char *variable_name, *value; int ivalue; if (tag == NULL) return; mobj = (mobjinfo_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // all values are integers ivalue = atoi(value); // If the value to be set is a frame, the frame number must // undergo transformation from a Heretic 1.0 index to a // Heretic 1.3 index. if (M_StrCaseStr(variable_name, "frame") != NULL) { ivalue = DEH_MapHereticFrameNumber(ivalue); } // Set the field value DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue); } static void DEH_ThingSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include #include "doomtype.h" #include "m_misc.h" #include "doomdef.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" #include "deh_htic.h" DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t) DEH_MAPPING("Ammo type", ammo) DEH_MAPPING("Deselect frame", upstate) DEH_MAPPING("Select frame", downstate) DEH_MAPPING("Bobbing frame", readystate) DEH_MAPPING("Shooting frame", atkstate) DEH_MAPPING("Firing frame", holdatkstate) DEH_MAPPING("Unknown frame", flashstate) DEH_END_MAPPING static void *DEH_WeaponStart(deh_context_t *context, char *line) { int weapon_number = 0; if (sscanf(line, "Weapon %i", &weapon_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (weapon_number < 0 || weapon_number >= NUMWEAPONS * 2) { DEH_Warning(context, "Invalid weapon number: %i", weapon_number); return NULL; } // Because of the tome of power, we have two levels of weapons: if (weapon_number < NUMWEAPONS) { return &wpnlev1info[weapon_number]; } else { return &wpnlev2info[weapon_number - NUMWEAPONS]; } } static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; weaponinfo_t *weapon; int ivalue; if (tag == NULL) return; weapon = (weaponinfo_t *) tag; if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); // If this is a frame field, we need to map from Heretic 1.0 frame // numbers to Heretic 1.3 frame numbers. if (M_StrCaseStr(variable_name, "frame") != NULL) { ivalue = DEH_MapHereticFrameNumber(ivalue); } DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue); } static void DEH_WeaponSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include //haleyjd: removed WATCOMC #include #define HERETIC_VERSION 130 #define HERETIC_VERSION_TEXT "v1.3" // if rangecheck is undefined, most parameter validation debugging code // will not be compiled //#define RANGECHECK // all external data is defined here #include "doomdata.h" // all important printed strings #include "dstrings.h" // header generated by multigen utility #include "info.h" // WAD file access #include "w_wad.h" // fixed_t #include "m_fixed.h" // angle_t #include "tables.h" // events #include "d_event.h" // gamemode/mission #include "d_mode.h" // ticcmd_t #include "d_ticcmd.h" #include "d_loop.h" #define SAVEGAMENAME "hticsav" /* =============================================================================== GLOBAL TYPES =============================================================================== */ #define NUMARTIFCTS 28 #define MAXPLAYERS 4 #define BT_ATTACK 1 #define BT_USE 2 #define BT_CHANGE 4 // if true, the next 3 bits hold weapon num #define BT_WEAPONMASK (8+16+32) #define BT_WEAPONSHIFT 3 #define BT_SPECIAL 128 // game events, not really buttons #define BTS_SAVEMASK (4+8+16) #define BTS_SAVESHIFT 2 #define BT_SPECIALMASK 3 #define BTS_PAUSE 1 // pause the game #define BTS_SAVEGAME 2 // save the game at each console // savegame slot numbers occupy the second byte of buttons typedef enum { GS_LEVEL, GS_INTERMISSION, GS_FINALE, GS_DEMOSCREEN } gamestate_t; typedef enum { ga_nothing, ga_loadlevel, ga_newgame, ga_loadgame, ga_savegame, ga_playdemo, ga_completed, ga_victory, ga_worlddone, ga_screenshot } gameaction_t; typedef enum { wipe_0, wipe_1, wipe_2, wipe_3, wipe_4, NUMWIPES, wipe_random } wipe_t; /* =============================================================================== MAPOBJ DATA =============================================================================== */ // think_t is a function pointer to a routine to handle an actor typedef void (*think_t) (); typedef struct thinker_s { struct thinker_s *prev, *next; think_t function; } thinker_t; typedef union { int i; struct mobj_s *m; } specialval_t; struct player_s; typedef struct mobj_s { thinker_t thinker; // thinker links // info for drawing fixed_t x, y, z; struct mobj_s *snext, *sprev; // links in sector (if needed) angle_t angle; spritenum_t sprite; // used to find patch_t and flip value int frame; // might be ord with FF_FULLBRIGHT // interaction info struct mobj_s *bnext, *bprev; // links in blocks (if needed) struct subsector_s *subsector; fixed_t floorz, ceilingz; // closest together of contacted secs fixed_t radius, height; // for movement checking fixed_t momx, momy, momz; // momentums int validcount; // if == validcount, already checked mobjtype_t type; mobjinfo_t *info; // &mobjinfo[mobj->type] int tics; // state tic counter state_t *state; int damage; // For missiles int flags; int flags2; // Heretic flags specialval_t special1; // Special info specialval_t special2; // Special info int health; int movedir; // 0-7 int movecount; // when 0, select a new dir struct mobj_s *target; // thing being chased/attacked (or NULL) // also the originator for missiles int reactiontime; // if non 0, don't attack yet // used by player to freeze a bit after // teleporting int threshold; // if >0, the target will be chased // no matter what (even if shot) struct player_s *player; // only valid if type == MT_PLAYER int lastlook; // player number last looked for mapthing_t spawnpoint; // for nightmare respawn } mobj_t; // each sector has a degenmobj_t in it's center for sound origin purposes typedef struct { thinker_t thinker; // not used for anything fixed_t x, y, z; } degenmobj_t; // // frame flags // #define FF_FULLBRIGHT 0x8000 // flag in thing->frame #define FF_FRAMEMASK 0x7fff // --- mobj.flags --- #define MF_SPECIAL 1 // call P_SpecialThing when touched #define MF_SOLID 2 #define MF_SHOOTABLE 4 #define MF_NOSECTOR 8 // don't use the sector links // (invisible but touchable) #define MF_NOBLOCKMAP 16 // don't use the blocklinks // (inert but displayable) #define MF_AMBUSH 32 #define MF_JUSTHIT 64 // try to attack right back #define MF_JUSTATTACKED 128 // take at least one step before attacking #define MF_SPAWNCEILING 256 // hang from ceiling instead of floor #define MF_NOGRAVITY 512 // don't apply gravity every tic // movement flags #define MF_DROPOFF 0x400 // allow jumps from high places #define MF_PICKUP 0x800 // for players to pick up items #define MF_NOCLIP 0x1000 // player cheat #define MF_SLIDE 0x2000 // keep info about sliding along walls #define MF_FLOAT 0x4000 // allow moves to any height, no gravity #define MF_TELEPORT 0x8000 // don't cross lines or look at heights #define MF_MISSILE 0x10000 // don't hit same species, explode on block #define MF_DROPPED 0x20000 // dropped by a demon, not level spawned #define MF_SHADOW 0x40000 // use translucent draw (shadow demons / invis) #define MF_NOBLOOD 0x80000 // don't bleed when shot (use puff) #define MF_CORPSE 0x100000 // don't stop moving halfway off a step #define MF_INFLOAT 0x200000 // floating to a height for a move, don't // auto float to target's height #define MF_COUNTKILL 0x400000 // count towards intermission kill total #define MF_COUNTITEM 0x800000 // count towards intermission item total #define MF_SKULLFLY 0x1000000 // skull in flight #define MF_NOTDMATCH 0x2000000 // don't spawn in death match (key cards) #define MF_TRANSLATION 0xc000000 // if 0x4 0x8 or 0xc, use a translation #define MF_TRANSSHIFT 26 // table for player colormaps // --- mobj.flags2 --- #define MF2_LOGRAV 0x00000001 // alternate gravity setting #define MF2_WINDTHRUST 0x00000002 // gets pushed around by the wind // specials #define MF2_FLOORBOUNCE 0x00000004 // bounces off the floor #define MF2_THRUGHOST 0x00000008 // missile will pass through ghosts #define MF2_FLY 0x00000010 // fly mode is active #define MF2_FOOTCLIP 0x00000020 // if feet are allowed to be clipped #define MF2_SPAWNFLOAT 0x00000040 // spawn random float z #define MF2_NOTELEPORT 0x00000080 // does not teleport #define MF2_RIP 0x00000100 // missile rips through solid // targets #define MF2_PUSHABLE 0x00000200 // can be pushed by other moving // mobjs #define MF2_SLIDE 0x00000400 // slides against walls #define MF2_ONMOBJ 0x00000800 // mobj is resting on top of another // mobj #define MF2_PASSMOBJ 0x00001000 // Enable z block checking. If on, // this flag will allow the mobj to // pass over/under other mobjs. #define MF2_CANNOTPUSH 0x00002000 // cannot push other pushable mobjs #define MF2_FEETARECLIPPED 0x00004000 // a mobj's feet are now being cut #define MF2_BOSS 0x00008000 // mobj is a major boss #define MF2_FIREDAMAGE 0x00010000 // does fire damage #define MF2_NODMGTHRUST 0x00020000 // does not thrust target when // damaging #define MF2_TELESTOMP 0x00040000 // mobj can stomp another #define MF2_FLOATBOB 0x00080000 // use float bobbing z movement #define MF2_DONTDRAW 0X00100000 // don't generate a vissprite //============================================================================= typedef enum { PST_LIVE, // playing PST_DEAD, // dead on the ground PST_REBORN // ready to restart } playerstate_t; // psprites are scaled shapes 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, sy; } pspdef_t; typedef enum { key_yellow, key_green, key_blue, NUMKEYS } keytype_t; typedef enum { wp_staff, wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace, wp_gauntlets, wp_beak, NUMWEAPONS, wp_nochange } weapontype_t; #define AMMO_GWND_WIMPY 10 #define AMMO_GWND_HEFTY 50 #define AMMO_CBOW_WIMPY 5 #define AMMO_CBOW_HEFTY 20 #define AMMO_BLSR_WIMPY 10 #define AMMO_BLSR_HEFTY 25 #define AMMO_SKRD_WIMPY 20 #define AMMO_SKRD_HEFTY 100 #define AMMO_PHRD_WIMPY 1 #define AMMO_PHRD_HEFTY 10 #define AMMO_MACE_WIMPY 20 #define AMMO_MACE_HEFTY 100 typedef enum { am_goldwand, am_crossbow, am_blaster, am_skullrod, am_phoenixrod, am_mace, NUMAMMO, am_noammo // staff, gauntlets } ammotype_t; typedef struct { ammotype_t ammo; int upstate; int downstate; int readystate; int atkstate; int holdatkstate; int flashstate; } weaponinfo_t; extern weaponinfo_t wpnlev1info[NUMWEAPONS]; extern weaponinfo_t wpnlev2info[NUMWEAPONS]; typedef enum { arti_none, arti_invulnerability, arti_invisibility, arti_health, arti_superhealth, arti_tomeofpower, arti_torch, arti_firebomb, arti_egg, arti_fly, arti_teleport, NUMARTIFACTS } artitype_t; typedef enum { pw_None, pw_invulnerability, pw_invisibility, pw_allmap, pw_infrared, pw_weaponlevel2, pw_flight, pw_shield, pw_health2, NUMPOWERS } powertype_t; #define INVULNTICS (30*35) #define INVISTICS (60*35) #define INFRATICS (120*35) #define IRONTICS (60*35) #define WPNLEV2TICS (40*35) #define FLIGHTTICS (60*35) #define CHICKENTICS (40*35) #define MESSAGETICS (4*35) #define BLINKTHRESHOLD (4*32) #define NUMINVENTORYSLOTS 14 typedef struct { int type; int count; } inventory_t; /* ================ = = player_t = ================ */ typedef struct player_s { mobj_t *mo; playerstate_t playerstate; ticcmd_t cmd; fixed_t viewz; // focal origin above r.z fixed_t viewheight; // base height above floor for viewz fixed_t deltaviewheight; // squat speed fixed_t bob; // bounded/scaled total momentum int flyheight; int lookdir; boolean centering; int health; // only used between levels, mo->health // is used during levels int armorpoints, armortype; // armor type is 0-2 inventory_t inventory[NUMINVENTORYSLOTS]; artitype_t readyArtifact; int artifactCount; int inventorySlotNum; int powers[NUMPOWERS]; boolean keys[NUMKEYS]; boolean backpack; signed int frags[MAXPLAYERS]; // kills of other players weapontype_t readyweapon; weapontype_t pendingweapon; // wp_nochange if not changing boolean weaponowned[NUMWEAPONS]; int ammo[NUMAMMO]; int maxammo[NUMAMMO]; int attackdown, usedown; // true if button down last tic int cheats; // bit flags int refire; // refired shots are less accurate int killcount, itemcount, secretcount; // for intermission const char *message; // hint messages int messageTics; // counter for showing messages int damagecount, bonuscount; // for screen flashing int flamecount; // for flame thrower duration mobj_t *attacker; // who did damage (NULL for floors) int extralight; // so gun flashes light up areas int fixedcolormap; // can be set to REDCOLORMAP, etc int colormap; // 0-3 for which color to draw player pspdef_t psprites[NUMPSPRITES]; // view sprites (gun, etc) boolean didsecret; // true if secret level has been done int chickenTics; // player is a chicken if > 0 int chickenPeck; // chicken peck countdown mobj_t *rain1; // active rain maker 1 mobj_t *rain2; // active rain maker 2 } player_t; #define CF_NOCLIP 1 #define CF_GODMODE 2 #define CF_NOMOMENTUM 4 // not really a cheat, just a debug aid #define SBARHEIGHT (42 << crispy->hires) // status bar height at bottom of screen /* =============================================================================== GLOBAL VARIABLES =============================================================================== */ #define TELEFOGHEIGHT (32*FRACUNIT) extern gameaction_t gameaction; extern boolean paused; extern GameMode_t gamemode; extern boolean ExtendedWAD; // true if main WAD is the extended version extern boolean nomonsters; // checkparm of -nomonsters extern boolean respawnparm; // checkparm of -respawn extern boolean debugmode; // checkparm of -debug extern boolean usergame; // ok to save / end game extern boolean ravpic; // checkparm of -ravpic extern boolean altpal; // checkparm to use an alternate palette routine extern boolean cdrom; // true if cd-rom mode active ("-cdrom") extern boolean deathmatch; // only if started as net death extern boolean netgame; // only true if >1 player extern boolean playeringame[MAXPLAYERS]; extern int consoleplayer; // player taking events and displaying extern int displayplayer; extern int viewangleoffset; // ANG90 = left side, ANG270 = right extern player_t players[MAXPLAYERS]; extern boolean DebugSound; // debug flag for displaying sound info extern int GetWeaponAmmo[NUMWEAPONS]; extern boolean demorecording; extern boolean demoplayback; extern boolean demoextend; // allow demos to persist through exit/respawn extern int skytexture; // Truncate angleturn in ticcmds to nearest 256. // Used when recording Vanilla demos in netgames. extern boolean lowres_turn; extern gamestate_t gamestate; extern skill_t gameskill; extern boolean respawnmonsters; extern int gameepisode; extern int gamemap; extern int prevmap; extern int totalkills, totalitems, totalsecret; // for intermission extern int levelstarttic; // gametic at level start extern int leveltime; // tics in game play for par extern ticcmd_t *netcmds; #define SAVEGAMESIZE 0x30000*16 #define SAVESTRINGSIZE 24 extern mapthing_t *deathmatch_p; extern mapthing_t deathmatchstarts[10]; extern mapthing_t playerstarts[MAXPLAYERS]; extern boolean playerstartsingame[MAXPLAYERS]; extern int mouseSensitivity; extern boolean precache; // if true, load all graphics at level load extern boolean singledemo; // quit after playing a demo from cmdline extern int bodyqueslot; extern skill_t startskill; extern int startepisode; extern int startmap; extern boolean autostart; extern boolean testcontrols; extern int testcontrols_mousespeed; extern int vanilla_savegame_limit; extern int vanilla_demo_limit; /* =============================================================================== GLOBAL FUNCTIONS =============================================================================== */ #include "z_zone.h" //---------- //BASE LEVEL //---------- void D_DoomMain(void); void IncThermo(void); void InitThermo(int max); void tprintf(const char *string, int initflag); // not a globally visible function, just included for source reference // calls all startup code // parses command line options // if not overrided, calls N_AdvanceDemo void D_DoomLoop(void); // 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 functions // calls I_GetTime, I_StartFrame, and I_StartTic //--------- //SYSTEM IO //--------- byte *I_AllocLow(int length); // allocates from low memory under dos, just mallocs under unix // haleyjd: was WATCOMC, preserved for historical interest. // This is similar to the -control structure in DOOM v1.4 and Strife. #if 0 extern boolean useexterndriver; #define EBT_FIRE 1 #define EBT_OPENDOOR 2 #define EBT_SPEED 4 #define EBT_STRAFE 8 #define EBT_MAP 0x10 #define EBT_INVENTORYLEFT 0x20 #define EBT_INVENTORYRIGHT 0x40 #define EBT_USEARTIFACT 0x80 #define EBT_FLYDROP 0x100 #define EBT_CENTERVIEW 0x200 #define EBT_PAUSE 0x400 #define EBT_WEAPONCYCLE 0x800 typedef struct { short vector; // Interrupt vector signed char moveForward; // forward/backward (maxes at 50) signed char moveSideways; // strafe (maxes at 24) short angleTurn; // turning speed (640 [slow] 1280 [fast]) short angleHead; // head angle (+2080 [left] : 0 [center] : -2048 [right]) signed char pitch; // look up/down (-110 : +90) signed char flyDirection; // flyheight (+1/-1) unsigned short buttons; // EBT_* flags } externdata_t; #endif //---- //GAME //---- 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); // can be called by the startup code or M_Responder // a normal game starts at map 1, but a warp test can start elsewhere void G_DeferedPlayDemo(const char *demo); void G_LoadGame(char *name); // can be called by the startup code or M_Responder // calls P_SetupLevel or W_EnterWorld void G_DoLoadGame(void); void G_SaveGame(int slot, char *description); // called by M_Responder #define SAVE_GAME_TERMINATOR 0x1d // Support routines for saving games char *SV_Filename(int slot); void SV_Open(char *fileName); void SV_OpenRead(char *fileName); void SV_Close(char *fileName); void SV_Write(void *buffer, int size); void SV_WriteByte(byte val); void SV_WriteWord(unsigned short val); void SV_WriteLong(unsigned int val); void SV_Read(void *buffer, int size); byte SV_ReadByte(void); uint16_t SV_ReadWord(void); uint32_t SV_ReadLong(void); extern char *savegamedir; void G_RecordDemo(skill_t skill, int numplayers, int episode, int map, char *name); // only called by startup code void G_PlayDemo(char *name); void G_TimeDemo(char *name); void G_ExitLevel(void); void G_SecretExitLevel(void); void G_WorldDone(void); void G_Ticker(void); boolean G_Responder(event_t * ev); void G_ScreenShot(void); //----- //PLAY //----- void P_Ticker(void); // called by C_Ticker // can call G_PlayerExited // carries out all thinking of monsters and players void P_SetupLevel(int episode, int map, int playermask, skill_t skill); // called by W_Ticker void P_Init(void); // called by startup code 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); // load / save game routines //------- //REFRESH //------- extern boolean setsizeneeded; extern boolean BorderNeedRefresh; extern boolean BorderTopRefresh; extern int UpdateState; // define the different areas for the dirty map #define I_NOUPDATE 0 #define I_FULLVIEW 1 #define I_STATBAR 2 #define I_MESSAGES 4 #define I_FULLSCRN 8 void R_RenderPlayerView(player_t * player); // called by G_Drawer void R_Init(void); // called by startup code void R_DrawViewBorder(void); void R_DrawTopBorder(void); // if the view size is not full screen, draws a border around it void R_SetViewSize(int blocks, int detail); // called by M_Responder int R_FlatNumForName(const char *name); int R_TextureNumForName(const char *name); int R_CheckTextureNumForName(const char *name); // called by P_Ticker for switches and animations // returns the texture number for the texture name //---- //MISC //---- // returns the position of the given parameter in the arg list (0 if not found) int M_DrawText(int x, int y, boolean direct, char *string); //---------------------- // Interlude (IN_lude.c) //---------------------- extern boolean intermission; void IN_Start(void); void IN_Ticker(void); void IN_Drawer(void); //---------------------- // Chat mode (CT_chat.c) //---------------------- void CT_Init(void); void CT_Drawer(void); boolean CT_Responder(event_t * ev); void CT_Ticker(void); char CT_dequeueChatChar(void); extern boolean chatmodeon; extern boolean ultimatemsg; //-------------------- // Finale (F_finale.c) //-------------------- void F_Drawer(void); void F_Ticker(void); void F_StartFinale(void); //---------------------- // STATUS BAR (SB_bar.c) //---------------------- void SB_Init(void); boolean SB_Responder(event_t * event); void SB_Ticker(void); void SB_Drawer(void); //----------------- // MENU (MN_menu.c) //----------------- extern boolean MenuActive; void MN_Init(void); void MN_ActivateMenu(void); void MN_DeactivateMenu(void); boolean MN_Responder(event_t * event); void MN_Ticker(void); void MN_Drawer(void); void MN_DrTextA(const char *text, int x, int y); int MN_TextAWidth(const char *text); void MN_DrTextB(const char *text, int x, int y); int MN_TextBWidth(const char *text); #include "sounds.h" #endif // __DOOMDEF__ crispy-doom-crispy-doom-5.6.4/src/heretic/dstrings.h000066400000000000000000000212541360717211000224600ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DStrings.h //--------------------------------------------------------------------------- // // P_inter.c // //--------------------------------------------------------------------------- // Keys #define TXT_GOTBLUEKEY "BLUE KEY" #define TXT_GOTYELLOWKEY "YELLOW KEY" #define TXT_GOTGREENKEY "GREEN KEY" // Artifacts #define TXT_ARTIHEALTH "QUARTZ FLASK" #define TXT_ARTIFLY "WINGS OF WRATH" #define TXT_ARTIINVULNERABILITY "RING OF INVINCIBILITY" #define TXT_ARTITOMEOFPOWER "TOME OF POWER" #define TXT_ARTIINVISIBILITY "SHADOWSPHERE" #define TXT_ARTIEGG "MORPH OVUM" #define TXT_ARTISUPERHEALTH "MYSTIC URN" #define TXT_ARTITORCH "TORCH" #define TXT_ARTIFIREBOMB "TIME BOMB OF THE ANCIENTS" #define TXT_ARTITELEPORT "CHAOS DEVICE" // Items #define TXT_ITEMHEALTH "CRYSTAL VIAL" #define TXT_ITEMBAGOFHOLDING "BAG OF HOLDING" #define TXT_ITEMSHIELD1 "SILVER SHIELD" #define TXT_ITEMSHIELD2 "ENCHANTED SHIELD" #define TXT_ITEMSUPERMAP "MAP SCROLL" // Ammo #define TXT_AMMOGOLDWAND1 "WAND CRYSTAL" #define TXT_AMMOGOLDWAND2 "CRYSTAL GEODE" #define TXT_AMMOMACE1 "MACE SPHERES" #define TXT_AMMOMACE2 "PILE OF MACE SPHERES" #define TXT_AMMOCROSSBOW1 "ETHEREAL ARROWS" #define TXT_AMMOCROSSBOW2 "QUIVER OF ETHEREAL ARROWS" #define TXT_AMMOBLASTER1 "CLAW ORB" #define TXT_AMMOBLASTER2 "ENERGY ORB" #define TXT_AMMOSKULLROD1 "LESSER RUNES" #define TXT_AMMOSKULLROD2 "GREATER RUNES" #define TXT_AMMOPHOENIXROD1 "FLAME ORB" #define TXT_AMMOPHOENIXROD2 "INFERNO ORB" // Weapons #define TXT_WPNMACE "FIREMACE" #define TXT_WPNCROSSBOW "ETHEREAL CROSSBOW" #define TXT_WPNBLASTER "DRAGON CLAW" #define TXT_WPNSKULLROD "HELLSTAFF" #define TXT_WPNPHOENIXROD "PHOENIX ROD" #define TXT_WPNGAUNTLETS "GAUNTLETS OF THE NECROMANCER" //--------------------------------------------------------------------------- // // SB_bar.c // //--------------------------------------------------------------------------- #define TXT_CHEATGODON "GOD MODE ON" #define TXT_CHEATGODOFF "GOD MODE OFF" #define TXT_CHEATNOCLIPON "NO CLIPPING ON" #define TXT_CHEATNOCLIPOFF "NO CLIPPING OFF" #define TXT_CHEATWEAPONS "ALL WEAPONS" #define TXT_CHEATFLIGHTON "FLIGHT ON" #define TXT_CHEATFLIGHTOFF "FLIGHT OFF" #define TXT_CHEATPOWERON "POWER ON" #define TXT_CHEATPOWEROFF "POWER OFF" #define TXT_CHEATHEALTH "FULL HEALTH" #define TXT_CHEATKEYS "ALL KEYS" #define TXT_CHEATSOUNDON "SOUND DEBUG ON" #define TXT_CHEATSOUNDOFF "SOUND DEBUG OFF" #define TXT_CHEATTICKERON "TICKER ON" #define TXT_CHEATTICKEROFF "TICKER OFF" #define TXT_CHEATARTIFACTS1 "CHOOSE AN ARTIFACT ( A - J )" #define TXT_CHEATARTIFACTS2 "HOW MANY ( 1 - 9 )" #define TXT_CHEATARTIFACTS3 "YOU GOT IT" #define TXT_CHEATARTIFACTSFAIL "BAD INPUT" #define TXT_CHEATWARP "LEVEL WARP" #define TXT_CHEATSCREENSHOT "SCREENSHOT" #define TXT_CHEATCHICKENON "CHICKEN ON" #define TXT_CHEATCHICKENOFF "CHICKEN OFF" #define TXT_CHEATMASSACRE "MASSACRE" #define TXT_CHEATIDDQD "TRYING TO CHEAT, EH? NOW YOU DIE!" #define TXT_CHEATIDKFA "CHEATER - YOU DON'T DESERVE WEAPONS" //--------------------------------------------------------------------------- // // P_doors.c // //--------------------------------------------------------------------------- #define TXT_NEEDBLUEKEY "YOU NEED A BLUE KEY TO OPEN THIS DOOR" #define TXT_NEEDGREENKEY "YOU NEED A GREEN KEY TO OPEN THIS DOOR" #define TXT_NEEDYELLOWKEY "YOU NEED A YELLOW KEY TO OPEN THIS DOOR" //--------------------------------------------------------------------------- // // G_game.c // //--------------------------------------------------------------------------- #define TXT_GAMESAVED "GAME SAVED" //--------------------------------------------------------------------------- // // 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" //--------------------------------------------------------------------------- // // F_finale.c // //--------------------------------------------------------------------------- #define E1TEXT "with the destruction of the iron\n"\ "liches and their minions, the last\n"\ "of the undead are cleared from this\n"\ "plane of existence.\n\n"\ "those creatures had to come from\n"\ "somewhere, though, and you have the\n"\ "sneaky suspicion that the fiery\n"\ "portal of hell's maw opens onto\n"\ "their home dimension.\n\n"\ "to make sure that more undead\n"\ "(or even worse things) don't come\n"\ "through, you'll have to seal hell's\n"\ "maw from the other side. of course\n"\ "this means you may get stuck in a\n"\ "very unfriendly world, but no one\n"\ "ever said being a Heretic was easy!" #define E2TEXT "the mighty maulotaurs have proved\n"\ "to be no match for you, and as\n"\ "their steaming corpses slide to the\n"\ "ground you feel a sense of grim\n"\ "satisfaction that they have been\n"\ "destroyed.\n\n"\ "the gateways which they guarded\n"\ "have opened, revealing what you\n"\ "hope is the way home. but as you\n"\ "step through, mocking laughter\n"\ "rings in your ears.\n\n"\ "was some other force controlling\n"\ "the maulotaurs? could there be even\n"\ "more horrific beings through this\n"\ "gate? the sweep of a crystal dome\n"\ "overhead where the sky should be is\n"\ "certainly not a good sign...." #define E3TEXT "the death of d'sparil has loosed\n"\ "the magical bonds holding his\n"\ "creatures on this plane, their\n"\ "dying screams overwhelming his own\n"\ "cries of agony.\n\n"\ "your oath of vengeance fulfilled,\n"\ "you enter the portal to your own\n"\ "world, mere moments before the dome\n"\ "shatters into a million pieces.\n\n"\ "but if d'sparil's power is broken\n"\ "forever, why don't you feel safe?\n"\ "was it that last shout just before\n"\ "his death, the one that sounded\n"\ "like a curse? or a summoning? you\n"\ "can't really be sure, but it might\n"\ "just have been a scream.\n\n"\ "then again, what about the other\n"\ "serpent riders?" #define E4TEXT "you thought you would return to your\n"\ "own world after d'sparil died, but\n"\ "his final act banished you to his\n"\ "own plane. here you entered the\n"\ "shattered remnants of lands\n"\ "conquered by d'sparil. you defeated\n"\ "the last guardians of these lands,\n"\ "but now you stand before the gates\n"\ "to d'sparil's stronghold. until this\n"\ "moment you had no doubts about your\n"\ "ability to face anything you might\n"\ "encounter, but beyond this portal\n"\ "lies the very heart of the evil\n"\ "which invaded your world. d'sparil\n"\ "might be dead, but the pit where he\n"\ "was spawned remains. now you must\n"\ "enter that pit in the hopes of\n"\ "finding a way out. and somewhere,\n"\ "in the darkest corner of d'sparil's\n"\ "demesne, his personal bodyguards\n"\ "await your arrival ..." #define E5TEXT "as the final maulotaur bellows his\n"\ "death-agony, you realize that you\n"\ "have never come so close to your own\n"\ "destruction. not even the fight with\n"\ "d'sparil and his disciples had been\n"\ "this desperate. grimly you stare at\n"\ "the gates which open before you,\n"\ "wondering if they lead home, or if\n"\ "they open onto some undreamed-of\n"\ "horror. you find yourself wondering\n"\ "if you have the strength to go on,\n"\ "if nothing but death and pain await\n"\ "you. but what else can you do, if\n"\ "the will to fight is gone? can you\n"\ "force yourself to continue in the\n"\ "face of such despair? do you have\n"\ "the courage? you find, in the end,\n"\ "that it is not within you to\n"\ "surrender without a fight. eyes\n"\ "wide, you go to meet your fate." crispy-doom-crispy-doom-5.6.4/src/heretic/f_finale.c000066400000000000000000000235341360717211000223640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // F_finale.c #include #include "doomdef.h" #include "deh_str.h" #include "i_swap.h" #include "i_video.h" #include "s_sound.h" #include "v_video.h" static int finalestage; // 0 = text, 1 = art screen static int finalecount; #define TEXTSPEED 3 #define TEXTWAIT 250 static const char *finaletext; static const char *finaleflat; static int FontABaseLump; extern boolean automapactive; extern boolean viewactive; extern void D_StartTitle(void); /* ======================= = = F_StartFinale = ======================= */ void F_StartFinale(void) { gameaction = ga_nothing; gamestate = GS_FINALE; viewactive = false; automapactive = false; players[consoleplayer].messageTics = 1; players[consoleplayer].message = NULL; switch (gameepisode) { case 1: finaleflat = DEH_String("FLOOR25"); finaletext = DEH_String(E1TEXT); break; case 2: finaleflat = DEH_String("FLATHUH1"); finaletext = DEH_String(E2TEXT); break; case 3: finaleflat = DEH_String("FLTWAWA2"); finaletext = DEH_String(E3TEXT); break; case 4: finaleflat = DEH_String("FLOOR28"); finaletext = DEH_String(E4TEXT); break; case 5: finaleflat = DEH_String("FLOOR08"); finaletext = DEH_String(E5TEXT); break; } finalestage = 0; finalecount = 0; FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1; // S_ChangeMusic(mus_victor, true); S_StartSong(mus_cptd, true); } boolean F_Responder(event_t * event) { if (event->type != ev_keydown) { return false; } if (finalestage == 1 && gameepisode == 2) { // we're showing the water pic, make any key kick to demo mode finalestage++; /* memset((byte *) 0xa0000, 0, SCREENWIDTH * SCREENHEIGHT); memset(I_VideoBuffer, 0, SCREENWIDTH * SCREENHEIGHT); I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); */ return true; } return false; } /* ======================= = = F_Ticker = ======================= */ void F_Ticker(void) { finalecount++; if (!finalestage && finalecount > strlen(finaletext) * TEXTSPEED + TEXTWAIT) { finalecount = 0; if (!finalestage) { finalestage = 1; } // wipegamestate = -1; // force a wipe /* if (gameepisode == 3) S_StartMusic (mus_bunny); */ } } /* ======================= = = F_TextWrite = ======================= */ //#include "hu_stuff.h" //extern patch_t *hu_font[HU_FONTSIZE]; void F_TextWrite(void) { byte *src, *dest; int x, y; int count; const char *ch; int c; int cx, cy; patch_t *w; // // erase the entire screen to a tiled background // src = W_CacheLumpName(finaleflat, PU_CACHE); dest = I_VideoBuffer; for (y = 0; y < SCREENHEIGHT; y++) { for (x = 0; x < SCREENWIDTH / 64; x++) { memcpy(dest, src + ((y & 63) << 6), 64); dest += 64; } if (SCREENWIDTH & 63) { memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); dest += (SCREENWIDTH & 63); } } // V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); // // draw some of the text onto the screen // cx = 20; cy = 5; ch = finaletext; count = (finalecount - 10) / TEXTSPEED; if (count < 0) count = 0; for (; count; count--) { c = *ch++; if (!c) break; if (c == '\n') { cx = 20; cy += 9; continue; } c = toupper(c); if (c < 33) { cx += 5; continue; } w = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); if (cx + SHORT(w->width) > SCREENWIDTH) break; V_DrawPatch(cx, cy, w); cx += SHORT(w->width); } } void F_DrawPatchCol(int x, patch_t * patch, int col) { column_t *column; byte *source, *dest, *desttop; int count; column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col])); desttop = I_VideoBuffer + x; // step through the posts in a column while (column->topdelta != 0xff) { source = (byte *) column + 3; dest = desttop + column->topdelta * SCREENWIDTH; count = column->length; while (count--) { *dest = *source++; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } /* ================== = = F_DemonScroll = ================== */ void F_DemonScroll(void) { byte *p1, *p2; static int yval = 0; static int nextscroll = 0; if (finalecount < nextscroll) { return; } p1 = W_CacheLumpName(DEH_String("FINAL1"), PU_LEVEL); p2 = W_CacheLumpName(DEH_String("FINAL2"), PU_LEVEL); if (finalecount < 70) { V_CopyScaledBuffer(I_VideoBuffer, p1, ORIGHEIGHT * ORIGWIDTH); nextscroll = finalecount; return; } if (yval < 64000) { V_CopyScaledBuffer(I_VideoBuffer, p2 + ORIGHEIGHT * ORIGWIDTH - yval, yval); V_CopyScaledBuffer(I_VideoBuffer + (yval << (2 * crispy->hires)), p1, ORIGHEIGHT * ORIGWIDTH - yval); yval += ORIGWIDTH; nextscroll = finalecount + 3; } else { //else, we'll just sit here and wait, for now V_CopyScaledBuffer(I_VideoBuffer, p2, ORIGWIDTH * ORIGHEIGHT); } } /* ================== = = F_DrawUnderwater = ================== */ void F_DrawUnderwater(void) { static boolean underwawa = false; extern boolean askforquit; const char *lumpname; byte *palette; // The underwater screen has its own palette, which is rather annoying. // The palette doesn't correspond to the normal palette. Because of // this, we must regenerate the lookup tables used in the video scaling // code. switch (finalestage) { case 1: if (!underwawa) { underwawa = true; V_DrawFilledBox(0, 0, SCREENWIDTH, SCREENHEIGHT, 0); lumpname = DEH_String("E2PAL"); palette = W_CacheLumpName(lumpname, PU_STATIC); I_SetPalette(palette); W_ReleaseLumpName(lumpname); V_DrawRawScreen(W_CacheLumpName(DEH_String("E2END"), PU_CACHE)); } paused = false; MenuActive = false; askforquit = false; break; case 2: if (underwawa) { lumpname = DEH_String("PLAYPAL"); palette = W_CacheLumpName(lumpname, PU_STATIC); I_SetPalette(palette); W_ReleaseLumpName(lumpname); underwawa = false; } V_DrawRawScreen(W_CacheLumpName(DEH_String("TITLE"), PU_CACHE)); //D_StartTitle(); // go to intro/demo mode. } } #if 0 /* ================== = = F_BunnyScroll = ================== */ void F_BunnyScroll(void) { int scrolled, x; patch_t *p1, *p2; char name[10]; int stage; static int laststage; p1 = W_CacheLumpName("PFUB2", PU_LEVEL); p2 = W_CacheLumpName("PFUB1", PU_LEVEL); V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); scrolled = 320 - (finalecount - 230) / 2; if (scrolled > 320) scrolled = 320; if (scrolled < 0) scrolled = 0; for (x = 0; x < SCREENWIDTH; x++) { if (x + scrolled < 320) F_DrawPatchCol(x, p1, x + scrolled); else F_DrawPatchCol(x, p2, x + scrolled - 320); } if (finalecount < 1130) return; if (finalecount < 1180) { V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, 0, W_CacheLumpName("END0", PU_CACHE)); laststage = 0; return; } stage = (finalecount - 1180) / 5; if (stage > 6) stage = 6; if (stage > laststage) { S_StartSound(NULL, sfx_pistol); laststage = stage; } M_snprintf(name, sizeof(name), "END%i", stage); V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, W_CacheLumpName(name, PU_CACHE)); } #endif /* ======================= = = F_Drawer = ======================= */ void F_Drawer(void) { UpdateState |= I_FULLSCRN; if (!finalestage) F_TextWrite(); else { switch (gameepisode) { case 1: if (gamemode == shareware) { V_DrawRawScreen(W_CacheLumpName("ORDER", PU_CACHE)); } else { V_DrawRawScreen(W_CacheLumpName("CREDIT", PU_CACHE)); } break; case 2: F_DrawUnderwater(); break; case 3: F_DemonScroll(); break; case 4: // Just show credits screen for extended episodes case 5: V_DrawRawScreen(W_CacheLumpName("CREDIT", PU_CACHE)); break; } } } crispy-doom-crispy-doom-5.6.4/src/heretic/g_game.c000066400000000000000000001372751360717211000220500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // G_game.c #include #include #include #include "doomdef.h" #include "doomkeys.h" #include "deh_str.h" #include "i_input.h" #include "i_timer.h" #include "i_system.h" #include "m_argv.h" #include "m_controls.h" #include "m_misc.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" // Macros #define AM_STARTKEY 9 // Functions boolean G_CheckDemoStatus(void); void G_ReadDemoTiccmd(ticcmd_t * cmd); void G_WriteDemoTiccmd(ticcmd_t * cmd); void G_PlayerReborn(int player); void G_DoReborn(int playernum); void G_DoLoadLevel(void); void G_DoNewGame(void); void G_DoPlayDemo(void); void G_DoCompleted(void); void G_DoVictory(void); void G_DoWorldDone(void); void G_DoSaveGame(void); void D_PageTicker(void); void D_AdvanceDemo(void); struct { int type; // mobjtype_t int speed[2]; } MonsterMissileInfo[] = { { MT_IMPBALL, { 10, 20 } }, { MT_MUMMYFX1, { 9, 18 } }, { MT_KNIGHTAXE, { 9, 18 } }, { MT_REDAXE, { 9, 18 } }, { MT_BEASTBALL, { 12, 20 } }, { MT_WIZFX1, { 18, 24 } }, { MT_SNAKEPRO_A, { 14, 20 } }, { MT_SNAKEPRO_B, { 14, 20 } }, { MT_HEADFX1, { 13, 20 } }, { MT_HEADFX3, { 10, 18 } }, { MT_MNTRFX1, { 20, 26 } }, { MT_MNTRFX2, { 14, 20 } }, { MT_SRCRFX1, { 20, 28 } }, { MT_SOR2FX1, { 20, 28 } }, { -1, { -1, -1 } } // Terminator }; gameaction_t gameaction; gamestate_t gamestate; skill_t gameskill; boolean respawnmonsters; int gameepisode; int gamemap; int prevmap; boolean paused; boolean sendpause; // send a pause event next tic boolean sendsave; // send a save event next tic boolean usergame; // ok to save / end game boolean timingdemo; // if true, exit with report on completion int starttime; // for comparative timing purposes boolean viewactive; boolean deathmatch; // only if started as net death boolean netgame; // only true if packets are broadcast boolean playeringame[MAXPLAYERS]; player_t players[MAXPLAYERS]; int consoleplayer; // player taking events and displaying int displayplayer; // view being displayed int levelstarttic; // gametic at level start int totalkills, totalitems, totalsecret; // for intermission int mouseSensitivity; char demoname[32]; boolean demorecording; boolean longtics; // specify high resolution turning in demos boolean lowres_turn; boolean shortticfix; // calculate lowres turning like doom boolean demoplayback; boolean demoextend; byte *demobuffer, *demo_p, *demoend; boolean singledemo; // quit after playing a demo from cmdline boolean precache = true; // if true, load all graphics at start // TODO: Heretic uses 16-bit shorts for consistency? byte consistancy[MAXPLAYERS][BACKUPTICS]; char *savegamedir; boolean testcontrols = false; int testcontrols_mousespeed; // // controls (have defaults) // #define MAXPLMOVE 0x32 fixed_t forwardmove[2] = { 0x19, 0x32 }; fixed_t sidemove[2] = { 0x18, 0x28 }; fixed_t angleturn[3] = { 640, 1280, 320 }; // + slow turn static int *weapon_keys[] = { &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, &key_weapon5, &key_weapon6, &key_weapon7 }; // 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_staff, wp_staff }, { wp_gauntlets, wp_staff }, { wp_goldwand, wp_goldwand }, { wp_crossbow, wp_crossbow }, { wp_blaster, wp_blaster }, { wp_skullrod, wp_skullrod }, { wp_phoenixrod, wp_phoenixrod }, { wp_mace, wp_mace }, { wp_beak, wp_beak }, }; #define SLOWTURNTICS 6 #define NUMKEYS 256 boolean gamekeydown[NUMKEYS]; int turnheld; // for accelerative turning int lookheld; boolean mousearray[MAX_MOUSE_BUTTONS + 1]; boolean *mousebuttons = &mousearray[1]; // allow [-1] int mousex, mousey; // mouse values are used once int dclicktime, dclickstate, dclicks; int dclicktime2, dclickstate2, dclicks2; #define MAX_JOY_BUTTONS 20 int joyxmove, joyymove; // joystick values are repeated int joystrafemove; int joylook; boolean joyarray[MAX_JOY_BUTTONS + 1]; boolean *joybuttons = &joyarray[1]; // allow [-1] int savegameslot; char savedescription[32]; int vanilla_demo_limit = 1; int inventoryTics; // haleyjd: removed WATCOMC //============================================================================= // Not used - ripped out for Heretic /* int G_CmdChecksum(ticcmd_t *cmd) { int i; int sum; sum = 0; for(i = 0; i < sizeof(*cmd)/4-1; i++) { sum += ((int *)cmd)[i]; } return(sum); } */ static boolean WeaponSelectable(weapontype_t weapon) { if (weapon == wp_beak) { return false; } return players[consoleplayer].weaponowned[weapon]; } static int G_NextWeapon(int direction) { weapontype_t weapon; int start_i, i; // Find index in the table. if (players[consoleplayer].pendingweapon == wp_nochange) { weapon = players[consoleplayer].readyweapon; } else { weapon = players[consoleplayer].pendingweapon; } for (i=0; iconsistancy = // consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS]; cmd->consistancy = consistancy[consoleplayer][maketic % BACKUPTICS]; //printf ("cons: %i\n",cmd->consistancy); strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; speed = joybspeed >= MAX_JOY_BUTTONS || gamekeydown[key_speed] || joybuttons[joybspeed]; // haleyjd: removed externdriver crap forward = side = look = arti = flyheight = 0; // // use two stage accelerative turning on the keyboard and joystick // if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || gamekeydown[key_left]) turnheld += ticdup; else turnheld = 0; if (turnheld < SLOWTURNTICS) tspeed = 2; // slow turn else tspeed = speed; if (gamekeydown[key_lookdown] || gamekeydown[key_lookup]) { lookheld += ticdup; } else { lookheld = 0; } if (lookheld < SLOWTURNTICS) { lspeed = 1; } else { lspeed = 2; } // // let movement keys cancel each other out // if (strafe) { if (gamekeydown[key_right]) side += sidemove[speed]; if (gamekeydown[key_left]) side -= sidemove[speed]; if (joyxmove > 0) side += sidemove[speed]; if (joyxmove < 0) side -= sidemove[speed]; } else { if (gamekeydown[key_right]) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left]) cmd->angleturn += angleturn[tspeed]; if (joyxmove > 0) cmd->angleturn -= angleturn[tspeed]; if (joyxmove < 0) cmd->angleturn += angleturn[tspeed]; } if (gamekeydown[key_up]) forward += forwardmove[speed]; if (gamekeydown[key_down]) forward -= forwardmove[speed]; if (joyymove < 0) forward += forwardmove[speed]; if (joyymove > 0) forward -= forwardmove[speed]; if (gamekeydown[key_straferight] || mousebuttons[mousebstraferight] || joybuttons[joybstraferight] || joystrafemove > 0) side += sidemove[speed]; if (gamekeydown[key_strafeleft] || mousebuttons[mousebstrafeleft] || joybuttons[joybstrafeleft] || joystrafemove < 0) side -= sidemove[speed]; // Look up/down/center keys if (gamekeydown[key_lookup] || joylook < 0) { look = lspeed; } if (gamekeydown[key_lookdown] || joylook > 0) { look = -lspeed; } // haleyjd: removed externdriver crap if (gamekeydown[key_lookcenter]) { look = TOCENTER; } // haleyjd: removed externdriver crap // Fly up/down/drop keys if (gamekeydown[key_flyup]) { flyheight = 5; // note that the actual flyheight will be twice this } if (gamekeydown[key_flydown]) { flyheight = -5; } if (gamekeydown[key_flycenter]) { flyheight = TOCENTER; // haleyjd: removed externdriver crap look = TOCENTER; } // Use artifact key if (gamekeydown[key_useartifact]) { if (gamekeydown[key_speed] && !noartiskip) { if (players[consoleplayer].inventory[inv_ptr].type != arti_none) { gamekeydown[key_useartifact] = false; cmd->arti = 0xff; // skip artifact code } } else { if (inventory) { players[consoleplayer].readyArtifact = players[consoleplayer].inventory[inv_ptr].type; inventory = false; cmd->arti = 0; usearti = false; } else if (usearti) { cmd->arti = players[consoleplayer].inventory[inv_ptr].type; usearti = false; } } } if (gamekeydown[127] && !cmd->arti && !players[consoleplayer].powers[pw_weaponlevel2]) { gamekeydown[127] = false; cmd->arti = arti_tomeofpower; } // // buttons // cmd->chatchar = CT_dequeueChatChar(); if (gamekeydown[key_fire] || mousebuttons[mousebfire] || joybuttons[joybfire]) cmd->buttons |= BT_ATTACK; if (gamekeydown[key_use] || joybuttons[joybuse] || mousebuttons[mousebuse]) { cmd->buttons |= BT_USE; dclicks = 0; // clear double clicks if hit use button } // If the previous or next weapon button is pressed, the // next_weapon variable is set to change weapons when // we generate a ticcmd. Choose a new weapon. // (Can't weapon cycle when the player is a chicken) if (gamestate == GS_LEVEL && players[consoleplayer].chickenTics == 0 && next_weapon != 0) { i = G_NextWeapon(next_weapon); cmd->buttons |= BT_CHANGE; cmd->buttons |= i << BT_WEAPONSHIFT; } else { for (i=0; ibuttons |= BT_CHANGE; cmd->buttons |= i< 1) { dclickstate = mousebuttons[mousebforward]; if (dclickstate) dclicks++; if (dclicks == 2) { cmd->buttons |= BT_USE; dclicks = 0; } else dclicktime = 0; } else { dclicktime += ticdup; if (dclicktime > 20) { dclicks = 0; dclickstate = 0; } } // // strafe double click // bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; if (bstrafe != dclickstate2 && dclicktime2 > 1) { dclickstate2 = bstrafe; if (dclickstate2) dclicks2++; if (dclicks2 == 2) { cmd->buttons |= BT_USE; dclicks2 = 0; } else dclicktime2 = 0; } else { dclicktime2 += ticdup; if (dclicktime2 > 20) { dclicks2 = 0; dclickstate2 = 0; } } } if (strafe) { side += mousex * 2; } else { cmd->angleturn -= mousex * 0x8; } // No mouse movement in previous frame? if (mousex == 0) { testcontrols_mousespeed = 0; } if (!novert) forward += mousey; mousex = mousey = 0; if (forward > MAXPLMOVE) forward = MAXPLMOVE; else if (forward < -MAXPLMOVE) forward = -MAXPLMOVE; if (side > MAXPLMOVE) side = MAXPLMOVE; else if (side < -MAXPLMOVE) side = -MAXPLMOVE; cmd->forwardmove += forward; cmd->sidemove += side; if (players[consoleplayer].playerstate == PST_LIVE) { if (look < 0) { look += 16; } cmd->lookfly = look; } if (flyheight < 0) { flyheight += 16; } cmd->lookfly |= flyheight << 4; // // special buttons // if (sendpause) { sendpause = false; cmd->buttons = BT_SPECIAL | BTS_PAUSE; } if (sendsave) { sendsave = false; cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT); } if (lowres_turn) { if (shortticfix) { static signed short carry = 0; signed short desired_angleturn; desired_angleturn = cmd->angleturn + carry; // round angleturn to the nearest 256 unit boundary // for recording demos with single byte values for turn cmd->angleturn = (desired_angleturn + 128) & 0xff00; // Carry forward the error from the reduced resolution to the // next tic, so that successive small movements can accumulate. carry = desired_angleturn - cmd->angleturn; } else { // truncate angleturn to the nearest 256 boundary // for recording demos with single byte values for turn cmd->angleturn &= 0xff00; } } } /* ============== = = G_DoLoadLevel = ============== */ void G_DoLoadLevel(void) { int i; levelstarttic = gametic; // for time calculation gamestate = GS_LEVEL; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && players[i].playerstate == PST_DEAD) players[i].playerstate = PST_REBORN; memset(players[i].frags, 0, sizeof(players[i].frags)); } P_SetupLevel(gameepisode, gamemap, 0, gameskill); displayplayer = consoleplayer; // view the guy you are playing gameaction = ga_nothing; Z_CheckHeap(); // // clear cmd building stuff // memset(gamekeydown, 0, sizeof(gamekeydown)); joyxmove = joyymove = joystrafemove = joylook = 0; mousex = mousey = 0; sendpause = sendsave = paused = false; memset(mousearray, 0, sizeof(mousearray)); memset(joyarray, 0, sizeof(joyarray)); if (testcontrols) { P_SetMessage(&players[consoleplayer], "PRESS ESCAPE TO QUIT.", false); } } static void SetJoyButtons(unsigned int buttons_mask) { int i; for (i=0; itype == ev_keyup && ev->data1 == key_useartifact) { // flag to denote that it's okay to use an artifact if (!inventory) { plr->readyArtifact = plr->inventory[inv_ptr].type; } usearti = true; } // Check for spy mode player cycle if (gamestate == GS_LEVEL && ev->type == ev_keydown && ev->data1 == KEY_F12 && !deathmatch) { // Cycle the display player do { displayplayer++; if (displayplayer == MAXPLAYERS) { displayplayer = 0; } } while (!playeringame[displayplayer] && displayplayer != consoleplayer); return (true); } if (gamestate == GS_LEVEL) { if (CT_Responder(ev)) { // Chat ate the event return (true); } if (SB_Responder(ev)) { // Status bar ate the event return (true); } if (AM_Responder(ev)) { // Automap ate the event return (true); } } if (ev->type == ev_mouse) { testcontrols_mousespeed = abs(ev->data2); } if (ev->type == ev_keydown && ev->data1 == key_prevweapon) { next_weapon = -1; } else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) { next_weapon = 1; } switch (ev->type) { case ev_keydown: if (ev->data1 == key_invleft) { inventoryTics = 5 * 35; if (!inventory) { inventory = true; break; } inv_ptr--; if (inv_ptr < 0) { inv_ptr = 0; } else { curpos--; if (curpos < 0) { curpos = 0; } } return (true); } if (ev->data1 == key_invright) { inventoryTics = 5 * 35; if (!inventory) { inventory = true; break; } inv_ptr++; if (inv_ptr >= plr->inventorySlotNum) { inv_ptr--; if (inv_ptr < 0) inv_ptr = 0; } else { curpos++; if (curpos > 6) { curpos = 6; } } return (true); } if (ev->data1 == key_pause && !MenuActive) { sendpause = true; return (true); } if (ev->data1 < NUMKEYS) { gamekeydown[ev->data1] = true; } return (true); // eat key down events case ev_keyup: if (ev->data1 < NUMKEYS) { gamekeydown[ev->data1] = false; } return (false); // always let key up events filter down case ev_mouse: SetMouseButtons(ev->data1); mousex = ev->data2 * (mouseSensitivity + 5) / 10; mousey = ev->data3 * (mouseSensitivity + 5) / 10; return (true); // eat events case ev_joystick: SetJoyButtons(ev->data1); joyxmove = ev->data2; joyymove = ev->data3; joystrafemove = ev->data4; joylook = ev->data5; return (true); // eat events default: break; } return (false); } /* =============================================================================== = = G_Ticker = =============================================================================== */ void G_Ticker(void) { int i, buf; ticcmd_t *cmd = NULL; // // do player reborns if needed // for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].playerstate == PST_REBORN) G_DoReborn(i); // // do things to change the game state // while (gameaction != ga_nothing) { switch (gameaction) { case ga_loadlevel: G_DoLoadLevel(); break; case ga_newgame: G_DoNewGame(); break; case ga_loadgame: G_DoLoadGame(); break; case ga_savegame: G_DoSaveGame(); break; case ga_playdemo: G_DoPlayDemo(); break; case ga_screenshot: V_ScreenShot("HTIC%02i.%s"); gameaction = ga_nothing; break; case ga_completed: G_DoCompleted(); break; case ga_worlddone: G_DoWorldDone(); break; case ga_victory: F_StartFinale(); break; default: break; } } // // get commands, check consistancy, and build new consistancy check // //buf = gametic%BACKUPTICS; buf = (gametic / ticdup) % BACKUPTICS; for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) { cmd = &players[i].cmd; memcpy(cmd, &netcmds[i], sizeof(ticcmd_t)); if (demoplayback) G_ReadDemoTiccmd(cmd); if (demorecording) G_WriteDemoTiccmd(cmd); if (netgame && !(gametic % ticdup)) { if (gametic > BACKUPTICS && consistancy[i][buf] != cmd->consistancy) { I_Error("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] = rndindex; } } // // check for special buttons // for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) { if (players[i].cmd.buttons & BT_SPECIAL) { switch (players[i].cmd.buttons & BT_SPECIALMASK) { case BTS_PAUSE: paused ^= 1; if (paused) { S_PauseSound(); } else { S_ResumeSound(); } break; case BTS_SAVEGAME: if (!savedescription[0]) { if (netgame) { M_StringCopy(savedescription, DEH_String("NET GAME"), sizeof(savedescription)); } else { M_StringCopy(savedescription, DEH_String("SAVE GAME"), sizeof(savedescription)); } } savegameslot = (players[i].cmd. buttons & BTS_SAVEMASK) >> BTS_SAVESHIFT; gameaction = ga_savegame; break; } } } // turn inventory off after a certain amount of time if (inventory && !(--inventoryTics)) { players[consoleplayer].readyArtifact = players[consoleplayer].inventory[inv_ptr].type; inventory = false; cmd->arti = 0; } // // do main actions // // // do main actions // switch (gamestate) { case GS_LEVEL: P_Ticker(); SB_Ticker(); AM_Ticker(); CT_Ticker(); break; case GS_INTERMISSION: IN_Ticker(); break; case GS_FINALE: F_Ticker(); break; case GS_DEMOSCREEN: D_PageTicker(); break; } } /* ============================================================================== PLAYER STRUCTURE FUNCTIONS also see P_SpawnPlayer in P_Things ============================================================================== */ /* ==================== = = G_InitPlayer = = Called at the start = Called by the game initialization functions ==================== */ void G_InitPlayer(int player) { // clear everything else to defaults G_PlayerReborn(player); } /* ==================== = = G_PlayerFinishLevel = = Can when a player completes a level ==================== */ extern int playerkeys; void G_PlayerFinishLevel(int player) { player_t *p; int i; /* // BIG HACK inv_ptr = 0; curpos = 0; */ // END HACK p = &players[player]; for (i = 0; i < p->inventorySlotNum; i++) { p->inventory[i].count = 1; } p->artifactCount = p->inventorySlotNum; if (!deathmatch) { for (i = 0; i < 16; i++) { P_PlayerUseArtifact(p, arti_fly); } } memset(p->powers, 0, sizeof(p->powers)); memset(p->keys, 0, sizeof(p->keys)); playerkeys = 0; // memset(p->inventory, 0, sizeof(p->inventory)); if (p->chickenTics) { p->readyweapon = p->mo->special1.i; // Restore weapon p->chickenTics = 0; } p->messageTics = 0; p->lookdir = 0; p->mo->flags &= ~MF_SHADOW; // Remove invisibility p->extralight = 0; // Remove weapon flashes p->fixedcolormap = 0; // Remove torch p->damagecount = 0; // No palette changes p->bonuscount = 0; p->rain1 = NULL; p->rain2 = NULL; if (p == &players[consoleplayer]) { SB_state = -1; // refresh the status bar } } /* ==================== = = 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, itemcount, secretcount; boolean secret; secret = false; memcpy(frags, players[player].frags, sizeof(frags)); killcount = players[player].killcount; itemcount = players[player].itemcount; secretcount = players[player].secretcount; p = &players[player]; if (p->didsecret) { secret = true; } memset(p, 0, sizeof(*p)); memcpy(players[player].frags, frags, sizeof(players[player].frags)); players[player].killcount = killcount; players[player].itemcount = itemcount; players[player].secretcount = secretcount; p->usedown = p->attackdown = true; // don't do anything immediately p->playerstate = PST_LIVE; p->health = MAXHEALTH; p->readyweapon = p->pendingweapon = wp_goldwand; p->weaponowned[wp_staff] = true; p->weaponowned[wp_goldwand] = true; p->messageTics = 0; p->lookdir = 0; p->ammo[am_goldwand] = 50; for (i = 0; i < NUMAMMO; i++) { p->maxammo[i] = maxammo[i]; } if (gamemap == 9 || secret) { p->didsecret = true; } if (p == &players[consoleplayer]) { SB_state = -1; // refresh the status bar inv_ptr = 0; // reset the inventory pointer curpos = 0; } } /* ==================== = = G_CheckSpot = = Returns false if the player cannot be respawned at the given mapthing_t spot = because something is occupying it ==================== */ void P_SpawnPlayer(mapthing_t * mthing); boolean G_CheckSpot(int playernum, mapthing_t * mthing) { fixed_t x, y; subsector_t *ss; unsigned an; mobj_t *mo; x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; players[playernum].mo->flags2 &= ~MF2_PASSMOBJ; if (!P_CheckPosition(players[playernum].mo, x, y)) { players[playernum].mo->flags2 |= MF2_PASSMOBJ; return false; } players[playernum].mo->flags2 |= MF2_PASSMOBJ; // spawn a teleport fog ss = R_PointInSubsector(x, y); an = ((unsigned) ANG45 * (mthing->angle / 45)) >> ANGLETOFINESHIFT; mo = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an], ss->sector->floorheight + TELEFOGHEIGHT, MT_TFOG); if (players[consoleplayer].viewz != 1) S_StartSound(mo, sfx_telept); // don't start sound on first frame return true; } /* ==================== = = G_DeathMatchSpawnPlayer = = Spawns a player at one of the random death match spots = called at level load and each death ==================== */ void G_DeathMatchSpawnPlayer(int playernum) { int i, j; int selections; selections = deathmatch_p - deathmatchstarts; if (selections < 4) I_Error("Only %i deathmatch spots, 4 required", selections); for (j = 0; j < 20; j++) { i = P_Random() % selections; if (G_CheckSpot(playernum, &deathmatchstarts[i])) { deathmatchstarts[i].type = playernum + 1; P_SpawnPlayer(&deathmatchstarts[i]); return; } } // no good spot, so the player will probably get stuck P_SpawnPlayer(&playerstarts[playernum]); } /* ==================== = = G_DoReborn = ==================== */ void G_DoReborn(int playernum) { int i; // quit demo unless -demoextend if (!demoextend && G_CheckDemoStatus()) return; if (!netgame) gameaction = ga_loadlevel; // reload the level from scratch else { // respawn at the start players[playernum].mo->player = NULL; // dissasociate the corpse // spawn at random spot if in death match if (deathmatch) { G_DeathMatchSpawnPlayer(playernum); return; } if (G_CheckSpot(playernum, &playerstarts[playernum])) { P_SpawnPlayer(&playerstarts[playernum]); return; } // try to spawn at one of the other players spots for (i = 0; i < MAXPLAYERS; i++) if (G_CheckSpot(playernum, &playerstarts[i])) { playerstarts[i].type = playernum + 1; // fake as other player P_SpawnPlayer(&playerstarts[i]); playerstarts[i].type = i + 1; // restore return; } // he's going to be inside something. Too bad. P_SpawnPlayer(&playerstarts[playernum]); } } void G_ScreenShot(void) { gameaction = ga_screenshot; } /* ==================== = = G_DoCompleted = ==================== */ boolean secretexit; void G_ExitLevel(void) { secretexit = false; gameaction = ga_completed; } void G_SecretExitLevel(void) { secretexit = true; gameaction = ga_completed; } void G_DoCompleted(void) { int i; static int afterSecret[5] = { 7, 5, 5, 5, 4 }; gameaction = ga_nothing; // quit demo unless -demoextend if (!demoextend && G_CheckDemoStatus()) { return; } for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { G_PlayerFinishLevel(i); } } prevmap = gamemap; if (secretexit == true) { gamemap = 9; } else if (gamemap == 9) { // Finished secret level gamemap = afterSecret[gameepisode - 1]; } else if (gamemap == 8) { gameaction = ga_victory; return; } else { gamemap++; } gamestate = GS_INTERMISSION; IN_Start(); } //============================================================================ // // G_WorldDone // //============================================================================ void G_WorldDone(void) { gameaction = ga_worlddone; } //============================================================================ // // G_DoWorldDone // //============================================================================ void G_DoWorldDone(void) { gamestate = GS_LEVEL; G_DoLoadLevel(); gameaction = ga_nothing; viewactive = true; } //--------------------------------------------------------------------------- // // PROC G_LoadGame // // Can be called by the startup code or the menu task. // //--------------------------------------------------------------------------- static char *savename = NULL; void G_LoadGame(char *name) { savename = M_StringDuplicate(name); gameaction = ga_loadgame; } //--------------------------------------------------------------------------- // // PROC G_DoLoadGame // // Called by G_Ticker based on gameaction. // //--------------------------------------------------------------------------- #define VERSIONSIZE 16 void G_DoLoadGame(void) { int i; int a, b, c; char savestr[SAVESTRINGSIZE]; char vcheck[VERSIONSIZE], readversion[VERSIONSIZE]; gameaction = ga_nothing; SV_OpenRead(savename); free(savename); savename = NULL; // Skip the description field SV_Read(savestr, SAVESTRINGSIZE); memset(vcheck, 0, sizeof(vcheck)); DEH_snprintf(vcheck, VERSIONSIZE, "version %i", HERETIC_VERSION); SV_Read(readversion, VERSIONSIZE); if (strncmp(readversion, vcheck, VERSIONSIZE) != 0) { // Bad version return; } gameskill = SV_ReadByte(); gameepisode = SV_ReadByte(); gamemap = SV_ReadByte(); for (i = 0; i < MAXPLAYERS; i++) { playeringame[i] = SV_ReadByte(); } // Load a base level G_InitNew(gameskill, gameepisode, gamemap); // Create leveltime a = SV_ReadByte(); b = SV_ReadByte(); c = SV_ReadByte(); leveltime = (a << 16) + (b << 8) + c; // De-archive all the modifications P_UnArchivePlayers(); P_UnArchiveWorld(); P_UnArchiveThinkers(); P_UnArchiveSpecials(); if (SV_ReadByte() != SAVE_GAME_TERMINATOR) { // Missing savegame termination marker I_Error("Bad savegame"); } } /* ==================== = = G_InitNew = = Can be called by the startup code or the menu task = consoleplayer, displayplayer, playeringame[] should be set ==================== */ skill_t d_skill; int d_episode; int d_map; void G_DeferedInitNew(skill_t skill, int episode, int map) { d_skill = skill; d_episode = episode; d_map = map; gameaction = ga_newgame; } void G_DoNewGame(void) { G_InitNew(d_skill, d_episode, d_map); gameaction = ga_nothing; } void G_InitNew(skill_t skill, int episode, int map) { int i; int speed; static const char *skyLumpNames[5] = { "SKY1", "SKY2", "SKY3", "SKY1", "SKY3" }; if (paused) { paused = false; S_ResumeSound(); } if (skill < sk_baby) skill = sk_baby; if (skill > sk_nightmare) skill = sk_nightmare; if (episode < 1) episode = 1; // Up to 9 episodes for testing if (episode > 9) episode = 9; if (map < 1) map = 1; if (map > 9) map = 9; M_ClearRandom(); if (respawnparm) { respawnmonsters = true; } else { respawnmonsters = false; } // Set monster missile speeds speed = skill == sk_nightmare; for (i = 0; MonsterMissileInfo[i].type != -1; i++) { mobjinfo[MonsterMissileInfo[i].type].speed = MonsterMissileInfo[i].speed[speed] << FRACBITS; } // Force players to be initialized upon first level load for (i = 0; i < MAXPLAYERS; i++) { players[i].playerstate = PST_REBORN; players[i].didsecret = false; } // Set up a bunch of globals usergame = true; // will be set false if a demo paused = false; demorecording = false; demoplayback = false; viewactive = true; gameepisode = episode; gamemap = map; gameskill = skill; viewactive = true; BorderNeedRefresh = true; // Set the sky map if (episode > 5) { skytexture = R_TextureNumForName(DEH_String("SKY1")); } else { skytexture = R_TextureNumForName(DEH_String(skyLumpNames[episode - 1])); } // // give one null ticcmd_t // #if 0 gametic = 0; maketic = 1; for (i = 0; i < MAXPLAYERS; i++) nettics[i] = 1; // one null event for this gametic memset(localcmds, 0, sizeof(localcmds)); memset(netcmds, 0, sizeof(netcmds)); #endif G_DoLoadLevel(); } /* =============================================================================== DEMO RECORDING =============================================================================== */ #define DEMOMARKER 0x80 #define DEMOHEADER_RESPAWN 0x20 #define DEMOHEADER_LONGTICS 0x10 #define DEMOHEADER_NOMONSTERS 0x02 void G_ReadDemoTiccmd(ticcmd_t * cmd) { if (*demo_p == DEMOMARKER) { // end of demo data stream G_CheckDemoStatus(); return; } cmd->forwardmove = ((signed char) *demo_p++); cmd->sidemove = ((signed char) *demo_p++); // If this is a longtics demo, read back in higher resolution if (longtics) { cmd->angleturn = *demo_p++; cmd->angleturn |= (*demo_p++) << 8; } else { cmd->angleturn = ((unsigned char) *demo_p++) << 8; } cmd->buttons = (unsigned char) *demo_p++; cmd->lookfly = (unsigned char) *demo_p++; cmd->arti = (unsigned char) *demo_p++; } // Increase the size of the demo buffer to allow unlimited demos static void IncreaseDemoBuffer(void) { int current_length; byte *new_demobuffer; byte *new_demop; int new_length; // Find the current size current_length = demoend - demobuffer; // Generate a new buffer twice the size new_length = current_length * 2; new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0); new_demop = new_demobuffer + (demo_p - demobuffer); // Copy over the old data memcpy(new_demobuffer, demobuffer, current_length); // Free the old buffer and point the demo pointers at the new buffer. Z_Free(demobuffer); demobuffer = new_demobuffer; demo_p = new_demop; demoend = demobuffer + new_length; } void G_WriteDemoTiccmd(ticcmd_t * cmd) { byte *demo_start; if (gamekeydown[key_demo_quit]) // press to end demo recording G_CheckDemoStatus(); demo_start = demo_p; *demo_p++ = cmd->forwardmove; *demo_p++ = cmd->sidemove; // If this is a longtics demo, record in higher resolution if (longtics) { *demo_p++ = (cmd->angleturn & 0xff); *demo_p++ = (cmd->angleturn >> 8) & 0xff; } else { *demo_p++ = cmd->angleturn >> 8; } *demo_p++ = cmd->buttons; *demo_p++ = cmd->lookfly; *demo_p++ = cmd->arti; // reset demo pointer back demo_p = demo_start; if (demo_p > demoend - 16) { if (vanilla_demo_limit) { // no more space G_CheckDemoStatus(); return; } else { // Vanilla demo limit disabled: unlimited // demo lengths! IncreaseDemoBuffer(); } } G_ReadDemoTiccmd(cmd); // make SURE it is exactly the same } /* =================== = = G_RecordDemo = =================== */ void G_RecordDemo(skill_t skill, int numplayers, int episode, int map, char *name) { int i; int maxsize; //! // @category demo // // Record or playback a demo with high resolution turning. // longtics = D_NonVanillaRecord(M_ParmExists("-longtics"), "vvHeretic longtics demo"); // If not recording a longtics demo, record in low res lowres_turn = !longtics; //! // @category demo // // Smooth out low resolution turning when recording a demo. // shortticfix = M_ParmExists("-shortticfix"); G_InitNew(skill, episode, map); usergame = false; M_StringCopy(demoname, name, sizeof(demoname)); M_StringConcat(demoname, ".lmp", sizeof(demoname)); maxsize = 0x20000; //! // @arg // @category demo // @vanilla // // Specify the demo buffer size (KiB) // i = M_CheckParmWithArgs("-maxdemo", 1); if (i) maxsize = atoi(myargv[i + 1]) * 1024; demobuffer = Z_Malloc(maxsize, PU_STATIC, NULL); demoend = demobuffer + maxsize; demo_p = demobuffer; *demo_p++ = skill; *demo_p++ = episode; *demo_p++ = map; // Write special parameter bits onto player one byte. // This aligns with vvHeretic demo usage: // 0x20 = -respawn // 0x10 = -longtics // 0x02 = -nomonsters *demo_p = 1; // assume player one exists if (D_NonVanillaRecord(respawnparm, "vvHeretic -respawn header flag")) { *demo_p |= DEMOHEADER_RESPAWN; } if (longtics) { *demo_p |= DEMOHEADER_LONGTICS; } if (D_NonVanillaRecord(nomonsters, "vvHeretic -nomonsters header flag")) { *demo_p |= DEMOHEADER_NOMONSTERS; } demo_p++; for (i = 1; i < MAXPLAYERS; i++) *demo_p++ = playeringame[i]; demorecording = true; } /* =================== = = G_PlayDemo = =================== */ static const char *defdemoname; void G_DeferedPlayDemo(const char *name) { defdemoname = name; gameaction = ga_playdemo; } void G_DoPlayDemo(void) { skill_t skill; int i, lumpnum, episode, map; gameaction = ga_nothing; lumpnum = W_GetNumForName(defdemoname); demobuffer = W_CacheLumpNum(lumpnum, PU_STATIC); demo_p = demobuffer; skill = *demo_p++; episode = *demo_p++; map = *demo_p++; // vvHeretic allows extra options to be stored in the upper bits of // the player 1 present byte. However, this is a non-vanilla extension. if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_LONGTICS) != 0, lumpnum, "vvHeretic longtics demo")) { longtics = true; } if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_RESPAWN) != 0, lumpnum, "vvHeretic -respawn header flag")) { respawnparm = true; } if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_NOMONSTERS) != 0, lumpnum, "vvHeretic -nomonsters header flag")) { nomonsters = true; } for (i = 0; i < MAXPLAYERS; i++) playeringame[i] = (*demo_p++) != 0; precache = false; // don't spend a lot of time in loadlevel G_InitNew(skill, episode, map); precache = true; usergame = false; demoplayback = true; } /* =================== = = G_TimeDemo = =================== */ void G_TimeDemo(char *name) { skill_t skill; int episode, map, i; demobuffer = demo_p = W_CacheLumpName(name, PU_STATIC); skill = *demo_p++; episode = *demo_p++; map = *demo_p++; // Read special parameter bits: see G_RecordDemo() for details. longtics = (*demo_p & DEMOHEADER_LONGTICS) != 0; // don't overwrite arguments from the command line respawnparm |= (*demo_p & DEMOHEADER_RESPAWN) != 0; nomonsters |= (*demo_p & DEMOHEADER_NOMONSTERS) != 0; for (i = 0; i < MAXPLAYERS; i++) { playeringame[i] = (*demo_p++) != 0; } G_InitNew(skill, episode, map); starttime = I_GetTime(); usergame = false; demoplayback = true; timingdemo = true; singletics = true; } /* =================== = = 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 =================== */ boolean G_CheckDemoStatus(void) { int endtime, realtics; if (timingdemo) { float fps; endtime = I_GetTime(); realtics = endtime - starttime; fps = ((float) gametic * TICRATE) / realtics; I_Error("timed %i gametics in %i realtics (%f fps)", gametic, realtics, fps); } if (demoplayback) { if (singledemo) I_Quit(); W_ReleaseLumpName(defdemoname); demoplayback = false; D_AdvanceDemo(); return true; } if (demorecording) { *demo_p++ = DEMOMARKER; M_WriteFile(demoname, demobuffer, demo_p - demobuffer); Z_Free(demobuffer); demorecording = false; I_Error("Demo %s recorded", demoname); } return false; } /**************************************************************************/ /**************************************************************************/ //========================================================================== // // G_SaveGame // // Called by the menu task. is a 24 byte text string. // //========================================================================== void G_SaveGame(int slot, char *description) { savegameslot = slot; M_StringCopy(savedescription, description, sizeof(savedescription)); sendsave = true; } //========================================================================== // // G_DoSaveGame // // Called by G_Ticker based on gameaction. // //========================================================================== void G_DoSaveGame(void) { int i; char *filename; char verString[VERSIONSIZE]; char *description; filename = SV_Filename(savegameslot); description = savedescription; SV_Open(filename); SV_Write(description, SAVESTRINGSIZE); memset(verString, 0, sizeof(verString)); DEH_snprintf(verString, VERSIONSIZE, "version %i", HERETIC_VERSION); SV_Write(verString, VERSIONSIZE); SV_WriteByte(gameskill); SV_WriteByte(gameepisode); SV_WriteByte(gamemap); for (i = 0; i < MAXPLAYERS; i++) { SV_WriteByte(playeringame[i]); } SV_WriteByte(leveltime >> 16); SV_WriteByte(leveltime >> 8); SV_WriteByte(leveltime); P_ArchivePlayers(); P_ArchiveWorld(); P_ArchiveThinkers(); P_ArchiveSpecials(); SV_Close(filename); gameaction = ga_nothing; savedescription[0] = 0; P_SetMessage(&players[consoleplayer], DEH_String(TXT_GAMESAVED), true); free(filename); } crispy-doom-crispy-doom-5.6.4/src/heretic/in_lude.c000066400000000000000000000650721360717211000222430ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // /* ======================== = = IN_lude.c = ======================== */ #include "doomdef.h" #include "deh_str.h" #include "p_local.h" #include "s_sound.h" #include "i_swap.h" #include "i_system.h" #include "i_video.h" #include "v_video.h" typedef enum { SINGLE, COOPERATIVE, DEATHMATCH } gametype_t; // Public functions boolean intermission; // Private functions static void IN_WaitStop(void); static void IN_Stop(void); static void IN_LoadPics(void); static void IN_UnloadPics(void); static void IN_CheckForSkip(void); static void IN_InitStats(void); static void IN_DrawOldLevel(void); static void IN_DrawYAH(void); static void IN_DrawStatBack(void); static void IN_DrawSingleStats(void); static void IN_DrawCoopStats(void); static void IN_DrawDMStats(void); static void IN_DrawNumber(int val, int x, int y, int digits); static void IN_DrawTime(int x, int y, int h, int m, int s); static void IN_DrTextB(const char *text, int x, int y); static boolean skipintermission; static int interstate = 0; static int intertime = -1; static int oldintertime = 0; static gametype_t gametype; static int cnt; static int hours; static int minutes; static int seconds; static int slaughterboy; // in DM, the player with the most kills static int killPercent[MAXPLAYERS]; static int bonusPercent[MAXPLAYERS]; static int secretPercent[MAXPLAYERS]; static patch_t *patchINTERPIC; static patch_t *patchBEENTHERE; static patch_t *patchGOINGTHERE; static patch_t *FontBNumbers[10]; static patch_t *FontBNegative; static patch_t *FontBSlash; static patch_t *FontBPercent; static int FontBLump; static int FontBLumpBase; static int patchFaceOkayBase; static int patchFaceDeadBase; static signed int totalFrags[MAXPLAYERS]; static fixed_t dSlideX[MAXPLAYERS]; static fixed_t dSlideY[MAXPLAYERS]; static const char *KillersText[] = { "K", "I", "L", "L", "E", "R", "S" }; extern const char *LevelNames[]; typedef struct { int x; int y; } yahpt_t; static yahpt_t YAHspot[3][9] = { { {172, 78}, {86, 90}, {73, 66}, {159, 95}, {148, 126}, {132, 54}, {131, 74}, {208, 138}, {52, 101} }, { {218, 57}, {137, 81}, {155, 124}, {171, 68}, {250, 86}, {136, 98}, {203, 90}, {220, 140}, {279, 106} }, { {86, 99}, {124, 103}, {154, 79}, {202, 83}, {178, 59}, {142, 58}, {219, 66}, {247, 57}, {107, 80} } }; static const char *NameForMap(int map) { const char *name = LevelNames[(gameepisode - 1) * 9 + map - 1]; name = DEH_String(name); if (strlen(name) < 7) { return ""; } return name + 7; } //======================================================================== // // IN_Start // //======================================================================== extern void AM_Stop(void); void IN_Start(void) { I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE)); IN_LoadPics(); IN_InitStats(); intermission = true; interstate = -1; skipintermission = false; intertime = 0; oldintertime = 0; AM_Stop(); S_StartSong(mus_intr, true); } //======================================================================== // // IN_WaitStop // //======================================================================== void IN_WaitStop(void) { if (!--cnt) { IN_Stop(); G_WorldDone(); } } //======================================================================== // // IN_Stop // //======================================================================== void IN_Stop(void) { intermission = false; IN_UnloadPics(); SB_state = -1; BorderNeedRefresh = true; } //======================================================================== // // IN_InitStats // // Initializes the stats for single player mode //======================================================================== void IN_InitStats(void) { int i; int j; signed int slaughterfrags; int posnum; int slaughtercount; int playercount; int count; if (!netgame) { gametype = SINGLE; count = leveltime / 35; hours = count / 3600; count -= hours * 3600; minutes = count / 60; count -= minutes * 60; seconds = count; } else if (netgame && !deathmatch) { gametype = COOPERATIVE; memset(killPercent, 0, MAXPLAYERS * sizeof(int)); memset(bonusPercent, 0, MAXPLAYERS * sizeof(int)); memset(secretPercent, 0, MAXPLAYERS * sizeof(int)); for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { if (totalkills) { killPercent[i] = players[i].killcount * 100 / totalkills; } if (totalitems) { bonusPercent[i] = players[i].itemcount * 100 / totalitems; } if (totalsecret) { secretPercent[i] = players[i].secretcount * 100 / totalsecret; } } } } else { gametype = DEATHMATCH; slaughterboy = 0; slaughterfrags = -9999; posnum = 0; playercount = 0; slaughtercount = 0; for (i = 0; i < MAXPLAYERS; i++) { totalFrags[i] = 0; if (playeringame[i]) { playercount++; for (j = 0; j < MAXPLAYERS; j++) { if (playeringame[j]) { totalFrags[i] += players[i].frags[j]; } } dSlideX[i] = (43 * posnum * FRACUNIT) / 20; dSlideY[i] = (36 * posnum * FRACUNIT) / 20; posnum++; } if (totalFrags[i] > slaughterfrags) { slaughterboy = 1 << i; slaughterfrags = totalFrags[i]; slaughtercount = 1; } else if (totalFrags[i] == slaughterfrags) { slaughterboy |= 1 << i; slaughtercount++; } } if (playercount == slaughtercount) { // don't do the slaughter stuff if everyone is equal slaughterboy = 0; } } } static void IN_LoadUnloadPics(void (*callback)(const char *lumpname, int lumpnum, patch_t **ptr)) { int i; switch (gameepisode) { case 1: callback(DEH_String("MAPE1"), 0, &patchINTERPIC); break; case 2: callback(DEH_String("MAPE2"), 0, &patchINTERPIC); break; case 3: callback(DEH_String("MAPE3"), 0, &patchINTERPIC); break; default: break; } callback(DEH_String("IN_X"), 0, &patchBEENTHERE); callback(DEH_String("IN_YAH"), 0, &patchGOINGTHERE); callback(DEH_String("FONTB13"), 0, &FontBNegative); callback(DEH_String("FONTB15"), 0, &FontBSlash); callback(DEH_String("FONTB05"), 0, &FontBPercent); FontBLumpBase = W_GetNumForName(DEH_String("FONTB16")); for (i = 0; i < 10; i++) { callback(NULL, FontBLumpBase + i, &FontBNumbers[i]); } } //======================================================================== // // IN_LoadPics // //======================================================================== static void LoadLumpCallback(const char *lumpname, int lumpnum, patch_t **ptr) { if (lumpname != NULL) { lumpnum = W_GetNumForName(lumpname); } // Cache the lump *ptr = W_CacheLumpNum(lumpnum, PU_STATIC); } void IN_LoadPics(void) { FontBLump = W_GetNumForName(DEH_String("FONTB_S")) + 1; patchFaceOkayBase = W_GetNumForName(DEH_String("FACEA0")); patchFaceDeadBase = W_GetNumForName(DEH_String("FACEB0")); IN_LoadUnloadPics(LoadLumpCallback); } //======================================================================== // // IN_UnloadPics // //======================================================================== static void UnloadLumpCallback(const char *lumpname, int lumpnum, patch_t **ptr) { if (lumpname != NULL) { W_ReleaseLumpName(lumpname); } else { W_ReleaseLumpNum(lumpnum); } } void IN_UnloadPics(void) { IN_LoadUnloadPics(UnloadLumpCallback); } //======================================================================== // // IN_Ticker // //======================================================================== void IN_Ticker(void) { if (!intermission) { return; } if (interstate == 3) { IN_WaitStop(); return; } IN_CheckForSkip(); intertime++; if (oldintertime < intertime) { interstate++; if (gameepisode > 3 && interstate >= 1) { // Extended Wad levels: skip directly to the next level interstate = 3; } switch (interstate) { case 0: oldintertime = intertime + 300; if (gameepisode > 3) { oldintertime = intertime + 1200; } break; case 1: oldintertime = intertime + 200; break; case 2: oldintertime = INT_MAX; break; case 3: cnt = 10; break; default: break; } } if (skipintermission) { if (interstate == 0 && intertime < 150) { intertime = 150; skipintermission = false; return; } else if (interstate < 2 && gameepisode < 4) { interstate = 2; skipintermission = false; S_StartSound(NULL, sfx_dorcls); return; } interstate = 3; cnt = 10; skipintermission = false; S_StartSound(NULL, sfx_dorcls); } } //======================================================================== // // IN_CheckForSkip // // Check to see if any player hit a key //======================================================================== void IN_CheckForSkip(void) { int i; player_t *player; for (i = 0, player = players; i < MAXPLAYERS; i++, player++) { if (playeringame[i]) { if (player->cmd.buttons & BT_ATTACK) { if (!player->attackdown) { skipintermission = 1; } player->attackdown = true; } else { player->attackdown = false; } if (player->cmd.buttons & BT_USE) { if (!player->usedown) { skipintermission = 1; } player->usedown = true; } else { player->usedown = false; } } } } //======================================================================== // // IN_Drawer // //======================================================================== void IN_Drawer(void) { static int oldinterstate; if (!intermission) { return; } if (interstate == 3) { return; } UpdateState |= I_FULLSCRN; if (oldinterstate != 2 && interstate == 2) { S_StartSound(NULL, sfx_pstop); } oldinterstate = interstate; switch (interstate) { case 0: // draw stats IN_DrawStatBack(); switch (gametype) { case SINGLE: IN_DrawSingleStats(); break; case COOPERATIVE: IN_DrawCoopStats(); break; case DEATHMATCH: IN_DrawDMStats(); break; } break; case 1: // leaving old level if (gameepisode < 4) { V_DrawPatch(0, 0, patchINTERPIC); IN_DrawOldLevel(); } break; case 2: // going to the next level if (gameepisode < 4) { V_DrawPatch(0, 0, patchINTERPIC); IN_DrawYAH(); } break; case 3: // waiting before going to the next level if (gameepisode < 4) { V_DrawPatch(0, 0, patchINTERPIC); } break; default: I_Error("IN_lude: Intermission state out of range.\n"); break; } } //======================================================================== // // IN_DrawStatBack // //======================================================================== void IN_DrawStatBack(void) { int x; int y; byte *src; byte *dest; src = W_CacheLumpName(DEH_String("FLOOR16"), PU_CACHE); dest = I_VideoBuffer; for (y = 0; y < SCREENHEIGHT; y++) { for (x = 0; x < SCREENWIDTH / 64; x++) { memcpy(dest, src + ((y & 63) << 6), 64); dest += 64; } if (SCREENWIDTH & 63) { memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); dest += (SCREENWIDTH & 63); } } } //======================================================================== // // IN_DrawOldLevel // //======================================================================== void IN_DrawOldLevel(void) { const char *level_name = NameForMap(prevmap); int i; int x; x = 160 - MN_TextBWidth(level_name) / 2; IN_DrTextB(level_name, x, 3); x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2; MN_DrTextA(DEH_String("FINISHED"), x, 25); if (prevmap == 9) { for (i = 0; i < gamemap - 1; i++) { V_DrawPatch(YAHspot[gameepisode - 1][i].x, YAHspot[gameepisode - 1][i].y, patchBEENTHERE); } if (!(intertime & 16)) { V_DrawPatch(YAHspot[gameepisode - 1][8].x, YAHspot[gameepisode - 1][8].y, patchBEENTHERE); } } else { for (i = 0; i < prevmap - 1; i++) { V_DrawPatch(YAHspot[gameepisode - 1][i].x, YAHspot[gameepisode - 1][i].y, patchBEENTHERE); } if (players[consoleplayer].didsecret) { V_DrawPatch(YAHspot[gameepisode - 1][8].x, YAHspot[gameepisode - 1][8].y, patchBEENTHERE); } if (!(intertime & 16)) { V_DrawPatch(YAHspot[gameepisode - 1][prevmap - 1].x, YAHspot[gameepisode - 1][prevmap - 1].y, patchBEENTHERE); } } } //======================================================================== // // IN_DrawYAH // //======================================================================== void IN_DrawYAH(void) { const char *level_name = NameForMap(gamemap); int i; int x; x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2; MN_DrTextA(DEH_String("NOW ENTERING:"), x, 10); x = 160 - MN_TextBWidth(level_name) / 2; IN_DrTextB(level_name, x, 20); if (prevmap == 9) { prevmap = gamemap - 1; } for (i = 0; i < prevmap; i++) { V_DrawPatch(YAHspot[gameepisode - 1][i].x, YAHspot[gameepisode - 1][i].y, patchBEENTHERE); } if (players[consoleplayer].didsecret) { V_DrawPatch(YAHspot[gameepisode - 1][8].x, YAHspot[gameepisode - 1][8].y, patchBEENTHERE); } if (!(intertime & 16) || interstate == 3) { // draw the destination 'X' V_DrawPatch(YAHspot[gameepisode - 1][gamemap - 1].x, YAHspot[gameepisode - 1][gamemap - 1].y, patchGOINGTHERE); } } //======================================================================== // // IN_DrawSingleStats // //======================================================================== void IN_DrawSingleStats(void) { const char *prev_level_name = NameForMap(prevmap); const char *next_level_name = NameForMap(gamemap); int x; static int sounds; IN_DrTextB(DEH_String("KILLS"), 50, 65); IN_DrTextB(DEH_String("ITEMS"), 50, 90); IN_DrTextB(DEH_String("SECRETS"), 50, 115); x = 160 - MN_TextBWidth(prev_level_name) / 2; IN_DrTextB(prev_level_name, x, 3); x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2; MN_DrTextA(DEH_String("FINISHED"), x, 25); if (intertime < 30) { sounds = 0; return; } if (sounds < 1 && intertime >= 30) { S_StartSound(NULL, sfx_dorcls); sounds++; } IN_DrawNumber(players[consoleplayer].killcount, 200, 65, 3); V_DrawShadowedPatch(237, 65, FontBSlash); IN_DrawNumber(totalkills, 248, 65, 3); if (intertime < 60) { return; } if (sounds < 2 && intertime >= 60) { S_StartSound(NULL, sfx_dorcls); sounds++; } IN_DrawNumber(players[consoleplayer].itemcount, 200, 90, 3); V_DrawShadowedPatch(237, 90, FontBSlash); IN_DrawNumber(totalitems, 248, 90, 3); if (intertime < 90) { return; } if (sounds < 3 && intertime >= 90) { S_StartSound(NULL, sfx_dorcls); sounds++; } IN_DrawNumber(players[consoleplayer].secretcount, 200, 115, 3); V_DrawShadowedPatch(237, 115, FontBSlash); IN_DrawNumber(totalsecret, 248, 115, 3); if (intertime < 150) { return; } if (sounds < 4 && intertime >= 150) { S_StartSound(NULL, sfx_dorcls); sounds++; } if (gamemode != retail || gameepisode <= 3) { IN_DrTextB(DEH_String("TIME"), 85, 160); IN_DrawTime(155, 160, hours, minutes, seconds); } else { x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2; MN_DrTextA(DEH_String("NOW ENTERING:"), x, 160); x = 160 - MN_TextBWidth(next_level_name) / 2; IN_DrTextB(next_level_name, x, 170); skipintermission = false; } } //======================================================================== // // IN_DrawCoopStats // //======================================================================== void IN_DrawCoopStats(void) { const char *level_name = NameForMap(prevmap); int i; int x; int ypos; static int sounds; IN_DrTextB(DEH_String("KILLS"), 95, 35); IN_DrTextB(DEH_String("BONUS"), 155, 35); IN_DrTextB(DEH_String("SECRET"), 232, 35); x = 160 - MN_TextBWidth(level_name) / 2; IN_DrTextB(level_name, x, 3); x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2; MN_DrTextA(DEH_String("FINISHED"), x, 25); ypos = 50; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { V_DrawShadowedPatch(25, ypos, W_CacheLumpNum(patchFaceOkayBase + i, PU_CACHE)); if (intertime < 40) { sounds = 0; ypos += 37; continue; } else if (intertime >= 40 && sounds < 1) { S_StartSound(NULL, sfx_dorcls); sounds++; } IN_DrawNumber(killPercent[i], 85, ypos + 10, 3); V_DrawShadowedPatch(121, ypos + 10, FontBPercent); IN_DrawNumber(bonusPercent[i], 160, ypos + 10, 3); V_DrawShadowedPatch(196, ypos + 10, FontBPercent); IN_DrawNumber(secretPercent[i], 237, ypos + 10, 3); V_DrawShadowedPatch(273, ypos + 10, FontBPercent); ypos += 37; } } } //======================================================================== // // IN_DrawDMStats // //======================================================================== void IN_DrawDMStats(void) { int i; int j; int ypos; int xpos; int kpos; static int sounds; xpos = 90; ypos = 55; IN_DrTextB(DEH_String("TOTAL"), 265, 30); MN_DrTextA(DEH_String("VICTIMS"), 140, 8); for (i = 0; i < 7; i++) { MN_DrTextA(DEH_String(KillersText[i]), 10, 80 + 9 * i); } if (intertime < 20) { for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { V_DrawShadowedPatch(40, ((ypos << FRACBITS) + dSlideY[i] * intertime) >> FRACBITS, W_CacheLumpNum(patchFaceOkayBase + i, PU_CACHE)); V_DrawShadowedPatch(((xpos << FRACBITS) + dSlideX[i] * intertime) >> FRACBITS, 18, W_CacheLumpNum(patchFaceDeadBase + i, PU_CACHE)); } } sounds = 0; return; } if (intertime >= 20 && sounds < 1) { S_StartSound(NULL, sfx_dorcls); sounds++; } if (intertime >= 100 && slaughterboy && sounds < 2) { S_StartSound(NULL, sfx_wpnup); sounds++; } for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { if (intertime < 100 || i == consoleplayer) { V_DrawShadowedPatch(40, ypos, W_CacheLumpNum(patchFaceOkayBase + i, PU_CACHE)); V_DrawShadowedPatch(xpos, 18, W_CacheLumpNum(patchFaceDeadBase + i, PU_CACHE)); } else { V_DrawTLPatch(40, ypos, W_CacheLumpNum(patchFaceOkayBase + i, PU_CACHE)); V_DrawTLPatch(xpos, 18, W_CacheLumpNum(patchFaceDeadBase + i, PU_CACHE)); } kpos = 86; for (j = 0; j < MAXPLAYERS; j++) { if (playeringame[j]) { IN_DrawNumber(players[i].frags[j], kpos, ypos + 10, 3); kpos += 43; } } if (slaughterboy & (1 << i)) { if (!(intertime & 16)) { IN_DrawNumber(totalFrags[i], 263, ypos + 10, 3); } } else { IN_DrawNumber(totalFrags[i], 263, ypos + 10, 3); } ypos += 36; xpos += 43; } } } //======================================================================== // // IN_DrawTime // //======================================================================== void IN_DrawTime(int x, int y, int h, int m, int s) { if (h) { IN_DrawNumber(h, x, y, 2); IN_DrTextB(DEH_String(":"), x + 26, y); } x += 34; if (m || h) { IN_DrawNumber(m, x, y, 2); } x += 34; if (s) { IN_DrTextB(DEH_String(":"), x - 8, y); IN_DrawNumber(s, x, y, 2); } } //======================================================================== // // IN_DrawNumber // //======================================================================== void IN_DrawNumber(int val, int x, int y, int digits) { patch_t *patch; int xpos; int oldval; int realdigits; boolean neg; oldval = val; xpos = x; neg = false; realdigits = 1; if (val < 0) { //...this should reflect negative frags val = -val; neg = true; if (val > 99) { val = 99; } } if (val > 9) { realdigits++; if (digits < realdigits) { realdigits = digits; val = 9; } } if (val > 99) { realdigits++; if (digits < realdigits) { realdigits = digits; val = 99; } } if (val > 999) { realdigits++; if (digits < realdigits) { realdigits = digits; val = 999; } } if (digits == 4) { patch = FontBNumbers[val / 1000]; V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2 - 12, y, patch); } if (digits > 2) { if (realdigits > 2) { patch = FontBNumbers[val / 100]; V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } xpos += 12; } val = val % 100; if (digits > 1) { if (val > 9) { patch = FontBNumbers[val / 10]; V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } else if (digits == 2 || oldval > 99) { V_DrawShadowedPatch(xpos, y, FontBNumbers[0]); } xpos += 12; } val = val % 10; patch = FontBNumbers[val]; V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); if (neg) { patch = FontBNegative; V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2 - 12 * (realdigits), y, patch); } } //======================================================================== // // IN_DrTextB // //======================================================================== void IN_DrTextB(const char *text, int x, int y) { char c; patch_t *p; while ((c = *text++) != 0) { if (c < 33) { x += 8; } else { p = W_CacheLumpNum(FontBLump + c - 33, PU_CACHE); V_DrawShadowedPatch(x, y, p); x += SHORT(p->width) - 1; } } } crispy-doom-crispy-doom-5.6.4/src/heretic/info.c000066400000000000000000010251371360717211000215560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "doomdef.h" #include "p_action.h" const char *sprnames[] = { "IMPX","ACLO","PTN1","SHLD","SHD2","BAGH","SPMP","INVS","PTN2","SOAR", "INVU","PWBK","EGGC","EGGM","FX01","SPHL","TRCH","FBMB","XPL1","ATLP", "PPOD","AMG1","SPSH","LVAS","SLDG","SKH1","SKH2","SKH3","SKH4","CHDL", "SRTC","SMPL","STGS","STGL","STCS","STCL","KFR1","BARL","BRPL","MOS1", "MOS2","WTRH","HCOR","KGZ1","KGZB","KGZG","KGZY","VLCO","VFBL","VTFB", "SFFI","TGLT","TELE","STFF","PUF3","PUF4","BEAK","WGNT","GAUN","PUF1", "WBLS","BLSR","FX18","FX17","WMCE","MACE","FX02","WSKL","HROD","FX00", "FX20","FX21","FX22","FX23","GWND","PUF2","WPHX","PHNX","FX04","FX08", "FX09","WBOW","CRBW","FX03","BLOD","PLAY","FDTH","BSKL","CHKN","MUMM", "FX15","BEAS","FRB1","SNKE","SNFX","HEAD","FX05","FX06","FX07","CLNK", "WZRD","FX11","FX10","KNIG","SPAX","RAXE","SRCR","FX14","SOR2","SDTH", "FX16","MNTR","FX12","FX13","AKYY","BKYY","CKYY","AMG2","AMM1","AMM2", "AMC1","AMC2","AMS1","AMS2","AMP1","AMP2","AMB1","AMB2", NULL }; state_t states[NUMSTATES] = { {SPR_IMPX, 0, -1, NULL, S_NULL, 0, 0}, // S_NULL {SPR_ACLO, 4, 1050, A_FreeTargMobj, S_NULL, 0, 0}, // S_FREETARGMOBJ {SPR_PTN1, 0, 3, NULL, S_ITEM_PTN1_2, 0, 0}, // S_ITEM_PTN1_1 {SPR_PTN1, 1, 3, NULL, S_ITEM_PTN1_3, 0, 0}, // S_ITEM_PTN1_2 {SPR_PTN1, 2, 3, NULL, S_ITEM_PTN1_1, 0, 0}, // S_ITEM_PTN1_3 {SPR_SHLD, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_SHLD1 {SPR_SHD2, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_SHD2_1 {SPR_BAGH, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_BAGH1 {SPR_SPMP, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_SPMP1 {SPR_ACLO, 4, 1400, NULL, S_HIDESPECIAL2, 0, 0}, // S_HIDESPECIAL1 {SPR_ACLO, 0, 4, A_RestoreSpecialThing1, S_HIDESPECIAL3, 0, 0}, // S_HIDESPECIAL2 {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL4, 0, 0}, // S_HIDESPECIAL3 {SPR_ACLO, 0, 4, NULL, S_HIDESPECIAL5, 0, 0}, // S_HIDESPECIAL4 {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL6, 0, 0}, // S_HIDESPECIAL5 {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL7, 0, 0}, // S_HIDESPECIAL6 {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL8, 0, 0}, // S_HIDESPECIAL7 {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL9, 0, 0}, // S_HIDESPECIAL8 {SPR_ACLO, 3, 4, NULL, S_HIDESPECIAL10, 0, 0}, // S_HIDESPECIAL9 {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL11, 0, 0}, // S_HIDESPECIAL10 {SPR_ACLO, 3, 4, A_RestoreSpecialThing2, S_NULL, 0, 0}, // S_HIDESPECIAL11 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI2, 0, 0}, // S_DORMANTARTI1 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3, 0, 0}, // S_DORMANTARTI2 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI4, 0, 0}, // S_DORMANTARTI3 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI5, 0, 0}, // S_DORMANTARTI4 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI6, 0, 0}, // S_DORMANTARTI5 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI7, 0, 0}, // S_DORMANTARTI6 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI8, 0, 0}, // S_DORMANTARTI7 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI9, 0, 0}, // S_DORMANTARTI8 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI10, 0, 0}, // S_DORMANTARTI9 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI11, 0, 0}, // S_DORMANTARTI10 {SPR_ACLO, 0, 1400, A_HideThing, S_DORMANTARTI12, 0, 0}, // S_DORMANTARTI11 {SPR_ACLO, 0, 3, A_UnHideThing, S_DORMANTARTI13, 0, 0}, // S_DORMANTARTI12 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI14, 0, 0}, // S_DORMANTARTI13 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI15, 0, 0}, // S_DORMANTARTI14 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI16, 0, 0}, // S_DORMANTARTI15 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI17, 0, 0}, // S_DORMANTARTI16 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI18, 0, 0}, // S_DORMANTARTI17 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI19, 0, 0}, // S_DORMANTARTI18 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI20, 0, 0}, // S_DORMANTARTI19 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI21, 0, 0}, // S_DORMANTARTI20 {SPR_ACLO, 3, 3, A_RestoreArtifact, S_NULL, 0, 0}, // S_DORMANTARTI21 {SPR_ACLO, 3, 3, NULL, S_DEADARTI2, 0, 0}, // S_DEADARTI1 {SPR_ACLO, 2, 3, NULL, S_DEADARTI3, 0, 0}, // S_DEADARTI2 {SPR_ACLO, 3, 3, NULL, S_DEADARTI4, 0, 0}, // S_DEADARTI3 {SPR_ACLO, 2, 3, NULL, S_DEADARTI5, 0, 0}, // S_DEADARTI4 {SPR_ACLO, 1, 3, NULL, S_DEADARTI6, 0, 0}, // S_DEADARTI5 {SPR_ACLO, 2, 3, NULL, S_DEADARTI7, 0, 0}, // S_DEADARTI6 {SPR_ACLO, 1, 3, NULL, S_DEADARTI8, 0, 0}, // S_DEADARTI7 {SPR_ACLO, 0, 3, NULL, S_DEADARTI9, 0, 0}, // S_DEADARTI8 {SPR_ACLO, 1, 3, NULL, S_DEADARTI10, 0, 0}, // S_DEADARTI9 {SPR_ACLO, 0, 3, NULL, S_NULL, 0, 0}, // S_DEADARTI10 {SPR_INVS, 32768, 350, NULL, S_ARTI_INVS1, 0, 0}, // S_ARTI_INVS1 {SPR_PTN2, 0, 4, NULL, S_ARTI_PTN2_2, 0, 0}, // S_ARTI_PTN2_1 {SPR_PTN2, 1, 4, NULL, S_ARTI_PTN2_3, 0, 0}, // S_ARTI_PTN2_2 {SPR_PTN2, 2, 4, NULL, S_ARTI_PTN2_1, 0, 0}, // S_ARTI_PTN2_3 {SPR_SOAR, 0, 5, NULL, S_ARTI_SOAR2, 0, 0}, // S_ARTI_SOAR1 {SPR_SOAR, 1, 5, NULL, S_ARTI_SOAR3, 0, 0}, // S_ARTI_SOAR2 {SPR_SOAR, 2, 5, NULL, S_ARTI_SOAR4, 0, 0}, // S_ARTI_SOAR3 {SPR_SOAR, 1, 5, NULL, S_ARTI_SOAR1, 0, 0}, // S_ARTI_SOAR4 {SPR_INVU, 0, 3, NULL, S_ARTI_INVU2, 0, 0}, // S_ARTI_INVU1 {SPR_INVU, 1, 3, NULL, S_ARTI_INVU3, 0, 0}, // S_ARTI_INVU2 {SPR_INVU, 2, 3, NULL, S_ARTI_INVU4, 0, 0}, // S_ARTI_INVU3 {SPR_INVU, 3, 3, NULL, S_ARTI_INVU1, 0, 0}, // S_ARTI_INVU4 {SPR_PWBK, 0, 350, NULL, S_ARTI_PWBK1, 0, 0}, // S_ARTI_PWBK1 {SPR_EGGC, 0, 6, NULL, S_ARTI_EGGC2, 0, 0}, // S_ARTI_EGGC1 {SPR_EGGC, 1, 6, NULL, S_ARTI_EGGC3, 0, 0}, // S_ARTI_EGGC2 {SPR_EGGC, 2, 6, NULL, S_ARTI_EGGC4, 0, 0}, // S_ARTI_EGGC3 {SPR_EGGC, 1, 6, NULL, S_ARTI_EGGC1, 0, 0}, // S_ARTI_EGGC4 {SPR_EGGM, 0, 4, NULL, S_EGGFX2, 0, 0}, // S_EGGFX1 {SPR_EGGM, 1, 4, NULL, S_EGGFX3, 0, 0}, // S_EGGFX2 {SPR_EGGM, 2, 4, NULL, S_EGGFX4, 0, 0}, // S_EGGFX3 {SPR_EGGM, 3, 4, NULL, S_EGGFX5, 0, 0}, // S_EGGFX4 {SPR_EGGM, 4, 4, NULL, S_EGGFX1, 0, 0}, // S_EGGFX5 {SPR_FX01, 32772, 3, NULL, S_EGGFXI1_2, 0, 0}, // S_EGGFXI1_1 {SPR_FX01, 32773, 3, NULL, S_EGGFXI1_3, 0, 0}, // S_EGGFXI1_2 {SPR_FX01, 32774, 3, NULL, S_EGGFXI1_4, 0, 0}, // S_EGGFXI1_3 {SPR_FX01, 32775, 3, NULL, S_NULL, 0, 0}, // S_EGGFXI1_4 {SPR_SPHL, 0, 350, NULL, S_ARTI_SPHL1, 0, 0}, // S_ARTI_SPHL1 {SPR_TRCH, 32768, 3, NULL, S_ARTI_TRCH2, 0, 0}, // S_ARTI_TRCH1 {SPR_TRCH, 32769, 3, NULL, S_ARTI_TRCH3, 0, 0}, // S_ARTI_TRCH2 {SPR_TRCH, 32770, 3, NULL, S_ARTI_TRCH1, 0, 0}, // S_ARTI_TRCH3 {SPR_FBMB, 4, 350, NULL, S_ARTI_FBMB1, 0, 0}, // S_ARTI_FBMB1 {SPR_FBMB, 0, 10, NULL, S_FIREBOMB2, 0, 0}, // S_FIREBOMB1 {SPR_FBMB, 1, 10, NULL, S_FIREBOMB3, 0, 0}, // S_FIREBOMB2 {SPR_FBMB, 2, 10, NULL, S_FIREBOMB4, 0, 0}, // S_FIREBOMB3 {SPR_FBMB, 3, 10, NULL, S_FIREBOMB5, 0, 0}, // S_FIREBOMB4 {SPR_FBMB, 4, 6, A_Scream, S_FIREBOMB6, 0, 0}, // S_FIREBOMB5 {SPR_XPL1, 32768, 4, A_Explode, S_FIREBOMB7, 0, 0}, // S_FIREBOMB6 {SPR_XPL1, 32769, 4, NULL, S_FIREBOMB8, 0, 0}, // S_FIREBOMB7 {SPR_XPL1, 32770, 4, NULL, S_FIREBOMB9, 0, 0}, // S_FIREBOMB8 {SPR_XPL1, 32771, 4, NULL, S_FIREBOMB10, 0, 0}, // S_FIREBOMB9 {SPR_XPL1, 32772, 4, NULL, S_FIREBOMB11, 0, 0}, // S_FIREBOMB10 {SPR_XPL1, 32773, 4, NULL, S_NULL, 0, 0}, // S_FIREBOMB11 {SPR_ATLP, 0, 4, NULL, S_ARTI_ATLP2, 0, 0}, // S_ARTI_ATLP1 {SPR_ATLP, 1, 4, NULL, S_ARTI_ATLP3, 0, 0}, // S_ARTI_ATLP2 {SPR_ATLP, 2, 4, NULL, S_ARTI_ATLP4, 0, 0}, // S_ARTI_ATLP3 {SPR_ATLP, 1, 4, NULL, S_ARTI_ATLP1, 0, 0}, // S_ARTI_ATLP4 {SPR_PPOD, 0, 10, NULL, S_POD_WAIT1, 0, 0}, // S_POD_WAIT1 {SPR_PPOD, 1, 14, A_PodPain, S_POD_WAIT1, 0, 0}, // S_POD_PAIN1 {SPR_PPOD, 32770, 5, A_RemovePod, S_POD_DIE2, 0, 0}, // S_POD_DIE1 {SPR_PPOD, 32771, 5, A_Scream, S_POD_DIE3, 0, 0}, // S_POD_DIE2 {SPR_PPOD, 32772, 5, A_Explode, S_POD_DIE4, 0, 0}, // S_POD_DIE3 {SPR_PPOD, 32773, 10, NULL, S_FREETARGMOBJ, 0, 0}, // S_POD_DIE4 {SPR_PPOD, 8, 3, NULL, S_POD_GROW2, 0, 0}, // S_POD_GROW1 {SPR_PPOD, 9, 3, NULL, S_POD_GROW3, 0, 0}, // S_POD_GROW2 {SPR_PPOD, 10, 3, NULL, S_POD_GROW4, 0, 0}, // S_POD_GROW3 {SPR_PPOD, 11, 3, NULL, S_POD_GROW5, 0, 0}, // S_POD_GROW4 {SPR_PPOD, 12, 3, NULL, S_POD_GROW6, 0, 0}, // S_POD_GROW5 {SPR_PPOD, 13, 3, NULL, S_POD_GROW7, 0, 0}, // S_POD_GROW6 {SPR_PPOD, 14, 3, NULL, S_POD_GROW8, 0, 0}, // S_POD_GROW7 {SPR_PPOD, 15, 3, NULL, S_POD_WAIT1, 0, 0}, // S_POD_GROW8 {SPR_PPOD, 6, 8, NULL, S_PODGOO2, 0, 0}, // S_PODGOO1 {SPR_PPOD, 7, 8, NULL, S_PODGOO1, 0, 0}, // S_PODGOO2 {SPR_PPOD, 6, 10, NULL, S_NULL, 0, 0}, // S_PODGOOX {SPR_AMG1, 0, 35, A_MakePod, S_PODGENERATOR, 0, 0}, // S_PODGENERATOR {SPR_SPSH, 0, 8, NULL, S_SPLASH2, 0, 0}, // S_SPLASH1 {SPR_SPSH, 1, 8, NULL, S_SPLASH3, 0, 0}, // S_SPLASH2 {SPR_SPSH, 2, 8, NULL, S_SPLASH4, 0, 0}, // S_SPLASH3 {SPR_SPSH, 3, 16, NULL, S_NULL, 0, 0}, // S_SPLASH4 {SPR_SPSH, 3, 10, NULL, S_NULL, 0, 0}, // S_SPLASHX {SPR_SPSH, 4, 5, NULL, S_SPLASHBASE2, 0, 0}, // S_SPLASHBASE1 {SPR_SPSH, 5, 5, NULL, S_SPLASHBASE3, 0, 0}, // S_SPLASHBASE2 {SPR_SPSH, 6, 5, NULL, S_SPLASHBASE4, 0, 0}, // S_SPLASHBASE3 {SPR_SPSH, 7, 5, NULL, S_SPLASHBASE5, 0, 0}, // S_SPLASHBASE4 {SPR_SPSH, 8, 5, NULL, S_SPLASHBASE6, 0, 0}, // S_SPLASHBASE5 {SPR_SPSH, 9, 5, NULL, S_SPLASHBASE7, 0, 0}, // S_SPLASHBASE6 {SPR_SPSH, 10, 5, NULL, S_NULL, 0, 0}, // S_SPLASHBASE7 {SPR_LVAS, 32768, 5, NULL, S_LAVASPLASH2, 0, 0}, // S_LAVASPLASH1 {SPR_LVAS, 32769, 5, NULL, S_LAVASPLASH3, 0, 0}, // S_LAVASPLASH2 {SPR_LVAS, 32770, 5, NULL, S_LAVASPLASH4, 0, 0}, // S_LAVASPLASH3 {SPR_LVAS, 32771, 5, NULL, S_LAVASPLASH5, 0, 0}, // S_LAVASPLASH4 {SPR_LVAS, 32772, 5, NULL, S_LAVASPLASH6, 0, 0}, // S_LAVASPLASH5 {SPR_LVAS, 32773, 5, NULL, S_NULL, 0, 0}, // S_LAVASPLASH6 {SPR_LVAS, 32774, 5, NULL, S_LAVASMOKE2, 0, 0}, // S_LAVASMOKE1 {SPR_LVAS, 32775, 5, NULL, S_LAVASMOKE3, 0, 0}, // S_LAVASMOKE2 {SPR_LVAS, 32776, 5, NULL, S_LAVASMOKE4, 0, 0}, // S_LAVASMOKE3 {SPR_LVAS, 32777, 5, NULL, S_LAVASMOKE5, 0, 0}, // S_LAVASMOKE4 {SPR_LVAS, 32778, 5, NULL, S_NULL, 0, 0}, // S_LAVASMOKE5 {SPR_SLDG, 0, 8, NULL, S_SLUDGECHUNK2, 0, 0}, // S_SLUDGECHUNK1 {SPR_SLDG, 1, 8, NULL, S_SLUDGECHUNK3, 0, 0}, // S_SLUDGECHUNK2 {SPR_SLDG, 2, 8, NULL, S_SLUDGECHUNK4, 0, 0}, // S_SLUDGECHUNK3 {SPR_SLDG, 3, 8, NULL, S_NULL, 0, 0}, // S_SLUDGECHUNK4 {SPR_SLDG, 3, 6, NULL, S_NULL, 0, 0}, // S_SLUDGECHUNKX {SPR_SLDG, 4, 5, NULL, S_SLUDGESPLASH2, 0, 0}, // S_SLUDGESPLASH1 {SPR_SLDG, 5, 5, NULL, S_SLUDGESPLASH3, 0, 0}, // S_SLUDGESPLASH2 {SPR_SLDG, 6, 5, NULL, S_SLUDGESPLASH4, 0, 0}, // S_SLUDGESPLASH3 {SPR_SLDG, 7, 5, NULL, S_NULL, 0, 0}, // S_SLUDGESPLASH4 {SPR_SKH1, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG70_1 {SPR_SKH2, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG60_1 {SPR_SKH3, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG45_1 {SPR_SKH4, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG35_1 {SPR_CHDL, 0, 4, NULL, S_CHANDELIER2, 0, 0}, // S_CHANDELIER1 {SPR_CHDL, 1, 4, NULL, S_CHANDELIER3, 0, 0}, // S_CHANDELIER2 {SPR_CHDL, 2, 4, NULL, S_CHANDELIER1, 0, 0}, // S_CHANDELIER3 {SPR_SRTC, 0, 4, NULL, S_SERPTORCH2, 0, 0}, // S_SERPTORCH1 {SPR_SRTC, 1, 4, NULL, S_SERPTORCH3, 0, 0}, // S_SERPTORCH2 {SPR_SRTC, 2, 4, NULL, S_SERPTORCH1, 0, 0}, // S_SERPTORCH3 {SPR_SMPL, 0, -1, NULL, S_NULL, 0, 0}, // S_SMALLPILLAR {SPR_STGS, 0, -1, NULL, S_NULL, 0, 0}, // S_STALAGMITESMALL {SPR_STGL, 0, -1, NULL, S_NULL, 0, 0}, // S_STALAGMITELARGE {SPR_STCS, 0, -1, NULL, S_NULL, 0, 0}, // S_STALACTITESMALL {SPR_STCL, 0, -1, NULL, S_NULL, 0, 0}, // S_STALACTITELARGE {SPR_KFR1, 32768, 3, NULL, S_FIREBRAZIER2, 0, 0}, // S_FIREBRAZIER1 {SPR_KFR1, 32769, 3, NULL, S_FIREBRAZIER3, 0, 0}, // S_FIREBRAZIER2 {SPR_KFR1, 32770, 3, NULL, S_FIREBRAZIER4, 0, 0}, // S_FIREBRAZIER3 {SPR_KFR1, 32771, 3, NULL, S_FIREBRAZIER5, 0, 0}, // S_FIREBRAZIER4 {SPR_KFR1, 32772, 3, NULL, S_FIREBRAZIER6, 0, 0}, // S_FIREBRAZIER5 {SPR_KFR1, 32773, 3, NULL, S_FIREBRAZIER7, 0, 0}, // S_FIREBRAZIER6 {SPR_KFR1, 32774, 3, NULL, S_FIREBRAZIER8, 0, 0}, // S_FIREBRAZIER7 {SPR_KFR1, 32775, 3, NULL, S_FIREBRAZIER1, 0, 0}, // S_FIREBRAZIER8 {SPR_BARL, 0, -1, NULL, S_NULL, 0, 0}, // S_BARREL {SPR_BRPL, 0, -1, NULL, S_NULL, 0, 0}, // S_BRPILLAR {SPR_MOS1, 0, -1, NULL, S_NULL, 0, 0}, // S_MOSS1 {SPR_MOS2, 0, -1, NULL, S_NULL, 0, 0}, // S_MOSS2 {SPR_WTRH, 32768, 6, NULL, S_WALLTORCH2, 0, 0}, // S_WALLTORCH1 {SPR_WTRH, 32769, 6, NULL, S_WALLTORCH3, 0, 0}, // S_WALLTORCH2 {SPR_WTRH, 32770, 6, NULL, S_WALLTORCH1, 0, 0}, // S_WALLTORCH3 {SPR_HCOR, 0, -1, NULL, S_NULL, 0, 0}, // S_HANGINGCORPSE {SPR_KGZ1, 0, 1, NULL, S_KEYGIZMO2, 0, 0}, // S_KEYGIZMO1 {SPR_KGZ1, 0, 1, A_InitKeyGizmo, S_KEYGIZMO3, 0, 0}, // S_KEYGIZMO2 {SPR_KGZ1, 0, -1, NULL, S_NULL, 0, 0}, // S_KEYGIZMO3 {SPR_KGZB, 0, 1, NULL, S_KGZ_START, 0, 0}, // S_KGZ_START {SPR_KGZB, 32768, -1, NULL, S_NULL, 0, 0}, // S_KGZ_BLUEFLOAT1 {SPR_KGZG, 32768, -1, NULL, S_NULL, 0, 0}, // S_KGZ_GREENFLOAT1 {SPR_KGZY, 32768, -1, NULL, S_NULL, 0, 0}, // S_KGZ_YELLOWFLOAT1 {SPR_VLCO, 0, 350, NULL, S_VOLCANO2, 0, 0}, // S_VOLCANO1 {SPR_VLCO, 0, 35, A_VolcanoSet, S_VOLCANO3, 0, 0}, // S_VOLCANO2 {SPR_VLCO, 1, 3, NULL, S_VOLCANO4, 0, 0}, // S_VOLCANO3 {SPR_VLCO, 2, 3, NULL, S_VOLCANO5, 0, 0}, // S_VOLCANO4 {SPR_VLCO, 3, 3, NULL, S_VOLCANO6, 0, 0}, // S_VOLCANO5 {SPR_VLCO, 1, 3, NULL, S_VOLCANO7, 0, 0}, // S_VOLCANO6 {SPR_VLCO, 2, 3, NULL, S_VOLCANO8, 0, 0}, // S_VOLCANO7 {SPR_VLCO, 3, 3, NULL, S_VOLCANO9, 0, 0}, // S_VOLCANO8 {SPR_VLCO, 4, 10, A_VolcanoBlast, S_VOLCANO2, 0, 0}, // S_VOLCANO9 {SPR_VFBL, 0, 4, A_BeastPuff, S_VOLCANOBALL2, 0, 0}, // S_VOLCANOBALL1 {SPR_VFBL, 1, 4, A_BeastPuff, S_VOLCANOBALL1, 0, 0}, // S_VOLCANOBALL2 {SPR_XPL1, 0, 4, A_VolcBallImpact, S_VOLCANOBALLX2, 0, 0}, // S_VOLCANOBALLX1 {SPR_XPL1, 1, 4, NULL, S_VOLCANOBALLX3, 0, 0}, // S_VOLCANOBALLX2 {SPR_XPL1, 2, 4, NULL, S_VOLCANOBALLX4, 0, 0}, // S_VOLCANOBALLX3 {SPR_XPL1, 3, 4, NULL, S_VOLCANOBALLX5, 0, 0}, // S_VOLCANOBALLX4 {SPR_XPL1, 4, 4, NULL, S_VOLCANOBALLX6, 0, 0}, // S_VOLCANOBALLX5 {SPR_XPL1, 5, 4, NULL, S_NULL, 0, 0}, // S_VOLCANOBALLX6 {SPR_VTFB, 0, 4, NULL, S_VOLCANOTBALL2, 0, 0}, // S_VOLCANOTBALL1 {SPR_VTFB, 1, 4, NULL, S_VOLCANOTBALL1, 0, 0}, // S_VOLCANOTBALL2 {SPR_SFFI, 2, 4, NULL, S_VOLCANOTBALLX2, 0, 0}, // S_VOLCANOTBALLX1 {SPR_SFFI, 1, 4, NULL, S_VOLCANOTBALLX3, 0, 0}, // S_VOLCANOTBALLX2 {SPR_SFFI, 0, 4, NULL, S_VOLCANOTBALLX4, 0, 0}, // S_VOLCANOTBALLX3 {SPR_SFFI, 1, 4, NULL, S_VOLCANOTBALLX5, 0, 0}, // S_VOLCANOTBALLX4 {SPR_SFFI, 2, 4, NULL, S_VOLCANOTBALLX6, 0, 0}, // S_VOLCANOTBALLX5 {SPR_SFFI, 3, 4, NULL, S_VOLCANOTBALLX7, 0, 0}, // S_VOLCANOTBALLX6 {SPR_SFFI, 4, 4, NULL, S_NULL, 0, 0}, // S_VOLCANOTBALLX7 {SPR_TGLT, 0, 8, A_SpawnTeleGlitter, S_TELEGLITGEN1, 0, 0}, // S_TELEGLITGEN1 {SPR_TGLT, 5, 8, A_SpawnTeleGlitter2, S_TELEGLITGEN2, 0, 0}, // S_TELEGLITGEN2 {SPR_TGLT, 32768, 2, NULL, S_TELEGLITTER1_2, 0, 0}, // S_TELEGLITTER1_1 {SPR_TGLT, 32769, 2, A_AccTeleGlitter, S_TELEGLITTER1_3, 0, 0}, // S_TELEGLITTER1_2 {SPR_TGLT, 32770, 2, NULL, S_TELEGLITTER1_4, 0, 0}, // S_TELEGLITTER1_3 {SPR_TGLT, 32771, 2, A_AccTeleGlitter, S_TELEGLITTER1_5, 0, 0}, // S_TELEGLITTER1_4 {SPR_TGLT, 32772, 2, NULL, S_TELEGLITTER1_1, 0, 0}, // S_TELEGLITTER1_5 {SPR_TGLT, 32773, 2, NULL, S_TELEGLITTER2_2, 0, 0}, // S_TELEGLITTER2_1 {SPR_TGLT, 32774, 2, A_AccTeleGlitter, S_TELEGLITTER2_3, 0, 0}, // S_TELEGLITTER2_2 {SPR_TGLT, 32775, 2, NULL, S_TELEGLITTER2_4, 0, 0}, // S_TELEGLITTER2_3 {SPR_TGLT, 32776, 2, A_AccTeleGlitter, S_TELEGLITTER2_5, 0, 0}, // S_TELEGLITTER2_4 {SPR_TGLT, 32777, 2, NULL, S_TELEGLITTER2_1, 0, 0}, // S_TELEGLITTER2_5 {SPR_TELE, 32768, 6, NULL, S_TFOG2, 0, 0}, // S_TFOG1 {SPR_TELE, 32769, 6, NULL, S_TFOG3, 0, 0}, // S_TFOG2 {SPR_TELE, 32770, 6, NULL, S_TFOG4, 0, 0}, // S_TFOG3 {SPR_TELE, 32771, 6, NULL, S_TFOG5, 0, 0}, // S_TFOG4 {SPR_TELE, 32772, 6, NULL, S_TFOG6, 0, 0}, // S_TFOG5 {SPR_TELE, 32773, 6, NULL, S_TFOG7, 0, 0}, // S_TFOG6 {SPR_TELE, 32774, 6, NULL, S_TFOG8, 0, 0}, // S_TFOG7 {SPR_TELE, 32775, 6, NULL, S_TFOG9, 0, 0}, // S_TFOG8 {SPR_TELE, 32774, 6, NULL, S_TFOG10, 0, 0}, // S_TFOG9 {SPR_TELE, 32773, 6, NULL, S_TFOG11, 0, 0}, // S_TFOG10 {SPR_TELE, 32772, 6, NULL, S_TFOG12, 0, 0}, // S_TFOG11 {SPR_TELE, 32771, 6, NULL, S_TFOG13, 0, 0}, // S_TFOG12 {SPR_TELE, 32770, 6, NULL, S_NULL, 0, 0}, // S_TFOG13 {SPR_STFF, 0, 0, A_Light0, S_NULL, 0, 0}, // S_LIGHTDONE {SPR_STFF, 0, 1, A_WeaponReady, S_STAFFREADY, 0, 0}, // S_STAFFREADY {SPR_STFF, 0, 1, A_Lower, S_STAFFDOWN, 0, 0}, // S_STAFFDOWN {SPR_STFF, 0, 1, A_Raise, S_STAFFUP, 0, 0}, // S_STAFFUP {SPR_STFF, 3, 4, A_WeaponReady, S_STAFFREADY2_2, 0, 0}, // S_STAFFREADY2_1 {SPR_STFF, 4, 4, A_WeaponReady, S_STAFFREADY2_3, 0, 0}, // S_STAFFREADY2_2 {SPR_STFF, 5, 4, A_WeaponReady, S_STAFFREADY2_1, 0, 0}, // S_STAFFREADY2_3 {SPR_STFF, 3, 1, A_Lower, S_STAFFDOWN2, 0, 0}, // S_STAFFDOWN2 {SPR_STFF, 3, 1, A_Raise, S_STAFFUP2, 0, 0}, // S_STAFFUP2 {SPR_STFF, 1, 6, NULL, S_STAFFATK1_2, 0, 0}, // S_STAFFATK1_1 {SPR_STFF, 2, 8, A_StaffAttackPL1, S_STAFFATK1_3, 0, 0}, // S_STAFFATK1_2 {SPR_STFF, 1, 8, A_ReFire, S_STAFFREADY, 0, 0}, // S_STAFFATK1_3 {SPR_STFF, 6, 6, NULL, S_STAFFATK2_2, 0, 0}, // S_STAFFATK2_1 {SPR_STFF, 7, 8, A_StaffAttackPL2, S_STAFFATK2_3, 0, 0}, // S_STAFFATK2_2 {SPR_STFF, 6, 8, A_ReFire, S_STAFFREADY2_1, 0, 0}, // S_STAFFATK2_3 {SPR_PUF3, 32768, 4, NULL, S_STAFFPUFF2, 0, 0}, // S_STAFFPUFF1 {SPR_PUF3, 1, 4, NULL, S_STAFFPUFF3, 0, 0}, // S_STAFFPUFF2 {SPR_PUF3, 2, 4, NULL, S_STAFFPUFF4, 0, 0}, // S_STAFFPUFF3 {SPR_PUF3, 3, 4, NULL, S_NULL, 0, 0}, // S_STAFFPUFF4 {SPR_PUF4, 32768, 4, NULL, S_STAFFPUFF2_2, 0, 0}, // S_STAFFPUFF2_1 {SPR_PUF4, 32769, 4, NULL, S_STAFFPUFF2_3, 0, 0}, // S_STAFFPUFF2_2 {SPR_PUF4, 32770, 4, NULL, S_STAFFPUFF2_4, 0, 0}, // S_STAFFPUFF2_3 {SPR_PUF4, 32771, 4, NULL, S_STAFFPUFF2_5, 0, 0}, // S_STAFFPUFF2_4 {SPR_PUF4, 32772, 4, NULL, S_STAFFPUFF2_6, 0, 0}, // S_STAFFPUFF2_5 {SPR_PUF4, 32773, 4, NULL, S_NULL, 0, 0}, // S_STAFFPUFF2_6 {SPR_BEAK, 0, 1, A_BeakReady, S_BEAKREADY, 0, 0}, // S_BEAKREADY {SPR_BEAK, 0, 1, A_Lower, S_BEAKDOWN, 0, 0}, // S_BEAKDOWN {SPR_BEAK, 0, 1, A_BeakRaise, S_BEAKUP, 0, 0}, // S_BEAKUP {SPR_BEAK, 0, 18, A_BeakAttackPL1, S_BEAKREADY, 0, 0}, // S_BEAKATK1_1 {SPR_BEAK, 0, 12, A_BeakAttackPL2, S_BEAKREADY, 0, 0}, // S_BEAKATK2_1 {SPR_WGNT, 0, -1, NULL, S_NULL, 0, 0}, // S_WGNT {SPR_GAUN, 0, 1, A_WeaponReady, S_GAUNTLETREADY, 0, 0}, // S_GAUNTLETREADY {SPR_GAUN, 0, 1, A_Lower, S_GAUNTLETDOWN, 0, 0}, // S_GAUNTLETDOWN {SPR_GAUN, 0, 1, A_Raise, S_GAUNTLETUP, 0, 0}, // S_GAUNTLETUP {SPR_GAUN, 6, 4, A_WeaponReady, S_GAUNTLETREADY2_2, 0, 0}, // S_GAUNTLETREADY2_1 {SPR_GAUN, 7, 4, A_WeaponReady, S_GAUNTLETREADY2_3, 0, 0}, // S_GAUNTLETREADY2_2 {SPR_GAUN, 8, 4, A_WeaponReady, S_GAUNTLETREADY2_1, 0, 0}, // S_GAUNTLETREADY2_3 {SPR_GAUN, 6, 1, A_Lower, S_GAUNTLETDOWN2, 0, 0}, // S_GAUNTLETDOWN2 {SPR_GAUN, 6, 1, A_Raise, S_GAUNTLETUP2, 0, 0}, // S_GAUNTLETUP2 {SPR_GAUN, 1, 4, NULL, S_GAUNTLETATK1_2, 0, 0}, // S_GAUNTLETATK1_1 {SPR_GAUN, 2, 4, NULL, S_GAUNTLETATK1_3, 0, 0}, // S_GAUNTLETATK1_2 {SPR_GAUN, 32771, 4, A_GauntletAttack, S_GAUNTLETATK1_4, 0, 0}, // S_GAUNTLETATK1_3 {SPR_GAUN, 32772, 4, A_GauntletAttack, S_GAUNTLETATK1_5, 0, 0}, // S_GAUNTLETATK1_4 {SPR_GAUN, 32773, 4, A_GauntletAttack, S_GAUNTLETATK1_6, 0, 0}, // S_GAUNTLETATK1_5 {SPR_GAUN, 2, 4, A_ReFire, S_GAUNTLETATK1_7, 0, 0}, // S_GAUNTLETATK1_6 {SPR_GAUN, 1, 4, A_Light0, S_GAUNTLETREADY, 0, 0}, // S_GAUNTLETATK1_7 {SPR_GAUN, 9, 4, NULL, S_GAUNTLETATK2_2, 0, 0}, // S_GAUNTLETATK2_1 {SPR_GAUN, 10, 4, NULL, S_GAUNTLETATK2_3, 0, 0}, // S_GAUNTLETATK2_2 {SPR_GAUN, 32779, 4, A_GauntletAttack, S_GAUNTLETATK2_4, 0, 0}, // S_GAUNTLETATK2_3 {SPR_GAUN, 32780, 4, A_GauntletAttack, S_GAUNTLETATK2_5, 0, 0}, // S_GAUNTLETATK2_4 {SPR_GAUN, 32781, 4, A_GauntletAttack, S_GAUNTLETATK2_6, 0, 0}, // S_GAUNTLETATK2_5 {SPR_GAUN, 10, 4, A_ReFire, S_GAUNTLETATK2_7, 0, 0}, // S_GAUNTLETATK2_6 {SPR_GAUN, 9, 4, A_Light0, S_GAUNTLETREADY2_1, 0, 0}, // S_GAUNTLETATK2_7 {SPR_PUF1, 32768, 4, NULL, S_GAUNTLETPUFF1_2, 0, 0}, // S_GAUNTLETPUFF1_1 {SPR_PUF1, 32769, 4, NULL, S_GAUNTLETPUFF1_3, 0, 0}, // S_GAUNTLETPUFF1_2 {SPR_PUF1, 32770, 4, NULL, S_GAUNTLETPUFF1_4, 0, 0}, // S_GAUNTLETPUFF1_3 {SPR_PUF1, 32771, 4, NULL, S_NULL, 0, 0}, // S_GAUNTLETPUFF1_4 {SPR_PUF1, 32772, 4, NULL, S_GAUNTLETPUFF2_2, 0, 0}, // S_GAUNTLETPUFF2_1 {SPR_PUF1, 32773, 4, NULL, S_GAUNTLETPUFF2_3, 0, 0}, // S_GAUNTLETPUFF2_2 {SPR_PUF1, 32774, 4, NULL, S_GAUNTLETPUFF2_4, 0, 0}, // S_GAUNTLETPUFF2_3 {SPR_PUF1, 32775, 4, NULL, S_NULL, 0, 0}, // S_GAUNTLETPUFF2_4 {SPR_WBLS, 0, -1, NULL, S_NULL, 0, 0}, // S_BLSR {SPR_BLSR, 0, 1, A_WeaponReady, S_BLASTERREADY, 0, 0}, // S_BLASTERREADY {SPR_BLSR, 0, 1, A_Lower, S_BLASTERDOWN, 0, 0}, // S_BLASTERDOWN {SPR_BLSR, 0, 1, A_Raise, S_BLASTERUP, 0, 0}, // S_BLASTERUP {SPR_BLSR, 1, 3, NULL, S_BLASTERATK1_2, 0, 0}, // S_BLASTERATK1_1 {SPR_BLSR, 2, 3, NULL, S_BLASTERATK1_3, 0, 0}, // S_BLASTERATK1_2 {SPR_BLSR, 3, 2, A_FireBlasterPL1, S_BLASTERATK1_4, 0, 0}, // S_BLASTERATK1_3 {SPR_BLSR, 2, 2, NULL, S_BLASTERATK1_5, 0, 0}, // S_BLASTERATK1_4 {SPR_BLSR, 1, 2, NULL, S_BLASTERATK1_6, 0, 0}, // S_BLASTERATK1_5 {SPR_BLSR, 0, 0, A_ReFire, S_BLASTERREADY, 0, 0}, // S_BLASTERATK1_6 {SPR_BLSR, 1, 0, NULL, S_BLASTERATK2_2, 0, 0}, // S_BLASTERATK2_1 {SPR_BLSR, 2, 0, NULL, S_BLASTERATK2_3, 0, 0}, // S_BLASTERATK2_2 {SPR_BLSR, 3, 3, A_FireBlasterPL2, S_BLASTERATK2_4, 0, 0}, // S_BLASTERATK2_3 {SPR_BLSR, 2, 4, NULL, S_BLASTERATK2_5, 0, 0}, // S_BLASTERATK2_4 {SPR_BLSR, 1, 4, NULL, S_BLASTERATK2_6, 0, 0}, // S_BLASTERATK2_5 {SPR_BLSR, 0, 0, A_ReFire, S_BLASTERREADY, 0, 0}, // S_BLASTERATK2_6 {SPR_ACLO, 4, 200, NULL, S_BLASTERFX1_1, 0, 0}, // S_BLASTERFX1_1 {SPR_FX18, 32768, 3, A_SpawnRippers, S_BLASTERFXI1_2, 0, 0}, // S_BLASTERFXI1_1 {SPR_FX18, 32769, 3, NULL, S_BLASTERFXI1_3, 0, 0}, // S_BLASTERFXI1_2 {SPR_FX18, 32770, 4, NULL, S_BLASTERFXI1_4, 0, 0}, // S_BLASTERFXI1_3 {SPR_FX18, 32771, 4, NULL, S_BLASTERFXI1_5, 0, 0}, // S_BLASTERFXI1_4 {SPR_FX18, 32772, 4, NULL, S_BLASTERFXI1_6, 0, 0}, // S_BLASTERFXI1_5 {SPR_FX18, 32773, 4, NULL, S_BLASTERFXI1_7, 0, 0}, // S_BLASTERFXI1_6 {SPR_FX18, 32774, 4, NULL, S_NULL, 0, 0}, // S_BLASTERFXI1_7 {SPR_FX18, 7, 4, NULL, S_BLASTERSMOKE2, 0, 0}, // S_BLASTERSMOKE1 {SPR_FX18, 8, 4, NULL, S_BLASTERSMOKE3, 0, 0}, // S_BLASTERSMOKE2 {SPR_FX18, 9, 4, NULL, S_BLASTERSMOKE4, 0, 0}, // S_BLASTERSMOKE3 {SPR_FX18, 10, 4, NULL, S_BLASTERSMOKE5, 0, 0}, // S_BLASTERSMOKE4 {SPR_FX18, 11, 4, NULL, S_NULL, 0, 0}, // S_BLASTERSMOKE5 {SPR_FX18, 12, 4, NULL, S_RIPPER2, 0, 0}, // S_RIPPER1 {SPR_FX18, 13, 5, NULL, S_RIPPER1, 0, 0}, // S_RIPPER2 {SPR_FX18, 32782, 4, NULL, S_RIPPERX2, 0, 0}, // S_RIPPERX1 {SPR_FX18, 32783, 4, NULL, S_RIPPERX3, 0, 0}, // S_RIPPERX2 {SPR_FX18, 32784, 4, NULL, S_RIPPERX4, 0, 0}, // S_RIPPERX3 {SPR_FX18, 32785, 4, NULL, S_RIPPERX5, 0, 0}, // S_RIPPERX4 {SPR_FX18, 32786, 4, NULL, S_NULL, 0, 0}, // S_RIPPERX5 {SPR_FX17, 32768, 4, NULL, S_BLASTERPUFF1_2, 0, 0}, // S_BLASTERPUFF1_1 {SPR_FX17, 32769, 4, NULL, S_BLASTERPUFF1_3, 0, 0}, // S_BLASTERPUFF1_2 {SPR_FX17, 32770, 4, NULL, S_BLASTERPUFF1_4, 0, 0}, // S_BLASTERPUFF1_3 {SPR_FX17, 32771, 4, NULL, S_BLASTERPUFF1_5, 0, 0}, // S_BLASTERPUFF1_4 {SPR_FX17, 32772, 4, NULL, S_NULL, 0, 0}, // S_BLASTERPUFF1_5 {SPR_FX17, 32773, 3, NULL, S_BLASTERPUFF2_2, 0, 0}, // S_BLASTERPUFF2_1 {SPR_FX17, 32774, 3, NULL, S_BLASTERPUFF2_3, 0, 0}, // S_BLASTERPUFF2_2 {SPR_FX17, 32775, 4, NULL, S_BLASTERPUFF2_4, 0, 0}, // S_BLASTERPUFF2_3 {SPR_FX17, 32776, 4, NULL, S_BLASTERPUFF2_5, 0, 0}, // S_BLASTERPUFF2_4 {SPR_FX17, 32777, 4, NULL, S_BLASTERPUFF2_6, 0, 0}, // S_BLASTERPUFF2_5 {SPR_FX17, 32778, 4, NULL, S_BLASTERPUFF2_7, 0, 0}, // S_BLASTERPUFF2_6 {SPR_FX17, 32779, 4, NULL, S_NULL, 0, 0}, // S_BLASTERPUFF2_7 {SPR_WMCE, 0, -1, NULL, S_NULL, 0, 0}, // S_WMCE {SPR_MACE, 0, 1, A_WeaponReady, S_MACEREADY, 0, 0}, // S_MACEREADY {SPR_MACE, 0, 1, A_Lower, S_MACEDOWN, 0, 0}, // S_MACEDOWN {SPR_MACE, 0, 1, A_Raise, S_MACEUP, 0, 0}, // S_MACEUP {SPR_MACE, 1, 4, NULL, S_MACEATK1_2, 0, 0}, // S_MACEATK1_1 {SPR_MACE, 2, 3, A_FireMacePL1, S_MACEATK1_3, 0, 0}, // S_MACEATK1_2 {SPR_MACE, 3, 3, A_FireMacePL1, S_MACEATK1_4, 0, 0}, // S_MACEATK1_3 {SPR_MACE, 4, 3, A_FireMacePL1, S_MACEATK1_5, 0, 0}, // S_MACEATK1_4 {SPR_MACE, 5, 3, A_FireMacePL1, S_MACEATK1_6, 0, 0}, // S_MACEATK1_5 {SPR_MACE, 2, 4, A_ReFire, S_MACEATK1_7, 0, 0}, // S_MACEATK1_6 {SPR_MACE, 3, 4, NULL, S_MACEATK1_8, 0, 0}, // S_MACEATK1_7 {SPR_MACE, 4, 4, NULL, S_MACEATK1_9, 0, 0}, // S_MACEATK1_8 {SPR_MACE, 5, 4, NULL, S_MACEATK1_10, 0, 0}, // S_MACEATK1_9 {SPR_MACE, 1, 4, NULL, S_MACEREADY, 0, 0}, // S_MACEATK1_10 {SPR_MACE, 1, 4, NULL, S_MACEATK2_2, 0, 0}, // S_MACEATK2_1 {SPR_MACE, 3, 4, A_FireMacePL2, S_MACEATK2_3, 0, 0}, // S_MACEATK2_2 {SPR_MACE, 1, 4, NULL, S_MACEATK2_4, 0, 0}, // S_MACEATK2_3 {SPR_MACE, 0, 8, A_ReFire, S_MACEREADY, 0, 0}, // S_MACEATK2_4 {SPR_FX02, 0, 4, A_MacePL1Check, S_MACEFX1_2, 0, 0}, // S_MACEFX1_1 {SPR_FX02, 1, 4, A_MacePL1Check, S_MACEFX1_1, 0, 0}, // S_MACEFX1_2 {SPR_FX02, 32773, 4, A_MaceBallImpact, S_MACEFXI1_2, 0, 0}, // S_MACEFXI1_1 {SPR_FX02, 32774, 4, NULL, S_MACEFXI1_3, 0, 0}, // S_MACEFXI1_2 {SPR_FX02, 32775, 4, NULL, S_MACEFXI1_4, 0, 0}, // S_MACEFXI1_3 {SPR_FX02, 32776, 4, NULL, S_MACEFXI1_5, 0, 0}, // S_MACEFXI1_4 {SPR_FX02, 32777, 4, NULL, S_NULL, 0, 0}, // S_MACEFXI1_5 {SPR_FX02, 2, 4, NULL, S_MACEFX2_2, 0, 0}, // S_MACEFX2_1 {SPR_FX02, 3, 4, NULL, S_MACEFX2_1, 0, 0}, // S_MACEFX2_2 {SPR_FX02, 32773, 4, A_MaceBallImpact2, S_MACEFXI1_2, 0, 0}, // S_MACEFXI2_1 {SPR_FX02, 0, 4, NULL, S_MACEFX3_2, 0, 0}, // S_MACEFX3_1 {SPR_FX02, 1, 4, NULL, S_MACEFX3_1, 0, 0}, // S_MACEFX3_2 {SPR_FX02, 4, 99, NULL, S_MACEFX4_1, 0, 0}, // S_MACEFX4_1 {SPR_FX02, 32770, 4, A_DeathBallImpact, S_MACEFXI1_2, 0, 0}, // S_MACEFXI4_1 {SPR_WSKL, 0, -1, NULL, S_NULL, 0, 0}, // S_WSKL {SPR_HROD, 0, 1, A_WeaponReady, S_HORNRODREADY, 0, 0}, // S_HORNRODREADY {SPR_HROD, 0, 1, A_Lower, S_HORNRODDOWN, 0, 0}, // S_HORNRODDOWN {SPR_HROD, 0, 1, A_Raise, S_HORNRODUP, 0, 0}, // S_HORNRODUP {SPR_HROD, 0, 4, A_FireSkullRodPL1, S_HORNRODATK1_2, 0, 0}, // S_HORNRODATK1_1 {SPR_HROD, 1, 4, A_FireSkullRodPL1, S_HORNRODATK1_3, 0, 0}, // S_HORNRODATK1_2 {SPR_HROD, 1, 0, A_ReFire, S_HORNRODREADY, 0, 0}, // S_HORNRODATK1_3 {SPR_HROD, 2, 2, NULL, S_HORNRODATK2_2, 0, 0}, // S_HORNRODATK2_1 {SPR_HROD, 3, 3, NULL, S_HORNRODATK2_3, 0, 0}, // S_HORNRODATK2_2 {SPR_HROD, 4, 2, NULL, S_HORNRODATK2_4, 0, 0}, // S_HORNRODATK2_3 {SPR_HROD, 5, 3, NULL, S_HORNRODATK2_5, 0, 0}, // S_HORNRODATK2_4 {SPR_HROD, 6, 4, A_FireSkullRodPL2, S_HORNRODATK2_6, 0, 0}, // S_HORNRODATK2_5 {SPR_HROD, 5, 2, NULL, S_HORNRODATK2_7, 0, 0}, // S_HORNRODATK2_6 {SPR_HROD, 4, 3, NULL, S_HORNRODATK2_8, 0, 0}, // S_HORNRODATK2_7 {SPR_HROD, 3, 2, NULL, S_HORNRODATK2_9, 0, 0}, // S_HORNRODATK2_8 {SPR_HROD, 2, 2, A_ReFire, S_HORNRODREADY, 0, 0}, // S_HORNRODATK2_9 {SPR_FX00, 32768, 6, NULL, S_HRODFX1_2, 0, 0}, // S_HRODFX1_1 {SPR_FX00, 32769, 6, NULL, S_HRODFX1_1, 0, 0}, // S_HRODFX1_2 {SPR_FX00, 32775, 5, NULL, S_HRODFXI1_2, 0, 0}, // S_HRODFXI1_1 {SPR_FX00, 32776, 5, NULL, S_HRODFXI1_3, 0, 0}, // S_HRODFXI1_2 {SPR_FX00, 32777, 4, NULL, S_HRODFXI1_4, 0, 0}, // S_HRODFXI1_3 {SPR_FX00, 32778, 4, NULL, S_HRODFXI1_5, 0, 0}, // S_HRODFXI1_4 {SPR_FX00, 32779, 3, NULL, S_HRODFXI1_6, 0, 0}, // S_HRODFXI1_5 {SPR_FX00, 32780, 3, NULL, S_NULL, 0, 0}, // S_HRODFXI1_6 {SPR_FX00, 32770, 3, NULL, S_HRODFX2_2, 0, 0}, // S_HRODFX2_1 {SPR_FX00, 32771, 3, A_SkullRodPL2Seek, S_HRODFX2_3, 0, 0}, // S_HRODFX2_2 {SPR_FX00, 32772, 3, NULL, S_HRODFX2_4, 0, 0}, // S_HRODFX2_3 {SPR_FX00, 32773, 3, A_SkullRodPL2Seek, S_HRODFX2_1, 0, 0}, // S_HRODFX2_4 {SPR_FX00, 32775, 5, A_AddPlayerRain, S_HRODFXI2_2, 0, 0}, // S_HRODFXI2_1 {SPR_FX00, 32776, 5, NULL, S_HRODFXI2_3, 0, 0}, // S_HRODFXI2_2 {SPR_FX00, 32777, 4, NULL, S_HRODFXI2_4, 0, 0}, // S_HRODFXI2_3 {SPR_FX00, 32778, 3, NULL, S_HRODFXI2_5, 0, 0}, // S_HRODFXI2_4 {SPR_FX00, 32779, 3, NULL, S_HRODFXI2_6, 0, 0}, // S_HRODFXI2_5 {SPR_FX00, 32780, 3, NULL, S_HRODFXI2_7, 0, 0}, // S_HRODFXI2_6 {SPR_FX00, 6, 1, A_HideInCeiling, S_HRODFXI2_8, 0, 0}, // S_HRODFXI2_7 {SPR_FX00, 6, 1, A_SkullRodStorm, S_HRODFXI2_8, 0, 0}, // S_HRODFXI2_8 {SPR_FX20, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR1_1 {SPR_FX21, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR2_1 {SPR_FX22, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR3_1 {SPR_FX23, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR4_1 {SPR_FX20, 32769, 4, A_RainImpact, S_RAINPLR1X_2, 0, 0}, // S_RAINPLR1X_1 {SPR_FX20, 32770, 4, NULL, S_RAINPLR1X_3, 0, 0}, // S_RAINPLR1X_2 {SPR_FX20, 32771, 4, NULL, S_RAINPLR1X_4, 0, 0}, // S_RAINPLR1X_3 {SPR_FX20, 32772, 4, NULL, S_RAINPLR1X_5, 0, 0}, // S_RAINPLR1X_4 {SPR_FX20, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR1X_5 {SPR_FX21, 32769, 4, A_RainImpact, S_RAINPLR2X_2, 0, 0}, // S_RAINPLR2X_1 {SPR_FX21, 32770, 4, NULL, S_RAINPLR2X_3, 0, 0}, // S_RAINPLR2X_2 {SPR_FX21, 32771, 4, NULL, S_RAINPLR2X_4, 0, 0}, // S_RAINPLR2X_3 {SPR_FX21, 32772, 4, NULL, S_RAINPLR2X_5, 0, 0}, // S_RAINPLR2X_4 {SPR_FX21, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR2X_5 {SPR_FX22, 32769, 4, A_RainImpact, S_RAINPLR3X_2, 0, 0}, // S_RAINPLR3X_1 {SPR_FX22, 32770, 4, NULL, S_RAINPLR3X_3, 0, 0}, // S_RAINPLR3X_2 {SPR_FX22, 32771, 4, NULL, S_RAINPLR3X_4, 0, 0}, // S_RAINPLR3X_3 {SPR_FX22, 32772, 4, NULL, S_RAINPLR3X_5, 0, 0}, // S_RAINPLR3X_4 {SPR_FX22, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR3X_5 {SPR_FX23, 32769, 4, A_RainImpact, S_RAINPLR4X_2, 0, 0}, // S_RAINPLR4X_1 {SPR_FX23, 32770, 4, NULL, S_RAINPLR4X_3, 0, 0}, // S_RAINPLR4X_2 {SPR_FX23, 32771, 4, NULL, S_RAINPLR4X_4, 0, 0}, // S_RAINPLR4X_3 {SPR_FX23, 32772, 4, NULL, S_RAINPLR4X_5, 0, 0}, // S_RAINPLR4X_4 {SPR_FX23, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR4X_5 {SPR_FX20, 32774, 4, NULL, S_RAINAIRXPLR1_2, 0, 0}, // S_RAINAIRXPLR1_1 {SPR_FX21, 32774, 4, NULL, S_RAINAIRXPLR2_2, 0, 0}, // S_RAINAIRXPLR2_1 {SPR_FX22, 32774, 4, NULL, S_RAINAIRXPLR3_2, 0, 0}, // S_RAINAIRXPLR3_1 {SPR_FX23, 32774, 4, NULL, S_RAINAIRXPLR4_2, 0, 0}, // S_RAINAIRXPLR4_1 {SPR_FX20, 32775, 4, NULL, S_RAINAIRXPLR1_3, 0, 0}, // S_RAINAIRXPLR1_2 {SPR_FX21, 32775, 4, NULL, S_RAINAIRXPLR2_3, 0, 0}, // S_RAINAIRXPLR2_2 {SPR_FX22, 32775, 4, NULL, S_RAINAIRXPLR3_3, 0, 0}, // S_RAINAIRXPLR3_2 {SPR_FX23, 32775, 4, NULL, S_RAINAIRXPLR4_3, 0, 0}, // S_RAINAIRXPLR4_2 {SPR_FX20, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR1_3 {SPR_FX21, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR2_3 {SPR_FX22, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR3_3 {SPR_FX23, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR4_3 {SPR_GWND, 0, 1, A_WeaponReady, S_GOLDWANDREADY, 0, 0}, // S_GOLDWANDREADY {SPR_GWND, 0, 1, A_Lower, S_GOLDWANDDOWN, 0, 0}, // S_GOLDWANDDOWN {SPR_GWND, 0, 1, A_Raise, S_GOLDWANDUP, 0, 0}, // S_GOLDWANDUP {SPR_GWND, 1, 3, NULL, S_GOLDWANDATK1_2, 0, 0}, // S_GOLDWANDATK1_1 {SPR_GWND, 2, 5, A_FireGoldWandPL1, S_GOLDWANDATK1_3, 0, 0}, // S_GOLDWANDATK1_2 {SPR_GWND, 3, 3, NULL, S_GOLDWANDATK1_4, 0, 0}, // S_GOLDWANDATK1_3 {SPR_GWND, 3, 0, A_ReFire, S_GOLDWANDREADY, 0, 0}, // S_GOLDWANDATK1_4 {SPR_GWND, 1, 3, NULL, S_GOLDWANDATK2_2, 0, 0}, // S_GOLDWANDATK2_1 {SPR_GWND, 2, 4, A_FireGoldWandPL2, S_GOLDWANDATK2_3, 0, 0}, // S_GOLDWANDATK2_2 {SPR_GWND, 3, 3, NULL, S_GOLDWANDATK2_4, 0, 0}, // S_GOLDWANDATK2_3 {SPR_GWND, 3, 0, A_ReFire, S_GOLDWANDREADY, 0, 0}, // S_GOLDWANDATK2_4 {SPR_FX01, 32768, 6, NULL, S_GWANDFX1_2, 0, 0}, // S_GWANDFX1_1 {SPR_FX01, 32769, 6, NULL, S_GWANDFX1_1, 0, 0}, // S_GWANDFX1_2 {SPR_FX01, 32772, 3, NULL, S_GWANDFXI1_2, 0, 0}, // S_GWANDFXI1_1 {SPR_FX01, 32773, 3, NULL, S_GWANDFXI1_3, 0, 0}, // S_GWANDFXI1_2 {SPR_FX01, 32774, 3, NULL, S_GWANDFXI1_4, 0, 0}, // S_GWANDFXI1_3 {SPR_FX01, 32775, 3, NULL, S_NULL, 0, 0}, // S_GWANDFXI1_4 {SPR_FX01, 32770, 6, NULL, S_GWANDFX2_2, 0, 0}, // S_GWANDFX2_1 {SPR_FX01, 32771, 6, NULL, S_GWANDFX2_1, 0, 0}, // S_GWANDFX2_2 {SPR_PUF2, 32768, 3, NULL, S_GWANDPUFF1_2, 0, 0}, // S_GWANDPUFF1_1 {SPR_PUF2, 32769, 3, NULL, S_GWANDPUFF1_3, 0, 0}, // S_GWANDPUFF1_2 {SPR_PUF2, 32770, 3, NULL, S_GWANDPUFF1_4, 0, 0}, // S_GWANDPUFF1_3 {SPR_PUF2, 32771, 3, NULL, S_GWANDPUFF1_5, 0, 0}, // S_GWANDPUFF1_4 {SPR_PUF2, 32772, 3, NULL, S_NULL, 0, 0}, // S_GWANDPUFF1_5 {SPR_WPHX, 0, -1, NULL, S_NULL, 0, 0}, // S_WPHX {SPR_PHNX, 0, 1, A_WeaponReady, S_PHOENIXREADY, 0, 0}, // S_PHOENIXREADY {SPR_PHNX, 0, 1, A_Lower, S_PHOENIXDOWN, 0, 0}, // S_PHOENIXDOWN {SPR_PHNX, 0, 1, A_Raise, S_PHOENIXUP, 0, 0}, // S_PHOENIXUP {SPR_PHNX, 1, 5, NULL, S_PHOENIXATK1_2, 0, 0}, // S_PHOENIXATK1_1 {SPR_PHNX, 2, 7, A_FirePhoenixPL1, S_PHOENIXATK1_3, 0, 0}, // S_PHOENIXATK1_2 {SPR_PHNX, 3, 4, NULL, S_PHOENIXATK1_4, 0, 0}, // S_PHOENIXATK1_3 {SPR_PHNX, 1, 4, NULL, S_PHOENIXATK1_5, 0, 0}, // S_PHOENIXATK1_4 {SPR_PHNX, 1, 0, A_ReFire, S_PHOENIXREADY, 0, 0}, // S_PHOENIXATK1_5 {SPR_PHNX, 1, 3, A_InitPhoenixPL2, S_PHOENIXATK2_2, 0, 0}, // S_PHOENIXATK2_1 {SPR_PHNX, 32770, 1, A_FirePhoenixPL2, S_PHOENIXATK2_3, 0, 0}, // S_PHOENIXATK2_2 {SPR_PHNX, 1, 4, A_ReFire, S_PHOENIXATK2_4, 0, 0}, // S_PHOENIXATK2_3 {SPR_PHNX, 1, 4, A_ShutdownPhoenixPL2, S_PHOENIXREADY, 0, 0}, // S_PHOENIXATK2_4 {SPR_FX04, 32768, 4, A_PhoenixPuff, S_PHOENIXFX1_1, 0, 0}, // S_PHOENIXFX1_1 {SPR_FX08, 32768, 6, A_Explode, S_PHOENIXFXI1_2, 0, 0}, // S_PHOENIXFXI1_1 {SPR_FX08, 32769, 5, NULL, S_PHOENIXFXI1_3, 0, 0}, // S_PHOENIXFXI1_2 {SPR_FX08, 32770, 5, NULL, S_PHOENIXFXI1_4, 0, 0}, // S_PHOENIXFXI1_3 {SPR_FX08, 32771, 4, NULL, S_PHOENIXFXI1_5, 0, 0}, // S_PHOENIXFXI1_4 {SPR_FX08, 32772, 4, NULL, S_PHOENIXFXI1_6, 0, 0}, // S_PHOENIXFXI1_5 {SPR_FX08, 32773, 4, NULL, S_PHOENIXFXI1_7, 0, 0}, // S_PHOENIXFXI1_6 {SPR_FX08, 32774, 4, NULL, S_PHOENIXFXI1_8, 0, 0}, // S_PHOENIXFXI1_7 {SPR_FX08, 32775, 4, NULL, S_NULL, 0, 0}, // S_PHOENIXFXI1_8 {SPR_FX08, 32776, 8, NULL, S_PHOENIXFXIX_1, 0, 0 }, // S_PHOENIXFXIX_1 {SPR_FX08, 32777, 8, A_RemovedPhoenixFunc, S_PHOENIXFXIX_2, 0, 0 }, // S_PHOENIXFXIX_2 {SPR_FX08, 32778, 8, NULL, S_NULL, 0, 0 }, // S_PHOENIXFXIX_3 {SPR_FX04, 1, 4, NULL, S_PHOENIXPUFF2, 0, 0}, // S_PHOENIXPUFF1 {SPR_FX04, 2, 4, NULL, S_PHOENIXPUFF3, 0, 0}, // S_PHOENIXPUFF2 {SPR_FX04, 3, 4, NULL, S_PHOENIXPUFF4, 0, 0}, // S_PHOENIXPUFF3 {SPR_FX04, 4, 4, NULL, S_PHOENIXPUFF5, 0, 0}, // S_PHOENIXPUFF4 {SPR_FX04, 5, 4, NULL, S_NULL, 0, 0}, // S_PHOENIXPUFF5 {SPR_FX09, 32768, 2, NULL, S_PHOENIXFX2_2, 0, 0}, // S_PHOENIXFX2_1 {SPR_FX09, 32769, 2, NULL, S_PHOENIXFX2_3, 0, 0}, // S_PHOENIXFX2_2 {SPR_FX09, 32768, 2, NULL, S_PHOENIXFX2_4, 0, 0}, // S_PHOENIXFX2_3 {SPR_FX09, 32769, 2, NULL, S_PHOENIXFX2_5, 0, 0}, // S_PHOENIXFX2_4 {SPR_FX09, 32768, 2, NULL, S_PHOENIXFX2_6, 0, 0}, // S_PHOENIXFX2_5 {SPR_FX09, 32769, 2, A_FlameEnd, S_PHOENIXFX2_7, 0, 0}, // S_PHOENIXFX2_6 {SPR_FX09, 32770, 2, NULL, S_PHOENIXFX2_8, 0, 0}, // S_PHOENIXFX2_7 {SPR_FX09, 32771, 2, NULL, S_PHOENIXFX2_9, 0, 0}, // S_PHOENIXFX2_8 {SPR_FX09, 32772, 2, NULL, S_PHOENIXFX2_10, 0, 0}, // S_PHOENIXFX2_9 {SPR_FX09, 32773, 2, NULL, S_NULL, 0, 0}, // S_PHOENIXFX2_10 {SPR_FX09, 32774, 3, NULL, S_PHOENIXFXI2_2, 0, 0}, // S_PHOENIXFXI2_1 {SPR_FX09, 32775, 3, A_FloatPuff, S_PHOENIXFXI2_3, 0, 0}, // S_PHOENIXFXI2_2 {SPR_FX09, 32776, 4, NULL, S_PHOENIXFXI2_4, 0, 0}, // S_PHOENIXFXI2_3 {SPR_FX09, 32777, 5, NULL, S_PHOENIXFXI2_5, 0, 0}, // S_PHOENIXFXI2_4 {SPR_FX09, 32778, 5, NULL, S_NULL, 0, 0}, // S_PHOENIXFXI2_5 {SPR_WBOW, 0, -1, NULL, S_NULL, 0, 0}, // S_WBOW {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW2, 0, 0}, // S_CRBOW1 {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW3, 0, 0}, // S_CRBOW2 {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW4, 0, 0}, // S_CRBOW3 {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW5, 0, 0}, // S_CRBOW4 {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW6, 0, 0}, // S_CRBOW5 {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW7, 0, 0}, // S_CRBOW6 {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW8, 0, 0}, // S_CRBOW7 {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW9, 0, 0}, // S_CRBOW8 {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW10, 0, 0}, // S_CRBOW9 {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW11, 0, 0}, // S_CRBOW10 {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW12, 0, 0}, // S_CRBOW11 {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW13, 0, 0}, // S_CRBOW12 {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW14, 0, 0}, // S_CRBOW13 {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW15, 0, 0}, // S_CRBOW14 {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW16, 0, 0}, // S_CRBOW15 {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW17, 0, 0}, // S_CRBOW16 {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW18, 0, 0}, // S_CRBOW17 {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW1, 0, 0}, // S_CRBOW18 {SPR_CRBW, 0, 1, A_Lower, S_CRBOWDOWN, 0, 0}, // S_CRBOWDOWN {SPR_CRBW, 0, 1, A_Raise, S_CRBOWUP, 0, 0}, // S_CRBOWUP {SPR_CRBW, 3, 6, A_FireCrossbowPL1, S_CRBOWATK1_2, 0, 0}, // S_CRBOWATK1_1 {SPR_CRBW, 4, 3, NULL, S_CRBOWATK1_3, 0, 0}, // S_CRBOWATK1_2 {SPR_CRBW, 5, 3, NULL, S_CRBOWATK1_4, 0, 0}, // S_CRBOWATK1_3 {SPR_CRBW, 6, 3, NULL, S_CRBOWATK1_5, 0, 0}, // S_CRBOWATK1_4 {SPR_CRBW, 7, 3, NULL, S_CRBOWATK1_6, 0, 0}, // S_CRBOWATK1_5 {SPR_CRBW, 0, 4, NULL, S_CRBOWATK1_7, 0, 0}, // S_CRBOWATK1_6 {SPR_CRBW, 1, 4, NULL, S_CRBOWATK1_8, 0, 0}, // S_CRBOWATK1_7 {SPR_CRBW, 2, 5, A_ReFire, S_CRBOW1, 0, 0}, // S_CRBOWATK1_8 {SPR_CRBW, 3, 5, A_FireCrossbowPL2, S_CRBOWATK2_2, 0, 0}, // S_CRBOWATK2_1 {SPR_CRBW, 4, 3, NULL, S_CRBOWATK2_3, 0, 0}, // S_CRBOWATK2_2 {SPR_CRBW, 5, 2, NULL, S_CRBOWATK2_4, 0, 0}, // S_CRBOWATK2_3 {SPR_CRBW, 6, 3, NULL, S_CRBOWATK2_5, 0, 0}, // S_CRBOWATK2_4 {SPR_CRBW, 7, 2, NULL, S_CRBOWATK2_6, 0, 0}, // S_CRBOWATK2_5 {SPR_CRBW, 0, 3, NULL, S_CRBOWATK2_7, 0, 0}, // S_CRBOWATK2_6 {SPR_CRBW, 1, 3, NULL, S_CRBOWATK2_8, 0, 0}, // S_CRBOWATK2_7 {SPR_CRBW, 2, 4, A_ReFire, S_CRBOW1, 0, 0}, // S_CRBOWATK2_8 {SPR_FX03, 32769, 1, NULL, S_CRBOWFX1, 0, 0}, // S_CRBOWFX1 {SPR_FX03, 32775, 8, NULL, S_CRBOWFXI1_2, 0, 0}, // S_CRBOWFXI1_1 {SPR_FX03, 32776, 8, NULL, S_CRBOWFXI1_3, 0, 0}, // S_CRBOWFXI1_2 {SPR_FX03, 32777, 8, NULL, S_NULL, 0, 0}, // S_CRBOWFXI1_3 {SPR_FX03, 32769, 1, A_BoltSpark, S_CRBOWFX2, 0, 0}, // S_CRBOWFX2 {SPR_FX03, 32768, 1, NULL, S_CRBOWFX3, 0, 0}, // S_CRBOWFX3 {SPR_FX03, 32770, 8, NULL, S_CRBOWFXI3_2, 0, 0}, // S_CRBOWFXI3_1 {SPR_FX03, 32771, 8, NULL, S_CRBOWFXI3_3, 0, 0}, // S_CRBOWFXI3_2 {SPR_FX03, 32772, 8, NULL, S_NULL, 0, 0}, // S_CRBOWFXI3_3 {SPR_FX03, 32773, 8, NULL, S_CRBOWFX4_2, 0, 0}, // S_CRBOWFX4_1 {SPR_FX03, 32774, 8, NULL, S_NULL, 0, 0}, // S_CRBOWFX4_2 {SPR_BLOD, 2, 8, NULL, S_BLOOD2, 0, 0}, // S_BLOOD1 {SPR_BLOD, 1, 8, NULL, S_BLOOD3, 0, 0}, // S_BLOOD2 {SPR_BLOD, 0, 8, NULL, S_NULL, 0, 0}, // S_BLOOD3 {SPR_BLOD, 2, 8, NULL, S_BLOODSPLATTER2, 0, 0}, // S_BLOODSPLATTER1 {SPR_BLOD, 1, 8, NULL, S_BLOODSPLATTER3, 0, 0}, // S_BLOODSPLATTER2 {SPR_BLOD, 0, 8, NULL, S_NULL, 0, 0}, // S_BLOODSPLATTER3 {SPR_BLOD, 0, 6, NULL, S_NULL, 0, 0}, // S_BLOODSPLATTERX {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, 6, NULL, S_PLAY_DIE2, 0, 0}, // S_PLAY_DIE1 {SPR_PLAY, 8, 6, A_Scream, S_PLAY_DIE3, 0, 0}, // S_PLAY_DIE2 {SPR_PLAY, 9, 6, NULL, S_PLAY_DIE4, 0, 0}, // S_PLAY_DIE3 {SPR_PLAY, 10, 6, NULL, S_PLAY_DIE5, 0, 0}, // S_PLAY_DIE4 {SPR_PLAY, 11, 6, A_NoBlocking, S_PLAY_DIE6, 0, 0}, // S_PLAY_DIE5 {SPR_PLAY, 12, 6, NULL, S_PLAY_DIE7, 0, 0}, // S_PLAY_DIE6 {SPR_PLAY, 13, 6, NULL, S_PLAY_DIE8, 0, 0}, // S_PLAY_DIE7 {SPR_PLAY, 14, 6, NULL, S_PLAY_DIE9, 0, 0}, // S_PLAY_DIE8 {SPR_PLAY, 15, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_PLAY_DIE9 {SPR_PLAY, 16, 5, A_Scream, S_PLAY_XDIE2, 0, 0}, // S_PLAY_XDIE1 {SPR_PLAY, 17, 5, A_SkullPop, S_PLAY_XDIE3, 0, 0}, // S_PLAY_XDIE2 {SPR_PLAY, 18, 5, A_NoBlocking, S_PLAY_XDIE4, 0, 0}, // S_PLAY_XDIE3 {SPR_PLAY, 19, 5, NULL, S_PLAY_XDIE5, 0, 0}, // S_PLAY_XDIE4 {SPR_PLAY, 20, 5, NULL, S_PLAY_XDIE6, 0, 0}, // S_PLAY_XDIE5 {SPR_PLAY, 21, 5, NULL, S_PLAY_XDIE7, 0, 0}, // S_PLAY_XDIE6 {SPR_PLAY, 22, 5, NULL, S_PLAY_XDIE8, 0, 0}, // S_PLAY_XDIE7 {SPR_PLAY, 23, 5, NULL, S_PLAY_XDIE9, 0, 0}, // S_PLAY_XDIE8 {SPR_PLAY, 24, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_PLAY_XDIE9 {SPR_FDTH, 32768, 5, A_FlameSnd, S_PLAY_FDTH2, 0, 0}, // S_PLAY_FDTH1 {SPR_FDTH, 32769, 4, NULL, S_PLAY_FDTH3, 0, 0}, // S_PLAY_FDTH2 {SPR_FDTH, 32770, 5, NULL, S_PLAY_FDTH4, 0, 0}, // S_PLAY_FDTH3 {SPR_FDTH, 32771, 4, A_Scream, S_PLAY_FDTH5, 0, 0}, // S_PLAY_FDTH4 {SPR_FDTH, 32772, 5, NULL, S_PLAY_FDTH6, 0, 0}, // S_PLAY_FDTH5 {SPR_FDTH, 32773, 4, NULL, S_PLAY_FDTH7, 0, 0}, // S_PLAY_FDTH6 {SPR_FDTH, 32774, 5, A_FlameSnd, S_PLAY_FDTH8, 0, 0}, // S_PLAY_FDTH7 {SPR_FDTH, 32775, 4, NULL, S_PLAY_FDTH9, 0, 0}, // S_PLAY_FDTH8 {SPR_FDTH, 32776, 5, NULL, S_PLAY_FDTH10, 0, 0}, // S_PLAY_FDTH9 {SPR_FDTH, 32777, 4, NULL, S_PLAY_FDTH11, 0, 0}, // S_PLAY_FDTH10 {SPR_FDTH, 32778, 5, NULL, S_PLAY_FDTH12, 0, 0}, // S_PLAY_FDTH11 {SPR_FDTH, 32779, 4, NULL, S_PLAY_FDTH13, 0, 0}, // S_PLAY_FDTH12 {SPR_FDTH, 32780, 5, NULL, S_PLAY_FDTH14, 0, 0}, // S_PLAY_FDTH13 {SPR_FDTH, 32781, 4, NULL, S_PLAY_FDTH15, 0, 0}, // S_PLAY_FDTH14 {SPR_FDTH, 32782, 5, A_NoBlocking, S_PLAY_FDTH16, 0, 0}, // S_PLAY_FDTH15 {SPR_FDTH, 32783, 4, NULL, S_PLAY_FDTH17, 0, 0}, // S_PLAY_FDTH16 {SPR_FDTH, 32784, 5, NULL, S_PLAY_FDTH18, 0, 0}, // S_PLAY_FDTH17 {SPR_FDTH, 32785, 4, NULL, S_PLAY_FDTH19, 0, 0}, // S_PLAY_FDTH18 {SPR_ACLO, 4, 35, A_CheckBurnGone, S_PLAY_FDTH19, 0, 0}, // S_PLAY_FDTH19 {SPR_ACLO, 4, 8, NULL, S_NULL, 0, 0}, // S_PLAY_FDTH20 {SPR_BSKL, 0, 5, A_CheckSkullFloor, S_BLOODYSKULL2, 0, 0}, // S_BLOODYSKULL1 {SPR_BSKL, 1, 5, A_CheckSkullFloor, S_BLOODYSKULL3, 0, 0}, // S_BLOODYSKULL2 {SPR_BSKL, 2, 5, A_CheckSkullFloor, S_BLOODYSKULL4, 0, 0}, // S_BLOODYSKULL3 {SPR_BSKL, 3, 5, A_CheckSkullFloor, S_BLOODYSKULL5, 0, 0}, // S_BLOODYSKULL4 {SPR_BSKL, 4, 5, A_CheckSkullFloor, S_BLOODYSKULL1, 0, 0}, // S_BLOODYSKULL5 {SPR_BSKL, 5, 16, A_CheckSkullDone, S_BLOODYSKULLX1, 0, 0}, // S_BLOODYSKULLX1 {SPR_BSKL, 5, 1050, NULL, S_NULL, 0, 0}, // S_BLOODYSKULLX2 {SPR_CHKN, 0, -1, NULL, S_NULL, 0, 0}, // S_CHICPLAY {SPR_CHKN, 0, 3, NULL, S_CHICPLAY_RUN2, 0, 0}, // S_CHICPLAY_RUN1 {SPR_CHKN, 1, 3, NULL, S_CHICPLAY_RUN3, 0, 0}, // S_CHICPLAY_RUN2 {SPR_CHKN, 0, 3, NULL, S_CHICPLAY_RUN4, 0, 0}, // S_CHICPLAY_RUN3 {SPR_CHKN, 1, 3, NULL, S_CHICPLAY_RUN1, 0, 0}, // S_CHICPLAY_RUN4 {SPR_CHKN, 2, 12, NULL, S_CHICPLAY, 0, 0}, // S_CHICPLAY_ATK1 {SPR_CHKN, 3, 4, A_Feathers, S_CHICPLAY_PAIN2, 0, 0}, // S_CHICPLAY_PAIN {SPR_CHKN, 2, 4, A_Pain, S_CHICPLAY, 0, 0}, // S_CHICPLAY_PAIN2 {SPR_CHKN, 0, 10, A_ChicLook, S_CHICKEN_LOOK2, 0, 0}, // S_CHICKEN_LOOK1 {SPR_CHKN, 1, 10, A_ChicLook, S_CHICKEN_LOOK1, 0, 0}, // S_CHICKEN_LOOK2 {SPR_CHKN, 0, 3, A_ChicChase, S_CHICKEN_WALK2, 0, 0}, // S_CHICKEN_WALK1 {SPR_CHKN, 1, 3, A_ChicChase, S_CHICKEN_WALK1, 0, 0}, // S_CHICKEN_WALK2 {SPR_CHKN, 3, 5, A_Feathers, S_CHICKEN_PAIN2, 0, 0}, // S_CHICKEN_PAIN1 {SPR_CHKN, 2, 5, A_ChicPain, S_CHICKEN_WALK1, 0, 0}, // S_CHICKEN_PAIN2 {SPR_CHKN, 0, 8, A_FaceTarget, S_CHICKEN_ATK2, 0, 0}, // S_CHICKEN_ATK1 {SPR_CHKN, 2, 10, A_ChicAttack, S_CHICKEN_WALK1, 0, 0}, // S_CHICKEN_ATK2 {SPR_CHKN, 4, 6, A_Scream, S_CHICKEN_DIE2, 0, 0}, // S_CHICKEN_DIE1 {SPR_CHKN, 5, 6, A_Feathers, S_CHICKEN_DIE3, 0, 0}, // S_CHICKEN_DIE2 {SPR_CHKN, 6, 6, NULL, S_CHICKEN_DIE4, 0, 0}, // S_CHICKEN_DIE3 {SPR_CHKN, 7, 6, A_NoBlocking, S_CHICKEN_DIE5, 0, 0}, // S_CHICKEN_DIE4 {SPR_CHKN, 8, 6, NULL, S_CHICKEN_DIE6, 0, 0}, // S_CHICKEN_DIE5 {SPR_CHKN, 9, 6, NULL, S_CHICKEN_DIE7, 0, 0}, // S_CHICKEN_DIE6 {SPR_CHKN, 10, 6, NULL, S_CHICKEN_DIE8, 0, 0}, // S_CHICKEN_DIE7 {SPR_CHKN, 11, -1, NULL, S_NULL, 0, 0}, // S_CHICKEN_DIE8 {SPR_CHKN, 12, 3, NULL, S_FEATHER2, 0, 0}, // S_FEATHER1 {SPR_CHKN, 13, 3, NULL, S_FEATHER3, 0, 0}, // S_FEATHER2 {SPR_CHKN, 14, 3, NULL, S_FEATHER4, 0, 0}, // S_FEATHER3 {SPR_CHKN, 15, 3, NULL, S_FEATHER5, 0, 0}, // S_FEATHER4 {SPR_CHKN, 16, 3, NULL, S_FEATHER6, 0, 0}, // S_FEATHER5 {SPR_CHKN, 15, 3, NULL, S_FEATHER7, 0, 0}, // S_FEATHER6 {SPR_CHKN, 14, 3, NULL, S_FEATHER8, 0, 0}, // S_FEATHER7 {SPR_CHKN, 13, 3, NULL, S_FEATHER1, 0, 0}, // S_FEATHER8 {SPR_CHKN, 13, 6, NULL, S_NULL, 0, 0}, // S_FEATHERX {SPR_MUMM, 0, 10, A_Look, S_MUMMY_LOOK2, 0, 0}, // S_MUMMY_LOOK1 {SPR_MUMM, 1, 10, A_Look, S_MUMMY_LOOK1, 0, 0}, // S_MUMMY_LOOK2 {SPR_MUMM, 0, 4, A_Chase, S_MUMMY_WALK2, 0, 0}, // S_MUMMY_WALK1 {SPR_MUMM, 1, 4, A_Chase, S_MUMMY_WALK3, 0, 0}, // S_MUMMY_WALK2 {SPR_MUMM, 2, 4, A_Chase, S_MUMMY_WALK4, 0, 0}, // S_MUMMY_WALK3 {SPR_MUMM, 3, 4, A_Chase, S_MUMMY_WALK1, 0, 0}, // S_MUMMY_WALK4 {SPR_MUMM, 4, 6, A_FaceTarget, S_MUMMY_ATK2, 0, 0}, // S_MUMMY_ATK1 {SPR_MUMM, 5, 6, A_MummyAttack, S_MUMMY_ATK3, 0, 0}, // S_MUMMY_ATK2 {SPR_MUMM, 6, 6, A_FaceTarget, S_MUMMY_WALK1, 0, 0}, // S_MUMMY_ATK3 {SPR_MUMM, 23, 5, A_FaceTarget, S_MUMMYL_ATK2, 0, 0}, // S_MUMMYL_ATK1 {SPR_MUMM, 32792, 5, A_FaceTarget, S_MUMMYL_ATK3, 0, 0}, // S_MUMMYL_ATK2 {SPR_MUMM, 23, 5, A_FaceTarget, S_MUMMYL_ATK4, 0, 0}, // S_MUMMYL_ATK3 {SPR_MUMM, 32792, 5, A_FaceTarget, S_MUMMYL_ATK5, 0, 0}, // S_MUMMYL_ATK4 {SPR_MUMM, 23, 5, A_FaceTarget, S_MUMMYL_ATK6, 0, 0}, // S_MUMMYL_ATK5 {SPR_MUMM, 32792, 15, A_MummyAttack2, S_MUMMY_WALK1, 0, 0}, // S_MUMMYL_ATK6 {SPR_MUMM, 7, 4, NULL, S_MUMMY_PAIN2, 0, 0}, // S_MUMMY_PAIN1 {SPR_MUMM, 7, 4, A_Pain, S_MUMMY_WALK1, 0, 0}, // S_MUMMY_PAIN2 {SPR_MUMM, 8, 5, NULL, S_MUMMY_DIE2, 0, 0}, // S_MUMMY_DIE1 {SPR_MUMM, 9, 5, A_Scream, S_MUMMY_DIE3, 0, 0}, // S_MUMMY_DIE2 {SPR_MUMM, 10, 5, A_MummySoul, S_MUMMY_DIE4, 0, 0}, // S_MUMMY_DIE3 {SPR_MUMM, 11, 5, NULL, S_MUMMY_DIE5, 0, 0}, // S_MUMMY_DIE4 {SPR_MUMM, 12, 5, A_NoBlocking, S_MUMMY_DIE6, 0, 0}, // S_MUMMY_DIE5 {SPR_MUMM, 13, 5, NULL, S_MUMMY_DIE7, 0, 0}, // S_MUMMY_DIE6 {SPR_MUMM, 14, 5, NULL, S_MUMMY_DIE8, 0, 0}, // S_MUMMY_DIE7 {SPR_MUMM, 15, -1, NULL, S_NULL, 0, 0}, // S_MUMMY_DIE8 {SPR_MUMM, 16, 5, NULL, S_MUMMY_SOUL2, 0, 0}, // S_MUMMY_SOUL1 {SPR_MUMM, 17, 5, NULL, S_MUMMY_SOUL3, 0, 0}, // S_MUMMY_SOUL2 {SPR_MUMM, 18, 5, NULL, S_MUMMY_SOUL4, 0, 0}, // S_MUMMY_SOUL3 {SPR_MUMM, 19, 9, NULL, S_MUMMY_SOUL5, 0, 0}, // S_MUMMY_SOUL4 {SPR_MUMM, 20, 5, NULL, S_MUMMY_SOUL6, 0, 0}, // S_MUMMY_SOUL5 {SPR_MUMM, 21, 5, NULL, S_MUMMY_SOUL7, 0, 0}, // S_MUMMY_SOUL6 {SPR_MUMM, 22, 5, NULL, S_NULL, 0, 0}, // S_MUMMY_SOUL7 {SPR_FX15, 32768, 5, A_ContMobjSound, S_MUMMYFX1_2, 0, 0}, // S_MUMMYFX1_1 {SPR_FX15, 32769, 5, A_MummyFX1Seek, S_MUMMYFX1_3, 0, 0}, // S_MUMMYFX1_2 {SPR_FX15, 32770, 5, NULL, S_MUMMYFX1_4, 0, 0}, // S_MUMMYFX1_3 {SPR_FX15, 32769, 5, A_MummyFX1Seek, S_MUMMYFX1_1, 0, 0}, // S_MUMMYFX1_4 {SPR_FX15, 32771, 5, NULL, S_MUMMYFXI1_2, 0, 0}, // S_MUMMYFXI1_1 {SPR_FX15, 32772, 5, NULL, S_MUMMYFXI1_3, 0, 0}, // S_MUMMYFXI1_2 {SPR_FX15, 32773, 5, NULL, S_MUMMYFXI1_4, 0, 0}, // S_MUMMYFXI1_3 {SPR_FX15, 32774, 5, NULL, S_NULL, 0, 0}, // S_MUMMYFXI1_4 {SPR_BEAS, 0, 10, A_Look, S_BEAST_LOOK2, 0, 0}, // S_BEAST_LOOK1 {SPR_BEAS, 1, 10, A_Look, S_BEAST_LOOK1, 0, 0}, // S_BEAST_LOOK2 {SPR_BEAS, 0, 3, A_Chase, S_BEAST_WALK2, 0, 0}, // S_BEAST_WALK1 {SPR_BEAS, 1, 3, A_Chase, S_BEAST_WALK3, 0, 0}, // S_BEAST_WALK2 {SPR_BEAS, 2, 3, A_Chase, S_BEAST_WALK4, 0, 0}, // S_BEAST_WALK3 {SPR_BEAS, 3, 3, A_Chase, S_BEAST_WALK5, 0, 0}, // S_BEAST_WALK4 {SPR_BEAS, 4, 3, A_Chase, S_BEAST_WALK6, 0, 0}, // S_BEAST_WALK5 {SPR_BEAS, 5, 3, A_Chase, S_BEAST_WALK1, 0, 0}, // S_BEAST_WALK6 {SPR_BEAS, 7, 10, A_FaceTarget, S_BEAST_ATK2, 0, 0}, // S_BEAST_ATK1 {SPR_BEAS, 8, 10, A_BeastAttack, S_BEAST_WALK1, 0, 0}, // S_BEAST_ATK2 {SPR_BEAS, 6, 3, NULL, S_BEAST_PAIN2, 0, 0}, // S_BEAST_PAIN1 {SPR_BEAS, 6, 3, A_Pain, S_BEAST_WALK1, 0, 0}, // S_BEAST_PAIN2 {SPR_BEAS, 17, 6, NULL, S_BEAST_DIE2, 0, 0}, // S_BEAST_DIE1 {SPR_BEAS, 18, 6, A_Scream, S_BEAST_DIE3, 0, 0}, // S_BEAST_DIE2 {SPR_BEAS, 19, 6, NULL, S_BEAST_DIE4, 0, 0}, // S_BEAST_DIE3 {SPR_BEAS, 20, 6, NULL, S_BEAST_DIE5, 0, 0}, // S_BEAST_DIE4 {SPR_BEAS, 21, 6, NULL, S_BEAST_DIE6, 0, 0}, // S_BEAST_DIE5 {SPR_BEAS, 22, 6, A_NoBlocking, S_BEAST_DIE7, 0, 0}, // S_BEAST_DIE6 {SPR_BEAS, 23, 6, NULL, S_BEAST_DIE8, 0, 0}, // S_BEAST_DIE7 {SPR_BEAS, 24, 6, NULL, S_BEAST_DIE9, 0, 0}, // S_BEAST_DIE8 {SPR_BEAS, 25, -1, NULL, S_NULL, 0, 0}, // S_BEAST_DIE9 {SPR_BEAS, 9, 5, NULL, S_BEAST_XDIE2, 0, 0}, // S_BEAST_XDIE1 {SPR_BEAS, 10, 6, A_Scream, S_BEAST_XDIE3, 0, 0}, // S_BEAST_XDIE2 {SPR_BEAS, 11, 5, NULL, S_BEAST_XDIE4, 0, 0}, // S_BEAST_XDIE3 {SPR_BEAS, 12, 6, NULL, S_BEAST_XDIE5, 0, 0}, // S_BEAST_XDIE4 {SPR_BEAS, 13, 5, NULL, S_BEAST_XDIE6, 0, 0}, // S_BEAST_XDIE5 {SPR_BEAS, 14, 6, A_NoBlocking, S_BEAST_XDIE7, 0, 0}, // S_BEAST_XDIE6 {SPR_BEAS, 15, 5, NULL, S_BEAST_XDIE8, 0, 0}, // S_BEAST_XDIE7 {SPR_BEAS, 16, -1, NULL, S_NULL, 0, 0}, // S_BEAST_XDIE8 {SPR_FRB1, 0, 2, A_BeastPuff, S_BEASTBALL2, 0, 0}, // S_BEASTBALL1 {SPR_FRB1, 0, 2, A_BeastPuff, S_BEASTBALL3, 0, 0}, // S_BEASTBALL2 {SPR_FRB1, 1, 2, A_BeastPuff, S_BEASTBALL4, 0, 0}, // S_BEASTBALL3 {SPR_FRB1, 1, 2, A_BeastPuff, S_BEASTBALL5, 0, 0}, // S_BEASTBALL4 {SPR_FRB1, 2, 2, A_BeastPuff, S_BEASTBALL6, 0, 0}, // S_BEASTBALL5 {SPR_FRB1, 2, 2, A_BeastPuff, S_BEASTBALL1, 0, 0}, // S_BEASTBALL6 {SPR_FRB1, 3, 4, NULL, S_BEASTBALLX2, 0, 0}, // S_BEASTBALLX1 {SPR_FRB1, 4, 4, NULL, S_BEASTBALLX3, 0, 0}, // S_BEASTBALLX2 {SPR_FRB1, 5, 4, NULL, S_BEASTBALLX4, 0, 0}, // S_BEASTBALLX3 {SPR_FRB1, 6, 4, NULL, S_BEASTBALLX5, 0, 0}, // S_BEASTBALLX4 {SPR_FRB1, 7, 4, NULL, S_NULL, 0, 0}, // S_BEASTBALLX5 {SPR_FRB1, 0, 4, NULL, S_BURNBALL2, 0, 0}, // S_BURNBALL1 {SPR_FRB1, 1, 4, NULL, S_BURNBALL3, 0, 0}, // S_BURNBALL2 {SPR_FRB1, 2, 4, NULL, S_BURNBALL4, 0, 0}, // S_BURNBALL3 {SPR_FRB1, 3, 4, NULL, S_BURNBALL5, 0, 0}, // S_BURNBALL4 {SPR_FRB1, 4, 4, NULL, S_BURNBALL6, 0, 0}, // S_BURNBALL5 {SPR_FRB1, 5, 4, NULL, S_BURNBALL7, 0, 0}, // S_BURNBALL6 {SPR_FRB1, 6, 4, NULL, S_BURNBALL8, 0, 0}, // S_BURNBALL7 {SPR_FRB1, 7, 4, NULL, S_NULL, 0, 0}, // S_BURNBALL8 {SPR_FRB1, 32768, 4, NULL, S_BURNBALLFB2, 0, 0}, // S_BURNBALLFB1 {SPR_FRB1, 32769, 4, NULL, S_BURNBALLFB3, 0, 0}, // S_BURNBALLFB2 {SPR_FRB1, 32770, 4, NULL, S_BURNBALLFB4, 0, 0}, // S_BURNBALLFB3 {SPR_FRB1, 32771, 4, NULL, S_BURNBALLFB5, 0, 0}, // S_BURNBALLFB4 {SPR_FRB1, 32772, 4, NULL, S_BURNBALLFB6, 0, 0}, // S_BURNBALLFB5 {SPR_FRB1, 32773, 4, NULL, S_BURNBALLFB7, 0, 0}, // S_BURNBALLFB6 {SPR_FRB1, 32774, 4, NULL, S_BURNBALLFB8, 0, 0}, // S_BURNBALLFB7 {SPR_FRB1, 32775, 4, NULL, S_NULL, 0, 0}, // S_BURNBALLFB8 {SPR_FRB1, 3, 4, NULL, S_PUFFY2, 0, 0}, // S_PUFFY1 {SPR_FRB1, 4, 4, NULL, S_PUFFY3, 0, 0}, // S_PUFFY2 {SPR_FRB1, 5, 4, NULL, S_PUFFY4, 0, 0}, // S_PUFFY3 {SPR_FRB1, 6, 4, NULL, S_PUFFY5, 0, 0}, // S_PUFFY4 {SPR_FRB1, 7, 4, NULL, S_NULL, 0, 0}, // S_PUFFY5 {SPR_SNKE, 0, 10, A_Look, S_SNAKE_LOOK2, 0, 0}, // S_SNAKE_LOOK1 {SPR_SNKE, 1, 10, A_Look, S_SNAKE_LOOK1, 0, 0}, // S_SNAKE_LOOK2 {SPR_SNKE, 0, 4, A_Chase, S_SNAKE_WALK2, 0, 0}, // S_SNAKE_WALK1 {SPR_SNKE, 1, 4, A_Chase, S_SNAKE_WALK3, 0, 0}, // S_SNAKE_WALK2 {SPR_SNKE, 2, 4, A_Chase, S_SNAKE_WALK4, 0, 0}, // S_SNAKE_WALK3 {SPR_SNKE, 3, 4, A_Chase, S_SNAKE_WALK1, 0, 0}, // S_SNAKE_WALK4 {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK2, 0, 0}, // S_SNAKE_ATK1 {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK3, 0, 0}, // S_SNAKE_ATK2 {SPR_SNKE, 5, 4, A_SnakeAttack, S_SNAKE_ATK4, 0, 0}, // S_SNAKE_ATK3 {SPR_SNKE, 5, 4, A_SnakeAttack, S_SNAKE_ATK5, 0, 0}, // S_SNAKE_ATK4 {SPR_SNKE, 5, 4, A_SnakeAttack, S_SNAKE_ATK6, 0, 0}, // S_SNAKE_ATK5 {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK7, 0, 0}, // S_SNAKE_ATK6 {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK8, 0, 0}, // S_SNAKE_ATK7 {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK9, 0, 0}, // S_SNAKE_ATK8 {SPR_SNKE, 5, 4, A_SnakeAttack2, S_SNAKE_WALK1, 0, 0}, // S_SNAKE_ATK9 {SPR_SNKE, 4, 3, NULL, S_SNAKE_PAIN2, 0, 0}, // S_SNAKE_PAIN1 {SPR_SNKE, 4, 3, A_Pain, S_SNAKE_WALK1, 0, 0}, // S_SNAKE_PAIN2 {SPR_SNKE, 6, 5, NULL, S_SNAKE_DIE2, 0, 0}, // S_SNAKE_DIE1 {SPR_SNKE, 7, 5, A_Scream, S_SNAKE_DIE3, 0, 0}, // S_SNAKE_DIE2 {SPR_SNKE, 8, 5, NULL, S_SNAKE_DIE4, 0, 0}, // S_SNAKE_DIE3 {SPR_SNKE, 9, 5, NULL, S_SNAKE_DIE5, 0, 0}, // S_SNAKE_DIE4 {SPR_SNKE, 10, 5, NULL, S_SNAKE_DIE6, 0, 0}, // S_SNAKE_DIE5 {SPR_SNKE, 11, 5, NULL, S_SNAKE_DIE7, 0, 0}, // S_SNAKE_DIE6 {SPR_SNKE, 12, 5, A_NoBlocking, S_SNAKE_DIE8, 0, 0}, // S_SNAKE_DIE7 {SPR_SNKE, 13, 5, NULL, S_SNAKE_DIE9, 0, 0}, // S_SNAKE_DIE8 {SPR_SNKE, 14, 5, NULL, S_SNAKE_DIE10, 0, 0}, // S_SNAKE_DIE9 {SPR_SNKE, 15, -1, NULL, S_NULL, 0, 0}, // S_SNAKE_DIE10 {SPR_SNFX, 32768, 5, NULL, S_SNAKEPRO_A2, 0, 0}, // S_SNAKEPRO_A1 {SPR_SNFX, 32769, 5, NULL, S_SNAKEPRO_A3, 0, 0}, // S_SNAKEPRO_A2 {SPR_SNFX, 32770, 5, NULL, S_SNAKEPRO_A4, 0, 0}, // S_SNAKEPRO_A3 {SPR_SNFX, 32771, 5, NULL, S_SNAKEPRO_A1, 0, 0}, // S_SNAKEPRO_A4 {SPR_SNFX, 32772, 5, NULL, S_SNAKEPRO_AX2, 0, 0}, // S_SNAKEPRO_AX1 {SPR_SNFX, 32773, 5, NULL, S_SNAKEPRO_AX3, 0, 0}, // S_SNAKEPRO_AX2 {SPR_SNFX, 32774, 4, NULL, S_SNAKEPRO_AX4, 0, 0}, // S_SNAKEPRO_AX3 {SPR_SNFX, 32775, 3, NULL, S_SNAKEPRO_AX5, 0, 0}, // S_SNAKEPRO_AX4 {SPR_SNFX, 32776, 3, NULL, S_NULL, 0, 0}, // S_SNAKEPRO_AX5 {SPR_SNFX, 32777, 6, NULL, S_SNAKEPRO_B2, 0, 0}, // S_SNAKEPRO_B1 {SPR_SNFX, 32778, 6, NULL, S_SNAKEPRO_B1, 0, 0}, // S_SNAKEPRO_B2 {SPR_SNFX, 32779, 5, NULL, S_SNAKEPRO_BX2, 0, 0}, // S_SNAKEPRO_BX1 {SPR_SNFX, 32780, 5, NULL, S_SNAKEPRO_BX3, 0, 0}, // S_SNAKEPRO_BX2 {SPR_SNFX, 32781, 4, NULL, S_SNAKEPRO_BX4, 0, 0}, // S_SNAKEPRO_BX3 {SPR_SNFX, 32782, 3, NULL, S_NULL, 0, 0}, // S_SNAKEPRO_BX4 {SPR_HEAD, 0, 10, A_Look, S_HEAD_LOOK, 0, 0}, // S_HEAD_LOOK {SPR_HEAD, 0, 4, A_Chase, S_HEAD_FLOAT, 0, 0}, // S_HEAD_FLOAT {SPR_HEAD, 0, 5, A_FaceTarget, S_HEAD_ATK2, 0, 0}, // S_HEAD_ATK1 {SPR_HEAD, 1, 20, A_HeadAttack, S_HEAD_FLOAT, 0, 0}, // S_HEAD_ATK2 {SPR_HEAD, 0, 4, NULL, S_HEAD_PAIN2, 0, 0}, // S_HEAD_PAIN1 {SPR_HEAD, 0, 4, A_Pain, S_HEAD_FLOAT, 0, 0}, // S_HEAD_PAIN2 {SPR_HEAD, 2, 7, NULL, S_HEAD_DIE2, 0, 0}, // S_HEAD_DIE1 {SPR_HEAD, 3, 7, A_Scream, S_HEAD_DIE3, 0, 0}, // S_HEAD_DIE2 {SPR_HEAD, 4, 7, NULL, S_HEAD_DIE4, 0, 0}, // S_HEAD_DIE3 {SPR_HEAD, 5, 7, NULL, S_HEAD_DIE5, 0, 0}, // S_HEAD_DIE4 {SPR_HEAD, 6, 7, A_NoBlocking, S_HEAD_DIE6, 0, 0}, // S_HEAD_DIE5 {SPR_HEAD, 7, 7, NULL, S_HEAD_DIE7, 0, 0}, // S_HEAD_DIE6 {SPR_HEAD, 8, -1, A_BossDeath, S_NULL, 0, 0}, // S_HEAD_DIE7 {SPR_FX05, 0, 6, NULL, S_HEADFX1_2, 0, 0}, // S_HEADFX1_1 {SPR_FX05, 1, 6, NULL, S_HEADFX1_3, 0, 0}, // S_HEADFX1_2 {SPR_FX05, 2, 6, NULL, S_HEADFX1_1, 0, 0}, // S_HEADFX1_3 {SPR_FX05, 3, 5, A_HeadIceImpact, S_HEADFXI1_2, 0, 0}, // S_HEADFXI1_1 {SPR_FX05, 4, 5, NULL, S_HEADFXI1_3, 0, 0}, // S_HEADFXI1_2 {SPR_FX05, 5, 5, NULL, S_HEADFXI1_4, 0, 0}, // S_HEADFXI1_3 {SPR_FX05, 6, 5, NULL, S_NULL, 0, 0}, // S_HEADFXI1_4 {SPR_FX05, 7, 6, NULL, S_HEADFX2_2, 0, 0}, // S_HEADFX2_1 {SPR_FX05, 8, 6, NULL, S_HEADFX2_3, 0, 0}, // S_HEADFX2_2 {SPR_FX05, 9, 6, NULL, S_HEADFX2_1, 0, 0}, // S_HEADFX2_3 {SPR_FX05, 3, 5, NULL, S_HEADFXI2_2, 0, 0}, // S_HEADFXI2_1 {SPR_FX05, 4, 5, NULL, S_HEADFXI2_3, 0, 0}, // S_HEADFXI2_2 {SPR_FX05, 5, 5, NULL, S_HEADFXI2_4, 0, 0}, // S_HEADFXI2_3 {SPR_FX05, 6, 5, NULL, S_NULL, 0, 0}, // S_HEADFXI2_4 {SPR_FX06, 0, 4, A_HeadFireGrow, S_HEADFX3_2, 0, 0}, // S_HEADFX3_1 {SPR_FX06, 1, 4, A_HeadFireGrow, S_HEADFX3_3, 0, 0}, // S_HEADFX3_2 {SPR_FX06, 2, 4, A_HeadFireGrow, S_HEADFX3_1, 0, 0}, // S_HEADFX3_3 {SPR_FX06, 0, 5, NULL, S_HEADFX3_5, 0, 0}, // S_HEADFX3_4 {SPR_FX06, 1, 5, NULL, S_HEADFX3_6, 0, 0}, // S_HEADFX3_5 {SPR_FX06, 2, 5, NULL, S_HEADFX3_4, 0, 0}, // S_HEADFX3_6 {SPR_FX06, 3, 5, NULL, S_HEADFXI3_2, 0, 0}, // S_HEADFXI3_1 {SPR_FX06, 4, 5, NULL, S_HEADFXI3_3, 0, 0}, // S_HEADFXI3_2 {SPR_FX06, 5, 5, NULL, S_HEADFXI3_4, 0, 0}, // S_HEADFXI3_3 {SPR_FX06, 6, 5, NULL, S_NULL, 0, 0}, // S_HEADFXI3_4 {SPR_FX07, 3, 3, NULL, S_HEADFX4_2, 0, 0}, // S_HEADFX4_1 {SPR_FX07, 4, 3, NULL, S_HEADFX4_3, 0, 0}, // S_HEADFX4_2 {SPR_FX07, 5, 3, NULL, S_HEADFX4_4, 0, 0}, // S_HEADFX4_3 {SPR_FX07, 6, 3, NULL, S_HEADFX4_5, 0, 0}, // S_HEADFX4_4 {SPR_FX07, 0, 3, A_WhirlwindSeek, S_HEADFX4_6, 0, 0}, // S_HEADFX4_5 {SPR_FX07, 1, 3, A_WhirlwindSeek, S_HEADFX4_7, 0, 0}, // S_HEADFX4_6 {SPR_FX07, 2, 3, A_WhirlwindSeek, S_HEADFX4_5, 0, 0}, // S_HEADFX4_7 {SPR_FX07, 6, 4, NULL, S_HEADFXI4_2, 0, 0}, // S_HEADFXI4_1 {SPR_FX07, 5, 4, NULL, S_HEADFXI4_3, 0, 0}, // S_HEADFXI4_2 {SPR_FX07, 4, 4, NULL, S_HEADFXI4_4, 0, 0}, // S_HEADFXI4_3 {SPR_FX07, 3, 4, NULL, S_NULL, 0, 0}, // S_HEADFXI4_4 {SPR_CLNK, 0, 10, A_Look, S_CLINK_LOOK2, 0, 0}, // S_CLINK_LOOK1 {SPR_CLNK, 1, 10, A_Look, S_CLINK_LOOK1, 0, 0}, // S_CLINK_LOOK2 {SPR_CLNK, 0, 3, A_Chase, S_CLINK_WALK2, 0, 0}, // S_CLINK_WALK1 {SPR_CLNK, 1, 3, A_Chase, S_CLINK_WALK3, 0, 0}, // S_CLINK_WALK2 {SPR_CLNK, 2, 3, A_Chase, S_CLINK_WALK4, 0, 0}, // S_CLINK_WALK3 {SPR_CLNK, 3, 3, A_Chase, S_CLINK_WALK1, 0, 0}, // S_CLINK_WALK4 {SPR_CLNK, 4, 5, A_FaceTarget, S_CLINK_ATK2, 0, 0}, // S_CLINK_ATK1 {SPR_CLNK, 5, 4, A_FaceTarget, S_CLINK_ATK3, 0, 0}, // S_CLINK_ATK2 {SPR_CLNK, 6, 7, A_ClinkAttack, S_CLINK_WALK1, 0, 0}, // S_CLINK_ATK3 {SPR_CLNK, 7, 3, NULL, S_CLINK_PAIN2, 0, 0}, // S_CLINK_PAIN1 {SPR_CLNK, 7, 3, A_Pain, S_CLINK_WALK1, 0, 0}, // S_CLINK_PAIN2 {SPR_CLNK, 8, 6, NULL, S_CLINK_DIE2, 0, 0}, // S_CLINK_DIE1 {SPR_CLNK, 9, 6, NULL, S_CLINK_DIE3, 0, 0}, // S_CLINK_DIE2 {SPR_CLNK, 10, 5, A_Scream, S_CLINK_DIE4, 0, 0}, // S_CLINK_DIE3 {SPR_CLNK, 11, 5, A_NoBlocking, S_CLINK_DIE5, 0, 0}, // S_CLINK_DIE4 {SPR_CLNK, 12, 5, NULL, S_CLINK_DIE6, 0, 0}, // S_CLINK_DIE5 {SPR_CLNK, 13, 5, NULL, S_CLINK_DIE7, 0, 0}, // S_CLINK_DIE6 {SPR_CLNK, 14, -1, NULL, S_NULL, 0, 0}, // S_CLINK_DIE7 {SPR_WZRD, 0, 10, A_Look, S_WIZARD_LOOK2, 0, 0}, // S_WIZARD_LOOK1 {SPR_WZRD, 1, 10, A_Look, S_WIZARD_LOOK1, 0, 0}, // S_WIZARD_LOOK2 {SPR_WZRD, 0, 3, A_Chase, S_WIZARD_WALK2, 0, 0}, // S_WIZARD_WALK1 {SPR_WZRD, 0, 4, A_Chase, S_WIZARD_WALK3, 0, 0}, // S_WIZARD_WALK2 {SPR_WZRD, 0, 3, A_Chase, S_WIZARD_WALK4, 0, 0}, // S_WIZARD_WALK3 {SPR_WZRD, 0, 4, A_Chase, S_WIZARD_WALK5, 0, 0}, // S_WIZARD_WALK4 {SPR_WZRD, 1, 3, A_Chase, S_WIZARD_WALK6, 0, 0}, // S_WIZARD_WALK5 {SPR_WZRD, 1, 4, A_Chase, S_WIZARD_WALK7, 0, 0}, // S_WIZARD_WALK6 {SPR_WZRD, 1, 3, A_Chase, S_WIZARD_WALK8, 0, 0}, // S_WIZARD_WALK7 {SPR_WZRD, 1, 4, A_Chase, S_WIZARD_WALK1, 0, 0}, // S_WIZARD_WALK8 {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK2, 0, 0}, // S_WIZARD_ATK1 {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK3, 0, 0}, // S_WIZARD_ATK2 {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK4, 0, 0}, // S_WIZARD_ATK3 {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK5, 0, 0}, // S_WIZARD_ATK4 {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK6, 0, 0}, // S_WIZARD_ATK5 {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK7, 0, 0}, // S_WIZARD_ATK6 {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK8, 0, 0}, // S_WIZARD_ATK7 {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK9, 0, 0}, // S_WIZARD_ATK8 {SPR_WZRD, 3, 12, A_WizAtk3, S_WIZARD_WALK1, 0, 0}, // S_WIZARD_ATK9 {SPR_WZRD, 4, 3, A_GhostOff, S_WIZARD_PAIN2, 0, 0}, // S_WIZARD_PAIN1 {SPR_WZRD, 4, 3, A_Pain, S_WIZARD_WALK1, 0, 0}, // S_WIZARD_PAIN2 {SPR_WZRD, 5, 6, A_GhostOff, S_WIZARD_DIE2, 0, 0}, // S_WIZARD_DIE1 {SPR_WZRD, 6, 6, A_Scream, S_WIZARD_DIE3, 0, 0}, // S_WIZARD_DIE2 {SPR_WZRD, 7, 6, NULL, S_WIZARD_DIE4, 0, 0}, // S_WIZARD_DIE3 {SPR_WZRD, 8, 6, NULL, S_WIZARD_DIE5, 0, 0}, // S_WIZARD_DIE4 {SPR_WZRD, 9, 6, A_NoBlocking, S_WIZARD_DIE6, 0, 0}, // S_WIZARD_DIE5 {SPR_WZRD, 10, 6, NULL, S_WIZARD_DIE7, 0, 0}, // S_WIZARD_DIE6 {SPR_WZRD, 11, 6, NULL, S_WIZARD_DIE8, 0, 0}, // S_WIZARD_DIE7 {SPR_WZRD, 12, -1, NULL, S_NULL, 0, 0}, // S_WIZARD_DIE8 {SPR_FX11, 32768, 6, NULL, S_WIZFX1_2, 0, 0}, // S_WIZFX1_1 {SPR_FX11, 32769, 6, NULL, S_WIZFX1_1, 0, 0}, // S_WIZFX1_2 {SPR_FX11, 32770, 5, NULL, S_WIZFXI1_2, 0, 0}, // S_WIZFXI1_1 {SPR_FX11, 32771, 5, NULL, S_WIZFXI1_3, 0, 0}, // S_WIZFXI1_2 {SPR_FX11, 32772, 5, NULL, S_WIZFXI1_4, 0, 0}, // S_WIZFXI1_3 {SPR_FX11, 32773, 5, NULL, S_WIZFXI1_5, 0, 0}, // S_WIZFXI1_4 {SPR_FX11, 32774, 5, NULL, S_NULL, 0, 0}, // S_WIZFXI1_5 {SPR_IMPX, 0, 10, A_Look, S_IMP_LOOK2, 0, 0}, // S_IMP_LOOK1 {SPR_IMPX, 1, 10, A_Look, S_IMP_LOOK3, 0, 0}, // S_IMP_LOOK2 {SPR_IMPX, 2, 10, A_Look, S_IMP_LOOK4, 0, 0}, // S_IMP_LOOK3 {SPR_IMPX, 1, 10, A_Look, S_IMP_LOOK1, 0, 0}, // S_IMP_LOOK4 {SPR_IMPX, 0, 3, A_Chase, S_IMP_FLY2, 0, 0}, // S_IMP_FLY1 {SPR_IMPX, 0, 3, A_Chase, S_IMP_FLY3, 0, 0}, // S_IMP_FLY2 {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY4, 0, 0}, // S_IMP_FLY3 {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY5, 0, 0}, // S_IMP_FLY4 {SPR_IMPX, 2, 3, A_Chase, S_IMP_FLY6, 0, 0}, // S_IMP_FLY5 {SPR_IMPX, 2, 3, A_Chase, S_IMP_FLY7, 0, 0}, // S_IMP_FLY6 {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY8, 0, 0}, // S_IMP_FLY7 {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY1, 0, 0}, // S_IMP_FLY8 {SPR_IMPX, 3, 6, A_FaceTarget, S_IMP_MEATK2, 0, 0}, // S_IMP_MEATK1 {SPR_IMPX, 4, 6, A_FaceTarget, S_IMP_MEATK3, 0, 0}, // S_IMP_MEATK2 {SPR_IMPX, 5, 6, A_ImpMeAttack, S_IMP_FLY1, 0, 0}, // S_IMP_MEATK3 {SPR_IMPX, 0, 10, A_FaceTarget, S_IMP_MSATK1_2, 0, 0}, // S_IMP_MSATK1_1 {SPR_IMPX, 1, 6, A_ImpMsAttack, S_IMP_MSATK1_3, 0, 0}, // S_IMP_MSATK1_2 {SPR_IMPX, 2, 6, NULL, S_IMP_MSATK1_4, 0, 0}, // S_IMP_MSATK1_3 {SPR_IMPX, 1, 6, NULL, S_IMP_MSATK1_5, 0, 0}, // S_IMP_MSATK1_4 {SPR_IMPX, 0, 6, NULL, S_IMP_MSATK1_6, 0, 0}, // S_IMP_MSATK1_5 {SPR_IMPX, 1, 6, NULL, S_IMP_MSATK1_3, 0, 0}, // S_IMP_MSATK1_6 {SPR_IMPX, 3, 6, A_FaceTarget, S_IMP_MSATK2_2, 0, 0}, // S_IMP_MSATK2_1 {SPR_IMPX, 4, 6, A_FaceTarget, S_IMP_MSATK2_3, 0, 0}, // S_IMP_MSATK2_2 {SPR_IMPX, 5, 6, A_ImpMsAttack2, S_IMP_FLY1, 0, 0}, // S_IMP_MSATK2_3 {SPR_IMPX, 6, 3, NULL, S_IMP_PAIN2, 0, 0}, // S_IMP_PAIN1 {SPR_IMPX, 6, 3, A_Pain, S_IMP_FLY1, 0, 0}, // S_IMP_PAIN2 {SPR_IMPX, 6, 4, A_ImpDeath, S_IMP_DIE2, 0, 0}, // S_IMP_DIE1 {SPR_IMPX, 7, 5, NULL, S_IMP_DIE2, 0, 0}, // S_IMP_DIE2 {SPR_IMPX, 18, 5, A_ImpXDeath1, S_IMP_XDIE2, 0, 0}, // S_IMP_XDIE1 {SPR_IMPX, 19, 5, NULL, S_IMP_XDIE3, 0, 0}, // S_IMP_XDIE2 {SPR_IMPX, 20, 5, NULL, S_IMP_XDIE4, 0, 0}, // S_IMP_XDIE3 {SPR_IMPX, 21, 5, A_ImpXDeath2, S_IMP_XDIE5, 0, 0}, // S_IMP_XDIE4 {SPR_IMPX, 22, 5, NULL, S_IMP_XDIE5, 0, 0}, // S_IMP_XDIE5 {SPR_IMPX, 8, 7, A_ImpExplode, S_IMP_CRASH2, 0, 0}, // S_IMP_CRASH1 {SPR_IMPX, 9, 7, A_Scream, S_IMP_CRASH3, 0, 0}, // S_IMP_CRASH2 {SPR_IMPX, 10, 7, NULL, S_IMP_CRASH4, 0, 0}, // S_IMP_CRASH3 {SPR_IMPX, 11, -1, NULL, S_NULL, 0, 0}, // S_IMP_CRASH4 {SPR_IMPX, 23, 7, NULL, S_IMP_XCRASH2, 0, 0}, // S_IMP_XCRASH1 {SPR_IMPX, 24, 7, NULL, S_IMP_XCRASH3, 0, 0}, // S_IMP_XCRASH2 {SPR_IMPX, 25, -1, NULL, S_NULL, 0, 0}, // S_IMP_XCRASH3 {SPR_IMPX, 12, 5, NULL, S_IMP_CHUNKA2, 0, 0}, // S_IMP_CHUNKA1 {SPR_IMPX, 13, 700, NULL, S_IMP_CHUNKA3, 0, 0}, // S_IMP_CHUNKA2 {SPR_IMPX, 14, 700, NULL, S_NULL, 0, 0}, // S_IMP_CHUNKA3 {SPR_IMPX, 15, 5, NULL, S_IMP_CHUNKB2, 0, 0}, // S_IMP_CHUNKB1 {SPR_IMPX, 16, 700, NULL, S_IMP_CHUNKB3, 0, 0}, // S_IMP_CHUNKB2 {SPR_IMPX, 17, 700, NULL, S_NULL, 0, 0}, // S_IMP_CHUNKB3 {SPR_FX10, 32768, 6, NULL, S_IMPFX2, 0, 0}, // S_IMPFX1 {SPR_FX10, 32769, 6, NULL, S_IMPFX3, 0, 0}, // S_IMPFX2 {SPR_FX10, 32770, 6, NULL, S_IMPFX1, 0, 0}, // S_IMPFX3 {SPR_FX10, 32771, 5, NULL, S_IMPFXI2, 0, 0}, // S_IMPFXI1 {SPR_FX10, 32772, 5, NULL, S_IMPFXI3, 0, 0}, // S_IMPFXI2 {SPR_FX10, 32773, 5, NULL, S_IMPFXI4, 0, 0}, // S_IMPFXI3 {SPR_FX10, 32774, 5, NULL, S_NULL, 0, 0}, // S_IMPFXI4 {SPR_KNIG, 0, 10, A_Look, S_KNIGHT_STND2, 0, 0}, // S_KNIGHT_STND1 {SPR_KNIG, 1, 10, A_Look, S_KNIGHT_STND1, 0, 0}, // S_KNIGHT_STND2 {SPR_KNIG, 0, 4, A_Chase, S_KNIGHT_WALK2, 0, 0}, // S_KNIGHT_WALK1 {SPR_KNIG, 1, 4, A_Chase, S_KNIGHT_WALK3, 0, 0}, // S_KNIGHT_WALK2 {SPR_KNIG, 2, 4, A_Chase, S_KNIGHT_WALK4, 0, 0}, // S_KNIGHT_WALK3 {SPR_KNIG, 3, 4, A_Chase, S_KNIGHT_WALK1, 0, 0}, // S_KNIGHT_WALK4 {SPR_KNIG, 4, 10, A_FaceTarget, S_KNIGHT_ATK2, 0, 0}, // S_KNIGHT_ATK1 {SPR_KNIG, 5, 8, A_FaceTarget, S_KNIGHT_ATK3, 0, 0}, // S_KNIGHT_ATK2 {SPR_KNIG, 6, 8, A_KnightAttack, S_KNIGHT_ATK4, 0, 0}, // S_KNIGHT_ATK3 {SPR_KNIG, 4, 10, A_FaceTarget, S_KNIGHT_ATK5, 0, 0}, // S_KNIGHT_ATK4 {SPR_KNIG, 5, 8, A_FaceTarget, S_KNIGHT_ATK6, 0, 0}, // S_KNIGHT_ATK5 {SPR_KNIG, 6, 8, A_KnightAttack, S_KNIGHT_WALK1, 0, 0}, // S_KNIGHT_ATK6 {SPR_KNIG, 7, 3, NULL, S_KNIGHT_PAIN2, 0, 0}, // S_KNIGHT_PAIN1 {SPR_KNIG, 7, 3, A_Pain, S_KNIGHT_WALK1, 0, 0}, // S_KNIGHT_PAIN2 {SPR_KNIG, 8, 6, NULL, S_KNIGHT_DIE2, 0, 0}, // S_KNIGHT_DIE1 {SPR_KNIG, 9, 6, A_Scream, S_KNIGHT_DIE3, 0, 0}, // S_KNIGHT_DIE2 {SPR_KNIG, 10, 6, NULL, S_KNIGHT_DIE4, 0, 0}, // S_KNIGHT_DIE3 {SPR_KNIG, 11, 6, A_NoBlocking, S_KNIGHT_DIE5, 0, 0}, // S_KNIGHT_DIE4 {SPR_KNIG, 12, 6, NULL, S_KNIGHT_DIE6, 0, 0}, // S_KNIGHT_DIE5 {SPR_KNIG, 13, 6, NULL, S_KNIGHT_DIE7, 0, 0}, // S_KNIGHT_DIE6 {SPR_KNIG, 14, -1, NULL, S_NULL, 0, 0}, // S_KNIGHT_DIE7 {SPR_SPAX, 32768, 3, A_ContMobjSound, S_SPINAXE2, 0, 0}, // S_SPINAXE1 {SPR_SPAX, 32769, 3, NULL, S_SPINAXE3, 0, 0}, // S_SPINAXE2 {SPR_SPAX, 32770, 3, NULL, S_SPINAXE1, 0, 0}, // S_SPINAXE3 {SPR_SPAX, 32771, 6, NULL, S_SPINAXEX2, 0, 0}, // S_SPINAXEX1 {SPR_SPAX, 32772, 6, NULL, S_SPINAXEX3, 0, 0}, // S_SPINAXEX2 {SPR_SPAX, 32773, 6, NULL, S_NULL, 0, 0}, // S_SPINAXEX3 {SPR_RAXE, 32768, 5, A_DripBlood, S_REDAXE2, 0, 0}, // S_REDAXE1 {SPR_RAXE, 32769, 5, A_DripBlood, S_REDAXE1, 0, 0}, // S_REDAXE2 {SPR_RAXE, 32770, 6, NULL, S_REDAXEX2, 0, 0}, // S_REDAXEX1 {SPR_RAXE, 32771, 6, NULL, S_REDAXEX3, 0, 0}, // S_REDAXEX2 {SPR_RAXE, 32772, 6, NULL, S_NULL, 0, 0}, // S_REDAXEX3 {SPR_SRCR, 0, 10, A_Look, S_SRCR1_LOOK2, 0, 0}, // S_SRCR1_LOOK1 {SPR_SRCR, 1, 10, A_Look, S_SRCR1_LOOK1, 0, 0}, // S_SRCR1_LOOK2 {SPR_SRCR, 0, 5, A_Sor1Chase, S_SRCR1_WALK2, 0, 0}, // S_SRCR1_WALK1 {SPR_SRCR, 1, 5, A_Sor1Chase, S_SRCR1_WALK3, 0, 0}, // S_SRCR1_WALK2 {SPR_SRCR, 2, 5, A_Sor1Chase, S_SRCR1_WALK4, 0, 0}, // S_SRCR1_WALK3 {SPR_SRCR, 3, 5, A_Sor1Chase, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_WALK4 {SPR_SRCR, 16, 6, A_Sor1Pain, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_PAIN1 {SPR_SRCR, 16, 7, A_FaceTarget, S_SRCR1_ATK2, 0, 0}, // S_SRCR1_ATK1 {SPR_SRCR, 17, 6, A_FaceTarget, S_SRCR1_ATK3, 0, 0}, // S_SRCR1_ATK2 {SPR_SRCR, 18, 10, A_Srcr1Attack, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_ATK3 {SPR_SRCR, 18, 10, A_FaceTarget, S_SRCR1_ATK5, 0, 0}, // S_SRCR1_ATK4 {SPR_SRCR, 16, 7, A_FaceTarget, S_SRCR1_ATK6, 0, 0}, // S_SRCR1_ATK5 {SPR_SRCR, 17, 6, A_FaceTarget, S_SRCR1_ATK7, 0, 0}, // S_SRCR1_ATK6 {SPR_SRCR, 18, 10, A_Srcr1Attack, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_ATK7 {SPR_SRCR, 4, 7, NULL, S_SRCR1_DIE2, 0, 0}, // S_SRCR1_DIE1 {SPR_SRCR, 5, 7, A_Scream, S_SRCR1_DIE3, 0, 0}, // S_SRCR1_DIE2 {SPR_SRCR, 6, 7, NULL, S_SRCR1_DIE4, 0, 0}, // S_SRCR1_DIE3 {SPR_SRCR, 7, 6, NULL, S_SRCR1_DIE5, 0, 0}, // S_SRCR1_DIE4 {SPR_SRCR, 8, 6, NULL, S_SRCR1_DIE6, 0, 0}, // S_SRCR1_DIE5 {SPR_SRCR, 9, 6, NULL, S_SRCR1_DIE7, 0, 0}, // S_SRCR1_DIE6 {SPR_SRCR, 10, 6, NULL, S_SRCR1_DIE8, 0, 0}, // S_SRCR1_DIE7 {SPR_SRCR, 11, 25, A_SorZap, S_SRCR1_DIE9, 0, 0}, // S_SRCR1_DIE8 {SPR_SRCR, 12, 5, NULL, S_SRCR1_DIE10, 0, 0}, // S_SRCR1_DIE9 {SPR_SRCR, 13, 5, NULL, S_SRCR1_DIE11, 0, 0}, // S_SRCR1_DIE10 {SPR_SRCR, 14, 4, NULL, S_SRCR1_DIE12, 0, 0}, // S_SRCR1_DIE11 {SPR_SRCR, 11, 20, A_SorZap, S_SRCR1_DIE13, 0, 0}, // S_SRCR1_DIE12 {SPR_SRCR, 12, 5, NULL, S_SRCR1_DIE14, 0, 0}, // S_SRCR1_DIE13 {SPR_SRCR, 13, 5, NULL, S_SRCR1_DIE15, 0, 0}, // S_SRCR1_DIE14 {SPR_SRCR, 14, 4, NULL, S_SRCR1_DIE16, 0, 0}, // S_SRCR1_DIE15 {SPR_SRCR, 11, 12, NULL, S_SRCR1_DIE17, 0, 0}, // S_SRCR1_DIE16 {SPR_SRCR, 15, -1, A_SorcererRise, S_NULL, 0, 0}, // S_SRCR1_DIE17 {SPR_FX14, 32768, 6, NULL, S_SRCRFX1_2, 0, 0}, // S_SRCRFX1_1 {SPR_FX14, 32769, 6, NULL, S_SRCRFX1_3, 0, 0}, // S_SRCRFX1_2 {SPR_FX14, 32770, 6, NULL, S_SRCRFX1_1, 0, 0}, // S_SRCRFX1_3 {SPR_FX14, 32771, 5, NULL, S_SRCRFXI1_2, 0, 0}, // S_SRCRFXI1_1 {SPR_FX14, 32772, 5, NULL, S_SRCRFXI1_3, 0, 0}, // S_SRCRFXI1_2 {SPR_FX14, 32773, 5, NULL, S_SRCRFXI1_4, 0, 0}, // S_SRCRFXI1_3 {SPR_FX14, 32774, 5, NULL, S_SRCRFXI1_5, 0, 0}, // S_SRCRFXI1_4 {SPR_FX14, 32775, 5, NULL, S_NULL, 0, 0}, // S_SRCRFXI1_5 {SPR_SOR2, 0, 4, NULL, S_SOR2_RISE2, 0, 0}, // S_SOR2_RISE1 {SPR_SOR2, 1, 4, NULL, S_SOR2_RISE3, 0, 0}, // S_SOR2_RISE2 {SPR_SOR2, 2, 4, A_SorRise, S_SOR2_RISE4, 0, 0}, // S_SOR2_RISE3 {SPR_SOR2, 3, 4, NULL, S_SOR2_RISE5, 0, 0}, // S_SOR2_RISE4 {SPR_SOR2, 4, 4, NULL, S_SOR2_RISE6, 0, 0}, // S_SOR2_RISE5 {SPR_SOR2, 5, 4, NULL, S_SOR2_RISE7, 0, 0}, // S_SOR2_RISE6 {SPR_SOR2, 6, 12, A_SorSightSnd, S_SOR2_WALK1, 0, 0}, // S_SOR2_RISE7 {SPR_SOR2, 12, 10, A_Look, S_SOR2_LOOK2, 0, 0}, // S_SOR2_LOOK1 {SPR_SOR2, 13, 10, A_Look, S_SOR2_LOOK1, 0, 0}, // S_SOR2_LOOK2 {SPR_SOR2, 12, 4, A_Chase, S_SOR2_WALK2, 0, 0}, // S_SOR2_WALK1 {SPR_SOR2, 13, 4, A_Chase, S_SOR2_WALK3, 0, 0}, // S_SOR2_WALK2 {SPR_SOR2, 14, 4, A_Chase, S_SOR2_WALK4, 0, 0}, // S_SOR2_WALK3 {SPR_SOR2, 15, 4, A_Chase, S_SOR2_WALK1, 0, 0}, // S_SOR2_WALK4 {SPR_SOR2, 16, 3, NULL, S_SOR2_PAIN2, 0, 0}, // S_SOR2_PAIN1 {SPR_SOR2, 16, 6, A_Pain, S_SOR2_WALK1, 0, 0}, // S_SOR2_PAIN2 {SPR_SOR2, 17, 9, A_Srcr2Decide, S_SOR2_ATK2, 0, 0}, // S_SOR2_ATK1 {SPR_SOR2, 18, 9, A_FaceTarget, S_SOR2_ATK3, 0, 0}, // S_SOR2_ATK2 {SPR_SOR2, 19, 20, A_Srcr2Attack, S_SOR2_WALK1, 0, 0}, // S_SOR2_ATK3 {SPR_SOR2, 11, 6, NULL, S_SOR2_TELE2, 0, 0}, // S_SOR2_TELE1 {SPR_SOR2, 10, 6, NULL, S_SOR2_TELE3, 0, 0}, // S_SOR2_TELE2 {SPR_SOR2, 9, 6, NULL, S_SOR2_TELE4, 0, 0}, // S_SOR2_TELE3 {SPR_SOR2, 8, 6, NULL, S_SOR2_TELE5, 0, 0}, // S_SOR2_TELE4 {SPR_SOR2, 7, 6, NULL, S_SOR2_TELE6, 0, 0}, // S_SOR2_TELE5 {SPR_SOR2, 6, 6, NULL, S_SOR2_WALK1, 0, 0}, // S_SOR2_TELE6 {SPR_SDTH, 0, 8, A_Sor2DthInit, S_SOR2_DIE2, 0, 0}, // S_SOR2_DIE1 {SPR_SDTH, 1, 8, NULL, S_SOR2_DIE3, 0, 0}, // S_SOR2_DIE2 {SPR_SDTH, 2, 8, A_SorDSph, S_SOR2_DIE4, 0, 0}, // S_SOR2_DIE3 {SPR_SDTH, 3, 7, NULL, S_SOR2_DIE5, 0, 0}, // S_SOR2_DIE4 {SPR_SDTH, 4, 7, NULL, S_SOR2_DIE6, 0, 0}, // S_SOR2_DIE5 {SPR_SDTH, 5, 7, A_Sor2DthLoop, S_SOR2_DIE7, 0, 0}, // S_SOR2_DIE6 {SPR_SDTH, 6, 6, A_SorDExp, S_SOR2_DIE8, 0, 0}, // S_SOR2_DIE7 {SPR_SDTH, 7, 6, NULL, S_SOR2_DIE9, 0, 0}, // S_SOR2_DIE8 {SPR_SDTH, 8, 18, NULL, S_SOR2_DIE10, 0, 0}, // S_SOR2_DIE9 {SPR_SDTH, 9, 6, A_NoBlocking, S_SOR2_DIE11, 0, 0}, // S_SOR2_DIE10 {SPR_SDTH, 10, 6, A_SorDBon, S_SOR2_DIE12, 0, 0}, // S_SOR2_DIE11 {SPR_SDTH, 11, 6, NULL, S_SOR2_DIE13, 0, 0}, // S_SOR2_DIE12 {SPR_SDTH, 12, 6, NULL, S_SOR2_DIE14, 0, 0}, // S_SOR2_DIE13 {SPR_SDTH, 13, 6, NULL, S_SOR2_DIE15, 0, 0}, // S_SOR2_DIE14 {SPR_SDTH, 14, -1, A_BossDeath, S_NULL, 0, 0}, // S_SOR2_DIE15 {SPR_FX16, 32768, 3, A_BlueSpark, S_SOR2FX1_2, 0, 0}, // S_SOR2FX1_1 {SPR_FX16, 32769, 3, A_BlueSpark, S_SOR2FX1_3, 0, 0}, // S_SOR2FX1_2 {SPR_FX16, 32770, 3, A_BlueSpark, S_SOR2FX1_1, 0, 0}, // S_SOR2FX1_3 {SPR_FX16, 32774, 5, A_Explode, S_SOR2FXI1_2, 0, 0}, // S_SOR2FXI1_1 {SPR_FX16, 32775, 5, NULL, S_SOR2FXI1_3, 0, 0}, // S_SOR2FXI1_2 {SPR_FX16, 32776, 5, NULL, S_SOR2FXI1_4, 0, 0}, // S_SOR2FXI1_3 {SPR_FX16, 32777, 5, NULL, S_SOR2FXI1_5, 0, 0}, // S_SOR2FXI1_4 {SPR_FX16, 32778, 5, NULL, S_SOR2FXI1_6, 0, 0}, // S_SOR2FXI1_5 {SPR_FX16, 32779, 5, NULL, S_NULL, 0, 0}, // S_SOR2FXI1_6 {SPR_FX16, 32771, 12, NULL, S_SOR2FXSPARK2, 0, 0}, // S_SOR2FXSPARK1 {SPR_FX16, 32772, 12, NULL, S_SOR2FXSPARK3, 0, 0}, // S_SOR2FXSPARK2 {SPR_FX16, 32773, 12, NULL, S_NULL, 0, 0}, // S_SOR2FXSPARK3 {SPR_FX11, 32768, 35, NULL, S_SOR2FX2_2, 0, 0}, // S_SOR2FX2_1 {SPR_FX11, 32768, 5, A_GenWizard, S_SOR2FX2_3, 0, 0}, // S_SOR2FX2_2 {SPR_FX11, 32769, 5, NULL, S_SOR2FX2_2, 0, 0}, // S_SOR2FX2_3 {SPR_FX11, 32770, 5, NULL, S_SOR2FXI2_2, 0, 0}, // S_SOR2FXI2_1 {SPR_FX11, 32771, 5, NULL, S_SOR2FXI2_3, 0, 0}, // S_SOR2FXI2_2 {SPR_FX11, 32772, 5, NULL, S_SOR2FXI2_4, 0, 0}, // S_SOR2FXI2_3 {SPR_FX11, 32773, 5, NULL, S_SOR2FXI2_5, 0, 0}, // S_SOR2FXI2_4 {SPR_FX11, 32774, 5, NULL, S_NULL, 0, 0}, // S_SOR2FXI2_5 {SPR_SOR2, 6, 8, NULL, S_SOR2TELEFADE2, 0, 0}, // S_SOR2TELEFADE1 {SPR_SOR2, 7, 6, NULL, S_SOR2TELEFADE3, 0, 0}, // S_SOR2TELEFADE2 {SPR_SOR2, 8, 6, NULL, S_SOR2TELEFADE4, 0, 0}, // S_SOR2TELEFADE3 {SPR_SOR2, 9, 6, NULL, S_SOR2TELEFADE5, 0, 0}, // S_SOR2TELEFADE4 {SPR_SOR2, 10, 6, NULL, S_SOR2TELEFADE6, 0, 0}, // S_SOR2TELEFADE5 {SPR_SOR2, 11, 6, NULL, S_NULL, 0, 0}, // S_SOR2TELEFADE6 {SPR_MNTR, 0, 10, A_Look, S_MNTR_LOOK2, 0, 0}, // S_MNTR_LOOK1 {SPR_MNTR, 1, 10, A_Look, S_MNTR_LOOK1, 0, 0}, // S_MNTR_LOOK2 {SPR_MNTR, 0, 5, A_Chase, S_MNTR_WALK2, 0, 0}, // S_MNTR_WALK1 {SPR_MNTR, 1, 5, A_Chase, S_MNTR_WALK3, 0, 0}, // S_MNTR_WALK2 {SPR_MNTR, 2, 5, A_Chase, S_MNTR_WALK4, 0, 0}, // S_MNTR_WALK3 {SPR_MNTR, 3, 5, A_Chase, S_MNTR_WALK1, 0, 0}, // S_MNTR_WALK4 {SPR_MNTR, 21, 10, A_FaceTarget, S_MNTR_ATK1_2, 0, 0}, // S_MNTR_ATK1_1 {SPR_MNTR, 22, 7, A_FaceTarget, S_MNTR_ATK1_3, 0, 0}, // S_MNTR_ATK1_2 {SPR_MNTR, 23, 12, A_MinotaurAtk1, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK1_3 {SPR_MNTR, 21, 10, A_MinotaurDecide, S_MNTR_ATK2_2, 0, 0}, // S_MNTR_ATK2_1 {SPR_MNTR, 24, 4, A_FaceTarget, S_MNTR_ATK2_3, 0, 0}, // S_MNTR_ATK2_2 {SPR_MNTR, 25, 9, A_MinotaurAtk2, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK2_3 {SPR_MNTR, 21, 10, A_FaceTarget, S_MNTR_ATK3_2, 0, 0}, // S_MNTR_ATK3_1 {SPR_MNTR, 22, 7, A_FaceTarget, S_MNTR_ATK3_3, 0, 0}, // S_MNTR_ATK3_2 {SPR_MNTR, 23, 12, A_MinotaurAtk3, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK3_3 {SPR_MNTR, 23, 12, NULL, S_MNTR_ATK3_1, 0, 0}, // S_MNTR_ATK3_4 {SPR_MNTR, 20, 2, A_MinotaurCharge, S_MNTR_ATK4_1, 0, 0}, // S_MNTR_ATK4_1 {SPR_MNTR, 4, 3, NULL, S_MNTR_PAIN2, 0, 0}, // S_MNTR_PAIN1 {SPR_MNTR, 4, 6, A_Pain, S_MNTR_WALK1, 0, 0}, // S_MNTR_PAIN2 {SPR_MNTR, 5, 6, NULL, S_MNTR_DIE2, 0, 0}, // S_MNTR_DIE1 {SPR_MNTR, 6, 5, NULL, S_MNTR_DIE3, 0, 0}, // S_MNTR_DIE2 {SPR_MNTR, 7, 6, A_Scream, S_MNTR_DIE4, 0, 0}, // S_MNTR_DIE3 {SPR_MNTR, 8, 5, NULL, S_MNTR_DIE5, 0, 0}, // S_MNTR_DIE4 {SPR_MNTR, 9, 6, NULL, S_MNTR_DIE6, 0, 0}, // S_MNTR_DIE5 {SPR_MNTR, 10, 5, NULL, S_MNTR_DIE7, 0, 0}, // S_MNTR_DIE6 {SPR_MNTR, 11, 6, NULL, S_MNTR_DIE8, 0, 0}, // S_MNTR_DIE7 {SPR_MNTR, 12, 5, A_NoBlocking, S_MNTR_DIE9, 0, 0}, // S_MNTR_DIE8 {SPR_MNTR, 13, 6, NULL, S_MNTR_DIE10, 0, 0}, // S_MNTR_DIE9 {SPR_MNTR, 14, 5, NULL, S_MNTR_DIE11, 0, 0}, // S_MNTR_DIE10 {SPR_MNTR, 15, 6, NULL, S_MNTR_DIE12, 0, 0}, // S_MNTR_DIE11 {SPR_MNTR, 16, 5, NULL, S_MNTR_DIE13, 0, 0}, // S_MNTR_DIE12 {SPR_MNTR, 17, 6, NULL, S_MNTR_DIE14, 0, 0}, // S_MNTR_DIE13 {SPR_MNTR, 18, 5, NULL, S_MNTR_DIE15, 0, 0}, // S_MNTR_DIE14 {SPR_MNTR, 19, -1, A_BossDeath, S_NULL, 0, 0}, // S_MNTR_DIE15 {SPR_FX12, 32768, 6, NULL, S_MNTRFX1_2, 0, 0}, // S_MNTRFX1_1 {SPR_FX12, 32769, 6, NULL, S_MNTRFX1_1, 0, 0}, // S_MNTRFX1_2 {SPR_FX12, 32770, 5, NULL, S_MNTRFXI1_2, 0, 0}, // S_MNTRFXI1_1 {SPR_FX12, 32771, 5, NULL, S_MNTRFXI1_3, 0, 0}, // S_MNTRFXI1_2 {SPR_FX12, 32772, 5, NULL, S_MNTRFXI1_4, 0, 0}, // S_MNTRFXI1_3 {SPR_FX12, 32773, 5, NULL, S_MNTRFXI1_5, 0, 0}, // S_MNTRFXI1_4 {SPR_FX12, 32774, 5, NULL, S_MNTRFXI1_6, 0, 0}, // S_MNTRFXI1_5 {SPR_FX12, 32775, 5, NULL, S_NULL, 0, 0}, // S_MNTRFXI1_6 {SPR_FX13, 0, 2, A_MntrFloorFire, S_MNTRFX2_1, 0, 0}, // S_MNTRFX2_1 {SPR_FX13, 32776, 4, A_Explode, S_MNTRFXI2_2, 0, 0}, // S_MNTRFXI2_1 {SPR_FX13, 32777, 4, NULL, S_MNTRFXI2_3, 0, 0}, // S_MNTRFXI2_2 {SPR_FX13, 32778, 4, NULL, S_MNTRFXI2_4, 0, 0}, // S_MNTRFXI2_3 {SPR_FX13, 32779, 4, NULL, S_MNTRFXI2_5, 0, 0}, // S_MNTRFXI2_4 {SPR_FX13, 32780, 4, NULL, S_NULL, 0, 0}, // S_MNTRFXI2_5 {SPR_FX13, 32771, 4, NULL, S_MNTRFX3_2, 0, 0}, // S_MNTRFX3_1 {SPR_FX13, 32770, 4, NULL, S_MNTRFX3_3, 0, 0}, // S_MNTRFX3_2 {SPR_FX13, 32769, 5, NULL, S_MNTRFX3_4, 0, 0}, // S_MNTRFX3_3 {SPR_FX13, 32770, 5, NULL, S_MNTRFX3_5, 0, 0}, // S_MNTRFX3_4 {SPR_FX13, 32771, 5, NULL, S_MNTRFX3_6, 0, 0}, // S_MNTRFX3_5 {SPR_FX13, 32772, 5, NULL, S_MNTRFX3_7, 0, 0}, // S_MNTRFX3_6 {SPR_FX13, 32773, 4, NULL, S_MNTRFX3_8, 0, 0}, // S_MNTRFX3_7 {SPR_FX13, 32774, 4, NULL, S_MNTRFX3_9, 0, 0}, // S_MNTRFX3_8 {SPR_FX13, 32775, 4, NULL, S_NULL, 0, 0}, // S_MNTRFX3_9 {SPR_AKYY, 32768, 3, NULL, S_AKYY2, 0, 0}, // S_AKYY1 {SPR_AKYY, 32769, 3, NULL, S_AKYY3, 0, 0}, // S_AKYY2 {SPR_AKYY, 32770, 3, NULL, S_AKYY4, 0, 0}, // S_AKYY3 {SPR_AKYY, 32771, 3, NULL, S_AKYY5, 0, 0}, // S_AKYY4 {SPR_AKYY, 32772, 3, NULL, S_AKYY6, 0, 0}, // S_AKYY5 {SPR_AKYY, 32773, 3, NULL, S_AKYY7, 0, 0}, // S_AKYY6 {SPR_AKYY, 32774, 3, NULL, S_AKYY8, 0, 0}, // S_AKYY7 {SPR_AKYY, 32775, 3, NULL, S_AKYY9, 0, 0}, // S_AKYY8 {SPR_AKYY, 32776, 3, NULL, S_AKYY10, 0, 0}, // S_AKYY9 {SPR_AKYY, 32777, 3, NULL, S_AKYY1, 0, 0}, // S_AKYY10 {SPR_BKYY, 32768, 3, NULL, S_BKYY2, 0, 0}, // S_BKYY1 {SPR_BKYY, 32769, 3, NULL, S_BKYY3, 0, 0}, // S_BKYY2 {SPR_BKYY, 32770, 3, NULL, S_BKYY4, 0, 0}, // S_BKYY3 {SPR_BKYY, 32771, 3, NULL, S_BKYY5, 0, 0}, // S_BKYY4 {SPR_BKYY, 32772, 3, NULL, S_BKYY6, 0, 0}, // S_BKYY5 {SPR_BKYY, 32773, 3, NULL, S_BKYY7, 0, 0}, // S_BKYY6 {SPR_BKYY, 32774, 3, NULL, S_BKYY8, 0, 0}, // S_BKYY7 {SPR_BKYY, 32775, 3, NULL, S_BKYY9, 0, 0}, // S_BKYY8 {SPR_BKYY, 32776, 3, NULL, S_BKYY10, 0, 0}, // S_BKYY9 {SPR_BKYY, 32777, 3, NULL, S_BKYY1, 0, 0}, // S_BKYY10 {SPR_CKYY, 32768, 3, NULL, S_CKYY2, 0, 0}, // S_CKYY1 {SPR_CKYY, 32769, 3, NULL, S_CKYY3, 0, 0}, // S_CKYY2 {SPR_CKYY, 32770, 3, NULL, S_CKYY4, 0, 0}, // S_CKYY3 {SPR_CKYY, 32771, 3, NULL, S_CKYY5, 0, 0}, // S_CKYY4 {SPR_CKYY, 32772, 3, NULL, S_CKYY6, 0, 0}, // S_CKYY5 {SPR_CKYY, 32773, 3, NULL, S_CKYY7, 0, 0}, // S_CKYY6 {SPR_CKYY, 32774, 3, NULL, S_CKYY8, 0, 0}, // S_CKYY7 {SPR_CKYY, 32775, 3, NULL, S_CKYY9, 0, 0}, // S_CKYY8 {SPR_CKYY, 32776, 3, NULL, S_CKYY1, 0, 0}, // S_CKYY9 {SPR_AMG1, 0, -1, NULL, S_NULL, 0, 0}, // S_AMG1 {SPR_AMG2, 0, 4, NULL, S_AMG2_2, 0, 0}, // S_AMG2_1 {SPR_AMG2, 1, 4, NULL, S_AMG2_3, 0, 0}, // S_AMG2_2 {SPR_AMG2, 2, 4, NULL, S_AMG2_1, 0, 0}, // S_AMG2_3 {SPR_AMM1, 0, -1, NULL, S_NULL, 0, 0}, // S_AMM1 {SPR_AMM2, 0, -1, NULL, S_NULL, 0, 0}, // S_AMM2 {SPR_AMC1, 0, -1, NULL, S_NULL, 0, 0}, // S_AMC1 {SPR_AMC2, 0, 5, NULL, S_AMC2_2, 0, 0}, // S_AMC2_1 {SPR_AMC2, 1, 5, NULL, S_AMC2_3, 0, 0}, // S_AMC2_2 {SPR_AMC2, 2, 5, NULL, S_AMC2_1, 0, 0}, // S_AMC2_3 {SPR_AMS1, 0, 5, NULL, S_AMS1_2, 0, 0}, // S_AMS1_1 {SPR_AMS1, 1, 5, NULL, S_AMS1_1, 0, 0}, // S_AMS1_2 {SPR_AMS2, 0, 5, NULL, S_AMS2_2, 0, 0}, // S_AMS2_1 {SPR_AMS2, 1, 5, NULL, S_AMS2_1, 0, 0}, // S_AMS2_2 {SPR_AMP1, 0, 4, NULL, S_AMP1_2, 0, 0}, // S_AMP1_1 {SPR_AMP1, 1, 4, NULL, S_AMP1_3, 0, 0}, // S_AMP1_2 {SPR_AMP1, 2, 4, NULL, S_AMP1_1, 0, 0}, // S_AMP1_3 {SPR_AMP2, 0, 4, NULL, S_AMP2_2, 0, 0}, // S_AMP2_1 {SPR_AMP2, 1, 4, NULL, S_AMP2_3, 0, 0}, // S_AMP2_2 {SPR_AMP2, 2, 4, NULL, S_AMP2_1, 0, 0}, // S_AMP2_3 {SPR_AMB1, 0, 4, NULL, S_AMB1_2, 0, 0}, // S_AMB1_1 {SPR_AMB1, 1, 4, NULL, S_AMB1_3, 0, 0}, // S_AMB1_2 {SPR_AMB1, 2, 4, NULL, S_AMB1_1, 0, 0}, // S_AMB1_3 {SPR_AMB2, 0, 4, NULL, S_AMB2_2, 0, 0}, // S_AMB2_1 {SPR_AMB2, 1, 4, NULL, S_AMB2_3, 0, 0}, // S_AMB2_2 {SPR_AMB2, 2, 4, NULL, S_AMB2_1, 0, 0}, // S_AMB2_3 {SPR_AMG1, 0, 100, A_ESound, S_SND_WIND, 0, 0}, // S_SND_WIND {SPR_AMG1, 0, 85, A_ESound, S_SND_WATERFALL, 0, 0} // S_SND_WATERFALL }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = { { // MT_MISC0 81, // doomednum S_ITEM_PTN1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ITEMSHIELD1 85, // doomednum S_ITEM_SHLD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ITEMSHIELD2 31, // doomednum S_ITEM_SHD2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_MISC1 8, // doomednum S_ITEM_BAGH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_MISC2 35, // doomednum S_ITEM_SPMP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTIINVISIBILITY 75, // doomednum S_ARTI_INVS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW | MF_COUNTITEM, // flags MF2_FLOATBOB // flags2 }, { // MT_MISC3 82, // doomednum S_ARTI_PTN2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTIFLY 83, // doomednum S_ARTI_SOAR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTIINVULNERABILITY 84, // doomednum S_ARTI_INVU1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTITOMEOFPOWER 86, // doomednum S_ARTI_PWBK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTIEGG 30, // doomednum S_ARTI_EGGC1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_EGGFX -1, // doomednum S_EGGFX1, // 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_NULL, // crashstate S_EGGFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 18 * FRACUNIT, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_ARTISUPERHEAL 32, // doomednum S_ARTI_SPHL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_MISC4 33, // doomednum S_ARTI_TRCH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_MISC5 34, // doomednum S_ARTI_FBMB1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_FIREBOMB -1, // doomednum S_FIREBOMB1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_phohit, // deathsound 0, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOGRAVITY | MF_SHADOW, // flags 0 // flags2 }, { // MT_ARTITELEPORT 36, // doomednum S_ARTI_ATLP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_POD 2035, // doomednum S_POD_WAIT1, // spawnstate 45, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_POD_PAIN1, // painstate 255, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_POD_DIE1, // deathstate S_NULL, // xdeathstate sfx_podexp, // deathsound 0, // speed 16 * FRACUNIT, // radius 54 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID | MF_NOBLOOD | MF_SHOOTABLE | MF_DROPOFF, // flags MF2_WINDTHRUST | MF2_PUSHABLE | MF2_SLIDE | MF2_PASSMOBJ | MF2_TELESTOMP // flags2 }, { // MT_PODGOO -1, // doomednum S_PODGOO1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_PODGOOX, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2 }, { // MT_PODGENERATOR 43, // doomednum S_PODGENERATOR, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_SPLASH -1, // doomednum S_SPLASH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SPLASHX, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2 }, { // MT_SPLASHBASE -1, // doomednum S_SPLASHBASE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_LAVASPLASH -1, // doomednum S_LAVASPLASH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_LAVASMOKE -1, // doomednum S_LAVASMOKE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_SLUDGECHUNK -1, // doomednum S_SLUDGECHUNK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SLUDGECHUNKX, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2 }, { // MT_SLUDGESPLASH -1, // doomednum S_SLUDGESPLASH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_SKULLHANG70 17, // doomednum S_SKULLHANG70_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20 * FRACUNIT, // radius 70 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_SKULLHANG60 24, // doomednum S_SKULLHANG60_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20 * FRACUNIT, // radius 60 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_SKULLHANG45 25, // doomednum S_SKULLHANG45_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20 * FRACUNIT, // radius 45 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_SKULLHANG35 26, // doomednum S_SKULLHANG35_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20 * FRACUNIT, // radius 35 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_CHANDELIER 28, // doomednum S_CHANDELIER1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20 * FRACUNIT, // radius 60 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_SERPTORCH 27, // doomednum S_SERPTORCH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 12 * FRACUNIT, // radius 54 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_SMALLPILLAR 29, // doomednum S_SMALLPILLAR, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 16 * FRACUNIT, // radius 34 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_STALAGMITESMALL 37, // doomednum S_STALAGMITESMALL, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 8 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_STALAGMITELARGE 38, // doomednum S_STALAGMITELARGE, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 12 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_STALACTITESMALL 39, // doomednum S_STALACTITESMALL, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 8 * FRACUNIT, // radius 36 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_STALACTITELARGE 40, // doomednum S_STALACTITELARGE, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 12 * FRACUNIT, // radius 68 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC6 76, // doomednum S_FIREBRAZIER1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 16 * FRACUNIT, // radius 44 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_BARREL 44, // doomednum S_BARREL, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 12 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC7 47, // doomednum S_BRPILLAR, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 14 * FRACUNIT, // radius 128 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC8 48, // doomednum S_MOSS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20 * FRACUNIT, // radius 23 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC9 49, // doomednum S_MOSS2, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 20 * FRACUNIT, // radius 27 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC10 50, // doomednum S_WALLTORCH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC11 51, // doomednum S_HANGINGCORPSE, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 8 * FRACUNIT, // radius 104 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_KEYGIZMOBLUE 94, // doomednum S_KEYGIZMO1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 16 * FRACUNIT, // radius 50 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_KEYGIZMOGREEN 95, // doomednum S_KEYGIZMO1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 16 * FRACUNIT, // radius 50 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_KEYGIZMOYELLOW 96, // doomednum S_KEYGIZMO1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 16 * FRACUNIT, // radius 50 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_KEYGIZMOFLOAT -1, // doomednum S_KGZ_START, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC12 87, // doomednum S_VOLCANO1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 12 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_VOLCANOBLAST -1, // doomednum S_VOLCANOBALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_VOLCANOBALLX1, // deathstate S_NULL, // xdeathstate sfx_volhit, // deathsound 2 * FRACUNIT, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_LOGRAV | MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_VOLCANOTBLAST -1, // doomednum S_VOLCANOTBALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_VOLCANOTBALLX1, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 2 * FRACUNIT, // speed 8 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_LOGRAV | MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_TELEGLITGEN 74, // doomednum S_TELEGLITGEN1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOSECTOR, // flags 0 // flags2 }, { // MT_TELEGLITGEN2 52, // doomednum S_TELEGLITGEN2, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOSECTOR, // flags 0 // flags2 }, { // MT_TELEGLITTER -1, // doomednum S_TELEGLITTER1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_MISSILE, // flags 0 // flags2 }, { // MT_TELEGLITTER2 -1, // doomednum S_TELEGLITTER2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_MISSILE, // flags 0 // flags2 }, { // MT_TFOG -1, // doomednum S_TFOG1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // 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, // crashstate 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 0 // flags2 }, { // MT_STAFFPUFF -1, // doomednum S_STAFFPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_stfhit, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_STAFFPUFF2 -1, // doomednum S_STAFFPUFF2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_stfpow, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_BEAKPUFF -1, // doomednum S_STAFFPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_chicatk, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC13 2005, // doomednum S_WGNT, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_GAUNTLETPUFF1 -1, // doomednum S_GAUNTLETPUFF1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_GAUNTLETPUFF2 -1, // doomednum S_GAUNTLETPUFF2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_MISC14 53, // doomednum S_BLSR, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_BLASTERFX1 -1, // doomednum S_BLASTERFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_BLASTERFXI1_1, // deathstate S_NULL, // xdeathstate sfx_blshit, // deathsound 184 * FRACUNIT, // speed 12 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_BLASTERSMOKE -1, // doomednum S_BLASTERSMOKE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2 }, { // MT_RIPPER -1, // doomednum S_RIPPER1, // 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_NULL, // crashstate S_RIPPERX1, // deathstate S_NULL, // xdeathstate sfx_hrnhit, // deathsound 14 * FRACUNIT, // speed 8 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_RIP // flags2 }, { // MT_BLASTERPUFF1 -1, // doomednum S_BLASTERPUFF1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_BLASTERPUFF2 -1, // doomednum S_BLASTERPUFF2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_WMACE 2002, // doomednum S_WMCE, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MACEFX1 -1, // doomednum S_MACEFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_lobsht, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_MACEFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 20 * FRACUNIT, // speed 8 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_NOTELEPORT // flags2 }, { // MT_MACEFX2 -1, // doomednum S_MACEFX2_1, // 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_NULL, // crashstate S_MACEFXI2_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 8 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 6, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_LOGRAV | MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_NOTELEPORT // flags2 }, { // MT_MACEFX3 -1, // doomednum S_MACEFX3_1, // 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_NULL, // crashstate S_MACEFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 7 * FRACUNIT, // speed 8 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 4, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_LOGRAV | MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_NOTELEPORT // flags2 }, { // MT_MACEFX4 -1, // doomednum S_MACEFX4_1, // 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_NULL, // crashstate S_MACEFXI4_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 7 * FRACUNIT, // speed 8 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 18, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_LOGRAV | MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_TELESTOMP // flags2 }, { // MT_WSKULLROD 2004, // doomednum S_WSKL, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_HORNRODFX1 -1, // doomednum S_HRODFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_hrnsht, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_HRODFXI1_1, // deathstate S_NULL, // xdeathstate sfx_hrnhit, // deathsound 22 * FRACUNIT, // speed 12 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 3, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_WINDTHRUST | MF2_NOTELEPORT // flags2 }, { // MT_HORNRODFX2 -1, // doomednum S_HRODFX2_1, // spawnstate 4 * 35, // spawnhealth S_NULL, // seestate sfx_hrnsht, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_HRODFXI2_1, // deathstate S_NULL, // xdeathstate sfx_ramphit, // deathsound 22 * FRACUNIT, // speed 12 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 10, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_RAINPLR1 -1, // doomednum S_RAINPLR1_1, // 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_NULL, // crashstate S_RAINPLR1X_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 12 * FRACUNIT, // speed 5 * FRACUNIT, // radius 12 * FRACUNIT, // height 100, // mass 5, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_RAINPLR2 -1, // doomednum S_RAINPLR2_1, // 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_NULL, // crashstate S_RAINPLR2X_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 12 * FRACUNIT, // speed 5 * FRACUNIT, // radius 12 * FRACUNIT, // height 100, // mass 5, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_RAINPLR3 -1, // doomednum S_RAINPLR3_1, // 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_NULL, // crashstate S_RAINPLR3X_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 12 * FRACUNIT, // speed 5 * FRACUNIT, // radius 12 * FRACUNIT, // height 100, // mass 5, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_RAINPLR4 -1, // doomednum S_RAINPLR4_1, // 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_NULL, // crashstate S_RAINPLR4X_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 12 * FRACUNIT, // speed 5 * FRACUNIT, // radius 12 * FRACUNIT, // height 100, // mass 5, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_GOLDWANDFX1 -1, // doomednum S_GWANDFX1_1, // 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_NULL, // crashstate S_GWANDFXI1_1, // deathstate S_NULL, // xdeathstate sfx_gldhit, // deathsound 22 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_GOLDWANDFX2 -1, // doomednum S_GWANDFX2_1, // 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_NULL, // crashstate S_GWANDFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 18 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_GOLDWANDPUFF1 -1, // doomednum S_GWANDPUFF1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_GOLDWANDPUFF2 -1, // doomednum S_GWANDFXI1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_WPHOENIXROD 2003, // doomednum S_WPHX, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_PHOENIXFX1 -1, // doomednum S_PHOENIXFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_phosht, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_PHOENIXFXI1_1, // deathstate S_NULL, // xdeathstate sfx_phohit, // 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 MF2_THRUGHOST | MF2_NOTELEPORT // flags2 }, // The following thing is present in the mobjinfo table from Heretic 1.0, // but not in Heretic 1.3 (ie. it was removed). It has been re-inserted // here to support HHE patches. { // MT_PHOENIXFX_REMOVED -1, // doomednum S_PHOENIXFXIX_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_PHOENIXFXIX_3, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_PHOENIXPUFF -1, // doomednum S_PHOENIXPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2 }, { // MT_PHOENIXFX2 -1, // doomednum S_PHOENIXFX2_1, // 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_NULL, // crashstate S_PHOENIXFXI2_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 6 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_MISC15 2001, // doomednum S_WBOW, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_CRBOWFX1 -1, // doomednum S_CRBOWFX1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_bowsht, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CRBOWFXI1_1, // deathstate S_NULL, // xdeathstate sfx_hrnhit, // deathsound 30 * FRACUNIT, // speed 11 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 10, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_CRBOWFX2 -1, // doomednum S_CRBOWFX2, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_bowsht, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CRBOWFXI1_1, // deathstate S_NULL, // xdeathstate sfx_hrnhit, // deathsound 32 * FRACUNIT, // speed 11 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 6, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_CRBOWFX3 -1, // doomednum S_CRBOWFX3, // 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_NULL, // crashstate S_CRBOWFXI3_1, // deathstate S_NULL, // xdeathstate sfx_hrnhit, // deathsound 20 * FRACUNIT, // speed 11 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_WINDTHRUST | MF2_THRUGHOST | MF2_NOTELEPORT // flags2 }, { // MT_CRBOWFX4 -1, // doomednum S_CRBOWFX4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_LOGRAV // flags2 }, { // 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, // crashstate 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 0 // flags2 }, { // MT_BLOODSPLATTER -1, // doomednum S_BLOODSPLATTER1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_BLOODSPLATTERX, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2 }, { // 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_plrpai, // painsound S_NULL, // meleestate S_PLAY_ATK1, // missilestate S_NULL, // crashstate S_PLAY_DIE1, // deathstate S_PLAY_XDIE1, // xdeathstate sfx_plrdth, // 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 MF2_WINDTHRUST | MF2_FOOTCLIP | MF2_SLIDE | MF2_PASSMOBJ | MF2_TELESTOMP // flags2 }, { // MT_BLOODYSKULL -1, // doomednum S_BLOODYSKULL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_LOGRAV | MF2_CANNOTPUSH // flags2 }, { // MT_CHICPLAYER -1, // doomednum S_CHICPLAY, // spawnstate 100, // spawnhealth S_CHICPLAY_RUN1, // seestate sfx_None, // seesound 0, // reactiontime sfx_None, // attacksound S_CHICPLAY_PAIN, // painstate 255, // painchance sfx_chicpai, // painsound S_NULL, // meleestate S_CHICPLAY_ATK1, // missilestate S_NULL, // crashstate S_CHICKEN_DIE1, // deathstate S_NULL, // xdeathstate sfx_chicdth, // deathsound 0, // speed 16 * FRACUNIT, // radius 24 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_NOTDMATCH, // flags MF2_WINDTHRUST | MF2_SLIDE | MF2_PASSMOBJ | MF2_FOOTCLIP | MF2_LOGRAV | MF2_TELESTOMP // flags2 }, { // MT_CHICKEN -1, // doomednum S_CHICKEN_LOOK1, // spawnstate 10, // spawnhealth S_CHICKEN_WALK1, // seestate sfx_chicpai, // seesound 8, // reactiontime sfx_chicatk, // attacksound S_CHICKEN_PAIN1, // painstate 200, // painchance sfx_chicpai, // painsound S_CHICKEN_ATK1, // meleestate 0, // missilestate S_NULL, // crashstate S_CHICKEN_DIE1, // deathstate S_NULL, // xdeathstate sfx_chicdth, // deathsound 4, // speed 9 * FRACUNIT, // radius 22 * FRACUNIT, // height 40, // mass 0, // damage sfx_chicact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF, // flags MF2_WINDTHRUST | MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_FEATHER -1, // doomednum S_FEATHER1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FEATHERX, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH | MF2_WINDTHRUST // flags2 }, { // MT_MUMMY 68, // doomednum S_MUMMY_LOOK1, // spawnstate 80, // spawnhealth S_MUMMY_WALK1, // seestate sfx_mumsit, // seesound 8, // reactiontime sfx_mumat1, // attacksound S_MUMMY_PAIN1, // painstate 128, // painchance sfx_mumpai, // painsound S_MUMMY_ATK1, // meleestate 0, // missilestate S_NULL, // crashstate S_MUMMY_DIE1, // deathstate S_NULL, // xdeathstate sfx_mumdth, // deathsound 12, // speed 22 * FRACUNIT, // radius 62 * FRACUNIT, // height 75, // mass 0, // damage sfx_mumact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_MUMMYLEADER 45, // doomednum S_MUMMY_LOOK1, // spawnstate 100, // spawnhealth S_MUMMY_WALK1, // seestate sfx_mumsit, // seesound 8, // reactiontime sfx_mumat1, // attacksound S_MUMMY_PAIN1, // painstate 64, // painchance sfx_mumpai, // painsound S_MUMMY_ATK1, // meleestate S_MUMMYL_ATK1, // missilestate S_NULL, // crashstate S_MUMMY_DIE1, // deathstate S_NULL, // xdeathstate sfx_mumdth, // deathsound 12, // speed 22 * FRACUNIT, // radius 62 * FRACUNIT, // height 75, // mass 0, // damage sfx_mumact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_MUMMYGHOST 69, // doomednum S_MUMMY_LOOK1, // spawnstate 80, // spawnhealth S_MUMMY_WALK1, // seestate sfx_mumsit, // seesound 8, // reactiontime sfx_mumat1, // attacksound S_MUMMY_PAIN1, // painstate 128, // painchance sfx_mumpai, // painsound S_MUMMY_ATK1, // meleestate 0, // missilestate S_NULL, // crashstate S_MUMMY_DIE1, // deathstate S_NULL, // xdeathstate sfx_mumdth, // deathsound 12, // speed 22 * FRACUNIT, // radius 62 * FRACUNIT, // height 75, // mass 0, // damage sfx_mumact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_SHADOW, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_MUMMYLEADERGHOST 46, // doomednum S_MUMMY_LOOK1, // spawnstate 100, // spawnhealth S_MUMMY_WALK1, // seestate sfx_mumsit, // seesound 8, // reactiontime sfx_mumat1, // attacksound S_MUMMY_PAIN1, // painstate 64, // painchance sfx_mumpai, // painsound S_MUMMY_ATK1, // meleestate S_MUMMYL_ATK1, // missilestate S_NULL, // crashstate S_MUMMY_DIE1, // deathstate S_NULL, // xdeathstate sfx_mumdth, // deathsound 12, // speed 22 * FRACUNIT, // radius 62 * FRACUNIT, // height 75, // mass 0, // damage sfx_mumact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_SHADOW, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_MUMMYSOUL -1, // doomednum S_MUMMY_SOUL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MUMMYFX1 -1, // doomednum S_MUMMYFX1_1, // 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_NULL, // crashstate S_MUMMYFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 9 * FRACUNIT, // speed 8 * FRACUNIT, // radius 14 * FRACUNIT, // height 100, // mass 4, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_BEAST 70, // doomednum S_BEAST_LOOK1, // spawnstate 220, // spawnhealth S_BEAST_WALK1, // seestate sfx_bstsit, // seesound 8, // reactiontime sfx_bstatk, // attacksound S_BEAST_PAIN1, // painstate 100, // painchance sfx_bstpai, // painsound 0, // meleestate S_BEAST_ATK1, // missilestate S_NULL, // crashstate S_BEAST_DIE1, // deathstate S_BEAST_XDIE1, // xdeathstate sfx_bstdth, // deathsound 14, // speed 32 * FRACUNIT, // radius 74 * FRACUNIT, // height 200, // mass 0, // damage sfx_bstact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_BEASTBALL -1, // doomednum S_BEASTBALL1, // 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_NULL, // crashstate S_BEASTBALLX1, // deathstate S_NULL, // xdeathstate 0, // deathsound 12 * FRACUNIT, // speed 9 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 4, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_WINDTHRUST | MF2_NOTELEPORT // flags2 }, { // MT_BURNBALL -1, // doomednum S_BURNBALL1, // 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_NULL, // crashstate S_BEASTBALLX1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 6 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_BURNBALLFB -1, // doomednum S_BURNBALLFB1, // 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_NULL, // crashstate S_BEASTBALLX1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 6 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_PUFFY -1, // doomednum S_PUFFY1, // 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_NULL, // crashstate S_PUFFY1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 6 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_SNAKE 92, // doomednum S_SNAKE_LOOK1, // spawnstate 280, // spawnhealth S_SNAKE_WALK1, // seestate sfx_snksit, // seesound 8, // reactiontime sfx_snkatk, // attacksound S_SNAKE_PAIN1, // painstate 48, // painchance sfx_snkpai, // painsound 0, // meleestate S_SNAKE_ATK1, // missilestate S_NULL, // crashstate S_SNAKE_DIE1, // deathstate S_NULL, // xdeathstate sfx_snkdth, // deathsound 10, // speed 22 * FRACUNIT, // radius 70 * FRACUNIT, // height 100, // mass 0, // damage sfx_snkact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_SNAKEPRO_A -1, // doomednum S_SNAKEPRO_A1, // 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_NULL, // crashstate S_SNAKEPRO_AX1, // deathstate S_NULL, // xdeathstate 0, // deathsound 14 * FRACUNIT, // speed 12 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_WINDTHRUST | MF2_NOTELEPORT // flags2 }, { // MT_SNAKEPRO_B -1, // doomednum S_SNAKEPRO_B1, // 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_NULL, // crashstate S_SNAKEPRO_BX1, // deathstate S_NULL, // xdeathstate 0, // deathsound 14 * FRACUNIT, // speed 12 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 3, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_HEAD 6, // doomednum S_HEAD_LOOK, // spawnstate 700, // spawnhealth S_HEAD_FLOAT, // seestate sfx_hedsit, // seesound 8, // reactiontime sfx_hedat1, // attacksound S_HEAD_PAIN1, // painstate 32, // painchance sfx_hedpai, // painsound 0, // meleestate S_HEAD_ATK1, // missilestate S_NULL, // crashstate S_HEAD_DIE1, // deathstate S_NULL, // xdeathstate sfx_heddth, // deathsound 6, // speed 40 * FRACUNIT, // radius 72 * FRACUNIT, // height 325, // mass 0, // damage sfx_hedact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_NOBLOOD, // flags MF2_PASSMOBJ // flags2 }, { // MT_HEADFX1 -1, // doomednum S_HEADFX1_1, // 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_NULL, // crashstate S_HEADFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 13 * FRACUNIT, // speed 12 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_THRUGHOST // flags2 }, { // MT_HEADFX2 -1, // doomednum S_HEADFX2_1, // 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_NULL, // crashstate S_HEADFXI2_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 8 * FRACUNIT, // speed 12 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 3, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_HEADFX3 -1, // doomednum S_HEADFX3_1, // 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_NULL, // crashstate S_HEADFXI3_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 14 * FRACUNIT, // radius 12 * FRACUNIT, // height 100, // mass 5, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_WINDTHRUST | MF2_NOTELEPORT // flags2 }, { // MT_WHIRLWIND -1, // doomednum S_HEADFX4_1, // 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_NULL, // crashstate S_HEADFXI4_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 16 * FRACUNIT, // radius 74 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_SHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_CLINK 90, // doomednum S_CLINK_LOOK1, // spawnstate 150, // spawnhealth S_CLINK_WALK1, // seestate sfx_clksit, // seesound 8, // reactiontime sfx_clkatk, // attacksound S_CLINK_PAIN1, // painstate 32, // painchance sfx_clkpai, // painsound S_CLINK_ATK1, // meleestate 0, // missilestate S_NULL, // crashstate S_CLINK_DIE1, // deathstate S_NULL, // xdeathstate sfx_clkdth, // deathsound 14, // speed 20 * FRACUNIT, // radius 64 * FRACUNIT, // height 75, // mass 0, // damage sfx_clkact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_NOBLOOD, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_WIZARD 15, // doomednum S_WIZARD_LOOK1, // spawnstate 180, // spawnhealth S_WIZARD_WALK1, // seestate sfx_wizsit, // seesound 8, // reactiontime sfx_wizatk, // attacksound S_WIZARD_PAIN1, // painstate 64, // painchance sfx_wizpai, // painsound 0, // meleestate S_WIZARD_ATK1, // missilestate S_NULL, // crashstate S_WIZARD_DIE1, // deathstate S_NULL, // xdeathstate sfx_wizdth, // deathsound 12, // speed 16 * FRACUNIT, // radius 68 * FRACUNIT, // height 100, // mass 0, // damage sfx_wizact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_FLOAT | MF_NOGRAVITY, // flags MF2_PASSMOBJ // flags2 }, { // MT_WIZFX1 -1, // doomednum S_WIZFX1_1, // 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_NULL, // crashstate S_WIZFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 18 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 3, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_IMP 66, // doomednum S_IMP_LOOK1, // spawnstate 40, // spawnhealth S_IMP_FLY1, // seestate sfx_impsit, // seesound 8, // reactiontime sfx_impat1, // attacksound S_IMP_PAIN1, // painstate 200, // painchance sfx_imppai, // painsound S_IMP_MEATK1, // meleestate S_IMP_MSATK1_1, // missilestate S_IMP_CRASH1, // crashstate S_IMP_DIE1, // deathstate S_IMP_XDIE1, // xdeathstate sfx_impdth, // deathsound 10, // speed 16 * FRACUNIT, // radius 36 * FRACUNIT, // height 50, // mass 0, // damage sfx_impact, // activesound MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags MF2_SPAWNFLOAT | MF2_PASSMOBJ // flags2 }, { // MT_IMPLEADER 5, // doomednum S_IMP_LOOK1, // spawnstate 80, // spawnhealth S_IMP_FLY1, // seestate sfx_impsit, // seesound 8, // reactiontime sfx_impat2, // attacksound S_IMP_PAIN1, // painstate 200, // painchance sfx_imppai, // painsound 0, // meleestate S_IMP_MSATK2_1, // missilestate S_IMP_CRASH1, // crashstate S_IMP_DIE1, // deathstate S_IMP_XDIE1, // xdeathstate sfx_impdth, // deathsound 10, // speed 16 * FRACUNIT, // radius 36 * FRACUNIT, // height 50, // mass 0, // damage sfx_impact, // activesound MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags MF2_SPAWNFLOAT | MF2_PASSMOBJ // flags2 }, { // MT_IMPCHUNK1 -1, // doomednum S_IMP_CHUNKA1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_IMPCHUNK2 -1, // doomednum S_IMP_CHUNKB1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_IMPBALL -1, // doomednum S_IMPFX1, // 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_NULL, // crashstate S_IMPFXI1, // deathstate S_NULL, // xdeathstate 0, // deathsound 10 * FRACUNIT, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_WINDTHRUST | MF2_NOTELEPORT // flags2 }, { // MT_KNIGHT 64, // doomednum S_KNIGHT_STND1, // spawnstate 200, // spawnhealth S_KNIGHT_WALK1, // seestate sfx_kgtsit, // seesound 8, // reactiontime sfx_kgtatk, // attacksound S_KNIGHT_PAIN1, // painstate 100, // painchance sfx_kgtpai, // painsound S_KNIGHT_ATK1, // meleestate S_KNIGHT_ATK1, // missilestate S_NULL, // crashstate S_KNIGHT_DIE1, // deathstate S_NULL, // xdeathstate sfx_kgtdth, // deathsound 12, // speed 24 * FRACUNIT, // radius 78 * FRACUNIT, // height 150, // mass 0, // damage sfx_kgtact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_KNIGHTGHOST 65, // doomednum S_KNIGHT_STND1, // spawnstate 200, // spawnhealth S_KNIGHT_WALK1, // seestate sfx_kgtsit, // seesound 8, // reactiontime sfx_kgtatk, // attacksound S_KNIGHT_PAIN1, // painstate 100, // painchance sfx_kgtpai, // painsound S_KNIGHT_ATK1, // meleestate S_KNIGHT_ATK1, // missilestate S_NULL, // crashstate S_KNIGHT_DIE1, // deathstate S_NULL, // xdeathstate sfx_kgtdth, // deathsound 12, // speed 24 * FRACUNIT, // radius 78 * FRACUNIT, // height 150, // mass 0, // damage sfx_kgtact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_SHADOW, // flags MF2_FOOTCLIP | MF2_PASSMOBJ // flags2 }, { // MT_KNIGHTAXE -1, // doomednum S_SPINAXE1, // 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_NULL, // crashstate S_SPINAXEX1, // deathstate S_NULL, // xdeathstate sfx_hrnhit, // deathsound 9 * FRACUNIT, // speed 10 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_WINDTHRUST | MF2_NOTELEPORT | MF2_THRUGHOST // flags2 }, { // MT_REDAXE -1, // doomednum S_REDAXE1, // 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_NULL, // crashstate S_REDAXEX1, // deathstate S_NULL, // xdeathstate sfx_hrnhit, // deathsound 9 * FRACUNIT, // speed 10 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 7, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_THRUGHOST // flags2 }, { // MT_SORCERER1 7, // doomednum S_SRCR1_LOOK1, // spawnstate 2000, // spawnhealth S_SRCR1_WALK1, // seestate sfx_sbtsit, // seesound 8, // reactiontime sfx_sbtatk, // attacksound S_SRCR1_PAIN1, // painstate 56, // painchance sfx_sbtpai, // painsound 0, // meleestate S_SRCR1_ATK1, // missilestate S_NULL, // crashstate S_SRCR1_DIE1, // deathstate S_NULL, // xdeathstate sfx_sbtdth, // deathsound 16, // speed 28 * FRACUNIT, // radius 100 * FRACUNIT, // height 800, // mass 0, // damage sfx_sbtact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FOOTCLIP | MF2_PASSMOBJ | MF2_BOSS // flags2 }, { // MT_SRCRFX1 -1, // doomednum S_SRCRFX1_1, // 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_NULL, // crashstate S_SRCRFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 20 * FRACUNIT, // speed 10 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 10, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_SORCERER2 -1, // doomednum S_SOR2_LOOK1, // spawnstate 3500, // spawnhealth S_SOR2_WALK1, // seestate sfx_sorsit, // seesound 8, // reactiontime sfx_soratk, // attacksound S_SOR2_PAIN1, // painstate 32, // painchance sfx_sorpai, // painsound 0, // meleestate S_SOR2_ATK1, // missilestate S_NULL, // crashstate S_SOR2_DIE1, // deathstate S_NULL, // xdeathstate 0, // deathsound 14, // speed 16 * FRACUNIT, // radius 70 * FRACUNIT, // height 300, // mass 0, // damage sfx_soract, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF, // flags MF2_FOOTCLIP | MF2_PASSMOBJ | MF2_BOSS // flags2 }, { // MT_SOR2FX1 -1, // doomednum S_SOR2FX1_1, // 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_NULL, // crashstate S_SOR2FXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 20 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 1, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_SOR2FXSPARK -1, // doomednum S_SOR2FXSPARK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2 }, { // MT_SOR2FX2 -1, // doomednum S_SOR2FX2_1, // 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_NULL, // crashstate S_SOR2FXI2_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 6 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 10, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_SOR2TELEFADE -1, // doomednum S_SOR2TELEFADE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MINOTAUR 9, // doomednum S_MNTR_LOOK1, // spawnstate 3000, // spawnhealth S_MNTR_WALK1, // seestate sfx_minsit, // seesound 8, // reactiontime sfx_minat1, // attacksound S_MNTR_PAIN1, // painstate 25, // painchance sfx_minpai, // painsound S_MNTR_ATK1_1, // meleestate S_MNTR_ATK2_1, // missilestate S_NULL, // crashstate S_MNTR_DIE1, // deathstate S_NULL, // xdeathstate sfx_mindth, // deathsound 16, // speed 28 * FRACUNIT, // radius 100 * FRACUNIT, // height 800, // mass 7, // damage sfx_minact, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF, // flags MF2_FOOTCLIP | MF2_PASSMOBJ | MF2_BOSS // flags2 }, { // MT_MNTRFX1 -1, // doomednum S_MNTRFX1_1, // 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_NULL, // crashstate S_MNTRFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 20 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 3, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_MNTRFX2 -1, // doomednum S_MNTRFX2_1, // 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_NULL, // crashstate S_MNTRFXI2_1, // deathstate S_NULL, // xdeathstate sfx_phohit, // deathsound 14 * FRACUNIT, // speed 5 * FRACUNIT, // radius 12 * FRACUNIT, // height 100, // mass 4, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_MNTRFX3 -1, // doomednum S_MNTRFX3_1, // 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_NULL, // crashstate S_MNTRFXI2_1, // deathstate S_NULL, // xdeathstate sfx_phohit, // deathsound 0, // speed 8 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 4, // damage sfx_None, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_AKYY 73, // doomednum S_AKYY1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_BKYY 79, // doomednum S_BKYY1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_CKEY 80, // doomednum S_CKYY1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMGWNDWIMPY 10, // doomednum S_AMG1, // spawnstate AMMO_GWND_WIMPY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMGWNDHEFTY 12, // doomednum S_AMG2_1, // spawnstate AMMO_GWND_HEFTY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMMACEWIMPY 13, // doomednum S_AMM1, // spawnstate AMMO_MACE_WIMPY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMMACEHEFTY 16, // doomednum S_AMM2, // spawnstate AMMO_MACE_HEFTY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMCBOWWIMPY 18, // doomednum S_AMC1, // spawnstate AMMO_CBOW_WIMPY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMCBOWHEFTY 19, // doomednum S_AMC2_1, // spawnstate AMMO_CBOW_HEFTY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMSKRDWIMPY 20, // doomednum S_AMS1_1, // spawnstate AMMO_SKRD_WIMPY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMSKRDHEFTY 21, // doomednum S_AMS2_1, // spawnstate AMMO_SKRD_HEFTY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMPHRDWIMPY 22, // doomednum S_AMP1_1, // spawnstate AMMO_PHRD_WIMPY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMPHRDHEFTY 23, // doomednum S_AMP2_1, // spawnstate AMMO_PHRD_HEFTY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMBLSRWIMPY 54, // doomednum S_AMB1_1, // spawnstate AMMO_BLSR_WIMPY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AMBLSRHEFTY 55, // doomednum S_AMB2_1, // spawnstate AMMO_BLSR_HEFTY, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_SOUNDWIND 42, // doomednum S_SND_WIND, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_SOUNDWATERFALL 41, // doomednum S_SND_WATERFALL, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 } }; crispy-doom-crispy-doom-5.6.4/src/heretic/info.h000066400000000000000000000677171360717211000215740ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef HERETIC_INFO_H #define HERETIC_INFO_H typedef enum { SPR_IMPX, SPR_ACLO, SPR_PTN1, SPR_SHLD, SPR_SHD2, SPR_BAGH, SPR_SPMP, SPR_INVS, SPR_PTN2, SPR_SOAR, SPR_INVU, SPR_PWBK, SPR_EGGC, SPR_EGGM, SPR_FX01, SPR_SPHL, SPR_TRCH, SPR_FBMB, SPR_XPL1, SPR_ATLP, SPR_PPOD, SPR_AMG1, SPR_SPSH, SPR_LVAS, SPR_SLDG, SPR_SKH1, SPR_SKH2, SPR_SKH3, SPR_SKH4, SPR_CHDL, SPR_SRTC, SPR_SMPL, SPR_STGS, SPR_STGL, SPR_STCS, SPR_STCL, SPR_KFR1, SPR_BARL, SPR_BRPL, SPR_MOS1, SPR_MOS2, SPR_WTRH, SPR_HCOR, SPR_KGZ1, SPR_KGZB, SPR_KGZG, SPR_KGZY, SPR_VLCO, SPR_VFBL, SPR_VTFB, SPR_SFFI, SPR_TGLT, SPR_TELE, SPR_STFF, SPR_PUF3, SPR_PUF4, SPR_BEAK, SPR_WGNT, SPR_GAUN, SPR_PUF1, SPR_WBLS, SPR_BLSR, SPR_FX18, SPR_FX17, SPR_WMCE, SPR_MACE, SPR_FX02, SPR_WSKL, SPR_HROD, SPR_FX00, SPR_FX20, SPR_FX21, SPR_FX22, SPR_FX23, SPR_GWND, SPR_PUF2, SPR_WPHX, SPR_PHNX, SPR_FX04, SPR_FX08, SPR_FX09, SPR_WBOW, SPR_CRBW, SPR_FX03, SPR_BLOD, SPR_PLAY, SPR_FDTH, SPR_BSKL, SPR_CHKN, SPR_MUMM, SPR_FX15, SPR_BEAS, SPR_FRB1, SPR_SNKE, SPR_SNFX, SPR_HEAD, SPR_FX05, SPR_FX06, SPR_FX07, SPR_CLNK, SPR_WZRD, SPR_FX11, SPR_FX10, SPR_KNIG, SPR_SPAX, SPR_RAXE, SPR_SRCR, SPR_FX14, SPR_SOR2, SPR_SDTH, SPR_FX16, SPR_MNTR, SPR_FX12, SPR_FX13, SPR_AKYY, SPR_BKYY, SPR_CKYY, SPR_AMG2, SPR_AMM1, SPR_AMM2, SPR_AMC1, SPR_AMC2, SPR_AMS1, SPR_AMS2, SPR_AMP1, SPR_AMP2, SPR_AMB1, SPR_AMB2, NUMSPRITES } spritenum_t; typedef enum { S_NULL, S_FREETARGMOBJ, S_ITEM_PTN1_1, S_ITEM_PTN1_2, S_ITEM_PTN1_3, S_ITEM_SHLD1, S_ITEM_SHD2_1, S_ITEM_BAGH1, S_ITEM_SPMP1, S_HIDESPECIAL1, S_HIDESPECIAL2, S_HIDESPECIAL3, S_HIDESPECIAL4, S_HIDESPECIAL5, S_HIDESPECIAL6, S_HIDESPECIAL7, S_HIDESPECIAL8, S_HIDESPECIAL9, S_HIDESPECIAL10, S_HIDESPECIAL11, S_DORMANTARTI1, S_DORMANTARTI2, S_DORMANTARTI3, S_DORMANTARTI4, S_DORMANTARTI5, S_DORMANTARTI6, S_DORMANTARTI7, S_DORMANTARTI8, S_DORMANTARTI9, S_DORMANTARTI10, S_DORMANTARTI11, S_DORMANTARTI12, S_DORMANTARTI13, S_DORMANTARTI14, S_DORMANTARTI15, S_DORMANTARTI16, S_DORMANTARTI17, S_DORMANTARTI18, S_DORMANTARTI19, S_DORMANTARTI20, S_DORMANTARTI21, S_DEADARTI1, S_DEADARTI2, S_DEADARTI3, S_DEADARTI4, S_DEADARTI5, S_DEADARTI6, S_DEADARTI7, S_DEADARTI8, S_DEADARTI9, S_DEADARTI10, S_ARTI_INVS1, S_ARTI_PTN2_1, S_ARTI_PTN2_2, S_ARTI_PTN2_3, S_ARTI_SOAR1, S_ARTI_SOAR2, S_ARTI_SOAR3, S_ARTI_SOAR4, S_ARTI_INVU1, S_ARTI_INVU2, S_ARTI_INVU3, S_ARTI_INVU4, S_ARTI_PWBK1, S_ARTI_EGGC1, S_ARTI_EGGC2, S_ARTI_EGGC3, S_ARTI_EGGC4, S_EGGFX1, S_EGGFX2, S_EGGFX3, S_EGGFX4, S_EGGFX5, S_EGGFXI1_1, S_EGGFXI1_2, S_EGGFXI1_3, S_EGGFXI1_4, S_ARTI_SPHL1, S_ARTI_TRCH1, S_ARTI_TRCH2, S_ARTI_TRCH3, S_ARTI_FBMB1, S_FIREBOMB1, S_FIREBOMB2, S_FIREBOMB3, S_FIREBOMB4, S_FIREBOMB5, S_FIREBOMB6, S_FIREBOMB7, S_FIREBOMB8, S_FIREBOMB9, S_FIREBOMB10, S_FIREBOMB11, S_ARTI_ATLP1, S_ARTI_ATLP2, S_ARTI_ATLP3, S_ARTI_ATLP4, S_POD_WAIT1, S_POD_PAIN1, S_POD_DIE1, S_POD_DIE2, S_POD_DIE3, S_POD_DIE4, S_POD_GROW1, S_POD_GROW2, S_POD_GROW3, S_POD_GROW4, S_POD_GROW5, S_POD_GROW6, S_POD_GROW7, S_POD_GROW8, S_PODGOO1, S_PODGOO2, S_PODGOOX, S_PODGENERATOR, S_SPLASH1, S_SPLASH2, S_SPLASH3, S_SPLASH4, S_SPLASHX, S_SPLASHBASE1, S_SPLASHBASE2, S_SPLASHBASE3, S_SPLASHBASE4, S_SPLASHBASE5, S_SPLASHBASE6, S_SPLASHBASE7, S_LAVASPLASH1, S_LAVASPLASH2, S_LAVASPLASH3, S_LAVASPLASH4, S_LAVASPLASH5, S_LAVASPLASH6, S_LAVASMOKE1, S_LAVASMOKE2, S_LAVASMOKE3, S_LAVASMOKE4, S_LAVASMOKE5, S_SLUDGECHUNK1, S_SLUDGECHUNK2, S_SLUDGECHUNK3, S_SLUDGECHUNK4, S_SLUDGECHUNKX, S_SLUDGESPLASH1, S_SLUDGESPLASH2, S_SLUDGESPLASH3, S_SLUDGESPLASH4, S_SKULLHANG70_1, S_SKULLHANG60_1, S_SKULLHANG45_1, S_SKULLHANG35_1, S_CHANDELIER1, S_CHANDELIER2, S_CHANDELIER3, S_SERPTORCH1, S_SERPTORCH2, S_SERPTORCH3, S_SMALLPILLAR, S_STALAGMITESMALL, S_STALAGMITELARGE, S_STALACTITESMALL, S_STALACTITELARGE, S_FIREBRAZIER1, S_FIREBRAZIER2, S_FIREBRAZIER3, S_FIREBRAZIER4, S_FIREBRAZIER5, S_FIREBRAZIER6, S_FIREBRAZIER7, S_FIREBRAZIER8, S_BARREL, S_BRPILLAR, S_MOSS1, S_MOSS2, S_WALLTORCH1, S_WALLTORCH2, S_WALLTORCH3, S_HANGINGCORPSE, S_KEYGIZMO1, S_KEYGIZMO2, S_KEYGIZMO3, S_KGZ_START, S_KGZ_BLUEFLOAT1, S_KGZ_GREENFLOAT1, S_KGZ_YELLOWFLOAT1, S_VOLCANO1, S_VOLCANO2, S_VOLCANO3, S_VOLCANO4, S_VOLCANO5, S_VOLCANO6, S_VOLCANO7, S_VOLCANO8, S_VOLCANO9, S_VOLCANOBALL1, S_VOLCANOBALL2, S_VOLCANOBALLX1, S_VOLCANOBALLX2, S_VOLCANOBALLX3, S_VOLCANOBALLX4, S_VOLCANOBALLX5, S_VOLCANOBALLX6, S_VOLCANOTBALL1, S_VOLCANOTBALL2, S_VOLCANOTBALLX1, S_VOLCANOTBALLX2, S_VOLCANOTBALLX3, S_VOLCANOTBALLX4, S_VOLCANOTBALLX5, S_VOLCANOTBALLX6, S_VOLCANOTBALLX7, S_TELEGLITGEN1, S_TELEGLITGEN2, S_TELEGLITTER1_1, S_TELEGLITTER1_2, S_TELEGLITTER1_3, S_TELEGLITTER1_4, S_TELEGLITTER1_5, S_TELEGLITTER2_1, S_TELEGLITTER2_2, S_TELEGLITTER2_3, S_TELEGLITTER2_4, S_TELEGLITTER2_5, S_TFOG1, S_TFOG2, S_TFOG3, S_TFOG4, S_TFOG5, S_TFOG6, S_TFOG7, S_TFOG8, S_TFOG9, S_TFOG10, S_TFOG11, S_TFOG12, S_TFOG13, S_LIGHTDONE, S_STAFFREADY, S_STAFFDOWN, S_STAFFUP, S_STAFFREADY2_1, S_STAFFREADY2_2, S_STAFFREADY2_3, S_STAFFDOWN2, S_STAFFUP2, S_STAFFATK1_1, S_STAFFATK1_2, S_STAFFATK1_3, S_STAFFATK2_1, S_STAFFATK2_2, S_STAFFATK2_3, S_STAFFPUFF1, S_STAFFPUFF2, S_STAFFPUFF3, S_STAFFPUFF4, S_STAFFPUFF2_1, S_STAFFPUFF2_2, S_STAFFPUFF2_3, S_STAFFPUFF2_4, S_STAFFPUFF2_5, S_STAFFPUFF2_6, S_BEAKREADY, S_BEAKDOWN, S_BEAKUP, S_BEAKATK1_1, S_BEAKATK2_1, S_WGNT, S_GAUNTLETREADY, S_GAUNTLETDOWN, S_GAUNTLETUP, S_GAUNTLETREADY2_1, S_GAUNTLETREADY2_2, S_GAUNTLETREADY2_3, S_GAUNTLETDOWN2, S_GAUNTLETUP2, S_GAUNTLETATK1_1, S_GAUNTLETATK1_2, S_GAUNTLETATK1_3, S_GAUNTLETATK1_4, S_GAUNTLETATK1_5, S_GAUNTLETATK1_6, S_GAUNTLETATK1_7, S_GAUNTLETATK2_1, S_GAUNTLETATK2_2, S_GAUNTLETATK2_3, S_GAUNTLETATK2_4, S_GAUNTLETATK2_5, S_GAUNTLETATK2_6, S_GAUNTLETATK2_7, S_GAUNTLETPUFF1_1, S_GAUNTLETPUFF1_2, S_GAUNTLETPUFF1_3, S_GAUNTLETPUFF1_4, S_GAUNTLETPUFF2_1, S_GAUNTLETPUFF2_2, S_GAUNTLETPUFF2_3, S_GAUNTLETPUFF2_4, S_BLSR, S_BLASTERREADY, S_BLASTERDOWN, S_BLASTERUP, S_BLASTERATK1_1, S_BLASTERATK1_2, S_BLASTERATK1_3, S_BLASTERATK1_4, S_BLASTERATK1_5, S_BLASTERATK1_6, S_BLASTERATK2_1, S_BLASTERATK2_2, S_BLASTERATK2_3, S_BLASTERATK2_4, S_BLASTERATK2_5, S_BLASTERATK2_6, S_BLASTERFX1_1, S_BLASTERFXI1_1, S_BLASTERFXI1_2, S_BLASTERFXI1_3, S_BLASTERFXI1_4, S_BLASTERFXI1_5, S_BLASTERFXI1_6, S_BLASTERFXI1_7, S_BLASTERSMOKE1, S_BLASTERSMOKE2, S_BLASTERSMOKE3, S_BLASTERSMOKE4, S_BLASTERSMOKE5, S_RIPPER1, S_RIPPER2, S_RIPPERX1, S_RIPPERX2, S_RIPPERX3, S_RIPPERX4, S_RIPPERX5, S_BLASTERPUFF1_1, S_BLASTERPUFF1_2, S_BLASTERPUFF1_3, S_BLASTERPUFF1_4, S_BLASTERPUFF1_5, S_BLASTERPUFF2_1, S_BLASTERPUFF2_2, S_BLASTERPUFF2_3, S_BLASTERPUFF2_4, S_BLASTERPUFF2_5, S_BLASTERPUFF2_6, S_BLASTERPUFF2_7, S_WMCE, S_MACEREADY, S_MACEDOWN, S_MACEUP, S_MACEATK1_1, S_MACEATK1_2, S_MACEATK1_3, S_MACEATK1_4, S_MACEATK1_5, S_MACEATK1_6, S_MACEATK1_7, S_MACEATK1_8, S_MACEATK1_9, S_MACEATK1_10, S_MACEATK2_1, S_MACEATK2_2, S_MACEATK2_3, S_MACEATK2_4, S_MACEFX1_1, S_MACEFX1_2, S_MACEFXI1_1, S_MACEFXI1_2, S_MACEFXI1_3, S_MACEFXI1_4, S_MACEFXI1_5, S_MACEFX2_1, S_MACEFX2_2, S_MACEFXI2_1, S_MACEFX3_1, S_MACEFX3_2, S_MACEFX4_1, S_MACEFXI4_1, S_WSKL, S_HORNRODREADY, S_HORNRODDOWN, S_HORNRODUP, S_HORNRODATK1_1, S_HORNRODATK1_2, S_HORNRODATK1_3, S_HORNRODATK2_1, S_HORNRODATK2_2, S_HORNRODATK2_3, S_HORNRODATK2_4, S_HORNRODATK2_5, S_HORNRODATK2_6, S_HORNRODATK2_7, S_HORNRODATK2_8, S_HORNRODATK2_9, S_HRODFX1_1, S_HRODFX1_2, S_HRODFXI1_1, S_HRODFXI1_2, S_HRODFXI1_3, S_HRODFXI1_4, S_HRODFXI1_5, S_HRODFXI1_6, S_HRODFX2_1, S_HRODFX2_2, S_HRODFX2_3, S_HRODFX2_4, S_HRODFXI2_1, S_HRODFXI2_2, S_HRODFXI2_3, S_HRODFXI2_4, S_HRODFXI2_5, S_HRODFXI2_6, S_HRODFXI2_7, S_HRODFXI2_8, S_RAINPLR1_1, S_RAINPLR2_1, S_RAINPLR3_1, S_RAINPLR4_1, S_RAINPLR1X_1, S_RAINPLR1X_2, S_RAINPLR1X_3, S_RAINPLR1X_4, S_RAINPLR1X_5, S_RAINPLR2X_1, S_RAINPLR2X_2, S_RAINPLR2X_3, S_RAINPLR2X_4, S_RAINPLR2X_5, S_RAINPLR3X_1, S_RAINPLR3X_2, S_RAINPLR3X_3, S_RAINPLR3X_4, S_RAINPLR3X_5, S_RAINPLR4X_1, S_RAINPLR4X_2, S_RAINPLR4X_3, S_RAINPLR4X_4, S_RAINPLR4X_5, S_RAINAIRXPLR1_1, S_RAINAIRXPLR2_1, S_RAINAIRXPLR3_1, S_RAINAIRXPLR4_1, S_RAINAIRXPLR1_2, S_RAINAIRXPLR2_2, S_RAINAIRXPLR3_2, S_RAINAIRXPLR4_2, S_RAINAIRXPLR1_3, S_RAINAIRXPLR2_3, S_RAINAIRXPLR3_3, S_RAINAIRXPLR4_3, S_GOLDWANDREADY, S_GOLDWANDDOWN, S_GOLDWANDUP, S_GOLDWANDATK1_1, S_GOLDWANDATK1_2, S_GOLDWANDATK1_3, S_GOLDWANDATK1_4, S_GOLDWANDATK2_1, S_GOLDWANDATK2_2, S_GOLDWANDATK2_3, S_GOLDWANDATK2_4, S_GWANDFX1_1, S_GWANDFX1_2, S_GWANDFXI1_1, S_GWANDFXI1_2, S_GWANDFXI1_3, S_GWANDFXI1_4, S_GWANDFX2_1, S_GWANDFX2_2, S_GWANDPUFF1_1, S_GWANDPUFF1_2, S_GWANDPUFF1_3, S_GWANDPUFF1_4, S_GWANDPUFF1_5, S_WPHX, S_PHOENIXREADY, S_PHOENIXDOWN, S_PHOENIXUP, S_PHOENIXATK1_1, S_PHOENIXATK1_2, S_PHOENIXATK1_3, S_PHOENIXATK1_4, S_PHOENIXATK1_5, S_PHOENIXATK2_1, S_PHOENIXATK2_2, S_PHOENIXATK2_3, S_PHOENIXATK2_4, S_PHOENIXFX1_1, S_PHOENIXFXI1_1, S_PHOENIXFXI1_2, S_PHOENIXFXI1_3, S_PHOENIXFXI1_4, S_PHOENIXFXI1_5, S_PHOENIXFXI1_6, S_PHOENIXFXI1_7, S_PHOENIXFXI1_8, S_PHOENIXFXIX_1, // [ States in Heretic 1.0 that were removed S_PHOENIXFXIX_2, S_PHOENIXFXIX_3, // ] S_PHOENIXPUFF1, S_PHOENIXPUFF2, S_PHOENIXPUFF3, S_PHOENIXPUFF4, S_PHOENIXPUFF5, S_PHOENIXFX2_1, S_PHOENIXFX2_2, S_PHOENIXFX2_3, S_PHOENIXFX2_4, S_PHOENIXFX2_5, S_PHOENIXFX2_6, S_PHOENIXFX2_7, S_PHOENIXFX2_8, S_PHOENIXFX2_9, S_PHOENIXFX2_10, S_PHOENIXFXI2_1, S_PHOENIXFXI2_2, S_PHOENIXFXI2_3, S_PHOENIXFXI2_4, S_PHOENIXFXI2_5, S_WBOW, S_CRBOW1, S_CRBOW2, S_CRBOW3, S_CRBOW4, S_CRBOW5, S_CRBOW6, S_CRBOW7, S_CRBOW8, S_CRBOW9, S_CRBOW10, S_CRBOW11, S_CRBOW12, S_CRBOW13, S_CRBOW14, S_CRBOW15, S_CRBOW16, S_CRBOW17, S_CRBOW18, S_CRBOWDOWN, S_CRBOWUP, S_CRBOWATK1_1, S_CRBOWATK1_2, S_CRBOWATK1_3, S_CRBOWATK1_4, S_CRBOWATK1_5, S_CRBOWATK1_6, S_CRBOWATK1_7, S_CRBOWATK1_8, S_CRBOWATK2_1, S_CRBOWATK2_2, S_CRBOWATK2_3, S_CRBOWATK2_4, S_CRBOWATK2_5, S_CRBOWATK2_6, S_CRBOWATK2_7, S_CRBOWATK2_8, S_CRBOWFX1, S_CRBOWFXI1_1, S_CRBOWFXI1_2, S_CRBOWFXI1_3, S_CRBOWFX2, S_CRBOWFX3, S_CRBOWFXI3_1, S_CRBOWFXI3_2, S_CRBOWFXI3_3, S_CRBOWFX4_1, S_CRBOWFX4_2, S_BLOOD1, S_BLOOD2, S_BLOOD3, S_BLOODSPLATTER1, S_BLOODSPLATTER2, S_BLOODSPLATTER3, S_BLOODSPLATTERX, 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_DIE8, S_PLAY_DIE9, 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_PLAY_FDTH1, S_PLAY_FDTH2, S_PLAY_FDTH3, S_PLAY_FDTH4, S_PLAY_FDTH5, S_PLAY_FDTH6, S_PLAY_FDTH7, S_PLAY_FDTH8, S_PLAY_FDTH9, S_PLAY_FDTH10, S_PLAY_FDTH11, S_PLAY_FDTH12, S_PLAY_FDTH13, S_PLAY_FDTH14, S_PLAY_FDTH15, S_PLAY_FDTH16, S_PLAY_FDTH17, S_PLAY_FDTH18, S_PLAY_FDTH19, // < These two frames were not present in the Heretic S_PLAY_FDTH20, // < 1.0 executable (fire death animation was extended) S_BLOODYSKULL1, S_BLOODYSKULL2, S_BLOODYSKULL3, S_BLOODYSKULL4, S_BLOODYSKULL5, S_BLOODYSKULLX1, S_BLOODYSKULLX2, S_CHICPLAY, S_CHICPLAY_RUN1, S_CHICPLAY_RUN2, S_CHICPLAY_RUN3, S_CHICPLAY_RUN4, S_CHICPLAY_ATK1, S_CHICPLAY_PAIN, S_CHICPLAY_PAIN2, S_CHICKEN_LOOK1, S_CHICKEN_LOOK2, S_CHICKEN_WALK1, S_CHICKEN_WALK2, S_CHICKEN_PAIN1, S_CHICKEN_PAIN2, S_CHICKEN_ATK1, S_CHICKEN_ATK2, S_CHICKEN_DIE1, S_CHICKEN_DIE2, S_CHICKEN_DIE3, S_CHICKEN_DIE4, S_CHICKEN_DIE5, S_CHICKEN_DIE6, S_CHICKEN_DIE7, S_CHICKEN_DIE8, S_FEATHER1, S_FEATHER2, S_FEATHER3, S_FEATHER4, S_FEATHER5, S_FEATHER6, S_FEATHER7, S_FEATHER8, S_FEATHERX, S_MUMMY_LOOK1, S_MUMMY_LOOK2, S_MUMMY_WALK1, S_MUMMY_WALK2, S_MUMMY_WALK3, S_MUMMY_WALK4, S_MUMMY_ATK1, S_MUMMY_ATK2, S_MUMMY_ATK3, S_MUMMYL_ATK1, S_MUMMYL_ATK2, S_MUMMYL_ATK3, S_MUMMYL_ATK4, S_MUMMYL_ATK5, S_MUMMYL_ATK6, S_MUMMY_PAIN1, S_MUMMY_PAIN2, S_MUMMY_DIE1, S_MUMMY_DIE2, S_MUMMY_DIE3, S_MUMMY_DIE4, S_MUMMY_DIE5, S_MUMMY_DIE6, S_MUMMY_DIE7, S_MUMMY_DIE8, S_MUMMY_SOUL1, S_MUMMY_SOUL2, S_MUMMY_SOUL3, S_MUMMY_SOUL4, S_MUMMY_SOUL5, S_MUMMY_SOUL6, S_MUMMY_SOUL7, S_MUMMYFX1_1, S_MUMMYFX1_2, S_MUMMYFX1_3, S_MUMMYFX1_4, S_MUMMYFXI1_1, S_MUMMYFXI1_2, S_MUMMYFXI1_3, S_MUMMYFXI1_4, S_BEAST_LOOK1, S_BEAST_LOOK2, S_BEAST_WALK1, S_BEAST_WALK2, S_BEAST_WALK3, S_BEAST_WALK4, S_BEAST_WALK5, S_BEAST_WALK6, S_BEAST_ATK1, S_BEAST_ATK2, S_BEAST_PAIN1, S_BEAST_PAIN2, S_BEAST_DIE1, S_BEAST_DIE2, S_BEAST_DIE3, S_BEAST_DIE4, S_BEAST_DIE5, S_BEAST_DIE6, S_BEAST_DIE7, S_BEAST_DIE8, S_BEAST_DIE9, S_BEAST_XDIE1, S_BEAST_XDIE2, S_BEAST_XDIE3, S_BEAST_XDIE4, S_BEAST_XDIE5, S_BEAST_XDIE6, S_BEAST_XDIE7, S_BEAST_XDIE8, S_BEASTBALL1, S_BEASTBALL2, S_BEASTBALL3, S_BEASTBALL4, S_BEASTBALL5, S_BEASTBALL6, S_BEASTBALLX1, S_BEASTBALLX2, S_BEASTBALLX3, S_BEASTBALLX4, S_BEASTBALLX5, S_BURNBALL1, S_BURNBALL2, S_BURNBALL3, S_BURNBALL4, S_BURNBALL5, S_BURNBALL6, S_BURNBALL7, S_BURNBALL8, S_BURNBALLFB1, S_BURNBALLFB2, S_BURNBALLFB3, S_BURNBALLFB4, S_BURNBALLFB5, S_BURNBALLFB6, S_BURNBALLFB7, S_BURNBALLFB8, S_PUFFY1, S_PUFFY2, S_PUFFY3, S_PUFFY4, S_PUFFY5, S_SNAKE_LOOK1, S_SNAKE_LOOK2, S_SNAKE_WALK1, S_SNAKE_WALK2, S_SNAKE_WALK3, S_SNAKE_WALK4, S_SNAKE_ATK1, S_SNAKE_ATK2, S_SNAKE_ATK3, S_SNAKE_ATK4, S_SNAKE_ATK5, S_SNAKE_ATK6, S_SNAKE_ATK7, S_SNAKE_ATK8, S_SNAKE_ATK9, S_SNAKE_PAIN1, S_SNAKE_PAIN2, S_SNAKE_DIE1, S_SNAKE_DIE2, S_SNAKE_DIE3, S_SNAKE_DIE4, S_SNAKE_DIE5, S_SNAKE_DIE6, S_SNAKE_DIE7, S_SNAKE_DIE8, S_SNAKE_DIE9, S_SNAKE_DIE10, S_SNAKEPRO_A1, S_SNAKEPRO_A2, S_SNAKEPRO_A3, S_SNAKEPRO_A4, S_SNAKEPRO_AX1, S_SNAKEPRO_AX2, S_SNAKEPRO_AX3, S_SNAKEPRO_AX4, S_SNAKEPRO_AX5, S_SNAKEPRO_B1, S_SNAKEPRO_B2, S_SNAKEPRO_BX1, S_SNAKEPRO_BX2, S_SNAKEPRO_BX3, S_SNAKEPRO_BX4, S_HEAD_LOOK, S_HEAD_FLOAT, S_HEAD_ATK1, S_HEAD_ATK2, S_HEAD_PAIN1, S_HEAD_PAIN2, S_HEAD_DIE1, S_HEAD_DIE2, S_HEAD_DIE3, S_HEAD_DIE4, S_HEAD_DIE5, S_HEAD_DIE6, S_HEAD_DIE7, S_HEADFX1_1, S_HEADFX1_2, S_HEADFX1_3, S_HEADFXI1_1, S_HEADFXI1_2, S_HEADFXI1_3, S_HEADFXI1_4, S_HEADFX2_1, S_HEADFX2_2, S_HEADFX2_3, S_HEADFXI2_1, S_HEADFXI2_2, S_HEADFXI2_3, S_HEADFXI2_4, S_HEADFX3_1, S_HEADFX3_2, S_HEADFX3_3, S_HEADFX3_4, S_HEADFX3_5, S_HEADFX3_6, S_HEADFXI3_1, S_HEADFXI3_2, S_HEADFXI3_3, S_HEADFXI3_4, S_HEADFX4_1, S_HEADFX4_2, S_HEADFX4_3, S_HEADFX4_4, S_HEADFX4_5, S_HEADFX4_6, S_HEADFX4_7, S_HEADFXI4_1, S_HEADFXI4_2, S_HEADFXI4_3, S_HEADFXI4_4, S_CLINK_LOOK1, S_CLINK_LOOK2, S_CLINK_WALK1, S_CLINK_WALK2, S_CLINK_WALK3, S_CLINK_WALK4, S_CLINK_ATK1, S_CLINK_ATK2, S_CLINK_ATK3, S_CLINK_PAIN1, S_CLINK_PAIN2, S_CLINK_DIE1, S_CLINK_DIE2, S_CLINK_DIE3, S_CLINK_DIE4, S_CLINK_DIE5, S_CLINK_DIE6, S_CLINK_DIE7, S_WIZARD_LOOK1, S_WIZARD_LOOK2, S_WIZARD_WALK1, S_WIZARD_WALK2, S_WIZARD_WALK3, S_WIZARD_WALK4, S_WIZARD_WALK5, S_WIZARD_WALK6, S_WIZARD_WALK7, S_WIZARD_WALK8, S_WIZARD_ATK1, S_WIZARD_ATK2, S_WIZARD_ATK3, S_WIZARD_ATK4, S_WIZARD_ATK5, S_WIZARD_ATK6, S_WIZARD_ATK7, S_WIZARD_ATK8, S_WIZARD_ATK9, S_WIZARD_PAIN1, S_WIZARD_PAIN2, S_WIZARD_DIE1, S_WIZARD_DIE2, S_WIZARD_DIE3, S_WIZARD_DIE4, S_WIZARD_DIE5, S_WIZARD_DIE6, S_WIZARD_DIE7, S_WIZARD_DIE8, S_WIZFX1_1, S_WIZFX1_2, S_WIZFXI1_1, S_WIZFXI1_2, S_WIZFXI1_3, S_WIZFXI1_4, S_WIZFXI1_5, S_IMP_LOOK1, S_IMP_LOOK2, S_IMP_LOOK3, S_IMP_LOOK4, S_IMP_FLY1, S_IMP_FLY2, S_IMP_FLY3, S_IMP_FLY4, S_IMP_FLY5, S_IMP_FLY6, S_IMP_FLY7, S_IMP_FLY8, S_IMP_MEATK1, S_IMP_MEATK2, S_IMP_MEATK3, S_IMP_MSATK1_1, S_IMP_MSATK1_2, S_IMP_MSATK1_3, S_IMP_MSATK1_4, S_IMP_MSATK1_5, S_IMP_MSATK1_6, S_IMP_MSATK2_1, S_IMP_MSATK2_2, S_IMP_MSATK2_3, S_IMP_PAIN1, S_IMP_PAIN2, S_IMP_DIE1, S_IMP_DIE2, S_IMP_XDIE1, S_IMP_XDIE2, S_IMP_XDIE3, S_IMP_XDIE4, S_IMP_XDIE5, S_IMP_CRASH1, S_IMP_CRASH2, S_IMP_CRASH3, S_IMP_CRASH4, S_IMP_XCRASH1, S_IMP_XCRASH2, S_IMP_XCRASH3, S_IMP_CHUNKA1, S_IMP_CHUNKA2, S_IMP_CHUNKA3, S_IMP_CHUNKB1, S_IMP_CHUNKB2, S_IMP_CHUNKB3, S_IMPFX1, S_IMPFX2, S_IMPFX3, S_IMPFXI1, S_IMPFXI2, S_IMPFXI3, S_IMPFXI4, S_KNIGHT_STND1, S_KNIGHT_STND2, S_KNIGHT_WALK1, S_KNIGHT_WALK2, S_KNIGHT_WALK3, S_KNIGHT_WALK4, S_KNIGHT_ATK1, S_KNIGHT_ATK2, S_KNIGHT_ATK3, S_KNIGHT_ATK4, S_KNIGHT_ATK5, S_KNIGHT_ATK6, S_KNIGHT_PAIN1, S_KNIGHT_PAIN2, S_KNIGHT_DIE1, S_KNIGHT_DIE2, S_KNIGHT_DIE3, S_KNIGHT_DIE4, S_KNIGHT_DIE5, S_KNIGHT_DIE6, S_KNIGHT_DIE7, S_SPINAXE1, S_SPINAXE2, S_SPINAXE3, S_SPINAXEX1, S_SPINAXEX2, S_SPINAXEX3, S_REDAXE1, S_REDAXE2, S_REDAXEX1, S_REDAXEX2, S_REDAXEX3, S_SRCR1_LOOK1, S_SRCR1_LOOK2, S_SRCR1_WALK1, S_SRCR1_WALK2, S_SRCR1_WALK3, S_SRCR1_WALK4, S_SRCR1_PAIN1, S_SRCR1_ATK1, S_SRCR1_ATK2, S_SRCR1_ATK3, S_SRCR1_ATK4, S_SRCR1_ATK5, S_SRCR1_ATK6, S_SRCR1_ATK7, S_SRCR1_DIE1, S_SRCR1_DIE2, S_SRCR1_DIE3, S_SRCR1_DIE4, S_SRCR1_DIE5, S_SRCR1_DIE6, S_SRCR1_DIE7, S_SRCR1_DIE8, S_SRCR1_DIE9, S_SRCR1_DIE10, S_SRCR1_DIE11, S_SRCR1_DIE12, S_SRCR1_DIE13, S_SRCR1_DIE14, S_SRCR1_DIE15, S_SRCR1_DIE16, S_SRCR1_DIE17, S_SRCRFX1_1, S_SRCRFX1_2, S_SRCRFX1_3, S_SRCRFXI1_1, S_SRCRFXI1_2, S_SRCRFXI1_3, S_SRCRFXI1_4, S_SRCRFXI1_5, S_SOR2_RISE1, S_SOR2_RISE2, S_SOR2_RISE3, S_SOR2_RISE4, S_SOR2_RISE5, S_SOR2_RISE6, S_SOR2_RISE7, S_SOR2_LOOK1, S_SOR2_LOOK2, S_SOR2_WALK1, S_SOR2_WALK2, S_SOR2_WALK3, S_SOR2_WALK4, S_SOR2_PAIN1, S_SOR2_PAIN2, S_SOR2_ATK1, S_SOR2_ATK2, S_SOR2_ATK3, S_SOR2_TELE1, S_SOR2_TELE2, S_SOR2_TELE3, S_SOR2_TELE4, S_SOR2_TELE5, S_SOR2_TELE6, S_SOR2_DIE1, S_SOR2_DIE2, S_SOR2_DIE3, S_SOR2_DIE4, S_SOR2_DIE5, S_SOR2_DIE6, S_SOR2_DIE7, S_SOR2_DIE8, S_SOR2_DIE9, S_SOR2_DIE10, S_SOR2_DIE11, S_SOR2_DIE12, S_SOR2_DIE13, S_SOR2_DIE14, S_SOR2_DIE15, S_SOR2FX1_1, S_SOR2FX1_2, S_SOR2FX1_3, S_SOR2FXI1_1, S_SOR2FXI1_2, S_SOR2FXI1_3, S_SOR2FXI1_4, S_SOR2FXI1_5, S_SOR2FXI1_6, S_SOR2FXSPARK1, S_SOR2FXSPARK2, S_SOR2FXSPARK3, S_SOR2FX2_1, S_SOR2FX2_2, S_SOR2FX2_3, S_SOR2FXI2_1, S_SOR2FXI2_2, S_SOR2FXI2_3, S_SOR2FXI2_4, S_SOR2FXI2_5, S_SOR2TELEFADE1, S_SOR2TELEFADE2, S_SOR2TELEFADE3, S_SOR2TELEFADE4, S_SOR2TELEFADE5, S_SOR2TELEFADE6, S_MNTR_LOOK1, S_MNTR_LOOK2, S_MNTR_WALK1, S_MNTR_WALK2, S_MNTR_WALK3, S_MNTR_WALK4, S_MNTR_ATK1_1, S_MNTR_ATK1_2, S_MNTR_ATK1_3, S_MNTR_ATK2_1, S_MNTR_ATK2_2, S_MNTR_ATK2_3, S_MNTR_ATK3_1, S_MNTR_ATK3_2, S_MNTR_ATK3_3, S_MNTR_ATK3_4, S_MNTR_ATK4_1, S_MNTR_PAIN1, S_MNTR_PAIN2, S_MNTR_DIE1, S_MNTR_DIE2, S_MNTR_DIE3, S_MNTR_DIE4, S_MNTR_DIE5, S_MNTR_DIE6, S_MNTR_DIE7, S_MNTR_DIE8, S_MNTR_DIE9, S_MNTR_DIE10, S_MNTR_DIE11, S_MNTR_DIE12, S_MNTR_DIE13, S_MNTR_DIE14, S_MNTR_DIE15, S_MNTRFX1_1, S_MNTRFX1_2, S_MNTRFXI1_1, S_MNTRFXI1_2, S_MNTRFXI1_3, S_MNTRFXI1_4, S_MNTRFXI1_5, S_MNTRFXI1_6, S_MNTRFX2_1, S_MNTRFXI2_1, S_MNTRFXI2_2, S_MNTRFXI2_3, S_MNTRFXI2_4, S_MNTRFXI2_5, S_MNTRFX3_1, S_MNTRFX3_2, S_MNTRFX3_3, S_MNTRFX3_4, S_MNTRFX3_5, S_MNTRFX3_6, S_MNTRFX3_7, S_MNTRFX3_8, S_MNTRFX3_9, S_AKYY1, S_AKYY2, S_AKYY3, S_AKYY4, S_AKYY5, S_AKYY6, S_AKYY7, S_AKYY8, S_AKYY9, S_AKYY10, S_BKYY1, S_BKYY2, S_BKYY3, S_BKYY4, S_BKYY5, S_BKYY6, S_BKYY7, S_BKYY8, S_BKYY9, S_BKYY10, S_CKYY1, S_CKYY2, S_CKYY3, S_CKYY4, S_CKYY5, S_CKYY6, S_CKYY7, S_CKYY8, S_CKYY9, S_AMG1, S_AMG2_1, S_AMG2_2, S_AMG2_3, S_AMM1, S_AMM2, S_AMC1, S_AMC2_1, S_AMC2_2, S_AMC2_3, S_AMS1_1, S_AMS1_2, S_AMS2_1, S_AMS2_2, S_AMP1_1, S_AMP1_2, S_AMP1_3, S_AMP2_1, S_AMP2_2, S_AMP2_3, S_AMB1_1, S_AMB1_2, S_AMB1_3, S_AMB2_1, S_AMB2_2, S_AMB2_3, S_SND_WIND, S_SND_WATERFALL, NUMSTATES } statenum_t; typedef struct { spritenum_t sprite; int frame; int tics; void (*action) (); statenum_t nextstate; int misc1, misc2; } state_t; extern state_t states[NUMSTATES]; extern const char *sprnames[]; typedef enum { MT_MISC0, MT_ITEMSHIELD1, MT_ITEMSHIELD2, MT_MISC1, MT_MISC2, MT_ARTIINVISIBILITY, MT_MISC3, MT_ARTIFLY, MT_ARTIINVULNERABILITY, MT_ARTITOMEOFPOWER, MT_ARTIEGG, MT_EGGFX, MT_ARTISUPERHEAL, MT_MISC4, MT_MISC5, MT_FIREBOMB, MT_ARTITELEPORT, MT_POD, MT_PODGOO, MT_PODGENERATOR, MT_SPLASH, MT_SPLASHBASE, MT_LAVASPLASH, MT_LAVASMOKE, MT_SLUDGECHUNK, MT_SLUDGESPLASH, MT_SKULLHANG70, MT_SKULLHANG60, MT_SKULLHANG45, MT_SKULLHANG35, MT_CHANDELIER, MT_SERPTORCH, MT_SMALLPILLAR, MT_STALAGMITESMALL, MT_STALAGMITELARGE, MT_STALACTITESMALL, MT_STALACTITELARGE, MT_MISC6, MT_BARREL, MT_MISC7, MT_MISC8, MT_MISC9, MT_MISC10, MT_MISC11, MT_KEYGIZMOBLUE, MT_KEYGIZMOGREEN, MT_KEYGIZMOYELLOW, MT_KEYGIZMOFLOAT, MT_MISC12, MT_VOLCANOBLAST, MT_VOLCANOTBLAST, MT_TELEGLITGEN, MT_TELEGLITGEN2, MT_TELEGLITTER, MT_TELEGLITTER2, MT_TFOG, MT_TELEPORTMAN, MT_STAFFPUFF, MT_STAFFPUFF2, MT_BEAKPUFF, MT_MISC13, MT_GAUNTLETPUFF1, MT_GAUNTLETPUFF2, MT_MISC14, MT_BLASTERFX1, MT_BLASTERSMOKE, MT_RIPPER, MT_BLASTERPUFF1, MT_BLASTERPUFF2, MT_WMACE, MT_MACEFX1, MT_MACEFX2, MT_MACEFX3, MT_MACEFX4, MT_WSKULLROD, MT_HORNRODFX1, MT_HORNRODFX2, MT_RAINPLR1, MT_RAINPLR2, MT_RAINPLR3, MT_RAINPLR4, MT_GOLDWANDFX1, MT_GOLDWANDFX2, MT_GOLDWANDPUFF1, MT_GOLDWANDPUFF2, MT_WPHOENIXROD, MT_PHOENIXFX1, MT_PHOENIXFX_REMOVED, // In Heretic 1.0, but removed. MT_PHOENIXPUFF, MT_PHOENIXFX2, MT_MISC15, MT_CRBOWFX1, MT_CRBOWFX2, MT_CRBOWFX3, MT_CRBOWFX4, MT_BLOOD, MT_BLOODSPLATTER, MT_PLAYER, MT_BLOODYSKULL, MT_CHICPLAYER, MT_CHICKEN, MT_FEATHER, MT_MUMMY, MT_MUMMYLEADER, MT_MUMMYGHOST, MT_MUMMYLEADERGHOST, MT_MUMMYSOUL, MT_MUMMYFX1, MT_BEAST, MT_BEASTBALL, MT_BURNBALL, MT_BURNBALLFB, MT_PUFFY, MT_SNAKE, MT_SNAKEPRO_A, MT_SNAKEPRO_B, MT_HEAD, MT_HEADFX1, MT_HEADFX2, MT_HEADFX3, MT_WHIRLWIND, MT_CLINK, MT_WIZARD, MT_WIZFX1, MT_IMP, MT_IMPLEADER, MT_IMPCHUNK1, MT_IMPCHUNK2, MT_IMPBALL, MT_KNIGHT, MT_KNIGHTGHOST, MT_KNIGHTAXE, MT_REDAXE, MT_SORCERER1, MT_SRCRFX1, MT_SORCERER2, MT_SOR2FX1, MT_SOR2FXSPARK, MT_SOR2FX2, MT_SOR2TELEFADE, MT_MINOTAUR, MT_MNTRFX1, MT_MNTRFX2, MT_MNTRFX3, MT_AKYY, MT_BKYY, MT_CKEY, MT_AMGWNDWIMPY, MT_AMGWNDHEFTY, MT_AMMACEWIMPY, MT_AMMACEHEFTY, MT_AMCBOWWIMPY, MT_AMCBOWHEFTY, MT_AMSKRDWIMPY, MT_AMSKRDHEFTY, MT_AMPHRDWIMPY, MT_AMPHRDHEFTY, MT_AMBLSRWIMPY, MT_AMBLSRHEFTY, MT_SOUNDWIND, MT_SOUNDWATERFALL, NUMMOBJTYPES } mobjtype_t; typedef struct { int doomednum; int spawnstate; int spawnhealth; int seestate; int seesound; int reactiontime; int attacksound; int painstate; int painchance; int painsound; int meleestate; int missilestate; int crashstate; int deathstate; int xdeathstate; int deathsound; int speed; int radius; int height; int mass; int damage; int activesound; int flags; int flags2; } mobjinfo_t; extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; #endif /* #ifndef HERETIC_INFO_H */ crispy-doom-crispy-doom-5.6.4/src/heretic/m_random.c000066400000000000000000000046221360717211000224120ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "m_random.h" /* =============== = = M_Random = = Returns a 0-255 number = =============== */ const unsigned int rndtable[256] = { 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188, 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224, 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242, 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0, 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235, 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113, 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75, 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196, 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113, 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241, 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224, 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136, 120, 163, 236, 249 }; int rndindex = 0; int prndindex = 0; int P_Random(void) { prndindex = (prndindex + 1) & 0xff; return rndtable[prndindex]; } int M_Random(void) { rndindex = (rndindex + 1) & 0xff; return rndtable[rndindex]; } void M_ClearRandom(void) { rndindex = prndindex = 0; } // inspired by the same routine in Eternity, thanks haleyjd int P_SubRandom (void) { int r = P_Random(); return r - P_Random(); } crispy-doom-crispy-doom-5.6.4/src/heretic/m_random.h000066400000000000000000000020611360717211000224120ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef HERETIC_M_RANDOM_H #define HERETIC_M_RANDOM_H // Most damage defined using HITDICE #define HITDICE(a) ((1+(P_Random()&7))*a) int M_Random(void); // returns a number from 0 to 255 int P_Random(void); // as M_Random, but used only by the play simulation void M_ClearRandom(void); // fix randoms for demos extern int rndindex; // Defined version of P_Random() - P_Random() int P_SubRandom (void); #endif // HERETIC_M_RANDOM_H crispy-doom-crispy-doom-5.6.4/src/heretic/mn_menu.c000066400000000000000000001253641360717211000222630ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // MN_menu.c #include #include #include "deh_str.h" #include "doomdef.h" #include "doomkeys.h" #include "i_input.h" #include "i_system.h" #include "i_swap.h" #include "m_controls.h" #include "m_misc.h" #include "p_local.h" #include "r_local.h" #include "s_sound.h" #include "v_video.h" // Macros #define LEFT_DIR 0 #define RIGHT_DIR 1 #define ITEM_HEIGHT 20 #define SELECTOR_XOFFSET (-28) #define SELECTOR_YOFFSET (-1) #define SLOTTEXTLEN 16 #define ASCII_CURSOR '[' // Types typedef enum { ITT_EMPTY, ITT_EFUNC, ITT_LRFUNC, ITT_SETMENU, ITT_INERT } ItemType_t; typedef enum { MENU_MAIN, MENU_EPISODE, MENU_SKILL, MENU_OPTIONS, MENU_OPTIONS2, MENU_FILES, MENU_LOAD, MENU_SAVE, MENU_NONE } MenuType_t; typedef struct { ItemType_t type; const char *text; boolean(*func) (int option); int option; MenuType_t menu; } MenuItem_t; typedef struct { int x; int y; void (*drawFunc) (void); int itemCount; MenuItem_t *items; int oldItPos; MenuType_t prevMenu; } Menu_t; // Private Functions static void InitFonts(void); static void SetMenu(MenuType_t menu); static boolean SCNetCheck(int option); static boolean SCQuitGame(int option); static boolean SCEpisode(int option); static boolean SCSkill(int option); static boolean SCMouseSensi(int option); static boolean SCSfxVolume(int option); static boolean SCMusicVolume(int option); static boolean SCScreenSize(int option); static boolean SCLoadGame(int option); static boolean SCSaveGame(int option); static boolean SCMessages(int option); static boolean SCEndGame(int option); static boolean SCInfo(int option); static void DrawMainMenu(void); static void DrawEpisodeMenu(void); static void DrawSkillMenu(void); static void DrawOptionsMenu(void); static void DrawOptions2Menu(void); static void DrawFileSlots(Menu_t * menu); static void DrawFilesMenu(void); static void MN_DrawInfo(void); static void DrawLoadMenu(void); static void DrawSaveMenu(void); static void DrawSlider(Menu_t * menu, int item, int width, int slot); void MN_LoadSlotText(void); // External Data extern int detailLevel; extern int screenblocks; // Public Data boolean MenuActive; int InfoType; boolean messageson; // Private Data static int FontABaseLump; static int FontBBaseLump; static int SkullBaseLump; static Menu_t *CurrentMenu; static int CurrentItPos; static int MenuEpisode; static int MenuTime; static boolean soundchanged; boolean askforquit; static int typeofask; static boolean FileMenuKeySteal; static boolean slottextloaded; static char SlotText[6][SLOTTEXTLEN + 2]; static char oldSlotText[SLOTTEXTLEN + 2]; static int SlotStatus[6]; static int slotptr; static int currentSlot; static int quicksave; static int quickload; static MenuItem_t MainItems[] = { {ITT_EFUNC, "NEW GAME", SCNetCheck, 1, MENU_EPISODE}, {ITT_SETMENU, "OPTIONS", NULL, 0, MENU_OPTIONS}, {ITT_SETMENU, "GAME FILES", NULL, 0, MENU_FILES}, {ITT_EFUNC, "INFO", SCInfo, 0, MENU_NONE}, {ITT_EFUNC, "QUIT GAME", SCQuitGame, 0, MENU_NONE} }; static Menu_t MainMenu = { 110, 56, DrawMainMenu, 5, MainItems, 0, MENU_NONE }; static MenuItem_t EpisodeItems[] = { {ITT_EFUNC, "CITY OF THE DAMNED", SCEpisode, 1, MENU_NONE}, {ITT_EFUNC, "HELL'S MAW", SCEpisode, 2, MENU_NONE}, {ITT_EFUNC, "THE DOME OF D'SPARIL", SCEpisode, 3, MENU_NONE}, {ITT_EFUNC, "THE OSSUARY", SCEpisode, 4, MENU_NONE}, {ITT_EFUNC, "THE STAGNANT DEMESNE", SCEpisode, 5, MENU_NONE} }; static Menu_t EpisodeMenu = { 80, 50, DrawEpisodeMenu, 3, EpisodeItems, 0, MENU_MAIN }; static MenuItem_t FilesItems[] = { {ITT_EFUNC, "LOAD GAME", SCNetCheck, 2, MENU_LOAD}, {ITT_SETMENU, "SAVE GAME", NULL, 0, MENU_SAVE} }; static Menu_t FilesMenu = { 110, 60, DrawFilesMenu, 2, FilesItems, 0, MENU_MAIN }; static MenuItem_t LoadItems[] = { {ITT_EFUNC, NULL, SCLoadGame, 0, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 1, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 2, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 3, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 4, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 5, MENU_NONE} }; static Menu_t LoadMenu = { 70, 30, DrawLoadMenu, 6, LoadItems, 0, MENU_FILES }; static MenuItem_t SaveItems[] = { {ITT_EFUNC, NULL, SCSaveGame, 0, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 1, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 2, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 3, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 4, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 5, MENU_NONE} }; static Menu_t SaveMenu = { 70, 30, DrawSaveMenu, 6, SaveItems, 0, MENU_FILES }; static MenuItem_t SkillItems[] = { {ITT_EFUNC, "THOU NEEDETH A WET-NURSE", SCSkill, sk_baby, MENU_NONE}, {ITT_EFUNC, "YELLOWBELLIES-R-US", SCSkill, sk_easy, MENU_NONE}, {ITT_EFUNC, "BRINGEST THEM ONETH", SCSkill, sk_medium, MENU_NONE}, {ITT_EFUNC, "THOU ART A SMITE-MEISTER", SCSkill, sk_hard, MENU_NONE}, {ITT_EFUNC, "BLACK PLAGUE POSSESSES THEE", SCSkill, sk_nightmare, MENU_NONE} }; static Menu_t SkillMenu = { 38, 30, DrawSkillMenu, 5, SkillItems, 2, MENU_EPISODE }; static MenuItem_t OptionsItems[] = { {ITT_EFUNC, "END GAME", SCEndGame, 0, MENU_NONE}, {ITT_EFUNC, "MESSAGES : ", SCMessages, 0, MENU_NONE}, {ITT_LRFUNC, "MOUSE SENSITIVITY", SCMouseSensi, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE}, {ITT_SETMENU, "MORE...", NULL, 0, MENU_OPTIONS2} }; static Menu_t OptionsMenu = { 88, 30, DrawOptionsMenu, 5, OptionsItems, 0, MENU_MAIN }; static MenuItem_t Options2Items[] = { {ITT_LRFUNC, "SCREEN SIZE", SCScreenSize, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE}, {ITT_LRFUNC, "SFX VOLUME", SCSfxVolume, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE}, {ITT_LRFUNC, "MUSIC VOLUME", SCMusicVolume, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE} }; static Menu_t Options2Menu = { 90, 20, DrawOptions2Menu, 6, Options2Items, 0, MENU_OPTIONS }; static Menu_t *Menus[] = { &MainMenu, &EpisodeMenu, &SkillMenu, &OptionsMenu, &Options2Menu, &FilesMenu, &LoadMenu, &SaveMenu }; //--------------------------------------------------------------------------- // // PROC MN_Init // //--------------------------------------------------------------------------- void MN_Init(void) { InitFonts(); MenuActive = false; messageson = true; SkullBaseLump = W_GetNumForName(DEH_String("M_SKL00")); if (gamemode == retail) { // Add episodes 4 and 5 to the menu EpisodeMenu.itemCount = 5; EpisodeMenu.y -= ITEM_HEIGHT; } } //--------------------------------------------------------------------------- // // PROC InitFonts // //--------------------------------------------------------------------------- static void InitFonts(void) { FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1; FontBBaseLump = W_GetNumForName(DEH_String("FONTB_S")) + 1; } //--------------------------------------------------------------------------- // // PROC MN_DrTextA // // Draw text using font A. // //--------------------------------------------------------------------------- void MN_DrTextA(const char *text, int x, int y) { char c; patch_t *p; while ((c = *text++) != 0) { if (c < 33) { x += 5; } else { p = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); V_DrawPatch(x, y, p); x += SHORT(p->width) - 1; } } } //--------------------------------------------------------------------------- // // FUNC MN_TextAWidth // // Returns the pixel width of a string using font A. // //--------------------------------------------------------------------------- int MN_TextAWidth(const char *text) { char c; int width; patch_t *p; width = 0; while ((c = *text++) != 0) { if (c < 33) { width += 5; } else { p = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); width += SHORT(p->width) - 1; } } return (width); } //--------------------------------------------------------------------------- // // PROC MN_DrTextB // // Draw text using font B. // //--------------------------------------------------------------------------- void MN_DrTextB(const char *text, int x, int y) { char c; patch_t *p; while ((c = *text++) != 0) { if (c < 33) { x += 8; } else { p = W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE); V_DrawPatch(x, y, p); x += SHORT(p->width) - 1; } } } //--------------------------------------------------------------------------- // // FUNC MN_TextBWidth // // Returns the pixel width of a string using font B. // //--------------------------------------------------------------------------- int MN_TextBWidth(const char *text) { char c; int width; patch_t *p; width = 0; while ((c = *text++) != 0) { if (c < 33) { width += 5; } else { p = W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE); width += SHORT(p->width) - 1; } } return (width); } //--------------------------------------------------------------------------- // // PROC MN_Ticker // //--------------------------------------------------------------------------- void MN_Ticker(void) { if (MenuActive == false) { return; } MenuTime++; } //--------------------------------------------------------------------------- // // PROC MN_Drawer // //--------------------------------------------------------------------------- const char *QuitEndMsg[] = { "ARE YOU SURE YOU WANT TO QUIT?", "ARE YOU SURE YOU WANT TO END THE GAME?", "DO YOU WANT TO QUICKSAVE THE GAME NAMED", "DO YOU WANT TO QUICKLOAD THE GAME NAMED" }; void MN_Drawer(void) { int i; int x; int y; MenuItem_t *item; const char *message; const char *selName; if (MenuActive == false) { if (askforquit) { message = DEH_String(QuitEndMsg[typeofask - 1]); MN_DrTextA(message, 160 - MN_TextAWidth(message) / 2, 80); if (typeofask == 3) { MN_DrTextA(SlotText[quicksave - 1], 160 - MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90); MN_DrTextA(DEH_String("?"), 160 + MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90); } if (typeofask == 4) { MN_DrTextA(SlotText[quickload - 1], 160 - MN_TextAWidth(SlotText[quickload - 1]) / 2, 90); MN_DrTextA(DEH_String("?"), 160 + MN_TextAWidth(SlotText[quickload - 1]) / 2, 90); } UpdateState |= I_FULLSCRN; } return; } else { UpdateState |= I_FULLSCRN; if (InfoType) { MN_DrawInfo(); return; } if (screenblocks < 10) { BorderNeedRefresh = true; } if (CurrentMenu->drawFunc != NULL) { CurrentMenu->drawFunc(); } x = CurrentMenu->x; y = CurrentMenu->y; item = CurrentMenu->items; for (i = 0; i < CurrentMenu->itemCount; i++) { if (item->type != ITT_EMPTY && item->text) { MN_DrTextB(DEH_String(item->text), x, y); } y += ITEM_HEIGHT; item++; } y = CurrentMenu->y + (CurrentItPos * ITEM_HEIGHT) + SELECTOR_YOFFSET; selName = DEH_String(MenuTime & 16 ? "M_SLCTR1" : "M_SLCTR2"); V_DrawPatch(x + SELECTOR_XOFFSET, y, W_CacheLumpName(selName, PU_CACHE)); } } //--------------------------------------------------------------------------- // // PROC DrawMainMenu // //--------------------------------------------------------------------------- static void DrawMainMenu(void) { int frame; frame = (MenuTime / 3) % 18; V_DrawPatch(88, 0, W_CacheLumpName(DEH_String("M_HTIC"), PU_CACHE)); V_DrawPatch(40, 10, W_CacheLumpNum(SkullBaseLump + (17 - frame), PU_CACHE)); V_DrawPatch(232, 10, W_CacheLumpNum(SkullBaseLump + frame, PU_CACHE)); } //--------------------------------------------------------------------------- // // PROC DrawEpisodeMenu // //--------------------------------------------------------------------------- static void DrawEpisodeMenu(void) { } //--------------------------------------------------------------------------- // // PROC DrawSkillMenu // //--------------------------------------------------------------------------- static void DrawSkillMenu(void) { } //--------------------------------------------------------------------------- // // PROC DrawFilesMenu // //--------------------------------------------------------------------------- static void DrawFilesMenu(void) { // clear out the quicksave/quickload stuff quicksave = 0; quickload = 0; players[consoleplayer].message = NULL; players[consoleplayer].messageTics = 1; } //--------------------------------------------------------------------------- // // PROC DrawLoadMenu // //--------------------------------------------------------------------------- static void DrawLoadMenu(void) { const char *title; title = DEH_String("LOAD GAME"); MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10); if (!slottextloaded) { MN_LoadSlotText(); } DrawFileSlots(&LoadMenu); } //--------------------------------------------------------------------------- // // PROC DrawSaveMenu // //--------------------------------------------------------------------------- static void DrawSaveMenu(void) { const char *title; title = DEH_String("SAVE GAME"); MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10); if (!slottextloaded) { MN_LoadSlotText(); } DrawFileSlots(&SaveMenu); } //=========================================================================== // // MN_LoadSlotText // // Loads in the text message for each slot //=========================================================================== void MN_LoadSlotText(void) { FILE *fp; int i; char *filename; for (i = 0; i < 6; i++) { int retval; filename = SV_Filename(i); fp = fopen(filename, "rb+"); free(filename); if (!fp) { SlotText[i][0] = 0; // empty the string SlotStatus[i] = 0; continue; } retval = fread(&SlotText[i], 1, SLOTTEXTLEN, fp); fclose(fp); SlotStatus[i] = retval == SLOTTEXTLEN; } slottextloaded = true; } //--------------------------------------------------------------------------- // // PROC DrawFileSlots // //--------------------------------------------------------------------------- static void DrawFileSlots(Menu_t * menu) { int i; int x; int y; x = menu->x; y = menu->y; for (i = 0; i < 6; i++) { V_DrawPatch(x, y, W_CacheLumpName(DEH_String("M_FSLOT"), PU_CACHE)); if (SlotStatus[i]) { MN_DrTextA(SlotText[i], x + 5, y + 5); } y += ITEM_HEIGHT; } } //--------------------------------------------------------------------------- // // PROC DrawOptionsMenu // //--------------------------------------------------------------------------- static void DrawOptionsMenu(void) { if (messageson) { MN_DrTextB(DEH_String("ON"), 196, 50); } else { MN_DrTextB(DEH_String("OFF"), 196, 50); } DrawSlider(&OptionsMenu, 3, 10, mouseSensitivity); } //--------------------------------------------------------------------------- // // PROC DrawOptions2Menu // //--------------------------------------------------------------------------- static void DrawOptions2Menu(void) { DrawSlider(&Options2Menu, 1, 9, screenblocks - 3); DrawSlider(&Options2Menu, 3, 16, snd_MaxVolume); DrawSlider(&Options2Menu, 5, 16, snd_MusicVolume); } //--------------------------------------------------------------------------- // // PROC SCNetCheck // //--------------------------------------------------------------------------- static boolean SCNetCheck(int option) { if (!netgame) { // okay to go into the menu return true; } switch (option) { case 1: P_SetMessage(&players[consoleplayer], "YOU CAN'T START A NEW GAME IN NETPLAY!", true); break; case 2: P_SetMessage(&players[consoleplayer], "YOU CAN'T LOAD A GAME IN NETPLAY!", true); break; default: break; } MenuActive = false; return false; } //--------------------------------------------------------------------------- // // PROC SCQuitGame // //--------------------------------------------------------------------------- static boolean SCQuitGame(int option) { MenuActive = false; askforquit = true; typeofask = 1; //quit game if (!netgame && !demoplayback) { paused = true; } return true; } //--------------------------------------------------------------------------- // // PROC SCEndGame // //--------------------------------------------------------------------------- static boolean SCEndGame(int option) { if (demoplayback || netgame) { return false; } MenuActive = false; askforquit = true; typeofask = 2; //endgame if (!netgame && !demoplayback) { paused = true; } return true; } //--------------------------------------------------------------------------- // // PROC SCMessages // //--------------------------------------------------------------------------- static boolean SCMessages(int option) { messageson ^= 1; if (messageson) { P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES ON"), true); } else { P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES OFF"), true); } S_StartSound(NULL, sfx_chat); return true; } //--------------------------------------------------------------------------- // // PROC SCLoadGame // //--------------------------------------------------------------------------- static boolean SCLoadGame(int option) { char *filename; if (!SlotStatus[option]) { // slot's empty...don't try and load return false; } filename = SV_Filename(option); G_LoadGame(filename); free(filename); MN_DeactivateMenu(); BorderNeedRefresh = true; if (quickload == -1) { quickload = option + 1; players[consoleplayer].message = NULL; players[consoleplayer].messageTics = 1; } return true; } //--------------------------------------------------------------------------- // // PROC SCSaveGame // //--------------------------------------------------------------------------- static boolean SCSaveGame(int option) { char *ptr; if (!FileMenuKeySteal) { int x, y; FileMenuKeySteal = true; // We need to activate the text input interface to type the save // game name: x = SaveMenu.x + 1; y = SaveMenu.y + 1 + option * ITEM_HEIGHT; I_StartTextInput(x, y, x + 190, y + ITEM_HEIGHT - 2); M_StringCopy(oldSlotText, SlotText[option], sizeof(oldSlotText)); ptr = SlotText[option]; while (*ptr) { ptr++; } *ptr = '['; *(ptr + 1) = 0; SlotStatus[option]++; currentSlot = option; slotptr = ptr - SlotText[option]; return false; } else { G_SaveGame(option, SlotText[option]); FileMenuKeySteal = false; I_StopTextInput(); MN_DeactivateMenu(); } BorderNeedRefresh = true; if (quicksave == -1) { quicksave = option + 1; players[consoleplayer].message = NULL; players[consoleplayer].messageTics = 1; } return true; } //--------------------------------------------------------------------------- // // PROC SCEpisode // //--------------------------------------------------------------------------- static boolean SCEpisode(int option) { if (gamemode == shareware && option > 1) { P_SetMessage(&players[consoleplayer], "ONLY AVAILABLE IN THE REGISTERED VERSION", true); } else { MenuEpisode = option; SetMenu(MENU_SKILL); } return true; } //--------------------------------------------------------------------------- // // PROC SCSkill // //--------------------------------------------------------------------------- static boolean SCSkill(int option) { G_DeferedInitNew(option, MenuEpisode, 1); MN_DeactivateMenu(); return true; } //--------------------------------------------------------------------------- // // PROC SCMouseSensi // //--------------------------------------------------------------------------- static boolean SCMouseSensi(int option) { if (option == RIGHT_DIR) { if (mouseSensitivity < 9) { mouseSensitivity++; } } else if (mouseSensitivity) { mouseSensitivity--; } return true; } //--------------------------------------------------------------------------- // // PROC SCSfxVolume // //--------------------------------------------------------------------------- static boolean SCSfxVolume(int option) { if (option == RIGHT_DIR) { if (snd_MaxVolume < 15) { snd_MaxVolume++; } } else if (snd_MaxVolume) { snd_MaxVolume--; } S_SetMaxVolume(false); // don't recalc the sound curve, yet soundchanged = true; // we'll set it when we leave the menu return true; } //--------------------------------------------------------------------------- // // PROC SCMusicVolume // //--------------------------------------------------------------------------- static boolean SCMusicVolume(int option) { if (option == RIGHT_DIR) { if (snd_MusicVolume < 15) { snd_MusicVolume++; } } else if (snd_MusicVolume) { snd_MusicVolume--; } S_SetMusicVolume(); return true; } //--------------------------------------------------------------------------- // // PROC SCScreenSize // //--------------------------------------------------------------------------- static boolean SCScreenSize(int option) { if (option == RIGHT_DIR) { if (screenblocks < 11) { screenblocks++; } } else if (screenblocks > 3) { screenblocks--; } R_SetViewSize(screenblocks, detailLevel); return true; } //--------------------------------------------------------------------------- // // PROC SCInfo // //--------------------------------------------------------------------------- static boolean SCInfo(int option) { InfoType = 1; S_StartSound(NULL, sfx_dorcls); if (!netgame && !demoplayback) { paused = true; } return true; } //--------------------------------------------------------------------------- // // FUNC MN_Responder // //--------------------------------------------------------------------------- boolean MN_Responder(event_t * event) { int charTyped; int key; int i; MenuItem_t *item; extern boolean automapactive; extern void D_StartTitle(void); extern void G_CheckDemoStatus(void); char *textBuffer; // In testcontrols mode, none of the function keys should do anything // - the only key is escape to quit. if (testcontrols) { if (event->type == ev_quit || (event->type == ev_keydown && (event->data1 == key_menu_activate || event->data1 == key_menu_quit))) { I_Quit(); return true; } return false; } // "close" button pressed on window? if (event->type == ev_quit) { // First click on close = bring up quit confirm message. // Second click = confirm quit. if (!MenuActive && askforquit && typeofask == 1) { G_CheckDemoStatus(); I_Quit(); } else { SCQuitGame(0); S_StartSound(NULL, sfx_chat); } return true; } // Allow the menu to be activated from a joystick button if a button // is bound for joybmenu. if (event->type == ev_joystick) { if (joybmenu >= 0 && (event->data1 & (1 << joybmenu)) != 0) { MN_ActivateMenu(); return true; } } if (event->type != ev_keydown) { return false; } key = event->data1; charTyped = event->data2; if (InfoType) { if (gamemode == shareware) { InfoType = (InfoType + 1) % 5; } else { InfoType = (InfoType + 1) % 4; } if (key == KEY_ESCAPE) { InfoType = 0; } if (!InfoType) { paused = false; MN_DeactivateMenu(); SB_state = -1; //refresh the statbar BorderNeedRefresh = true; } S_StartSound(NULL, sfx_dorcls); return (true); //make the info screen eat the keypress } if ((ravpic && key == KEY_F1) || (key != 0 && key == key_menu_screenshot)) { G_ScreenShot(); return (true); } if (askforquit) { if (key == key_menu_confirm) { switch (typeofask) { case 1: G_CheckDemoStatus(); I_Quit(); return false; case 2: players[consoleplayer].messageTics = 0; //set the msg to be cleared players[consoleplayer].message = NULL; paused = false; I_SetPalette(W_CacheLumpName ("PLAYPAL", PU_CACHE)); D_StartTitle(); // go to intro/demo mode. break; case 3: P_SetMessage(&players[consoleplayer], "QUICKSAVING....", false); FileMenuKeySteal = true; SCSaveGame(quicksave - 1); BorderNeedRefresh = true; break; case 4: P_SetMessage(&players[consoleplayer], "QUICKLOADING....", false); SCLoadGame(quickload - 1); BorderNeedRefresh = true; break; default: break; } askforquit = false; typeofask = 0; return true; } else if (key == key_menu_abort || key == KEY_ESCAPE) { players[consoleplayer].messageTics = 1; //set the msg to be cleared askforquit = false; typeofask = 0; paused = false; UpdateState |= I_FULLSCRN; BorderNeedRefresh = true; return true; } return false; // don't let the keys filter thru } if (!MenuActive && !chatmodeon) { if (key == key_menu_decscreen) { if (automapactive) { // Don't screen size in automap return (false); } SCScreenSize(LEFT_DIR); S_StartSound(NULL, sfx_keyup); BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; return (true); } else if (key == key_menu_incscreen) { if (automapactive) { // Don't screen size in automap return (false); } SCScreenSize(RIGHT_DIR); S_StartSound(NULL, sfx_keyup); BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; return (true); } else if (key == key_menu_help) // F1 { SCInfo(0); // start up info screens MenuActive = true; return (true); } else if (key == key_menu_save) // F2 (save game) { if (gamestate == GS_LEVEL && !demoplayback) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &SaveMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, sfx_dorcls); slottextloaded = false; //reload the slot text, when needed } return true; } else if (key == key_menu_load) // F3 (load game) { if (SCNetCheck(2)) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &LoadMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, sfx_dorcls); slottextloaded = false; //reload the slot text, when needed } return true; } else if (key == key_menu_volume) // F4 (volume) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &Options2Menu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, sfx_dorcls); slottextloaded = false; //reload the slot text, when needed return true; } else if (key == key_menu_detail) // F5 (detail) { // F5 isn't used in Heretic. (detail level) return true; } else if (key == key_menu_qsave) // F6 (quicksave) { if (gamestate == GS_LEVEL && !demoplayback) { if (!quicksave || quicksave == -1) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &SaveMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, sfx_dorcls); slottextloaded = false; //reload the slot text, when needed quicksave = -1; P_SetMessage(&players[consoleplayer], "CHOOSE A QUICKSAVE SLOT", true); } else { askforquit = true; typeofask = 3; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, sfx_chat); } } return true; } else if (key == key_menu_endgame) // F7 (end game) { if (gamestate == GS_LEVEL && !demoplayback) { S_StartSound(NULL, sfx_chat); SCEndGame(0); } return true; } else if (key == key_menu_messages) // F8 (toggle messages) { SCMessages(0); return true; } else if (key == key_menu_qload) // F9 (quickload) { if (!quickload || quickload == -1) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &LoadMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, sfx_dorcls); slottextloaded = false; //reload the slot text, when needed quickload = -1; P_SetMessage(&players[consoleplayer], "CHOOSE A QUICKLOAD SLOT", true); } else { askforquit = true; if (!netgame && !demoplayback) { paused = true; } typeofask = 4; S_StartSound(NULL, sfx_chat); } return true; } else if (key == key_menu_quit) // F10 (quit) { if (gamestate == GS_LEVEL) { SCQuitGame(0); S_StartSound(NULL, sfx_chat); } return true; } else if (key == key_menu_gamma) // F11 (gamma correction) { usegamma++; if (usegamma > 4+4) // [crispy] intermediate gamma levels { usegamma = 0; } I_SetPalette((byte *) W_CacheLumpName("PLAYPAL", PU_CACHE)); return true; } } if (!MenuActive) { if (key == key_menu_activate || gamestate == GS_DEMOSCREEN || demoplayback) { MN_ActivateMenu(); return (true); } return (false); } if (!FileMenuKeySteal) { item = &CurrentMenu->items[CurrentItPos]; if (key == key_menu_down) // Next menu item { do { if (CurrentItPos + 1 > CurrentMenu->itemCount - 1) { CurrentItPos = 0; } else { CurrentItPos++; } } while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY); S_StartSound(NULL, sfx_switch); return (true); } else if (key == key_menu_up) // Previous menu item { do { if (CurrentItPos == 0) { CurrentItPos = CurrentMenu->itemCount - 1; } else { CurrentItPos--; } } while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY); S_StartSound(NULL, sfx_switch); return (true); } else if (key == key_menu_left) // Slider left { if (item->type == ITT_LRFUNC && item->func != NULL) { item->func(LEFT_DIR); S_StartSound(NULL, sfx_keyup); } return (true); } else if (key == key_menu_right) // Slider right { if (item->type == ITT_LRFUNC && item->func != NULL) { item->func(RIGHT_DIR); S_StartSound(NULL, sfx_keyup); } return (true); } else if (key == key_menu_forward) // Activate item (enter) { if (item->type == ITT_SETMENU) { SetMenu(item->menu); } else if (item->func != NULL) { CurrentMenu->oldItPos = CurrentItPos; if (item->type == ITT_LRFUNC) { item->func(RIGHT_DIR); } else if (item->type == ITT_EFUNC) { if (item->func(item->option)) { if (item->menu != MENU_NONE) { SetMenu(item->menu); } } } } S_StartSound(NULL, sfx_dorcls); return (true); } else if (key == key_menu_activate) // Toggle menu { MN_DeactivateMenu(); return (true); } else if (key == key_menu_back) // Go back to previous menu { S_StartSound(NULL, sfx_switch); if (CurrentMenu->prevMenu == MENU_NONE) { MN_DeactivateMenu(); } else { SetMenu(CurrentMenu->prevMenu); } return (true); } else if (charTyped != 0) { // Jump to menu item based on first letter: for (i = 0; i < CurrentMenu->itemCount; i++) { if (CurrentMenu->items[i].text) { if (toupper(charTyped) == toupper(DEH_String(CurrentMenu->items[i].text)[0])) { CurrentItPos = i; return (true); } } } } return (false); } else { // Editing file names // When typing a savegame name, we use the fully shifted and // translated input value from event->data3. charTyped = event->data3; textBuffer = &SlotText[currentSlot][slotptr]; if (key == KEY_BACKSPACE) { if (slotptr) { *textBuffer-- = 0; *textBuffer = ASCII_CURSOR; slotptr--; } return (true); } if (key == KEY_ESCAPE) { memset(SlotText[currentSlot], 0, SLOTTEXTLEN + 2); M_StringCopy(SlotText[currentSlot], oldSlotText, sizeof(SlotText[currentSlot])); SlotStatus[currentSlot]--; MN_DeactivateMenu(); return (true); } if (key == KEY_ENTER) { SlotText[currentSlot][slotptr] = 0; // clear the cursor item = &CurrentMenu->items[CurrentItPos]; CurrentMenu->oldItPos = CurrentItPos; if (item->type == ITT_EFUNC) { item->func(item->option); if (item->menu != MENU_NONE) { SetMenu(item->menu); } } return (true); } if (slotptr < SLOTTEXTLEN && key != KEY_BACKSPACE) { if (isalpha(charTyped)) { *textBuffer++ = toupper(charTyped); *textBuffer = ASCII_CURSOR; slotptr++; return (true); } if (isdigit(charTyped) || charTyped == ' ' || charTyped == ',' || charTyped == '.' || charTyped == '-' || charTyped == '!') { *textBuffer++ = charTyped; *textBuffer = ASCII_CURSOR; slotptr++; return (true); } } return (true); } return (false); } //--------------------------------------------------------------------------- // // PROC MN_ActivateMenu // //--------------------------------------------------------------------------- void MN_ActivateMenu(void) { if (MenuActive) { return; } if (paused) { S_ResumeSound(); } MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &MainMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, sfx_dorcls); slottextloaded = false; //reload the slot text, when needed } //--------------------------------------------------------------------------- // // PROC MN_DeactivateMenu // //--------------------------------------------------------------------------- void MN_DeactivateMenu(void) { if (CurrentMenu != NULL) { CurrentMenu->oldItPos = CurrentItPos; } MenuActive = false; if (FileMenuKeySteal) { I_StopTextInput(); } if (!netgame) { paused = false; } S_StartSound(NULL, sfx_dorcls); if (soundchanged) { S_SetMaxVolume(true); //recalc the sound curve soundchanged = false; } players[consoleplayer].message = NULL; players[consoleplayer].messageTics = 1; } //--------------------------------------------------------------------------- // // PROC MN_DrawInfo // //--------------------------------------------------------------------------- void MN_DrawInfo(void) { I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); V_DrawRawScreen(W_CacheLumpNum(W_GetNumForName("TITLE") + InfoType, PU_CACHE)); // V_DrawPatch(0, 0, W_CacheLumpNum(W_GetNumForName("TITLE")+InfoType, // PU_CACHE)); } //--------------------------------------------------------------------------- // // PROC SetMenu // //--------------------------------------------------------------------------- static void SetMenu(MenuType_t menu) { CurrentMenu->oldItPos = CurrentItPos; CurrentMenu = Menus[menu]; CurrentItPos = CurrentMenu->oldItPos; } //--------------------------------------------------------------------------- // // PROC DrawSlider // //--------------------------------------------------------------------------- static void DrawSlider(Menu_t * menu, int item, int width, int slot) { int x; int y; int x2; int count; x = menu->x + 24; y = menu->y + 2 + (item * ITEM_HEIGHT); V_DrawPatch(x - 32, y, W_CacheLumpName(DEH_String("M_SLDLT"), PU_CACHE)); for (x2 = x, count = width; count--; x2 += 8) { V_DrawPatch(x2, y, W_CacheLumpName(DEH_String(count & 1 ? "M_SLDMD1" : "M_SLDMD2"), PU_CACHE)); } V_DrawPatch(x2, y, W_CacheLumpName(DEH_String("M_SLDRT"), PU_CACHE)); V_DrawPatch(x + 4 + slot * 8, y + 7, W_CacheLumpName(DEH_String("M_SLDKB"), PU_CACHE)); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_action.h000066400000000000000000000067631360717211000224270ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // External definitions for action pointer functions. // #ifndef HERETIC_P_ACTION_H #define HERETIC_P_ACTION_H void A_FreeTargMobj(); void A_RestoreSpecialThing1(); void A_RestoreSpecialThing2(); void A_HideThing(); void A_UnHideThing(); void A_RestoreArtifact(); void A_Scream(); void A_Explode(); void A_PodPain(); void A_RemovePod(); void A_MakePod(); void A_InitKeyGizmo(); void A_VolcanoSet(); void A_VolcanoBlast(); void A_BeastPuff(); void A_VolcBallImpact(); void A_SpawnTeleGlitter(); void A_SpawnTeleGlitter2(); void A_AccTeleGlitter(); void A_Light0(); void A_WeaponReady(); void A_Lower(); void A_Raise(); void A_StaffAttackPL1(); void A_ReFire(); void A_StaffAttackPL2(); void A_BeakReady(); void A_BeakRaise(); void A_BeakAttackPL1(); void A_BeakAttackPL2(); void A_GauntletAttack(); void A_FireBlasterPL1(); void A_FireBlasterPL2(); void A_SpawnRippers(); void A_FireMacePL1(); void A_FireMacePL2(); void A_MacePL1Check(); void A_MaceBallImpact(); void A_MaceBallImpact2(); void A_DeathBallImpact(); void A_FireSkullRodPL1(); void A_FireSkullRodPL2(); void A_SkullRodPL2Seek(); void A_AddPlayerRain(); void A_HideInCeiling(); void A_SkullRodStorm(); void A_RainImpact(); void A_FireGoldWandPL1(); void A_FireGoldWandPL2(); void A_FirePhoenixPL1(); void A_InitPhoenixPL2(); void A_FirePhoenixPL2(); void A_ShutdownPhoenixPL2(); void A_PhoenixPuff(); void A_RemovedPhoenixFunc(); void A_FlameEnd(); void A_FloatPuff(); void A_FireCrossbowPL1(); void A_FireCrossbowPL2(); void A_BoltSpark(); void A_Pain(); void A_NoBlocking(); void A_AddPlayerCorpse(); void A_SkullPop(); void A_FlameSnd(); void A_CheckBurnGone(); void A_CheckSkullFloor(); void A_CheckSkullDone(); void A_Feathers(); void A_ChicLook(); void A_ChicChase(); void A_ChicPain(); void A_FaceTarget(); void A_ChicAttack(); void A_Look(); void A_Chase(); void A_MummyAttack(); void A_MummyAttack2(); void A_MummySoul(); void A_ContMobjSound(); void A_MummyFX1Seek(); void A_BeastAttack(); void A_SnakeAttack(); void A_SnakeAttack2(); void A_HeadAttack(); void A_BossDeath(); void A_HeadIceImpact(); void A_HeadFireGrow(); void A_WhirlwindSeek(); void A_ClinkAttack(); void A_WizAtk1(); void A_WizAtk2(); void A_WizAtk3(); void A_GhostOff(); void A_ImpMeAttack(); void A_ImpMsAttack(); void A_ImpMsAttack2(); void A_ImpDeath(); void A_ImpXDeath1(); void A_ImpXDeath2(); void A_ImpExplode(); void A_KnightAttack(); void A_DripBlood(); void A_Sor1Chase(); void A_Sor1Pain(); void A_Srcr1Attack(); void A_SorZap(); void A_SorcererRise(); void A_SorRise(); void A_SorSightSnd(); void A_Srcr2Decide(); void A_Srcr2Attack(); void A_Sor2DthInit(); void A_SorDSph(); void A_Sor2DthLoop(); void A_SorDExp(); void A_SorDBon(); void A_BlueSpark(); void A_GenWizard(); void A_MinotaurAtk1(); void A_MinotaurDecide(); void A_MinotaurAtk2(); void A_MinotaurAtk3(); void A_MinotaurCharge(); void A_MntrFloorFire(); void A_ESound(); #endif /* #ifndef HERETIC_P_ACTION_H */ crispy-doom-crispy-doom-5.6.4/src/heretic/p_ceilng.c000066400000000000000000000174731360717211000224060ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" //================================================================== //================================================================== // // CEILINGS // //================================================================== //================================================================== ceiling_t *activeceilings[MAXCEILINGS]; //================================================================== // // T_MoveCeiling // //================================================================== void T_MoveCeiling(ceiling_t * ceiling) { result_e res; switch (ceiling->direction) { case 0: // IN STASIS break; case 1: // UP res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false, 1, ceiling->direction); if (!(leveltime & 7)) S_StartSound(&ceiling->sector->soundorg, sfx_dormov); if (res == pastdest) switch (ceiling->type) { case raiseToHighest: P_RemoveActiveCeiling(ceiling); break; case fastCrushAndRaise: case crushAndRaise: ceiling->direction = -1; break; default: break; } break; case -1: // DOWN res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush, 1, ceiling->direction); if (!(leveltime & 7)) S_StartSound(&ceiling->sector->soundorg, sfx_dormov); if (res == pastdest) switch (ceiling->type) { case crushAndRaise: ceiling->speed = CEILSPEED; case fastCrushAndRaise: ceiling->direction = 1; break; case lowerAndCrush: case lowerToFloor: P_RemoveActiveCeiling(ceiling); break; default: break; } else if (res == crushed) switch (ceiling->type) { case crushAndRaise: case lowerAndCrush: ceiling->speed = CEILSPEED / 8; break; default: break; } break; } } //================================================================== // // EV_DoCeiling // Move a ceiling up/down and all around! // //================================================================== int EV_DoCeiling(line_t * line, ceiling_e type) { int secnum, rtn; sector_t *sec; ceiling_t *ceiling; secnum = -1; rtn = 0; // // Reactivate in-stasis ceilings...for certain types. // switch (type) { case fastCrushAndRaise: case crushAndRaise: P_ActivateInStasisCeiling(line); default: break; } while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // // new door thinker // rtn = 1; ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVSPEC, 0); P_AddThinker(&ceiling->thinker); sec->specialdata = ceiling; ceiling->thinker.function = T_MoveCeiling; ceiling->sector = sec; ceiling->crush = false; switch (type) { case fastCrushAndRaise: ceiling->crush = true; ceiling->topheight = sec->ceilingheight; ceiling->bottomheight = sec->floorheight + (8 * FRACUNIT); ceiling->direction = -1; ceiling->speed = CEILSPEED * 2; break; case crushAndRaise: ceiling->crush = true; ceiling->topheight = sec->ceilingheight; case lowerAndCrush: case lowerToFloor: ceiling->bottomheight = sec->floorheight; if (type != lowerToFloor) ceiling->bottomheight += 8 * FRACUNIT; ceiling->direction = -1; ceiling->speed = CEILSPEED; break; case raiseToHighest: ceiling->topheight = P_FindHighestCeilingSurrounding(sec); ceiling->direction = 1; ceiling->speed = CEILSPEED; break; } ceiling->tag = sec->tag; ceiling->type = type; P_AddActiveCeiling(ceiling); } return rtn; } //================================================================== // // Add an active ceiling // //================================================================== void P_AddActiveCeiling(ceiling_t * c) { int i; for (i = 0; i < MAXCEILINGS; i++) if (activeceilings[i] == NULL) { activeceilings[i] = c; return; } } //================================================================== // // Remove a ceiling's thinker // //================================================================== void P_RemoveActiveCeiling(ceiling_t * c) { int i; for (i = 0; i < MAXCEILINGS; i++) if (activeceilings[i] == c) { activeceilings[i]->sector->specialdata = NULL; P_RemoveThinker(&activeceilings[i]->thinker); activeceilings[i] = NULL; break; } } //================================================================== // // Restart a ceiling that's in-stasis // //================================================================== void P_ActivateInStasisCeiling(line_t * line) { int i; for (i = 0; i < MAXCEILINGS; i++) if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction == 0)) { activeceilings[i]->direction = activeceilings[i]->olddirection; activeceilings[i]->thinker.function = T_MoveCeiling; } } //================================================================== // // EV_CeilingCrushStop // Stop a ceiling from crushing! // //================================================================== int EV_CeilingCrushStop(line_t * line) { int i; int rtn; rtn = 0; for (i = 0; i < MAXCEILINGS; i++) if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction != 0)) { activeceilings[i]->olddirection = activeceilings[i]->direction; activeceilings[i]->thinker.function = NULL; activeceilings[i]->direction = 0; // in-stasis rtn = 1; } return rtn; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_doors.c000066400000000000000000000266361360717211000222740ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_doors.c #include "doomdef.h" #include "deh_str.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" //================================================================== //================================================================== // // VERTICAL DOORS // //================================================================== //================================================================== //================================================================== // // T_VerticalDoor // //================================================================== void T_VerticalDoor(vldoor_t * door) { result_e res; switch (door->direction) { case 0: // WAITING if (!--door->topcountdown) switch (door->type) { case vld_normal: door->direction = -1; // time to go back down S_StartSound(&door->sector->soundorg, sfx_doropn); break; case vld_close30ThenOpen: door->direction = 1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } break; case 2: // INITIAL WAIT if (!--door->topcountdown) { switch (door->type) { case vld_raiseIn5Mins: door->direction = 1; door->type = vld_normal; S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } } break; case -1: // DOWN res = T_MovePlane(door->sector, door->speed, door->sector->floorheight, false, 1, door->direction); if (res == pastdest) { switch (door->type) { case vld_normal: case vld_close: door->sector->specialdata = NULL; P_RemoveThinker(&door->thinker); // unlink and free S_StartSound(&door->sector->soundorg, sfx_dorcls); break; case vld_close30ThenOpen: door->direction = 0; door->topcountdown = 35 * 30; break; default: break; } } else if (res == crushed) { switch (door->type) { case vld_close: // DON'T GO BACK UP! break; default: door->direction = 1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; } } break; case 1: // UP res = T_MovePlane(door->sector, door->speed, door->topheight, false, 1, door->direction); if (res == pastdest) { switch (door->type) { case vld_normal: door->direction = 0; // wait at top door->topcountdown = door->topwait; break; case vld_close30ThenOpen: case vld_open: door->sector->specialdata = NULL; P_RemoveThinker(&door->thinker); // unlink and free S_StopSound(&door->sector->soundorg); break; default: break; } } break; } } //---------------------------------------------------------------------------- // // EV_DoDoor // // Move a door up/down // //---------------------------------------------------------------------------- int EV_DoDoor(line_t * line, vldoor_e type, fixed_t speed) { int secnum; int retcode; sector_t *sec; vldoor_t *door; secnum = -1; retcode = 0; while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) { continue; } // Add new door thinker retcode = 1; door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; door->thinker.function = T_VerticalDoor; door->sector = sec; switch (type) { case vld_close: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4 * FRACUNIT; door->direction = -1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; case vld_close30ThenOpen: door->topheight = sec->ceilingheight; door->direction = -1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; case vld_normal: case vld_open: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4 * FRACUNIT; if (door->topheight != sec->ceilingheight) { S_StartSound(&door->sector->soundorg, sfx_doropn); } break; default: break; } door->type = type; door->speed = speed; door->topwait = VDOORWAIT; } return (retcode); } //================================================================== // // EV_VerticalDoor : open a door manually, no tag value // //================================================================== void EV_VerticalDoor(line_t * line, mobj_t * thing) { player_t *player; sector_t *sec; vldoor_t *door; int side; side = 0; // only front sides can be used // // Check for locks // player = thing->player; switch (line->special) { case 26: // Blue Lock case 32: if (!player) { return; } if (!player->keys[key_blue]) { P_SetMessage(player, DEH_String(TXT_NEEDBLUEKEY), false); S_StartSound(NULL, sfx_plroof); return; } break; case 27: // Yellow Lock case 34: if (!player) { return; } if (!player->keys[key_yellow]) { P_SetMessage(player, DEH_String(TXT_NEEDYELLOWKEY), false); S_StartSound(NULL, sfx_plroof); return; } break; case 28: // Green Lock case 33: if (!player) { return; } if (!player->keys[key_green]) { P_SetMessage(player, DEH_String(TXT_NEEDGREENKEY), false); S_StartSound(NULL, sfx_plroof); return; } break; } // if the sector has an active thinker, use it sec = sides[line->sidenum[side ^ 1]].sector; if (sec->specialdata) { door = sec->specialdata; switch (line->special) { case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s case 26: case 27: case 28: if (door->direction == -1) { door->direction = 1; // go back up } else { if (!thing->player) { // Monsters don't close doors return; } door->direction = -1; // start going down immediately } return; } } // for proper sound switch (line->special) { case 1: // NORMAL DOOR SOUND case 31: S_StartSound(&sec->soundorg, sfx_doropn); //S_StartSound(&sec->soundorg, sfx_dormov); break; default: // LOCKED DOOR SOUND S_StartSound(&sec->soundorg, sfx_doropn); //S_StartSound(&sec->soundorg, sfx_dormov); break; } // // new door thinker // door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 1; switch (line->special) { case 1: case 26: case 27: case 28: door->type = vld_normal; break; case 31: case 32: case 33: case 34: door->type = vld_open; line->special = 0; break; } door->speed = VDOORSPEED; door->topwait = VDOORWAIT; // // find the top and bottom of the movement range // door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4 * FRACUNIT; } //================================================================== // // Spawn a door that closes after 30 seconds // //================================================================== void P_SpawnDoorCloseIn30(sector_t * sec) { vldoor_t *door; door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 0; door->type = vld_normal; door->speed = VDOORSPEED; door->topcountdown = 30 * 35; } //================================================================== // // Spawn a door that opens after 5 minutes // //================================================================== void P_SpawnDoorRaiseIn5Mins(sector_t * sec, int secnum) { vldoor_t *door; door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 2; door->type = vld_raiseIn5Mins; door->speed = VDOORSPEED; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4 * FRACUNIT; door->topwait = VDOORWAIT; door->topcountdown = 5 * 60 * 35; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_enemy.c000066400000000000000000002065631360717211000222620ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_enemy.c #include #include "doomdef.h" #include "i_system.h" #include "i_timer.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" // Macros #define MAX_BOSS_SPOTS 8 // Types typedef struct { fixed_t x; fixed_t y; angle_t angle; } BossSpot_t; // Private Data static int BossSpotCount; static BossSpot_t BossSpots[MAX_BOSS_SPOTS]; //---------------------------------------------------------------------------- // // PROC P_InitMonsters // // Called at level load. // //---------------------------------------------------------------------------- void P_InitMonsters(void) { BossSpotCount = 0; } //---------------------------------------------------------------------------- // // PROC P_AddBossSpot // //---------------------------------------------------------------------------- void P_AddBossSpot(fixed_t x, fixed_t y, angle_t angle) { if (BossSpotCount == MAX_BOSS_SPOTS) { I_Error("Too many boss spots."); } BossSpots[BossSpotCount].x = x; BossSpots[BossSpotCount].y = y; BossSpots[BossSpotCount].angle = angle; BossSpotCount++; } //---------------------------------------------------------------------------- // // PROC P_RecursiveSound // //---------------------------------------------------------------------------- mobj_t *soundtarget; void P_RecursiveSound(sector_t * sec, int soundblocks) { int i; line_t *check; sector_t *other; // Wake up all monsters in this sector if (sec->validcount == validcount && sec->soundtraversed <= soundblocks + 1) { // Already flooded return; } sec->validcount = validcount; sec->soundtraversed = soundblocks + 1; sec->soundtarget = soundtarget; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; if (!(check->flags & ML_TWOSIDED)) { continue; } P_LineOpening(check); if (openrange <= 0) { // Closed door continue; } if (sides[check->sidenum[0]].sector == sec) { other = sides[check->sidenum[1]].sector; } else { other = sides[check->sidenum[0]].sector; } if (check->flags & ML_SOUNDBLOCK) { if (!soundblocks) { P_RecursiveSound(other, 1); } } else { P_RecursiveSound(other, soundblocks); } } } //---------------------------------------------------------------------------- // // PROC P_NoiseAlert // // If a monster yells at a player, it will alert other monsters to the // player. // //---------------------------------------------------------------------------- void P_NoiseAlert(mobj_t * target, mobj_t * emmiter) { soundtarget = target; validcount++; P_RecursiveSound(emmiter->subsector->sector, 0); } //---------------------------------------------------------------------------- // // FUNC P_CheckMeleeRange // //---------------------------------------------------------------------------- boolean P_CheckMeleeRange(mobj_t * actor) { mobj_t *mo; fixed_t dist; if (!actor->target) { return (false); } mo = actor->target; dist = P_AproxDistance(mo->x - actor->x, mo->y - actor->y); if (dist >= MELEERANGE) { return (false); } if (!P_CheckSight(actor, mo)) { return (false); } if (mo->z > actor->z + actor->height) { // Target is higher than the attacker return (false); } else if (actor->z > mo->z + mo->height) { // Attacker is higher return (false); } return (true); } //---------------------------------------------------------------------------- // // FUNC P_CheckMissileRange // //---------------------------------------------------------------------------- boolean P_CheckMissileRange(mobj_t * actor) { fixed_t dist; if (!P_CheckSight(actor, actor->target)) { return (false); } if (actor->flags & MF_JUSTHIT) { // The target just hit the enemy, so fight back! actor->flags &= ~MF_JUSTHIT; return (true); } if (actor->reactiontime) { // Don't attack yet return (false); } dist = (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) >> FRACBITS) - 64; if (!actor->info->meleestate) { // No melee attack, so fire more frequently dist -= 128; } if (actor->type == MT_IMP) { // Imp's fly attack from far away dist >>= 1; } if (dist > 200) { dist = 200; } if (P_Random() < dist) { return (false); } return (true); } /* ================ = = P_Move = = Move in the current direction = returns false if the move is blocked ================ */ fixed_t xspeed[8] = { FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000, 0, 47000 }; fixed_t yspeed[8] = { 0, 47000, FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000 }; #define MAXSPECIALCROSS 8 extern line_t *spechit[MAXSPECIALCROSS]; extern int numspechit; boolean P_Move(mobj_t * actor) { fixed_t tryx, tryy; line_t *ld; boolean good; if (actor->movedir == DI_NODIR) { return (false); } tryx = actor->x + actor->info->speed * xspeed[actor->movedir]; tryy = actor->y + actor->info->speed * yspeed[actor->movedir]; if (!P_TryMove(actor, tryx, tryy)) { // open any specials if (actor->flags & MF_FLOAT && floatok) { // must adjust height if (actor->z < tmfloorz) { actor->z += FLOATSPEED; } else { actor->z -= FLOATSPEED; } actor->flags |= MF_INFLOAT; return (true); } if (!numspechit) { return false; } actor->movedir = DI_NODIR; good = false; while (numspechit--) { ld = spechit[numspechit]; // if the special isn't a door that can be opened, return false if (P_UseSpecialLine(actor, ld)) { good = true; } } return (good); } else { actor->flags &= ~MF_INFLOAT; } if (!(actor->flags & MF_FLOAT)) { if (actor->z > actor->floorz) { P_HitFloor(actor); } actor->z = actor->floorz; } return (true); } //---------------------------------------------------------------------------- // // FUNC P_TryWalk // // Attempts to move actor in its current (ob->moveangle) direction. // If blocked by either a wall or an actor returns FALSE. // If move is either clear of block only by a door, returns TRUE and sets. // If a door is in the way, an OpenDoor call is made to start it opening. // //---------------------------------------------------------------------------- boolean P_TryWalk(mobj_t * actor) { if (!P_Move(actor)) { return (false); } actor->movecount = P_Random() & 15; return (true); } /* ================ = = P_NewChaseDir = ================ */ dirtype_t opposite[] = { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR }; dirtype_t diags[] = { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST }; void P_NewChaseDir(mobj_t * actor) { fixed_t deltax, deltay; dirtype_t d[3]; dirtype_t tdir, olddir, turnaround; if (!actor->target) I_Error("P_NewChaseDir: called with no target"); olddir = actor->movedir; turnaround = opposite[olddir]; deltax = actor->target->x - actor->x; deltay = actor->target->y - actor->y; if (deltax > 10 * FRACUNIT) d[1] = DI_EAST; else if (deltax < -10 * FRACUNIT) d[1] = DI_WEST; else d[1] = DI_NODIR; if (deltay < -10 * FRACUNIT) d[2] = DI_SOUTH; else if (deltay > 10 * FRACUNIT) d[2] = DI_NORTH; else d[2] = DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { actor->movedir = diags[((deltay < 0) << 1) + (deltax > 0)]; if (actor->movedir != turnaround && P_TryWalk(actor)) return; } // try other directions if (P_Random() > 200 || abs(deltay) > abs(deltax)) { tdir = d[1]; d[1] = d[2]; d[2] = tdir; } if (d[1] == turnaround) d[1] = DI_NODIR; if (d[2] == turnaround) d[2] = DI_NODIR; if (d[1] != DI_NODIR) { actor->movedir = d[1]; if (P_TryWalk(actor)) return; /*either moved forward or attacked */ } if (d[2] != DI_NODIR) { actor->movedir = d[2]; if (P_TryWalk(actor)) return; } /* there is no direct path to the player, so pick another direction */ if (olddir != DI_NODIR) { actor->movedir = olddir; if (P_TryWalk(actor)) return; } if (P_Random() & 1) /*randomly determine direction of search */ { for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) { if (tdir != turnaround) { actor->movedir = tdir; if (P_TryWalk(actor)) return; } } } else { // Iterate over all movedirs. tdir = DI_SOUTHEAST; for (;;) { if (tdir != turnaround) { actor->movedir = tdir; if (P_TryWalk(actor)) return; } if (tdir == DI_EAST) { break; } --tdir; } } if (turnaround != DI_NODIR) { actor->movedir = turnaround; if (P_TryWalk(actor)) return; } actor->movedir = DI_NODIR; // can't move } //--------------------------------------------------------------------------- // // FUNC P_LookForMonsters // //--------------------------------------------------------------------------- #define MONS_LOOK_RANGE (20*64*FRACUNIT) #define MONS_LOOK_LIMIT 64 boolean P_LookForMonsters(mobj_t * actor) { int count; mobj_t *mo; thinker_t *think; if (!P_CheckSight(players[0].mo, actor)) { // Player can't see monster return (false); } count = 0; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *) think; if (!(mo->flags & MF_COUNTKILL) || (mo == actor) || (mo->health <= 0)) { // Not a valid monster continue; } if (P_AproxDistance(actor->x - mo->x, actor->y - mo->y) > MONS_LOOK_RANGE) { // Out of range continue; } if (P_Random() < 16) { // Skip continue; } if (count++ > MONS_LOOK_LIMIT) { // Stop searching return (false); } if (!P_CheckSight(actor, mo)) { // Out of sight continue; } // Found a target monster actor->target = mo; return (true); } return (false); } /* ================ = = P_LookForPlayers = = If allaround is false, only look 180 degrees in front = returns true if a player is targeted ================ */ boolean P_LookForPlayers(mobj_t * actor, boolean allaround) { int c; int stop; player_t *player; angle_t an; fixed_t dist; if (!netgame && players[0].health <= 0) { // Single player game and player is dead, look for monsters return (P_LookForMonsters(actor)); } c = 0; stop = (actor->lastlook - 1) & 3; for (;; actor->lastlook = (actor->lastlook + 1) & 3) { if (!playeringame[actor->lastlook]) continue; if (c++ == 2 || actor->lastlook == stop) return false; // done looking player = &players[actor->lastlook]; if (player->health <= 0) continue; // dead if (!P_CheckSight(actor, player->mo)) continue; // out of sight if (!allaround) { an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; if (an > ANG90 && an < ANG270) { dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y); // if real close, react anyway if (dist > MELEERANGE) continue; // behind back } } if (player->mo->flags & MF_SHADOW) { // Player is invisible if ((P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) > 2 * MELEERANGE) && P_AproxDistance(player->mo->momx, player->mo->momy) < 5 * FRACUNIT) { // Player is sneaking - can't detect return (false); } if (P_Random() < 225) { // Player isn't sneaking, but still didn't detect return (false); } } actor->target = player->mo; return (true); } return (false); } /* =============================================================================== ACTION ROUTINES =============================================================================== */ /* ============== = = A_Look = = Stay in state until a player is sighted = ============== */ void A_Look(mobj_t * actor) { mobj_t *targ; actor->threshold = 0; // any shot will wake up targ = actor->subsector->sector->soundtarget; if (targ && (targ->flags & MF_SHOOTABLE)) { actor->target = targ; if (actor->flags & MF_AMBUSH) { if (P_CheckSight(actor, actor->target)) goto seeyou; } else goto seeyou; } if (!P_LookForPlayers(actor, false)) return; // go into chase state seeyou: if (actor->info->seesound) { int sound; /* switch (actor->info->seesound) { case sfx_posit1: case sfx_posit2: case sfx_posit3: sound = sfx_posit1+P_Random()%3; break; case sfx_bgsit1: case sfx_bgsit2: sound = sfx_bgsit1+P_Random()%2; break; default: sound = actor->info->seesound; break; } */ sound = actor->info->seesound; if (actor->flags2 & MF2_BOSS) { // Full volume S_StartSound(NULL, sound); } else { S_StartSound(actor, sound); } } P_SetMobjState(actor, actor->info->seestate); } /* ============== = = A_Chase = = Actor has a melee attack, so it tries to close as fast as possible = ============== */ void A_Chase(mobj_t * actor) { int delta; if (actor->reactiontime) { actor->reactiontime--; } // Modify target threshold if (actor->threshold) { actor->threshold--; } if (gameskill == sk_nightmare) { // Monsters move faster in nightmare mode actor->tics -= actor->tics / 2; if (actor->tics < 3) { actor->tics = 3; } } // // turn towards movement direction if not there yet // if (actor->movedir < 8) { actor->angle &= (7 << 29); delta = actor->angle - (actor->movedir << 29); if (delta > 0) { actor->angle -= ANG90 / 2; } else if (delta < 0) { actor->angle += ANG90 / 2; } } if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) { // look for a new target if (P_LookForPlayers(actor, true)) { // got a new target return; } P_SetMobjState(actor, actor->info->spawnstate); return; } // // don't attack twice in a row // if (actor->flags & MF_JUSTATTACKED) { actor->flags &= ~MF_JUSTATTACKED; if (gameskill != sk_nightmare) P_NewChaseDir(actor); return; } // // check for melee attack // if (actor->info->meleestate && P_CheckMeleeRange(actor)) { if (actor->info->attacksound) S_StartSound(actor, actor->info->attacksound); P_SetMobjState(actor, actor->info->meleestate); return; } // // check for missile attack // if (actor->info->missilestate) { if (gameskill < sk_nightmare && actor->movecount) goto nomissile; if (!P_CheckMissileRange(actor)) goto nomissile; P_SetMobjState(actor, actor->info->missilestate); actor->flags |= MF_JUSTATTACKED; return; } nomissile: // // possibly choose another target // if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target)) { if (P_LookForPlayers(actor, true)) return; // got a new target } // // chase towards player // if (--actor->movecount < 0 || !P_Move(actor)) { P_NewChaseDir(actor); } // // make active sound // if (actor->info->activesound && P_Random() < 3) { if (actor->type == MT_WIZARD && P_Random() < 128) { S_StartSound(actor, actor->info->seesound); } else if (actor->type == MT_SORCERER2) { S_StartSound(NULL, actor->info->activesound); } else { S_StartSound(actor, actor->info->activesound); } } } //---------------------------------------------------------------------------- // // PROC 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) { // Target is a ghost actor->angle += P_SubRandom() << 21; } } //---------------------------------------------------------------------------- // // PROC A_Pain // //---------------------------------------------------------------------------- void A_Pain(mobj_t * actor) { if (actor->info->painsound) { S_StartSound(actor, actor->info->painsound); } } //---------------------------------------------------------------------------- // // PROC A_DripBlood // //---------------------------------------------------------------------------- void A_DripBlood(mobj_t * actor) { mobj_t *mo; int r1,r2; r1 = P_SubRandom(); r2 = P_SubRandom(); mo = P_SpawnMobj(actor->x + (r2 << 11), actor->y + (r1 << 11), actor->z, MT_BLOOD); mo->momx = P_SubRandom() << 10; mo->momy = P_SubRandom() << 10; mo->flags2 |= MF2_LOGRAV; } //---------------------------------------------------------------------------- // // PROC A_KnightAttack // //---------------------------------------------------------------------------- void A_KnightAttack(mobj_t * actor) { if (!actor->target) { return; } if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(3)); S_StartSound(actor, sfx_kgtat2); return; } // Throw axe S_StartSound(actor, actor->info->attacksound); if (actor->type == MT_KNIGHTGHOST || P_Random() < 40) { // Red axe P_SpawnMissile(actor, actor->target, MT_REDAXE); return; } // Green axe P_SpawnMissile(actor, actor->target, MT_KNIGHTAXE); } //---------------------------------------------------------------------------- // // PROC A_ImpExplode // //---------------------------------------------------------------------------- void A_ImpExplode(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK1); mo->momx = P_SubRandom() << 10; mo->momy = P_SubRandom() << 10; mo->momz = 9 * FRACUNIT; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK2); mo->momx = P_SubRandom() << 10; mo->momy = P_SubRandom() << 10; mo->momz = 9 * FRACUNIT; if (actor->special1.i == 666) { // Extreme death crash P_SetMobjState(actor, S_IMP_XCRASH1); } } //---------------------------------------------------------------------------- // // PROC A_BeastPuff // //---------------------------------------------------------------------------- void A_BeastPuff(mobj_t * actor) { if (P_Random() > 64) { int r1,r2,r3; r1 = P_SubRandom(); r2 = P_SubRandom(); r3 = P_SubRandom(); P_SpawnMobj(actor->x + (r3 << 10), actor->y + (r2 << 10), actor->z + (r1 << 10), MT_PUFFY); } } //---------------------------------------------------------------------------- // // PROC A_ImpMeAttack // //---------------------------------------------------------------------------- void A_ImpMeAttack(mobj_t * actor) { if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, 5 + (P_Random() & 7)); } } //---------------------------------------------------------------------------- // // PROC A_ImpMsAttack // //---------------------------------------------------------------------------- void A_ImpMsAttack(mobj_t * actor) { mobj_t *dest; angle_t an; int dist; if (!actor->target || P_Random() > 64) { P_SetMobjState(actor, actor->info->seestate); return; } dest = actor->target; actor->flags |= MF_SKULLFLY; S_StartSound(actor, actor->info->attacksound); A_FaceTarget(actor); an = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(12 * FRACUNIT, finecosine[an]); actor->momy = FixedMul(12 * FRACUNIT, finesine[an]); dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); dist = dist / (12 * FRACUNIT); if (dist < 1) { dist = 1; } actor->momz = (dest->z + (dest->height >> 1) - actor->z) / dist; } //---------------------------------------------------------------------------- // // PROC A_ImpMsAttack2 // // Fireball attack of the imp leader. // //---------------------------------------------------------------------------- void A_ImpMsAttack2(mobj_t * actor) { if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, 5 + (P_Random() & 7)); return; } P_SpawnMissile(actor, actor->target, MT_IMPBALL); } //---------------------------------------------------------------------------- // // PROC A_ImpDeath // //---------------------------------------------------------------------------- void A_ImpDeath(mobj_t * actor) { actor->flags &= ~MF_SOLID; actor->flags2 |= MF2_FOOTCLIP; if (actor->z <= actor->floorz) { P_SetMobjState(actor, S_IMP_CRASH1); } } //---------------------------------------------------------------------------- // // PROC A_ImpXDeath1 // //---------------------------------------------------------------------------- void A_ImpXDeath1(mobj_t * actor) { actor->flags &= ~MF_SOLID; actor->flags |= MF_NOGRAVITY; actor->flags2 |= MF2_FOOTCLIP; actor->special1.i = 666; // Flag the crash routine } //---------------------------------------------------------------------------- // // PROC A_ImpXDeath2 // //---------------------------------------------------------------------------- void A_ImpXDeath2(mobj_t * actor) { actor->flags &= ~MF_NOGRAVITY; if (actor->z <= actor->floorz) { P_SetMobjState(actor, S_IMP_CRASH1); } } //---------------------------------------------------------------------------- // // FUNC P_UpdateChicken // // Returns true if the chicken morphs. // //---------------------------------------------------------------------------- boolean P_UpdateChicken(mobj_t * actor, int tics) { mobj_t *fog; fixed_t x; fixed_t y; fixed_t z; mobjtype_t moType; mobj_t *mo; mobj_t oldChicken; actor->special1.i -= tics; if (actor->special1.i > 0) { return (false); } moType = actor->special2.i; x = actor->x; y = actor->y; z = actor->z; oldChicken = *actor; P_SetMobjState(actor, S_FREETARGMOBJ); mo = P_SpawnMobj(x, y, z, moType); if (P_TestMobjLocation(mo) == false) { // Didn't fit P_RemoveMobj(mo); mo = P_SpawnMobj(x, y, z, MT_CHICKEN); mo->angle = oldChicken.angle; mo->flags = oldChicken.flags; mo->health = oldChicken.health; mo->target = oldChicken.target; mo->special1.i = 5 * 35; // Next try in 5 seconds mo->special2.i = moType; return (false); } mo->angle = oldChicken.angle; mo->target = oldChicken.target; fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, sfx_telept); return (true); } //---------------------------------------------------------------------------- // // PROC A_ChicAttack // //---------------------------------------------------------------------------- void A_ChicAttack(mobj_t * actor) { if (P_UpdateChicken(actor, 18)) { return; } if (!actor->target) { return; } if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, 1 + (P_Random() & 1)); } } //---------------------------------------------------------------------------- // // PROC A_ChicLook // //---------------------------------------------------------------------------- void A_ChicLook(mobj_t * actor) { if (P_UpdateChicken(actor, 10)) { return; } A_Look(actor); } //---------------------------------------------------------------------------- // // PROC A_ChicChase // //---------------------------------------------------------------------------- void A_ChicChase(mobj_t * actor) { if (P_UpdateChicken(actor, 3)) { return; } A_Chase(actor); } //---------------------------------------------------------------------------- // // PROC A_ChicPain // //---------------------------------------------------------------------------- void A_ChicPain(mobj_t * actor) { if (P_UpdateChicken(actor, 10)) { return; } S_StartSound(actor, actor->info->painsound); } //---------------------------------------------------------------------------- // // PROC A_Feathers // //---------------------------------------------------------------------------- void A_Feathers(mobj_t * actor) { int i; int count; mobj_t *mo; if (actor->health > 0) { // Pain count = P_Random() < 32 ? 2 : 1; } else { // Death count = 5 + (P_Random() & 3); } for (i = 0; i < count; i++) { mo = P_SpawnMobj(actor->x, actor->y, actor->z + 20 * FRACUNIT, MT_FEATHER); mo->target = actor; mo->momx = P_SubRandom() << 8; mo->momy = P_SubRandom() << 8; mo->momz = FRACUNIT + (P_Random() << 9); P_SetMobjState(mo, S_FEATHER1 + (P_Random() & 7)); } } //---------------------------------------------------------------------------- // // PROC A_MummyAttack // //---------------------------------------------------------------------------- void A_MummyAttack(mobj_t * actor) { if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(2)); S_StartSound(actor, sfx_mumat2); return; } S_StartSound(actor, sfx_mumat1); } //---------------------------------------------------------------------------- // // PROC A_MummyAttack2 // // Mummy leader missile attack. // //---------------------------------------------------------------------------- void A_MummyAttack2(mobj_t * actor) { mobj_t *mo; if (!actor->target) { return; } //S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(2)); return; } mo = P_SpawnMissile(actor, actor->target, MT_MUMMYFX1); //mo = P_SpawnMissile(actor, actor->target, MT_EGGFX); if (mo != NULL) { mo->special1.m = actor->target; } } //---------------------------------------------------------------------------- // // PROC A_MummyFX1Seek // //---------------------------------------------------------------------------- void A_MummyFX1Seek(mobj_t * actor) { P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 20); } //---------------------------------------------------------------------------- // // PROC A_MummySoul // //---------------------------------------------------------------------------- void A_MummySoul(mobj_t * mummy) { mobj_t *mo; mo = P_SpawnMobj(mummy->x, mummy->y, mummy->z + 10 * FRACUNIT, MT_MUMMYSOUL); mo->momz = FRACUNIT; } //---------------------------------------------------------------------------- // // PROC A_Sor1Pain // //---------------------------------------------------------------------------- void A_Sor1Pain(mobj_t * actor) { actor->special1.i = 20; // Number of steps to walk fast A_Pain(actor); } //---------------------------------------------------------------------------- // // PROC A_Sor1Chase // //---------------------------------------------------------------------------- void A_Sor1Chase(mobj_t * actor) { if (actor->special1.i) { actor->special1.i--; actor->tics -= 3; } A_Chase(actor); } //---------------------------------------------------------------------------- // // PROC A_Srcr1Attack // // Sorcerer demon attack. // //---------------------------------------------------------------------------- void A_Srcr1Attack(mobj_t * actor) { mobj_t *mo; fixed_t momz; angle_t angle; if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(8)); return; } if (actor->health > (actor->info->spawnhealth / 3) * 2) { // Spit one fireball P_SpawnMissile(actor, actor->target, MT_SRCRFX1); } else { // Spit three fireballs mo = P_SpawnMissile(actor, actor->target, MT_SRCRFX1); if (mo) { momz = mo->momz; angle = mo->angle; P_SpawnMissileAngle(actor, MT_SRCRFX1, angle - ANG1_X * 3, momz); P_SpawnMissileAngle(actor, MT_SRCRFX1, angle + ANG1_X * 3, momz); } if (actor->health < actor->info->spawnhealth / 3) { // Maybe attack again if (actor->special1.i) { // Just attacked, so don't attack again actor->special1.i = 0; } else { // Set state to attack again actor->special1.i = 1; P_SetMobjState(actor, S_SRCR1_ATK4); } } } } //---------------------------------------------------------------------------- // // PROC A_SorcererRise // //---------------------------------------------------------------------------- void A_SorcererRise(mobj_t * actor) { mobj_t *mo; actor->flags &= ~MF_SOLID; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCERER2); P_SetMobjState(mo, S_SOR2_RISE1); mo->angle = actor->angle; mo->target = actor->target; } //---------------------------------------------------------------------------- // // PROC P_DSparilTeleport // //---------------------------------------------------------------------------- void P_DSparilTeleport(mobj_t * actor) { int i; fixed_t x; fixed_t y; fixed_t prevX; fixed_t prevY; fixed_t prevZ; mobj_t *mo; if (!BossSpotCount) { // No spots return; } i = P_Random(); do { i++; x = BossSpots[i % BossSpotCount].x; y = BossSpots[i % BossSpotCount].y; } while (P_AproxDistance(actor->x - x, actor->y - y) < 128 * FRACUNIT); prevX = actor->x; prevY = actor->y; prevZ = actor->z; if (P_TeleportMove(actor, x, y)) { mo = P_SpawnMobj(prevX, prevY, prevZ, MT_SOR2TELEFADE); S_StartSound(mo, sfx_telept); P_SetMobjState(actor, S_SOR2_TELE1); S_StartSound(actor, sfx_telept); actor->z = actor->floorz; actor->angle = BossSpots[i % BossSpotCount].angle; actor->momx = actor->momy = actor->momz = 0; } } //---------------------------------------------------------------------------- // // PROC A_Srcr2Decide // //---------------------------------------------------------------------------- void A_Srcr2Decide(mobj_t * actor) { static int chance[] = { 192, 120, 120, 120, 64, 64, 32, 16, 0 }; if (!BossSpotCount) { // No spots return; } if (P_Random() < chance[actor->health / (actor->info->spawnhealth / 8)]) { P_DSparilTeleport(actor); } } //---------------------------------------------------------------------------- // // PROC A_Srcr2Attack // //---------------------------------------------------------------------------- void A_Srcr2Attack(mobj_t * actor) { int chance; if (!actor->target) { return; } S_StartSound(NULL, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(20)); return; } chance = actor->health < actor->info->spawnhealth / 2 ? 96 : 48; if (P_Random() < chance) { // Wizard spawners P_SpawnMissileAngle(actor, MT_SOR2FX2, actor->angle - ANG45, FRACUNIT / 2); P_SpawnMissileAngle(actor, MT_SOR2FX2, actor->angle + ANG45, FRACUNIT / 2); } else { // Blue bolt P_SpawnMissile(actor, actor->target, MT_SOR2FX1); } } //---------------------------------------------------------------------------- // // PROC A_BlueSpark // //---------------------------------------------------------------------------- void A_BlueSpark(mobj_t * actor) { int i; mobj_t *mo; for (i = 0; i < 2; i++) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SOR2FXSPARK); mo->momx = P_SubRandom() << 9; mo->momy = P_SubRandom() << 9; mo->momz = FRACUNIT + (P_Random() << 8); } } //---------------------------------------------------------------------------- // // PROC A_GenWizard // //---------------------------------------------------------------------------- void A_GenWizard(mobj_t * actor) { mobj_t *mo; mobj_t *fog; mo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[MT_WIZARD].height / 2, MT_WIZARD); if (P_TestMobjLocation(mo) == false) { // Didn't fit P_RemoveMobj(mo); return; } actor->momx = actor->momy = actor->momz = 0; P_SetMobjState(actor, mobjinfo[actor->type].deathstate); actor->flags &= ~MF_MISSILE; fog = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TFOG); S_StartSound(fog, sfx_telept); } //---------------------------------------------------------------------------- // // PROC A_Sor2DthInit // //---------------------------------------------------------------------------- void A_Sor2DthInit(mobj_t * actor) { actor->special1.i = 7; // Animation loop counter P_Massacre(); // Kill monsters early } //---------------------------------------------------------------------------- // // PROC A_Sor2DthLoop // //---------------------------------------------------------------------------- void A_Sor2DthLoop(mobj_t * actor) { if (--actor->special1.i) { // Need to loop P_SetMobjState(actor, S_SOR2_DIE4); } } //---------------------------------------------------------------------------- // // D'Sparil Sound Routines // //---------------------------------------------------------------------------- void A_SorZap(mobj_t * actor) { S_StartSound(NULL, sfx_sorzap); } void A_SorRise(mobj_t * actor) { S_StartSound(NULL, sfx_sorrise); } void A_SorDSph(mobj_t * actor) { S_StartSound(NULL, sfx_sordsph); } void A_SorDExp(mobj_t * actor) { S_StartSound(NULL, sfx_sordexp); } void A_SorDBon(mobj_t * actor) { S_StartSound(NULL, sfx_sordbon); } void A_SorSightSnd(mobj_t * actor) { S_StartSound(NULL, sfx_sorsit); } //---------------------------------------------------------------------------- // // PROC A_MinotaurAtk1 // // Melee attack. // //---------------------------------------------------------------------------- void A_MinotaurAtk1(mobj_t * actor) { player_t *player; if (!actor->target) { return; } S_StartSound(actor, sfx_stfpow); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(4)); if ((player = actor->target->player) != NULL) { // Squish the player player->deltaviewheight = -16 * FRACUNIT; } } } //---------------------------------------------------------------------------- // // PROC A_MinotaurDecide // // Choose a missile attack. // //---------------------------------------------------------------------------- #define MNTR_CHARGE_SPEED (13*FRACUNIT) void A_MinotaurDecide(mobj_t * actor) { angle_t angle; mobj_t *target; int dist; target = actor->target; if (!target) { return; } S_StartSound(actor, sfx_minsit); dist = P_AproxDistance(actor->x - target->x, actor->y - target->y); if (target->z + target->height > actor->z && target->z + target->height < actor->z + actor->height && dist < 8 * 64 * FRACUNIT && dist > 1 * 64 * FRACUNIT && P_Random() < 150) { // Charge attack // Don't call the state function right away P_SetMobjStateNF(actor, S_MNTR_ATK4_1); actor->flags |= MF_SKULLFLY; A_FaceTarget(actor); angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]); actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]); actor->special1.i = 35 / 2; // Charge duration } else if (target->z == target->floorz && dist < 9 * 64 * FRACUNIT && P_Random() < 220) { // Floor fire attack P_SetMobjState(actor, S_MNTR_ATK3_1); actor->special2.i = 0; } else { // Swing attack A_FaceTarget(actor); // Don't need to call P_SetMobjState because the current state // falls through to the swing attack } } //---------------------------------------------------------------------------- // // PROC A_MinotaurCharge // //---------------------------------------------------------------------------- void A_MinotaurCharge(mobj_t * actor) { mobj_t *puff; if (actor->special1.i) { puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF); puff->momz = 2 * FRACUNIT; actor->special1.i--; } else { actor->flags &= ~MF_SKULLFLY; P_SetMobjState(actor, actor->info->seestate); } } //---------------------------------------------------------------------------- // // PROC A_MinotaurAtk2 // // Swing attack. // //---------------------------------------------------------------------------- void A_MinotaurAtk2(mobj_t * actor) { mobj_t *mo; angle_t angle; fixed_t momz; if (!actor->target) { return; } S_StartSound(actor, sfx_minat2); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(5)); return; } mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1); if (mo) { S_StartSound(mo, sfx_minat2); momz = mo->momz; angle = mo->angle; P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 8), momz); P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 8), momz); P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 16), momz); P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 16), momz); } } //---------------------------------------------------------------------------- // // PROC A_MinotaurAtk3 // // Floor fire attack. // //---------------------------------------------------------------------------- void A_MinotaurAtk3(mobj_t * actor) { mobj_t *mo; player_t *player; if (!actor->target) { return; } if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(5)); if ((player = actor->target->player) != NULL) { // Squish the player player->deltaviewheight = -16 * FRACUNIT; } } else { mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2); if (mo != NULL) { S_StartSound(mo, sfx_minat1); } } if (P_Random() < 192 && actor->special2.i == 0) { P_SetMobjState(actor, S_MNTR_ATK3_4); actor->special2.i = 1; } } //---------------------------------------------------------------------------- // // PROC A_MntrFloorFire // //---------------------------------------------------------------------------- void A_MntrFloorFire(mobj_t * actor) { mobj_t *mo; int r1, r2; r1 = P_SubRandom(); r2 = P_SubRandom(); actor->z = actor->floorz; mo = P_SpawnMobj(actor->x + (r2 << 10), actor->y + (r1 << 10), ONFLOORZ, MT_MNTRFX3); mo->target = actor->target; mo->momx = 1; // Force block checking P_CheckMissileSpawn(mo); } //---------------------------------------------------------------------------- // // PROC A_BeastAttack // //---------------------------------------------------------------------------- void A_BeastAttack(mobj_t * actor) { if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(3)); return; } P_SpawnMissile(actor, actor->target, MT_BEASTBALL); } //---------------------------------------------------------------------------- // // PROC A_HeadAttack // //---------------------------------------------------------------------------- void A_HeadAttack(mobj_t * actor) { int i; mobj_t *fire; mobj_t *baseFire; mobj_t *mo; mobj_t *target; int randAttack; static int atkResolve1[] = { 50, 150 }; static int atkResolve2[] = { 150, 200 }; int dist; // Ice ball (close 20% : far 60%) // Fire column (close 40% : far 20%) // Whirlwind (close 40% : far 20%) // Distance threshold = 8 cells target = actor->target; if (target == NULL) { return; } A_FaceTarget(actor); if (P_CheckMeleeRange(actor)) { P_DamageMobj(target, actor, actor, HITDICE(6)); return; } dist = P_AproxDistance(actor->x - target->x, actor->y - target->y) > 8 * 64 * FRACUNIT; randAttack = P_Random(); if (randAttack < atkResolve1[dist]) { // Ice ball P_SpawnMissile(actor, target, MT_HEADFX1); S_StartSound(actor, sfx_hedat2); } else if (randAttack < atkResolve2[dist]) { // Fire column baseFire = P_SpawnMissile(actor, target, MT_HEADFX3); if (baseFire != NULL) { P_SetMobjState(baseFire, S_HEADFX3_4); // Don't grow for (i = 0; i < 5; i++) { fire = P_SpawnMobj(baseFire->x, baseFire->y, baseFire->z, MT_HEADFX3); if (i == 0) { S_StartSound(actor, sfx_hedat1); } fire->target = baseFire->target; fire->angle = baseFire->angle; fire->momx = baseFire->momx; fire->momy = baseFire->momy; fire->momz = baseFire->momz; fire->damage = 0; fire->health = (i + 1) * 2; P_CheckMissileSpawn(fire); } } } else { // Whirlwind mo = P_SpawnMissile(actor, target, MT_WHIRLWIND); if (mo != NULL) { mo->z -= 32 * FRACUNIT; mo->special1.m = target; mo->special2.i = 50; // Timer for active sound mo->health = 20 * TICRATE; // Duration S_StartSound(actor, sfx_hedat3); } } } //---------------------------------------------------------------------------- // // PROC A_WhirlwindSeek // //---------------------------------------------------------------------------- void A_WhirlwindSeek(mobj_t * actor) { actor->health -= 3; if (actor->health < 0) { actor->momx = actor->momy = actor->momz = 0; P_SetMobjState(actor, mobjinfo[actor->type].deathstate); actor->flags &= ~MF_MISSILE; return; } if ((actor->special2.i -= 3) < 0) { actor->special2.i = 58 + (P_Random() & 31); S_StartSound(actor, sfx_hedat3); } if (actor->special1.m && (((mobj_t *) (actor->special1.m))->flags & MF_SHADOW)) { return; } P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 30); } //---------------------------------------------------------------------------- // // PROC A_HeadIceImpact // //---------------------------------------------------------------------------- void A_HeadIceImpact(mobj_t * ice) { unsigned int i; angle_t angle; mobj_t *shard; for (i = 0; i < 8; i++) { shard = P_SpawnMobj(ice->x, ice->y, ice->z, MT_HEADFX2); angle = i * ANG45; shard->target = ice->target; shard->angle = angle; angle >>= ANGLETOFINESHIFT; shard->momx = FixedMul(shard->info->speed, finecosine[angle]); shard->momy = FixedMul(shard->info->speed, finesine[angle]); shard->momz = (fixed_t)(-.6 * FRACUNIT); P_CheckMissileSpawn(shard); } } //---------------------------------------------------------------------------- // // PROC A_HeadFireGrow // //---------------------------------------------------------------------------- void A_HeadFireGrow(mobj_t * fire) { fire->health--; fire->z += 9 * FRACUNIT; if (fire->health == 0) { fire->damage = fire->info->damage; P_SetMobjState(fire, S_HEADFX3_4); } } //---------------------------------------------------------------------------- // // PROC A_SnakeAttack // //---------------------------------------------------------------------------- void A_SnakeAttack(mobj_t * actor) { if (!actor->target) { P_SetMobjState(actor, S_SNAKE_WALK1); return; } S_StartSound(actor, actor->info->attacksound); A_FaceTarget(actor); P_SpawnMissile(actor, actor->target, MT_SNAKEPRO_A); } //---------------------------------------------------------------------------- // // PROC A_SnakeAttack2 // //---------------------------------------------------------------------------- void A_SnakeAttack2(mobj_t * actor) { if (!actor->target) { P_SetMobjState(actor, S_SNAKE_WALK1); return; } S_StartSound(actor, actor->info->attacksound); A_FaceTarget(actor); P_SpawnMissile(actor, actor->target, MT_SNAKEPRO_B); } //---------------------------------------------------------------------------- // // PROC A_ClinkAttack // //---------------------------------------------------------------------------- void A_ClinkAttack(mobj_t * actor) { int damage; if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { damage = ((P_Random() % 7) + 3); P_DamageMobj(actor->target, actor, actor, damage); } } //---------------------------------------------------------------------------- // // PROC A_GhostOff // //---------------------------------------------------------------------------- void A_GhostOff(mobj_t * actor) { actor->flags &= ~MF_SHADOW; } //---------------------------------------------------------------------------- // // PROC A_WizAtk1 // //---------------------------------------------------------------------------- void A_WizAtk1(mobj_t * actor) { A_FaceTarget(actor); actor->flags &= ~MF_SHADOW; } //---------------------------------------------------------------------------- // // PROC A_WizAtk2 // //---------------------------------------------------------------------------- void A_WizAtk2(mobj_t * actor) { A_FaceTarget(actor); actor->flags |= MF_SHADOW; } //---------------------------------------------------------------------------- // // PROC A_WizAtk3 // //---------------------------------------------------------------------------- void A_WizAtk3(mobj_t * actor) { mobj_t *mo; angle_t angle; fixed_t momz; actor->flags &= ~MF_SHADOW; if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(4)); return; } mo = P_SpawnMissile(actor, actor->target, MT_WIZFX1); if (mo) { momz = mo->momz; angle = mo->angle; P_SpawnMissileAngle(actor, MT_WIZFX1, angle - (ANG45 / 8), momz); P_SpawnMissileAngle(actor, MT_WIZFX1, angle + (ANG45 / 8), momz); } } //---------------------------------------------------------------------------- // // PROC A_Scream // //---------------------------------------------------------------------------- void A_Scream(mobj_t * actor) { switch (actor->type) { case MT_CHICPLAYER: case MT_SORCERER1: case MT_MINOTAUR: // Make boss death sounds full volume S_StartSound(NULL, actor->info->deathsound); break; case MT_PLAYER: // Handle the different player death screams if (actor->special1.i < 10) { // Wimpy death sound S_StartSound(actor, sfx_plrwdth); } else if (actor->health > -50) { // Normal death sound S_StartSound(actor, actor->info->deathsound); } else if (actor->health > -100) { // Crazy death sound S_StartSound(actor, sfx_plrcdth); } else { // Extreme death sound S_StartSound(actor, sfx_gibdth); } break; default: S_StartSound(actor, actor->info->deathsound); break; } } //--------------------------------------------------------------------------- // // PROC P_DropItem // //--------------------------------------------------------------------------- void P_DropItem(mobj_t * source, mobjtype_t type, int special, int chance) { mobj_t *mo; if (P_Random() > chance) { return; } mo = P_SpawnMobj(source->x, source->y, source->z + (source->height >> 1), type); mo->momx = P_SubRandom() << 8; mo->momy = P_SubRandom() << 8; mo->momz = FRACUNIT * 5 + (P_Random() << 10); mo->flags |= MF_DROPPED; mo->health = special; } //---------------------------------------------------------------------------- // // PROC A_NoBlocking // //---------------------------------------------------------------------------- void A_NoBlocking(mobj_t * actor) { actor->flags &= ~MF_SOLID; // Check for monsters dropping things switch (actor->type) { case MT_MUMMY: case MT_MUMMYLEADER: case MT_MUMMYGHOST: case MT_MUMMYLEADERGHOST: P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84); break; case MT_KNIGHT: case MT_KNIGHTGHOST: P_DropItem(actor, MT_AMCBOWWIMPY, 5, 84); break; case MT_WIZARD: P_DropItem(actor, MT_AMBLSRWIMPY, 10, 84); P_DropItem(actor, MT_ARTITOMEOFPOWER, 0, 4); break; case MT_HEAD: P_DropItem(actor, MT_AMBLSRWIMPY, 10, 84); P_DropItem(actor, MT_ARTIEGG, 0, 51); break; case MT_BEAST: P_DropItem(actor, MT_AMCBOWWIMPY, 10, 84); break; case MT_CLINK: P_DropItem(actor, MT_AMSKRDWIMPY, 20, 84); break; case MT_SNAKE: P_DropItem(actor, MT_AMPHRDWIMPY, 5, 84); break; case MT_MINOTAUR: P_DropItem(actor, MT_ARTISUPERHEAL, 0, 51); P_DropItem(actor, MT_AMPHRDWIMPY, 10, 84); break; default: break; } } //---------------------------------------------------------------------------- // // PROC A_Explode // // Handles a bunch of exploding things. // //---------------------------------------------------------------------------- void A_Explode(mobj_t * actor) { int damage; damage = 128; switch (actor->type) { case MT_FIREBOMB: // Time Bombs actor->z += 32 * FRACUNIT; actor->flags &= ~MF_SHADOW; break; case MT_MNTRFX2: // Minotaur floor fire damage = 24; break; case MT_SOR2FX1: // D'Sparil missile damage = 80 + (P_Random() & 31); break; default: break; } P_RadiusAttack(actor, actor->target, damage); P_HitFloor(actor); } //---------------------------------------------------------------------------- // // PROC A_PodPain // //---------------------------------------------------------------------------- void A_PodPain(mobj_t * actor) { int i; int count; int chance; mobj_t *goo; chance = P_Random(); if (chance < 128) { return; } count = chance > 240 ? 2 : 1; for (i = 0; i < count; i++) { goo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT, MT_PODGOO); goo->target = actor; goo->momx = P_SubRandom() << 9; goo->momy = P_SubRandom() << 9; goo->momz = FRACUNIT / 2 + (P_Random() << 9); } } //---------------------------------------------------------------------------- // // PROC A_RemovePod // //---------------------------------------------------------------------------- void A_RemovePod(mobj_t * actor) { mobj_t *mo; if (actor->special2.m) { mo = (mobj_t *) actor->special2.m; if (mo->special1.i > 0) { mo->special1.i--; } } } //---------------------------------------------------------------------------- // // PROC A_MakePod // //---------------------------------------------------------------------------- #define MAX_GEN_PODS 16 void A_MakePod(mobj_t * actor) { mobj_t *mo; fixed_t x; fixed_t y; if (actor->special1.i == MAX_GEN_PODS) { // Too many generated pods return; } x = actor->x; y = actor->y; mo = P_SpawnMobj(x, y, ONFLOORZ, MT_POD); if (P_CheckPosition(mo, x, y) == false) { // Didn't fit P_RemoveMobj(mo); return; } P_SetMobjState(mo, S_POD_GROW1); P_ThrustMobj(mo, P_Random() << 24, (fixed_t) (4.5 * FRACUNIT)); S_StartSound(mo, sfx_newpod); actor->special1.i++; // Increment generated pod count mo->special2.m = actor; // Link the generator to the pod return; } //---------------------------------------------------------------------------- // // PROC P_Massacre // // Kills all monsters. // //---------------------------------------------------------------------------- void P_Massacre(void) { mobj_t *mo; thinker_t *think; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *) think; if ((mo->flags & MF_COUNTKILL) && (mo->health > 0)) { P_DamageMobj(mo, NULL, NULL, 10000); } } } //---------------------------------------------------------------------------- // // PROC A_BossDeath // // Trigger special effects if all bosses are dead. // //---------------------------------------------------------------------------- void A_BossDeath(mobj_t * actor) { mobj_t *mo; thinker_t *think; line_t dummyLine; static mobjtype_t bossType[6] = { MT_HEAD, MT_MINOTAUR, MT_SORCERER2, MT_HEAD, MT_MINOTAUR, -1 }; if (gamemap != 8) { // Not a boss level return; } if (actor->type != bossType[gameepisode - 1]) { // Not considered a boss in this episode return; } // Make sure all bosses are dead for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *) think; if ((mo != actor) && (mo->type == actor->type) && (mo->health > 0)) { // Found a living boss return; } } if (gameepisode > 1) { // Kill any remaining monsters P_Massacre(); } dummyLine.tag = 666; EV_DoFloor(&dummyLine, lowerFloor); } //---------------------------------------------------------------------------- // // PROC A_ESound // //---------------------------------------------------------------------------- void A_ESound(mobj_t * mo) { int sound = sfx_None; switch (mo->type) { case MT_SOUNDWATERFALL: sound = sfx_waterfl; break; case MT_SOUNDWIND: sound = sfx_wind; break; default: break; } S_StartSound(mo, sound); } //---------------------------------------------------------------------------- // // PROC A_SpawnTeleGlitter // //---------------------------------------------------------------------------- void A_SpawnTeleGlitter(mobj_t * actor) { mobj_t *mo; int r1, r2; r1 = P_Random(); r2 = P_Random(); mo = P_SpawnMobj(actor->x + ((r2 & 31) - 16) * FRACUNIT, actor->y + ((r1 & 31) - 16) * FRACUNIT, actor->subsector->sector->floorheight, MT_TELEGLITTER); mo->momz = FRACUNIT / 4; } //---------------------------------------------------------------------------- // // PROC A_SpawnTeleGlitter2 // //---------------------------------------------------------------------------- void A_SpawnTeleGlitter2(mobj_t * actor) { mobj_t *mo; int r1, r2; r1 = P_Random(); r2 = P_Random(); mo = P_SpawnMobj(actor->x + ((r2 & 31) - 16) * FRACUNIT, actor->y + ((r1 & 31) - 16) * FRACUNIT, actor->subsector->sector->floorheight, MT_TELEGLITTER2); mo->momz = FRACUNIT / 4; } //---------------------------------------------------------------------------- // // PROC A_AccTeleGlitter // //---------------------------------------------------------------------------- void A_AccTeleGlitter(mobj_t * actor) { if (++actor->health > 35) { actor->momz += actor->momz / 2; } } //---------------------------------------------------------------------------- // // PROC A_InitKeyGizmo // //---------------------------------------------------------------------------- void A_InitKeyGizmo(mobj_t * gizmo) { mobj_t *mo; statenum_t state = S_NULL; switch (gizmo->type) { case MT_KEYGIZMOBLUE: state = S_KGZ_BLUEFLOAT1; break; case MT_KEYGIZMOGREEN: state = S_KGZ_GREENFLOAT1; break; case MT_KEYGIZMOYELLOW: state = S_KGZ_YELLOWFLOAT1; break; default: break; } mo = P_SpawnMobj(gizmo->x, gizmo->y, gizmo->z + 60 * FRACUNIT, MT_KEYGIZMOFLOAT); P_SetMobjState(mo, state); } //---------------------------------------------------------------------------- // // PROC A_VolcanoSet // //---------------------------------------------------------------------------- void A_VolcanoSet(mobj_t * volcano) { volcano->tics = 105 + (P_Random() & 127); } //---------------------------------------------------------------------------- // // PROC A_VolcanoBlast // //---------------------------------------------------------------------------- void A_VolcanoBlast(mobj_t * volcano) { int i; int count; mobj_t *blast; angle_t angle; count = 1 + (P_Random() % 3); for (i = 0; i < count; i++) { blast = P_SpawnMobj(volcano->x, volcano->y, volcano->z + 44 * FRACUNIT, MT_VOLCANOBLAST); // MT_VOLCANOBLAST blast->target = volcano; angle = P_Random() << 24; blast->angle = angle; angle >>= ANGLETOFINESHIFT; blast->momx = FixedMul(1 * FRACUNIT, finecosine[angle]); blast->momy = FixedMul(1 * FRACUNIT, finesine[angle]); blast->momz = (fixed_t)(2.5 * FRACUNIT) + (P_Random() << 10); S_StartSound(blast, sfx_volsht); P_CheckMissileSpawn(blast); } } //---------------------------------------------------------------------------- // // PROC A_VolcBallImpact // //---------------------------------------------------------------------------- void A_VolcBallImpact(mobj_t * ball) { unsigned int i; mobj_t *tiny; angle_t angle; if (ball->z <= ball->floorz) { ball->flags |= MF_NOGRAVITY; ball->flags2 &= ~MF2_LOGRAV; ball->z += 28 * FRACUNIT; //ball->momz = 3*FRACUNIT; } P_RadiusAttack(ball, ball->target, 25); for (i = 0; i < 4; i++) { tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_VOLCANOTBLAST); tiny->target = ball; angle = i * ANG90; tiny->angle = angle; angle >>= ANGLETOFINESHIFT; tiny->momx = FixedMul((fixed_t)(FRACUNIT * .7), finecosine[angle]); tiny->momy = FixedMul((fixed_t)(FRACUNIT * .7), finesine[angle]); tiny->momz = FRACUNIT + (P_Random() << 9); P_CheckMissileSpawn(tiny); } } //---------------------------------------------------------------------------- // // PROC A_SkullPop // //---------------------------------------------------------------------------- void A_SkullPop(mobj_t * actor) { mobj_t *mo; player_t *player; actor->flags &= ~MF_SOLID; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT, MT_BLOODYSKULL); //mo->target = actor; mo->momx = P_SubRandom() << 9; mo->momy = P_SubRandom() << 9; mo->momz = FRACUNIT * 2 + (P_Random() << 6); // Attach player mobj to bloody skull player = actor->player; actor->player = NULL; mo->player = player; mo->health = actor->health; mo->angle = actor->angle; // fraggle: This check wasn't originally here in the Vanilla Heretic // source, causing crashes if the player respawns before this // function is called. if (player != NULL) { player->mo = mo; player->lookdir = 0; player->damagecount = 32; } } //---------------------------------------------------------------------------- // // PROC A_CheckSkullFloor // //---------------------------------------------------------------------------- void A_CheckSkullFloor(mobj_t * actor) { if (actor->z <= actor->floorz) { P_SetMobjState(actor, S_BLOODYSKULLX1); } } //---------------------------------------------------------------------------- // // PROC A_CheckSkullDone // //---------------------------------------------------------------------------- void A_CheckSkullDone(mobj_t * actor) { if (actor->special2.i == 666) { P_SetMobjState(actor, S_BLOODYSKULLX2); } } //---------------------------------------------------------------------------- // // PROC A_CheckBurnGone // //---------------------------------------------------------------------------- void A_CheckBurnGone(mobj_t * actor) { if (actor->special2.i == 666) { P_SetMobjState(actor, S_PLAY_FDTH20); } } //---------------------------------------------------------------------------- // // PROC A_FreeTargMobj // //---------------------------------------------------------------------------- void A_FreeTargMobj(mobj_t * mo) { mo->momx = mo->momy = mo->momz = 0; mo->z = mo->ceilingz + 4 * FRACUNIT; mo->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_SOLID); mo->flags |= MF_CORPSE | MF_DROPOFF | MF_NOGRAVITY; mo->flags2 &= ~(MF2_PASSMOBJ | MF2_LOGRAV); mo->player = NULL; } //---------------------------------------------------------------------------- // // PROC A_AddPlayerCorpse // //---------------------------------------------------------------------------- #define BODYQUESIZE 32 mobj_t *bodyque[BODYQUESIZE]; int bodyqueslot; void A_AddPlayerCorpse(mobj_t * actor) { if (bodyqueslot >= BODYQUESIZE) { // Too many player corpses - remove an old one P_RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]); } bodyque[bodyqueslot % BODYQUESIZE] = actor; bodyqueslot++; } //---------------------------------------------------------------------------- // // PROC A_FlameSnd // //---------------------------------------------------------------------------- void A_FlameSnd(mobj_t * actor) { S_StartSound(actor, sfx_hedat1); // Burn sound } //---------------------------------------------------------------------------- // // PROC A_HideThing // //---------------------------------------------------------------------------- void A_HideThing(mobj_t * actor) { //P_UnsetThingPosition(actor); actor->flags2 |= MF2_DONTDRAW; } //---------------------------------------------------------------------------- // // PROC A_UnHideThing // //---------------------------------------------------------------------------- void A_UnHideThing(mobj_t * actor) { //P_SetThingPosition(actor); actor->flags2 &= ~MF2_DONTDRAW; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_floor.c000066400000000000000000000374501360717211000222630ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" //================================================================== //================================================================== // // FLOORS // //================================================================== //================================================================== //================================================================== // // Move a plane (floor or ceiling) and check for crushing // //================================================================== result_e T_MovePlane(sector_t * sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction) { boolean flag; fixed_t lastpos; switch (floorOrCeiling) { case 0: // FLOOR switch (direction) { case -1: // DOWN if (sector->floorheight - speed < dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); //return crushed; } return pastdest; } else { lastpos = sector->floorheight; sector->floorheight -= speed; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); return crushed; } } break; case 1: // UP if (sector->floorheight + speed > dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); //return crushed; } return pastdest; } else // COULD GET CRUSHED { lastpos = sector->floorheight; sector->floorheight += speed; flag = P_ChangeSector(sector, crush); if (flag == true) { if (crush == true) return crushed; sector->floorheight = lastpos; P_ChangeSector(sector, crush); return crushed; } } break; } break; case 1: // CEILING switch (direction) { case -1: // DOWN if (sector->ceilingheight - speed < dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); //return crushed; } return pastdest; } else // COULD GET CRUSHED { lastpos = sector->ceilingheight; sector->ceilingheight -= speed; flag = P_ChangeSector(sector, crush); if (flag == true) { if (crush == true) return crushed; sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); return crushed; } } break; case 1: // UP if (sector->ceilingheight + speed > dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); //return crushed; } return pastdest; } else { lastpos = sector->ceilingheight; sector->ceilingheight += speed; flag = P_ChangeSector(sector, crush); #if 0 if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); return crushed; } #endif } break; } break; } return ok; } //================================================================== // // MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) // //================================================================== void T_MoveFloor(floormove_t * floor) { result_e res; res = T_MovePlane(floor->sector, floor->speed, floor->floordestheight, floor->crush, 0, floor->direction); if (!(leveltime & 7)) { S_StartSound(&floor->sector->soundorg, sfx_dormov); } if (res == pastdest) { floor->sector->specialdata = NULL; if (floor->type == raiseBuildStep) { S_StartSound(&floor->sector->soundorg, sfx_pstop); } if (floor->direction == 1) switch (floor->type) { case donutRaise: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } else if (floor->direction == -1) switch (floor->type) { case lowerAndChange: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } P_RemoveThinker(&floor->thinker); } } //================================================================== // // HANDLE FLOOR TYPES // //================================================================== int EV_DoFloor(line_t * line, floor_e floortype) { int secnum; int rtn; int i; sector_t *sec; floormove_t *floor; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // // new floor thinker // rtn = 1; floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = floortype; floor->crush = false; switch (floortype) { case lowerFloor: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindHighestFloorSurrounding(sec); break; case lowerFloorToLowest: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); break; case turboLower: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED * 4; floor->floordestheight = (8 * FRACUNIT) + P_FindHighestFloorSurrounding(sec); break; case raiseFloorCrush: floor->crush = true; case raiseFloor: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestCeilingSurrounding(sec); if (floor->floordestheight > sec->ceilingheight) floor->floordestheight = sec->ceilingheight; floor->floordestheight -= (8 * FRACUNIT) * (floortype == raiseFloorCrush); break; case raiseFloorToNearest: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindNextHighestFloor(sec, sec->floorheight); break; case raiseFloor24: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; break; case raiseFloor24AndChange: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; sec->floorpic = line->frontsector->floorpic; sec->special = line->frontsector->special; break; case raiseToTexture: { int minsize = INT_MAX; side_t *side; floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; for (i = 0; i < sec->linecount; i++) if (twoSided(secnum, i)) { side = getSide(secnum, i, 0); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; side = getSide(secnum, i, 1); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; } floor->floordestheight = floor->sector->floorheight + minsize; } break; case lowerAndChange: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); floor->texture = sec->floorpic; for (i = 0; i < sec->linecount; i++) if (twoSided(secnum, i)) { if (getSide(secnum, i, 0)->sector - sectors == secnum) { sec = getSector(secnum, i, 1); floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } else { sec = getSector(secnum, i, 0); floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } } default: break; } } return rtn; } //================================================================== // // BUILD A STAIRCASE! // //================================================================== int EV_BuildStairs(line_t * line, fixed_t stepDelta) { int secnum; int height; int i; int newsecnum; int texture; int ok; int rtn; sector_t *sec, *tsec; floormove_t *floor; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // // new floor thinker // rtn = 1; height = sec->floorheight + stepDelta; floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = raiseBuildStep; floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = height; texture = sec->floorpic; // // Find next sector to raise // 1. Find 2-sided line with same sector side[0] // 2. Other side is the next sector to raise // do { ok = 0; for (i = 0; i < sec->linecount; i++) { if (!((sec->lines[i])->flags & ML_TWOSIDED)) continue; tsec = (sec->lines[i])->frontsector; newsecnum = tsec - sectors; if (secnum != newsecnum) continue; tsec = (sec->lines[i])->backsector; newsecnum = tsec - sectors; if (tsec->floorpic != texture) continue; height += stepDelta; if (tsec->specialdata) continue; sec = tsec; secnum = newsecnum; floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = raiseBuildStep; floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = height; ok = 1; break; } } while (ok); } return (rtn); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_inter.c000066400000000000000000001256601360717211000222640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_inter.c #include "doomdef.h" #include "deh_str.h" #include "i_system.h" #include "i_timer.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" #define BONUSADD 6 int WeaponValue[] = { 1, // staff 3, // goldwand 4, // crossbow 5, // blaster 6, // skullrod 7, // phoenixrod 8, // mace 2, // gauntlets 0 // beak }; int maxammo[NUMAMMO] = { 100, // gold wand 50, // crossbow 200, // blaster 200, // skull rod 20, // phoenix rod 150 // mace }; int GetWeaponAmmo[NUMWEAPONS] = { 0, // staff 25, // gold wand 10, // crossbow 30, // blaster 50, // skull rod 2, // phoenix rod 50, // mace 0, // gauntlets 0 // beak }; static weapontype_t GetAmmoChange[] = { wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace }; /* static boolean GetAmmoChangePL1[NUMWEAPONS][NUMAMMO] = { // staff {wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace}, // gold wand {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace}, // crossbow {-1, -1, wp_blaster, wp_skullrod, -1, -1}, // blaster {-1, -1, -1, -1, -1, -1}, // skull rod {-1, -1, -1, -1, -1, -1}, // phoenix rod {-1, -1, -1, -1, -1, -1}, // mace {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1}, // gauntlets {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace} }; */ /* static boolean GetAmmoChangePL2[NUMWEAPONS][NUMAMMO] = { // staff {wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace}, // gold wand {-1, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace}, // crossbow {-1, -1, wp_blaster, wp_skullrod, wp_phoenixrod, -1}, // blaster {-1, -1, -1, wp_skullrod, wp_phoenixrod, -1}, // skull rod {-1, -1, -1, -1, -1, -1}, // phoenix rod {-1, -1, -1, -1, -1, -1}, // mace {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1}, // gauntlets {-1, -1, -1, wp_skullrod, wp_phoenixrod, wp_mace} }; */ //-------------------------------------------------------------------------- // // PROC P_SetMessage // //-------------------------------------------------------------------------- boolean ultimatemsg; void P_SetMessage(player_t * player, const char *message, boolean ultmsg) { extern boolean messageson; if ((ultimatemsg || !messageson) && !ultmsg) { return; } player->message = message; player->messageTics = MESSAGETICS; BorderTopRefresh = true; if (ultmsg) { ultimatemsg = true; } } //-------------------------------------------------------------------------- // // FUNC P_GiveAmmo // // Returns true if the player accepted the ammo, false if it was // refused (player has maxammo[ammo]). // //-------------------------------------------------------------------------- boolean P_GiveAmmo(player_t * player, ammotype_t ammo, int count) { int prevAmmo; //weapontype_t changeWeapon; if (ammo == am_noammo) { return (false); } if ((unsigned int) ammo > NUMAMMO) { I_Error("P_GiveAmmo: bad type %i", ammo); } if (player->ammo[ammo] == player->maxammo[ammo]) { return (false); } if (gameskill == sk_baby || gameskill == sk_nightmare) { // extra ammo in baby mode and nightmare mode count += count >> 1; } prevAmmo = player->ammo[ammo]; player->ammo[ammo] += count; if (player->ammo[ammo] > player->maxammo[ammo]) { player->ammo[ammo] = player->maxammo[ammo]; } if (prevAmmo) { // Don't attempt to change weapons if the player already had // ammo of the type just given return (true); } if (player->readyweapon == wp_staff || player->readyweapon == wp_gauntlets) { if (player->weaponowned[GetAmmoChange[ammo]]) { player->pendingweapon = GetAmmoChange[ammo]; } } /* if(player->powers[pw_weaponlevel2]) { changeWeapon = GetAmmoChangePL2[player->readyweapon][ammo]; } else { changeWeapon = GetAmmoChangePL1[player->readyweapon][ammo]; } if(changeWeapon != -1) { if(player->weaponowned[changeWeapon]) { player->pendingweapon = changeWeapon; } } */ return (true); } //-------------------------------------------------------------------------- // // FUNC P_GiveWeapon // // Returns true if the weapon or its ammo was accepted. // //-------------------------------------------------------------------------- boolean P_GiveWeapon(player_t * player, weapontype_t weapon) { boolean gaveAmmo; boolean gaveWeapon; if (netgame && !deathmatch) { // Cooperative net-game if (player->weaponowned[weapon]) { return (false); } player->bonuscount += BONUSADD; player->weaponowned[weapon] = true; P_GiveAmmo(player, wpnlev1info[weapon].ammo, GetWeaponAmmo[weapon]); player->pendingweapon = weapon; if (player == &players[consoleplayer]) { S_StartSound(NULL, sfx_wpnup); } return (false); } gaveAmmo = P_GiveAmmo(player, wpnlev1info[weapon].ammo, GetWeaponAmmo[weapon]); if (player->weaponowned[weapon]) { gaveWeapon = false; } else { gaveWeapon = true; player->weaponowned[weapon] = true; if (WeaponValue[weapon] > WeaponValue[player->readyweapon]) { // Only switch to more powerful weapons player->pendingweapon = weapon; } } return (gaveWeapon || gaveAmmo); } //--------------------------------------------------------------------------- // // FUNC P_GiveBody // // Returns false if the body isn't needed at all. // //--------------------------------------------------------------------------- boolean P_GiveBody(player_t * player, int num) { int max; max = MAXHEALTH; if (player->chickenTics) { max = MAXCHICKENHEALTH; } if (player->health >= max) { return (false); } player->health += num; if (player->health > max) { player->health = max; } player->mo->health = player->health; return (true); } //--------------------------------------------------------------------------- // // FUNC P_GiveArmor // // Returns false if the armor is worse than the current armor. // //--------------------------------------------------------------------------- boolean P_GiveArmor(player_t * player, int armortype) { int hits; hits = armortype * 100; if (player->armorpoints >= hits) { return (false); } player->armortype = armortype; player->armorpoints = hits; return (true); } //--------------------------------------------------------------------------- // // PROC P_GiveKey // //--------------------------------------------------------------------------- void P_GiveKey(player_t * player, keytype_t key) { extern int playerkeys; extern vertex_t KeyPoints[]; if (player->keys[key]) { return; } if (player == &players[consoleplayer]) { playerkeys |= 1 << key; KeyPoints[key].x = 0; KeyPoints[key].y = 0; } player->bonuscount = BONUSADD; player->keys[key] = true; } //--------------------------------------------------------------------------- // // FUNC P_GivePower // // Returns true if power accepted. // //--------------------------------------------------------------------------- boolean P_GivePower(player_t * player, powertype_t power) { if (power == pw_invulnerability) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = INVULNTICS; return (true); } if (power == pw_weaponlevel2) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = WPNLEV2TICS; return (true); } if (power == pw_invisibility) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = INVISTICS; player->mo->flags |= MF_SHADOW; return (true); } if (power == pw_flight) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = FLIGHTTICS; player->mo->flags2 |= MF2_FLY; player->mo->flags |= MF_NOGRAVITY; if (player->mo->z <= player->mo->floorz) { player->flyheight = 10; // thrust the player in the air a bit } return (true); } if (power == pw_infrared) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = INFRATICS; return (true); } /* if(power == pw_ironfeet) { player->powers[power] = IRONTICS; return(true); } if(power == pw_strength) { P_GiveBody(player, 100); player->powers[power] = 1; return(true); } */ if (player->powers[power]) { return (false); // already got it } player->powers[power] = 1; return (true); } //--------------------------------------------------------------------------- // // FUNC P_GiveArtifact // // Returns true if artifact accepted. // //--------------------------------------------------------------------------- boolean P_GiveArtifact(player_t * player, artitype_t arti, mobj_t * mo) { int i; i = 0; while (player->inventory[i].type != arti && i < player->inventorySlotNum) { i++; } if (i == player->inventorySlotNum) { player->inventory[i].count = 1; player->inventory[i].type = arti; player->inventorySlotNum++; } else { if (player->inventory[i].count >= 16) { // Player already has 16 of this item return (false); } player->inventory[i].count++; } if (player->artifactCount == 0) { player->readyArtifact = arti; } player->artifactCount++; if (mo && (mo->flags & MF_COUNTITEM)) { player->itemcount++; } return (true); } //--------------------------------------------------------------------------- // // PROC P_SetDormantArtifact // // Removes the MF_SPECIAL flag, and initiates the artifact pickup // animation. // //--------------------------------------------------------------------------- void P_SetDormantArtifact(mobj_t * arti) { arti->flags &= ~MF_SPECIAL; if (deathmatch && (arti->type != MT_ARTIINVULNERABILITY) && (arti->type != MT_ARTIINVISIBILITY)) { P_SetMobjState(arti, S_DORMANTARTI1); } else { // Don't respawn P_SetMobjState(arti, S_DEADARTI1); } S_StartSound(arti, sfx_artiup); } //--------------------------------------------------------------------------- // // PROC A_RestoreArtifact // //--------------------------------------------------------------------------- void A_RestoreArtifact(mobj_t * arti) { arti->flags |= MF_SPECIAL; P_SetMobjState(arti, arti->info->spawnstate); S_StartSound(arti, sfx_respawn); } //---------------------------------------------------------------------------- // // PROC P_HideSpecialThing // //---------------------------------------------------------------------------- void P_HideSpecialThing(mobj_t * thing) { thing->flags &= ~MF_SPECIAL; thing->flags2 |= MF2_DONTDRAW; P_SetMobjState(thing, S_HIDESPECIAL1); } //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing1 // // Make a special thing visible again. // //--------------------------------------------------------------------------- void A_RestoreSpecialThing1(mobj_t * thing) { if (thing->type == MT_WMACE) { // Do random mace placement P_RepositionMace(thing); } thing->flags2 &= ~MF2_DONTDRAW; S_StartSound(thing, sfx_respawn); } //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing2 // //--------------------------------------------------------------------------- void A_RestoreSpecialThing2(mobj_t * thing) { thing->flags |= MF_SPECIAL; P_SetMobjState(thing, thing->info->spawnstate); } //--------------------------------------------------------------------------- // // PROC P_TouchSpecialThing // //--------------------------------------------------------------------------- void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { int i; player_t *player; fixed_t delta; int sound; boolean respawn; delta = special->z - toucher->z; if (delta > toucher->height || delta < -32 * FRACUNIT) { // Out of reach return; } if (toucher->health <= 0) { // Toucher is dead return; } sound = sfx_itemup; player = toucher->player; respawn = true; switch (special->sprite) { // Items case SPR_PTN1: // Item_HealingPotion if (!P_GiveBody(player, 10)) { return; } P_SetMessage(player, DEH_String(TXT_ITEMHEALTH), false); break; case SPR_SHLD: // Item_Shield1 if (!P_GiveArmor(player, 1)) { return; } P_SetMessage(player, DEH_String(TXT_ITEMSHIELD1), false); break; case SPR_SHD2: // Item_Shield2 if (!P_GiveArmor(player, 2)) { return; } P_SetMessage(player, DEH_String(TXT_ITEMSHIELD2), false); break; case SPR_BAGH: // Item_BagOfHolding if (!player->backpack) { for (i = 0; i < NUMAMMO; i++) { player->maxammo[i] *= 2; } player->backpack = true; } P_GiveAmmo(player, am_goldwand, AMMO_GWND_WIMPY); P_GiveAmmo(player, am_blaster, AMMO_BLSR_WIMPY); P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY); P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY); P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY); P_SetMessage(player, DEH_String(TXT_ITEMBAGOFHOLDING), false); break; case SPR_SPMP: // Item_SuperMap if (!P_GivePower(player, pw_allmap)) { return; } P_SetMessage(player, DEH_String(TXT_ITEMSUPERMAP), false); break; // Keys case SPR_BKYY: // Key_Blue if (!player->keys[key_blue]) { P_SetMessage(player, DEH_String(TXT_GOTBLUEKEY), false); } P_GiveKey(player, key_blue); sound = sfx_keyup; if (!netgame) { break; } return; case SPR_CKYY: // Key_Yellow if (!player->keys[key_yellow]) { P_SetMessage(player, DEH_String(TXT_GOTYELLOWKEY), false); } sound = sfx_keyup; P_GiveKey(player, key_yellow); if (!netgame) { break; } return; case SPR_AKYY: // Key_Green if (!player->keys[key_green]) { P_SetMessage(player, DEH_String(TXT_GOTGREENKEY), false); } sound = sfx_keyup; P_GiveKey(player, key_green); if (!netgame) { break; } return; // Artifacts case SPR_PTN2: // Arti_HealingPotion if (P_GiveArtifact(player, arti_health, special)) { P_SetMessage(player, DEH_String(TXT_ARTIHEALTH), false); P_SetDormantArtifact(special); } return; case SPR_SOAR: // Arti_Fly if (P_GiveArtifact(player, arti_fly, special)) { P_SetMessage(player, DEH_String(TXT_ARTIFLY), false); P_SetDormantArtifact(special); } return; case SPR_INVU: // Arti_Invulnerability if (P_GiveArtifact(player, arti_invulnerability, special)) { P_SetMessage(player, DEH_String(TXT_ARTIINVULNERABILITY), false); P_SetDormantArtifact(special); } return; case SPR_PWBK: // Arti_TomeOfPower if (P_GiveArtifact(player, arti_tomeofpower, special)) { P_SetMessage(player, DEH_String(TXT_ARTITOMEOFPOWER), false); P_SetDormantArtifact(special); } return; case SPR_INVS: // Arti_Invisibility if (P_GiveArtifact(player, arti_invisibility, special)) { P_SetMessage(player, DEH_String(TXT_ARTIINVISIBILITY), false); P_SetDormantArtifact(special); } return; case SPR_EGGC: // Arti_Egg if (P_GiveArtifact(player, arti_egg, special)) { P_SetMessage(player, DEH_String(TXT_ARTIEGG), false); P_SetDormantArtifact(special); } return; case SPR_SPHL: // Arti_SuperHealth if (P_GiveArtifact(player, arti_superhealth, special)) { P_SetMessage(player, DEH_String(TXT_ARTISUPERHEALTH), false); P_SetDormantArtifact(special); } return; case SPR_TRCH: // Arti_Torch if (P_GiveArtifact(player, arti_torch, special)) { P_SetMessage(player, DEH_String(TXT_ARTITORCH), false); P_SetDormantArtifact(special); } return; case SPR_FBMB: // Arti_FireBomb if (P_GiveArtifact(player, arti_firebomb, special)) { P_SetMessage(player, DEH_String(TXT_ARTIFIREBOMB), false); P_SetDormantArtifact(special); } return; case SPR_ATLP: // Arti_Teleport if (P_GiveArtifact(player, arti_teleport, special)) { P_SetMessage(player, DEH_String(TXT_ARTITELEPORT), false); P_SetDormantArtifact(special); } return; // Ammo case SPR_AMG1: // Ammo_GoldWandWimpy if (!P_GiveAmmo(player, am_goldwand, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND1), false); break; case SPR_AMG2: // Ammo_GoldWandHefty if (!P_GiveAmmo(player, am_goldwand, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND2), false); break; case SPR_AMM1: // Ammo_MaceWimpy if (!P_GiveAmmo(player, am_mace, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOMACE1), false); break; case SPR_AMM2: // Ammo_MaceHefty if (!P_GiveAmmo(player, am_mace, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOMACE2), false); break; case SPR_AMC1: // Ammo_CrossbowWimpy if (!P_GiveAmmo(player, am_crossbow, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW1), false); break; case SPR_AMC2: // Ammo_CrossbowHefty if (!P_GiveAmmo(player, am_crossbow, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW2), false); break; case SPR_AMB1: // Ammo_BlasterWimpy if (!P_GiveAmmo(player, am_blaster, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOBLASTER1), false); break; case SPR_AMB2: // Ammo_BlasterHefty if (!P_GiveAmmo(player, am_blaster, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOBLASTER2), false); break; case SPR_AMS1: // Ammo_SkullRodWimpy if (!P_GiveAmmo(player, am_skullrod, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD1), false); break; case SPR_AMS2: // Ammo_SkullRodHefty if (!P_GiveAmmo(player, am_skullrod, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD2), false); break; case SPR_AMP1: // Ammo_PhoenixRodWimpy if (!P_GiveAmmo(player, am_phoenixrod, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD1), false); break; case SPR_AMP2: // Ammo_PhoenixRodHefty if (!P_GiveAmmo(player, am_phoenixrod, special->health)) { return; } P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD2), false); break; // Weapons case SPR_WMCE: // Weapon_Mace if (!P_GiveWeapon(player, wp_mace)) { return; } P_SetMessage(player, DEH_String(TXT_WPNMACE), false); sound = sfx_wpnup; break; case SPR_WBOW: // Weapon_Crossbow if (!P_GiveWeapon(player, wp_crossbow)) { return; } P_SetMessage(player, DEH_String(TXT_WPNCROSSBOW), false); sound = sfx_wpnup; break; case SPR_WBLS: // Weapon_Blaster if (!P_GiveWeapon(player, wp_blaster)) { return; } P_SetMessage(player, DEH_String(TXT_WPNBLASTER), false); sound = sfx_wpnup; break; case SPR_WSKL: // Weapon_SkullRod if (!P_GiveWeapon(player, wp_skullrod)) { return; } P_SetMessage(player, DEH_String(TXT_WPNSKULLROD), false); sound = sfx_wpnup; break; case SPR_WPHX: // Weapon_PhoenixRod if (!P_GiveWeapon(player, wp_phoenixrod)) { return; } P_SetMessage(player, DEH_String(TXT_WPNPHOENIXROD), false); sound = sfx_wpnup; break; case SPR_WGNT: // Weapon_Gauntlets if (!P_GiveWeapon(player, wp_gauntlets)) { return; } P_SetMessage(player, DEH_String(TXT_WPNGAUNTLETS), false); sound = sfx_wpnup; break; default: I_Error("P_SpecialThing: Unknown gettable thing"); } if (special->flags & MF_COUNTITEM) { player->itemcount++; } if (deathmatch && respawn && !(special->flags & MF_DROPPED)) { P_HideSpecialThing(special); } else { P_RemoveMobj(special); } player->bonuscount += BONUSADD; if (player == &players[consoleplayer]) { S_StartSound(NULL, sound); SB_PaletteFlash(); } } //--------------------------------------------------------------------------- // // PROC P_KillMobj // //--------------------------------------------------------------------------- void P_KillMobj(mobj_t * source, mobj_t * target) { target->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_NOGRAVITY); target->flags |= MF_CORPSE | MF_DROPOFF; target->flags2 &= ~MF2_PASSMOBJ; target->height >>= 2; if (source && source->player) { if (target->flags & MF_COUNTKILL) { // Count for intermission source->player->killcount++; } if (target->player) { // Frag stuff if (target == source) { // Self-frag target->player->frags[target->player - players]--; } else { source->player->frags[target->player - players]++; if (source->player == &players[consoleplayer]) { S_StartSound(NULL, sfx_gfrag); } if (source->player->chickenTics) { // Make a super chicken P_GivePower(source->player, pw_weaponlevel2); } } } } else if (!netgame && (target->flags & MF_COUNTKILL)) { // Count all monster deaths players[0].killcount++; } if (target->player) { if (!source) { // Self-frag target->player->frags[target->player - players]--; } target->flags &= ~MF_SOLID; target->flags2 &= ~MF2_FLY; target->player->powers[pw_flight] = 0; target->player->powers[pw_weaponlevel2] = 0; target->player->playerstate = PST_DEAD; P_DropWeapon(target->player); if (target->flags2 & MF2_FIREDAMAGE) { // Player flame death P_SetMobjState(target, S_PLAY_FDTH1); //S_StartSound(target, sfx_hedat1); // Burn sound return; } } if (target->health < -(target->info->spawnhealth >> 1) && target->info->xdeathstate) { // Extreme death P_SetMobjState(target, target->info->xdeathstate); } else { // Normal death P_SetMobjState(target, target->info->deathstate); } target->tics -= P_Random() & 3; // I_StartSound(&actor->r, actor->info->deathsound); } //--------------------------------------------------------------------------- // // FUNC P_MinotaurSlam // //--------------------------------------------------------------------------- void P_MinotaurSlam(mobj_t * source, mobj_t * target) { angle_t angle; fixed_t thrust; angle = R_PointToAngle2(source->x, source->y, target->x, target->y); angle >>= ANGLETOFINESHIFT; thrust = 16 * FRACUNIT + (P_Random() << 10); target->momx += FixedMul(thrust, finecosine[angle]); target->momy += FixedMul(thrust, finesine[angle]); P_DamageMobj(target, NULL, NULL, HITDICE(6)); if (target->player) { target->reactiontime = 14 + (P_Random() & 7); } } //--------------------------------------------------------------------------- // // FUNC P_TouchWhirlwind // //--------------------------------------------------------------------------- void P_TouchWhirlwind(mobj_t * target) { int randVal; target->angle += P_SubRandom() << 20; target->momx += P_SubRandom() << 10; target->momy += P_SubRandom() << 10; if (leveltime & 16 && !(target->flags2 & MF2_BOSS)) { randVal = P_Random(); if (randVal > 160) { randVal = 160; } target->momz += randVal << 10; if (target->momz > 12 * FRACUNIT) { target->momz = 12 * FRACUNIT; } } if (!(leveltime & 7)) { P_DamageMobj(target, NULL, NULL, 3); } } //--------------------------------------------------------------------------- // // FUNC P_ChickenMorphPlayer // // Returns true if the player gets turned into a chicken. // //--------------------------------------------------------------------------- boolean P_ChickenMorphPlayer(player_t * player) { mobj_t *pmo; mobj_t *fog; mobj_t *chicken; fixed_t x; fixed_t y; fixed_t z; angle_t angle; int oldFlags2; if (player->chickenTics) { if ((player->chickenTics < CHICKENTICS - TICRATE) && !player->powers[pw_weaponlevel2]) { // Make a super chicken P_GivePower(player, pw_weaponlevel2); } return (false); } if (player->powers[pw_invulnerability]) { // Immune when invulnerable return (false); } pmo = player->mo; x = pmo->x; y = pmo->y; z = pmo->z; angle = pmo->angle; oldFlags2 = pmo->flags2; P_SetMobjState(pmo, S_FREETARGMOBJ); fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, sfx_telept); chicken = P_SpawnMobj(x, y, z, MT_CHICPLAYER); chicken->special1.i = player->readyweapon; chicken->angle = angle; chicken->player = player; player->health = chicken->health = MAXCHICKENHEALTH; player->mo = chicken; player->armorpoints = player->armortype = 0; player->powers[pw_invisibility] = 0; player->powers[pw_weaponlevel2] = 0; if (oldFlags2 & MF2_FLY) { chicken->flags2 |= MF2_FLY; } player->chickenTics = CHICKENTICS; P_ActivateBeak(player); return (true); } //--------------------------------------------------------------------------- // // FUNC P_ChickenMorph // //--------------------------------------------------------------------------- boolean P_ChickenMorph(mobj_t * actor) { mobj_t *fog; mobj_t *chicken; mobj_t *target; mobjtype_t moType; fixed_t x; fixed_t y; fixed_t z; angle_t angle; int ghost; if (actor->player) { return (false); } moType = actor->type; switch (moType) { case MT_POD: case MT_CHICKEN: case MT_HEAD: case MT_MINOTAUR: case MT_SORCERER1: case MT_SORCERER2: return (false); default: break; } x = actor->x; y = actor->y; z = actor->z; angle = actor->angle; ghost = actor->flags & MF_SHADOW; target = actor->target; P_SetMobjState(actor, S_FREETARGMOBJ); fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, sfx_telept); chicken = P_SpawnMobj(x, y, z, MT_CHICKEN); chicken->special2.i = moType; chicken->special1.i = CHICKENTICS + P_Random(); chicken->flags |= ghost; chicken->target = target; chicken->angle = angle; return (true); } //--------------------------------------------------------------------------- // // FUNC P_AutoUseChaosDevice // //--------------------------------------------------------------------------- boolean P_AutoUseChaosDevice(player_t * player) { int i; for (i = 0; i < player->inventorySlotNum; i++) { if (player->inventory[i].type == arti_teleport) { P_PlayerUseArtifact(player, arti_teleport); player->health = player->mo->health = (player->health + 1) / 2; return (true); } } return (false); } //--------------------------------------------------------------------------- // // PROC P_AutoUseHealth // //--------------------------------------------------------------------------- void P_AutoUseHealth(player_t * player, int saveHealth) { int i; int count; int normalCount; int normalSlot; int superCount; int superSlot; normalCount = 0; superCount = 0; normalSlot = 0; superSlot = 0; for (i = 0; i < player->inventorySlotNum; i++) { if (player->inventory[i].type == arti_health) { normalSlot = i; normalCount = player->inventory[i].count; } else if (player->inventory[i].type == arti_superhealth) { superSlot = i; superCount = player->inventory[i].count; } } if ((gameskill == sk_baby) && (normalCount * 25 >= saveHealth)) { // Use quartz flasks count = (saveHealth + 24) / 25; for (i = 0; i < count; i++) { player->health += 25; P_PlayerRemoveArtifact(player, normalSlot); } } else if (superCount * 100 >= saveHealth) { // Use mystic urns count = (saveHealth + 99) / 100; for (i = 0; i < count; i++) { player->health += 100; P_PlayerRemoveArtifact(player, superSlot); } } else if ((gameskill == sk_baby) && (superCount * 100 + normalCount * 25 >= saveHealth)) { // Use mystic urns and quartz flasks count = (saveHealth + 24) / 25; saveHealth -= count * 25; for (i = 0; i < count; i++) { player->health += 25; P_PlayerRemoveArtifact(player, normalSlot); } count = (saveHealth + 99) / 100; for (i = 0; i < count; i++) { player->health += 100; P_PlayerRemoveArtifact(player, normalSlot); } } player->mo->health = player->health; } /* ================= = = 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 barrel explosions and other environmental stuff ================== */ void P_DamageMobj (mobj_t * target, mobj_t * inflictor, mobj_t * source, int damage) { unsigned ang; int saved; player_t *player; fixed_t thrust; int temp; if (!(target->flags & MF_SHOOTABLE)) { // Shouldn't happen return; } if (target->health <= 0) { return; } if (target->flags & MF_SKULLFLY) { if (target->type == MT_MINOTAUR) { // Minotaur is invulnerable during charge attack return; } target->momx = target->momy = target->momz = 0; } player = target->player; if (player && gameskill == sk_baby) { // Take half damage in trainer mode damage >>= 1; } // Special damage types if (inflictor) { switch (inflictor->type) { case MT_EGGFX: if (player) { P_ChickenMorphPlayer(player); } else { P_ChickenMorph(target); } return; // Always return case MT_WHIRLWIND: P_TouchWhirlwind(target); return; case MT_MINOTAUR: if (inflictor->flags & MF_SKULLFLY) { // Slam only when in charge mode P_MinotaurSlam(inflictor, target); return; } break; case MT_MACEFX4: // Death ball if ((target->flags2 & MF2_BOSS) || target->type == MT_HEAD) { // Don't allow cheap boss kills break; } else if (target->player) { // Player specific checks if (target->player->powers[pw_invulnerability]) { // Can't hurt invulnerable players break; } if (P_AutoUseChaosDevice(target->player)) { // Player was saved using chaos device return; } } damage = 10000; // Something's gonna die break; case MT_PHOENIXFX2: // Flame thrower if (target->player && P_Random() < 128) { // Freeze player for a bit target->reactiontime += 4; } break; case MT_RAINPLR1: // Rain missiles case MT_RAINPLR2: case MT_RAINPLR3: case MT_RAINPLR4: if (target->flags2 & MF2_BOSS) { // Decrease damage for bosses damage = (P_Random() & 7) + 1; } break; case MT_HORNRODFX2: case MT_PHOENIXFX1: if (target->type == MT_SORCERER2 && P_Random() < 96) { // D'Sparil teleports away P_DSparilTeleport(target); return; } break; case MT_BLASTERFX1: case MT_RIPPER: if (target->type == MT_HEAD) { // Less damage to Ironlich bosses damage = P_Random() & 1; if (!damage) { return; } } break; default: break; } } // Push the target unless source is using the gauntlets if (inflictor && (!source || !source->player || source->player->readyweapon != wp_gauntlets) && !(inflictor->flags2 & MF2_NODMGTHRUST)) { ang = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y); //thrust = damage*(FRACUNIT>>3)*100/target->info->mass; thrust = damage * (FRACUNIT >> 3) * 150 / target->info->mass; // make fall forwards sometimes if ((damage < 40) && (damage > target->health) && (target->z - inflictor->z > 64 * FRACUNIT) && (P_Random() & 1)) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; if (source && source->player && (source == inflictor) && source->player->powers[pw_weaponlevel2] && source->player->readyweapon == wp_staff) { // Staff power level 2 target->momx += FixedMul(10 * FRACUNIT, finecosine[ang]); target->momy += FixedMul(10 * FRACUNIT, finesine[ang]); if (!(target->flags & MF_NOGRAVITY)) { target->momz += 5 * FRACUNIT; } } else { target->momx += FixedMul(thrust, finecosine[ang]); target->momy += FixedMul(thrust, finesine[ang]); } } // // player specific // if (player) { // end of game hell hack //if(target->subsector->sector->special == 11 // && damage >= target->health) //{ // damage = target->health - 1; //} if (damage < 1000 && ((player->cheats & CF_GODMODE) || player->powers[pw_invulnerability])) { return; } if (player->armortype) { if (player->armortype == 1) { saved = damage >> 1; } else { saved = (damage >> 1) + (damage >> 2); } if (player->armorpoints <= saved) { // armor is used up saved = player->armorpoints; player->armortype = 0; } player->armorpoints -= saved; damage -= saved; } if (damage >= player->health && ((gameskill == sk_baby) || deathmatch) && !player->chickenTics) { // Try to use some inventory health P_AutoUseHealth(player, damage - player->health + 1); } player->health -= damage; // mirror mobj health here for Dave if (player->health < 0) { player->health = 0; } player->attacker = source; player->damagecount += damage; // add damage after armor / invuln if (player->damagecount > 100) { player->damagecount = 100; // teleport stomp does 10k points... } temp = damage < 100 ? damage : 100; if (player == &players[consoleplayer]) { I_Tactile(40, 10, 40 + temp * 2); SB_PaletteFlash(); } } // // do the damage // target->health -= damage; if (target->health <= 0) { // Death target->special1.i = damage; if (target->type == MT_POD && source && source->type != MT_POD) { // Make sure players get frags for chain-reaction kills target->target = source; } if (player && inflictor && !player->chickenTics) { // Check for flame death if ((inflictor->flags2 & MF2_FIREDAMAGE) || ((inflictor->type == MT_PHOENIXFX1) && (target->health > -50) && (damage > 25))) { target->flags2 |= MF2_FIREDAMAGE; } } P_KillMobj(source, target); return; } if ((P_Random() < target->info->painchance) && !(target->flags & MF_SKULLFLY)) { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState(target, target->info->painstate); } target->reactiontime = 0; // we're awake now... if (!target->threshold && source && !(source->flags2 & MF2_BOSS) && !(target->type == MT_SORCERER2 && source->type == MT_WIZARD)) { // Target actor is not intent on another actor, // so make him chase after source target->target = source; target->threshold = BASETHRESHOLD; if (target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL) { P_SetMobjState(target, target->info->seestate); } } } crispy-doom-crispy-doom-5.6.4/src/heretic/p_lights.c000066400000000000000000000173101360717211000224250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "doomdef.h" #include "m_random.h" #include "p_local.h" #include "v_video.h" //================================================================== //================================================================== // // BROKEN LIGHT FLASHING // //================================================================== //================================================================== //================================================================== // // T_LightFlash // // After the map has been loaded, scan each sector for specials // that spawn thinkers // //================================================================== void T_LightFlash(lightflash_t * flash) { if (--flash->count) return; if (flash->sector->lightlevel == flash->maxlight) { flash->sector->lightlevel = flash->minlight; flash->count = (P_Random() & flash->mintime) + 1; } else { flash->sector->lightlevel = flash->maxlight; flash->count = (P_Random() & flash->maxtime) + 1; } } //================================================================== // // P_SpawnLightFlash // // After the map has been loaded, scan each sector for specials that spawn thinkers // //================================================================== void P_SpawnLightFlash(sector_t * sector) { lightflash_t *flash; sector->special = 0; // nothing special about it during gameplay flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0); 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() & flash->maxtime) + 1; } //================================================================== // // STROBE LIGHT FLASHING // //================================================================== //================================================================== // // T_StrobeFlash // // After the map has been loaded, scan each sector for specials that spawn thinkers // //================================================================== void T_StrobeFlash(strobe_t * flash) { if (--flash->count) return; if (flash->sector->lightlevel == flash->minlight) { flash->sector->lightlevel = flash->maxlight; flash->count = flash->brighttime; } else { flash->sector->lightlevel = flash->minlight; flash->count = flash->darktime; } } //================================================================== // // P_SpawnLightFlash // // After the map has been loaded, scan each sector for specials that spawn thinkers // //================================================================== void P_SpawnStrobeFlash(sector_t * sector, int fastOrSlow, int inSync) { strobe_t *flash; flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0); P_AddThinker(&flash->thinker); flash->sector = sector; flash->darktime = fastOrSlow; flash->brighttime = STROBEBRIGHT; flash->thinker.function = T_StrobeFlash; flash->maxlight = sector->lightlevel; flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); if (flash->minlight == flash->maxlight) flash->minlight = 0; sector->special = 0; // nothing special about it during gameplay if (!inSync) flash->count = (P_Random() & 7) + 1; else flash->count = 1; } //================================================================== // // Start strobing lights (usually from a trigger) // //================================================================== void EV_StartLightStrobing(line_t * line) { int secnum; sector_t *sec; secnum = -1; while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; P_SpawnStrobeFlash(sec, SLOWDARK, 0); } } //================================================================== // // TURN LINE'S TAG LIGHTS OFF // //================================================================== void EV_TurnTagLightsOff(line_t * line) { int i; int j; int min; sector_t *sector; sector_t *tsec; line_t *templine; sector = sectors; for (j = 0; j < numsectors; j++, sector++) if (sector->tag == line->tag) { min = sector->lightlevel; for (i = 0; i < sector->linecount; i++) { templine = sector->lines[i]; tsec = getNextSector(templine, sector); if (!tsec) continue; if (tsec->lightlevel < min) min = tsec->lightlevel; } sector->lightlevel = min; } } //================================================================== // // TURN LINE'S TAG LIGHTS ON // //================================================================== void EV_LightTurnOn(line_t * line, int bright) { int i; int j; sector_t *sector; sector_t *temp; line_t *templine; sector = sectors; for (i = 0; i < numsectors; i++, sector++) if (sector->tag == line->tag) { // // bright = 0 means to search for highest // light level surrounding sector // if (!bright) { for (j = 0; j < sector->linecount; j++) { templine = sector->lines[j]; temp = getNextSector(templine, sector); if (!temp) continue; if (temp->lightlevel > bright) bright = temp->lightlevel; } } sector->lightlevel = bright; } } //================================================================== // // Spawn glowing light // //================================================================== void T_Glow(glow_t * g) { switch (g->direction) { case -1: // DOWN g->sector->lightlevel -= GLOWSPEED; if (g->sector->lightlevel <= g->minlight) { g->sector->lightlevel += GLOWSPEED; g->direction = 1; } break; case 1: // UP g->sector->lightlevel += GLOWSPEED; if (g->sector->lightlevel >= g->maxlight) { g->sector->lightlevel -= GLOWSPEED; g->direction = -1; } break; } } void P_SpawnGlowingLight(sector_t * sector) { glow_t *g; g = Z_Malloc(sizeof(*g), PU_LEVSPEC, 0); P_AddThinker(&g->thinker); g->sector = sector; g->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); g->maxlight = sector->lightlevel; g->thinker.function = T_Glow; g->direction = -1; sector->special = 0; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_local.h000066400000000000000000000205461360717211000222370ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_local.h #ifndef __P_LOCAL__ #define __P_LOCAL__ #ifndef __R_LOCAL__ #include "r_local.h" #endif #define STARTREDPALS 1 #define STARTBONUSPALS 9 #define NUMREDPALS 8 #define NUMBONUSPALS 4 #define FOOTCLIPSIZE 10*FRACUNIT #define TOCENTER -8 #define FLOATSPEED (FRACUNIT*4) #define MAXHEALTH 100 #define MAXCHICKENHEALTH 30 #define VIEWHEIGHT (41*FRACUNIT) // mapblocks are used to check movement against lines and things #define MAPBLOCKUNITS 128 #define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) #define MAPBLOCKSHIFT (FRACBITS+7) #define MAPBMASK (MAPBLOCKSIZE-1) #define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) // player radius for movement checking #define PLAYERRADIUS 16*FRACUNIT // MAXRADIUS is for precalculated sector block boxes // the spider demon is larger, but we don't have any moving sectors // nearby #define MAXRADIUS 32*FRACUNIT #define GRAVITY FRACUNIT #define MAXMOVE (30*FRACUNIT) #define USERANGE (64*FRACUNIT) #define MELEERANGE (64*FRACUNIT) #define MISSILERANGE (32*64*FRACUNIT) typedef enum { DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_NODIR, NUMDIRS } dirtype_t; #define BASETHRESHOLD 100 // follow a player exlusively for 3 seconds // ***** P_TICK ***** extern thinker_t thinkercap; // both the head and tail of the thinker list extern int TimerGame; // tic countdown for deathmatch void P_InitThinkers(void); void P_AddThinker(thinker_t * thinker); void P_RemoveThinker(thinker_t * thinker); // ***** P_PSPR ***** #define USE_GWND_AMMO_1 1 #define USE_GWND_AMMO_2 1 #define USE_CBOW_AMMO_1 1 #define USE_CBOW_AMMO_2 1 #define USE_BLSR_AMMO_1 1 #define USE_BLSR_AMMO_2 5 #define USE_SKRD_AMMO_1 1 #define USE_SKRD_AMMO_2 5 #define USE_PHRD_AMMO_1 1 #define USE_PHRD_AMMO_2 1 #define USE_MACE_AMMO_1 1 #define USE_MACE_AMMO_2 5 void P_OpenWeapons(void); void P_CloseWeapons(void); void P_AddMaceSpot(mapthing_t * mthing); void P_RepositionMace(mobj_t * mo); void P_SetPsprite(player_t * player, int position, statenum_t stnum); void P_SetupPsprites(player_t * curplayer); void P_MovePsprites(player_t * curplayer); void P_DropWeapon(player_t * player); void P_ActivateBeak(player_t * player); void P_PostChickenWeapon(player_t * player, weapontype_t weapon); void P_UpdateBeak(player_t * player, pspdef_t * psp); // ***** P_USER ***** void P_PlayerThink(player_t * player); void P_Thrust(player_t * player, angle_t angle, fixed_t move); void P_PlayerRemoveArtifact(player_t * player, int slot); void P_PlayerUseArtifact(player_t * player, artitype_t arti); boolean P_UseArtifact(player_t * player, artitype_t arti); int P_GetPlayerNum(player_t * player); // ***** P_MOBJ ***** #define FLOOR_SOLID 0 #define FLOOR_WATER 1 #define FLOOR_LAVA 2 #define FLOOR_SLUDGE 3 #define ONFLOORZ INT_MIN #define ONCEILINGZ INT_MAX #define FLOATRANDZ (INT_MAX-1) extern mobjtype_t PuffType; extern mobj_t *MissileMobj; mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); void P_RemoveMobj(mobj_t * th); boolean P_SetMobjState(mobj_t * mobj, statenum_t state); boolean P_SetMobjStateNF(mobj_t * mobj, statenum_t state); void P_ThrustMobj(mobj_t * mo, angle_t angle, fixed_t move); int P_FaceMobj(mobj_t * source, mobj_t * target, angle_t * delta); boolean P_SeekerMissile(mobj_t * actor, angle_t thresh, angle_t turnMax); void P_MobjThinker(mobj_t * mobj); void P_BlasterMobjThinker(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); void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator); void P_RipperBlood(mobj_t * mo); int P_GetThingFloorType(mobj_t * thing); int P_HitFloor(mobj_t * thing); boolean P_CheckMissileSpawn(mobj_t * missile); mobj_t *P_SpawnMissile(mobj_t * source, mobj_t * dest, mobjtype_t type); mobj_t *P_SpawnMissileAngle(mobj_t * source, mobjtype_t type, angle_t angle, fixed_t momz); mobj_t *P_SpawnPlayerMissile(mobj_t * source, mobjtype_t type); mobj_t *P_SPMAngle(mobj_t * source, mobjtype_t type, angle_t angle); // ***** P_ENEMY ***** void P_NoiseAlert(mobj_t * target, mobj_t * emmiter); void P_InitMonsters(void); void P_AddBossSpot(fixed_t x, fixed_t y, angle_t angle); void P_Massacre(void); void P_DSparilTeleport(mobj_t * actor); // ***** P_MAPUTL ***** typedef struct { fixed_t x, y, dx, dy; } divline_t; typedef struct { fixed_t frac; // along trace line boolean isaline; union { mobj_t *thing; line_t *line; } d; } intercept_t; #define MAXINTERCEPTS 128 extern intercept_t intercepts[MAXINTERCEPTS], *intercept_p; typedef boolean(*traverser_t) (intercept_t * in); fixed_t P_AproxDistance(fixed_t dx, fixed_t dy); int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line); int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line); void P_MakeDivline(line_t * li, divline_t * dl); fixed_t P_InterceptVector(divline_t * v2, divline_t * v1); int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld); extern fixed_t opentop, openbottom, openrange; extern fixed_t lowfloor; void P_LineOpening(line_t * linedef); boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *)); boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *)); #define PT_ADDLINES 1 #define PT_ADDTHINGS 2 #define PT_EARLYOUT 4 extern divline_t trace; boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean(*trav) (intercept_t *)); void P_UnsetThingPosition(mobj_t * thing); void P_SetThingPosition(mobj_t * thing); // ***** P_MAP ***** extern boolean floatok; // if true, move would be ok if extern fixed_t tmfloorz, tmceilingz; // within tmfloorz - tmceilingz extern line_t *ceilingline; boolean P_TestMobjLocation(mobj_t * mobj); boolean P_CheckPosition(mobj_t * thing, fixed_t x, fixed_t y); mobj_t *P_CheckOnmobj(mobj_t * thing); void P_FakeZMovement(mobj_t * mo); boolean P_TryMove(mobj_t * thing, fixed_t x, fixed_t y); boolean P_TeleportMove(mobj_t * thing, fixed_t x, fixed_t y); void P_SlideMove(mobj_t * mo); boolean P_CheckSight(mobj_t * t1, mobj_t * t2); void P_UseLines(player_t * player); boolean P_ChangeSector(sector_t * sector, boolean crunch); extern mobj_t *linetarget; // who got hit (or NULL) fixed_t P_AimLineAttack(mobj_t * t1, angle_t angle, fixed_t distance); void P_LineAttack(mobj_t * t1, angle_t angle, fixed_t distance, fixed_t slope, int damage); void P_RadiusAttack(mobj_t * spot, mobj_t * source, int damage); // ***** P_SETUP ***** extern byte *rejectmatrix; // for fast sight rejection extern int32_t *blockmaplump; // offsets in blockmap are from here // [crispy] BLOCKMAP limit extern int32_t *blockmap; // [crispy] BLOCKMAP limit extern int bmapwidth, bmapheight; // in mapblocks extern fixed_t bmaporgx, bmaporgy; // origin of block map extern mobj_t **blocklinks; // for thing chains // ***** P_INTER ***** extern int maxammo[NUMAMMO]; extern int clipammo[NUMAMMO]; void P_SetMessage(player_t * player, const char *message, boolean ultmsg); void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher); void P_DamageMobj(mobj_t * target, mobj_t * inflictor, mobj_t * source, int damage); boolean P_GiveAmmo(player_t * player, ammotype_t ammo, int count); boolean P_GiveArtifact(player_t * player, artitype_t arti, mobj_t * mo); boolean P_GiveBody(player_t * player, int num); boolean P_GivePower(player_t * player, powertype_t power); boolean P_ChickenMorphPlayer(player_t * player); // ***** AM_MAP ***** boolean AM_Responder(event_t * ev); void AM_Ticker(void); void AM_Drawer(void); // ***** SB_BAR ***** extern int SB_state; extern int ArtifactFlash; void SB_PaletteFlash(void); #include "p_spec.h" #endif // __P_LOCAL__ crispy-doom-crispy-doom-5.6.4/src/heretic/p_map.c000066400000000000000000001314001360717211000217050ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_map.c #include #include "doomdef.h" #include "i_system.h" #include "m_bbox.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" /* =============================================================================== NOTES: =============================================================================== */ /* =============================================================================== mobj_t NOTES 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 it's 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 be 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 it's origin contained. A valid mobj_t is a mobj_t that has the proper subsector_t filled in for it's xy coordinates and is linked into the subsector's sector 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. =============================================================================== */ fixed_t tmbbox[4]; mobj_t *tmthing; int tmflags; fixed_t tmx, tmy; boolean floatok; // if true, move would be ok if // within tmfloorz - tmceilingz fixed_t tmfloorz, tmceilingz, tmdropoffz; // keep track of the line that lowers the ceiling, so missiles don't explode // against sky hack walls line_t *ceilingline; // keep track of special lines as they are hit, but don't process them // until the move is proven valid #define MAXSPECIALCROSS 8 line_t *spechit[MAXSPECIALCROSS]; int numspechit; mobj_t *onmobj; //generic global onmobj...used for landing on pods/players /* =============================================================================== TELEPORT MOVE =============================================================================== */ /* ================== = = PIT_StompThing = ================== */ boolean PIT_StompThing(mobj_t * thing) { fixed_t blockdist; if (!(thing->flags & MF_SHOOTABLE)) return true; blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) return true; // didn't hit it if (thing == tmthing) return true; // don't clip against self if (!(tmthing->flags2 & MF2_TELESTOMP)) { // Not allowed to stomp things return (false); } P_DamageMobj(thing, tmthing, tmthing, 10000); return true; } /* =================== = = P_TeleportMove = =================== */ boolean P_TeleportMove(mobj_t * thing, fixed_t x, fixed_t y) { int xl, xh, yl, yh, bx, by; subsector_t *newsubsec; // // kill anything occupying the position // tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector(x, y); ceilingline = NULL; // // the base floor / ceiling is from the subsector that contains the // point. Any contacted lines the step closer together will adjust them // tmfloorz = tmdropoffz = newsubsec->sector->floorheight; tmceilingz = newsubsec->sector->ceilingheight; validcount++; numspechit = 0; // // stomp on any things contacted // xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockThingsIterator(bx, by, PIT_StompThing)) return false; // // the move is ok, so link the thing into its new position // P_UnsetThingPosition(thing); thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; P_SetThingPosition(thing); return true; } /* =============================================================================== MOVEMENT ITERATOR FUNCTIONS =============================================================================== */ /* ================== = = PIT_CheckLine = = Adjusts tmfloorz and tmceilingz as lines are contacted ================== */ boolean PIT_CheckLine(line_t * ld) { if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) { return (true); } if (P_BoxOnLineSide(tmbbox, ld) != -1) { return (true); } // a line has been hit /* = = The moving thing's destination position will cross the given line. = If this should not be allowed, return false. = If the line is special, keep track of it to process later if the move = is proven ok. NOTE: specials are NOT sorted by order, so two special lines = that are only 8 pixels apart could be crossed in either order. */ if (!ld->backsector) { // One sided line if (tmthing->flags & MF_MISSILE) { // Missiles can trigger impact specials if (ld->special) { spechit[numspechit] = ld; numspechit++; } } return false; } if (!(tmthing->flags & MF_MISSILE)) { if (ld->flags & ML_BLOCKING) { // Explicitly blocking everything return (false); } if (!tmthing->player && ld->flags & ML_BLOCKMONSTERS && tmthing->type != MT_POD) { // Block monsters only return (false); } } P_LineOpening(ld); // set openrange, opentop, openbottom // adjust floor / ceiling heights if (opentop < tmceilingz) { tmceilingz = opentop; ceilingline = ld; } if (openbottom > tmfloorz) { tmfloorz = openbottom; } if (lowfloor < tmdropoffz) { tmdropoffz = lowfloor; } if (ld->special) { // Contacted a special line, add it to the list spechit[numspechit] = ld; numspechit++; } return (true); } //--------------------------------------------------------------------------- // // FUNC PIT_CheckThing // //--------------------------------------------------------------------------- boolean PIT_CheckThing(mobj_t * thing) { fixed_t blockdist; boolean solid; int damage; if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) { // Can't hit thing return (true); } blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) { // Didn't hit thing return (true); } if (thing == tmthing) { // Don't clip against self return (true); } if (tmthing->flags2 & MF2_PASSMOBJ) { // check if a mobj passed over/under another object if ((tmthing->type == MT_IMP || tmthing->type == MT_WIZARD) && (thing->type == MT_IMP || thing->type == MT_WIZARD)) { // don't let imps/wizards fly over other imps/wizards return false; } if (tmthing->z > thing->z + thing->height && !(thing->flags & MF_SPECIAL)) { return (true); } else if (tmthing->z + tmthing->height < thing->z && !(thing->flags & MF_SPECIAL)) { // under thing return (true); } } // Check for skulls slamming into things if (tmthing->flags & MF_SKULLFLY) { damage = ((P_Random() % 8) + 1) * tmthing->damage; P_DamageMobj(thing, tmthing, tmthing, damage); tmthing->flags &= ~MF_SKULLFLY; tmthing->momx = tmthing->momy = tmthing->momz = 0; P_SetMobjState(tmthing, tmthing->info->seestate); return (false); } // Check for missile if (tmthing->flags & MF_MISSILE) { // Check for passing through a ghost if ((thing->flags & MF_SHADOW) && (tmthing->flags2 & MF2_THRUGHOST)) { return (true); } // Check if it went over / under if (tmthing->z > thing->z + thing->height) { // Over thing return (true); } if (tmthing->z + tmthing->height < thing->z) { // Under thing return (true); } if (tmthing->target && tmthing->target->type == thing->type) { // Don't hit same species as originator if (thing == tmthing->target) { // Don't missile self return (true); } if (thing->type != MT_PLAYER) { // Hit same species as originator, explode, no damage return (false); } } if (!(thing->flags & MF_SHOOTABLE)) { // Didn't do any damage return !(thing->flags & MF_SOLID); } if (tmthing->flags2 & MF2_RIP) { if (!(thing->flags & MF_NOBLOOD)) { // Ok to spawn some blood P_RipperBlood(tmthing); } S_StartSound(tmthing, sfx_ripslop); damage = ((P_Random() & 3) + 2) * tmthing->damage; P_DamageMobj(thing, tmthing, tmthing->target, damage); if (thing->flags2 & MF2_PUSHABLE && !(tmthing->flags2 & MF2_CANNOTPUSH)) { // Push thing thing->momx += tmthing->momx >> 2; thing->momy += tmthing->momy >> 2; } numspechit = 0; return (true); } // Do damage damage = ((P_Random() % 8) + 1) * tmthing->damage; if (damage) { if (!(thing->flags & MF_NOBLOOD) && P_Random() < 192) { P_BloodSplatter(tmthing->x, tmthing->y, tmthing->z, thing); } P_DamageMobj(thing, tmthing, tmthing->target, damage); } return (false); } if (thing->flags2 & MF2_PUSHABLE && !(tmthing->flags2 & MF2_CANNOTPUSH)) { // Push thing thing->momx += tmthing->momx >> 2; thing->momy += tmthing->momy >> 2; } // Check for special thing if (thing->flags & MF_SPECIAL) { solid = (thing->flags & MF_SOLID) != 0; if (tmflags & MF_PICKUP) { // Can be picked up by tmthing P_TouchSpecialThing(thing, tmthing); // Can remove thing } return (!solid); } return (!(thing->flags & MF_SOLID)); } //--------------------------------------------------------------------------- // // PIT_CheckOnmobjZ // //--------------------------------------------------------------------------- boolean PIT_CheckOnmobjZ(mobj_t * thing) { fixed_t blockdist; if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) { // Can't hit thing return (true); } blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) { // Didn't hit thing return (true); } if (thing == tmthing) { // Don't clip against self return (true); } if (tmthing->z > thing->z + thing->height) { return (true); } else if (tmthing->z + tmthing->height < thing->z) { // under thing return (true); } if (thing->flags & MF_SOLID) { onmobj = thing; } return (!(thing->flags & MF_SOLID)); } /* =============================================================================== MOVEMENT CLIPPING =============================================================================== */ //---------------------------------------------------------------------------- // // FUNC P_TestMobjLocation // // Returns true if the mobj is not blocked by anything at its current // location, otherwise returns false. // //---------------------------------------------------------------------------- boolean P_TestMobjLocation(mobj_t * mobj) { int flags; flags = mobj->flags; mobj->flags &= ~MF_PICKUP; if (P_CheckPosition(mobj, mobj->x, mobj->y)) { // XY is ok, now check Z mobj->flags = flags; if ((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz)) { // Bad Z return (false); } return (true); } mobj->flags = flags; return (false); } /* ================== = = 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 ================== */ boolean P_CheckPosition(mobj_t * thing, fixed_t x, fixed_t y) { int xl, xh, yl, yh, bx, by; subsector_t *newsubsec; tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector(x, y); ceilingline = NULL; // // the base floor / ceiling is from the subsector that contains the // point. Any contacted lines the step closer together will adjust them // tmfloorz = tmdropoffz = newsubsec->sector->floorheight; tmceilingz = newsubsec->sector->ceilingheight; validcount++; numspechit = 0; if (tmflags & MF_NOCLIP) return true; // // check things first, possibly picking things up // the bounding box is extended by MAXRADIUS because mobj_ts are grouped // into mapblocks based on their origin point, and can overlap into adjacent // blocks by up to MAXRADIUS units // xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockThingsIterator(bx, by, PIT_CheckThing)) return false; // // check lines // xl = (tmbbox[BOXLEFT] - bmaporgx) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy) >> MAPBLOCKSHIFT; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockLinesIterator(bx, by, PIT_CheckLine)) return false; return true; } //============================================================================= // // P_CheckOnmobj(mobj_t *thing) // // Checks if the new Z position is legal //============================================================================= mobj_t *P_CheckOnmobj(mobj_t * thing) { int xl, xh, yl, yh, bx, by; subsector_t *newsubsec; fixed_t x; fixed_t y; mobj_t oldmo; x = thing->x; y = thing->y; tmthing = thing; tmflags = thing->flags; oldmo = *thing; // save the old mobj before the fake zmovement P_FakeZMovement(tmthing); 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; if (tmflags & MF_NOCLIP) return NULL; // // check things first, possibly picking things up // the bounding box is extended by MAXRADIUS because mobj_ts are grouped // into mapblocks based on their origin point, and can overlap into adjacent // blocks by up to MAXRADIUS units // xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockThingsIterator(bx, by, PIT_CheckOnmobjZ)) { *tmthing = oldmo; return onmobj; } *tmthing = oldmo; return NULL; } //============================================================================= // // P_FakeZMovement // // Fake the zmovement so that we can check if a move is legal //============================================================================= void P_FakeZMovement(mobj_t * mo) { int dist; int delta; // // adjust height // mo->z += mo->momz; if (mo->flags & MF_FLOAT && mo->target) { // float down towards target if too close if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT)) { dist = P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y); delta = (mo->target->z + (mo->height >> 1)) - mo->z; if (delta < 0 && dist < -(delta * 3)) mo->z -= FLOATSPEED; else if (delta > 0 && dist < (delta * 3)) mo->z += FLOATSPEED; } } if (mo->player && mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz) && leveltime & 2) { mo->z += finesine[(FINEANGLES / 20 * leveltime >> 2) & FINEMASK]; } // // clip movement // if (mo->z <= mo->floorz) { // Hit the floor mo->z = mo->floorz; if (mo->momz < 0) { mo->momz = 0; } if (mo->flags & MF_SKULLFLY) { // The skull slammed into something mo->momz = -mo->momz; } if (mo->info->crashstate && (mo->flags & MF_CORPSE)) { return; } } else if (mo->flags2 & MF2_LOGRAV) { if (mo->momz == 0) mo->momz = -(GRAVITY >> 3) * 2; else mo->momz -= GRAVITY >> 3; } else if (!(mo->flags & MF_NOGRAVITY)) { if (mo->momz == 0) mo->momz = -GRAVITY * 2; else mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // hit the ceiling if (mo->momz > 0) mo->momz = 0; mo->z = mo->ceilingz - mo->height; if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } } } //========================================================================== // // CheckMissileImpact // //========================================================================== void CheckMissileImpact(mobj_t * mobj) { int i; if (!numspechit || !(mobj->flags & MF_MISSILE) || !mobj->target) { return; } if (!mobj->target->player) { return; } for (i = numspechit - 1; i >= 0; i--) { P_ShootSpecialLine(mobj->target, spechit[i]); } } /* =================== = = P_TryMove = = Attempt to move to a new position, crossing special lines unless MF_TELEPORT = is set = =================== */ boolean P_TryMove(mobj_t * thing, fixed_t x, fixed_t y) { fixed_t oldx, oldy; int side, oldside; line_t *ld; floatok = false; if (!P_CheckPosition(thing, x, y)) { // Solid wall or thing CheckMissileImpact(thing); return false; } if (!(thing->flags & MF_NOCLIP)) { if (tmceilingz - tmfloorz < thing->height) { // Doesn't fit CheckMissileImpact(thing); return false; } floatok = true; if (!(thing->flags & MF_TELEPORT) && tmceilingz - thing->z < thing->height && !(thing->flags2 & MF2_FLY)) { // mobj must lower itself to fit CheckMissileImpact(thing); return false; } if (thing->flags2 & MF2_FLY) { 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; } } if (!(thing->flags & MF_TELEPORT) // The Minotaur floor fire (MT_MNTRFX2) can step up any amount && thing->type != MT_MNTRFX2 && tmfloorz - thing->z > 24 * FRACUNIT) { // Too big a step up CheckMissileImpact(thing); return false; } if ((thing->flags & MF_MISSILE) && tmfloorz > thing->z) { CheckMissileImpact(thing); } if (!(thing->flags & (MF_DROPOFF | MF_FLOAT)) && tmfloorz - tmdropoffz > 24 * FRACUNIT) { // Can't move over a dropoff return false; } } // // the move is ok, so link the thing into its new position // P_UnsetThingPosition(thing); oldx = thing->x; oldy = thing->y; thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; P_SetThingPosition(thing); if (thing->flags2 & MF2_FOOTCLIP && P_GetThingFloorType(thing) != FLOOR_SOLID) { thing->flags2 |= MF2_FEETARECLIPPED; } else if (thing->flags2 & MF2_FEETARECLIPPED) { thing->flags2 &= ~MF2_FEETARECLIPPED; } // // if any special lines were hit, do the effect // if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) while (numspechit--) { // see if the line was crossed ld = spechit[numspechit]; side = P_PointOnLineSide(thing->x, thing->y, ld); oldside = P_PointOnLineSide(oldx, oldy, ld); if (side != oldside) { if (ld->special) P_CrossSpecialLine(ld - lines, oldside, thing); } } return true; } /* ================== = = P_ThingHeightClip = = Takes a valid thing and adjusts the thing->floorz, thing->ceilingz, = anf 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 ================== */ boolean P_ThingHeightClip(mobj_t * thing) { boolean onfloor; onfloor = (thing->z == thing->floorz); P_CheckPosition(thing, thing->x, thing->y); // what about stranding a monster partially off an edge? thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; if (onfloor) // walking monsters rise and fall with the floor thing->z = thing->floorz; else { // don't adjust a floating monster unless forced to if (thing->z + thing->height > thing->ceilingz) thing->z = thing->ceilingz - thing->height; } if (thing->ceilingz - thing->floorz < thing->height) return false; return true; } /* ============================================================================== SLIDE MOVE Allows the player to slide along any angled walls ============================================================================== */ fixed_t bestslidefrac, secondslidefrac; line_t *bestslideline, *secondslideline; mobj_t *slidemo; fixed_t tmxmove, tmymove; /* ================== = = P_HitSlideLine = = Adjusts the xmove / ymove so that the next move will slide along the wall ================== */ void P_HitSlideLine(line_t * ld) { int side; angle_t lineangle, moveangle, deltaangle; fixed_t movelen, newlen; if (ld->slopetype == ST_HORIZONTAL) { tmymove = 0; return; } if (ld->slopetype == ST_VERTICAL) { tmxmove = 0; return; } side = P_PointOnLineSide(slidemo->x, slidemo->y, ld); lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy); if (side == 1) lineangle += ANG180; moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove); deltaangle = moveangle - lineangle; if (deltaangle > ANG180) deltaangle += ANG180; // I_Error ("SlideLine: ang>ANG180"); lineangle >>= ANGLETOFINESHIFT; deltaangle >>= ANGLETOFINESHIFT; movelen = P_AproxDistance(tmxmove, tmymove); newlen = FixedMul(movelen, finecosine[deltaangle]); tmxmove = FixedMul(newlen, finecosine[lineangle]); tmymove = FixedMul(newlen, finesine[lineangle]); } /* ============== = = PTR_SlideTraverse = ============== */ boolean 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; } P_LineOpening(li); // set openrange, opentop, openbottom 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 return true; // this line doesn't block movement // the line does block movement, see if it is closer than best so far isblocking: if (in->frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in->frac; bestslideline = li; } return false; // stop } /* ================== = = P_SlideMove = = The momx / momy move is bad, so try to slide along a wall = = Find the first line hit, move flush to it, and slide along it = = This is a kludgy mess. ================== */ void P_SlideMove(mobj_t * mo) { fixed_t leadx, leady; fixed_t trailx, traily; fixed_t newx, newy; int hitcount; slidemo = mo; hitcount = 0; retry: if (++hitcount == 3) goto stairstep; // don't loop forever // // trace along the three leading corners // if (mo->momx > 0) { leadx = mo->x + mo->radius; trailx = mo->x - mo->radius; } else { leadx = mo->x - mo->radius; trailx = mo->x + mo->radius; } if (mo->momy > 0) { leady = mo->y + mo->radius; traily = mo->y - mo->radius; } else { leady = mo->y - mo->radius; traily = mo->y + mo->radius; } bestslidefrac = FRACUNIT + 1; P_PathTraverse(leadx, leady, leadx + mo->momx, leady + mo->momy, PT_ADDLINES, PTR_SlideTraverse); P_PathTraverse(trailx, leady, trailx + mo->momx, leady + mo->momy, PT_ADDLINES, PTR_SlideTraverse); P_PathTraverse(leadx, traily, leadx + mo->momx, traily + mo->momy, PT_ADDLINES, PTR_SlideTraverse); // // move up to the wall // if (bestslidefrac == FRACUNIT + 1) { // the move most have hit the middle, so stairstep stairstep: if (!P_TryMove(mo, mo->x, mo->y + mo->momy)) P_TryMove(mo, mo->x + mo->momx, mo->y); return; } bestslidefrac -= 0x800; // fudge a bit to make sure it doesn't hit if (bestslidefrac > 0) { newx = FixedMul(mo->momx, bestslidefrac); newy = FixedMul(mo->momy, bestslidefrac); if (!P_TryMove(mo, mo->x + newx, mo->y + newy)) goto stairstep; } // // now continue along the wall // bestslidefrac = FRACUNIT - (bestslidefrac + 0x800); // remainder if (bestslidefrac > FRACUNIT) bestslidefrac = FRACUNIT; if (bestslidefrac <= 0) return; tmxmove = FixedMul(mo->momx, bestslidefrac); tmymove = FixedMul(mo->momy, bestslidefrac); P_HitSlideLine(bestslideline); // clip the moves mo->momx = tmxmove; mo->momy = tmymove; if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove)) { goto retry; } } /* ============================================================================== P_LineAttack ============================================================================== */ mobj_t *linetarget; // who got hit (or NULL) mobj_t *shootthing; fixed_t shootz; // height if not aiming up or down // ???: use slope for monsters? int la_damage; fixed_t attackrange; fixed_t aimslope; extern fixed_t topslope, bottomslope; // slopes to top and bottom of target /* =============================================================================== = = PTR_AimTraverse = = Sets linetaget and aimslope when a target is aimed at =============================================================================== */ boolean PTR_AimTraverse(intercept_t * in) { line_t *li; mobj_t *th; fixed_t slope, thingtopslope, thingbottomslope; fixed_t dist; if (in->isaline) { li = in->d.line; if (!(li->flags & ML_TWOSIDED)) return false; // stop // // crosses a two sided line // a two sided line will restrict the possible target ranges P_LineOpening(li); if (openbottom >= opentop) return false; // stop dist = FixedMul(attackrange, in->frac); if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv(openbottom - shootz, dist); if (slope > bottomslope) bottomslope = slope; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv(opentop - shootz, dist); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop return true; // shot continues } // // shoot a thing // th = in->d.thing; if (th == shootthing) return true; // can't shoot self if (!(th->flags & MF_SHOOTABLE)) return true; // corpse or something if (th->type == MT_POD) { // Can't auto-aim at pods 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 = ============================================================================== */ boolean PTR_ShootTraverse(intercept_t * in) { fixed_t x, y, z; fixed_t frac; line_t *li; mobj_t *th; fixed_t slope; fixed_t dist; fixed_t thingtopslope, thingbottomslope; mobj_t *mo; if (in->isaline) { li = in->d.line; if (li->special) P_ShootSpecialLine(shootthing, li); if (!(li->flags & ML_TWOSIDED)) goto hitline; // // crosses a two sided line // P_LineOpening(li); dist = FixedMul(attackrange, in->frac); if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv(openbottom - shootz, dist); if (slope > aimslope) goto hitline; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv(opentop - shootz, dist); if (slope < aimslope) goto hitline; } return true; // shot continues // // hit line // hitline: // position a bit closer frac = in->frac - FixedDiv(4 * FRACUNIT, attackrange); x = trace.x + FixedMul(trace.dx, frac); y = trace.y + FixedMul(trace.dy, frac); z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange)); if (li->frontsector->ceilingpic == skyflatnum) { if (z > li->frontsector->ceilingheight) return false; // don't shoot the sky! if (li->backsector && li->backsector->ceilingpic == skyflatnum) return false; // it's a sky hack wall } P_SpawnPuff(x, y, z); return false; // don't go any farther } // // 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 for physical attacks on a ghost // if (th->flags & MF_SHADOW && shootthing->player->readyweapon == wp_staff) { 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 < 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)); if (PuffType == MT_BLASTERPUFF1) { // Make blaster big puff mo = P_SpawnMobj(x, y, z, MT_BLASTERPUFF2); S_StartSound(mo, sfx_blshit); } else { P_SpawnPuff(x, y, z); } if (la_damage) { if (!(in->d.thing->flags & MF_NOBLOOD) && P_Random() < 192) { P_BloodSplatter(x, y, z, in->d.thing); } P_DamageMobj(th, shootthing, shootthing, la_damage); } return (false); // don't go any farther } /* ================= = = P_AimLineAttack = ================= */ fixed_t P_AimLineAttack(mobj_t * t1, angle_t angle, fixed_t distance) { fixed_t x2, y2; angle >>= ANGLETOFINESHIFT; shootthing = t1; x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; topslope = 100 * FRACUNIT / 160; // can't shoot outside view angles bottomslope = -100 * FRACUNIT / 160; attackrange = distance; linetarget = NULL; 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, 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; if (t1->flags2 & MF2_FEETARECLIPPED) { shootz -= FOOTCLIPSIZE; } attackrange = distance; aimslope = slope; P_PathTraverse(t1->x, t1->y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, PTR_ShootTraverse); } /* ============================================================================== USE LINES ============================================================================== */ mobj_t *usething; boolean PTR_UseTraverse(intercept_t * in) { if (!in->d.line->special) { P_LineOpening(in->d.line); if (openrange <= 0) { //S_StartSound (usething, sfx_noway); return false; // can't use through a wall } return true; // not a special line, but keep checking } if (P_PointOnLineSide(usething->x, usething->y, in->d.line) == 1) return false; // don't use back sides P_UseSpecialLine(usething, in->d.line); return false; // can't use for than one special line in a row } /* ================ = = P_UseLines = = Looks for special lines in front of the player to activate ================ */ void P_UseLines(player_t * player) { int angle; fixed_t x1, y1, x2, y2; usething = player->mo; angle = player->mo->angle >> ANGLETOFINESHIFT; x1 = player->mo->x; y1 = player->mo->y; x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse); } /* ============================================================================== RADIUS ATTACK ============================================================================== */ mobj_t *bombsource; mobj_t *bombspot; int bombdamage; /* ================= = = PIT_RadiusAttack = = Source is the creature that casued the explosion at spot ================= */ boolean PIT_RadiusAttack(mobj_t * thing) { fixed_t dx, dy, dist; if (!(thing->flags & MF_SHOOTABLE)) { return true; } if (thing->type == MT_MINOTAUR || thing->type == MT_SORCERER1 || thing->type == MT_SORCERER2) { // Episode 2 and 3 bosses take no damage from PIT_RadiusAttack return (true); } dx = abs(thing->x - bombspot->x); dy = abs(thing->y - bombspot->y); dist = dx > dy ? dx : dy; dist = (dist - thing->radius) >> FRACBITS; if (dist < 0) { dist = 0; } if (dist >= bombdamage) { // Out of range return true; } if (P_CheckSight(thing, bombspot)) { // OK to damage, target is in direct path P_DamageMobj(thing, bombspot, bombsource, bombdamage - dist); } return (true); } /* ================= = = P_RadiusAttack = = Source is the creature that casued the explosion at spot ================= */ void P_RadiusAttack(mobj_t * spot, mobj_t * source, int damage) { int x, y, xl, xh, yl, yh; fixed_t dist; dist = (damage + MAXRADIUS) << FRACBITS; yh = (spot->y + dist - bmaporgy) >> MAPBLOCKSHIFT; yl = (spot->y - dist - bmaporgy) >> MAPBLOCKSHIFT; xh = (spot->x + dist - bmaporgx) >> MAPBLOCKSHIFT; xl = (spot->x - dist - bmaporgx) >> MAPBLOCKSHIFT; bombspot = spot; if (spot->type == MT_POD && spot->target) { bombsource = spot->target; } else { bombsource = source; } bombdamage = damage; for (y = yl; y <= yh; y++) for (x = xl; x <= xh; x++) P_BlockThingsIterator(x, y, PIT_RadiusAttack); } /* ============================================================================== SECTOR HEIGHT CHANGING = After modifying a sectors floor or ceiling height, call this = routine to adjust the positions of all things that touch the = sector. = = If anything doesn't fit anymore, true will be returned. = If crunch is true, they will take damage as they are being crushed = If Crunch is false, you should set the sector height back the way it = was and call P_ChangeSector again to undo the changes ============================================================================== */ boolean crushchange; boolean nofit; /* =============== = = PIT_ChangeSector = =============== */ boolean 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); thing->height = 0; thing->radius = 0; return true; // keep checking } // crunch dropped items if (thing->flags & MF_DROPPED) { P_RemoveMobj(thing); return true; // keep checking } if (!(thing->flags & MF_SHOOTABLE)) return true; // assume it is bloody gibs or something nofit = true; if (crushchange && !(leveltime & 3)) { P_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); mo->momx = P_SubRandom() << 12; mo->momy = P_SubRandom() << 12; } return true; // keep checking (crush other things) } /* =============== = = P_ChangeSector = =============== */ boolean P_ChangeSector(sector_t * sector, boolean crunch) { int x, y; nofit = false; crushchange = crunch; // recheck 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; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_maputl.c000066400000000000000000000434021360717211000224360ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_maputl.c #include #include "doomdef.h" #include "m_bbox.h" #include "p_local.h" /* =================== = = P_AproxDistance = = Gives an estimation of distance (not exact) = =================== */ fixed_t P_AproxDistance(fixed_t dx, fixed_t dy) { dx = abs(dx); dy = abs(dy); if (dx < dy) return dx + dy - (dx >> 1); return dx + dy - (dy >> 1); } /* ================== = = P_PointOnLineSide = = Returns 0 or 1 ================== */ int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line) { fixed_t dx, dy; fixed_t left, right; if (!line->dx) { if (x <= line->v1->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->v1->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->v1->x); dy = (y - line->v1->y); left = FixedMul(line->dy >> FRACBITS, dx); right = FixedMul(dy, line->dx >> FRACBITS); if (right < left) return 0; // front side return 1; // back side } /* ================= = = P_BoxOnLineSide = = Considers the line to be infinite = Returns side 0 or 1, -1 if box crosses the line ================= */ int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld) { int p1 = 0, p2 = 0; switch (ld->slopetype) { case ST_HORIZONTAL: p1 = tmbox[BOXTOP] > ld->v1->y; p2 = tmbox[BOXBOTTOM] > ld->v1->y; if (ld->dx < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_VERTICAL: p1 = tmbox[BOXRIGHT] < ld->v1->x; p2 = tmbox[BOXLEFT] < ld->v1->x; if (ld->dy < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_POSITIVE: p1 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); break; case ST_NEGATIVE: p1 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); break; } if (p1 == p2) return p1; return -1; } /* ================== = = P_PointOnDivlineSide = = Returns 0 or 1 ================== */ int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line) { fixed_t dx, dy; fixed_t left, right; if (!line->dx) { if (x <= line->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->x); dy = (y - line->y); // try to quickly decide by looking at sign bits if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000) { if ((line->dy ^ dx) & 0x80000000) return 1; // (left is negative) return 0; } left = FixedMul(line->dy >> 8, dx >> 8); right = FixedMul(dy >> 8, line->dx >> 8); if (right < left) return 0; // front side return 1; // back side } /* ============== = = P_MakeDivline = ============== */ void P_MakeDivline(line_t * li, divline_t * dl) { dl->x = li->v1->x; dl->y = li->v1->y; dl->dx = li->dx; dl->dy = li->dy; } /* =============== = = P_InterceptVector = = Returns the fractional intercept point along the first divline = = This is only called by the addthings and addlines traversers =============== */ fixed_t P_InterceptVector(divline_t * v2, divline_t * v1) { #if 1 fixed_t frac, num, den; den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul((v1->x - v2->x) >> 8, v1->dy) + FixedMul((v2->y - v1->y) >> 8, v1->dx); frac = FixedDiv(num, den); return frac; #else float frac, num, den, v1x, v1y, v1dx, v1dy, v2x, v2y, v2dx, v2dy; v1x = (float) v1->x / FRACUNIT; v1y = (float) v1->y / FRACUNIT; v1dx = (float) v1->dx / FRACUNIT; v1dy = (float) v1->dy / FRACUNIT; v2x = (float) v2->x / FRACUNIT; v2y = (float) v2->y / FRACUNIT; v2dx = (float) v2->dx / FRACUNIT; v2dy = (float) v2->dy / FRACUNIT; den = v1dy * v2dx - v1dx * v2dy; if (den == 0) return 0; // parallel num = (v1x - v2x) * v1dy + (v2y - v1y) * v1dx; frac = num / den; return frac * FRACUNIT; #endif } /* ================== = = P_LineOpening = = Sets opentop and openbottom to the window through a two sided line = OPTIMIZE: keep this precalculated ================== */ fixed_t opentop, openbottom, openrange; fixed_t lowfloor; void P_LineOpening(line_t * linedef) { sector_t *front, *back; if (linedef->sidenum[1] == -1) { // single sided line openrange = 0; return; } front = linedef->frontsector; back = linedef->backsector; if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; if (front->floorheight > back->floorheight) { openbottom = front->floorheight; lowfloor = back->floorheight; } else { openbottom = back->floorheight; lowfloor = front->floorheight; } openrange = opentop - openbottom; } /* =============================================================================== THING POSITION SETTING =============================================================================== */ /* =================== = = P_UnsetThingPosition = = Unlinks a thing from block map and sectors = =================== */ void P_UnsetThingPosition(mobj_t * thing) { int blockx, blocky; if (!(thing->flags & MF_NOSECTOR)) { // inert things don't need to be in blockmap // unlink from subsector if (thing->snext) thing->snext->sprev = thing->sprev; if (thing->sprev) thing->sprev->snext = thing->snext; else thing->subsector->sector->thinglist = thing->snext; } if (!(thing->flags & MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap // unlink from block map if (thing->bnext) thing->bnext->bprev = thing->bprev; if (thing->bprev) thing->bprev->bnext = thing->bnext; else { blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; if (blockx >= 0 && blockx < bmapwidth && blocky >= 0 && blocky < bmapheight) blocklinks[blocky * bmapwidth + blockx] = thing->bnext; } } } /* =================== = = P_SetThingPosition = = Links a thing into both a block and a subsector based on it's x y = Sets thing->subsector properly = =================== */ void P_SetThingPosition(mobj_t * thing) { subsector_t *ss; sector_t *sec; int blockx, blocky; mobj_t **link; // // link into subsector // ss = R_PointInSubsector(thing->x, thing->y); thing->subsector = ss; if (!(thing->flags & MF_NOSECTOR)) { // invisible things don't go into the sector links sec = ss->sector; thing->sprev = NULL; thing->snext = sec->thinglist; if (sec->thinglist) sec->thinglist->sprev = thing; sec->thinglist = thing; } // // link into blockmap // if (!(thing->flags & MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; if (blockx >= 0 && blockx < bmapwidth && blocky >= 0 && blocky < bmapheight) { link = &blocklinks[blocky * bmapwidth + blockx]; thing->bprev = NULL; thing->bnext = *link; if (*link) (*link)->bprev = thing; *link = thing; } else { // thing is off the map thing->bnext = thing->bprev = NULL; } } } /* =============================================================================== BLOCK MAP ITERATORS For each line/thing in the given mapblock, call the passed 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 =================== */ boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *)) { int offset; int32_t *list; // [crispy] BLOCKMAP limit line_t *ld; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) return true; offset = y * bmapwidth + x; offset = *(blockmap + offset); for (list = blockmaplump + offset; *list != -1; list++) { ld = &lines[*list]; if (ld->validcount == validcount) continue; // line has already been checked ld->validcount = validcount; if (!func(ld)) return false; } return true; // everything was checked } /* ================== = = P_BlockThingsIterator = ================== */ boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *)) { mobj_t *mobj; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) return true; for (mobj = blocklinks[y * bmapwidth + x]; mobj; mobj = mobj->bnext) if (!func(mobj)) return false; return true; } /* =============================================================================== INTERCEPT ROUTINES =============================================================================== */ intercept_t intercepts[MAXINTERCEPTS], *intercept_p; divline_t trace; boolean earlyout; int ptflags; /* ================== = = PIT_AddLineIntercepts = = Looks for lines in the given block that intercept the given trace = to add to the intercepts list = A line is crossed if its endpoints are on opposite sides of the trace = Returns true if earlyout and a solid line hit ================== */ boolean PIT_AddLineIntercepts(line_t * ld) { int s1, s2; fixed_t frac; divline_t dl; // avoid precision problems with two routines if (trace.dx > FRACUNIT * 16 || trace.dy > FRACUNIT * 16 || trace.dx < -FRACUNIT * 16 || trace.dy < -FRACUNIT * 16) { s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace); s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace); } else { s1 = P_PointOnLineSide(trace.x, trace.y, ld); s2 = P_PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy, ld); } if (s1 == s2) return true; // line isn't crossed // // hit the line // P_MakeDivline(ld, &dl); frac = P_InterceptVector(&trace, &dl); if (frac < 0) return true; // behind source // try to early out the check if (earlyout && frac < FRACUNIT && !ld->backsector) return false; // stop checking intercept_p->frac = frac; intercept_p->isaline = true; intercept_p->d.line = ld; intercept_p++; // [crispy] catch intercepts overflows if (intercept_p - intercepts == MAXINTERCEPTS) return false; return true; // continue } /* ================== = = PIT_AddThingIntercepts = ================== */ boolean PIT_AddThingIntercepts(mobj_t * thing) { fixed_t x1, y1, x2, y2; int s1, s2; boolean tracepositive; divline_t dl; fixed_t frac; tracepositive = (trace.dx ^ trace.dy) > 0; // check a corner to corner crossection for hit if (tracepositive) { x1 = thing->x - thing->radius; y1 = thing->y + thing->radius; x2 = thing->x + thing->radius; y2 = thing->y - thing->radius; } else { x1 = thing->x - thing->radius; y1 = thing->y - thing->radius; x2 = thing->x + thing->radius; y2 = thing->y + thing->radius; } s1 = P_PointOnDivlineSide(x1, y1, &trace); s2 = P_PointOnDivlineSide(x2, y2, &trace); if (s1 == s2) return true; // line isn't crossed dl.x = x1; dl.y = y1; dl.dx = x2 - x1; dl.dy = y2 - y1; frac = P_InterceptVector(&trace, &dl); if (frac < 0) return true; // behind source intercept_p->frac = frac; intercept_p->isaline = false; intercept_p->d.thing = thing; intercept_p++; // [crispy] catch intercepts overflows if (intercept_p - intercepts == MAXINTERCEPTS) return false; return true; // keep going } /* ==================== = = P_TraverseIntercepts = = Returns true if the traverser function returns true for all lines ==================== */ boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac) { int count; fixed_t dist; intercept_t *scan, *in; count = intercept_p - intercepts; in = 0; // shut up compiler warning while (count--) { dist = INT_MAX; for (scan = intercepts; scan < intercept_p; scan++) if (scan->frac < dist) { dist = scan->frac; in = scan; } if (dist > maxfrac) return true; // checked everything in range #if 0 { // don't check these yet, ther may be others inserted in = scan = intercepts; for (scan = intercepts; scan < intercept_p; scan++) if (scan->frac > maxfrac) *in++ = *scan; intercept_p = in; return false; } #endif if (!func(in)) return false; // don't bother going farther in->frac = INT_MAX; } return true; // everything was traversed } /* ================== = = 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 ================== */ boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean(*trav) (intercept_t *)) { fixed_t xt1, yt1, xt2, yt2; fixed_t xstep, ystep; fixed_t partial; fixed_t xintercept, yintercept; int mapx, mapy, mapxstep, mapystep; int count; earlyout = (flags & PT_EARLYOUT) != 0; validcount++; intercept_p = intercepts; if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) y1 += FRACUNIT; // don't side exactly on a line trace.x = x1; trace.y = y1; trace.dx = x2 - x1; trace.dy = y2 - y1; x1 -= bmaporgx; y1 -= bmaporgy; xt1 = x1 >> MAPBLOCKSHIFT; yt1 = y1 >> MAPBLOCKSHIFT; x2 -= bmaporgx; y2 -= bmaporgy; xt2 = x2 >> MAPBLOCKSHIFT; yt2 = y2 >> MAPBLOCKSHIFT; if (xt2 > xt1) { mapxstep = 1; partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1)); ystep = 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 (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); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_mobj.c000066400000000000000000001257431360717211000220740ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_mobj.c #include "doomdef.h" #include "i_system.h" #include "m_random.h" #include "p_local.h" #include "sounds.h" #include "s_sound.h" void G_PlayerReborn(int player); void P_SpawnMapThing(mapthing_t * mthing); mobjtype_t PuffType; mobj_t *MissileMobj; static fixed_t FloatBobOffsets[64] = { 0, 51389, 102283, 152192, 200636, 247147, 291278, 332604, 370727, 405280, 435929, 462380, 484378, 501712, 514213, 521763, 524287, 521763, 514213, 501712, 484378, 462380, 435929, 405280, 370727, 332604, 291278, 247147, 200636, 152192, 102283, 51389, -1, -51390, -102284, -152193, -200637, -247148, -291279, -332605, -370728, -405281, -435930, -462381, -484380, -501713, -514215, -521764, -524288, -521764, -514214, -501713, -484379, -462381, -435930, -405280, -370728, -332605, -291279, -247148, -200637, -152193, -102284, -51389 }; //---------------------------------------------------------------------------- // // FUNC P_SetMobjState // // Returns true if the mobj is still present. // //---------------------------------------------------------------------------- boolean P_SetMobjState(mobj_t * mobj, statenum_t state) { state_t *st; if (state == S_NULL) { // Remove mobj mobj->state = (state_t *) S_NULL; P_RemoveMobj(mobj); return (false); } st = &states[state]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; if (st->action) { // Call action function st->action(mobj); } return (true); } //---------------------------------------------------------------------------- // // FUNC P_SetMobjStateNF // // Same as P_SetMobjState, but does not call the state function. // //---------------------------------------------------------------------------- boolean P_SetMobjStateNF(mobj_t * mobj, statenum_t state) { state_t *st; if (state == S_NULL) { // Remove mobj mobj->state = (state_t *) S_NULL; P_RemoveMobj(mobj); return (false); } st = &states[state]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; return (true); } //---------------------------------------------------------------------------- // // PROC P_ExplodeMissile // //---------------------------------------------------------------------------- void P_ExplodeMissile(mobj_t * mo) { if (mo->type == MT_WHIRLWIND) { if (++mo->special2.i < 60) { return; } } mo->momx = mo->momy = mo->momz = 0; P_SetMobjState(mo, mobjinfo[mo->type].deathstate); //mo->tics -= P_Random()&3; mo->flags &= ~MF_MISSILE; if (mo->info->deathsound) { S_StartSound(mo, mo->info->deathsound); } } //---------------------------------------------------------------------------- // // PROC P_FloorBounceMissile // //---------------------------------------------------------------------------- void P_FloorBounceMissile(mobj_t * mo) { mo->momz = -mo->momz; P_SetMobjState(mo, mobjinfo[mo->type].deathstate); } //---------------------------------------------------------------------------- // // PROC P_ThrustMobj // //---------------------------------------------------------------------------- void P_ThrustMobj(mobj_t * mo, angle_t angle, fixed_t move) { angle >>= ANGLETOFINESHIFT; mo->momx += FixedMul(move, finecosine[angle]); mo->momy += FixedMul(move, finesine[angle]); } //---------------------------------------------------------------------------- // // FUNC P_FaceMobj // // Returns 1 if 'source' needs to turn clockwise, or 0 if 'source' needs // to turn counter clockwise. 'delta' is set to the amount 'source' // needs to turn. // //---------------------------------------------------------------------------- int P_FaceMobj(mobj_t * source, mobj_t * target, angle_t * delta) { angle_t diff; angle_t angle1; angle_t angle2; angle1 = source->angle; angle2 = R_PointToAngle2(source->x, source->y, target->x, target->y); if (angle2 > angle1) { diff = angle2 - angle1; if (diff > ANG180) { *delta = ANG_MAX - diff; return (0); } else { *delta = diff; return (1); } } else { diff = angle1 - angle2; if (diff > ANG180) { *delta = ANG_MAX - diff; return (1); } else { *delta = diff; return (0); } } } //---------------------------------------------------------------------------- // // FUNC P_SeekerMissile // // The missile special1 field must be mobj_t *target. Returns true if // target was tracked, false if not. // //---------------------------------------------------------------------------- boolean P_SeekerMissile(mobj_t * actor, angle_t thresh, angle_t turnMax) { int dir; int dist; angle_t delta; angle_t angle; mobj_t *target; target = (mobj_t *) actor->special1.m; if (target == NULL) { return (false); } if (!(target->flags & MF_SHOOTABLE)) { // Target died actor->special1.m = NULL; return (false); } dir = P_FaceMobj(actor, target, &delta); if (delta > thresh) { delta >>= 1; if (delta > turnMax) { delta = turnMax; } } if (dir) { // Turn clockwise actor->angle += delta; } else { // Turn counter clockwise actor->angle -= delta; } angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(actor->info->speed, finecosine[angle]); actor->momy = FixedMul(actor->info->speed, finesine[angle]); if (actor->z + actor->height < target->z || target->z + target->height < actor->z) { // Need to seek vertically dist = P_AproxDistance(target->x - actor->x, target->y - actor->y); dist = dist / actor->info->speed; if (dist < 1) { dist = 1; } actor->momz = (target->z - actor->z) / dist; } return (true); } //---------------------------------------------------------------------------- // // PROC P_XYMovement // //---------------------------------------------------------------------------- #define STOPSPEED 0x1000 #define FRICTION_NORMAL 0xe800 #define FRICTION_LOW 0xf900 #define FRICTION_FLY 0xeb00 void P_XYMovement(mobj_t * mo) { fixed_t ptryx, ptryy; player_t *player; fixed_t xmove, ymove; int special; static int windTab[3] = { 2048 * 5, 2048 * 10, 2048 * 25 }; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // A flying mobj slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState(mo, mo->info->seestate); } return; } special = mo->subsector->sector->special; if (mo->flags2 & MF2_WINDTHRUST) { switch (special) { case 40: case 41: case 42: // Wind_East P_ThrustMobj(mo, 0, windTab[special - 40]); break; case 43: case 44: case 45: // Wind_North P_ThrustMobj(mo, ANG90, windTab[special - 43]); break; case 46: case 47: case 48: // Wind_South P_ThrustMobj(mo, ANG270, windTab[special - 46]); break; case 49: case 50: case 51: // Wind_West P_ThrustMobj(mo, ANG180, windTab[special - 49]); break; } } player = mo->player; if (mo->momx > MAXMOVE) { mo->momx = MAXMOVE; } else if (mo->momx < -MAXMOVE) { mo->momx = -MAXMOVE; } if (mo->momy > MAXMOVE) { mo->momy = MAXMOVE; } else if (mo->momy < -MAXMOVE) { mo->momy = -MAXMOVE; } xmove = mo->momx; ymove = mo->momy; do { if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2) { ptryx = mo->x + xmove / 2; ptryy = mo->y + ymove / 2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } if (!P_TryMove(mo, ptryx, ptryy)) { // Blocked move if (mo->flags2 & MF2_SLIDE) { // Try to slide along it P_SlideMove(mo); } else if (mo->flags & MF_MISSILE) { // Explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) { // Hack to prevent missiles exploding against the sky if (mo->type == MT_BLOODYSKULL) { mo->momx = mo->momy = 0; mo->momz = -FRACUNIT; } else { P_RemoveMobj(mo); } return; } P_ExplodeMissile(mo); } //else if(mo->info->crashstate) //{ // mo->momx = mo->momy = 0; // P_SetMobjState(mo, mo->info->crashstate); // return; //} else { mo->momx = mo->momy = 0; } } } while (xmove || ymove); // Friction if (player && player->cheats & CF_NOMOMENTUM) { // Debug option for no sliding at all mo->momx = mo->momy = 0; return; } if (mo->flags & (MF_MISSILE | MF_SKULLFLY)) { // No friction for missiles return; } if (mo->z > mo->floorz && !(mo->flags2 & MF2_FLY) && !(mo->flags2 & MF2_ONMOBJ)) { // No friction when falling return; } if (mo->flags & MF_CORPSE) { // Don't stop sliding if halfway off a step with some momentum if (mo->momx > FRACUNIT / 4 || mo->momx < -FRACUNIT / 4 || mo->momy > FRACUNIT / 4 || mo->momy < -FRACUNIT / 4) { if (mo->floorz != mo->subsector->sector->floorheight) { return; } } } if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && mo->momy > -STOPSPEED && mo->momy < STOPSPEED && (!player || (player->cmd.forwardmove == 0 && player->cmd.sidemove == 0))) { // If in a walking frame, stop moving if (player) { if (player->chickenTics) { if ((unsigned) ((player->mo->state - states) - S_CHICPLAY_RUN1) < 4) { P_SetMobjState(player->mo, S_CHICPLAY); } } else { if ((unsigned) ((player->mo->state - states) - S_PLAY_RUN1) < 4) { P_SetMobjState(player->mo, S_PLAY); } } } mo->momx = 0; mo->momy = 0; } else { if (mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz) && !(mo->flags2 & MF2_ONMOBJ)) { mo->momx = FixedMul(mo->momx, FRICTION_FLY); mo->momy = FixedMul(mo->momy, FRICTION_FLY); } else if (special == 15) // Friction_Low { mo->momx = FixedMul(mo->momx, FRICTION_LOW); mo->momy = FixedMul(mo->momy, FRICTION_LOW); } else { mo->momx = FixedMul(mo->momx, FRICTION_NORMAL); mo->momy = FixedMul(mo->momy, FRICTION_NORMAL); } } } /* =============== = = P_ZMovement = =============== */ void P_ZMovement(mobj_t * mo) { int dist; int delta; // // check for smooth step up // if (mo->player && mo->z < mo->floorz) { mo->player->viewheight -= mo->floorz - mo->z; mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight) >> 3; } // // adjust height // mo->z += mo->momz; if (mo->flags & MF_FLOAT && mo->target) { // float down towards target if too close if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT)) { dist = P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y); delta = (mo->target->z + (mo->height >> 1)) - mo->z; if (delta < 0 && dist < -(delta * 3)) mo->z -= FLOATSPEED; else if (delta > 0 && dist < (delta * 3)) mo->z += FLOATSPEED; } } if (mo->player && mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz) && leveltime & 2) { mo->z += finesine[(FINEANGLES / 20 * leveltime >> 2) & FINEMASK]; } // // clip movement // if (mo->z <= mo->floorz) { // Hit the floor if (mo->flags & MF_MISSILE) { mo->z = mo->floorz; if (mo->flags2 & MF2_FLOORBOUNCE) { P_FloorBounceMissile(mo); return; } else if (mo->type == MT_MNTRFX2) { // Minotaur floor fire can go up steps return; } else { P_ExplodeMissile(mo); return; } } if (mo->z - mo->momz > mo->floorz) { // Spawn splashes, etc. P_HitFloor(mo); } mo->z = mo->floorz; if (mo->momz < 0) { if (mo->player && mo->momz < -GRAVITY * 8 && !(mo->flags2 & MF2_FLY)) // squat down { mo->player->deltaviewheight = mo->momz >> 3; S_StartSound(mo, sfx_plroof); // haleyjd: removed externdriver crap mo->player->centering = true; } mo->momz = 0; } if (mo->flags & MF_SKULLFLY) { // The skull slammed into something mo->momz = -mo->momz; } if (mo->info->crashstate && (mo->flags & MF_CORPSE)) { P_SetMobjState(mo, mo->info->crashstate); return; } } else if (mo->flags2 & MF2_LOGRAV) { if (mo->momz == 0) mo->momz = -(GRAVITY >> 3) * 2; else mo->momz -= GRAVITY >> 3; } else if (!(mo->flags & MF_NOGRAVITY)) { if (mo->momz == 0) mo->momz = -GRAVITY * 2; else mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // hit the ceiling if (mo->momz > 0) mo->momz = 0; mo->z = mo->ceilingz - mo->height; if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } if (mo->flags & MF_MISSILE) { if (mo->subsector->sector->ceilingpic == skyflatnum) { if (mo->type == MT_BLOODYSKULL) { mo->momx = mo->momy = 0; mo->momz = -FRACUNIT; } else { P_RemoveMobj(mo); } return; } P_ExplodeMissile(mo); return; } } } /* ================ = = P_NightmareRespawn = ================ */ void P_NightmareRespawn(mobj_t * mobj) { fixed_t x, y, z; subsector_t *ss; mobj_t *mo; mapthing_t *mthing; x = mobj->spawnpoint.x << FRACBITS; y = mobj->spawnpoint.y << FRACBITS; if (!P_CheckPosition(mobj, x, y)) return; // somthing is occupying it's position // spawn a teleport fog at old spot mo = P_SpawnMobj(mobj->x, mobj->y, mobj->subsector->sector->floorheight + TELEFOGHEIGHT, MT_TFOG); 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 + TELEFOGHEIGHT, MT_TFOG); S_StartSound(mo, sfx_telept); // spawn the new monster mthing = &mobj->spawnpoint; // spawn it if (mobj->info->flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; mo = P_SpawnMobj(x, y, z, mobj->type); mo->spawnpoint = mobj->spawnpoint; mo->angle = ANG45 * (mthing->angle / 45); if (mthing->options & MTF_AMBUSH) mo->flags |= MF_AMBUSH; mo->reactiontime = 18; // remove the old monster P_RemoveMobj(mobj); } //---------------------------------------------------------------------------- // // PROC P_BlasterMobjThinker // // Thinker for the ultra-fast blaster PL2 ripper-spawning missile. // //---------------------------------------------------------------------------- void P_BlasterMobjThinker(mobj_t * mobj) { int i; fixed_t xfrac; fixed_t yfrac; fixed_t zfrac; fixed_t z; boolean changexy; // Handle movement if (mobj->momx || mobj->momy || (mobj->z != mobj->floorz) || mobj->momz) { xfrac = mobj->momx >> 3; yfrac = mobj->momy >> 3; zfrac = mobj->momz >> 3; changexy = xfrac || yfrac; for (i = 0; i < 8; i++) { if (changexy) { if (!P_TryMove(mobj, mobj->x + xfrac, mobj->y + yfrac)) { // Blocked move P_ExplodeMissile(mobj); return; } } mobj->z += zfrac; if (mobj->z <= mobj->floorz) { // Hit the floor mobj->z = mobj->floorz; P_HitFloor(mobj); P_ExplodeMissile(mobj); return; } if (mobj->z + mobj->height > mobj->ceilingz) { // Hit the ceiling mobj->z = mobj->ceilingz - mobj->height; P_ExplodeMissile(mobj); return; } if (changexy && (P_Random() < 64)) { z = mobj->z - 8 * FRACUNIT; if (z < mobj->floorz) { z = mobj->floorz; } P_SpawnMobj(mobj->x, mobj->y, z, MT_BLASTERSMOKE); } } } // Advance the state if (mobj->tics != -1) { mobj->tics--; while (!mobj->tics) { if (!P_SetMobjState(mobj, mobj->state->nextstate)) { // mobj was removed return; } } } } //---------------------------------------------------------------------------- // // PROC P_MobjThinker // //---------------------------------------------------------------------------- void P_MobjThinker(mobj_t * mobj) { mobj_t *onmo; // Handle X and Y momentums if (mobj->momx || mobj->momy || (mobj->flags & MF_SKULLFLY)) { P_XYMovement(mobj); if (mobj->thinker.function == (think_t) - 1) { // mobj was removed return; } } if (mobj->flags2 & MF2_FLOATBOB) { // Floating item bobbing motion mobj->z = mobj->floorz + FloatBobOffsets[(mobj->health++) & 63]; } else if ((mobj->z != mobj->floorz) || mobj->momz) { // Handle Z momentum and gravity if (mobj->flags2 & MF2_PASSMOBJ) { if (!(onmo = P_CheckOnmobj(mobj))) { P_ZMovement(mobj); } else { if (mobj->player && mobj->momz < 0) { mobj->flags2 |= MF2_ONMOBJ; mobj->momz = 0; } if (mobj->player && (onmo->player || onmo->type == MT_POD)) { mobj->momx = onmo->momx; mobj->momy = onmo->momy; if (onmo->z < onmo->floorz) { mobj->z += onmo->floorz - onmo->z; if (onmo->player) { onmo->player->viewheight -= onmo->floorz - onmo->z; onmo->player->deltaviewheight = (VIEWHEIGHT - onmo->player->viewheight) >> 3; } onmo->z = onmo->floorz; } } } } else { P_ZMovement(mobj); } if (mobj->thinker.function == (think_t) - 1) { // mobj was removed return; } } // // cycle through states, calling action functions at transitions // if (mobj->tics != -1) { mobj->tics--; // you can cycle through multiple states in a tic while (!mobj->tics) { if (!P_SetMobjState(mobj, mobj->state->nextstate)) { // mobj was removed return; } } } else { // Check for monster respawn if (!(mobj->flags & MF_COUNTKILL)) { return; } if (!respawnmonsters) { return; } mobj->movecount++; if (mobj->movecount < 12 * 35) { return; } if (leveltime & 31) { return; } if (P_Random() > 4) { return; } P_NightmareRespawn(mobj); } } /* =============== = = P_SpawnMobj = =============== */ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { mobj_t *mobj; state_t *st; mobjinfo_t *info; fixed_t space; mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, NULL); memset(mobj, 0, sizeof(*mobj)); info = &mobjinfo[type]; mobj->type = type; mobj->info = info; mobj->x = x; mobj->y = y; mobj->radius = info->radius; mobj->height = info->height; mobj->flags = info->flags; mobj->flags2 = info->flags2; mobj->damage = info->damage; mobj->health = info->spawnhealth; if (gameskill != sk_nightmare) { mobj->reactiontime = info->reactiontime; } mobj->lastlook = P_Random() % MAXPLAYERS; // Set the state, but do not use P_SetMobjState, because action // routines can't be called yet. If the spawnstate has an action // routine, it will not be called. st = &states[info->spawnstate]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // Set subsector and/or block links. P_SetThingPosition(mobj); mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; if (z == ONFLOORZ) { mobj->z = mobj->floorz; } else if (z == ONCEILINGZ) { mobj->z = mobj->ceilingz - mobj->info->height; } else if (z == FLOATRANDZ) { space = ((mobj->ceilingz) - (mobj->info->height)) - mobj->floorz; if (space > 48 * FRACUNIT) { space -= 40 * FRACUNIT; mobj->z = ((space * P_Random()) >> 8) + mobj->floorz + 40 * FRACUNIT; } else { mobj->z = mobj->floorz; } } else { mobj->z = z; } if (mobj->flags2 & MF2_FOOTCLIP && P_GetThingFloorType(mobj) != FLOOR_SOLID && mobj->floorz == mobj->subsector->sector->floorheight) { mobj->flags2 |= MF2_FEETARECLIPPED; } else { mobj->flags2 &= ~MF2_FEETARECLIPPED; } mobj->thinker.function = P_MobjThinker; P_AddThinker(&mobj->thinker); return (mobj); } /* =============== = = P_RemoveMobj = =============== */ void P_RemoveMobj(mobj_t * mobj) { // unlink from sector and block lists P_UnsetThingPosition(mobj); // stop any playing sound S_StopSound(mobj); // free block P_RemoveThinker((thinker_t *) mobj); } //============================================================================= /* ============ = = P_SpawnPlayer = = Called when a player is spawned on the level = Most of the player structure stays unchanged between levels ============ */ void P_SpawnPlayer(mapthing_t * mthing) { player_t *p; fixed_t x, y, z; mobj_t *mobj; int i; extern int playerkeys; if (!playeringame[mthing->type - 1]) return; // not playing p = &players[mthing->type - 1]; if (p->playerstate == PST_REBORN) G_PlayerReborn(mthing->type - 1); x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; z = ONFLOORZ; mobj = P_SpawnMobj(x, y, z, MT_PLAYER); if (mthing->type > 1) // set color translations for player sprites mobj->flags |= (mthing->type - 1) << MF_TRANSSHIFT; mobj->angle = ANG45 * (mthing->angle / 45); mobj->player = p; mobj->health = p->health; p->mo = mobj; p->playerstate = PST_LIVE; p->refire = 0; p->message = NULL; p->damagecount = 0; p->bonuscount = 0; p->chickenTics = 0; p->rain1 = NULL; p->rain2 = NULL; p->extralight = 0; p->fixedcolormap = 0; p->viewheight = VIEWHEIGHT; P_SetupPsprites(p); // setup gun psprite if (deathmatch) { // Give all keys in death match mode for (i = 0; i < NUMKEYS; i++) { p->keys[i] = true; if (p == &players[consoleplayer]) { playerkeys = 7; UpdateState |= I_STATBAR; } } } else if (p == &players[consoleplayer]) { playerkeys = 0; UpdateState |= I_STATBAR; } } //---------------------------------------------------------------------------- // // PROC P_SpawnMapThing // // The fields of the mapthing should already be in host byte order. // //---------------------------------------------------------------------------- void P_SpawnMapThing(mapthing_t * mthing) { int i; int bit; mobj_t *mobj; fixed_t x, y, z; // count deathmatch start positions if (mthing->type == 11) { if (deathmatch_p < &deathmatchstarts[10]) { memcpy(deathmatch_p, mthing, sizeof(*mthing)); deathmatch_p++; } return; } // check for players specially if (mthing->type <= 4) { // save spots for respawning in network games playerstarts[mthing->type - 1] = *mthing; playerstartsingame[mthing->type - 1] = true; if (!deathmatch) { P_SpawnPlayer(mthing); } return; } // Ambient sound sequences if (mthing->type >= 1200 && mthing->type < 1300) { P_AddAmbientSfx(mthing->type - 1200); return; } // Check for boss spots if (mthing->type == 56) // Monster_BossSpot { P_AddBossSpot(mthing->x << FRACBITS, mthing->y << FRACBITS, ANG45 * (mthing->angle / 45)); return; } // check for apropriate skill level if (!netgame && (mthing->options & 16)) return; if (gameskill == sk_baby) bit = 1; else if (gameskill == sk_nightmare) bit = 4; else bit = 1 << (gameskill - 1); if (!(mthing->options & bit)) return; // find which type to spawn for (i = 0; i < NUMMOBJTYPES; i++) if (mthing->type == mobjinfo[i].doomednum) break; if (i == NUMMOBJTYPES) I_Error("P_SpawnMapThing: Unknown type %i at (%i, %i)", mthing->type, mthing->x, mthing->y); // don't spawn keys and players in deathmatch if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) return; // don't spawn any monsters if -nomonsters if (nomonsters && (mobjinfo[i].flags & MF_COUNTKILL)) return; // spawn it switch (i) { // Special stuff case MT_WSKULLROD: case MT_WPHOENIXROD: case MT_AMSKRDWIMPY: case MT_AMSKRDHEFTY: case MT_AMPHRDWIMPY: case MT_AMPHRDHEFTY: case MT_AMMACEWIMPY: case MT_AMMACEHEFTY: case MT_ARTISUPERHEAL: case MT_ARTITELEPORT: case MT_ITEMSHIELD2: if (gamemode == shareware) { // Don't place on map in shareware version return; } break; case MT_WMACE: if (gamemode != shareware) { // Put in the mace spot list P_AddMaceSpot(mthing); return; } return; default: break; } x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; if (mobjinfo[i].flags & MF_SPAWNCEILING) { z = ONCEILINGZ; } else if (mobjinfo[i].flags2 & MF2_SPAWNFLOAT) { z = FLOATRANDZ; } else { z = ONFLOORZ; } mobj = P_SpawnMobj(x, y, z, i); if (mobj->flags2 & MF2_FLOATBOB) { // Seed random starting index for bobbing motion mobj->health = P_Random(); } if (mobj->tics > 0) { mobj->tics = 1 + (P_Random() % mobj->tics); } if (mobj->flags & MF_COUNTKILL) { totalkills++; mobj->spawnpoint = *mthing; } if (mobj->flags & MF_COUNTITEM) { totalitems++; } mobj->angle = ANG45 * (mthing->angle / 45); if (mthing->options & MTF_AMBUSH) { mobj->flags |= MF_AMBUSH; } } /* =============================================================================== GAME SPAWN FUNCTIONS =============================================================================== */ //--------------------------------------------------------------------------- // // PROC P_SpawnPuff // //--------------------------------------------------------------------------- extern fixed_t attackrange; void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z) { mobj_t *puff; z += (P_SubRandom() << 10); puff = P_SpawnMobj(x, y, z, PuffType); if (puff->info->attacksound) { S_StartSound(puff, puff->info->attacksound); } switch (PuffType) { case MT_BEAKPUFF: case MT_STAFFPUFF: puff->momz = FRACUNIT; break; case MT_GAUNTLETPUFF1: case MT_GAUNTLETPUFF2: puff->momz = (fixed_t)(.8 * FRACUNIT); default: break; } } /* ================ = = P_SpawnBlood = ================ */ /* void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage) { mobj_t *th; z += (P_SubRandom()<<10); th = P_SpawnMobj (x,y,z, MT_BLOOD); th->momz = FRACUNIT*2; th->tics -= P_Random()&3; if (damage <= 12 && damage >= 9) P_SetMobjState (th,S_BLOOD2); else if (damage < 9) P_SetMobjState (th,S_BLOOD3); } */ //--------------------------------------------------------------------------- // // PROC P_BloodSplatter // //--------------------------------------------------------------------------- void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator) { mobj_t *mo; mo = P_SpawnMobj(x, y, z, MT_BLOODSPLATTER); mo->target = originator; mo->momx = P_SubRandom() << 9; mo->momy = P_SubRandom() << 9; mo->momz = FRACUNIT * 2; } //--------------------------------------------------------------------------- // // PROC P_RipperBlood // //--------------------------------------------------------------------------- void P_RipperBlood(mobj_t * mo) { mobj_t *th; fixed_t x, y, z; x = mo->x + (P_SubRandom() << 12); y = mo->y + (P_SubRandom() << 12); z = mo->z + (P_SubRandom() << 12); th = P_SpawnMobj(x, y, z, MT_BLOOD); th->flags |= MF_NOGRAVITY; th->momx = mo->momx >> 1; th->momy = mo->momy >> 1; th->tics += P_Random() & 3; } //--------------------------------------------------------------------------- // // FUNC P_GetThingFloorType // //--------------------------------------------------------------------------- int P_GetThingFloorType(mobj_t * thing) { return (TerrainTypes[thing->subsector->sector->floorpic]); /* if(thing->subsector->sector->floorpic == W_GetNumForName("FLTWAWA1")-firstflat) { return(FLOOR_WATER); } else { return(FLOOR_SOLID); } */ } //--------------------------------------------------------------------------- // // FUNC P_HitFloor // //--------------------------------------------------------------------------- int P_HitFloor(mobj_t * thing) { mobj_t *mo; if (thing->floorz != thing->subsector->sector->floorheight) { // don't splash if landing on the edge above water/lava/etc.... return (FLOOR_SOLID); } switch (P_GetThingFloorType(thing)) { case FLOOR_WATER: P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE); mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASH); mo->target = thing; mo->momx = P_SubRandom() << 8; mo->momy = P_SubRandom() << 8; mo->momz = 2 * FRACUNIT + (P_Random() << 8); S_StartSound(mo, sfx_gloop); return (FLOOR_WATER); case FLOOR_LAVA: P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH); mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASMOKE); mo->momz = FRACUNIT + (P_Random() << 7); S_StartSound(mo, sfx_burn); return (FLOOR_LAVA); case FLOOR_SLUDGE: P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGESPLASH); mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGECHUNK); mo->target = thing; mo->momx = P_SubRandom() << 8; mo->momy = P_SubRandom() << 8; mo->momz = FRACUNIT + (P_Random() << 8); return (FLOOR_SLUDGE); } return (FLOOR_SOLID); } //--------------------------------------------------------------------------- // // FUNC P_CheckMissileSpawn // // Returns true if the missile is at a valid spawn point, otherwise // explodes it and returns false. // //--------------------------------------------------------------------------- boolean P_CheckMissileSpawn(mobj_t * missile) { //missile->tics -= P_Random()&3; // move a little forward so an angle can be computed if it // immediately explodes missile->x += (missile->momx >> 1); missile->y += (missile->momy >> 1); missile->z += (missile->momz >> 1); if (!P_TryMove(missile, missile->x, missile->y)) { P_ExplodeMissile(missile); return (false); } return (true); } //--------------------------------------------------------------------------- // // FUNC P_SpawnMissile // // Returns NULL if the missile exploded immediately, otherwise returns // a mobj_t pointer to the missile. // //--------------------------------------------------------------------------- mobj_t *P_SpawnMissile(mobj_t * source, mobj_t * dest, mobjtype_t type) { fixed_t z; mobj_t *th; angle_t an; int dist; switch (type) { case MT_MNTRFX1: // Minotaur swing attack missile z = source->z + 40 * FRACUNIT; break; case MT_MNTRFX2: // Minotaur floor fire missile z = ONFLOORZ; break; case MT_SRCRFX1: // Sorcerer Demon fireball z = source->z + 48 * FRACUNIT; break; case MT_KNIGHTAXE: // Knight normal axe case MT_REDAXE: // Knight red power axe z = source->z + 36 * FRACUNIT; break; default: z = source->z + 32 * FRACUNIT; break; } if (source->flags2 & MF2_FEETARECLIPPED) { z -= FOOTCLIPSIZE; } th = P_SpawnMobj(source->x, source->y, z, type); if (th->info->seesound) { S_StartSound(th, th->info->seesound); } th->target = source; // Originator an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); if (dest->flags & MF_SHADOW) { // Invisible target an += P_SubRandom() << 21; } 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; return (P_CheckMissileSpawn(th) ? th : NULL); } //--------------------------------------------------------------------------- // // FUNC P_SpawnMissileAngle // // Returns NULL if the missile exploded immediately, otherwise returns // a mobj_t pointer to the missile. // //--------------------------------------------------------------------------- mobj_t *P_SpawnMissileAngle(mobj_t * source, mobjtype_t type, angle_t angle, fixed_t momz) { fixed_t z; mobj_t *mo; switch (type) { case MT_MNTRFX1: // Minotaur swing attack missile z = source->z + 40 * FRACUNIT; break; case MT_MNTRFX2: // Minotaur floor fire missile z = ONFLOORZ; break; case MT_SRCRFX1: // Sorcerer Demon fireball z = source->z + 48 * FRACUNIT; break; default: z = source->z + 32 * FRACUNIT; break; } if (source->flags2 & MF2_FEETARECLIPPED) { z -= FOOTCLIPSIZE; } mo = P_SpawnMobj(source->x, source->y, z, type); if (mo->info->seesound) { S_StartSound(mo, mo->info->seesound); } mo->target = source; // Originator mo->angle = angle; angle >>= ANGLETOFINESHIFT; mo->momx = FixedMul(mo->info->speed, finecosine[angle]); mo->momy = FixedMul(mo->info->speed, finesine[angle]); mo->momz = momz; return (P_CheckMissileSpawn(mo) ? mo : NULL); } /* ================ = = P_SpawnPlayerMissile = = Tries to aim at a nearby monster ================ */ mobj_t *P_SpawnPlayerMissile(mobj_t * source, mobjtype_t type) { angle_t an; fixed_t x, y, z, slope; // Try to find a target an = source->angle; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an += 1 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an -= 2 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); } if (!linetarget) { an = source->angle; slope = ((source->player->lookdir) << FRACBITS) / 173; } } x = source->x; y = source->y; z = source->z + 4 * 8 * FRACUNIT + ((source->player->lookdir) << FRACBITS) / 173; if (source->flags2 & MF2_FEETARECLIPPED) { z -= FOOTCLIPSIZE; } MissileMobj = P_SpawnMobj(x, y, z, type); if (MissileMobj->info->seesound) { S_StartSound(MissileMobj, MissileMobj->info->seesound); } MissileMobj->target = source; MissileMobj->angle = an; MissileMobj->momx = FixedMul(MissileMobj->info->speed, finecosine[an >> ANGLETOFINESHIFT]); MissileMobj->momy = FixedMul(MissileMobj->info->speed, finesine[an >> ANGLETOFINESHIFT]); MissileMobj->momz = FixedMul(MissileMobj->info->speed, slope); if (MissileMobj->type == MT_BLASTERFX1) { // Ultra-fast ripper spawning missile MissileMobj->x += (MissileMobj->momx >> 3); MissileMobj->y += (MissileMobj->momy >> 3); MissileMobj->z += (MissileMobj->momz >> 3); } else { // Normal missile MissileMobj->x += (MissileMobj->momx >> 1); MissileMobj->y += (MissileMobj->momy >> 1); MissileMobj->z += (MissileMobj->momz >> 1); } if (!P_TryMove(MissileMobj, MissileMobj->x, MissileMobj->y)) { // Exploded immediately P_ExplodeMissile(MissileMobj); return (NULL); } return (MissileMobj); } //--------------------------------------------------------------------------- // // PROC P_SPMAngle // //--------------------------------------------------------------------------- mobj_t *P_SPMAngle(mobj_t * source, mobjtype_t type, angle_t angle) { mobj_t *th; angle_t an; fixed_t x, y, z, slope; // // see which target is to be aimed at // an = angle; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an += 1 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an -= 2 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); } if (!linetarget) { an = angle; slope = ((source->player->lookdir) << FRACBITS) / 173; } } x = source->x; y = source->y; z = source->z + 4 * 8 * FRACUNIT + ((source->player->lookdir) << FRACBITS) / 173; if (source->flags2 & MF2_FEETARECLIPPED) { z -= FOOTCLIPSIZE; } th = P_SpawnMobj(x, y, z, type); if (th->info->seesound) { S_StartSound(th, th->info->seesound); } th->target = source; th->angle = an; th->momx = FixedMul(th->info->speed, finecosine[an >> ANGLETOFINESHIFT]); th->momy = FixedMul(th->info->speed, finesine[an >> ANGLETOFINESHIFT]); th->momz = FixedMul(th->info->speed, slope); return (P_CheckMissileSpawn(th) ? th : NULL); } //--------------------------------------------------------------------------- // // PROC A_ContMobjSound // //--------------------------------------------------------------------------- void A_ContMobjSound(mobj_t * actor) { switch (actor->type) { case MT_KNIGHTAXE: S_StartSound(actor, sfx_kgtatk); break; case MT_MUMMYFX1: S_StartSound(actor, sfx_mumhed); break; default: break; } } crispy-doom-crispy-doom-5.6.4/src/heretic/p_plats.c000066400000000000000000000173471360717211000222700ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_plats.c #include "doomdef.h" #include "i_system.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" plat_t *activeplats[MAXPLATS]; //================================================================== // // Move a plat up and down // //================================================================== void T_PlatRaise(plat_t * plat) { result_e res; switch (plat->status) { case up: res = T_MovePlane(plat->sector, plat->speed, plat->high, plat->crush, 0, 1); if (!(leveltime & 31)) { S_StartSound(&plat->sector->soundorg, sfx_stnmov); } if (plat->type == raiseAndChange || plat->type == raiseToNearestAndChange) { if (!(leveltime & 7)) { S_StartSound(&plat->sector->soundorg, sfx_stnmov); } } if (res == crushed && (!plat->crush)) { plat->count = plat->wait; plat->status = down; S_StartSound(&plat->sector->soundorg, sfx_pstart); } else if (res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg, sfx_pstop); switch (plat->type) { case downWaitUpStay: P_RemoveActivePlat(plat); break; case raiseAndChange: P_RemoveActivePlat(plat); break; default: break; } } break; case down: res = T_MovePlane(plat->sector, plat->speed, plat->low, false, 0, -1); if (res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg, sfx_pstop); } else { if (!(leveltime & 31)) { S_StartSound(&plat->sector->soundorg, sfx_stnmov); } } break; case waiting: if (!--plat->count) { if (plat->sector->floorheight == plat->low) plat->status = up; else plat->status = down; S_StartSound(&plat->sector->soundorg, sfx_pstart); } case in_stasis: break; } } //================================================================== // // Do Platforms // "amount" is only used for SOME platforms. // //================================================================== int EV_DoPlat(line_t * line, plattype_e type, int amount) { plat_t *plat; int secnum; int rtn; sector_t *sec; secnum = -1; rtn = 0; // // Activate all plats that are in_stasis // switch (type) { case perpetualRaise: P_ActivateInStasis(line->tag); break; default: break; } while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // // Find lowest & highest floors around sector // rtn = 1; plat = Z_Malloc(sizeof(*plat), PU_LEVSPEC, 0); P_AddThinker(&plat->thinker); plat->type = type; plat->sector = sec; plat->sector->specialdata = plat; plat->thinker.function = T_PlatRaise; plat->crush = false; plat->tag = line->tag; switch (type) { case raiseToNearestAndChange: plat->speed = PLATSPEED / 2; sec->floorpic = sides[line->sidenum[0]].sector->floorpic; plat->high = P_FindNextHighestFloor(sec, sec->floorheight); plat->wait = 0; plat->status = up; sec->special = 0; // NO MORE DAMAGE, IF APPLICABLE S_StartSound(&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(&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(&sec->soundorg, sfx_pstart); break; case perpetualRaise: plat->speed = PLATSPEED; plat->low = P_FindLowestFloorSurrounding(sec); if (plat->low > sec->floorheight) plat->low = sec->floorheight; plat->high = P_FindHighestFloorSurrounding(sec); if (plat->high < sec->floorheight) plat->high = sec->floorheight; plat->wait = 35 * PLATWAIT; plat->status = P_Random() & 1; S_StartSound(&sec->soundorg, sfx_pstart); break; } P_AddActivePlat(plat); } return rtn; } void P_ActivateInStasis(int tag) { int i; for (i = 0; i < MAXPLATS; i++) if (activeplats[i] && (activeplats[i])->tag == tag && (activeplats[i])->status == in_stasis) { (activeplats[i])->status = (activeplats[i])->oldstatus; (activeplats[i])->thinker.function = T_PlatRaise; } } void EV_StopPlat(line_t * line) { int j; for (j = 0; j < MAXPLATS; j++) if (activeplats[j] && ((activeplats[j])->status != in_stasis) && ((activeplats[j])->tag == line->tag)) { (activeplats[j])->oldstatus = (activeplats[j])->status; (activeplats[j])->status = in_stasis; (activeplats[j])->thinker.function = NULL; } } void P_AddActivePlat(plat_t * plat) { int i; for (i = 0; i < MAXPLATS; i++) if (activeplats[i] == NULL) { activeplats[i] = plat; return; } I_Error("P_AddActivePlat: no more plats!"); } void P_RemoveActivePlat(plat_t * plat) { int i; for (i = 0; i < MAXPLATS; i++) if (plat == activeplats[i]) { (activeplats[i])->sector->specialdata = NULL; P_RemoveThinker(&(activeplats[i])->thinker); activeplats[i] = NULL; return; } I_Error("P_RemoveActivePlat: can't find plat!"); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_pspr.c000066400000000000000000001544771360717211000221370ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_pspr.c #include "doomdef.h" #include "i_system.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" // Macros #define LOWERSPEED FRACUNIT*6 #define RAISESPEED FRACUNIT*6 #define WEAPONBOTTOM 128*FRACUNIT #define WEAPONTOP 32*FRACUNIT #define FLAME_THROWER_TICS 10*35 #define MAGIC_JUNK 1234 #define MAX_MACE_SPOTS 8 static int MaceSpotCount; static struct { fixed_t x; fixed_t y; } MaceSpots[MAX_MACE_SPOTS]; fixed_t bulletslope; static int WeaponAmmoUsePL1[NUMWEAPONS] = { 0, // staff USE_GWND_AMMO_1, // gold wand USE_CBOW_AMMO_1, // crossbow USE_BLSR_AMMO_1, // blaster USE_SKRD_AMMO_1, // skull rod USE_PHRD_AMMO_1, // phoenix rod USE_MACE_AMMO_1, // mace 0, // gauntlets 0 // beak }; static int WeaponAmmoUsePL2[NUMWEAPONS] = { 0, // staff USE_GWND_AMMO_2, // gold wand USE_CBOW_AMMO_2, // crossbow USE_BLSR_AMMO_2, // blaster USE_SKRD_AMMO_2, // skull rod USE_PHRD_AMMO_2, // phoenix rod USE_MACE_AMMO_2, // mace 0, // gauntlets 0 // beak }; weaponinfo_t wpnlev1info[NUMWEAPONS] = { { // Staff am_noammo, // ammo S_STAFFUP, // upstate S_STAFFDOWN, // downstate S_STAFFREADY, // readystate S_STAFFATK1_1, // atkstate S_STAFFATK1_1, // holdatkstate S_NULL // flashstate }, { // Gold wand am_goldwand, // ammo S_GOLDWANDUP, // upstate S_GOLDWANDDOWN, // downstate S_GOLDWANDREADY, // readystate S_GOLDWANDATK1_1, // atkstate S_GOLDWANDATK1_1, // holdatkstate S_NULL // flashstate }, { // Crossbow am_crossbow, // ammo S_CRBOWUP, // upstate S_CRBOWDOWN, // downstate S_CRBOW1, // readystate S_CRBOWATK1_1, // atkstate S_CRBOWATK1_1, // holdatkstate S_NULL // flashstate }, { // Blaster am_blaster, // ammo S_BLASTERUP, // upstate S_BLASTERDOWN, // downstate S_BLASTERREADY, // readystate S_BLASTERATK1_1, // atkstate S_BLASTERATK1_3, // holdatkstate S_NULL // flashstate }, { // Skull rod am_skullrod, // ammo S_HORNRODUP, // upstate S_HORNRODDOWN, // downstate S_HORNRODREADY, // readystae S_HORNRODATK1_1, // atkstate S_HORNRODATK1_1, // holdatkstate S_NULL // flashstate }, { // Phoenix rod am_phoenixrod, // ammo S_PHOENIXUP, // upstate S_PHOENIXDOWN, // downstate S_PHOENIXREADY, // readystate S_PHOENIXATK1_1, // atkstate S_PHOENIXATK1_1, // holdatkstate S_NULL // flashstate }, { // Mace am_mace, // ammo S_MACEUP, // upstate S_MACEDOWN, // downstate S_MACEREADY, // readystate S_MACEATK1_1, // atkstate S_MACEATK1_2, // holdatkstate S_NULL // flashstate }, { // Gauntlets am_noammo, // ammo S_GAUNTLETUP, // upstate S_GAUNTLETDOWN, // downstate S_GAUNTLETREADY, // readystate S_GAUNTLETATK1_1, // atkstate S_GAUNTLETATK1_3, // holdatkstate S_NULL // flashstate }, { // Beak am_noammo, // ammo S_BEAKUP, // upstate S_BEAKDOWN, // downstate S_BEAKREADY, // readystate S_BEAKATK1_1, // atkstate S_BEAKATK1_1, // holdatkstate S_NULL // flashstate } }; weaponinfo_t wpnlev2info[NUMWEAPONS] = { { // Staff am_noammo, // ammo S_STAFFUP2, // upstate S_STAFFDOWN2, // downstate S_STAFFREADY2_1, // readystate S_STAFFATK2_1, // atkstate S_STAFFATK2_1, // holdatkstate S_NULL // flashstate }, { // Gold wand am_goldwand, // ammo S_GOLDWANDUP, // upstate S_GOLDWANDDOWN, // downstate S_GOLDWANDREADY, // readystate S_GOLDWANDATK2_1, // atkstate S_GOLDWANDATK2_1, // holdatkstate S_NULL // flashstate }, { // Crossbow am_crossbow, // ammo S_CRBOWUP, // upstate S_CRBOWDOWN, // downstate S_CRBOW1, // readystate S_CRBOWATK2_1, // atkstate S_CRBOWATK2_1, // holdatkstate S_NULL // flashstate }, { // Blaster am_blaster, // ammo S_BLASTERUP, // upstate S_BLASTERDOWN, // downstate S_BLASTERREADY, // readystate S_BLASTERATK2_1, // atkstate S_BLASTERATK2_3, // holdatkstate S_NULL // flashstate }, { // Skull rod am_skullrod, // ammo S_HORNRODUP, // upstate S_HORNRODDOWN, // downstate S_HORNRODREADY, // readystae S_HORNRODATK2_1, // atkstate S_HORNRODATK2_1, // holdatkstate S_NULL // flashstate }, { // Phoenix rod am_phoenixrod, // ammo S_PHOENIXUP, // upstate S_PHOENIXDOWN, // downstate S_PHOENIXREADY, // readystate S_PHOENIXATK2_1, // atkstate S_PHOENIXATK2_2, // holdatkstate S_NULL // flashstate }, { // Mace am_mace, // ammo S_MACEUP, // upstate S_MACEDOWN, // downstate S_MACEREADY, // readystate S_MACEATK2_1, // atkstate S_MACEATK2_1, // holdatkstate S_NULL // flashstate }, { // Gauntlets am_noammo, // ammo S_GAUNTLETUP2, // upstate S_GAUNTLETDOWN2, // downstate S_GAUNTLETREADY2_1, // readystate S_GAUNTLETATK2_1, // atkstate S_GAUNTLETATK2_3, // holdatkstate S_NULL // flashstate }, { // Beak am_noammo, // ammo S_BEAKUP, // upstate S_BEAKDOWN, // downstate S_BEAKREADY, // readystate S_BEAKATK2_1, // atkstate S_BEAKATK2_1, // holdatkstate S_NULL // flashstate } }; //--------------------------------------------------------------------------- // // PROC P_OpenWeapons // // Called at level load before things are loaded. // //--------------------------------------------------------------------------- void P_OpenWeapons(void) { MaceSpotCount = 0; } //--------------------------------------------------------------------------- // // PROC P_AddMaceSpot // //--------------------------------------------------------------------------- void P_AddMaceSpot(mapthing_t * mthing) { if (MaceSpotCount == MAX_MACE_SPOTS) { I_Error("Too many mace spots."); } MaceSpots[MaceSpotCount].x = mthing->x << FRACBITS; MaceSpots[MaceSpotCount].y = mthing->y << FRACBITS; MaceSpotCount++; } //--------------------------------------------------------------------------- // // PROC P_RepositionMace // // Chooses the next spot to place the mace. // //--------------------------------------------------------------------------- void P_RepositionMace(mobj_t * mo) { int spot; subsector_t *ss; P_UnsetThingPosition(mo); spot = P_Random() % MaceSpotCount; mo->x = MaceSpots[spot].x; mo->y = MaceSpots[spot].y; ss = R_PointInSubsector(mo->x, mo->y); mo->z = mo->floorz = ss->sector->floorheight; mo->ceilingz = ss->sector->ceilingheight; P_SetThingPosition(mo); } //--------------------------------------------------------------------------- // // PROC P_CloseWeapons // // Called at level load after things are loaded. // //--------------------------------------------------------------------------- void P_CloseWeapons(void) { int spot; if (!MaceSpotCount) { // No maces placed return; } if (!deathmatch && P_Random() < 64) { // Sometimes doesn't show up if not in deathmatch return; } spot = P_Random() % MaceSpotCount; P_SpawnMobj(MaceSpots[spot].x, MaceSpots[spot].y, ONFLOORZ, MT_WMACE); } //--------------------------------------------------------------------------- // // PROC P_SetPsprite // //--------------------------------------------------------------------------- void P_SetPsprite(player_t * player, int position, statenum_t stnum) { pspdef_t *psp; state_t *state; psp = &player->psprites[position]; do { if (!stnum) { // Object removed itself. psp->state = NULL; break; } state = &states[stnum]; psp->state = state; psp->tics = state->tics; // could be 0 if (state->misc1) { // Set coordinates. psp->sx = state->misc1 << FRACBITS; psp->sy = state->misc2 << FRACBITS; } if (state->action) { // Call action routine. state->action(player, psp); if (!psp->state) { break; } } stnum = psp->state->nextstate; } while (!psp->tics); // An initial state of 0 could cycle through. } /* ================= = = P_CalcSwing = ================= */ /* fixed_t swingx, swingy; void P_CalcSwing (player_t *player) { fixed_t swing; int angle; // OPTIMIZE: tablify this swing = player->bob; angle = (FINEANGLES/70*leveltime)&FINEMASK; swingx = FixedMul ( swing, finesine[angle]); angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK; swingy = -FixedMul ( swingx, finesine[angle]); } */ //--------------------------------------------------------------------------- // // PROC P_ActivateBeak // //--------------------------------------------------------------------------- void P_ActivateBeak(player_t * player) { player->pendingweapon = wp_nochange; player->readyweapon = wp_beak; player->psprites[ps_weapon].sy = WEAPONTOP; P_SetPsprite(player, ps_weapon, S_BEAKREADY); } //--------------------------------------------------------------------------- // // PROC P_PostChickenWeapon // //--------------------------------------------------------------------------- void P_PostChickenWeapon(player_t * player, weapontype_t weapon) { if (weapon == wp_beak) { // Should never happen weapon = wp_staff; } player->pendingweapon = wp_nochange; player->readyweapon = weapon; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite(player, ps_weapon, wpnlev1info[weapon].upstate); } //--------------------------------------------------------------------------- // // PROC P_BringUpWeapon // // Starts bringing the pending weapon up from the bottom of the screen. // //--------------------------------------------------------------------------- void P_BringUpWeapon(player_t * player) { statenum_t new; if (player->pendingweapon == wp_nochange) { player->pendingweapon = player->readyweapon; } if (player->pendingweapon == wp_gauntlets) { S_StartSound(player->mo, sfx_gntact); } if (player->powers[pw_weaponlevel2]) { new = wpnlev2info[player->pendingweapon].upstate; } else { new = wpnlev1info[player->pendingweapon].upstate; } player->pendingweapon = wp_nochange; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite(player, ps_weapon, new); } //--------------------------------------------------------------------------- // // FUNC P_CheckAmmo // // Returns true if there is enough ammo to shoot. If not, selects the // next weapon to use. // //--------------------------------------------------------------------------- boolean P_CheckAmmo(player_t * player) { ammotype_t ammo; int *ammoUse; int count; ammo = wpnlev1info[player->readyweapon].ammo; if (player->powers[pw_weaponlevel2] && !deathmatch) { ammoUse = WeaponAmmoUsePL2; } else { ammoUse = WeaponAmmoUsePL1; } count = ammoUse[player->readyweapon]; if (ammo == am_noammo || player->ammo[ammo] >= count) { return (true); } // out of ammo, pick a weapon to change to do { if (player->weaponowned[wp_skullrod] && player->ammo[am_skullrod] > ammoUse[wp_skullrod]) { player->pendingweapon = wp_skullrod; } else if (player->weaponowned[wp_blaster] && player->ammo[am_blaster] > ammoUse[wp_blaster]) { player->pendingweapon = wp_blaster; } else if (player->weaponowned[wp_crossbow] && player->ammo[am_crossbow] > ammoUse[wp_crossbow]) { player->pendingweapon = wp_crossbow; } else if (player->weaponowned[wp_mace] && player->ammo[am_mace] > ammoUse[wp_mace]) { player->pendingweapon = wp_mace; } else if (player->ammo[am_goldwand] > ammoUse[wp_goldwand]) { player->pendingweapon = wp_goldwand; } else if (player->weaponowned[wp_gauntlets]) { player->pendingweapon = wp_gauntlets; } else if (player->weaponowned[wp_phoenixrod] && player->ammo[am_phoenixrod] > ammoUse[wp_phoenixrod]) { player->pendingweapon = wp_phoenixrod; } else { player->pendingweapon = wp_staff; } } while (player->pendingweapon == wp_nochange); if (player->powers[pw_weaponlevel2]) { P_SetPsprite(player, ps_weapon, wpnlev2info[player->readyweapon].downstate); } else { P_SetPsprite(player, ps_weapon, wpnlev1info[player->readyweapon].downstate); } return (false); } //--------------------------------------------------------------------------- // // PROC P_FireWeapon // //--------------------------------------------------------------------------- void P_FireWeapon(player_t * player) { weaponinfo_t *wpinfo; statenum_t attackState; if (!P_CheckAmmo(player)) { return; } P_SetMobjState(player->mo, S_PLAY_ATK2); wpinfo = player->powers[pw_weaponlevel2] ? &wpnlev2info[0] : &wpnlev1info[0]; attackState = player->refire ? wpinfo[player->readyweapon].holdatkstate : wpinfo[player->readyweapon].atkstate; P_SetPsprite(player, ps_weapon, attackState); P_NoiseAlert(player->mo, player->mo); if (player->readyweapon == wp_gauntlets && !player->refire) { // Play the sound for the initial gauntlet attack S_StartSound(player->mo, sfx_gntuse); } } //--------------------------------------------------------------------------- // // PROC P_DropWeapon // // The player died, so put the weapon away. // //--------------------------------------------------------------------------- void P_DropWeapon(player_t * player) { if (player->powers[pw_weaponlevel2]) { P_SetPsprite(player, ps_weapon, wpnlev2info[player->readyweapon].downstate); } else { P_SetPsprite(player, ps_weapon, wpnlev1info[player->readyweapon].downstate); } } //--------------------------------------------------------------------------- // // PROC A_WeaponReady // // The player can fire the weapon or change to another weapon at this time. // //--------------------------------------------------------------------------- void A_WeaponReady(player_t * player, pspdef_t * psp) { int angle; if (player->chickenTics) { // Change to the chicken beak P_ActivateBeak(player); return; } // Change player from attack state if (player->mo->state == &states[S_PLAY_ATK1] || player->mo->state == &states[S_PLAY_ATK2]) { P_SetMobjState(player->mo, S_PLAY); } // Check for staff PL2 active sound if ((player->readyweapon == wp_staff) && (psp->state == &states[S_STAFFREADY2_1]) && P_Random() < 128) { S_StartSound(player->mo, sfx_stfcrk); } // Put the weapon away if the player has a pending weapon or has // died. if (player->pendingweapon != wp_nochange || !player->health) { if (player->powers[pw_weaponlevel2]) { P_SetPsprite(player, ps_weapon, wpnlev2info[player->readyweapon].downstate); } else { P_SetPsprite(player, ps_weapon, wpnlev1info[player->readyweapon].downstate); } return; } // Check for fire. The phoenix rod does not auto fire. if (player->cmd.buttons & BT_ATTACK) { if (!player->attackdown || (player->readyweapon != wp_phoenixrod)) { player->attackdown = true; P_FireWeapon(player); return; } } else { player->attackdown = false; } // Bob the weapon based on movement speed. angle = (128 * leveltime) & FINEMASK; psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); angle &= FINEANGLES / 2 - 1; psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); } //--------------------------------------------------------------------------- // // PROC P_UpdateBeak // //--------------------------------------------------------------------------- void P_UpdateBeak(player_t * player, pspdef_t * psp) { psp->sy = WEAPONTOP + (player->chickenPeck << (FRACBITS - 1)); } //--------------------------------------------------------------------------- // // PROC A_BeakReady // //--------------------------------------------------------------------------- void A_BeakReady(player_t * player, pspdef_t * psp) { if (player->cmd.buttons & BT_ATTACK) { // Chicken beak attack player->attackdown = true; P_SetMobjState(player->mo, S_CHICPLAY_ATK1); if (player->powers[pw_weaponlevel2]) { P_SetPsprite(player, ps_weapon, S_BEAKATK2_1); } else { P_SetPsprite(player, ps_weapon, S_BEAKATK1_1); } P_NoiseAlert(player->mo, player->mo); } else { if (player->mo->state == &states[S_CHICPLAY_ATK1]) { // Take out of attack state P_SetMobjState(player->mo, S_CHICPLAY); } player->attackdown = false; } } //--------------------------------------------------------------------------- // // PROC A_ReFire // // The player can re fire the weapon without lowering it entirely. // //--------------------------------------------------------------------------- void A_ReFire(player_t * player, pspdef_t * psp) { if ((player->cmd.buttons & BT_ATTACK) && player->pendingweapon == wp_nochange && player->health) { player->refire++; P_FireWeapon(player); } else { player->refire = 0; P_CheckAmmo(player); } } //--------------------------------------------------------------------------- // // PROC A_Lower // //--------------------------------------------------------------------------- void A_Lower(player_t * player, pspdef_t * psp) { if (player->chickenTics) { psp->sy = WEAPONBOTTOM; } else { psp->sy += LOWERSPEED; } if (psp->sy < WEAPONBOTTOM) { // Not lowered all the way yet return; } if (player->playerstate == PST_DEAD) { // Player is dead, so don't bring up a pending weapon psp->sy = WEAPONBOTTOM; return; } if (!player->health) { // Player is dead, so keep the weapon off screen P_SetPsprite(player, ps_weapon, S_NULL); return; } player->readyweapon = player->pendingweapon; P_BringUpWeapon(player); } //--------------------------------------------------------------------------- // // PROC A_BeakRaise // //--------------------------------------------------------------------------- void A_BeakRaise(player_t * player, pspdef_t * psp) { psp->sy = WEAPONTOP; P_SetPsprite(player, ps_weapon, wpnlev1info[player->readyweapon].readystate); } //--------------------------------------------------------------------------- // // PROC A_Raise // //--------------------------------------------------------------------------- void A_Raise(player_t * player, pspdef_t * psp) { psp->sy -= RAISESPEED; if (psp->sy > WEAPONTOP) { // Not raised all the way yet return; } psp->sy = WEAPONTOP; if (player->powers[pw_weaponlevel2]) { P_SetPsprite(player, ps_weapon, wpnlev2info[player->readyweapon].readystate); } else { P_SetPsprite(player, ps_weapon, wpnlev1info[player->readyweapon].readystate); } } /* =============== = = P_BulletSlope = = Sets a slope so a near miss is at aproximately the height of the = intended target = =============== */ void P_BulletSlope(mobj_t * mo) { angle_t an; // // see which target is to be aimed at // an = mo->angle; bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); if (!linetarget) { an += 1 << 26; bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); if (!linetarget) { an -= 2 << 26; bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT); } if (!linetarget) { an += 2 << 26; bulletslope = (mo->player->lookdir << FRACBITS) / 173; } } } //**************************************************************************** // // WEAPON ATTACKS // //**************************************************************************** //---------------------------------------------------------------------------- // // PROC A_BeakAttackPL1 // //---------------------------------------------------------------------------- void A_BeakAttackPL1(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; damage = 1 + (P_Random() & 3); angle = player->mo->angle; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); PuffType = MT_BEAKPUFF; P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); if (linetarget) { player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, linetarget->x, linetarget->y); } S_StartSound(player->mo, sfx_chicpk1 + (P_Random() % 3)); player->chickenPeck = 12; psp->tics -= P_Random() & 7; } //---------------------------------------------------------------------------- // // PROC A_BeakAttackPL2 // //---------------------------------------------------------------------------- void A_BeakAttackPL2(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; damage = HITDICE(4); angle = player->mo->angle; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); PuffType = MT_BEAKPUFF; P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); if (linetarget) { player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, linetarget->x, linetarget->y); } S_StartSound(player->mo, sfx_chicpk1 + (P_Random() % 3)); player->chickenPeck = 12; psp->tics -= P_Random() & 3; } //---------------------------------------------------------------------------- // // PROC A_StaffAttackPL1 // //---------------------------------------------------------------------------- void A_StaffAttackPL1(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; damage = 5 + (P_Random() & 15); angle = player->mo->angle; angle += P_SubRandom() << 18; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); PuffType = MT_STAFFPUFF; P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); if (linetarget) { //S_StartSound(player->mo, sfx_stfhit); // turn to face target player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, linetarget->x, linetarget->y); } } //---------------------------------------------------------------------------- // // PROC A_StaffAttackPL2 // //---------------------------------------------------------------------------- void A_StaffAttackPL2(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; // P_inter.c:P_DamageMobj() handles target momentums damage = 18 + (P_Random() & 63); angle = player->mo->angle; angle += P_SubRandom() << 18; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); PuffType = MT_STAFFPUFF2; P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); if (linetarget) { //S_StartSound(player->mo, sfx_stfpow); // turn to face target player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, linetarget->x, linetarget->y); } } //---------------------------------------------------------------------------- // // PROC A_FireBlasterPL1 // //---------------------------------------------------------------------------- void A_FireBlasterPL1(player_t * player, pspdef_t * psp) { mobj_t *mo; angle_t angle; int damage; mo = player->mo; S_StartSound(mo, sfx_gldhit); player->ammo[am_blaster] -= USE_BLSR_AMMO_1; P_BulletSlope(mo); damage = HITDICE(4); angle = mo->angle; if (player->refire) { angle += P_SubRandom() << 18; } PuffType = MT_BLASTERPUFF1; P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); S_StartSound(player->mo, sfx_blssht); } //---------------------------------------------------------------------------- // // PROC A_FireBlasterPL2 // //---------------------------------------------------------------------------- void A_FireBlasterPL2(player_t * player, pspdef_t * psp) { mobj_t *mo; player->ammo[am_blaster] -= deathmatch ? USE_BLSR_AMMO_1 : USE_BLSR_AMMO_2; mo = P_SpawnPlayerMissile(player->mo, MT_BLASTERFX1); if (mo) { mo->thinker.function = P_BlasterMobjThinker; } S_StartSound(player->mo, sfx_blssht); } //---------------------------------------------------------------------------- // // PROC A_FireGoldWandPL1 // //---------------------------------------------------------------------------- void A_FireGoldWandPL1(player_t * player, pspdef_t * psp) { mobj_t *mo; angle_t angle; int damage; mo = player->mo; player->ammo[am_goldwand] -= USE_GWND_AMMO_1; P_BulletSlope(mo); damage = 7 + (P_Random() & 7); angle = mo->angle; if (player->refire) { angle += P_SubRandom() << 18; } PuffType = MT_GOLDWANDPUFF1; P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); S_StartSound(player->mo, sfx_gldhit); } //---------------------------------------------------------------------------- // // PROC A_FireGoldWandPL2 // //---------------------------------------------------------------------------- void A_FireGoldWandPL2(player_t * player, pspdef_t * psp) { int i; mobj_t *mo; angle_t angle; int damage; fixed_t momz; mo = player->mo; player->ammo[am_goldwand] -= deathmatch ? USE_GWND_AMMO_1 : USE_GWND_AMMO_2; PuffType = MT_GOLDWANDPUFF2; P_BulletSlope(mo); momz = FixedMul(mobjinfo[MT_GOLDWANDFX2].speed, bulletslope); P_SpawnMissileAngle(mo, MT_GOLDWANDFX2, mo->angle - (ANG45 / 8), momz); P_SpawnMissileAngle(mo, MT_GOLDWANDFX2, mo->angle + (ANG45 / 8), momz); angle = mo->angle - (ANG45 / 8); for (i = 0; i < 5; i++) { damage = 1 + (P_Random() & 7); P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); angle += ((ANG45 / 8) * 2) / 4; } S_StartSound(player->mo, sfx_gldhit); } //---------------------------------------------------------------------------- // // PROC A_FireMacePL1B // //---------------------------------------------------------------------------- void A_FireMacePL1B(player_t * player, pspdef_t * psp) { mobj_t *pmo; mobj_t *ball; angle_t angle; if (player->ammo[am_mace] < USE_MACE_AMMO_1) { return; } player->ammo[am_mace] -= USE_MACE_AMMO_1; pmo = player->mo; // Vanilla bug here: // Original code here looks like: // (pmo->flags2 & MF2_FEETARECLIPPED != 0) // C's operator precedence interprets this as: // (pmo->flags2 & (MF2_FEETARECLIPPED != 0)) // Which simplifies to: // (pmo->flags2 & 1) ball = P_SpawnMobj(pmo->x, pmo->y, pmo->z + 28 * FRACUNIT - FOOTCLIPSIZE * (pmo->flags2 & 1), MT_MACEFX2); ball->momz = 2 * FRACUNIT + ((player->lookdir) << (FRACBITS - 5)); angle = pmo->angle; ball->target = pmo; ball->angle = angle; ball->z += (player->lookdir) << (FRACBITS - 4); angle >>= ANGLETOFINESHIFT; ball->momx = (pmo->momx >> 1) + FixedMul(ball->info->speed, finecosine[angle]); ball->momy = (pmo->momy >> 1) + FixedMul(ball->info->speed, finesine[angle]); S_StartSound(ball, sfx_lobsht); P_CheckMissileSpawn(ball); } //---------------------------------------------------------------------------- // // PROC A_FireMacePL1 // //---------------------------------------------------------------------------- void A_FireMacePL1(player_t * player, pspdef_t * psp) { mobj_t *ball; if (P_Random() < 28) { A_FireMacePL1B(player, psp); return; } if (player->ammo[am_mace] < USE_MACE_AMMO_1) { return; } player->ammo[am_mace] -= USE_MACE_AMMO_1; psp->sx = ((P_Random() & 3) - 2) * FRACUNIT; psp->sy = WEAPONTOP + (P_Random() & 3) * FRACUNIT; ball = P_SPMAngle(player->mo, MT_MACEFX1, player->mo->angle + (((P_Random() & 7) - 4) << 24)); if (ball) { ball->special1.i = 16; // tics till dropoff } } //---------------------------------------------------------------------------- // // PROC A_MacePL1Check // //---------------------------------------------------------------------------- void A_MacePL1Check(mobj_t * ball) { angle_t angle; if (ball->special1.i == 0) { return; } ball->special1.i -= 4; if (ball->special1.i > 0) { return; } ball->special1.i = 0; ball->flags2 |= MF2_LOGRAV; angle = ball->angle >> ANGLETOFINESHIFT; ball->momx = FixedMul(7 * FRACUNIT, finecosine[angle]); ball->momy = FixedMul(7 * FRACUNIT, finesine[angle]); ball->momz -= ball->momz >> 1; } //---------------------------------------------------------------------------- // // PROC A_MaceBallImpact // //---------------------------------------------------------------------------- void A_MaceBallImpact(mobj_t * ball) { if ((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID)) { // Landed in some sort of liquid P_RemoveMobj(ball); return; } if ((ball->health != MAGIC_JUNK) && (ball->z <= ball->floorz) && ball->momz) { // Bounce ball->health = MAGIC_JUNK; ball->momz = (ball->momz * 192) >> 8; ball->flags2 &= ~MF2_FLOORBOUNCE; P_SetMobjState(ball, ball->info->spawnstate); S_StartSound(ball, sfx_bounce); } else { // Explode ball->flags |= MF_NOGRAVITY; ball->flags2 &= ~MF2_LOGRAV; S_StartSound(ball, sfx_lobhit); } } //---------------------------------------------------------------------------- // // PROC A_MaceBallImpact2 // //---------------------------------------------------------------------------- void A_MaceBallImpact2(mobj_t * ball) { mobj_t *tiny; angle_t angle; if ((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID)) { // Landed in some sort of liquid P_RemoveMobj(ball); return; } if ((ball->z != ball->floorz) || (ball->momz < 2 * FRACUNIT)) { // Explode ball->momx = ball->momy = ball->momz = 0; ball->flags |= MF_NOGRAVITY; ball->flags2 &= ~(MF2_LOGRAV | MF2_FLOORBOUNCE); } else { // Bounce ball->momz = (ball->momz * 192) >> 8; P_SetMobjState(ball, ball->info->spawnstate); tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_MACEFX3); angle = ball->angle + ANG90; tiny->target = ball->target; tiny->angle = angle; angle >>= ANGLETOFINESHIFT; tiny->momx = (ball->momx >> 1) + FixedMul(ball->momz - FRACUNIT, finecosine[angle]); tiny->momy = (ball->momy >> 1) + FixedMul(ball->momz - FRACUNIT, finesine[angle]); tiny->momz = ball->momz; P_CheckMissileSpawn(tiny); tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_MACEFX3); angle = ball->angle - ANG90; tiny->target = ball->target; tiny->angle = angle; angle >>= ANGLETOFINESHIFT; tiny->momx = (ball->momx >> 1) + FixedMul(ball->momz - FRACUNIT, finecosine[angle]); tiny->momy = (ball->momy >> 1) + FixedMul(ball->momz - FRACUNIT, finesine[angle]); tiny->momz = ball->momz; P_CheckMissileSpawn(tiny); } } //---------------------------------------------------------------------------- // // PROC A_FireMacePL2 // //---------------------------------------------------------------------------- void A_FireMacePL2(player_t * player, pspdef_t * psp) { mobj_t *mo; player->ammo[am_mace] -= deathmatch ? USE_MACE_AMMO_1 : USE_MACE_AMMO_2; mo = P_SpawnPlayerMissile(player->mo, MT_MACEFX4); if (mo) { mo->momx += player->mo->momx; mo->momy += player->mo->momy; mo->momz = 2 * FRACUNIT + ((player->lookdir) << (FRACBITS - 5)); if (linetarget) { mo->special1.m = linetarget; } } S_StartSound(player->mo, sfx_lobsht); } //---------------------------------------------------------------------------- // // PROC A_DeathBallImpact // //---------------------------------------------------------------------------- void A_DeathBallImpact(mobj_t * ball) { int i; mobj_t *target; angle_t angle; boolean newAngle; if ((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID)) { // Landed in some sort of liquid P_RemoveMobj(ball); return; } if ((ball->z <= ball->floorz) && ball->momz) { // Bounce newAngle = false; target = (mobj_t *) ball->special1.m; if (target) { if (!(target->flags & MF_SHOOTABLE)) { // Target died ball->special1.m = NULL; } else { // Seek angle = R_PointToAngle2(ball->x, ball->y, target->x, target->y); newAngle = true; } } else { // Find new target angle = 0; for (i = 0; i < 16; i++) { P_AimLineAttack(ball, angle, 10 * 64 * FRACUNIT); if (linetarget && ball->target != linetarget) { ball->special1.m = linetarget; angle = R_PointToAngle2(ball->x, ball->y, linetarget->x, linetarget->y); newAngle = true; break; } angle += ANG45 / 2; } } if (newAngle) { ball->angle = angle; angle >>= ANGLETOFINESHIFT; ball->momx = FixedMul(ball->info->speed, finecosine[angle]); ball->momy = FixedMul(ball->info->speed, finesine[angle]); } P_SetMobjState(ball, ball->info->spawnstate); S_StartSound(ball, sfx_pstop); } else { // Explode ball->flags |= MF_NOGRAVITY; ball->flags2 &= ~MF2_LOGRAV; S_StartSound(ball, sfx_phohit); } } //---------------------------------------------------------------------------- // // PROC A_SpawnRippers // //---------------------------------------------------------------------------- void A_SpawnRippers(mobj_t * actor) { unsigned int i; angle_t angle; mobj_t *ripper; for (i = 0; i < 8; i++) { ripper = P_SpawnMobj(actor->x, actor->y, actor->z, MT_RIPPER); angle = i * ANG45; ripper->target = actor->target; ripper->angle = angle; angle >>= ANGLETOFINESHIFT; ripper->momx = FixedMul(ripper->info->speed, finecosine[angle]); ripper->momy = FixedMul(ripper->info->speed, finesine[angle]); P_CheckMissileSpawn(ripper); } } //---------------------------------------------------------------------------- // // PROC A_FireCrossbowPL1 // //---------------------------------------------------------------------------- void A_FireCrossbowPL1(player_t * player, pspdef_t * psp) { mobj_t *pmo; pmo = player->mo; player->ammo[am_crossbow] -= USE_CBOW_AMMO_1; P_SpawnPlayerMissile(pmo, MT_CRBOWFX1); P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle - (ANG45 / 10)); P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle + (ANG45 / 10)); } //---------------------------------------------------------------------------- // // PROC A_FireCrossbowPL2 // //---------------------------------------------------------------------------- void A_FireCrossbowPL2(player_t * player, pspdef_t * psp) { mobj_t *pmo; pmo = player->mo; player->ammo[am_crossbow] -= deathmatch ? USE_CBOW_AMMO_1 : USE_CBOW_AMMO_2; P_SpawnPlayerMissile(pmo, MT_CRBOWFX2); P_SPMAngle(pmo, MT_CRBOWFX2, pmo->angle - (ANG45 / 10)); P_SPMAngle(pmo, MT_CRBOWFX2, pmo->angle + (ANG45 / 10)); P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle - (ANG45 / 5)); P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle + (ANG45 / 5)); } //---------------------------------------------------------------------------- // // PROC A_BoltSpark // //---------------------------------------------------------------------------- void A_BoltSpark(mobj_t * bolt) { mobj_t *spark; if (P_Random() > 50) { spark = P_SpawnMobj(bolt->x, bolt->y, bolt->z, MT_CRBOWFX4); spark->x += P_SubRandom() << 10; spark->y += P_SubRandom() << 10; } } //---------------------------------------------------------------------------- // // PROC A_FireSkullRodPL1 // //---------------------------------------------------------------------------- void A_FireSkullRodPL1(player_t * player, pspdef_t * psp) { mobj_t *mo; if (player->ammo[am_skullrod] < USE_SKRD_AMMO_1) { return; } player->ammo[am_skullrod] -= USE_SKRD_AMMO_1; mo = P_SpawnPlayerMissile(player->mo, MT_HORNRODFX1); // Randomize the first frame if (mo && P_Random() > 128) { P_SetMobjState(mo, S_HRODFX1_2); } } //---------------------------------------------------------------------------- // // PROC A_FireSkullRodPL2 // // The special2 field holds the player number that shot the rain missile. // The special1 field is used for the seeking routines, then as a counter // for the sound looping. // //---------------------------------------------------------------------------- void A_FireSkullRodPL2(player_t * player, pspdef_t * psp) { player->ammo[am_skullrod] -= deathmatch ? USE_SKRD_AMMO_1 : USE_SKRD_AMMO_2; P_SpawnPlayerMissile(player->mo, MT_HORNRODFX2); // Use MissileMobj instead of the return value from // P_SpawnPlayerMissile because we need to give info to the mobj // even if it exploded immediately. if (netgame) { // Multi-player game MissileMobj->special2.i = P_GetPlayerNum(player); } else { // Always use red missiles in single player games MissileMobj->special2.i = 2; } if (linetarget) { MissileMobj->special1.m = linetarget; } S_StartSound(MissileMobj, sfx_hrnpow); } //---------------------------------------------------------------------------- // // PROC A_SkullRodPL2Seek // //---------------------------------------------------------------------------- void A_SkullRodPL2Seek(mobj_t * actor) { P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 30); } //---------------------------------------------------------------------------- // // PROC A_AddPlayerRain // //---------------------------------------------------------------------------- void A_AddPlayerRain(mobj_t * actor) { int playerNum; player_t *player; playerNum = netgame ? actor->special2.i : 0; if (!playeringame[playerNum]) { // Player left the game return; } player = &players[playerNum]; if (player->health <= 0) { // Player is dead return; } if (player->rain1 && player->rain2) { // Terminate an active rain if (player->rain1->health < player->rain2->health) { if (player->rain1->health > 16) { player->rain1->health = 16; } player->rain1 = NULL; } else { if (player->rain2->health > 16) { player->rain2->health = 16; } player->rain2 = NULL; } } // Add rain mobj to list if (player->rain1) { player->rain2 = actor; } else { player->rain1 = actor; } } //---------------------------------------------------------------------------- // // PROC A_SkullRodStorm // //---------------------------------------------------------------------------- void A_SkullRodStorm(mobj_t * actor) { fixed_t x; fixed_t y; mobj_t *mo; int playerNum; player_t *player; if (actor->health-- == 0) { P_SetMobjState(actor, S_NULL); playerNum = netgame ? actor->special2.i : 0; if (!playeringame[playerNum]) { // Player left the game return; } player = &players[playerNum]; if (player->health <= 0) { // Player is dead return; } if (player->rain1 == actor) { player->rain1 = NULL; } else if (player->rain2 == actor) { player->rain2 = NULL; } return; } if (P_Random() < 25) { // Fudge rain frequency return; } x = actor->x + ((P_Random() & 127) - 64) * FRACUNIT; y = actor->y + ((P_Random() & 127) - 64) * FRACUNIT; mo = P_SpawnMobj(x, y, ONCEILINGZ, MT_RAINPLR1 + actor->special2.i); mo->target = actor->target; mo->momx = 1; // Force collision detection mo->momz = -mo->info->speed; mo->special2.i = actor->special2.i; // Transfer player number P_CheckMissileSpawn(mo); if (!(actor->special1.i & 31)) { S_StartSound(actor, sfx_ramrain); } actor->special1.i++; } //---------------------------------------------------------------------------- // // PROC A_RainImpact // //---------------------------------------------------------------------------- void A_RainImpact(mobj_t * actor) { if (actor->z > actor->floorz) { P_SetMobjState(actor, S_RAINAIRXPLR1_1 + actor->special2.i); } else if (P_Random() < 40) { P_HitFloor(actor); } } //---------------------------------------------------------------------------- // // PROC A_HideInCeiling // //---------------------------------------------------------------------------- void A_HideInCeiling(mobj_t * actor) { actor->z = actor->ceilingz + 4 * FRACUNIT; } //---------------------------------------------------------------------------- // // PROC A_FirePhoenixPL1 // //---------------------------------------------------------------------------- void A_FirePhoenixPL1(player_t * player, pspdef_t * psp) { angle_t angle; player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_1; P_SpawnPlayerMissile(player->mo, MT_PHOENIXFX1); //P_SpawnPlayerMissile(player->mo, MT_MNTRFX2); angle = player->mo->angle + ANG180; angle >>= ANGLETOFINESHIFT; player->mo->momx += FixedMul(4 * FRACUNIT, finecosine[angle]); player->mo->momy += FixedMul(4 * FRACUNIT, finesine[angle]); } //---------------------------------------------------------------------------- // // PROC A_PhoenixPuff // //---------------------------------------------------------------------------- void A_PhoenixPuff(mobj_t * actor) { mobj_t *puff; angle_t angle; P_SeekerMissile(actor, ANG1_X * 5, ANG1_X * 10); puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF); angle = actor->angle + ANG90; angle >>= ANGLETOFINESHIFT; puff->momx = FixedMul((fixed_t)(FRACUNIT * 1.3), finecosine[angle]); puff->momy = FixedMul((fixed_t)(FRACUNIT * 1.3), finesine[angle]); puff->momz = 0; puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF); angle = actor->angle - ANG90; angle >>= ANGLETOFINESHIFT; puff->momx = FixedMul((fixed_t)(FRACUNIT * 1.3), finecosine[angle]); puff->momy = FixedMul((fixed_t)(FRACUNIT * 1.3), finesine[angle]); puff->momz = 0; } // // This function was present in the Heretic 1.0 executable for the // removed "secondary phoenix flash" object (MT_PHOENIXFX_REMOVED). // The purpose of this object is unknown, as is this function. // void A_RemovedPhoenixFunc(mobj_t *actor) { I_Error("Action function invoked for removed Phoenix action!"); } //---------------------------------------------------------------------------- // // PROC A_InitPhoenixPL2 // //---------------------------------------------------------------------------- void A_InitPhoenixPL2(player_t * player, pspdef_t * psp) { player->flamecount = FLAME_THROWER_TICS; } //---------------------------------------------------------------------------- // // PROC A_FirePhoenixPL2 // // Flame thrower effect. // //---------------------------------------------------------------------------- void A_FirePhoenixPL2(player_t * player, pspdef_t * psp) { mobj_t *mo; mobj_t *pmo; angle_t angle; fixed_t x, y, z; fixed_t slope; if (--player->flamecount == 0) { // Out of flame P_SetPsprite(player, ps_weapon, S_PHOENIXATK2_4); player->refire = 0; return; } pmo = player->mo; angle = pmo->angle; x = pmo->x + (P_SubRandom() << 9); y = pmo->y + (P_SubRandom() << 9); z = pmo->z + 26 * FRACUNIT + ((player->lookdir) << FRACBITS) / 173; if (pmo->flags2 & MF2_FEETARECLIPPED) { z -= FOOTCLIPSIZE; } slope = ((player->lookdir) << FRACBITS) / 173 + (FRACUNIT / 10); mo = P_SpawnMobj(x, y, z, MT_PHOENIXFX2); mo->target = pmo; mo->angle = angle; mo->momx = pmo->momx + FixedMul(mo->info->speed, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = pmo->momy + FixedMul(mo->info->speed, finesine[angle >> ANGLETOFINESHIFT]); mo->momz = FixedMul(mo->info->speed, slope); if (!player->refire || !(leveltime % 38)) { S_StartSound(player->mo, sfx_phopow); } P_CheckMissileSpawn(mo); } //---------------------------------------------------------------------------- // // PROC A_ShutdownPhoenixPL2 // //---------------------------------------------------------------------------- void A_ShutdownPhoenixPL2(player_t * player, pspdef_t * psp) { player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_2; } //---------------------------------------------------------------------------- // // PROC A_FlameEnd // //---------------------------------------------------------------------------- void A_FlameEnd(mobj_t * actor) { actor->momz += (fixed_t)(1.5 * FRACUNIT); } //---------------------------------------------------------------------------- // // PROC A_FloatPuff // //---------------------------------------------------------------------------- void A_FloatPuff(mobj_t * puff) { puff->momz += (fixed_t)(1.8 * FRACUNIT); } //--------------------------------------------------------------------------- // // PROC A_GauntletAttack // //--------------------------------------------------------------------------- void A_GauntletAttack(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; int randVal; fixed_t dist; psp->sx = ((P_Random() & 3) - 2) * FRACUNIT; psp->sy = WEAPONTOP + (P_Random() & 3) * FRACUNIT; angle = player->mo->angle; if (player->powers[pw_weaponlevel2]) { damage = HITDICE(2); dist = 4 * MELEERANGE; angle += P_SubRandom() << 17; PuffType = MT_GAUNTLETPUFF2; } else { damage = HITDICE(2); dist = MELEERANGE + 1; angle += P_SubRandom() << 18; PuffType = MT_GAUNTLETPUFF1; } slope = P_AimLineAttack(player->mo, angle, dist); P_LineAttack(player->mo, angle, dist, slope, damage); if (!linetarget) { if (P_Random() > 64) { player->extralight = !player->extralight; } S_StartSound(player->mo, sfx_gntful); return; } randVal = P_Random(); if (randVal < 64) { player->extralight = 0; } else if (randVal < 160) { player->extralight = 1; } else { player->extralight = 2; } if (player->powers[pw_weaponlevel2]) { P_GiveBody(player, damage >> 1); S_StartSound(player->mo, sfx_gntpow); } else { S_StartSound(player->mo, sfx_gnthit); } // 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; } void A_Light0(player_t * player, pspdef_t * psp) { player->extralight = 0; } void A_Light1(player_t * player, pspdef_t * psp) { player->extralight = 1; } void A_Light2(player_t * player, pspdef_t * psp) { player->extralight = 2; } //------------------------------------------------------------------------ // // PROC P_SetupPsprites // // Called at start of level for each player // //------------------------------------------------------------------------ void P_SetupPsprites(player_t * player) { int i; // Remove all psprites for (i = 0; i < NUMPSPRITES; i++) { player->psprites[i].state = NULL; } // Spawn the ready weapon player->pendingweapon = player->readyweapon; P_BringUpWeapon(player); } //------------------------------------------------------------------------ // // PROC P_MovePsprites // // Called every tic by player thinking routine // //------------------------------------------------------------------------ void P_MovePsprites(player_t * player) { int i; pspdef_t *psp; state_t *state; psp = &player->psprites[0]; for (i = 0; i < NUMPSPRITES; i++, psp++) { if ((state = psp->state) != 0) // a null state means not active { // drop tic count and possibly change state if (psp->tics != -1) // a -1 tic count never changes { psp->tics--; if (!psp->tics) { P_SetPsprite(player, i, psp->state->nextstate); } } } } player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_saveg.c000066400000000000000000001155641360717211000222520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_tick.c #include #include "doomdef.h" #include "i_swap.h" #include "i_system.h" #include "m_misc.h" #include "p_local.h" #include "v_video.h" static FILE *SaveGameFP; int vanilla_savegame_limit = 1; //========================================================================== // // SV_Filename // // Generate the filename to use for a particular savegame slot. // Returns a malloc()'d buffer that must be freed by the caller. // //========================================================================== char *SV_Filename(int slot) { char *filename; size_t filename_len; filename_len = strlen(savegamedir) + strlen(SAVEGAMENAME) + 8; filename = malloc(filename_len); M_snprintf(filename, filename_len, "%s" SAVEGAMENAME "%d.hsg", savegamedir, slot); return filename; } //========================================================================== // // SV_Open // //========================================================================== void SV_Open(char *fileName) { SaveGameFP = fopen(fileName, "wb"); } void SV_OpenRead(char *filename) { SaveGameFP = fopen(filename, "rb"); if (SaveGameFP == NULL) { I_Error("Could not load savegame %s", filename); } } //========================================================================== // // SV_Close // //========================================================================== void SV_Close(char *fileName) { SV_WriteByte(SAVE_GAME_TERMINATOR); // Enforce the same savegame size limit as in Vanilla Heretic if (vanilla_savegame_limit && ftell(SaveGameFP) > SAVEGAMESIZE) { I_Error("Savegame buffer overrun"); } fclose(SaveGameFP); } //========================================================================== // // SV_Write // //========================================================================== void SV_Write(void *buffer, int size) { fwrite(buffer, size, 1, SaveGameFP); } void SV_WriteByte(byte val) { SV_Write(&val, sizeof(byte)); } void SV_WriteWord(unsigned short val) { val = SHORT(val); SV_Write(&val, sizeof(unsigned short)); } void SV_WriteLong(unsigned int val) { val = LONG(val); SV_Write(&val, sizeof(int)); } void SV_WritePtr(const void *ptr) { long val = (long)(intptr_t) ptr; SV_WriteLong(val & 0xffffffff); } //========================================================================== // // SV_Read // //========================================================================== void SV_Read(void *buffer, int size) { int retval = fread(buffer, 1, size, SaveGameFP); if (retval != size) { I_Error("Incomplete read in SV_Read: Expected %d, got %d bytes", size, retval); } } byte SV_ReadByte(void) { byte result; SV_Read(&result, sizeof(byte)); return result; } uint16_t SV_ReadWord(void) { uint16_t result; SV_Read(&result, sizeof(unsigned short)); return SHORT(result); } uint32_t SV_ReadLong(void) { uint32_t result; SV_Read(&result, sizeof(int)); return LONG(result); } // // ticcmd_t // static void saveg_read_ticcmd_t(ticcmd_t *str) { // char forwardmove; str->forwardmove = SV_ReadByte(); // char sidemove; str->sidemove = SV_ReadByte(); // short angleturn; str->angleturn = SV_ReadWord(); // short consistancy; str->consistancy = SV_ReadWord(); // byte chatchar; str->chatchar = SV_ReadByte(); // byte buttons; str->buttons = SV_ReadByte(); // byte lookfly; str->lookfly = SV_ReadByte(); // byte arti; str->arti = SV_ReadByte(); } static void saveg_write_ticcmd_t(ticcmd_t *str) { // char forwardmove; SV_WriteByte(str->forwardmove); // char sidemove; SV_WriteByte(str->sidemove); // short angleturn; SV_WriteWord(str->angleturn); // short consistancy; SV_WriteWord(str->consistancy); // byte chatchar; SV_WriteByte(str->chatchar); // byte buttons; SV_WriteByte(str->buttons); // byte lookfly; SV_WriteByte(str->lookfly); // byte arti; SV_WriteByte(str->arti); } // // inventory_t // static void saveg_read_inventory_t(inventory_t *str) { // int type; str->type = SV_ReadLong(); // int count; str->count = SV_ReadLong(); } static void saveg_write_inventory_t(inventory_t *str) { // int type; SV_WriteLong(str->type); // int count; SV_WriteLong(str->count); } // // state_t * // static void saveg_read_state_ptr(state_t **state) { int statenum; statenum = SV_ReadLong(); // We have read a state number, but it is indexed according to the state // table in Vanilla Heretic v1.3. To support v1.0 HHE patches we have // three extra states, so map the state number to our internal state // number. if (statenum >= S_PHOENIXFXIX_1) { statenum = (statenum - S_PHOENIXFXIX_1) + S_PHOENIXPUFF1; } if (statenum == 0) { *state = NULL; } else { *state = &states[statenum]; } } static void saveg_write_state_ptr(state_t *state) { int statenum; // NULL states are just written as zero. if (state == NULL) { SV_WriteLong(0); return; } statenum = state - states; // Our internal state table has three extra states than Vanilla, so map // to the state numbers used by Vanilla Heretic v1.3 for savegame // compatibility. if (statenum >= S_PHOENIXPUFF1) { statenum = (statenum - S_PHOENIXPUFF1) + S_PHOENIXFXIX_1; } else if (statenum >= S_PHOENIXFXIX_1) { // Now we're really in trouble. This state doesn't exist in Vanilla // Heretic v1.3 (but does in v1.0). Map to a frame that might be // vaguely sensible. statenum = S_PHOENIXFXI1_8; } SV_WriteLong(statenum); } // // pspdef_t // static void saveg_read_pspdef_t(pspdef_t *str) { // state_t *state; saveg_read_state_ptr(&str->state); // int tics; str->tics = SV_ReadLong(); // fixed_t sx, sy; str->sx = SV_ReadLong(); str->sy = SV_ReadLong(); } static void saveg_write_pspdef_t(pspdef_t *str) { // state_t *state; saveg_write_state_ptr(str->state); // int tics; SV_WriteLong(str->tics); // fixed_t sx, sy; SV_WriteLong(str->sx); SV_WriteLong(str->sy); } // // player_t // static void saveg_read_player_t(player_t *str) { int i; // mobj_t *mo; SV_ReadLong(); str->mo = NULL; // playerstate_t playerstate; str->playerstate = SV_ReadLong(); // ticcmd_t cmd; saveg_read_ticcmd_t(&str->cmd); // fixed_t viewz; str->viewz = SV_ReadLong(); // fixed_t viewheight; str->viewheight = SV_ReadLong(); // fixed_t deltaviewheight; str->deltaviewheight = SV_ReadLong(); // fixed_t bob; str->bob = SV_ReadLong(); // int flyheight; str->flyheight = SV_ReadLong(); // int lookdir; str->lookdir = SV_ReadLong(); // boolean centering; str->centering = SV_ReadLong(); // int health; str->health = SV_ReadLong(); // int armorpoints, armortype; str->armorpoints = SV_ReadLong(); str->armortype = SV_ReadLong(); // inventory_t inventory[NUMINVENTORYSLOTS]; for (i=0; iinventory[i]); } // artitype_t readyArtifact; str->readyArtifact = SV_ReadLong(); // int artifactCount; str->artifactCount = SV_ReadLong(); // int inventorySlotNum; str->inventorySlotNum = SV_ReadLong(); // int powers[NUMPOWERS]; for (i=0; ipowers[i] = SV_ReadLong(); } // boolean keys[NUMKEYS]; for (i=0; ikeys[i] = SV_ReadLong(); } // boolean backpack; str->backpack = SV_ReadLong(); // signed int frags[MAXPLAYERS]; for (i=0; ifrags[i] = SV_ReadLong(); } // weapontype_t readyweapon; str->readyweapon = SV_ReadLong(); // weapontype_t pendingweapon; str->pendingweapon = SV_ReadLong(); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i] = SV_ReadLong(); } // int ammo[NUMAMMO]; for (i=0; iammo[i] = SV_ReadLong(); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i] = SV_ReadLong(); } // int attackdown, usedown; str->attackdown = SV_ReadLong(); str->usedown = SV_ReadLong(); // int cheats; str->cheats = SV_ReadLong(); // int refire; str->refire = SV_ReadLong(); // int killcount, itemcount, secretcount; str->killcount = SV_ReadLong(); str->itemcount = SV_ReadLong(); str->secretcount = SV_ReadLong(); // char *message; SV_ReadLong(); str->message = NULL; // int messageTics; str->messageTics = SV_ReadLong(); // int damagecount, bonuscount; str->damagecount = SV_ReadLong(); str->bonuscount = SV_ReadLong(); // int flamecount; str->flamecount = SV_ReadLong(); // mobj_t *attacker; SV_ReadLong(); str->attacker = NULL; // int extralight; str->extralight = SV_ReadLong(); // int fixedcolormap; str->fixedcolormap = SV_ReadLong(); // int colormap; str->colormap = SV_ReadLong(); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // boolean didsecret; str->didsecret = SV_ReadLong(); // int chickenTics; str->chickenTics = SV_ReadLong(); // int chickenPeck; str->chickenPeck = SV_ReadLong(); // mobj_t *rain1; SV_ReadLong(); str->rain1 = NULL; // mobj_t *rain2; SV_ReadLong(); str->rain2 = NULL; } static void saveg_write_player_t(player_t *str) { int i; // mobj_t *mo; // pointer will be trashed, but it gets restored on load as // the player number reference is stored in the mo. SV_WritePtr(str->mo); // playerstate_t playerstate; SV_WriteLong(str->playerstate); // ticcmd_t cmd; saveg_write_ticcmd_t(&str->cmd); // fixed_t viewz; SV_WriteLong(str->viewz); // fixed_t viewheight; SV_WriteLong(str->viewheight); // fixed_t deltaviewheight; SV_WriteLong(str->deltaviewheight); // fixed_t bob; SV_WriteLong(str->bob); // int flyheight; SV_WriteLong(str->flyheight); // int lookdir; SV_WriteLong(str->lookdir); // boolean centering; SV_WriteLong(str->centering); // int health; SV_WriteLong(str->health); // int armorpoints, armortype; SV_WriteLong(str->armorpoints); SV_WriteLong(str->armortype); // inventory_t inventory[NUMINVENTORYSLOTS]; for (i=0; iinventory[i]); } // artitype_t readyArtifact; SV_WriteLong(str->readyArtifact); // int artifactCount; SV_WriteLong(str->artifactCount); // int inventorySlotNum; SV_WriteLong(str->inventorySlotNum); // int powers[NUMPOWERS]; for (i=0; ipowers[i]); } // boolean keys[NUMKEYS]; for (i=0; ikeys[i]); } // boolean backpack; SV_WriteLong(str->backpack); // signed int frags[MAXPLAYERS]; for (i=0; ifrags[i]); } // weapontype_t readyweapon; SV_WriteLong(str->readyweapon); // weapontype_t pendingweapon; SV_WriteLong(str->pendingweapon); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i]); } // int ammo[NUMAMMO]; for (i=0; iammo[i]); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i]); } // int attackdown, usedown; SV_WriteLong(str->attackdown); SV_WriteLong(str->usedown); // int cheats; SV_WriteLong(str->cheats); // int refire; SV_WriteLong(str->refire); // int killcount, itemcount, secretcount; SV_WriteLong(str->killcount); SV_WriteLong(str->itemcount); SV_WriteLong(str->secretcount); // char *message; SV_WritePtr(str->message); // int messageTics; SV_WriteLong(str->messageTics); // int damagecount, bonuscount; SV_WriteLong(str->damagecount); SV_WriteLong(str->bonuscount); // int flamecount; SV_WriteLong(str->flamecount); // mobj_t *attacker; SV_WritePtr(str->attacker); // int extralight; SV_WriteLong(str->extralight); // int fixedcolormap; SV_WriteLong(str->fixedcolormap); // int colormap; SV_WriteLong(str->colormap); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // boolean didsecret; SV_WriteLong(str->didsecret); // int chickenTics; SV_WriteLong(str->chickenTics); // int chickenPeck; SV_WriteLong(str->chickenPeck); // mobj_t *rain1; SV_WritePtr(str->rain1); // mobj_t *rain2; SV_WritePtr(str->rain2); } // // mapthing_t // static void saveg_read_mapthing_t(mapthing_t *str) { // short x, y; str->x = SV_ReadWord(); str->y = SV_ReadWord(); // short angle; str->angle = SV_ReadWord(); // short type; str->type = SV_ReadWord(); // short options; str->options = SV_ReadWord(); } static void saveg_write_mapthing_t(mapthing_t *str) { // short x, y; SV_WriteWord(str->x); SV_WriteWord(str->y); // short angle; SV_WriteWord(str->angle); // short type; SV_WriteWord(str->type); // short options; SV_WriteWord(str->options); } // // thinker_t // static void saveg_read_thinker_t(thinker_t *str) { // struct thinker_s *prev, *next; SV_ReadLong(); str->prev = NULL; SV_ReadLong(); str->next = NULL; // think_t function; SV_ReadLong(); str->function = NULL; } static void saveg_write_thinker_t(thinker_t *str) { // struct thinker_s *prev, *next; SV_WritePtr(str->prev); SV_WritePtr(str->next); // think_t function; SV_WritePtr(str->function); } // // specialval_t // static void saveg_read_specialval_t(specialval_t *str) { // This can also be a mobj_t ptr, but we just assume it's // an int. This is probably a really bad assumption that's // likely to end in tears. // int i; str->i = SV_ReadLong(); } static void saveg_write_specialval_t(specialval_t *str) { // int i; SV_WriteLong(str->i); } // // mobj_t // static void saveg_read_mobj_t(mobj_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // fixed_t x, y, z; str->x = SV_ReadLong(); str->y = SV_ReadLong(); str->z = SV_ReadLong(); // struct mobj_s *snext, *sprev; SV_ReadLong(); str->snext = NULL; SV_ReadLong(); str->sprev = NULL; // angle_t angle; str->angle = SV_ReadLong(); // spritenum_t sprite; str->sprite = SV_ReadLong(); // int frame; str->frame = SV_ReadLong(); // struct mobj_s *bnext, *bprev; SV_ReadLong(); str->bnext = NULL; SV_ReadLong(); str->bprev = NULL; // struct subsector_s *subsector; SV_ReadLong(); str->subsector = NULL; // fixed_t floorz, ceilingz; str->floorz = SV_ReadLong(); str->ceilingz = SV_ReadLong(); // fixed_t radius, height; str->radius = SV_ReadLong(); str->height = SV_ReadLong(); // fixed_t momx, momy, momz; str->momx = SV_ReadLong(); str->momy = SV_ReadLong(); str->momz = SV_ReadLong(); // int validcount; str->validcount = SV_ReadLong(); // mobjtype_t type; str->type = SV_ReadLong(); // An extra thing type was added for v1.0 HHE compatibility. // Map from the v1.3 thing type index to the internal one. if (str->type >= MT_PHOENIXFX_REMOVED) { ++str->type; } // mobjinfo_t *info; SV_ReadLong(); str->info = NULL; // int tics; str->tics = SV_ReadLong(); // state_t *state; saveg_read_state_ptr(&str->state); // int damage; str->damage = SV_ReadLong(); // int flags; str->flags = SV_ReadLong(); // int flags2; str->flags2 = SV_ReadLong(); // specialval_t special1; saveg_read_specialval_t(&str->special1); // specialval_t special2; saveg_read_specialval_t(&str->special2); // Now we have a bunch of hacks to try to NULL out special values // where special[12] contained a mobj_t pointer that isn't valid // any more. This isn't in Vanilla but at least it stops the game // from crashing. switch (str->type) { // Gas pods use special2.m to point to the pod generator // that made it. case MT_POD: str->special2.m = NULL; break; // Several thing types use special1.m to mean 'target': case MT_MACEFX4: // A_DeathBallImpact case MT_WHIRLWIND: // A_WhirlwindSeek case MT_MUMMYFX1: // A_MummyFX1Seek case MT_HORNRODFX2: // A_SkullRodPL2Seek case MT_PHOENIXFX1: // A_PhoenixPuff str->special1.m = NULL; break; default: break; } // int health; str->health = SV_ReadLong(); // int movedir; str->movedir = SV_ReadLong(); // int movecount; str->movecount = SV_ReadLong(); // struct mobj_s *target; SV_ReadLong(); str->target = NULL; // int reactiontime; str->reactiontime = SV_ReadLong(); // int threshold; str->threshold = SV_ReadLong(); // struct player_s *player; i = SV_ReadLong(); if (i != 0) { str->player = &players[i - 1]; str->player->mo = str; } else { str->player = NULL; } // int lastlook; str->lastlook = SV_ReadLong(); // mapthing_t spawnpoint; saveg_read_mapthing_t(&str->spawnpoint); } static void saveg_write_mobj_t(mobj_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // fixed_t x, y, z; SV_WriteLong(str->x); SV_WriteLong(str->y); SV_WriteLong(str->z); // struct mobj_s *snext, *sprev; SV_WritePtr(str->snext); SV_WritePtr(str->sprev); // angle_t angle; SV_WriteLong(str->angle); // spritenum_t sprite; SV_WriteLong(str->sprite); // int frame; SV_WriteLong(str->frame); // struct mobj_s *bnext, *bprev; SV_WritePtr(str->bnext); SV_WritePtr(str->bprev); // struct subsector_s *subsector; SV_WritePtr(str->subsector); // fixed_t floorz, ceilingz; SV_WriteLong(str->floorz); SV_WriteLong(str->ceilingz); // fixed_t radius, height; SV_WriteLong(str->radius); SV_WriteLong(str->height); // fixed_t momx, momy, momz; SV_WriteLong(str->momx); SV_WriteLong(str->momy); SV_WriteLong(str->momz); // int validcount; SV_WriteLong(str->validcount); // mobjtype_t type; // Our mobjinfo table has an extra entry, for compatibility with v1.0 // HHE patches. So translate the internal thing type index to the // equivalent for Vanilla Heretic v1.3, for savegame compatibility. if (str->type > MT_PHOENIXFX_REMOVED) { SV_WriteLong(str->type - 1); } else if (str->type == MT_PHOENIXFX_REMOVED) { // This should never happen, but just in case, do something // vaguely sensible ... ? SV_WriteLong(MT_PHOENIXFX1); } else { SV_WriteLong(str->type); } // mobjinfo_t *info; SV_WritePtr(str->info); // int tics; SV_WriteLong(str->tics); // state_t *state; saveg_write_state_ptr(str->state); // int damage; SV_WriteLong(str->damage); // int flags; SV_WriteLong(str->flags); // int flags2; SV_WriteLong(str->flags2); // specialval_t special1; saveg_write_specialval_t(&str->special1); // specialval_t special2; saveg_write_specialval_t(&str->special2); // int health; SV_WriteLong(str->health); // int movedir; SV_WriteLong(str->movedir); // int movecount; SV_WriteLong(str->movecount); // struct mobj_s *target; SV_WritePtr(str->target); // int reactiontime; SV_WriteLong(str->reactiontime); // int threshold; SV_WriteLong(str->threshold); // struct player_s *player; if (str->player != NULL) { SV_WriteLong(str->player - players + 1); } else { SV_WriteLong(0); } // int lastlook; SV_WriteLong(str->lastlook); // mapthing_t spawnpoint; saveg_write_mapthing_t(&str->spawnpoint); } // // ceiling_t // static void saveg_read_ceiling_t(ceiling_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // ceiling_e type; str->type = SV_ReadLong(); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // fixed_t bottomheight, topheight; str->bottomheight = SV_ReadLong(); str->topheight = SV_ReadLong(); // fixed_t speed; str->speed = SV_ReadLong(); // boolean crush; str->crush = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); // int tag; str->tag = SV_ReadLong(); // int olddirection; str->olddirection = SV_ReadLong(); } static void saveg_write_ceiling_t(ceiling_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // ceiling_e type; SV_WriteLong(str->type); // sector_t *sector; SV_WriteLong(str->sector - sectors); // fixed_t bottomheight, topheight; SV_WriteLong(str->bottomheight); SV_WriteLong(str->topheight); // fixed_t speed; SV_WriteLong(str->speed); // boolean crush; SV_WriteLong(str->crush); // int direction; SV_WriteLong(str->direction); // int tag; SV_WriteLong(str->tag); // int olddirection; SV_WriteLong(str->olddirection); } // // vldoor_t // static void saveg_read_vldoor_t(vldoor_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // vldoor_e type; str->type = SV_ReadLong(); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // fixed_t topheight; str->topheight = SV_ReadLong(); // fixed_t speed; str->speed = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); // int topwait; str->topwait = SV_ReadLong(); // int topcountdown; str->topcountdown = SV_ReadLong(); } static void saveg_write_vldoor_t(vldoor_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // vldoor_e type; SV_WriteLong(str->type); // sector_t *sector; SV_WriteLong(str->sector - sectors); // fixed_t topheight; SV_WriteLong(str->topheight); // fixed_t speed; SV_WriteLong(str->speed); // int direction; SV_WriteLong(str->direction); // int topwait; SV_WriteLong(str->topwait); // int topcountdown; SV_WriteLong(str->topcountdown); } // // floormove_t // static void saveg_read_floormove_t(floormove_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // floor_e type; str->type = SV_ReadLong(); // boolean crush; str->crush = SV_ReadLong(); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // int direction; str->direction = SV_ReadLong(); // int newspecial; str->newspecial = SV_ReadLong(); // short texture; str->texture = SV_ReadWord(); // fixed_t floordestheight; str->floordestheight = SV_ReadLong(); // fixed_t speed; str->speed = SV_ReadLong(); } static void saveg_write_floormove_t(floormove_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // floor_e type; SV_WriteLong(str->type); // boolean crush; SV_WriteLong(str->crush); // sector_t *sector; SV_WriteLong(str->sector - sectors); // int direction; SV_WriteLong(str->direction); // int newspecial; SV_WriteLong(str->newspecial); // short texture; SV_WriteWord(str->texture); // fixed_t floordestheight; SV_WriteLong(str->floordestheight); // fixed_t speed; SV_WriteLong(str->speed); } // // plat_t // static void saveg_read_plat_t(plat_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // fixed_t speed; str->speed = SV_ReadLong(); // fixed_t low; str->low = SV_ReadLong(); // fixed_t high; str->high = SV_ReadLong(); // int wait; str->wait = SV_ReadLong(); // int count; str->count = SV_ReadLong(); // plat_e status; str->status = SV_ReadLong(); // plat_e oldstatus; str->oldstatus = SV_ReadLong(); // boolean crush; str->crush = SV_ReadLong(); // int tag; str->tag = SV_ReadLong(); // plattype_e type; str->type = SV_ReadLong(); } static void saveg_write_plat_t(plat_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // fixed_t speed; SV_WriteLong(str->speed); // fixed_t low; SV_WriteLong(str->low); // fixed_t high; SV_WriteLong(str->high); // int wait; SV_WriteLong(str->wait); // int count; SV_WriteLong(str->count); // plat_e status; SV_WriteLong(str->status); // plat_e oldstatus; SV_WriteLong(str->oldstatus); // boolean crush; SV_WriteLong(str->crush); // int tag; SV_WriteLong(str->tag); // plattype_e type; SV_WriteLong(str->type); } // // lightflash_t // static void saveg_read_lightflash_t(lightflash_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // int count; str->count = SV_ReadLong(); // int maxlight; str->maxlight = SV_ReadLong(); // int minlight; str->minlight = SV_ReadLong(); // int maxtime; str->maxtime = SV_ReadLong(); // int mintime; str->mintime = SV_ReadLong(); } static void saveg_write_lightflash_t(lightflash_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // int count; SV_WriteLong(str->count); // int maxlight; SV_WriteLong(str->maxlight); // int minlight; SV_WriteLong(str->minlight); // int maxtime; SV_WriteLong(str->maxtime); // int mintime; SV_WriteLong(str->mintime); } // // strobe_t // static void saveg_read_strobe_t(strobe_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // int count; str->count = SV_ReadLong(); // int minlight; str->minlight = SV_ReadLong(); // int maxlight; str->maxlight = SV_ReadLong(); // int darktime; str->darktime = SV_ReadLong(); // int brighttime; str->brighttime = SV_ReadLong(); } static void saveg_write_strobe_t(strobe_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // int count; SV_WriteLong(str->count); // int minlight; SV_WriteLong(str->minlight); // int maxlight; SV_WriteLong(str->maxlight); // int darktime; SV_WriteLong(str->darktime); // int brighttime; SV_WriteLong(str->brighttime); } // // glow_t // static void saveg_read_glow_t(glow_t *str) { int i; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // int minlight; str->minlight = SV_ReadLong(); // int maxlight; str->maxlight = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); } static void saveg_write_glow_t(glow_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // int minlight; SV_WriteLong(str->minlight); // int maxlight; SV_WriteLong(str->maxlight); // int direction; SV_WriteLong(str->direction); } /* ==================== = = P_ArchivePlayers = ==================== */ void P_ArchivePlayers(void) { int i; for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) { continue; } saveg_write_player_t(&players[i]); } } /* ==================== = = P_UnArchivePlayers = ==================== */ void P_UnArchivePlayers(void) { int i; for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) continue; saveg_read_player_t(&players[i]); players[i].mo = NULL; // will be set when unarc thinker players[i].message = NULL; players[i].attacker = NULL; } } //============================================================================= /* ==================== = = P_ArchiveWorld = ==================== */ void P_ArchiveWorld(void) { int i, j; sector_t *sec; line_t *li; side_t *si; // Sectors for (i = 0, sec = sectors; i < numsectors; i++, sec++) { SV_WriteWord(sec->floorheight >> FRACBITS); SV_WriteWord(sec->ceilingheight >> FRACBITS); SV_WriteWord(sec->floorpic); SV_WriteWord(sec->ceilingpic); SV_WriteWord(sec->lightlevel); SV_WriteWord(sec->special); // needed? SV_WriteWord(sec->tag); // needed? } // Lines for (i = 0, li = lines; i < numlines; i++, li++) { SV_WriteWord(li->flags); SV_WriteWord(li->special); SV_WriteWord(li->tag); for (j = 0; j < 2; j++) { if (li->sidenum[j] == -1) { continue; } si = &sides[li->sidenum[j]]; SV_WriteWord(si->textureoffset >> FRACBITS); SV_WriteWord(si->rowoffset >> FRACBITS); SV_WriteWord(si->toptexture); SV_WriteWord(si->bottomtexture); SV_WriteWord(si->midtexture); } } } /* ==================== = = P_UnArchiveWorld = ==================== */ void P_UnArchiveWorld(void) { int i, j; sector_t *sec; line_t *li; side_t *si; // // do sectors // for (i = 0, sec = sectors; i < numsectors; i++, sec++) { sec->floorheight = SV_ReadWord() << FRACBITS; sec->ceilingheight = SV_ReadWord() << FRACBITS; sec->floorpic = SV_ReadWord(); sec->ceilingpic = SV_ReadWord(); sec->lightlevel = SV_ReadWord(); sec->special = SV_ReadWord(); // needed? sec->tag = SV_ReadWord(); // needed? sec->specialdata = 0; sec->soundtarget = 0; } // // do lines // for (i = 0, li = lines; i < numlines; i++, li++) { li->flags = SV_ReadWord(); li->special = SV_ReadWord(); li->tag = SV_ReadWord(); for (j = 0; j < 2; j++) { if (li->sidenum[j] == -1) continue; si = &sides[li->sidenum[j]]; si->textureoffset = SV_ReadWord() << FRACBITS; si->rowoffset = SV_ReadWord() << FRACBITS; si->toptexture = SV_ReadWord(); si->bottomtexture = SV_ReadWord(); si->midtexture = SV_ReadWord(); } } } //============================================================================= typedef enum { tc_end, tc_mobj } thinkerclass_t; /* ==================== = = P_ArchiveThinkers = ==================== */ void P_ArchiveThinkers(void) { thinker_t *th; for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function == P_MobjThinker) { SV_WriteByte(tc_mobj); saveg_write_mobj_t((mobj_t *) th); } //I_Error("P_ArchiveThinkers: Unknown thinker function"); } // Add a terminating marker SV_WriteByte(tc_end); } /* ==================== = = P_UnArchiveThinkers = ==================== */ void P_UnArchiveThinkers(void) { byte tclass; thinker_t *currentthinker, *next; mobj_t *mobj; // // remove all the current thinkers // currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { next = currentthinker->next; if (currentthinker->function == P_MobjThinker) P_RemoveMobj((mobj_t *) currentthinker); else Z_Free(currentthinker); currentthinker = next; } P_InitThinkers(); // read in saved thinkers while (1) { tclass = SV_ReadByte(); switch (tclass) { case tc_end: return; // end of list case tc_mobj: mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, NULL); saveg_read_mobj_t(mobj); mobj->target = NULL; P_SetThingPosition(mobj); mobj->info = &mobjinfo[mobj->type]; mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->thinker.function = P_MobjThinker; P_AddThinker(&mobj->thinker); break; default: I_Error("Unknown tclass %i in savegame", tclass); } } } //============================================================================= /* ==================== = = P_ArchiveSpecials = ==================== */ enum { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, tc_endspecials } specials_e; void P_ArchiveSpecials(void) { /* 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 */ thinker_t *th; for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function == T_MoveCeiling) { SV_WriteByte(tc_ceiling); saveg_write_ceiling_t((ceiling_t *) th); } else if (th->function == T_VerticalDoor) { SV_WriteByte(tc_door); saveg_write_vldoor_t((vldoor_t *) th); } else if (th->function == T_MoveFloor) { SV_WriteByte(tc_floor); saveg_write_floormove_t((floormove_t *) th); } else if (th->function == T_PlatRaise) { SV_WriteByte(tc_plat); saveg_write_plat_t((plat_t *) th); } else if (th->function == T_LightFlash) { SV_WriteByte(tc_flash); saveg_write_lightflash_t((lightflash_t *) th); } else if (th->function == T_StrobeFlash) { SV_WriteByte(tc_strobe); saveg_write_strobe_t((strobe_t *) th); } else if (th->function == T_Glow) { SV_WriteByte(tc_glow); saveg_write_glow_t((glow_t *) th); } } // Add a terminating marker SV_WriteByte(tc_endspecials); } /* ==================== = = P_UnArchiveSpecials = ==================== */ void P_UnArchiveSpecials(void) { byte tclass; ceiling_t *ceiling; vldoor_t *door; floormove_t *floor; plat_t *plat; lightflash_t *flash; strobe_t *strobe; glow_t *glow; // read in saved thinkers while (1) { tclass = SV_ReadByte(); switch (tclass) { case tc_endspecials: return; // end of list case tc_ceiling: ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVEL, NULL); saveg_read_ceiling_t(ceiling); ceiling->sector->specialdata = T_MoveCeiling; // ??? ceiling->thinker.function = T_MoveCeiling; P_AddThinker(&ceiling->thinker); P_AddActiveCeiling(ceiling); break; case tc_door: door = Z_Malloc(sizeof(*door), PU_LEVEL, NULL); saveg_read_vldoor_t(door); door->sector->specialdata = door; door->thinker.function = T_VerticalDoor; P_AddThinker(&door->thinker); break; case tc_floor: floor = Z_Malloc(sizeof(*floor), PU_LEVEL, NULL); saveg_read_floormove_t(floor); floor->sector->specialdata = T_MoveFloor; floor->thinker.function = T_MoveFloor; P_AddThinker(&floor->thinker); break; case tc_plat: plat = Z_Malloc(sizeof(*plat), PU_LEVEL, NULL); saveg_read_plat_t(plat); plat->sector->specialdata = T_PlatRaise; // In the original Heretic code this was a conditional "fix" // of the thinker function, but the save code (above) decides // whether to save a T_PlatRaise based on thinker function // anyway, so it can't be NULL. Having the conditional causes // a bug, as our saveg_read_thinker_t sets these to NULL. // if (plat->thinker.function) plat->thinker.function = T_PlatRaise; P_AddThinker(&plat->thinker); P_AddActivePlat(plat); break; case tc_flash: flash = Z_Malloc(sizeof(*flash), PU_LEVEL, NULL); saveg_read_lightflash_t(flash); flash->thinker.function = T_LightFlash; P_AddThinker(&flash->thinker); break; case tc_strobe: strobe = Z_Malloc(sizeof(*strobe), PU_LEVEL, NULL); saveg_read_strobe_t(strobe); strobe->thinker.function = T_StrobeFlash; P_AddThinker(&strobe->thinker); break; case tc_glow: glow = Z_Malloc(sizeof(*glow), PU_LEVEL, NULL); saveg_read_glow_t(glow); glow->thinker.function = T_Glow; P_AddThinker(&glow->thinker); break; default: I_Error("P_UnarchiveSpecials:Unknown tclass %i " "in savegame", tclass); } } } crispy-doom-crispy-doom-5.6.4/src/heretic/p_setup.c000066400000000000000000000435231360717211000223000ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_main.c #include #include #include "doomdef.h" #include "i_swap.h" #include "i_system.h" #include "m_argv.h" #include "m_bbox.h" #include "p_local.h" #include "s_sound.h" void P_SpawnMapThing(mapthing_t * mthing); 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; int32_t *blockmap; // offsets in blockmap are from here // [crispy] BLOCKMAP limit int32_t *blockmaplump; // [crispy] BLOCKMAP limit int bmapwidth, bmapheight; // in mapblocks fixed_t bmaporgx, bmaporgy; // origin of block map mobj_t **blocklinks; // for thing chains byte *rejectmatrix; // for fast sight rejection mapthing_t deathmatchstarts[10], *deathmatch_p; mapthing_t playerstarts[MAXPLAYERS]; boolean playerstartsingame[MAXPLAYERS]; /* ================= = = P_LoadVertexes = ================= */ void P_LoadVertexes(int lump) { byte *data; int i; mapvertex_t *ml; vertex_t *li; numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t); vertexes = Z_Malloc(numvertexes * sizeof(vertex_t), PU_LEVEL, 0); data = W_CacheLumpNum(lump, PU_STATIC); ml = (mapvertex_t *) data; li = vertexes; for (i = 0; i < numvertexes; i++, li++, ml++) { li->x = SHORT(ml->x) << FRACBITS; li->y = SHORT(ml->y) << FRACBITS; // [crispy] initialize pseudovertexes with actual vertex coordinates li->r_x = li->x; li->r_y = li->y; li->moved = false; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSegs = ================= */ void P_LoadSegs(int lump) { byte *data; int i; mapseg_t *ml; seg_t *li; line_t *ldef; int linedef, side; numsegs = W_LumpLength(lump) / sizeof(mapseg_t); segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); memset(segs, 0, numsegs * sizeof(seg_t)); data = W_CacheLumpNum(lump, PU_STATIC); ml = (mapseg_t *) data; li = segs; for (i = 0; i < numsegs; i++, li++, ml++) { li->v1 = &vertexes[SHORT(ml->v1)]; li->v2 = &vertexes[SHORT(ml->v2)]; li->angle = (SHORT(ml->angle)) << 16; li->offset = (SHORT(ml->offset)) << 16; linedef = SHORT(ml->linedef); ldef = &lines[linedef]; li->linedef = ldef; side = SHORT(ml->side); li->sidedef = &sides[ldef->sidenum[side]]; li->frontsector = sides[ldef->sidenum[side]].sector; if (ldef->flags & ML_TWOSIDED) li->backsector = sides[ldef->sidenum[side ^ 1]].sector; else li->backsector = 0; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSubsectors = ================= */ void P_LoadSubsectors(int lump) { byte *data; int i; mapsubsector_t *ms; subsector_t *ss; numsubsectors = W_LumpLength(lump) / sizeof(mapsubsector_t); subsectors = Z_Malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); data = W_CacheLumpNum(lump, PU_STATIC); ms = (mapsubsector_t *) data; memset(subsectors, 0, numsubsectors * sizeof(subsector_t)); ss = subsectors; for (i = 0; i < numsubsectors; i++, ss++, ms++) { ss->numlines = SHORT(ms->numsegs); ss->firstline = SHORT(ms->firstseg); } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSectors = ================= */ void P_LoadSectors(int lump) { byte *data; int i; mapsector_t *ms; sector_t *ss; numsectors = W_LumpLength(lump) / sizeof(mapsector_t); sectors = Z_Malloc(numsectors * sizeof(sector_t), PU_LEVEL, 0); memset(sectors, 0, numsectors * sizeof(sector_t)); data = W_CacheLumpNum(lump, PU_STATIC); ms = (mapsector_t *) data; ss = sectors; for (i = 0; i < numsectors; i++, ss++, ms++) { ss->floorheight = SHORT(ms->floorheight) << FRACBITS; ss->ceilingheight = SHORT(ms->ceilingheight) << FRACBITS; ss->floorpic = R_FlatNumForName(ms->floorpic); ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); ss->lightlevel = SHORT(ms->lightlevel); ss->special = SHORT(ms->special); ss->tag = SHORT(ms->tag); ss->thinglist = NULL; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadNodes = ================= */ void P_LoadNodes(int lump) { byte *data; int i, j, k; mapnode_t *mn; node_t *no; numnodes = W_LumpLength(lump) / sizeof(mapnode_t); nodes = Z_Malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); data = W_CacheLumpNum(lump, PU_STATIC); mn = (mapnode_t *) data; no = nodes; for (i = 0; i < numnodes; i++, no++, mn++) { no->x = SHORT(mn->x) << FRACBITS; no->y = SHORT(mn->y) << FRACBITS; no->dx = SHORT(mn->dx) << FRACBITS; no->dy = SHORT(mn->dy) << FRACBITS; for (j = 0; j < 2; j++) { no->children[j] = SHORT(mn->children[j]); for (k = 0; k < 4; k++) no->bbox[j][k] = SHORT(mn->bbox[j][k]) << FRACBITS; } } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadThings = ================= */ void P_LoadThings(int lump) { byte *data; int i; mapthing_t spawnthing; mapthing_t *mt; int numthings; data = W_CacheLumpNum(lump, PU_STATIC); numthings = W_LumpLength(lump) / sizeof(mapthing_t); mt = (mapthing_t *) data; for (i = 0; i < numthings; i++, mt++) { spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); spawnthing.options = SHORT(mt->options); P_SpawnMapThing(&spawnthing); } if (!deathmatch) { for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && !playerstartsingame[i]) { I_Error("P_LoadThings: Player %d start missing (vanilla crashes here)", i + 1); } playerstartsingame[i] = false; } } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadLineDefs = = Also counts secret lines for intermissions ================= */ void P_LoadLineDefs(int lump) { byte *data; int i; maplinedef_t *mld; line_t *ld; vertex_t *v1, *v2; numlines = W_LumpLength(lump) / sizeof(maplinedef_t); lines = Z_Malloc(numlines * sizeof(line_t), PU_LEVEL, 0); memset(lines, 0, numlines * sizeof(line_t)); data = W_CacheLumpNum(lump, PU_STATIC); mld = (maplinedef_t *) data; ld = lines; for (i = 0; i < numlines; i++, mld++, ld++) { ld->flags = SHORT(mld->flags); ld->special = SHORT(mld->special); ld->tag = SHORT(mld->tag); v1 = ld->v1 = &vertexes[SHORT(mld->v1)]; v2 = ld->v2 = &vertexes[SHORT(mld->v2)]; ld->dx = v2->x - v1->x; ld->dy = v2->y - v1->y; if (!ld->dx) ld->slopetype = ST_VERTICAL; else if (!ld->dy) ld->slopetype = ST_HORIZONTAL; else { if (FixedDiv(ld->dy, ld->dx) > 0) ld->slopetype = ST_POSITIVE; else ld->slopetype = ST_NEGATIVE; } if (v1->x < v2->x) { ld->bbox[BOXLEFT] = v1->x; ld->bbox[BOXRIGHT] = v2->x; } else { ld->bbox[BOXLEFT] = v2->x; ld->bbox[BOXRIGHT] = v1->x; } if (v1->y < v2->y) { ld->bbox[BOXBOTTOM] = v1->y; ld->bbox[BOXTOP] = v2->y; } else { ld->bbox[BOXBOTTOM] = v2->y; ld->bbox[BOXTOP] = v1->y; } ld->sidenum[0] = SHORT(mld->sidenum[0]); ld->sidenum[1] = SHORT(mld->sidenum[1]); if (ld->sidenum[0] != -1) ld->frontsector = sides[ld->sidenum[0]].sector; else ld->frontsector = 0; if (ld->sidenum[1] != -1) ld->backsector = sides[ld->sidenum[1]].sector; else ld->backsector = 0; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSideDefs = ================= */ void P_LoadSideDefs(int lump) { byte *data; int i; mapsidedef_t *msd; side_t *sd; numsides = W_LumpLength(lump) / sizeof(mapsidedef_t); sides = Z_Malloc(numsides * sizeof(side_t), PU_LEVEL, 0); memset(sides, 0, numsides * sizeof(side_t)); data = W_CacheLumpNum(lump, PU_STATIC); msd = (mapsidedef_t *) data; sd = sides; for (i = 0; i < numsides; i++, msd++, sd++) { sd->textureoffset = SHORT(msd->textureoffset) << FRACBITS; sd->rowoffset = SHORT(msd->rowoffset) << FRACBITS; sd->toptexture = R_TextureNumForName(msd->toptexture); sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); sd->midtexture = R_TextureNumForName(msd->midtexture); sd->sector = §ors[SHORT(msd->sector)]; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadBlockMap = ================= */ void P_LoadBlockMap(int lump) { int i, count; int lumplen; short *wadblockmaplump; lumplen = W_LumpLength(lump); count = lumplen / 2; // [crispy] remove BLOCKMAP limit // [crispy] remove BLOCKMAP limit wadblockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL); W_ReadLump(lump, wadblockmaplump); blockmaplump = Z_Malloc(sizeof(*blockmaplump) * count, PU_LEVEL, NULL); blockmap = blockmaplump + 4; blockmaplump[0] = SHORT(wadblockmaplump[0]); blockmaplump[1] = SHORT(wadblockmaplump[1]); blockmaplump[2] = (int32_t)(SHORT(wadblockmaplump[2])) & 0xffff; blockmaplump[3] = (int32_t)(SHORT(wadblockmaplump[3])) & 0xffff; // Swap all short integers to native byte ordering: // count = lumplen / 2; // [crispy] moved up for (i=4; ifirstline]; ss->sector = seg->sidedef->sector; } // count number of lines in each sector li = lines; total = 0; for (i = 0; i < numlines; i++, li++) { total++; li->frontsector->linecount++; if (li->backsector && li->backsector != li->frontsector) { li->backsector->linecount++; total++; } } // build line tables for each sector linebuffer = Z_Malloc(total * sizeof(line_t *), PU_LEVEL, 0); sector = sectors; for (i = 0; i < numsectors; i++, sector++) { M_ClearBox(bbox); sector->lines = linebuffer; li = lines; for (j = 0; j < numlines; j++, li++) { if (li->frontsector == sector || li->backsector == sector) { *linebuffer++ = li; M_AddToBox(bbox, li->v1->x, li->v1->y); M_AddToBox(bbox, li->v2->x, li->v2->y); } } if (linebuffer - sector->lines != sector->linecount) I_Error("P_GroupLines: miscounted"); // set the degenmobj_t to the middle of the bounding box sector->soundorg.x = (bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2; sector->soundorg.y = (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2; // adjust bounding box to map blocks block = (bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; block = block >= bmapheight ? bmapheight - 1 : block; sector->blockbox[BOXTOP] = block; block = (bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXBOTTOM] = block; block = (bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; block = block >= bmapwidth ? bmapwidth - 1 : block; sector->blockbox[BOXRIGHT] = block; block = (bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXLEFT] = block; } } //============================================================================= // [crispy] remove slime trails // mostly taken from Lee Killough's implementation in mbfsrc/P_SETUP.C:849-924, // with the exception that not the actual vertex coordinates are modified, // but separate coordinates that are *only* used in rendering, // i.e. r_bsp.c:R_AddLine() static void P_RemoveSlimeTrails(void) { int i; for (i = 0; i < numsegs; i++) { const line_t *l = segs[i].linedef; vertex_t *v = segs[i].v1; // [crispy] ignore exactly vertical or horizontal linedefs if (l->dx && l->dy) { do { // [crispy] vertex wasn't already moved if (!v->moved) { v->moved = true; // [crispy] ignore endpoints of linedefs if (v != l->v1 && v != l->v2) { // [crispy] move the vertex towards the linedef // by projecting it using the law of cosines int64_t dx2 = (l->dx >> FRACBITS) * (l->dx >> FRACBITS); int64_t dy2 = (l->dy >> FRACBITS) * (l->dy >> FRACBITS); int64_t dxy = (l->dx >> FRACBITS) * (l->dy >> FRACBITS); int64_t s = dx2 + dy2; // [crispy] MBF actually overrides v->x and v->y here v->r_x = (fixed_t)((dx2 * v->x + dy2 * l->v1->x + dxy * (v->y - l->v1->y)) / s); v->r_y = (fixed_t)((dy2 * v->y + dx2 * l->v1->y + dxy * (v->x - l->v1->x)) / s); // [crispy] wait a minute... moved more than 8 map units? // maybe that's a linguortal then, back to the original coordinates if (abs(v->r_x - v->x) > 8*FRACUNIT || abs(v->r_y - v->y) > 8*FRACUNIT) { v->r_x = v->x; v->r_y = v->y; } } } // [crispy] if v doesn't point to the second vertex of the seg already, point it there } while ((v != segs[i].v2) && (v = segs[i].v2)); } } } /* ================= = = P_SetupLevel = ================= */ void P_SetupLevel(int episode, int map, int playermask, skill_t skill) { int i; int parm; char lumpname[9]; int lumpnum; mobj_t *mobj; totalkills = totalitems = totalsecret = 0; for (i = 0; i < MAXPLAYERS; i++) { players[i].killcount = players[i].secretcount = players[i].itemcount = 0; } players[consoleplayer].viewz = 1; // will be set by player think S_Start(); // make sure all sounds are stopped before Z_FreeTags Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); P_InitThinkers(); // // look for a regular (development) map first // lumpname[0] = 'E'; lumpname[1] = '0' + episode; lumpname[2] = 'M'; lumpname[3] = '0' + map; lumpname[4] = 0; leveltime = 0; lumpnum = W_GetNumForName(lumpname); // note: most of this ordering is important P_LoadBlockMap(lumpnum + ML_BLOCKMAP); P_LoadVertexes(lumpnum + ML_VERTEXES); P_LoadSectors(lumpnum + ML_SECTORS); P_LoadSideDefs(lumpnum + ML_SIDEDEFS); P_LoadLineDefs(lumpnum + ML_LINEDEFS); P_LoadSubsectors(lumpnum + ML_SSECTORS); P_LoadNodes(lumpnum + ML_NODES); P_LoadSegs(lumpnum + ML_SEGS); rejectmatrix = W_CacheLumpNum(lumpnum + ML_REJECT, PU_LEVEL); P_GroupLines(); // [crispy] remove slime trails P_RemoveSlimeTrails(); bodyqueslot = 0; deathmatch_p = deathmatchstarts; P_InitAmbientSound(); P_InitMonsters(); P_OpenWeapons(); P_LoadThings(lumpnum + ML_THINGS); P_CloseWeapons(); // // if deathmatch, randomly spawn the active players // TimerGame = 0; if (deathmatch) { for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { // must give a player spot before deathmatchspawn mobj = P_SpawnMobj(playerstarts[i].x << 16, playerstarts[i].y << 16, 0, MT_PLAYER); players[i].mo = mobj; G_DeathMatchSpawnPlayer(i); P_RemoveMobj(mobj); } } //! // @arg // @category net // @vanilla // // For multiplayer games: exit each level after n minutes. // parm = M_CheckParmWithArgs("-timer", 1); if (parm) { TimerGame = atoi(myargv[parm + 1]) * 35 * 60; } } // set up world state P_SpawnSpecials(); // build subsector connect matrix // P_ConnectSubsectors (); // preload graphics if (precache) R_PrecacheLevel(); //printf ("free memory: 0x%x\n", Z_FreeMemory()); } /* ================= = = P_Init = ================= */ void P_Init(void) { P_InitSwitchList(); P_InitPicAnims(); P_InitTerrainTypes(); P_InitLava(); R_InitSprites(sprnames); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_sight.c000066400000000000000000000203701360717211000222510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_sight.c #include #include "doomdef.h" #include "p_local.h" /* ============================================================================== 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]; /* ============== = = PTR_SightTraverse = ============== */ boolean 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 = =================== */ boolean P_SightBlockLinesIterator(int x, int y) { int offset; int32_t *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 // store the line for later intersection testing intercept_p->d.line = ld; intercept_p++; // [crispy] catch intercepts overflows if (intercept_p - intercepts == MAXINTERCEPTS) return false; } return true; // everything was checked } /* ==================== = = P_SightTraverseIntercepts = = Returns true if the traverser function returns true for all lines ==================== */ boolean 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; scan < intercept_p; scan++) { P_MakeDivline(scan->d.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; scan < intercept_p; scan++) if (scan->frac < 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 ================== */ boolean 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 = x1 >> MAPBLOCKSHIFT; yt1 = y1 >> MAPBLOCKSHIFT; x2 -= bmaporgx; y2 -= bmaporgy; xt2 = x2 >> MAPBLOCKSHIFT; yt2 = y2 >> MAPBLOCKSHIFT; // 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 = ===================== */ boolean P_CheckSight(mobj_t * t1, mobj_t * t2) { int s1, s2; int pnum, bytenum, bitnum; // // check for trivial rejection // s1 = (t1->subsector->sector - sectors); s2 = (t2->subsector->sector - sectors); 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); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_spec.c000066400000000000000000001125201360717211000220640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_Spec.c #include "doomdef.h" #include "deh_str.h" #include "i_system.h" #include "i_timer.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" // Macros #define MAX_AMBIENT_SFX 8 // Per level // Types typedef enum { afxcmd_play, // (sound) afxcmd_playabsvol, // (sound, volume) afxcmd_playrelvol, // (sound, volume) afxcmd_delay, // (ticks) afxcmd_delayrand, // (andbits) afxcmd_end // () } afxcmd_t; // Data int *LevelAmbientSfx[MAX_AMBIENT_SFX]; int *AmbSfxPtr; int AmbSfxCount; int AmbSfxTics; int AmbSfxVolume; int AmbSndSeqInit[] = { // Startup afxcmd_end }; int AmbSndSeq1[] = { // Scream afxcmd_play, sfx_amb1, afxcmd_end }; int AmbSndSeq2[] = { // Squish afxcmd_play, sfx_amb2, afxcmd_end }; int AmbSndSeq3[] = { // Drops afxcmd_play, sfx_amb3, afxcmd_delay, 16, afxcmd_delayrand, 31, afxcmd_play, sfx_amb7, afxcmd_delay, 16, afxcmd_delayrand, 31, afxcmd_play, sfx_amb3, afxcmd_delay, 16, afxcmd_delayrand, 31, afxcmd_play, sfx_amb7, afxcmd_delay, 16, afxcmd_delayrand, 31, afxcmd_play, sfx_amb3, afxcmd_delay, 16, afxcmd_delayrand, 31, afxcmd_play, sfx_amb7, afxcmd_delay, 16, afxcmd_delayrand, 31, afxcmd_end }; int AmbSndSeq4[] = { // SlowFootSteps afxcmd_play, sfx_amb4, afxcmd_delay, 15, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_delay, 15, afxcmd_playrelvol, sfx_amb4, -3, afxcmd_delay, 15, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_delay, 15, afxcmd_playrelvol, sfx_amb4, -3, afxcmd_delay, 15, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_delay, 15, afxcmd_playrelvol, sfx_amb4, -3, afxcmd_delay, 15, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_end }; int AmbSndSeq5[] = { // Heartbeat afxcmd_play, sfx_amb5, afxcmd_delay, 35, afxcmd_play, sfx_amb5, afxcmd_delay, 35, afxcmd_play, sfx_amb5, afxcmd_delay, 35, afxcmd_play, sfx_amb5, afxcmd_end }; int AmbSndSeq6[] = { // Bells afxcmd_play, sfx_amb6, afxcmd_delay, 17, afxcmd_playrelvol, sfx_amb6, -8, afxcmd_delay, 17, afxcmd_playrelvol, sfx_amb6, -8, afxcmd_delay, 17, afxcmd_playrelvol, sfx_amb6, -8, afxcmd_end }; int AmbSndSeq7[] = { // Growl afxcmd_play, sfx_bstsit, afxcmd_end }; int AmbSndSeq8[] = { // Magic afxcmd_play, sfx_amb8, afxcmd_end }; int AmbSndSeq9[] = { // Laughter afxcmd_play, sfx_amb9, afxcmd_delay, 16, afxcmd_playrelvol, sfx_amb9, -4, afxcmd_delay, 16, afxcmd_playrelvol, sfx_amb9, -4, afxcmd_delay, 16, afxcmd_playrelvol, sfx_amb10, -4, afxcmd_delay, 16, afxcmd_playrelvol, sfx_amb10, -4, afxcmd_delay, 16, afxcmd_playrelvol, sfx_amb10, -4, afxcmd_end }; int AmbSndSeq10[] = { // FastFootsteps afxcmd_play, sfx_amb4, afxcmd_delay, 8, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_delay, 8, afxcmd_playrelvol, sfx_amb4, -3, afxcmd_delay, 8, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_delay, 8, afxcmd_playrelvol, sfx_amb4, -3, afxcmd_delay, 8, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_delay, 8, afxcmd_playrelvol, sfx_amb4, -3, afxcmd_delay, 8, afxcmd_playrelvol, sfx_amb11, -3, afxcmd_end }; int *AmbientSfx[] = { AmbSndSeq1, // Scream AmbSndSeq2, // Squish AmbSndSeq3, // Drops AmbSndSeq4, // SlowFootsteps AmbSndSeq5, // Heartbeat AmbSndSeq6, // Bells AmbSndSeq7, // Growl AmbSndSeq8, // Magic AmbSndSeq9, // Laughter AmbSndSeq10 // FastFootsteps }; animdef_t animdefs[] = { // false = flat // true = texture {false, "FLTWAWA3", "FLTWAWA1", 8}, // Water {false, "FLTSLUD3", "FLTSLUD1", 8}, // Sludge {false, "FLTTELE4", "FLTTELE1", 6}, // Teleport {false, "FLTFLWW3", "FLTFLWW1", 9}, // River - West {false, "FLTLAVA4", "FLTLAVA1", 8}, // Lava {false, "FLATHUH4", "FLATHUH1", 8}, // Super Lava {true, "LAVAFL3", "LAVAFL1", 6}, // Texture: Lavaflow {true, "WATRWAL3", "WATRWAL1", 4}, // Texture: Waterfall {-1} }; anim_t anims[MAXANIMS]; anim_t *lastanim; int *TerrainTypes; struct { const char *name; int type; } TerrainTypeDefs[] = { { "FLTWAWA1", FLOOR_WATER }, { "FLTFLWW1", FLOOR_WATER }, { "FLTLAVA1", FLOOR_LAVA }, { "FLATHUH1", FLOOR_LAVA }, { "FLTSLUD1", FLOOR_SLUDGE }, { "END", -1 } }; mobj_t LavaInflictor; //---------------------------------------------------------------------------- // // PROC P_InitLava // //---------------------------------------------------------------------------- void P_InitLava(void) { memset(&LavaInflictor, 0, sizeof(mobj_t)); LavaInflictor.type = MT_PHOENIXFX2; LavaInflictor.flags2 = MF2_FIREDAMAGE | MF2_NODMGTHRUST; } //---------------------------------------------------------------------------- // // PROC P_InitTerrainTypes // //---------------------------------------------------------------------------- void P_InitTerrainTypes(void) { int i; int lump; int size; size = (numflats + 1) * sizeof(int); TerrainTypes = Z_Malloc(size, PU_STATIC, 0); memset(TerrainTypes, 0, size); for (i = 0; TerrainTypeDefs[i].type != -1; i++) { lump = W_CheckNumForName(TerrainTypeDefs[i].name); if (lump != -1) { TerrainTypes[lump - firstflat] = TerrainTypeDefs[i].type; } } } //---------------------------------------------------------------------------- // // PROC P_InitPicAnims // //---------------------------------------------------------------------------- void P_InitPicAnims(void) { const char *startname; const char *endname; int i; lastanim = anims; for (i = 0; animdefs[i].istexture != -1; i++) { startname = DEH_String(animdefs[i].startname); endname = DEH_String(animdefs[i].endname); if (animdefs[i].istexture) { // Texture animation if (R_CheckTextureNumForName(startname) == -1) { // Texture doesn't exist continue; } lastanim->picnum = R_TextureNumForName(endname); lastanim->basepic = R_TextureNumForName(startname); } else { // Flat animation if (W_CheckNumForName(startname) == -1) { // Flat doesn't exist continue; } lastanim->picnum = R_FlatNumForName(endname); lastanim->basepic = R_FlatNumForName(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", startname, endname); } lastanim->speed = animdefs[i].speed; lastanim++; } } /* ============================================================================== UTILITIES ============================================================================== */ // // Will return a side_t* given the number of the current sector, // the line number, and the side (0/1) that you want. // side_t *getSide(int currentSector, int line, int side) { return &sides[(sectors[currentSector].lines[line])->sidenum[side]]; } // // Will return a sector_t* given the number of the current sector, // the line number and the side (0/1) that you want. // sector_t *getSector(int currentSector, int line, int side) { return sides[(sectors[currentSector].lines[line])->sidenum[side]].sector; } // // Given the sector number and the line number, will tell you whether // the line is two-sided or not. // int twoSided(int sector, int line) { return (sectors[sector].lines[line])->flags & ML_TWOSIDED; } //================================================================== // // Return sector_t * of sector next to current. NULL if not two-sided line // //================================================================== sector_t *getNextSector(line_t * line, sector_t * sec) { if (!(line->flags & ML_TWOSIDED)) return NULL; if (line->frontsector == sec) return line->backsector; return line->frontsector; } //================================================================== // // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS // //================================================================== fixed_t P_FindLowestFloorSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t floor = sec->floorheight; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->floorheight < floor) floor = other->floorheight; } return floor; } //================================================================== // // FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS // //================================================================== fixed_t P_FindHighestFloorSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t floor = -500 * FRACUNIT; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->floorheight > floor) floor = other->floorheight; } return floor; } //================================================================== // // FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS // //================================================================== fixed_t P_FindNextHighestFloor(sector_t * sec, int currentheight) { int i; int h; fixed_t min; line_t *check; sector_t *other; fixed_t height = currentheight; min = INT_MAX; for (i = 0, h = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (other != NULL && other->floorheight > height) { if (other->floorheight < min) { min = other->floorheight; } ++h; } } // Don't return INT_MAX if no higher floor is found. if (!h) { return height; } // Compatibility note, in case of demo desyncs. if (h > 20) { fprintf(stderr, "P_FindNextHighestFloor: exceeded Vanilla limit\n"); } return min; } //================================================================== // // FIND LOWEST CEILING IN THE SURROUNDING SECTORS // //================================================================== fixed_t P_FindLowestCeilingSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t height = INT_MAX; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->ceilingheight < height) height = other->ceilingheight; } return height; } //================================================================== // // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS // //================================================================== fixed_t P_FindHighestCeilingSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t height = 0; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->ceilingheight > height) height = other->ceilingheight; } return height; } //================================================================== // // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO // //================================================================== int P_FindSectorFromLineTag(line_t * line, int start) { int i; for (i = start + 1; i < numsectors; i++) if (sectors[i].tag == line->tag) return i; return -1; } //================================================================== // // Find minimum light from an adjacent sector // //================================================================== int P_FindMinSurroundingLight(sector_t * sector, int max) { int i; int min; line_t *line; sector_t *check; min = max; for (i = 0; i < sector->linecount; i++) { line = sector->lines[i]; check = getNextSector(line, sector); if (!check) continue; if (check->lightlevel < min) min = check->lightlevel; } return min; } /* ============================================================================== EVENTS Events are operations triggered by using, crossing, or shooting special lines, or by timed thinkers ============================================================================== */ /* =============================================================================== = = P_CrossSpecialLine - TRIGGER = = Called every time a thing origin is about to cross = a line with a non 0 special = =============================================================================== */ void P_CrossSpecialLine(int linenum, int side, mobj_t * thing) { line_t *line; line = &lines[linenum]; if (!thing->player) { // Check if trigger allowed by non-player mobj switch (line->special) { case 39: // Trigger_TELEPORT case 97: // Retrigger_TELEPORT case 4: // Trigger_Raise_Door //case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER //case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER break; default: return; break; } } switch (line->special) { //==================================================== // TRIGGERS //==================================================== case 2: // Open Door EV_DoDoor(line, vld_open, VDOORSPEED); line->special = 0; break; case 3: // Close Door EV_DoDoor(line, vld_close, VDOORSPEED); line->special = 0; break; case 4: // Raise Door EV_DoDoor(line, vld_normal, VDOORSPEED); line->special = 0; break; case 5: // Raise Floor EV_DoFloor(line, raiseFloor); line->special = 0; break; case 6: // Fast Ceiling Crush & Raise EV_DoCeiling(line, fastCrushAndRaise); line->special = 0; break; case 8: // Trigger_Build_Stairs (8 pixel steps) EV_BuildStairs(line, 8 * FRACUNIT); line->special = 0; break; case 106: // Trigger_Build_Stairs_16 (16 pixel steps) EV_BuildStairs(line, 16 * FRACUNIT); line->special = 0; break; case 10: // PlatDownWaitUp EV_DoPlat(line, downWaitUpStay, 0); line->special = 0; break; case 12: // Light Turn On - brightest near EV_LightTurnOn(line, 0); line->special = 0; break; case 13: // Light Turn On 255 EV_LightTurnOn(line, 255); line->special = 0; break; case 16: // Close Door 30 EV_DoDoor(line, vld_close30ThenOpen, VDOORSPEED); line->special = 0; break; case 17: // Start Light Strobing EV_StartLightStrobing(line); line->special = 0; break; case 19: // Lower Floor EV_DoFloor(line, lowerFloor); line->special = 0; break; case 22: // Raise floor to nearest height and change texture EV_DoPlat(line, raiseToNearestAndChange, 0); line->special = 0; break; case 25: // Ceiling Crush and Raise EV_DoCeiling(line, crushAndRaise); line->special = 0; break; case 30: // Raise floor to shortest texture height // on either side of lines EV_DoFloor(line, raiseToTexture); line->special = 0; break; case 35: // Lights Very Dark EV_LightTurnOn(line, 35); line->special = 0; break; case 36: // Lower Floor (TURBO) EV_DoFloor(line, turboLower); line->special = 0; break; case 37: // LowerAndChange EV_DoFloor(line, lowerAndChange); line->special = 0; break; case 38: // Lower Floor To Lowest EV_DoFloor(line, lowerFloorToLowest); line->special = 0; break; case 39: // TELEPORT! EV_Teleport(line, side, thing); line->special = 0; break; case 40: // RaiseCeilingLowerFloor EV_DoCeiling(line, raiseToHighest); EV_DoFloor(line, lowerFloorToLowest); line->special = 0; break; case 44: // Ceiling Crush EV_DoCeiling(line, lowerAndCrush); line->special = 0; break; case 52: // EXIT! G_ExitLevel(); line->special = 0; break; case 53: // Perpetual Platform Raise EV_DoPlat(line, perpetualRaise, 0); line->special = 0; break; case 54: // Platform Stop EV_StopPlat(line); line->special = 0; break; case 56: // Raise Floor Crush EV_DoFloor(line, raiseFloorCrush); line->special = 0; break; case 57: // Ceiling Crush Stop EV_CeilingCrushStop(line); line->special = 0; break; case 58: // Raise Floor 24 EV_DoFloor(line, raiseFloor24); line->special = 0; break; case 59: // Raise Floor 24 And Change EV_DoFloor(line, raiseFloor24AndChange); line->special = 0; break; case 104: // Turn lights off in sector(tag) EV_TurnTagLightsOff(line); line->special = 0; break; case 105: // Trigger_SecretExit G_SecretExitLevel(); line->special = 0; break; //==================================================== // RE-DOABLE TRIGGERS //==================================================== 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, vld_close, VDOORSPEED); break; case 76: // Close Door 30 EV_DoDoor(line, vld_close30ThenOpen, VDOORSPEED); 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, vld_open, VDOORSPEED); 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, vld_normal, VDOORSPEED); break; case 100: // Retrigger_Raise_Door_Turbo EV_DoDoor(line, vld_normal, VDOORSPEED * 3); 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; } } //---------------------------------------------------------------------------- // // PROC P_ShootSpecialLine // // Called when a thing shoots a special line. // //---------------------------------------------------------------------------- void P_ShootSpecialLine(mobj_t * thing, line_t * line) { if (!thing->player) { // Check if trigger allowed by non-player mobj switch (line->special) { case 46: // Impact_OpenDoor break; default: return; break; } } switch (line->special) { case 24: // Impact_RaiseFloor EV_DoFloor(line, raiseFloor); P_ChangeSwitchTexture(line, 0); break; case 46: // Impact_OpenDoor EV_DoDoor(line, vld_open, VDOORSPEED); P_ChangeSwitchTexture(line, 1); break; case 47: // Impact_RaiseFloorNear&Change EV_DoPlat(line, raiseToNearestAndChange, 0); P_ChangeSwitchTexture(line, 0); break; } } //---------------------------------------------------------------------------- // // PROC P_PlayerInSpecialSector // // Called every tic frame that the player origin is in a special sector. // //---------------------------------------------------------------------------- void P_PlayerInSpecialSector(player_t * player) { sector_t *sector; static int pushTab[5] = { 2048 * 5, 2048 * 10, 2048 * 25, 2048 * 30, 2048 * 35 }; sector = player->mo->subsector->sector; if (player->mo->z != sector->floorheight) { // Player is not touching the floor return; } switch (sector->special) { case 7: // Damage_Sludge if (!(leveltime & 31)) { P_DamageMobj(player->mo, NULL, NULL, 4); } break; case 5: // Damage_LavaWimpy if (!(leveltime & 15)) { P_DamageMobj(player->mo, &LavaInflictor, NULL, 5); P_HitFloor(player->mo); } break; case 16: // Damage_LavaHefty if (!(leveltime & 15)) { P_DamageMobj(player->mo, &LavaInflictor, NULL, 8); P_HitFloor(player->mo); } break; case 4: // Scroll_EastLavaDamage P_Thrust(player, 0, 2048 * 28); if (!(leveltime & 15)) { P_DamageMobj(player->mo, &LavaInflictor, NULL, 5); P_HitFloor(player->mo); } break; case 9: // SecretArea player->secretcount++; sector->special = 0; break; case 11: // Exit_SuperDamage (DOOM E1M8 finale) /* player->cheats &= ~CF_GODMODE; if(!(leveltime&0x1f)) { P_DamageMobj(player->mo, NULL, NULL, 20); } if(player->health <= 10) { G_ExitLevel(); } */ break; case 25: case 26: case 27: case 28: case 29: // Scroll_North P_Thrust(player, ANG90, pushTab[sector->special - 25]); break; case 20: case 21: case 22: case 23: case 24: // Scroll_East P_Thrust(player, 0, pushTab[sector->special - 20]); break; case 30: case 31: case 32: case 33: case 34: // Scroll_South P_Thrust(player, ANG270, pushTab[sector->special - 30]); break; case 35: case 36: case 37: case 38: case 39: // Scroll_West P_Thrust(player, ANG180, pushTab[sector->special - 35]); break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: // Wind specials are handled in (P_mobj):P_XYMovement break; case 15: // Friction_Low // Only used in (P_mobj):P_XYMovement and (P_user):P_Thrust break; default: I_Error("P_PlayerInSpecialSector: " "unknown special %i", sector->special); } } //---------------------------------------------------------------------------- // // PROC P_UpdateSpecials // // Animate planes, scroll walls, etc. // //---------------------------------------------------------------------------- void P_UpdateSpecials(void) { int i; int pic; anim_t *anim; line_t *line; // Animate flats and textures for (anim = anims; anim < lastanim; anim++) { for (i = anim->basepic; i < anim->basepic + anim->numpics; i++) { pic = anim->basepic + ((leveltime / anim->speed + i) % anim->numpics); if (anim->istexture) { texturetranslation[i] = pic; } else { flattranslation[i] = pic; } } } // Update scrolling texture offsets for (i = 0; i < numlinespecials; i++) { line = linespeciallist[i]; switch (line->special) { case 48: // Effect_Scroll_Left sides[line->sidenum[0]].textureoffset += FRACUNIT; break; case 99: // Effect_Scroll_Right sides[line->sidenum[0]].textureoffset -= FRACUNIT; break; } } // Handle buttons for (i = 0; i < MAXBUTTONS; i++) { if (buttonlist[i].btimer) { buttonlist[i].btimer--; if (!buttonlist[i].btimer) { switch (buttonlist[i].where) { case top: sides[buttonlist[i].line->sidenum[0]].toptexture = buttonlist[i].btexture; break; case middle: sides[buttonlist[i].line->sidenum[0]].midtexture = buttonlist[i].btexture; break; case bottom: sides[buttonlist[i].line->sidenum[0]].bottomtexture = buttonlist[i].btexture; break; } S_StartSound(buttonlist[i].soundorg, sfx_switch); memset(&buttonlist[i], 0, sizeof(button_t)); } } } } //============================================================ // // Special Stuff that can't be categorized // //============================================================ int EV_DoDonut(line_t * line) { sector_t *s1; sector_t *s2; sector_t *s3; int secnum; int rtn; int i; floormove_t *floor; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0) { s1 = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (s1->specialdata) continue; rtn = 1; s2 = getNextSector(s1->lines[0], s1); for (i = 0; i < s2->linecount; i++) { // Note: This was originally part of the following test: // (!s2->lines[i]->flags & ML_TWOSIDED) || // Due to the apparent mistaken formatting, this can never be // true. if (s2->lines[i]->backsector == s1) continue; s3 = s2->lines[i]->backsector; // // Spawn rising slime // floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker(&floor->thinker); s2->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = donutRaise; floor->crush = false; floor->direction = 1; floor->sector = s2; floor->speed = FLOORSPEED / 2; floor->texture = s3->floorpic; floor->newspecial = 0; floor->floordestheight = s3->floorheight; // // Spawn lowering donut-hole // floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker(&floor->thinker); s1->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = lowerFloor; floor->crush = false; floor->direction = -1; floor->sector = s1; floor->speed = FLOORSPEED / 2; floor->floordestheight = s3->floorheight; break; } } return rtn; } /* ============================================================================== SPECIAL SPAWNING ============================================================================== */ /* ================================================================================ = P_SpawnSpecials = = After the map has been loaded, scan for specials that = spawn thinkers = =============================================================================== */ short numlinespecials; line_t *linespeciallist[MAXLINEANIMS]; void P_SpawnSpecials(void) { sector_t *sector; int i; // // Init special SECTORs // sector = sectors; for (i = 0; i < numsectors; i++, sector++) { if (!sector->special) continue; switch (sector->special) { case 1: // FLICKERING LIGHTS P_SpawnLightFlash(sector); break; case 2: // STROBE FAST P_SpawnStrobeFlash(sector, FASTDARK, 0); break; case 3: // STROBE SLOW P_SpawnStrobeFlash(sector, SLOWDARK, 0); break; case 4: // STROBE FAST/DEATH SLIME P_SpawnStrobeFlash(sector, FASTDARK, 0); sector->special = 4; break; case 8: // GLOWING LIGHT P_SpawnGlowingLight(sector); break; case 9: // SECRET SECTOR totalsecret++; break; case 10: // DOOR CLOSE IN 30 SECONDS P_SpawnDoorCloseIn30(sector); break; case 12: // SYNC STROBE SLOW P_SpawnStrobeFlash(sector, SLOWDARK, 1); break; case 13: // SYNC STROBE FAST P_SpawnStrobeFlash(sector, FASTDARK, 1); break; case 14: // DOOR RAISE IN 5 MINUTES P_SpawnDoorRaiseIn5Mins(sector, i); break; } } // // Init line EFFECTs // numlinespecials = 0; for (i = 0; i < numlines; i++) switch (lines[i].special) { case 48: // Effect_Scroll_Left case 99: // Effect_Scroll_Right linespeciallist[numlinespecials] = &lines[i]; numlinespecials++; break; } // // Init other misc stuff // for (i = 0; i < MAXCEILINGS; i++) activeceilings[i] = NULL; for (i = 0; i < MAXPLATS; i++) activeplats[i] = NULL; for (i = 0; i < MAXBUTTONS; i++) memset(&buttonlist[i], 0, sizeof(button_t)); } //---------------------------------------------------------------------------- // // PROC P_InitAmbientSound // //---------------------------------------------------------------------------- void P_InitAmbientSound(void) { AmbSfxCount = 0; AmbSfxVolume = 0; AmbSfxTics = 10 * TICRATE; AmbSfxPtr = AmbSndSeqInit; } //---------------------------------------------------------------------------- // // PROC P_AddAmbientSfx // // Called by (P_mobj):P_SpawnMapThing during (P_setup):P_SetupLevel. // //---------------------------------------------------------------------------- void P_AddAmbientSfx(int sequence) { if (AmbSfxCount == MAX_AMBIENT_SFX) { I_Error("Too many ambient sound sequences"); } LevelAmbientSfx[AmbSfxCount++] = AmbientSfx[sequence]; } //---------------------------------------------------------------------------- // // PROC P_AmbientSound // // Called every tic by (P_tick):P_Ticker. // //---------------------------------------------------------------------------- void P_AmbientSound(void) { afxcmd_t cmd; int sound; boolean done; if (!AmbSfxCount) { // No ambient sound sequences on current level return; } if (--AmbSfxTics) { return; } done = false; do { cmd = *AmbSfxPtr++; switch (cmd) { case afxcmd_play: AmbSfxVolume = P_Random() >> 2; S_StartSoundAtVolume(NULL, *AmbSfxPtr++, AmbSfxVolume); break; case afxcmd_playabsvol: sound = *AmbSfxPtr++; AmbSfxVolume = *AmbSfxPtr++; S_StartSoundAtVolume(NULL, sound, AmbSfxVolume); break; case afxcmd_playrelvol: sound = *AmbSfxPtr++; AmbSfxVolume += *AmbSfxPtr++; if (AmbSfxVolume < 0) { AmbSfxVolume = 0; } else if (AmbSfxVolume > 127) { AmbSfxVolume = 127; } S_StartSoundAtVolume(NULL, sound, AmbSfxVolume); break; case afxcmd_delay: AmbSfxTics = *AmbSfxPtr++; done = true; break; case afxcmd_delayrand: AmbSfxTics = P_Random() & (*AmbSfxPtr++); done = true; break; case afxcmd_end: AmbSfxTics = 6 * TICRATE + P_Random(); AmbSfxPtr = LevelAmbientSfx[P_Random() % AmbSfxCount]; done = true; break; default: I_Error("P_AmbientSound: Unknown afxcmd %d", cmd); break; } } while (done == false); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_spec.h000066400000000000000000000222431360717211000220730ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_spec.h /* =============================================================================== P_SPEC =============================================================================== */ // // Animating textures and planes // typedef struct { boolean istexture; int picnum; int basepic; int numpics; int speed; } anim_t; // // source animation definition // typedef struct { int istexture; // if false, it's a flat char endname[9]; char startname[9]; int speed; } animdef_t; #define MAXANIMS 32 extern anim_t anims[MAXANIMS], *lastanim; extern int *TerrainTypes; // // Animating line specials // #define MAXLINEANIMS 64*256 extern short numlinespecials; extern line_t *linespeciallist[MAXLINEANIMS]; // Define values for map objects #define MO_TELEPORTMAN 14 // at game start void P_InitPicAnims(void); void P_InitTerrainTypes(void); void P_InitLava(void); // at map load void P_SpawnSpecials(void); void P_InitAmbientSound(void); void P_AddAmbientSfx(int sequence); // every tic void P_UpdateSpecials(void); void P_AmbientSound(void); // when needed boolean P_UseSpecialLine(mobj_t * thing, line_t * line); void P_ShootSpecialLine(mobj_t * thing, line_t * line); void P_CrossSpecialLine(int linenum, int side, mobj_t * thing); void P_PlayerInSpecialSector(player_t * player); int twoSided(int sector, int line); sector_t *getSector(int currentSector, int line, int side); side_t *getSide(int currentSector, int line, int side); fixed_t P_FindLowestFloorSurrounding(sector_t * sec); fixed_t P_FindHighestFloorSurrounding(sector_t * sec); fixed_t P_FindNextHighestFloor(sector_t * sec, int currentheight); fixed_t P_FindLowestCeilingSurrounding(sector_t * sec); fixed_t P_FindHighestCeilingSurrounding(sector_t * sec); int P_FindSectorFromLineTag(line_t * line, int start); int P_FindMinSurroundingLight(sector_t * sector, int max); sector_t *getNextSector(line_t * line, sector_t * sec); // // SPECIAL // int EV_DoDonut(line_t * line); /* =============================================================================== P_LIGHTS =============================================================================== */ typedef struct { thinker_t thinker; sector_t *sector; int count; int maxlight; int minlight; int maxtime; int mintime; } lightflash_t; typedef struct { thinker_t thinker; sector_t *sector; int count; int minlight; int maxlight; int darktime; int brighttime; } strobe_t; typedef struct { thinker_t thinker; sector_t *sector; int minlight; int maxlight; int direction; } glow_t; #define GLOWSPEED 8 #define STROBEBRIGHT 5 #define FASTDARK 15 #define SLOWDARK 35 void T_LightFlash(lightflash_t * flash); void P_SpawnLightFlash(sector_t * sector); void T_StrobeFlash(strobe_t * flash); void P_SpawnStrobeFlash(sector_t * sector, int fastOrSlow, int inSync); void EV_StartLightStrobing(line_t * line); void EV_TurnTagLightsOff(line_t * line); void EV_LightTurnOn(line_t * line, int bright); void T_Glow(glow_t * g); void P_SpawnGlowingLight(sector_t * sector); /* =============================================================================== P_SWITCH =============================================================================== */ typedef struct { char name1[9]; char name2[9]; short episode; } switchlist_t; typedef enum { top, middle, bottom } bwhere_e; typedef struct { line_t *line; bwhere_e where; int btexture; int btimer; void *soundorg; } button_t; #define MAXSWITCHES 50 // max # of wall switches in a level #define MAXBUTTONS 16 // 4 players, 4 buttons each at once, max. #define BUTTONTIME 35 // 1 second extern button_t buttonlist[MAXBUTTONS]; void P_ChangeSwitchTexture(line_t * line, int useAgain); void P_InitSwitchList(void); /* =============================================================================== P_PLATS =============================================================================== */ typedef enum { up, down, waiting, in_stasis } plat_e; typedef enum { perpetualRaise, downWaitUpStay, raiseAndChange, raiseToNearestAndChange } plattype_e; typedef struct { thinker_t thinker; sector_t *sector; fixed_t speed; fixed_t low; fixed_t high; int wait; int count; plat_e status; plat_e oldstatus; boolean crush; int tag; plattype_e type; } plat_t; #define PLATWAIT 3 #define PLATSPEED FRACUNIT #define MAXPLATS 30*256 extern plat_t *activeplats[MAXPLATS]; void T_PlatRaise(plat_t * plat); int EV_DoPlat(line_t * line, plattype_e type, int amount); void P_AddActivePlat(plat_t * plat); void P_RemoveActivePlat(plat_t * plat); void EV_StopPlat(line_t * line); void P_ActivateInStasis(int tag); /* =============================================================================== P_DOORS =============================================================================== */ typedef enum { vld_normal, vld_close30ThenOpen, vld_close, vld_open, vld_raiseIn5Mins } vldoor_e; typedef struct { thinker_t thinker; vldoor_e type; sector_t *sector; fixed_t topheight; fixed_t speed; int direction; // 1 = up, 0 = waiting at top, -1 = down int topwait; // tics to wait at the top // (keep in case a door going down is reset) int topcountdown; // when it reaches 0, start going down } vldoor_t; #define VDOORSPEED FRACUNIT*2 #define VDOORWAIT 150 void EV_VerticalDoor(line_t * line, mobj_t * thing); int EV_DoDoor(line_t * line, vldoor_e type, fixed_t speed); void T_VerticalDoor(vldoor_t * door); void P_SpawnDoorCloseIn30(sector_t * sec); void P_SpawnDoorRaiseIn5Mins(sector_t * sec, int secnum); /* =============================================================================== P_CEILNG =============================================================================== */ typedef enum { lowerToFloor, raiseToHighest, lowerAndCrush, crushAndRaise, fastCrushAndRaise } ceiling_e; typedef struct { thinker_t thinker; ceiling_e type; sector_t *sector; fixed_t bottomheight, topheight; fixed_t speed; boolean crush; int direction; // 1 = up, 0 = waiting, -1 = down int tag; // ID int olddirection; } ceiling_t; #define CEILSPEED FRACUNIT #define CEILWAIT 150 #define MAXCEILINGS 30 extern ceiling_t *activeceilings[MAXCEILINGS]; int EV_DoCeiling(line_t * line, ceiling_e type); void T_MoveCeiling(ceiling_t * ceiling); void P_AddActiveCeiling(ceiling_t * c); void P_RemoveActiveCeiling(ceiling_t * c); int EV_CeilingCrushStop(line_t * line); void P_ActivateInStasisCeiling(line_t * line); /* =============================================================================== P_FLOOR =============================================================================== */ typedef enum { lowerFloor, // lower floor to highest surrounding floor lowerFloorToLowest, // lower floor to lowest surrounding floor turboLower, // lower floor to highest surrounding floor VERY FAST raiseFloor, // raise floor to lowest surrounding CEILING raiseFloorToNearest, // raise floor to next highest surrounding floor raiseToTexture, // raise floor to shortest height texture around it lowerAndChange, // lower floor to lowest surrounding floor and change // floorpic raiseFloor24, raiseFloor24AndChange, raiseFloorCrush, donutRaise, raiseBuildStep // One step of a staircase } floor_e; typedef struct { thinker_t thinker; floor_e type; boolean crush; sector_t *sector; int direction; int newspecial; short texture; fixed_t floordestheight; fixed_t speed; } floormove_t; #define FLOORSPEED FRACUNIT typedef enum { ok, crushed, pastdest } result_e; result_e T_MovePlane(sector_t * sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction); int EV_BuildStairs(line_t * line, fixed_t stepDelta); int EV_DoFloor(line_t * line, floor_e floortype); void T_MoveFloor(floormove_t * floor); /* =============================================================================== P_TELEPT =============================================================================== */ boolean P_Teleport(mobj_t * thing, fixed_t x, fixed_t y, angle_t angle); boolean EV_Teleport(line_t * line, int side, mobj_t * thing); crispy-doom-crispy-doom-5.6.4/src/heretic/p_switch.c000066400000000000000000000325521360717211000224410ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "doomdef.h" #include "deh_str.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" //================================================================== // // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE // //================================================================== switchlist_t alphSwitchList[] = { {"SW1OFF", "SW1ON", 1}, {"SW2OFF", "SW2ON", 1}, /* {"SW1CTY", "SW2CTY", 1}, {"SW1ORGRY", "SW2ORGRY", 1}, {"SW1GRSTN", "SW2GRSTN", 1}, {"SW1SNDP", "SW2SNDP", 1}, {"SW1SPINE", "SW2SPINE", 1}, {"SW1SQPEB", "SW2SQPEB", 1}, {"SW1TRST1", "SW2TRST1", 1}, {"SW1CSTL", "SW2CSTL", 1}, {"SW1MOSS", "SW2MOSS", 1}, {"SW1SNDSQ", "SW2SNDSQ", 1}, {"SW1RED", "SW2RED", 1}, {"SW1WOOD", "SW2WOOD", 1}, {"SW1BROWN", "SW2BROWN", 1}, {"SW1TRST2", "SW2TRST2", 2}, {"SW1MSC", "SW2MSC", 2}, {"SW1MSC2", "SW2MSC2", 2}, {"SW1GRDMD", "SW2GRDMD", 2}, */ #if 0 {"SW1BRCOM", "SW2BRCOM", 1}, {"SW1BRN1", "SW2BRN1", 1}, {"SW1BRN2", "SW2BRN2", 1}, {"SW1BRNGN", "SW2BRNGN", 1}, {"SW1BROWN", "SW2BROWN", 1}, {"SW1COMM", "SW2COMM", 1}, {"SW1COMP", "SW2COMP", 1}, {"SW1DIRT", "SW2DIRT", 1}, {"SW1EXIT", "SW2EXIT", 1}, {"SW1GRAY", "SW2GRAY", 1}, {"SW1GRAY1", "SW2GRAY1", 1}, {"SW1METAL", "SW2METAL", 1}, {"SW1PIPE", "SW2PIPE", 1}, {"SW1SLAD", "SW2SLAD", 1}, {"SW1STARG", "SW2STARG", 1}, {"SW1STON1", "SW2STON1", 1}, {"SW1STON2", "SW2STON2", 1}, {"SW1STONE", "SW2STONE", 1}, {"SW1STRTN", "SW2STRTN", 1}, {"SW1BLUE", "SW2BLUE", 2}, {"SW1CMT", "SW2CMT", 2}, {"SW1GARG", "SW2GARG", 2}, {"SW1GSTON", "SW2GSTON", 2}, {"SW1HOT", "SW2HOT", 2}, {"SW1LION", "SW2LION", 2}, {"SW1SATYR", "SW2SATYR", 2}, {"SW1SKIN", "SW2SKIN", 2}, {"SW1VINE", "SW2VINE", 2}, {"SW1WOOD", "SW2WOOD", 2}, #endif }; int switchlist[MAXSWITCHES * 2]; int numswitches; button_t buttonlist[MAXBUTTONS]; /* =============== = = P_InitSwitchList = = Only called at game initialization = =============== */ void P_InitSwitchList(void) { int i, slindex, episode; // Note that this is called "episode" here but it's actually something // quite different. As we progress from Shareware->Registered->Doom II // we support more switch textures. if (gamemode == shareware) { episode = 1; } else { episode = 2; } slindex = 0; for (i = 0; i < arrlen(alphSwitchList); i++) { if (alphSwitchList[i].episode <= episode) { switchlist[slindex++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name1)); switchlist[slindex++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name2)); } } numswitches = slindex / 2; switchlist[slindex] = -1; } //================================================================== // // Start a button counting down till it turns off. // //================================================================== void P_StartButton(line_t * line, bwhere_e w, int texture, int time) { int i; for (i = 0; i < MAXBUTTONS; i++) if (!buttonlist[i].btimer) { buttonlist[i].line = line; buttonlist[i].where = w; buttonlist[i].btexture = texture; buttonlist[i].btimer = time; buttonlist[i].soundorg = &line->frontsector->soundorg; return; } I_Error("P_StartButton: no button slots left!"); } //================================================================== // // Function that changes wall texture. // Tell it if switch is ok to use again (1=yes, it's a button). // //================================================================== void P_ChangeSwitchTexture(line_t * line, int useAgain) { int texTop; int texMid; int texBot; int i; int sound; if (!useAgain) line->special = 0; texTop = sides[line->sidenum[0]].toptexture; texMid = sides[line->sidenum[0]].midtexture; texBot = sides[line->sidenum[0]].bottomtexture; sound = sfx_switch; //if (line->special == 11) // EXIT SWITCH? // sound = sfx_swtchx; for (i = 0; i < numswitches * 2; i++) if (switchlist[i] == texTop) { S_StartSound(buttonlist->soundorg, sound); sides[line->sidenum[0]].toptexture = switchlist[i ^ 1]; if (useAgain) P_StartButton(line, top, switchlist[i], BUTTONTIME); return; } else if (switchlist[i] == texMid) { S_StartSound(buttonlist->soundorg, sound); sides[line->sidenum[0]].midtexture = switchlist[i ^ 1]; if (useAgain) P_StartButton(line, middle, switchlist[i], BUTTONTIME); return; } else if (switchlist[i] == texBot) { S_StartSound(buttonlist->soundorg, sound); sides[line->sidenum[0]].bottomtexture = switchlist[i ^ 1]; if (useAgain) P_StartButton(line, bottom, switchlist[i], BUTTONTIME); return; } } /* ============================================================================== = = P_UseSpecialLine = = Called when a thing uses a special line = Only the front sides of lines are usable =============================================================================== */ boolean P_UseSpecialLine(mobj_t * thing, line_t * line) { // // Switches that other things can activate // if (!thing->player) { if (line->flags & ML_SECRET) return false; // never open secret doors switch (line->special) { case 1: // MANUAL DOOR RAISE case 32: // MANUAL BLUE case 33: // MANUAL RED case 34: // MANUAL YELLOW break; default: return false; } } // // do something // switch (line->special) { //=============================================== // MANUALS //=============================================== case 1: // Vertical Door case 26: // Blue Door/Locked case 27: // Yellow Door /Locked case 28: // Red Door /Locked case 31: // Manual door open case 32: // Blue locked door open case 33: // Red locked door open case 34: // Yellow locked door open EV_VerticalDoor(line, thing); break; //=============================================== // SWITCHES //=============================================== case 7: // Switch_Build_Stairs (8 pixel steps) if (EV_BuildStairs(line, 8 * FRACUNIT)) { P_ChangeSwitchTexture(line, 0); } break; case 107: // Switch_Build_Stairs_16 (16 pixel steps) if (EV_BuildStairs(line, 16 * FRACUNIT)) { P_ChangeSwitchTexture(line, 0); } break; case 9: // Change Donut if (EV_DoDonut(line)) P_ChangeSwitchTexture(line, 0); break; case 11: // Exit level G_ExitLevel(); P_ChangeSwitchTexture(line, 0); 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, vld_normal, VDOORSPEED)) 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: // Lower Ceiling And Crush if (EV_DoCeiling(line, lowerAndCrush)) P_ChangeSwitchTexture(line, 0); break; case 50: // Close Door if (EV_DoDoor(line, vld_close, VDOORSPEED)) P_ChangeSwitchTexture(line, 0); break; case 51: // Secret EXIT G_SecretExitLevel(); P_ChangeSwitchTexture(line, 0); 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, vld_open, VDOORSPEED)) P_ChangeSwitchTexture(line, 0); break; //=============================================== // BUTTONS //=============================================== case 42: // Close Door if (EV_DoDoor(line, vld_close, VDOORSPEED)) 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, vld_open, VDOORSPEED)) 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, vld_normal, VDOORSPEED)) 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; } return true; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_telept.c000066400000000000000000000113001360717211000224210ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_telept.c #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" //---------------------------------------------------------------------------- // // FUNC P_Teleport // //---------------------------------------------------------------------------- boolean P_Teleport(mobj_t * thing, fixed_t x, fixed_t y, angle_t angle) { fixed_t oldx; fixed_t oldy; fixed_t oldz; fixed_t aboveFloor; fixed_t fogDelta; player_t *player; unsigned an; mobj_t *fog; oldx = thing->x; oldy = thing->y; oldz = thing->z; aboveFloor = thing->z - thing->floorz; if (!P_TeleportMove(thing, x, y)) { return (false); } if (thing->player) { player = thing->player; if (player->powers[pw_flight] && aboveFloor) { thing->z = thing->floorz + aboveFloor; if (thing->z + thing->height > thing->ceilingz) { thing->z = thing->ceilingz - thing->height; } player->viewz = thing->z + player->viewheight; } else { thing->z = thing->floorz; player->viewz = thing->z + player->viewheight; player->lookdir = 0; } } else if (thing->flags & MF_MISSILE) { thing->z = thing->floorz + aboveFloor; if (thing->z + thing->height > thing->ceilingz) { thing->z = thing->ceilingz - thing->height; } } else { thing->z = thing->floorz; } // Spawn teleport fog at source and destination fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; fog = P_SpawnMobj(oldx, oldy, oldz + fogDelta, MT_TFOG); S_StartSound(fog, sfx_telept); an = angle >> ANGLETOFINESHIFT; fog = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an], thing->z + fogDelta, MT_TFOG); S_StartSound(fog, sfx_telept); if (thing->player && !thing->player->powers[pw_weaponlevel2]) { // Freeze player for about .5 sec thing->reactiontime = 18; } thing->angle = angle; if (thing->flags2 & MF2_FOOTCLIP && P_GetThingFloorType(thing) != FLOOR_SOLID) { thing->flags2 |= MF2_FEETARECLIPPED; } else if (thing->flags2 & MF2_FEETARECLIPPED) { thing->flags2 &= ~MF2_FEETARECLIPPED; } if (thing->flags & MF_MISSILE) { angle >>= ANGLETOFINESHIFT; thing->momx = FixedMul(thing->info->speed, finecosine[angle]); thing->momy = FixedMul(thing->info->speed, finesine[angle]); } else { thing->momx = thing->momy = thing->momz = 0; } return (true); } //---------------------------------------------------------------------------- // // FUNC EV_Teleport // //---------------------------------------------------------------------------- boolean EV_Teleport(line_t * line, int side, mobj_t * thing) { int i; int tag; mobj_t *m; thinker_t *thinker; sector_t *sector; if (thing->flags2 & MF2_NOTELEPORT) { return (false); } if (side == 1) { // Don't teleport when crossing back side return (false); } tag = line->tag; for (i = 0; i < numsectors; i++) { if (sectors[i].tag == tag) { thinker = thinkercap.next; for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { if (thinker->function != P_MobjThinker) { // Not a mobj continue; } m = (mobj_t *) thinker; if (m->type != MT_TELEPORTMAN) { // Not a teleportman continue; } sector = m->subsector->sector; if (sector - sectors != i) { // Wrong sector continue; } return (P_Teleport(thing, m->x, m->y, m->angle)); } } } return (false); } crispy-doom-crispy-doom-5.6.4/src/heretic/p_tick.c000066400000000000000000000065221360717211000220700ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_tick.c #include "doomdef.h" #include "i_system.h" #include "p_local.h" #include "v_video.h" int leveltime; int TimerGame; /* =============================================================================== 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. =============================================================================== */ thinker_t thinkercap; // both the head and tail of the thinker list /* =============== = = P_InitThinkers = =============== */ void P_InitThinkers(void) { thinkercap.prev = thinkercap.next = &thinkercap; } /* =============== = = P_AddThinker = = Adds a new thinker at the end of the list = =============== */ void P_AddThinker(thinker_t * thinker) { thinkercap.prev->next = thinker; thinker->next = &thinkercap; thinker->prev = thinkercap.prev; thinkercap.prev = thinker; } /* =============== = = P_RemoveThinker = = Deallocation is lazy -- it will not actually be freed until its = thinking turn comes up = =============== */ void P_RemoveThinker(thinker_t * thinker) { thinker->function = (think_t) - 1; } /* =============== = = P_AllocateThinker = = Allocates memory and adds a new thinker at the end of the list = =============== */ void P_AllocateThinker(thinker_t * thinker) { } /* =============== = = P_RunThinkers = =============== */ void P_RunThinkers(void) { thinker_t *currentthinker, *nextthinker; currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { if (currentthinker->function == (think_t) - 1) { // time to remove it nextthinker = currentthinker->next; currentthinker->next->prev = currentthinker->prev; currentthinker->prev->next = currentthinker->next; Z_Free(currentthinker); } else { if (currentthinker->function) currentthinker->function(currentthinker); nextthinker = currentthinker->next; } currentthinker = nextthinker; } } //---------------------------------------------------------------------------- // // PROC P_Ticker // //---------------------------------------------------------------------------- void P_Ticker(void) { int i; if (paused) { return; } for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { P_PlayerThink(&players[i]); } } if (TimerGame) { if (!--TimerGame) { G_ExitLevel(); } } P_RunThinkers(); P_UpdateSpecials(); P_AmbientSound(); leveltime++; } crispy-doom-crispy-doom-5.6.4/src/heretic/p_user.c000066400000000000000000000667021360717211000221220ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // P_user.c #include #include "doomdef.h" #include "deh_str.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" void P_PlayerNextArtifact(player_t * player); // Macros #define MAXBOB 0x100000 // 16 pixels of bob // Data boolean onground; int newtorch; // used in the torch flicker effect. int newtorchdelta; boolean WeaponInShareware[] = { true, // Staff true, // Gold wand true, // Crossbow true, // Blaster false, // Skull rod false, // Phoenix rod false, // Mace true, // Gauntlets true // Beak }; /* ================== = = P_Thrust = = moves the given origin along a given angle = ================== */ void P_Thrust(player_t * player, angle_t angle, fixed_t move) { angle >>= ANGLETOFINESHIFT; if (player->powers[pw_flight] && !(player->mo->z <= player->mo->floorz)) { player->mo->momx += FixedMul(move, finecosine[angle]); player->mo->momy += FixedMul(move, finesine[angle]); } else if (player->mo->subsector->sector->special == 15) // Friction_Low { player->mo->momx += FixedMul(move >> 2, finecosine[angle]); player->mo->momy += FixedMul(move >> 2, finesine[angle]); } else { player->mo->momx += FixedMul(move, finecosine[angle]); player->mo->momy += FixedMul(move, finesine[angle]); } } /* ================== = = P_CalcHeight = =Calculate the walking / running height adjustment = ================== */ void P_CalcHeight(player_t * player) { int angle; fixed_t bob; // // regular movement bobbing (needs to be calculated for gun swing even // if not on ground) // OPTIMIZE: tablify angle player->bob = FixedMul(player->mo->momx, player->mo->momx) + FixedMul(player->mo->momy, player->mo->momy); player->bob >>= 2; if (player->bob > MAXBOB) player->bob = MAXBOB; if (player->mo->flags2 & MF2_FLY && !onground) { player->bob = FRACUNIT / 2; } if ((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; 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; } } if (player->chickenTics) { player->viewz = player->mo->z + player->viewheight - (20 * FRACUNIT); } else { player->viewz = player->mo->z + player->viewheight + bob; } if (player->mo->flags2 & MF2_FEETARECLIPPED && player->playerstate != PST_DEAD && player->mo->z <= player->mo->floorz) { player->viewz -= FOOTCLIPSIZE; } if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) { player->viewz = player->mo->ceilingz - 4 * FRACUNIT; } if (player->viewz < player->mo->floorz + 4 * FRACUNIT) { player->viewz = player->mo->floorz + 4 * FRACUNIT; } } /* ================= = = P_MovePlayer = ================= */ void P_MovePlayer(player_t * player) { int look; int fly; ticcmd_t *cmd; cmd = &player->cmd; player->mo->angle += (cmd->angleturn << 16); onground = (player->mo->z <= player->mo->floorz || (player->mo->flags2 & MF2_ONMOBJ)); if (player->chickenTics) { // Chicken speed if (cmd->forwardmove && (onground || player->mo->flags2 & MF2_FLY)) P_Thrust(player, player->mo->angle, cmd->forwardmove * 2500); if (cmd->sidemove && (onground || player->mo->flags2 & MF2_FLY)) P_Thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2500); } else { // Normal speed if (cmd->forwardmove && (onground || player->mo->flags2 & MF2_FLY)) P_Thrust(player, player->mo->angle, cmd->forwardmove * 2048); if (cmd->sidemove && (onground || player->mo->flags2 & MF2_FLY)) P_Thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2048); } if (cmd->forwardmove || cmd->sidemove) { if (player->chickenTics) { if (player->mo->state == &states[S_CHICPLAY]) { P_SetMobjState(player->mo, S_CHICPLAY_RUN1); } } else { if (player->mo->state == &states[S_PLAY]) { P_SetMobjState(player->mo, S_PLAY_RUN1); } } } look = cmd->lookfly & 15; if (look > 7) { look -= 16; } if (look) { if (look == TOCENTER) { player->centering = true; } else { player->lookdir += 5 * look; if (player->lookdir > 90 || player->lookdir < -110) { player->lookdir -= 5 * look; } } } if (player->centering) { if (player->lookdir > 0) { player->lookdir -= 8; } else if (player->lookdir < 0) { player->lookdir += 8; } if (abs(player->lookdir) < 8) { player->lookdir = 0; player->centering = false; } } fly = cmd->lookfly >> 4; if (fly > 7) { fly -= 16; } if (fly && player->powers[pw_flight]) { if (fly != TOCENTER) { player->flyheight = fly * 2; if (!(player->mo->flags2 & MF2_FLY)) { player->mo->flags2 |= MF2_FLY; player->mo->flags |= MF_NOGRAVITY; } } else { player->mo->flags2 &= ~MF2_FLY; player->mo->flags &= ~MF_NOGRAVITY; } } else if (fly > 0) { P_PlayerUseArtifact(player, arti_fly); } if (player->mo->flags2 & MF2_FLY) { player->mo->momz = player->flyheight * FRACUNIT; if (player->flyheight) { player->flyheight /= 2; } } } /* ================= = = P_DeathThink = ================= */ #define ANG5 (ANG90/18) extern int inv_ptr; extern int curpos; void P_DeathThink(player_t * player) { angle_t angle, delta; int lookDelta; P_MovePsprites(player); onground = (player->mo->z <= player->mo->floorz); if (player->mo->type == MT_BLOODYSKULL) { // Flying bloody skull player->viewheight = 6 * FRACUNIT; player->deltaviewheight = 0; //player->damagecount = 20; if (onground) { if (player->lookdir < 60) { lookDelta = (60 - player->lookdir) / 8; if (lookDelta < 1 && (leveltime & 1)) { lookDelta = 1; } else if (lookDelta > 6) { lookDelta = 6; } player->lookdir += lookDelta; } } } else { // Fall to ground player->deltaviewheight = 0; if (player->viewheight > 6 * FRACUNIT) player->viewheight -= FRACUNIT; if (player->viewheight < 6 * FRACUNIT) player->viewheight = 6 * FRACUNIT; if (player->lookdir > 0) { player->lookdir -= 6; } else if (player->lookdir < 0) { player->lookdir += 6; } if (abs(player->lookdir) < 6) { player->lookdir = 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) { if (player == &players[consoleplayer]) { I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE)); inv_ptr = 0; curpos = 0; newtorch = 0; newtorchdelta = 0; } player->playerstate = PST_REBORN; // Let the mobj know the player has entered the reborn state. Some // mobjs need to know when it's ok to remove themselves. player->mo->special2.i = 666; } } //---------------------------------------------------------------------------- // // PROC P_ChickenPlayerThink // //---------------------------------------------------------------------------- void P_ChickenPlayerThink(player_t * player) { mobj_t *pmo; if (player->health > 0) { // Handle beak movement P_UpdateBeak(player, &player->psprites[ps_weapon]); } if (player->chickenTics & 15) { return; } pmo = player->mo; if (!(pmo->momx + pmo->momy) && P_Random() < 160) { // Twitch view angle pmo->angle += P_SubRandom() << 19; } if ((pmo->z <= pmo->floorz) && (P_Random() < 32)) { // Jump and noise pmo->momz += FRACUNIT; P_SetMobjState(pmo, S_CHICPLAY_PAIN); return; } if (P_Random() < 48) { // Just noise S_StartSound(pmo, sfx_chicact); } } //---------------------------------------------------------------------------- // // FUNC P_GetPlayerNum // //---------------------------------------------------------------------------- int P_GetPlayerNum(player_t * player) { int i; for (i = 0; i < MAXPLAYERS; i++) { if (player == &players[i]) { return (i); } } return (0); } //---------------------------------------------------------------------------- // // FUNC P_UndoPlayerChicken // //---------------------------------------------------------------------------- boolean P_UndoPlayerChicken(player_t * player) { mobj_t *fog; mobj_t *mo; mobj_t *pmo; fixed_t x; fixed_t y; fixed_t z; angle_t angle; int playerNum; weapontype_t weapon; int oldFlags; int oldFlags2; pmo = player->mo; x = pmo->x; y = pmo->y; z = pmo->z; angle = pmo->angle; weapon = pmo->special1.i; oldFlags = pmo->flags; oldFlags2 = pmo->flags2; P_SetMobjState(pmo, S_FREETARGMOBJ); mo = P_SpawnMobj(x, y, z, MT_PLAYER); if (P_TestMobjLocation(mo) == false) { // Didn't fit P_RemoveMobj(mo); mo = P_SpawnMobj(x, y, z, MT_CHICPLAYER); mo->angle = angle; mo->health = player->health; mo->special1.i = weapon; mo->player = player; mo->flags = oldFlags; mo->flags2 = oldFlags2; player->mo = mo; player->chickenTics = 2 * 35; return (false); } playerNum = P_GetPlayerNum(player); if (playerNum != 0) { // Set color translation mo->flags |= playerNum << MF_TRANSSHIFT; } mo->angle = angle; mo->player = player; mo->reactiontime = 18; if (oldFlags2 & MF2_FLY) { mo->flags2 |= MF2_FLY; mo->flags |= MF_NOGRAVITY; } player->chickenTics = 0; player->powers[pw_weaponlevel2] = 0; player->health = mo->health = MAXHEALTH; player->mo = mo; angle >>= ANGLETOFINESHIFT; fog = P_SpawnMobj(x + 20 * finecosine[angle], y + 20 * finesine[angle], z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, sfx_telept); P_PostChickenWeapon(player, weapon); return (true); } //---------------------------------------------------------------------------- // // PROC P_PlayerThink // //---------------------------------------------------------------------------- void P_PlayerThink(player_t * player) { ticcmd_t *cmd; weapontype_t newweapon; // No-clip cheat if (player->cheats & CF_NOCLIP) { player->mo->flags |= MF_NOCLIP; } else { player->mo->flags &= ~MF_NOCLIP; } cmd = &player->cmd; if (player->mo->flags & MF_JUSTATTACKED) { // Gauntlets attack auto forward motion cmd->angleturn = 0; cmd->forwardmove = 0xc800 / 512; cmd->sidemove = 0; player->mo->flags &= ~MF_JUSTATTACKED; } // messageTics is above the rest of the counters so that messages will // go away, even in death. player->messageTics--; // Can go negative if (!player->messageTics) { // Refresh the screen when a message goes away ultimatemsg = false; // clear out any chat messages. BorderTopRefresh = true; } if (player->playerstate == PST_DEAD) { P_DeathThink(player); return; } if (player->chickenTics) { P_ChickenPlayerThink(player); } // Handle movement if (player->mo->reactiontime) { // Player is frozen player->mo->reactiontime--; } else { P_MovePlayer(player); } P_CalcHeight(player); if (player->mo->subsector->sector->special) { P_PlayerInSpecialSector(player); } if (cmd->arti) { // Use an artifact if (cmd->arti == 0xff) { P_PlayerNextArtifact(player); } else { P_PlayerUseArtifact(player, cmd->arti); } } // Check for weapon change if (cmd->buttons & BT_SPECIAL) { // A special event has no other buttons cmd->buttons = 0; } if (cmd->buttons & BT_CHANGE) { // The actual changing of the weapon is done when the weapon // psprite can do it (A_WeaponReady), so it doesn't happen in // the middle of an attack. newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT; if (newweapon == wp_staff && player->weaponowned[wp_gauntlets] && !(player->readyweapon == wp_gauntlets)) { newweapon = wp_gauntlets; } if (player->weaponowned[newweapon] && newweapon != player->readyweapon) { if (WeaponInShareware[newweapon] || 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; } // Chicken counter if (player->chickenTics) { if (player->chickenPeck) { // Chicken attack counter player->chickenPeck -= 3; } if (!--player->chickenTics) { // Attempt to undo the chicken P_UndoPlayerChicken(player); } } // Cycle psprites P_MovePsprites(player); // Other Counters if (player->powers[pw_invulnerability]) { player->powers[pw_invulnerability]--; } if (player->powers[pw_invisibility]) { if (!--player->powers[pw_invisibility]) { player->mo->flags &= ~MF_SHADOW; } } if (player->powers[pw_infrared]) { player->powers[pw_infrared]--; } if (player->powers[pw_flight]) { if (!--player->powers[pw_flight]) { // haleyjd: removed externdriver crap if (player->mo->z != player->mo->floorz) { player->centering = true; } player->mo->flags2 &= ~MF2_FLY; player->mo->flags &= ~MF_NOGRAVITY; BorderTopRefresh = true; //make sure the sprite's cleared out } } if (player->powers[pw_weaponlevel2]) { if (!--player->powers[pw_weaponlevel2]) { if ((player->readyweapon == wp_phoenixrod) && (player->psprites[ps_weapon].state != &states[S_PHOENIXREADY]) && (player->psprites[ps_weapon].state != &states[S_PHOENIXUP])) { P_SetPsprite(player, ps_weapon, S_PHOENIXREADY); player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_2; player->refire = 0; } else if ((player->readyweapon == wp_gauntlets) || (player->readyweapon == wp_staff)) { player->pendingweapon = player->readyweapon; } BorderTopRefresh = true; } } if (player->damagecount) { player->damagecount--; } if (player->bonuscount) { player->bonuscount--; } // Colormaps if (player->powers[pw_invulnerability]) { if (player->powers[pw_invulnerability] > BLINKTHRESHOLD || (player->powers[pw_invulnerability] & 8)) { player->fixedcolormap = INVERSECOLORMAP; } else { player->fixedcolormap = 0; } } else if (player->powers[pw_infrared]) { if (player->powers[pw_infrared] <= BLINKTHRESHOLD) { if (player->powers[pw_infrared] & 8) { player->fixedcolormap = 0; } else { player->fixedcolormap = 1; } } else if (!(leveltime & 16) && player == &players[consoleplayer]) { if (newtorch) { if (player->fixedcolormap + newtorchdelta > 7 || player->fixedcolormap + newtorchdelta < 1 || newtorch == player->fixedcolormap) { newtorch = 0; } else { player->fixedcolormap += newtorchdelta; } } else { newtorch = (M_Random() & 7) + 1; newtorchdelta = (newtorch == player->fixedcolormap) ? 0 : ((newtorch > player->fixedcolormap) ? 1 : -1); } } } else { player->fixedcolormap = 0; } } //---------------------------------------------------------------------------- // // PROC P_ArtiTele // //---------------------------------------------------------------------------- void P_ArtiTele(player_t * player) { int i; int selections; fixed_t destX; fixed_t destY; angle_t destAngle; if (deathmatch) { selections = deathmatch_p - deathmatchstarts; i = P_Random() % selections; destX = deathmatchstarts[i].x << FRACBITS; destY = deathmatchstarts[i].y << FRACBITS; destAngle = ANG45 * (deathmatchstarts[i].angle / 45); } else { destX = playerstarts[0].x << FRACBITS; destY = playerstarts[0].y << FRACBITS; destAngle = ANG45 * (playerstarts[0].angle / 45); } P_Teleport(player->mo, destX, destY, destAngle); S_StartSound(NULL, sfx_wpnup); // Full volume laugh } //---------------------------------------------------------------------------- // // PROC P_PlayerNextArtifact // //---------------------------------------------------------------------------- void P_PlayerNextArtifact(player_t * player) { if (player == &players[consoleplayer]) { inv_ptr--; if (inv_ptr < 6) { curpos--; if (curpos < 0) { curpos = 0; } } if (inv_ptr < 0) { inv_ptr = player->inventorySlotNum - 1; if (inv_ptr < 6) { curpos = inv_ptr; } else { curpos = 6; } } player->readyArtifact = player->inventory[inv_ptr].type; } } //---------------------------------------------------------------------------- // // PROC P_PlayerRemoveArtifact // //---------------------------------------------------------------------------- void P_PlayerRemoveArtifact(player_t * player, int slot) { int i; player->artifactCount--; if (!(--player->inventory[slot].count)) { // Used last of a type - compact the artifact list player->readyArtifact = arti_none; player->inventory[slot].type = arti_none; for (i = slot + 1; i < player->inventorySlotNum; i++) { player->inventory[i - 1] = player->inventory[i]; } player->inventorySlotNum--; if (player == &players[consoleplayer]) { // Set position markers and get next readyArtifact inv_ptr--; if (inv_ptr < 6) { curpos--; if (curpos < 0) { curpos = 0; } } if (inv_ptr >= player->inventorySlotNum) { inv_ptr = player->inventorySlotNum - 1; } if (inv_ptr < 0) { inv_ptr = 0; } player->readyArtifact = player->inventory[inv_ptr].type; } } } //---------------------------------------------------------------------------- // // PROC P_PlayerUseArtifact // //---------------------------------------------------------------------------- void P_PlayerUseArtifact(player_t * player, artitype_t arti) { int i; for (i = 0; i < player->inventorySlotNum; i++) { if (player->inventory[i].type == arti) { // Found match - try to use if (P_UseArtifact(player, arti)) { // Artifact was used - remove it from inventory P_PlayerRemoveArtifact(player, i); if (player == &players[consoleplayer]) { S_StartSound(NULL, sfx_artiuse); ArtifactFlash = 4; } } else { // Unable to use artifact, advance pointer P_PlayerNextArtifact(player); } break; } } } //---------------------------------------------------------------------------- // // FUNC P_UseArtifact // // Returns true if artifact was used. // //---------------------------------------------------------------------------- boolean P_UseArtifact(player_t * player, artitype_t arti) { mobj_t *mo; angle_t angle; switch (arti) { case arti_invulnerability: if (!P_GivePower(player, pw_invulnerability)) { return (false); } break; case arti_invisibility: if (!P_GivePower(player, pw_invisibility)) { return (false); } break; case arti_health: if (!P_GiveBody(player, 25)) { return (false); } break; case arti_superhealth: if (!P_GiveBody(player, 100)) { return (false); } break; case arti_tomeofpower: if (player->chickenTics) { // Attempt to undo chicken if (P_UndoPlayerChicken(player) == false) { // Failed P_DamageMobj(player->mo, NULL, NULL, 10000); } else { // Succeeded player->chickenTics = 0; S_StartSound(player->mo, sfx_wpnup); } } else { if (!P_GivePower(player, pw_weaponlevel2)) { return (false); } if (player->readyweapon == wp_staff) { P_SetPsprite(player, ps_weapon, S_STAFFREADY2_1); } else if (player->readyweapon == wp_gauntlets) { P_SetPsprite(player, ps_weapon, S_GAUNTLETREADY2_1); } } break; case arti_torch: if (!P_GivePower(player, pw_infrared)) { return (false); } break; case arti_firebomb: angle = player->mo->angle >> ANGLETOFINESHIFT; // Vanilla bug here: // Original code here looks like: // (player->mo->flags2 & MF2_FEETARECLIPPED != 0), // Which under C's operator precedence is: // (player->mo->flags2 & (MF2_FEETARECLIPPED != 0)), // Which simplifies to: // (player->mo->flags2 & 1), mo = P_SpawnMobj(player->mo->x + 24 * finecosine[angle], player->mo->y + 24 * finesine[angle], player->mo->z - 15 * FRACUNIT * (player->mo->flags2 & 1), MT_FIREBOMB); mo->target = player->mo; break; case arti_egg: mo = player->mo; P_SpawnPlayerMissile(mo, MT_EGGFX); P_SPMAngle(mo, MT_EGGFX, mo->angle - (ANG45 / 6)); P_SPMAngle(mo, MT_EGGFX, mo->angle + (ANG45 / 6)); P_SPMAngle(mo, MT_EGGFX, mo->angle - (ANG45 / 3)); P_SPMAngle(mo, MT_EGGFX, mo->angle + (ANG45 / 3)); break; case arti_fly: if (!P_GivePower(player, pw_flight)) { return (false); } break; case arti_teleport: P_ArtiTele(player); break; default: return (false); } return (true); } crispy-doom-crispy-doom-5.6.4/src/heretic/r_bsp.c000066400000000000000000000303421360717211000217210ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // R_bsp.c #include "doomdef.h" #include "i_system.h" #include "m_bbox.h" #include "i_system.h" #include "r_local.h" seg_t *curline; side_t *sidedef; line_t *linedef; sector_t *frontsector, *backsector; drawseg_t *drawsegs = NULL, *ds_p; int numdrawsegs = 0; void R_StoreWallRange(int start, int stop); /* ==================== = = R_ClearDrawSegs = ==================== */ void R_ClearDrawSegs(void) { ds_p = drawsegs; } //============================================================================= /* =============================================================================== = = ClipWallSegment = = Clips the given range of columns and includes it in the new clip list =============================================================================== */ typedef struct { int first, last; } cliprange_t; // We must expand MAXSEGS to the theoretical limit of the number of solidsegs // that can be generated in a scene by the DOOM engine. This was determined by // Lee Killough during BOOM development to be a function of the screensize. // The simplest thing we can do, other than fix this bug, is to let the game // render overage and then bomb out by detecting the overflow after the // fact. -haleyjd //#define MAXSEGS 32 #define MAXSEGS (MAXWIDTH / 2 + 1) cliprange_t solidsegs[MAXSEGS], *newend; // newend is one past the last valid seg void R_ClipSolidWallSegment(int first, int last) { cliprange_t *next, *start; // find the first range that touches the range (adjacent pixels are touching) start = solidsegs; while (start->last < first - 1) start++; if (first < start->first) { if (last < start->first - 1) { // post is entirely visible (above start), so insert a new clippost R_StoreWallRange(first, last); next = newend; newend++; while (next != start) { *next = *(next - 1); next--; } next->first = first; next->last = last; return; } // there is a fragment above *start R_StoreWallRange(first, start->first - 1); start->first = first; // adjust the clip size } if (last <= start->last) return; // bottom contained in start next = start; while (last >= (next + 1)->first - 1) { // there is a fragment between two posts R_StoreWallRange(next->last + 1, (next + 1)->first - 1); next++; if (last <= next->last) { // bottom is contained in next start->last = next->last; // adjust the clip size goto crunch; } } // there is a fragment after *next R_StoreWallRange(next->last + 1, last); start->last = last; // adjust the clip size // remove start+1 to next from the clip list, // because start now covers their area crunch: if (next == start) return; // post just extended past the bottom of one post while (next++ != newend) // remove a post *++start = *next; newend = start + 1; } /* =============================================================================== = = R_ClipPassWallSegment = = Clips the given range of columns, but does not includes it in the clip list =============================================================================== */ void R_ClipPassWallSegment(int first, int last) { cliprange_t *start; // find the first range that touches the range (adjacent pixels are touching) start = solidsegs; while (start->last < first - 1) start++; if (first < start->first) { if (last < start->first - 1) { // post is entirely visible (above start) R_StoreWallRange(first, last); return; } // there is a fragment above *start R_StoreWallRange(first, start->first - 1); } if (last <= start->last) return; // bottom contained in start while (last >= (start + 1)->first - 1) { // there is a fragment between two posts R_StoreWallRange(start->last + 1, (start + 1)->first - 1); start++; if (last <= start->last) return; } // there is a fragment after *next R_StoreWallRange(start->last + 1, last); } /* ==================== = = R_ClearClipSegs = ==================== */ void R_ClearClipSegs(void) { solidsegs[0].first = -0x7fffffff; solidsegs[0].last = -1; solidsegs[1].first = viewwidth; solidsegs[1].last = 0x7fffffff; newend = solidsegs + 2; } //============================================================================= /* ====================== = = R_AddLine = = Clips the given segment and adds any visible pieces to the line list = ====================== */ void R_AddLine(seg_t * line) { int x1, x2; angle_t angle1, angle2, span, tspan; curline = line; // OPTIMIZE: quickly reject orthogonal back sides // [crispy] remove slime trails angle1 = R_PointToAngle(line->v1->r_x, line->v1->r_y); angle2 = R_PointToAngle(line->v2->r_x, line->v2->r_y); // // clip to view edges // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW) span = angle1 - angle2; if (span >= ANG180) return; // back side rw_angle1 = angle1; // global angle needed by segcalc angle1 -= viewangle; angle2 -= viewangle; tspan = angle1 + clipangle; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return; // totally off the left edge angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return; // totally off the left edge angle2 = -clipangle; } // // the seg is in the view range, but not necessarily visible // angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; x1 = viewangletox[angle1]; x2 = viewangletox[angle2]; if (x1 == x2) return; // does not cross a pixel backsector = line->backsector; if (!backsector) goto clipsolid; // single sided line if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) goto clipsolid; // closed door if (backsector->ceilingheight != frontsector->ceilingheight || backsector->floorheight != frontsector->floorheight) goto clippass; // window // reject empty lines used for triggers and special events if (backsector->ceilingpic == frontsector->ceilingpic && backsector->floorpic == frontsector->floorpic && backsector->lightlevel == frontsector->lightlevel && curline->sidedef->midtexture == 0) return; clippass: R_ClipPassWallSegment(x1, x2 - 1); return; clipsolid: R_ClipSolidWallSegment(x1, x2 - 1); } //============================================================================ /* =============================================================================== = = R_CheckBBox = = Returns true if some part of the bbox might be visible = =============================================================================== */ int checkcoord[12][4] = { {3, 0, 2, 1}, {3, 0, 2, 0}, {3, 1, 2, 0}, {0}, {2, 0, 2, 1}, {0, 0, 0, 0}, {3, 1, 3, 0}, {0}, {2, 0, 3, 1}, {2, 1, 3, 1}, {2, 1, 3, 0} }; boolean R_CheckBBox(fixed_t * bspcoord) { int boxx, boxy, boxpos; fixed_t x1, y1, x2, y2; angle_t angle1, angle2, span, tspan; cliprange_t *start; int sx1, sx2; // find the corners of the box that define the edges from current viewpoint if (viewx <= bspcoord[BOXLEFT]) boxx = 0; else if (viewx < bspcoord[BOXRIGHT]) boxx = 1; else boxx = 2; if (viewy >= bspcoord[BOXTOP]) boxy = 0; else if (viewy > bspcoord[BOXBOTTOM]) boxy = 1; else boxy = 2; boxpos = (boxy << 2) + boxx; if (boxpos == 5) return true; x1 = bspcoord[checkcoord[boxpos][0]]; y1 = bspcoord[checkcoord[boxpos][1]]; x2 = bspcoord[checkcoord[boxpos][2]]; y2 = bspcoord[checkcoord[boxpos][3]]; // // check clip list for an open space // angle1 = R_PointToAngle(x1, y1) - viewangle; angle2 = R_PointToAngle(x2, y2) - viewangle; span = angle1 - angle2; if (span >= ANG180) return true; // sitting on a line tspan = angle1 + clipangle; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return false; // totally off the left edge angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return false; // totally off the left edge angle2 = -clipangle; } // find the first clippost that touches the source post (adjacent pixels are touching) angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; sx1 = viewangletox[angle1]; sx2 = viewangletox[angle2]; if (sx1 == sx2) return false; // does not cross a pixel sx2--; start = solidsegs; while (start->last < sx2) start++; if (sx1 >= start->first && sx2 <= start->last) return false; // the clippost contains the new span return true; } /* ================ = = R_Subsector = = Draw one or more segments ================ */ void R_Subsector(int num) { int count; seg_t *line; subsector_t *sub; #ifdef RANGECHECK if (num >= numsubsectors) I_Error("R_Subsector: ss %i with numss = %i", num, numsubsectors); #endif sscount++; sub = &subsectors[num]; frontsector = sub->sector; count = sub->numlines; line = &segs[sub->firstline]; if (frontsector->floorheight < viewz) floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, frontsector->lightlevel, frontsector->special); else floorplane = NULL; if (frontsector->ceilingheight > viewz || frontsector->ceilingpic == skyflatnum) ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, frontsector->lightlevel, 0); else ceilingplane = NULL; R_AddSprites(frontsector); while (count--) { R_AddLine(line); line++; } // check for solidsegs overflow - extremely unsatisfactory! if(newend > &solidsegs[32] && false) I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n"); } /* =============================================================================== = = RenderBSPNode = =============================================================================== */ void R_RenderBSPNode(int bspnum) { node_t *bsp; int side; if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) R_Subsector(0); else R_Subsector(bspnum & (~NF_SUBSECTOR)); return; } bsp = &nodes[bspnum]; // // decide which side the view point is on // side = R_PointOnSide(viewx, viewy, bsp); R_RenderBSPNode(bsp->children[side]); // recursively divide front space if (R_CheckBBox(bsp->bbox[side ^ 1])) // possibly divide back space R_RenderBSPNode(bsp->children[side ^ 1]); } crispy-doom-crispy-doom-5.6.4/src/heretic/r_data.c000066400000000000000000000436321360717211000220540ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // R_data.c #include "doomdef.h" #include "deh_str.h" #include "i_swap.h" #include "i_system.h" #include "m_misc.h" #include "r_local.h" #include "p_local.h" extern void CheckAbortStartup(void); typedef struct { int originx; // block origin (allways UL), which has allready int originy; // accounted for the patch's internal origin int patch; } texpatch_t; // a maptexturedef_t describes a rectangular texture, which is composed of one // or more mappatch_t structures that arrange graphic patches typedef struct { char name[8]; // for switch changing, etc short width; short height; short patchcount; texpatch_t patches[1]; // [patchcount] drawn back to front // into the cached texture } texture_t; int firstflat, lastflat, numflats; int firstpatch, lastpatch, numpatches; int firstspritelump, lastspritelump, numspritelumps; int numtextures; texture_t **textures; int *texturewidthmask; fixed_t *textureheight; // needed for texture pegging int *texturecompositesize; short **texturecolumnlump; unsigned short **texturecolumnofs; byte **texturecomposite; int *flattranslation; // for global animation int *texturetranslation; // for global animation fixed_t *spritewidth; // needed for pre rendering fixed_t *spriteoffset; fixed_t *spritetopoffset; lighttable_t *colormaps; /* ============================================================================== MAPTEXTURE_T CACHING when a texture is first needed, it counts the number of composite columns required in the texture and allocates space for a column directory and any new columns. The directory will simply point inside other patches if there is only one patch in a given column, but any columns with multiple patches will have new column_ts generated. ============================================================================== */ /* =================== = = R_DrawColumnInCache = = Clip and draw a column from a patch into a cached post = =================== */ void R_DrawColumnInCache(column_t * patch, byte * cache, int originy, int cacheheight) { int count, position; byte *source; while (patch->topdelta != 0xff) { source = (byte *) patch + 3; count = patch->length; position = originy + patch->topdelta; if (position < 0) { count += position; position = 0; } if (position + count > cacheheight) count = cacheheight - position; if (count > 0) memcpy(cache + position, source, count); patch = (column_t *) ((byte *) patch + patch->length + 4); } } /* =================== = = R_GenerateComposite = =================== */ void R_GenerateComposite(int texnum) { byte *block; texture_t *texture; texpatch_t *patch; patch_t *realpatch; int x, x1, x2; int i; column_t *patchcol; short *collump; unsigned short *colofs; texture = textures[texnum]; block = Z_Malloc(texturecompositesize[texnum], PU_STATIC, &texturecomposite[texnum]); collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // // composite the columns together // patch = texture->patches; for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for (; x < x2; x++) { if (collump[x] >= 0) continue; // column does not have multiple patches patchcol = (column_t *) ((byte *) realpatch + LONG(realpatch->columnofs[x - x1])); R_DrawColumnInCache(patchcol, block + colofs[x], patch->originy, texture->height); } } // now that the texture has been built, it is purgable Z_ChangeTag(block, PU_CACHE); } /* =================== = = R_GenerateLookup = =================== */ void R_GenerateLookup(int texnum) { texture_t *texture; byte *patchcount; // [texture->width] texpatch_t *patch; patch_t *realpatch; int x, x1, x2; int i; short *collump; unsigned short *colofs; texture = textures[texnum]; texturecomposite[texnum] = 0; // composited not created yet texturecompositesize[texnum] = 0; collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // // count the number of columns that are covered by more than one patch // fill in the lump / offset, so columns with only a single patch are // all done // patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount); memset(patchcount, 0, texture->width); patch = texture->patches; for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for (; x < x2; x++) { patchcount[x]++; collump[x] = patch->patch; colofs[x] = LONG(realpatch->columnofs[x - x1]) + 3; } } for (x = 0; x < texture->width; x++) { if (!patchcount[x]) { printf("R_GenerateLookup: column without a patch (%s)\n", texture->name); return; } // I_Error ("R_GenerateLookup: column without a patch"); if (patchcount[x] > 1) { collump[x] = -1; // use the cached block colofs[x] = texturecompositesize[texnum]; if (texturecompositesize[texnum] > 0x10000 - texture->height) I_Error("R_GenerateLookup: texture %i is >64k", texnum); texturecompositesize[texnum] += texture->height; } } Z_Free(patchcount); } /* ================ = = R_GetColumn = ================ */ byte *R_GetColumn(int tex, int col) { int lump, ofs; col &= texturewidthmask[tex]; lump = texturecolumnlump[tex][col]; ofs = texturecolumnofs[tex][col]; if (lump > 0) return (byte *) W_CacheLumpNum(lump, PU_CACHE) + ofs; if (!texturecomposite[tex]) R_GenerateComposite(tex); return texturecomposite[tex] + ofs; } /* ================== = = R_InitTextures = = Initializes the texture list with the textures from the world map = ================== */ void R_InitTextures(void) { maptexture_t *mtexture; texture_t *texture; mappatch_t *mpatch; texpatch_t *patch; int i, j; int *maptex, *maptex2, *maptex1; char name[9], *names, *name_p; int *patchlookup; int totalwidth; int nummappatches; int offset, maxoff, maxoff2; int numtextures1, numtextures2; int *directory; const char *texture1, *texture2, *pnames; texture1 = DEH_String("TEXTURE1"); texture2 = DEH_String("TEXTURE2"); pnames = DEH_String("PNAMES"); // // load the patch names from pnames.lmp // names = W_CacheLumpName(pnames, PU_STATIC); nummappatches = LONG(*((int *) names)); name_p = names + 4; patchlookup = Z_Malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL); for (i = 0; i < nummappatches; i++) { M_StringCopy(name, name_p + i * 8, sizeof(name)); patchlookup[i] = W_CheckNumForName(name); } W_ReleaseLumpName(pnames); // // load the map texture definitions from textures.lmp // maptex = maptex1 = W_CacheLumpName(texture1, PU_STATIC); numtextures1 = LONG(*maptex); maxoff = W_LumpLength(W_GetNumForName(texture1)); directory = maptex + 1; if (W_CheckNumForName(texture2) != -1) { maptex2 = W_CacheLumpName(texture2, PU_STATIC); numtextures2 = LONG(*maptex2); maxoff2 = W_LumpLength(W_GetNumForName(texture2)); } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; } numtextures = numtextures1 + numtextures2; // // Init the startup thermometer at this point... // { int start, end; int spramount; start = W_GetNumForName(DEH_String("S_START")); end = W_GetNumForName(DEH_String("S_END")); spramount = end - start + 1; InitThermo(spramount + numtextures + 6); } textures = Z_Malloc(numtextures * sizeof(texture_t *), PU_STATIC, 0); texturecolumnlump = Z_Malloc(numtextures * sizeof(short *), PU_STATIC, 0); texturecolumnofs = Z_Malloc(numtextures * sizeof(unsigned short *), PU_STATIC, 0); texturecomposite = Z_Malloc(numtextures * sizeof(byte *), PU_STATIC, 0); texturecompositesize = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); texturewidthmask = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); textureheight = Z_Malloc(numtextures * sizeof(fixed_t), PU_STATIC, 0); totalwidth = 0; for (i = 0; i < numtextures; i++, directory++) { #ifdef __NEXT__ if (!(i & 63)) printf("."); #else IncThermo(); #endif if (i == numtextures1) { // start looking in second texture file maptex = maptex2; maxoff = maxoff2; directory = maptex + 1; } offset = LONG(*directory); if (offset > maxoff) I_Error("R_InitTextures: bad texture directory"); mtexture = (maptexture_t *) ((byte *) maptex + offset); texture = textures[i] = Z_Malloc(sizeof(texture_t) + sizeof(texpatch_t) * (SHORT(mtexture->patchcount) - 1), PU_STATIC, 0); texture->width = SHORT(mtexture->width); texture->height = SHORT(mtexture->height); texture->patchcount = SHORT(mtexture->patchcount); memcpy(texture->name, mtexture->name, sizeof(texture->name)); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; for (j = 0; j < texture->patchcount; j++, mpatch++, patch++) { patch->originx = SHORT(mpatch->originx); patch->originy = SHORT(mpatch->originy); patch->patch = patchlookup[SHORT(mpatch->patch)]; if (patch->patch == -1) I_Error("R_InitTextures: Missing patch in texture %s", texture->name); } texturecolumnlump[i] = Z_Malloc(texture->width * sizeof(short), PU_STATIC, 0); texturecolumnofs[i] = Z_Malloc(texture->width * sizeof(short), PU_STATIC, 0); j = 1; while (j * 2 <= texture->width) j <<= 1; texturewidthmask[i] = j - 1; textureheight[i] = texture->height << FRACBITS; totalwidth += texture->width; } Z_Free(patchlookup); W_ReleaseLumpName(texture1); if (maptex2) { W_ReleaseLumpName(texture2); } // // precalculate whatever possible // for (i = 0; i < numtextures; i++) { R_GenerateLookup(i); CheckAbortStartup(); } // // translation table for global animation // texturetranslation = Z_Malloc((numtextures + 1) * sizeof(int), PU_STATIC, 0); for (i = 0; i < numtextures; i++) texturetranslation[i] = i; } /* ================ = = R_InitFlats = ================= */ void R_InitFlats(void) { int i; firstflat = W_GetNumForName(DEH_String("F_START")) + 1; lastflat = W_GetNumForName(DEH_String("F_END")) - 1; numflats = lastflat - firstflat + 1; // translation table for global animation flattranslation = Z_Malloc((numflats + 1) * sizeof(int), PU_STATIC, 0); for (i = 0; i < numflats; i++) flattranslation[i] = i; } /* ================ = = R_InitSpriteLumps = = Finds the width and hoffset of all sprites in the wad, so the sprite doesn't = need to be cached just for the header during rendering ================= */ void R_InitSpriteLumps(void) { int i; patch_t *patch; firstspritelump = W_GetNumForName(DEH_String("S_START")) + 1; lastspritelump = W_GetNumForName(DEH_String("S_END")) - 1; numspritelumps = lastspritelump - firstspritelump + 1; spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); spritetopoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); for (i = 0; i < numspritelumps; i++) { #ifdef __NEXT__ if (!(i & 63)) printf("."); #else IncThermo(); #endif patch = W_CacheLumpNum(firstspritelump + i, PU_CACHE); spritewidth[i] = SHORT(patch->width) << FRACBITS; spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS; spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS; } } /* ================ = = R_InitColormaps = ================= */ void R_InitColormaps(void) { int lump, length; // // load in the light tables // 256 byte align tables // lump = W_GetNumForName(DEH_String("COLORMAP")); length = W_LumpLength(lump); colormaps = Z_Malloc(length, PU_STATIC, 0); W_ReadLump(lump, colormaps); } /* ================ = = R_InitData = = Locates all the lumps that will be used by all views = Must be called after W_Init ================= */ void R_InitData(void) { //tprintf("\nR_InitTextures ", 0); R_InitTextures(); printf ("."); //tprintf("R_InitFlats\n", 0); R_InitFlats(); IncThermo(); printf ("."); //tprintf("R_InitSpriteLumps ", 0); R_InitSpriteLumps(); IncThermo(); printf ("."); R_InitColormaps(); } //============================================================================= /* ================ = = R_FlatNumForName = ================ */ int R_FlatNumForName(const char *name) { int i; char namet[9]; i = W_CheckNumForName(name); if (i == -1) { namet[8] = 0; memcpy(namet, name, 8); I_Error("R_FlatNumForName: %s not found", namet); } return i - firstflat; } /* ================ = = R_CheckTextureNumForName = ================ */ int R_CheckTextureNumForName(const char *name) { int i; if (name[0] == '-') // no texture marker return 0; for (i = 0; i < numtextures; i++) if (!strncasecmp(textures[i]->name, name, 8)) return i; return -1; } /* ================ = = R_TextureNumForName = ================ */ int R_TextureNumForName(const char *name) { int i; //char namet[9]; i = R_CheckTextureNumForName(name); if (i == -1) I_Error("R_TextureNumForName: %s not found", name); return i; } /* ================= = = R_PrecacheLevel = = Preloads all relevent graphics for the level ================= */ int flatmemory, texturememory, spritememory; void R_PrecacheLevel(void) { char *flatpresent; char *texturepresent; char *spritepresent; int i, j, k, lump; texture_t *texture; thinker_t *th; spriteframe_t *sf; if (demoplayback) return; // // precache flats // flatpresent = Z_Malloc(numflats, PU_STATIC, NULL); memset(flatpresent, 0, numflats); for (i = 0; i < numsectors; i++) { flatpresent[sectors[i].floorpic] = 1; flatpresent[sectors[i].ceilingpic] = 1; } flatmemory = 0; for (i = 0; i < numflats; i++) if (flatpresent[i]) { lump = firstflat + i; flatmemory += lumpinfo[lump]->size; W_CacheLumpNum(lump, PU_CACHE); } Z_Free(flatpresent); // // precache textures // texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL); memset(texturepresent, 0, numtextures); for (i = 0; i < numsides; i++) { texturepresent[sides[i].toptexture] = 1; texturepresent[sides[i].midtexture] = 1; texturepresent[sides[i].bottomtexture] = 1; } texturepresent[skytexture] = 1; texturememory = 0; for (i = 0; i < numtextures; i++) { if (!texturepresent[i]) continue; texture = textures[i]; for (j = 0; j < texture->patchcount; j++) { lump = texture->patches[j].patch; texturememory += lumpinfo[lump]->size; W_CacheLumpNum(lump, PU_CACHE); } } Z_Free(texturepresent); // // precache sprites // spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL); memset(spritepresent, 0, numsprites); for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function == P_MobjThinker) spritepresent[((mobj_t *) th)->sprite] = 1; } spritememory = 0; for (i = 0; i < numsprites; i++) { if (!spritepresent[i]) continue; for (j = 0; j < sprites[i].numframes; j++) { sf = &sprites[i].spriteframes[j]; for (k = 0; k < 8; k++) { lump = firstspritelump + sf->lump[k]; spritememory += lumpinfo[lump]->size; W_CacheLumpNum(lump, PU_CACHE); } } } Z_Free(spritepresent); } crispy-doom-crispy-doom-5.6.4/src/heretic/r_draw.c000066400000000000000000000316301360717211000220730ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // R_draw.c #include "doomdef.h" #include "deh_str.h" #include "r_local.h" #include "i_video.h" #include "v_video.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. */ byte *viewimage; int viewwidth, scaledviewwidth, viewheight, viewwindowx, viewwindowy; byte *ylookup[MAXHEIGHT]; int columnofs[MAXWIDTH]; byte translations[3][256]; // color tables for different players /* ================== = = R_DrawColumn = = Source is the top of the column to scale = ================== */ lighttable_t *dc_colormap; int dc_x; int dc_yl; int dc_yh; fixed_t dc_iscale; fixed_t dc_texturemid; int dc_texheight; byte *dc_source; // first pixel in a column (possibly virtual) int dccount; // just for profiling void R_DrawColumn(void) { int count; byte *dest; fixed_t frac, fracstep; int heightmask = dc_texheight - 1; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; if (dc_texheight & heightmask) // not a power of 2 -- killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0); else while (frac >= heightmask) frac -= heightmask; do { *dest = dc_colormap[dc_source[frac>>FRACBITS]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (count--); } else // texture height is a power of 2 -- killough { do { *dest = dc_colormap[dc_source[(frac >> FRACBITS) & heightmask]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } } void R_DrawColumnLow(void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); // dccount++; #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } // Translucent column draw - blended with background using tinttable. void R_DrawTLColumn(void) { int count; byte *dest; fixed_t frac, fracstep; if (!dc_yl) dc_yl = 1; if (dc_yh == viewheight - 1) dc_yh = viewheight - 2; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawTLColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = tinttable[((*dest) << 8) + dc_colormap[dc_source[(frac >> FRACBITS) & 127]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } /* ======================== = = R_DrawTranslatedColumn = ======================== */ byte *dc_translation; byte *translationtables; void R_DrawTranslatedColumn(void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } void R_DrawTranslatedTLColumn(void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = tinttable[((*dest) << 8) + dc_colormap[dc_translation [dc_source[frac >> FRACBITS]]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } //-------------------------------------------------------------------------- // // PROC R_InitTranslationTables // //-------------------------------------------------------------------------- void R_InitTranslationTables(void) { int i; V_LoadTintTable(); // Allocate translation tables translationtables = Z_Malloc(256 * 3, PU_STATIC, 0); // Fill out the translation tables for (i = 0; i < 256; i++) { if (i >= 225 && i <= 240) { translationtables[i] = 114 + (i - 225); // yellow translationtables[i + 256] = 145 + (i - 225); // red translationtables[i + 512] = 190 + (i - 225); // blue } else { translationtables[i] = translationtables[i + 256] = translationtables[i + 512] = i; } } } /* ================ = = R_DrawSpan = ================ */ int ds_y; int ds_x1; int ds_x2; lighttable_t *ds_colormap; fixed_t ds_xfrac; fixed_t ds_yfrac; fixed_t ds_xstep; fixed_t ds_ystep; byte *ds_source; // start of a 64*64 tile image int dscount; // just for profiling void R_DrawSpan(void) { fixed_t xfrac, yfrac; byte *dest; int count, spot; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || (unsigned) ds_y > SCREENHEIGHT) I_Error("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y); // dscount++; #endif xfrac = ds_xfrac; yfrac = ds_yfrac; dest = ylookup[ds_y] + columnofs[ds_x1]; count = ds_x2 - ds_x1; do { spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); *dest++ = ds_colormap[ds_source[spot]]; xfrac += ds_xstep; yfrac += ds_ystep; } while (count--); } void R_DrawSpanLow(void) { fixed_t xfrac, yfrac; byte *dest; int count, spot; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || (unsigned) ds_y > SCREENHEIGHT) I_Error("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y); // dscount++; #endif xfrac = ds_xfrac; yfrac = ds_yfrac; dest = ylookup[ds_y] + columnofs[ds_x1]; count = ds_x2 - ds_x1; do { spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); *dest++ = ds_colormap[ds_source[spot]]; xfrac += ds_xstep; yfrac += ds_ystep; } while (count--); } /* ================ = = R_InitBuffer = ================= */ void R_InitBuffer(int width, int height) { int i; viewwindowx = (SCREENWIDTH - width) >> 1; for (i = 0; i < width; i++) columnofs[i] = viewwindowx + i; if (width == SCREENWIDTH) viewwindowy = 0; else viewwindowy = (SCREENHEIGHT - SBARHEIGHT - height) >> 1; for (i = 0; i < height; i++) ylookup[i] = I_VideoBuffer + (i + viewwindowy) * SCREENWIDTH; } /* ================== = = R_DrawViewBorder = = Draws the border around the view for different size windows ================== */ boolean BorderNeedRefresh; void R_DrawViewBorder(void) { byte *src, *dest; int x, y; if (scaledviewwidth == SCREENWIDTH) return; if (gamemode == shareware) { src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE); } else { src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE); } dest = I_VideoBuffer; for (y = 0; y < SCREENHEIGHT - SBARHEIGHT; y++) { for (x = 0; x < SCREENWIDTH / 64; x++) { memcpy(dest, src + ((y & 63) << 6), 64); dest += 64; } if (SCREENWIDTH & 63) { memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); dest += (SCREENWIDTH & 63); } } for (x = (viewwindowx >> crispy->hires); x < ((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires)); x += 16) { V_DrawPatch(x, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName(DEH_String("bordt"), PU_CACHE)); V_DrawPatch(x, (viewwindowy >> crispy->hires) + (viewheight >> crispy->hires), W_CacheLumpName(DEH_String("bordb"), PU_CACHE)); } for (y = (viewwindowy >> crispy->hires); y < ((viewwindowy >> crispy->hires) + (viewheight >> crispy->hires)); y += 16) { V_DrawPatch((viewwindowx >> crispy->hires) - 4, y, W_CacheLumpName(DEH_String("bordl"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), y, W_CacheLumpName(DEH_String("bordr"), PU_CACHE)); } V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName(DEH_String("bordtl"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) - 4, W_CacheLumpName(DEH_String("bordtr"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) + (viewheight >> crispy->hires), W_CacheLumpName(DEH_String("bordbr"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) + (viewheight >> crispy->hires), W_CacheLumpName(DEH_String("bordbl"), PU_CACHE)); } /* ================== = = R_DrawTopBorder = = Draws the top border around the view for different size windows ================== */ boolean BorderTopRefresh; void R_DrawTopBorder(void) { byte *src, *dest; int x, y; if (scaledviewwidth == SCREENWIDTH) return; if (gamemode == shareware) { src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE); } else { src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE); } dest = I_VideoBuffer; for (y = 0; y < 30; y++) { for (x = 0; x < SCREENWIDTH / 64; x++) { memcpy(dest, src + ((y & 63) << 6), 64); dest += 64; } if (SCREENWIDTH & 63) { memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); dest += (SCREENWIDTH & 63); } } if ((viewwindowy >> crispy->hires) < 25) { for (x = (viewwindowx >> crispy->hires); x < ((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires)); x += 16) { V_DrawPatch(x, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName(DEH_String("bordt"), PU_CACHE)); } V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires), W_CacheLumpName(DEH_String("bordl"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires), W_CacheLumpName(DEH_String("bordr"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) + 16, W_CacheLumpName(DEH_String("bordl"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) + 16, W_CacheLumpName(DEH_String("bordr"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName(DEH_String("bordtl"), PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) - 4, W_CacheLumpName(DEH_String("bordtr"), PU_CACHE)); } } crispy-doom-crispy-doom-5.6.4/src/heretic/r_local.h000066400000000000000000000315561360717211000222440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // R_local.h #ifndef __R_LOCAL__ #define __R_LOCAL__ #include "i_video.h" #include "v_patch.h" #define ANGLETOSKYSHIFT 22 // sky map is 256*128*4 maps #define BASEYCENTER 100 //#define MAXWIDTH 1120 //#define MAXHEIGHT 832 #define PI 3.141592657 #define CENTERY (SCREENHEIGHT/2) #define MINZ (FRACUNIT*4) #define FIELDOFVIEW 2048 // fineangles in the SCREENWIDTH wide window // // lighting constants // #define LIGHTLEVELS 16 #define LIGHTSEGSHIFT 4 #define MAXLIGHTSCALE 48 #define LIGHTSCALESHIFT 12 #define MAXLIGHTZ 128 #define LIGHTZSHIFT 20 #define NUMCOLORMAPS 32 // number of diminishing #define INVERSECOLORMAP 32 /* ============================================================================== INTERNAL MAP TYPES ============================================================================== */ //================ used by play and refresh typedef struct { fixed_t x, y; // [crispy] remove slime trails // vertex coordinates *only* used in rendering that have been // moved towards the linedef associated with their seg by projecting them // using the law of cosines in p_setup.c:P_RemoveSlimeTrails(); fixed_t r_x; fixed_t r_y; boolean moved; } vertex_t; struct line_s; typedef struct { fixed_t floorheight, ceilingheight; short floorpic, ceilingpic; short lightlevel; short special, tag; 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 degenmobj_t soundorg; // for any sounds played by the sector int validcount; // if == validcount, already checked mobj_t *thinglist; // list of mobjs in sector void *specialdata; // thinker_t for reversable actions int linecount; struct line_s **lines; // [linecount] size } sector_t; typedef struct { fixed_t textureoffset; // add this to the calculated texture col fixed_t rowoffset; // add this to the calculated texture top short toptexture, bottomtexture, midtexture; sector_t *sector; } side_t; typedef enum { ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE } slopetype_t; typedef struct line_s { vertex_t *v1, *v2; fixed_t dx, dy; // v2 - v1 for side checking short flags; short special, tag; short sidenum[2]; // sidenum[1] will be -1 if one sided fixed_t bbox[4]; slopetype_t slopetype; // to aid move clipping sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked void *specialdata; // thinker_t for reversable actions } line_t; typedef struct subsector_s { sector_t *sector; short numlines; short firstline; } subsector_t; typedef struct { vertex_t *v1, *v2; fixed_t offset; angle_t angle; side_t *sidedef; line_t *linedef; sector_t *frontsector; sector_t *backsector; // NULL for one sided lines } seg_t; 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 } node_t; /* ============================================================================== OTHER TYPES ============================================================================== */ typedef byte lighttable_t; // this could be wider for >8 bit display #define MAXVISPLANES 128 #define MAXOPENINGS MAXWIDTH*64*4 typedef struct { fixed_t height; int picnum; int lightlevel; int special; int minx, maxx; unsigned int pad1; // [crispy] hires / 32-bit integer math unsigned int top[MAXWIDTH]; // [crispy] hires / 32-bit integer math unsigned int pad2; // [crispy] hires / 32-bit integer math unsigned int pad3; // [crispy] hires / 32-bit integer math unsigned int bottom[MAXWIDTH]; // [crispy] hires / 32-bit integer math unsigned int pad4; // [crispy] hires / 32-bit integer math } visplane_t; typedef struct drawseg_s { seg_t *curline; int x1, x2; fixed_t scale1, scale2, scalestep; int silhouette; // 0=none, 1=bottom, 2=top, 3=both fixed_t bsilheight; // don't clip sprites above this fixed_t tsilheight; // don't clip sprites below this // pointers to lists for sprite clipping int *sprtopclip; // [crispy] 32-bit integer math int *sprbottomclip; // [crispy] 32-bit integer math int *maskedtexturecol; // [crispy] 32-bit integer math } drawseg_t; #define SIL_NONE 0 #define SIL_BOTTOM 1 #define SIL_TOP 2 #define SIL_BOTH 3 #define MAXDRAWSEGS 256 // A vissprite_t is a thing that will be drawn during a refresh typedef struct vissprite_s { struct vissprite_s *prev, *next; int 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; lighttable_t *colormap; int mobjflags; // for color translation and shadow draw boolean psprite; // true if psprite fixed_t footclip; // foot clipping } vissprite_t; extern visplane_t *floorplane, *ceilingplane; // Sprites are patches with a special naming convention so they can be // recognized by R_InitSprites. 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. Some sprites will only have one picture used // for all views. typedef struct { boolean rotate; // if false use 0 for any position short lump[8]; // lump to use for view angles 0-7 byte flip[8]; // flip (1 = flip) to use for view angles 0-7 } spriteframe_t; typedef struct { int numframes; spriteframe_t *spriteframes; } spritedef_t; extern spritedef_t *sprites; extern int numsprites; //============================================================================= 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 fixed_t viewx, viewy, viewz; extern angle_t viewangle; extern player_t *viewplayer; extern angle_t clipangle; extern int viewangletox[FINEANGLES / 2]; extern angle_t xtoviewangle[MAXWIDTH + 1]; extern fixed_t rw_distance; extern angle_t rw_normalangle; // // R_main.c // extern int viewwidth, viewheight, viewwindowx, viewwindowy; extern int scaledviewwidth; extern int centerx, centery; extern int flyheight; extern fixed_t centerxfrac; extern fixed_t centeryfrac; extern fixed_t projection; extern int validcount; extern int sscount, linecount, loopcount; extern lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; extern lighttable_t *scalelightfixed[MAXLIGHTSCALE]; extern lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; extern int extralight; extern lighttable_t *fixedcolormap; extern fixed_t viewcos, viewsin; extern int detailshift; // 0 = high, 1 = low extern void (*colfunc) (void); extern void (*basecolfunc) (void); extern void (*tlcolfunc) (void); extern void (*spanfunc) (void); int R_PointOnSide(fixed_t x, fixed_t y, node_t * node); int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t * line); angle_t R_PointToAngle(fixed_t x, fixed_t y); angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); fixed_t R_PointToDist(fixed_t x, fixed_t y); fixed_t R_ScaleFromGlobalAngle(angle_t visangle); subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); void R_AddPointToBox(int x, int y, fixed_t * box); // // R_bsp.c // extern seg_t *curline; extern side_t *sidedef; extern line_t *linedef; extern sector_t *frontsector, *backsector; extern int rw_x; extern int rw_stopx; extern boolean segtextured; extern boolean markfloor; // false if the back side is the same plane extern boolean markceiling; extern boolean skymap; extern drawseg_t *drawsegs, *ds_p; extern int numdrawsegs; extern lighttable_t **hscalelight, **vscalelight, **dscalelight; typedef void (*drawfunc_t) (int start, int stop); void R_ClearClipSegs(void); void R_ClearDrawSegs(void); void R_InitSkyMap(void); void R_RenderBSPNode(int bspnum); // // R_segs.c // extern int rw_angle1; // angle to line origin void R_RenderMaskedSegRange(drawseg_t * ds, int x1, int x2); // // R_plane.c // typedef void (*planefunction_t) (int top, int bottom); extern planefunction_t floorfunc, ceilingfunc; extern int skyflatnum; extern int *lastopening; // [crispy] 32-bit integer math extern int floorclip[MAXWIDTH]; // [crispy] 32-bit integer math extern int ceilingclip[MAXWIDTH]; // [crispy] 32-bit integer math extern fixed_t yslope[MAXHEIGHT]; extern fixed_t distscale[MAXWIDTH]; void R_InitPlanes(void); void R_ClearPlanes(void); void R_MapPlane(int y, int x1, int x2); void R_MakeSpans(int x, unsigned int t1, unsigned int b1, unsigned int t2, unsigned int b2); // [crispy] 32-bit integer math void R_DrawPlanes(void); visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel, int special); visplane_t *R_CheckPlane(visplane_t * pl, int start, int stop); // // R_debug.m // extern int drawbsp; // // R_data.c // extern fixed_t *textureheight; // needed for texture pegging extern fixed_t *spritewidth; // needed for pre rendering (fracs) extern fixed_t *spriteoffset; extern fixed_t *spritetopoffset; extern lighttable_t *colormaps; extern int firstflat; extern int numflats; extern int *flattranslation; // for global animation extern int *texturetranslation; // for global animation extern int firstspritelump, lastspritelump, numspritelumps; byte *R_GetColumn(int tex, int col); void R_InitData(void); void R_PrecacheLevel(void); // // R_things.c // #define MAXVISSPRITES 128 extern vissprite_t *vissprites, *vissprite_p; extern vissprite_t vsprsortedhead; // constant arrays used for psprite clipping and initializing clipping extern int negonearray[MAXWIDTH]; // [crispy] 32-bit integer math extern int screenheightarray[MAXWIDTH]; // [crispy] 32-bit integer math // vars for R_DrawMaskedColumn extern int *mfloorclip; // [crispy] 32-bit integer math extern int *mceilingclip; // [crispy] 32-bit integer math extern fixed_t spryscale; extern fixed_t sprtopscreen; extern fixed_t sprbotscreen; extern fixed_t pspritescale, pspriteiscale; void R_DrawMaskedColumn(column_t * column, signed int baseclip); void R_SortVisSprites(void); void R_AddSprites(sector_t * sec); void R_AddPSprites(void); void R_DrawSprites(void); void R_InitSprites(const char **namelist); void R_ClearSprites(void); void R_DrawMasked(void); void R_ClipVisSprite(vissprite_t * vis, int xl, int xh); //============================================================================= // // R_draw.c // //============================================================================= extern lighttable_t *dc_colormap; extern int dc_x; extern int dc_yl; extern int dc_yh; extern fixed_t dc_iscale; extern fixed_t dc_texturemid; extern int dc_texheight; extern byte *dc_source; // first pixel in a column void R_DrawColumn(void); void R_DrawColumnLow(void); void R_DrawTLColumn(void); void R_DrawTLColumnLow(void); void R_DrawTranslatedColumn(void); void R_DrawTranslatedTLColumn(void); void R_DrawTranslatedColumnLow(void); extern int ds_y; extern int ds_x1; extern int ds_x2; extern lighttable_t *ds_colormap; extern fixed_t ds_xfrac; extern fixed_t ds_yfrac; extern fixed_t ds_xstep; extern fixed_t ds_ystep; extern byte *ds_source; // start of a 64*64 tile image extern byte *translationtables; extern byte *dc_translation; void R_DrawSpan(void); void R_DrawSpanLow(void); void R_InitBuffer(int width, int height); void R_InitTranslationTables(void); #endif // __R_LOCAL__ crispy-doom-crispy-doom-5.6.4/src/heretic/r_main.c000066400000000000000000000440351360717211000220650ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // R_main.c #include #include #include "doomdef.h" #include "m_bbox.h" #include "r_local.h" #include "tables.h" int viewangleoffset; // haleyjd: removed WATCOMC int validcount = 1; // increment every time a check is made lighttable_t *fixedcolormap; extern lighttable_t **walllights; int centerx, centery; fixed_t centerxfrac, centeryfrac; fixed_t projection; int framecount; // just for profiling purposes int sscount, linecount, loopcount; fixed_t viewx, viewy, viewz; angle_t viewangle; fixed_t viewcos, viewsin; player_t *viewplayer; int detailshift; // 0 = high, 1 = low // // precalculated math tables // angle_t clipangle; // The viewangletox[viewangle + FINEANGLES/4] lookup maps the visible view // angles to screen X coordinates, flattening the arc to a flat projection // plane. There will be many angles mapped to the same X. int viewangletox[FINEANGLES / 2]; // The xtoviewangleangle[] table maps a screen pixel to the lowest viewangle // that maps back to x ranges from clipangle to -clipangle angle_t xtoviewangle[MAXWIDTH + 1]; lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; lighttable_t *scalelightfixed[MAXLIGHTSCALE]; lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; int extralight; // bumped light from gun blasts void (*colfunc) (void); void (*basecolfunc) (void); void (*tlcolfunc) (void); void (*transcolfunc) (void); void (*spanfunc) (void); /* =================== = = R_AddPointToBox = =================== */ void R_AddPointToBox(int x, int y, fixed_t * box) { if (x < box[BOXLEFT]) box[BOXLEFT] = x; if (x > box[BOXRIGHT]) box[BOXRIGHT] = x; if (y < box[BOXBOTTOM]) box[BOXBOTTOM] = y; if (y > box[BOXTOP]) box[BOXTOP] = y; } /* =============================================================================== = = R_PointOnSide = = Returns side 0 (front) or 1 (back) =============================================================================== */ int R_PointOnSide(fixed_t x, fixed_t y, node_t * node) { fixed_t dx, dy; fixed_t left, right; if (!node->dx) { if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); // try to quickly decide by looking at sign bits if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000) { if ((node->dy ^ dx) & 0x80000000) return 1; // (left is negative) return 0; } left = FixedMul(node->dy >> FRACBITS, dx); right = FixedMul(dy, node->dx >> FRACBITS); if (right < left) return 0; // front side return 1; // back side } int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t * line) { fixed_t lx, ly; fixed_t ldx, ldy; fixed_t dx, dy; fixed_t left, right; lx = line->v1->x; ly = line->v1->y; ldx = line->v2->x - lx; ldy = line->v2->y - ly; if (!ldx) { if (x <= lx) return ldy > 0; return ldy < 0; } if (!ldy) { if (y <= ly) return ldx < 0; return ldx > 0; } dx = (x - lx); dy = (y - ly); // try to quickly decide by looking at sign bits if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000) { if ((ldy ^ dx) & 0x80000000) return 1; // (left is negative) return 0; } left = FixedMul(ldy >> FRACBITS, dx); right = FixedMul(dy, ldx >> FRACBITS); if (right < left) return 0; // front side return 1; // back side } /* =============================================================================== = = R_PointToAngle = =============================================================================== */ angle_t R_PointToAngle(fixed_t x, fixed_t y) { x -= viewx; y -= viewy; if ((!x) && (!y)) return 0; if (x >= 0) { // x >=0 if (y >= 0) { // y>= 0 if (x > y) return tantoangle[SlopeDiv(y, x)]; // octant 0 else return ANG90 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 1 } else { // y<0 y = -y; if (x > y) return -tantoangle[SlopeDiv(y, x)]; // octant 8 else return ANG270 + tantoangle[SlopeDiv(x, y)]; // octant 7 } } else { // x<0 x = -x; if (y >= 0) { // y>= 0 if (x > y) return ANG180 - 1 - tantoangle[SlopeDiv(y, x)]; // octant 3 else return ANG90 + tantoangle[SlopeDiv(x, y)]; // octant 2 } else { // y<0 y = -y; if (x > y) return ANG180 + tantoangle[SlopeDiv(y, x)]; // octant 4 else return ANG270 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 5 } } return 0; } angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) { viewx = x1; viewy = y1; return R_PointToAngle(x2, y2); } fixed_t R_PointToDist(fixed_t x, fixed_t y) { int angle; fixed_t dx, dy, temp; fixed_t dist; dx = abs(x - viewx); dy = abs(y - viewy); if (dy > dx) { temp = dx; dx = dy; dy = temp; } angle = (tantoangle[FixedDiv(dy, dx) >> DBITS] + ANG90) >> ANGLETOFINESHIFT; dist = FixedDiv(dx, finesine[angle]); // use as cosine return dist; } /* ================= = = R_InitPointToAngle = ================= */ void R_InitPointToAngle(void) { // now getting from tables.c #if 0 int i; long t; float f; // // slope (tangent) to angle lookup // for (i = 0; i <= SLOPERANGE; i++) { f = atan((float) i / SLOPERANGE) / (3.141592657 * 2); t = 0xffffffff * f; tantoangle[i] = t; } #endif } //============================================================================= /* ================ = = R_ScaleFromGlobalAngle = = Returns the texture mapping scale for the current line at the given angle = rw_distance must be calculated first ================ */ fixed_t R_ScaleFromGlobalAngle(angle_t visangle) { fixed_t scale; int anglea, angleb; int sinea, sineb; fixed_t num, den; #if 0 { fixed_t dist, z; fixed_t sinv, cosv; sinv = finesine[(visangle - rw_normalangle) >> ANGLETOFINESHIFT]; dist = FixedDiv(rw_distance, sinv); cosv = finecosine[(viewangle - visangle) >> ANGLETOFINESHIFT]; z = abs(FixedMul(dist, cosv)); scale = FixedDiv(projection, z); return scale; } #endif anglea = ANG90 + (visangle - viewangle); angleb = ANG90 + (visangle - rw_normalangle); // bothe sines are allways positive sinea = finesine[anglea >> ANGLETOFINESHIFT]; sineb = finesine[angleb >> ANGLETOFINESHIFT]; num = FixedMul(projection, sineb) << detailshift; den = FixedMul(rw_distance, sinea); if (den > num >> 16) { scale = FixedDiv(num, den); if (scale > 64 * FRACUNIT) scale = 64 * FRACUNIT; else if (scale < 256) scale = 256; } else scale = 64 * FRACUNIT; return scale; } /* ================= = = R_InitTables = ================= */ void R_InitTables(void) { // now getting from tables.c #if 0 int i; float a, fv; int t; // // viewangle tangent table // for (i = 0; i < FINEANGLES / 2; i++) { a = (i - FINEANGLES / 4 + 0.5) * PI * 2 / FINEANGLES; fv = FRACUNIT * tan(a); t = fv; finetangent[i] = t; } // // finesine table // for (i = 0; i < 5 * FINEANGLES / 4; i++) { // OPTIMIZE: mirror... a = (i + 0.5) * PI * 2 / FINEANGLES; t = FRACUNIT * sin(a); finesine[i] = t; } #endif } /* ================= = = R_InitTextureMapping = ================= */ void R_InitTextureMapping(void) { int i; int x; int t; fixed_t focallength; // // use tangent table to generate viewangletox // viewangletox will give the next greatest x after the view angle // // calc focallength so FIELDOFVIEW angles covers SCREENWIDTH focallength = FixedDiv(centerxfrac, finetangent[FINEANGLES / 4 + FIELDOFVIEW / 2]); for (i = 0; i < FINEANGLES / 2; i++) { if (finetangent[i] > FRACUNIT * 2) t = -1; else if (finetangent[i] < -FRACUNIT * 2) t = viewwidth + 1; else { t = FixedMul(finetangent[i], focallength); t = (centerxfrac - t + FRACUNIT - 1) >> FRACBITS; if (t < -1) t = -1; else if (t > viewwidth + 1) t = viewwidth + 1; } viewangletox[i] = t; } // // scan viewangletox[] to generate xtoviewangleangle[] // // xtoviewangle will give the smallest view angle that maps to x for (x = 0; x <= viewwidth; x++) { i = 0; while (viewangletox[i] > x) i++; xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANG90; } // // take out the fencepost cases from viewangletox // for (i = 0; i < FINEANGLES / 2; i++) { t = FixedMul(finetangent[i], focallength); t = centerx - t; if (viewangletox[i] == -1) viewangletox[i] = 0; else if (viewangletox[i] == viewwidth + 1) viewangletox[i] = viewwidth; } clipangle = xtoviewangle[0]; } //============================================================================= /* ==================== = = R_InitLightTables = = Only inits the zlight table, because the scalelight table changes = with view size = ==================== */ #define DISTMAP 2 void R_InitLightTables(void) { int i, j, level, startmap; int scale; // // Calculate the light levels to use for each level / distance combination // for (i = 0; i < LIGHTLEVELS; i++) { startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; for (j = 0; j < MAXLIGHTZ; j++) { scale = FixedDiv((ORIGWIDTH / 2 * FRACUNIT), (j + 1) << LIGHTZSHIFT); scale >>= LIGHTSCALESHIFT; level = startmap - scale / DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; zlight[i][j] = colormaps + level * 256; } } } /* ============== = = R_SetViewSize = = Don't really change anything here, because i might be in the middle of = a refresh. The change will take effect next refresh. = ============== */ boolean setsizeneeded; int setblocks, setdetail; void R_SetViewSize(int blocks, int detail) { setsizeneeded = true; setblocks = blocks; setdetail = detail; } /* ============== = = R_ExecuteSetViewSize = ============== */ void R_ExecuteSetViewSize(void) { fixed_t cosadj, dy; int i, j, level, startmap; setsizeneeded = false; if (setblocks == 11) { scaledviewwidth = SCREENWIDTH; viewheight = SCREENHEIGHT; } else { scaledviewwidth = (setblocks * 32) << crispy->hires; viewheight = ((setblocks * 158 / 10)) << crispy->hires; } detailshift = setdetail; viewwidth = scaledviewwidth >> detailshift; centery = viewheight / 2; centerx = viewwidth / 2; centerxfrac = centerx << FRACBITS; centeryfrac = centery << FRACBITS; projection = centerxfrac; if (!detailshift) { colfunc = basecolfunc = R_DrawColumn; tlcolfunc = R_DrawTLColumn; transcolfunc = R_DrawTranslatedColumn; spanfunc = R_DrawSpan; } else { colfunc = basecolfunc = R_DrawColumnLow; tlcolfunc = R_DrawTLColumn; transcolfunc = R_DrawTranslatedColumn; spanfunc = R_DrawSpanLow; } R_InitBuffer(scaledviewwidth, viewheight); R_InitTextureMapping(); // // psprite scales // pspritescale = FRACUNIT * viewwidth / ORIGWIDTH; pspriteiscale = FRACUNIT * ORIGWIDTH / viewwidth; // // thing clipping // for (i = 0; i < viewwidth; i++) screenheightarray[i] = viewheight; // // planes // for (i = 0; i < viewheight; i++) { dy = ((i - viewheight / 2) << FRACBITS) + FRACUNIT / 2; dy = abs(dy); yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, dy); } for (i = 0; i < viewwidth; i++) { cosadj = abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]); distscale[i] = FixedDiv(FRACUNIT, cosadj); } // // Calculate the light levels to use for each level / scale combination // for (i = 0; i < LIGHTLEVELS; i++) { startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; for (j = 0; j < MAXLIGHTSCALE; j++) { level = startmap - j * SCREENWIDTH / (viewwidth << detailshift) / DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; scalelight[i][j] = colormaps + level * 256; } } // // draw the border // R_DrawViewBorder(); // erase old menu stuff } /* ============== = = R_Init = ============== */ int detailLevel; int screenblocks = 10; void R_Init(void) { //tprintf("R_InitData ", 1); R_InitData(); printf ("."); //tprintf("R_InitPointToAngle\n", 0); R_InitPointToAngle(); printf ("."); //tprintf("R_InitTables ", 0); R_InitTables(); // viewwidth / viewheight / detailLevel are set by the defaults printf ("."); R_SetViewSize(screenblocks, detailLevel); //tprintf("R_InitPlanes\n", 0); R_InitPlanes(); printf ("."); //tprintf("R_InitLightTables ", 0); R_InitLightTables(); printf ("."); //tprintf("R_InitSkyMap\n", 0); R_InitSkyMap(); printf ("."); R_InitTranslationTables(); framecount = 0; } /* ============== = = R_PointInSubsector = ============== */ subsector_t *R_PointInSubsector(fixed_t x, fixed_t y) { node_t *node; int side, nodenum; if (!numnodes) // single subsector is a special case return subsectors; nodenum = numnodes - 1; while (!(nodenum & NF_SUBSECTOR)) { node = &nodes[nodenum]; side = R_PointOnSide(x, y, node); nodenum = node->children[side]; } return &subsectors[nodenum & ~NF_SUBSECTOR]; } //---------------------------------------------------------------------------- // // PROC R_SetupFrame // //---------------------------------------------------------------------------- void R_SetupFrame(player_t * player) { int i; int tableAngle; int tempCentery; //drawbsp = 1; viewplayer = player; // haleyjd: removed WATCOMC // haleyjd FIXME: viewangleoffset handling? viewangle = player->mo->angle + viewangleoffset; tableAngle = viewangle >> ANGLETOFINESHIFT; if (player->chickenTics && player->chickenPeck) { // Set chicken attack view position viewx = player->mo->x + player->chickenPeck * finecosine[tableAngle]; viewy = player->mo->y + player->chickenPeck * finesine[tableAngle]; } else { // Normal view position viewx = player->mo->x; viewy = player->mo->y; } extralight = player->extralight; viewz = player->viewz; tempCentery = viewheight / 2 + ((player->lookdir) << crispy->hires) * screenblocks / 10; if (centery != tempCentery) { centery = tempCentery; centeryfrac = centery << FRACBITS; for (i = 0; i < viewheight; i++) { yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, abs(((i - centery) << FRACBITS) + FRACUNIT / 2)); } } viewsin = finesine[tableAngle]; viewcos = finecosine[tableAngle]; sscount = 0; if (player->fixedcolormap) { fixedcolormap = colormaps + player->fixedcolormap * 256 * sizeof(lighttable_t); walllights = scalelightfixed; for (i = 0; i < MAXLIGHTSCALE; i++) { scalelightfixed[i] = fixedcolormap; } } else { fixedcolormap = 0; } framecount++; validcount++; if (BorderNeedRefresh) { if (setblocks < 10) { R_DrawViewBorder(); } BorderNeedRefresh = false; BorderTopRefresh = false; UpdateState |= I_FULLSCRN; } if (BorderTopRefresh) { if (setblocks < 10) { R_DrawTopBorder(); } BorderTopRefresh = false; UpdateState |= I_MESSAGES; } } /* ============== = = R_RenderView = ============== */ void R_RenderPlayerView(player_t * player) { R_SetupFrame(player); R_ClearClipSegs(); R_ClearDrawSegs(); R_ClearPlanes(); R_ClearSprites(); NetUpdate(); // check for new console commands R_RenderBSPNode(numnodes - 1); // the head node is the last node output NetUpdate(); // check for new console commands R_DrawPlanes(); NetUpdate(); // check for new console commands R_DrawMasked(); NetUpdate(); // check for new console commands } crispy-doom-crispy-doom-5.6.4/src/heretic/r_plane.c000066400000000000000000000304571360717211000222430ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // R_planes.c #include #include "doomdef.h" #include "deh_str.h" #include "i_system.h" #include "r_local.h" planefunction_t floorfunc, ceilingfunc; // // sky mapping // int skyflatnum; int skytexture; int skytexturemid; fixed_t skyiscale; // // opening // visplane_t *visplanes = NULL, *lastvisplane; visplane_t *floorplane, *ceilingplane; static int numvisplanes; int openings[MAXOPENINGS], *lastopening; // [crispy] 32-bit integer math // // clip values are the solid pixel bounding the range // floorclip starts out SCREENHEIGHT // ceilingclip starts out -1 // int floorclip[MAXWIDTH]; // [crispy] 32-bit integer math int ceilingclip[MAXWIDTH]; // [crispy] 32-bit integer math // // spanstart holds the start of a plane span // initialized to 0 at start // int spanstart[MAXHEIGHT]; int spanstop[MAXHEIGHT]; // // texture mapping // lighttable_t **planezlight; fixed_t planeheight; fixed_t yslope[MAXHEIGHT]; fixed_t distscale[MAXWIDTH]; fixed_t basexscale, baseyscale; fixed_t cachedheight[MAXHEIGHT]; fixed_t cacheddistance[MAXHEIGHT]; fixed_t cachedxstep[MAXHEIGHT]; fixed_t cachedystep[MAXHEIGHT]; /* ================ = = R_InitSkyMap = = Called whenever the view size changes = ================ */ void R_InitSkyMap(void) { skyflatnum = R_FlatNumForName(DEH_String("F_SKY1")); skytexturemid = 200 * FRACUNIT; skyiscale = FRACUNIT >> crispy->hires; } /* ==================== = = R_InitPlanes = = Only at game startup ==================== */ void R_InitPlanes(void) { } /* ================ = = R_MapPlane = global vars: planeheight ds_source basexscale baseyscale viewx viewy BASIC PRIMITIVE ================ */ void R_MapPlane(int y, int x1, int x2) { angle_t angle; fixed_t distance, length; unsigned index; #ifdef RANGECHECK if (x2 < x1 || x1 < 0 || x2 >= viewwidth || (unsigned) y > viewheight) I_Error("R_MapPlane: %i, %i at %i", x1, x2, y); #endif if (planeheight != cachedheight[y]) { cachedheight[y] = planeheight; distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]); ds_xstep = cachedxstep[y] = FixedMul(distance, basexscale); ds_ystep = cachedystep[y] = FixedMul(distance, baseyscale); } else { distance = cacheddistance[y]; ds_xstep = cachedxstep[y]; ds_ystep = cachedystep[y]; } length = FixedMul(distance, distscale[x1]); angle = (viewangle + xtoviewangle[x1]) >> ANGLETOFINESHIFT; ds_xfrac = viewx + FixedMul(finecosine[angle], length); ds_yfrac = -viewy - FixedMul(finesine[angle], length); if (fixedcolormap) ds_colormap = fixedcolormap; else { index = distance >> LIGHTZSHIFT; if (index >= MAXLIGHTZ) index = MAXLIGHTZ - 1; ds_colormap = planezlight[index]; } ds_y = y; ds_x1 = x1; ds_x2 = x2; spanfunc(); // high or low detail } //============================================================================= /* ==================== = = R_ClearPlanes = = At begining of frame ==================== */ void R_ClearPlanes(void) { int i; angle_t angle; // // opening / clipping determination // for (i = 0; i < viewwidth; i++) { floorclip[i] = viewheight; ceilingclip[i] = -1; } lastvisplane = visplanes; lastopening = openings; // // texture calculation // memset(cachedheight, 0, sizeof(cachedheight)); angle = (viewangle - ANG90) >> ANGLETOFINESHIFT; // left to right mapping // scale will be unit scale at SCREENWIDTH/2 distance basexscale = FixedDiv(finecosine[angle], centerxfrac); baseyscale = -FixedDiv(finesine[angle], centerxfrac); } // [crispy] remove MAXVISPLANES limit static void R_RaiseVisplanes (visplane_t** vp) { if (lastvisplane - visplanes == numvisplanes) { int numvisplanes_old = numvisplanes; visplane_t* visplanes_old = visplanes; numvisplanes = numvisplanes ? 2 * numvisplanes : MAXVISPLANES; visplanes = I_Realloc(visplanes, numvisplanes * sizeof(*visplanes)); memset(visplanes + numvisplanes_old, 0, (numvisplanes - numvisplanes_old) * sizeof(*visplanes)); lastvisplane = visplanes + numvisplanes_old; floorplane = visplanes + (floorplane - visplanes_old); ceilingplane = visplanes + (ceilingplane - visplanes_old); if (numvisplanes_old) fprintf(stderr, "R_FindPlane: Hit MAXVISPLANES limit at %d, raised to %d.\n", numvisplanes_old, numvisplanes); // keep the pointer passed as argument in relation to the visplanes pointer if (vp) *vp = visplanes + (*vp - visplanes_old); } } /* =============== = = R_FindPlane = =============== */ visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel, int special) { visplane_t *check; if (picnum == skyflatnum) { // all skies map together height = 0; lightlevel = 0; } for (check = visplanes; check < lastvisplane; check++) { if (height == check->height && picnum == check->picnum && lightlevel == check->lightlevel && special == check->special) break; } if (check < lastvisplane) { return (check); } R_RaiseVisplanes(&check); lastvisplane++; check->height = height; check->picnum = picnum; check->lightlevel = lightlevel; check->special = special; check->minx = SCREENWIDTH; check->maxx = -1; memset(check->top, 0xff, sizeof(check->top)); return (check); } /* =============== = = R_CheckPlane = =============== */ visplane_t *R_CheckPlane(visplane_t * pl, int start, int stop) { int intrl, intrh; int unionl, unionh; int x; if (start < pl->minx) { intrl = pl->minx; unionl = start; } else { unionl = pl->minx; intrl = start; } if (stop > pl->maxx) { intrh = pl->maxx; unionh = stop; } else { unionh = pl->maxx; intrh = stop; } for (x = intrl; x <= intrh; x++) if (pl->top[x] != 0xffffffffu) // [crispy] hires / 32-bit integer math break; if (x > intrh) { pl->minx = unionl; pl->maxx = unionh; return pl; // use the same one } // make a new visplane R_RaiseVisplanes(&pl); lastvisplane->height = pl->height; lastvisplane->picnum = pl->picnum; lastvisplane->lightlevel = pl->lightlevel; lastvisplane->special = pl->special; pl = lastvisplane++; pl->minx = start; pl->maxx = stop; memset(pl->top, 0xff, sizeof(pl->top)); return pl; } //============================================================================= /* ================ = = R_MakeSpans = ================ */ void R_MakeSpans(int x, unsigned int t1, unsigned int b1, unsigned int t2, unsigned int b2) // [crispy] 32-bit integer math { while (t1 < t2 && t1 <= b1) { R_MapPlane(t1, spanstart[t1], x - 1); t1++; } while (b1 > b2 && b1 >= t1) { R_MapPlane(b1, spanstart[b1], x - 1); b1--; } while (t2 < t1 && t2 <= b2) { spanstart[t2] = x; t2++; } while (b2 > b1 && b2 >= t2) { spanstart[b2] = x; b2--; } } /* ================ = = R_DrawPlanes = = At the end of each frame ================ */ void R_DrawPlanes(void) { visplane_t *pl; int light; int x, stop; int lumpnum; int angle; byte *tempSource; byte *dest; int count; fixed_t frac, fracstep; extern byte *ylookup[MAXHEIGHT]; extern int columnofs[MAXWIDTH]; #ifdef RANGECHECK if (ds_p - drawsegs > numdrawsegs) I_Error("R_DrawPlanes: drawsegs overflow (%" PRIiPTR ")", ds_p - drawsegs); if (lastvisplane - visplanes > numvisplanes) I_Error("R_DrawPlanes: visplane overflow (%" PRIiPTR ")", lastvisplane - visplanes); if (lastopening - openings > MAXOPENINGS) I_Error("R_DrawPlanes: opening overflow (%" PRIiPTR ")", lastopening - openings); #endif for (pl = visplanes; pl < lastvisplane; pl++) { if (pl->minx > pl->maxx) continue; // // sky flat // if (pl->picnum == skyflatnum) { dc_iscale = skyiscale; dc_colormap = colormaps; // sky is allways drawn full bright dc_texturemid = skytexturemid; dc_texheight = textureheight[skytexture]>>FRACBITS; for (x = pl->minx; x <= pl->maxx; x++) { dc_yl = pl->top[x]; dc_yh = pl->bottom[x]; if ((unsigned) dc_yl <= dc_yh) // [crispy] 32-bit integer math { angle = (viewangle + xtoviewangle[x]) >> ANGLETOSKYSHIFT; dc_x = x; dc_source = R_GetColumn(skytexture, angle); count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = dc_source[frac >> FRACBITS]; dest += SCREENWIDTH; frac += fracstep; } while (count--); // colfunc (); } } continue; } // // regular flat // lumpnum = firstflat + flattranslation[pl->picnum]; tempSource = W_CacheLumpNum(lumpnum, PU_STATIC); switch (pl->special) { case 25: case 26: case 27: case 28: case 29: // Scroll_North ds_source = tempSource; break; case 20: case 21: case 22: case 23: case 24: // Scroll_East ds_source = tempSource + ((63 - ((leveltime >> 1) & 63)) << (pl->special - 20) & 63); //ds_source = tempSource+((leveltime>>1)&63); break; case 30: case 31: case 32: case 33: case 34: // Scroll_South ds_source = tempSource; break; case 35: case 36: case 37: case 38: case 39: // Scroll_West ds_source = tempSource; break; case 4: // Scroll_EastLavaDamage ds_source = tempSource + (((63 - ((leveltime >> 1) & 63)) << 3) & 63); break; default: ds_source = tempSource; } planeheight = abs(pl->height - viewz); light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight; if (light >= LIGHTLEVELS) light = LIGHTLEVELS - 1; if (light < 0) light = 0; planezlight = zlight[light]; pl->top[pl->maxx + 1] = 0xffffffffu; // [crispy] hires / 32-bit integer math pl->top[pl->minx - 1] = 0xffffffffu; // [crispy] hires / 32-bit integer math stop = pl->maxx + 1; for (x = pl->minx; x <= stop; x++) R_MakeSpans(x, pl->top[x - 1], pl->bottom[x - 1], pl->top[x], pl->bottom[x]); W_ReleaseLumpNum(lumpnum); } } crispy-doom-crispy-doom-5.6.4/src/heretic/r_segs.c000066400000000000000000000503251360717211000221010ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************** //** //** R_SEGS.C //** //** This version has the tall-sector-crossing-precision-bug fixed. //** //************************************************************************** #include #include "doomdef.h" #include "i_system.h" // [crispy] I_Realloc() #include "r_local.h" // OPTIMIZE: closed two sided lines as single sided boolean segtextured; // true if any of the segs textures might be vis boolean markfloor; // false if the back side is the same plane boolean markceiling; boolean maskedtexture; int toptexture, bottomtexture, midtexture; angle_t rw_normalangle; int rw_angle1; // angle to line origin // // wall // int rw_x; int rw_stopx; angle_t rw_centerangle; fixed_t rw_offset; fixed_t rw_distance; fixed_t rw_scale; fixed_t rw_scalestep; fixed_t rw_midtexturemid; fixed_t rw_toptexturemid; fixed_t rw_bottomtexturemid; int worldtop, worldbottom, worldhigh, worldlow; fixed_t pixhigh, pixlow; fixed_t pixhighstep, pixlowstep; fixed_t topfrac, topstep; fixed_t bottomfrac, bottomstep; lighttable_t **walllights; int *maskedtexturecol; // [crispy] 32-bit integer math /* ================ = = R_RenderMaskedSegRange = ================ */ void R_RenderMaskedSegRange(drawseg_t * ds, int x1, int x2) { unsigned index; column_t *col; int lightnum; int texnum; // // calculate light table // use different light tables for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally curline = ds->curline; frontsector = curline->frontsector; backsector = curline->backsector; texnum = texturetranslation[curline->sidedef->midtexture]; lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS - 1]; else walllights = scalelight[lightnum]; maskedtexturecol = ds->maskedtexturecol; rw_scalestep = ds->scalestep; spryscale = ds->scale1 + (x1 - ds->x1) * rw_scalestep; mfloorclip = ds->sprbottomclip; mceilingclip = ds->sprtopclip; // // find positioning // if (curline->linedef->flags & ML_DONTPEGBOTTOM) { dc_texturemid = frontsector->floorheight > backsector->floorheight ? frontsector->floorheight : backsector->floorheight; dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; } else { dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight ? frontsector->ceilingheight : backsector->ceilingheight; dc_texturemid = dc_texturemid - viewz; } dc_texturemid += curline->sidedef->rowoffset; if (fixedcolormap) dc_colormap = fixedcolormap; // // draw the columns // for (dc_x = x1; dc_x <= x2; dc_x++) { // calculate lighting if (maskedtexturecol[dc_x] != INT_MAX) // [crispy] 32-bit integer math { if (!fixedcolormap) { index = spryscale >> (LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; dc_colormap = walllights[index]; } sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); dc_iscale = 0xffffffffu / (unsigned) spryscale; // // draw the texture // col = (column_t *) ((byte *) R_GetColumn(texnum, maskedtexturecol[dc_x]) - 3); R_DrawMaskedColumn(col, -1); maskedtexturecol[dc_x] = INT_MAX; // [crispy] 32-bit integer math } spryscale += rw_scalestep; } } /* ================ = = R_RenderSegLoop = = Draws zero, one, or two textures (and possibly a masked texture) for walls = Can draw or mark the starting pixel of floor and ceiling textures = = CALLED: CORE LOOPING ROUTINE ================ */ #define HEIGHTBITS 12 #define HEIGHTUNIT (1<> HEIGHTBITS; if (yl < ceilingclip[rw_x] + 1) yl = ceilingclip[rw_x] + 1; // no space above wall if (markceiling) { top = ceilingclip[rw_x] + 1; bottom = yl - 1; if (bottom >= floorclip[rw_x]) bottom = floorclip[rw_x] - 1; if (top <= bottom) { ceilingplane->top[rw_x] = top; ceilingplane->bottom[rw_x] = bottom; } } yh = bottomfrac >> HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x] - 1; if (markfloor) { top = yh + 1; bottom = floorclip[rw_x] - 1; if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x] + 1; if (top <= bottom) { floorplane->top[rw_x] = top; floorplane->bottom[rw_x] = bottom; } } // // texturecolumn and lighting are independent of wall tiers // if (segtextured) { // calculate texture offset angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT; texturecolumn = rw_offset - FixedMul(finetangent[angle], rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale >> (LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; dc_colormap = walllights[index]; dc_x = rw_x; dc_iscale = 0xffffffffu / (unsigned) rw_scale; } // // draw the wall tiers // if (midtexture) { // single sided line dc_yl = yl; dc_yh = yh; dc_texturemid = rw_midtexturemid; dc_source = R_GetColumn(midtexture, texturecolumn); dc_texheight = textureheight[midtexture]>>FRACBITS; colfunc(); ceilingclip[rw_x] = viewheight; floorclip[rw_x] = -1; } else { // two sided line if (toptexture) { // top wall mid = pixhigh >> HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x] - 1; if (mid >= yl) { dc_yl = yl; dc_yh = mid; dc_texturemid = rw_toptexturemid; dc_source = R_GetColumn(toptexture, texturecolumn); dc_texheight = textureheight[toptexture]>>FRACBITS; colfunc(); ceilingclip[rw_x] = mid; } else ceilingclip[rw_x] = yl - 1; } else { // no top wall if (markceiling) ceilingclip[rw_x] = yl - 1; } if (bottomtexture) { // bottom wall mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; pixlow += pixlowstep; if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x] + 1; // no space above wall if (mid <= yh) { dc_yl = mid; dc_yh = yh; dc_texturemid = rw_bottomtexturemid; dc_source = R_GetColumn(bottomtexture, texturecolumn); dc_texheight = textureheight[bottomtexture]>>FRACBITS; colfunc(); floorclip[rw_x] = mid; } else floorclip[rw_x] = yh + 1; } else { // no bottom wall if (markfloor) floorclip[rw_x] = yh + 1; } if (maskedtexture) { // save texturecol for backdrawing of masked mid texture maskedtexturecol[rw_x] = texturecolumn; } } rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } } /* ===================== = = R_StoreWallRange = = A wall segment will be drawn between start and stop pixels (inclusive) = ====================== */ void R_StoreWallRange(int start, int stop) { fixed_t hyp; fixed_t sineval; angle_t distangle, offsetangle; fixed_t vtop; int lightnum; // [crispy] remove MAXDRAWSEGS limit if (ds_p == &drawsegs[numdrawsegs]) { int numdrawsegs_old = numdrawsegs; numdrawsegs = numdrawsegs ? 2 * numdrawsegs : MAXDRAWSEGS; drawsegs = I_Realloc(drawsegs, numdrawsegs * sizeof(*drawsegs)); memset(drawsegs + numdrawsegs_old, 0, (numdrawsegs - numdrawsegs_old) * sizeof(*drawsegs)); ds_p = drawsegs + numdrawsegs_old; if (numdrawsegs_old) fprintf(stderr, "R_StoreWallRange: Hit MAXDRAWSEGS limit at %d, raised to %d.\n", numdrawsegs_old, numdrawsegs); } #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->angle + ANG90; offsetangle = abs(rw_normalangle - rw_angle1); if (offsetangle > ANG90) offsetangle = ANG90; distangle = ANG90 - offsetangle; hyp = R_PointToDist(curline->v1->x, curline->v1->y); sineval = finesine[distangle >> ANGLETOFINESHIFT]; rw_distance = FixedMul(hyp, sineval); ds_p->x1 = rw_x = start; ds_p->x2 = stop; ds_p->curline = curline; rw_stopx = stop + 1; // // calculate scale at both ends and step // ds_p->scale1 = rw_scale = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[start]); if (stop > start) { ds_p->scale2 = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[stop]); ds_p->scalestep = rw_scalestep = (ds_p->scale2 - rw_scale) / (stop - start); } else { // // try to fix the stretched line bug // #if 0 if (rw_distance < FRACUNIT / 2) { fixed_t trx, try; fixed_t gxt, gyt; trx = curline->v1->x - viewx; try = curline->v1->y - viewy; gxt = FixedMul(trx, viewcos); gyt = -FixedMul(try, viewsin); ds_p->scale1 = FixedDiv(projection, gxt - gyt); } #endif ds_p->scale2 = ds_p->scale1; } // // calculate texture boundaries and decide if floor / ceiling marks // are needed // worldtop = frontsector->ceilingheight - viewz; worldbottom = frontsector->floorheight - viewz; midtexture = toptexture = bottomtexture = maskedtexture = 0; ds_p->maskedtexturecol = NULL; if (!backsector) { // // single sided line // midtexture = texturetranslation[sidedef->midtexture]; // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; if (linedef->flags & ML_DONTPEGBOTTOM) { vtop = frontsector->floorheight + textureheight[sidedef->midtexture]; rw_midtexturemid = vtop - viewz; // bottom of texture at bottom } else rw_midtexturemid = worldtop; // top of texture at top rw_midtexturemid += sidedef->rowoffset; ds_p->silhouette = SIL_BOTH; ds_p->sprtopclip = screenheightarray; ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->tsilheight = INT_MIN; } else { // // two sided line // ds_p->sprtopclip = ds_p->sprbottomclip = NULL; ds_p->silhouette = 0; if (frontsector->floorheight > backsector->floorheight) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = frontsector->floorheight; } else if (backsector->floorheight > viewz) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = INT_MAX; // ds_p->sprbottomclip = negonearray; } if (frontsector->ceilingheight < backsector->ceilingheight) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = frontsector->ceilingheight; } else if (backsector->ceilingheight < viewz) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT_MIN; // ds_p->sprtopclip = screenheightarray; } if (backsector->ceilingheight <= frontsector->floorheight) { ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->silhouette |= SIL_BOTTOM; } if (backsector->floorheight >= frontsector->ceilingheight) { ds_p->sprtopclip = screenheightarray; ds_p->tsilheight = INT_MIN; ds_p->silhouette |= SIL_TOP; } worldhigh = backsector->ceilingheight - viewz; worldlow = backsector->floorheight - viewz; // hack to allow height changes in outdoor areas if (frontsector->ceilingpic == skyflatnum && backsector->ceilingpic == skyflatnum) worldtop = worldhigh; if (worldlow != worldbottom || backsector->floorpic != frontsector->floorpic || backsector->lightlevel != frontsector->lightlevel) markfloor = true; else markfloor = false; // same plane on both sides if (worldhigh != worldtop || backsector->ceilingpic != frontsector->ceilingpic || backsector->lightlevel != frontsector->lightlevel) markceiling = true; else markceiling = false; // same plane on both sides if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) markceiling = markfloor = true; // closed door if (worldhigh < worldtop) { // top texture toptexture = texturetranslation[sidedef->toptexture]; if (linedef->flags & ML_DONTPEGTOP) rw_toptexturemid = worldtop; // top of texture at top else { vtop = backsector->ceilingheight + textureheight[sidedef->toptexture]; rw_toptexturemid = vtop - viewz; // bottom of texture } } if (worldlow > worldbottom) { // bottom texture bottomtexture = texturetranslation[sidedef->bottomtexture]; if (linedef->flags & ML_DONTPEGBOTTOM) { // bottom of texture at bottom rw_bottomtexturemid = worldtop; // top of texture at top } else // top of texture at top rw_bottomtexturemid = worldlow; } rw_toptexturemid += sidedef->rowoffset; rw_bottomtexturemid += sidedef->rowoffset; // // allocate space for masked texture tables // if (sidedef->midtexture) { // masked midtexture maskedtexture = true; ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; lastopening += rw_stopx - rw_x; } } // // calculate rw_offset (only needed for textured lines) // segtextured = midtexture | toptexture | bottomtexture | maskedtexture; if (segtextured) { offsetangle = rw_normalangle - rw_angle1; if (offsetangle > ANG180) offsetangle = -offsetangle; if (offsetangle > ANG90) offsetangle = ANG90; sineval = finesine[offsetangle >> ANGLETOFINESHIFT]; rw_offset = FixedMul(hyp, sineval); if (rw_normalangle - rw_angle1 < ANG180) rw_offset = -rw_offset; rw_offset += sidedef->textureoffset + curline->offset; rw_centerangle = ANG90 + viewangle - rw_normalangle; // // calculate light table // use different light tables for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally if (!fixedcolormap) { lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS - 1]; else walllights = scalelight[lightnum]; } } // // if a floor / ceiling plane is on the wrong side of the view plane // it is definately invisible and doesn't need to be marked // if (frontsector->floorheight >= viewz) markfloor = false; // above view plane if (frontsector->ceilingheight <= viewz && frontsector->ceilingpic != skyflatnum) markceiling = false; // below view plane // // calculate incremental stepping values for texture edges // worldtop >>= 4; worldbottom >>= 4; topstep = -FixedMul(rw_scalestep, worldtop); topfrac = (centeryfrac >> 4) - FixedMul(worldtop, rw_scale); bottomstep = -FixedMul(rw_scalestep, worldbottom); bottomfrac = (centeryfrac >> 4) - FixedMul(worldbottom, rw_scale); if (backsector) { worldhigh >>= 4; worldlow >>= 4; if (worldhigh < worldtop) { pixhigh = (centeryfrac >> 4) - FixedMul(worldhigh, rw_scale); pixhighstep = -FixedMul(rw_scalestep, worldhigh); } if (worldlow > worldbottom) { pixlow = (centeryfrac >> 4) - FixedMul(worldlow, rw_scale); pixlowstep = -FixedMul(rw_scalestep, worldlow); } } // // render it // if (markceiling) ceilingplane = R_CheckPlane(ceilingplane, rw_x, rw_stopx - 1); if (markfloor) floorplane = R_CheckPlane(floorplane, rw_x, rw_stopx - 1); R_RenderSegLoop(); // // save sprite clipping info // if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) { memcpy(lastopening, ceilingclip + start, sizeof(lastopening) * (rw_stopx - start)); // [crispy] 32-bit integer math 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)); // [crispy] 32-bit integer math 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++; } crispy-doom-crispy-doom-5.6.4/src/heretic/r_things.c000066400000000000000000000640541360717211000224400ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // R_things.c #include #include #include "doomdef.h" #include "deh_str.h" #include "i_swap.h" #include "i_system.h" #include "r_local.h" typedef struct { int x1, x2; int column; int topclip; int bottomclip; } maskdraw_t; /* Sprite rotation 0 is facing the viewer, rotation 1 is one angle turn CLOCKWISE around the axis. This is not the same as the angle, which increases counter clockwise (protractor). There was a lot of stuff grabbed wrong, so I changed it... */ fixed_t pspritescale, pspriteiscale; lighttable_t **spritelights; // constant arrays used for psprite clipping and initializing clipping int negonearray[MAXWIDTH]; // [crispy] 32-bit integer math int screenheightarray[MAXWIDTH]; // [crispy] 32-bit integer math /* =============================================================================== INITIALIZATION FUNCTIONS =============================================================================== */ // variables used to look up and range check thing_t sprites patches spritedef_t *sprites; int numsprites; spriteframe_t sprtemp[26]; int maxframe; const char *spritename; /* ================= = = R_InstallSpriteLump = = Local function for R_InitSprites ================= */ void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation, boolean flipped) { int r; if (frame >= 26 || rotation > 8) I_Error("R_InstallSpriteLump: Bad frame characters in lump %i", lump); if ((int) frame > maxframe) maxframe = frame; if (rotation == 0) { // the lump should be used for all rotations if (sprtemp[frame].rotate == false) I_Error("R_InitSprites: Sprite %s frame %c has multip rot=0 lump", spritename, 'A' + frame); if (sprtemp[frame].rotate == true) I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump", spritename, 'A' + frame); sprtemp[frame].rotate = false; for (r = 0; r < 8; r++) { sprtemp[frame].lump[r] = lump - firstspritelump; sprtemp[frame].flip[r] = (byte) flipped; } return; } // the lump is only used for one rotation if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump", spritename, 'A' + frame); sprtemp[frame].rotate = true; rotation--; // make 0 based if (sprtemp[frame].lump[rotation] != -1) I_Error ("R_InitSprites: Sprite %s : %c : %c has two lumps mapped to it", spritename, 'A' + frame, '1' + rotation); sprtemp[frame].lump[rotation] = lump - firstspritelump; sprtemp[frame].flip[rotation] = (byte) flipped; } /* ================= = = R_InitSpriteDefs = = Pass a null terminated list of sprite names (4 chars exactly) to be used = Builds the sprite rotation matrixes to account for horizontally flipped = sprites. Will report an error if the lumps are inconsistant =Only called at startup = = Sprite lump names are 4 characters for the actor, a letter for the frame, = and a number for the rotation, A sprite that is flippable will have an = additional letter/number appended. The rotation character can be 0 to = signify no rotations ================= */ void R_InitSpriteDefs(const char **namelist) { const char **check; int i, l, frame, rotation; int start, end; // count the number of sprite names check = namelist; while (*check != NULL) check++; numsprites = check - namelist; if (!numsprites) return; sprites = Z_Malloc(numsprites * sizeof(*sprites), PU_STATIC, NULL); start = firstspritelump - 1; end = lastspritelump + 1; // scan all the lump names for each of the names, noting the highest // frame letter // Just compare 4 characters as ints for (i = 0; i < numsprites; i++) { spritename = DEH_String(namelist[i]); memset(sprtemp, -1, sizeof(sprtemp)); maxframe = -1; // // scan the lumps, filling in the frames for whatever is found // for (l = start + 1; l < end; l++) if (!strncasecmp(lumpinfo[l]->name, spritename, 4)) { frame = lumpinfo[l]->name[4] - 'A'; rotation = lumpinfo[l]->name[5] - '0'; R_InstallSpriteLump(l, frame, rotation, false); if (lumpinfo[l]->name[6]) { frame = lumpinfo[l]->name[6] - 'A'; rotation = lumpinfo[l]->name[7] - '0'; R_InstallSpriteLump(l, frame, rotation, true); } } // // check the frames that were found for completeness // if (maxframe == -1) { //continue; sprites[i].numframes = 0; if (gamemode == shareware) continue; I_Error("R_InitSprites: No lumps found for sprite %s", spritename); } maxframe++; for (frame = 0; frame < maxframe; frame++) { switch ((int) sprtemp[frame].rotate) { case -1: // no rotations were found for that frame at all I_Error("R_InitSprites: No patches found for %s frame %c", spritename, frame + 'A'); case 0: // only the first rotation is needed break; case 1: // must have all 8 frames for (rotation = 0; rotation < 8; rotation++) if (sprtemp[frame].lump[rotation] == -1) I_Error ("R_InitSprites: Sprite %s frame %c is missing rotations", spritename, frame + 'A'); } } // // allocate space for the frames present and copy sprtemp to it // sprites[i].numframes = maxframe; sprites[i].spriteframes = Z_Malloc(maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); memcpy(sprites[i].spriteframes, sprtemp, maxframe * sizeof(spriteframe_t)); } } /* =============================================================================== GAME FUNCTIONS =============================================================================== */ vissprite_t *vissprites = NULL, *vissprite_p; int newvissprite; static int numvissprites; /* =================== = = R_InitSprites = = Called at program start =================== */ void R_InitSprites(const char **namelist) { int i; for (i = 0; i < SCREENWIDTH; i++) { negonearray[i] = -1; } R_InitSpriteDefs(namelist); } /* =================== = = R_ClearSprites = = Called at frame start =================== */ void R_ClearSprites(void) { vissprite_p = vissprites; } /* =================== = = R_NewVisSprite = =================== */ vissprite_t overflowsprite; vissprite_t *R_NewVisSprite(void) { // [crispy] remove MAXVISSPRITE limit if (vissprite_p == &vissprites[numvissprites]) { static int cap; int numvissprites_old = numvissprites; // [crispy] cap MAXVISSPRITES limit at 4096 if (!cap && numvissprites == 32 * MAXVISSPRITES) { fprintf(stderr, "R_NewVisSprite: MAXVISSPRITES limit capped at %d.\n", numvissprites); cap++; } if (cap) return &overflowsprite; numvissprites = numvissprites ? 2 * numvissprites : MAXVISSPRITES; vissprites = I_Realloc(vissprites, numvissprites * sizeof(*vissprites)); memset(vissprites + numvissprites_old, 0, (numvissprites - numvissprites_old) * sizeof(*vissprites)); vissprite_p = vissprites + numvissprites_old; if (numvissprites_old) fprintf(stderr, "R_NewVisSprite: Hit MAXVISSPRITES limit at %d, raised to %d.\n", numvissprites_old, numvissprites); } vissprite_p++; return vissprite_p - 1; } /* ================ = = R_DrawMaskedColumn = = Used for sprites and masked mid textures ================ */ int *mfloorclip; // [crispy] 32-bit integer math int *mceilingclip; // [crispy] 32-bit integer math fixed_t spryscale; fixed_t sprtopscreen; fixed_t sprbotscreen; void R_DrawMaskedColumn(column_t * column, signed int baseclip) { int topscreen, bottomscreen; fixed_t basetexturemid; basetexturemid = dc_texturemid; dc_texheight = 0; for (; column->topdelta != 0xff;) { // calculate unclipped screen coordinates for post topscreen = sprtopscreen + spryscale * column->topdelta; bottomscreen = topscreen + spryscale * column->length; dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS; dc_yh = (bottomscreen - 1) >> FRACBITS; if (dc_yh >= mfloorclip[dc_x]) dc_yh = mfloorclip[dc_x] - 1; if (dc_yl <= mceilingclip[dc_x]) dc_yl = mceilingclip[dc_x] + 1; if (dc_yh >= baseclip && baseclip != -1) dc_yh = baseclip; if (dc_yl <= dc_yh) { dc_source = (byte *) column + 3; dc_texturemid = basetexturemid - (column->topdelta << FRACBITS); // dc_source = (byte *)column + 3 - column->topdelta; colfunc(); // either R_DrawColumn or R_DrawTLColumn } column = (column_t *) ((byte *) column + column->length + 4); } dc_texturemid = basetexturemid; } /* ================ = = R_DrawVisSprite = = mfloorclip and mceilingclip should also be set ================ */ void R_DrawVisSprite(vissprite_t * vis, int x1, int x2) { column_t *column; int texturecolumn; fixed_t frac; patch_t *patch; fixed_t baseclip; patch = W_CacheLumpNum(vis->patch + firstspritelump, PU_CACHE); dc_colormap = vis->colormap; // if(!dc_colormap) // colfunc = tlcolfunc; // NULL colormap = shadow draw if (vis->mobjflags & MF_SHADOW) { if (vis->mobjflags & MF_TRANSLATION) { colfunc = R_DrawTranslatedTLColumn; dc_translation = translationtables - 256 + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); } else { // Draw using shadow column function colfunc = tlcolfunc; } } else if (vis->mobjflags & MF_TRANSLATION) { // Draw using translated column function colfunc = R_DrawTranslatedColumn; dc_translation = translationtables - 256 + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); } dc_iscale = abs(vis->xiscale) >> detailshift; dc_texturemid = vis->texturemid; frac = vis->startfrac; spryscale = vis->scale; sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); // check to see if weapon is a vissprite if (vis->psprite) { dc_texturemid += FixedMul(((centery - viewheight / 2) << FRACBITS), vis->xiscale); sprtopscreen += (viewheight / 2 - centery) << FRACBITS; } if (vis->footclip && !vis->psprite) { sprbotscreen = sprtopscreen + FixedMul(SHORT(patch->height) << FRACBITS, spryscale); baseclip = (sprbotscreen - FixedMul(vis->footclip << FRACBITS, spryscale)) >> FRACBITS; } else { baseclip = -1; } for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) { texturecolumn = frac >> FRACBITS; #ifdef RANGECHECK if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) I_Error("R_DrawSpriteRange: bad texturecolumn"); #endif column = (column_t *) ((byte *) patch + LONG(patch->columnofs[texturecolumn])); R_DrawMaskedColumn(column, baseclip); } colfunc = basecolfunc; } /* =================== = = R_ProjectSprite = = Generates a vissprite for a thing if it might be visible = =================== */ void R_ProjectSprite(mobj_t * thing) { fixed_t trx, try; fixed_t gxt, gyt; fixed_t tx, tz; fixed_t xscale; int x1, x2; spritedef_t *sprdef; spriteframe_t *sprframe; int lump; unsigned rot; boolean flip; int index; vissprite_t *vis; angle_t ang; fixed_t iscale; if (thing->flags2 & MF2_DONTDRAW) { // Never make a vissprite when MF2_DONTDRAW is flagged. return; } // // transform the origin point // trx = thing->x - viewx; try = thing->y - viewy; gxt = FixedMul(trx, viewcos); gyt = -FixedMul(try, viewsin); tz = gxt - gyt; if (tz < MINZ) return; // thing is behind view plane xscale = FixedDiv(projection, tz); gxt = -FixedMul(trx, viewsin); gyt = FixedMul(try, viewcos); tx = -(gyt + gxt); if (abs(tx) > (tz << 2)) return; // too far off the side // // decide which patch to use for sprite reletive to player // #ifdef RANGECHECK if ((unsigned) thing->sprite >= 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 sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; if (sprframe->rotate) { // choose a different rotation based on player view ang = R_PointToAngle(thing->x, thing->y); rot = (ang - thing->angle + (unsigned) (ANG45 / 2) * 9) >> 29; lump = sprframe->lump[rot]; flip = (boolean) sprframe->flip[rot]; } else { // use single rotation for all views lump = sprframe->lump[0]; flip = (boolean) sprframe->flip[0]; } // // calculate edges of the shape // tx -= spriteoffset[lump]; x1 = (centerxfrac + FixedMul(tx, xscale)) >> FRACBITS; if (x1 > viewwidth) return; // off the right side tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul(tx, xscale)) >> FRACBITS) - 1; if (x2 < 0) return; // off the left side // // store information in a vissprite // vis = R_NewVisSprite(); vis->mobjflags = thing->flags; vis->psprite = false; vis->scale = xscale << detailshift; vis->gx = thing->x; vis->gy = thing->y; vis->gz = thing->z; vis->gzt = thing->z + spritetopoffset[lump]; // foot clipping if (thing->flags2 & MF2_FEETARECLIPPED && thing->z <= thing->subsector->sector->floorheight) { vis->footclip = 10; } else vis->footclip = 0; vis->texturemid = vis->gzt - viewz - (vis->footclip << FRACBITS); vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; iscale = FixedDiv(FRACUNIT, xscale); if (flip) { vis->startfrac = spritewidth[lump] - 1; vis->xiscale = -iscale; } else { vis->startfrac = 0; vis->xiscale = iscale; } if (vis->x1 > x1) vis->startfrac += vis->xiscale * (vis->x1 - x1); vis->patch = lump; // // get light level // // if (thing->flags & MF_SHADOW) // vis->colormap = NULL; // shadow draw // else ... if (fixedcolormap) vis->colormap = fixedcolormap; // fixed map else if (thing->frame & FF_FULLBRIGHT) vis->colormap = colormaps; // full bright else { // diminished light index = xscale >> (LIGHTSCALESHIFT - detailshift + crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; vis->colormap = spritelights[index]; } } /* ======================== = = R_AddSprites = ======================== */ void R_AddSprites(sector_t * sec) { mobj_t *thing; int lightnum; if (sec->validcount == validcount) return; // already added sec->validcount = validcount; lightnum = (sec->lightlevel >> LIGHTSEGSHIFT) + extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS - 1]; else spritelights = scalelight[lightnum]; for (thing = sec->thinglist; thing; thing = thing->snext) R_ProjectSprite(thing); } /* ======================== = = R_DrawPSprite = ======================== */ int PSpriteSY[NUMWEAPONS] = { 0, // staff 5 * FRACUNIT, // goldwand 15 * FRACUNIT, // crossbow 15 * FRACUNIT, // blaster 15 * FRACUNIT, // skullrod 15 * FRACUNIT, // phoenix rod 15 * FRACUNIT, // mace 15 * FRACUNIT, // gauntlets 15 * FRACUNIT // beak }; void R_DrawPSprite(pspdef_t * psp) { fixed_t tx; int x1, x2; spritedef_t *sprdef; spriteframe_t *sprframe; int lump; boolean flip; vissprite_t *vis, avis; int tempangle; // // decide which patch to use // #ifdef RANGECHECK if ((unsigned) psp->state->sprite >= 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 : %i ", psp->state->sprite, psp->state->frame); #endif sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; lump = sprframe->lump[0]; flip = (boolean) sprframe->flip[0]; // // calculate edges of the shape // tx = psp->sx - 160 * FRACUNIT; tx -= spriteoffset[lump]; if (viewangleoffset) { tempangle = ((centerxfrac / 1024) * (viewangleoffset >> ANGLETOFINESHIFT)); } else { tempangle = 0; } x1 = (centerxfrac + FixedMul(tx, pspritescale) + tempangle) >> FRACBITS; if (x1 > viewwidth) return; // off the right side tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul(tx, pspritescale) + tempangle) >> FRACBITS) - 1; if (x2 < 0) return; // off the left side // // store information in a vissprite // vis = &avis; vis->mobjflags = 0; vis->psprite = true; vis->texturemid = (BASEYCENTER << FRACBITS) /* + FRACUNIT / 2 */ - (psp->sy - spritetopoffset[lump]); if (viewheight == SCREENHEIGHT) { vis->texturemid -= PSpriteSY[players[consoleplayer].readyweapon]; } vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; vis->scale = pspritescale << detailshift; if (flip) { vis->xiscale = -pspriteiscale; vis->startfrac = spritewidth[lump] - 1; } else { vis->xiscale = pspriteiscale; vis->startfrac = 0; } if (vis->x1 > x1) vis->startfrac += vis->xiscale * (vis->x1 - x1); vis->patch = lump; if (viewplayer->powers[pw_invisibility] > 4 * 32 || viewplayer->powers[pw_invisibility] & 8) { // Invisibility vis->colormap = spritelights[MAXLIGHTSCALE - 1]; vis->mobjflags |= MF_SHADOW; } else if (fixedcolormap) { // Fixed color vis->colormap = fixedcolormap; } else if (psp->state->frame & FF_FULLBRIGHT) { // Full bright vis->colormap = colormaps; } else { // local light vis->colormap = spritelights[MAXLIGHTSCALE - 1]; } R_DrawVisSprite(vis, vis->x1, vis->x2); } /* ======================== = = R_DrawPlayerSprites = ======================== */ void R_DrawPlayerSprites(void) { int i, lightnum; pspdef_t *psp; // // get light level // lightnum = (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) + extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS - 1]; else spritelights = scalelight[lightnum]; // // clip to screen bounds // mfloorclip = screenheightarray; mceilingclip = negonearray; // // add all active psprites // for (i = 0, psp = viewplayer->psprites; i < NUMPSPRITES; i++, psp++) if (psp->state) R_DrawPSprite(psp); } /* ======================== = = R_SortVisSprites = ======================== */ vissprite_t vsprsortedhead; void R_SortVisSprites(void) { int i, count; vissprite_t *ds, *best; vissprite_t unsorted; fixed_t bestscale; count = vissprite_p - vissprites; unsorted.next = unsorted.prev = &unsorted; if (!count) return; for (ds = vissprites; ds < vissprite_p; ds++) { ds->next = ds + 1; ds->prev = ds - 1; } vissprites[0].prev = &unsorted; unsorted.next = &vissprites[0]; (vissprite_p - 1)->next = &unsorted; unsorted.prev = vissprite_p - 1; // // pull the vissprites out by scale // best = 0; // shut up the compiler warning vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; for (i = 0; i < count; i++) { bestscale = INT_MAX; for (ds = unsorted.next; ds != &unsorted; ds = ds->next) { if (ds->scale < bestscale) { bestscale = ds->scale; best = ds; } } best->next->prev = best->prev; best->prev->next = best->next; best->next = &vsprsortedhead; best->prev = vsprsortedhead.prev; vsprsortedhead.prev->next = best; vsprsortedhead.prev = best; } } /* ======================== = = R_DrawSprite = ======================== */ void R_DrawSprite(vissprite_t * spr) { drawseg_t *ds; int clipbot[MAXWIDTH], cliptop[MAXWIDTH]; // [crispy] 32-bit integer math int x, r1, r2; fixed_t scale, lowscale; int silhouette; for (x = spr->x1; x <= spr->x2; x++) clipbot[x] = cliptop[x] = -2; // // scan drawsegs from end to start for obscuring segs // the first drawseg that has a greater scale is the clip seg // for (ds = ds_p - 1; ds >= drawsegs; ds--) { // // determine if the drawseg obscures the sprite // if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || (!ds->silhouette && !ds->maskedtexturecol)) continue; // doesn't cover sprite r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; if (ds->scale1 > ds->scale2) { lowscale = ds->scale2; scale = ds->scale1; } else { lowscale = ds->scale1; scale = ds->scale2; } if (scale < spr->scale || (lowscale < spr->scale && !R_PointOnSegSide(spr->gx, spr->gy, ds->curline))) { if (ds->maskedtexturecol) // masked mid texture R_RenderMaskedSegRange(ds, r1, r2); continue; // seg is behind sprite } // // clip this piece of the sprite // silhouette = ds->silhouette; if (spr->gz >= ds->bsilheight) silhouette &= ~SIL_BOTTOM; if (spr->gzt <= ds->tsilheight) silhouette &= ~SIL_TOP; if (silhouette == 1) { // bottom sil for (x = r1; x <= r2; x++) if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; } else if (silhouette == 2) { // top sil for (x = r1; x <= r2; x++) if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } else if (silhouette == 3) { // both for (x = r1; x <= r2; x++) { if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } } } // // all clipping has been performed, so draw the sprite // // check for unclipped columns for (x = spr->x1; x <= spr->x2; x++) { if (clipbot[x] == -2) clipbot[x] = viewheight; if (cliptop[x] == -2) cliptop[x] = -1; } mfloorclip = clipbot; mceilingclip = cliptop; R_DrawVisSprite(spr, spr->x1, spr->x2); } /* ======================== = = R_DrawMasked = ======================== */ void R_DrawMasked(void) { vissprite_t *spr; drawseg_t *ds; R_SortVisSprites(); if (vissprite_p > vissprites) { // draw all vissprites back to front for (spr = vsprsortedhead.next; spr != &vsprsortedhead; spr = spr->next) R_DrawSprite(spr); } // // render any remaining masked mid textures // for (ds = ds_p - 1; ds >= drawsegs; ds--) if (ds->maskedtexturecol) R_RenderMaskedSegRange(ds, ds->x1, ds->x2); // // draw the psprites on top of everything // // Added for the sideviewing with an external device if (viewangleoffset <= 1024 << ANGLETOFINESHIFT || viewangleoffset >= -(1024 << ANGLETOFINESHIFT)) { // don't draw on side views R_DrawPlayerSprites(); } // if (!viewangleoffset) // don't draw on side views // R_DrawPlayerSprites (); } crispy-doom-crispy-doom-5.6.4/src/heretic/s_sound.c000066400000000000000000000347511360717211000222760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "doomdef.h" #include "i_system.h" #include "m_random.h" #include "sounds.h" #include "s_sound.h" #include "i_sound.h" #include "r_local.h" #include "p_local.h" #include "sounds.h" #include "w_wad.h" #include "z_zone.h" /* =============================================================================== MUSIC & SFX API =============================================================================== */ void S_ShutDown(void); boolean S_StopSoundID(int sound_id, int priority); static channel_t channel[MAX_CHANNELS]; static void *rs; // Handle for the registered song int mus_song = -1; int mus_lumpnum; void *mus_sndptr; byte *soundCurve; int snd_MaxVolume = 10; int snd_MusicVolume = 10; int snd_Channels = 16; int AmbChan; void S_Start(void) { int i; S_StartSong((gameepisode - 1) * 9 + gamemap - 1, true); //stop all sounds for (i = 0; i < snd_Channels; i++) { if (channel[i].handle) { S_StopSound(channel[i].mo); } } memset(channel, 0, 8 * sizeof(channel_t)); } void S_StartSong(int song, boolean loop) { int mus_len; if (song == mus_song) { // don't replay an old song return; } if (rs != NULL) { I_StopSong(); I_UnRegisterSong(rs); } if (song < mus_e1m1 || song > NUMMUSIC) { return; } mus_lumpnum = W_GetNumForName(S_music[song].name); mus_sndptr = W_CacheLumpNum(mus_lumpnum, PU_MUSIC); mus_len = W_LumpLength(mus_lumpnum); rs = I_RegisterSong(mus_sndptr, mus_len); I_PlaySong(rs, loop); //'true' denotes endless looping. mus_song = song; } static mobj_t *GetSoundListener(void) { static degenmobj_t dummy_listener; // If we are at the title screen, the console player doesn't have an // object yet, so return a pointer to a static dummy listener instead. if (players[consoleplayer].mo != NULL) { return players[consoleplayer].mo; } else { dummy_listener.x = 0; dummy_listener.y = 0; dummy_listener.z = 0; return (mobj_t *) &dummy_listener; } } void S_StartSound(void *_origin, int sound_id) { mobj_t *origin = _origin; mobj_t *listener; int dist, vol; int i; int priority; int sep; int angle; int absx; int absy; static int sndcount = 0; int chan; listener = GetSoundListener(); if (sound_id == 0 || snd_MaxVolume == 0) return; if (origin == NULL) { origin = listener; } // calculate the distance before other stuff so that we can throw out // sounds that are beyond the hearing range. absx = abs(origin->x - listener->x); absy = abs(origin->y - listener->y); dist = absx + absy - (absx > absy ? absy >> 1 : absx >> 1); dist >>= FRACBITS; // dist = P_AproxDistance(origin->x-viewx, origin->y-viewy)>>FRACBITS; if (dist >= MAX_SND_DIST) { // dist = MAX_SND_DIST - 1; return; //sound is beyond the hearing range... } if (dist < 0) { dist = 0; } priority = S_sfx[sound_id].priority; priority *= (10 - (dist / 160)); if (!S_StopSoundID(sound_id, priority)) { return; // other sounds have greater priority } for (i = 0; i < snd_Channels; i++) { if (origin->player) { i = snd_Channels; break; // let the player have more than one sound. } if (origin == channel[i].mo) { // only allow other mobjs one sound S_StopSound(channel[i].mo); break; } } if (i >= snd_Channels) { if (sound_id >= sfx_wind) { if (AmbChan != -1 && S_sfx[sound_id].priority <= S_sfx[channel[AmbChan].sound_id].priority) { return; //ambient channel already in use } else { AmbChan = -1; } } for (i = 0; i < snd_Channels; i++) { if (channel[i].mo == NULL) { break; } } if (i >= snd_Channels) { //look for a lower priority sound to replace. sndcount++; if (sndcount >= snd_Channels) { sndcount = 0; } for (chan = 0; chan < snd_Channels; chan++) { i = (sndcount + chan) % snd_Channels; if (priority >= channel[i].priority) { chan = -1; //denote that sound should be replaced. break; } } if (chan != -1) { return; //no free channels. } else //replace the lower priority sound. { if (channel[i].handle) { if (I_SoundIsPlaying(channel[i].handle)) { I_StopSound(channel[i].handle); } if (S_sfx[channel[i].sound_id].usefulness > 0) { S_sfx[channel[i].sound_id].usefulness--; } if (AmbChan == i) { AmbChan = -1; } } } } } if (S_sfx[sound_id].lumpnum == 0) { S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]); } // calculate the volume based upon the distance from the sound origin. // vol = (snd_MaxVolume*16 + dist*(-snd_MaxVolume*16)/MAX_SND_DIST)>>9; vol = soundCurve[dist]; if (origin == listener) { sep = 128; } else { angle = R_PointToAngle2(listener->x, listener->y, origin->x, origin->y); angle = (angle - viewangle) >> 24; sep = angle * 2 - 128; if (sep < 64) sep = -sep; if (sep > 192) sep = 512 - sep; } channel[i].pitch = (byte) (NORM_PITCH + (M_Random() & 7) - (M_Random() & 7)); channel[i].handle = I_StartSound(&S_sfx[sound_id], i, vol, sep, channel[i].pitch); channel[i].mo = origin; channel[i].sound_id = sound_id; channel[i].priority = priority; if (sound_id >= sfx_wind) { AmbChan = i; } if (S_sfx[sound_id].usefulness == -1) { S_sfx[sound_id].usefulness = 1; } else { S_sfx[sound_id].usefulness++; } } void S_StartSoundAtVolume(void *_origin, int sound_id, int volume) { mobj_t *origin = _origin; mobj_t *listener; int i; listener = GetSoundListener(); if (sound_id == 0 || snd_MaxVolume == 0) return; if (origin == NULL) { origin = listener; } if (volume == 0) { return; } volume = (volume * (snd_MaxVolume + 1) * 8) >> 7; // no priority checking, as ambient sounds would be the LOWEST. for (i = 0; i < snd_Channels; i++) { if (channel[i].mo == NULL) { break; } } if (i >= snd_Channels) { return; } if (S_sfx[sound_id].lumpnum == 0) { S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]); } channel[i].pitch = (byte) (NORM_PITCH - (M_Random() & 3) + (M_Random() & 3)); channel[i].handle = I_StartSound(&S_sfx[sound_id], i, volume, 128, channel[i].pitch); channel[i].mo = origin; channel[i].sound_id = sound_id; channel[i].priority = 1; //super low priority. if (S_sfx[sound_id].usefulness == -1) { S_sfx[sound_id].usefulness = 1; } else { S_sfx[sound_id].usefulness++; } } boolean S_StopSoundID(int sound_id, int priority) { int i; int lp; //least priority int found; if (S_sfx[sound_id].numchannels == -1) { return (true); } lp = -1; //denote the argument sound_id found = 0; for (i = 0; i < snd_Channels; i++) { if (channel[i].sound_id == sound_id && channel[i].mo) { found++; //found one. Now, should we replace it?? if (priority >= channel[i].priority) { // if we're gonna kill one, then this'll be it lp = i; priority = channel[i].priority; } } } if (found < S_sfx[sound_id].numchannels) { return (true); } else if (lp == -1) { return (false); // don't replace any sounds } if (channel[lp].handle) { if (I_SoundIsPlaying(channel[lp].handle)) { I_StopSound(channel[lp].handle); } if (S_sfx[channel[i].sound_id].usefulness > 0) { S_sfx[channel[i].sound_id].usefulness--; } channel[lp].mo = NULL; } return (true); } void S_StopSound(void *_origin) { mobj_t *origin = _origin; int i; for (i = 0; i < snd_Channels; i++) { if (channel[i].mo == origin) { I_StopSound(channel[i].handle); if (S_sfx[channel[i].sound_id].usefulness > 0) { S_sfx[channel[i].sound_id].usefulness--; } channel[i].handle = 0; channel[i].mo = NULL; if (AmbChan == i) { AmbChan = -1; } } } } void S_SoundLink(mobj_t * oldactor, mobj_t * newactor) { int i; for (i = 0; i < snd_Channels; i++) { if (channel[i].mo == oldactor) channel[i].mo = newactor; } } void S_PauseSound(void) { I_PauseSong(); } void S_ResumeSound(void) { I_ResumeSong(); } void S_UpdateSounds(mobj_t * listener) { int i, dist, vol; int angle; int sep; int priority; int absx; int absy; I_UpdateSound(); listener = GetSoundListener(); if (snd_MaxVolume == 0) { return; } for (i = 0; i < snd_Channels; i++) { if (!channel[i].handle || S_sfx[channel[i].sound_id].usefulness == -1) { continue; } if (!I_SoundIsPlaying(channel[i].handle)) { if (S_sfx[channel[i].sound_id].usefulness > 0) { S_sfx[channel[i].sound_id].usefulness--; } channel[i].handle = 0; channel[i].mo = NULL; channel[i].sound_id = 0; if (AmbChan == i) { AmbChan = -1; } } if (channel[i].mo == NULL || channel[i].sound_id == 0 || channel[i].mo == listener || listener == NULL) { continue; } else { absx = abs(channel[i].mo->x - listener->x); absy = abs(channel[i].mo->y - listener->y); dist = absx + absy - (absx > absy ? absy >> 1 : absx >> 1); dist >>= FRACBITS; // dist = P_AproxDistance(channel[i].mo->x-listener->x, channel[i].mo->y-listener->y)>>FRACBITS; if (dist >= MAX_SND_DIST) { S_StopSound(channel[i].mo); continue; } if (dist < 0) dist = 0; // calculate the volume based upon the distance from the sound origin. // vol = (*((byte *)W_CacheLumpName("SNDCURVE", PU_CACHE)+dist)*(snd_MaxVolume*8))>>7; vol = soundCurve[dist]; angle = R_PointToAngle2(listener->x, listener->y, channel[i].mo->x, channel[i].mo->y); angle = (angle - viewangle) >> 24; sep = angle * 2 - 128; if (sep < 64) sep = -sep; if (sep > 192) sep = 512 - sep; // TODO: Pitch shifting. I_UpdateSoundParams(channel[i].handle, vol, sep); priority = S_sfx[channel[i].sound_id].priority; priority *= (10 - (dist >> 8)); channel[i].priority = priority; } } } void S_Init(void) { I_SetOPLDriverVer(opl_doom2_1_666); soundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL); if (snd_Channels > 8) { snd_Channels = 8; } I_SetMusicVolume(snd_MusicVolume * 8); S_SetMaxVolume(true); I_AtExit(S_ShutDown, true); // Heretic defaults to pitch-shifting on if (snd_pitchshift == -1) { snd_pitchshift = 1; } I_PrecacheSounds(S_sfx, NUMSFX); } void S_GetChannelInfo(SoundInfo_t * s) { int i; ChanInfo_t *c; s->channelCount = snd_Channels; s->musicVolume = snd_MusicVolume; s->soundVolume = snd_MaxVolume; for (i = 0; i < snd_Channels; i++) { c = &s->chan[i]; c->id = channel[i].sound_id; c->priority = channel[i].priority; c->name = S_sfx[c->id].name; c->mo = channel[i].mo; if (c->mo != NULL) { c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy) >> FRACBITS; } else { c->distance = 0; } } } void S_SetMaxVolume(boolean fullprocess) { int i; if (!fullprocess) { soundCurve[0] = (*((byte *) W_CacheLumpName("SNDCURVE", PU_CACHE)) * (snd_MaxVolume * 8)) >> 7; } else { for (i = 0; i < MAX_SND_DIST; i++) { soundCurve[i] = (*((byte *) W_CacheLumpName("SNDCURVE", PU_CACHE) + i) * (snd_MaxVolume * 8)) >> 7; } } } static boolean musicPaused; void S_SetMusicVolume(void) { I_SetMusicVolume(snd_MusicVolume * 8); if (snd_MusicVolume == 0) { I_PauseSong(); musicPaused = true; } else if (musicPaused) { musicPaused = false; I_ResumeSong(); } } void S_ShutDown(void) { I_StopSong(); I_UnRegisterSong(rs); I_ShutdownSound(); } crispy-doom-crispy-doom-5.6.4/src/heretic/s_sound.h000066400000000000000000000022401360717211000222670ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // soundst.h #ifndef __SOUNDSTH__ #define __SOUNDSTH__ extern int snd_MaxVolume; extern int snd_MusicVolume; void S_Start(void); void S_StartSound(void *origin, int sound_id); void S_StartSoundAtVolume(void *origin, int sound_id, int volume); void S_StopSound(void *origin); void S_PauseSound(void); void S_ResumeSound(void); void S_UpdateSounds(mobj_t * listener); void S_StartSong(int song, boolean loop); void S_Init(void); void S_GetChannelInfo(SoundInfo_t * s); void S_SetMaxVolume(boolean fullprocess); void S_SetMusicVolume(void); #endif crispy-doom-crispy-doom-5.6.4/src/heretic/sb_bar.c000066400000000000000000001051631360717211000220500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // SB_bar.c #include "doomdef.h" #include "deh_str.h" #include "i_video.h" #include "i_swap.h" #include "m_cheat.h" #include "m_misc.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" // Types typedef struct Cheat_s { void (*func) (player_t * player, struct Cheat_s * cheat); cheatseq_t *seq; } Cheat_t; // Private Functions static void DrawSoundInfo(void); static void ShadeLine(int x, int y, int height, int shade); static void ShadeChain(void); static void DrINumber(signed int val, int x, int y); static void DrBNumber(signed int val, int x, int y); static void DrawCommonBar(void); static void DrawMainBar(void); static void DrawInventoryBar(void); static void DrawFullScreenStuff(void); static boolean HandleCheats(byte key); static void CheatGodFunc(player_t * player, Cheat_t * cheat); static void CheatNoClipFunc(player_t * player, Cheat_t * cheat); static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat); static void CheatPowerFunc(player_t * player, Cheat_t * cheat); static void CheatHealthFunc(player_t * player, Cheat_t * cheat); static void CheatKeysFunc(player_t * player, Cheat_t * cheat); static void CheatSoundFunc(player_t * player, Cheat_t * cheat); static void CheatTickerFunc(player_t * player, Cheat_t * cheat); static void CheatArtifact1Func(player_t * player, Cheat_t * cheat); static void CheatArtifact2Func(player_t * player, Cheat_t * cheat); static void CheatArtifact3Func(player_t * player, Cheat_t * cheat); static void CheatWarpFunc(player_t * player, Cheat_t * cheat); static void CheatChickenFunc(player_t * player, Cheat_t * cheat); static void CheatMassacreFunc(player_t * player, Cheat_t * cheat); static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat); static void CheatIDDQDFunc(player_t * player, Cheat_t * cheat); // Public Data boolean DebugSound; // debug flag for displaying sound info boolean inventory; int curpos; int inv_ptr; int ArtifactFlash; static int DisplayTicker = 0; // Private Data static int HealthMarker; static int ChainWiggle; static player_t *CPlayer; int playpalette; patch_t *PatchLTFACE; patch_t *PatchRTFACE; patch_t *PatchBARBACK; patch_t *PatchCHAIN; patch_t *PatchSTATBAR; patch_t *PatchLIFEGEM; //patch_t *PatchEMPWEAP; //patch_t *PatchLIL4BOX; patch_t *PatchLTFCTOP; patch_t *PatchRTFCTOP; //patch_t *PatchARMORBOX; //patch_t *PatchARTIBOX; patch_t *PatchSELECTBOX; //patch_t *PatchKILLSPIC; //patch_t *PatchMANAPIC; //patch_t *PatchPOWERICN; patch_t *PatchINVLFGEM1; patch_t *PatchINVLFGEM2; patch_t *PatchINVRTGEM1; patch_t *PatchINVRTGEM2; patch_t *PatchINumbers[10]; patch_t *PatchNEGATIVE; patch_t *PatchSmNumbers[10]; patch_t *PatchBLACKSQ; patch_t *PatchINVBAR; patch_t *PatchARMCLEAR; patch_t *PatchCHAINBACK; //byte *ShadeTables; int FontBNumBase; int spinbooklump; int spinflylump; // Toggle god mode cheatseq_t CheatGodSeq = CHEAT("quicken", 0); // Toggle no clipping mode cheatseq_t CheatNoClipSeq = CHEAT("kitty", 0); // Get all weapons and ammo cheatseq_t CheatWeaponsSeq = CHEAT("rambo", 0); // Toggle tome of power cheatseq_t CheatPowerSeq = CHEAT("shazam", 0); // Get full health cheatseq_t CheatHealthSeq = CHEAT("ponce", 0); // Get all keys cheatseq_t CheatKeysSeq = CHEAT("skel", 0); // Toggle sound debug info cheatseq_t CheatSoundSeq = CHEAT("noise", 0); // Toggle ticker cheatseq_t CheatTickerSeq = CHEAT("ticker", 0); // Get an artifact 1st stage (ask for type) cheatseq_t CheatArtifact1Seq = CHEAT("gimme", 0); // Get an artifact 2nd stage (ask for count) cheatseq_t CheatArtifact2Seq = CHEAT("gimme", 1); // Get an artifact final stage cheatseq_t CheatArtifact3Seq = CHEAT("gimme", 2); // Warp to new level cheatseq_t CheatWarpSeq = CHEAT("engage", 2); // Save a screenshot cheatseq_t CheatChickenSeq = CHEAT("cockadoodledoo", 0); // Kill all monsters cheatseq_t CheatMassacreSeq = CHEAT("massacre", 0); cheatseq_t CheatIDKFASeq = CHEAT("idkfa", 0); cheatseq_t CheatIDDQDSeq = CHEAT("iddqd", 0); static Cheat_t Cheats[] = { {CheatGodFunc, &CheatGodSeq}, {CheatNoClipFunc, &CheatNoClipSeq}, {CheatWeaponsFunc, &CheatWeaponsSeq}, {CheatPowerFunc, &CheatPowerSeq}, {CheatHealthFunc, &CheatHealthSeq}, {CheatKeysFunc, &CheatKeysSeq}, {CheatSoundFunc, &CheatSoundSeq}, {CheatTickerFunc, &CheatTickerSeq}, {CheatArtifact1Func, &CheatArtifact1Seq}, {CheatArtifact2Func, &CheatArtifact2Seq}, {CheatArtifact3Func, &CheatArtifact3Seq}, {CheatWarpFunc, &CheatWarpSeq}, {CheatChickenFunc, &CheatChickenSeq}, {CheatMassacreFunc, &CheatMassacreSeq}, {CheatIDKFAFunc, &CheatIDKFASeq}, {CheatIDDQDFunc, &CheatIDDQDSeq}, {NULL, NULL} }; //--------------------------------------------------------------------------- // // PROC SB_Init // //--------------------------------------------------------------------------- void SB_Init(void) { int i; int startLump; PatchLTFACE = W_CacheLumpName(DEH_String("LTFACE"), PU_STATIC); PatchRTFACE = W_CacheLumpName(DEH_String("RTFACE"), PU_STATIC); PatchBARBACK = W_CacheLumpName(DEH_String("BARBACK"), PU_STATIC); PatchINVBAR = W_CacheLumpName(DEH_String("INVBAR"), PU_STATIC); PatchCHAIN = W_CacheLumpName(DEH_String("CHAIN"), PU_STATIC); if (deathmatch) { PatchSTATBAR = W_CacheLumpName(DEH_String("STATBAR"), PU_STATIC); } else { PatchSTATBAR = W_CacheLumpName(DEH_String("LIFEBAR"), PU_STATIC); } if (!netgame) { // single player game uses red life gem PatchLIFEGEM = W_CacheLumpName(DEH_String("LIFEGEM2"), PU_STATIC); } else { PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName(DEH_String("LIFEGEM0")) + consoleplayer, PU_STATIC); } PatchLTFCTOP = W_CacheLumpName(DEH_String("LTFCTOP"), PU_STATIC); PatchRTFCTOP = W_CacheLumpName(DEH_String("RTFCTOP"), PU_STATIC); PatchSELECTBOX = W_CacheLumpName(DEH_String("SELECTBOX"), PU_STATIC); PatchINVLFGEM1 = W_CacheLumpName(DEH_String("INVGEML1"), PU_STATIC); PatchINVLFGEM2 = W_CacheLumpName(DEH_String("INVGEML2"), PU_STATIC); PatchINVRTGEM1 = W_CacheLumpName(DEH_String("INVGEMR1"), PU_STATIC); PatchINVRTGEM2 = W_CacheLumpName(DEH_String("INVGEMR2"), PU_STATIC); PatchBLACKSQ = W_CacheLumpName(DEH_String("BLACKSQ"), PU_STATIC); PatchARMCLEAR = W_CacheLumpName(DEH_String("ARMCLEAR"), PU_STATIC); PatchCHAINBACK = W_CacheLumpName(DEH_String("CHAINBACK"), PU_STATIC); startLump = W_GetNumForName(DEH_String("IN0")); for (i = 0; i < 10; i++) { PatchINumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC); } PatchNEGATIVE = W_CacheLumpName(DEH_String("NEGNUM"), PU_STATIC); FontBNumBase = W_GetNumForName(DEH_String("FONTB16")); startLump = W_GetNumForName(DEH_String("SMALLIN0")); for (i = 0; i < 10; i++) { PatchSmNumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC); } playpalette = W_GetNumForName(DEH_String("PLAYPAL")); spinbooklump = W_GetNumForName(DEH_String("SPINBK0")); spinflylump = W_GetNumForName(DEH_String("SPFLY0")); } //--------------------------------------------------------------------------- // // PROC SB_Ticker // //--------------------------------------------------------------------------- void SB_Ticker(void) { int delta; int curHealth; if (leveltime & 1) { ChainWiggle = P_Random() & 1; } curHealth = players[consoleplayer].mo->health; if (curHealth < 0) { curHealth = 0; } if (curHealth < HealthMarker) { delta = (HealthMarker - curHealth) >> 2; if (delta < 1) { delta = 1; } else if (delta > 8) { delta = 8; } HealthMarker -= delta; } else if (curHealth > HealthMarker) { delta = (curHealth - HealthMarker) >> 2; if (delta < 1) { delta = 1; } else if (delta > 8) { delta = 8; } HealthMarker += delta; } } //--------------------------------------------------------------------------- // // PROC DrINumber // // Draws a three digit number. // //--------------------------------------------------------------------------- static void DrINumber(signed int val, int x, int y) { patch_t *patch; int oldval; oldval = val; if (val < 0) { if (val < -9) { V_DrawPatch(x + 1, y + 1, W_CacheLumpName(DEH_String("LAME"), PU_CACHE)); } else { val = -val; V_DrawPatch(x + 18, y, PatchINumbers[val]); V_DrawPatch(x + 9, y, PatchNEGATIVE); } return; } if (val > 99) { patch = PatchINumbers[val / 100]; V_DrawPatch(x, y, patch); } val = val % 100; if (val > 9 || oldval > 99) { patch = PatchINumbers[val / 10]; V_DrawPatch(x + 9, y, patch); } val = val % 10; patch = PatchINumbers[val]; V_DrawPatch(x + 18, y, patch); } //--------------------------------------------------------------------------- // // PROC DrBNumber // // Draws a three digit number using FontB // //--------------------------------------------------------------------------- static void DrBNumber(signed int val, int x, int y) { patch_t *patch; int xpos; int oldval; oldval = val; xpos = x; if (val < 0) { val = 0; } if (val > 99) { patch = W_CacheLumpNum(FontBNumBase + val / 100, PU_CACHE); V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } val = val % 100; xpos += 12; if (val > 9 || oldval > 99) { patch = W_CacheLumpNum(FontBNumBase + val / 10, PU_CACHE); V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } val = val % 10; xpos += 12; patch = W_CacheLumpNum(FontBNumBase + val, PU_CACHE); V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } //--------------------------------------------------------------------------- // // PROC DrSmallNumber // // Draws a small two digit number. // //--------------------------------------------------------------------------- static void DrSmallNumber(int val, int x, int y) { patch_t *patch; if (val == 1) { return; } if (val > 9) { patch = PatchSmNumbers[val / 10]; V_DrawPatch(x, y, patch); } val = val % 10; patch = PatchSmNumbers[val]; V_DrawPatch(x + 4, y, patch); } //--------------------------------------------------------------------------- // // PROC ShadeLine // //--------------------------------------------------------------------------- static void ShadeLine(int x, int y, int height, int shade) { byte *dest; byte *shades; x <<= crispy->hires; y <<= crispy->hires; height <<= crispy->hires; shades = colormaps + 9 * 256 + shade * 2 * 256; dest = I_VideoBuffer + y * SCREENWIDTH + x; while (height--) { if (crispy->hires) *(dest + 1) = *(shades + *dest); *(dest) = *(shades + *dest); dest += SCREENWIDTH; } } //--------------------------------------------------------------------------- // // PROC ShadeChain // //--------------------------------------------------------------------------- static void ShadeChain(void) { int i; for (i = 0; i < 16; i++) { ShadeLine(277 + i, 190, 10, i / 2); ShadeLine(19 + i, 190, 10, 7 - (i / 2)); } } //--------------------------------------------------------------------------- // // PROC DrawSoundInfo // // Displays sound debugging information. // //--------------------------------------------------------------------------- static void DrawSoundInfo(void) { int i; SoundInfo_t s; ChanInfo_t *c; char text[32]; int x; int y; int xPos[7] = { 1, 75, 112, 156, 200, 230, 260 }; if (leveltime & 16) { MN_DrTextA(DEH_String("*** SOUND DEBUG INFO ***"), xPos[0], 20); } S_GetChannelInfo(&s); if (s.channelCount == 0) { return; } x = 0; MN_DrTextA(DEH_String("NAME"), xPos[x++], 30); MN_DrTextA(DEH_String("MO.T"), xPos[x++], 30); MN_DrTextA(DEH_String("MO.X"), xPos[x++], 30); MN_DrTextA(DEH_String("MO.Y"), xPos[x++], 30); MN_DrTextA(DEH_String("ID"), xPos[x++], 30); MN_DrTextA(DEH_String("PRI"), xPos[x++], 30); MN_DrTextA(DEH_String("DIST"), xPos[x++], 30); for (i = 0; i < s.channelCount; i++) { c = &s.chan[i]; x = 0; y = 40 + i * 10; if (c->mo == NULL) { // Channel is unused MN_DrTextA(DEH_String("------"), xPos[0], y); continue; } M_snprintf(text, sizeof(text), "%s", c->name); M_ForceUppercase(text); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->mo->type); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->mo->x >> FRACBITS); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->mo->y >> FRACBITS); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->id); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->priority); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->distance); MN_DrTextA(text, xPos[x++], y); } UpdateState |= I_FULLSCRN; BorderNeedRefresh = true; } //--------------------------------------------------------------------------- // // PROC SB_Drawer // //--------------------------------------------------------------------------- char patcharti[][10] = { {"ARTIBOX"}, // none {"ARTIINVU"}, // invulnerability {"ARTIINVS"}, // invisibility {"ARTIPTN2"}, // health {"ARTISPHL"}, // superhealth {"ARTIPWBK"}, // tomeofpower {"ARTITRCH"}, // torch {"ARTIFBMB"}, // firebomb {"ARTIEGGC"}, // egg {"ARTISOAR"}, // fly {"ARTIATLP"} // teleport }; char ammopic[][10] = { {"INAMGLD"}, {"INAMBOW"}, {"INAMBST"}, {"INAMRAM"}, {"INAMPNX"}, {"INAMLOB"} }; int SB_state = -1; static int oldarti = 0; static int oldartiCount = 0; static int oldfrags = -9999; static int oldammo = -1; static int oldarmor = -1; static int oldweapon = -1; static int oldhealth = -1; static int oldlife = -1; static int oldkeys = -1; int playerkeys = 0; extern boolean automapactive; void SB_Drawer(void) { int frame; static boolean hitCenterFrame; // Sound info debug stuff if (DebugSound == true) { DrawSoundInfo(); } CPlayer = &players[consoleplayer]; if (viewheight == SCREENHEIGHT && !automapactive) { DrawFullScreenStuff(); SB_state = -1; } else { if (SB_state == -1) { V_DrawPatch(0, 158, PatchBARBACK); if (players[consoleplayer].cheats & CF_GODMODE) { V_DrawPatch(16, 167, W_CacheLumpName(DEH_String("GOD1"), PU_CACHE)); V_DrawPatch(287, 167, W_CacheLumpName(DEH_String("GOD2"), PU_CACHE)); } oldhealth = -1; } DrawCommonBar(); if (!inventory) { if (SB_state != 0) { // Main interface V_DrawPatch(34, 160, PatchSTATBAR); oldarti = 0; oldammo = -1; oldarmor = -1; oldweapon = -1; oldfrags = -9999; //can't use -1, 'cuz of negative frags oldlife = -1; oldkeys = -1; } DrawMainBar(); SB_state = 0; } else { if (SB_state != 1) { V_DrawPatch(34, 160, PatchINVBAR); } DrawInventoryBar(); SB_state = 1; } } SB_PaletteFlash(); // Flight icons if (CPlayer->powers[pw_flight]) { if (CPlayer->powers[pw_flight] > BLINKTHRESHOLD || !(CPlayer->powers[pw_flight] & 16)) { frame = (leveltime / 3) & 15; if (CPlayer->mo->flags2 & MF2_FLY) { if (hitCenterFrame && (frame != 15 && frame != 0)) { V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + 15, PU_CACHE)); } else { V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + frame, PU_CACHE)); hitCenterFrame = false; } } else { if (!hitCenterFrame && (frame != 15 && frame != 0)) { V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + frame, PU_CACHE)); hitCenterFrame = false; } else { V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + 15, PU_CACHE)); hitCenterFrame = true; } } BorderTopRefresh = true; UpdateState |= I_MESSAGES; } else { BorderTopRefresh = true; UpdateState |= I_MESSAGES; } } if (CPlayer->powers[pw_weaponlevel2] && !CPlayer->chickenTics) { if (CPlayer->powers[pw_weaponlevel2] > BLINKTHRESHOLD || !(CPlayer->powers[pw_weaponlevel2] & 16)) { frame = (leveltime / 3) & 15; V_DrawPatch(300, 17, W_CacheLumpNum(spinbooklump + frame, PU_CACHE)); BorderTopRefresh = true; UpdateState |= I_MESSAGES; } else { BorderTopRefresh = true; UpdateState |= I_MESSAGES; } } /* if(CPlayer->powers[pw_weaponlevel2] > BLINKTHRESHOLD || (CPlayer->powers[pw_weaponlevel2]&8)) { V_DrawPatch(291, 0, W_CacheLumpName("ARTIPWBK", PU_CACHE)); } else { BorderTopRefresh = true; } } */ } // sets the new palette based upon current values of player->damagecount // and player->bonuscount void SB_PaletteFlash(void) { static int sb_palette = 0; int palette; byte *pal; CPlayer = &players[consoleplayer]; if (CPlayer->damagecount) { palette = (CPlayer->damagecount + 7) >> 3; if (palette >= NUMREDPALS) { palette = NUMREDPALS - 1; } palette += STARTREDPALS; } else if (CPlayer->bonuscount) { palette = (CPlayer->bonuscount + 7) >> 3; if (palette >= NUMBONUSPALS) { palette = NUMBONUSPALS - 1; } palette += STARTBONUSPALS; } else { palette = 0; } if (palette != sb_palette) { sb_palette = palette; pal = (byte *) W_CacheLumpNum(playpalette, PU_CACHE) + palette * 768; I_SetPalette(pal); } } //--------------------------------------------------------------------------- // // PROC DrawCommonBar // //--------------------------------------------------------------------------- void DrawCommonBar(void) { int chainY; int healthPos; V_DrawPatch(0, 148, PatchLTFCTOP); V_DrawPatch(290, 148, PatchRTFCTOP); if (oldhealth != HealthMarker) { oldhealth = HealthMarker; healthPos = HealthMarker; if (healthPos < 0) { healthPos = 0; } if (healthPos > 100) { healthPos = 100; } healthPos = (healthPos * 256) / 100; chainY = (HealthMarker == CPlayer->mo->health) ? 191 : 191 + ChainWiggle; V_DrawPatch(0, 190, PatchCHAINBACK); V_DrawPatch(2 + (healthPos % 17), chainY, PatchCHAIN); V_DrawPatch(17 + healthPos, chainY, PatchLIFEGEM); V_DrawPatch(0, 190, PatchLTFACE); V_DrawPatch(276, 190, PatchRTFACE); ShadeChain(); UpdateState |= I_STATBAR; } } //--------------------------------------------------------------------------- // // PROC DrawMainBar // //--------------------------------------------------------------------------- void DrawMainBar(void) { int i; int temp; // Ready artifact if (ArtifactFlash) { V_DrawPatch(180, 161, PatchBLACKSQ); temp = W_GetNumForName(DEH_String("useartia")) + ArtifactFlash - 1; V_DrawPatch(182, 161, W_CacheLumpNum(temp, PU_CACHE)); ArtifactFlash--; oldarti = -1; // so that the correct artifact fills in after the flash UpdateState |= I_STATBAR; } else if (oldarti != CPlayer->readyArtifact || oldartiCount != CPlayer->inventory[inv_ptr].count) { V_DrawPatch(180, 161, PatchBLACKSQ); if (CPlayer->readyArtifact > 0) { V_DrawPatch(179, 160, W_CacheLumpName(DEH_String(patcharti[CPlayer->readyArtifact]), PU_CACHE)); DrSmallNumber(CPlayer->inventory[inv_ptr].count, 201, 182); } oldarti = CPlayer->readyArtifact; oldartiCount = CPlayer->inventory[inv_ptr].count; UpdateState |= I_STATBAR; } // Frags if (deathmatch) { temp = 0; for (i = 0; i < MAXPLAYERS; i++) { temp += CPlayer->frags[i]; } if (temp != oldfrags) { V_DrawPatch(57, 171, PatchARMCLEAR); DrINumber(temp, 61, 170); oldfrags = temp; UpdateState |= I_STATBAR; } } else { temp = HealthMarker; if (temp < 0) { temp = 0; } else if (temp > 100) { temp = 100; } if (oldlife != temp) { oldlife = temp; V_DrawPatch(57, 171, PatchARMCLEAR); DrINumber(temp, 61, 170); UpdateState |= I_STATBAR; } } // Keys if (oldkeys != playerkeys) { if (CPlayer->keys[key_yellow]) { V_DrawPatch(153, 164, W_CacheLumpName(DEH_String("ykeyicon"), PU_CACHE)); } if (CPlayer->keys[key_green]) { V_DrawPatch(153, 172, W_CacheLumpName(DEH_String("gkeyicon"), PU_CACHE)); } if (CPlayer->keys[key_blue]) { V_DrawPatch(153, 180, W_CacheLumpName(DEH_String("bkeyicon"), PU_CACHE)); } oldkeys = playerkeys; UpdateState |= I_STATBAR; } // Ammo temp = CPlayer->ammo[wpnlev1info[CPlayer->readyweapon].ammo]; if (oldammo != temp || oldweapon != CPlayer->readyweapon) { V_DrawPatch(108, 161, PatchBLACKSQ); if (temp && CPlayer->readyweapon > 0 && CPlayer->readyweapon < 7) { DrINumber(temp, 109, 162); V_DrawPatch(111, 172, W_CacheLumpName(DEH_String(ammopic[CPlayer->readyweapon - 1]), PU_CACHE)); } oldammo = temp; oldweapon = CPlayer->readyweapon; UpdateState |= I_STATBAR; } // Armor if (oldarmor != CPlayer->armorpoints) { V_DrawPatch(224, 171, PatchARMCLEAR); DrINumber(CPlayer->armorpoints, 228, 170); oldarmor = CPlayer->armorpoints; UpdateState |= I_STATBAR; } } //--------------------------------------------------------------------------- // // PROC DrawInventoryBar // //--------------------------------------------------------------------------- void DrawInventoryBar(void) { const char *patch; int i; int x; x = inv_ptr - curpos; UpdateState |= I_STATBAR; V_DrawPatch(34, 160, PatchINVBAR); for (i = 0; i < 7; i++) { //V_DrawPatch(50+i*31, 160, W_CacheLumpName("ARTIBOX", PU_CACHE)); if (CPlayer->inventorySlotNum > x + i && CPlayer->inventory[x + i].type != arti_none) { patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]); V_DrawPatch(50 + i * 31, 160, W_CacheLumpName(patch, PU_CACHE)); DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31, 182); } } V_DrawPatch(50 + curpos * 31, 189, PatchSELECTBOX); if (x != 0) { V_DrawPatch(38, 159, !(leveltime & 4) ? PatchINVLFGEM1 : PatchINVLFGEM2); } if (CPlayer->inventorySlotNum - x > 7) { V_DrawPatch(269, 159, !(leveltime & 4) ? PatchINVRTGEM1 : PatchINVRTGEM2); } } void DrawFullScreenStuff(void) { const char *patch; int i; int x; int temp; UpdateState |= I_FULLSCRN; if (CPlayer->mo->health > 0) { DrBNumber(CPlayer->mo->health, 5, 180); } else { DrBNumber(0, 5, 180); } if (deathmatch) { temp = 0; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { temp += CPlayer->frags[i]; } } DrINumber(temp, 45, 185); } if (!inventory) { if (CPlayer->readyArtifact > 0) { patch = DEH_String(patcharti[CPlayer->readyArtifact]); V_DrawTLPatch(286, 170, W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE)); V_DrawPatch(286, 170, W_CacheLumpName(patch, PU_CACHE)); DrSmallNumber(CPlayer->inventory[inv_ptr].count, 307, 192); } } else { x = inv_ptr - curpos; for (i = 0; i < 7; i++) { V_DrawTLPatch(50 + i * 31, 168, W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE)); if (CPlayer->inventorySlotNum > x + i && CPlayer->inventory[x + i].type != arti_none) { patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]); V_DrawPatch(50 + i * 31, 168, W_CacheLumpName(patch, PU_CACHE)); DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31, 190); } } V_DrawPatch(50 + curpos * 31, 197, PatchSELECTBOX); if (x != 0) { V_DrawPatch(38, 167, !(leveltime & 4) ? PatchINVLFGEM1 : PatchINVLFGEM2); } if (CPlayer->inventorySlotNum - x > 7) { V_DrawPatch(269, 167, !(leveltime & 4) ? PatchINVRTGEM1 : PatchINVRTGEM2); } } } //-------------------------------------------------------------------------- // // FUNC SB_Responder // //-------------------------------------------------------------------------- boolean SB_Responder(event_t * event) { if (event->type == ev_keydown) { if (HandleCheats(event->data1)) { // Need to eat the key return (true); } } return (false); } //-------------------------------------------------------------------------- // // FUNC HandleCheats // // Returns true if the caller should eat the key. // //-------------------------------------------------------------------------- static boolean HandleCheats(byte key) { int i; boolean eat; if (netgame || gameskill == sk_nightmare) { // Can't cheat in a net-game, or in nightmare mode return (false); } if (players[consoleplayer].health <= 0) { // Dead players can't cheat return (false); } eat = false; for (i = 0; Cheats[i].func != NULL; i++) { if (cht_CheckCheat(Cheats[i].seq, key)) { Cheats[i].func(&players[consoleplayer], &Cheats[i]); S_StartSound(NULL, sfx_dorcls); } } return (eat); } //-------------------------------------------------------------------------- // // CHEAT FUNCTIONS // //-------------------------------------------------------------------------- static void CheatGodFunc(player_t * player, Cheat_t * cheat) { player->cheats ^= CF_GODMODE; if (player->cheats & CF_GODMODE) { P_SetMessage(player, DEH_String(TXT_CHEATGODON), false); } else { P_SetMessage(player, DEH_String(TXT_CHEATGODOFF), false); } SB_state = -1; } static void CheatNoClipFunc(player_t * player, Cheat_t * cheat) { player->cheats ^= CF_NOCLIP; if (player->cheats & CF_NOCLIP) { P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPON), false); } else { P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPOFF), false); } } static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat) { int i; //extern boolean *WeaponInShareware; player->armorpoints = 200; player->armortype = 2; if (!player->backpack) { for (i = 0; i < NUMAMMO; i++) { player->maxammo[i] *= 2; } player->backpack = true; } for (i = 0; i < NUMWEAPONS - 1; i++) { player->weaponowned[i] = true; } if (gamemode == shareware) { player->weaponowned[wp_skullrod] = false; player->weaponowned[wp_phoenixrod] = false; player->weaponowned[wp_mace] = false; } for (i = 0; i < NUMAMMO; i++) { player->ammo[i] = player->maxammo[i]; } P_SetMessage(player, DEH_String(TXT_CHEATWEAPONS), false); } static void CheatPowerFunc(player_t * player, Cheat_t * cheat) { if (player->powers[pw_weaponlevel2]) { player->powers[pw_weaponlevel2] = 0; P_SetMessage(player, DEH_String(TXT_CHEATPOWEROFF), false); } else { P_UseArtifact(player, arti_tomeofpower); P_SetMessage(player, DEH_String(TXT_CHEATPOWERON), false); } } static void CheatHealthFunc(player_t * player, Cheat_t * cheat) { if (player->chickenTics) { player->health = player->mo->health = MAXCHICKENHEALTH; } else { player->health = player->mo->health = MAXHEALTH; } P_SetMessage(player, DEH_String(TXT_CHEATHEALTH), false); } static void CheatKeysFunc(player_t * player, Cheat_t * cheat) { player->keys[key_yellow] = true; player->keys[key_green] = true; player->keys[key_blue] = true; playerkeys = 7; // Key refresh flags P_SetMessage(player, DEH_String(TXT_CHEATKEYS), false); } static void CheatSoundFunc(player_t * player, Cheat_t * cheat) { DebugSound = !DebugSound; if (DebugSound) { P_SetMessage(player, DEH_String(TXT_CHEATSOUNDON), false); } else { P_SetMessage(player, DEH_String(TXT_CHEATSOUNDOFF), false); } } static void CheatTickerFunc(player_t * player, Cheat_t * cheat) { DisplayTicker = !DisplayTicker; if (DisplayTicker) { P_SetMessage(player, DEH_String(TXT_CHEATTICKERON), false); } else { P_SetMessage(player, DEH_String(TXT_CHEATTICKEROFF), false); } I_DisplayFPSDots(DisplayTicker); } static void CheatArtifact1Func(player_t * player, Cheat_t * cheat) { P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS1), false); } static void CheatArtifact2Func(player_t * player, Cheat_t * cheat) { P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS2), false); } static void CheatArtifact3Func(player_t * player, Cheat_t * cheat) { char args[2]; int i; int j; int type; int count; cht_GetParam(cheat->seq, args); type = args[0] - 'a' + 1; count = args[1] - '0'; if (type == 26 && count == 0) { // All artifacts for (i = arti_none + 1; i < NUMARTIFACTS; i++) { if (gamemode == shareware && (i == arti_superhealth || i == arti_teleport)) { continue; } for (j = 0; j < 16; j++) { P_GiveArtifact(player, i, NULL); } } P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false); } else if (type > arti_none && type < NUMARTIFACTS && count > 0 && count < 10) { if (gamemode == shareware && (type == arti_superhealth || type == arti_teleport)) { P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false); return; } for (i = 0; i < count; i++) { P_GiveArtifact(player, type, NULL); } P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false); } else { // Bad input P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false); } } static void CheatWarpFunc(player_t * player, Cheat_t * cheat) { char args[2]; int episode; int map; cht_GetParam(cheat->seq, args); episode = args[0] - '0'; map = args[1] - '0'; if (D_ValidEpisodeMap(heretic, gamemode, episode, map)) { G_DeferedInitNew(gameskill, episode, map); P_SetMessage(player, DEH_String(TXT_CHEATWARP), false); } } static void CheatChickenFunc(player_t * player, Cheat_t * cheat) { extern boolean P_UndoPlayerChicken(player_t * player); if (player->chickenTics) { if (P_UndoPlayerChicken(player)) { P_SetMessage(player, DEH_String(TXT_CHEATCHICKENOFF), false); } } else if (P_ChickenMorphPlayer(player)) { P_SetMessage(player, DEH_String(TXT_CHEATCHICKENON), false); } } static void CheatMassacreFunc(player_t * player, Cheat_t * cheat) { P_Massacre(); P_SetMessage(player, DEH_String(TXT_CHEATMASSACRE), false); } static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat) { int i; if (player->chickenTics) { return; } for (i = 1; i < 8; i++) { player->weaponowned[i] = false; } player->pendingweapon = wp_staff; P_SetMessage(player, DEH_String(TXT_CHEATIDKFA), true); } static void CheatIDDQDFunc(player_t * player, Cheat_t * cheat) { P_DamageMobj(player->mo, NULL, player->mo, 10000); P_SetMessage(player, DEH_String(TXT_CHEATIDDQD), true); } crispy-doom-crispy-doom-5.6.4/src/heretic/sounds.c000066400000000000000000000157221360717211000221340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // sounds.c #include "doomdef.h" #include "i_sound.h" #include "sounds.h" // Music info #define MUSIC(name) \ { name, 0, NULL, NULL } musicinfo_t S_music[] = { MUSIC("MUS_E1M1"), // 1-1 MUSIC("MUS_E1M2"), MUSIC("MUS_E1M3"), MUSIC("MUS_E1M4"), MUSIC("MUS_E1M5"), MUSIC("MUS_E1M6"), MUSIC("MUS_E1M7"), MUSIC("MUS_E1M8"), MUSIC("MUS_E1M9"), MUSIC("MUS_E2M1"), // 2-1 MUSIC("MUS_E2M2"), MUSIC("MUS_E2M3"), MUSIC("MUS_E2M4"), MUSIC("MUS_E1M4"), MUSIC("MUS_E2M6"), MUSIC("MUS_E2M7"), MUSIC("MUS_E2M8"), MUSIC("MUS_E2M9"), MUSIC("MUS_E1M1"), // 3-1 MUSIC("MUS_E3M2"), MUSIC("MUS_E3M3"), MUSIC("MUS_E1M6"), MUSIC("MUS_E1M3"), MUSIC("MUS_E1M2"), MUSIC("MUS_E1M5"), MUSIC("MUS_E1M9"), MUSIC("MUS_E2M6"), MUSIC("MUS_E1M6"), // 4-1 MUSIC("MUS_E1M2"), MUSIC("MUS_E1M3"), MUSIC("MUS_E1M4"), MUSIC("MUS_E1M5"), MUSIC("MUS_E1M1"), MUSIC("MUS_E1M7"), MUSIC("MUS_E1M8"), MUSIC("MUS_E1M9"), MUSIC("MUS_E2M1"), // 5-1 MUSIC("MUS_E2M2"), MUSIC("MUS_E2M3"), MUSIC("MUS_E2M4"), MUSIC("MUS_E1M4"), MUSIC("MUS_E2M6"), MUSIC("MUS_E2M7"), MUSIC("MUS_E2M8"), MUSIC("MUS_E2M9"), MUSIC("MUS_E3M2"), // 6-1 MUSIC("MUS_E3M3"), // 6-2 MUSIC("MUS_E1M6"), // 6-3 MUSIC("MUS_TITL"), MUSIC("MUS_INTR"), MUSIC("MUS_CPTD") }; // Sound info /* Macro for original heretic sfxinfo_t #define SOUND(name, priority, numchannels) \ { name, NULL, priority, -1, NULL, 0, numchannels } #define SOUND_LINK(name, link_id, priority, numchannels) \ { name, &S_sfx[link_id], priority, -1, NULL, 0, numchannels } */ #define SOUND(name, priority, numchannels) \ { NULL, name, priority, NULL, -1, -1, -1, 0, numchannels, NULL } #define SOUND_LINK(name, link_id, priority, numchannels) \ { NULL, name, priority, &S_sfx[link_id], 0, 0, -1, 0, numchannels, NULL } sfxinfo_t S_sfx[] = { SOUND("", 0, 0), SOUND("gldhit", 32, 2), SOUND("gntful", 32, -1), SOUND("gnthit", 32, -1), SOUND("gntpow", 32, -1), SOUND("gntact", 32, -1), SOUND("gntuse", 32, -1), SOUND("phosht", 32, 2), SOUND("phohit", 32, -1), SOUND_LINK("-phopow", sfx_hedat1, 32, 1), SOUND("lobsht", 20, 2), SOUND("lobhit", 20, 2), SOUND("lobpow", 20, 2), SOUND("hrnsht", 32, 2), SOUND("hrnhit", 32, 2), SOUND("hrnpow", 32, 2), SOUND("ramphit", 32, 2), SOUND("ramrain", 10, 2), SOUND("bowsht", 32, 2), SOUND("stfhit", 32, 2), SOUND("stfpow", 32, 2), SOUND("stfcrk", 32, 2), SOUND("impsit", 32, 2), SOUND("impat1", 32, 2), SOUND("impat2", 32, 2), SOUND("impdth", 80, 2), SOUND_LINK("-impact", sfx_impsit, 20, 2), SOUND("imppai", 32, 2), SOUND("mumsit", 32, 2), SOUND("mumat1", 32, 2), SOUND("mumat2", 32, 2), SOUND("mumdth", 80, 2), SOUND_LINK("-mumact", sfx_mumsit, 20, 2), SOUND("mumpai", 32, 2), SOUND("mumhed", 32, 2), SOUND("bstsit", 32, 2), SOUND("bstatk", 32, 2), SOUND("bstdth", 80, 2), SOUND("bstact", 20, 2), SOUND("bstpai", 32, 2), SOUND("clksit", 32, 2), SOUND("clkatk", 32, 2), SOUND("clkdth", 80, 2), SOUND("clkact", 20, 2), SOUND("clkpai", 32, 2), SOUND("snksit", 32, 2), SOUND("snkatk", 32, 2), SOUND("snkdth", 80, 2), SOUND("snkact", 20, 2), SOUND("snkpai", 32, 2), SOUND("kgtsit", 32, 2), SOUND("kgtatk", 32, 2), SOUND("kgtat2", 32, 2), SOUND("kgtdth", 80, 2), SOUND_LINK("-kgtact", sfx_kgtsit, 20, 2), SOUND("kgtpai", 32, 2), SOUND("wizsit", 32, 2), SOUND("wizatk", 32, 2), SOUND("wizdth", 80, 2), SOUND("wizact", 20, 2), SOUND("wizpai", 32, 2), SOUND("minsit", 32, 2), SOUND("minat1", 32, 2), SOUND("minat2", 32, 2), SOUND("minat3", 32, 2), SOUND("mindth", 80, 2), SOUND("minact", 20, 2), SOUND("minpai", 32, 2), SOUND("hedsit", 32, 2), SOUND("hedat1", 32, 2), SOUND("hedat2", 32, 2), SOUND("hedat3", 32, 2), SOUND("heddth", 80, 2), SOUND("hedact", 20, 2), SOUND("hedpai", 32, 2), SOUND("sorzap", 32, 2), SOUND("sorrise", 32, 2), SOUND("sorsit", 200, 2), SOUND("soratk", 32, 2), SOUND("soract", 200, 2), SOUND("sorpai", 200, 2), SOUND("sordsph", 200, 2), SOUND("sordexp", 200, 2), SOUND("sordbon", 200, 2), SOUND_LINK("-sbtsit", sfx_bstsit, 32, 2), SOUND_LINK("-sbtatk", sfx_bstatk, 32, 2), SOUND("sbtdth", 80, 2), SOUND("sbtact", 20, 2), SOUND("sbtpai", 32, 2), SOUND("plroof", 32, 2), SOUND("plrpai", 32, 2), SOUND("plrdth", 80, 2), SOUND("gibdth", 100, 2), SOUND("plrwdth", 80, 2), SOUND("plrcdth", 100, 2), SOUND("itemup", 32, 2), SOUND("wpnup", 32, 2), SOUND("telept", 50, 2), SOUND("doropn", 40, 2), SOUND("dorcls", 40, 2), SOUND("dormov", 40, 2), SOUND("artiup", 32, 2), SOUND("switch", 40, 2), SOUND("pstart", 40, 2), SOUND("pstop", 40, 2), SOUND("stnmov", 40, 2), SOUND("chicpai", 32, 2), SOUND("chicatk", 32, 2), SOUND("chicdth", 40, 2), SOUND("chicact", 32, 2), SOUND("chicpk1", 32, 2), SOUND("chicpk2", 32, 2), SOUND("chicpk3", 32, 2), SOUND("keyup", 50, 2), SOUND("ripslop", 16, 2), SOUND("newpod", 16, -1), SOUND("podexp", 40, -1), SOUND("bounce", 16, 2), SOUND_LINK("-volsht", sfx_bstatk, 16, 2), SOUND_LINK("-volhit", sfx_lobhit, 16, 2), SOUND("burn", 10, 2), SOUND("splash", 10, 1), SOUND("gloop", 10, 2), SOUND("respawn", 10, 1), SOUND("blssht", 32, 2), SOUND("blshit", 32, 2), SOUND("chat", 100, 1), SOUND("artiuse", 32, 1), SOUND("gfrag", 100, 1), SOUND("waterfl", 16, 2), // Monophonic sounds SOUND("wind", 16, 1), SOUND("amb1", 1, 1), SOUND("amb2", 1, 1), SOUND("amb3", 1, 1), SOUND("amb4", 1, 1), SOUND("amb5", 1, 1), SOUND("amb6", 1, 1), SOUND("amb7", 1, 1), SOUND("amb8", 1, 1), SOUND("amb9", 1, 1), SOUND("amb10", 1, 1), SOUND("amb11", 1, 0) }; crispy-doom-crispy-doom-5.6.4/src/heretic/sounds.h000066400000000000000000000114141360717211000221330ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // sounds.h #ifndef __SOUNDSH__ #define __SOUNDSH__ #include "i_sound.h" #define MAX_SND_DIST 1600 #define MAX_CHANNELS 16 // Music identifiers typedef enum { 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_e4m1, mus_e4m2, mus_e4m3, mus_e4m4, mus_e4m5, mus_e4m6, mus_e4m7, mus_e4m8, mus_e4m9, mus_e5m1, mus_e5m2, mus_e5m3, mus_e5m4, mus_e5m5, mus_e5m6, mus_e5m7, mus_e5m8, mus_e5m9, mus_e6m1, mus_e6m2, mus_e6m3, mus_titl, mus_intr, mus_cptd, NUMMUSIC } musicenum_t; #if 0 typedef struct { char name[8]; } musicinfo_t; typedef struct sfxinfo_s { char name[8]; struct sfxinfo_s *link; // Make alias for another sound unsigned short priority; // Higher priority takes precendence int usefulness; // Determines when a sound should be cached out void *snd_ptr; int lumpnum; int numchannels; // total number of channels a sound type may occupy } sfxinfo_t; #endif typedef struct { mobj_t *mo; int sound_id; int handle; int pitch; int priority; } channel_t; typedef struct { int id; unsigned short priority; char *name; mobj_t *mo; int distance; } ChanInfo_t; typedef struct { int channelCount; int musicVolume; int soundVolume; ChanInfo_t chan[8]; } SoundInfo_t; // Sound identifiers typedef enum { sfx_None, sfx_gldhit, sfx_gntful, sfx_gnthit, sfx_gntpow, sfx_gntact, sfx_gntuse, sfx_phosht, sfx_phohit, sfx_phopow, sfx_lobsht, sfx_lobhit, sfx_lobpow, sfx_hrnsht, sfx_hrnhit, sfx_hrnpow, sfx_ramphit, sfx_ramrain, sfx_bowsht, sfx_stfhit, sfx_stfpow, sfx_stfcrk, sfx_impsit, sfx_impat1, sfx_impat2, sfx_impdth, sfx_impact, sfx_imppai, sfx_mumsit, sfx_mumat1, sfx_mumat2, sfx_mumdth, sfx_mumact, sfx_mumpai, sfx_mumhed, sfx_bstsit, sfx_bstatk, sfx_bstdth, sfx_bstact, sfx_bstpai, sfx_clksit, sfx_clkatk, sfx_clkdth, sfx_clkact, sfx_clkpai, sfx_snksit, sfx_snkatk, sfx_snkdth, sfx_snkact, sfx_snkpai, sfx_kgtsit, sfx_kgtatk, sfx_kgtat2, sfx_kgtdth, sfx_kgtact, sfx_kgtpai, sfx_wizsit, sfx_wizatk, sfx_wizdth, sfx_wizact, sfx_wizpai, sfx_minsit, sfx_minat1, sfx_minat2, sfx_minat3, sfx_mindth, sfx_minact, sfx_minpai, sfx_hedsit, sfx_hedat1, sfx_hedat2, sfx_hedat3, sfx_heddth, sfx_hedact, sfx_hedpai, sfx_sorzap, sfx_sorrise, sfx_sorsit, sfx_soratk, sfx_soract, sfx_sorpai, sfx_sordsph, sfx_sordexp, sfx_sordbon, sfx_sbtsit, sfx_sbtatk, sfx_sbtdth, sfx_sbtact, sfx_sbtpai, sfx_plroof, sfx_plrpai, sfx_plrdth, // Normal sfx_gibdth, // Extreme sfx_plrwdth, // Wimpy sfx_plrcdth, // Crazy sfx_itemup, sfx_wpnup, sfx_telept, sfx_doropn, sfx_dorcls, sfx_dormov, sfx_artiup, sfx_switch, sfx_pstart, sfx_pstop, sfx_stnmov, sfx_chicpai, sfx_chicatk, sfx_chicdth, sfx_chicact, sfx_chicpk1, sfx_chicpk2, sfx_chicpk3, sfx_keyup, sfx_ripslop, sfx_newpod, sfx_podexp, sfx_bounce, sfx_volsht, sfx_volhit, sfx_burn, sfx_splash, sfx_gloop, sfx_respawn, sfx_blssht, sfx_blshit, sfx_chat, sfx_artiuse, sfx_gfrag, sfx_waterfl, // Monophonic sounds sfx_wind, sfx_amb1, sfx_amb2, sfx_amb3, sfx_amb4, sfx_amb5, sfx_amb6, sfx_amb7, sfx_amb8, sfx_amb9, sfx_amb10, sfx_amb11, NUMSFX } sfxenum_t; extern sfxinfo_t S_sfx[]; extern musicinfo_t S_music[]; #endif crispy-doom-crispy-doom-5.6.4/src/hexen/000077500000000000000000000000001360717211000201325ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/src/hexen/.gitignore000066400000000000000000000000451360717211000221210ustar00rootroot00000000000000Makefile Makefile.in .deps tags TAGS crispy-doom-crispy-doom-5.6.4/src/hexen/CMakeLists.txt000066400000000000000000000031301360717211000226670ustar00rootroot00000000000000add_library(hexen STATIC a_action.c am_data.h am_map.c am_map.h ct_chat.c ct_chat.h d_net.c f_finale.c g_game.c h2def.h h2_main.c info.c info.h in_lude.c m_random.c m_random.h mn_menu.c p_acs.c p_anim.c p_ceilng.c p_doors.c p_enemy.c p_floor.c p_inter.c p_lights.c p_local.h p_map.c p_maputl.c p_mobj.c po_man.c p_plats.c p_pspr.c p_setup.c p_sight.c p_spec.c p_spec.h p_switch.c p_telept.c p_things.c p_tick.c p_user.c r_bsp.c r_data.c r_draw.c r_local.h r_main.c r_plane.c r_segs.c r_things.c s_sound.c s_sound.h sb_bar.c sc_man.c sn_sonix.c sounds.c sounds.h st_start.c st_start.h sv_save.c textdefs.h xddefs.h) target_include_directories(hexen PRIVATE "../" "${CMAKE_CURRENT_BINARY_DIR}/../../") target_link_libraries(hexen SDL2::SDL2 SDL2::mixer SDL2::net) crispy-doom-crispy-doom-5.6.4/src/hexen/Makefile.am000066400000000000000000000044261360717211000221740ustar00rootroot00000000000000AM_CFLAGS=-I$(top_srcdir)/src @SDL_CFLAGS@ @SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@ EXTRA_DIST = CMakeLists.txt noinst_LIBRARIES=libhexen.a libhexen_a_SOURCES = \ a_action.c \ am_data.h \ am_map.c am_map.h \ ct_chat.c \ ct_chat.h \ d_net.c \ f_finale.c \ g_game.c \ h2def.h \ h2_main.c \ info.c info.h \ in_lude.c \ m_random.c m_random.h \ mn_menu.c \ p_acs.c \ p_anim.c \ p_ceilng.c \ p_doors.c \ p_enemy.c \ p_floor.c \ p_inter.c \ p_lights.c \ p_local.h \ p_map.c \ p_maputl.c \ p_mobj.c \ po_man.c \ p_plats.c \ p_pspr.c \ p_setup.c \ p_sight.c \ p_spec.c p_spec.h \ p_switch.c \ p_telept.c \ p_things.c \ p_tick.c \ p_user.c \ r_bsp.c \ r_data.c \ r_draw.c \ r_local.h \ r_main.c \ r_plane.c \ r_segs.c \ r_things.c \ s_sound.c s_sound.h \ sb_bar.c \ sc_man.c \ sn_sonix.c \ sounds.c sounds.h \ st_start.c st_start.h \ sv_save.c \ textdefs.h \ xddefs.h crispy-doom-crispy-doom-5.6.4/src/hexen/a_action.c000066400000000000000000001126201360717211000220550ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern fixed_t FloatBobOffsets[64]; // PUBLIC DATA DEFINITIONS ------------------------------------------------- int orbitTableX[256] = { 983025, 982725, 981825, 980340, 978255, 975600, 972330, 968490, 964065, 959070, 953475, 947325, 940590, 933300, 925440, 917025, 908055, 898545, 888495, 877905, 866775, 855135, 842985, 830310, 817155, 803490, 789360, 774735, 759660, 744120, 728130, 711690, 694845, 677565, 659880, 641805, 623340, 604500, 585285, 565725, 545820, 525600, 505050, 484200, 463065, 441645, 419955, 398010, 375840, 353430, 330810, 307995, 285000, 261825, 238485, 215010, 191400, 167685, 143865, 119955, 95970, 71940, 47850, 23745, -375, -24495, -48600, -72690, -96720, -120705, -144600, -168420, -192150, -215745, -239220, -262545, -285720, -308715, -331530, -354135, -376530, -398700, -420630, -442320, -463725, -484860, -505695, -526230, -546450, -566340, -585885, -605085, -623925, -642375, -660435, -678105, -695370, -712215, -728625, -744600, -760125, -775200, -789795, -803925, -817575, -830715, -843375, -855510, -867135, -878235, -888810, -898845, -908340, -917295, -925695, -933540, -940815, -947520, -953670, -959235, -964215, -968625, -972450, -975690, -978330, -980400, -981870, -982740, -983025, -982725, -981825, -980340, -978255, -975600, -972330, -968490, -964065, -959070, -953475, -947325, -940590, -933300, -925440, -917025, -908055, -898545, -888495, -877905, -866775, -855135, -842985, -830310, -817155, -803490, -789360, -774735, -759660, -744120, -728130, -711690, -694845, -677565, -659880, -641805, -623340, -604485, -585285, -565725, -545820, -525600, -505050, -484200, -463065, -441645, -419955, -398010, -375840, -353430, -330810, -307995, -285000, -261825, -238485, -215010, -191400, -167685, -143865, -119955, -95970, -71940, -47850, -23745, 375, 24495, 48600, 72690, 96720, 120705, 144600, 168420, 192150, 215745, 239220, 262545, 285720, 308715, 331530, 354135, 376530, 398700, 420630, 442320, 463725, 484860, 505695, 526230, 546450, 566340, 585885, 605085, 623925, 642375, 660435, 678105, 695370, 712215, 728625, 744600, 760125, 775200, 789795, 803925, 817575, 830715, 843375, 855510, 867135, 878235, 888810, 898845, 908340, 917295, 925695, 933540, 940815, 947520, 953670, 959235, 964215, 968625, 972450, 975690, 978330, 980400, 981870, 982740 }; int orbitTableY[256] = { 375, 24495, 48600, 72690, 96720, 120705, 144600, 168420, 192150, 215745, 239220, 262545, 285720, 308715, 331530, 354135, 376530, 398700, 420630, 442320, 463725, 484860, 505695, 526230, 546450, 566340, 585885, 605085, 623925, 642375, 660435, 678105, 695370, 712215, 728625, 744600, 760125, 775200, 789795, 803925, 817575, 830715, 843375, 855510, 867135, 878235, 888810, 898845, 908340, 917295, 925695, 933540, 940815, 947520, 953670, 959235, 964215, 968625, 972450, 975690, 978330, 980400, 981870, 982740, 983025, 982725, 981825, 980340, 978255, 975600, 972330, 968490, 964065, 959070, 953475, 947325, 940590, 933300, 925440, 917025, 908055, 898545, 888495, 877905, 866775, 855135, 842985, 830310, 817155, 803490, 789360, 774735, 759660, 744120, 728130, 711690, 694845, 677565, 659880, 641805, 623340, 604500, 585285, 565725, 545820, 525600, 505050, 484200, 463065, 441645, 419955, 398010, 375840, 353430, 330810, 307995, 285000, 261825, 238485, 215010, 191400, 167685, 143865, 119955, 95970, 71940, 47850, 23745, -375, -24495, -48600, -72690, -96720, -120705, -144600, -168420, -192150, -215745, -239220, -262545, -285720, -308715, -331530, -354135, -376530, -398700, -420630, -442320, -463725, -484860, -505695, -526230, -546450, -566340, -585885, -605085, -623925, -642375, -660435, -678105, -695370, -712215, -728625, -744600, -760125, -775200, -789795, -803925, -817575, -830715, -843375, -855510, -867135, -878235, -888810, -898845, -908340, -917295, -925695, -933540, -940815, -947520, -953670, -959235, -964215, -968625, -972450, -975690, -978330, -980400, -981870, -982740, -983025, -982725, -981825, -980340, -978255, -975600, -972330, -968490, -964065, -959070, -953475, -947325, -940590, -933300, -925440, -917025, -908055, -898545, -888495, -877905, -866775, -855135, -842985, -830310, -817155, -803490, -789360, -774735, -759660, -744120, -728130, -711690, -694845, -677565, -659880, -641805, -623340, -604485, -585285, -565725, -545820, -525600, -505050, -484200, -463065, -441645, -419955, -398010, -375840, -353430, -330810, -307995, -285000, -261825, -238485, -215010, -191400, -167685, -143865, -119955, -95970, -71940, -47850, -23745 }; // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- //-------------------------------------------------------------------------- // // Environmental Action routines // //-------------------------------------------------------------------------- //========================================================================== // // A_DripBlood // //========================================================================== /* void A_DripBlood(mobj_t *actor) { mobj_t *mo; int r; r = P_SubRandom(); mo = P_SpawnMobj(actor->x+(r<<11), actor->y+(P_SubRandom()<<11), actor->z, MT_BLOOD); mo->momx = P_SubRandom()<<10; mo->momy = P_SubRandom()<<10; mo->flags2 |= MF2_LOGRAV; } */ //============================================================================ // // A_PotteryExplode // //============================================================================ void A_PotteryExplode(mobj_t * actor) { mobj_t *mo = NULL; int i; for (i = (P_Random() & 3) + 3; i; i--) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_POTTERYBIT1); P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 5)); mo->momz = ((P_Random() & 7) + 5) * (3 * FRACUNIT / 4); mo->momx = P_SubRandom() << (FRACBITS - 6); mo->momy = P_SubRandom() << (FRACBITS - 6); } S_StartSound(mo, SFX_POTTERY_EXPLODE); if (actor->args[0]) { // Spawn an item if (!nomonsters || !(mobjinfo[TranslateThingType[actor->args[0]]]. flags & MF_COUNTKILL)) { // Only spawn monsters if not -nomonsters P_SpawnMobj(actor->x, actor->y, actor->z, TranslateThingType[actor->args[0]]); } } P_RemoveMobj(actor); } //============================================================================ // // A_PotteryChooseBit // //============================================================================ void A_PotteryChooseBit(mobj_t * actor) { P_SetMobjState(actor, actor->info->deathstate + (P_Random() % 5) + 1); actor->tics = 256 + (P_Random() << 1); } //============================================================================ // // A_PotteryCheck // //============================================================================ void A_PotteryCheck(mobj_t * actor) { int i; mobj_t *pmo; if (!netgame) { pmo = players[consoleplayer].mo; if (P_CheckSight(actor, pmo) && (abs((int)R_PointToAngle2(pmo->x, pmo->y, actor->x, actor->y) - (int)pmo->angle) <= ANG45)) { // Previous state (pottery bit waiting state) P_SetMobjState(actor, actor->state - &states[0] - 1); } else { return; } } else { for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) { continue; } pmo = players[i].mo; if (P_CheckSight(actor, pmo) && (abs((int)R_PointToAngle2(pmo->x, pmo->y, actor->x, actor->y) - (int)pmo->angle) <= ANG45)) { // Previous state (pottery bit waiting state) P_SetMobjState(actor, actor->state - &states[0] - 1); return; } } } } //============================================================================ // // A_CorpseBloodDrip // //============================================================================ void A_CorpseBloodDrip(mobj_t * actor) { if (P_Random() > 128) { return; } P_SpawnMobj(actor->x, actor->y, actor->z + actor->height / 2, MT_CORPSEBLOODDRIP); } //============================================================================ // // A_CorpseExplode // //============================================================================ void A_CorpseExplode(mobj_t * actor) { mobj_t *mo; int i; for (i = (P_Random() & 3) + 3; i; i--) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_CORPSEBIT); P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3)); mo->momz = ((P_Random() & 7) + 5) * (3 * FRACUNIT / 4); mo->momx = P_SubRandom() << (FRACBITS - 6); mo->momy = P_SubRandom() << (FRACBITS - 6); } // Spawn a skull mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_CORPSEBIT); P_SetMobjState(mo, S_CORPSEBIT_4); if (mo) { mo->momz = ((P_Random() & 7) + 5) * (3 * FRACUNIT / 4); mo->momx = P_SubRandom() << (FRACBITS - 6); mo->momy = P_SubRandom() << (FRACBITS - 6); S_StartSound(mo, SFX_FIRED_DEATH); } P_RemoveMobj(actor); } //============================================================================ // // A_LeafSpawn // //============================================================================ void A_LeafSpawn(mobj_t * actor) { mobj_t *mo; int i; for (i = (P_Random() & 3) + 1; i; i--) { // Official release of Hexen's source code relies on unspecified behavior // the in order of function's argument evaluation, // see ISO-IEC 9899-1999, [6.5.2.2.10] mobjtype_t type = MT_LEAF1 + (P_Random() & 1); fixed_t z = actor->z + (P_Random() << 14); fixed_t y = actor->y + (P_SubRandom() << 14); fixed_t x = actor->x + (P_SubRandom() << 14); mo = P_SpawnMobj(x, y, z, type); if (mo) { P_ThrustMobj(mo, actor->angle, (P_Random() << 9) + 3 * FRACUNIT); mo->target = actor; mo->special1.i = 0; } } } //============================================================================ // // A_LeafThrust // //============================================================================ void A_LeafThrust(mobj_t * actor) { if (P_Random() > 96) { return; } actor->momz += (P_Random() << 9) + FRACUNIT; } //============================================================================ // // A_LeafCheck // //============================================================================ void A_LeafCheck(mobj_t * actor) { actor->special1.i++; if (actor->special1.i >= 20) { P_SetMobjState(actor, S_NULL); return; } if (P_Random() > 64) { if (!actor->momx && !actor->momy) { P_ThrustMobj(actor, actor->target->angle, (P_Random() << 9) + FRACUNIT); } return; } P_SetMobjState(actor, S_LEAF1_8); actor->momz = (P_Random() << 9) + FRACUNIT; P_ThrustMobj(actor, actor->target->angle, (P_Random() << 9) + 2 * FRACUNIT); actor->flags |= MF_MISSILE; } /* #define ORBIT_RADIUS (15*FRACUNIT) void GenerateOrbitTable(void) { int angle; for (angle=0; angle<256; angle++) { orbitTableX[angle] = FixedMul(ORBIT_RADIUS, finecosine[angle<<5]); orbitTableY[angle] = FixedMul(ORBIT_RADIUS, finesine[angle<<5]); } printf("int orbitTableX[256]=\n{\n"); for (angle=0; angle<256; angle+=8) { printf("%d, %d, %d, %d, %d, %d, %d, %d,\n", orbitTableX[angle], orbitTableX[angle+1], orbitTableX[angle+2], orbitTableX[angle+3], orbitTableX[angle+4], orbitTableX[angle+5], orbitTableX[angle+6], orbitTableX[angle+7]); } printf("};\n\n"); printf("int orbitTableY[256]=\n{\n"); for (angle=0; angle<256; angle+=8) { printf("%d, %d, %d, %d, %d, %d, %d, %d,\n", orbitTableY[angle], orbitTableY[angle+1], orbitTableY[angle+2], orbitTableY[angle+3], orbitTableY[angle+4], orbitTableY[angle+5], orbitTableY[angle+6], orbitTableY[angle+7]); } printf("};\n"); } */ // New bridge stuff // Parent // special1 true == removing from world // // Child // target pointer to center mobj // args[0] angle of ball void A_BridgeOrbit(mobj_t * actor) { if (actor->target->special1.i) { P_SetMobjState(actor, S_NULL); } actor->args[0] += 3; actor->x = actor->target->x + orbitTableX[actor->args[0]]; actor->y = actor->target->y + orbitTableY[actor->args[0]]; actor->z = actor->target->z; } void A_BridgeInit(mobj_t * actor) { byte startangle; mobj_t *ball1, *ball2, *ball3; fixed_t cx, cy, cz; // GenerateOrbitTable(); cx = actor->x; cy = actor->y; cz = actor->z; startangle = P_Random(); actor->special1.i = 0; // Spawn triad into world ball1 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL); ball1->args[0] = startangle; ball1->target = actor; ball2 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL); ball2->args[0] = (startangle + 85) & 255; ball2->target = actor; ball3 = P_SpawnMobj(cx, cy, cz, MT_BRIDGEBALL); ball3->args[0] = (startangle + 170) & 255; ball3->target = actor; A_BridgeOrbit(ball1); A_BridgeOrbit(ball2); A_BridgeOrbit(ball3); } void A_BridgeRemove(mobj_t * actor) { actor->special1.i = true; // Removing the bridge actor->flags &= ~MF_SOLID; P_SetMobjState(actor, S_FREE_BRIDGE1); } //========================================================================== // // A_GhostOn // //========================================================================== /* void A_GhostOn(mobj_t *actor) { actor->flags |= MF_SHADOW; } */ //========================================================================== // // A_GhostOff // //========================================================================== /* void A_GhostOff(mobj_t *actor) { actor->flags &= ~MF_SHADOW; } */ //========================================================================== // // A_HideThing // //========================================================================== void A_HideThing(mobj_t * actor) { actor->flags2 |= MF2_DONTDRAW; } //========================================================================== // // A_UnHideThing // //========================================================================== void A_UnHideThing(mobj_t * actor) { actor->flags2 &= ~MF2_DONTDRAW; } //========================================================================== // // A_SetShootable // //========================================================================== void A_SetShootable(mobj_t * actor) { actor->flags2 &= ~MF2_NONSHOOTABLE; actor->flags |= MF_SHOOTABLE; } //========================================================================== // // A_UnSetShootable // //========================================================================== void A_UnSetShootable(mobj_t * actor) { actor->flags2 |= MF2_NONSHOOTABLE; actor->flags &= ~MF_SHOOTABLE; } //========================================================================== // // A_SetAltShadow // //========================================================================== void A_SetAltShadow(mobj_t * actor) { actor->flags &= ~MF_SHADOW; actor->flags |= MF_ALTSHADOW; } //========================================================================== // // A_UnSetAltShadow // //========================================================================== /* void A_UnSetAltShadow(mobj_t *actor) { actor->flags &= ~MF_ALTSHADOW; } */ //-------------------------------------------------------------------------- // // Sound Action Routines // //-------------------------------------------------------------------------- //========================================================================== // // A_ContMobjSound // //========================================================================== void A_ContMobjSound(mobj_t * actor) { switch (actor->type) { case MT_SERPENTFX: S_StartSound(actor, SFX_SERPENTFX_CONTINUOUS); break; case MT_HAMMER_MISSILE: S_StartSound(actor, SFX_FIGHTER_HAMMER_CONTINUOUS); break; case MT_QUAKE_FOCUS: S_StartSound(actor, SFX_EARTHQUAKE); break; default: break; } } //========================================================================== // // PROC A_ESound // //========================================================================== void A_ESound(mobj_t * mo) { int sound; switch (mo->type) { case MT_SOUNDWIND: sound = SFX_WIND; break; default: sound = SFX_NONE; break; } S_StartSound(mo, sound); } //========================================================================== // Summon Minotaur -- see p_enemy for variable descriptions //========================================================================== void A_Summon(mobj_t * actor) { mobj_t *mo; mobj_t *master; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_MINOTAUR); if (mo) { if (P_TestMobjLocation(mo) == false || !actor->special1.m) { // Didn't fit - change back to artifact P_SetMobjState(mo, S_NULL); mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SUMMONMAULATOR); if (mo) mo->flags2 |= MF2_DROPPED; return; } // Store leveltime into mo->args. This must be stored in little- // endian format for Vanilla savegame compatibility. mo->args[0] = leveltime & 0xff; mo->args[1] = (leveltime >> 8) & 0xff; mo->args[2] = (leveltime >> 16) & 0xff; mo->args[3] = (leveltime >> 24) & 0xff; master = actor->special1.m; if (master->flags & MF_CORPSE) { // Master dead mo->special1.m = NULL; // No master } else { mo->special1.m = actor->special1.m; // Pointer to master (mobj_t *) P_GivePower(master->player, pw_minotaur); } // Make smoke puff P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE); S_StartSound(actor, SFX_MAULATOR_ACTIVE); } } //========================================================================== // Fog Variables: // // args[0] Speed (0..10) of fog // args[1] Angle of spread (0..128) // args[2] Frequency of spawn (1..10) // args[3] Lifetime countdown // args[4] Boolean: fog moving? // special1 Internal: Counter for spawn frequency // special2 Internal: Index into floatbob table // //========================================================================== void A_FogSpawn(mobj_t * actor) { mobj_t *mo = NULL; angle_t delta; if (actor->special1.i-- > 0) return; actor->special1.i = actor->args[2]; // Reset frequency count switch (P_Random() % 3) { case 0: mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHS); break; case 1: mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHM); break; case 2: mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FOGPATCHL); break; } if (mo) { delta = actor->args[1]; if (delta == 0) delta = 1; mo->angle = actor->angle + (((P_Random() % delta) - (delta >> 1)) << 24); mo->target = actor; if (actor->args[0] < 1) actor->args[0] = 1; mo->args[0] = (P_Random() % (actor->args[0])) + 1; // Random speed mo->args[3] = actor->args[3]; // Set lifetime mo->args[4] = 1; // Set to moving mo->special2.i = P_Random() & 63; } } void A_FogMove(mobj_t * actor) { int speed = actor->args[0] << FRACBITS; angle_t angle; int weaveindex; if (!(actor->args[4])) return; if (actor->args[3]-- <= 0) { P_SetMobjStateNF(actor, actor->info->deathstate); return; } if ((actor->args[3] % 4) == 0) { weaveindex = actor->special2.i; actor->z += FloatBobOffsets[weaveindex] >> 1; actor->special2.i = (weaveindex + 1) & 63; } angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(speed, finecosine[angle]); actor->momy = FixedMul(speed, finesine[angle]); } //=========================================================================== // // A_PoisonBagInit // //=========================================================================== void A_PoisonBagInit(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 28 * FRACUNIT, MT_POISONCLOUD); if (!mo) { return; } mo->momx = 1; // missile objects must move to impact other objects mo->special1.i = 24 + (P_Random() & 7); mo->special2.i = 0; mo->target = actor->target; mo->radius = 20 * FRACUNIT; mo->height = 30 * FRACUNIT; mo->flags &= ~MF_NOCLIP; } //=========================================================================== // // A_PoisonBagCheck // //=========================================================================== void A_PoisonBagCheck(mobj_t * actor) { if (!--actor->special1.i) { P_SetMobjState(actor, S_POISONCLOUD_X1); } else { return; } } //=========================================================================== // // A_PoisonBagDamage // //=========================================================================== void A_PoisonBagDamage(mobj_t * actor) { int bobIndex; extern void A_Explode(mobj_t * actor); A_Explode(actor); bobIndex = actor->special2.i; actor->z += FloatBobOffsets[bobIndex] >> 4; actor->special2.i = (bobIndex + 1) & 63; } //=========================================================================== // // A_PoisonShroom // //=========================================================================== void A_PoisonShroom(mobj_t * actor) { actor->tics = 128 + (P_Random() << 1); } //=========================================================================== // // A_CheckThrowBomb // //=========================================================================== void A_CheckThrowBomb(mobj_t * actor) { if (abs(actor->momx) < 1.5 * FRACUNIT && abs(actor->momy) < 1.5 * FRACUNIT && actor->momz < 2 * FRACUNIT && actor->state == &states[S_THROWINGBOMB6]) { P_SetMobjState(actor, S_THROWINGBOMB7); actor->z = actor->floorz; actor->momz = 0; actor->flags2 &= ~MF2_FLOORBOUNCE; actor->flags &= ~MF_MISSILE; } if (!--actor->health) { P_SetMobjState(actor, actor->info->deathstate); } } //=========================================================================== // Quake variables // // args[0] Intensity on richter scale (2..9) // args[1] Duration in tics // args[2] Radius for damage // args[3] Radius for tremor // args[4] TID of map thing for focus of quake // //=========================================================================== //=========================================================================== // // A_LocalQuake // //=========================================================================== boolean A_LocalQuake(byte * args, mobj_t * actor) { mobj_t *focus, *target; int lastfound = 0; int success = false; // Find all quake foci do { target = P_FindMobjFromTID(args[4], &lastfound); if (target) { focus = P_SpawnMobj(target->x, target->y, target->z, MT_QUAKE_FOCUS); if (focus) { focus->args[0] = args[0]; focus->args[1] = args[1] >> 1; // decremented every 2 tics focus->args[2] = args[2]; focus->args[3] = args[3]; focus->args[4] = args[4]; success = true; } } } while (target != NULL); return (success); } //=========================================================================== // // A_Quake // //=========================================================================== int localQuakeHappening[MAXPLAYERS]; void A_Quake(mobj_t * actor) { angle_t an; player_t *player; mobj_t *victim; int richters = actor->args[0]; int playnum; fixed_t dist; if (actor->args[1]-- > 0) { for (playnum = 0; playnum < maxplayers; playnum++) { player = &players[playnum]; if (!playeringame[playnum]) continue; victim = player->mo; dist = P_AproxDistance(actor->x - victim->x, actor->y - victim->y) >> (FRACBITS + 6); // Tested in tile units (64 pixels) if (dist < actor->args[3]) // In tremor radius { localQuakeHappening[playnum] = richters; } // Check if in damage radius if ((dist < actor->args[2]) && (victim->z <= victim->floorz)) { if (P_Random() < 50) { P_DamageMobj(victim, NULL, NULL, HITDICE(1)); } // Thrust player around an = victim->angle + ANG1 * P_Random(); P_ThrustMobj(victim, an, richters << (FRACBITS - 1)); } } } else { for (playnum = 0; playnum < maxplayers; playnum++) { localQuakeHappening[playnum] = false; } P_SetMobjState(actor, S_NULL); } } //=========================================================================== // // Teleport other stuff // //=========================================================================== #define TELEPORT_LIFE 1 void A_TeloSpawnA(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX2); if (mo) { mo->special1.i = TELEPORT_LIFE; // Lifetime countdown mo->angle = actor->angle; mo->target = actor->target; mo->momx = actor->momx >> 1; mo->momy = actor->momy >> 1; mo->momz = actor->momz >> 1; } } void A_TeloSpawnB(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX3); if (mo) { mo->special1.i = TELEPORT_LIFE; // Lifetime countdown mo->angle = actor->angle; mo->target = actor->target; mo->momx = actor->momx >> 1; mo->momy = actor->momy >> 1; mo->momz = actor->momz >> 1; } } void A_TeloSpawnC(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX4); if (mo) { mo->special1.i = TELEPORT_LIFE; // Lifetime countdown mo->angle = actor->angle; mo->target = actor->target; mo->momx = actor->momx >> 1; mo->momy = actor->momy >> 1; mo->momz = actor->momz >> 1; } } void A_TeloSpawnD(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TELOTHER_FX5); if (mo) { mo->special1.i = TELEPORT_LIFE; // Lifetime countdown mo->angle = actor->angle; mo->target = actor->target; mo->momx = actor->momx >> 1; mo->momy = actor->momy >> 1; mo->momz = actor->momz >> 1; } } void A_CheckTeleRing(mobj_t * actor) { if (actor->special1.i-- <= 0) { P_SetMobjState(actor, actor->info->deathstate); } } // Dirt stuff void P_SpawnDirt(mobj_t * actor, fixed_t radius) { fixed_t x, y, z; int dtype = 0; mobj_t *mo; angle_t angle; angle = P_Random() << 5; // <<24 >>19 x = actor->x + FixedMul(radius, finecosine[angle]); y = actor->y + FixedMul(radius, finesine[angle]); // x = actor->x + (P_SubRandom()%radius)<y + ((P_SubRandom()<z + (P_Random() << 9) + FRACUNIT; switch (P_Random() % 6) { case 0: dtype = MT_DIRT1; break; case 1: dtype = MT_DIRT2; break; case 2: dtype = MT_DIRT3; break; case 3: dtype = MT_DIRT4; break; case 4: dtype = MT_DIRT5; break; case 5: dtype = MT_DIRT6; break; } mo = P_SpawnMobj(x, y, z, dtype); if (mo) { mo->momz = P_Random() << 10; } } //=========================================================================== // // Thrust floor stuff // // Thrust Spike Variables // special1 pointer to dirt clump mobj // special2 speed of raise // args[0] 0 = lowered, 1 = raised // args[1] 0 = normal, 1 = bloody //=========================================================================== void A_ThrustInitUp(mobj_t * actor) { actor->special2.i = 5; // Raise speed actor->args[0] = 1; // Mark as up actor->floorclip = 0; actor->flags = MF_SOLID; actor->flags2 = MF2_NOTELEPORT | MF2_FLOORCLIP; actor->special1.m = NULL; } void A_ThrustInitDn(mobj_t * actor) { mobj_t *mo; actor->special2.i = 5; // Raise speed actor->args[0] = 0; // Mark as down actor->floorclip = actor->info->height; actor->flags = 0; actor->flags2 = MF2_NOTELEPORT | MF2_FLOORCLIP | MF2_DONTDRAW; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_DIRTCLUMP); actor->special1.m = mo; } void A_ThrustRaise(mobj_t * actor) { if (A_RaiseMobj(actor)) { // Reached it's target height actor->args[0] = 1; if (actor->args[1]) P_SetMobjStateNF(actor, S_BTHRUSTINIT2_1); else P_SetMobjStateNF(actor, S_THRUSTINIT2_1); } // Lose the dirt clump if ((actor->floorclip < actor->height) && actor->special1.m) { P_RemoveMobj(actor->special1.m); actor->special1.m = NULL; } // Spawn some dirt if (P_Random() < 40) P_SpawnDirt(actor, actor->radius); actor->special2.i++; // Increase raise speed } void A_ThrustLower(mobj_t * actor) { if (A_SinkMobj(actor)) { actor->args[0] = 0; if (actor->args[1]) P_SetMobjStateNF(actor, S_BTHRUSTINIT1_1); else P_SetMobjStateNF(actor, S_THRUSTINIT1_1); } } void A_ThrustBlock(mobj_t * actor) { actor->flags |= MF_SOLID; } void A_ThrustImpale(mobj_t * actor) { // Impale all shootables in radius PIT_ThrustSpike(actor); } //=========================================================================== // // A_SoAExplode - Suit of Armor Explode // //=========================================================================== void A_SoAExplode(mobj_t * actor) { mobj_t *mo; int i; int r1,r2,r3; for (i = 0; i < 10; i++) { r1 = P_Random(); r2 = P_Random(); r3 = P_Random(); mo = P_SpawnMobj(actor->x + ((r3 - 128) << 12), actor->y + ((r2 - 128) << 12), actor->z + (r1 * actor->height / 256), MT_ZARMORCHUNK); P_SetMobjState(mo, mo->info->spawnstate + i); mo->momz = ((P_Random() & 7) + 5) * FRACUNIT; mo->momx = P_SubRandom() << (FRACBITS - 6); mo->momy = P_SubRandom() << (FRACBITS - 6); } if (actor->args[0]) { // Spawn an item if (!nomonsters || !(mobjinfo[TranslateThingType[actor->args[0]]]. flags & MF_COUNTKILL)) { // Only spawn monsters if not -nomonsters P_SpawnMobj(actor->x, actor->y, actor->z, TranslateThingType[actor->args[0]]); } } S_StartSound(mo, SFX_SUITOFARMOR_BREAK); P_RemoveMobj(actor); } //=========================================================================== // // A_BellReset1 // //=========================================================================== void A_BellReset1(mobj_t * actor) { actor->flags |= MF_NOGRAVITY; actor->height <<= 2; } //=========================================================================== // // A_BellReset2 // //=========================================================================== void A_BellReset2(mobj_t * actor) { actor->flags |= MF_SHOOTABLE; actor->flags &= ~MF_CORPSE; actor->health = 5; } //=========================================================================== // // A_FlameCheck // //=========================================================================== void A_FlameCheck(mobj_t * actor) { if (!actor->args[0]--) // Called every 8 tics { P_SetMobjState(actor, S_NULL); } } //=========================================================================== // Bat Spawner Variables // special1 frequency counter // special2 // args[0] frequency of spawn (1=fastest, 10=slowest) // args[1] spread angle (0..255) // args[2] // args[3] duration of bats (in octics) // args[4] turn amount per move (in degrees) // // Bat Variables // special2 lifetime counter // args[4] turn amount per move (in degrees) //=========================================================================== void A_BatSpawnInit(mobj_t * actor) { actor->special1.i = 0; // Frequency count } void A_BatSpawn(mobj_t * actor) { mobj_t *mo; int delta; angle_t angle; // Countdown until next spawn if (actor->special1.i-- > 0) return; actor->special1.i = actor->args[0]; // Reset frequency count delta = actor->args[1]; if (delta == 0) delta = 1; angle = actor->angle + (((P_Random() % delta) - (delta >> 1)) << 24); mo = P_SpawnMissileAngle(actor, MT_BAT, angle, 0); if (mo) { mo->args[0] = P_Random() & 63; // floatbob index mo->args[4] = actor->args[4]; // turn degrees mo->special2.i = actor->args[3] << 3; // Set lifetime mo->target = actor; } } void A_BatMove(mobj_t * actor) { angle_t newangle; fixed_t speed; if (actor->special2.i < 0) { P_SetMobjState(actor, actor->info->deathstate); } actor->special2.i -= 2; // Called every 2 tics if (P_Random() < 128) { newangle = actor->angle + ANG1 * actor->args[4]; } else { newangle = actor->angle - ANG1 * actor->args[4]; } // Adjust momentum vector to new direction newangle >>= ANGLETOFINESHIFT; speed = FixedMul(actor->info->speed, P_Random() << 10); actor->momx = FixedMul(speed, finecosine[newangle]); actor->momy = FixedMul(speed, finesine[newangle]); if (P_Random() < 15) S_StartSound(actor, SFX_BAT_SCREAM); // Handle Z movement actor->z = actor->target->z + 2 * FloatBobOffsets[actor->args[0]]; actor->args[0] = (actor->args[0] + 3) & 63; } //=========================================================================== // // A_TreeDeath // //=========================================================================== void A_TreeDeath(mobj_t * actor) { if (!(actor->flags2 & MF2_FIREDAMAGE)) { actor->height <<= 2; actor->flags |= MF_SHOOTABLE; actor->flags &= ~(MF_CORPSE + MF_DROPOFF); actor->health = 35; return; } else { P_SetMobjState(actor, actor->info->meleestate); } } //=========================================================================== // // A_NoGravity // //=========================================================================== void A_NoGravity(mobj_t * actor) { actor->flags |= MF_NOGRAVITY; } crispy-doom-crispy-doom-5.6.4/src/hexen/am_data.h000066400000000000000000000067711360717211000217040ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __AMDATA_H__ #define __AMDATA_H__ // a line drawing of the player pointing right, starting from the middle. #define R ((8*PLAYERRADIUS)/7) mline_t player_arrow[] = { { { -R+R/4, 0 }, { 0, 0} }, // center line. { { -R+R/4, R/8 }, { R, 0} }, // blade { { -R+R/4, -R/8 }, { R, 0 } }, { { -R+R/4, -R/4 }, { -R+R/4, R/4 } }, // crosspiece { { -R+R/8, -R/4 }, { -R+R/8, R/4 } }, { { -R+R/8, -R/4 }, { -R+R/4, -R/4} }, //crosspiece connectors { { -R+R/8, R/4 }, { -R+R/4, R/4} }, { { -R-R/4, R/8 }, { -R-R/4, -R/8 } }, //pommel { { -R-R/4, R/8 }, { -R+R/8, R/8 } }, { { -R-R/4, -R/8}, { -R+R/8, -R/8 } } }; /* mline_t keysquare[] = { { { 0, 0 }, { R/4, -R/2 } }, { { R/4, -R/2 }, { R/2, -R/2 } }, { { R/2, -R/2 }, { R/2, R/2 } }, { { R/2, R/2 }, { R/4, R/2 } }, { { R/4, R/2 }, { 0, 0 } }, // handle part type thing { { 0, 0 }, { -R, 0 } }, // stem { { -R, 0 }, { -R, -R/2 } }, // end lockpick part { { -3*R/4, 0 }, { -3*R/4, -R/4 } } }; */ /*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 NUMKEYSQUARELINES (sizeof(keysquare)/sizeof(mline_t)) /* #define R ((8*PLAYERRADIUS)/7) mline_t cheat_player_arrow[] = { { { -R+R/8, 0 }, { R, 0 } }, // ----- { { R, 0 }, { R-R/2, R/6 } }, // -----> { { R, 0 }, { R-R/2, -R/6 } }, { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >-----> { { -R+R/8, 0 }, { -R-R/8, -R/6 } }, { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>-----> { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } }, { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d---> { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } }, { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } }, { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd--> { { -R/6, -R/6 }, { 0, -R/6 } }, { { 0, -R/6 }, { 0, R/4 } }, { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt-> { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } }, { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } } }; #undef R #define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) */ /* #define R (FRACUNIT) mline_t triangle_guy[] = { { { -.867*R, -.5*R }, { .867*R, -.5*R } }, { { .867*R, -.5*R } , { 0, R } }, { { 0, R }, { -.867*R, -.5*R } } }; #undef R #define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) */ #define R (FRACUNIT) mline_t thintriangle_guy[] = { { { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (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)) #endif crispy-doom-crispy-doom-5.6.4/src/hexen/am_map.c000066400000000000000000001223071360717211000215350ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "h2def.h" #include "doomkeys.h" #include "i_video.h" #include "i_swap.h" #include "i_timer.h" #include "m_controls.h" #include "m_misc.h" #include "p_local.h" #include "am_map.h" #include "am_data.h" #include "v_video.h" #define NUMALIAS 3 // Number of antialiased lines. int cheating = 0; static int grid = 0; static int leveljuststarted = 1; // kluge until AM_LevelInit() is called boolean automapactive = false; static int finit_width;// = SCREENWIDTH; static int finit_height;// = SCREENHEIGHT - SBARHEIGHT - (3 << crispy->hires); static int f_x, f_y; // location of window on screen static int f_w, f_h; // size of window on screen static int lightlev; // used for funky strobing effect static byte *fb; // pseudo-frame buffer static int amclock; static mpoint_t m_paninc; // how far the window pans each tic (map coords) static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords) static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords) // width/height of window on map (map coords) static fixed_t m_w, m_h; static fixed_t min_x, min_y; // based on level size static fixed_t max_x, max_y; // based on level size static fixed_t max_w, max_h; // max_x-min_x, max_y-min_y static fixed_t min_w, min_h; // based on player size static fixed_t min_scale_mtof; // used to tell when to stop zooming out static fixed_t max_scale_mtof; // used to tell when to stop zooming in // old stuff for recovery later static fixed_t old_m_w, old_m_h; static fixed_t old_m_x, old_m_y; // old location used by the Follower routine static mpoint_t f_oldloc; // used by MTOF to scale from map-to-frame-buffer coords static fixed_t scale_mtof = INITSCALEMTOF; // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) static fixed_t scale_ftom; static player_t *plr; // the player represented by an arrow static vertex_t oldplr; //static patch_t *marknums[10]; // numbers used for marking by the automap //static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are //static int markpointnum = 0; // next point to be assigned static int followplayer = 1; // specifies whether to follow the player around static char cheat_kills[] = { 'k', 'i', 'l', 'l', 's' }; static boolean ShowKills = 0; static unsigned ShowKillsCount = 0; extern boolean viewactive; static byte antialias[NUMALIAS][8] = { {83, 84, 85, 86, 87, 88, 89, 90}, {96, 96, 95, 94, 93, 92, 91, 90}, {107, 108, 109, 110, 111, 112, 89, 90} }; /* static byte *aliasmax[NUMALIAS] = { &antialias[0][7], &antialias[1][7], &antialias[2][7] };*/ static byte *maplump; // pointer to the raw data for the automap background. static short mapystart = 0; // y-value for the start of the map bitmap...used in //the parallax stuff. static short mapxstart = 0; //x-value for the bitmap. //byte screens[][SCREENWIDTH*SCREENHEIGHT]; //void V_MarkRect (int x, int y, int width, int height); // Functions void DrawWuLine(int X0, int Y0, int X1, int Y1, byte * BaseColor, int NumLevels, unsigned short IntensityBits); void AM_DrawDeathmatchStats(void); static void DrawWorldTimer(void); // Calculates the slope and slope according to the x-axis of a line // segment in map coordinates (with the upright y-axis n' all) so // that it can be used with the brain-dead drawing stuff. // Ripped out for Heretic /* void AM_getIslope(mline_t *ml, islope_t *is) { int dx, dy; dy = ml->a.y - ml->b.y; dx = ml->b.x - ml->a.x; if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX); else is->islp = FixedDiv(dx, dy); if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX); else is->slp = FixedDiv(dy, dx); } */ void AM_activateNewScale(void) { m_x += m_w / 2; m_y += m_h / 2; m_w = FTOM(f_w); m_h = FTOM(f_h); m_x -= m_w / 2; m_y -= m_h / 2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; } void AM_saveScaleAndLoc(void) { old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; } void AM_restoreScaleAndLoc(void) { m_w = old_m_w; m_h = old_m_h; if (!followplayer) { m_x = old_m_x; m_y = old_m_y; } else { m_x = plr->mo->x - m_w / 2; m_y = plr->mo->y - m_h / 2; } m_x2 = m_x + m_w; m_y2 = m_y + m_h; // Change the scaling multipliers scale_mtof = FixedDiv(f_w << FRACBITS, m_w); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } // adds a marker at the current location /* void AM_addMark(void) { markpoints[markpointnum].x = m_x + m_w/2; markpoints[markpointnum].y = m_y + m_h/2; markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; } */ void AM_findMinMaxBoundaries(void) { int i; fixed_t a, b; min_x = min_y = INT_MAX; max_x = max_y = -INT_MAX; for (i = 0; i < numvertexes; i++) { if (vertexes[i].x < min_x) min_x = vertexes[i].x; else if (vertexes[i].x > max_x) max_x = vertexes[i].x; if (vertexes[i].y < min_y) min_y = vertexes[i].y; else if (vertexes[i].y > max_y) max_y = vertexes[i].y; } max_w = max_x - min_x; max_h = max_y - min_y; min_w = 2 * PLAYERRADIUS; min_h = 2 * PLAYERRADIUS; a = FixedDiv(f_w << FRACBITS, max_w); b = FixedDiv(f_h << FRACBITS, max_h); min_scale_mtof = a < b ? a : b; max_scale_mtof = FixedDiv(f_h << FRACBITS, 2 * PLAYERRADIUS); } void AM_changeWindowLoc(void) { if (m_paninc.x || m_paninc.y) { followplayer = 0; f_oldloc.x = INT_MAX; } m_x += m_paninc.x; m_y += m_paninc.y; if (m_x + m_w / 2 > max_x) { m_x = max_x - m_w / 2; m_paninc.x = 0; } else if (m_x + m_w / 2 < min_x) { m_x = min_x - m_w / 2; m_paninc.x = 0; } if (m_y + m_h / 2 > max_y) { m_y = max_y - m_h / 2; m_paninc.y = 0; } else if (m_y + m_h / 2 < min_y) { m_y = min_y - m_h / 2; m_paninc.y = 0; } // The following code was commented out in the released Hexen source, // but I believe we need to do this here to stop the background moving // when we reach the map boundaries. (In the released source it's done // in AM_clearFB). mapxstart += MTOF(m_paninc.x+FRACUNIT/2); mapystart -= MTOF(m_paninc.y+FRACUNIT/2); if(mapxstart >= (finit_width >> crispy->hires)) mapxstart -= (finit_width >> crispy->hires); if(mapxstart < 0) mapxstart += (finit_width >> crispy->hires); if(mapystart >= (finit_height >> crispy->hires)) mapystart -= (finit_height >> crispy->hires); if(mapystart < 0) mapystart += (finit_height >> crispy->hires); // - end of code that was commented-out m_x2 = m_x + m_w; m_y2 = m_y + m_h; } void AM_initVariables(void) { int pnum; thinker_t *think; //static event_t st_notify = { ev_keyup, AM_MSGENTERED }; automapactive = true; fb = I_VideoBuffer; f_oldloc.x = INT_MAX; amclock = 0; lightlev = 0; m_paninc.x = m_paninc.y = 0; ftom_zoommul = FRACUNIT; mtof_zoommul = FRACUNIT; m_w = FTOM(f_w); m_h = FTOM(f_h); // find player to center on initially if (!playeringame[pnum = consoleplayer]) for (pnum = 0; pnum < maxplayers; pnum++) if (playeringame[pnum]) break; plr = &players[pnum]; oldplr.x = plr->mo->x; oldplr.y = plr->mo->y; m_x = plr->mo->x - m_w / 2; m_y = plr->mo->y - m_h / 2; AM_changeWindowLoc(); // for saving & restoring old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; // load in the location of keys, if in baby mode // memset(KeyPoints, 0, sizeof(vertex_t)*3); if (gameskill == sk_baby) { for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { //not a mobj continue; } } } // inform the status bar of the change //c ST_Responder(&st_notify); } void AM_loadPics(void) { maplump = W_CacheLumpName("AUTOPAGE", PU_STATIC); } /* void AM_clearMarks(void) { int i; for (i=0;ihires); f_x = f_y = 0; f_w = finit_width; f_h = finit_height; mapxstart = mapystart = 0; // AM_clearMarks(); AM_findMinMaxBoundaries(); scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7 * FRACUNIT)); if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } static boolean stopped = true; void AM_Stop(void) { //static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED }; // AM_unloadPics(); automapactive = false; // ST_Responder(&st_notify); stopped = true; BorderNeedRefresh = true; } void AM_Start(void) { static int lastlevel = -1, lastepisode = -1; if (!stopped) AM_Stop(); stopped = false; if (gamestate != GS_LEVEL) { return; // don't show automap if we aren't in a game! } if (lastlevel != gamemap || lastepisode != gameepisode) { AM_LevelInit(); lastlevel = gamemap; lastepisode = gameepisode; } AM_initVariables(); AM_loadPics(); } // set the window scale to the maximum size void AM_minOutWindowScale(void) { scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // set the window scale to the minimum size void AM_maxOutWindowScale(void) { scale_mtof = max_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } boolean AM_Responder(event_t * ev) { int rc; int key; static int bigstate = 0; static int joywait = 0; key = ev->data1; if (ev->type == ev_joystick && joybautomap >= 0 && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime()) { joywait = I_GetTime() + 5; if (!automapactive) { AM_Start (); SB_state = -1; viewactive = false; } else { bigstate = 0; viewactive = true; AM_Stop (); SB_state = -1; } return true; } rc = false; if (!automapactive) { if (ev->type == ev_keydown && key == key_map_toggle && gamestate == GS_LEVEL) { AM_Start(); SB_state = -1; viewactive = false; rc = true; } } else if (ev->type == ev_keydown) { rc = true; if (key == key_map_east) // pan right { if (!followplayer) m_paninc.x = FTOM(F_PANINC); else rc = false; } else if (key == key_map_west) // pan left { if (!followplayer) m_paninc.x = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_north) // pan up { if (!followplayer) m_paninc.y = FTOM(F_PANINC); else rc = false; } else if (key == key_map_south) // pan down { if (!followplayer) m_paninc.y = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_zoomout) // zoom out { mtof_zoommul = M_ZOOMOUT; ftom_zoommul = M_ZOOMIN; } else if (key == key_map_zoomin) // zoom in { mtof_zoommul = M_ZOOMIN; ftom_zoommul = M_ZOOMOUT; } else if (key == key_map_toggle) { bigstate = 0; viewactive = true; AM_Stop(); SB_state = -1; } else if (key == key_map_maxzoom) { bigstate = !bigstate; if (bigstate) { AM_saveScaleAndLoc(); AM_minOutWindowScale(); } else AM_restoreScaleAndLoc(); } else if (key == key_map_follow) { followplayer = !followplayer; f_oldloc.x = INT_MAX; P_SetMessage(plr, followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF, true); } else { rc = false; } if (cheat_kills[ShowKillsCount] == ev->data1 && netgame && deathmatch) { ShowKillsCount++; if (ShowKillsCount == 5) { ShowKillsCount = 0; rc = false; ShowKills ^= 1; } } else { ShowKillsCount = 0; } } else if (ev->type == ev_keyup) { rc = false; if (key == key_map_east) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_west) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_north) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_south) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_zoomin || key == key_map_zoomout) { mtof_zoommul = FRACUNIT; ftom_zoommul = FRACUNIT; } } return rc; } void AM_changeWindowScale(void) { // Change the scaling multipliers scale_mtof = FixedMul(scale_mtof, mtof_zoommul); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); if (scale_mtof < min_scale_mtof) AM_minOutWindowScale(); else if (scale_mtof > max_scale_mtof) AM_maxOutWindowScale(); else AM_activateNewScale(); } void AM_doFollowPlayer(void) { if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) { // m_x = FTOM(MTOF(plr->mo->x - m_w/2)); // m_y = FTOM(MTOF(plr->mo->y - m_h/2)); // m_x = plr->mo->x - m_w/2; // m_y = plr->mo->y - m_h/2; m_x = FTOM(MTOF(plr->mo->x)) - m_w / 2; m_y = FTOM(MTOF(plr->mo->y)) - m_h / 2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; // do the parallax parchment scrolling. /* dmapx = (MTOF(plr->mo->x)-MTOF(f_oldloc.x)); //fixed point dmapy = (MTOF(f_oldloc.y)-MTOF(plr->mo->y)); if(f_oldloc.x == INT_MAX) //to eliminate an error when the user first dmapx=0; //goes into the automap. mapxstart += dmapx; mapystart += dmapy; while(mapxstart >= finit_width) mapxstart -= finit_width; while(mapxstart < 0) mapxstart += finit_width; while(mapystart >= finit_height) mapystart -= finit_height; while(mapystart < 0) mapystart += finit_height; */ f_oldloc.x = plr->mo->x; f_oldloc.y = plr->mo->y; } } // Ripped out for Heretic /* void AM_updateLightLev(void) { static nexttic = 0; //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; static int litelevelscnt = 0; // Change light level if (amclock>nexttic) { lightlev = litelevels[litelevelscnt++]; if (litelevelscnt == sizeof(litelevels)/sizeof(int)) litelevelscnt = 0; nexttic = amclock + 6 - (amclock % 6); } } */ void AM_Ticker(void) { if (!automapactive) return; amclock++; if (followplayer) AM_doFollowPlayer(); // Change the zoom if necessary if (ftom_zoommul != FRACUNIT) AM_changeWindowScale(); // Change x,y location if (m_paninc.x || m_paninc.y) AM_changeWindowLoc(); // Update light level // AM_updateLightLev(); } void AM_clearFB(int color) { int i, j; int dmapx; int dmapy; if (followplayer) { dmapx = (MTOF(plr->mo->x) - MTOF(oldplr.x)); //fixed point dmapy = (MTOF(oldplr.y) - MTOF(plr->mo->y)); oldplr.x = plr->mo->x; oldplr.y = plr->mo->y; // if(f_oldloc.x == INT_MAX) //to eliminate an error when the user first // dmapx=0; //goes into the automap. mapxstart += dmapx >> 1; mapystart += dmapy >> 1; while (mapxstart >= (finit_width >> crispy->hires)) mapxstart -= (finit_width >> crispy->hires); while (mapxstart < 0) mapxstart += (finit_width >> crispy->hires); while (mapystart >= (finit_height >> crispy->hires)) mapystart -= (finit_height >> crispy->hires); while (mapystart < 0) mapystart += (finit_height >> crispy->hires); } else { // The released Hexen source does this here, but this causes a bug // where the map background keeps moving when we reach the map // boundaries. This is instead done in AM_changeWindowLoc. /* mapxstart += (MTOF(m_paninc.x) >> 1); mapystart -= (MTOF(m_paninc.y) >> 1); if (mapxstart >= (finit_width >> crispy->hires)) mapxstart -= (finit_width >> crispy->hires); if (mapxstart < 0) mapxstart += (finit_width >> crispy->hires); if (mapystart >= (finit_height >> crispy->hires)) mapystart -= (finit_height >> crispy->hires); if (mapystart < 0) mapystart += (finit_height >> crispy->hires); */ } //blit the automap background to the screen. j = (mapystart & ~crispy->hires) * (finit_width >> crispy->hires); for (i = 0; i < SCREENHEIGHT - SBARHEIGHT; i++) { memcpy(I_VideoBuffer + i * finit_width, maplump + j + mapxstart, finit_width - mapxstart); memcpy(I_VideoBuffer + i * finit_width + finit_width - mapxstart, maplump + j, mapxstart); j += finit_width; if (j >= (finit_height >> crispy->hires) * (finit_width >> crispy->hires)) j = 0; } // memcpy(I_VideoBuffer, maplump, finit_width*finit_height); // memset(fb, color, f_w*f_h); } // Based on Cohen-Sutherland clipping algorithm but with a slightly // faster reject and precalculated slopes. If I need the speed, will // hash algorithm to the common cases. boolean AM_clipMline(mline_t * ml, fline_t * fl) { enum { LEFT = 1, RIGHT = 2, BOTTOM = 4, TOP = 8 }; int outcode1 = 0, outcode2 = 0, outside; fpoint_t tmp = { 0, 0 }; int dx, dy; #define DOOUTCODE(oc, mx, my) \ (oc) = 0; \ if ((my) < 0) (oc) |= TOP; \ else if ((my) >= f_h) (oc) |= BOTTOM; \ if ((mx) < 0) (oc) |= LEFT; \ else if ((mx) >= f_w) (oc) |= RIGHT // do trivial rejects and outcodes if (ml->a.y > m_y2) outcode1 = TOP; else if (ml->a.y < m_y) outcode1 = BOTTOM; if (ml->b.y > m_y2) outcode2 = TOP; else if (ml->b.y < m_y) outcode2 = BOTTOM; if (outcode1 & outcode2) return false; // trivially outside if (ml->a.x < m_x) outcode1 |= LEFT; else if (ml->a.x > m_x2) outcode1 |= RIGHT; if (ml->b.x < m_x) outcode2 |= LEFT; else if (ml->b.x > m_x2) outcode2 |= RIGHT; if (outcode1 & outcode2) return false; // trivially outside // transform to frame-buffer coordinates. fl->a.x = CXMTOF(ml->a.x); fl->a.y = CYMTOF(ml->a.y); fl->b.x = CXMTOF(ml->b.x); fl->b.y = CYMTOF(ml->b.y); DOOUTCODE(outcode1, fl->a.x, fl->a.y); DOOUTCODE(outcode2, fl->b.x, fl->b.y); if (outcode1 & outcode2) return false; while (outcode1 | outcode2) { // may be partially inside box // find an outside point if (outcode1) outside = outcode1; else outside = outcode2; // clip to each side if (outside & TOP) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx * (fl->a.y)) / dy; tmp.y = 0; } else if (outside & BOTTOM) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx * (fl->a.y - f_h)) / dy; tmp.y = f_h - 1; } else if (outside & RIGHT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy * (f_w - 1 - fl->a.x)) / dx; tmp.x = f_w - 1; } else if (outside & LEFT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy * (-fl->a.x)) / dx; tmp.x = 0; } if (outside == outcode1) { fl->a = tmp; DOOUTCODE(outcode1, fl->a.x, fl->a.y); } else { fl->b = tmp; DOOUTCODE(outcode2, fl->b.x, fl->b.y); } if (outcode1 & outcode2) return false; // trivially outside } return true; } #undef DOOUTCODE // Classic Bresenham w/ whatever optimizations I need for speed void AM_drawFline(fline_t * fl, int color) { register int x, y, dx, dy, sx, sy, ax, ay, d; //static fuck = 0; switch (color) { case WALLCOLORS: DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[0][0], 8, 3); break; case FDWALLCOLORS: DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[1][0], 8, 3); break; case CDWALLCOLORS: DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[2][0], 8, 3); break; default: { // For debugging only if (fl->a.x < 0 || fl->a.x >= f_w || fl->a.y < 0 || fl->a.y >= f_h || fl->b.x < 0 || fl->b.x >= f_w || fl->b.y < 0 || fl->b.y >= f_h) { //fprintf(stderr, "fuck %d \r", fuck++); return; } #define DOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) //the MACRO! 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) { DOT(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) { DOT(x, y, color); if (y == fl->b.y) return; if (d >= 0) { x += sx; d -= ay; } y += sy; d += ax; } } } } } /* Wu antialiased line drawer. * (X0,Y0),(X1,Y1) = line to draw * BaseColor = color # of first color in block used for antialiasing, the * 100% intensity version of the drawing color * NumLevels = size of color block, with BaseColor+NumLevels-1 being the * 0% intensity version of the drawing color * IntensityBits = log base 2 of NumLevels; the # of bits used to describe * the intensity of the drawing color. 2**IntensityBits==NumLevels */ void PUTDOT(short xx, short yy, byte * cc, byte * cm) { static int oldyy; static int oldyyshifted; byte *oldcc = cc; if (xx < 32) cc += 7 - (xx >> 2); else if (xx > (finit_width - 32)) cc += 7 - ((finit_width - xx) >> 2); // if(cc==oldcc) //make sure that we don't double fade the corners. // { if (yy < 32) cc += 7 - (yy >> 2); else if (yy > (finit_height - 32)) cc += 7 - ((finit_height - yy) >> 2); // } if (cc > cm && cm != NULL) { cc = cm; } else if (cc > oldcc + 6) // don't let the color escape from the fade table... { cc = oldcc + 6; } if (yy == oldyy + 1) { oldyy++; oldyyshifted += (320 << crispy->hires); } else if (yy == oldyy - 1) { oldyy--; oldyyshifted -= (320 << crispy->hires); } else if (yy != oldyy) { oldyy = yy; oldyyshifted = yy * (320 << crispy->hires); } fb[oldyyshifted + xx] = *(cc); // fb[(yy)*f_w+(xx)]=*(cc); } void DrawWuLine(int X0, int Y0, int X1, int Y1, byte * BaseColor, int NumLevels, unsigned short IntensityBits) { unsigned short IntensityShift, ErrorAdj, ErrorAcc; unsigned short ErrorAccTemp, Weighting, WeightingComplementMask; short DeltaX, DeltaY, Temp, XDir; /* Make sure the line runs top to bottom */ if (Y0 > Y1) { Temp = Y0; Y0 = Y1; Y1 = Temp; Temp = X0; X0 = X1; X1 = Temp; } /* Draw the initial pixel, which is always exactly intersected by the line and so needs no weighting */ PUTDOT(X0, Y0, &BaseColor[0], NULL); if ((DeltaX = X1 - X0) >= 0) { XDir = 1; } else { XDir = -1; DeltaX = -DeltaX; /* make DeltaX positive */ } /* Special-case horizontal, vertical, and diagonal lines, which require no weighting because they go right through the center of every pixel */ if ((DeltaY = Y1 - Y0) == 0) { /* Horizontal line */ while (DeltaX-- != 0) { X0 += XDir; PUTDOT(X0, Y0, &BaseColor[0], NULL); } return; } if (DeltaX == 0) { /* Vertical line */ do { Y0++; PUTDOT(X0, Y0, &BaseColor[0], NULL); } while (--DeltaY != 0); return; } //diagonal line. if (DeltaX == DeltaY) { do { X0 += XDir; Y0++; PUTDOT(X0, Y0, &BaseColor[0], NULL); } while (--DeltaY != 0); return; } /* Line is not horizontal, diagonal, or vertical */ ErrorAcc = 0; /* initialize the line error accumulator to 0 */ /* # of bits by which to shift ErrorAcc to get intensity level */ IntensityShift = 16 - IntensityBits; /* Mask used to flip all bits in an intensity weighting, producing the result (1 - intensity weighting) */ WeightingComplementMask = NumLevels - 1; /* Is this an X-major or Y-major line? */ if (DeltaY > DeltaX) { /* Y-major line; calculate 16-bit fixed-point fractional part of a pixel that X advances each time Y advances 1 pixel, truncating the result so that we won't overrun the endpoint along the X axis */ ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY; /* Draw all pixels other than the first and last */ while (--DeltaY) { ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */ ErrorAcc += ErrorAdj; /* calculate error for next pixel */ if (ErrorAcc <= ErrorAccTemp) { /* The error accumulator turned over, so advance the X coord */ X0 += XDir; } Y0++; /* Y-major, so always advance Y */ /* The IntensityBits most significant bits of ErrorAcc give us the intensity weighting for this pixel, and the complement of the weighting for the paired pixel */ Weighting = ErrorAcc >> IntensityShift; PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]); PUTDOT(X0 + XDir, Y0, &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]); } /* Draw the final pixel, which is always exactly intersected by the line and so needs no weighting */ PUTDOT(X1, Y1, &BaseColor[0], NULL); return; } /* It's an X-major line; calculate 16-bit fixed-point fractional part of a pixel that Y advances each time X advances 1 pixel, truncating the result to avoid overrunning the endpoint along the X axis */ ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX; /* Draw all pixels other than the first and last */ while (--DeltaX) { ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */ ErrorAcc += ErrorAdj; /* calculate error for next pixel */ if (ErrorAcc <= ErrorAccTemp) { /* The error accumulator turned over, so advance the Y coord */ Y0++; } X0 += XDir; /* X-major, so always advance X */ /* The IntensityBits most significant bits of ErrorAcc give us the intensity weighting for this pixel, and the complement of the weighting for the paired pixel */ Weighting = ErrorAcc >> IntensityShift; PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]); PUTDOT(X0, Y0 + 1, &BaseColor[(Weighting ^ WeightingComplementMask)], &BaseColor[7]); } /* Draw the final pixel, which is always exactly intersected by the line and so needs no weighting */ PUTDOT(X1, Y1, &BaseColor[0], NULL); } void AM_drawMline(mline_t * ml, int color) { static fline_t fl; if (AM_clipMline(ml, &fl)) AM_drawFline(&fl, color); // draws it on frame buffer using fb coords } void AM_drawGrid(int color) { fixed_t x, y; fixed_t start, end; mline_t ml; // Figure out start of vertical gridlines start = m_x; if ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)) start += (MAPBLOCKUNITS << FRACBITS) - ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)); end = m_x + m_w; // draw vertical gridlines ml.a.y = m_y; ml.b.y = m_y + m_h; for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS)) { ml.a.x = x; ml.b.x = x; AM_drawMline(&ml, color); } // Figure out start of horizontal gridlines start = m_y; if ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)) start += (MAPBLOCKUNITS << FRACBITS) - ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)); end = m_y + m_h; // draw horizontal gridlines ml.a.x = m_x; ml.b.x = m_x + m_w; for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS)) { ml.a.y = y; ml.b.y = y; AM_drawMline(&ml, color); } } void AM_drawWalls(void) { int i; static mline_t l; for (i = 0; i < numlines; i++) { l.a.x = lines[i].v1->x; l.a.y = lines[i].v1->y; l.b.x = lines[i].v2->x; l.b.y = lines[i].v2->y; if (cheating || (lines[i].flags & ML_MAPPED)) { if ((lines[i].flags & LINE_NEVERSEE) && !cheating) continue; if (!lines[i].backsector) { AM_drawMline(&l, WALLCOLORS + lightlev); } else { if (lines[i].flags & ML_SECRET) // secret door { if (cheating) AM_drawMline(&l, 0); else AM_drawMline(&l, WALLCOLORS + lightlev); } else if (lines[i].special == 13 || lines[i].special == 83) { // Locked door line -- all locked doors are greed AM_drawMline(&l, GREENKEY); } else if (lines[i].special == 70 || lines[i].special == 71) { // intra-level teleports are blue AM_drawMline(&l, BLUEKEY); } else if (lines[i].special == 74 || lines[i].special == 75) { // inter-level teleport/game-winning exit -- both are red AM_drawMline(&l, BLOODRED); } else if (lines[i].backsector->floorheight != lines[i].frontsector->floorheight) { AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change } else if (lines[i].backsector->ceilingheight != lines[i].frontsector->ceilingheight) { AM_drawMline(&l, CDWALLCOLORS + lightlev); // ceiling level change } else if (cheating) { AM_drawMline(&l, TSWALLCOLORS + lightlev); } } } else if (plr->powers[pw_allmap]) { if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS + 3); } } } void AM_rotate(fixed_t * x, fixed_t * y, angle_t a) { fixed_t tmpx; tmpx = FixedMul(*x, finecosine[a >> ANGLETOFINESHIFT]) - FixedMul(*y, finesine[a >> ANGLETOFINESHIFT]); *y = FixedMul(*x, finesine[a >> ANGLETOFINESHIFT]) + FixedMul(*y, finecosine[a >> ANGLETOFINESHIFT]); *x = tmpx; } void AM_drawLineCharacter(mline_t * lineguy, int lineguylines, fixed_t scale, angle_t angle, int color, fixed_t x, fixed_t y) { int i; mline_t l; for (i = 0; i < lineguylines; i++) { l.a.x = lineguy[i].a.x; l.a.y = lineguy[i].a.y; if (scale) { l.a.x = FixedMul(scale, l.a.x); l.a.y = FixedMul(scale, l.a.y); } if (angle) AM_rotate(&l.a.x, &l.a.y, angle); l.a.x += x; l.a.y += y; l.b.x = lineguy[i].b.x; l.b.y = lineguy[i].b.y; if (scale) { l.b.x = FixedMul(scale, l.b.x); l.b.y = FixedMul(scale, l.b.y); } if (angle) AM_rotate(&l.b.x, &l.b.y, angle); l.b.x += x; l.b.y += y; AM_drawMline(&l, color); } } void AM_drawPlayers(void) { int i; player_t *p; static int their_colors[] = { AM_PLR1_COLOR, AM_PLR2_COLOR, AM_PLR3_COLOR, AM_PLR4_COLOR, AM_PLR5_COLOR, AM_PLR6_COLOR, AM_PLR7_COLOR, AM_PLR8_COLOR }; int their_color = -1; int color; if (!netgame) { AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); return; } for (i = 0; i < maxplayers; i++) { their_color++; p = &players[i]; if (deathmatch && !singledemo && p != plr) { continue; } if (!playeringame[i]) continue; color = their_colors[their_color]; AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle, color, p->mo->x, p->mo->y); } } void AM_drawThings(int colors, int colorrange) { int i; mobj_t *t; for (i = 0; i < numsectors; i++) { t = sectors[i].thinglist; while (t) { AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16 << FRACBITS, t->angle, colors + lightlev, t->x, t->y); t = t->snext; } } } /* void AM_drawMarks(void) { int i, fx, fy, w, h; for (i=0;iwidth); h = SHORT(marknums[i]->height); fx = CXMTOF(markpoints[i].x); fy = CYMTOF(markpoints[i].y); if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) V_DrawPatch(fx, fy, marknums[i]); } } } */ /* void AM_drawkeys(void) { if(KeyPoints[0].x != 0 || KeyPoints[0].y != 0) { AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, YELLOWKEY, KeyPoints[0].x, KeyPoints[0].y); } if(KeyPoints[1].x != 0 || KeyPoints[1].y != 0) { AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, GREENKEY, KeyPoints[1].x, KeyPoints[1].y); } if(KeyPoints[2].x != 0 || KeyPoints[2].y != 0) { AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, BLUEKEY, KeyPoints[2].x, KeyPoints[2].y); } } */ /* void AM_drawCrosshair(int color) { fb[(f_w*(f_h+1))/2] = color; // single point for now } */ void AM_Drawer(void) { if (!automapactive) return; UpdateState |= I_FULLSCRN; AM_clearFB(BACKGROUND); if (grid) AM_drawGrid(GRIDCOLORS); AM_drawWalls(); AM_drawPlayers(); DrawWorldTimer(); if (cheating == 2) AM_drawThings(THINGCOLORS, THINGRANGE); // AM_drawCrosshair(XHAIRCOLORS); // AM_drawMarks(); // if(gameskill == sk_baby) AM_drawkeys(); MN_DrTextA(P_GetMapName(gamemap), 38, 144); if (ShowKills && netgame && deathmatch) { AM_DrawDeathmatchStats(); } // I_Update(); // V_MarkRect(f_x, f_y, f_w, f_h); } //=========================================================================== // // AM_DrawDeathmatchStats // //=========================================================================== // 8-player note: Proper player color names here, too const char *PlayerColorText[MAXPLAYERS] = { "BLUE:", "RED:", "YELLOW:", "GREEN:", "JADE:", "WHITE:", "HAZEL:", "PURPLE:" }; void AM_DrawDeathmatchStats(void) { int i, j, k, m; int fragCount[MAXPLAYERS]; int order[MAXPLAYERS]; char textBuffer[80]; int yPosition; for (i = 0; i < maxplayers; i++) { fragCount[i] = 0; order[i] = -1; } for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) { continue; } else { for (j = 0; j < maxplayers; j++) { if (playeringame[j]) { fragCount[i] += players[i].frags[j]; } } for (k = 0; k < maxplayers; k++) { if (order[k] == -1) { order[k] = i; break; } else if (fragCount[i] > fragCount[order[k]]) { for (m = maxplayers - 1; m > k; m--) { order[m] = order[m - 1]; } order[k] = i; break; } } } } yPosition = 15; for (i = 0; i < maxplayers; i++) { if (!playeringame[order[i]]) { continue; } else { MN_DrTextA(PlayerColorText[order[i]], 8, yPosition); M_snprintf(textBuffer, sizeof(textBuffer), "%d", fragCount[order[i]]); MN_DrTextA(textBuffer, 80, yPosition); yPosition += 10; } } } //=========================================================================== // // DrawWorldTimer // //=========================================================================== static void DrawWorldTimer(void) { int days; int hours; int minutes; int seconds; int worldTimer; char timeBuffer[15]; char dayBuffer[20]; worldTimer = players[consoleplayer].worldTimer; worldTimer /= 35; days = worldTimer / 86400; worldTimer -= days * 86400; hours = worldTimer / 3600; worldTimer -= hours * 3600; minutes = worldTimer / 60; worldTimer -= minutes * 60; seconds = worldTimer; M_snprintf(timeBuffer, sizeof(timeBuffer), "%.2d : %.2d : %.2d", hours, minutes, seconds); MN_DrTextA(timeBuffer, 240, 8); if (days) { if (days == 1) { M_snprintf(dayBuffer, sizeof(dayBuffer), "%.2d DAY", days); } else { M_snprintf(dayBuffer, sizeof(dayBuffer), "%.2d DAYS", days); } MN_DrTextA(dayBuffer, 240, 20); if (days >= 5) { MN_DrTextA("YOU FREAK!!!", 230, 35); } } } crispy-doom-crispy-doom-5.6.4/src/hexen/am_map.h000066400000000000000000000062631360717211000215440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __AMMAP_H__ #define __AMMAP_H__ // For use if I do walls with outsides/insides #define REDS 12*8 #define REDRANGE 1 //16 #define BLUES (256-4*16+8) #define BLUERANGE 1 //8 #define GREENS (33*8) #define GREENRANGE 1 //16 #define GRAYS (5*8) #define GRAYSRANGE 1 //16 #define BROWNS (14*8) #define BROWNRANGE 1 //16 #define YELLOWS 10*8 #define YELLOWRANGE 1 #define BLACK 0 #define WHITE 4*8 #define PARCH 13*8-1 #define BLOODRED 177 #define BLUEKEY 157 #define YELLOWKEY 137 #define GREENKEY 198 // Automap colors #define AM_PLR1_COLOR 157 // Blue #define AM_PLR2_COLOR 177 // Red #define AM_PLR3_COLOR 137 // Yellow #define AM_PLR4_COLOR 198 // Green #define AM_PLR5_COLOR 215 // Jade #define AM_PLR6_COLOR 32 // White #define AM_PLR7_COLOR 106 // Hazel #define AM_PLR8_COLOR 234 // Purple #define BACKGROUND PARCH #define YOURCOLORS WHITE #define YOURRANGE 0 #define WALLCOLORS REDS #define WALLRANGE REDRANGE #define TSWALLCOLORS GRAYS #define TSWALLRANGE GRAYSRANGE #define FDWALLCOLORS BROWNS #define FDWALLRANGE BROWNRANGE #define CDWALLCOLORS YELLOWS #define CDWALLRANGE YELLOWRANGE #define THINGCOLORS GREENS #define THINGRANGE GREENRANGE #define SECRETWALLCOLORS WALLCOLORS #define SECRETWALLRANGE WALLRANGE #define GRIDCOLORS (GRAYS + GRAYSRANGE/2) #define GRIDRANGE 0 #define XHAIRCOLORS GRAYS // drawing stuff #define AM_NUMMARKPOINTS 10 #define AM_MSGHEADER (('a'<<24)+('m'<<16)) #define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) #define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) #define INITSCALEMTOF (.2*FRACUNIT) // scale on entry // how much the automap moves window per tic in frame-buffer coordinates #define F_PANINC 4 // moves 140 pixels in 1 second // how much zoom-in per tic #define M_ZOOMIN ((int) (1.02*FRACUNIT)) // goes to 2x in 1 second // how much zoom-out per tic #define M_ZOOMOUT ((int) (FRACUNIT/1.02)) // pulls out to 0.5x in 1 second // translates between frame-buffer and map distances #define FTOM(x) FixedMul(((x)<<16),scale_ftom) #define MTOF(x) (FixedMul((x),scale_mtof)>>16) // translates between frame-buffer and map coordinates #define CXMTOF(x) (f_x + MTOF((x)-m_x)) #define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) // the following is crap #define LINE_NEVERSEE ML_DONTDRAW typedef struct { int x, y; } fpoint_t; typedef struct { fpoint_t a, b; } fline_t; typedef vertex_t mpoint_t; typedef struct { mpoint_t a, b; } mline_t; typedef struct { fixed_t slp, islp; } islope_t; // extern int f_x, f_y, f_w, f_h; #endif crispy-doom-crispy-doom-5.6.4/src/hexen/ct_chat.c000066400000000000000000000306351360717211000217120ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include "h2def.h" #include "i_input.h" #include "s_sound.h" #include "doomkeys.h" #include "m_controls.h" #include "m_misc.h" #include "p_local.h" #include "v_video.h" #define NUMKEYS 256 #define QUEUESIZE 128 #define MESSAGESIZE 128 #define MESSAGELEN 265 // 8-player note: Change this stuff (CT_PLR_*, and the key mappings) enum { CT_PLR_BLUE = 1, CT_PLR_RED, CT_PLR_YELLOW, CT_PLR_GREEN, CT_PLR_PLAYER5, CT_PLR_PLAYER6, CT_PLR_PLAYER7, CT_PLR_PLAYER8, CT_PLR_ALL }; #define CT_ESCAPE 6 // Public data boolean chatmodeon; // Private data void CT_queueChatChar(char ch); void CT_ClearChatMessage(int player); void CT_AddChar(int player, char c); void CT_BackSpace(int player); int head; int tail; byte ChatQueue[QUEUESIZE]; int chat_dest[MAXPLAYERS]; char chat_msg[MAXPLAYERS][MESSAGESIZE]; char plr_lastmsg[MAXPLAYERS][MESSAGESIZE + 9]; int msgptr[MAXPLAYERS]; int msglen[MAXPLAYERS]; boolean cheated; static int FontABaseLump; const char *CT_FromPlrText[MAXPLAYERS] = { "BLUE: ", "RED: ", "YELLOW: ", "GREEN: ", "JADE: ", "WHITE: ", "HAZEL: ", "PURPLE: " }; char *chat_macros[10] = { HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8, HUSTR_CHATMACRO9, }; boolean altdown; boolean shiftdown; extern boolean usearti; //=========================================================================== // // CT_Init // // Initialize chat mode data //=========================================================================== void CT_Init(void) { int i; head = 0; //initialize the queue index tail = 0; chatmodeon = false; memset(ChatQueue, 0, QUEUESIZE); for (i = 0; i < maxplayers; i++) { chat_dest[i] = 0; msgptr[i] = 0; memset(plr_lastmsg[i], 0, MESSAGESIZE); memset(chat_msg[i], 0, MESSAGESIZE); } FontABaseLump = W_GetNumForName("FONTA_S") + 1; return; } //=========================================================================== // // CT_Stop // //=========================================================================== void CT_Stop(void) { chatmodeon = false; I_StopTextInput(); return; } // These keys are allowed by Vanilla Heretic: static boolean ValidChatChar(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '!' || c == '?' || c == ' ' || c == '\'' || c == ',' || c == '.' || c == '-' || c == '='; } //=========================================================================== // // CT_Responder // //=========================================================================== boolean CT_Responder(event_t * ev) { char *macro; int sendto; if (!netgame) { return false; } if (ev->data1 == KEY_RALT) { altdown = (ev->type == ev_keydown); return false; } if (ev->data1 == KEY_RSHIFT) { shiftdown = (ev->type == ev_keydown); return false; } if (gamestate != GS_LEVEL || ev->type != ev_keydown) { return false; } if (!chatmodeon) { sendto = 0; if (ev->data1 == key_multi_msg) { sendto = CT_PLR_ALL; } else if (ev->data1 == key_multi_msgplayer[0]) { sendto = CT_PLR_BLUE; } else if (ev->data1 == key_multi_msgplayer[1]) { sendto = CT_PLR_RED; } else if (ev->data1 == key_multi_msgplayer[2]) { sendto = CT_PLR_YELLOW; } else if (ev->data1 == key_multi_msgplayer[3]) { sendto = CT_PLR_GREEN; } else if (ev->data1 == key_multi_msgplayer[4]) { sendto = CT_PLR_PLAYER5; } else if (ev->data1 == key_multi_msgplayer[5]) { sendto = CT_PLR_PLAYER6; } else if (ev->data1 == key_multi_msgplayer[6]) { sendto = CT_PLR_PLAYER7; } else if (ev->data1 == key_multi_msgplayer[7]) { sendto = CT_PLR_PLAYER8; } if (sendto == 0 || (sendto != CT_PLR_ALL && !playeringame[sendto - 1]) || sendto == consoleplayer + 1) { return false; } CT_queueChatChar(sendto); chatmodeon = true; I_StartTextInput(25, 10, SCREENWIDTH, 18); return true; } else { if (altdown) { if (ev->data1 >= '0' && ev->data1 <= '9') { if (ev->data1 == '0') { // macro 0 comes after macro 9 ev->data1 = '9' + 1; } macro = chat_macros[ev->data1 - '1']; CT_queueChatChar(KEY_ENTER); //send old message CT_queueChatChar(chat_dest[consoleplayer]); // chose the dest. while (*macro) { CT_queueChatChar(toupper(*macro++)); } CT_queueChatChar(KEY_ENTER); //send it off... CT_Stop(); return true; } } if (ev->data1 == KEY_ENTER) { CT_queueChatChar(KEY_ENTER); usearti = false; CT_Stop(); return true; } else if (ev->data1 == KEY_ESCAPE) { CT_queueChatChar(CT_ESCAPE); CT_Stop(); return true; } else if (ev->data1 == KEY_BACKSPACE) { CT_queueChatChar(KEY_BACKSPACE); return true; } else if (ValidChatChar(ev->data3)) { CT_queueChatChar(toupper(ev->data3)); return true; } } return false; } //=========================================================================== // // CT_Ticker // //=========================================================================== void CT_Ticker(void) { int i; int j; char c; int numplayers; for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) { continue; } if ((c = players[i].cmd.chatchar) != 0) { if (c <= CT_PLR_ALL) { chat_dest[i] = c; continue; } else if (c == CT_ESCAPE) { CT_ClearChatMessage(i); } else if (c == KEY_ENTER) { numplayers = 0; for (j = 0; j < maxplayers; j++) { numplayers += playeringame[j]; } CT_AddChar(i, 0); // set the end of message character if (numplayers > 2) { M_StringCopy(plr_lastmsg[i], CT_FromPlrText[i], sizeof(plr_lastmsg[i])); M_StringConcat(plr_lastmsg[i], chat_msg[i], sizeof(plr_lastmsg[i])); } else { M_StringCopy(plr_lastmsg[i], chat_msg[i], sizeof(plr_lastmsg[i])); } if (i != consoleplayer && (chat_dest[i] == consoleplayer + 1 || chat_dest[i] == CT_PLR_ALL) && *chat_msg[i]) { P_SetMessage(&players[consoleplayer], plr_lastmsg[i], true); S_StartSound(NULL, SFX_CHAT); } else if (i == consoleplayer && (*chat_msg[i])) { if (numplayers <= 1) { P_SetMessage(&players[consoleplayer], "THERE ARE NO OTHER PLAYERS IN THE GAME!", true); S_StartSound(NULL, SFX_CHAT); } } CT_ClearChatMessage(i); } else if (c == KEY_BACKSPACE) { CT_BackSpace(i); } else { CT_AddChar(i, c); } } } return; } //=========================================================================== // // CT_Drawer // //=========================================================================== void CT_Drawer(void) { int i; int x; patch_t *patch; if (chatmodeon) { x = 25; for (i = 0; i < msgptr[consoleplayer]; i++) { if (chat_msg[consoleplayer][i] < 33) { x += 6; } else { patch = W_CacheLumpNum(FontABaseLump + chat_msg[consoleplayer][i] - 33, PU_CACHE); V_DrawPatch(x, 10, patch); x += patch->width; } } V_DrawPatch(x, 10, W_CacheLumpName("FONTA59", PU_CACHE)); BorderTopRefresh = true; UpdateState |= I_MESSAGES; } } //=========================================================================== // // CT_queueChatChar // //=========================================================================== void CT_queueChatChar(char ch) { if (((tail + 1) & (QUEUESIZE - 1)) == head) { // the queue is full return; } ChatQueue[tail] = ch; tail = (tail + 1) & (QUEUESIZE - 1); } //=========================================================================== // // CT_dequeueChatChar // //=========================================================================== char CT_dequeueChatChar(void) { byte temp; if (head == tail) { // queue is empty return 0; } temp = ChatQueue[head]; head = (head + 1) & (QUEUESIZE - 1); return temp; } //=========================================================================== // // CT_AddChar // //=========================================================================== void CT_AddChar(int player, char c) { patch_t *patch; if (msgptr[player] + 1 >= MESSAGESIZE || msglen[player] >= MESSAGELEN) { // full. return; } chat_msg[player][msgptr[player]] = c; msgptr[player]++; if (c < 33) { msglen[player] += 6; } else { patch = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); msglen[player] += patch->width; } } //=========================================================================== // // CT_BackSpace // // Backs up a space, when the user hits (obviously) backspace //=========================================================================== void CT_BackSpace(int player) { patch_t *patch; char c; if (msgptr[player] == 0) { // message is already blank return; } msgptr[player]--; c = chat_msg[player][msgptr[player]]; if (c < 33) { msglen[player] -= 6; } else { patch = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); msglen[player] -= patch->width; } chat_msg[player][msgptr[player]] = 0; } //=========================================================================== // // CT_ClearChatMessage // // Clears out the data for the chat message, but the player's message // is still saved in plrmsg. //=========================================================================== void CT_ClearChatMessage(int player) { memset(chat_msg[player], 0, MESSAGESIZE); msgptr[player] = 0; msglen[player] = 0; } crispy-doom-crispy-doom-5.6.4/src/hexen/ct_chat.h000066400000000000000000000017341360717211000217150ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Chat mode stuff // #ifndef HEXEN_CT_CHAT_H #define HEXEN_CT_CHAT_H #define CT_PLR_GREEN 1 #define CT_PLR_YELLOW 2 #define CT_PLR_RED 3 #define CT_PLR_BLUE 4 #define CT_PLR_ALL 5 #define CT_KEY_GREEN 'g' #define CT_KEY_YELLOW 'y' #define CT_KEY_RED 'r' #define CT_KEY_BLUE 'b' #define CT_KEY_ALL 't' extern char *chat_macros[10]; #endif crispy-doom-crispy-doom-5.6.4/src/hexen/d_net.c000066400000000000000000000152721360717211000213760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM Network game communication and protocol, // all OS independend parts. // #include #include "m_argv.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "i_videohr.h" #include "h2def.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" #include "w_checksum.h" #include "deh_main.h" #include "d_loop.h" ticcmd_t *netcmds; extern void H2_DoAdvanceDemo(void); extern void H2_ProcessEvents(void); extern void G_BuildTiccmd(ticcmd_t *cmd, int maketic); extern boolean G_CheckDemoStatus(void); extern boolean demorecording; // Called when a player leaves the game static void PlayerQuitGame(player_t *player) { static char exitmsg[80]; unsigned int player_num; player_num = player - players; M_StringCopy(exitmsg, "PLAYER 1 LEFT THE GAME", sizeof(exitmsg)); exitmsg[7] += player_num; P_SetMessage(&players[consoleplayer], exitmsg, true); S_StartSound(NULL, SFX_CHAT); playeringame[player_num] = false; // TODO: check if it is sensible to do this: if (demorecording) { G_CheckDemoStatus (); } } static void RunTic(ticcmd_t *cmds, boolean *ingame) { extern boolean advancedemo; unsigned int i; // Check for player quits. for (i = 0; i < maxplayers; ++i) { if (!demoplayback && playeringame[i] && !ingame[i]) { PlayerQuitGame(&players[i]); } } netcmds = cmds; // check that there are players in the game. if not, we cannot // run a tic. if (advancedemo) H2_DoAdvanceDemo (); G_Ticker (); } static loop_interface_t hexen_loop_interface = { H2_ProcessEvents, G_BuildTiccmd, RunTic, MN_Ticker }; // Load game settings from the specified structure and // set global variables. static void LoadGameSettings(net_gamesettings_t *settings) { unsigned int i; deathmatch = settings->deathmatch; ticdup = settings->ticdup; startepisode = settings->episode; startmap = settings->map; startskill = settings->skill; // TODO startloadgame = settings->loadgame; lowres_turn = settings->lowres_turn; nomonsters = settings->nomonsters; respawnparm = settings->respawn_monsters; consoleplayer = settings->consoleplayer; if (lowres_turn) { printf("NOTE: Turning resolution is reduced; this is probably " "because there is a client recording a Vanilla demo.\n"); } for (i=0; inum_players; PlayerClass[i] = settings->player_classes[i]; if (PlayerClass[i] >= NUMCLASSES) { PlayerClass[i] = PCLASS_FIGHTER; } } } // Save the game settings from global variables to the specified // game settings structure. static void SaveGameSettings(net_gamesettings_t *settings) { // jhaley 20120715: Some parts of the structure are being left // uninitialized. If -class is not used on the command line, this // can lead to a crash in SB_Init due to player class == 0xCCCCCCCC. memset(settings, 0, sizeof(*settings)); // Fill in game settings structure with appropriate parameters // for the new game settings->deathmatch = deathmatch; settings->episode = startepisode; settings->map = startmap; settings->skill = startskill; // TODO settings->loadgame = startloadgame; settings->gameversion = exe_hexen_1_1; settings->nomonsters = nomonsters; settings->respawn_monsters = respawnparm; settings->timelimit = 0; settings->lowres_turn = M_ParmExists("-record") && !M_ParmExists("-longtics"); } static void InitConnectData(net_connect_data_t *connect_data) { int i; // // Connect data // // Game type fields: connect_data->gamemode = gamemode; connect_data->gamemission = hexen; // Are we recording a demo? Possibly set lowres turn mode connect_data->lowres_turn = M_ParmExists("-record") && !M_ParmExists("-longtics"); connect_data->drone = false; connect_data->max_players = maxplayers; //! // @category net // @arg // // Specify player class: 0=fighter, 1=cleric, 2=mage, 3=pig. // i = M_CheckParmWithArgs("-class", 1); if (i > 0) { connect_data->player_class = atoi(myargv[i + 1]); } else { connect_data->player_class = PCLASS_FIGHTER; } // Read checksums of our WAD directory and dehacked information W_Checksum(connect_data->wad_sha1sum); memset(connect_data->deh_sha1sum, 0, sizeof(sha1_digest_t)); connect_data->is_freedoom = 0; } void D_ConnectNetGame(void) { net_connect_data_t connect_data; InitConnectData(&connect_data); netgame = D_InitNetGame(&connect_data); //! // @category net // // Start the game playing as though in a netgame with a single // player. This can also be used to play back single player netgame // demos. // if (M_CheckParm("-solo-net") > 0) { netgame = true; } } static boolean StartupProgress(int now_ready, int total) { static int ready = 0; while (ready < now_ready) { ST_NetProgress(); ++ready; } ready = now_ready; // Allow the user to hit escape during netgame startup to abort. return !I_CheckAbortHR(); } // // D_CheckNetGame // Works out player numbers among the net participants // void D_CheckNetGame(void) { net_gamesettings_t settings; D_RegisterLoopCallbacks(&hexen_loop_interface); if (netgame) { autostart = true; } SaveGameSettings(&settings); D_StartNetGame(&settings, StartupProgress); LoadGameSettings(&settings); // Finish netgame progress on startup screen. if (netgame) { StartupProgress(settings.num_players, settings.num_players); ST_NetDone(); } } //========================================================================== // // NET_SendFrags // //========================================================================== void NET_SendFrags(player_t * player) { // Not sure what this is intended for. Unused? } crispy-doom-crispy-doom-5.6.4/src/hexen/f_finale.c000066400000000000000000000244731360717211000220530ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "i_system.h" #include "i_video.h" #include "p_local.h" #include "s_sound.h" #include #include "v_video.h" #include "i_swap.h" // MACROS ------------------------------------------------------------------ #define TEXTSPEED 3 #define TEXTWAIT 250 // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void TextWrite(void); static void DrawPic(void); static void InitializeFade(boolean fadeIn); static void DeInitializeFade(void); static void FadePic(void); static char *GetFinaleText(int sequence); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern boolean automapactive; extern boolean viewactive; // PUBLIC DATA DECLARATIONS ------------------------------------------------ // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int FinaleStage; static int FinaleCount; static int FinaleEndCount; static int FinaleLumpNum; static int FontABaseLump; static char *FinaleText; static fixed_t *Palette; static fixed_t *PaletteDelta; static byte *RealPalette; // CODE -------------------------------------------------------------------- //=========================================================================== // // F_StartFinale // //=========================================================================== void F_StartFinale(void) { gameaction = ga_nothing; gamestate = GS_FINALE; viewactive = false; automapactive = false; P_ClearMessage(&players[consoleplayer]); FinaleStage = 0; FinaleCount = 0; FinaleText = GetFinaleText(0); FinaleEndCount = 70; FinaleLumpNum = W_GetNumForName("FINALE1"); FontABaseLump = W_GetNumForName("FONTA_S") + 1; InitializeFade(1); // S_ChangeMusic(mus_victor, true); S_StartSongName("hall", false); // don't loop the song } //=========================================================================== // // F_Responder // //=========================================================================== boolean F_Responder(event_t * event) { return false; } //=========================================================================== // // F_Ticker // //=========================================================================== void F_Ticker(void) { FinaleCount++; if (FinaleStage < 5 && FinaleCount >= FinaleEndCount) { FinaleCount = 0; FinaleStage++; switch (FinaleStage) { case 1: // Text 1 FinaleEndCount = strlen(FinaleText) * TEXTSPEED + TEXTWAIT; break; case 2: // Pic 2, Text 2 FinaleText = GetFinaleText(1); FinaleEndCount = strlen(FinaleText) * TEXTSPEED + TEXTWAIT; FinaleLumpNum = W_GetNumForName("FINALE2"); S_StartSongName("orb", false); break; case 3: // Pic 2 -- Fade out FinaleEndCount = 70; DeInitializeFade(); InitializeFade(0); break; case 4: // Pic 3 -- Fade in FinaleLumpNum = W_GetNumForName("FINALE3"); FinaleEndCount = 71; DeInitializeFade(); InitializeFade(1); S_StartSongName("chess", true); break; case 5: // Pic 3 , Text 3 FinaleText = GetFinaleText(2); DeInitializeFade(); break; default: break; } return; } if (FinaleStage == 0 || FinaleStage == 3 || FinaleStage == 4) { FadePic(); } } //=========================================================================== // // TextWrite // //=========================================================================== static void TextWrite(void) { int count; char *ch; int c; int cx, cy; patch_t *w; V_CopyScaledBuffer(I_VideoBuffer, W_CacheLumpNum(FinaleLumpNum, PU_CACHE), ORIGWIDTH * ORIGHEIGHT); if (FinaleStage == 5) { // Chess pic, draw the correct character graphic if (netgame) { V_DrawPatch(20, 0, W_CacheLumpName("chessall", PU_CACHE)); } else if (PlayerClass[consoleplayer]) { V_DrawPatch(60, 0, W_CacheLumpNum(W_GetNumForName("chessc") + PlayerClass[consoleplayer] - 1, PU_CACHE)); } } // Draw the actual text if (FinaleStage == 5) { cy = 135; } else { cy = 5; } cx = 20; ch = FinaleText; count = (FinaleCount - 10) / TEXTSPEED; if (count < 0) { count = 0; } for (; count; count--) { c = *ch++; if (!c) { break; } if (c == '\n') { cx = 20; cy += 9; continue; } if (c < 32) { continue; } c = toupper(c); if (c == 32) { cx += 5; continue; } w = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); if (cx + SHORT(w->width) > SCREENWIDTH) { break; } V_DrawPatch(cx, cy, w); cx += SHORT(w->width); } } //=========================================================================== // // InitializeFade // //=========================================================================== static void InitializeFade(boolean fadeIn) { unsigned i; Palette = Z_Malloc(768 * sizeof(fixed_t), PU_STATIC, 0); PaletteDelta = Z_Malloc(768 * sizeof(fixed_t), PU_STATIC, 0); RealPalette = Z_Malloc(768 * sizeof(byte), PU_STATIC, 0); if (fadeIn) { memset(RealPalette, 0, 768 * sizeof(byte)); for (i = 0; i < 768; i++) { Palette[i] = 0; PaletteDelta[i] = FixedDiv((*((byte *) W_CacheLumpName("playpal", PU_CACHE) + i)) << FRACBITS, 70 * FRACUNIT); } } else { for (i = 0; i < 768; i++) { RealPalette[i] = *((byte *) W_CacheLumpName("playpal", PU_CACHE) + i); Palette[i] = RealPalette[i] << FRACBITS; PaletteDelta[i] = FixedDiv(Palette[i], -70 * FRACUNIT); } } I_SetPalette(RealPalette); } //=========================================================================== // // DeInitializeFade // //=========================================================================== static void DeInitializeFade(void) { Z_Free(Palette); Z_Free(PaletteDelta); Z_Free(RealPalette); } //=========================================================================== // // FadePic // //=========================================================================== static void FadePic(void) { unsigned i; for (i = 0; i < 768; i++) { Palette[i] += PaletteDelta[i]; RealPalette[i] = Palette[i] >> FRACBITS; } I_SetPalette(RealPalette); } //=========================================================================== // // DrawPic // //=========================================================================== static void DrawPic(void) { V_CopyScaledBuffer(I_VideoBuffer, W_CacheLumpNum(FinaleLumpNum, PU_CACHE), ORIGWIDTH * ORIGHEIGHT); if (FinaleStage == 4 || FinaleStage == 5) { // Chess pic, draw the correct character graphic if (netgame) { V_DrawPatch(20, 0, W_CacheLumpName("chessall", PU_CACHE)); } else if (PlayerClass[consoleplayer]) { V_DrawPatch(60, 0, W_CacheLumpNum(W_GetNumForName("chessc") + PlayerClass[consoleplayer] - 1, PU_CACHE)); } } } //=========================================================================== // // F_Drawer // //=========================================================================== void F_Drawer(void) { switch (FinaleStage) { case 0: // Fade in initial finale screen DrawPic(); break; case 1: case 2: TextWrite(); break; case 3: // Fade screen out DrawPic(); break; case 4: // Fade in chess screen DrawPic(); break; case 5: TextWrite(); break; } UpdateState |= I_FULLSCRN; } //========================================================================== // // GetFinaleText // //========================================================================== static char *GetFinaleText(int sequence) { const char *msgLumpName; int msgSize; int msgLump; static const char *winMsgLumpNames[] = { "win1msg", "win2msg", "win3msg" }; msgLumpName = winMsgLumpNames[sequence]; msgLump = W_GetNumForName(msgLumpName); msgSize = W_LumpLength(msgLump); if (msgSize >= MAX_INTRMSN_MESSAGE_SIZE) { I_Error("Finale message too long (%s)", msgLumpName); } W_ReadLump(msgLump, ClusterMessage); ClusterMessage[msgSize] = 0; // Append terminator return ClusterMessage; } crispy-doom-crispy-doom-5.6.4/src/hexen/g_game.c000066400000000000000000001514341360717211000215250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "m_random.h" #include "h2def.h" #include "s_sound.h" #include "doomkeys.h" #include "i_input.h" #include "i_video.h" #include "i_system.h" #include "i_timer.h" #include "m_argv.h" #include "m_controls.h" #include "m_misc.h" #include "p_local.h" #include "v_video.h" #define AM_STARTKEY 9 // External functions extern void R_InitSky(int map); extern void P_PlayerNextArtifact(player_t * player); // Functions boolean G_CheckDemoStatus(void); void G_ReadDemoTiccmd(ticcmd_t * cmd); void G_WriteDemoTiccmd(ticcmd_t * cmd); void G_DoReborn(int playernum); void G_DoLoadLevel(void); void G_DoInitNew(void); void G_DoNewGame(void); void G_DoPlayDemo(void); void G_DoTeleportNewMap(void); void G_DoCompleted(void); void G_DoVictory(void); void G_DoWorldDone(void); void G_DoSaveGame(void); void G_DoSingleReborn(void); void H2_PageTicker(void); void H2_AdvanceDemo(void); extern boolean mn_SuicideConsole; gameaction_t gameaction; gamestate_t gamestate; skill_t gameskill; //boolean respawnmonsters; int gameepisode; int gamemap; int prevmap; boolean paused; boolean sendpause; // send a pause event next tic boolean sendsave; // send a save event next tic boolean usergame; // ok to save / end game boolean timingdemo; // if true, exit with report on completion int starttime; // for comparative timing purposes boolean viewactive; boolean deathmatch; // only if started as net death boolean netgame; // only true if packets are broadcast boolean playeringame[MAXPLAYERS]; player_t players[MAXPLAYERS]; pclass_t PlayerClass[MAXPLAYERS]; // Position indicator for cooperative net-play reborn int RebornPosition; int consoleplayer; // player taking events and displaying int displayplayer; // view being displayed int levelstarttic; // gametic at level start char demoname[32]; boolean demorecording; boolean longtics; // specify high resolution turning in demos boolean lowres_turn; boolean shortticfix; // calculate lowres turning like doom boolean demoplayback; boolean demoextend; byte *demobuffer, *demo_p, *demoend; boolean singledemo; // quit after playing a demo from cmdline boolean precache = true; // if true, load all graphics at start // TODO: Hexen uses 16-bit shorts for consistancy? byte consistancy[MAXPLAYERS][BACKUPTICS]; int mouseSensitivity = 5; int LeaveMap; static int LeavePosition; //#define MAXPLMOVE 0x32 // Old Heretic Max move fixed_t MaxPlayerMove[NUMCLASSES] = { 0x3C, 0x32, 0x2D, 0x31 }; fixed_t forwardmove[NUMCLASSES][2] = { {0x1D, 0x3C}, {0x19, 0x32}, {0x16, 0x2E}, {0x18, 0x31} }; fixed_t sidemove[NUMCLASSES][2] = { {0x1B, 0x3B}, {0x18, 0x28}, {0x15, 0x25}, {0x17, 0x27} }; fixed_t angleturn[3] = { 640, 1280, 320 }; // + slow turn static int *weapon_keys[] = { &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, }; static int next_weapon = 0; #define SLOWTURNTICS 6 #define NUMKEYS 256 boolean gamekeydown[NUMKEYS]; int turnheld; // for accelerative turning int lookheld; boolean mousearray[MAX_MOUSE_BUTTONS + 1]; boolean *mousebuttons = &mousearray[1]; // allow [-1] int mousex, mousey; // mouse values are used once int dclicktime, dclickstate, dclicks; int dclicktime2, dclickstate2, dclicks2; #define MAX_JOY_BUTTONS 20 int joyxmove, joyymove; // joystick values are repeated int joystrafemove; int joylook; boolean joyarray[MAX_JOY_BUTTONS + 1]; boolean *joybuttons = &joyarray[1]; // allow [-1] int savegameslot; char savedescription[32]; int vanilla_demo_limit = 1; int inventoryTics; // haleyjd: removed externdriver crap static skill_t TempSkill; static int TempEpisode; static int TempMap; boolean testcontrols = false; int testcontrols_mousespeed; //============================================================================= /* ==================== = = 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 ==================== */ extern boolean inventory; boolean usearti = true; void G_BuildTiccmd(ticcmd_t *cmd, int maketic) { int i; boolean strafe, bstrafe; int speed, tspeed, lspeed; int forward, side; int look, arti; int flyheight; int pClass; extern boolean artiskip; // haleyjd: removed externdriver crap pClass = players[consoleplayer].class; memset(cmd, 0, sizeof(*cmd)); // cmd->consistancy = // consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS]; cmd->consistancy = consistancy[consoleplayer][maketic % BACKUPTICS]; //printf ("cons: %i\n",cmd->consistancy); strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; // Allow joybspeed hack. speed = key_speed >= NUMKEYS || joybspeed >= MAX_JOY_BUTTONS || gamekeydown[key_speed] || joybuttons[joybspeed]; // haleyjd: removed externdriver crap forward = side = look = arti = flyheight = 0; // // use two stage accelerative turning on the keyboard and joystick // if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || gamekeydown[key_left]) turnheld += ticdup; else turnheld = 0; if (turnheld < SLOWTURNTICS) tspeed = 2; // slow turn else tspeed = speed; if (gamekeydown[key_lookdown] || gamekeydown[key_lookup]) { lookheld += ticdup; } else { lookheld = 0; } if (lookheld < SLOWTURNTICS) { lspeed = 1; // 3; } else { lspeed = 2; // 5; } // // let movement keys cancel each other out // if (strafe) { if (gamekeydown[key_right]) { side += sidemove[pClass][speed]; } if (gamekeydown[key_left]) { side -= sidemove[pClass][speed]; } if (joyxmove > 0) { side += sidemove[pClass][speed]; } if (joyxmove < 0) { side -= sidemove[pClass][speed]; } } else { if (gamekeydown[key_right]) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left]) cmd->angleturn += angleturn[tspeed]; if (joyxmove > 0) cmd->angleturn -= angleturn[tspeed]; if (joyxmove < 0) cmd->angleturn += angleturn[tspeed]; } if (gamekeydown[key_up]) { forward += forwardmove[pClass][speed]; } if (gamekeydown[key_down]) { forward -= forwardmove[pClass][speed]; } if (joyymove < 0) { forward += forwardmove[pClass][speed]; } if (joyymove > 0) { forward -= forwardmove[pClass][speed]; } if (gamekeydown[key_straferight] || mousebuttons[mousebstraferight] || joystrafemove > 0 || joybuttons[joybstraferight]) { side += sidemove[pClass][speed]; } if (gamekeydown[key_strafeleft] || mousebuttons[mousebstrafeleft] || joystrafemove < 0 || joybuttons[joybstrafeleft]) { side -= sidemove[pClass][speed]; } // Look up/down/center keys if (gamekeydown[key_lookup] || joylook < 0) { look = lspeed; } if (gamekeydown[key_lookdown] || joylook > 0) { look = -lspeed; } // haleyjd: removed externdriver crap if (gamekeydown[key_lookcenter]) { look = TOCENTER; } // haleyjd: removed externdriver crap // Fly up/down/drop keys if (gamekeydown[key_flyup]) { flyheight = 5; // note that the actual flyheight will be twice this } if (gamekeydown[key_flydown]) { flyheight = -5; } if (gamekeydown[key_flycenter]) { flyheight = TOCENTER; // haleyjd: removed externdriver crap look = TOCENTER; } // Use artifact key if (gamekeydown[key_useartifact]) { if (gamekeydown[key_speed] && artiskip) { if (players[consoleplayer].inventory[inv_ptr].type != arti_none) { // Skip an artifact gamekeydown[key_useartifact] = false; P_PlayerNextArtifact(&players[consoleplayer]); } } else { if (inventory) { players[consoleplayer].readyArtifact = players[consoleplayer].inventory[inv_ptr].type; inventory = false; cmd->arti = 0; usearti = false; } else if (usearti) { cmd->arti |= players[consoleplayer].inventory[inv_ptr]. type & AFLAG_MASK; usearti = false; } } } if (gamekeydown[key_jump] || mousebuttons[mousebjump] || joybuttons[joybjump]) { cmd->arti |= AFLAG_JUMP; } if (mn_SuicideConsole) { cmd->arti |= AFLAG_SUICIDE; mn_SuicideConsole = false; } // Artifact hot keys if (gamekeydown[key_arti_all] && !cmd->arti) { gamekeydown[key_arti_all] = false; // Use one of each artifact cmd->arti = NUMARTIFACTS; } else if (gamekeydown[key_arti_health] && !cmd->arti && (players[consoleplayer].mo->health < MAXHEALTH)) { gamekeydown[key_arti_health] = false; cmd->arti = arti_health; } else if (gamekeydown[key_arti_poisonbag] && !cmd->arti) { gamekeydown[key_arti_poisonbag] = false; cmd->arti = arti_poisonbag; } else if (gamekeydown[key_arti_blastradius] && !cmd->arti) { gamekeydown[key_arti_blastradius] = false; cmd->arti = arti_blastradius; } else if (gamekeydown[key_arti_teleport] && !cmd->arti) { gamekeydown[key_arti_teleport] = false; cmd->arti = arti_teleport; } else if (gamekeydown[key_arti_teleportother] && !cmd->arti) { gamekeydown[key_arti_teleportother] = false; cmd->arti = arti_teleportother; } else if (gamekeydown[key_arti_egg] && !cmd->arti) { gamekeydown[key_arti_egg] = false; cmd->arti = arti_egg; } else if (gamekeydown[key_arti_invulnerability] && !cmd->arti && !players[consoleplayer].powers[pw_invulnerability]) { gamekeydown[key_arti_invulnerability] = false; cmd->arti = arti_invulnerability; } // // buttons // cmd->chatchar = CT_dequeueChatChar(); if (gamekeydown[key_fire] || mousebuttons[mousebfire] || joybuttons[joybfire]) cmd->buttons |= BT_ATTACK; if (gamekeydown[key_use] || joybuttons[joybuse] || mousebuttons[mousebuse]) { cmd->buttons |= BT_USE; dclicks = 0; // clear double clicks if hit use button } // Weapon cycling. Switch to previous or next weapon. // (Disabled when player is a pig). if (gamestate == GS_LEVEL && players[consoleplayer].morphTics == 0 && next_weapon != 0) { int start_i; if (players[consoleplayer].pendingweapon == WP_NOCHANGE) { i = players[consoleplayer].readyweapon; } else { i = players[consoleplayer].pendingweapon; } // Don't loop forever. start_i = i; do { i = (i + next_weapon + NUMWEAPONS) % NUMWEAPONS; } while (i != start_i && !players[consoleplayer].weaponowned[i]); cmd->buttons |= BT_CHANGE; cmd->buttons |= i << BT_WEAPONSHIFT; } else { for (i=0; ibuttons |= BT_CHANGE; cmd->buttons |= i< 1) { dclickstate = mousebuttons[mousebforward]; if (dclickstate) dclicks++; if (dclicks == 2) { cmd->buttons |= BT_USE; dclicks = 0; } else dclicktime = 0; } else { dclicktime += ticdup; if (dclicktime > 20) { dclicks = 0; dclickstate = 0; } } // // strafe double click // bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; if (bstrafe != dclickstate2 && dclicktime2 > 1) { dclickstate2 = bstrafe; if (dclickstate2) dclicks2++; if (dclicks2 == 2) { cmd->buttons |= BT_USE; dclicks2 = 0; } else dclicktime2 = 0; } else { dclicktime2 += ticdup; if (dclicktime2 > 20) { dclicks2 = 0; dclickstate2 = 0; } } } if (strafe) { side += mousex * 2; } else { cmd->angleturn -= mousex * 0x8; } if (mousex == 0) { testcontrols_mousespeed = 0; } if (!novert) forward += mousey; mousex = mousey = 0; if (forward > MaxPlayerMove[pClass]) { forward = MaxPlayerMove[pClass]; } else if (forward < -MaxPlayerMove[pClass]) { forward = -MaxPlayerMove[pClass]; } if (side > MaxPlayerMove[pClass]) { side = MaxPlayerMove[pClass]; } else if (side < -MaxPlayerMove[pClass]) { side = -MaxPlayerMove[pClass]; } if (players[consoleplayer].powers[pw_speed] && !players[consoleplayer].morphTics) { // Adjust for a player with a speed artifact forward = (3 * forward) >> 1; side = (3 * side) >> 1; } cmd->forwardmove += forward; cmd->sidemove += side; if (players[consoleplayer].playerstate == PST_LIVE) { if (look < 0) { look += 16; } cmd->lookfly = look; } if (flyheight < 0) { flyheight += 16; } cmd->lookfly |= flyheight << 4; // // special buttons // if (sendpause) { sendpause = false; cmd->buttons = BT_SPECIAL | BTS_PAUSE; } if (sendsave) { sendsave = false; cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT); } if (lowres_turn) { if (shortticfix) { static signed short carry = 0; signed short desired_angleturn; desired_angleturn = cmd->angleturn + carry; // round angleturn to the nearest 256 unit boundary // for recording demos with single byte values for turn cmd->angleturn = (desired_angleturn + 128) & 0xff00; // Carry forward the error from the reduced resolution to the // next tic, so that successive small movements can accumulate. carry = desired_angleturn - cmd->angleturn; } else { // truncate angleturn to the nearest 256 boundary // for recording demos with single byte values for turn cmd->angleturn &= 0xff00; } } } /* ============== = = G_DoLoadLevel = ============== */ void G_DoLoadLevel(void) { int i; levelstarttic = gametic; // for time calculation gamestate = GS_LEVEL; for (i = 0; i < maxplayers; i++) { if (playeringame[i] && players[i].playerstate == PST_DEAD) players[i].playerstate = PST_REBORN; memset(players[i].frags, 0, sizeof(players[i].frags)); } SN_StopAllSequences(); P_SetupLevel(gameepisode, gamemap, 0, gameskill); displayplayer = consoleplayer; // view the guy you are playing gameaction = ga_nothing; Z_CheckHeap(); // // clear cmd building stuff // memset(gamekeydown, 0, sizeof(gamekeydown)); joyxmove = joyymove = joystrafemove = joylook = 0; mousex = mousey = 0; sendpause = sendsave = paused = false; memset(mousearray, 0, sizeof(mousearray)); memset(joyarray, 0, sizeof(joyarray)); if (testcontrols) { P_SetMessage(&players[consoleplayer], "PRESS ESCAPE TO QUIT.", false); } } static void SetJoyButtons(unsigned int buttons_mask) { int i; for (i=0; itype == ev_keyup && ev->data1 == key_useartifact) { // flag to denote that it's okay to use an artifact if (!inventory) { plr->readyArtifact = plr->inventory[inv_ptr].type; } usearti = true; } // Check for spy mode player cycle if (gamestate == GS_LEVEL && ev->type == ev_keydown && ev->data1 == key_spy && !deathmatch) { // Cycle the display player do { displayplayer++; if (displayplayer == maxplayers) { displayplayer = 0; } } while (!playeringame[displayplayer] && displayplayer != consoleplayer); return (true); } if (CT_Responder(ev)) { // Chat ate the event return (true); } if (gamestate == GS_LEVEL) { if (SB_Responder(ev)) { // Status bar ate the event return (true); } if (AM_Responder(ev)) { // Automap ate the event return (true); } } if (ev->type == ev_mouse) { testcontrols_mousespeed = abs(ev->data2); } if (ev->type == ev_keydown && ev->data1 == key_prevweapon) { next_weapon = -1; } else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) { next_weapon = 1; } switch (ev->type) { case ev_keydown: if (ev->data1 == key_invleft) { inventoryTics = 5 * 35; if (!inventory) { inventory = true; break; } inv_ptr--; if (inv_ptr < 0) { inv_ptr = 0; } else { curpos--; if (curpos < 0) { curpos = 0; } } return (true); } if (ev->data1 == key_invright) { inventoryTics = 5 * 35; if (!inventory) { inventory = true; break; } inv_ptr++; if (inv_ptr >= plr->inventorySlotNum) { inv_ptr--; if (inv_ptr < 0) inv_ptr = 0; } else { curpos++; if (curpos > 6) { curpos = 6; } } return (true); } if (ev->data1 == key_pause && !MenuActive) { sendpause = true; return (true); } if (ev->data1 < NUMKEYS) { gamekeydown[ev->data1] = true; } return (true); // eat key down events case ev_keyup: if (ev->data1 < NUMKEYS) { gamekeydown[ev->data1] = false; } return (false); // always let key up events filter down case ev_mouse: SetMouseButtons(ev->data1); mousex = ev->data2 * (mouseSensitivity + 5) / 10; mousey = ev->data3 * (mouseSensitivity + 5) / 10; return (true); // eat events case ev_joystick: SetJoyButtons(ev->data1); joyxmove = ev->data2; joyymove = ev->data3; joystrafemove = ev->data4; joylook = ev->data5; return (true); // eat events default: break; } return (false); } //========================================================================== // // G_Ticker // //========================================================================== void G_Ticker(void) { int i, buf; ticcmd_t *cmd = NULL; // // do player reborns if needed // for (i = 0; i < maxplayers; i++) if (playeringame[i] && players[i].playerstate == PST_REBORN) G_DoReborn(i); // // do things to change the game state // while (gameaction != ga_nothing) { switch (gameaction) { case ga_loadlevel: G_DoLoadLevel(); break; case ga_initnew: G_DoInitNew(); break; case ga_newgame: G_DoNewGame(); break; case ga_loadgame: Draw_LoadIcon(); G_DoLoadGame(); break; case ga_savegame: Draw_SaveIcon(); G_DoSaveGame(); break; case ga_singlereborn: G_DoSingleReborn(); break; case ga_playdemo: G_DoPlayDemo(); break; case ga_screenshot: V_ScreenShot("HEXEN%02i.%s"); P_SetMessage(&players[consoleplayer], "SCREEN SHOT", false); gameaction = ga_nothing; break; case ga_leavemap: Draw_TeleportIcon(); G_DoTeleportNewMap(); break; case ga_completed: G_DoCompleted(); break; case ga_worlddone: G_DoWorldDone(); break; case ga_victory: F_StartFinale(); break; default: break; } } // // get commands, check consistancy, and build new consistancy check // //buf = gametic%BACKUPTICS; buf = (gametic / ticdup) % BACKUPTICS; for (i = 0; i < maxplayers; i++) if (playeringame[i]) { cmd = &players[i].cmd; memcpy(cmd, &netcmds[i], sizeof(ticcmd_t)); if (demoplayback) G_ReadDemoTiccmd(cmd); if (demorecording) G_WriteDemoTiccmd(cmd); if (netgame && !(gametic % ticdup)) { if (gametic > BACKUPTICS && consistancy[i][buf] != cmd->consistancy) { I_Error("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] = rndindex; } } // // check for special buttons // for (i = 0; i < maxplayers; i++) if (playeringame[i]) { if (players[i].cmd.buttons & BT_SPECIAL) { switch (players[i].cmd.buttons & BT_SPECIALMASK) { case BTS_PAUSE: paused ^= 1; if (paused) { S_PauseSound(); } else { S_ResumeSound(); } break; case BTS_SAVEGAME: if (!savedescription[0]) { if (netgame) { M_StringCopy(savedescription, "NET GAME", sizeof(savedescription)); } else { M_StringCopy(savedescription, "SAVE GAME", sizeof(savedescription)); } } savegameslot = (players[i].cmd. buttons & BTS_SAVEMASK) >> BTS_SAVESHIFT; gameaction = ga_savegame; break; } } } // turn inventory off after a certain amount of time if (inventory && !(--inventoryTics)) { players[consoleplayer].readyArtifact = players[consoleplayer].inventory[inv_ptr].type; inventory = false; cmd->arti = 0; } // // do main actions // // // do main actions // switch (gamestate) { case GS_LEVEL: P_Ticker(); SB_Ticker(); AM_Ticker(); CT_Ticker(); break; case GS_INTERMISSION: IN_Ticker(); break; case GS_FINALE: F_Ticker(); break; case GS_DEMOSCREEN: H2_PageTicker(); break; } } /* ============================================================================== PLAYER STRUCTURE FUNCTIONS also see P_SpawnPlayer in P_Things ============================================================================== */ //========================================================================== // // G_PlayerExitMap // // Called when the player leaves a map. // //========================================================================== void G_PlayerExitMap(int playerNumber) { int i; player_t *player; int flightPower; player = &players[playerNumber]; // if(deathmatch) // { // // Strip all but one of each type of artifact // for(i = 0; i < player->inventorySlotNum; i++) // { // player->inventory[i].count = 1; // } // player->artifactCount = player->inventorySlotNum; // } // else // Strip all current powers (retain flight) flightPower = player->powers[pw_flight]; memset(player->powers, 0, sizeof(player->powers)); player->powers[pw_flight] = flightPower; if (deathmatch) { player->powers[pw_flight] = 0; } else { if (P_GetMapCluster(gamemap) != P_GetMapCluster(LeaveMap)) { // Entering new cluster // Strip all keys player->keys = 0; // Strip flight artifact for (i = 0; i < 25; i++) { player->powers[pw_flight] = 0; P_PlayerUseArtifact(player, arti_fly); } player->powers[pw_flight] = 0; } } if (player->morphTics) { player->readyweapon = player->mo->special1.i; // Restore weapon player->morphTics = 0; } player->messageTics = 0; player->lookdir = 0; player->mo->flags &= ~MF_SHADOW; // Remove invisibility player->extralight = 0; // Remove weapon flashes player->fixedcolormap = 0; // Remove torch player->damagecount = 0; // No palette changes player->bonuscount = 0; player->poisoncount = 0; if (player == &players[consoleplayer]) { SB_state = -1; // refresh the status bar viewangleoffset = 0; } } //========================================================================== // // G_PlayerReborn // // Called after a player dies. Almost everything is cleared and // initialized. // //========================================================================== void G_PlayerReborn(int player) { player_t *p; int frags[MAXPLAYERS]; int killcount, itemcount, secretcount; unsigned int worldTimer; memcpy(frags, players[player].frags, sizeof(frags)); killcount = players[player].killcount; itemcount = players[player].itemcount; secretcount = players[player].secretcount; worldTimer = players[player].worldTimer; p = &players[player]; memset(p, 0, sizeof(*p)); memcpy(players[player].frags, frags, sizeof(players[player].frags)); players[player].killcount = killcount; players[player].itemcount = itemcount; players[player].secretcount = secretcount; players[player].worldTimer = worldTimer; players[player].class = PlayerClass[player]; p->usedown = p->attackdown = true; // don't do anything immediately p->playerstate = PST_LIVE; p->health = MAXHEALTH; p->readyweapon = p->pendingweapon = WP_FIRST; p->weaponowned[WP_FIRST] = true; p->messageTics = 0; p->lookdir = 0; localQuakeHappening[player] = false; if (p == &players[consoleplayer]) { SB_state = -1; // refresh the status bar inv_ptr = 0; // reset the inventory pointer curpos = 0; viewangleoffset = 0; } } /* ==================== = = G_CheckSpot = = Returns false if the player cannot be respawned at the given mapthing_t spot = because something is occupying it ==================== */ void P_SpawnPlayer(mapthing_t * mthing); boolean G_CheckSpot(int playernum, mapthing_t * mthing) { fixed_t x, y; subsector_t *ss; unsigned an; mobj_t *mo; x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; players[playernum].mo->flags2 &= ~MF2_PASSMOBJ; if (!P_CheckPosition(players[playernum].mo, x, y)) { players[playernum].mo->flags2 |= MF2_PASSMOBJ; return false; } players[playernum].mo->flags2 |= MF2_PASSMOBJ; // spawn a teleport fog ss = R_PointInSubsector(x, y); an = ((unsigned) ANG45 * (mthing->angle / 45)) >> ANGLETOFINESHIFT; mo = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an], ss->sector->floorheight + TELEFOGHEIGHT, MT_TFOG); if (players[consoleplayer].viewz != 1) S_StartSound(mo, SFX_TELEPORT); // don't start sound on first frame return true; } /* ==================== = = G_DeathMatchSpawnPlayer = = Spawns a player at one of the random death match spots = called at level load and each death ==================== */ void G_DeathMatchSpawnPlayer(int playernum) { int i, j; int selections; selections = deathmatch_p - deathmatchstarts; // This check has been moved to p_setup.c:P_LoadThings() //if (selections < 8) // I_Error ("Only %i deathmatch spots, 8 required", selections); for (j = 0; j < 20; j++) { i = P_Random() % selections; if (G_CheckSpot(playernum, &deathmatchstarts[i])) { deathmatchstarts[i].type = playernum + 1; P_SpawnPlayer(&deathmatchstarts[i]); return; } } // no good spot, so the player will probably get stuck P_SpawnPlayer(&playerstarts[0][playernum]); } //========================================================================== // // G_DoReborn // //========================================================================== void G_DoReborn(int playernum) { int i; boolean oldWeaponowned[NUMWEAPONS]; int oldKeys; int oldPieces; boolean foundSpot; int bestWeapon; // quit demo unless -demoextend if (!demoextend && G_CheckDemoStatus()) { return; } if (!netgame) { if (SV_RebornSlotAvailable()) { // Use the reborn code if the slot is available gameaction = ga_singlereborn; } else { // Start a new game if there's no reborn info gameaction = ga_newgame; } } else { // Net-game players[playernum].mo->player = NULL; // Dissassociate the corpse if (deathmatch) { // Spawn at random spot if in death match G_DeathMatchSpawnPlayer(playernum); return; } // Cooperative net-play, retain keys and weapons oldKeys = players[playernum].keys; oldPieces = players[playernum].pieces; for (i = 0; i < NUMWEAPONS; i++) { oldWeaponowned[i] = players[playernum].weaponowned[i]; } foundSpot = false; if (G_CheckSpot(playernum, &playerstarts[RebornPosition][playernum])) { // Appropriate player start spot is open P_SpawnPlayer(&playerstarts[RebornPosition][playernum]); foundSpot = true; } else { // Try to spawn at one of the other player start spots for (i = 0; i < maxplayers; i++) { if (G_CheckSpot(playernum, &playerstarts[RebornPosition][i])) { // Found an open start spot // Fake as other player playerstarts[RebornPosition][i].type = playernum + 1; P_SpawnPlayer(&playerstarts[RebornPosition][i]); // Restore proper player type playerstarts[RebornPosition][i].type = i + 1; foundSpot = true; break; } } } if (foundSpot == false) { // Player's going to be inside something P_SpawnPlayer(&playerstarts[RebornPosition][playernum]); } // Restore keys and weapons players[playernum].keys = oldKeys; players[playernum].pieces = oldPieces; for (bestWeapon = 0, i = 0; i < NUMWEAPONS; i++) { if (oldWeaponowned[i]) { bestWeapon = i; players[playernum].weaponowned[i] = true; } } players[playernum].mana[MANA_1] = 25; players[playernum].mana[MANA_2] = 25; if (bestWeapon) { // Bring up the best weapon players[playernum].pendingweapon = bestWeapon; } } } void G_ScreenShot(void) { gameaction = ga_screenshot; } //========================================================================== // // G_StartNewInit // //========================================================================== void G_StartNewInit(void) { SV_InitBaseSlot(); SV_ClearRebornSlot(); P_ACSInitNewGame(); // Default the player start spot group to 0 RebornPosition = 0; } //========================================================================== // // G_StartNewGame // //========================================================================== void G_StartNewGame(skill_t skill) { int realMap; G_StartNewInit(); realMap = P_TranslateMap(1); if (realMap == -1) { realMap = 1; } G_InitNew(TempSkill, 1, realMap); } //========================================================================== // // G_TeleportNewMap // // Only called by the warp cheat code. Works just like normal map to map // teleporting, but doesn't do any interlude stuff. // //========================================================================== void G_TeleportNewMap(int map, int position) { gameaction = ga_leavemap; LeaveMap = map; LeavePosition = position; } //========================================================================== // // G_DoTeleportNewMap // //========================================================================== void G_DoTeleportNewMap(void) { SV_MapTeleport(LeaveMap, LeavePosition); gamestate = GS_LEVEL; gameaction = ga_nothing; RebornPosition = LeavePosition; } /* boolean secretexit; void G_ExitLevel (void) { secretexit = false; gameaction = ga_completed; } void G_SecretExitLevel (void) { secretexit = true; gameaction = ga_completed; } */ //========================================================================== // // G_Completed // // Starts intermission routine, which is used only during hub exits, // and DeathMatch games. //========================================================================== void G_Completed(int map, int position) { if (gamemode == shareware && map > 4) { P_SetMessage(&players[consoleplayer], "ACCESS DENIED -- DEMO", true); S_StartSound(NULL, SFX_CHAT); return; } gameaction = ga_completed; LeaveMap = map; LeavePosition = position; } void G_DoCompleted(void) { int i; gameaction = ga_nothing; // quit demo unless -demoextend if (!demoextend && G_CheckDemoStatus()) { return; } for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { G_PlayerExitMap(i); } } if (LeaveMap == -1 && LeavePosition == -1) { gameaction = ga_victory; return; } else { gamestate = GS_INTERMISSION; IN_Start(); } /* int i; static int afterSecret[3] = { 7, 5, 5 }; gameaction = ga_nothing; if(G_CheckDemoStatus()) { return; } for(i = 0; i < maxplayers; i++) { if(playeringame[i]) { G_PlayerFinishLevel(i); } } prevmap = gamemap; if(secretexit == true) { gamemap = 9; } else if(gamemap == 9) { // Finished secret level gamemap = afterSecret[gameepisode-1]; } else if(gamemap == 8) { gameaction = ga_victory; return; } else { gamemap++; } gamestate = GS_INTERMISSION; IN_Start(); */ } //============================================================================ // // G_WorldDone // //============================================================================ void G_WorldDone(void) { gameaction = ga_worlddone; } //============================================================================ // // G_DoWorldDone // //============================================================================ void G_DoWorldDone(void) { gamestate = GS_LEVEL; G_DoLoadLevel(); gameaction = ga_nothing; viewactive = true; } //========================================================================== // // G_DoSingleReborn // // Called by G_Ticker based on gameaction. Loads a game from the reborn // save slot. // //========================================================================== void G_DoSingleReborn(void) { gameaction = ga_nothing; SV_LoadGame(SV_GetRebornSlot()); SB_SetClassData(); } //========================================================================== // // G_LoadGame // // Can be called by the startup code or the menu task. // //========================================================================== static int GameLoadSlot; void G_LoadGame(int slot) { GameLoadSlot = slot; gameaction = ga_loadgame; } //========================================================================== // // G_DoLoadGame // // Called by G_Ticker based on gameaction. // //========================================================================== void G_DoLoadGame(void) { gameaction = ga_nothing; SV_LoadGame(GameLoadSlot); if (!netgame) { // Copy the base slot to the reborn slot SV_UpdateRebornSlot(); } SB_SetClassData(); } //========================================================================== // // G_SaveGame // // Called by the menu task. is a 24 byte text string. // //========================================================================== void G_SaveGame(int slot, char *description) { savegameslot = slot; M_StringCopy(savedescription, description, sizeof(savedescription)); sendsave = true; } //========================================================================== // // G_DoSaveGame // // Called by G_Ticker based on gameaction. // //========================================================================== void G_DoSaveGame(void) { SV_SaveGame(savegameslot, savedescription); gameaction = ga_nothing; savedescription[0] = 0; P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true); } //========================================================================== // // G_DeferredNewGame // //========================================================================== void G_DeferredNewGame(skill_t skill) { TempSkill = skill; gameaction = ga_newgame; } //========================================================================== // // G_DoNewGame // //========================================================================== void G_DoNewGame(void) { G_StartNewGame(TempSkill); gameaction = ga_nothing; } /* ==================== = = G_InitNew = = Can be called by the startup code or the menu task = consoleplayer, displayplayer, playeringame[] should be set ==================== */ void G_DeferedInitNew(skill_t skill, int episode, int map) { TempSkill = skill; TempEpisode = episode; TempMap = map; gameaction = ga_initnew; } void G_DoInitNew(void) { SV_InitBaseSlot(); G_InitNew(TempSkill, TempEpisode, TempMap); gameaction = ga_nothing; } void G_InitNew(skill_t skill, int episode, int map) { int i; if (paused) { paused = false; S_ResumeSound(); } if (skill < sk_baby) { skill = sk_baby; } if (skill > sk_nightmare) { skill = sk_nightmare; } if (map < 1) { map = 1; } if (map > 99) { map = 99; } M_ClearRandom(); // Force players to be initialized upon first level load for (i = 0; i < maxplayers; i++) { players[i].playerstate = PST_REBORN; players[i].worldTimer = 0; } // Set up a bunch of globals if (!demoextend) { // This prevents map-loading from interrupting a demo. // demoextend is set back to false only if starting a new game or // loading a saved one from the menu, and only during playback. demorecording = false; demoplayback = false; usergame = true; // will be set false if a demo } paused = false; viewactive = true; gameepisode = episode; gamemap = map; gameskill = skill; BorderNeedRefresh = true; // Initialize the sky R_InitSky(map); // Give one null ticcmd_t //gametic = 0; //maketic = 1; //for (i=0 ; iforwardmove = ((signed char) *demo_p++); cmd->sidemove = ((signed char) *demo_p++); // If this is a longtics demo, read back in higher resolution if (longtics) { cmd->angleturn = *demo_p++; cmd->angleturn |= (*demo_p++) << 8; } else { cmd->angleturn = ((unsigned char) *demo_p++) << 8; } cmd->buttons = (unsigned char) *demo_p++; cmd->lookfly = (unsigned char) *demo_p++; cmd->arti = (unsigned char) *demo_p++; } // Increase the size of the demo buffer to allow unlimited demos static void IncreaseDemoBuffer(void) { int current_length; byte *new_demobuffer; byte *new_demop; int new_length; // Find the current size current_length = demoend - demobuffer; // Generate a new buffer twice the size new_length = current_length * 2; new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0); new_demop = new_demobuffer + (demo_p - demobuffer); // Copy over the old data memcpy(new_demobuffer, demobuffer, current_length); // Free the old buffer and point the demo pointers at the new buffer. Z_Free(demobuffer); demobuffer = new_demobuffer; demo_p = new_demop; demoend = demobuffer + new_length; } void G_WriteDemoTiccmd(ticcmd_t * cmd) { byte *demo_start; if (gamekeydown[key_demo_quit]) // press to end demo recording G_CheckDemoStatus(); demo_start = demo_p; *demo_p++ = cmd->forwardmove; *demo_p++ = cmd->sidemove; // If this is a longtics demo, record in higher resolution if (longtics) { *demo_p++ = (cmd->angleturn & 0xff); *demo_p++ = (cmd->angleturn >> 8) & 0xff; } else { *demo_p++ = cmd->angleturn >> 8; } *demo_p++ = cmd->buttons; *demo_p++ = cmd->lookfly; *demo_p++ = cmd->arti; // reset demo pointer back demo_p = demo_start; if (demo_p > demoend - 16) { if (vanilla_demo_limit) { // no more space G_CheckDemoStatus(); return; } else { // Vanilla demo limit disabled: unlimited // demo lengths! IncreaseDemoBuffer(); } } G_ReadDemoTiccmd(cmd); // make SURE it is exactly the same } /* =================== = = G_RecordDemo = =================== */ void G_RecordDemo(skill_t skill, int numplayers, int episode, int map, char *name) { int i; int maxsize; //! // @category demo // // Record or playback a demo with high resolution turning. // longtics = D_NonVanillaRecord(M_ParmExists("-longtics"), "vvHeretic longtics demo"); // If not recording a longtics demo, record in low res lowres_turn = !longtics; //! // @category demo // // Smooth out low resolution turning when recording a demo. // shortticfix = M_ParmExists("-shortticfix"); G_InitNew(skill, episode, map); usergame = false; M_StringCopy(demoname, name, sizeof(demoname)); M_StringConcat(demoname, ".lmp", sizeof(demoname)); maxsize = 0x20000; //! // @arg // @category demo // @vanilla // // Specify the demo buffer size (KiB) // i = M_CheckParmWithArgs("-maxdemo", 1); if (i) maxsize = atoi(myargv[i + 1]) * 1024; demobuffer = Z_Malloc(maxsize, PU_STATIC, NULL); demoend = demobuffer + maxsize; demo_p = demobuffer; *demo_p++ = skill; *demo_p++ = episode; *demo_p++ = map; // Write special parameter bits onto player one byte. // This aligns with vvHeretic demo usage. Hexen demo support has no // precedent here so consistency with another game is chosen: // 0x20 = -respawn // 0x10 = -longtics // 0x02 = -nomonsters *demo_p = 1; // assume player one exists if (D_NonVanillaRecord(respawnparm, "vvHeretic -respawn header flag")) { *demo_p |= DEMOHEADER_RESPAWN; } if (longtics) { *demo_p |= DEMOHEADER_LONGTICS; } if (D_NonVanillaRecord(nomonsters, "vvHeretic -nomonsters header flag")) { *demo_p |= DEMOHEADER_NOMONSTERS; } demo_p++; *demo_p++ = PlayerClass[0]; for (i = 1; i < maxplayers; i++) { *demo_p++ = playeringame[i]; *demo_p++ = PlayerClass[i]; } demorecording = true; } /* =================== = = G_PlayDemo = =================== */ static const char *defdemoname; void G_DeferedPlayDemo(const char *name) { defdemoname = name; gameaction = ga_playdemo; } void G_DoPlayDemo(void) { skill_t skill; int i, lumpnum, episode, map; gameaction = ga_nothing; lumpnum = W_GetNumForName(defdemoname); demobuffer = W_CacheLumpNum(lumpnum, PU_STATIC); demo_p = demobuffer; skill = *demo_p++; episode = *demo_p++; map = *demo_p++; // When recording we store some extra options inside the upper bits // of the player 1 present byte. However, this is a non-vanilla extension. // Note references to vvHeretic here; these are the extensions used by // vvHeretic, which we're just reusing for Hexen demos too. There is no // vvHexen. if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_LONGTICS) != 0, lumpnum, "vvHeretic longtics demo")) { longtics = true; } if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_RESPAWN) != 0, lumpnum, "vvHeretic -respawn header flag")) { respawnparm = true; } if (D_NonVanillaPlayback((*demo_p & DEMOHEADER_NOMONSTERS) != 0, lumpnum, "vvHeretic -nomonsters header flag")) { nomonsters = true; } for (i = 0; i < maxplayers; i++) { playeringame[i] = (*demo_p++) != 0; PlayerClass[i] = *demo_p++; } // Initialize world info, etc. G_StartNewInit(); precache = false; // don't spend a lot of time in loadlevel G_InitNew(skill, episode, map); precache = true; usergame = false; demoplayback = true; } /* =================== = = G_TimeDemo = =================== */ void G_TimeDemo(char *name) { skill_t skill; int episode, map, i; demobuffer = demo_p = W_CacheLumpName(name, PU_STATIC); skill = *demo_p++; episode = *demo_p++; map = *demo_p++; // Read special parameter bits: see G_RecordDemo() for details. longtics = (*demo_p & DEMOHEADER_LONGTICS) != 0; // don't overwrite arguments from the command line respawnparm |= (*demo_p & DEMOHEADER_RESPAWN) != 0; nomonsters |= (*demo_p & DEMOHEADER_NOMONSTERS) != 0; for (i = 0; i < maxplayers; i++) { playeringame[i] = (*demo_p++) != 0; PlayerClass[i] = *demo_p++; } G_InitNew(skill, episode, map); starttime = I_GetTime(); usergame = false; demoplayback = true; timingdemo = true; singletics = true; } /* =================== = = 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 =================== */ boolean G_CheckDemoStatus(void) { int endtime, realtics; if (timingdemo) { float fps; endtime = I_GetTime(); realtics = endtime - starttime; fps = ((float) gametic * TICRATE) / realtics; I_Error("timed %i gametics in %i realtics (%f fps)", gametic, realtics, fps); } if (demoplayback) { if (singledemo) I_Quit(); W_ReleaseLumpName(defdemoname); demoplayback = false; H2_AdvanceDemo(); return true; } if (demorecording) { *demo_p++ = DEMOMARKER; M_WriteFile(demoname, demobuffer, demo_p - demobuffer); Z_Free(demobuffer); demorecording = false; I_Error("Demo %s recorded", demoname); } return false; } crispy-doom-crispy-doom-5.6.4/src/hexen/h2_main.c000066400000000000000000000652621360717211000216260ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ // haleyjd: removed WATCOMC #include #include #include #include "config.h" #include "h2def.h" #include "ct_chat.h" #include "d_iwad.h" #include "d_mode.h" #include "m_misc.h" #include "s_sound.h" #include "i_input.h" #include "i_joystick.h" #include "i_system.h" #include "i_timer.h" #include "m_argv.h" #include "m_config.h" #include "m_controls.h" #include "net_client.h" #include "p_local.h" #include "v_video.h" #include "w_main.h" // MACROS ------------------------------------------------------------------ #define MAXWADFILES 20 #define CT_KEY_BLUE 'b' #define CT_KEY_RED 'r' #define CT_KEY_YELLOW 'y' #define CT_KEY_GREEN 'g' #define CT_KEY_PLAYER5 'j' // Jade #define CT_KEY_PLAYER6 'w' // White #define CT_KEY_PLAYER7 'h' // Hazel #define CT_KEY_PLAYER8 'p' // Purple #define CT_KEY_ALL 't' // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void R_ExecuteSetViewSize(void); void D_ConnectNetGame(void); void D_CheckNetGame(void); boolean F_Responder(event_t * ev); void I_StartupKeyboard(void); void I_StartupJoystick(void); void I_ShutdownKeyboard(void); void S_InitScript(void); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void H2_ProcessEvents(void); void H2_DoAdvanceDemo(void); void H2_AdvanceDemo(void); void H2_StartTitle(void); void H2_PageTicker(void); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void DrawMessage(void); static void PageDrawer(void); static void HandleArgs(void); static void CheckRecordFrom(void); static void DrawAndBlit(void); static void CreateSavePath(void); static void WarpCheck(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern boolean automapactive; extern boolean MenuActive; extern boolean askforquit; // PUBLIC DATA DEFINITIONS ------------------------------------------------- GameMode_t gamemode; static const char *gamedescription; char *iwadfile; static char demolumpname[9]; // Demo lump to start playing. boolean nomonsters; // checkparm of -nomonsters boolean respawnparm; // checkparm of -respawn boolean randomclass; // checkparm of -randclass boolean debugmode; // checkparm of -debug boolean ravpic; // checkparm of -ravpic boolean cdrom = false; // true if cd-rom mode active boolean cmdfrag; // true if a CMD_FRAG packet should be sent out boolean artiskip; // whether shift-enter skips an artifact int maxzone = 0x800000; // Maximum allocated for zone heap (8meg default) skill_t startskill; int startepisode; int startmap; boolean autostart; boolean advancedemo; FILE *debugfile; int UpdateState; int maxplayers = MAXPLAYERS; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int WarpMap; static int demosequence; static int pagetic; static const char *pagename; static char *SavePathConfig; // CODE -------------------------------------------------------------------- void D_BindVariables(void) { int i; M_ApplyPlatformDefaults(); I_BindInputVariables(); I_BindVideoVariables(); I_BindJoystickVariables(); I_BindSoundVariables(); M_BindBaseControls(); M_BindMapControls(); M_BindMenuControls(); M_BindWeaponControls(); M_BindChatControls(MAXPLAYERS); M_BindHereticControls(); M_BindHexenControls(); key_multi_msgplayer[0] = CT_KEY_BLUE; key_multi_msgplayer[1] = CT_KEY_RED; key_multi_msgplayer[2] = CT_KEY_YELLOW; key_multi_msgplayer[3] = CT_KEY_GREEN; key_multi_msgplayer[4] = CT_KEY_PLAYER5; key_multi_msgplayer[5] = CT_KEY_PLAYER6; key_multi_msgplayer[6] = CT_KEY_PLAYER7; key_multi_msgplayer[7] = CT_KEY_PLAYER8; NET_BindVariables(); M_BindIntVariable("graphical_startup", &graphical_startup); M_BindIntVariable("mouse_sensitivity", &mouseSensitivity); M_BindIntVariable("sfx_volume", &snd_MaxVolume); M_BindIntVariable("music_volume", &snd_MusicVolume); M_BindIntVariable("messageson", &messageson); M_BindIntVariable("screenblocks", &screenblocks); M_BindIntVariable("snd_channels", &snd_Channels); M_BindIntVariable("vanilla_savegame_limit", &vanilla_savegame_limit); M_BindIntVariable("vanilla_demo_limit", &vanilla_demo_limit); M_BindStringVariable("savedir", &SavePathConfig); // Multiplayer chat macros for (i=0; i<10; ++i) { char buf[12]; M_snprintf(buf, sizeof(buf), "chatmacro%i", i); M_BindStringVariable(buf, &chat_macros[i]); } } // Set the default directory where hub savegames are saved. static void D_SetDefaultSavePath(void) { SavePath = M_GetSaveGameDir("hexen.wad"); if (!strcmp(SavePath, "")) { // only get hexen.cfg path if one is not already found if (SavePathConfig == NULL || !strcmp(SavePathConfig, "")) { // If we are not using a savegame path (probably because we are on // Windows and not using a config dir), behave like Vanilla Hexen // and use hexndata/: SavePath = malloc(10); M_snprintf(SavePath, 10, "hexndata%c", DIR_SEPARATOR); } else { SavePath = M_StringDuplicate(SavePathConfig); } } // only set hexen.cfg path if using default handling if (!M_ParmExists("-savedir") && !M_ParmExists("-cdrom")) { SavePathConfig = SavePath; } } // The Mac version of the Hexen IWAD is different to the "normal" DOS // version - it doesn't include lumps used by the DOS DMX library. // This means that we can't do GUS or OPL emulation and need to apply // a workaround. static void AdjustForMacIWAD(void) { boolean adjust_music = false; switch (snd_musicdevice) { case SNDDEVICE_ADLIB: case SNDDEVICE_SB: adjust_music = W_CheckNumForName("GENMIDI") < 0; break; case SNDDEVICE_GUS: adjust_music = W_CheckNumForName("DMXGUS") < 0; break; default: break; } if (adjust_music) { printf("** Note: You appear to be using the Mac version of the Hexen\n" "** IWAD file. This is missing the lumps required for OPL or\n" "** GUS emulation. Your music configuration is being adjusted\n" "** to a different setting that won't cause the game to " "crash.\n"); snd_musicdevice = SNDDEVICE_GENMIDI; } } // // D_GrabMouseCallback // // Called to determine whether to grab the mouse pointer // static boolean D_GrabMouseCallback(void) { // 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) && !advancedemo && !demoplayback; } // Message displayed when quitting Hexen static void D_HexenQuitMessage(void) { printf("\nHexen: Beyond Heretic\n"); } static void D_AddFile(char *filename) { printf(" adding %s\n", filename); W_AddFile(filename); } // Find out what version of Hexen is playing. void D_IdentifyVersion(void) { // The Hexen Shareware, ne 4 Level Demo Version, is missing the SKY1 lump // and uses the SKY2 lump instead. Let's use this fact and the missing // levels from MAP05 onward to identify it and set gamemode accordingly. if (W_CheckNumForName("SKY1") == -1 && W_CheckNumForName("MAP05") == -1 ) { gamemode = shareware; maxplayers = 4; } // The v1.0 IWAD file is missing a bunch of lumps that can cause the game // to crash, so we exit with an error if the user tries to play with it. // But we provide an override command line flag if they really want to // do it. //! // If provided, the check for the v1.0 IWAD file is disabled, even though // it will almost certainly cause the game to crash. // // @category compat // if (!M_ParmExists("-v10override") && gamemode != shareware && W_CheckNumForName("CLUS1MSG") < 0) { I_Error( "You are trying to use the Hexen v1.0 IWAD. This isn't\n" "supported by " PACKAGE_NAME ". Please upgrade to the v1.1\n" "IWAD file. See here for more information:\n" " https://www.doomworld.com/classicdoom/info/patches.php"); } } // Set the gamedescription string. void D_SetGameDescription(void) { /* NB: The 4 Level Demo Version actually prints a four-lined banner (and indeed waits for a keypress): Hexen: Beyond Heretic 4 Level Demo Version Press any key to continue. */ if (gamemode == shareware) { gamedescription = "Hexen: 4 Level Demo Version"; } else { gamedescription = "Hexen"; } } //========================================================================== // // H2_Main // //========================================================================== void InitMapMusicInfo(void); void D_DoomMain(void) { GameMission_t gamemission; int p; I_AtExit(D_HexenQuitMessage, false); startepisode = 1; autostart = false; startskill = sk_medium; startmap = 1; gamemode = commercial; I_PrintBanner(PACKAGE_STRING); // Initialize subsystems ST_Message("V_Init: allocate screens.\n"); V_Init(); // Load defaults before initing other systems ST_Message("M_LoadDefaults: Load system defaults.\n"); D_BindVariables(); #ifdef _WIN32 //! // @category obscure // @platform windows // @vanilla // // Save configuration data and savegames in c:\hexndata, // allowing play from CD. // cdrom = M_ParmExists("-cdrom"); #endif if (cdrom) { M_SetConfigDir("c:\\hexndata\\"); } else { M_SetConfigDir(NULL); } M_SetConfigFilenames("hexen.cfg", PROGRAM_PREFIX "hexen.cfg"); M_LoadDefaults(); D_SetDefaultSavePath(); I_AtExit(M_SaveDefaults, false); // Now that the savedir is loaded, make sure it exists CreateSavePath(); ST_Message("Z_Init: Init zone memory allocation daemon.\n"); Z_Init(); // haleyjd: removed WATCOMC ST_Message("W_Init: Init WADfiles.\n"); iwadfile = D_FindIWAD(IWAD_MASK_HEXEN, &gamemission); if (iwadfile == NULL) { I_Error("Game mode indeterminate. No IWAD was found. Try specifying\n" "one with the '-iwad' command line parameter."); } D_AddFile(iwadfile); W_CheckCorrectIWAD(hexen); D_IdentifyVersion(); D_SetGameDescription(); AdjustForMacIWAD(); //! // @category mod // // Disable auto-loading of .wad files. // if (!M_ParmExists("-noautoload")) { char *autoload_dir; autoload_dir = M_GetAutoloadDir("hexen.wad"); // TODO? DEH_AutoLoadPatches(autoload_dir); W_AutoLoadWADs(autoload_dir); free(autoload_dir); } HandleArgs(); // Generate the WAD hash table. Speed things up a bit. W_GenerateHashTable(); I_PrintStartupBanner(gamedescription); ST_Message("MN_Init: Init menu system.\n"); MN_Init(); ST_Message("CT_Init: Init chat mode data.\n"); CT_Init(); InitMapMusicInfo(); // Init music fields in mapinfo ST_Message("S_InitScript\n"); S_InitScript(); ST_Message("SN_InitSequenceScript: Registering sound sequences.\n"); SN_InitSequenceScript(); ST_Message("I_Init: Setting up machine state.\n"); I_CheckIsScreensaver(); I_InitTimer(); I_InitJoystick(); I_InitSound(false); I_InitMusic(); ST_Message("NET_Init: Init networking subsystem.\n"); NET_Init(); D_ConnectNetGame(); S_Init(); S_Start(); ST_Message("ST_Init: Init startup screen.\n"); ST_Init(); // Show version message now, so it's visible during R_Init() ST_Message("R_Init: Init Hexen refresh daemon"); R_Init(); ST_Message("\n"); //if (M_CheckParm("-net")) // ST_NetProgress(); // Console player found ST_Message("P_Init: Init Playloop state.\n"); P_Init(); // Check for command line warping. Follows P_Init() because the // MAPINFO.TXT script must be already processed. WarpCheck(); ST_Message("D_CheckNetGame: Checking network game status.\n"); D_CheckNetGame(); ST_Message("SB_Init: Loading patches.\n"); SB_Init(); ST_Done(); if (autostart) { ST_Message("Warp to Map %d (\"%s\":%d), Skill %d\n", WarpMap, P_GetMapName(startmap), startmap, startskill + 1); } CheckRecordFrom(); //! // @arg // @category demo // @vanilla // // Record a demo named x.lmp. // p = M_CheckParm("-record"); if (p && p < myargc - 1) { G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p + 1]); H2_GameLoop(); // Never returns } p = M_CheckParmWithArgs("-playdemo", 1); if (p) { singledemo = true; // Quit after one demo G_DeferedPlayDemo(demolumpname); H2_GameLoop(); // Never returns } p = M_CheckParmWithArgs("-timedemo", 1); if (p) { G_TimeDemo(demolumpname); H2_GameLoop(); // Never returns } //! // @category game // @arg // @vanilla // // Load the game in savegame slot s. // p = M_CheckParmWithArgs("-loadgame", 1); if (p) { G_LoadGame(atoi(myargv[p + 1])); } if (gameaction != ga_loadgame) { UpdateState |= I_FULLSCRN; BorderNeedRefresh = true; if (autostart || netgame) { G_StartNewInit(); G_InitNew(startskill, startepisode, startmap); } else { H2_StartTitle(); } } H2_GameLoop(); // Never returns } //========================================================================== // // HandleArgs // //========================================================================== static void HandleArgs(void) { int p; //! // @category game // @vanilla // // Disable monsters. // nomonsters = M_ParmExists("-nomonsters"); //! // @category game // @vanilla // // Monsters respawn after being killed. // respawnparm = M_ParmExists("-respawn"); //! // @vanilla // @category net // // In deathmatch mode, change a player's class each time the // player respawns. // randomclass = M_ParmExists("-randclass"); //! // @vanilla // // Take screenshots when F1 is pressed. // ravpic = M_ParmExists("-ravpic"); //! // @category obscure // @vanilla // // Don't allow artifacts to be used when the run key is held down. // artiskip = M_ParmExists("-artiskip"); debugmode = M_ParmExists("-debug"); //! // @vanilla // @category net // // Start a deathmatch game. // deathmatch = M_ParmExists("-deathmatch"); // currently broken or unused: cmdfrag = M_ParmExists("-cmdfrag"); // Check WAD file command line options W_ParseCommandLine(); //! // @category obscure // @vanilla // @arg // // Development option to specify path to level scripts. // p = M_CheckParmWithArgs("-scripts", 1); if (p) { sc_FileScripts = true; sc_ScriptsDir = myargv[p+1]; } //! // @category game // @arg // @vanilla // // Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of // 0 disables all monsters. // p = M_CheckParmWithArgs("-skill", 1); if (p) { startskill = myargv[p+1][0] - '1'; autostart = true; } //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp. // p = M_CheckParmWithArgs("-playdemo", 1); if (!p) { //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp, determining the framerate // of the screen. // p = M_CheckParmWithArgs("-timedemo", 1); } if (p) { char *uc_filename; char file[256]; M_StringCopy(file, myargv[p+1], sizeof(file)); // With Vanilla Hexen you have to specify the file without // extension, but make that optional. uc_filename = strdup(myargv[p + 1]); M_ForceUppercase(uc_filename); if (!M_StringEndsWith(uc_filename, ".LMP")) { M_StringConcat(file, ".lmp", sizeof(file)); } free(uc_filename); if (W_AddFile(file) != NULL) { M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name, sizeof(demolumpname)); } else { // The file failed to load, but copy the original arg as a // demo name to make tricks like -playdemo demo1 possible. M_StringCopy(demolumpname, myargv[p+1], sizeof(demolumpname)); } ST_Message("Playing demo %s.\n", myargv[p+1]); } //! // @category demo // // Record or playback a demo without automatically quitting // after either level exit or player respawn. // demoextend = M_ParmExists("-demoextend"); if (M_ParmExists("-testcontrols")) { autostart = true; testcontrols = true; } } //========================================================================== // // WarpCheck // //========================================================================== static void WarpCheck(void) { int p; int map; //! // @category game // @arg x // @vanilla // // Start a game immediately, warping to MAPx. // p = M_CheckParm("-warp"); if (p && p < myargc - 1) { WarpMap = atoi(myargv[p + 1]); map = P_TranslateMap(WarpMap); if (map == -1) { // Couldn't find real map number startmap = 1; ST_Message("-WARP: Invalid map number.\n"); } else { // Found a valid startmap startmap = map; autostart = true; } } else { WarpMap = 1; startmap = P_TranslateMap(1); if (startmap == -1) { startmap = 1; } } } //========================================================================== // // H2_GameLoop // //========================================================================== void H2_GameLoop(void) { if (M_CheckParm("-debugfile")) { char filename[20]; M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer); debugfile = fopen(filename, "w"); } I_SetWindowTitle(gamedescription); I_GraphicsCheckCommandLine(); I_SetGrabMouseCallback(D_GrabMouseCallback); I_InitGraphics(); while (1) { // Frame syncronous IO operations I_StartFrame(); // Process one or more tics // Will run at least one tic TryRunTics(); // Move positional sounds S_UpdateSounds(players[displayplayer].mo); DrawAndBlit(); } } //========================================================================== // // H2_ProcessEvents // // Send all the events of the given timestamp down the responder chain. // //========================================================================== void H2_ProcessEvents(void) { event_t *ev; for (;;) { ev = D_PopEvent(); if (ev == NULL) { break; } if (F_Responder(ev)) { continue; } if (MN_Responder(ev)) { continue; } G_Responder(ev); } } //========================================================================== // // DrawAndBlit // //========================================================================== static void DrawAndBlit(void) { // Change the view size if needed if (setsizeneeded) { R_ExecuteSetViewSize(); } // Do buffered drawing switch (gamestate) { case GS_LEVEL: if (!gametic) { break; } if (automapactive) { AM_Drawer(); } else { R_RenderPlayerView(&players[displayplayer]); } CT_Drawer(); UpdateState |= I_FULLVIEW; SB_Drawer(); break; case GS_INTERMISSION: IN_Drawer(); break; case GS_FINALE: F_Drawer(); break; case GS_DEMOSCREEN: PageDrawer(); break; } if (testcontrols) { V_DrawMouseSpeedBox(testcontrols_mousespeed); } if (paused && !MenuActive && !askforquit) { if (!netgame) { V_DrawPatch(160, (viewwindowy >> crispy->hires) + 5, W_CacheLumpName("PAUSED", PU_CACHE)); } else { V_DrawPatch(160, 70, W_CacheLumpName("PAUSED", PU_CACHE)); } } // Draw current message DrawMessage(); // Draw Menu MN_Drawer(); // Send out any new accumulation NetUpdate(); // Flush buffered stuff to screen I_FinishUpdate(); } //========================================================================== // // DrawMessage // //========================================================================== static void DrawMessage(void) { player_t *player; player = &players[consoleplayer]; if (player->messageTics <= 0) { // No message return; } if (player->yellowMessage) { MN_DrTextAYellow(player->message, 160 - MN_TextAWidth(player->message) / 2, 1); } else { MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message) / 2, 1); } } //========================================================================== // // H2_PageTicker // //========================================================================== void H2_PageTicker(void) { if (--pagetic < 0) { H2_AdvanceDemo(); } } //========================================================================== // // PageDrawer // //========================================================================== static void PageDrawer(void) { V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE)); if (demosequence == 1) { V_DrawPatch(4, 160, W_CacheLumpName("ADVISOR", PU_CACHE)); } UpdateState |= I_FULLSCRN; } //========================================================================== // // H2_AdvanceDemo // // Called after each demo or intro demosequence finishes. // //========================================================================== void H2_AdvanceDemo(void) { advancedemo = true; } //========================================================================== // // H2_DoAdvanceDemo // //========================================================================== void H2_DoAdvanceDemo(void) { players[consoleplayer].playerstate = PST_LIVE; // don't reborn advancedemo = false; usergame = false; // can't save/end game here paused = false; gameaction = ga_nothing; demosequence = (demosequence + 1) % 7; switch (demosequence) { case 0: pagetic = 280; gamestate = GS_DEMOSCREEN; pagename = "TITLE"; S_StartSongName("hexen", true); break; case 1: pagetic = 210; gamestate = GS_DEMOSCREEN; pagename = "TITLE"; break; case 2: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; G_DeferedPlayDemo("demo1"); break; case 3: pagetic = 200; gamestate = GS_DEMOSCREEN; pagename = "CREDIT"; break; case 4: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; G_DeferedPlayDemo("demo2"); break; case 5: pagetic = 200; gamestate = GS_DEMOSCREEN; pagename = "CREDIT"; break; case 6: BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; G_DeferedPlayDemo("demo3"); break; } } //========================================================================== // // H2_StartTitle // //========================================================================== void H2_StartTitle(void) { gameaction = ga_nothing; demosequence = -1; H2_AdvanceDemo(); } //========================================================================== // // CheckRecordFrom // // -recordfrom // //========================================================================== static void CheckRecordFrom(void) { int p; //! // @vanilla // @category demo // @arg // // Record a demo, loading from the given filename. Equivalent // to -loadgame -record . // p = M_CheckParm("-recordfrom"); if (!p || p > myargc - 2) { // Bad args return; } G_LoadGame(atoi(myargv[p + 1])); G_DoLoadGame(); // Load the gameskill etc info from savegame G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]); H2_GameLoop(); // Never returns } // haleyjd: removed WATCOMC /* void CleanExit(void) { union REGS regs; I_ShutdownKeyboard(); regs.x.eax = 0x3; int386(0x10, ®s, ®s); printf("Exited from HEXEN: Beyond Heretic.\n"); exit(1); } */ //========================================================================== // // CreateSavePath // //========================================================================== static void CreateSavePath(void) { M_MakeDirectory(SavePath); } crispy-doom-crispy-doom-5.6.4/src/hexen/h2def.h000066400000000000000000000714331360717211000213030ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __H2DEF__ #define __H2DEF__ #include #include #include //#include #include "st_start.h" // haleyjd: removed WATCOMC // ticcmd: #include "d_ticcmd.h" // events #include "d_event.h" // gamemode/mission #include "d_mode.h" // for fixed_t: #include "m_fixed.h" // angle definitions: #include "tables.h" #include "d_loop.h" #include "net_defs.h" #define HEXEN_VERSION 110 #define HEXEN_VERSION_TEXT "v1.1" // if rangecheck is undefined, most parameter validation debugging code // will not be compiled #ifndef NORANGECHECKING #define RANGECHECK #endif // Past distributions //#ifndef VER_ID //#define VER_ID "DVL" //#endif //#define HEXEN_VERSIONTEXT "ID V1.2" //#define HEXEN_VERSIONTEXT "RETAIL STORE BETA" // 9/26/95 //#define HEXEN_VERSIONTEXT "DVL BETA 10 05 95" // Used for GT for testing //#define HEXEN_VERSIONTEXT "DVL BETA 10 07 95" // Just an update for Romero //#define HEXEN_VERSIONTEXT "FINAL 1.0 (10 13 95)" // Just an update for Romero //#ifdef RANGECHECK //#define HEXEN_VERSIONTEXT "Version 1.1 +R "__DATE__" ("VER_ID")" //#else //#define HEXEN_VERSIONTEXT "Version 1.1 "__DATE__" ("VER_ID")" //#endif #define HEXEN_VERSIONTEXT ((gamemode == shareware) ? \ "DEMO 10 16 95" : \ "VERSION 1.1 MAR 22 1996 (BCP)") // all exterior data is defined here #include "xddefs.h" // all important printed strings #include "textdefs.h" // header generated by multigen utility #include "info.h" /* =============================================================================== GLOBAL TYPES =============================================================================== */ //#define NUMARTIFCTS 28 #define MAXPLAYERS 8 #define BT_ATTACK 1 #define BT_USE 2 #define BT_CHANGE 4 // if true, the next 3 bits hold weapon num #define BT_WEAPONMASK (8+16+32) #define BT_WEAPONSHIFT 3 #define BT_SPECIAL 128 // game events, not really buttons #define BTS_SAVEMASK (4+8+16) #define BTS_SAVESHIFT 2 #define BT_SPECIALMASK 3 #define BTS_PAUSE 1 // pause the game #define BTS_SAVEGAME 2 // save the game at each console // savegame slot numbers occupy the second byte of buttons // The top 3 bits of the artifact field in the ticcmd_t struct are used // as additional flags #define AFLAG_MASK 0x3F #define AFLAG_SUICIDE 0x40 #define AFLAG_JUMP 0x80 typedef enum { GS_LEVEL, GS_INTERMISSION, GS_FINALE, GS_DEMOSCREEN } gamestate_t; typedef enum { ga_nothing, ga_loadlevel, ga_initnew, ga_newgame, ga_loadgame, ga_savegame, ga_playdemo, ga_completed, ga_leavemap, ga_singlereborn, ga_victory, ga_worlddone, ga_screenshot } gameaction_t; typedef enum { wipe_0, wipe_1, wipe_2, wipe_3, wipe_4, NUMWIPES, wipe_random } wipe_t; /* =============================================================================== MAPOBJ DATA =============================================================================== */ // think_t is a function pointer to a routine to handle an actor typedef void (*think_t) (); typedef struct thinker_s { struct thinker_s *prev, *next; think_t function; } thinker_t; struct player_s; typedef union { intptr_t i; struct mobj_s *m; struct player_s *p; } specialval_t; typedef struct mobj_s { thinker_t thinker; // thinker node // info for drawing fixed_t x, y, z; struct mobj_s *snext, *sprev; // links in sector (if needed) angle_t angle; spritenum_t sprite; // used to find patch_t and flip value int frame; // might be ord with FF_FULLBRIGHT // interaction info struct mobj_s *bnext, *bprev; // links in blocks (if needed) struct subsector_s *subsector; fixed_t floorz, ceilingz; // closest together of contacted secs fixed_t floorpic; // contacted sec floorpic fixed_t radius, height; // for movement checking fixed_t momx, momy, momz; // momentums int validcount; // if == validcount, already checked mobjtype_t type; mobjinfo_t *info; // &mobjinfo[mobj->type] int tics; // state tic counter state_t *state; int damage; // For missiles int flags; int flags2; // Heretic flags specialval_t special1; // Special info specialval_t special2; // Special info int health; int movedir; // 0-7 int movecount; // when 0, select a new dir struct mobj_s *target; // thing being chased/attacked (or NULL) // also the originator for missiles int reactiontime; // if non 0, don't attack yet // used by player to freeze a bit after // teleporting int threshold; // if > 0, the target will be chased // no matter what (even if shot) struct player_s *player; // only valid if type == MT_PLAYER int lastlook; // player number last looked for fixed_t floorclip; // value to use for floor clipping int archiveNum; // Identity during archive short tid; // thing identifier byte special; // special byte args[5]; // special arguments } mobj_t; // each sector has a degenmobj_t in it's center for sound origin purposes typedef struct { thinker_t thinker; // not used for anything fixed_t x, y, z; } degenmobj_t; // // frame flags // #define FF_FULLBRIGHT 0x8000 // flag in thing->frame #define FF_FRAMEMASK 0x7fff // --- mobj.flags --- #define MF_SPECIAL 1 // call P_SpecialThing when touched #define MF_SOLID 2 #define MF_SHOOTABLE 4 #define MF_NOSECTOR 8 // don't use the sector links // (invisible but touchable) #define MF_NOBLOCKMAP 16 // don't use the blocklinks // (inert but displayable) #define MF_AMBUSH 32 #define MF_JUSTHIT 64 // try to attack right back #define MF_JUSTATTACKED 128 // take at least one step before attacking #define MF_SPAWNCEILING 256 // hang from ceiling instead of floor #define MF_NOGRAVITY 512 // don't apply gravity every tic // movement flags #define MF_DROPOFF 0x400 // allow jumps from high places #define MF_PICKUP 0x800 // for players to pick up items #define MF_NOCLIP 0x1000 // player cheat #define MF_SLIDE 0x2000 // keep info about sliding along walls #define MF_FLOAT 0x4000 // allow moves to any height, no gravity #define MF_TELEPORT 0x8000 // don't cross lines or look at heights #define MF_MISSILE 0x10000 // don't hit same species, explode on block #define MF_ALTSHADOW 0x20000 // alternate translucent draw #define MF_SHADOW 0x40000 // use translucent draw (shadow demons / invis) #define MF_NOBLOOD 0x80000 // don't bleed when shot (use puff) #define MF_CORPSE 0x100000 // don't stop moving halfway off a step #define MF_INFLOAT 0x200000 // floating to a height for a move, don't // auto float to target's height #define MF_COUNTKILL 0x400000 // count towards intermission kill total #define MF_ICECORPSE 0x800000 // a frozen corpse (for blasting) #define MF_SKULLFLY 0x1000000 // skull in flight #define MF_NOTDMATCH 0x2000000 // don't spawn in death match (key cards) //#define MF_TRANSLATION 0xc000000 // if 0x4 0x8 or 0xc, use a translation #define MF_TRANSLATION 0x1c000000 // use a translation table (>>MF_TRANSHIFT) #define MF_TRANSSHIFT 26 // table for player colormaps // --- mobj.flags2 --- #define MF2_LOGRAV 0x00000001 // alternate gravity setting #define MF2_WINDTHRUST 0x00000002 // gets pushed around by the wind // specials #define MF2_FLOORBOUNCE 0x00000004 // bounces off the floor #define MF2_BLASTED 0x00000008 // missile will pass through ghosts #define MF2_FLY 0x00000010 // fly mode is active #define MF2_FLOORCLIP 0x00000020 // if feet are allowed to be clipped #define MF2_SPAWNFLOAT 0x00000040 // spawn random float z #define MF2_NOTELEPORT 0x00000080 // does not teleport #define MF2_RIP 0x00000100 // missile rips through solid // targets #define MF2_PUSHABLE 0x00000200 // can be pushed by other moving // mobjs #define MF2_SLIDE 0x00000400 // slides against walls #define MF2_ONMOBJ 0x00000800 // mobj is resting on top of another // mobj #define MF2_PASSMOBJ 0x00001000 // Enable z block checking. If on, // this flag will allow the mobj to // pass over/under other mobjs. #define MF2_CANNOTPUSH 0x00002000 // cannot push other pushable mobjs #define MF2_DROPPED 0x00004000 // dropped by a demon #define MF2_BOSS 0x00008000 // mobj is a major boss #define MF2_FIREDAMAGE 0x00010000 // does fire damage #define MF2_NODMGTHRUST 0x00020000 // does not thrust target when // damaging #define MF2_TELESTOMP 0x00040000 // mobj can stomp another #define MF2_FLOATBOB 0x00080000 // use float bobbing z movement #define MF2_DONTDRAW 0x00100000 // don't generate a vissprite #define MF2_IMPACT 0x00200000 // an MF_MISSILE mobj can activate // SPAC_IMPACT #define MF2_PUSHWALL 0x00400000 // mobj can push walls #define MF2_MCROSS 0x00800000 // can activate monster cross lines #define MF2_PCROSS 0x01000000 // can activate projectile cross lines #define MF2_CANTLEAVEFLOORPIC 0x02000000 // stay within a certain floor type #define MF2_NONSHOOTABLE 0x04000000 // mobj is totally non-shootable, // but still considered solid #define MF2_INVULNERABLE 0x08000000 // mobj is invulnerable #define MF2_DORMANT 0x10000000 // thing is dormant #define MF2_ICEDAMAGE 0x20000000 // does ice damage #define MF2_SEEKERMISSILE 0x40000000 // is a seeker (for reflection) #define MF2_REFLECTIVE 0x80000000 // reflects missiles //============================================================================= // ===== Player Class Types ===== typedef enum { PCLASS_FIGHTER, PCLASS_CLERIC, PCLASS_MAGE, PCLASS_PIG, NUMCLASSES } pclass_t; typedef enum { PST_LIVE, // playing PST_DEAD, // dead on the ground PST_REBORN // ready to restart } playerstate_t; // psprites are scaled shapes 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, sy; } pspdef_t; /* Old Heretic key type typedef enum { key_yellow, key_green, key_blue, NUMKEYS } keytype_t; */ typedef enum { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_A, KEY_B, NUMKEYS } keytype_t; typedef enum { ARMOR_ARMOR, ARMOR_SHIELD, ARMOR_HELMET, ARMOR_AMULET, NUMARMOR } armortype_t; typedef enum { WP_FIRST, WP_SECOND, WP_THIRD, WP_FOURTH, NUMWEAPONS, WP_NOCHANGE } weapontype_t; typedef enum { MANA_1, MANA_2, NUMMANA, MANA_BOTH, MANA_NONE } manatype_t; #define MAX_MANA 200 #define WPIECE1 1 #define WPIECE2 2 #define WPIECE3 4 typedef struct { manatype_t mana; int upstate; int downstate; int readystate; int atkstate; int holdatkstate; int flashstate; } weaponinfo_t; extern weaponinfo_t WeaponInfo[NUMWEAPONS][NUMCLASSES]; typedef enum { arti_none, arti_invulnerability, arti_health, arti_superhealth, arti_healingradius, arti_summon, arti_torch, arti_egg, arti_fly, arti_blastradius, arti_poisonbag, arti_teleportother, arti_speed, arti_boostmana, arti_boostarmor, arti_teleport, // Puzzle artifacts arti_firstpuzzitem, arti_puzzskull = arti_firstpuzzitem, arti_puzzgembig, arti_puzzgemred, arti_puzzgemgreen1, arti_puzzgemgreen2, arti_puzzgemblue1, arti_puzzgemblue2, arti_puzzbook1, arti_puzzbook2, arti_puzzskull2, arti_puzzfweapon, arti_puzzcweapon, arti_puzzmweapon, arti_puzzgear1, arti_puzzgear2, arti_puzzgear3, arti_puzzgear4, NUMARTIFACTS } artitype_t; typedef enum { pw_None, pw_invulnerability, pw_allmap, pw_infrared, pw_flight, pw_shield, pw_health2, pw_speed, pw_minotaur, NUMPOWERS } powertype_t; #define INVULNTICS (30*35) #define INVISTICS (60*35) #define INFRATICS (120*35) #define IRONTICS (60*35) #define WPNLEV2TICS (40*35) #define FLIGHTTICS (60*35) #define SPEEDTICS (45*35) #define MORPHTICS (40*35) #define MAULATORTICS (25*35) #define MESSAGETICS (4*35) #define BLINKTHRESHOLD (4*35) #define NUMINVENTORYSLOTS NUMARTIFACTS typedef struct { int type; int count; } inventory_t; /* ================ = = player_t = ================ */ typedef struct player_s { mobj_t *mo; playerstate_t playerstate; ticcmd_t cmd; pclass_t class; // player class type fixed_t viewz; // focal origin above r.z fixed_t viewheight; // base height above floor for viewz fixed_t deltaviewheight; // squat speed fixed_t bob; // bounded/scaled total momentum int flyheight; int lookdir; boolean centering; int health; // only used between levels, mo->health // is used during levels int armorpoints[NUMARMOR]; inventory_t inventory[NUMINVENTORYSLOTS]; artitype_t readyArtifact; int artifactCount; int inventorySlotNum; int powers[NUMPOWERS]; int keys; int pieces; // Fourth Weapon pieces signed int frags[MAXPLAYERS]; // kills of other players weapontype_t readyweapon; weapontype_t pendingweapon; // wp_nochange if not changing boolean weaponowned[NUMWEAPONS]; int mana[NUMMANA]; int attackdown, usedown; // true if button down last tic int cheats; // bit flags int refire; // refired shots are less accurate int killcount, itemcount, secretcount; // for intermission char message[80]; // hint messages int messageTics; // counter for showing messages short ultimateMessage; short yellowMessage; int damagecount, bonuscount; // for screen flashing int poisoncount; // screen flash for poison damage mobj_t *poisoner; // NULL for non-player mobjs mobj_t *attacker; // who did damage (NULL for floors) int extralight; // so gun flashes light up areas int fixedcolormap; // can be set to REDCOLORMAP, etc int colormap; // 0-3 for which color to draw player pspdef_t psprites[NUMPSPRITES]; // view sprites (gun, etc) int morphTics; // player is a pig if > 0 unsigned int jumpTics; // delay the next jump for a moment unsigned int worldTimer; // total time the player's been playing } player_t; #define CF_NOCLIP 1 #define CF_GODMODE 2 #define CF_NOMOMENTUM 4 // not really a cheat, just a debug aid #define SBARHEIGHT (39 << crispy->hires) // status bar height at bottom of screen void NET_SendFrags(player_t * player); /* =============================================================================== GLOBAL VARIABLES =============================================================================== */ #define TELEFOGHEIGHT (32*FRACUNIT) extern GameMode_t gamemode; // Always commercial extern gameaction_t gameaction; extern boolean paused; extern boolean DevMaps; // true = map development mode extern char *DevMapsDir; // development maps directory extern boolean nomonsters; // checkparm of -nomonsters extern boolean respawnparm; // checkparm of -respawn extern boolean randomclass; // checkparm of -randclass extern boolean debugmode; // checkparm of -debug extern boolean usergame; // ok to save / end game extern boolean ravpic; // checkparm of -ravpic extern boolean altpal; // checkparm to use an alternate palette routine extern boolean cdrom; // true if cd-rom mode active ("-cdrom") extern boolean deathmatch; // only if started as net death extern boolean netgame; // only true if >1 player extern boolean cmdfrag; // true if a CMD_FRAG packet should be sent out every // kill extern boolean playeringame[MAXPLAYERS]; extern pclass_t PlayerClass[MAXPLAYERS]; extern int consoleplayer; // player taking events and displaying extern int displayplayer; extern int viewangleoffset; // ANG90 = left side, ANG270 = right extern player_t players[MAXPLAYERS]; extern boolean DebugSound; // debug flag for displaying sound info extern boolean demoplayback; extern boolean demoextend; // allow demos to persist through exit/respawn extern int maxzone; // Maximum chunk allocated for zone heap // Truncate angleturn in ticcmds to nearest 256. // Used when recording Vanilla demos in netgames. extern boolean lowres_turn; extern int Sky1Texture; extern int Sky2Texture; extern gamestate_t gamestate; extern skill_t gameskill; //extern boolean respawnmonsters; extern int gameepisode; extern int gamemap; extern int prevmap; extern int levelstarttic; // gametic at level start extern int leveltime; // tics in game play for par extern ticcmd_t *netcmds; #define MAXDEATHMATCHSTARTS 16 extern mapthing_t *deathmatch_p; extern mapthing_t deathmatchstarts[MAXDEATHMATCHSTARTS]; // Position indicator for cooperative net-play reborn extern int RebornPosition; #define MAX_PLAYER_STARTS 8 extern mapthing_t playerstarts[MAX_PLAYER_STARTS][MAXPLAYERS]; extern int maxplayers; extern int mouseSensitivity; extern boolean precache; // if true, load all graphics at level load extern byte *screen; // off screen work buffer, from V_video.c extern boolean singledemo; // quit after playing a demo from cmdline extern int bodyqueslot; extern skill_t startskill; extern int startepisode; extern int startmap; extern boolean autostart; extern boolean testcontrols; extern int testcontrols_mousespeed; extern int vanilla_savegame_limit; extern int vanilla_demo_limit; /* =============================================================================== GLOBAL FUNCTIONS =============================================================================== */ #include "w_wad.h" #include "z_zone.h" //---------- //BASE LEVEL //---------- void H2_Main(void); // not a globally visible function, just included for source reference // calls all startup code // parses command line options // if not overrided, calls N_AdvanceDemo void H2_GameLoop(void); // not a globally visible function, just included for source reference // called by H2_Main, never exits // manages timing and IO // calls all ?_Responder, ?_Ticker, and ?_Drawer functions // calls I_GetTime, I_StartFrame, and I_StartTic //--------- //SYSTEM IO //--------- byte *I_AllocLow(int length); // allocates from low memory under dos, just mallocs under unix // haleyjd: was WATCOMC, again preserved for historical interest as in Heretic #if 0 extern boolean useexterndriver; #define EBT_FIRE 1 #define EBT_OPENDOOR 2 #define EBT_SPEED 4 #define EBT_STRAFE 8 #define EBT_MAP 0x10 #define EBT_INVENTORYLEFT 0x20 #define EBT_INVENTORYRIGHT 0x40 #define EBT_USEARTIFACT 0x80 #define EBT_FLYDROP 0x100 #define EBT_CENTERVIEW 0x200 #define EBT_PAUSE 0x400 #define EBT_WEAPONCYCLE 0x800 #define EBT_JUMP 0x1000 typedef struct { short vector; // Interrupt vector signed char moveForward; // forward/backward (maxes at 50) signed char moveSideways; // strafe (maxes at 24) short angleTurn; // turning speed (640 [slow] 1280 [fast]) short angleHead; // head angle (+2080 [left] : 0 [center] : -2048 [right]) signed char pitch; // look up/down (-110 : +90) signed char flyDirection; // flyheight (+1/-1) unsigned short buttons; // EBT_* flags } externdata_t; #endif //---- //GAME //---- 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); // can be called by the startup code or M_Responder // a normal game starts at map 1, but a warp test can start elsewhere void G_DeferredNewGame(skill_t skill); void G_DeferedPlayDemo(const char *demo); void G_LoadGame(int slot); // can be called by the startup code or M_Responder // calls P_SetupLevel or W_EnterWorld void G_DoLoadGame(void); void G_SaveGame(int slot, char *description); // called by M_Responder void G_RecordDemo(skill_t skill, int numplayers, int episode, int map, char *name); // only called by startup code void G_PlayDemo(char *name); void G_TimeDemo(char *name); void G_TeleportNewMap(int map, int position); void G_Completed(int map, int position); //void G_ExitLevel (void); //void G_SecretExitLevel (void); void G_StartNewGame(skill_t skill); void G_StartNewInit(void); void G_WorldDone(void); void G_Ticker(void); boolean G_Responder(event_t * ev); void G_ScreenShot(void); //------- //SV_SAVE //------- #define HXS_VERSION_TEXT "HXS Ver 2.37" #define HXS_VERSION_TEXT_LENGTH 16 #define HXS_DESCRIPTION_LENGTH 24 extern char *SavePath; void SV_SaveGame(int slot, const char *description); void SV_SaveMap(boolean savePlayers); void SV_LoadGame(int slot); void SV_MapTeleport(int map, int position); void SV_LoadMap(void); void SV_InitBaseSlot(void); void SV_UpdateRebornSlot(void); void SV_ClearRebornSlot(void); boolean SV_RebornSlotAvailable(void); int SV_GetRebornSlot(void); //----- //PLAY //----- void P_Ticker(void); // called by C_Ticker // can call G_PlayerExited // carries out all thinking of monsters and players void P_SetupLevel(int episode, int map, int playermask, skill_t skill); // called by W_Ticker void P_Init(void); // called by startup code int P_GetMapCluster(int map); int P_TranslateMap(int map); int P_GetMapCDTrack(int map); int P_GetMapWarpTrans(int map); int P_GetMapNextMap(int map); int P_GetMapSky1Texture(int map); int P_GetMapSky2Texture(int map); char *P_GetMapName(int map); fixed_t P_GetMapSky1ScrollDelta(int map); fixed_t P_GetMapSky2ScrollDelta(int map); boolean P_GetMapDoubleSky(int map); boolean P_GetMapLightning(int map); boolean P_GetMapFadeTable(int map); char *P_GetMapSongLump(int map); void P_PutMapSongLump(int map, char *lumpName); int P_GetCDStartTrack(void); int P_GetCDEnd1Track(void); int P_GetCDEnd2Track(void); int P_GetCDEnd3Track(void); int P_GetCDIntermissionTrack(void); int P_GetCDTitleTrack(void); //------- //REFRESH //------- extern boolean setsizeneeded; extern boolean BorderNeedRefresh; extern boolean BorderTopRefresh; extern int UpdateState; // define the different areas for the dirty map #define I_NOUPDATE 0 #define I_FULLVIEW 1 #define I_STATBAR 2 #define I_MESSAGES 4 #define I_FULLSCRN 8 void R_RenderPlayerView(player_t * player); // called by G_Drawer void R_Init(void); // called by startup code void R_DrawViewBorder(void); void R_DrawTopBorder(void); // if the view size is not full screen, draws a border around it void R_SetViewSize(int blocks, int detail); // called by M_Responder int R_FlatNumForName(const char *name); int R_TextureNumForName(const char *name); int R_CheckTextureNumForName(const char *name); // called by P_Ticker for switches and animations // returns the texture number for the texture name //---- //MISC //---- extern int localQuakeHappening[MAXPLAYERS]; int M_DrawText(int x, int y, boolean direct, char *string); //------------------------------ // SC_man.c //------------------------------ void SC_Open(const char *name); void SC_OpenLump(const char *name); void SC_OpenFile(const char *name); void SC_Close(void); boolean SC_GetString(void); void SC_MustGetString(void); void SC_MustGetStringName(char *name); boolean SC_GetNumber(void); void SC_MustGetNumber(void); void SC_UnGet(void); //boolean SC_Check(void); boolean 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 boolean sc_End; extern boolean sc_Crossed; extern boolean sc_FileScripts; extern const char *sc_ScriptsDir; //------------------------------ // SN_sonix.c //------------------------------ enum { SEQ_PLATFORM, SEQ_PLATFORM_HEAVY, // same script as a normal platform SEQ_PLATFORM_METAL, SEQ_PLATFORM_CREAK, // same script as a normal platform SEQ_PLATFORM_SILENCE, SEQ_PLATFORM_LAVA, SEQ_PLATFORM_WATER, SEQ_PLATFORM_ICE, SEQ_PLATFORM_EARTH, SEQ_PLATFORM_METAL2, SEQ_DOOR_STONE, SEQ_DOOR_HEAVY, SEQ_DOOR_METAL, SEQ_DOOR_CREAK, SEQ_DOOR_SILENCE, SEQ_DOOR_LAVA, SEQ_DOOR_WATER, SEQ_DOOR_ICE, SEQ_DOOR_EARTH, SEQ_DOOR_METAL2, SEQ_ESOUND_WIND, SEQ_NUMSEQ }; typedef enum { SEQTYPE_STONE, SEQTYPE_HEAVY, SEQTYPE_METAL, SEQTYPE_CREAK, SEQTYPE_SILENCE, SEQTYPE_LAVA, SEQTYPE_WATER, SEQTYPE_ICE, SEQTYPE_EARTH, SEQTYPE_METAL2, SEQTYPE_NUMSEQ } seqtype_t; void SN_InitSequenceScript(void); void SN_StartSequence(mobj_t * mobj, int sequence); void SN_StartSequenceName(mobj_t * mobj, char *name); void SN_StopSequence(mobj_t * mobj); void SN_UpdateActiveSequences(void); void SN_StopAllSequences(void); int SN_GetSequenceOffset(int sequence, int *sequencePtr); void SN_ChangeNodeData(int nodeNum, int seqOffset, int delayTics, int volume, int currentSoundID); typedef struct seqnode_s seqnode_t; struct seqnode_s { int *sequencePtr; int sequence; mobj_t *mobj; int currentSoundID; int delayTics; int volume; int stopSound; seqnode_t *prev; seqnode_t *next; }; extern int ActiveSequences; extern seqnode_t *SequenceListHead; //---------------------- // Interlude (IN_lude.c) //---------------------- #define MAX_INTRMSN_MESSAGE_SIZE 1024 extern boolean intermission; extern char ClusterMessage[MAX_INTRMSN_MESSAGE_SIZE]; void IN_Start(void); void IN_Ticker(void); void IN_Drawer(void); //---------------------- // Chat mode (CT_chat.c) //---------------------- void CT_Init(void); void CT_Drawer(void); boolean CT_Responder(event_t * ev); void CT_Ticker(void); char CT_dequeueChatChar(void); extern boolean chatmodeon; //-------------------- // Finale (F_finale.c) //-------------------- void F_Drawer(void); void F_Ticker(void); void F_StartFinale(void); //---------------------- // STATUS BAR (SB_bar.c) //---------------------- extern int inv_ptr; extern int curpos; void SB_Init(void); void SB_SetClassData(void); boolean SB_Responder(event_t * event); void SB_Ticker(void); void SB_Drawer(void); void Draw_TeleportIcon(void); void Draw_SaveIcon(void); void Draw_LoadIcon(void); //----------------- // MENU (MN_menu.c) //----------------- void MN_Init(void); void MN_ActivateMenu(void); void MN_DeactivateMenu(void); boolean MN_Responder(event_t * event); void MN_Ticker(void); void MN_Drawer(void); void MN_DrTextA(const char *text, int x, int y); void MN_DrTextAYellow(const char *text, int x, int y); int MN_TextAWidth(const char *text); void MN_DrTextB(const char *text, int x, int y); int MN_TextBWidth(const char *text); extern int messageson; #include "sounds.h" #endif // __H2DEF__ crispy-doom-crispy-doom-5.6.4/src/hexen/in_lude.c000066400000000000000000000363441360717211000217270ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "h2def.h" #include "s_sound.h" #include "i_system.h" #include "i_video.h" #include "m_misc.h" #include "p_local.h" #include "v_video.h" #include "i_swap.h" // MACROS ------------------------------------------------------------------ #define TEXTSPEED 3 #define TEXTWAIT 140 // TYPES ------------------------------------------------------------------- typedef enum { SINGLE, COOPERATIVE, DEATHMATCH } gametype_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void WaitStop(void); static void Stop(void); static void LoadPics(void); static void UnloadPics(void); static void CheckForSkip(void); static void InitStats(void); static void DrDeathTally(void); static void DrNumber(int val, int x, int y, int wrapThresh); static void DrNumberBold(int val, int x, int y, int wrapThresh); static void DrawHubText(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DECLARATIONS ------------------------------------------------ boolean intermission; char ClusterMessage[MAX_INTRMSN_MESSAGE_SIZE]; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static boolean skipintermission; static int interstate = 0; static int intertime = -1; static gametype_t gametype; static int cnt; static int slaughterboy; // in DM, the player with the most kills static patch_t *patchINTERPIC; static patch_t *FontBNumbers[10]; static patch_t *FontBNegative; static patch_t *FontBSlash; static patch_t *FontBPercent; static int FontABaseLump; static int FontBLump; static int FontBLumpBase; static signed int totalFrags[MAXPLAYERS]; static int HubCount; static char *HubText; // CODE -------------------------------------------------------------------- //======================================================================== // // IN_Start // //======================================================================== extern void AM_Stop(void); void IN_Start(void) { int i; I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); InitStats(); LoadPics(); intermission = true; interstate = 0; skipintermission = false; intertime = 0; AM_Stop(); for (i = 0; i < maxplayers; i++) { players[i].messageTics = 0; players[i].message[0] = 0; } SN_StopAllSequences(); } //======================================================================== // // WaitStop // //======================================================================== void WaitStop(void) { if (!--cnt) { Stop(); // gamestate = GS_LEVEL; // G_DoLoadLevel(); gameaction = ga_leavemap; // G_WorldDone(); } } //======================================================================== // // Stop // //======================================================================== static void Stop(void) { intermission = false; UnloadPics(); SB_state = -1; BorderNeedRefresh = true; } //======================================================================== // // InitStats // // Initializes the stats for single player mode //======================================================================== static const char *ClusMsgLumpNames[] = { "clus1msg", "clus2msg", "clus3msg", "clus4msg", "clus5msg" }; static void InitStats(void) { int i; int j; int oldCluster; signed int slaughterfrags; int posnum; int slaughtercount; int playercount; const char *msgLumpName; int msgSize; int msgLump; extern int LeaveMap; if (!deathmatch) { gametype = SINGLE; HubCount = 0; oldCluster = P_GetMapCluster(gamemap); if (oldCluster != P_GetMapCluster(LeaveMap)) { if (oldCluster >= 1 && oldCluster <= 5) { msgLumpName = ClusMsgLumpNames[oldCluster - 1]; msgLump = W_GetNumForName(msgLumpName); msgSize = W_LumpLength(msgLump); if (msgSize >= MAX_INTRMSN_MESSAGE_SIZE) { I_Error("Cluster message too long (%s)", msgLumpName); } W_ReadLump(msgLump, ClusterMessage); ClusterMessage[msgSize] = 0; // Append terminator HubText = ClusterMessage; HubCount = strlen(HubText) * TEXTSPEED + TEXTWAIT; S_StartSongName("hub", true); } } } else { gametype = DEATHMATCH; slaughterboy = 0; slaughterfrags = -9999; posnum = 0; playercount = 0; slaughtercount = 0; for (i = 0; i < maxplayers; i++) { totalFrags[i] = 0; if (playeringame[i]) { playercount++; for (j = 0; j < maxplayers; j++) { if (playeringame[j]) { totalFrags[i] += players[i].frags[j]; } } posnum++; } if (totalFrags[i] > slaughterfrags) { slaughterboy = 1 << i; slaughterfrags = totalFrags[i]; slaughtercount = 1; } else if (totalFrags[i] == slaughterfrags) { slaughterboy |= 1 << i; slaughtercount++; } } if (playercount == slaughtercount) { // don't do the slaughter stuff if everyone is equal slaughterboy = 0; } S_StartSongName("hub", true); } } //======================================================================== // // LoadPics // //======================================================================== static void LoadPics(void) { int i; if (HubCount || gametype == DEATHMATCH) { patchINTERPIC = W_CacheLumpName("INTERPIC", PU_STATIC); FontBLumpBase = W_GetNumForName("FONTB16"); for (i = 0; i < 10; i++) { FontBNumbers[i] = W_CacheLumpNum(FontBLumpBase + i, PU_STATIC); } FontBLump = W_GetNumForName("FONTB_S") + 1; FontBNegative = W_CacheLumpName("FONTB13", PU_STATIC); FontABaseLump = W_GetNumForName("FONTA_S") + 1; FontBSlash = W_CacheLumpName("FONTB15", PU_STATIC); FontBPercent = W_CacheLumpName("FONTB05", PU_STATIC); } } //======================================================================== // // UnloadPics // //======================================================================== static void UnloadPics(void) { int i; if (HubCount || gametype == DEATHMATCH) { W_ReleaseLumpName("INTERPIC"); patchINTERPIC = W_CacheLumpName("INTERPIC", PU_STATIC); FontBLumpBase = W_GetNumForName("FONTB16"); for (i = 0; i < 10; i++) { W_ReleaseLumpNum(FontBLumpBase + i); } W_ReleaseLumpName("FONTB13"); W_ReleaseLumpName("FONTB15"); W_ReleaseLumpName("FONTB05"); } } //======================================================================== // // IN_Ticker // //======================================================================== void IN_Ticker(void) { if (!intermission) { return; } if (interstate) { WaitStop(); return; } skipintermission = false; CheckForSkip(); intertime++; if (skipintermission || (gametype == SINGLE && !HubCount)) { interstate = 1; cnt = 10; skipintermission = false; //S_StartSound(NULL, sfx_dorcls); } } //======================================================================== // // CheckForSkip // // Check to see if any player hit a key //======================================================================== static void CheckForSkip(void) { int i; player_t *player; static boolean triedToSkip; for (i = 0, player = players; i < maxplayers; i++, player++) { if (playeringame[i]) { if (player->cmd.buttons & BT_ATTACK) { if (!player->attackdown) { skipintermission = 1; } player->attackdown = true; } else { player->attackdown = false; } if (player->cmd.buttons & BT_USE) { if (!player->usedown) { skipintermission = 1; } player->usedown = true; } else { player->usedown = false; } } } if (deathmatch && intertime < 140) { // wait for 4 seconds before allowing a skip if (skipintermission == 1) { triedToSkip = true; skipintermission = 0; } } else { if (triedToSkip) { skipintermission = 1; triedToSkip = false; } } } //======================================================================== // // IN_Drawer // //======================================================================== void IN_Drawer(void) { if (!intermission) { return; } if (interstate) { return; } UpdateState |= I_FULLSCRN; V_CopyScaledBuffer(I_VideoBuffer, (byte *) patchINTERPIC, ORIGWIDTH * ORIGHEIGHT); if (gametype == SINGLE) { if (HubCount) { DrawHubText(); } } else { DrDeathTally(); } } //======================================================================== // // DrDeathTally // //======================================================================== #define TALLY_EFFECT_TICKS 20 #define TALLY_FINAL_X_DELTA (23*FRACUNIT) #define TALLY_FINAL_Y_DELTA (13*FRACUNIT) #define TALLY_START_XPOS (178*FRACUNIT) #define TALLY_STOP_XPOS (90*FRACUNIT) #define TALLY_START_YPOS (132*FRACUNIT) #define TALLY_STOP_YPOS (83*FRACUNIT) #define TALLY_TOP_X 85 #define TALLY_TOP_Y 9 #define TALLY_LEFT_X 7 #define TALLY_LEFT_Y 71 #define TALLY_TOTALS_X 291 static void DrDeathTally(void) { int i, j; fixed_t xPos, yPos; fixed_t xDelta, yDelta; fixed_t xStart, scale; int x, y; boolean bold; static boolean showTotals; int temp; V_DrawPatch(TALLY_TOP_X, TALLY_TOP_Y, W_CacheLumpName("tallytop", PU_CACHE)); V_DrawPatch(TALLY_LEFT_X, TALLY_LEFT_Y, W_CacheLumpName("tallylft", PU_CACHE)); if (intertime < TALLY_EFFECT_TICKS) { showTotals = false; scale = (intertime * FRACUNIT) / TALLY_EFFECT_TICKS; xDelta = FixedMul(scale, TALLY_FINAL_X_DELTA); yDelta = FixedMul(scale, TALLY_FINAL_Y_DELTA); xStart = TALLY_START_XPOS - FixedMul(scale, TALLY_START_XPOS - TALLY_STOP_XPOS); yPos = TALLY_START_YPOS - FixedMul(scale, TALLY_START_YPOS - TALLY_STOP_YPOS); } else { xDelta = TALLY_FINAL_X_DELTA; yDelta = TALLY_FINAL_Y_DELTA; xStart = TALLY_STOP_XPOS; yPos = TALLY_STOP_YPOS; } if (intertime >= TALLY_EFFECT_TICKS && showTotals == false) { showTotals = true; S_StartSound(NULL, SFX_PLATFORM_STOP); } y = yPos >> FRACBITS; for (i = 0; i < maxplayers; i++) { xPos = xStart; for (j = 0; j < maxplayers; j++, xPos += xDelta) { x = xPos >> FRACBITS; bold = (i == consoleplayer || j == consoleplayer); if (playeringame[i] && playeringame[j]) { if (bold) { DrNumberBold(players[i].frags[j], x, y, 100); } else { DrNumber(players[i].frags[j], x, y, 100); } } else { temp = MN_TextAWidth("--") / 2; if (bold) { MN_DrTextAYellow("--", x - temp, y); } else { MN_DrTextA("--", x - temp, y); } } } if (showTotals && playeringame[i] && !((slaughterboy & (1 << i)) && !(intertime & 16))) { DrNumber(totalFrags[i], TALLY_TOTALS_X, y, 1000); } yPos += yDelta; y = yPos >> FRACBITS; } } //========================================================================== // // DrNumber // //========================================================================== static void DrNumber(int val, int x, int y, int wrapThresh) { char buff[8] = "XX"; if (!(val < -9 && wrapThresh < 1000)) { M_snprintf(buff, sizeof(buff), "%d", val >= wrapThresh ? val % wrapThresh : val); } MN_DrTextA(buff, x - MN_TextAWidth(buff) / 2, y); } //========================================================================== // // DrNumberBold // //========================================================================== static void DrNumberBold(int val, int x, int y, int wrapThresh) { char buff[8] = "XX"; if (!(val < -9 && wrapThresh < 1000)) { M_snprintf(buff, sizeof(buff), "%d", val >= wrapThresh ? val % wrapThresh : val); } MN_DrTextAYellow(buff, x - MN_TextAWidth(buff) / 2, y); } //=========================================================================== // // DrawHubText // //=========================================================================== static void DrawHubText(void) { int count; char *ch; int c; int cx, cy; patch_t *w; cy = 5; cx = 10; ch = HubText; count = (intertime - 10) / TEXTSPEED; if (count < 0) { count = 0; } for (; count; count--) { c = *ch++; if (!c) { break; } if (c == '\n') { cx = 10; cy += 9; continue; } if (c < 32) { continue; } c = toupper(c); if (c == 32) { cx += 5; continue; } w = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); if (cx + SHORT(w->width) > SCREENWIDTH) { break; } V_DrawPatch(cx, cy, w); cx += SHORT(w->width); } } crispy-doom-crispy-doom-5.6.4/src/hexen/info.c000066400000000000000000024557771360717211000212640ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_swap.h" // generated by stateco const char *sprnames[] = { "MAN1","ACLO","TLGL","FBL1","XPL1","ARRW","DART","RIPP","CFCF","BLAD", "SHRD","FFSM","FFLG","PTN1","PTN2","SOAR","INVU","SUMN","TSPK","TELO", "TRNG","ROCK","FOGS","FOGM","FOGL","SGSA","SGSB","PORK","EGGM","FHFX", "SPHL","STWN","GMPD","ASKU","ABGM","AGMR","AGMG","AGG2","AGMB","AGB2", "ABK1","ABK2","ASK2","AFWP","ACWP","AMWP","AGER","AGR2","AGR3","AGR4", "TRCH","PSBG","ATLP","THRW","SPED","BMAN","BRAC","BLST","HRAD","SPSH", "LVAS","SLDG","STTW","RCK1","RCK2","RCK3","RCK4","CDLR","TRE1","TRDT", "TRE2","TRE3","STM1","STM2","STM3","STM4","MSH1","MSH2","MSH3","MSH4", "MSH5","MSH6","MSH7","MSH8","SGMP","SGM1","SGM2","SGM3","SLC1","SLC2", "SLC3","MSS1","MSS2","SWMV","CPS1","CPS2","TMS1","TMS2","TMS3","TMS4", "TMS5","TMS6","TMS7","CPS3","STT2","STT3","STT4","STT5","GAR1","GAR2", "GAR3","GAR4","GAR5","GAR6","GAR7","GAR8","GAR9","BNR1","TRE4","TRE5", "TRE6","TRE7","LOGG","ICT1","ICT2","ICT3","ICT4","ICM1","ICM2","ICM3", "ICM4","RKBL","RKBS","RKBK","RBL1","RBL2","RBL3","VASE","POT1","POT2", "POT3","PBIT","CPS4","CPS5","CPS6","CPB1","CPB2","CPB3","CPB4","BDRP", "BDSH","BDPL","CNDL","LEF1","LEF3","LEF2","TWTR","WLTR","BARL","SHB1", "SHB2","BCKT","SHRM","FBUL","FSKL","BRTR","SUIT","BBLL","CAND","IRON", "XMAS","CDRN","CHNS","TST1","TST2","TST3","TST4","TST5","TST6","TST7", "TST8","TST9","TST0","TELE","TSMK","FPCH","WFAX","FAXE","WFHM","FHMR", "FSRD","FSFX","CMCE","WCSS","CSSF","WCFM","CFLM","CFFX","CHLY","SPIR", "MWND","WMLG","MLNG","MLFX","MLF2","MSTF","MSP1","MSP2","WFR1","WFR2", "WFR3","WCH1","WCH2","WCH3","WMS1","WMS2","WMS3","WPIG","WMCS","CONE", "SHEX","BLOD","GIBS","PLAY","FDTH","BSKL","ICEC","CLER","MAGE","PIGY", "CENT","CTXD","CTFX","CTDP","DEMN","DEMA","DEMB","DEMC","DEMD","DEME", "DMFX","DEM2","DMBA","DMBB","DMBC","DMBD","DMBE","D2FX","WRTH","WRT2", "WRBL","MNTR","FX12","FX13","MNSM","SSPT","SSDV","SSXD","SSFX","BISH", "BPFX","DRAG","DRFX","ARM1","ARM2","ARM3","ARM4","MAN2","MAN3","KEY1", "KEY2","KEY3","KEY4","KEY5","KEY6","KEY7","KEY8","KEY9","KEYA","KEYB", "ETTN","ETTB","FDMN","FDMB","ICEY","ICPR","ICWS","SORC","SBMP","SBS4", "SBMB","SBS3","SBMG","SBS1","SBS2","SBFX","RADE","WATR","KORX","ABAT", NULL }; void A_FreeTargMobj(); void A_FlameCheck(); void A_HideThing(); void A_UnHideThing(); void A_RestoreSpecialThing1(); void A_RestoreSpecialThing2(); void A_RestoreArtifact(); void A_Summon(); void A_ThrustInitUp(); void A_ThrustInitDn(); void A_ThrustRaise(); void A_ThrustBlock(); void A_ThrustImpale(); void A_ThrustLower(); void A_TeloSpawnC(); void A_TeloSpawnB(); void A_TeloSpawnA(); void A_TeloSpawnD(); void A_CheckTeleRing(); void A_FogSpawn(); void A_FogMove(); void A_Quake(); void A_ContMobjSound(); void A_Scream(); void A_Explode(); void A_PoisonBagInit(); void A_PoisonBagDamage(); void A_PoisonBagCheck(); void A_CheckThrowBomb(); void A_NoGravity(); void A_PotteryExplode(); void A_PotteryChooseBit(); void A_PotteryCheck(); void A_CorpseBloodDrip(); void A_CorpseExplode(); void A_LeafSpawn(); void A_LeafThrust(); void A_LeafCheck(); void A_BridgeInit(); void A_BridgeOrbit(); void A_TreeDeath(); void A_PoisonShroom(); void A_Pain(); void A_SoAExplode(); void A_BellReset1(); void A_BellReset2(); void A_NoBlocking(); void A_Light0(); void A_WeaponReady(); void A_Lower(); void A_Raise(); void A_FPunchAttack(); void A_ReFire(); void A_FAxeAttack(); void A_FHammerAttack(); void A_FHammerThrow(); void A_FSwordAttack(); void A_FSwordFlames(); void A_CMaceAttack(); void A_CStaffInitBlink(); void A_CStaffCheckBlink(); void A_CStaffCheck(); void A_CStaffAttack(); void A_CStaffMissileSlither(); void A_CFlameAttack(); void A_CFlameRotate(); void A_CFlamePuff(); void A_CFlameMissile(); void A_CHolyAttack(); void A_CHolyPalette(); void A_CHolySeek(); void A_CHolyCheckScream(); void A_CHolyTail(); void A_CHolySpawnPuff(); void A_CHolyAttack2(); void A_MWandAttack(); void A_LightningReady(); void A_MLightningAttack(); void A_LightningZap(); void A_LightningClip(); void A_LightningRemove(); void A_LastZap(); void A_ZapMimic(); void A_MStaffAttack(); void A_MStaffPalette(); void A_MStaffWeave(); void A_MStaffTrack(); void A_SnoutAttack(); void A_FireConePL1(); void A_ShedShard(); void A_AddPlayerCorpse(); void A_SkullPop(); void A_FreezeDeath(); void A_FreezeDeathChunks(); void A_CheckBurnGone(); void A_CheckSkullFloor(); void A_CheckSkullDone(); void A_SpeedFade(); void A_IceSetTics(); void A_IceCheckHeadDone(); void A_PigPain(); void A_PigLook(); void A_PigChase(); void A_FaceTarget(); void A_PigAttack(); void A_QueueCorpse(); void A_Look(); void A_Chase(); void A_CentaurAttack(); void A_CentaurAttack2(); void A_SetReflective(); void A_CentaurDefend(); void A_UnSetReflective(); void A_CentaurDropStuff(); void A_CheckFloor(); void A_DemonAttack1(); void A_DemonAttack2(); void A_DemonDeath(); void A_Demon2Death(); void A_WraithRaiseInit(); void A_WraithRaise(); void A_WraithInit(); void A_WraithLook(); void A_WraithChase(); void A_WraithFX3(); void A_WraithMelee(); void A_WraithMissile(); void A_WraithFX2(); void A_MinotaurFade1(); void A_MinotaurFade2(); void A_MinotaurLook(); void A_MinotaurChase(); void A_MinotaurRoam(); void A_MinotaurAtk1(); void A_MinotaurDecide(); void A_MinotaurAtk2(); void A_MinotaurAtk3(); void A_MinotaurCharge(); void A_SmokePuffExit(); void A_MinotaurFade0(); void A_MntrFloorFire(); void A_SerpentChase(); void A_SerpentHumpDecide(); void A_SerpentUnHide(); void A_SerpentRaiseHump(); void A_SerpentLowerHump(); void A_SerpentHide(); void A_SerpentBirthScream(); void A_SetShootable(); void A_SerpentCheckForAttack(); void A_UnSetShootable(); void A_SerpentDiveSound(); void A_SerpentWalk(); void A_SerpentChooseAttack(); void A_SerpentMeleeAttack(); void A_SerpentMissileAttack(); void A_SerpentHeadPop(); void A_SerpentSpawnGibs(); void A_SerpentHeadCheck(); void A_FloatGib(); void A_DelayGib(); void A_SinkGib(); void A_BishopDecide(); void A_BishopDoBlur(); void A_BishopSpawnBlur(); void A_BishopChase(); void A_BishopAttack(); void A_BishopAttack2(); void A_BishopPainBlur(); void A_BishopPuff(); void A_SetAltShadow(); void A_BishopMissileWeave(); void A_BishopMissileSeek(); void A_DragonInitFlight(); void A_DragonFlap(); void A_DragonFlight(); void A_DragonAttack(); void A_DragonPain(); void A_DragonCheckCrash(); void A_DragonFX2(); void A_ESound(); void A_EttinAttack(); void A_DropMace(); void A_FiredRocks(); void A_UnSetInvulnerable(); void A_FiredChase(); void A_FiredAttack(); void A_FiredSplotch(); void A_SmBounce(); void A_IceGuyLook(); void A_IceGuyChase(); void A_IceGuyAttack(); void A_IceGuyDie(); void A_IceGuyMissilePuff(); void A_IceGuyMissileExplode(); void A_ClassBossHealth(); void A_FastChase(); void A_FighterAttack(); void A_ClericAttack(); void A_MageAttack(); void A_SorcSpinBalls(); void A_SpeedBalls(); void A_SpawnFizzle(); void A_SorcBossAttack(); void A_SorcBallOrbit(); void A_SorcBallPop(); void A_BounceCheck(); void A_SorcFX1Seek(); void A_SorcFX2Split(); void A_SorcFX2Orbit(); void A_SorcererBishopEntry(); void A_SpawnBishop(); void A_SorcFX4Check(); void A_KoraxStep2(); void A_KoraxChase(); void A_KoraxStep(); void A_KoraxDecide(); void A_KoraxMissile(); void A_KoraxCommand(); void A_KoraxBonePop(); void A_KSpiritRoam(); void A_KBoltRaise(); void A_KBolt(); void A_BatSpawnInit(); void A_BatSpawn(); void A_BatMove(); state_t states[NUMSTATES] = { {SPR_MAN1, 0, -1, NULL, S_NULL, 0, 0}, // S_NULL {SPR_ACLO, 4, 1050, A_FreeTargMobj, S_NULL, 0, 0}, // S_FREETARGMOBJ {SPR_TLGL, 0, -1, NULL, S_NULL, 0, 0}, // S_MAPSPOT {SPR_FBL1, 32768, 4, NULL, S_FIREBALL1_2, 0, 0}, // S_FIREBALL1_1 {SPR_FBL1, 32769, 4, NULL, S_FIREBALL1_1, 0, 0}, // S_FIREBALL1_2 {SPR_XPL1, 32768, 4, NULL, S_FIREBALL1_X2, 0, 0}, // S_FIREBALL1_X1 {SPR_XPL1, 32769, 4, NULL, S_FIREBALL1_X3, 0, 0}, // S_FIREBALL1_X2 {SPR_XPL1, 32770, 4, NULL, S_FIREBALL1_X4, 0, 0}, // S_FIREBALL1_X3 {SPR_XPL1, 32771, 4, NULL, S_FIREBALL1_X5, 0, 0}, // S_FIREBALL1_X4 {SPR_XPL1, 32772, 4, NULL, S_FIREBALL1_X6, 0, 0}, // S_FIREBALL1_X5 {SPR_XPL1, 32773, 4, NULL, S_NULL, 0, 0}, // S_FIREBALL1_X6 {SPR_ARRW, 0, -1, NULL, S_NULL, 0, 0}, // S_ARROW_1 {SPR_ARRW, 0, 1, NULL, S_NULL, 0, 0}, // S_ARROW_X1 {SPR_DART, 0, -1, NULL, S_NULL, 0, 0}, // S_DART_1 {SPR_DART, 0, 1, NULL, S_NULL, 0, 0}, // S_DART_X1 {SPR_DART, 0, -1, NULL, S_NULL, 0, 0}, // S_POISONDART_1 {SPR_DART, 0, 1, NULL, S_NULL, 0, 0}, // S_POISONDART_X1 {SPR_RIPP, 0, 3, NULL, S_RIPPERBALL_2, 0, 0}, // S_RIPPERBALL_1 {SPR_RIPP, 1, 3, NULL, S_RIPPERBALL_3, 0, 0}, // S_RIPPERBALL_2 {SPR_RIPP, 2, 3, NULL, S_RIPPERBALL_1, 0, 0}, // S_RIPPERBALL_3 {SPR_CFCF, 32784, 4, NULL, S_RIPPERBALL_X2, 0, 0}, // S_RIPPERBALL_X1 {SPR_CFCF, 32785, 3, NULL, S_RIPPERBALL_X3, 0, 0}, // S_RIPPERBALL_X2 {SPR_CFCF, 32786, 4, NULL, S_RIPPERBALL_X4, 0, 0}, // S_RIPPERBALL_X3 {SPR_CFCF, 32787, 3, NULL, S_RIPPERBALL_X5, 0, 0}, // S_RIPPERBALL_X4 {SPR_CFCF, 32788, 4, NULL, S_RIPPERBALL_X6, 0, 0}, // S_RIPPERBALL_X5 {SPR_CFCF, 32789, 3, NULL, S_RIPPERBALL_X7, 0, 0}, // S_RIPPERBALL_X6 {SPR_CFCF, 32790, 4, NULL, S_RIPPERBALL_X8, 0, 0}, // S_RIPPERBALL_X7 {SPR_CFCF, 32791, 3, NULL, S_RIPPERBALL_X9, 0, 0}, // S_RIPPERBALL_X8 {SPR_CFCF, 32792, 4, NULL, S_RIPPERBALL_X10, 0, 0}, // S_RIPPERBALL_X9 {SPR_CFCF, 32793, 3, NULL, S_NULL, 0, 0}, // S_RIPPERBALL_X10 {SPR_BLAD, 0, -1, NULL, S_NULL, 0, 0}, // S_PRJ_BLADE1 {SPR_BLAD, 0, 1, NULL, S_NULL, 0, 0}, // S_PRJ_BLADE_X1 {SPR_SHRD, 32768, 3, NULL, S_ICESHARD2, 0, 0}, // S_ICESHARD1 {SPR_SHRD, 32769, 3, NULL, S_ICESHARD3, 0, 0}, // S_ICESHARD2 {SPR_SHRD, 32770, 3, NULL, S_ICESHARD1, 0, 0}, // S_ICESHARD3 {SPR_FFSM, 32768, 3, NULL, S_FLAME_TSMALL2, 0, 0}, // S_FLAME_TSMALL1 {SPR_FFSM, 32769, 3, NULL, S_FLAME_TSMALL3, 0, 0}, // S_FLAME_TSMALL2 {SPR_FFSM, 32770, 2, A_FlameCheck, S_FLAME_TSMALL4, 0, 0}, // S_FLAME_TSMALL3 {SPR_FFSM, 32770, 2, NULL, S_FLAME_TSMALL5, 0, 0}, // S_FLAME_TSMALL4 {SPR_FFSM, 32771, 3, NULL, S_FLAME_TSMALL6, 0, 0}, // S_FLAME_TSMALL5 {SPR_FFSM, 32772, 3, A_FlameCheck, S_FLAME_TSMALL1, 0, 0}, // S_FLAME_TSMALL6 {SPR_FFLG, 32768, 4, NULL, S_FLAME_TLARGE2, 0, 0}, // S_FLAME_TLARGE1 {SPR_FFLG, 32769, 4, A_FlameCheck, S_FLAME_TLARGE3, 0, 0}, // S_FLAME_TLARGE2 {SPR_FFLG, 32770, 4, NULL, S_FLAME_TLARGE4, 0, 0}, // S_FLAME_TLARGE3 {SPR_FFLG, 32771, 4, A_FlameCheck, S_FLAME_TLARGE5, 0, 0}, // S_FLAME_TLARGE4 {SPR_FFLG, 32772, 4, NULL, S_FLAME_TLARGE6, 0, 0}, // S_FLAME_TLARGE5 {SPR_FFLG, 32773, 4, A_FlameCheck, S_FLAME_TLARGE7, 0, 0}, // S_FLAME_TLARGE6 {SPR_FFLG, 32774, 4, NULL, S_FLAME_TLARGE8, 0, 0}, // S_FLAME_TLARGE7 {SPR_FFLG, 32775, 4, A_FlameCheck, S_FLAME_TLARGE9, 0, 0}, // S_FLAME_TLARGE8 {SPR_FFLG, 32776, 4, NULL, S_FLAME_TLARGE10, 0, 0}, // S_FLAME_TLARGE9 {SPR_FFLG, 32777, 4, A_FlameCheck, S_FLAME_TLARGE11, 0, 0}, // S_FLAME_TLARGE10 {SPR_FFLG, 32778, 4, NULL, S_FLAME_TLARGE12, 0, 0}, // S_FLAME_TLARGE11 {SPR_FFLG, 32779, 4, A_FlameCheck, S_FLAME_TLARGE13, 0, 0}, // S_FLAME_TLARGE12 {SPR_FFLG, 32780, 4, NULL, S_FLAME_TLARGE14, 0, 0}, // S_FLAME_TLARGE13 {SPR_FFLG, 32781, 4, A_FlameCheck, S_FLAME_TLARGE15, 0, 0}, // S_FLAME_TLARGE14 {SPR_FFLG, 32782, 4, NULL, S_FLAME_TLARGE16, 0, 0}, // S_FLAME_TLARGE15 {SPR_FFLG, 32783, 4, A_FlameCheck, S_FLAME_TLARGE5, 0, 0}, // S_FLAME_TLARGE16 {SPR_FFSM, 0, 2, NULL, S_FLAME_SDORM2, 0, 0}, // S_FLAME_SDORM1 {SPR_FFSM, 1, 2, A_HideThing, S_FLAME_SDORM3, 0, 0}, // S_FLAME_SDORM2 {SPR_FFSM, 2, 200, NULL, S_FLAME_SDORM3, 0, 0}, // S_FLAME_SDORM3 {SPR_FFSM, 32768, 3, NULL, S_FLAME_SMALL2, 0, 0}, // S_FLAME_SMALL1 {SPR_FFSM, 32768, 3, A_UnHideThing, S_FLAME_SMALL3, 0, 0}, // S_FLAME_SMALL2 {SPR_FFSM, 32768, 3, NULL, S_FLAME_SMALL4, 0, 0}, // S_FLAME_SMALL3 {SPR_FFSM, 32769, 3, NULL, S_FLAME_SMALL5, 0, 0}, // S_FLAME_SMALL4 {SPR_FFSM, 32770, 3, NULL, S_FLAME_SMALL6, 0, 0}, // S_FLAME_SMALL5 {SPR_FFSM, 32771, 3, NULL, S_FLAME_SMALL7, 0, 0}, // S_FLAME_SMALL6 {SPR_FFSM, 32772, 3, NULL, S_FLAME_SMALL3, 0, 0}, // S_FLAME_SMALL7 {SPR_FFLG, 3, 2, NULL, S_FLAME_LDORM2, 0, 0}, // S_FLAME_LDORM1 {SPR_FFLG, 2, 2, NULL, S_FLAME_LDORM3, 0, 0}, // S_FLAME_LDORM2 {SPR_FFLG, 1, 2, NULL, S_FLAME_LDORM4, 0, 0}, // S_FLAME_LDORM3 {SPR_FFLG, 0, 2, A_HideThing, S_FLAME_LDORM5, 0, 0}, // S_FLAME_LDORM4 {SPR_FFLG, 0, 200, NULL, S_FLAME_LDORM5, 0, 0}, // S_FLAME_LDORM5 {SPR_FFLG, 32768, 2, NULL, S_FLAME_LARGE2, 0, 0}, // S_FLAME_LARGE1 {SPR_FFLG, 32768, 2, A_UnHideThing, S_FLAME_LARGE3, 0, 0}, // S_FLAME_LARGE2 {SPR_FFLG, 32768, 4, NULL, S_FLAME_LARGE4, 0, 0}, // S_FLAME_LARGE3 {SPR_FFLG, 32769, 4, NULL, S_FLAME_LARGE5, 0, 0}, // S_FLAME_LARGE4 {SPR_FFLG, 32770, 4, NULL, S_FLAME_LARGE6, 0, 0}, // S_FLAME_LARGE5 {SPR_FFLG, 32771, 4, NULL, S_FLAME_LARGE7, 0, 0}, // S_FLAME_LARGE6 {SPR_FFLG, 32772, 4, NULL, S_FLAME_LARGE8, 0, 0}, // S_FLAME_LARGE7 {SPR_FFLG, 32773, 4, NULL, S_FLAME_LARGE9, 0, 0}, // S_FLAME_LARGE8 {SPR_FFLG, 32774, 4, NULL, S_FLAME_LARGE10, 0, 0}, // S_FLAME_LARGE9 {SPR_FFLG, 32775, 4, NULL, S_FLAME_LARGE11, 0, 0}, // S_FLAME_LARGE10 {SPR_FFLG, 32776, 4, NULL, S_FLAME_LARGE12, 0, 0}, // S_FLAME_LARGE11 {SPR_FFLG, 32777, 4, NULL, S_FLAME_LARGE13, 0, 0}, // S_FLAME_LARGE12 {SPR_FFLG, 32778, 4, NULL, S_FLAME_LARGE14, 0, 0}, // S_FLAME_LARGE13 {SPR_FFLG, 32779, 4, NULL, S_FLAME_LARGE15, 0, 0}, // S_FLAME_LARGE14 {SPR_FFLG, 32780, 4, NULL, S_FLAME_LARGE16, 0, 0}, // S_FLAME_LARGE15 {SPR_FFLG, 32781, 4, NULL, S_FLAME_LARGE17, 0, 0}, // S_FLAME_LARGE16 {SPR_FFLG, 32782, 4, NULL, S_FLAME_LARGE18, 0, 0}, // S_FLAME_LARGE17 {SPR_FFLG, 32783, 4, NULL, S_FLAME_LARGE7, 0, 0}, // S_FLAME_LARGE18 {SPR_PTN1, 0, 3, NULL, S_ITEM_PTN1_2, 0, 0}, // S_ITEM_PTN1_1 {SPR_PTN1, 1, 3, NULL, S_ITEM_PTN1_3, 0, 0}, // S_ITEM_PTN1_2 {SPR_PTN1, 2, 3, NULL, S_ITEM_PTN1_1, 0, 0}, // S_ITEM_PTN1_3 {SPR_ACLO, 4, 1400, NULL, S_HIDESPECIAL2, 0, 0}, // S_HIDESPECIAL1 {SPR_ACLO, 0, 4, A_RestoreSpecialThing1, S_HIDESPECIAL3, 0, 0}, // S_HIDESPECIAL2 {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL4, 0, 0}, // S_HIDESPECIAL3 {SPR_ACLO, 0, 4, NULL, S_HIDESPECIAL5, 0, 0}, // S_HIDESPECIAL4 {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL6, 0, 0}, // S_HIDESPECIAL5 {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL7, 0, 0}, // S_HIDESPECIAL6 {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL8, 0, 0}, // S_HIDESPECIAL7 {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL9, 0, 0}, // S_HIDESPECIAL8 {SPR_ACLO, 3, 4, NULL, S_HIDESPECIAL10, 0, 0}, // S_HIDESPECIAL9 {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL11, 0, 0}, // S_HIDESPECIAL10 {SPR_ACLO, 3, 4, A_RestoreSpecialThing2, S_NULL, 0, 0}, // S_HIDESPECIAL11 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI1_2, 0, 0}, // S_DORMANTARTI1_1 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI1_3, 0, 0}, // S_DORMANTARTI1_2 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI1_4, 0, 0}, // S_DORMANTARTI1_3 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI1_5, 0, 0}, // S_DORMANTARTI1_4 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI1_6, 0, 0}, // S_DORMANTARTI1_5 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI1_7, 0, 0}, // S_DORMANTARTI1_6 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI1_8, 0, 0}, // S_DORMANTARTI1_7 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI1_9, 0, 0}, // S_DORMANTARTI1_8 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI1_10, 0, 0}, // S_DORMANTARTI1_9 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI1_11, 0, 0}, // S_DORMANTARTI1_10 {SPR_ACLO, 0, 1400, A_HideThing, S_DORMANTARTI1_12, 0, 0}, // S_DORMANTARTI1_11 {SPR_ACLO, 0, 3, A_UnHideThing, S_DORMANTARTI1_13, 0, 0}, // S_DORMANTARTI1_12 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI1_14, 0, 0}, // S_DORMANTARTI1_13 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI1_15, 0, 0}, // S_DORMANTARTI1_14 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI1_16, 0, 0}, // S_DORMANTARTI1_15 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI1_17, 0, 0}, // S_DORMANTARTI1_16 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI1_18, 0, 0}, // S_DORMANTARTI1_17 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI1_19, 0, 0}, // S_DORMANTARTI1_18 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI1_20, 0, 0}, // S_DORMANTARTI1_19 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI1_21, 0, 0}, // S_DORMANTARTI1_20 {SPR_ACLO, 3, 3, A_RestoreArtifact, S_NULL, 0, 0}, // S_DORMANTARTI1_21 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI2_2, 0, 0}, // S_DORMANTARTI2_1 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI2_3, 0, 0}, // S_DORMANTARTI2_2 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI2_4, 0, 0}, // S_DORMANTARTI2_3 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI2_5, 0, 0}, // S_DORMANTARTI2_4 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI2_6, 0, 0}, // S_DORMANTARTI2_5 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI2_7, 0, 0}, // S_DORMANTARTI2_6 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI2_8, 0, 0}, // S_DORMANTARTI2_7 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI2_9, 0, 0}, // S_DORMANTARTI2_8 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI2_10, 0, 0}, // S_DORMANTARTI2_9 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI2_11, 0, 0}, // S_DORMANTARTI2_10 {SPR_ACLO, 0, 4200, A_HideThing, S_DORMANTARTI2_12, 0, 0}, // S_DORMANTARTI2_11 {SPR_ACLO, 0, 3, A_UnHideThing, S_DORMANTARTI2_13, 0, 0}, // S_DORMANTARTI2_12 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI2_14, 0, 0}, // S_DORMANTARTI2_13 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI2_15, 0, 0}, // S_DORMANTARTI2_14 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI2_16, 0, 0}, // S_DORMANTARTI2_15 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI2_17, 0, 0}, // S_DORMANTARTI2_16 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI2_18, 0, 0}, // S_DORMANTARTI2_17 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI2_19, 0, 0}, // S_DORMANTARTI2_18 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI2_20, 0, 0}, // S_DORMANTARTI2_19 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI2_21, 0, 0}, // S_DORMANTARTI2_20 {SPR_ACLO, 3, 3, A_RestoreArtifact, S_NULL, 0, 0}, // S_DORMANTARTI2_21 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI3_2, 0, 0}, // S_DORMANTARTI3_1 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3_3, 0, 0}, // S_DORMANTARTI3_2 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI3_4, 0, 0}, // S_DORMANTARTI3_3 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3_5, 0, 0}, // S_DORMANTARTI3_4 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI3_6, 0, 0}, // S_DORMANTARTI3_5 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3_7, 0, 0}, // S_DORMANTARTI3_6 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI3_8, 0, 0}, // S_DORMANTARTI3_7 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI3_9, 0, 0}, // S_DORMANTARTI3_8 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI3_10, 0, 0}, // S_DORMANTARTI3_9 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI3_11, 0, 0}, // S_DORMANTARTI3_10 {SPR_ACLO, 0, 21000, A_HideThing, S_DORMANTARTI3_12, 0, 0}, // S_DORMANTARTI3_11 {SPR_ACLO, 0, 3, A_UnHideThing, S_DORMANTARTI3_13, 0, 0}, // S_DORMANTARTI3_12 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI3_14, 0, 0}, // S_DORMANTARTI3_13 {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI3_15, 0, 0}, // S_DORMANTARTI3_14 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI3_16, 0, 0}, // S_DORMANTARTI3_15 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3_17, 0, 0}, // S_DORMANTARTI3_16 {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI3_18, 0, 0}, // S_DORMANTARTI3_17 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3_19, 0, 0}, // S_DORMANTARTI3_18 {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI3_20, 0, 0}, // S_DORMANTARTI3_19 {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3_21, 0, 0}, // S_DORMANTARTI3_20 {SPR_ACLO, 3, 3, A_RestoreArtifact, S_NULL, 0, 0}, // S_DORMANTARTI3_21 {SPR_ACLO, 3, 3, NULL, S_DEADARTI2, 0, 0}, // S_DEADARTI1 {SPR_ACLO, 2, 3, NULL, S_DEADARTI3, 0, 0}, // S_DEADARTI2 {SPR_ACLO, 3, 3, NULL, S_DEADARTI4, 0, 0}, // S_DEADARTI3 {SPR_ACLO, 2, 3, NULL, S_DEADARTI5, 0, 0}, // S_DEADARTI4 {SPR_ACLO, 1, 3, NULL, S_DEADARTI6, 0, 0}, // S_DEADARTI5 {SPR_ACLO, 2, 3, NULL, S_DEADARTI7, 0, 0}, // S_DEADARTI6 {SPR_ACLO, 1, 3, NULL, S_DEADARTI8, 0, 0}, // S_DEADARTI7 {SPR_ACLO, 0, 3, NULL, S_DEADARTI9, 0, 0}, // S_DEADARTI8 {SPR_ACLO, 1, 3, NULL, S_DEADARTI10, 0, 0}, // S_DEADARTI9 {SPR_ACLO, 0, 3, NULL, S_NULL, 0, 0}, // S_DEADARTI10 {SPR_PTN2, 0, 4, NULL, S_ARTI_PTN2_2, 0, 0}, // S_ARTI_PTN2_1 {SPR_PTN2, 1, 4, NULL, S_ARTI_PTN2_3, 0, 0}, // S_ARTI_PTN2_2 {SPR_PTN2, 2, 4, NULL, S_ARTI_PTN2_1, 0, 0}, // S_ARTI_PTN2_3 {SPR_SOAR, 0, 5, NULL, S_ARTI_SOAR2, 0, 0}, // S_ARTI_SOAR1 {SPR_SOAR, 1, 5, NULL, S_ARTI_SOAR3, 0, 0}, // S_ARTI_SOAR2 {SPR_SOAR, 2, 5, NULL, S_ARTI_SOAR4, 0, 0}, // S_ARTI_SOAR3 {SPR_SOAR, 1, 5, NULL, S_ARTI_SOAR1, 0, 0}, // S_ARTI_SOAR4 {SPR_INVU, 0, 3, NULL, S_ARTI_INVU2, 0, 0}, // S_ARTI_INVU1 {SPR_INVU, 1, 3, NULL, S_ARTI_INVU3, 0, 0}, // S_ARTI_INVU2 {SPR_INVU, 2, 3, NULL, S_ARTI_INVU4, 0, 0}, // S_ARTI_INVU3 {SPR_INVU, 3, 3, NULL, S_ARTI_INVU1, 0, 0}, // S_ARTI_INVU4 {SPR_SUMN, 0, 350, NULL, S_ARTI_SUMMON, 0, 0}, // S_ARTI_SUMMON {SPR_SUMN, 0, 4, NULL, S_SUMMON_FX1_1, 0, 0}, // S_SUMMON_FX1_1 {SPR_SUMN, 0, 4, NULL, S_SUMMON_FX2_2, 0, 0}, // S_SUMMON_FX2_1 {SPR_SUMN, 0, 4, NULL, S_SUMMON_FX2_3, 0, 0}, // S_SUMMON_FX2_2 {SPR_SUMN, 0, 4, A_Summon, S_NULL, 0, 0}, // S_SUMMON_FX2_3 {SPR_TSPK, 0, 3, NULL, S_THRUSTINIT2_2, 0, 0}, // S_THRUSTINIT2_1 {SPR_TSPK, 0, 4, A_ThrustInitUp, S_THRUSTBLOCK, 0, 0}, // S_THRUSTINIT2_2 {SPR_TSPK, 1, 3, NULL, S_BTHRUSTINIT2_2, 0, 0}, // S_BTHRUSTINIT2_1 {SPR_TSPK, 1, 4, A_ThrustInitUp, S_BTHRUSTBLOCK, 0, 0}, // S_BTHRUSTINIT2_2 {SPR_TSPK, 0, 3, NULL, S_THRUSTINIT1_2, 0, 0}, // S_THRUSTINIT1_1 {SPR_TSPK, 0, 4, A_ThrustInitDn, S_THRUSTSTAY, 0, 0}, // S_THRUSTINIT1_2 {SPR_TSPK, 1, 3, NULL, S_BTHRUSTINIT1_2, 0, 0}, // S_BTHRUSTINIT1_1 {SPR_TSPK, 1, 4, A_ThrustInitDn, S_BTHRUSTSTAY, 0, 0}, // S_BTHRUSTINIT1_2 {SPR_TSPK, 0, 8, A_ThrustRaise, S_THRUSTRAISE2, 0, 0}, // S_THRUSTRAISE1 {SPR_TSPK, 0, 6, A_ThrustRaise, S_THRUSTRAISE3, 0, 0}, // S_THRUSTRAISE2 {SPR_TSPK, 0, 4, A_ThrustRaise, S_THRUSTRAISE4, 0, 0}, // S_THRUSTRAISE3 {SPR_TSPK, 0, 3, A_ThrustBlock, S_THRUSTIMPALE, 0, 0}, // S_THRUSTRAISE4 {SPR_TSPK, 1, 8, A_ThrustRaise, S_BTHRUSTRAISE2, 0, 0}, // S_BTHRUSTRAISE1 {SPR_TSPK, 1, 6, A_ThrustRaise, S_BTHRUSTRAISE3, 0, 0}, // S_BTHRUSTRAISE2 {SPR_TSPK, 1, 4, A_ThrustRaise, S_BTHRUSTRAISE4, 0, 0}, // S_BTHRUSTRAISE3 {SPR_TSPK, 1, 3, A_ThrustBlock, S_BTHRUSTIMPALE, 0, 0}, // S_BTHRUSTRAISE4 {SPR_TSPK, 0, 2, A_ThrustImpale, S_THRUSTRAISE, 0, 0}, // S_THRUSTIMPALE {SPR_TSPK, 1, 2, A_ThrustImpale, S_BTHRUSTRAISE, 0, 0}, // S_BTHRUSTIMPALE {SPR_TSPK, 0, 2, A_ThrustRaise, S_THRUSTRAISE, 0, 0}, // S_THRUSTRAISE {SPR_TSPK, 1, 2, A_ThrustRaise, S_BTHRUSTRAISE, 0, 0}, // S_BTHRUSTRAISE {SPR_TSPK, 0, 10, NULL, S_THRUSTBLOCK, 0, 0}, // S_THRUSTBLOCK {SPR_TSPK, 1, 10, NULL, S_BTHRUSTBLOCK, 0, 0}, // S_BTHRUSTBLOCK {SPR_TSPK, 0, 2, A_ThrustLower, S_THRUSTLOWER, 0, 0}, // S_THRUSTLOWER {SPR_TSPK, 1, 2, A_ThrustLower, S_BTHRUSTLOWER, 0, 0}, // S_BTHRUSTLOWER {SPR_TSPK, 0, -1, NULL, S_THRUSTSTAY, 0, 0}, // S_THRUSTSTAY {SPR_TSPK, 1, -1, NULL, S_BTHRUSTSTAY, 0, 0}, // S_BTHRUSTSTAY {SPR_TELO, 0, 5, NULL, S_ARTI_TELOTHER2, 0, 0}, // S_ARTI_TELOTHER1 {SPR_TELO, 1, 5, NULL, S_ARTI_TELOTHER3, 0, 0}, // S_ARTI_TELOTHER2 {SPR_TELO, 2, 5, NULL, S_ARTI_TELOTHER4, 0, 0}, // S_ARTI_TELOTHER3 {SPR_TELO, 3, 5, NULL, S_ARTI_TELOTHER1, 0, 0}, // S_ARTI_TELOTHER4 {SPR_TRNG, 32772, 5, NULL, S_TELO_FX2, 0, 0}, // S_TELO_FX1 {SPR_TRNG, 32771, 4, NULL, S_TELO_FX3, 0, 0}, // S_TELO_FX2 {SPR_TRNG, 32770, 3, A_TeloSpawnC, S_TELO_FX4, 0, 0}, // S_TELO_FX3 {SPR_TRNG, 32769, 3, A_TeloSpawnB, S_TELO_FX5, 0, 0}, // S_TELO_FX4 {SPR_TRNG, 32768, 3, A_TeloSpawnA, S_TELO_FX6, 0, 0}, // S_TELO_FX5 {SPR_TRNG, 32769, 3, A_TeloSpawnB, S_TELO_FX7, 0, 0}, // S_TELO_FX6 {SPR_TRNG, 32770, 3, A_TeloSpawnC, S_TELO_FX8, 0, 0}, // S_TELO_FX7 {SPR_TRNG, 32771, 3, A_TeloSpawnD, S_TELO_FX3, 0, 0}, // S_TELO_FX8 {SPR_TRNG, 32772, 3, NULL, S_NULL, 0, 0}, // S_TELO_FX9 {SPR_TRNG, 32769, 4, NULL, S_TELO_FX2_2, 0, 0}, // S_TELO_FX2_1 {SPR_TRNG, 32770, 4, NULL, S_TELO_FX2_3, 0, 0}, // S_TELO_FX2_2 {SPR_TRNG, 32771, 4, NULL, S_TELO_FX2_4, 0, 0}, // S_TELO_FX2_3 {SPR_TRNG, 32770, 4, NULL, S_TELO_FX2_5, 0, 0}, // S_TELO_FX2_4 {SPR_TRNG, 32769, 4, NULL, S_TELO_FX2_6, 0, 0}, // S_TELO_FX2_5 {SPR_TRNG, 32768, 4, A_CheckTeleRing, S_TELO_FX2_1, 0, 0}, // S_TELO_FX2_6 {SPR_TRNG, 32770, 4, NULL, S_TELO_FX3_2, 0, 0}, // S_TELO_FX3_1 {SPR_TRNG, 32771, 4, NULL, S_TELO_FX3_3, 0, 0}, // S_TELO_FX3_2 {SPR_TRNG, 32770, 4, NULL, S_TELO_FX3_4, 0, 0}, // S_TELO_FX3_3 {SPR_TRNG, 32769, 4, NULL, S_TELO_FX3_5, 0, 0}, // S_TELO_FX3_4 {SPR_TRNG, 32768, 4, NULL, S_TELO_FX3_6, 0, 0}, // S_TELO_FX3_5 {SPR_TRNG, 32769, 4, A_CheckTeleRing, S_TELO_FX3_1, 0, 0}, // S_TELO_FX3_6 {SPR_TRNG, 32771, 4, NULL, S_TELO_FX4_2, 0, 0}, // S_TELO_FX4_1 {SPR_TRNG, 32770, 4, NULL, S_TELO_FX4_3, 0, 0}, // S_TELO_FX4_2 {SPR_TRNG, 32769, 4, NULL, S_TELO_FX4_4, 0, 0}, // S_TELO_FX4_3 {SPR_TRNG, 32768, 4, NULL, S_TELO_FX4_5, 0, 0}, // S_TELO_FX4_4 {SPR_TRNG, 32769, 4, NULL, S_TELO_FX4_6, 0, 0}, // S_TELO_FX4_5 {SPR_TRNG, 32770, 4, A_CheckTeleRing, S_TELO_FX4_1, 0, 0}, // S_TELO_FX4_6 {SPR_TRNG, 32770, 4, NULL, S_TELO_FX5_2, 0, 0}, // S_TELO_FX5_1 {SPR_TRNG, 32769, 4, NULL, S_TELO_FX5_3, 0, 0}, // S_TELO_FX5_2 {SPR_TRNG, 32768, 4, NULL, S_TELO_FX5_4, 0, 0}, // S_TELO_FX5_3 {SPR_TRNG, 32769, 4, NULL, S_TELO_FX5_5, 0, 0}, // S_TELO_FX5_4 {SPR_TRNG, 32770, 4, NULL, S_TELO_FX5_6, 0, 0}, // S_TELO_FX5_5 {SPR_TRNG, 32771, 4, A_CheckTeleRing, S_TELO_FX5_1, 0, 0}, // S_TELO_FX5_6 {SPR_ROCK, 3, 20, NULL, S_DIRT1_1, 0, 0}, // S_DIRT1_1 {SPR_ROCK, 3, 10, NULL, S_NULL, 0, 0}, // S_DIRT1_D {SPR_ROCK, 4, 20, NULL, S_DIRT2_1, 0, 0}, // S_DIRT2_1 {SPR_ROCK, 4, 10, NULL, S_NULL, 0, 0}, // S_DIRT2_D {SPR_ROCK, 5, 20, NULL, S_DIRT3_1, 0, 0}, // S_DIRT3_1 {SPR_ROCK, 5, 10, NULL, S_NULL, 0, 0}, // S_DIRT3_D {SPR_ROCK, 6, 20, NULL, S_DIRT4_1, 0, 0}, // S_DIRT4_1 {SPR_ROCK, 6, 10, NULL, S_NULL, 0, 0}, // S_DIRT4_D {SPR_ROCK, 7, 20, NULL, S_DIRT5_1, 0, 0}, // S_DIRT5_1 {SPR_ROCK, 7, 10, NULL, S_NULL, 0, 0}, // S_DIRT5_D {SPR_ROCK, 8, 20, NULL, S_DIRT6_1, 0, 0}, // S_DIRT6_1 {SPR_ROCK, 8, 10, NULL, S_NULL, 0, 0}, // S_DIRT6_D {SPR_TSPK, 2, 20, NULL, S_DIRTCLUMP1, 0, 0}, // S_DIRTCLUMP1 {SPR_ROCK, 0, 20, NULL, S_ROCK1_1, 0, 0}, // S_ROCK1_1 {SPR_ROCK, 0, 10, NULL, S_NULL, 0, 0}, // S_ROCK1_D {SPR_ROCK, 1, 20, NULL, S_ROCK2_1, 0, 0}, // S_ROCK2_1 {SPR_ROCK, 1, 10, NULL, S_NULL, 0, 0}, // S_ROCK2_D {SPR_ROCK, 2, 20, NULL, S_ROCK3_1, 0, 0}, // S_ROCK3_1 {SPR_ROCK, 2, 10, NULL, S_NULL, 0, 0}, // S_ROCK3_D {SPR_MAN1, 0, 20, A_FogSpawn, S_SPAWNFOG1, 0, 0}, // S_SPAWNFOG1 {SPR_FOGS, 0, 7, A_FogMove, S_FOGPATCHS2, 0, 0}, // S_FOGPATCHS1 {SPR_FOGS, 1, 7, A_FogMove, S_FOGPATCHS3, 0, 0}, // S_FOGPATCHS2 {SPR_FOGS, 2, 7, A_FogMove, S_FOGPATCHS4, 0, 0}, // S_FOGPATCHS3 {SPR_FOGS, 3, 7, A_FogMove, S_FOGPATCHS5, 0, 0}, // S_FOGPATCHS4 {SPR_FOGS, 4, 7, A_FogMove, S_FOGPATCHS1, 0, 0}, // S_FOGPATCHS5 {SPR_FOGS, 4, 5, NULL, S_NULL, 0, 0}, // S_FOGPATCHS0 {SPR_FOGM, 0, 7, A_FogMove, S_FOGPATCHM2, 0, 0}, // S_FOGPATCHM1 {SPR_FOGM, 1, 7, A_FogMove, S_FOGPATCHM3, 0, 0}, // S_FOGPATCHM2 {SPR_FOGM, 2, 7, A_FogMove, S_FOGPATCHM4, 0, 0}, // S_FOGPATCHM3 {SPR_FOGM, 3, 7, A_FogMove, S_FOGPATCHM5, 0, 0}, // S_FOGPATCHM4 {SPR_FOGM, 4, 7, A_FogMove, S_FOGPATCHM1, 0, 0}, // S_FOGPATCHM5 {SPR_FOGS, 0, 5, NULL, S_FOGPATCHMA, 0, 0}, // S_FOGPATCHM0 {SPR_FOGS, 1, 5, NULL, S_FOGPATCHMB, 0, 0}, // S_FOGPATCHMA {SPR_FOGS, 2, 5, NULL, S_FOGPATCHMC, 0, 0}, // S_FOGPATCHMB {SPR_FOGS, 3, 5, NULL, S_FOGPATCHMD, 0, 0}, // S_FOGPATCHMC {SPR_FOGS, 4, 5, NULL, S_FOGPATCHS0, 0, 0}, // S_FOGPATCHMD {SPR_FOGL, 0, 7, A_FogMove, S_FOGPATCHL2, 0, 0}, // S_FOGPATCHL1 {SPR_FOGL, 1, 7, A_FogMove, S_FOGPATCHL3, 0, 0}, // S_FOGPATCHL2 {SPR_FOGL, 2, 7, A_FogMove, S_FOGPATCHL4, 0, 0}, // S_FOGPATCHL3 {SPR_FOGL, 3, 7, A_FogMove, S_FOGPATCHL5, 0, 0}, // S_FOGPATCHL4 {SPR_FOGL, 4, 7, A_FogMove, S_FOGPATCHL1, 0, 0}, // S_FOGPATCHL5 {SPR_FOGM, 0, 4, NULL, S_FOGPATCHLA, 0, 0}, // S_FOGPATCHL0 {SPR_FOGM, 1, 4, NULL, S_FOGPATCHLB, 0, 0}, // S_FOGPATCHLA {SPR_FOGM, 2, 4, NULL, S_FOGPATCHLC, 0, 0}, // S_FOGPATCHLB {SPR_FOGM, 3, 4, NULL, S_FOGPATCHLD, 0, 0}, // S_FOGPATCHLC {SPR_FOGM, 4, 4, NULL, S_FOGPATCHM0, 0, 0}, // S_FOGPATCHLD {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE2, 0, 0}, // S_QUAKE_ACTIVE1 {SPR_MAN1, 0, 1, A_ContMobjSound, S_QUAKE_ACTIVE3, 0, 0}, // S_QUAKE_ACTIVE2 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE4, 0, 0}, // S_QUAKE_ACTIVE3 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE5, 0, 0}, // S_QUAKE_ACTIVE4 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE6, 0, 0}, // S_QUAKE_ACTIVE5 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE7, 0, 0}, // S_QUAKE_ACTIVE6 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE8, 0, 0}, // S_QUAKE_ACTIVE7 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE9, 0, 0}, // S_QUAKE_ACTIVE8 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE0, 0, 0}, // S_QUAKE_ACTIVE9 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEA, 0, 0}, // S_QUAKE_ACTIVE0 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEB, 0, 0}, // S_QUAKE_ACTIVEA {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEC, 0, 0}, // S_QUAKE_ACTIVEB {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVED, 0, 0}, // S_QUAKE_ACTIVEC {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEE, 0, 0}, // S_QUAKE_ACTIVED {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEF, 0, 0}, // S_QUAKE_ACTIVEE {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEG, 0, 0}, // S_QUAKE_ACTIVEF {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEH, 0, 0}, // S_QUAKE_ACTIVEG {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEI, 0, 0}, // S_QUAKE_ACTIVEH {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEJ, 0, 0}, // S_QUAKE_ACTIVEI {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEK, 0, 0}, // S_QUAKE_ACTIVEJ {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEL, 0, 0}, // S_QUAKE_ACTIVEK {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEM, 0, 0}, // S_QUAKE_ACTIVEL {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEN, 0, 0}, // S_QUAKE_ACTIVEM {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEO, 0, 0}, // S_QUAKE_ACTIVEN {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEP, 0, 0}, // S_QUAKE_ACTIVEO {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEQ, 0, 0}, // S_QUAKE_ACTIVEP {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVER, 0, 0}, // S_QUAKE_ACTIVEQ {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVES, 0, 0}, // S_QUAKE_ACTIVER {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVET, 0, 0}, // S_QUAKE_ACTIVES {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEU, 0, 0}, // S_QUAKE_ACTIVET {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEV, 0, 0}, // S_QUAKE_ACTIVEU {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEW, 0, 0}, // S_QUAKE_ACTIVEV {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEX, 0, 0}, // S_QUAKE_ACTIVEW {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEY, 0, 0}, // S_QUAKE_ACTIVEX {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVEZ, 0, 0}, // S_QUAKE_ACTIVEY {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT1, 0, 0}, // S_QUAKE_ACTIVEZ {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT2, 0, 0}, // S_QUAKE_ACT1 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT3, 0, 0}, // S_QUAKE_ACT2 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT4, 0, 0}, // S_QUAKE_ACT3 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT5, 0, 0}, // S_QUAKE_ACT4 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT6, 0, 0}, // S_QUAKE_ACT5 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT7, 0, 0}, // S_QUAKE_ACT6 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT8, 0, 0}, // S_QUAKE_ACT7 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT9, 0, 0}, // S_QUAKE_ACT8 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACT0, 0, 0}, // S_QUAKE_ACT9 {SPR_MAN1, 0, 2, A_Quake, S_QUAKE_ACTIVE1, 0, 0}, // S_QUAKE_ACT0 {SPR_SGSA, 0, 4, NULL, S_SGSHARD1_2, 0, 0}, // S_SGSHARD1_1 {SPR_SGSA, 1, 4, NULL, S_SGSHARD1_3, 0, 0}, // S_SGSHARD1_2 {SPR_SGSA, 2, 4, NULL, S_SGSHARD1_4, 0, 0}, // S_SGSHARD1_3 {SPR_SGSA, 3, 4, NULL, S_SGSHARD1_5, 0, 0}, // S_SGSHARD1_4 {SPR_SGSA, 4, 4, NULL, S_SGSHARD1_1, 0, 0}, // S_SGSHARD1_5 {SPR_SGSA, 4, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD1_D {SPR_SGSA, 5, 4, NULL, S_SGSHARD2_2, 0, 0}, // S_SGSHARD2_1 {SPR_SGSA, 6, 4, NULL, S_SGSHARD2_3, 0, 0}, // S_SGSHARD2_2 {SPR_SGSA, 7, 4, NULL, S_SGSHARD2_4, 0, 0}, // S_SGSHARD2_3 {SPR_SGSA, 8, 4, NULL, S_SGSHARD2_5, 0, 0}, // S_SGSHARD2_4 {SPR_SGSA, 9, 4, NULL, S_SGSHARD2_1, 0, 0}, // S_SGSHARD2_5 {SPR_SGSA, 9, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD2_D {SPR_SGSA, 10, 4, NULL, S_SGSHARD3_2, 0, 0}, // S_SGSHARD3_1 {SPR_SGSA, 11, 4, NULL, S_SGSHARD3_3, 0, 0}, // S_SGSHARD3_2 {SPR_SGSA, 12, 4, NULL, S_SGSHARD3_4, 0, 0}, // S_SGSHARD3_3 {SPR_SGSA, 13, 4, NULL, S_SGSHARD3_5, 0, 0}, // S_SGSHARD3_4 {SPR_SGSA, 14, 4, NULL, S_SGSHARD3_1, 0, 0}, // S_SGSHARD3_5 {SPR_SGSA, 14, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD3_D {SPR_SGSA, 15, 4, NULL, S_SGSHARD4_2, 0, 0}, // S_SGSHARD4_1 {SPR_SGSA, 16, 4, NULL, S_SGSHARD4_3, 0, 0}, // S_SGSHARD4_2 {SPR_SGSA, 17, 4, NULL, S_SGSHARD4_4, 0, 0}, // S_SGSHARD4_3 {SPR_SGSA, 18, 4, NULL, S_SGSHARD4_5, 0, 0}, // S_SGSHARD4_4 {SPR_SGSA, 19, 4, NULL, S_SGSHARD4_1, 0, 0}, // S_SGSHARD4_5 {SPR_SGSA, 19, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD4_D {SPR_SGSA, 20, 4, NULL, S_SGSHARD5_2, 0, 0}, // S_SGSHARD5_1 {SPR_SGSA, 21, 4, NULL, S_SGSHARD5_3, 0, 0}, // S_SGSHARD5_2 {SPR_SGSA, 22, 4, NULL, S_SGSHARD5_4, 0, 0}, // S_SGSHARD5_3 {SPR_SGSA, 23, 4, NULL, S_SGSHARD5_5, 0, 0}, // S_SGSHARD5_4 {SPR_SGSA, 24, 4, NULL, S_SGSHARD5_1, 0, 0}, // S_SGSHARD5_5 {SPR_SGSA, 24, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD5_D {SPR_SGSB, 0, 4, NULL, S_SGSHARD6_1, 0, 0}, // S_SGSHARD6_1 {SPR_SGSB, 0, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD6_D {SPR_SGSB, 1, 4, NULL, S_SGSHARD7_1, 0, 0}, // S_SGSHARD7_1 {SPR_SGSB, 1, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD7_D {SPR_SGSB, 2, 4, NULL, S_SGSHARD8_1, 0, 0}, // S_SGSHARD8_1 {SPR_SGSB, 2, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD8_D {SPR_SGSB, 3, 4, NULL, S_SGSHARD9_1, 0, 0}, // S_SGSHARD9_1 {SPR_SGSB, 3, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD9_D {SPR_SGSB, 4, 4, NULL, S_SGSHARD0_1, 0, 0}, // S_SGSHARD0_1 {SPR_SGSB, 4, 30, NULL, S_NULL, 0, 0}, // S_SGSHARD0_D {SPR_PORK, 0, 5, NULL, S_ARTI_EGGC2, 0, 0}, // S_ARTI_EGGC1 {SPR_PORK, 1, 5, NULL, S_ARTI_EGGC3, 0, 0}, // S_ARTI_EGGC2 {SPR_PORK, 2, 5, NULL, S_ARTI_EGGC4, 0, 0}, // S_ARTI_EGGC3 {SPR_PORK, 3, 5, NULL, S_ARTI_EGGC5, 0, 0}, // S_ARTI_EGGC4 {SPR_PORK, 4, 5, NULL, S_ARTI_EGGC6, 0, 0}, // S_ARTI_EGGC5 {SPR_PORK, 5, 5, NULL, S_ARTI_EGGC7, 0, 0}, // S_ARTI_EGGC6 {SPR_PORK, 6, 5, NULL, S_ARTI_EGGC8, 0, 0}, // S_ARTI_EGGC7 {SPR_PORK, 7, 5, NULL, S_ARTI_EGGC1, 0, 0}, // S_ARTI_EGGC8 {SPR_EGGM, 0, 4, NULL, S_EGGFX2, 0, 0}, // S_EGGFX1 {SPR_EGGM, 1, 4, NULL, S_EGGFX3, 0, 0}, // S_EGGFX2 {SPR_EGGM, 2, 4, NULL, S_EGGFX4, 0, 0}, // S_EGGFX3 {SPR_EGGM, 3, 4, NULL, S_EGGFX5, 0, 0}, // S_EGGFX4 {SPR_EGGM, 4, 4, NULL, S_EGGFX1, 0, 0}, // S_EGGFX5 {SPR_FHFX, 32776, 3, NULL, S_EGGFXI1_2, 0, 0}, // S_EGGFXI1_1 {SPR_FHFX, 32777, 3, NULL, S_EGGFXI1_3, 0, 0}, // S_EGGFXI1_2 {SPR_FHFX, 32778, 3, NULL, S_EGGFXI1_4, 0, 0}, // S_EGGFXI1_3 {SPR_FHFX, 32779, 3, NULL, S_NULL, 0, 0}, // S_EGGFXI1_4 {SPR_SPHL, 0, 350, NULL, S_ARTI_SPHL1, 0, 0}, // S_ARTI_SPHL1 {SPR_STWN, 0, -1, NULL, S_NULL, 0, 0}, // S_ZWINGEDSTATUENOSKULL {SPR_STWN, 1, -1, NULL, S_NULL, 0, 0}, // S_ZWINGEDSTATUENOSKULL2 {SPR_GMPD, 0, -1, NULL, S_NULL, 0, 0}, // S_ZGEMPEDESTAL1 {SPR_GMPD, 1, -1, NULL, S_NULL, 0, 0}, // S_ZGEMPEDESTAL2 {SPR_ASKU, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZSKULL {SPR_ABGM, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZGEMBIG {SPR_AGMR, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZGEMRED {SPR_AGMG, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZGEMGREEN1 {SPR_AGG2, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZGEMGREEN2 {SPR_AGMB, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZGEMBLUE1 {SPR_AGB2, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZGEMBLUE2 {SPR_ABK1, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZBOOK1 {SPR_ABK2, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZBOOK2 {SPR_ASK2, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZSKULL2 {SPR_AFWP, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZFWEAPON {SPR_ACWP, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZCWEAPON {SPR_AMWP, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTIPUZZMWEAPON {SPR_AGER, 32768, 4, NULL, S_ARTIPUZZGEAR_2, 0, 0}, // S_ARTIPUZZGEAR_1 {SPR_AGER, 32769, 4, NULL, S_ARTIPUZZGEAR_3, 0, 0}, // S_ARTIPUZZGEAR_2 {SPR_AGER, 32770, 4, NULL, S_ARTIPUZZGEAR_4, 0, 0}, // S_ARTIPUZZGEAR_3 {SPR_AGER, 32771, 4, NULL, S_ARTIPUZZGEAR_5, 0, 0}, // S_ARTIPUZZGEAR_4 {SPR_AGER, 32772, 4, NULL, S_ARTIPUZZGEAR_6, 0, 0}, // S_ARTIPUZZGEAR_5 {SPR_AGER, 32773, 4, NULL, S_ARTIPUZZGEAR_7, 0, 0}, // S_ARTIPUZZGEAR_6 {SPR_AGER, 32774, 4, NULL, S_ARTIPUZZGEAR_8, 0, 0}, // S_ARTIPUZZGEAR_7 {SPR_AGER, 32775, 4, NULL, S_ARTIPUZZGEAR_1, 0, 0}, // S_ARTIPUZZGEAR_8 {SPR_AGR2, 32768, 4, NULL, S_ARTIPUZZGEAR2_2, 0, 0}, // S_ARTIPUZZGEAR2_1 {SPR_AGR2, 32769, 4, NULL, S_ARTIPUZZGEAR2_3, 0, 0}, // S_ARTIPUZZGEAR2_2 {SPR_AGR2, 32770, 4, NULL, S_ARTIPUZZGEAR2_4, 0, 0}, // S_ARTIPUZZGEAR2_3 {SPR_AGR2, 32771, 4, NULL, S_ARTIPUZZGEAR2_5, 0, 0}, // S_ARTIPUZZGEAR2_4 {SPR_AGR2, 32772, 4, NULL, S_ARTIPUZZGEAR2_6, 0, 0}, // S_ARTIPUZZGEAR2_5 {SPR_AGR2, 32773, 4, NULL, S_ARTIPUZZGEAR2_7, 0, 0}, // S_ARTIPUZZGEAR2_6 {SPR_AGR2, 32774, 4, NULL, S_ARTIPUZZGEAR2_8, 0, 0}, // S_ARTIPUZZGEAR2_7 {SPR_AGR2, 32775, 4, NULL, S_ARTIPUZZGEAR2_1, 0, 0}, // S_ARTIPUZZGEAR2_8 {SPR_AGR3, 32768, 4, NULL, S_ARTIPUZZGEAR3_2, 0, 0}, // S_ARTIPUZZGEAR3_1 {SPR_AGR3, 32769, 4, NULL, S_ARTIPUZZGEAR3_3, 0, 0}, // S_ARTIPUZZGEAR3_2 {SPR_AGR3, 32770, 4, NULL, S_ARTIPUZZGEAR3_4, 0, 0}, // S_ARTIPUZZGEAR3_3 {SPR_AGR3, 32771, 4, NULL, S_ARTIPUZZGEAR3_5, 0, 0}, // S_ARTIPUZZGEAR3_4 {SPR_AGR3, 32772, 4, NULL, S_ARTIPUZZGEAR3_6, 0, 0}, // S_ARTIPUZZGEAR3_5 {SPR_AGR3, 32773, 4, NULL, S_ARTIPUZZGEAR3_7, 0, 0}, // S_ARTIPUZZGEAR3_6 {SPR_AGR3, 32774, 4, NULL, S_ARTIPUZZGEAR3_8, 0, 0}, // S_ARTIPUZZGEAR3_7 {SPR_AGR3, 32775, 4, NULL, S_ARTIPUZZGEAR3_1, 0, 0}, // S_ARTIPUZZGEAR3_8 {SPR_AGR4, 32768, 4, NULL, S_ARTIPUZZGEAR4_2, 0, 0}, // S_ARTIPUZZGEAR4_1 {SPR_AGR4, 32769, 4, NULL, S_ARTIPUZZGEAR4_3, 0, 0}, // S_ARTIPUZZGEAR4_2 {SPR_AGR4, 32770, 4, NULL, S_ARTIPUZZGEAR4_4, 0, 0}, // S_ARTIPUZZGEAR4_3 {SPR_AGR4, 32771, 4, NULL, S_ARTIPUZZGEAR4_5, 0, 0}, // S_ARTIPUZZGEAR4_4 {SPR_AGR4, 32772, 4, NULL, S_ARTIPUZZGEAR4_6, 0, 0}, // S_ARTIPUZZGEAR4_5 {SPR_AGR4, 32773, 4, NULL, S_ARTIPUZZGEAR4_7, 0, 0}, // S_ARTIPUZZGEAR4_6 {SPR_AGR4, 32774, 4, NULL, S_ARTIPUZZGEAR4_8, 0, 0}, // S_ARTIPUZZGEAR4_7 {SPR_AGR4, 32775, 4, NULL, S_ARTIPUZZGEAR4_1, 0, 0}, // S_ARTIPUZZGEAR4_8 {SPR_TRCH, 32768, 3, NULL, S_ARTI_TRCH2, 0, 0}, // S_ARTI_TRCH1 {SPR_TRCH, 32769, 3, NULL, S_ARTI_TRCH3, 0, 0}, // S_ARTI_TRCH2 {SPR_TRCH, 32770, 3, NULL, S_ARTI_TRCH1, 0, 0}, // S_ARTI_TRCH3 {SPR_PSBG, 0, 20, NULL, S_FIREBOMB2, 0, 0}, // S_FIREBOMB1 {SPR_PSBG, 0, 10, NULL, S_FIREBOMB3, 0, 0}, // S_FIREBOMB2 {SPR_PSBG, 0, 10, NULL, S_FIREBOMB4, 0, 0}, // S_FIREBOMB3 {SPR_PSBG, 1, 4, NULL, S_FIREBOMB5, 0, 0}, // S_FIREBOMB4 {SPR_PSBG, 2, 4, A_Scream, S_FIREBOMB6, 0, 0}, // S_FIREBOMB5 {SPR_XPL1, 32768, 4, A_Explode, S_FIREBOMB7, 0, 0}, // S_FIREBOMB6 {SPR_XPL1, 32769, 4, NULL, S_FIREBOMB8, 0, 0}, // S_FIREBOMB7 {SPR_XPL1, 32770, 4, NULL, S_FIREBOMB9, 0, 0}, // S_FIREBOMB8 {SPR_XPL1, 32771, 4, NULL, S_FIREBOMB10, 0, 0}, // S_FIREBOMB9 {SPR_XPL1, 32772, 4, NULL, S_FIREBOMB11, 0, 0}, // S_FIREBOMB10 {SPR_XPL1, 32773, 4, NULL, S_NULL, 0, 0}, // S_FIREBOMB11 {SPR_ATLP, 0, 4, NULL, S_ARTI_ATLP2, 0, 0}, // S_ARTI_ATLP1 {SPR_ATLP, 1, 4, NULL, S_ARTI_ATLP3, 0, 0}, // S_ARTI_ATLP2 {SPR_ATLP, 2, 4, NULL, S_ARTI_ATLP4, 0, 0}, // S_ARTI_ATLP3 {SPR_ATLP, 1, 4, NULL, S_ARTI_ATLP1, 0, 0}, // S_ARTI_ATLP4 {SPR_PSBG, 0, -1, NULL, S_NULL, 0, 0}, // S_ARTI_PSBG1 {SPR_PSBG, 32768, 18, NULL, S_POISONBAG2, 0, 0}, // S_POISONBAG1 {SPR_PSBG, 32769, 4, NULL, S_POISONBAG3, 0, 0}, // S_POISONBAG2 {SPR_PSBG, 2, 3, NULL, S_POISONBAG4, 0, 0}, // S_POISONBAG3 {SPR_PSBG, 2, 1, A_PoisonBagInit, S_NULL, 0, 0}, // S_POISONBAG4 {SPR_PSBG, 3, 1, NULL, S_POISONCLOUD2, 0, 0}, // S_POISONCLOUD1 {SPR_PSBG, 3, 1, A_Scream, S_POISONCLOUD3, 0, 0}, // S_POISONCLOUD2 {SPR_PSBG, 3, 2, A_PoisonBagDamage, S_POISONCLOUD4, 0, 0}, // S_POISONCLOUD3 {SPR_PSBG, 4, 2, A_PoisonBagDamage, S_POISONCLOUD5, 0, 0}, // S_POISONCLOUD4 {SPR_PSBG, 4, 2, A_PoisonBagDamage, S_POISONCLOUD6, 0, 0}, // S_POISONCLOUD5 {SPR_PSBG, 4, 2, A_PoisonBagDamage, S_POISONCLOUD7, 0, 0}, // S_POISONCLOUD6 {SPR_PSBG, 5, 2, A_PoisonBagDamage, S_POISONCLOUD8, 0, 0}, // S_POISONCLOUD7 {SPR_PSBG, 5, 2, A_PoisonBagDamage, S_POISONCLOUD9, 0, 0}, // S_POISONCLOUD8 {SPR_PSBG, 5, 2, A_PoisonBagDamage, S_POISONCLOUD10, 0, 0}, // S_POISONCLOUD9 {SPR_PSBG, 6, 2, A_PoisonBagDamage, S_POISONCLOUD11, 0, 0}, // S_POISONCLOUD10 {SPR_PSBG, 6, 2, A_PoisonBagDamage, S_POISONCLOUD12, 0, 0}, // S_POISONCLOUD11 {SPR_PSBG, 6, 2, A_PoisonBagDamage, S_POISONCLOUD13, 0, 0}, // S_POISONCLOUD12 {SPR_PSBG, 7, 2, A_PoisonBagDamage, S_POISONCLOUD14, 0, 0}, // S_POISONCLOUD13 {SPR_PSBG, 7, 2, A_PoisonBagDamage, S_POISONCLOUD15, 0, 0}, // S_POISONCLOUD14 {SPR_PSBG, 7, 2, A_PoisonBagDamage, S_POISONCLOUD16, 0, 0}, // S_POISONCLOUD15 {SPR_PSBG, 8, 2, A_PoisonBagDamage, S_POISONCLOUD17, 0, 0}, // S_POISONCLOUD16 {SPR_PSBG, 8, 1, A_PoisonBagDamage, S_POISONCLOUD18, 0, 0}, // S_POISONCLOUD17 {SPR_PSBG, 8, 1, A_PoisonBagCheck, S_POISONCLOUD4, 0, 0}, // S_POISONCLOUD18 {SPR_PSBG, 7, 7, NULL, S_POISONCLOUD_X2, 0, 0}, // S_POISONCLOUD_X1 {SPR_PSBG, 6, 7, NULL, S_POISONCLOUD_X3, 0, 0}, // S_POISONCLOUD_X2 {SPR_PSBG, 5, 6, NULL, S_POISONCLOUD_X4, 0, 0}, // S_POISONCLOUD_X3 {SPR_PSBG, 3, 6, NULL, S_NULL, 0, 0}, // S_POISONCLOUD_X4 {SPR_THRW, 0, 4, A_CheckThrowBomb, S_THROWINGBOMB2, 0, 0}, // S_THROWINGBOMB1 {SPR_THRW, 1, 3, A_CheckThrowBomb, S_THROWINGBOMB3, 0, 0}, // S_THROWINGBOMB2 {SPR_THRW, 2, 3, A_CheckThrowBomb, S_THROWINGBOMB4, 0, 0}, // S_THROWINGBOMB3 {SPR_THRW, 3, 3, A_CheckThrowBomb, S_THROWINGBOMB5, 0, 0}, // S_THROWINGBOMB4 {SPR_THRW, 4, 3, A_CheckThrowBomb, S_THROWINGBOMB6, 0, 0}, // S_THROWINGBOMB5 {SPR_THRW, 5, 3, A_CheckThrowBomb, S_THROWINGBOMB1, 0, 0}, // S_THROWINGBOMB6 {SPR_THRW, 6, 6, A_CheckThrowBomb, S_THROWINGBOMB8, 0, 0}, // S_THROWINGBOMB7 {SPR_THRW, 5, 4, A_CheckThrowBomb, S_THROWINGBOMB9, 0, 0}, // S_THROWINGBOMB8 {SPR_THRW, 7, 6, A_CheckThrowBomb, S_THROWINGBOMB10, 0, 0}, // S_THROWINGBOMB9 {SPR_THRW, 5, 4, A_CheckThrowBomb, S_THROWINGBOMB11, 0, 0}, // S_THROWINGBOMB10 {SPR_THRW, 6, 6, A_CheckThrowBomb, S_THROWINGBOMB12, 0, 0}, // S_THROWINGBOMB11 {SPR_THRW, 5, 3, A_CheckThrowBomb, S_THROWINGBOMB12, 0, 0}, // S_THROWINGBOMB12 {SPR_CFCF, 32784, 4, A_NoGravity, S_THROWINGBOMB_X2, 0, 0}, // S_THROWINGBOMB_X1 {SPR_CFCF, 32785, 3, A_Scream, S_THROWINGBOMB_X3, 0, 0}, // S_THROWINGBOMB_X2 {SPR_CFCF, 32786, 4, A_Explode, S_THROWINGBOMB_X4, 0, 0}, // S_THROWINGBOMB_X3 {SPR_CFCF, 32787, 3, NULL, S_THROWINGBOMB_X5, 0, 0}, // S_THROWINGBOMB_X4 {SPR_CFCF, 32788, 4, NULL, S_THROWINGBOMB_X6, 0, 0}, // S_THROWINGBOMB_X5 {SPR_CFCF, 32790, 3, NULL, S_THROWINGBOMB_X7, 0, 0}, // S_THROWINGBOMB_X6 {SPR_CFCF, 32791, 4, NULL, S_THROWINGBOMB_X8, 0, 0}, // S_THROWINGBOMB_X7 {SPR_CFCF, 32793, 3, NULL, S_NULL, 0, 0}, // S_THROWINGBOMB_X8 {SPR_SPED, 32768, 3, NULL, S_ARTI_BOOTS2, 0, 0}, // S_ARTI_BOOTS1 {SPR_SPED, 32769, 3, NULL, S_ARTI_BOOTS3, 0, 0}, // S_ARTI_BOOTS2 {SPR_SPED, 32770, 3, NULL, S_ARTI_BOOTS4, 0, 0}, // S_ARTI_BOOTS3 {SPR_SPED, 32771, 3, NULL, S_ARTI_BOOTS5, 0, 0}, // S_ARTI_BOOTS4 {SPR_SPED, 32772, 3, NULL, S_ARTI_BOOTS6, 0, 0}, // S_ARTI_BOOTS5 {SPR_SPED, 32773, 3, NULL, S_ARTI_BOOTS7, 0, 0}, // S_ARTI_BOOTS6 {SPR_SPED, 32774, 3, NULL, S_ARTI_BOOTS8, 0, 0}, // S_ARTI_BOOTS7 {SPR_SPED, 32775, 3, NULL, S_ARTI_BOOTS1, 0, 0}, // S_ARTI_BOOTS8 {SPR_BMAN, 32768, -1, NULL, S_NULL, 0, 0}, // S_ARTI_MANA {SPR_BRAC, 32768, 4, NULL, S_ARTI_ARMOR2, 0, 0}, // S_ARTI_ARMOR1 {SPR_BRAC, 32769, 4, NULL, S_ARTI_ARMOR3, 0, 0}, // S_ARTI_ARMOR2 {SPR_BRAC, 32770, 4, NULL, S_ARTI_ARMOR4, 0, 0}, // S_ARTI_ARMOR3 {SPR_BRAC, 32771, 4, NULL, S_ARTI_ARMOR5, 0, 0}, // S_ARTI_ARMOR4 {SPR_BRAC, 32772, 4, NULL, S_ARTI_ARMOR6, 0, 0}, // S_ARTI_ARMOR5 {SPR_BRAC, 32773, 4, NULL, S_ARTI_ARMOR7, 0, 0}, // S_ARTI_ARMOR6 {SPR_BRAC, 32774, 4, NULL, S_ARTI_ARMOR8, 0, 0}, // S_ARTI_ARMOR7 {SPR_BRAC, 32775, 4, NULL, S_ARTI_ARMOR1, 0, 0}, // S_ARTI_ARMOR8 {SPR_BLST, 32768, 4, NULL, S_ARTI_BLAST2, 0, 0}, // S_ARTI_BLAST1 {SPR_BLST, 32769, 4, NULL, S_ARTI_BLAST3, 0, 0}, // S_ARTI_BLAST2 {SPR_BLST, 32770, 4, NULL, S_ARTI_BLAST4, 0, 0}, // S_ARTI_BLAST3 {SPR_BLST, 32771, 4, NULL, S_ARTI_BLAST5, 0, 0}, // S_ARTI_BLAST4 {SPR_BLST, 32772, 4, NULL, S_ARTI_BLAST6, 0, 0}, // S_ARTI_BLAST5 {SPR_BLST, 32773, 4, NULL, S_ARTI_BLAST7, 0, 0}, // S_ARTI_BLAST6 {SPR_BLST, 32774, 4, NULL, S_ARTI_BLAST8, 0, 0}, // S_ARTI_BLAST7 {SPR_BLST, 32775, 4, NULL, S_ARTI_BLAST1, 0, 0}, // S_ARTI_BLAST8 {SPR_HRAD, 32768, 4, NULL, S_ARTI_HEALRAD2, 0, 0}, // S_ARTI_HEALRAD1 {SPR_HRAD, 32769, 4, NULL, S_ARTI_HEALRAD3, 0, 0}, // S_ARTI_HEALRAD2 {SPR_HRAD, 32770, 4, NULL, S_ARTI_HEALRAD4, 0, 0}, // S_ARTI_HEALRAD3 {SPR_HRAD, 32771, 4, NULL, S_ARTI_HEALRAD5, 0, 0}, // S_ARTI_HEALRAD4 {SPR_HRAD, 32772, 4, NULL, S_ARTI_HEALRAD6, 0, 0}, // S_ARTI_HEALRAD5 {SPR_HRAD, 32773, 4, NULL, S_ARTI_HEALRAD7, 0, 0}, // S_ARTI_HEALRAD6 {SPR_HRAD, 32774, 4, NULL, S_ARTI_HEALRAD8, 0, 0}, // S_ARTI_HEALRAD7 {SPR_HRAD, 32775, 4, NULL, S_ARTI_HEALRAD9, 0, 0}, // S_ARTI_HEALRAD8 {SPR_HRAD, 32776, 4, NULL, S_ARTI_HEALRAD0, 0, 0}, // S_ARTI_HEALRAD9 {SPR_HRAD, 32777, 4, NULL, S_ARTI_HEALRADA, 0, 0}, // S_ARTI_HEALRAD0 {SPR_HRAD, 32778, 4, NULL, S_ARTI_HEALRADB, 0, 0}, // S_ARTI_HEALRADA {SPR_HRAD, 32779, 4, NULL, S_ARTI_HEALRADC, 0, 0}, // S_ARTI_HEALRADB {SPR_HRAD, 32780, 4, NULL, S_ARTI_HEALRADD, 0, 0}, // S_ARTI_HEALRADC {SPR_HRAD, 32781, 4, NULL, S_ARTI_HEALRADE, 0, 0}, // S_ARTI_HEALRADD {SPR_HRAD, 32782, 4, NULL, S_ARTI_HEALRADF, 0, 0}, // S_ARTI_HEALRADE {SPR_HRAD, 32783, 4, NULL, S_ARTI_HEALRAD1, 0, 0}, // S_ARTI_HEALRADF {SPR_SPSH, 0, 8, NULL, S_SPLASH2, 0, 0}, // S_SPLASH1 {SPR_SPSH, 1, 8, NULL, S_SPLASH3, 0, 0}, // S_SPLASH2 {SPR_SPSH, 2, 8, NULL, S_SPLASH4, 0, 0}, // S_SPLASH3 {SPR_SPSH, 3, 16, NULL, S_NULL, 0, 0}, // S_SPLASH4 {SPR_SPSH, 3, 10, NULL, S_NULL, 0, 0}, // S_SPLASHX {SPR_SPSH, 4, 5, NULL, S_SPLASHBASE2, 0, 0}, // S_SPLASHBASE1 {SPR_SPSH, 5, 5, NULL, S_SPLASHBASE3, 0, 0}, // S_SPLASHBASE2 {SPR_SPSH, 6, 5, NULL, S_SPLASHBASE4, 0, 0}, // S_SPLASHBASE3 {SPR_SPSH, 7, 5, NULL, S_SPLASHBASE5, 0, 0}, // S_SPLASHBASE4 {SPR_SPSH, 8, 5, NULL, S_SPLASHBASE6, 0, 0}, // S_SPLASHBASE5 {SPR_SPSH, 9, 5, NULL, S_SPLASHBASE7, 0, 0}, // S_SPLASHBASE6 {SPR_SPSH, 10, 5, NULL, S_NULL, 0, 0}, // S_SPLASHBASE7 {SPR_LVAS, 32768, 5, NULL, S_LAVASPLASH2, 0, 0}, // S_LAVASPLASH1 {SPR_LVAS, 32769, 5, NULL, S_LAVASPLASH3, 0, 0}, // S_LAVASPLASH2 {SPR_LVAS, 32770, 5, NULL, S_LAVASPLASH4, 0, 0}, // S_LAVASPLASH3 {SPR_LVAS, 32771, 5, NULL, S_LAVASPLASH5, 0, 0}, // S_LAVASPLASH4 {SPR_LVAS, 32772, 5, NULL, S_LAVASPLASH6, 0, 0}, // S_LAVASPLASH5 {SPR_LVAS, 32773, 5, NULL, S_NULL, 0, 0}, // S_LAVASPLASH6 {SPR_LVAS, 32774, 5, NULL, S_LAVASMOKE2, 0, 0}, // S_LAVASMOKE1 {SPR_LVAS, 32775, 5, NULL, S_LAVASMOKE3, 0, 0}, // S_LAVASMOKE2 {SPR_LVAS, 32776, 5, NULL, S_LAVASMOKE4, 0, 0}, // S_LAVASMOKE3 {SPR_LVAS, 32777, 5, NULL, S_LAVASMOKE5, 0, 0}, // S_LAVASMOKE4 {SPR_LVAS, 32778, 5, NULL, S_NULL, 0, 0}, // S_LAVASMOKE5 {SPR_SLDG, 0, 8, NULL, S_SLUDGECHUNK2, 0, 0}, // S_SLUDGECHUNK1 {SPR_SLDG, 1, 8, NULL, S_SLUDGECHUNK3, 0, 0}, // S_SLUDGECHUNK2 {SPR_SLDG, 2, 8, NULL, S_SLUDGECHUNK4, 0, 0}, // S_SLUDGECHUNK3 {SPR_SLDG, 3, 8, NULL, S_NULL, 0, 0}, // S_SLUDGECHUNK4 {SPR_SLDG, 3, 6, NULL, S_NULL, 0, 0}, // S_SLUDGECHUNKX {SPR_SLDG, 4, 6, NULL, S_SLUDGESPLASH2, 0, 0}, // S_SLUDGESPLASH1 {SPR_SLDG, 5, 6, NULL, S_SLUDGESPLASH3, 0, 0}, // S_SLUDGESPLASH2 {SPR_SLDG, 6, 6, NULL, S_SLUDGESPLASH4, 0, 0}, // S_SLUDGESPLASH3 {SPR_SLDG, 7, 6, NULL, S_NULL, 0, 0}, // S_SLUDGESPLASH4 {SPR_STTW, 0, -1, NULL, S_NULL, 0, 0}, // S_ZWINGEDSTATUE1 {SPR_RCK1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZROCK1_1 {SPR_RCK2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZROCK2_1 {SPR_RCK3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZROCK3_1 {SPR_RCK4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZROCK4_1 {SPR_CDLR, 0, 4, NULL, S_ZCHANDELIER2, 0, 0}, // S_ZCHANDELIER1 {SPR_CDLR, 1, 4, NULL, S_ZCHANDELIER3, 0, 0}, // S_ZCHANDELIER2 {SPR_CDLR, 2, 4, NULL, S_ZCHANDELIER1, 0, 0}, // S_ZCHANDELIER3 {SPR_CDLR, 3, -1, NULL, S_NULL, 0, 0}, // S_ZCHANDELIER_U {SPR_TRE1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREEDEAD1 {SPR_TRE1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREE {SPR_TRDT, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREEDESTRUCTIBLE1 {SPR_TRDT, 1, 5, NULL, S_ZTREEDES_D2, 0, 0}, // S_ZTREEDES_D1 {SPR_TRDT, 2, 5, A_Scream, S_ZTREEDES_D3, 0, 0}, // S_ZTREEDES_D2 {SPR_TRDT, 3, 5, NULL, S_ZTREEDES_D4, 0, 0}, // S_ZTREEDES_D3 {SPR_TRDT, 4, 5, NULL, S_ZTREEDES_D5, 0, 0}, // S_ZTREEDES_D4 {SPR_TRDT, 5, 5, NULL, S_ZTREEDES_D6, 0, 0}, // S_ZTREEDES_D5 {SPR_TRDT, 6, -1, NULL, S_NULL, 0, 0}, // S_ZTREEDES_D6 {SPR_TRDT, 32775, 5, NULL, S_ZTREEDES_X2, 0, 0}, // S_ZTREEDES_X1 {SPR_TRDT, 32776, 5, NULL, S_ZTREEDES_X3, 0, 0}, // S_ZTREEDES_X2 {SPR_TRDT, 32777, 5, NULL, S_ZTREEDES_X4, 0, 0}, // S_ZTREEDES_X3 {SPR_TRDT, 32778, 5, NULL, S_ZTREEDES_X5, 0, 0}, // S_ZTREEDES_X4 {SPR_TRDT, 32779, 5, NULL, S_ZTREEDES_X6, 0, 0}, // S_ZTREEDES_X5 {SPR_TRDT, 32780, 5, A_Explode, S_ZTREEDES_X7, 0, 0}, // S_ZTREEDES_X6 {SPR_TRDT, 32781, 5, NULL, S_ZTREEDES_X8, 0, 0}, // S_ZTREEDES_X7 {SPR_TRDT, 14, 5, NULL, S_ZTREEDES_X9, 0, 0}, // S_ZTREEDES_X8 {SPR_TRDT, 15, 5, NULL, S_ZTREEDES_X10, 0, 0}, // S_ZTREEDES_X9 {SPR_TRDT, 16, -1, NULL, S_NULL, 0, 0}, // S_ZTREEDES_X10 {SPR_TRE2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREESWAMP182_1 {SPR_TRE3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREESWAMP172_1 {SPR_STM1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTUMPBURNED1 {SPR_STM2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTUMPBARE1 {SPR_STM3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTUMPSWAMP1_1 {SPR_STM4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTUMPSWAMP2_1 {SPR_MSH1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMLARGE1_1 {SPR_MSH2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMLARGE2_1 {SPR_MSH3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMLARGE3_1 {SPR_MSH4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMSMALL1_1 {SPR_MSH5, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMSMALL2_1 {SPR_MSH6, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMSMALL3_1 {SPR_MSH7, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMSMALL4_1 {SPR_MSH8, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHROOMSMALL5_1 {SPR_SGMP, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITEPILLAR1 {SPR_SGM1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITELARGE1 {SPR_SGM2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITEMEDIUM1 {SPR_SGM3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITESMALL1 {SPR_SLC1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALACTITELARGE1 {SPR_SLC2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALACTITEMEDIUM1 {SPR_SLC3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALACTITESMALL1 {SPR_MSS1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZMOSSCEILING1_1 {SPR_MSS2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZMOSSCEILING2_1 {SPR_SWMV, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSWAMPVINE1 {SPR_CPS1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZCORPSEKABOB1 {SPR_CPS2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZCORPSESLEEPING1 {SPR_TMS1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTOMBSTONERIP1 {SPR_TMS2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTOMBSTONESHANE1 {SPR_TMS3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTOMBSTONEBIGCROSS1 {SPR_TMS4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTOMBSTONEBRIANR1 {SPR_TMS5, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTOMBSTONECROSSCIRCLE1 {SPR_TMS6, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTOMBSTONESMALLCROSS1 {SPR_TMS7, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTOMBSTONEBRIANP1 {SPR_CPS3, 0, -1, NULL, S_NULL, 0, 0}, // S_CORPSEHANGING_1 {SPR_STT2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEGREENTALL_1 {SPR_STT3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEBLUETALL_1 {SPR_STT4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEGREENSHORT_1 {SPR_STT5, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEBLUESHORT_1 {SPR_GAR1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLESTRIPETALL_1 {SPR_GAR2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEDARKREDTALL_1 {SPR_GAR3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEREDTALL_1 {SPR_GAR4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLETANTALL_1 {SPR_GAR5, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLERUSTTALL_1 {SPR_GAR6, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEDARKREDSHORT_1 {SPR_GAR7, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLEREDSHORT_1 {SPR_GAR8, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLETANSHORT_1 {SPR_GAR9, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTATUEGARGOYLERUSTSHORT_1 {SPR_BNR1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZBANNERTATTERED_1 {SPR_TRE4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREELARGE1 {SPR_TRE5, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREELARGE2 {SPR_TRE6, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREEGNARLED1 {SPR_TRE7, 0, -1, NULL, S_NULL, 0, 0}, // S_ZTREEGNARLED2 {SPR_LOGG, 0, -1, NULL, S_NULL, 0, 0}, // S_ZLOG {SPR_ICT1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALACTITEICELARGE {SPR_ICT2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALACTITEICEMEDIUM {SPR_ICT3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALACTITEICESMALL {SPR_ICT4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALACTITEICETINY {SPR_ICM1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITEICELARGE {SPR_ICM2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITEICEMEDIUM {SPR_ICM3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITEICESMALL {SPR_ICM4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSTALAGMITEICETINY {SPR_RKBL, 0, -1, NULL, S_NULL, 0, 0}, // S_ZROCKBROWN1 {SPR_RKBS, 0, -1, NULL, S_NULL, 0, 0}, // S_ZROCKBROWN2 {SPR_RKBK, 0, -1, NULL, S_NULL, 0, 0}, // S_ZROCKBLACK {SPR_RBL1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZRUBBLE1 {SPR_RBL2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZRUBBLE2 {SPR_RBL3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZRUBBLE3 {SPR_VASE, 0, -1, NULL, S_NULL, 0, 0}, // S_ZVASEPILLAR {SPR_POT1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZPOTTERY1 {SPR_POT2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZPOTTERY2 {SPR_POT3, 0, -1, NULL, S_NULL, 0, 0}, // S_ZPOTTERY3 {SPR_POT1, 0, 0, A_PotteryExplode, S_NULL, 0, 0}, // S_ZPOTTERY_EXPLODE {SPR_PBIT, 0, -1, NULL, S_NULL, 0, 0}, // S_POTTERYBIT_1 {SPR_PBIT, 1, -1, NULL, S_NULL, 0, 0}, // S_POTTERYBIT_2 {SPR_PBIT, 2, -1, NULL, S_NULL, 0, 0}, // S_POTTERYBIT_3 {SPR_PBIT, 3, -1, NULL, S_NULL, 0, 0}, // S_POTTERYBIT_4 {SPR_PBIT, 4, -1, NULL, S_NULL, 0, 0}, // S_POTTERYBIT_5 {SPR_PBIT, 5, 0, A_PotteryChooseBit, S_NULL, 0, 0}, // S_POTTERYBIT_EX0 {SPR_PBIT, 5, 140, NULL, S_POTTERYBIT_EX1_2, 0, 0}, // S_POTTERYBIT_EX1 {SPR_PBIT, 5, 1, A_PotteryCheck, S_NULL, 0, 0}, // S_POTTERYBIT_EX1_2 {SPR_PBIT, 6, 140, NULL, S_POTTERYBIT_EX2_2, 0, 0}, // S_POTTERYBIT_EX2 {SPR_PBIT, 6, 1, A_PotteryCheck, S_NULL, 0, 0}, // S_POTTERYBIT_EX2_2 {SPR_PBIT, 7, 140, NULL, S_POTTERYBIT_EX3_2, 0, 0}, // S_POTTERYBIT_EX3 {SPR_PBIT, 7, 1, A_PotteryCheck, S_NULL, 0, 0}, // S_POTTERYBIT_EX3_2 {SPR_PBIT, 8, 140, NULL, S_POTTERYBIT_EX4_2, 0, 0}, // S_POTTERYBIT_EX4 {SPR_PBIT, 8, 1, A_PotteryCheck, S_NULL, 0, 0}, // S_POTTERYBIT_EX4_2 {SPR_PBIT, 9, 140, NULL, S_POTTERYBIT_EX5_2, 0, 0}, // S_POTTERYBIT_EX5 {SPR_PBIT, 9, 1, A_PotteryCheck, S_NULL, 0, 0}, // S_POTTERYBIT_EX5_2 {SPR_CPS4, 0, -1, NULL, S_NULL, 0, 0}, // S_ZCORPSELYNCHED1 {SPR_CPS5, 0, 140, A_CorpseBloodDrip, S_ZCORPSELYNCHED2, 0, 0}, // S_ZCORPSELYNCHED2 {SPR_CPS6, 0, -1, NULL, S_NULL, 0, 0}, // S_ZCORPSESITTING {SPR_CPS6, 0, 1, A_CorpseExplode, S_NULL, 0, 0}, // S_ZCORPSESITTING_X {SPR_CPB1, 0, -1, NULL, S_NULL, 0, 0}, // S_CORPSEBIT_1 {SPR_CPB2, 0, -1, NULL, S_NULL, 0, 0}, // S_CORPSEBIT_2 {SPR_CPB3, 0, -1, NULL, S_NULL, 0, 0}, // S_CORPSEBIT_3 {SPR_CPB4, 0, -1, NULL, S_NULL, 0, 0}, // S_CORPSEBIT_4 {SPR_BDRP, 0, -1, NULL, S_NULL, 0, 0}, // S_CORPSEBLOODDRIP {SPR_BDSH, 0, 3, NULL, S_CORPSEBLOODDRIP_X2, 0, 0}, // S_CORPSEBLOODDRIP_X1 {SPR_BDSH, 1, 3, NULL, S_CORPSEBLOODDRIP_X3, 0, 0}, // S_CORPSEBLOODDRIP_X2 {SPR_BDSH, 2, 2, NULL, S_CORPSEBLOODDRIP_X4, 0, 0}, // S_CORPSEBLOODDRIP_X3 {SPR_BDSH, 3, 2, NULL, S_NULL, 0, 0}, // S_CORPSEBLOODDRIP_X4 {SPR_BDPL, 0, -1, NULL, S_NULL, 0, 0}, // S_BLOODPOOL {SPR_CNDL, 32768, 4, NULL, S_ZCANDLE2, 0, 0}, // S_ZCANDLE1 {SPR_CNDL, 32769, 4, NULL, S_ZCANDLE3, 0, 0}, // S_ZCANDLE2 {SPR_CNDL, 32770, 4, NULL, S_ZCANDLE1, 0, 0}, // S_ZCANDLE3 {SPR_MAN1, 0, 20, A_LeafSpawn, S_ZLEAFSPAWNER, 0, 0}, // S_ZLEAFSPAWNER {SPR_LEF1, 0, 4, NULL, S_LEAF1_2, 0, 0}, // S_LEAF1_1 {SPR_LEF1, 1, 4, NULL, S_LEAF1_3, 0, 0}, // S_LEAF1_2 {SPR_LEF1, 2, 4, NULL, S_LEAF1_4, 0, 0}, // S_LEAF1_3 {SPR_LEF1, 3, 4, A_LeafThrust, S_LEAF1_5, 0, 0}, // S_LEAF1_4 {SPR_LEF1, 4, 4, NULL, S_LEAF1_6, 0, 0}, // S_LEAF1_5 {SPR_LEF1, 5, 4, NULL, S_LEAF1_7, 0, 0}, // S_LEAF1_6 {SPR_LEF1, 6, 4, NULL, S_LEAF1_8, 0, 0}, // S_LEAF1_7 {SPR_LEF1, 7, 4, A_LeafThrust, S_LEAF1_9, 0, 0}, // S_LEAF1_8 {SPR_LEF1, 8, 4, NULL, S_LEAF1_10, 0, 0}, // S_LEAF1_9 {SPR_LEF1, 0, 4, NULL, S_LEAF1_11, 0, 0}, // S_LEAF1_10 {SPR_LEF1, 1, 4, NULL, S_LEAF1_12, 0, 0}, // S_LEAF1_11 {SPR_LEF1, 2, 4, A_LeafThrust, S_LEAF1_13, 0, 0}, // S_LEAF1_12 {SPR_LEF1, 3, 4, NULL, S_LEAF1_14, 0, 0}, // S_LEAF1_13 {SPR_LEF1, 4, 4, NULL, S_LEAF1_15, 0, 0}, // S_LEAF1_14 {SPR_LEF1, 5, 4, NULL, S_LEAF1_16, 0, 0}, // S_LEAF1_15 {SPR_LEF1, 6, 4, A_LeafThrust, S_LEAF1_17, 0, 0}, // S_LEAF1_16 {SPR_LEF1, 7, 4, NULL, S_LEAF1_18, 0, 0}, // S_LEAF1_17 {SPR_LEF1, 8, 4, NULL, S_NULL, 0, 0}, // S_LEAF1_18 {SPR_LEF3, 3, 10, A_LeafCheck, S_LEAF_X1, 0, 0}, // S_LEAF_X1 {SPR_LEF2, 0, 4, NULL, S_LEAF2_2, 0, 0}, // S_LEAF2_1 {SPR_LEF2, 1, 4, NULL, S_LEAF2_3, 0, 0}, // S_LEAF2_2 {SPR_LEF2, 2, 4, NULL, S_LEAF2_4, 0, 0}, // S_LEAF2_3 {SPR_LEF2, 3, 4, A_LeafThrust, S_LEAF2_5, 0, 0}, // S_LEAF2_4 {SPR_LEF2, 4, 4, NULL, S_LEAF2_6, 0, 0}, // S_LEAF2_5 {SPR_LEF2, 5, 4, NULL, S_LEAF2_7, 0, 0}, // S_LEAF2_6 {SPR_LEF2, 6, 4, NULL, S_LEAF2_8, 0, 0}, // S_LEAF2_7 {SPR_LEF2, 7, 4, A_LeafThrust, S_LEAF2_9, 0, 0}, // S_LEAF2_8 {SPR_LEF2, 8, 4, NULL, S_LEAF2_10, 0, 0}, // S_LEAF2_9 {SPR_LEF2, 0, 4, NULL, S_LEAF2_11, 0, 0}, // S_LEAF2_10 {SPR_LEF2, 1, 4, NULL, S_LEAF2_12, 0, 0}, // S_LEAF2_11 {SPR_LEF2, 2, 4, A_LeafThrust, S_LEAF2_13, 0, 0}, // S_LEAF2_12 {SPR_LEF2, 3, 4, NULL, S_LEAF2_14, 0, 0}, // S_LEAF2_13 {SPR_LEF2, 4, 4, NULL, S_LEAF2_15, 0, 0}, // S_LEAF2_14 {SPR_LEF2, 5, 4, NULL, S_LEAF2_16, 0, 0}, // S_LEAF2_15 {SPR_LEF2, 6, 4, A_LeafThrust, S_LEAF2_17, 0, 0}, // S_LEAF2_16 {SPR_LEF2, 7, 4, NULL, S_LEAF2_18, 0, 0}, // S_LEAF2_17 {SPR_LEF2, 8, 4, NULL, S_NULL, 0, 0}, // S_LEAF2_18 {SPR_TWTR, 32768, 4, NULL, S_ZTWINEDTORCH_2, 0, 0}, // S_ZTWINEDTORCH_1 {SPR_TWTR, 32769, 4, NULL, S_ZTWINEDTORCH_3, 0, 0}, // S_ZTWINEDTORCH_2 {SPR_TWTR, 32770, 4, NULL, S_ZTWINEDTORCH_4, 0, 0}, // S_ZTWINEDTORCH_3 {SPR_TWTR, 32771, 4, NULL, S_ZTWINEDTORCH_5, 0, 0}, // S_ZTWINEDTORCH_4 {SPR_TWTR, 32772, 4, NULL, S_ZTWINEDTORCH_6, 0, 0}, // S_ZTWINEDTORCH_5 {SPR_TWTR, 32773, 4, NULL, S_ZTWINEDTORCH_7, 0, 0}, // S_ZTWINEDTORCH_6 {SPR_TWTR, 32774, 4, NULL, S_ZTWINEDTORCH_8, 0, 0}, // S_ZTWINEDTORCH_7 {SPR_TWTR, 32775, 4, NULL, S_ZTWINEDTORCH_1, 0, 0}, // S_ZTWINEDTORCH_8 {SPR_TWTR, 8, -1, NULL, S_NULL, 0, 0}, // S_ZTWINEDTORCH_UNLIT {SPR_TLGL, 0, 2, NULL, S_BRIDGE2, 0, 0}, // S_BRIDGE1 {SPR_TLGL, 0, 2, A_BridgeInit, S_BRIDGE3, 0, 0}, // S_BRIDGE2 {SPR_TLGL, 0, -1, NULL, S_NULL, 0, 0}, // S_BRIDGE3 {SPR_TLGL, 0, 2, NULL, S_FREE_BRIDGE2, 0, 0}, // S_FREE_BRIDGE1 {SPR_TLGL, 0, 300, NULL, S_NULL, 0, 0}, // S_FREE_BRIDGE2 {SPR_TLGL, 0, 2, NULL, S_BBALL2, 0, 0}, // S_BBALL1 {SPR_TLGL, 0, 5, A_BridgeOrbit, S_BBALL2, 0, 0}, // S_BBALL2 {SPR_WLTR, 32768, 5, NULL, S_ZWALLTORCH2, 0, 0}, // S_ZWALLTORCH1 {SPR_WLTR, 32769, 5, NULL, S_ZWALLTORCH3, 0, 0}, // S_ZWALLTORCH2 {SPR_WLTR, 32770, 5, NULL, S_ZWALLTORCH4, 0, 0}, // S_ZWALLTORCH3 {SPR_WLTR, 32771, 5, NULL, S_ZWALLTORCH5, 0, 0}, // S_ZWALLTORCH4 {SPR_WLTR, 32772, 5, NULL, S_ZWALLTORCH6, 0, 0}, // S_ZWALLTORCH5 {SPR_WLTR, 32773, 5, NULL, S_ZWALLTORCH7, 0, 0}, // S_ZWALLTORCH6 {SPR_WLTR, 32774, 5, NULL, S_ZWALLTORCH8, 0, 0}, // S_ZWALLTORCH7 {SPR_WLTR, 32775, 5, NULL, S_ZWALLTORCH1, 0, 0}, // S_ZWALLTORCH8 {SPR_WLTR, 8, -1, NULL, S_NULL, 0, 0}, // S_ZWALLTORCH_U {SPR_BARL, 0, -1, NULL, S_NULL, 0, 0}, // S_ZBARREL1 {SPR_SHB1, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHRUB1 {SPR_SHB1, 0, 1, A_TreeDeath, S_ZSHRUB1, 0, 0}, // S_ZSHRUB1_DIE {SPR_SHB1, 32769, 7, NULL, S_ZSHRUB1_X2, 0, 0}, // S_ZSHRUB1_X1 {SPR_SHB1, 32770, 6, A_Scream, S_ZSHRUB1_X3, 0, 0}, // S_ZSHRUB1_X2 {SPR_SHB1, 32771, 5, NULL, S_NULL, 0, 0}, // S_ZSHRUB1_X3 {SPR_SHB2, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSHRUB2 {SPR_SHB2, 0, 1, A_TreeDeath, S_ZSHRUB2, 0, 0}, // S_ZSHRUB2_DIE {SPR_SHB2, 32769, 7, NULL, S_ZSHRUB2_X2, 0, 0}, // S_ZSHRUB2_X1 {SPR_SHB2, 32770, 6, A_Scream, S_ZSHRUB2_X3, 0, 0}, // S_ZSHRUB2_X2 {SPR_SHB2, 32771, 5, A_Explode, S_ZSHRUB2_X4, 0, 0}, // S_ZSHRUB2_X3 {SPR_SHB2, 32772, 5, NULL, S_NULL, 0, 0}, // S_ZSHRUB2_X4 {SPR_BCKT, 0, -1, NULL, S_NULL, 0, 0}, // S_ZBUCKET1 {SPR_SHRM, 0, 5, A_PoisonShroom, S_ZPOISONSHROOM_P2, 0, 0}, // S_ZPOISONSHROOM1 {SPR_SHRM, 0, 6, NULL, S_ZPOISONSHROOM_P2, 0, 0}, // S_ZPOISONSHROOM_P1 {SPR_SHRM, 1, 8, A_Pain, S_ZPOISONSHROOM1, 0, 0}, // S_ZPOISONSHROOM_P2 {SPR_SHRM, 2, 5, NULL, S_ZPOISONSHROOM_X2, 0, 0}, // S_ZPOISONSHROOM_X1 {SPR_SHRM, 3, 5, NULL, S_ZPOISONSHROOM_X3, 0, 0}, // S_ZPOISONSHROOM_X2 {SPR_SHRM, 4, 5, A_PoisonBagInit, S_ZPOISONSHROOM_X4, 0, 0}, // S_ZPOISONSHROOM_X3 {SPR_SHRM, 5, -1, NULL, S_NULL, 0, 0}, // S_ZPOISONSHROOM_X4 {SPR_FBUL, 32768, 4, NULL, S_ZFIREBULL2, 0, 0}, // S_ZFIREBULL1 {SPR_FBUL, 32769, 4, NULL, S_ZFIREBULL3, 0, 0}, // S_ZFIREBULL2 {SPR_FBUL, 32770, 4, NULL, S_ZFIREBULL4, 0, 0}, // S_ZFIREBULL3 {SPR_FBUL, 32771, 4, NULL, S_ZFIREBULL5, 0, 0}, // S_ZFIREBULL4 {SPR_FBUL, 32772, 4, NULL, S_ZFIREBULL6, 0, 0}, // S_ZFIREBULL5 {SPR_FBUL, 32773, 4, NULL, S_ZFIREBULL7, 0, 0}, // S_ZFIREBULL6 {SPR_FBUL, 32774, 4, NULL, S_ZFIREBULL1, 0, 0}, // S_ZFIREBULL7 {SPR_FBUL, 32777, 4, NULL, S_ZFIREBULL_DEATH2, 0, 0}, // S_ZFIREBULL_DEATH {SPR_FBUL, 32776, 4, NULL, S_ZFIREBULL_U, 0, 0}, // S_ZFIREBULL_DEATH2 {SPR_FBUL, 7, -1, NULL, S_NULL, 0, 0}, // S_ZFIREBULL_U {SPR_FBUL, 32776, 4, NULL, S_ZFIREBULL_BIRTH2, 0, 0}, // S_ZFIREBULL_BIRTH {SPR_FBUL, 32777, 4, NULL, S_ZFIREBULL1, 0, 0}, // S_ZFIREBULL_BIRTH2 {SPR_FSKL, 32768, 4, NULL, S_ZFIRETHING2, 0, 0}, // S_ZFIRETHING1 {SPR_FSKL, 32769, 3, NULL, S_ZFIRETHING3, 0, 0}, // S_ZFIRETHING2 {SPR_FSKL, 32770, 4, NULL, S_ZFIRETHING4, 0, 0}, // S_ZFIRETHING3 {SPR_FSKL, 32771, 3, NULL, S_ZFIRETHING5, 0, 0}, // S_ZFIRETHING4 {SPR_FSKL, 32772, 4, NULL, S_ZFIRETHING6, 0, 0}, // S_ZFIRETHING5 {SPR_FSKL, 32773, 3, NULL, S_ZFIRETHING7, 0, 0}, // S_ZFIRETHING6 {SPR_FSKL, 32774, 4, NULL, S_ZFIRETHING8, 0, 0}, // S_ZFIRETHING7 {SPR_FSKL, 32775, 3, NULL, S_ZFIRETHING9, 0, 0}, // S_ZFIRETHING8 {SPR_FSKL, 32776, 4, NULL, S_ZFIRETHING1, 0, 0}, // S_ZFIRETHING9 {SPR_BRTR, 32768, 4, NULL, S_ZBRASSTORCH2, 0, 0}, // S_ZBRASSTORCH1 {SPR_BRTR, 32769, 4, NULL, S_ZBRASSTORCH3, 0, 0}, // S_ZBRASSTORCH2 {SPR_BRTR, 32770, 4, NULL, S_ZBRASSTORCH4, 0, 0}, // S_ZBRASSTORCH3 {SPR_BRTR, 32771, 4, NULL, S_ZBRASSTORCH5, 0, 0}, // S_ZBRASSTORCH4 {SPR_BRTR, 32772, 4, NULL, S_ZBRASSTORCH6, 0, 0}, // S_ZBRASSTORCH5 {SPR_BRTR, 32773, 4, NULL, S_ZBRASSTORCH7, 0, 0}, // S_ZBRASSTORCH6 {SPR_BRTR, 32774, 4, NULL, S_ZBRASSTORCH8, 0, 0}, // S_ZBRASSTORCH7 {SPR_BRTR, 32775, 4, NULL, S_ZBRASSTORCH9, 0, 0}, // S_ZBRASSTORCH8 {SPR_BRTR, 32776, 4, NULL, S_ZBRASSTORCH10, 0, 0}, // S_ZBRASSTORCH9 {SPR_BRTR, 32777, 4, NULL, S_ZBRASSTORCH11, 0, 0}, // S_ZBRASSTORCH10 {SPR_BRTR, 32778, 4, NULL, S_ZBRASSTORCH12, 0, 0}, // S_ZBRASSTORCH11 {SPR_BRTR, 32779, 4, NULL, S_ZBRASSTORCH13, 0, 0}, // S_ZBRASSTORCH12 {SPR_BRTR, 32780, 4, NULL, S_ZBRASSTORCH1, 0, 0}, // S_ZBRASSTORCH13 {SPR_SUIT, 0, -1, NULL, S_NULL, 0, 0}, // S_ZSUITOFARMOR {SPR_SUIT, 0, 1, A_SoAExplode, S_NULL, 0, 0}, // S_ZSUITOFARMOR_X1 {SPR_SUIT, 1, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK1 {SPR_SUIT, 2, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK2 {SPR_SUIT, 3, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK3 {SPR_SUIT, 4, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK4 {SPR_SUIT, 5, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK5 {SPR_SUIT, 6, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK6 {SPR_SUIT, 7, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK7 {SPR_SUIT, 8, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK8 {SPR_SUIT, 9, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK9 {SPR_SUIT, 10, -1, NULL, S_NULL, 0, 0}, // S_ZARMORCHUNK10 {SPR_BBLL, 5, -1, NULL, S_NULL, 0, 0}, // S_ZBELL {SPR_BBLL, 0, 4, A_BellReset1, S_ZBELL_X2, 0, 0}, // S_ZBELL_X1 {SPR_BBLL, 1, 4, NULL, S_ZBELL_X3, 0, 0}, // S_ZBELL_X2 {SPR_BBLL, 2, 4, NULL, S_ZBELL_X4, 0, 0}, // S_ZBELL_X3 {SPR_BBLL, 3, 5, A_Scream, S_ZBELL_X5, 0, 0}, // S_ZBELL_X4 {SPR_BBLL, 2, 4, NULL, S_ZBELL_X6, 0, 0}, // S_ZBELL_X5 {SPR_BBLL, 1, 4, NULL, S_ZBELL_X7, 0, 0}, // S_ZBELL_X6 {SPR_BBLL, 0, 3, NULL, S_ZBELL_X8, 0, 0}, // S_ZBELL_X7 {SPR_BBLL, 4, 4, NULL, S_ZBELL_X9, 0, 0}, // S_ZBELL_X8 {SPR_BBLL, 5, 5, NULL, S_ZBELL_X10, 0, 0}, // S_ZBELL_X9 {SPR_BBLL, 6, 6, A_Scream, S_ZBELL_X11, 0, 0}, // S_ZBELL_X10 {SPR_BBLL, 5, 5, NULL, S_ZBELL_X12, 0, 0}, // S_ZBELL_X11 {SPR_BBLL, 4, 4, NULL, S_ZBELL_X13, 0, 0}, // S_ZBELL_X12 {SPR_BBLL, 0, 4, NULL, S_ZBELL_X14, 0, 0}, // S_ZBELL_X13 {SPR_BBLL, 1, 5, NULL, S_ZBELL_X15, 0, 0}, // S_ZBELL_X14 {SPR_BBLL, 2, 5, NULL, S_ZBELL_X16, 0, 0}, // S_ZBELL_X15 {SPR_BBLL, 3, 6, A_Scream, S_ZBELL_X17, 0, 0}, // S_ZBELL_X16 {SPR_BBLL, 2, 5, NULL, S_ZBELL_X18, 0, 0}, // S_ZBELL_X17 {SPR_BBLL, 1, 5, NULL, S_ZBELL_X19, 0, 0}, // S_ZBELL_X18 {SPR_BBLL, 0, 4, NULL, S_ZBELL_X20, 0, 0}, // S_ZBELL_X19 {SPR_BBLL, 4, 5, NULL, S_ZBELL_X21, 0, 0}, // S_ZBELL_X20 {SPR_BBLL, 5, 5, NULL, S_ZBELL_X22, 0, 0}, // S_ZBELL_X21 {SPR_BBLL, 6, 7, A_Scream, S_ZBELL_X23, 0, 0}, // S_ZBELL_X22 {SPR_BBLL, 5, 5, NULL, S_ZBELL_X24, 0, 0}, // S_ZBELL_X23 {SPR_BBLL, 4, 5, NULL, S_ZBELL_X25, 0, 0}, // S_ZBELL_X24 {SPR_BBLL, 0, 5, NULL, S_ZBELL_X26, 0, 0}, // S_ZBELL_X25 {SPR_BBLL, 1, 6, NULL, S_ZBELL_X27, 0, 0}, // S_ZBELL_X26 {SPR_BBLL, 2, 6, NULL, S_ZBELL_X28, 0, 0}, // S_ZBELL_X27 {SPR_BBLL, 3, 7, A_Scream, S_ZBELL_X29, 0, 0}, // S_ZBELL_X28 {SPR_BBLL, 2, 6, NULL, S_ZBELL_X30, 0, 0}, // S_ZBELL_X29 {SPR_BBLL, 1, 6, NULL, S_ZBELL_X31, 0, 0}, // S_ZBELL_X30 {SPR_BBLL, 0, 5, NULL, S_ZBELL_X32, 0, 0}, // S_ZBELL_X31 {SPR_BBLL, 4, 6, NULL, S_ZBELL_X33, 0, 0}, // S_ZBELL_X32 {SPR_BBLL, 5, 6, NULL, S_ZBELL_X34, 0, 0}, // S_ZBELL_X33 {SPR_BBLL, 6, 7, A_Scream, S_ZBELL_X35, 0, 0}, // S_ZBELL_X34 {SPR_BBLL, 5, 6, NULL, S_ZBELL_X36, 0, 0}, // S_ZBELL_X35 {SPR_BBLL, 4, 6, NULL, S_ZBELL_X37, 0, 0}, // S_ZBELL_X36 {SPR_BBLL, 0, 6, NULL, S_ZBELL_X38, 0, 0}, // S_ZBELL_X37 {SPR_BBLL, 1, 6, NULL, S_ZBELL_X39, 0, 0}, // S_ZBELL_X38 {SPR_BBLL, 2, 6, NULL, S_ZBELL_X40, 0, 0}, // S_ZBELL_X39 {SPR_BBLL, 1, 7, NULL, S_ZBELL_X41, 0, 0}, // S_ZBELL_X40 {SPR_BBLL, 0, 8, NULL, S_ZBELL_X42, 0, 0}, // S_ZBELL_X41 {SPR_BBLL, 4, 12, NULL, S_ZBELL_X43, 0, 0}, // S_ZBELL_X42 {SPR_BBLL, 0, 10, NULL, S_ZBELL_X44, 0, 0}, // S_ZBELL_X43 {SPR_BBLL, 1, 12, NULL, S_ZBELL_X45, 0, 0}, // S_ZBELL_X44 {SPR_BBLL, 0, 12, NULL, S_ZBELL_X46, 0, 0}, // S_ZBELL_X45 {SPR_BBLL, 4, 14, NULL, S_ZBELL_X47, 0, 0}, // S_ZBELL_X46 {SPR_BBLL, 0, 1, A_BellReset2, S_ZBELL, 0, 0}, // S_ZBELL_X47 {SPR_CAND, 32768, 5, NULL, S_ZBLUE_CANDLE2, 0, 0}, // S_ZBLUE_CANDLE1 {SPR_CAND, 32769, 5, NULL, S_ZBLUE_CANDLE3, 0, 0}, // S_ZBLUE_CANDLE2 {SPR_CAND, 32770, 5, NULL, S_ZBLUE_CANDLE4, 0, 0}, // S_ZBLUE_CANDLE3 {SPR_CAND, 32771, 5, NULL, S_ZBLUE_CANDLE5, 0, 0}, // S_ZBLUE_CANDLE4 {SPR_CAND, 32772, 5, NULL, S_ZBLUE_CANDLE1, 0, 0}, // S_ZBLUE_CANDLE5 {SPR_IRON, 0, -1, NULL, S_NULL, 0, 0}, // S_ZIRON_MAIDEN {SPR_XMAS, 0, -1, NULL, S_NULL, 0, 0}, // S_ZXMAS_TREE {SPR_XMAS, 0, 4, A_TreeDeath, S_ZXMAS_TREE, 0, 0}, // S_ZXMAS_TREE_DIE {SPR_XMAS, 32769, 6, NULL, S_ZXMAS_TREE_X2, 0, 0}, // S_ZXMAS_TREE_X1 {SPR_XMAS, 32770, 6, A_Scream, S_ZXMAS_TREE_X3, 0, 0}, // S_ZXMAS_TREE_X2 {SPR_XMAS, 32771, 5, NULL, S_ZXMAS_TREE_X4, 0, 0}, // S_ZXMAS_TREE_X3 {SPR_XMAS, 32772, 5, A_Explode, S_ZXMAS_TREE_X5, 0, 0}, // S_ZXMAS_TREE_X4 {SPR_XMAS, 32773, 5, NULL, S_ZXMAS_TREE_X6, 0, 0}, // S_ZXMAS_TREE_X5 {SPR_XMAS, 32774, 4, NULL, S_ZXMAS_TREE_X7, 0, 0}, // S_ZXMAS_TREE_X6 {SPR_XMAS, 7, 5, NULL, S_ZXMAS_TREE_X8, 0, 0}, // S_ZXMAS_TREE_X7 {SPR_XMAS, 8, 4, A_NoBlocking, S_ZXMAS_TREE_X9, 0, 0}, // S_ZXMAS_TREE_X8 {SPR_XMAS, 9, 4, NULL, S_ZXMAS_TREE_X10, 0, 0}, // S_ZXMAS_TREE_X9 {SPR_XMAS, 10, -1, NULL, S_NULL, 0, 0}, // S_ZXMAS_TREE_X10 {SPR_CDRN, 32769, 4, NULL, S_ZCAULDRON2, 0, 0}, // S_ZCAULDRON1 {SPR_CDRN, 32770, 4, NULL, S_ZCAULDRON3, 0, 0}, // S_ZCAULDRON2 {SPR_CDRN, 32771, 4, NULL, S_ZCAULDRON4, 0, 0}, // S_ZCAULDRON3 {SPR_CDRN, 32772, 4, NULL, S_ZCAULDRON5, 0, 0}, // S_ZCAULDRON4 {SPR_CDRN, 32773, 4, NULL, S_ZCAULDRON6, 0, 0}, // S_ZCAULDRON5 {SPR_CDRN, 32774, 4, NULL, S_ZCAULDRON7, 0, 0}, // S_ZCAULDRON6 {SPR_CDRN, 32775, 4, NULL, S_ZCAULDRON1, 0, 0}, // S_ZCAULDRON7 {SPR_CDRN, 0, -1, NULL, S_NULL, 0, 0}, // S_ZCAULDRON_U {SPR_CHNS, 0, -1, NULL, S_NULL, 0, 0}, // S_ZCHAINBIT32 {SPR_CHNS, 1, -1, NULL, S_NULL, 0, 0}, // S_ZCHAINBIT64 {SPR_CHNS, 2, -1, NULL, S_NULL, 0, 0}, // S_ZCHAINEND_HEART {SPR_CHNS, 3, -1, NULL, S_NULL, 0, 0}, // S_ZCHAINEND_HOOK1 {SPR_CHNS, 4, -1, NULL, S_NULL, 0, 0}, // S_ZCHAINEND_HOOK2 {SPR_CHNS, 5, -1, NULL, S_NULL, 0, 0}, // S_ZCHAINEND_SPIKE {SPR_CHNS, 6, -1, NULL, S_NULL, 0, 0}, // S_ZCHAINEND_SKULL {SPR_TST1, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT1 {SPR_TST2, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT2 {SPR_TST3, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT3 {SPR_TST4, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT4 {SPR_TST5, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT5 {SPR_TST6, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT6 {SPR_TST7, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT7 {SPR_TST8, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT8 {SPR_TST9, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT9 {SPR_TST0, 0, -1, NULL, S_NULL, 0, 0}, // S_TABLE_SHIT10 {SPR_TELE, 32768, 6, NULL, S_TFOG2, 0, 0}, // S_TFOG1 {SPR_TELE, 32769, 6, NULL, S_TFOG3, 0, 0}, // S_TFOG2 {SPR_TELE, 32770, 6, NULL, S_TFOG4, 0, 0}, // S_TFOG3 {SPR_TELE, 32771, 6, NULL, S_TFOG5, 0, 0}, // S_TFOG4 {SPR_TELE, 32772, 6, NULL, S_TFOG6, 0, 0}, // S_TFOG5 {SPR_TELE, 32773, 6, NULL, S_TFOG7, 0, 0}, // S_TFOG6 {SPR_TELE, 32774, 6, NULL, S_TFOG8, 0, 0}, // S_TFOG7 {SPR_TELE, 32775, 6, NULL, S_TFOG9, 0, 0}, // S_TFOG8 {SPR_TELE, 32774, 6, NULL, S_TFOG10, 0, 0}, // S_TFOG9 {SPR_TELE, 32773, 6, NULL, S_TFOG11, 0, 0}, // S_TFOG10 {SPR_TELE, 32772, 6, NULL, S_TFOG12, 0, 0}, // S_TFOG11 {SPR_TELE, 32771, 6, NULL, S_TFOG13, 0, 0}, // S_TFOG12 {SPR_TELE, 32770, 6, NULL, S_NULL, 0, 0}, // S_TFOG13 {SPR_TSMK, 0, 4, NULL, S_TELESMOKE2, 0, 0}, // S_TELESMOKE1 {SPR_TSMK, 1, 3, NULL, S_TELESMOKE3, 0, 0}, // S_TELESMOKE2 {SPR_TSMK, 2, 4, NULL, S_TELESMOKE4, 0, 0}, // S_TELESMOKE3 {SPR_TSMK, 3, 3, NULL, S_TELESMOKE5, 0, 0}, // S_TELESMOKE4 {SPR_TSMK, 4, 4, NULL, S_TELESMOKE6, 0, 0}, // S_TELESMOKE5 {SPR_TSMK, 5, 3, NULL, S_TELESMOKE7, 0, 0}, // S_TELESMOKE6 {SPR_TSMK, 6, 4, NULL, S_TELESMOKE8, 0, 0}, // S_TELESMOKE7 {SPR_TSMK, 7, 3, NULL, S_TELESMOKE9, 0, 0}, // S_TELESMOKE8 {SPR_TSMK, 8, 4, NULL, S_TELESMOKE10, 0, 0}, // S_TELESMOKE9 {SPR_TSMK, 9, 3, NULL, S_TELESMOKE11, 0, 0}, // S_TELESMOKE10 {SPR_TSMK, 10, 4, NULL, S_TELESMOKE12, 0, 0}, // S_TELESMOKE11 {SPR_TSMK, 11, 3, NULL, S_TELESMOKE13, 0, 0}, // S_TELESMOKE12 {SPR_TSMK, 12, 4, NULL, S_TELESMOKE14, 0, 0}, // S_TELESMOKE13 {SPR_TSMK, 13, 3, NULL, S_TELESMOKE15, 0, 0}, // S_TELESMOKE14 {SPR_TSMK, 14, 4, NULL, S_TELESMOKE16, 0, 0}, // S_TELESMOKE15 {SPR_TSMK, 15, 3, NULL, S_TELESMOKE17, 0, 0}, // S_TELESMOKE16 {SPR_TSMK, 16, 4, NULL, S_TELESMOKE18, 0, 0}, // S_TELESMOKE17 {SPR_TSMK, 17, 3, NULL, S_TELESMOKE19, 0, 0}, // S_TELESMOKE18 {SPR_TSMK, 18, 4, NULL, S_TELESMOKE20, 0, 0}, // S_TELESMOKE19 {SPR_TSMK, 19, 3, NULL, S_TELESMOKE21, 0, 0}, // S_TELESMOKE20 {SPR_TSMK, 20, 4, NULL, S_TELESMOKE22, 0, 0}, // S_TELESMOKE21 {SPR_TSMK, 21, 3, NULL, S_TELESMOKE23, 0, 0}, // S_TELESMOKE22 {SPR_TSMK, 22, 4, NULL, S_TELESMOKE24, 0, 0}, // S_TELESMOKE23 {SPR_TSMK, 23, 3, NULL, S_TELESMOKE25, 0, 0}, // S_TELESMOKE24 {SPR_TSMK, 24, 4, NULL, S_TELESMOKE26, 0, 0}, // S_TELESMOKE25 {SPR_TSMK, 25, 3, NULL, S_TELESMOKE1, 0, 0}, // S_TELESMOKE26 {SPR_FPCH, 0, 0, A_Light0, S_NULL, 0, 0}, // S_LIGHTDONE {SPR_FPCH, 0, 1, A_WeaponReady, S_PUNCHREADY, 0, 0}, // S_PUNCHREADY {SPR_FPCH, 0, 1, A_Lower, S_PUNCHDOWN, 0, 0}, // S_PUNCHDOWN {SPR_FPCH, 0, 1, A_Raise, S_PUNCHUP, 0, 0}, // S_PUNCHUP {SPR_FPCH, 1, 5, NULL, S_PUNCHATK1_2, 5, 40}, // S_PUNCHATK1_1 {SPR_FPCH, 2, 4, NULL, S_PUNCHATK1_3, 5, 40}, // S_PUNCHATK1_2 {SPR_FPCH, 3, 4, A_FPunchAttack, S_PUNCHATK1_4, 5, 40}, // S_PUNCHATK1_3 {SPR_FPCH, 2, 4, NULL, S_PUNCHATK1_5, 5, 40}, // S_PUNCHATK1_4 {SPR_FPCH, 1, 5, A_ReFire, S_PUNCHREADY, 5, 40}, // S_PUNCHATK1_5 {SPR_FPCH, 3, 4, NULL, S_PUNCHATK2_2, 5, 40}, // S_PUNCHATK2_1 {SPR_FPCH, 4, 4, NULL, S_PUNCHATK2_3, 5, 40}, // S_PUNCHATK2_2 {SPR_FPCH, 4, 1, NULL, S_PUNCHATK2_4, 15, 50}, // S_PUNCHATK2_3 {SPR_FPCH, 4, 1, NULL, S_PUNCHATK2_5, 25, 60}, // S_PUNCHATK2_4 {SPR_FPCH, 4, 1, NULL, S_PUNCHATK2_6, 35, 70}, // S_PUNCHATK2_5 {SPR_FPCH, 4, 1, NULL, S_PUNCHATK2_7, 45, 80}, // S_PUNCHATK2_6 {SPR_FPCH, 4, 1, NULL, S_PUNCHATK2_8, 55, 90}, // S_PUNCHATK2_7 {SPR_FPCH, 4, 1, NULL, S_PUNCHATK2_9, 65, 100}, // S_PUNCHATK2_8 {SPR_FPCH, 4, 10, NULL, S_PUNCHREADY, 0, 150}, // S_PUNCHATK2_9 {SPR_FHFX, 18, 4, NULL, S_PUNCHPUFF2, 0, 0}, // S_PUNCHPUFF1 {SPR_FHFX, 19, 4, NULL, S_PUNCHPUFF3, 0, 0}, // S_PUNCHPUFF2 {SPR_FHFX, 20, 4, NULL, S_PUNCHPUFF4, 0, 0}, // S_PUNCHPUFF3 {SPR_FHFX, 21, 4, NULL, S_PUNCHPUFF5, 0, 0}, // S_PUNCHPUFF4 {SPR_FHFX, 22, 4, NULL, S_NULL, 0, 0}, // S_PUNCHPUFF5 {SPR_WFAX, 0, -1, NULL, S_NULL, 0, 0}, // S_AXE {SPR_FAXE, 0, 1, A_WeaponReady, S_FAXEREADY, 0, 0}, // S_FAXEREADY {SPR_FAXE, 0, 1, A_Lower, S_FAXEDOWN, 0, 0}, // S_FAXEDOWN {SPR_FAXE, 0, 1, A_Raise, S_FAXEUP, 0, 0}, // S_FAXEUP {SPR_FAXE, 1, 4, NULL, S_FAXEATK_2, 15, 32}, // S_FAXEATK_1 {SPR_FAXE, 2, 3, NULL, S_FAXEATK_3, 15, 32}, // S_FAXEATK_2 {SPR_FAXE, 3, 2, NULL, S_FAXEATK_4, 15, 32}, // S_FAXEATK_3 {SPR_FAXE, 3, 1, A_FAxeAttack, S_FAXEATK_5, -5, 70}, // S_FAXEATK_4 {SPR_FAXE, 3, 2, NULL, S_FAXEATK_6, -25, 90}, // S_FAXEATK_5 {SPR_FAXE, 4, 1, NULL, S_FAXEATK_7, 15, 32}, // S_FAXEATK_6 {SPR_FAXE, 4, 2, NULL, S_FAXEATK_8, 10, 54}, // S_FAXEATK_7 {SPR_FAXE, 4, 7, NULL, S_FAXEATK_9, 10, 150}, // S_FAXEATK_8 {SPR_FAXE, 0, 1, A_ReFire, S_FAXEATK_10, 0, 60}, // S_FAXEATK_9 {SPR_FAXE, 0, 1, NULL, S_FAXEATK_11, 0, 52}, // S_FAXEATK_10 {SPR_FAXE, 0, 1, NULL, S_FAXEATK_12, 0, 44}, // S_FAXEATK_11 {SPR_FAXE, 0, 1, NULL, S_FAXEATK_13, 0, 36}, // S_FAXEATK_12 {SPR_FAXE, 0, 1, NULL, S_FAXEREADY, 0, 0}, // S_FAXEATK_13 {SPR_FAXE, 11, 1, A_WeaponReady, S_FAXEREADY_G1, 0, 0}, // S_FAXEREADY_G {SPR_FAXE, 11, 1, A_WeaponReady, S_FAXEREADY_G2, 0, 0}, // S_FAXEREADY_G1 {SPR_FAXE, 11, 1, A_WeaponReady, S_FAXEREADY_G3, 0, 0}, // S_FAXEREADY_G2 {SPR_FAXE, 12, 1, A_WeaponReady, S_FAXEREADY_G4, 0, 0}, // S_FAXEREADY_G3 {SPR_FAXE, 12, 1, A_WeaponReady, S_FAXEREADY_G5, 0, 0}, // S_FAXEREADY_G4 {SPR_FAXE, 12, 1, A_WeaponReady, S_FAXEREADY_G, 0, 0}, // S_FAXEREADY_G5 {SPR_FAXE, 11, 1, A_Lower, S_FAXEDOWN_G, 0, 0}, // S_FAXEDOWN_G {SPR_FAXE, 11, 1, A_Raise, S_FAXEUP_G, 0, 0}, // S_FAXEUP_G {SPR_FAXE, 13, 4, NULL, S_FAXEATK_G2, 15, 32}, // S_FAXEATK_G1 {SPR_FAXE, 14, 3, NULL, S_FAXEATK_G3, 15, 32}, // S_FAXEATK_G2 {SPR_FAXE, 15, 2, NULL, S_FAXEATK_G4, 15, 32}, // S_FAXEATK_G3 {SPR_FAXE, 15, 1, A_FAxeAttack, S_FAXEATK_G5, -5, 70}, // S_FAXEATK_G4 {SPR_FAXE, 15, 2, NULL, S_FAXEATK_G6, -25, 90}, // S_FAXEATK_G5 {SPR_FAXE, 16, 1, NULL, S_FAXEATK_G7, 15, 32}, // S_FAXEATK_G6 {SPR_FAXE, 16, 2, NULL, S_FAXEATK_G8, 10, 54}, // S_FAXEATK_G7 {SPR_FAXE, 16, 7, NULL, S_FAXEATK_G9, 10, 150}, // S_FAXEATK_G8 {SPR_FAXE, 0, 1, A_ReFire, S_FAXEATK_G10, 0, 60}, // S_FAXEATK_G9 {SPR_FAXE, 0, 1, NULL, S_FAXEATK_G11, 0, 52}, // S_FAXEATK_G10 {SPR_FAXE, 0, 1, NULL, S_FAXEATK_G12, 0, 44}, // S_FAXEATK_G11 {SPR_FAXE, 0, 1, NULL, S_FAXEATK_G13, 0, 36}, // S_FAXEATK_G12 {SPR_FAXE, 0, 1, NULL, S_FAXEREADY_G, 0, 0}, // S_FAXEATK_G13 {SPR_FAXE, 32785, 4, NULL, S_AXEPUFF_GLOW2, 0, 0}, // S_AXEPUFF_GLOW1 {SPR_FAXE, 32786, 4, NULL, S_AXEPUFF_GLOW3, 0, 0}, // S_AXEPUFF_GLOW2 {SPR_FAXE, 32787, 4, NULL, S_AXEPUFF_GLOW4, 0, 0}, // S_AXEPUFF_GLOW3 {SPR_FAXE, 32788, 4, NULL, S_AXEPUFF_GLOW5, 0, 0}, // S_AXEPUFF_GLOW4 {SPR_FAXE, 32789, 4, NULL, S_AXEPUFF_GLOW6, 0, 0}, // S_AXEPUFF_GLOW5 {SPR_FAXE, 32790, 4, NULL, S_AXEPUFF_GLOW7, 0, 0}, // S_AXEPUFF_GLOW6 {SPR_FAXE, 32791, 4, NULL, S_NULL, 0, 0}, // S_AXEPUFF_GLOW7 {SPR_FAXE, 5, 3, NULL, S_AXEBLOOD2, 0, 0}, // S_AXEBLOOD1 {SPR_FAXE, 6, 3, NULL, S_AXEBLOOD3, 0, 0}, // S_AXEBLOOD2 {SPR_FAXE, 7, 3, NULL, S_AXEBLOOD4, 0, 0}, // S_AXEBLOOD3 {SPR_FAXE, 8, 3, NULL, S_AXEBLOOD5, 0, 0}, // S_AXEBLOOD4 {SPR_FAXE, 9, 3, NULL, S_AXEBLOOD6, 0, 0}, // S_AXEBLOOD5 {SPR_FAXE, 10, 3, NULL, S_NULL, 0, 0}, // S_AXEBLOOD6 {SPR_WFHM, 0, -1, NULL, S_NULL, 0, 0}, // S_HAMM {SPR_FHMR, 0, 1, A_WeaponReady, S_FHAMMERREADY, 0, 0}, // S_FHAMMERREADY {SPR_FHMR, 0, 1, A_Lower, S_FHAMMERDOWN, 0, 0}, // S_FHAMMERDOWN {SPR_FHMR, 0, 1, A_Raise, S_FHAMMERUP, 0, 0}, // S_FHAMMERUP {SPR_FHMR, 1, 6, NULL, S_FHAMMERATK_2, 5, 0}, // S_FHAMMERATK_1 {SPR_FHMR, 2, 3, A_FHammerAttack, S_FHAMMERATK_3, 5, 0}, // S_FHAMMERATK_2 {SPR_FHMR, 3, 3, NULL, S_FHAMMERATK_4, 5, 0}, // S_FHAMMERATK_3 {SPR_FHMR, 4, 2, NULL, S_FHAMMERATK_5, 5, 0}, // S_FHAMMERATK_4 {SPR_FHMR, 4, 10, A_FHammerThrow, S_FHAMMERATK_6, 5, 150}, // S_FHAMMERATK_5 {SPR_FHMR, 0, 1, NULL, S_FHAMMERATK_7, 0, 60}, // S_FHAMMERATK_6 {SPR_FHMR, 0, 1, NULL, S_FHAMMERATK_8, 0, 55}, // S_FHAMMERATK_7 {SPR_FHMR, 0, 1, NULL, S_FHAMMERATK_9, 0, 50}, // S_FHAMMERATK_8 {SPR_FHMR, 0, 1, NULL, S_FHAMMERATK_10, 0, 45}, // S_FHAMMERATK_9 {SPR_FHMR, 0, 1, NULL, S_FHAMMERATK_11, 0, 40}, // S_FHAMMERATK_10 {SPR_FHMR, 0, 1, NULL, S_FHAMMERATK_12, 0, 35}, // S_FHAMMERATK_11 {SPR_FHMR, 0, 1, NULL, S_FHAMMERREADY, 0, 0}, // S_FHAMMERATK_12 {SPR_FHFX, 32768, 2, NULL, S_HAMMER_MISSILE_2, 0, 0}, // S_HAMMER_MISSILE_1 {SPR_FHFX, 32769, 2, A_ContMobjSound, S_HAMMER_MISSILE_3, 0, 0}, // S_HAMMER_MISSILE_2 {SPR_FHFX, 32770, 2, NULL, S_HAMMER_MISSILE_4, 0, 0}, // S_HAMMER_MISSILE_3 {SPR_FHFX, 32771, 2, NULL, S_HAMMER_MISSILE_5, 0, 0}, // S_HAMMER_MISSILE_4 {SPR_FHFX, 32772, 2, NULL, S_HAMMER_MISSILE_6, 0, 0}, // S_HAMMER_MISSILE_5 {SPR_FHFX, 32773, 2, NULL, S_HAMMER_MISSILE_7, 0, 0}, // S_HAMMER_MISSILE_6 {SPR_FHFX, 32774, 2, NULL, S_HAMMER_MISSILE_8, 0, 0}, // S_HAMMER_MISSILE_7 {SPR_FHFX, 32775, 2, NULL, S_HAMMER_MISSILE_1, 0, 0}, // S_HAMMER_MISSILE_8 {SPR_FHFX, 32776, 3, NULL, S_HAMMER_MISSILE_X2, 0, 0}, // S_HAMMER_MISSILE_X1 {SPR_FHFX, 32777, 3, NULL, S_HAMMER_MISSILE_X3, 0, 0}, // S_HAMMER_MISSILE_X2 {SPR_FHFX, 32778, 3, A_Explode, S_HAMMER_MISSILE_X4, 0, 0}, // S_HAMMER_MISSILE_X3 {SPR_FHFX, 32779, 3, NULL, S_HAMMER_MISSILE_X5, 0, 0}, // S_HAMMER_MISSILE_X4 {SPR_FHFX, 32780, 3, NULL, S_HAMMER_MISSILE_X6, 0, 0}, // S_HAMMER_MISSILE_X5 {SPR_FHFX, 13, 3, NULL, S_HAMMER_MISSILE_X7, 0, 0}, // S_HAMMER_MISSILE_X6 {SPR_FHFX, 32782, 3, NULL, S_HAMMER_MISSILE_X8, 0, 0}, // S_HAMMER_MISSILE_X7 {SPR_FHFX, 32783, 3, NULL, S_HAMMER_MISSILE_X9, 0, 0}, // S_HAMMER_MISSILE_X8 {SPR_FHFX, 32784, 3, NULL, S_HAMMER_MISSILE_X10, 0, 0}, // S_HAMMER_MISSILE_X9 {SPR_FHFX, 32785, 3, NULL, S_NULL, 0, 0}, // S_HAMMER_MISSILE_X10 {SPR_FHFX, 18, 4, NULL, S_HAMMERPUFF2, 0, 0}, // S_HAMMERPUFF1 {SPR_FHFX, 19, 4, NULL, S_HAMMERPUFF3, 0, 0}, // S_HAMMERPUFF2 {SPR_FHFX, 20, 4, NULL, S_HAMMERPUFF4, 0, 0}, // S_HAMMERPUFF3 {SPR_FHFX, 21, 4, NULL, S_HAMMERPUFF5, 0, 0}, // S_HAMMERPUFF4 {SPR_FHFX, 22, 4, NULL, S_NULL, 0, 0}, // S_HAMMERPUFF5 {SPR_FSRD, 32768, 1, A_WeaponReady, S_FSWORDREADY1, 0, 0}, // S_FSWORDREADY {SPR_FSRD, 32768, 1, A_WeaponReady, S_FSWORDREADY2, 0, 0}, // S_FSWORDREADY1 {SPR_FSRD, 32768, 1, A_WeaponReady, S_FSWORDREADY3, 0, 0}, // S_FSWORDREADY2 {SPR_FSRD, 32768, 1, A_WeaponReady, S_FSWORDREADY4, 0, 0}, // S_FSWORDREADY3 {SPR_FSRD, 32769, 1, A_WeaponReady, S_FSWORDREADY5, 0, 0}, // S_FSWORDREADY4 {SPR_FSRD, 32769, 1, A_WeaponReady, S_FSWORDREADY6, 0, 0}, // S_FSWORDREADY5 {SPR_FSRD, 32769, 1, A_WeaponReady, S_FSWORDREADY7, 0, 0}, // S_FSWORDREADY6 {SPR_FSRD, 32769, 1, A_WeaponReady, S_FSWORDREADY8, 0, 0}, // S_FSWORDREADY7 {SPR_FSRD, 32770, 1, A_WeaponReady, S_FSWORDREADY9, 0, 0}, // S_FSWORDREADY8 {SPR_FSRD, 32770, 1, A_WeaponReady, S_FSWORDREADY10, 0, 0}, // S_FSWORDREADY9 {SPR_FSRD, 32770, 1, A_WeaponReady, S_FSWORDREADY11, 0, 0}, // S_FSWORDREADY10 {SPR_FSRD, 32770, 1, A_WeaponReady, S_FSWORDREADY, 0, 0}, // S_FSWORDREADY11 {SPR_FSRD, 32768, 1, A_Lower, S_FSWORDDOWN, 0, 0}, // S_FSWORDDOWN {SPR_FSRD, 32768, 1, A_Raise, S_FSWORDUP, 0, 0}, // S_FSWORDUP {SPR_FSRD, 32771, 3, NULL, S_FSWORDATK_2, 5, 36}, // S_FSWORDATK_1 {SPR_FSRD, 32772, 3, NULL, S_FSWORDATK_3, 5, 36}, // S_FSWORDATK_2 {SPR_FSRD, 32773, 2, NULL, S_FSWORDATK_4, 5, 36}, // S_FSWORDATK_3 {SPR_FSRD, 32774, 3, A_FSwordAttack, S_FSWORDATK_5, 5, 36}, // S_FSWORDATK_4 {SPR_FSRD, 32775, 2, NULL, S_FSWORDATK_6, 5, 36}, // S_FSWORDATK_5 {SPR_FSRD, 32776, 2, NULL, S_FSWORDATK_7, 5, 36}, // S_FSWORDATK_6 {SPR_FSRD, 32776, 10, NULL, S_FSWORDATK_8, 5, 150}, // S_FSWORDATK_7 {SPR_FSRD, 32768, 1, NULL, S_FSWORDATK_9, 5, 60}, // S_FSWORDATK_8 {SPR_FSRD, 32769, 1, NULL, S_FSWORDATK_10, 5, 55}, // S_FSWORDATK_9 {SPR_FSRD, 32770, 1, NULL, S_FSWORDATK_11, 5, 50}, // S_FSWORDATK_10 {SPR_FSRD, 32768, 1, NULL, S_FSWORDATK_12, 5, 45}, // S_FSWORDATK_11 {SPR_FSRD, 32769, 1, NULL, S_FSWORDREADY, 5, 40}, // S_FSWORDATK_12 {SPR_FSFX, 32768, 3, NULL, S_FSWORD_MISSILE2, 0, 0}, // S_FSWORD_MISSILE1 {SPR_FSFX, 32769, 3, NULL, S_FSWORD_MISSILE3, 0, 0}, // S_FSWORD_MISSILE2 {SPR_FSFX, 32770, 3, NULL, S_FSWORD_MISSILE1, 0, 0}, // S_FSWORD_MISSILE3 {SPR_FSFX, 32771, 4, NULL, S_FSWORD_MISSILE_X2, 0, 0}, // S_FSWORD_MISSILE_X1 {SPR_FSFX, 32772, 3, A_FSwordFlames, S_FSWORD_MISSILE_X3, 0, 0}, // S_FSWORD_MISSILE_X2 {SPR_FSFX, 32773, 4, A_Explode, S_FSWORD_MISSILE_X4, 0, 0}, // S_FSWORD_MISSILE_X3 {SPR_FSFX, 32774, 3, NULL, S_FSWORD_MISSILE_X5, 0, 0}, // S_FSWORD_MISSILE_X4 {SPR_FSFX, 32775, 4, NULL, S_FSWORD_MISSILE_X6, 0, 0}, // S_FSWORD_MISSILE_X5 {SPR_FSFX, 32776, 3, NULL, S_FSWORD_MISSILE_X7, 0, 0}, // S_FSWORD_MISSILE_X6 {SPR_FSFX, 32777, 4, NULL, S_FSWORD_MISSILE_X8, 0, 0}, // S_FSWORD_MISSILE_X7 {SPR_FSFX, 32778, 3, NULL, S_FSWORD_MISSILE_X9, 0, 0}, // S_FSWORD_MISSILE_X8 {SPR_FSFX, 32779, 3, NULL, S_FSWORD_MISSILE_X10, 0, 0}, // S_FSWORD_MISSILE_X9 {SPR_FSFX, 32780, 3, NULL, S_NULL, 0, 0}, // S_FSWORD_MISSILE_X10 {SPR_FSFX, 32781, 3, NULL, S_FSWORD_FLAME2, 0, 0}, // S_FSWORD_FLAME1 {SPR_FSFX, 32782, 3, NULL, S_FSWORD_FLAME3, 0, 0}, // S_FSWORD_FLAME2 {SPR_FSFX, 32783, 3, NULL, S_FSWORD_FLAME4, 0, 0}, // S_FSWORD_FLAME3 {SPR_FSFX, 32784, 3, NULL, S_FSWORD_FLAME5, 0, 0}, // S_FSWORD_FLAME4 {SPR_FSFX, 32785, 3, NULL, S_FSWORD_FLAME6, 0, 0}, // S_FSWORD_FLAME5 {SPR_FSFX, 32786, 3, NULL, S_FSWORD_FLAME7, 0, 0}, // S_FSWORD_FLAME6 {SPR_FSFX, 32787, 3, NULL, S_FSWORD_FLAME8, 0, 0}, // S_FSWORD_FLAME7 {SPR_FSFX, 32788, 3, NULL, S_FSWORD_FLAME9, 0, 0}, // S_FSWORD_FLAME8 {SPR_FSFX, 32789, 3, NULL, S_FSWORD_FLAME10, 0, 0}, // S_FSWORD_FLAME9 {SPR_FSFX, 32790, 3, NULL, S_NULL, 0, 0}, // S_FSWORD_FLAME10 {SPR_CMCE, 0, 1, A_WeaponReady, S_CMACEREADY, 0, 0}, // S_CMACEREADY {SPR_CMCE, 0, 1, A_Lower, S_CMACEDOWN, 0, 0}, // S_CMACEDOWN {SPR_CMCE, 0, 1, A_Raise, S_CMACEUP, 0, 0}, // S_CMACEUP {SPR_CMCE, 1, 2, NULL, S_CMACEATK_2, 60, 20}, // S_CMACEATK_1 {SPR_CMCE, 1, 1, NULL, S_CMACEATK_3, 30, 33}, // S_CMACEATK_2 {SPR_CMCE, 1, 2, NULL, S_CMACEATK_4, 8, 45}, // S_CMACEATK_3 {SPR_CMCE, 2, 1, NULL, S_CMACEATK_5, 8, 45}, // S_CMACEATK_4 {SPR_CMCE, 3, 1, NULL, S_CMACEATK_6, 8, 45}, // S_CMACEATK_5 {SPR_CMCE, 4, 1, NULL, S_CMACEATK_7, 8, 45}, // S_CMACEATK_6 {SPR_CMCE, 4, 1, A_CMaceAttack, S_CMACEATK_8, -11, 58}, // S_CMACEATK_7 {SPR_CMCE, 5, 1, NULL, S_CMACEATK_9, 8, 45}, // S_CMACEATK_8 {SPR_CMCE, 5, 2, NULL, S_CMACEATK_10, -8, 74}, // S_CMACEATK_9 {SPR_CMCE, 5, 1, NULL, S_CMACEATK_11, -20, 96}, // S_CMACEATK_10 {SPR_CMCE, 5, 8, NULL, S_CMACEATK_12, -33, 160}, // S_CMACEATK_11 {SPR_CMCE, 0, 2, A_ReFire, S_CMACEATK_13, 8, 75}, // S_CMACEATK_12 {SPR_CMCE, 0, 1, NULL, S_CMACEATK_14, 8, 65}, // S_CMACEATK_13 {SPR_CMCE, 0, 2, NULL, S_CMACEATK_15, 8, 60}, // S_CMACEATK_14 {SPR_CMCE, 0, 1, NULL, S_CMACEATK_16, 8, 55}, // S_CMACEATK_15 {SPR_CMCE, 0, 2, NULL, S_CMACEATK_17, 8, 50}, // S_CMACEATK_16 {SPR_CMCE, 0, 1, NULL, S_CMACEREADY, 8, 45}, // S_CMACEATK_17 {SPR_WCSS, 0, -1, NULL, S_NULL, 0, 0}, // S_CSTAFF {SPR_CSSF, 2, 4, NULL, S_CSTAFFREADY1, 0, 0}, // S_CSTAFFREADY {SPR_CSSF, 1, 3, A_CStaffInitBlink, S_CSTAFFREADY2, 0, 0}, // S_CSTAFFREADY1 {SPR_CSSF, 0, 1, A_WeaponReady, S_CSTAFFREADY3, 0, 0}, // S_CSTAFFREADY2 {SPR_CSSF, 0, 1, A_WeaponReady, S_CSTAFFREADY4, 0, 0}, // S_CSTAFFREADY3 {SPR_CSSF, 0, 1, A_WeaponReady, S_CSTAFFREADY5, 0, 0}, // S_CSTAFFREADY4 {SPR_CSSF, 0, 1, A_WeaponReady, S_CSTAFFREADY6, 0, 0}, // S_CSTAFFREADY5 {SPR_CSSF, 0, 1, A_WeaponReady, S_CSTAFFREADY7, 0, 0}, // S_CSTAFFREADY6 {SPR_CSSF, 0, 1, A_WeaponReady, S_CSTAFFREADY8, 0, 0}, // S_CSTAFFREADY7 {SPR_CSSF, 0, 1, A_WeaponReady, S_CSTAFFREADY9, 0, 0}, // S_CSTAFFREADY8 {SPR_CSSF, 0, 1, A_CStaffCheckBlink, S_CSTAFFREADY2, 0, 0}, // S_CSTAFFREADY9 {SPR_CSSF, 1, 1, A_WeaponReady, S_CSTAFFBLINK2, 0, 0}, // S_CSTAFFBLINK1 {SPR_CSSF, 1, 1, A_WeaponReady, S_CSTAFFBLINK3, 0, 0}, // S_CSTAFFBLINK2 {SPR_CSSF, 1, 1, A_WeaponReady, S_CSTAFFBLINK4, 0, 0}, // S_CSTAFFBLINK3 {SPR_CSSF, 2, 1, A_WeaponReady, S_CSTAFFBLINK5, 0, 0}, // S_CSTAFFBLINK4 {SPR_CSSF, 2, 1, A_WeaponReady, S_CSTAFFBLINK6, 0, 0}, // S_CSTAFFBLINK5 {SPR_CSSF, 2, 1, A_WeaponReady, S_CSTAFFBLINK7, 0, 0}, // S_CSTAFFBLINK6 {SPR_CSSF, 2, 1, A_WeaponReady, S_CSTAFFBLINK8, 0, 0}, // S_CSTAFFBLINK7 {SPR_CSSF, 2, 1, A_WeaponReady, S_CSTAFFBLINK9, 0, 0}, // S_CSTAFFBLINK8 {SPR_CSSF, 1, 1, A_WeaponReady, S_CSTAFFBLINK10, 0, 0}, // S_CSTAFFBLINK9 {SPR_CSSF, 1, 1, A_WeaponReady, S_CSTAFFBLINK11, 0, 0}, // S_CSTAFFBLINK10 {SPR_CSSF, 1, 1, A_WeaponReady, S_CSTAFFREADY2, 0, 0}, // S_CSTAFFBLINK11 {SPR_CSSF, 1, 3, NULL, S_CSTAFFDOWN2, 0, 0}, // S_CSTAFFDOWN {SPR_CSSF, 2, 4, NULL, S_CSTAFFDOWN3, 0, 0}, // S_CSTAFFDOWN2 {SPR_CSSF, 2, 1, A_Lower, S_CSTAFFDOWN3, 0, 0}, // S_CSTAFFDOWN3 {SPR_CSSF, 2, 1, A_Raise, S_CSTAFFUP, 0, 0}, // S_CSTAFFUP {SPR_CSSF, 0, 1, A_CStaffCheck, S_CSTAFFATK_2, 0, 45}, // S_CSTAFFATK_1 {SPR_CSSF, 9, 1, A_CStaffAttack, S_CSTAFFATK_3, 0, 50}, // S_CSTAFFATK_2 {SPR_CSSF, 9, 2, NULL, S_CSTAFFATK_4, 0, 50}, // S_CSTAFFATK_3 {SPR_CSSF, 9, 2, NULL, S_CSTAFFATK_5, 0, 45}, // S_CSTAFFATK_4 {SPR_CSSF, 0, 2, NULL, S_CSTAFFATK_6, 0, 40}, // S_CSTAFFATK_5 {SPR_CSSF, 0, 2, NULL, S_CSTAFFREADY2, 0, 36}, // S_CSTAFFATK_6 {SPR_CSSF, 10, 10, NULL, S_CSTAFFREADY2, 0, 36}, // S_CSTAFFATK2_1 {SPR_CSSF, 32771, 1, A_CStaffMissileSlither, S_CSTAFF_MISSILE2, 0, 0}, // S_CSTAFF_MISSILE1 {SPR_CSSF, 32771, 1, A_CStaffMissileSlither, S_CSTAFF_MISSILE3, 0, 0}, // S_CSTAFF_MISSILE2 {SPR_CSSF, 32772, 1, A_CStaffMissileSlither, S_CSTAFF_MISSILE4, 0, 0}, // S_CSTAFF_MISSILE3 {SPR_CSSF, 32772, 1, A_CStaffMissileSlither, S_CSTAFF_MISSILE1, 0, 0}, // S_CSTAFF_MISSILE4 {SPR_CSSF, 32773, 4, NULL, S_CSTAFF_MISSILE_X2, 0, 0}, // S_CSTAFF_MISSILE_X1 {SPR_CSSF, 32774, 4, NULL, S_CSTAFF_MISSILE_X3, 0, 0}, // S_CSTAFF_MISSILE_X2 {SPR_CSSF, 32775, 3, NULL, S_CSTAFF_MISSILE_X4, 0, 0}, // S_CSTAFF_MISSILE_X3 {SPR_CSSF, 32776, 3, NULL, S_NULL, 0, 0}, // S_CSTAFF_MISSILE_X4 {SPR_FHFX, 18, 4, NULL, S_CSTAFFPUFF2, 0, 0}, // S_CSTAFFPUFF1 {SPR_FHFX, 19, 4, NULL, S_CSTAFFPUFF3, 0, 0}, // S_CSTAFFPUFF2 {SPR_FHFX, 20, 4, NULL, S_CSTAFFPUFF4, 0, 0}, // S_CSTAFFPUFF3 {SPR_FHFX, 21, 4, NULL, S_CSTAFFPUFF5, 0, 0}, // S_CSTAFFPUFF4 {SPR_FHFX, 22, 4, NULL, S_NULL, 0, 0}, // S_CSTAFFPUFF5 {SPR_WCFM, 32768, 4, NULL, S_CFLAME2, 0, 0}, // S_CFLAME1 {SPR_WCFM, 32769, 4, NULL, S_CFLAME3, 0, 0}, // S_CFLAME2 {SPR_WCFM, 32770, 4, NULL, S_CFLAME4, 0, 0}, // S_CFLAME3 {SPR_WCFM, 32771, 4, NULL, S_CFLAME5, 0, 0}, // S_CFLAME4 {SPR_WCFM, 32772, 4, NULL, S_CFLAME6, 0, 0}, // S_CFLAME5 {SPR_WCFM, 32773, 4, NULL, S_CFLAME7, 0, 0}, // S_CFLAME6 {SPR_WCFM, 32774, 4, NULL, S_CFLAME8, 0, 0}, // S_CFLAME7 {SPR_WCFM, 32775, 4, NULL, S_CFLAME1, 0, 0}, // S_CFLAME8 {SPR_CFLM, 0, 1, A_WeaponReady, S_CFLAMEREADY2, 0, 0}, // S_CFLAMEREADY1 {SPR_CFLM, 0, 1, A_WeaponReady, S_CFLAMEREADY3, 0, 0}, // S_CFLAMEREADY2 {SPR_CFLM, 0, 1, A_WeaponReady, S_CFLAMEREADY4, 0, 0}, // S_CFLAMEREADY3 {SPR_CFLM, 0, 1, A_WeaponReady, S_CFLAMEREADY5, 0, 0}, // S_CFLAMEREADY4 {SPR_CFLM, 1, 1, A_WeaponReady, S_CFLAMEREADY6, 0, 0}, // S_CFLAMEREADY5 {SPR_CFLM, 1, 1, A_WeaponReady, S_CFLAMEREADY7, 0, 0}, // S_CFLAMEREADY6 {SPR_CFLM, 1, 1, A_WeaponReady, S_CFLAMEREADY8, 0, 0}, // S_CFLAMEREADY7 {SPR_CFLM, 1, 1, A_WeaponReady, S_CFLAMEREADY9, 0, 0}, // S_CFLAMEREADY8 {SPR_CFLM, 2, 1, A_WeaponReady, S_CFLAMEREADY10, 0, 0}, // S_CFLAMEREADY9 {SPR_CFLM, 2, 1, A_WeaponReady, S_CFLAMEREADY11, 0, 0}, // S_CFLAMEREADY10 {SPR_CFLM, 2, 1, A_WeaponReady, S_CFLAMEREADY12, 0, 0}, // S_CFLAMEREADY11 {SPR_CFLM, 2, 1, A_WeaponReady, S_CFLAMEREADY1, 0, 0}, // S_CFLAMEREADY12 {SPR_CFLM, 0, 1, A_Lower, S_CFLAMEDOWN, 0, 0}, // S_CFLAMEDOWN {SPR_CFLM, 0, 1, A_Raise, S_CFLAMEUP, 0, 0}, // S_CFLAMEUP {SPR_CFLM, 0, 2, NULL, S_CFLAMEATK_2, 0, 40}, // S_CFLAMEATK_1 {SPR_CFLM, 3, 2, NULL, S_CFLAMEATK_3, 0, 50}, // S_CFLAMEATK_2 {SPR_CFLM, 3, 2, NULL, S_CFLAMEATK_4, 0, 36}, // S_CFLAMEATK_3 {SPR_CFLM, 32772, 4, NULL, S_CFLAMEATK_5, 0, 0}, // S_CFLAMEATK_4 {SPR_CFLM, 32773, 4, A_CFlameAttack, S_CFLAMEATK_6, 0, 0}, // S_CFLAMEATK_5 {SPR_CFLM, 32772, 4, NULL, S_CFLAMEATK_7, 0, 0}, // S_CFLAMEATK_6 {SPR_CFLM, 6, 2, NULL, S_CFLAMEATK_8, 0, 40}, // S_CFLAMEATK_7 {SPR_CFLM, 6, 2, NULL, S_CFLAMEREADY1, 0, 0}, // S_CFLAMEATK_8 {SPR_CFFX, 32781, 5, NULL, S_CFLAMEFLOOR2, 0, 0}, // S_CFLAMEFLOOR1 {SPR_CFFX, 32782, 4, NULL, S_CFLAMEFLOOR3, 0, 0}, // S_CFLAMEFLOOR2 {SPR_CFFX, 32783, 3, NULL, S_NULL, 0, 0}, // S_CFLAMEFLOOR3 {SPR_CFFX, 32768, 3, NULL, S_FLAMEPUFF2, 0, 0}, // S_FLAMEPUFF1 {SPR_CFFX, 32769, 3, NULL, S_FLAMEPUFF3, 0, 0}, // S_FLAMEPUFF2 {SPR_CFFX, 32770, 3, NULL, S_FLAMEPUFF4, 0, 0}, // S_FLAMEPUFF3 {SPR_CFFX, 32771, 4, NULL, S_FLAMEPUFF5, 0, 0}, // S_FLAMEPUFF4 {SPR_CFFX, 32772, 3, NULL, S_FLAMEPUFF6, 0, 0}, // S_FLAMEPUFF5 {SPR_CFFX, 32773, 4, NULL, S_FLAMEPUFF7, 0, 0}, // S_FLAMEPUFF6 {SPR_CFFX, 32774, 3, NULL, S_FLAMEPUFF8, 0, 0}, // S_FLAMEPUFF7 {SPR_CFFX, 32775, 4, NULL, S_FLAMEPUFF9, 0, 0}, // S_FLAMEPUFF8 {SPR_CFFX, 32776, 3, NULL, S_FLAMEPUFF10, 0, 0}, // S_FLAMEPUFF9 {SPR_CFFX, 32777, 4, NULL, S_FLAMEPUFF11, 0, 0}, // S_FLAMEPUFF10 {SPR_CFFX, 32778, 3, NULL, S_FLAMEPUFF12, 0, 0}, // S_FLAMEPUFF11 {SPR_CFFX, 32779, 4, NULL, S_FLAMEPUFF13, 0, 0}, // S_FLAMEPUFF12 {SPR_CFFX, 32780, 3, NULL, S_NULL, 0, 0}, // S_FLAMEPUFF13 {SPR_CFFX, 32768, 3, NULL, S_FLAMEPUFF2_2, 0, 0}, // S_FLAMEPUFF2_1 {SPR_CFFX, 32769, 3, NULL, S_FLAMEPUFF2_3, 0, 0}, // S_FLAMEPUFF2_2 {SPR_CFFX, 32770, 3, NULL, S_FLAMEPUFF2_4, 0, 0}, // S_FLAMEPUFF2_3 {SPR_CFFX, 32771, 4, NULL, S_FLAMEPUFF2_5, 0, 0}, // S_FLAMEPUFF2_4 {SPR_CFFX, 32772, 3, NULL, S_FLAMEPUFF2_6, 0, 0}, // S_FLAMEPUFF2_5 {SPR_CFFX, 32773, 4, NULL, S_FLAMEPUFF2_7, 0, 0}, // S_FLAMEPUFF2_6 {SPR_CFFX, 32774, 3, NULL, S_FLAMEPUFF2_8, 0, 0}, // S_FLAMEPUFF2_7 {SPR_CFFX, 32775, 4, NULL, S_FLAMEPUFF2_9, 0, 0}, // S_FLAMEPUFF2_8 {SPR_CFFX, 32776, 3, NULL, S_FLAMEPUFF2_10, 0, 0}, // S_FLAMEPUFF2_9 {SPR_CFFX, 32770, 3, NULL, S_FLAMEPUFF2_11, 0, 0}, // S_FLAMEPUFF2_10 {SPR_CFFX, 32771, 4, NULL, S_FLAMEPUFF2_12, 0, 0}, // S_FLAMEPUFF2_11 {SPR_CFFX, 32772, 3, NULL, S_FLAMEPUFF2_13, 0, 0}, // S_FLAMEPUFF2_12 {SPR_CFFX, 32773, 4, NULL, S_FLAMEPUFF2_14, 0, 0}, // S_FLAMEPUFF2_13 {SPR_CFFX, 32774, 3, NULL, S_FLAMEPUFF2_15, 0, 0}, // S_FLAMEPUFF2_14 {SPR_CFFX, 32775, 4, NULL, S_FLAMEPUFF2_16, 0, 0}, // S_FLAMEPUFF2_15 {SPR_CFFX, 32776, 3, NULL, S_FLAMEPUFF2_17, 0, 0}, // S_FLAMEPUFF2_16 {SPR_CFFX, 32777, 4, NULL, S_FLAMEPUFF2_18, 0, 0}, // S_FLAMEPUFF2_17 {SPR_CFFX, 32778, 3, NULL, S_FLAMEPUFF2_19, 0, 0}, // S_FLAMEPUFF2_18 {SPR_CFFX, 32779, 4, NULL, S_FLAMEPUFF2_20, 0, 0}, // S_FLAMEPUFF2_19 {SPR_CFFX, 32780, 3, NULL, S_NULL, 0, 0}, // S_FLAMEPUFF2_20 {SPR_CFCF, 32768, 4, NULL, S_CIRCLE_FLAME2, 0, 0}, // S_CIRCLE_FLAME1 {SPR_CFCF, 32769, 2, A_CFlameRotate, S_CIRCLE_FLAME3, 0, 0}, // S_CIRCLE_FLAME2 {SPR_CFCF, 32770, 2, NULL, S_CIRCLE_FLAME4, 0, 0}, // S_CIRCLE_FLAME3 {SPR_CFCF, 32771, 1, NULL, S_CIRCLE_FLAME5, 0, 0}, // S_CIRCLE_FLAME4 {SPR_CFCF, 32772, 2, NULL, S_CIRCLE_FLAME6, 0, 0}, // S_CIRCLE_FLAME5 {SPR_CFCF, 32773, 2, A_CFlameRotate, S_CIRCLE_FLAME7, 0, 0}, // S_CIRCLE_FLAME6 {SPR_CFCF, 32774, 1, NULL, S_CIRCLE_FLAME8, 0, 0}, // S_CIRCLE_FLAME7 {SPR_CFCF, 32775, 2, NULL, S_CIRCLE_FLAME9, 0, 0}, // S_CIRCLE_FLAME8 {SPR_CFCF, 32776, 2, NULL, S_CIRCLE_FLAME10, 0, 0}, // S_CIRCLE_FLAME9 {SPR_CFCF, 32777, 1, A_CFlameRotate, S_CIRCLE_FLAME11, 0, 0}, // S_CIRCLE_FLAME10 {SPR_CFCF, 32778, 2, NULL, S_CIRCLE_FLAME12, 0, 0}, // S_CIRCLE_FLAME11 {SPR_CFCF, 32779, 3, NULL, S_CIRCLE_FLAME13, 0, 0}, // S_CIRCLE_FLAME12 {SPR_CFCF, 32780, 3, NULL, S_CIRCLE_FLAME14, 0, 0}, // S_CIRCLE_FLAME13 {SPR_CFCF, 32781, 2, A_CFlameRotate, S_CIRCLE_FLAME15, 0, 0}, // S_CIRCLE_FLAME14 {SPR_CFCF, 32782, 3, NULL, S_CIRCLE_FLAME16, 0, 0}, // S_CIRCLE_FLAME15 {SPR_CFCF, 32783, 2, NULL, S_NULL, 0, 0}, // S_CIRCLE_FLAME16 {SPR_CFCF, 32784, 3, NULL, S_CIRCLE_FLAME_X2, 0, 0}, // S_CIRCLE_FLAME_X1 {SPR_CFCF, 32785, 3, NULL, S_CIRCLE_FLAME_X3, 0, 0}, // S_CIRCLE_FLAME_X2 {SPR_CFCF, 32786, 3, A_Explode, S_CIRCLE_FLAME_X4, 0, 0}, // S_CIRCLE_FLAME_X3 {SPR_CFCF, 32787, 3, NULL, S_CIRCLE_FLAME_X5, 0, 0}, // S_CIRCLE_FLAME_X4 {SPR_CFCF, 32788, 3, NULL, S_CIRCLE_FLAME_X6, 0, 0}, // S_CIRCLE_FLAME_X5 {SPR_CFCF, 32789, 3, NULL, S_CIRCLE_FLAME_X7, 0, 0}, // S_CIRCLE_FLAME_X6 {SPR_CFCF, 32790, 3, NULL, S_CIRCLE_FLAME_X8, 0, 0}, // S_CIRCLE_FLAME_X7 {SPR_CFCF, 32791, 3, NULL, S_CIRCLE_FLAME_X9, 0, 0}, // S_CIRCLE_FLAME_X8 {SPR_CFCF, 32792, 3, NULL, S_CIRCLE_FLAME_X10, 0, 0}, // S_CIRCLE_FLAME_X9 {SPR_CFCF, 32793, 3, NULL, S_NULL, 0, 0}, // S_CIRCLE_FLAME_X10 {SPR_CFFX, 32768, 4, NULL, S_CFLAME_MISSILE2, 0, 0}, // S_CFLAME_MISSILE1 {SPR_CFFX, 0, 1, A_CFlamePuff, S_FLAMEPUFF1, 0, 0}, // S_CFLAME_MISSILE2 {SPR_CFFX, 32768, 1, A_CFlameMissile, S_FLAMEPUFF1, 0, 0}, // S_CFLAME_MISSILE_X {SPR_CHLY, 0, 1, A_WeaponReady, S_CHOLYREADY, 0, 0}, // S_CHOLYREADY {SPR_CHLY, 0, 1, A_Lower, S_CHOLYDOWN, 0, 0}, // S_CHOLYDOWN {SPR_CHLY, 0, 1, A_Raise, S_CHOLYUP, 0, 0}, // S_CHOLYUP {SPR_CHLY, 32768, 1, NULL, S_CHOLYATK_2, 0, 40}, // S_CHOLYATK_1 {SPR_CHLY, 32769, 1, NULL, S_CHOLYATK_3, 0, 40}, // S_CHOLYATK_2 {SPR_CHLY, 32770, 2, NULL, S_CHOLYATK_4, 0, 43}, // S_CHOLYATK_3 {SPR_CHLY, 32771, 2, NULL, S_CHOLYATK_5, 0, 43}, // S_CHOLYATK_4 {SPR_CHLY, 32772, 2, NULL, S_CHOLYATK_6, 0, 45}, // S_CHOLYATK_5 {SPR_CHLY, 32773, 6, A_CHolyAttack, S_CHOLYATK_7, 0, 48}, // S_CHOLYATK_6 {SPR_CHLY, 32774, 2, A_CHolyPalette, S_CHOLYATK_8, 0, 40}, // S_CHOLYATK_7 {SPR_CHLY, 32774, 2, A_CHolyPalette, S_CHOLYATK_9, 0, 40}, // S_CHOLYATK_8 {SPR_CHLY, 32774, 2, A_CHolyPalette, S_CHOLYREADY, 0, 36}, // S_CHOLYATK_9 {SPR_SPIR, 0, 2, A_CHolySeek, S_HOLY_FX2, 0, 0}, // S_HOLY_FX1 {SPR_SPIR, 0, 2, A_CHolySeek, S_HOLY_FX3, 0, 0}, // S_HOLY_FX2 {SPR_SPIR, 1, 2, A_CHolySeek, S_HOLY_FX4, 0, 0}, // S_HOLY_FX3 {SPR_SPIR, 1, 2, A_CHolyCheckScream, S_HOLY_FX1, 0, 0}, // S_HOLY_FX4 {SPR_SPIR, 3, 4, NULL, S_HOLY_FX_X2, 0, 0}, // S_HOLY_FX_X1 {SPR_SPIR, 4, 4, A_Scream, S_HOLY_FX_X3, 0, 0}, // S_HOLY_FX_X2 {SPR_SPIR, 5, 4, NULL, S_HOLY_FX_X4, 0, 0}, // S_HOLY_FX_X3 {SPR_SPIR, 6, 4, NULL, S_HOLY_FX_X5, 0, 0}, // S_HOLY_FX_X4 {SPR_SPIR, 7, 4, NULL, S_HOLY_FX_X6, 0, 0}, // S_HOLY_FX_X5 {SPR_SPIR, 8, 4, NULL, S_NULL, 0, 0}, // S_HOLY_FX_X6 {SPR_SPIR, 2, 1, A_CHolyTail, S_HOLY_TAIL1, 0, 0}, // S_HOLY_TAIL1 {SPR_SPIR, 3, -1, NULL, S_NULL, 0, 0}, // S_HOLY_TAIL2 {SPR_SPIR, 10, 3, NULL, S_HOLY_PUFF2, 0, 0}, // S_HOLY_PUFF1 {SPR_SPIR, 11, 3, NULL, S_HOLY_PUFF3, 0, 0}, // S_HOLY_PUFF2 {SPR_SPIR, 12, 3, NULL, S_HOLY_PUFF4, 0, 0}, // S_HOLY_PUFF3 {SPR_SPIR, 13, 3, NULL, S_HOLY_PUFF5, 0, 0}, // S_HOLY_PUFF4 {SPR_SPIR, 14, 3, NULL, S_NULL, 0, 0}, // S_HOLY_PUFF5 {SPR_SPIR, 32783, 3, A_CHolySpawnPuff, S_HOLY_MISSILE2, 0, 0}, // S_HOLY_MISSILE1 {SPR_SPIR, 32783, 3, A_CHolySpawnPuff, S_HOLY_MISSILE3, 0, 0}, // S_HOLY_MISSILE2 {SPR_SPIR, 32783, 3, A_CHolySpawnPuff, S_HOLY_MISSILE4, 0, 0}, // S_HOLY_MISSILE3 {SPR_SPIR, 32783, 3, A_CHolySpawnPuff, S_HOLY_MISSILE_X, 0, 0}, // S_HOLY_MISSILE4 {SPR_SPIR, 32783, 1, A_CHolyAttack2, S_NULL, 0, 0}, // S_HOLY_MISSILE_X {SPR_SPIR, 16, 3, NULL, S_HOLY_MISSILE_P2, 0, 0}, // S_HOLY_MISSILE_P1 {SPR_SPIR, 17, 3, NULL, S_HOLY_MISSILE_P3, 0, 0}, // S_HOLY_MISSILE_P2 {SPR_SPIR, 18, 3, NULL, S_HOLY_MISSILE_P4, 0, 0}, // S_HOLY_MISSILE_P3 {SPR_SPIR, 19, 3, NULL, S_HOLY_MISSILE_P5, 0, 0}, // S_HOLY_MISSILE_P4 {SPR_SPIR, 20, 3, NULL, S_NULL, 0, 0}, // S_HOLY_MISSILE_P5 {SPR_MWND, 0, 1, A_WeaponReady, S_MWANDREADY, 0, 0}, // S_MWANDREADY {SPR_MWND, 0, 1, A_Lower, S_MWANDDOWN, 0, 0}, // S_MWANDDOWN {SPR_MWND, 0, 1, A_Raise, S_MWANDUP, 0, 0}, // S_MWANDUP {SPR_MWND, 0, 6, NULL, S_MWANDATK_2, 0, 0}, // S_MWANDATK_1 {SPR_MWND, 32769, 6, A_MWandAttack, S_MWANDATK_3, 0, 48}, // S_MWANDATK_2 {SPR_MWND, 0, 3, NULL, S_MWANDATK_4, 0, 40}, // S_MWANDATK_3 {SPR_MWND, 0, 3, A_ReFire, S_MWANDREADY, 0, 36}, // S_MWANDATK_4 {SPR_MWND, 32772, 4, NULL, S_MWANDPUFF2, 0, 0}, // S_MWANDPUFF1 {SPR_MWND, 32773, 3, NULL, S_MWANDPUFF3, 0, 0}, // S_MWANDPUFF2 {SPR_MWND, 32774, 4, NULL, S_MWANDPUFF4, 0, 0}, // S_MWANDPUFF3 {SPR_MWND, 32775, 3, NULL, S_MWANDPUFF5, 0, 0}, // S_MWANDPUFF4 {SPR_MWND, 32776, 4, NULL, S_NULL, 0, 0}, // S_MWANDPUFF5 {SPR_MWND, 2, 4, NULL, S_MWANDSMOKE2, 0, 0}, // S_MWANDSMOKE1 {SPR_MWND, 3, 4, NULL, S_MWANDSMOKE3, 0, 0}, // S_MWANDSMOKE2 {SPR_MWND, 2, 4, NULL, S_MWANDSMOKE4, 0, 0}, // S_MWANDSMOKE3 {SPR_MWND, 3, 4, NULL, S_NULL, 0, 0}, // S_MWANDSMOKE4 {SPR_MWND, 32770, 4, NULL, S_MWAND_MISSILE2, 0, 0}, // S_MWAND_MISSILE1 {SPR_MWND, 32771, 4, NULL, S_MWAND_MISSILE1, 0, 0}, // S_MWAND_MISSILE2 {SPR_WMLG, 32768, 4, NULL, S_MW_LIGHTNING2, 0, 0}, // S_MW_LIGHTNING1 {SPR_WMLG, 32769, 4, NULL, S_MW_LIGHTNING3, 0, 0}, // S_MW_LIGHTNING2 {SPR_WMLG, 32770, 4, NULL, S_MW_LIGHTNING4, 0, 0}, // S_MW_LIGHTNING3 {SPR_WMLG, 32771, 4, NULL, S_MW_LIGHTNING5, 0, 0}, // S_MW_LIGHTNING4 {SPR_WMLG, 32772, 4, NULL, S_MW_LIGHTNING6, 0, 0}, // S_MW_LIGHTNING5 {SPR_WMLG, 32773, 4, NULL, S_MW_LIGHTNING7, 0, 0}, // S_MW_LIGHTNING6 {SPR_WMLG, 32774, 4, NULL, S_MW_LIGHTNING8, 0, 0}, // S_MW_LIGHTNING7 {SPR_WMLG, 32775, 4, NULL, S_MW_LIGHTNING1, 0, 0}, // S_MW_LIGHTNING8 {SPR_MLNG, 32768, 1, A_WeaponReady, S_MLIGHTNINGREADY2, 0, 0}, // S_MLIGHTNINGREADY {SPR_MLNG, 32768, 1, A_WeaponReady, S_MLIGHTNINGREADY3, 0, 0}, // S_MLIGHTNINGREADY2 {SPR_MLNG, 32768, 1, A_WeaponReady, S_MLIGHTNINGREADY4, 0, 0}, // S_MLIGHTNINGREADY3 {SPR_MLNG, 32768, 1, A_WeaponReady, S_MLIGHTNINGREADY5, 0, 0}, // S_MLIGHTNINGREADY4 {SPR_MLNG, 32768, 1, A_WeaponReady, S_MLIGHTNINGREADY6, 0, 0}, // S_MLIGHTNINGREADY5 {SPR_MLNG, 32768, 1, A_LightningReady, S_MLIGHTNINGREADY7, 0, 0}, // S_MLIGHTNINGREADY6 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY8, 0, 0}, // S_MLIGHTNINGREADY7 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY9, 0, 0}, // S_MLIGHTNINGREADY8 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY10, 0, 0}, // S_MLIGHTNINGREADY9 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY11, 0, 0}, // S_MLIGHTNINGREADY10 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY12, 0, 0}, // S_MLIGHTNINGREADY11 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY13, 0, 0}, // S_MLIGHTNINGREADY12 {SPR_MLNG, 32770, 1, A_WeaponReady, S_MLIGHTNINGREADY14, 0, 0}, // S_MLIGHTNINGREADY13 {SPR_MLNG, 32770, 1, A_WeaponReady, S_MLIGHTNINGREADY15, 0, 0}, // S_MLIGHTNINGREADY14 {SPR_MLNG, 32770, 1, A_WeaponReady, S_MLIGHTNINGREADY16, 0, 0}, // S_MLIGHTNINGREADY15 {SPR_MLNG, 32770, 1, A_WeaponReady, S_MLIGHTNINGREADY17, 0, 0}, // S_MLIGHTNINGREADY16 {SPR_MLNG, 32770, 1, A_WeaponReady, S_MLIGHTNINGREADY18, 0, 0}, // S_MLIGHTNINGREADY17 {SPR_MLNG, 32770, 1, A_LightningReady, S_MLIGHTNINGREADY19, 0, 0}, // S_MLIGHTNINGREADY18 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY20, 0, 0}, // S_MLIGHTNINGREADY19 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY21, 0, 0}, // S_MLIGHTNINGREADY20 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY22, 0, 0}, // S_MLIGHTNINGREADY21 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY23, 0, 0}, // S_MLIGHTNINGREADY22 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY24, 0, 0}, // S_MLIGHTNINGREADY23 {SPR_MLNG, 32769, 1, A_WeaponReady, S_MLIGHTNINGREADY, 0, 0}, // S_MLIGHTNINGREADY24 {SPR_MLNG, 32768, 1, A_Lower, S_MLIGHTNINGDOWN, 0, 0}, // S_MLIGHTNINGDOWN {SPR_MLNG, 32768, 1, A_Raise, S_MLIGHTNINGUP, 0, 0}, // S_MLIGHTNINGUP {SPR_MLNG, 32771, 3, NULL, S_MLIGHTNINGATK_2, 0, 0}, // S_MLIGHTNINGATK_1 {SPR_MLNG, 32772, 3, NULL, S_MLIGHTNINGATK_3, 0, 0}, // S_MLIGHTNINGATK_2 {SPR_MLNG, 32773, 4, A_MLightningAttack, S_MLIGHTNINGATK_4, 0, 0}, // S_MLIGHTNINGATK_3 {SPR_MLNG, 32774, 4, NULL, S_MLIGHTNINGATK_5, 0, 0}, // S_MLIGHTNINGATK_4 {SPR_MLNG, 32775, 3, NULL, S_MLIGHTNINGATK_6, 0, 0}, // S_MLIGHTNINGATK_5 {SPR_MLNG, 32776, 3, NULL, S_MLIGHTNINGATK_7, 0, 0}, // S_MLIGHTNINGATK_6 {SPR_MLNG, 32776, 6, NULL, S_MLIGHTNINGATK_8, 0, 199}, // S_MLIGHTNINGATK_7 {SPR_MLNG, 32770, 2, NULL, S_MLIGHTNINGATK_9, 0, 55}, // S_MLIGHTNINGATK_8 {SPR_MLNG, 32769, 2, NULL, S_MLIGHTNINGATK_10, 0, 50}, // S_MLIGHTNINGATK_9 {SPR_MLNG, 32769, 2, NULL, S_MLIGHTNINGATK_11, 0, 45}, // S_MLIGHTNINGATK_10 {SPR_MLNG, 32769, 2, NULL, S_MLIGHTNINGREADY, 0, 40}, // S_MLIGHTNINGATK_11 {SPR_MLFX, 32768, 2, A_LightningZap, S_LIGHTNING_CEILING2, 0, 0}, // S_LIGHTNING_CEILING1 {SPR_MLFX, 32769, 2, A_LightningClip, S_LIGHTNING_CEILING3, 0, 0}, // S_LIGHTNING_CEILING2 {SPR_MLFX, 32770, 2, A_LightningClip, S_LIGHTNING_CEILING4, 0, 0}, // S_LIGHTNING_CEILING3 {SPR_MLFX, 32771, 2, A_LightningClip, S_LIGHTNING_CEILING1, 0, 0}, // S_LIGHTNING_CEILING4 {SPR_MLF2, 32768, 2, A_LightningRemove, S_LIGHTNING_C_X2, 0, 0}, // S_LIGHTNING_C_X1 {SPR_MLF2, 32769, 3, NULL, S_LIGHTNING_C_X3, 0, 0}, // S_LIGHTNING_C_X2 {SPR_MLF2, 32770, 3, NULL, S_LIGHTNING_C_X4, 0, 0}, // S_LIGHTNING_C_X3 {SPR_MLF2, 32771, 3, NULL, S_LIGHTNING_C_X5, 0, 0}, // S_LIGHTNING_C_X4 {SPR_MLF2, 32772, 3, NULL, S_LIGHTNING_C_X6, 0, 0}, // S_LIGHTNING_C_X5 {SPR_MLF2, 32778, 3, NULL, S_LIGHTNING_C_X7, 0, 0}, // S_LIGHTNING_C_X6 {SPR_MLF2, 32779, 3, NULL, S_LIGHTNING_C_X8, 0, 0}, // S_LIGHTNING_C_X7 {SPR_MLF2, 32780, 3, NULL, S_LIGHTNING_C_X9, 0, 0}, // S_LIGHTNING_C_X8 {SPR_ACLO, 4, 35, NULL, S_LIGHTNING_C_X10, 0, 0}, // S_LIGHTNING_C_X9 {SPR_MLF2, 32781, 3, NULL, S_LIGHTNING_C_X11, 0, 0}, // S_LIGHTNING_C_X10 {SPR_MLF2, 32782, 3, NULL, S_LIGHTNING_C_X12, 0, 0}, // S_LIGHTNING_C_X11 {SPR_MLF2, 32783, 4, NULL, S_LIGHTNING_C_X13, 0, 0}, // S_LIGHTNING_C_X12 {SPR_MLF2, 32784, 3, NULL, S_LIGHTNING_C_X14, 0, 0}, // S_LIGHTNING_C_X13 {SPR_MLF2, 32783, 3, NULL, S_LIGHTNING_C_X15, 0, 0}, // S_LIGHTNING_C_X14 {SPR_MLF2, 32784, 4, NULL, S_LIGHTNING_C_X16, 0, 0}, // S_LIGHTNING_C_X15 {SPR_MLF2, 32783, 3, NULL, S_LIGHTNING_C_X17, 0, 0}, // S_LIGHTNING_C_X16 {SPR_MLF2, 32782, 3, NULL, S_LIGHTNING_C_X18, 0, 0}, // S_LIGHTNING_C_X17 {SPR_MLF2, 32783, 3, NULL, S_LIGHTNING_C_X19, 0, 0}, // S_LIGHTNING_C_X18 {SPR_MLF2, 32783, 1, A_HideThing, S_FREETARGMOBJ, 0, 0}, // S_LIGHTNING_C_X19 {SPR_MLFX, 32772, 2, A_LightningZap, S_LIGHTNING_FLOOR2, 0, 0}, // S_LIGHTNING_FLOOR1 {SPR_MLFX, 32773, 2, A_LightningClip, S_LIGHTNING_FLOOR3, 0, 0}, // S_LIGHTNING_FLOOR2 {SPR_MLFX, 32774, 2, A_LightningClip, S_LIGHTNING_FLOOR4, 0, 0}, // S_LIGHTNING_FLOOR3 {SPR_MLFX, 32775, 2, A_LightningClip, S_LIGHTNING_FLOOR1, 0, 0}, // S_LIGHTNING_FLOOR4 {SPR_MLF2, 32773, 2, A_LightningRemove, S_LIGHTNING_F_X2, 0, 0}, // S_LIGHTNING_F_X1 {SPR_MLF2, 32774, 3, NULL, S_LIGHTNING_F_X3, 0, 0}, // S_LIGHTNING_F_X2 {SPR_MLF2, 32775, 3, NULL, S_LIGHTNING_F_X4, 0, 0}, // S_LIGHTNING_F_X3 {SPR_MLF2, 32776, 3, NULL, S_LIGHTNING_F_X5, 0, 0}, // S_LIGHTNING_F_X4 {SPR_MLF2, 32777, 3, NULL, S_LIGHTNING_F_X6, 0, 0}, // S_LIGHTNING_F_X5 {SPR_MLF2, 32778, 3, NULL, S_LIGHTNING_F_X7, 0, 0}, // S_LIGHTNING_F_X6 {SPR_MLF2, 32779, 3, NULL, S_LIGHTNING_F_X8, 0, 0}, // S_LIGHTNING_F_X7 {SPR_MLF2, 32780, 3, NULL, S_LIGHTNING_F_X9, 0, 0}, // S_LIGHTNING_F_X8 {SPR_ACLO, 4, 20, NULL, S_LIGHTNING_F_X10, 0, 0}, // S_LIGHTNING_F_X9 {SPR_MLF2, 32781, 3, NULL, S_LIGHTNING_F_X11, 0, 0}, // S_LIGHTNING_F_X10 {SPR_MLF2, 32782, 3, NULL, S_LIGHTNING_F_X12, 0, 0}, // S_LIGHTNING_F_X11 {SPR_MLF2, 32783, 4, NULL, S_LIGHTNING_F_X13, 0, 0}, // S_LIGHTNING_F_X12 {SPR_MLF2, 32784, 3, NULL, S_LIGHTNING_F_X14, 0, 0}, // S_LIGHTNING_F_X13 {SPR_MLF2, 32783, 3, NULL, S_LIGHTNING_F_X15, 0, 0}, // S_LIGHTNING_F_X14 {SPR_MLF2, 32784, 4, A_LastZap, S_LIGHTNING_F_X16, 0, 0}, // S_LIGHTNING_F_X15 {SPR_MLF2, 32783, 3, NULL, S_LIGHTNING_F_X17, 0, 0}, // S_LIGHTNING_F_X16 {SPR_MLF2, 32782, 3, NULL, S_LIGHTNING_F_X18, 0, 0}, // S_LIGHTNING_F_X17 {SPR_MLF2, 32783, 3, NULL, S_LIGHTNING_F_X19, 0, 0}, // S_LIGHTNING_F_X18 {SPR_MLF2, 32783, 1, A_HideThing, S_FREETARGMOBJ, 0, 0}, // S_LIGHTNING_F_X19 {SPR_MLFX, 32776, 2, A_ZapMimic, S_LIGHTNING_ZAP2, 0, 0}, // S_LIGHTNING_ZAP1 {SPR_MLFX, 32777, 2, A_ZapMimic, S_LIGHTNING_ZAP3, 0, 0}, // S_LIGHTNING_ZAP2 {SPR_MLFX, 32778, 2, A_ZapMimic, S_LIGHTNING_ZAP4, 0, 0}, // S_LIGHTNING_ZAP3 {SPR_MLFX, 32779, 2, A_ZapMimic, S_LIGHTNING_ZAP5, 0, 0}, // S_LIGHTNING_ZAP4 {SPR_MLFX, 32780, 2, A_ZapMimic, S_LIGHTNING_ZAP1, 0, 0}, // S_LIGHTNING_ZAP5 {SPR_MLFX, 32781, 2, NULL, S_LIGHTNING_ZAP_X2, 0, 0}, // S_LIGHTNING_ZAP_X1 {SPR_MLFX, 32782, 2, NULL, S_LIGHTNING_ZAP_X3, 0, 0}, // S_LIGHTNING_ZAP_X2 {SPR_MLFX, 32783, 2, NULL, S_LIGHTNING_ZAP_X4, 0, 0}, // S_LIGHTNING_ZAP_X3 {SPR_MLFX, 32784, 2, NULL, S_LIGHTNING_ZAP_X5, 0, 0}, // S_LIGHTNING_ZAP_X4 {SPR_MLFX, 32785, 2, NULL, S_LIGHTNING_ZAP_X6, 0, 0}, // S_LIGHTNING_ZAP_X5 {SPR_MLFX, 32786, 2, NULL, S_LIGHTNING_ZAP_X7, 0, 0}, // S_LIGHTNING_ZAP_X6 {SPR_MLFX, 32787, 2, NULL, S_LIGHTNING_ZAP_X8, 0, 0}, // S_LIGHTNING_ZAP_X7 {SPR_MLFX, 32788, 2, NULL, S_NULL, 0, 0}, // S_LIGHTNING_ZAP_X8 {SPR_MSTF, 0, 1, A_WeaponReady, S_MSTAFFREADY2, 0, 0}, // S_MSTAFFREADY {SPR_MSTF, 0, 1, A_WeaponReady, S_MSTAFFREADY3, 0, 0}, // S_MSTAFFREADY2 {SPR_MSTF, 0, 1, A_WeaponReady, S_MSTAFFREADY4, 0, 0}, // S_MSTAFFREADY3 {SPR_MSTF, 0, 1, A_WeaponReady, S_MSTAFFREADY5, 0, 0}, // S_MSTAFFREADY4 {SPR_MSTF, 0, 1, A_WeaponReady, S_MSTAFFREADY6, 0, 0}, // S_MSTAFFREADY5 {SPR_MSTF, 0, 1, A_WeaponReady, S_MSTAFFREADY7, 0, 0}, // S_MSTAFFREADY6 {SPR_MSTF, 1, 1, A_WeaponReady, S_MSTAFFREADY8, 0, 0}, // S_MSTAFFREADY7 {SPR_MSTF, 1, 1, A_WeaponReady, S_MSTAFFREADY9, 0, 0}, // S_MSTAFFREADY8 {SPR_MSTF, 1, 1, A_WeaponReady, S_MSTAFFREADY10, 0, 0}, // S_MSTAFFREADY9 {SPR_MSTF, 1, 1, A_WeaponReady, S_MSTAFFREADY11, 0, 0}, // S_MSTAFFREADY10 {SPR_MSTF, 1, 1, A_WeaponReady, S_MSTAFFREADY12, 0, 0}, // S_MSTAFFREADY11 {SPR_MSTF, 1, 1, A_WeaponReady, S_MSTAFFREADY13, 0, 0}, // S_MSTAFFREADY12 {SPR_MSTF, 2, 1, A_WeaponReady, S_MSTAFFREADY14, 0, 0}, // S_MSTAFFREADY13 {SPR_MSTF, 2, 1, A_WeaponReady, S_MSTAFFREADY15, 0, 0}, // S_MSTAFFREADY14 {SPR_MSTF, 2, 1, A_WeaponReady, S_MSTAFFREADY16, 0, 0}, // S_MSTAFFREADY15 {SPR_MSTF, 2, 1, A_WeaponReady, S_MSTAFFREADY17, 0, 0}, // S_MSTAFFREADY16 {SPR_MSTF, 2, 1, A_WeaponReady, S_MSTAFFREADY18, 0, 0}, // S_MSTAFFREADY17 {SPR_MSTF, 2, 1, A_WeaponReady, S_MSTAFFREADY19, 0, 0}, // S_MSTAFFREADY18 {SPR_MSTF, 3, 1, A_WeaponReady, S_MSTAFFREADY20, 0, 0}, // S_MSTAFFREADY19 {SPR_MSTF, 3, 1, A_WeaponReady, S_MSTAFFREADY21, 0, 0}, // S_MSTAFFREADY20 {SPR_MSTF, 3, 1, A_WeaponReady, S_MSTAFFREADY22, 0, 0}, // S_MSTAFFREADY21 {SPR_MSTF, 3, 1, A_WeaponReady, S_MSTAFFREADY23, 0, 0}, // S_MSTAFFREADY22 {SPR_MSTF, 3, 1, A_WeaponReady, S_MSTAFFREADY24, 0, 0}, // S_MSTAFFREADY23 {SPR_MSTF, 3, 1, A_WeaponReady, S_MSTAFFREADY25, 0, 0}, // S_MSTAFFREADY24 {SPR_MSTF, 4, 1, A_WeaponReady, S_MSTAFFREADY26, 0, 0}, // S_MSTAFFREADY25 {SPR_MSTF, 4, 1, A_WeaponReady, S_MSTAFFREADY27, 0, 0}, // S_MSTAFFREADY26 {SPR_MSTF, 4, 1, A_WeaponReady, S_MSTAFFREADY28, 0, 0}, // S_MSTAFFREADY27 {SPR_MSTF, 4, 1, A_WeaponReady, S_MSTAFFREADY29, 0, 0}, // S_MSTAFFREADY28 {SPR_MSTF, 4, 1, A_WeaponReady, S_MSTAFFREADY30, 0, 0}, // S_MSTAFFREADY29 {SPR_MSTF, 4, 1, A_WeaponReady, S_MSTAFFREADY31, 0, 0}, // S_MSTAFFREADY30 {SPR_MSTF, 5, 1, A_WeaponReady, S_MSTAFFREADY32, 0, 0}, // S_MSTAFFREADY31 {SPR_MSTF, 5, 1, A_WeaponReady, S_MSTAFFREADY33, 0, 0}, // S_MSTAFFREADY32 {SPR_MSTF, 5, 1, A_WeaponReady, S_MSTAFFREADY34, 0, 0}, // S_MSTAFFREADY33 {SPR_MSTF, 5, 1, A_WeaponReady, S_MSTAFFREADY35, 0, 0}, // S_MSTAFFREADY34 {SPR_MSTF, 5, 1, A_WeaponReady, S_MSTAFFREADY, 0, 0}, // S_MSTAFFREADY35 {SPR_MSTF, 0, 1, A_Lower, S_MSTAFFDOWN, 0, 0}, // S_MSTAFFDOWN {SPR_MSTF, 0, 1, A_Raise, S_MSTAFFUP, 0, 0}, // S_MSTAFFUP {SPR_MSTF, 6, 4, NULL, S_MSTAFFATK_2, 0, 40}, // S_MSTAFFATK_1 {SPR_MSTF, 32775, 4, A_MStaffAttack, S_MSTAFFATK_3, 0, 48}, // S_MSTAFFATK_2 {SPR_MSTF, 32775, 2, A_MStaffPalette, S_MSTAFFATK_4, 0, 48}, // S_MSTAFFATK_3 {SPR_MSTF, 8, 2, A_MStaffPalette, S_MSTAFFATK_5, 0, 48}, // S_MSTAFFATK_4 {SPR_MSTF, 8, 2, A_MStaffPalette, S_MSTAFFATK_6, 0, 48}, // S_MSTAFFATK_5 {SPR_MSTF, 8, 1, NULL, S_MSTAFFATK_7, 0, 40}, // S_MSTAFFATK_6 {SPR_MSTF, 9, 5, NULL, S_MSTAFFREADY, 0, 36}, // S_MSTAFFATK_7 {SPR_MSP1, 32768, 3, A_MStaffWeave, S_MSTAFF_FX1_2, 0, 0}, // S_MSTAFF_FX1_1 {SPR_MSP1, 32769, 3, A_MStaffWeave, S_MSTAFF_FX1_3, 0, 0}, // S_MSTAFF_FX1_2 {SPR_MSP1, 32770, 3, A_MStaffWeave, S_MSTAFF_FX1_4, 0, 0}, // S_MSTAFF_FX1_3 {SPR_MSP1, 32771, 3, A_MStaffWeave, S_MSTAFF_FX1_5, 0, 0}, // S_MSTAFF_FX1_4 {SPR_MSP1, 32772, 3, A_MStaffWeave, S_MSTAFF_FX1_6, 0, 0}, // S_MSTAFF_FX1_5 {SPR_MSP1, 32773, 3, A_MStaffWeave, S_MSTAFF_FX1_1, 0, 0}, // S_MSTAFF_FX1_6 {SPR_MSP1, 32774, 4, NULL, S_MSTAFF_FX_X2, 0, 0}, // S_MSTAFF_FX_X1 {SPR_MSP1, 32775, 5, A_Explode, S_MSTAFF_FX_X3, 0, 0}, // S_MSTAFF_FX_X2 {SPR_MSP1, 32776, 4, NULL, S_MSTAFF_FX_X4, 0, 0}, // S_MSTAFF_FX_X3 {SPR_MSP1, 32777, 5, NULL, S_MSTAFF_FX_X5, 0, 0}, // S_MSTAFF_FX_X4 {SPR_MSP1, 32778, 4, NULL, S_MSTAFF_FX_X6, 0, 0}, // S_MSTAFF_FX_X5 {SPR_MSP1, 32779, 5, NULL, S_MSTAFF_FX_X7, 0, 0}, // S_MSTAFF_FX_X6 {SPR_MSP1, 32780, 4, NULL, S_MSTAFF_FX_X8, 0, 0}, // S_MSTAFF_FX_X7 {SPR_MSP1, 32781, 5, NULL, S_MSTAFF_FX_X9, 0, 0}, // S_MSTAFF_FX_X8 {SPR_MSP1, 32782, 4, NULL, S_MSTAFF_FX_X10, 0, 0}, // S_MSTAFF_FX_X9 {SPR_MSP1, 32783, 4, NULL, S_NULL, 0, 0}, // S_MSTAFF_FX_X10 {SPR_MSP2, 32768, 2, A_MStaffTrack, S_MSTAFF_FX2_2, 0, 0}, // S_MSTAFF_FX2_1 {SPR_MSP2, 32769, 2, A_MStaffTrack, S_MSTAFF_FX2_3, 0, 0}, // S_MSTAFF_FX2_2 {SPR_MSP2, 32770, 2, A_MStaffTrack, S_MSTAFF_FX2_4, 0, 0}, // S_MSTAFF_FX2_3 {SPR_MSP2, 32771, 2, A_MStaffTrack, S_MSTAFF_FX2_1, 0, 0}, // S_MSTAFF_FX2_4 {SPR_MSP2, 32772, 4, NULL, S_MSTAFF_FX2_X2, 0, 0}, // S_MSTAFF_FX2_X1 {SPR_MSP2, 32773, 5, A_Explode, S_MSTAFF_FX2_X3, 0, 0}, // S_MSTAFF_FX2_X2 {SPR_MSP2, 32774, 5, NULL, S_MSTAFF_FX2_X4, 0, 0}, // S_MSTAFF_FX2_X3 {SPR_MSP2, 32775, 5, NULL, S_MSTAFF_FX2_X5, 0, 0}, // S_MSTAFF_FX2_X4 {SPR_MSP2, 32776, 4, NULL, S_NULL, 0, 0}, // S_MSTAFF_FX2_X5 {SPR_WFR1, 32768, -1, NULL, S_NULL, 0, 0}, // S_FSWORD1 {SPR_WFR2, 32768, -1, NULL, S_NULL, 0, 0}, // S_FSWORD2 {SPR_WFR3, 32768, -1, NULL, S_NULL, 0, 0}, // S_FSWORD3 {SPR_WCH1, 32768, -1, NULL, S_NULL, 0, 0}, // S_CHOLY1 {SPR_WCH2, 32768, -1, NULL, S_NULL, 0, 0}, // S_CHOLY2 {SPR_WCH3, 32768, -1, NULL, S_NULL, 0, 0}, // S_CHOLY3 {SPR_WMS1, 32768, -1, NULL, S_NULL, 0, 0}, // S_MSTAFF1 {SPR_WMS2, 32768, -1, NULL, S_NULL, 0, 0}, // S_MSTAFF2 {SPR_WMS3, 32768, -1, NULL, S_NULL, 0, 0}, // S_MSTAFF3 {SPR_WPIG, 0, 1, A_WeaponReady, S_SNOUTREADY, 0, 0}, // S_SNOUTREADY {SPR_WPIG, 0, 1, A_Lower, S_SNOUTDOWN, 0, 0}, // S_SNOUTDOWN {SPR_WPIG, 0, 1, A_Raise, S_SNOUTUP, 0, 0}, // S_SNOUTUP {SPR_WPIG, 0, 4, A_SnoutAttack, S_SNOUTATK2, 0, 0}, // S_SNOUTATK1 {SPR_WPIG, 1, 8, A_SnoutAttack, S_SNOUTREADY, 0, 0}, // S_SNOUTATK2 {SPR_WMCS, 32768, 8, NULL, S_COS2, 0, 0}, // S_COS1 {SPR_WMCS, 32769, 8, NULL, S_COS3, 0, 0}, // S_COS2 {SPR_WMCS, 32770, 8, NULL, S_COS1, 0, 0}, // S_COS3 {SPR_CONE, 0, 1, A_WeaponReady, S_CONEREADY, 0, 0}, // S_CONEREADY {SPR_CONE, 0, 1, A_Lower, S_CONEDOWN, 0, 0}, // S_CONEDOWN {SPR_CONE, 0, 1, A_Raise, S_CONEUP, 0, 0}, // S_CONEUP {SPR_CONE, 1, 3, NULL, S_CONEATK1_2, 0, 0}, // S_CONEATK1_1 {SPR_CONE, 2, 4, NULL, S_CONEATK1_3, 0, 0}, // S_CONEATK1_2 {SPR_CONE, 3, 3, NULL, S_CONEATK1_4, 0, 0}, // S_CONEATK1_3 {SPR_CONE, 4, 5, NULL, S_CONEATK1_5, 0, 0}, // S_CONEATK1_4 {SPR_CONE, 5, 3, A_FireConePL1, S_CONEATK1_6, 0, 0}, // S_CONEATK1_5 {SPR_CONE, 6, 3, NULL, S_CONEATK1_7, 0, 0}, // S_CONEATK1_6 {SPR_CONE, 0, 9, NULL, S_CONEATK1_8, 0, 0}, // S_CONEATK1_7 {SPR_CONE, 0, 10, A_ReFire, S_CONEREADY, 0, 0}, // S_CONEATK1_8 {SPR_SHRD, 32768, 2, NULL, S_SHARDFX1_2, 0, 0}, // S_SHARDFX1_1 {SPR_SHRD, 32768, 3, A_ShedShard, S_SHARDFX1_3, 0, 0}, // S_SHARDFX1_2 {SPR_SHRD, 32769, 3, NULL, S_SHARDFX1_4, 0, 0}, // S_SHARDFX1_3 {SPR_SHRD, 32770, 3, NULL, S_SHARDFX1_1, 0, 0}, // S_SHARDFX1_4 {SPR_SHEX, 32768, 5, NULL, S_SHARDFXE1_2, 0, 0}, // S_SHARDFXE1_1 {SPR_SHEX, 32769, 5, NULL, S_SHARDFXE1_3, 0, 0}, // S_SHARDFXE1_2 {SPR_SHEX, 32770, 5, NULL, S_SHARDFXE1_4, 0, 0}, // S_SHARDFXE1_3 {SPR_SHEX, 32771, 5, NULL, S_SHARDFXE1_5, 0, 0}, // S_SHARDFXE1_4 {SPR_SHEX, 32772, 5, NULL, S_NULL, 0, 0}, // S_SHARDFXE1_5 {SPR_BLOD, 2, 8, NULL, S_BLOOD2, 0, 0}, // S_BLOOD1 {SPR_BLOD, 1, 8, NULL, S_BLOOD3, 0, 0}, // S_BLOOD2 {SPR_BLOD, 0, 8, NULL, S_NULL, 0, 0}, // S_BLOOD3 {SPR_BLOD, 2, 8, NULL, S_BLOODSPLATTER2, 0, 0}, // S_BLOODSPLATTER1 {SPR_BLOD, 1, 8, NULL, S_BLOODSPLATTER3, 0, 0}, // S_BLOODSPLATTER2 {SPR_BLOD, 0, 8, NULL, S_NULL, 0, 0}, // S_BLOODSPLATTER3 {SPR_BLOD, 0, 6, NULL, S_NULL, 0, 0}, // S_BLOODSPLATTERX {SPR_GIBS, 0, -1, NULL, S_NULL, 0, 0}, // S_GIBS1 {SPR_PLAY, 0, -1, NULL, S_NULL, 0, 0}, // S_FPLAY {SPR_PLAY, 0, 4, NULL, S_FPLAY_RUN2, 0, 0}, // S_FPLAY_RUN1 {SPR_PLAY, 1, 4, NULL, S_FPLAY_RUN3, 0, 0}, // S_FPLAY_RUN2 {SPR_PLAY, 2, 4, NULL, S_FPLAY_RUN4, 0, 0}, // S_FPLAY_RUN3 {SPR_PLAY, 3, 4, NULL, S_FPLAY_RUN1, 0, 0}, // S_FPLAY_RUN4 {SPR_PLAY, 4, 8, NULL, S_FPLAY_ATK2, 0, 0}, // S_FPLAY_ATK1 {SPR_PLAY, 5, 8, NULL, S_FPLAY, 0, 0}, // S_FPLAY_ATK2 {SPR_PLAY, 6, 4, NULL, S_FPLAY_PAIN2, 0, 0}, // S_FPLAY_PAIN {SPR_PLAY, 6, 4, A_Pain, S_FPLAY, 0, 0}, // S_FPLAY_PAIN2 {SPR_PLAY, 7, 6, NULL, S_FPLAY_DIE2, 0, 0}, // S_FPLAY_DIE1 {SPR_PLAY, 8, 6, A_Scream, S_FPLAY_DIE3, 0, 0}, // S_FPLAY_DIE2 {SPR_PLAY, 9, 6, NULL, S_FPLAY_DIE4, 0, 0}, // S_FPLAY_DIE3 {SPR_PLAY, 10, 6, NULL, S_FPLAY_DIE5, 0, 0}, // S_FPLAY_DIE4 {SPR_PLAY, 11, 6, A_NoBlocking, S_FPLAY_DIE6, 0, 0}, // S_FPLAY_DIE5 {SPR_PLAY, 12, 6, NULL, S_FPLAY_DIE7, 0, 0}, // S_FPLAY_DIE6 {SPR_PLAY, 13, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_FPLAY_DIE7 {SPR_PLAY, 14, 5, A_Scream, S_FPLAY_XDIE2, 0, 0}, // S_FPLAY_XDIE1 {SPR_PLAY, 15, 5, A_SkullPop, S_FPLAY_XDIE3, 0, 0}, // S_FPLAY_XDIE2 {SPR_PLAY, 17, 5, A_NoBlocking, S_FPLAY_XDIE4, 0, 0}, // S_FPLAY_XDIE3 {SPR_PLAY, 18, 5, NULL, S_FPLAY_XDIE5, 0, 0}, // S_FPLAY_XDIE4 {SPR_PLAY, 19, 5, NULL, S_FPLAY_XDIE6, 0, 0}, // S_FPLAY_XDIE5 {SPR_PLAY, 20, 5, NULL, S_FPLAY_XDIE7, 0, 0}, // S_FPLAY_XDIE6 {SPR_PLAY, 21, 5, NULL, S_FPLAY_XDIE8, 0, 0}, // S_FPLAY_XDIE7 {SPR_PLAY, 22, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_FPLAY_XDIE8 {SPR_PLAY, 23, 5, A_FreezeDeath, S_FPLAY_ICE2, 0, 0}, // S_FPLAY_ICE {SPR_PLAY, 23, 1, A_FreezeDeathChunks, S_FPLAY_ICE2, 0, 0}, // S_FPLAY_ICE2 {SPR_FDTH, 32768, 5, NULL, S_PLAY_F_FDTH2, 0, 0}, // S_PLAY_F_FDTH1 {SPR_FDTH, 32769, 4, NULL, S_PLAY_FDTH3, 0, 0}, // S_PLAY_F_FDTH2 {SPR_FDTH, 32770, 5, NULL, S_PLAY_C_FDTH2, 0, 0}, // S_PLAY_C_FDTH1 {SPR_FDTH, 32771, 4, NULL, S_PLAY_FDTH3, 0, 0}, // S_PLAY_C_FDTH2 {SPR_FDTH, 32772, 5, NULL, S_PLAY_M_FDTH2, 0, 0}, // S_PLAY_M_FDTH1 {SPR_FDTH, 32773, 4, NULL, S_PLAY_FDTH3, 0, 0}, // S_PLAY_M_FDTH2 {SPR_FDTH, 32774, 5, NULL, S_PLAY_FDTH4, 0, 0}, // S_PLAY_FDTH3 {SPR_FDTH, 32775, 4, A_Scream, S_PLAY_FDTH5, 0, 0}, // S_PLAY_FDTH4 {SPR_FDTH, 32776, 5, NULL, S_PLAY_FDTH6, 0, 0}, // S_PLAY_FDTH5 {SPR_FDTH, 32777, 4, NULL, S_PLAY_FDTH7, 0, 0}, // S_PLAY_FDTH6 {SPR_FDTH, 32778, 5, NULL, S_PLAY_FDTH8, 0, 0}, // S_PLAY_FDTH7 {SPR_FDTH, 32779, 4, NULL, S_PLAY_FDTH9, 0, 0}, // S_PLAY_FDTH8 {SPR_FDTH, 32780, 5, NULL, S_PLAY_FDTH10, 0, 0}, // S_PLAY_FDTH9 {SPR_FDTH, 32781, 4, NULL, S_PLAY_FDTH11, 0, 0}, // S_PLAY_FDTH10 {SPR_FDTH, 32782, 5, NULL, S_PLAY_FDTH12, 0, 0}, // S_PLAY_FDTH11 {SPR_FDTH, 32783, 4, NULL, S_PLAY_FDTH13, 0, 0}, // S_PLAY_FDTH12 {SPR_FDTH, 32784, 5, NULL, S_PLAY_FDTH14, 0, 0}, // S_PLAY_FDTH13 {SPR_FDTH, 32785, 4, NULL, S_PLAY_FDTH15, 0, 0}, // S_PLAY_FDTH14 {SPR_FDTH, 32786, 5, A_NoBlocking, S_PLAY_FDTH16, 0, 0}, // S_PLAY_FDTH15 {SPR_FDTH, 32787, 4, NULL, S_PLAY_FDTH17, 0, 0}, // S_PLAY_FDTH16 {SPR_FDTH, 32788, 5, NULL, S_PLAY_FDTH18, 0, 0}, // S_PLAY_FDTH17 {SPR_FDTH, 32789, 4, NULL, S_PLAY_FDTH19, 0, 0}, // S_PLAY_FDTH18 {SPR_ACLO, 4, 35, A_CheckBurnGone, S_PLAY_FDTH19, 0, 0}, // S_PLAY_FDTH19 {SPR_ACLO, 4, 8, NULL, S_NULL, 0, 0}, // S_PLAY_FDTH20 {SPR_BSKL, 0, 5, A_CheckSkullFloor, S_BLOODYSKULL2, 0, 0}, // S_BLOODYSKULL1 {SPR_BSKL, 1, 5, A_CheckSkullFloor, S_BLOODYSKULL3, 0, 0}, // S_BLOODYSKULL2 {SPR_BSKL, 2, 5, A_CheckSkullFloor, S_BLOODYSKULL4, 0, 0}, // S_BLOODYSKULL3 {SPR_BSKL, 3, 5, A_CheckSkullFloor, S_BLOODYSKULL5, 0, 0}, // S_BLOODYSKULL4 {SPR_BSKL, 5, 5, A_CheckSkullFloor, S_BLOODYSKULL6, 0, 0}, // S_BLOODYSKULL5 {SPR_BSKL, 6, 5, A_CheckSkullFloor, S_BLOODYSKULL7, 0, 0}, // S_BLOODYSKULL6 {SPR_BSKL, 7, 5, A_CheckSkullFloor, S_BLOODYSKULL1, 0, 0}, // S_BLOODYSKULL7 {SPR_BSKL, 8, 16, A_CheckSkullDone, S_BLOODYSKULLX1, 0, 0}, // S_BLOODYSKULLX1 {SPR_BSKL, 8, 1050, NULL, S_NULL, 0, 0}, // S_BLOODYSKULLX2 {SPR_PLAY, 0, 5, NULL, S_PLAYER_SPEED2, 0, 0}, // S_PLAYER_SPEED1 {SPR_PLAY, 0, 3, A_SpeedFade, S_NULL, 0, 0}, // S_PLAYER_SPEED2 {SPR_ICEC, 0, 10, NULL, S_ICECHUNK2, 0, 0}, // S_ICECHUNK1 {SPR_ICEC, 1, 10, A_IceSetTics, S_ICECHUNK3, 0, 0}, // S_ICECHUNK2 {SPR_ICEC, 2, 10, A_IceSetTics, S_ICECHUNK4, 0, 0}, // S_ICECHUNK3 {SPR_ICEC, 3, 10, A_IceSetTics, S_NULL, 0, 0}, // S_ICECHUNK4 {SPR_ICEC, 0, 10, A_IceCheckHeadDone, S_ICECHUNK_HEAD, 0, 0}, // S_ICECHUNK_HEAD {SPR_ICEC, 0, 1050, NULL, S_NULL, 0, 0}, // S_ICECHUNK_HEAD2 {SPR_CLER, 0, -1, NULL, S_NULL, 0, 0}, // S_CPLAY {SPR_CLER, 0, 4, NULL, S_CPLAY_RUN2, 0, 0}, // S_CPLAY_RUN1 {SPR_CLER, 1, 4, NULL, S_CPLAY_RUN3, 0, 0}, // S_CPLAY_RUN2 {SPR_CLER, 2, 4, NULL, S_CPLAY_RUN4, 0, 0}, // S_CPLAY_RUN3 {SPR_CLER, 3, 4, NULL, S_CPLAY_RUN1, 0, 0}, // S_CPLAY_RUN4 {SPR_CLER, 4, 6, NULL, S_CPLAY_ATK2, 0, 0}, // S_CPLAY_ATK1 {SPR_CLER, 5, 6, NULL, S_CPLAY_ATK3, 0, 0}, // S_CPLAY_ATK2 {SPR_CLER, 6, 6, NULL, S_CPLAY, 0, 0}, // S_CPLAY_ATK3 {SPR_CLER, 7, 4, NULL, S_CPLAY_PAIN2, 0, 0}, // S_CPLAY_PAIN {SPR_CLER, 7, 4, A_Pain, S_CPLAY, 0, 0}, // S_CPLAY_PAIN2 {SPR_CLER, 8, 6, NULL, S_CPLAY_DIE2, 0, 0}, // S_CPLAY_DIE1 {SPR_CLER, 10, 6, A_Scream, S_CPLAY_DIE3, 0, 0}, // S_CPLAY_DIE2 {SPR_CLER, 11, 6, NULL, S_CPLAY_DIE4, 0, 0}, // S_CPLAY_DIE3 {SPR_CLER, 11, 6, NULL, S_CPLAY_DIE5, 0, 0}, // S_CPLAY_DIE4 {SPR_CLER, 12, 6, A_NoBlocking, S_CPLAY_DIE6, 0, 0}, // S_CPLAY_DIE5 {SPR_CLER, 13, 6, NULL, S_CPLAY_DIE7, 0, 0}, // S_CPLAY_DIE6 {SPR_CLER, 14, 6, NULL, S_CPLAY_DIE8, 0, 0}, // S_CPLAY_DIE7 {SPR_CLER, 15, 6, NULL, S_CPLAY_DIE9, 0, 0}, // S_CPLAY_DIE8 {SPR_CLER, 16, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_CPLAY_DIE9 {SPR_CLER, 17, 5, A_Scream, S_CPLAY_XDIE2, 0, 0}, // S_CPLAY_XDIE1 {SPR_CLER, 18, 5, NULL, S_CPLAY_XDIE3, 0, 0}, // S_CPLAY_XDIE2 {SPR_CLER, 19, 5, A_NoBlocking, S_CPLAY_XDIE4, 0, 0}, // S_CPLAY_XDIE3 {SPR_CLER, 20, 5, NULL, S_CPLAY_XDIE5, 0, 0}, // S_CPLAY_XDIE4 {SPR_CLER, 21, 5, NULL, S_CPLAY_XDIE6, 0, 0}, // S_CPLAY_XDIE5 {SPR_CLER, 22, 5, NULL, S_CPLAY_XDIE7, 0, 0}, // S_CPLAY_XDIE6 {SPR_CLER, 23, 5, NULL, S_CPLAY_XDIE8, 0, 0}, // S_CPLAY_XDIE7 {SPR_CLER, 24, 5, NULL, S_CPLAY_XDIE9, 0, 0}, // S_CPLAY_XDIE8 {SPR_CLER, 25, 5, NULL, S_CPLAY_XDIE10, 0, 0}, // S_CPLAY_XDIE9 {SPR_CLER, 26, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_CPLAY_XDIE10 {SPR_CLER, 27, 5, A_FreezeDeath, S_CPLAY_ICE2, 0, 0}, // S_CPLAY_ICE {SPR_CLER, 27, 1, A_FreezeDeathChunks, S_CPLAY_ICE2, 0, 0}, // S_CPLAY_ICE2 {SPR_MAGE, 0, -1, NULL, S_NULL, 0, 0}, // S_MPLAY {SPR_MAGE, 0, 4, NULL, S_MPLAY_RUN2, 0, 0}, // S_MPLAY_RUN1 {SPR_MAGE, 1, 4, NULL, S_MPLAY_RUN3, 0, 0}, // S_MPLAY_RUN2 {SPR_MAGE, 2, 4, NULL, S_MPLAY_RUN4, 0, 0}, // S_MPLAY_RUN3 {SPR_MAGE, 3, 4, NULL, S_MPLAY_RUN1, 0, 0}, // S_MPLAY_RUN4 {SPR_MAGE, 4, 8, NULL, S_MPLAY_ATK2, 0, 0}, // S_MPLAY_ATK1 {SPR_MAGE, 32773, 8, NULL, S_MPLAY, 0, 0}, // S_MPLAY_ATK2 {SPR_MAGE, 6, 4, NULL, S_MPLAY_PAIN2, 0, 0}, // S_MPLAY_PAIN {SPR_MAGE, 6, 4, A_Pain, S_MPLAY, 0, 0}, // S_MPLAY_PAIN2 {SPR_MAGE, 7, 6, NULL, S_MPLAY_DIE2, 0, 0}, // S_MPLAY_DIE1 {SPR_MAGE, 8, 6, A_Scream, S_MPLAY_DIE3, 0, 0}, // S_MPLAY_DIE2 {SPR_MAGE, 9, 6, NULL, S_MPLAY_DIE4, 0, 0}, // S_MPLAY_DIE3 {SPR_MAGE, 10, 6, NULL, S_MPLAY_DIE5, 0, 0}, // S_MPLAY_DIE4 {SPR_MAGE, 11, 6, A_NoBlocking, S_MPLAY_DIE6, 0, 0}, // S_MPLAY_DIE5 {SPR_MAGE, 12, 6, NULL, S_MPLAY_DIE7, 0, 0}, // S_MPLAY_DIE6 {SPR_MAGE, 13, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_MPLAY_DIE7 {SPR_MAGE, 14, 5, A_Scream, S_MPLAY_XDIE2, 0, 0}, // S_MPLAY_XDIE1 {SPR_MAGE, 15, 5, NULL, S_MPLAY_XDIE3, 0, 0}, // S_MPLAY_XDIE2 {SPR_MAGE, 17, 5, A_NoBlocking, S_MPLAY_XDIE4, 0, 0}, // S_MPLAY_XDIE3 {SPR_MAGE, 18, 5, NULL, S_MPLAY_XDIE5, 0, 0}, // S_MPLAY_XDIE4 {SPR_MAGE, 19, 5, NULL, S_MPLAY_XDIE6, 0, 0}, // S_MPLAY_XDIE5 {SPR_MAGE, 20, 5, NULL, S_MPLAY_XDIE7, 0, 0}, // S_MPLAY_XDIE6 {SPR_MAGE, 21, 5, NULL, S_MPLAY_XDIE8, 0, 0}, // S_MPLAY_XDIE7 {SPR_MAGE, 22, 5, NULL, S_MPLAY_XDIE9, 0, 0}, // S_MPLAY_XDIE8 {SPR_MAGE, 23, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_MPLAY_XDIE9 {SPR_MAGE, 24, 5, A_FreezeDeath, S_MPLAY_ICE2, 0, 0}, // S_MPLAY_ICE {SPR_MAGE, 24, 1, A_FreezeDeathChunks, S_MPLAY_ICE2, 0, 0}, // S_MPLAY_ICE2 {SPR_PIGY, 0, -1, NULL, S_NULL, 0, 0}, // S_PIGPLAY {SPR_PIGY, 0, 3, NULL, S_PIGPLAY_RUN2, 0, 0}, // S_PIGPLAY_RUN1 {SPR_PIGY, 1, 3, NULL, S_PIGPLAY_RUN3, 0, 0}, // S_PIGPLAY_RUN2 {SPR_PIGY, 2, 3, NULL, S_PIGPLAY_RUN4, 0, 0}, // S_PIGPLAY_RUN3 {SPR_PIGY, 3, 3, NULL, S_PIGPLAY_RUN1, 0, 0}, // S_PIGPLAY_RUN4 {SPR_PIGY, 0, 12, NULL, S_PIGPLAY, 0, 0}, // S_PIGPLAY_ATK1 {SPR_PIGY, 3, 4, A_PigPain, S_PIGPLAY, 0, 0}, // S_PIGPLAY_PAIN {SPR_PIGY, 1, 10, A_PigLook, S_PIG_LOOK1, 0, 0}, // S_PIG_LOOK1 {SPR_PIGY, 0, 3, A_PigChase, S_PIG_WALK2, 0, 0}, // S_PIG_WALK1 {SPR_PIGY, 1, 3, A_PigChase, S_PIG_WALK3, 0, 0}, // S_PIG_WALK2 {SPR_PIGY, 2, 3, A_PigChase, S_PIG_WALK4, 0, 0}, // S_PIG_WALK3 {SPR_PIGY, 3, 3, A_PigChase, S_PIG_WALK1, 0, 0}, // S_PIG_WALK4 {SPR_PIGY, 3, 4, A_PigPain, S_PIG_WALK1, 0, 0}, // S_PIG_PAIN {SPR_PIGY, 0, 5, A_FaceTarget, S_PIG_ATK2, 0, 0}, // S_PIG_ATK1 {SPR_PIGY, 0, 10, A_PigAttack, S_PIG_WALK1, 0, 0}, // S_PIG_ATK2 {SPR_PIGY, 4, 4, A_Scream, S_PIG_DIE2, 0, 0}, // S_PIG_DIE1 {SPR_PIGY, 5, 3, A_NoBlocking, S_PIG_DIE3, 0, 0}, // S_PIG_DIE2 {SPR_PIGY, 6, 4, A_QueueCorpse, S_PIG_DIE4, 0, 0}, // S_PIG_DIE3 {SPR_PIGY, 7, 3, NULL, S_PIG_DIE5, 0, 0}, // S_PIG_DIE4 {SPR_PIGY, 8, 4, NULL, S_PIG_DIE6, 0, 0}, // S_PIG_DIE5 {SPR_PIGY, 9, 4, NULL, S_PIG_DIE7, 0, 0}, // S_PIG_DIE6 {SPR_PIGY, 10, 4, NULL, S_PIG_DIE8, 0, 0}, // S_PIG_DIE7 {SPR_PIGY, 11, -1, NULL, S_NULL, 0, 0}, // S_PIG_DIE8 {SPR_PIGY, 12, 5, A_FreezeDeath, S_PIG_ICE2, 0, 0}, // S_PIG_ICE {SPR_PIGY, 12, 1, A_FreezeDeathChunks, S_PIG_ICE2, 0, 0}, // S_PIG_ICE2 {SPR_CENT, 0, 10, A_Look, S_CENTAUR_LOOK2, 0, 0}, // S_CENTAUR_LOOK1 {SPR_CENT, 1, 10, A_Look, S_CENTAUR_LOOK1, 0, 0}, // S_CENTAUR_LOOK2 {SPR_CENT, 0, 4, A_Chase, S_CENTAUR_WALK2, 0, 0}, // S_CENTAUR_WALK1 {SPR_CENT, 1, 4, A_Chase, S_CENTAUR_WALK3, 0, 0}, // S_CENTAUR_WALK2 {SPR_CENT, 2, 4, A_Chase, S_CENTAUR_WALK4, 0, 0}, // S_CENTAUR_WALK3 {SPR_CENT, 3, 4, A_Chase, S_CENTAUR_WALK1, 0, 0}, // S_CENTAUR_WALK4 {SPR_CENT, 7, 5, A_FaceTarget, S_CENTAUR_ATK2, 0, 0}, // S_CENTAUR_ATK1 {SPR_CENT, 8, 4, A_FaceTarget, S_CENTAUR_ATK3, 0, 0}, // S_CENTAUR_ATK2 {SPR_CENT, 9, 7, A_CentaurAttack, S_CENTAUR_WALK1, 0, 0}, // S_CENTAUR_ATK3 {SPR_CENT, 4, 10, A_FaceTarget, S_CENTAUR_MISSILE2, 0, 0}, // S_CENTAUR_MISSILE1 {SPR_CENT, 32773, 8, A_CentaurAttack2, S_CENTAUR_MISSILE3, 0, 0}, // S_CENTAUR_MISSILE2 {SPR_CENT, 4, 10, A_FaceTarget, S_CENTAUR_MISSILE4, 0, 0}, // S_CENTAUR_MISSILE3 {SPR_CENT, 32773, 8, A_CentaurAttack2, S_CENTAUR_WALK1, 0, 0}, // S_CENTAUR_MISSILE4 {SPR_CENT, 6, 6, A_Pain, S_CENTAUR_PAIN2, 0, 0}, // S_CENTAUR_PAIN1 {SPR_CENT, 6, 6, A_SetReflective, S_CENTAUR_PAIN3, 0, 0}, // S_CENTAUR_PAIN2 {SPR_CENT, 4, 15, A_CentaurDefend, S_CENTAUR_PAIN4, 0, 0}, // S_CENTAUR_PAIN3 {SPR_CENT, 4, 15, A_CentaurDefend, S_CENTAUR_PAIN5, 0, 0}, // S_CENTAUR_PAIN4 {SPR_CENT, 4, 15, A_CentaurDefend, S_CENTAUR_PAIN6, 0, 0}, // S_CENTAUR_PAIN5 {SPR_CENT, 4, 1, A_UnSetReflective, S_CENTAUR_WALK1, 0, 0}, // S_CENTAUR_PAIN6 {SPR_CENT, 10, 4, NULL, S_CENTAUR_DEATH2, 0, 0}, // S_CENTAUR_DEATH1 {SPR_CENT, 11, 4, A_Scream, S_CENTAUR_DEATH3, 0, 0}, // S_CENTAUR_DEATH2 {SPR_CENT, 12, 4, NULL, S_CENTAUR_DEATH4, 0, 0}, // S_CENTAUR_DEATH3 {SPR_CENT, 13, 4, NULL, S_CENTAUR_DEATH5, 0, 0}, // S_CENTAUR_DEATH4 {SPR_CENT, 14, 4, A_NoBlocking, S_CENTAUR_DEATH6, 0, 0}, // S_CENTAUR_DEATH5 {SPR_CENT, 15, 4, NULL, S_CENTAUR_DEATH7, 0, 0}, // S_CENTAUR_DEATH6 {SPR_CENT, 16, 4, NULL, S_CENTAUR_DEATH8, 0, 0}, // S_CENTAUR_DEATH7 {SPR_CENT, 17, 4, A_QueueCorpse, S_CENTAUR_DEATH9, 0, 0}, // S_CENTAUR_DEATH8 {SPR_CENT, 18, 4, NULL, S_CENTAUR_DEATH0, 0, 0}, // S_CENTAUR_DEATH9 {SPR_CENT, 19, -1, NULL, S_NULL, 0, 0}, // S_CENTAUR_DEATH0 {SPR_CTXD, 0, 4, NULL, S_CENTAUR_DEATH_X2, 0, 0}, // S_CENTAUR_DEATH_X1 {SPR_CTXD, 1, 4, A_NoBlocking, S_CENTAUR_DEATH_X3, 0, 0}, // S_CENTAUR_DEATH_X2 {SPR_CTXD, 2, 4, A_CentaurDropStuff, S_CENTAUR_DEATH_X4, 0, 0}, // S_CENTAUR_DEATH_X3 {SPR_CTXD, 3, 3, A_Scream, S_CENTAUR_DEATH_X5, 0, 0}, // S_CENTAUR_DEATH_X4 {SPR_CTXD, 4, 4, A_QueueCorpse, S_CENTAUR_DEATH_X6, 0, 0}, // S_CENTAUR_DEATH_X5 {SPR_CTXD, 5, 3, NULL, S_CENTAUR_DEATH_X7, 0, 0}, // S_CENTAUR_DEATH_X6 {SPR_CTXD, 6, 4, NULL, S_CENTAUR_DEATH_X8, 0, 0}, // S_CENTAUR_DEATH_X7 {SPR_CTXD, 7, 3, NULL, S_CENTAUR_DEATH_X9, 0, 0}, // S_CENTAUR_DEATH_X8 {SPR_CTXD, 8, 4, NULL, S_CENTAUR_DEATH_X10, 0, 0}, // S_CENTAUR_DEATH_X9 {SPR_CTXD, 9, 3, NULL, S_CENTAUR_DEATH_X11, 0, 0}, // S_CENTAUR_DEATH_X10 {SPR_CTXD, 10, -1, NULL, S_NULL, 0, 0}, // S_CENTAUR_DEATH_X11 {SPR_CENT, 20, 5, A_FreezeDeath, S_CENTAUR_ICE2, 0, 0}, // S_CENTAUR_ICE {SPR_CENT, 20, 1, A_FreezeDeathChunks, S_CENTAUR_ICE2, 0, 0}, // S_CENTAUR_ICE2 {SPR_CTFX, 32768, -1, NULL, S_NULL, 0, 0}, // S_CENTAUR_FX1 {SPR_CTFX, 32769, 4, NULL, S_CENTAUR_FX_X2, 0, 0}, // S_CENTAUR_FX_X1 {SPR_CTFX, 32770, 3, NULL, S_CENTAUR_FX_X3, 0, 0}, // S_CENTAUR_FX_X2 {SPR_CTFX, 32771, 4, NULL, S_CENTAUR_FX_X4, 0, 0}, // S_CENTAUR_FX_X3 {SPR_CTFX, 32772, 3, NULL, S_CENTAUR_FX_X5, 0, 0}, // S_CENTAUR_FX_X4 {SPR_CTFX, 32773, 2, NULL, S_NULL, 0, 0}, // S_CENTAUR_FX_X5 {SPR_CTDP, 0, 3, A_CheckFloor, S_CENTAUR_SHIELD2, 0, 0}, // S_CENTAUR_SHIELD1 {SPR_CTDP, 1, 3, A_CheckFloor, S_CENTAUR_SHIELD3, 0, 0}, // S_CENTAUR_SHIELD2 {SPR_CTDP, 2, 3, A_CheckFloor, S_CENTAUR_SHIELD4, 0, 0}, // S_CENTAUR_SHIELD3 {SPR_CTDP, 3, 3, A_CheckFloor, S_CENTAUR_SHIELD5, 0, 0}, // S_CENTAUR_SHIELD4 {SPR_CTDP, 4, 3, A_CheckFloor, S_CENTAUR_SHIELD6, 0, 0}, // S_CENTAUR_SHIELD5 {SPR_CTDP, 5, 3, A_CheckFloor, S_CENTAUR_SHIELD3, 0, 0}, // S_CENTAUR_SHIELD6 {SPR_CTDP, 6, 4, NULL, S_CENTAUR_SHIELD_X2, 0, 0}, // S_CENTAUR_SHIELD_X1 {SPR_CTDP, 7, 4, A_QueueCorpse, S_CENTAUR_SHIELD_X3, 0, 0}, // S_CENTAUR_SHIELD_X2 {SPR_CTDP, 8, 4, NULL, S_CENTAUR_SHIELD_X4, 0, 0}, // S_CENTAUR_SHIELD_X3 {SPR_CTDP, 9, -1, NULL, S_NULL, 0, 0}, // S_CENTAUR_SHIELD_X4 {SPR_CTDP, 10, 3, A_CheckFloor, S_CENTAUR_SWORD2, 0, 0}, // S_CENTAUR_SWORD1 {SPR_CTDP, 11, 3, A_CheckFloor, S_CENTAUR_SWORD3, 0, 0}, // S_CENTAUR_SWORD2 {SPR_CTDP, 12, 3, A_CheckFloor, S_CENTAUR_SWORD4, 0, 0}, // S_CENTAUR_SWORD3 {SPR_CTDP, 13, 3, A_CheckFloor, S_CENTAUR_SWORD5, 0, 0}, // S_CENTAUR_SWORD4 {SPR_CTDP, 14, 3, A_CheckFloor, S_CENTAUR_SWORD6, 0, 0}, // S_CENTAUR_SWORD5 {SPR_CTDP, 15, 3, A_CheckFloor, S_CENTAUR_SWORD7, 0, 0}, // S_CENTAUR_SWORD6 {SPR_CTDP, 16, 3, A_CheckFloor, S_CENTAUR_SWORD3, 0, 0}, // S_CENTAUR_SWORD7 {SPR_CTDP, 17, 4, NULL, S_CENTAUR_SWORD_X2, 0, 0}, // S_CENTAUR_SWORD_X1 {SPR_CTDP, 18, 4, A_QueueCorpse, S_CENTAUR_SWORD_X3, 0, 0}, // S_CENTAUR_SWORD_X2 {SPR_CTDP, 19, -1, NULL, S_NULL, 0, 0}, // S_CENTAUR_SWORD_X3 {SPR_DEMN, 0, 10, A_Look, S_DEMN_LOOK2, 0, 0}, // S_DEMN_LOOK1 {SPR_DEMN, 0, 10, A_Look, S_DEMN_LOOK1, 0, 0}, // S_DEMN_LOOK2 {SPR_DEMN, 0, 4, A_Chase, S_DEMN_CHASE2, 0, 0}, // S_DEMN_CHASE1 {SPR_DEMN, 1, 4, A_Chase, S_DEMN_CHASE3, 0, 0}, // S_DEMN_CHASE2 {SPR_DEMN, 2, 4, A_Chase, S_DEMN_CHASE4, 0, 0}, // S_DEMN_CHASE3 {SPR_DEMN, 3, 4, A_Chase, S_DEMN_CHASE1, 0, 0}, // S_DEMN_CHASE4 {SPR_DEMN, 4, 6, A_FaceTarget, S_DEMN_ATK1_2, 0, 0}, // S_DEMN_ATK1_1 {SPR_DEMN, 5, 8, A_FaceTarget, S_DEMN_ATK1_3, 0, 0}, // S_DEMN_ATK1_2 {SPR_DEMN, 6, 6, A_DemonAttack1, S_DEMN_CHASE1, 0, 0}, // S_DEMN_ATK1_3 {SPR_DEMN, 4, 5, A_FaceTarget, S_DEMN_ATK2_2, 0, 0}, // S_DEMN_ATK2_1 {SPR_DEMN, 5, 6, A_FaceTarget, S_DEMN_ATK2_3, 0, 0}, // S_DEMN_ATK2_2 {SPR_DEMN, 6, 5, A_DemonAttack2, S_DEMN_CHASE1, 0, 0}, // S_DEMN_ATK2_3 {SPR_DEMN, 4, 4, NULL, S_DEMN_PAIN2, 0, 0}, // S_DEMN_PAIN1 {SPR_DEMN, 4, 4, A_Pain, S_DEMN_CHASE1, 0, 0}, // S_DEMN_PAIN2 {SPR_DEMN, 7, 6, NULL, S_DEMN_DEATH2, 0, 0}, // S_DEMN_DEATH1 {SPR_DEMN, 8, 6, NULL, S_DEMN_DEATH3, 0, 0}, // S_DEMN_DEATH2 {SPR_DEMN, 9, 6, A_Scream, S_DEMN_DEATH4, 0, 0}, // S_DEMN_DEATH3 {SPR_DEMN, 10, 6, A_NoBlocking, S_DEMN_DEATH5, 0, 0}, // S_DEMN_DEATH4 {SPR_DEMN, 11, 6, A_QueueCorpse, S_DEMN_DEATH6, 0, 0}, // S_DEMN_DEATH5 {SPR_DEMN, 12, 6, NULL, S_DEMN_DEATH7, 0, 0}, // S_DEMN_DEATH6 {SPR_DEMN, 13, 6, NULL, S_DEMN_DEATH8, 0, 0}, // S_DEMN_DEATH7 {SPR_DEMN, 14, 6, NULL, S_DEMN_DEATH9, 0, 0}, // S_DEMN_DEATH8 {SPR_DEMN, 15, -1, NULL, S_NULL, 0, 0}, // S_DEMN_DEATH9 {SPR_DEMN, 7, 6, NULL, S_DEMN_XDEATH2, 0, 0}, // S_DEMN_XDEATH1 {SPR_DEMN, 8, 6, A_DemonDeath, S_DEMN_XDEATH3, 0, 0}, // S_DEMN_XDEATH2 {SPR_DEMN, 9, 6, A_Scream, S_DEMN_XDEATH4, 0, 0}, // S_DEMN_XDEATH3 {SPR_DEMN, 10, 6, A_NoBlocking, S_DEMN_XDEATH5, 0, 0}, // S_DEMN_XDEATH4 {SPR_DEMN, 11, 6, A_QueueCorpse, S_DEMN_XDEATH6, 0, 0}, // S_DEMN_XDEATH5 {SPR_DEMN, 12, 6, NULL, S_DEMN_XDEATH7, 0, 0}, // S_DEMN_XDEATH6 {SPR_DEMN, 13, 6, NULL, S_DEMN_XDEATH8, 0, 0}, // S_DEMN_XDEATH7 {SPR_DEMN, 14, 6, NULL, S_DEMN_XDEATH9, 0, 0}, // S_DEMN_XDEATH8 {SPR_DEMN, 15, -1, NULL, S_NULL, 0, 0}, // S_DEMN_XDEATH9 {SPR_DEMN, 16, 5, A_FreezeDeath, S_DEMON_ICE2, 0, 0}, // S_DEMON_ICE {SPR_DEMN, 16, 1, A_FreezeDeathChunks, S_DEMON_ICE2, 0, 0}, // S_DEMON_ICE2 {SPR_DEMA, 0, 4, NULL, S_DEMONCHUNK1_2, 0, 0}, // S_DEMONCHUNK1_1 {SPR_DEMA, 0, 10, A_QueueCorpse, S_DEMONCHUNK1_3, 0, 0}, // S_DEMONCHUNK1_2 {SPR_DEMA, 0, 20, NULL, S_DEMONCHUNK1_3, 0, 0}, // S_DEMONCHUNK1_3 {SPR_DEMA, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMONCHUNK1_4 {SPR_DEMB, 0, 4, NULL, S_DEMONCHUNK2_2, 0, 0}, // S_DEMONCHUNK2_1 {SPR_DEMB, 0, 10, A_QueueCorpse, S_DEMONCHUNK2_3, 0, 0}, // S_DEMONCHUNK2_2 {SPR_DEMB, 0, 20, NULL, S_DEMONCHUNK2_3, 0, 0}, // S_DEMONCHUNK2_3 {SPR_DEMB, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMONCHUNK2_4 {SPR_DEMC, 0, 4, NULL, S_DEMONCHUNK3_2, 0, 0}, // S_DEMONCHUNK3_1 {SPR_DEMC, 0, 10, A_QueueCorpse, S_DEMONCHUNK3_3, 0, 0}, // S_DEMONCHUNK3_2 {SPR_DEMC, 0, 20, NULL, S_DEMONCHUNK3_3, 0, 0}, // S_DEMONCHUNK3_3 {SPR_DEMC, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMONCHUNK3_4 {SPR_DEMD, 0, 4, NULL, S_DEMONCHUNK4_2, 0, 0}, // S_DEMONCHUNK4_1 {SPR_DEMD, 0, 10, A_QueueCorpse, S_DEMONCHUNK4_3, 0, 0}, // S_DEMONCHUNK4_2 {SPR_DEMD, 0, 20, NULL, S_DEMONCHUNK4_3, 0, 0}, // S_DEMONCHUNK4_3 {SPR_DEMD, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMONCHUNK4_4 {SPR_DEME, 0, 4, NULL, S_DEMONCHUNK5_2, 0, 0}, // S_DEMONCHUNK5_1 {SPR_DEME, 0, 10, A_QueueCorpse, S_DEMONCHUNK5_3, 0, 0}, // S_DEMONCHUNK5_2 {SPR_DEME, 0, 20, NULL, S_DEMONCHUNK5_3, 0, 0}, // S_DEMONCHUNK5_3 {SPR_DEME, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMONCHUNK5_4 {SPR_DMFX, 32768, 4, NULL, S_DEMONFX_MOVE2, 0, 0}, // S_DEMONFX_MOVE1 {SPR_DMFX, 32769, 4, NULL, S_DEMONFX_MOVE3, 0, 0}, // S_DEMONFX_MOVE2 {SPR_DMFX, 32770, 4, NULL, S_DEMONFX_MOVE1, 0, 0}, // S_DEMONFX_MOVE3 {SPR_DMFX, 32771, 4, NULL, S_DEMONFX_BOOM2, 0, 0}, // S_DEMONFX_BOOM1 {SPR_DMFX, 32772, 4, NULL, S_DEMONFX_BOOM3, 0, 0}, // S_DEMONFX_BOOM2 {SPR_DMFX, 32773, 3, NULL, S_DEMONFX_BOOM4, 0, 0}, // S_DEMONFX_BOOM3 {SPR_DMFX, 32774, 3, NULL, S_DEMONFX_BOOM5, 0, 0}, // S_DEMONFX_BOOM4 {SPR_DMFX, 32775, 3, NULL, S_NULL, 0, 0}, // S_DEMONFX_BOOM5 {SPR_DEM2, 0, 10, A_Look, S_DEMN2_LOOK2, 0, 0}, // S_DEMN2_LOOK1 {SPR_DEM2, 0, 10, A_Look, S_DEMN2_LOOK1, 0, 0}, // S_DEMN2_LOOK2 {SPR_DEM2, 0, 4, A_Chase, S_DEMN2_CHASE2, 0, 0}, // S_DEMN2_CHASE1 {SPR_DEM2, 1, 4, A_Chase, S_DEMN2_CHASE3, 0, 0}, // S_DEMN2_CHASE2 {SPR_DEM2, 2, 4, A_Chase, S_DEMN2_CHASE4, 0, 0}, // S_DEMN2_CHASE3 {SPR_DEM2, 3, 4, A_Chase, S_DEMN2_CHASE1, 0, 0}, // S_DEMN2_CHASE4 {SPR_DEM2, 4, 6, A_FaceTarget, S_DEMN2_ATK1_2, 0, 0}, // S_DEMN2_ATK1_1 {SPR_DEM2, 5, 8, A_FaceTarget, S_DEMN2_ATK1_3, 0, 0}, // S_DEMN2_ATK1_2 {SPR_DEM2, 6, 6, A_DemonAttack1, S_DEMN2_CHASE1, 0, 0}, // S_DEMN2_ATK1_3 {SPR_DEM2, 4, 5, A_FaceTarget, S_DEMN2_ATK2_2, 0, 0}, // S_DEMN2_ATK2_1 {SPR_DEM2, 5, 6, A_FaceTarget, S_DEMN2_ATK2_3, 0, 0}, // S_DEMN2_ATK2_2 {SPR_DEM2, 6, 5, A_DemonAttack2, S_DEMN2_CHASE1, 0, 0}, // S_DEMN2_ATK2_3 {SPR_DEM2, 4, 4, NULL, S_DEMN2_PAIN2, 0, 0}, // S_DEMN2_PAIN1 {SPR_DEM2, 4, 4, A_Pain, S_DEMN2_CHASE1, 0, 0}, // S_DEMN2_PAIN2 {SPR_DEM2, 7, 6, NULL, S_DEMN2_DEATH2, 0, 0}, // S_DEMN2_DEATH1 {SPR_DEM2, 8, 6, NULL, S_DEMN2_DEATH3, 0, 0}, // S_DEMN2_DEATH2 {SPR_DEM2, 9, 6, A_Scream, S_DEMN2_DEATH4, 0, 0}, // S_DEMN2_DEATH3 {SPR_DEM2, 10, 6, A_NoBlocking, S_DEMN2_DEATH5, 0, 0}, // S_DEMN2_DEATH4 {SPR_DEM2, 11, 6, A_QueueCorpse, S_DEMN2_DEATH6, 0, 0}, // S_DEMN2_DEATH5 {SPR_DEM2, 12, 6, NULL, S_DEMN2_DEATH7, 0, 0}, // S_DEMN2_DEATH6 {SPR_DEM2, 13, 6, NULL, S_DEMN2_DEATH8, 0, 0}, // S_DEMN2_DEATH7 {SPR_DEM2, 14, 6, NULL, S_DEMN2_DEATH9, 0, 0}, // S_DEMN2_DEATH8 {SPR_DEM2, 15, -1, NULL, S_NULL, 0, 0}, // S_DEMN2_DEATH9 {SPR_DEM2, 7, 6, NULL, S_DEMN2_XDEATH2, 0, 0}, // S_DEMN2_XDEATH1 {SPR_DEM2, 8, 6, A_Demon2Death, S_DEMN2_XDEATH3, 0, 0}, // S_DEMN2_XDEATH2 {SPR_DEM2, 9, 6, A_Scream, S_DEMN2_XDEATH4, 0, 0}, // S_DEMN2_XDEATH3 {SPR_DEM2, 10, 6, A_NoBlocking, S_DEMN2_XDEATH5, 0, 0}, // S_DEMN2_XDEATH4 {SPR_DEM2, 11, 6, A_QueueCorpse, S_DEMN2_XDEATH6, 0, 0}, // S_DEMN2_XDEATH5 {SPR_DEM2, 12, 6, NULL, S_DEMN2_XDEATH7, 0, 0}, // S_DEMN2_XDEATH6 {SPR_DEM2, 13, 6, NULL, S_DEMN2_XDEATH8, 0, 0}, // S_DEMN2_XDEATH7 {SPR_DEM2, 14, 6, NULL, S_DEMN2_XDEATH9, 0, 0}, // S_DEMN2_XDEATH8 {SPR_DEM2, 15, -1, NULL, S_NULL, 0, 0}, // S_DEMN2_XDEATH9 {SPR_DMBA, 0, 4, NULL, S_DEMON2CHUNK1_2, 0, 0}, // S_DEMON2CHUNK1_1 {SPR_DMBA, 0, 10, A_QueueCorpse, S_DEMON2CHUNK1_3, 0, 0}, // S_DEMON2CHUNK1_2 {SPR_DMBA, 0, 20, NULL, S_DEMON2CHUNK1_3, 0, 0}, // S_DEMON2CHUNK1_3 {SPR_DMBA, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMON2CHUNK1_4 {SPR_DMBB, 0, 4, NULL, S_DEMON2CHUNK2_2, 0, 0}, // S_DEMON2CHUNK2_1 {SPR_DMBB, 0, 10, A_QueueCorpse, S_DEMON2CHUNK2_3, 0, 0}, // S_DEMON2CHUNK2_2 {SPR_DMBB, 0, 20, NULL, S_DEMON2CHUNK2_3, 0, 0}, // S_DEMON2CHUNK2_3 {SPR_DMBB, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMON2CHUNK2_4 {SPR_DMBC, 0, 4, NULL, S_DEMON2CHUNK3_2, 0, 0}, // S_DEMON2CHUNK3_1 {SPR_DMBC, 0, 10, A_QueueCorpse, S_DEMON2CHUNK3_3, 0, 0}, // S_DEMON2CHUNK3_2 {SPR_DMBC, 0, 20, NULL, S_DEMON2CHUNK3_3, 0, 0}, // S_DEMON2CHUNK3_3 {SPR_DMBC, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMON2CHUNK3_4 {SPR_DMBD, 0, 4, NULL, S_DEMON2CHUNK4_2, 0, 0}, // S_DEMON2CHUNK4_1 {SPR_DMBD, 0, 10, A_QueueCorpse, S_DEMON2CHUNK4_3, 0, 0}, // S_DEMON2CHUNK4_2 {SPR_DMBD, 0, 20, NULL, S_DEMON2CHUNK4_3, 0, 0}, // S_DEMON2CHUNK4_3 {SPR_DMBD, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMON2CHUNK4_4 {SPR_DMBE, 0, 4, NULL, S_DEMON2CHUNK5_2, 0, 0}, // S_DEMON2CHUNK5_1 {SPR_DMBE, 0, 10, NULL, S_DEMON2CHUNK5_3, 0, 0}, // S_DEMON2CHUNK5_2 {SPR_DMBE, 0, 20, NULL, S_DEMON2CHUNK5_3, 0, 0}, // S_DEMON2CHUNK5_3 {SPR_DMBE, 0, -1, NULL, S_NULL, 0, 0}, // S_DEMON2CHUNK5_4 {SPR_D2FX, 32768, 4, NULL, S_DEMON2FX_MOVE2, 0, 0}, // S_DEMON2FX_MOVE1 {SPR_D2FX, 32769, 4, NULL, S_DEMON2FX_MOVE3, 0, 0}, // S_DEMON2FX_MOVE2 {SPR_D2FX, 32770, 4, NULL, S_DEMON2FX_MOVE4, 0, 0}, // S_DEMON2FX_MOVE3 {SPR_D2FX, 32771, 4, NULL, S_DEMON2FX_MOVE5, 0, 0}, // S_DEMON2FX_MOVE4 {SPR_D2FX, 32772, 4, NULL, S_DEMON2FX_MOVE6, 0, 0}, // S_DEMON2FX_MOVE5 {SPR_D2FX, 32773, 4, NULL, S_DEMON2FX_MOVE1, 0, 0}, // S_DEMON2FX_MOVE6 {SPR_D2FX, 32774, 4, NULL, S_DEMON2FX_BOOM2, 0, 0}, // S_DEMON2FX_BOOM1 {SPR_D2FX, 32775, 4, NULL, S_DEMON2FX_BOOM3, 0, 0}, // S_DEMON2FX_BOOM2 {SPR_D2FX, 32776, 4, NULL, S_DEMON2FX_BOOM4, 0, 0}, // S_DEMON2FX_BOOM3 {SPR_D2FX, 32777, 4, NULL, S_DEMON2FX_BOOM5, 0, 0}, // S_DEMON2FX_BOOM4 {SPR_D2FX, 32778, 3, NULL, S_DEMON2FX_BOOM6, 0, 0}, // S_DEMON2FX_BOOM5 {SPR_D2FX, 32779, 3, NULL, S_NULL, 0, 0}, // S_DEMON2FX_BOOM6 {SPR_WRTH, 0, 2, A_WraithRaiseInit, S_WRAITH_RAISE2, 0, 0}, // S_WRAITH_RAISE1 {SPR_WRTH, 0, 2, A_WraithRaise, S_WRAITH_RAISE3, 0, 0}, // S_WRAITH_RAISE2 {SPR_WRTH, 0, 2, A_FaceTarget, S_WRAITH_RAISE4, 0, 0}, // S_WRAITH_RAISE3 {SPR_WRTH, 1, 2, A_WraithRaise, S_WRAITH_RAISE5, 0, 0}, // S_WRAITH_RAISE4 {SPR_WRTH, 1, 2, A_WraithRaise, S_WRAITH_RAISE2, 0, 0}, // S_WRAITH_RAISE5 {SPR_WRTH, 0, 10, NULL, S_WRAITH_INIT2, 0, 0}, // S_WRAITH_INIT1 {SPR_WRTH, 1, 5, A_WraithInit, S_WRAITH_LOOK1, 0, 0}, // S_WRAITH_INIT2 {SPR_WRTH, 0, 15, A_WraithLook, S_WRAITH_LOOK2, 0, 0}, // S_WRAITH_LOOK1 {SPR_WRTH, 1, 15, A_WraithLook, S_WRAITH_LOOK1, 0, 0}, // S_WRAITH_LOOK2 {SPR_WRTH, 0, 4, A_WraithChase, S_WRAITH_CHASE2, 0, 0}, // S_WRAITH_CHASE1 {SPR_WRTH, 1, 4, A_WraithChase, S_WRAITH_CHASE3, 0, 0}, // S_WRAITH_CHASE2 {SPR_WRTH, 2, 4, A_WraithChase, S_WRAITH_CHASE4, 0, 0}, // S_WRAITH_CHASE3 {SPR_WRTH, 3, 4, A_WraithChase, S_WRAITH_CHASE1, 0, 0}, // S_WRAITH_CHASE4 {SPR_WRTH, 4, 6, A_FaceTarget, S_WRAITH_ATK1_2, 0, 0}, // S_WRAITH_ATK1_1 {SPR_WRTH, 5, 6, A_WraithFX3, S_WRAITH_ATK1_3, 0, 0}, // S_WRAITH_ATK1_2 {SPR_WRTH, 6, 6, A_WraithMelee, S_WRAITH_CHASE1, 0, 0}, // S_WRAITH_ATK1_3 {SPR_WRTH, 4, 6, A_FaceTarget, S_WRAITH_ATK2_2, 0, 0}, // S_WRAITH_ATK2_1 {SPR_WRTH, 5, 6, NULL, S_WRAITH_ATK2_3, 0, 0}, // S_WRAITH_ATK2_2 {SPR_WRTH, 6, 6, A_WraithMissile, S_WRAITH_CHASE1, 0, 0}, // S_WRAITH_ATK2_3 {SPR_WRTH, 0, 2, NULL, S_WRAITH_PAIN2, 0, 0}, // S_WRAITH_PAIN1 {SPR_WRTH, 7, 6, A_Pain, S_WRAITH_CHASE1, 0, 0}, // S_WRAITH_PAIN2 {SPR_WRTH, 8, 4, NULL, S_WRAITH_DEATH1_2, 0, 0}, // S_WRAITH_DEATH1_1 {SPR_WRTH, 9, 4, A_Scream, S_WRAITH_DEATH1_3, 0, 0}, // S_WRAITH_DEATH1_2 {SPR_WRTH, 10, 4, NULL, S_WRAITH_DEATH1_4, 0, 0}, // S_WRAITH_DEATH1_3 {SPR_WRTH, 11, 4, NULL, S_WRAITH_DEATH1_5, 0, 0}, // S_WRAITH_DEATH1_4 {SPR_WRTH, 12, 4, A_NoBlocking, S_WRAITH_DEATH1_6, 0, 0}, // S_WRAITH_DEATH1_5 {SPR_WRTH, 13, 4, A_QueueCorpse, S_WRAITH_DEATH1_7, 0, 0}, // S_WRAITH_DEATH1_6 {SPR_WRTH, 14, 4, NULL, S_WRAITH_DEATH1_8, 0, 0}, // S_WRAITH_DEATH1_7 {SPR_WRTH, 15, 5, NULL, S_WRAITH_DEATH1_9, 0, 0}, // S_WRAITH_DEATH1_8 {SPR_WRTH, 16, 5, NULL, S_WRAITH_DEATH1_0, 0, 0}, // S_WRAITH_DEATH1_9 {SPR_WRTH, 17, -1, NULL, S_NULL, 0, 0}, // S_WRAITH_DEATH1_0 {SPR_WRT2, 0, 5, NULL, S_WRAITH_DEATH2_2, 0, 0}, // S_WRAITH_DEATH2_1 {SPR_WRT2, 1, 5, A_Scream, S_WRAITH_DEATH2_3, 0, 0}, // S_WRAITH_DEATH2_2 {SPR_WRT2, 2, 5, NULL, S_WRAITH_DEATH2_4, 0, 0}, // S_WRAITH_DEATH2_3 {SPR_WRT2, 3, 5, NULL, S_WRAITH_DEATH2_5, 0, 0}, // S_WRAITH_DEATH2_4 {SPR_WRT2, 4, 5, A_NoBlocking, S_WRAITH_DEATH2_6, 0, 0}, // S_WRAITH_DEATH2_5 {SPR_WRT2, 5, 5, A_QueueCorpse, S_WRAITH_DEATH2_7, 0, 0}, // S_WRAITH_DEATH2_6 {SPR_WRT2, 6, 5, NULL, S_WRAITH_DEATH2_8, 0, 0}, // S_WRAITH_DEATH2_7 {SPR_WRT2, 7, -1, NULL, S_NULL, 0, 0}, // S_WRAITH_DEATH2_8 {SPR_WRT2, 8, 5, A_FreezeDeath, S_WRAITH_ICE2, 0, 0}, // S_WRAITH_ICE {SPR_WRT2, 8, 1, A_FreezeDeathChunks, S_WRAITH_ICE2, 0, 0}, // S_WRAITH_ICE2 {SPR_WRBL, 32768, 3, NULL, S_WRTHFX_MOVE2, 0, 0}, // S_WRTHFX_MOVE1 {SPR_WRBL, 32769, 3, A_WraithFX2, S_WRTHFX_MOVE3, 0, 0}, // S_WRTHFX_MOVE2 {SPR_WRBL, 32770, 3, NULL, S_WRTHFX_MOVE1, 0, 0}, // S_WRTHFX_MOVE3 {SPR_WRBL, 32771, 4, NULL, S_WRTHFX_BOOM2, 0, 0}, // S_WRTHFX_BOOM1 {SPR_WRBL, 32772, 4, A_WraithFX2, S_WRTHFX_BOOM3, 0, 0}, // S_WRTHFX_BOOM2 {SPR_WRBL, 32773, 4, NULL, S_WRTHFX_BOOM4, 0, 0}, // S_WRTHFX_BOOM3 {SPR_WRBL, 32774, 3, A_WraithFX2, S_WRTHFX_BOOM5, 0, 0}, // S_WRTHFX_BOOM4 {SPR_WRBL, 32775, 3, A_WraithFX2, S_WRTHFX_BOOM6, 0, 0}, // S_WRTHFX_BOOM5 {SPR_WRBL, 32776, 3, NULL, S_NULL, 0, 0}, // S_WRTHFX_BOOM6 {SPR_WRBL, 32777, 4, NULL, S_WRTHFX_SIZZLE2, 0, 0}, // S_WRTHFX_SIZZLE1 {SPR_WRBL, 32778, 4, NULL, S_WRTHFX_SIZZLE3, 0, 0}, // S_WRTHFX_SIZZLE2 {SPR_WRBL, 32779, 4, NULL, S_WRTHFX_SIZZLE4, 0, 0}, // S_WRTHFX_SIZZLE3 {SPR_WRBL, 32780, 4, NULL, S_WRTHFX_SIZZLE5, 0, 0}, // S_WRTHFX_SIZZLE4 {SPR_WRBL, 32781, 4, NULL, S_WRTHFX_SIZZLE6, 0, 0}, // S_WRTHFX_SIZZLE5 {SPR_WRBL, 32782, 4, NULL, S_WRTHFX_SIZZLE7, 0, 0}, // S_WRTHFX_SIZZLE6 {SPR_WRBL, 32783, 4, NULL, S_NULL, 0, 0}, // S_WRTHFX_SIZZLE7 {SPR_WRBL, 32784, 4, NULL, S_WRTHFX_DROP2, 0, 0}, // S_WRTHFX_DROP1 {SPR_WRBL, 32785, 4, NULL, S_WRTHFX_DROP3, 0, 0}, // S_WRTHFX_DROP2 {SPR_WRBL, 32786, 4, NULL, S_WRTHFX_DROP1, 0, 0}, // S_WRTHFX_DROP3 {SPR_WRBL, 32786, 4, NULL, S_NULL, 0, 0}, // S_WRTHFX_DEAD1 {SPR_WRBL, 19, 4, NULL, S_WRTHFX_ADROP2, 0, 0}, // S_WRTHFX_ADROP1 {SPR_WRBL, 20, 4, NULL, S_WRTHFX_ADROP3, 0, 0}, // S_WRTHFX_ADROP2 {SPR_WRBL, 21, 4, NULL, S_WRTHFX_ADROP4, 0, 0}, // S_WRTHFX_ADROP3 {SPR_WRBL, 22, 4, NULL, S_WRTHFX_ADROP1, 0, 0}, // S_WRTHFX_ADROP4 {SPR_WRBL, 22, 10, NULL, S_NULL, 0, 0}, // S_WRTHFX_ADEAD1 {SPR_WRBL, 23, 7, NULL, S_WRTHFX_BDROP2, 0, 0}, // S_WRTHFX_BDROP1 {SPR_WRBL, 24, 7, NULL, S_WRTHFX_BDROP3, 0, 0}, // S_WRTHFX_BDROP2 {SPR_WRBL, 25, 7, NULL, S_WRTHFX_BDROP1, 0, 0}, // S_WRTHFX_BDROP3 {SPR_WRBL, 25, 35, NULL, S_NULL, 0, 0}, // S_WRTHFX_BDEAD1 {SPR_MNTR, 0, 15, NULL, S_MNTR_SPAWN2, 0, 0}, // S_MNTR_SPAWN1 {SPR_MNTR, 0, 15, A_MinotaurFade1, S_MNTR_SPAWN3, 0, 0}, // S_MNTR_SPAWN2 {SPR_MNTR, 0, 3, A_MinotaurFade2, S_MNTR_LOOK1, 0, 0}, // S_MNTR_SPAWN3 {SPR_MNTR, 0, 10, A_MinotaurLook, S_MNTR_LOOK2, 0, 0}, // S_MNTR_LOOK1 {SPR_MNTR, 1, 10, A_MinotaurLook, S_MNTR_LOOK1, 0, 0}, // S_MNTR_LOOK2 {SPR_MNTR, 0, 5, A_MinotaurChase, S_MNTR_WALK2, 0, 0}, // S_MNTR_WALK1 {SPR_MNTR, 1, 5, A_MinotaurChase, S_MNTR_WALK3, 0, 0}, // S_MNTR_WALK2 {SPR_MNTR, 2, 5, A_MinotaurChase, S_MNTR_WALK4, 0, 0}, // S_MNTR_WALK3 {SPR_MNTR, 3, 5, A_MinotaurChase, S_MNTR_WALK1, 0, 0}, // S_MNTR_WALK4 {SPR_MNTR, 0, 5, A_MinotaurRoam, S_MNTR_ROAM2, 0, 0}, // S_MNTR_ROAM1 {SPR_MNTR, 1, 5, A_MinotaurRoam, S_MNTR_ROAM3, 0, 0}, // S_MNTR_ROAM2 {SPR_MNTR, 2, 5, A_MinotaurRoam, S_MNTR_ROAM4, 0, 0}, // S_MNTR_ROAM3 {SPR_MNTR, 3, 5, A_MinotaurRoam, S_MNTR_ROAM1, 0, 0}, // S_MNTR_ROAM4 {SPR_MNTR, 6, 10, A_FaceTarget, S_MNTR_ATK1_2, 0, 0}, // S_MNTR_ATK1_1 {SPR_MNTR, 7, 7, A_FaceTarget, S_MNTR_ATK1_3, 0, 0}, // S_MNTR_ATK1_2 {SPR_MNTR, 8, 12, A_MinotaurAtk1, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK1_3 {SPR_MNTR, 6, 10, A_MinotaurDecide, S_MNTR_ATK2_2, 0, 0}, // S_MNTR_ATK2_1 {SPR_MNTR, 9, 4, A_FaceTarget, S_MNTR_ATK2_3, 0, 0}, // S_MNTR_ATK2_2 {SPR_MNTR, 10, 9, A_MinotaurAtk2, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK2_3 {SPR_MNTR, 6, 10, A_FaceTarget, S_MNTR_ATK3_2, 0, 0}, // S_MNTR_ATK3_1 {SPR_MNTR, 7, 7, A_FaceTarget, S_MNTR_ATK3_3, 0, 0}, // S_MNTR_ATK3_2 {SPR_MNTR, 8, 12, A_MinotaurAtk3, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK3_3 {SPR_MNTR, 8, 12, NULL, S_MNTR_ATK3_1, 0, 0}, // S_MNTR_ATK3_4 {SPR_MNTR, 5, 2, A_MinotaurCharge, S_MNTR_ATK4_1, 0, 0}, // S_MNTR_ATK4_1 {SPR_MNTR, 4, 3, NULL, S_MNTR_PAIN2, 0, 0}, // S_MNTR_PAIN1 {SPR_MNTR, 4, 6, A_Pain, S_MNTR_WALK1, 0, 0}, // S_MNTR_PAIN2 {SPR_MNTR, 4, 6, NULL, S_MNTR_DIE2, 0, 0}, // S_MNTR_DIE1 {SPR_MNTR, 4, 2, A_Scream, S_MNTR_DIE3, 0, 0}, // S_MNTR_DIE2 {SPR_MNTR, 4, 5, A_SmokePuffExit, S_MNTR_DIE4, 0, 0}, // S_MNTR_DIE3 {SPR_MNTR, 4, 5, NULL, S_MNTR_DIE5, 0, 0}, // S_MNTR_DIE4 {SPR_MNTR, 4, 5, A_NoBlocking, S_MNTR_DIE6, 0, 0}, // S_MNTR_DIE5 {SPR_MNTR, 4, 5, NULL, S_MNTR_DIE7, 0, 0}, // S_MNTR_DIE6 {SPR_MNTR, 4, 5, A_MinotaurFade1, S_MNTR_DIE8, 0, 0}, // S_MNTR_DIE7 {SPR_MNTR, 4, 5, A_MinotaurFade0, S_MNTR_DIE9, 0, 0}, // S_MNTR_DIE8 {SPR_MNTR, 4, 10, NULL, S_NULL, 0, 0}, // S_MNTR_DIE9 {SPR_FX12, 32768, 6, NULL, S_MNTRFX1_2, 0, 0}, // S_MNTRFX1_1 {SPR_FX12, 32769, 6, NULL, S_MNTRFX1_1, 0, 0}, // S_MNTRFX1_2 {SPR_FX12, 32770, 5, NULL, S_MNTRFXI1_2, 0, 0}, // S_MNTRFXI1_1 {SPR_FX12, 32771, 5, NULL, S_MNTRFXI1_3, 0, 0}, // S_MNTRFXI1_2 {SPR_FX12, 32772, 5, NULL, S_MNTRFXI1_4, 0, 0}, // S_MNTRFXI1_3 {SPR_FX12, 32773, 5, NULL, S_MNTRFXI1_5, 0, 0}, // S_MNTRFXI1_4 {SPR_FX12, 32774, 5, NULL, S_MNTRFXI1_6, 0, 0}, // S_MNTRFXI1_5 {SPR_FX12, 32775, 5, NULL, S_NULL, 0, 0}, // S_MNTRFXI1_6 {SPR_FX13, 0, 2, A_MntrFloorFire, S_MNTRFX2_1, 0, 0}, // S_MNTRFX2_1 {SPR_FX13, 32776, 4, A_Explode, S_MNTRFXI2_2, 0, 0}, // S_MNTRFXI2_1 {SPR_FX13, 32777, 4, NULL, S_MNTRFXI2_3, 0, 0}, // S_MNTRFXI2_2 {SPR_FX13, 32778, 4, NULL, S_MNTRFXI2_4, 0, 0}, // S_MNTRFXI2_3 {SPR_FX13, 32779, 4, NULL, S_MNTRFXI2_5, 0, 0}, // S_MNTRFXI2_4 {SPR_FX13, 32780, 4, NULL, S_NULL, 0, 0}, // S_MNTRFXI2_5 {SPR_FX13, 32771, 4, NULL, S_MNTRFX3_2, 0, 0}, // S_MNTRFX3_1 {SPR_FX13, 32770, 4, NULL, S_MNTRFX3_3, 0, 0}, // S_MNTRFX3_2 {SPR_FX13, 32769, 5, NULL, S_MNTRFX3_4, 0, 0}, // S_MNTRFX3_3 {SPR_FX13, 32770, 5, NULL, S_MNTRFX3_5, 0, 0}, // S_MNTRFX3_4 {SPR_FX13, 32771, 5, NULL, S_MNTRFX3_6, 0, 0}, // S_MNTRFX3_5 {SPR_FX13, 32772, 5, NULL, S_MNTRFX3_7, 0, 0}, // S_MNTRFX3_6 {SPR_FX13, 32773, 4, NULL, S_MNTRFX3_8, 0, 0}, // S_MNTRFX3_7 {SPR_FX13, 32774, 4, NULL, S_MNTRFX3_9, 0, 0}, // S_MNTRFX3_8 {SPR_FX13, 32775, 4, NULL, S_NULL, 0, 0}, // S_MNTRFX3_9 {SPR_MNSM, 0, 3, NULL, S_MINOSMOKE2, 0, 0}, // S_MINOSMOKE1 {SPR_MNSM, 1, 3, NULL, S_MINOSMOKE3, 0, 0}, // S_MINOSMOKE2 {SPR_MNSM, 2, 3, NULL, S_MINOSMOKE4, 0, 0}, // S_MINOSMOKE3 {SPR_MNSM, 3, 3, NULL, S_MINOSMOKE5, 0, 0}, // S_MINOSMOKE4 {SPR_MNSM, 4, 3, NULL, S_MINOSMOKE6, 0, 0}, // S_MINOSMOKE5 {SPR_MNSM, 5, 3, NULL, S_MINOSMOKE7, 0, 0}, // S_MINOSMOKE6 {SPR_MNSM, 6, 3, NULL, S_MINOSMOKE8, 0, 0}, // S_MINOSMOKE7 {SPR_MNSM, 7, 3, NULL, S_MINOSMOKE9, 0, 0}, // S_MINOSMOKE8 {SPR_MNSM, 8, 3, NULL, S_MINOSMOKE0, 0, 0}, // S_MINOSMOKE9 {SPR_MNSM, 9, 3, NULL, S_MINOSMOKEA, 0, 0}, // S_MINOSMOKE0 {SPR_MNSM, 10, 3, NULL, S_MINOSMOKEB, 0, 0}, // S_MINOSMOKEA {SPR_MNSM, 11, 3, NULL, S_MINOSMOKEC, 0, 0}, // S_MINOSMOKEB {SPR_MNSM, 12, 3, NULL, S_MINOSMOKED, 0, 0}, // S_MINOSMOKEC {SPR_MNSM, 13, 3, NULL, S_MINOSMOKEE, 0, 0}, // S_MINOSMOKED {SPR_MNSM, 14, 3, NULL, S_MINOSMOKEF, 0, 0}, // S_MINOSMOKEE {SPR_MNSM, 15, 3, NULL, S_MINOSMOKEG, 0, 0}, // S_MINOSMOKEF {SPR_MNSM, 16, 3, NULL, S_NULL, 0, 0}, // S_MINOSMOKEG {SPR_MNSM, 0, 3, NULL, S_MINOSMOKEX2, 0, 0}, // S_MINOSMOKEX1 {SPR_MNSM, 1, 3, NULL, S_MINOSMOKEX3, 0, 0}, // S_MINOSMOKEX2 {SPR_MNSM, 2, 3, NULL, S_MINOSMOKEX4, 0, 0}, // S_MINOSMOKEX3 {SPR_MNSM, 3, 3, NULL, S_MINOSMOKEX5, 0, 0}, // S_MINOSMOKEX4 {SPR_MNSM, 4, 3, NULL, S_MINOSMOKEX6, 0, 0}, // S_MINOSMOKEX5 {SPR_MNSM, 5, 3, NULL, S_MINOSMOKEX7, 0, 0}, // S_MINOSMOKEX6 {SPR_MNSM, 6, 3, NULL, S_MINOSMOKEX8, 0, 0}, // S_MINOSMOKEX7 {SPR_MNSM, 7, 3, NULL, S_MINOSMOKEX9, 0, 0}, // S_MINOSMOKEX8 {SPR_MNSM, 8, 3, NULL, S_MINOSMOKEX0, 0, 0}, // S_MINOSMOKEX9 {SPR_MNSM, 9, 3, NULL, S_MINOSMOKEXA, 0, 0}, // S_MINOSMOKEX0 {SPR_MNSM, 8, 3, NULL, S_MINOSMOKEXB, 0, 0}, // S_MINOSMOKEXA {SPR_MNSM, 7, 3, NULL, S_MINOSMOKEXC, 0, 0}, // S_MINOSMOKEXB {SPR_MNSM, 6, 3, NULL, S_MINOSMOKEXD, 0, 0}, // S_MINOSMOKEXC {SPR_MNSM, 5, 3, NULL, S_MINOSMOKEXE, 0, 0}, // S_MINOSMOKEXD {SPR_MNSM, 4, 3, NULL, S_MINOSMOKEXF, 0, 0}, // S_MINOSMOKEXE {SPR_MNSM, 3, 3, NULL, S_MINOSMOKEXG, 0, 0}, // S_MINOSMOKEXF {SPR_MNSM, 2, 3, NULL, S_MINOSMOKEXH, 0, 0}, // S_MINOSMOKEXG {SPR_MNSM, 1, 3, NULL, S_MINOSMOKEXI, 0, 0}, // S_MINOSMOKEXH {SPR_MNSM, 0, 3, NULL, S_NULL, 0, 0}, // S_MINOSMOKEXI {SPR_SSPT, 7, 10, A_Look, S_SERPENT_LOOK1, 0, 0}, // S_SERPENT_LOOK1 {SPR_SSPT, 7, 1, A_SerpentChase, S_SERPENT_SWIM2, 0, 0}, // S_SERPENT_SWIM1 {SPR_SSPT, 7, 1, A_SerpentChase, S_SERPENT_SWIM3, 0, 0}, // S_SERPENT_SWIM2 {SPR_SSPT, 7, 2, A_SerpentHumpDecide, S_SERPENT_SWIM1, 0, 0}, // S_SERPENT_SWIM3 {SPR_SSPT, 7, 3, A_SerpentUnHide, S_SERPENT_HUMP2, 0, 0}, // S_SERPENT_HUMP1 {SPR_SSPT, 4, 3, A_SerpentRaiseHump, S_SERPENT_HUMP3, 0, 0}, // S_SERPENT_HUMP2 {SPR_SSPT, 5, 3, A_SerpentRaiseHump, S_SERPENT_HUMP4, 0, 0}, // S_SERPENT_HUMP3 {SPR_SSPT, 6, 3, A_SerpentRaiseHump, S_SERPENT_HUMP5, 0, 0}, // S_SERPENT_HUMP4 {SPR_SSPT, 4, 3, A_SerpentRaiseHump, S_SERPENT_HUMP6, 0, 0}, // S_SERPENT_HUMP5 {SPR_SSPT, 5, 3, A_SerpentRaiseHump, S_SERPENT_HUMP7, 0, 0}, // S_SERPENT_HUMP6 {SPR_SSPT, 6, 3, NULL, S_SERPENT_HUMP8, 0, 0}, // S_SERPENT_HUMP7 {SPR_SSPT, 4, 3, NULL, S_SERPENT_HUMP9, 0, 0}, // S_SERPENT_HUMP8 {SPR_SSPT, 5, 3, NULL, S_SERPENT_HUMP10, 0, 0}, // S_SERPENT_HUMP9 {SPR_SSPT, 6, 3, A_SerpentLowerHump, S_SERPENT_HUMP11, 0, 0}, // S_SERPENT_HUMP10 {SPR_SSPT, 4, 3, A_SerpentLowerHump, S_SERPENT_HUMP12, 0, 0}, // S_SERPENT_HUMP11 {SPR_SSPT, 5, 3, A_SerpentLowerHump, S_SERPENT_HUMP13, 0, 0}, // S_SERPENT_HUMP12 {SPR_SSPT, 6, 3, A_SerpentLowerHump, S_SERPENT_HUMP14, 0, 0}, // S_SERPENT_HUMP13 {SPR_SSPT, 4, 3, A_SerpentLowerHump, S_SERPENT_HUMP15, 0, 0}, // S_SERPENT_HUMP14 {SPR_SSPT, 5, 3, A_SerpentHide, S_SERPENT_SWIM1, 0, 0}, // S_SERPENT_HUMP15 {SPR_SSPT, 0, 1, A_UnHideThing, S_SERPENT_SURFACE2, 0, 0}, // S_SERPENT_SURFACE1 {SPR_SSPT, 0, 1, A_SerpentBirthScream, S_SERPENT_SURFACE3, 0, 0}, // S_SERPENT_SURFACE2 {SPR_SSPT, 1, 3, A_SetShootable, S_SERPENT_SURFACE4, 0, 0}, // S_SERPENT_SURFACE3 {SPR_SSPT, 2, 3, NULL, S_SERPENT_SURFACE5, 0, 0}, // S_SERPENT_SURFACE4 {SPR_SSPT, 3, 4, A_SerpentCheckForAttack, S_SERPENT_DIVE1, 0, 0}, // S_SERPENT_SURFACE5 {SPR_SSDV, 0, 4, NULL, S_SERPENT_DIVE2, 0, 0}, // S_SERPENT_DIVE1 {SPR_SSDV, 1, 4, NULL, S_SERPENT_DIVE3, 0, 0}, // S_SERPENT_DIVE2 {SPR_SSDV, 2, 4, NULL, S_SERPENT_DIVE4, 0, 0}, // S_SERPENT_DIVE3 {SPR_SSDV, 3, 4, A_UnSetShootable, S_SERPENT_DIVE5, 0, 0}, // S_SERPENT_DIVE4 {SPR_SSDV, 4, 3, A_SerpentDiveSound, S_SERPENT_DIVE6, 0, 0}, // S_SERPENT_DIVE5 {SPR_SSDV, 5, 3, NULL, S_SERPENT_DIVE7, 0, 0}, // S_SERPENT_DIVE6 {SPR_SSDV, 6, 4, NULL, S_SERPENT_DIVE8, 0, 0}, // S_SERPENT_DIVE7 {SPR_SSDV, 7, 4, NULL, S_SERPENT_DIVE9, 0, 0}, // S_SERPENT_DIVE8 {SPR_SSDV, 8, 3, NULL, S_SERPENT_DIVE10, 0, 0}, // S_SERPENT_DIVE9 {SPR_SSDV, 9, 3, A_SerpentHide, S_SERPENT_SWIM1, 0, 0}, // S_SERPENT_DIVE10 {SPR_SSPT, 8, 5, A_SerpentWalk, S_SERPENT_WALK2, 0, 0}, // S_SERPENT_WALK1 {SPR_SSPT, 9, 5, A_SerpentWalk, S_SERPENT_WALK3, 0, 0}, // S_SERPENT_WALK2 {SPR_SSPT, 8, 5, A_SerpentWalk, S_SERPENT_WALK4, 0, 0}, // S_SERPENT_WALK3 {SPR_SSPT, 9, 5, A_SerpentCheckForAttack, S_SERPENT_DIVE1, 0, 0}, // S_SERPENT_WALK4 {SPR_SSPT, 11, 5, NULL, S_SERPENT_PAIN2, 0, 0}, // S_SERPENT_PAIN1 {SPR_SSPT, 11, 5, A_Pain, S_SERPENT_DIVE1, 0, 0}, // S_SERPENT_PAIN2 {SPR_SSPT, 10, 6, A_FaceTarget, S_SERPENT_ATK2, 0, 0}, // S_SERPENT_ATK1 {SPR_SSPT, 11, 5, A_SerpentChooseAttack, S_SERPENT_MELEE1, 0, 0}, // S_SERPENT_ATK2 {SPR_SSPT, 13, 5, A_SerpentMeleeAttack, S_SERPENT_DIVE1, 0, 0}, // S_SERPENT_MELEE1 {SPR_SSPT, 13, 5, A_SerpentMissileAttack, S_SERPENT_DIVE1, 0, 0}, // S_SERPENT_MISSILE1 {SPR_SSPT, 14, 4, NULL, S_SERPENT_DIE2, 0, 0}, // S_SERPENT_DIE1 {SPR_SSPT, 15, 4, A_Scream, S_SERPENT_DIE3, 0, 0}, // S_SERPENT_DIE2 {SPR_SSPT, 16, 4, A_NoBlocking, S_SERPENT_DIE4, 0, 0}, // S_SERPENT_DIE3 {SPR_SSPT, 17, 4, NULL, S_SERPENT_DIE5, 0, 0}, // S_SERPENT_DIE4 {SPR_SSPT, 18, 4, NULL, S_SERPENT_DIE6, 0, 0}, // S_SERPENT_DIE5 {SPR_SSPT, 19, 4, NULL, S_SERPENT_DIE7, 0, 0}, // S_SERPENT_DIE6 {SPR_SSPT, 20, 4, NULL, S_SERPENT_DIE8, 0, 0}, // S_SERPENT_DIE7 {SPR_SSPT, 21, 4, NULL, S_SERPENT_DIE9, 0, 0}, // S_SERPENT_DIE8 {SPR_SSPT, 22, 4, NULL, S_SERPENT_DIE10, 0, 0}, // S_SERPENT_DIE9 {SPR_SSPT, 23, 4, NULL, S_SERPENT_DIE11, 0, 0}, // S_SERPENT_DIE10 {SPR_SSPT, 24, 4, NULL, S_SERPENT_DIE12, 0, 0}, // S_SERPENT_DIE11 {SPR_SSPT, 25, 4, NULL, S_NULL, 0, 0}, // S_SERPENT_DIE12 {SPR_SSXD, 0, 4, NULL, S_SERPENT_XDIE2, 0, 0}, // S_SERPENT_XDIE1 {SPR_SSXD, 1, 4, A_SerpentHeadPop, S_SERPENT_XDIE3, 0, 0}, // S_SERPENT_XDIE2 {SPR_SSXD, 2, 4, A_NoBlocking, S_SERPENT_XDIE4, 0, 0}, // S_SERPENT_XDIE3 {SPR_SSXD, 3, 4, NULL, S_SERPENT_XDIE5, 0, 0}, // S_SERPENT_XDIE4 {SPR_SSXD, 4, 4, NULL, S_SERPENT_XDIE6, 0, 0}, // S_SERPENT_XDIE5 {SPR_SSXD, 5, 3, NULL, S_SERPENT_XDIE7, 0, 0}, // S_SERPENT_XDIE6 {SPR_SSXD, 6, 3, NULL, S_SERPENT_XDIE8, 0, 0}, // S_SERPENT_XDIE7 {SPR_SSXD, 7, 3, A_SerpentSpawnGibs, S_NULL, 0, 0}, // S_SERPENT_XDIE8 {SPR_SSPT, 26, 5, A_FreezeDeath, S_SERPENT_ICE2, 0, 0}, // S_SERPENT_ICE {SPR_SSPT, 26, 1, A_FreezeDeathChunks, S_SERPENT_ICE2, 0, 0}, // S_SERPENT_ICE2 {SPR_SSFX, 32768, 3, A_ContMobjSound, S_SERPENT_FX2, 0, 0}, // S_SERPENT_FX1 {SPR_SSFX, 32769, 3, NULL, S_SERPENT_FX3, 0, 0}, // S_SERPENT_FX2 {SPR_SSFX, 32768, 3, NULL, S_SERPENT_FX4, 0, 0}, // S_SERPENT_FX3 {SPR_SSFX, 32769, 3, NULL, S_SERPENT_FX1, 0, 0}, // S_SERPENT_FX4 {SPR_SSFX, 32770, 4, NULL, S_SERPENT_FX_X2, 0, 0}, // S_SERPENT_FX_X1 {SPR_SSFX, 32771, 4, NULL, S_SERPENT_FX_X3, 0, 0}, // S_SERPENT_FX_X2 {SPR_SSFX, 32772, 4, NULL, S_SERPENT_FX_X4, 0, 0}, // S_SERPENT_FX_X3 {SPR_SSFX, 32773, 4, NULL, S_SERPENT_FX_X5, 0, 0}, // S_SERPENT_FX_X4 {SPR_SSFX, 32774, 4, NULL, S_SERPENT_FX_X6, 0, 0}, // S_SERPENT_FX_X5 {SPR_SSFX, 32775, 4, NULL, S_NULL, 0, 0}, // S_SERPENT_FX_X6 {SPR_SSXD, 8, 4, A_SerpentHeadCheck, S_SERPENT_HEAD2, 0, 0}, // S_SERPENT_HEAD1 {SPR_SSXD, 9, 4, A_SerpentHeadCheck, S_SERPENT_HEAD3, 0, 0}, // S_SERPENT_HEAD2 {SPR_SSXD, 10, 4, A_SerpentHeadCheck, S_SERPENT_HEAD4, 0, 0}, // S_SERPENT_HEAD3 {SPR_SSXD, 11, 4, A_SerpentHeadCheck, S_SERPENT_HEAD5, 0, 0}, // S_SERPENT_HEAD4 {SPR_SSXD, 12, 4, A_SerpentHeadCheck, S_SERPENT_HEAD6, 0, 0}, // S_SERPENT_HEAD5 {SPR_SSXD, 13, 4, A_SerpentHeadCheck, S_SERPENT_HEAD7, 0, 0}, // S_SERPENT_HEAD6 {SPR_SSXD, 14, 4, A_SerpentHeadCheck, S_SERPENT_HEAD8, 0, 0}, // S_SERPENT_HEAD7 {SPR_SSXD, 15, 4, A_SerpentHeadCheck, S_SERPENT_HEAD1, 0, 0}, // S_SERPENT_HEAD8 {SPR_SSXD, 18, -1, NULL, S_SERPENT_HEAD_X1, 0, 0}, // S_SERPENT_HEAD_X1 {SPR_SSXD, 16, 6, NULL, S_SERPENT_GIB1_2, 0, 0}, // S_SERPENT_GIB1_1 {SPR_SSXD, 16, 6, A_FloatGib, S_SERPENT_GIB1_3, 0, 0}, // S_SERPENT_GIB1_2 {SPR_SSXD, 16, 8, A_FloatGib, S_SERPENT_GIB1_4, 0, 0}, // S_SERPENT_GIB1_3 {SPR_SSXD, 16, 8, A_FloatGib, S_SERPENT_GIB1_5, 0, 0}, // S_SERPENT_GIB1_4 {SPR_SSXD, 16, 12, A_FloatGib, S_SERPENT_GIB1_6, 0, 0}, // S_SERPENT_GIB1_5 {SPR_SSXD, 16, 12, A_FloatGib, S_SERPENT_GIB1_7, 0, 0}, // S_SERPENT_GIB1_6 {SPR_SSXD, 16, 232, A_DelayGib, S_SERPENT_GIB1_8, 0, 0}, // S_SERPENT_GIB1_7 {SPR_SSXD, 16, 12, A_SinkGib, S_SERPENT_GIB1_9, 0, 0}, // S_SERPENT_GIB1_8 {SPR_SSXD, 16, 12, A_SinkGib, S_SERPENT_GIB1_10, 0, 0}, // S_SERPENT_GIB1_9 {SPR_SSXD, 16, 8, A_SinkGib, S_SERPENT_GIB1_11, 0, 0}, // S_SERPENT_GIB1_10 {SPR_SSXD, 16, 8, A_SinkGib, S_SERPENT_GIB1_12, 0, 0}, // S_SERPENT_GIB1_11 {SPR_SSXD, 16, 8, A_SinkGib, S_NULL, 0, 0}, // S_SERPENT_GIB1_12 {SPR_SSXD, 17, 6, NULL, S_SERPENT_GIB2_2, 0, 0}, // S_SERPENT_GIB2_1 {SPR_SSXD, 17, 6, A_FloatGib, S_SERPENT_GIB2_3, 0, 0}, // S_SERPENT_GIB2_2 {SPR_SSXD, 17, 8, A_FloatGib, S_SERPENT_GIB2_4, 0, 0}, // S_SERPENT_GIB2_3 {SPR_SSXD, 17, 8, A_FloatGib, S_SERPENT_GIB2_5, 0, 0}, // S_SERPENT_GIB2_4 {SPR_SSXD, 17, 12, A_FloatGib, S_SERPENT_GIB2_6, 0, 0}, // S_SERPENT_GIB2_5 {SPR_SSXD, 17, 12, A_FloatGib, S_SERPENT_GIB2_7, 0, 0}, // S_SERPENT_GIB2_6 {SPR_SSXD, 17, 232, A_DelayGib, S_SERPENT_GIB2_8, 0, 0}, // S_SERPENT_GIB2_7 {SPR_SSXD, 17, 12, A_SinkGib, S_SERPENT_GIB2_9, 0, 0}, // S_SERPENT_GIB2_8 {SPR_SSXD, 17, 12, A_SinkGib, S_SERPENT_GIB2_10, 0, 0}, // S_SERPENT_GIB2_9 {SPR_SSXD, 17, 8, A_SinkGib, S_SERPENT_GIB2_11, 0, 0}, // S_SERPENT_GIB2_10 {SPR_SSXD, 17, 8, A_SinkGib, S_SERPENT_GIB2_12, 0, 0}, // S_SERPENT_GIB2_11 {SPR_SSXD, 17, 8, A_SinkGib, S_NULL, 0, 0}, // S_SERPENT_GIB2_12 {SPR_SSXD, 19, 6, NULL, S_SERPENT_GIB3_2, 0, 0}, // S_SERPENT_GIB3_1 {SPR_SSXD, 19, 6, A_FloatGib, S_SERPENT_GIB3_3, 0, 0}, // S_SERPENT_GIB3_2 {SPR_SSXD, 19, 8, A_FloatGib, S_SERPENT_GIB3_4, 0, 0}, // S_SERPENT_GIB3_3 {SPR_SSXD, 19, 8, A_FloatGib, S_SERPENT_GIB3_5, 0, 0}, // S_SERPENT_GIB3_4 {SPR_SSXD, 19, 12, A_FloatGib, S_SERPENT_GIB3_6, 0, 0}, // S_SERPENT_GIB3_5 {SPR_SSXD, 19, 12, A_FloatGib, S_SERPENT_GIB3_7, 0, 0}, // S_SERPENT_GIB3_6 {SPR_SSXD, 19, 232, A_DelayGib, S_SERPENT_GIB3_8, 0, 0}, // S_SERPENT_GIB3_7 {SPR_SSXD, 19, 12, A_SinkGib, S_SERPENT_GIB3_9, 0, 0}, // S_SERPENT_GIB3_8 {SPR_SSXD, 19, 12, A_SinkGib, S_SERPENT_GIB3_10, 0, 0}, // S_SERPENT_GIB3_9 {SPR_SSXD, 19, 8, A_SinkGib, S_SERPENT_GIB3_11, 0, 0}, // S_SERPENT_GIB3_10 {SPR_SSXD, 19, 8, A_SinkGib, S_SERPENT_GIB3_12, 0, 0}, // S_SERPENT_GIB3_11 {SPR_SSXD, 19, 8, A_SinkGib, S_NULL, 0, 0}, // S_SERPENT_GIB3_12 {SPR_BISH, 0, 10, A_Look, S_BISHOP_LOOK1, 0, 0}, // S_BISHOP_LOOK1 {SPR_BISH, 0, 1, A_BishopDecide, S_BISHOP_WALK1, 0, 0}, // S_BISHOP_DECIDE {SPR_BISH, 0, 2, A_BishopDoBlur, S_BISHOP_BLUR2, 0, 0}, // S_BISHOP_BLUR1 {SPR_BISH, 0, 4, A_BishopSpawnBlur, S_BISHOP_BLUR2, 0, 0}, // S_BISHOP_BLUR2 {SPR_BISH, 0, 2, A_Chase, S_BISHOP_WALK2, 0, 0}, // S_BISHOP_WALK1 {SPR_BISH, 0, 2, A_BishopChase, S_BISHOP_WALK3, 0, 0}, // S_BISHOP_WALK2 {SPR_BISH, 0, 2, NULL, S_BISHOP_WALK4, 0, 0}, // S_BISHOP_WALK3 {SPR_BISH, 1, 2, A_BishopChase, S_BISHOP_WALK5, 0, 0}, // S_BISHOP_WALK4 {SPR_BISH, 1, 2, A_Chase, S_BISHOP_WALK6, 0, 0}, // S_BISHOP_WALK5 {SPR_BISH, 1, 2, A_BishopChase, S_BISHOP_DECIDE, 0, 0}, // S_BISHOP_WALK6 {SPR_BISH, 0, 3, A_FaceTarget, S_BISHOP_ATK2, 0, 0}, // S_BISHOP_ATK1 {SPR_BISH, 32771, 3, A_FaceTarget, S_BISHOP_ATK3, 0, 0}, // S_BISHOP_ATK2 {SPR_BISH, 32772, 3, A_FaceTarget, S_BISHOP_ATK4, 0, 0}, // S_BISHOP_ATK3 {SPR_BISH, 32773, 3, A_BishopAttack, S_BISHOP_ATK5, 0, 0}, // S_BISHOP_ATK4 {SPR_BISH, 32773, 5, A_BishopAttack2, S_BISHOP_ATK5, 0, 0}, // S_BISHOP_ATK5 {SPR_BISH, 2, 6, A_Pain, S_BISHOP_PAIN2, 0, 0}, // S_BISHOP_PAIN1 {SPR_BISH, 2, 6, A_BishopPainBlur, S_BISHOP_PAIN3, 0, 0}, // S_BISHOP_PAIN2 {SPR_BISH, 2, 6, A_BishopPainBlur, S_BISHOP_PAIN4, 0, 0}, // S_BISHOP_PAIN3 {SPR_BISH, 2, 6, A_BishopPainBlur, S_BISHOP_PAIN5, 0, 0}, // S_BISHOP_PAIN4 {SPR_BISH, 2, 0, NULL, S_BISHOP_WALK1, 0, 0}, // S_BISHOP_PAIN5 {SPR_BISH, 6, 6, NULL, S_BISHOP_DEATH2, 0, 0}, // S_BISHOP_DEATH1 {SPR_BISH, 32775, 6, A_Scream, S_BISHOP_DEATH3, 0, 0}, // S_BISHOP_DEATH2 {SPR_BISH, 32776, 5, A_NoBlocking, S_BISHOP_DEATH4, 0, 0}, // S_BISHOP_DEATH3 {SPR_BISH, 32777, 5, A_Explode, S_BISHOP_DEATH5, 0, 0}, // S_BISHOP_DEATH4 {SPR_BISH, 32778, 5, NULL, S_BISHOP_DEATH6, 0, 0}, // S_BISHOP_DEATH5 {SPR_BISH, 32779, 4, NULL, S_BISHOP_DEATH7, 0, 0}, // S_BISHOP_DEATH6 {SPR_BISH, 32780, 4, NULL, S_BISHOP_DEATH8, 0, 0}, // S_BISHOP_DEATH7 {SPR_BISH, 13, 4, A_BishopPuff, S_BISHOP_DEATH9, 0, 0}, // S_BISHOP_DEATH8 {SPR_BISH, 14, 4, A_QueueCorpse, S_BISHOP_DEATH10, 0, 0}, // S_BISHOP_DEATH9 {SPR_BISH, 15, -1, NULL, S_NULL, 0, 0}, // S_BISHOP_DEATH10 {SPR_BISH, 23, 5, A_FreezeDeath, S_BISHOP_ICE2, 0, 0}, // S_BISHOP_ICE {SPR_BISH, 23, 1, A_FreezeDeathChunks, S_BISHOP_ICE2, 0, 0}, // S_BISHOP_ICE2 {SPR_BISH, 16, 5, NULL, S_BISHOP_PUFF2, 0, 0}, // S_BISHOP_PUFF1 {SPR_BISH, 17, 5, NULL, S_BISHOP_PUFF3, 0, 0}, // S_BISHOP_PUFF2 {SPR_BISH, 18, 5, NULL, S_BISHOP_PUFF4, 0, 0}, // S_BISHOP_PUFF3 {SPR_BISH, 19, 5, NULL, S_BISHOP_PUFF5, 0, 0}, // S_BISHOP_PUFF4 {SPR_BISH, 20, 6, NULL, S_BISHOP_PUFF6, 0, 0}, // S_BISHOP_PUFF5 {SPR_BISH, 21, 6, NULL, S_BISHOP_PUFF7, 0, 0}, // S_BISHOP_PUFF6 {SPR_BISH, 22, 5, NULL, S_NULL, 0, 0}, // S_BISHOP_PUFF7 {SPR_BISH, 0, 16, NULL, S_BISHOPBLUR2, 0, 0}, // S_BISHOPBLUR1 {SPR_BISH, 0, 8, A_SetAltShadow, S_NULL, 0, 0}, // S_BISHOPBLUR2 {SPR_BISH, 2, 8, NULL, S_NULL, 0, 0}, // S_BISHOPPAINBLUR1 {SPR_BPFX, 32768, 1, A_BishopMissileWeave, S_BISHFX1_2, 0, 0}, // S_BISHFX1_1 {SPR_BPFX, 32769, 1, A_BishopMissileWeave, S_BISHFX1_3, 0, 0}, // S_BISHFX1_2 {SPR_BPFX, 32768, 1, A_BishopMissileWeave, S_BISHFX1_4, 0, 0}, // S_BISHFX1_3 {SPR_BPFX, 32769, 1, A_BishopMissileWeave, S_BISHFX1_5, 0, 0}, // S_BISHFX1_4 {SPR_BPFX, 32769, 0, A_BishopMissileSeek, S_BISHFX1_1, 0, 0}, // S_BISHFX1_5 {SPR_BPFX, 32770, 4, NULL, S_BISHFXI1_2, 0, 0}, // S_BISHFXI1_1 {SPR_BPFX, 32771, 4, NULL, S_BISHFXI1_3, 0, 0}, // S_BISHFXI1_2 {SPR_BPFX, 32772, 4, NULL, S_BISHFXI1_4, 0, 0}, // S_BISHFXI1_3 {SPR_BPFX, 32773, 4, NULL, S_BISHFXI1_5, 0, 0}, // S_BISHFXI1_4 {SPR_BPFX, 32774, 3, NULL, S_BISHFXI1_6, 0, 0}, // S_BISHFXI1_5 {SPR_BPFX, 32775, 3, NULL, S_NULL, 0, 0}, // S_BISHFXI1_6 {SPR_DRAG, 3, 10, A_Look, S_DRAGON_LOOK1, 0, 0}, // S_DRAGON_LOOK1 {SPR_DRAG, 2, 5, NULL, S_DRAGON_INIT2, 0, 0}, // S_DRAGON_INIT {SPR_DRAG, 1, 5, NULL, S_DRAGON_INIT3, 0, 0}, // S_DRAGON_INIT2 {SPR_DRAG, 0, 5, A_DragonInitFlight, S_DRAGON_WALK1, 0, 0}, // S_DRAGON_INIT3 {SPR_DRAG, 1, 3, A_DragonFlap, S_DRAGON_WALK2, 0, 0}, // S_DRAGON_WALK1 {SPR_DRAG, 1, 3, A_DragonFlight, S_DRAGON_WALK3, 0, 0}, // S_DRAGON_WALK2 {SPR_DRAG, 2, 3, A_DragonFlight, S_DRAGON_WALK4, 0, 0}, // S_DRAGON_WALK3 {SPR_DRAG, 2, 3, A_DragonFlight, S_DRAGON_WALK5, 0, 0}, // S_DRAGON_WALK4 {SPR_DRAG, 3, 3, A_DragonFlight, S_DRAGON_WALK6, 0, 0}, // S_DRAGON_WALK5 {SPR_DRAG, 3, 3, A_DragonFlight, S_DRAGON_WALK7, 0, 0}, // S_DRAGON_WALK6 {SPR_DRAG, 2, 3, A_DragonFlight, S_DRAGON_WALK8, 0, 0}, // S_DRAGON_WALK7 {SPR_DRAG, 2, 3, A_DragonFlight, S_DRAGON_WALK9, 0, 0}, // S_DRAGON_WALK8 {SPR_DRAG, 1, 3, A_DragonFlight, S_DRAGON_WALK10, 0, 0}, // S_DRAGON_WALK9 {SPR_DRAG, 1, 3, A_DragonFlight, S_DRAGON_WALK11, 0, 0}, // S_DRAGON_WALK10 {SPR_DRAG, 0, 3, A_DragonFlight, S_DRAGON_WALK12, 0, 0}, // S_DRAGON_WALK11 {SPR_DRAG, 0, 3, A_DragonFlight, S_DRAGON_WALK1, 0, 0}, // S_DRAGON_WALK12 {SPR_DRAG, 4, 8, A_DragonAttack, S_DRAGON_WALK1, 0, 0}, // S_DRAGON_ATK1 {SPR_DRAG, 5, 10, A_DragonPain, S_DRAGON_WALK1, 0, 0}, // S_DRAGON_PAIN1 {SPR_DRAG, 6, 5, A_Scream, S_DRAGON_DEATH2, 0, 0}, // S_DRAGON_DEATH1 {SPR_DRAG, 7, 4, A_NoBlocking, S_DRAGON_DEATH3, 0, 0}, // S_DRAGON_DEATH2 {SPR_DRAG, 8, 4, NULL, S_DRAGON_DEATH4, 0, 0}, // S_DRAGON_DEATH3 {SPR_DRAG, 9, 4, A_DragonCheckCrash, S_DRAGON_DEATH4, 0, 0}, // S_DRAGON_DEATH4 {SPR_DRAG, 10, 5, NULL, S_DRAGON_CRASH2, 0, 0}, // S_DRAGON_CRASH1 {SPR_DRAG, 11, 5, NULL, S_DRAGON_CRASH3, 0, 0}, // S_DRAGON_CRASH2 {SPR_DRAG, 12, -1, NULL, S_NULL, 0, 0}, // S_DRAGON_CRASH3 {SPR_DRFX, 32768, 4, NULL, S_DRAGON_FX1_2, 0, 0}, // S_DRAGON_FX1_1 {SPR_DRFX, 32769, 4, NULL, S_DRAGON_FX1_3, 0, 0}, // S_DRAGON_FX1_2 {SPR_DRFX, 32770, 4, NULL, S_DRAGON_FX1_4, 0, 0}, // S_DRAGON_FX1_3 {SPR_DRFX, 32771, 4, NULL, S_DRAGON_FX1_5, 0, 0}, // S_DRAGON_FX1_4 {SPR_DRFX, 32772, 4, NULL, S_DRAGON_FX1_6, 0, 0}, // S_DRAGON_FX1_5 {SPR_DRFX, 32773, 4, NULL, S_DRAGON_FX1_1, 0, 0}, // S_DRAGON_FX1_6 {SPR_DRFX, 32774, 4, NULL, S_DRAGON_FX1_X2, 0, 0}, // S_DRAGON_FX1_X1 {SPR_DRFX, 32775, 4, NULL, S_DRAGON_FX1_X3, 0, 0}, // S_DRAGON_FX1_X2 {SPR_DRFX, 32776, 4, NULL, S_DRAGON_FX1_X4, 0, 0}, // S_DRAGON_FX1_X3 {SPR_DRFX, 32777, 4, A_DragonFX2, S_DRAGON_FX1_X5, 0, 0}, // S_DRAGON_FX1_X4 {SPR_DRFX, 32778, 3, NULL, S_DRAGON_FX1_X6, 0, 0}, // S_DRAGON_FX1_X5 {SPR_DRFX, 32779, 3, NULL, S_NULL, 0, 0}, // S_DRAGON_FX1_X6 {SPR_CFCF, 32784, 1, NULL, S_DRAGON_FX2_2, 0, 0}, // S_DRAGON_FX2_1 {SPR_CFCF, 32784, 4, A_UnHideThing, S_DRAGON_FX2_3, 0, 0}, // S_DRAGON_FX2_2 {SPR_CFCF, 32785, 3, A_Scream, S_DRAGON_FX2_4, 0, 0}, // S_DRAGON_FX2_3 {SPR_CFCF, 32786, 4, NULL, S_DRAGON_FX2_5, 0, 0}, // S_DRAGON_FX2_4 {SPR_CFCF, 32787, 3, A_Explode, S_DRAGON_FX2_6, 0, 0}, // S_DRAGON_FX2_5 {SPR_CFCF, 32788, 4, NULL, S_DRAGON_FX2_7, 0, 0}, // S_DRAGON_FX2_6 {SPR_CFCF, 32789, 3, NULL, S_DRAGON_FX2_8, 0, 0}, // S_DRAGON_FX2_7 {SPR_CFCF, 32790, 4, NULL, S_DRAGON_FX2_9, 0, 0}, // S_DRAGON_FX2_8 {SPR_CFCF, 32791, 3, NULL, S_DRAGON_FX2_10, 0, 0}, // S_DRAGON_FX2_9 {SPR_CFCF, 32792, 4, NULL, S_DRAGON_FX2_11, 0, 0}, // S_DRAGON_FX2_10 {SPR_CFCF, 32793, 3, NULL, S_NULL, 0, 0}, // S_DRAGON_FX2_11 {SPR_ARM1, 0, -1, NULL, S_NULL, 0, 0}, // S_ARMOR_1 {SPR_ARM2, 0, -1, NULL, S_NULL, 0, 0}, // S_ARMOR_2 {SPR_ARM3, 0, -1, NULL, S_NULL, 0, 0}, // S_ARMOR_3 {SPR_ARM4, 0, -1, NULL, S_NULL, 0, 0}, // S_ARMOR_4 {SPR_MAN1, 32768, 4, NULL, S_MANA1_2, 0, 0}, // S_MANA1_1 {SPR_MAN1, 32769, 4, NULL, S_MANA1_3, 0, 0}, // S_MANA1_2 {SPR_MAN1, 32770, 4, NULL, S_MANA1_4, 0, 0}, // S_MANA1_3 {SPR_MAN1, 32771, 4, NULL, S_MANA1_5, 0, 0}, // S_MANA1_4 {SPR_MAN1, 32772, 4, NULL, S_MANA1_6, 0, 0}, // S_MANA1_5 {SPR_MAN1, 32773, 4, NULL, S_MANA1_7, 0, 0}, // S_MANA1_6 {SPR_MAN1, 32774, 4, NULL, S_MANA1_8, 0, 0}, // S_MANA1_7 {SPR_MAN1, 32775, 4, NULL, S_MANA1_9, 0, 0}, // S_MANA1_8 {SPR_MAN1, 32776, 4, NULL, S_MANA1_1, 0, 0}, // S_MANA1_9 {SPR_MAN2, 32768, 4, NULL, S_MANA2_2, 0, 0}, // S_MANA2_1 {SPR_MAN2, 32769, 4, NULL, S_MANA2_3, 0, 0}, // S_MANA2_2 {SPR_MAN2, 32770, 4, NULL, S_MANA2_4, 0, 0}, // S_MANA2_3 {SPR_MAN2, 32771, 4, NULL, S_MANA2_5, 0, 0}, // S_MANA2_4 {SPR_MAN2, 32772, 4, NULL, S_MANA2_6, 0, 0}, // S_MANA2_5 {SPR_MAN2, 32773, 4, NULL, S_MANA2_7, 0, 0}, // S_MANA2_6 {SPR_MAN2, 32774, 4, NULL, S_MANA2_8, 0, 0}, // S_MANA2_7 {SPR_MAN2, 32775, 4, NULL, S_MANA2_9, 0, 0}, // S_MANA2_8 {SPR_MAN2, 32776, 4, NULL, S_MANA2_10, 0, 0}, // S_MANA2_9 {SPR_MAN2, 32777, 4, NULL, S_MANA2_11, 0, 0}, // S_MANA2_10 {SPR_MAN2, 32778, 4, NULL, S_MANA2_12, 0, 0}, // S_MANA2_11 {SPR_MAN2, 32779, 4, NULL, S_MANA2_13, 0, 0}, // S_MANA2_12 {SPR_MAN2, 32780, 4, NULL, S_MANA2_14, 0, 0}, // S_MANA2_13 {SPR_MAN2, 32781, 4, NULL, S_MANA2_15, 0, 0}, // S_MANA2_14 {SPR_MAN2, 32782, 4, NULL, S_MANA2_16, 0, 0}, // S_MANA2_15 {SPR_MAN2, 32783, 4, NULL, S_MANA2_1, 0, 0}, // S_MANA2_16 {SPR_MAN3, 32768, 4, NULL, S_MANA3_2, 0, 0}, // S_MANA3_1 {SPR_MAN3, 32769, 4, NULL, S_MANA3_3, 0, 0}, // S_MANA3_2 {SPR_MAN3, 32770, 4, NULL, S_MANA3_4, 0, 0}, // S_MANA3_3 {SPR_MAN3, 32771, 4, NULL, S_MANA3_5, 0, 0}, // S_MANA3_4 {SPR_MAN3, 32772, 4, NULL, S_MANA3_6, 0, 0}, // S_MANA3_5 {SPR_MAN3, 32773, 4, NULL, S_MANA3_7, 0, 0}, // S_MANA3_6 {SPR_MAN3, 32774, 4, NULL, S_MANA3_8, 0, 0}, // S_MANA3_7 {SPR_MAN3, 32775, 4, NULL, S_MANA3_9, 0, 0}, // S_MANA3_8 {SPR_MAN3, 32776, 4, NULL, S_MANA3_10, 0, 0}, // S_MANA3_9 {SPR_MAN3, 32777, 4, NULL, S_MANA3_11, 0, 0}, // S_MANA3_10 {SPR_MAN3, 32778, 4, NULL, S_MANA3_12, 0, 0}, // S_MANA3_11 {SPR_MAN3, 32779, 4, NULL, S_MANA3_13, 0, 0}, // S_MANA3_12 {SPR_MAN3, 32780, 4, NULL, S_MANA3_14, 0, 0}, // S_MANA3_13 {SPR_MAN3, 32781, 4, NULL, S_MANA3_15, 0, 0}, // S_MANA3_14 {SPR_MAN3, 32782, 4, NULL, S_MANA3_16, 0, 0}, // S_MANA3_15 {SPR_MAN3, 32783, 4, NULL, S_MANA3_1, 0, 0}, // S_MANA3_16 {SPR_KEY1, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY1 {SPR_KEY2, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY2 {SPR_KEY3, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY3 {SPR_KEY4, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY4 {SPR_KEY5, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY5 {SPR_KEY6, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY6 {SPR_KEY7, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY7 {SPR_KEY8, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY8 {SPR_KEY9, 0, -1, NULL, S_NULL, 0, 0}, // S_KEY9 {SPR_KEYA, 0, -1, NULL, S_NULL, 0, 0}, // S_KEYA {SPR_KEYB, 0, -1, NULL, S_NULL, 0, 0}, // S_KEYB {SPR_TLGL, 0, 1, NULL, S_SND_WIND2, 0, 0}, // S_SND_WIND1 {SPR_TLGL, 0, 200, A_ESound, S_SND_WIND2, 0, 0}, // S_SND_WIND2 {SPR_TLGL, 0, 85, A_ESound, S_SND_WATERFALL, 0, 0}, // S_SND_WATERFALL {SPR_ETTN, 0, 10, A_Look, S_ETTIN_LOOK2, 0, 0}, // S_ETTIN_LOOK1 {SPR_ETTN, 0, 10, A_Look, S_ETTIN_LOOK1, 0, 0}, // S_ETTIN_LOOK2 {SPR_ETTN, 0, 5, A_Chase, S_ETTIN_CHASE2, 0, 0}, // S_ETTIN_CHASE1 {SPR_ETTN, 1, 5, A_Chase, S_ETTIN_CHASE3, 0, 0}, // S_ETTIN_CHASE2 {SPR_ETTN, 2, 5, A_Chase, S_ETTIN_CHASE4, 0, 0}, // S_ETTIN_CHASE3 {SPR_ETTN, 3, 5, A_Chase, S_ETTIN_CHASE1, 0, 0}, // S_ETTIN_CHASE4 {SPR_ETTN, 7, 7, A_Pain, S_ETTIN_CHASE1, 0, 0}, // S_ETTIN_PAIN1 {SPR_ETTN, 4, 6, A_FaceTarget, S_ETTIN_ATK1_2, 0, 0}, // S_ETTIN_ATK1_1 {SPR_ETTN, 5, 6, A_FaceTarget, S_ETTIN_ATK1_3, 0, 0}, // S_ETTIN_ATK1_2 {SPR_ETTN, 6, 8, A_EttinAttack, S_ETTIN_CHASE1, 0, 0}, // S_ETTIN_ATK1_3 {SPR_ETTN, 8, 4, NULL, S_ETTIN_DEATH1_2, 0, 0}, // S_ETTIN_DEATH1_1 {SPR_ETTN, 9, 4, NULL, S_ETTIN_DEATH1_3, 0, 0}, // S_ETTIN_DEATH1_2 {SPR_ETTN, 10, 4, A_Scream, S_ETTIN_DEATH1_4, 0, 0}, // S_ETTIN_DEATH1_3 {SPR_ETTN, 11, 4, A_NoBlocking, S_ETTIN_DEATH1_5, 0, 0}, // S_ETTIN_DEATH1_4 {SPR_ETTN, 12, 4, A_QueueCorpse, S_ETTIN_DEATH1_6, 0, 0}, // S_ETTIN_DEATH1_5 {SPR_ETTN, 13, 4, NULL, S_ETTIN_DEATH1_7, 0, 0}, // S_ETTIN_DEATH1_6 {SPR_ETTN, 14, 4, NULL, S_ETTIN_DEATH1_8, 0, 0}, // S_ETTIN_DEATH1_7 {SPR_ETTN, 15, 4, NULL, S_ETTIN_DEATH1_9, 0, 0}, // S_ETTIN_DEATH1_8 {SPR_ETTN, 16, -1, NULL, S_NULL, 0, 0}, // S_ETTIN_DEATH1_9 {SPR_ETTB, 0, 4, NULL, S_ETTIN_DEATH2_2, 0, 0}, // S_ETTIN_DEATH2_1 {SPR_ETTB, 1, 4, A_NoBlocking, S_ETTIN_DEATH2_3, 0, 0}, // S_ETTIN_DEATH2_2 {SPR_ETTB, 2, 4, A_DropMace, S_ETTIN_DEATH2_4, 0, 0}, // S_ETTIN_DEATH2_3 {SPR_ETTB, 3, 4, A_Scream, S_ETTIN_DEATH2_5, 0, 0}, // S_ETTIN_DEATH2_4 {SPR_ETTB, 4, 4, A_QueueCorpse, S_ETTIN_DEATH2_6, 0, 0}, // S_ETTIN_DEATH2_5 {SPR_ETTB, 5, 4, NULL, S_ETTIN_DEATH2_7, 0, 0}, // S_ETTIN_DEATH2_6 {SPR_ETTB, 6, 4, NULL, S_ETTIN_DEATH2_8, 0, 0}, // S_ETTIN_DEATH2_7 {SPR_ETTB, 7, 4, NULL, S_ETTIN_DEATH2_9, 0, 0}, // S_ETTIN_DEATH2_8 {SPR_ETTB, 8, 4, NULL, S_ETTIN_DEATH2_0, 0, 0}, // S_ETTIN_DEATH2_9 {SPR_ETTB, 9, 4, NULL, S_ETTIN_DEATH2_A, 0, 0}, // S_ETTIN_DEATH2_0 {SPR_ETTB, 10, 4, NULL, S_ETTIN_DEATH2_B, 0, 0}, // S_ETTIN_DEATH2_A {SPR_ETTB, 11, -1, NULL, S_NULL, 0, 0}, // S_ETTIN_DEATH2_B {SPR_ETTN, 17, 5, A_FreezeDeath, S_ETTIN_ICE2, 0, 0}, // S_ETTIN_ICE1 {SPR_ETTN, 17, 1, A_FreezeDeathChunks, S_ETTIN_ICE2, 0, 0}, // S_ETTIN_ICE2 {SPR_ETTB, 12, 5, A_CheckFloor, S_ETTIN_MACE2, 0, 0}, // S_ETTIN_MACE1 {SPR_ETTB, 13, 5, A_CheckFloor, S_ETTIN_MACE3, 0, 0}, // S_ETTIN_MACE2 {SPR_ETTB, 14, 5, A_CheckFloor, S_ETTIN_MACE4, 0, 0}, // S_ETTIN_MACE3 {SPR_ETTB, 15, 5, A_CheckFloor, S_ETTIN_MACE1, 0, 0}, // S_ETTIN_MACE4 {SPR_ETTB, 16, 5, NULL, S_ETTIN_MACE6, 0, 0}, // S_ETTIN_MACE5 {SPR_ETTB, 17, 5, A_QueueCorpse, S_ETTIN_MACE7, 0, 0}, // S_ETTIN_MACE6 {SPR_ETTB, 18, -1, NULL, S_NULL, 0, 0}, // S_ETTIN_MACE7 {SPR_FDMN, 32791, 5, NULL, S_FIRED_LOOK1, 0, 0}, // S_FIRED_SPAWN1 {SPR_FDMN, 32772, 10, A_Look, S_FIRED_LOOK2, 0, 0}, // S_FIRED_LOOK1 {SPR_FDMN, 32773, 10, A_Look, S_FIRED_LOOK3, 0, 0}, // S_FIRED_LOOK2 {SPR_FDMN, 32774, 10, A_Look, S_FIRED_LOOK1, 0, 0}, // S_FIRED_LOOK3 {SPR_FDMN, 32772, 8, NULL, S_FIRED_LOOK5, 0, 0}, // S_FIRED_LOOK4 {SPR_FDMN, 32773, 6, NULL, S_FIRED_LOOK6, 0, 0}, // S_FIRED_LOOK5 {SPR_FDMN, 32774, 5, NULL, S_FIRED_LOOK7, 0, 0}, // S_FIRED_LOOK6 {SPR_FDMN, 32773, 8, NULL, S_FIRED_LOOK8, 0, 0}, // S_FIRED_LOOK7 {SPR_FDMN, 32772, 6, NULL, S_FIRED_LOOK9, 0, 0}, // S_FIRED_LOOK8 {SPR_FDMN, 32773, 7, A_FiredRocks, S_FIRED_LOOK0, 0, 0}, // S_FIRED_LOOK9 {SPR_FDMN, 32775, 5, NULL, S_FIRED_LOOKA, 0, 0}, // S_FIRED_LOOK0 {SPR_FDMN, 32776, 5, NULL, S_FIRED_LOOKB, 0, 0}, // S_FIRED_LOOKA {SPR_FDMN, 32777, 5, A_UnSetInvulnerable, S_FIRED_WALK1, 0, 0}, // S_FIRED_LOOKB {SPR_FDMN, 32768, 5, A_FiredChase, S_FIRED_WALK2, 0, 0}, // S_FIRED_WALK1 {SPR_FDMN, 32769, 5, A_FiredChase, S_FIRED_WALK3, 0, 0}, // S_FIRED_WALK2 {SPR_FDMN, 32770, 5, A_FiredChase, S_FIRED_WALK1, 0, 0}, // S_FIRED_WALK3 {SPR_FDMN, 32771, 6, A_Pain, S_FIRED_WALK1, 0, 0}, // S_FIRED_PAIN1 {SPR_FDMN, 32778, 3, A_FaceTarget, S_FIRED_ATTACK2, 0, 0}, // S_FIRED_ATTACK1 {SPR_FDMN, 32778, 5, A_FiredAttack, S_FIRED_ATTACK3, 0, 0}, // S_FIRED_ATTACK2 {SPR_FDMN, 32778, 5, A_FiredAttack, S_FIRED_ATTACK4, 0, 0}, // S_FIRED_ATTACK3 {SPR_FDMN, 32778, 5, A_FiredAttack, S_FIRED_WALK1, 0, 0}, // S_FIRED_ATTACK4 {SPR_FDMN, 32771, 4, A_FaceTarget, S_FIRED_DEATH2, 0, 0}, // S_FIRED_DEATH1 {SPR_FDMN, 32779, 4, A_Scream, S_FIRED_DEATH3, 0, 0}, // S_FIRED_DEATH2 {SPR_FDMN, 32779, 4, A_NoBlocking, S_FIRED_DEATH4, 0, 0}, // S_FIRED_DEATH3 {SPR_FDMN, 32779, 200, NULL, S_NULL, 0, 0}, // S_FIRED_DEATH4 {SPR_FDMN, 12, 5, A_FaceTarget, S_FIRED_XDEATH2, 0, 0}, // S_FIRED_XDEATH1 {SPR_FDMN, 13, 5, A_NoBlocking, S_FIRED_XDEATH3, 0, 0}, // S_FIRED_XDEATH2 {SPR_FDMN, 14, 5, A_FiredSplotch, S_NULL, 0, 0}, // S_FIRED_XDEATH3 {SPR_FDMN, 17, 5, A_FreezeDeath, S_FIRED_ICE2, 0, 0}, // S_FIRED_ICE1 {SPR_FDMN, 17, 1, A_FreezeDeathChunks, S_FIRED_ICE2, 0, 0}, // S_FIRED_ICE2 {SPR_FDMN, 15, 3, NULL, S_FIRED_CORPSE2, 0, 0}, // S_FIRED_CORPSE1 {SPR_FDMN, 15, 6, A_QueueCorpse, S_FIRED_CORPSE3, 0, 0}, // S_FIRED_CORPSE2 {SPR_FDMN, 24, -1, NULL, S_NULL, 0, 0}, // S_FIRED_CORPSE3 {SPR_FDMN, 16, 3, NULL, S_FIRED_CORPSE5, 0, 0}, // S_FIRED_CORPSE4 {SPR_FDMN, 16, 6, A_QueueCorpse, S_FIRED_CORPSE6, 0, 0}, // S_FIRED_CORPSE5 {SPR_FDMN, 25, -1, NULL, S_NULL, 0, 0}, // S_FIRED_CORPSE6 {SPR_FDMN, 18, 4, NULL, S_FIRED_RDROP1, 0, 0}, // S_FIRED_RDROP1 {SPR_FDMN, 18, 5, A_SmBounce, S_FIRED_RDEAD1_2, 0, 0}, // S_FIRED_RDEAD1_1 {SPR_FDMN, 18, 200, NULL, S_NULL, 0, 0}, // S_FIRED_RDEAD1_2 {SPR_FDMN, 19, 4, NULL, S_FIRED_RDROP2, 0, 0}, // S_FIRED_RDROP2 {SPR_FDMN, 19, 5, A_SmBounce, S_FIRED_RDEAD2_2, 0, 0}, // S_FIRED_RDEAD2_1 {SPR_FDMN, 19, 200, NULL, S_NULL, 0, 0}, // S_FIRED_RDEAD2_2 {SPR_FDMN, 20, 4, NULL, S_FIRED_RDROP3, 0, 0}, // S_FIRED_RDROP3 {SPR_FDMN, 20, 5, A_SmBounce, S_FIRED_RDEAD3_2, 0, 0}, // S_FIRED_RDEAD3_1 {SPR_FDMN, 20, 200, NULL, S_NULL, 0, 0}, // S_FIRED_RDEAD3_2 {SPR_FDMN, 21, 4, NULL, S_FIRED_RDROP4, 0, 0}, // S_FIRED_RDROP4 {SPR_FDMN, 21, 5, A_SmBounce, S_FIRED_RDEAD4_2, 0, 0}, // S_FIRED_RDEAD4_1 {SPR_FDMN, 21, 200, NULL, S_NULL, 0, 0}, // S_FIRED_RDEAD4_2 {SPR_FDMN, 22, 4, NULL, S_FIRED_RDROP5, 0, 0}, // S_FIRED_RDROP5 {SPR_FDMN, 22, 5, A_SmBounce, S_FIRED_RDEAD5_2, 0, 0}, // S_FIRED_RDEAD5_1 {SPR_FDMN, 22, 200, NULL, S_NULL, 0, 0}, // S_FIRED_RDEAD5_2 {SPR_FDMB, 32768, 5, NULL, S_FIRED_FX6_1, 0, 0}, // S_FIRED_FX6_1 {SPR_FDMB, 32769, 5, NULL, S_FIRED_FX6_3, 0, 0}, // S_FIRED_FX6_2 {SPR_FDMB, 32770, 5, NULL, S_FIRED_FX6_4, 0, 0}, // S_FIRED_FX6_3 {SPR_FDMB, 32771, 5, NULL, S_FIRED_FX6_5, 0, 0}, // S_FIRED_FX6_4 {SPR_FDMB, 32772, 5, NULL, S_NULL, 0, 0}, // S_FIRED_FX6_5 {SPR_ICEY, 0, 10, A_IceGuyLook, S_ICEGUY_LOOK, 0, 0}, // S_ICEGUY_LOOK {SPR_ICEY, 0, -1, NULL, S_ICEGUY_LOOK, 0, 0}, // S_ICEGUY_DORMANT {SPR_ICEY, 0, 4, A_Chase, S_ICEGUY_WALK2, 0, 0}, // S_ICEGUY_WALK1 {SPR_ICEY, 1, 4, A_IceGuyChase, S_ICEGUY_WALK3, 0, 0}, // S_ICEGUY_WALK2 {SPR_ICEY, 2, 4, A_Chase, S_ICEGUY_WALK4, 0, 0}, // S_ICEGUY_WALK3 {SPR_ICEY, 3, 4, A_Chase, S_ICEGUY_WALK1, 0, 0}, // S_ICEGUY_WALK4 {SPR_ICEY, 4, 3, A_FaceTarget, S_ICEGUY_ATK2, 0, 0}, // S_ICEGUY_ATK1 {SPR_ICEY, 5, 3, A_FaceTarget, S_ICEGUY_ATK3, 0, 0}, // S_ICEGUY_ATK2 {SPR_ICEY, 32774, 8, A_IceGuyAttack, S_ICEGUY_ATK4, 0, 0}, // S_ICEGUY_ATK3 {SPR_ICEY, 5, 4, A_FaceTarget, S_ICEGUY_WALK1, 0, 0}, // S_ICEGUY_ATK4 {SPR_ICEY, 0, 1, A_Pain, S_ICEGUY_WALK1, 0, 0}, // S_ICEGUY_PAIN1 {SPR_ICEY, 0, 1, A_IceGuyDie, S_NULL, 0, 0}, // S_ICEGUY_DEATH {SPR_ICPR, 32768, 3, A_IceGuyMissilePuff, S_ICEGUY_FX2, 0, 0}, // S_ICEGUY_FX1 {SPR_ICPR, 32769, 3, A_IceGuyMissilePuff, S_ICEGUY_FX3, 0, 0}, // S_ICEGUY_FX2 {SPR_ICPR, 32770, 3, A_IceGuyMissilePuff, S_ICEGUY_FX1, 0, 0}, // S_ICEGUY_FX3 {SPR_ICPR, 32771, 4, NULL, S_ICEGUY_FX_X2, 0, 0}, // S_ICEGUY_FX_X1 {SPR_ICPR, 32772, 4, A_IceGuyMissileExplode, S_ICEGUY_FX_X3, 0, 0}, // S_ICEGUY_FX_X2 {SPR_ICPR, 32773, 4, NULL, S_ICEGUY_FX_X4, 0, 0}, // S_ICEGUY_FX_X3 {SPR_ICPR, 32774, 4, NULL, S_ICEGUY_FX_X5, 0, 0}, // S_ICEGUY_FX_X4 {SPR_ICPR, 32775, 3, NULL, S_NULL, 0, 0}, // S_ICEGUY_FX_X5 {SPR_ICPR, 8, 3, NULL, S_ICEFX_PUFF2, 0, 0}, // S_ICEFX_PUFF1 {SPR_ICPR, 9, 3, NULL, S_ICEFX_PUFF3, 0, 0}, // S_ICEFX_PUFF2 {SPR_ICPR, 10, 3, NULL, S_ICEFX_PUFF4, 0, 0}, // S_ICEFX_PUFF3 {SPR_ICPR, 11, 2, NULL, S_ICEFX_PUFF5, 0, 0}, // S_ICEFX_PUFF4 {SPR_ICPR, 12, 2, NULL, S_NULL, 0, 0}, // S_ICEFX_PUFF5 {SPR_ICPR, 32781, 3, NULL, S_ICEGUY_FX2_2, 0, 0}, // S_ICEGUY_FX2_1 {SPR_ICPR, 32782, 3, NULL, S_ICEGUY_FX2_3, 0, 0}, // S_ICEGUY_FX2_2 {SPR_ICPR, 32783, 3, NULL, S_ICEGUY_FX2_1, 0, 0}, // S_ICEGUY_FX2_3 {SPR_ICPR, 32784, 50, NULL, S_NULL, 0, 0}, // S_ICEGUY_BIT1 {SPR_ICPR, 32785, 50, NULL, S_NULL, 0, 0}, // S_ICEGUY_BIT2 {SPR_ICWS, 0, 2, NULL, S_ICEGUY_WISP1_2, 0, 0}, // S_ICEGUY_WISP1_1 {SPR_ICWS, 1, 2, NULL, S_ICEGUY_WISP1_3, 0, 0}, // S_ICEGUY_WISP1_2 {SPR_ICWS, 2, 2, NULL, S_ICEGUY_WISP1_4, 0, 0}, // S_ICEGUY_WISP1_3 {SPR_ICWS, 3, 2, NULL, S_ICEGUY_WISP1_5, 0, 0}, // S_ICEGUY_WISP1_4 {SPR_ICWS, 4, 2, NULL, S_ICEGUY_WISP1_6, 0, 0}, // S_ICEGUY_WISP1_5 {SPR_ICWS, 5, 2, NULL, S_ICEGUY_WISP1_7, 0, 0}, // S_ICEGUY_WISP1_6 {SPR_ICWS, 6, 2, NULL, S_ICEGUY_WISP1_8, 0, 0}, // S_ICEGUY_WISP1_7 {SPR_ICWS, 7, 2, NULL, S_ICEGUY_WISP1_9, 0, 0}, // S_ICEGUY_WISP1_8 {SPR_ICWS, 8, 2, NULL, S_NULL, 0, 0}, // S_ICEGUY_WISP1_9 {SPR_ICWS, 9, 2, NULL, S_ICEGUY_WISP2_2, 0, 0}, // S_ICEGUY_WISP2_1 {SPR_ICWS, 10, 2, NULL, S_ICEGUY_WISP2_3, 0, 0}, // S_ICEGUY_WISP2_2 {SPR_ICWS, 11, 2, NULL, S_ICEGUY_WISP2_4, 0, 0}, // S_ICEGUY_WISP2_3 {SPR_ICWS, 12, 2, NULL, S_ICEGUY_WISP2_5, 0, 0}, // S_ICEGUY_WISP2_4 {SPR_ICWS, 13, 2, NULL, S_ICEGUY_WISP2_6, 0, 0}, // S_ICEGUY_WISP2_5 {SPR_ICWS, 14, 2, NULL, S_ICEGUY_WISP2_7, 0, 0}, // S_ICEGUY_WISP2_6 {SPR_ICWS, 15, 2, NULL, S_ICEGUY_WISP2_8, 0, 0}, // S_ICEGUY_WISP2_7 {SPR_ICWS, 16, 2, NULL, S_ICEGUY_WISP2_9, 0, 0}, // S_ICEGUY_WISP2_8 {SPR_ICWS, 17, 2, NULL, S_NULL, 0, 0}, // S_ICEGUY_WISP2_9 {SPR_PLAY, 0, 2, NULL, S_FIGHTER2, 0, 0}, // S_FIGHTER {SPR_PLAY, 0, 3, A_ClassBossHealth, S_FIGHTERLOOK, 0, 0}, // S_FIGHTER2 {SPR_PLAY, 0, 5, A_Look, S_FIGHTERLOOK, 0, 0}, // S_FIGHTERLOOK {SPR_PLAY, 0, 4, A_FastChase, S_FIGHTER_RUN2, 0, 0}, // S_FIGHTER_RUN1 {SPR_PLAY, 1, 4, A_FastChase, S_FIGHTER_RUN3, 0, 0}, // S_FIGHTER_RUN2 {SPR_PLAY, 2, 4, A_FastChase, S_FIGHTER_RUN4, 0, 0}, // S_FIGHTER_RUN3 {SPR_PLAY, 3, 4, A_FastChase, S_FIGHTER_RUN1, 0, 0}, // S_FIGHTER_RUN4 {SPR_PLAY, 4, 8, A_FaceTarget, S_FIGHTER_ATK2, 0, 0}, // S_FIGHTER_ATK1 {SPR_PLAY, 5, 8, A_FighterAttack, S_FIGHTER_RUN1, 0, 0}, // S_FIGHTER_ATK2 {SPR_PLAY, 6, 4, NULL, S_FIGHTER_PAIN2, 0, 0}, // S_FIGHTER_PAIN {SPR_PLAY, 6, 4, A_Pain, S_FIGHTER_RUN1, 0, 0}, // S_FIGHTER_PAIN2 {SPR_PLAY, 7, 6, NULL, S_FIGHTER_DIE2, 0, 0}, // S_FIGHTER_DIE1 {SPR_PLAY, 8, 6, A_Scream, S_FIGHTER_DIE3, 0, 0}, // S_FIGHTER_DIE2 {SPR_PLAY, 9, 6, NULL, S_FIGHTER_DIE4, 0, 0}, // S_FIGHTER_DIE3 {SPR_PLAY, 10, 6, NULL, S_FIGHTER_DIE5, 0, 0}, // S_FIGHTER_DIE4 {SPR_PLAY, 11, 6, A_NoBlocking, S_FIGHTER_DIE6, 0, 0}, // S_FIGHTER_DIE5 {SPR_PLAY, 12, 6, NULL, S_FIGHTER_DIE7, 0, 0}, // S_FIGHTER_DIE6 {SPR_PLAY, 13, -1, NULL, S_NULL, 0, 0}, // S_FIGHTER_DIE7 {SPR_PLAY, 14, 5, A_Scream, S_FIGHTER_XDIE2, 0, 0}, // S_FIGHTER_XDIE1 {SPR_PLAY, 15, 5, A_SkullPop, S_FIGHTER_XDIE3, 0, 0}, // S_FIGHTER_XDIE2 {SPR_PLAY, 17, 5, A_NoBlocking, S_FIGHTER_XDIE4, 0, 0}, // S_FIGHTER_XDIE3 {SPR_PLAY, 18, 5, NULL, S_FIGHTER_XDIE5, 0, 0}, // S_FIGHTER_XDIE4 {SPR_PLAY, 19, 5, NULL, S_FIGHTER_XDIE6, 0, 0}, // S_FIGHTER_XDIE5 {SPR_PLAY, 20, 5, NULL, S_FIGHTER_XDIE7, 0, 0}, // S_FIGHTER_XDIE6 {SPR_PLAY, 21, 5, NULL, S_FIGHTER_XDIE8, 0, 0}, // S_FIGHTER_XDIE7 {SPR_PLAY, 22, -1, NULL, S_NULL, 0, 0}, // S_FIGHTER_XDIE8 {SPR_PLAY, 23, 5, A_FreezeDeath, S_FIGHTER_ICE2, 0, 0}, // S_FIGHTER_ICE {SPR_PLAY, 23, 1, A_FreezeDeathChunks, S_FIGHTER_ICE2, 0, 0}, // S_FIGHTER_ICE2 {SPR_CLER, 0, 2, NULL, S_CLERIC2, 0, 0}, // S_CLERIC {SPR_CLER, 0, 3, A_ClassBossHealth, S_CLERICLOOK, 0, 0}, // S_CLERIC2 {SPR_CLER, 0, 5, A_Look, S_CLERICLOOK, 0, 0}, // S_CLERICLOOK {SPR_CLER, 0, 4, A_FastChase, S_CLERIC_RUN2, 0, 0}, // S_CLERIC_RUN1 {SPR_CLER, 1, 4, A_FastChase, S_CLERIC_RUN3, 0, 0}, // S_CLERIC_RUN2 {SPR_CLER, 2, 4, A_FastChase, S_CLERIC_RUN4, 0, 0}, // S_CLERIC_RUN3 {SPR_CLER, 3, 4, A_FastChase, S_CLERIC_RUN1, 0, 0}, // S_CLERIC_RUN4 {SPR_CLER, 4, 8, A_FaceTarget, S_CLERIC_ATK2, 0, 0}, // S_CLERIC_ATK1 {SPR_CLER, 5, 8, A_FaceTarget, S_CLERIC_ATK3, 0, 0}, // S_CLERIC_ATK2 {SPR_CLER, 6, 10, A_ClericAttack, S_CLERIC_RUN1, 0, 0}, // S_CLERIC_ATK3 {SPR_CLER, 7, 4, NULL, S_CLERIC_PAIN2, 0, 0}, // S_CLERIC_PAIN {SPR_CLER, 7, 4, A_Pain, S_CLERIC_RUN1, 0, 0}, // S_CLERIC_PAIN2 {SPR_CLER, 8, 6, NULL, S_CLERIC_DIE2, 0, 0}, // S_CLERIC_DIE1 {SPR_CLER, 10, 6, A_Scream, S_CLERIC_DIE3, 0, 0}, // S_CLERIC_DIE2 {SPR_CLER, 11, 6, NULL, S_CLERIC_DIE4, 0, 0}, // S_CLERIC_DIE3 {SPR_CLER, 11, 6, NULL, S_CLERIC_DIE5, 0, 0}, // S_CLERIC_DIE4 {SPR_CLER, 12, 6, A_NoBlocking, S_CLERIC_DIE6, 0, 0}, // S_CLERIC_DIE5 {SPR_CLER, 13, 6, NULL, S_CLERIC_DIE7, 0, 0}, // S_CLERIC_DIE6 {SPR_CLER, 14, 6, NULL, S_CLERIC_DIE8, 0, 0}, // S_CLERIC_DIE7 {SPR_CLER, 15, 6, NULL, S_CLERIC_DIE9, 0, 0}, // S_CLERIC_DIE8 {SPR_CLER, 16, -1, NULL, S_NULL, 0, 0}, // S_CLERIC_DIE9 {SPR_CLER, 17, 5, A_Scream, S_CLERIC_XDIE2, 0, 0}, // S_CLERIC_XDIE1 {SPR_CLER, 18, 5, NULL, S_CLERIC_XDIE3, 0, 0}, // S_CLERIC_XDIE2 {SPR_CLER, 19, 5, A_NoBlocking, S_CLERIC_XDIE4, 0, 0}, // S_CLERIC_XDIE3 {SPR_CLER, 20, 5, NULL, S_CLERIC_XDIE5, 0, 0}, // S_CLERIC_XDIE4 {SPR_CLER, 21, 5, NULL, S_CLERIC_XDIE6, 0, 0}, // S_CLERIC_XDIE5 {SPR_CLER, 22, 5, NULL, S_CLERIC_XDIE7, 0, 0}, // S_CLERIC_XDIE6 {SPR_CLER, 23, 5, NULL, S_CLERIC_XDIE8, 0, 0}, // S_CLERIC_XDIE7 {SPR_CLER, 24, 5, NULL, S_CLERIC_XDIE9, 0, 0}, // S_CLERIC_XDIE8 {SPR_CLER, 25, 5, NULL, S_CLERIC_XDIE10, 0, 0}, // S_CLERIC_XDIE9 {SPR_CLER, 26, -1, NULL, S_NULL, 0, 0}, // S_CLERIC_XDIE10 {SPR_CLER, 27, 5, A_FreezeDeath, S_CLERIC_ICE2, 0, 0}, // S_CLERIC_ICE {SPR_CLER, 27, 1, A_FreezeDeathChunks, S_CLERIC_ICE2, 0, 0}, // S_CLERIC_ICE2 {SPR_MAGE, 0, 2, NULL, S_MAGE2, 0, 0}, // S_MAGE {SPR_MAGE, 0, 3, A_ClassBossHealth, S_MAGELOOK, 0, 0}, // S_MAGE2 {SPR_MAGE, 0, 5, A_Look, S_MAGELOOK, 0, 0}, // S_MAGELOOK {SPR_MAGE, 0, 4, A_FastChase, S_MAGE_RUN2, 0, 0}, // S_MAGE_RUN1 {SPR_MAGE, 1, 4, A_FastChase, S_MAGE_RUN3, 0, 0}, // S_MAGE_RUN2 {SPR_MAGE, 2, 4, A_FastChase, S_MAGE_RUN4, 0, 0}, // S_MAGE_RUN3 {SPR_MAGE, 3, 4, A_FastChase, S_MAGE_RUN1, 0, 0}, // S_MAGE_RUN4 {SPR_MAGE, 4, 8, A_FaceTarget, S_MAGE_ATK2, 0, 0}, // S_MAGE_ATK1 {SPR_MAGE, 32773, 8, A_MageAttack, S_MAGE_RUN1, 0, 0}, // S_MAGE_ATK2 {SPR_MAGE, 6, 4, NULL, S_MAGE_PAIN2, 0, 0}, // S_MAGE_PAIN {SPR_MAGE, 6, 4, A_Pain, S_MAGE_RUN1, 0, 0}, // S_MAGE_PAIN2 {SPR_MAGE, 7, 6, NULL, S_MAGE_DIE2, 0, 0}, // S_MAGE_DIE1 {SPR_MAGE, 8, 6, A_Scream, S_MAGE_DIE3, 0, 0}, // S_MAGE_DIE2 {SPR_MAGE, 9, 6, NULL, S_MAGE_DIE4, 0, 0}, // S_MAGE_DIE3 {SPR_MAGE, 10, 6, NULL, S_MAGE_DIE5, 0, 0}, // S_MAGE_DIE4 {SPR_MAGE, 11, 6, A_NoBlocking, S_MAGE_DIE6, 0, 0}, // S_MAGE_DIE5 {SPR_MAGE, 12, 6, NULL, S_MAGE_DIE7, 0, 0}, // S_MAGE_DIE6 {SPR_MAGE, 13, -1, NULL, S_NULL, 0, 0}, // S_MAGE_DIE7 {SPR_MAGE, 14, 5, A_Scream, S_MAGE_XDIE2, 0, 0}, // S_MAGE_XDIE1 {SPR_MAGE, 15, 5, NULL, S_MAGE_XDIE3, 0, 0}, // S_MAGE_XDIE2 {SPR_MAGE, 17, 5, A_NoBlocking, S_MAGE_XDIE4, 0, 0}, // S_MAGE_XDIE3 {SPR_MAGE, 18, 5, NULL, S_MAGE_XDIE5, 0, 0}, // S_MAGE_XDIE4 {SPR_MAGE, 19, 5, NULL, S_MAGE_XDIE6, 0, 0}, // S_MAGE_XDIE5 {SPR_MAGE, 20, 5, NULL, S_MAGE_XDIE7, 0, 0}, // S_MAGE_XDIE6 {SPR_MAGE, 21, 5, NULL, S_MAGE_XDIE8, 0, 0}, // S_MAGE_XDIE7 {SPR_MAGE, 22, 5, NULL, S_MAGE_XDIE9, 0, 0}, // S_MAGE_XDIE8 {SPR_MAGE, 23, -1, NULL, S_NULL, 0, 0}, // S_MAGE_XDIE9 {SPR_MAGE, 24, 5, A_FreezeDeath, S_MAGE_ICE2, 0, 0}, // S_MAGE_ICE {SPR_MAGE, 24, 1, A_FreezeDeathChunks, S_MAGE_ICE2, 0, 0}, // S_MAGE_ICE2 {SPR_SORC, 0, 3, NULL, S_SORC_SPAWN2, 0, 0}, // S_SORC_SPAWN1 {SPR_SORC, 0, 2, A_SorcSpinBalls, S_SORC_LOOK1, 0, 0}, // S_SORC_SPAWN2 {SPR_SORC, 0, 10, A_Look, S_SORC_LOOK1, 0, 0}, // S_SORC_LOOK1 {SPR_SORC, 0, 5, A_Chase, S_SORC_WALK2, 0, 0}, // S_SORC_WALK1 {SPR_SORC, 1, 5, A_Chase, S_SORC_WALK3, 0, 0}, // S_SORC_WALK2 {SPR_SORC, 2, 5, A_Chase, S_SORC_WALK4, 0, 0}, // S_SORC_WALK3 {SPR_SORC, 3, 5, A_Chase, S_SORC_WALK1, 0, 0}, // S_SORC_WALK4 {SPR_SORC, 6, 8, NULL, S_SORC_PAIN2, 0, 0}, // S_SORC_PAIN1 {SPR_SORC, 6, 8, A_Pain, S_SORC_WALK1, 0, 0}, // S_SORC_PAIN2 {SPR_SORC, 32773, 6, A_FaceTarget, S_SORC_ATK2_2, 0, 0}, // S_SORC_ATK2_1 {SPR_SORC, 32773, 6, A_SpeedBalls, S_SORC_ATK2_3, 0, 0}, // S_SORC_ATK2_2 {SPR_SORC, 32773, 6, A_FaceTarget, S_SORC_ATK2_3, 0, 0}, // S_SORC_ATK2_3 {SPR_SORC, 32772, 6, NULL, S_SORC_ATTACK2, 0, 0}, // S_SORC_ATTACK1 {SPR_SORC, 32772, 6, A_SpawnFizzle, S_SORC_ATTACK3, 0, 0}, // S_SORC_ATTACK2 {SPR_SORC, 32772, 5, A_FaceTarget, S_SORC_ATTACK2, 0, 0}, // S_SORC_ATTACK3 {SPR_SORC, 32772, 2, NULL, S_SORC_ATTACK5, 0, 0}, // S_SORC_ATTACK4 {SPR_SORC, 32772, 2, A_SorcBossAttack, S_SORC_WALK1, 0, 0}, // S_SORC_ATTACK5 {SPR_SORC, 32775, 5, NULL, S_SORC_DIE2, 0, 0}, // S_SORC_DIE1 {SPR_SORC, 32776, 5, A_FaceTarget, S_SORC_DIE3, 0, 0}, // S_SORC_DIE2 {SPR_SORC, 32777, 5, A_Scream, S_SORC_DIE4, 0, 0}, // S_SORC_DIE3 {SPR_SORC, 32778, 5, NULL, S_SORC_DIE5, 0, 0}, // S_SORC_DIE4 {SPR_SORC, 32779, 5, NULL, S_SORC_DIE6, 0, 0}, // S_SORC_DIE5 {SPR_SORC, 32780, 5, NULL, S_SORC_DIE7, 0, 0}, // S_SORC_DIE6 {SPR_SORC, 32781, 5, NULL, S_SORC_DIE8, 0, 0}, // S_SORC_DIE7 {SPR_SORC, 32782, 5, NULL, S_SORC_DIE9, 0, 0}, // S_SORC_DIE8 {SPR_SORC, 32783, 5, NULL, S_SORC_DIE0, 0, 0}, // S_SORC_DIE9 {SPR_SORC, 32784, 5, NULL, S_SORC_DIEA, 0, 0}, // S_SORC_DIE0 {SPR_SORC, 32785, 5, NULL, S_SORC_DIEB, 0, 0}, // S_SORC_DIEA {SPR_SORC, 32786, 5, NULL, S_SORC_DIEC, 0, 0}, // S_SORC_DIEB {SPR_SORC, 32787, 5, NULL, S_SORC_DIED, 0, 0}, // S_SORC_DIEC {SPR_SORC, 32788, 5, A_NoBlocking, S_SORC_DIEE, 0, 0}, // S_SORC_DIED {SPR_SORC, 32789, 5, NULL, S_SORC_DIEF, 0, 0}, // S_SORC_DIEE {SPR_SORC, 32790, 5, NULL, S_SORC_DIEG, 0, 0}, // S_SORC_DIEF {SPR_SORC, 32791, 5, NULL, S_SORC_DIEH, 0, 0}, // S_SORC_DIEG {SPR_SORC, 32792, 5, NULL, S_SORC_DIEI, 0, 0}, // S_SORC_DIEH {SPR_SORC, 32793, -1, NULL, S_NULL, 0, 0}, // S_SORC_DIEI {SPR_SBMP, 0, 2, A_SorcBallOrbit, S_SORCBALL1_2, 0, 0}, // S_SORCBALL1_1 {SPR_SBMP, 1, 2, A_SorcBallOrbit, S_SORCBALL1_3, 0, 0}, // S_SORCBALL1_2 {SPR_SBMP, 2, 2, A_SorcBallOrbit, S_SORCBALL1_4, 0, 0}, // S_SORCBALL1_3 {SPR_SBMP, 3, 2, A_SorcBallOrbit, S_SORCBALL1_5, 0, 0}, // S_SORCBALL1_4 {SPR_SBMP, 4, 2, A_SorcBallOrbit, S_SORCBALL1_6, 0, 0}, // S_SORCBALL1_5 {SPR_SBMP, 5, 2, A_SorcBallOrbit, S_SORCBALL1_7, 0, 0}, // S_SORCBALL1_6 {SPR_SBMP, 6, 2, A_SorcBallOrbit, S_SORCBALL1_8, 0, 0}, // S_SORCBALL1_7 {SPR_SBMP, 7, 2, A_SorcBallOrbit, S_SORCBALL1_9, 0, 0}, // S_SORCBALL1_8 {SPR_SBMP, 8, 2, A_SorcBallOrbit, S_SORCBALL1_0, 0, 0}, // S_SORCBALL1_9 {SPR_SBMP, 9, 2, A_SorcBallOrbit, S_SORCBALL1_A, 0, 0}, // S_SORCBALL1_0 {SPR_SBMP, 10, 2, A_SorcBallOrbit, S_SORCBALL1_B, 0, 0}, // S_SORCBALL1_A {SPR_SBMP, 11, 2, A_SorcBallOrbit, S_SORCBALL1_C, 0, 0}, // S_SORCBALL1_B {SPR_SBMP, 12, 2, A_SorcBallOrbit, S_SORCBALL1_D, 0, 0}, // S_SORCBALL1_C {SPR_SBMP, 13, 2, A_SorcBallOrbit, S_SORCBALL1_E, 0, 0}, // S_SORCBALL1_D {SPR_SBMP, 14, 2, A_SorcBallOrbit, S_SORCBALL1_F, 0, 0}, // S_SORCBALL1_E {SPR_SBMP, 15, 2, A_SorcBallOrbit, S_SORCBALL1_1, 0, 0}, // S_SORCBALL1_F {SPR_SBMP, 0, 5, A_SorcBallPop, S_SORCBALL1_D2, 0, 0}, // S_SORCBALL1_D1 {SPR_SBMP, 1, 2, A_BounceCheck, S_SORCBALL1_D2, 0, 0}, // S_SORCBALL1_D2 {SPR_SBS4, 3, 5, A_Explode, S_SORCBALL1_D6, 0, 0}, // S_SORCBALL1_D5 {SPR_SBS4, 4, 5, NULL, S_SORCBALL1_D7, 0, 0}, // S_SORCBALL1_D6 {SPR_SBS4, 5, 6, NULL, S_SORCBALL1_D8, 0, 0}, // S_SORCBALL1_D7 {SPR_SBS4, 6, 6, NULL, S_SORCBALL1_D9, 0, 0}, // S_SORCBALL1_D8 {SPR_SBS4, 7, 6, NULL, S_NULL, 0, 0}, // S_SORCBALL1_D9 {SPR_SBMB, 0, 2, A_SorcBallOrbit, S_SORCBALL2_2, 0, 0}, // S_SORCBALL2_1 {SPR_SBMB, 1, 2, A_SorcBallOrbit, S_SORCBALL2_3, 0, 0}, // S_SORCBALL2_2 {SPR_SBMB, 2, 2, A_SorcBallOrbit, S_SORCBALL2_4, 0, 0}, // S_SORCBALL2_3 {SPR_SBMB, 3, 2, A_SorcBallOrbit, S_SORCBALL2_5, 0, 0}, // S_SORCBALL2_4 {SPR_SBMB, 4, 2, A_SorcBallOrbit, S_SORCBALL2_6, 0, 0}, // S_SORCBALL2_5 {SPR_SBMB, 5, 2, A_SorcBallOrbit, S_SORCBALL2_7, 0, 0}, // S_SORCBALL2_6 {SPR_SBMB, 6, 2, A_SorcBallOrbit, S_SORCBALL2_8, 0, 0}, // S_SORCBALL2_7 {SPR_SBMB, 7, 2, A_SorcBallOrbit, S_SORCBALL2_9, 0, 0}, // S_SORCBALL2_8 {SPR_SBMB, 8, 2, A_SorcBallOrbit, S_SORCBALL2_0, 0, 0}, // S_SORCBALL2_9 {SPR_SBMB, 9, 2, A_SorcBallOrbit, S_SORCBALL2_A, 0, 0}, // S_SORCBALL2_0 {SPR_SBMB, 10, 2, A_SorcBallOrbit, S_SORCBALL2_B, 0, 0}, // S_SORCBALL2_A {SPR_SBMB, 11, 2, A_SorcBallOrbit, S_SORCBALL2_C, 0, 0}, // S_SORCBALL2_B {SPR_SBMB, 12, 2, A_SorcBallOrbit, S_SORCBALL2_D, 0, 0}, // S_SORCBALL2_C {SPR_SBMB, 13, 2, A_SorcBallOrbit, S_SORCBALL2_E, 0, 0}, // S_SORCBALL2_D {SPR_SBMB, 14, 2, A_SorcBallOrbit, S_SORCBALL2_F, 0, 0}, // S_SORCBALL2_E {SPR_SBMB, 15, 2, A_SorcBallOrbit, S_SORCBALL2_1, 0, 0}, // S_SORCBALL2_F {SPR_SBMB, 0, 5, A_SorcBallPop, S_SORCBALL2_D2, 0, 0}, // S_SORCBALL2_D1 {SPR_SBMB, 1, 2, A_BounceCheck, S_SORCBALL2_D2, 0, 0}, // S_SORCBALL2_D2 {SPR_SBS3, 3, 5, A_Explode, S_SORCBALL2_D6, 0, 0}, // S_SORCBALL2_D5 {SPR_SBS3, 4, 5, NULL, S_SORCBALL2_D7, 0, 0}, // S_SORCBALL2_D6 {SPR_SBS3, 5, 6, NULL, S_SORCBALL2_D8, 0, 0}, // S_SORCBALL2_D7 {SPR_SBS3, 6, 6, NULL, S_SORCBALL2_D9, 0, 0}, // S_SORCBALL2_D8 {SPR_SBS3, 7, 6, NULL, S_NULL, 0, 0}, // S_SORCBALL2_D9 {SPR_SBMG, 0, 2, A_SorcBallOrbit, S_SORCBALL3_2, 0, 0}, // S_SORCBALL3_1 {SPR_SBMG, 1, 2, A_SorcBallOrbit, S_SORCBALL3_3, 0, 0}, // S_SORCBALL3_2 {SPR_SBMG, 2, 2, A_SorcBallOrbit, S_SORCBALL3_4, 0, 0}, // S_SORCBALL3_3 {SPR_SBMG, 3, 2, A_SorcBallOrbit, S_SORCBALL3_5, 0, 0}, // S_SORCBALL3_4 {SPR_SBMG, 4, 2, A_SorcBallOrbit, S_SORCBALL3_6, 0, 0}, // S_SORCBALL3_5 {SPR_SBMG, 5, 2, A_SorcBallOrbit, S_SORCBALL3_7, 0, 0}, // S_SORCBALL3_6 {SPR_SBMG, 6, 2, A_SorcBallOrbit, S_SORCBALL3_8, 0, 0}, // S_SORCBALL3_7 {SPR_SBMG, 7, 2, A_SorcBallOrbit, S_SORCBALL3_9, 0, 0}, // S_SORCBALL3_8 {SPR_SBMG, 8, 2, A_SorcBallOrbit, S_SORCBALL3_0, 0, 0}, // S_SORCBALL3_9 {SPR_SBMG, 9, 2, A_SorcBallOrbit, S_SORCBALL3_A, 0, 0}, // S_SORCBALL3_0 {SPR_SBMG, 10, 2, A_SorcBallOrbit, S_SORCBALL3_B, 0, 0}, // S_SORCBALL3_A {SPR_SBMG, 11, 2, A_SorcBallOrbit, S_SORCBALL3_C, 0, 0}, // S_SORCBALL3_B {SPR_SBMG, 12, 2, A_SorcBallOrbit, S_SORCBALL3_D, 0, 0}, // S_SORCBALL3_C {SPR_SBMG, 13, 2, A_SorcBallOrbit, S_SORCBALL3_E, 0, 0}, // S_SORCBALL3_D {SPR_SBMG, 14, 2, A_SorcBallOrbit, S_SORCBALL3_F, 0, 0}, // S_SORCBALL3_E {SPR_SBMG, 15, 2, A_SorcBallOrbit, S_SORCBALL3_1, 0, 0}, // S_SORCBALL3_F {SPR_SBMG, 0, 5, A_SorcBallPop, S_SORCBALL3_D2, 0, 0}, // S_SORCBALL3_D1 {SPR_SBMG, 1, 2, A_BounceCheck, S_SORCBALL3_D2, 0, 0}, // S_SORCBALL3_D2 {SPR_SBS3, 3, 5, A_Explode, S_SORCBALL3_D6, 0, 0}, // S_SORCBALL3_D5 {SPR_SBS3, 4, 5, NULL, S_SORCBALL3_D7, 0, 0}, // S_SORCBALL3_D6 {SPR_SBS3, 5, 6, NULL, S_SORCBALL3_D8, 0, 0}, // S_SORCBALL3_D7 {SPR_SBS3, 6, 6, NULL, S_SORCBALL3_D9, 0, 0}, // S_SORCBALL3_D8 {SPR_SBS3, 7, 6, NULL, S_NULL, 0, 0}, // S_SORCBALL3_D9 {SPR_SBS1, 32768, 2, NULL, S_SORCFX1_2, 0, 0}, // S_SORCFX1_1 {SPR_SBS1, 32769, 3, A_SorcFX1Seek, S_SORCFX1_3, 0, 0}, // S_SORCFX1_2 {SPR_SBS1, 32770, 3, A_SorcFX1Seek, S_SORCFX1_4, 0, 0}, // S_SORCFX1_3 {SPR_SBS1, 32771, 3, A_SorcFX1Seek, S_SORCFX1_1, 0, 0}, // S_SORCFX1_4 {SPR_FHFX, 32786, 2, A_Explode, S_SORCFX1_D2, 0, 0}, // S_SORCFX1_D1 {SPR_FHFX, 32786, 6, NULL, S_SORCFX1_D3, 0, 0}, // S_SORCFX1_D2 {SPR_FHFX, 32786, 6, NULL, S_NULL, 0, 0}, // S_SORCFX1_D3 {SPR_SBS2, 32768, 3, A_SorcFX2Split, S_SORCFX2_SPLIT1, 0, 0}, // S_SORCFX2_SPLIT1 {SPR_SBS2, 32768, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT2, 0, 0}, // S_SORCFX2_ORBIT1 {SPR_SBS2, 32769, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT3, 0, 0}, // S_SORCFX2_ORBIT2 {SPR_SBS2, 32770, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT4, 0, 0}, // S_SORCFX2_ORBIT3 {SPR_SBS2, 32771, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT5, 0, 0}, // S_SORCFX2_ORBIT4 {SPR_SBS2, 32772, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT6, 0, 0}, // S_SORCFX2_ORBIT5 {SPR_SBS2, 32773, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT7, 0, 0}, // S_SORCFX2_ORBIT6 {SPR_SBS2, 32774, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT8, 0, 0}, // S_SORCFX2_ORBIT7 {SPR_SBS2, 32775, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT9, 0, 0}, // S_SORCFX2_ORBIT8 {SPR_SBS2, 32776, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT0, 0, 0}, // S_SORCFX2_ORBIT9 {SPR_SBS2, 32777, 2, A_SorcFX2Orbit, S_SORCFX2_ORBITA, 0, 0}, // S_SORCFX2_ORBIT0 {SPR_SBS2, 32778, 2, A_SorcFX2Orbit, S_SORCFX2_ORBITB, 0, 0}, // S_SORCFX2_ORBITA {SPR_SBS2, 32779, 2, A_SorcFX2Orbit, S_SORCFX2_ORBITC, 0, 0}, // S_SORCFX2_ORBITB {SPR_SBS2, 32780, 2, A_SorcFX2Orbit, S_SORCFX2_ORBITD, 0, 0}, // S_SORCFX2_ORBITC {SPR_SBS2, 32781, 2, A_SorcFX2Orbit, S_SORCFX2_ORBITE, 0, 0}, // S_SORCFX2_ORBITD {SPR_SBS2, 32782, 2, A_SorcFX2Orbit, S_SORCFX2_ORBITF, 0, 0}, // S_SORCFX2_ORBITE {SPR_SBS2, 32783, 2, A_SorcFX2Orbit, S_SORCFX2_ORBIT1, 0, 0}, // S_SORCFX2_ORBITF {SPR_SBS2, 0, 10, NULL, S_NULL, 0, 0}, // S_SORCFX2T1 {SPR_SBS3, 32768, 2, NULL, S_SORCFX3_2, 0, 0}, // S_SORCFX3_1 {SPR_SBS3, 32769, 2, NULL, S_SORCFX3_3, 0, 0}, // S_SORCFX3_2 {SPR_SBS3, 32770, 2, NULL, S_SORCFX3_1, 0, 0}, // S_SORCFX3_3 {SPR_SBS3, 32768, 4, NULL, S_BISHMORPHA, 0, 0}, // S_BISHMORPH1 {SPR_BISH, 15, 4, A_SorcererBishopEntry, S_BISHMORPHB, 0, 0}, // S_BISHMORPHA {SPR_BISH, 14, 4, NULL, S_BISHMORPHC, 0, 0}, // S_BISHMORPHB {SPR_BISH, 13, 4, NULL, S_BISHMORPHD, 0, 0}, // S_BISHMORPHC {SPR_BISH, 12, 3, NULL, S_BISHMORPHE, 0, 0}, // S_BISHMORPHD {SPR_BISH, 11, 3, NULL, S_BISHMORPHF, 0, 0}, // S_BISHMORPHE {SPR_BISH, 10, 3, NULL, S_BISHMORPHG, 0, 0}, // S_BISHMORPHF {SPR_BISH, 9, 3, NULL, S_BISHMORPHH, 0, 0}, // S_BISHMORPHG {SPR_BISH, 8, 3, NULL, S_BISHMORPHI, 0, 0}, // S_BISHMORPHH {SPR_BISH, 7, 3, NULL, S_BISHMORPHJ, 0, 0}, // S_BISHMORPHI {SPR_BISH, 6, 3, A_SpawnBishop, S_NULL, 0, 0}, // S_BISHMORPHJ {SPR_SBS3, 3, 3, NULL, S_SORCFX3_EXP2, 0, 0}, // S_SORCFX3_EXP1 {SPR_SBS3, 4, 3, NULL, S_SORCFX3_EXP3, 0, 0}, // S_SORCFX3_EXP2 {SPR_SBS3, 5, 3, NULL, S_SORCFX3_EXP4, 0, 0}, // S_SORCFX3_EXP3 {SPR_SBS3, 6, 3, NULL, S_SORCFX3_EXP5, 0, 0}, // S_SORCFX3_EXP4 {SPR_SBS3, 7, 3, NULL, S_NULL, 0, 0}, // S_SORCFX3_EXP5 {SPR_SBS4, 32768, 2, A_SorcFX4Check, S_SORCFX4_2, 0, 0}, // S_SORCFX4_1 {SPR_SBS4, 32769, 2, A_SorcFX4Check, S_SORCFX4_3, 0, 0}, // S_SORCFX4_2 {SPR_SBS4, 32770, 2, A_SorcFX4Check, S_SORCFX4_1, 0, 0}, // S_SORCFX4_3 {SPR_SBS4, 32771, 2, NULL, S_SORCFX4_D2, 0, 0}, // S_SORCFX4_D1 {SPR_SBS4, 32772, 2, A_Explode, S_SORCFX4_D3, 0, 0}, // S_SORCFX4_D2 {SPR_SBS4, 32773, 2, NULL, S_SORCFX4_D4, 0, 0}, // S_SORCFX4_D3 {SPR_SBS4, 32774, 2, NULL, S_SORCFX4_D5, 0, 0}, // S_SORCFX4_D4 {SPR_SBS4, 32775, 2, NULL, S_NULL, 0, 0}, // S_SORCFX4_D5 {SPR_SBFX, 32768, 4, NULL, S_SORCSPARK2, 0, 0}, // S_SORCSPARK1 {SPR_SBFX, 32769, 4, NULL, S_SORCSPARK3, 0, 0}, // S_SORCSPARK2 {SPR_SBFX, 32770, 4, NULL, S_SORCSPARK4, 0, 0}, // S_SORCSPARK3 {SPR_SBFX, 32771, 4, NULL, S_SORCSPARK5, 0, 0}, // S_SORCSPARK4 {SPR_SBFX, 32772, 4, NULL, S_SORCSPARK6, 0, 0}, // S_SORCSPARK5 {SPR_SBFX, 32773, 4, NULL, S_SORCSPARK7, 0, 0}, // S_SORCSPARK6 {SPR_SBFX, 32774, 4, NULL, S_NULL, 0, 0}, // S_SORCSPARK7 {SPR_RADE, 0, 4, NULL, S_BLASTEFFECT2, 0, 0}, // S_BLASTEFFECT1 {SPR_RADE, 1, 4, NULL, S_BLASTEFFECT3, 0, 0}, // S_BLASTEFFECT2 {SPR_RADE, 2, 4, NULL, S_BLASTEFFECT4, 0, 0}, // S_BLASTEFFECT3 {SPR_RADE, 3, 4, NULL, S_BLASTEFFECT5, 0, 0}, // S_BLASTEFFECT4 {SPR_RADE, 4, 4, NULL, S_BLASTEFFECT6, 0, 0}, // S_BLASTEFFECT5 {SPR_RADE, 5, 4, NULL, S_BLASTEFFECT7, 0, 0}, // S_BLASTEFFECT6 {SPR_RADE, 6, 4, NULL, S_BLASTEFFECT8, 0, 0}, // S_BLASTEFFECT7 {SPR_RADE, 7, 4, NULL, S_BLASTEFFECT9, 0, 0}, // S_BLASTEFFECT8 {SPR_RADE, 8, 4, NULL, S_NULL, 0, 0}, // S_BLASTEFFECT9 {SPR_WATR, 0, 5, NULL, S_WATERDRIP1, 0, 0}, // S_WATERDRIP1 {SPR_KORX, 0, 5, A_Look, S_KORAX_LOOK1, 0, 0}, // S_KORAX_LOOK1 {SPR_KORX, 0, 3, A_KoraxStep2, S_KORAX_CHASE2, 0, 0}, // S_KORAX_CHASE1 {SPR_KORX, 0, 3, A_KoraxChase, S_KORAX_CHASE3, 0, 0}, // S_KORAX_CHASE2 {SPR_KORX, 0, 3, A_KoraxChase, S_KORAX_CHASE4, 0, 0}, // S_KORAX_CHASE3 {SPR_KORX, 0, 3, A_KoraxChase, S_KORAX_CHASE5, 0, 0}, // S_KORAX_CHASE4 {SPR_KORX, 1, 3, A_KoraxStep, S_KORAX_CHASE6, 0, 0}, // S_KORAX_CHASE5 {SPR_KORX, 1, 3, A_KoraxChase, S_KORAX_CHASE7, 0, 0}, // S_KORAX_CHASE6 {SPR_KORX, 1, 3, A_KoraxChase, S_KORAX_CHASE8, 0, 0}, // S_KORAX_CHASE7 {SPR_KORX, 1, 3, A_KoraxChase, S_KORAX_CHASE9, 0, 0}, // S_KORAX_CHASE8 {SPR_KORX, 2, 3, A_KoraxStep2, S_KORAX_CHASE0, 0, 0}, // S_KORAX_CHASE9 {SPR_KORX, 2, 3, A_KoraxChase, S_KORAX_CHASEA, 0, 0}, // S_KORAX_CHASE0 {SPR_KORX, 2, 3, A_KoraxChase, S_KORAX_CHASEB, 0, 0}, // S_KORAX_CHASEA {SPR_KORX, 2, 3, A_KoraxChase, S_KORAX_CHASEC, 0, 0}, // S_KORAX_CHASEB {SPR_KORX, 3, 3, A_KoraxStep, S_KORAX_CHASED, 0, 0}, // S_KORAX_CHASEC {SPR_KORX, 3, 3, A_KoraxChase, S_KORAX_CHASEE, 0, 0}, // S_KORAX_CHASED {SPR_KORX, 3, 3, A_KoraxChase, S_KORAX_CHASEF, 0, 0}, // S_KORAX_CHASEE {SPR_KORX, 3, 3, A_KoraxChase, S_KORAX_CHASE1, 0, 0}, // S_KORAX_CHASEF {SPR_KORX, 7, 5, A_Pain, S_KORAX_PAIN2, 0, 0}, // S_KORAX_PAIN1 {SPR_KORX, 7, 5, NULL, S_KORAX_CHASE2, 0, 0}, // S_KORAX_PAIN2 {SPR_KORX, 32772, 2, A_FaceTarget, S_KORAX_ATTACK2, 0, 0}, // S_KORAX_ATTACK1 {SPR_KORX, 32772, 5, A_KoraxDecide, S_KORAX_ATTACK2, 0, 0}, // S_KORAX_ATTACK2 {SPR_KORX, 32772, 4, A_FaceTarget, S_KORAX_MISSILE2, 0, 0}, // S_KORAX_MISSILE1 {SPR_KORX, 32773, 8, A_KoraxMissile, S_KORAX_MISSILE3, 0, 0}, // S_KORAX_MISSILE2 {SPR_KORX, 32772, 8, NULL, S_KORAX_CHASE2, 0, 0}, // S_KORAX_MISSILE3 {SPR_KORX, 32772, 5, A_FaceTarget, S_KORAX_COMMAND2, 0, 0}, // S_KORAX_COMMAND1 {SPR_KORX, 32790, 10, A_FaceTarget, S_KORAX_COMMAND3, 0, 0}, // S_KORAX_COMMAND2 {SPR_KORX, 32774, 15, A_KoraxCommand, S_KORAX_COMMAND4, 0, 0}, // S_KORAX_COMMAND3 {SPR_KORX, 32790, 10, NULL, S_KORAX_COMMAND5, 0, 0}, // S_KORAX_COMMAND4 {SPR_KORX, 32772, 5, NULL, S_KORAX_CHASE2, 0, 0}, // S_KORAX_COMMAND5 {SPR_KORX, 8, 5, NULL, S_KORAX_DEATH2, 0, 0}, // S_KORAX_DEATH1 {SPR_KORX, 9, 5, A_FaceTarget, S_KORAX_DEATH3, 0, 0}, // S_KORAX_DEATH2 {SPR_KORX, 10, 5, A_Scream, S_KORAX_DEATH4, 0, 0}, // S_KORAX_DEATH3 {SPR_KORX, 11, 5, NULL, S_KORAX_DEATH5, 0, 0}, // S_KORAX_DEATH4 {SPR_KORX, 12, 5, NULL, S_KORAX_DEATH6, 0, 0}, // S_KORAX_DEATH5 {SPR_KORX, 13, 5, NULL, S_KORAX_DEATH7, 0, 0}, // S_KORAX_DEATH6 {SPR_KORX, 14, 5, NULL, S_KORAX_DEATH8, 0, 0}, // S_KORAX_DEATH7 {SPR_KORX, 15, 5, NULL, S_KORAX_DEATH9, 0, 0}, // S_KORAX_DEATH8 {SPR_KORX, 16, 10, NULL, S_KORAX_DEATH0, 0, 0}, // S_KORAX_DEATH9 {SPR_KORX, 17, 5, A_KoraxBonePop, S_KORAX_DEATHA, 0, 0}, // S_KORAX_DEATH0 {SPR_KORX, 18, 5, A_NoBlocking, S_KORAX_DEATHB, 0, 0}, // S_KORAX_DEATHA {SPR_KORX, 19, 5, NULL, S_KORAX_DEATHC, 0, 0}, // S_KORAX_DEATHB {SPR_KORX, 20, 5, NULL, S_KORAX_DEATHD, 0, 0}, // S_KORAX_DEATHC {SPR_KORX, 21, -1, NULL, S_NULL, 0, 0}, // S_KORAX_DEATHD {SPR_SPIR, 0, 5, A_KSpiritRoam, S_KSPIRIT_ROAM2, 0, 0}, // S_KSPIRIT_ROAM1 {SPR_SPIR, 1, 5, A_KSpiritRoam, S_KSPIRIT_ROAM1, 0, 0}, // S_KSPIRIT_ROAM2 {SPR_SPIR, 3, 5, NULL, S_KSPIRIT_DEATH2, 0, 0}, // S_KSPIRIT_DEATH1 {SPR_SPIR, 4, 5, NULL, S_KSPIRIT_DEATH3, 0, 0}, // S_KSPIRIT_DEATH2 {SPR_SPIR, 5, 5, NULL, S_KSPIRIT_DEATH4, 0, 0}, // S_KSPIRIT_DEATH3 {SPR_SPIR, 6, 5, NULL, S_KSPIRIT_DEATH5, 0, 0}, // S_KSPIRIT_DEATH4 {SPR_SPIR, 7, 5, NULL, S_KSPIRIT_DEATH6, 0, 0}, // S_KSPIRIT_DEATH5 {SPR_SPIR, 8, 5, NULL, S_NULL, 0, 0}, // S_KSPIRIT_DEATH6 {SPR_MLFX, 32776, 2, NULL, S_KBOLT2, 0, 0}, // S_KBOLT1 {SPR_MLFX, 32777, 2, A_KBoltRaise, S_KBOLT3, 0, 0}, // S_KBOLT2 {SPR_MLFX, 32776, 2, A_KBolt, S_KBOLT4, 0, 0}, // S_KBOLT3 {SPR_MLFX, 32777, 2, A_KBolt, S_KBOLT5, 0, 0}, // S_KBOLT4 {SPR_MLFX, 32778, 2, A_KBolt, S_KBOLT6, 0, 0}, // S_KBOLT5 {SPR_MLFX, 32779, 2, A_KBolt, S_KBOLT7, 0, 0}, // S_KBOLT6 {SPR_MLFX, 32780, 2, A_KBolt, S_KBOLT3, 0, 0}, // S_KBOLT7 {SPR_MAN1, 0, 2, NULL, S_SPAWNBATS2, 0, 0}, // S_SPAWNBATS1 {SPR_MAN1, 0, 2, A_BatSpawnInit, S_SPAWNBATS3, 0, 0}, // S_SPAWNBATS2 {SPR_MAN1, 0, 2, A_BatSpawn, S_SPAWNBATS3, 0, 0}, // S_SPAWNBATS3 {SPR_MAN1, 0, -1, NULL, S_NULL, 0, 0}, // S_SPAWNBATS_OFF {SPR_ABAT, 0, 2, A_BatMove, S_BAT2, 0, 0}, // S_BAT1 {SPR_ABAT, 1, 2, A_BatMove, S_BAT3, 0, 0}, // S_BAT2 {SPR_ABAT, 2, 2, A_BatMove, S_BAT1, 0, 0}, // S_BAT3 {SPR_ABAT, 0, 2, NULL, S_NULL, 0, 0} // S_BAT_DEATH }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = { { // MT_MAPSPOT 9001, // doomednum S_MAPSPOT, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MAPSPOTGRAVITY 9013, // doomednum S_MAPSPOT, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_DONTDRAW // flags2 }, { // MT_FIREBALL1 -1, // doomednum S_FIREBALL1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FIREBALL1_X1, // deathstate S_NULL, // xdeathstate SFX_FIREBALL, // deathsound 2 * FRACUNIT, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_ARROW -1, // doomednum S_ARROW_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ARROW_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 6 * FRACUNIT, // speed 8 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_DART -1, // doomednum S_DART_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DART_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 6 * FRACUNIT, // speed 8 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 2, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_POISONDART -1, // doomednum S_POISONDART_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_POISONDART_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 6 * FRACUNIT, // speed 8 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 2, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_RIPPERBALL -1, // doomednum S_RIPPERBALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_RIPPERBALL_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 6 * FRACUNIT, // speed 8 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 2, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_RIP // flags2 }, { // MT_PROJECTILE_BLADE -1, // doomednum S_PRJ_BLADE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_PRJ_BLADE_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 6 * FRACUNIT, // speed 6 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 3, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_ICESHARD -1, // doomednum S_ICESHARD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SHARDFXE1_1, // deathstate S_NULL, // xdeathstate SFX_MAGE_SHARDS_EXPLODE, // deathsound 25 * FRACUNIT, // speed 13 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 1, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_ICEDAMAGE // flags2 }, { // MT_FLAME_SMALL_TEMP 10500, // doomednum S_FLAME_TSMALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT // flags2 }, { // MT_FLAME_LARGE_TEMP 10502, // doomednum S_FLAME_TLARGE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT // flags2 }, { // MT_FLAME_SMALL 10501, // doomednum S_FLAME_SMALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT | MF2_DONTDRAW // flags2 }, { // MT_FLAME_LARGE 10503, // doomednum S_FLAME_LARGE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT | MF2_DONTDRAW // flags2 }, { // MT_HEALINGBOTTLE 81, // doomednum S_ITEM_PTN1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_HEALTHFLASK 82, // doomednum S_ARTI_PTN2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTIFLY 83, // doomednum S_ARTI_SOAR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTIINVULNERABILITY 84, // doomednum S_ARTI_INVU1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_SUMMONMAULATOR 86, // doomednum S_ARTI_SUMMON, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_SUMMON_FX -1, // doomednum S_SUMMON_FX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SUMMON_FX2_1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 20 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_MISSILE | MF_DROPOFF | MF_NOBLOCKMAP, // flags MF2_NOTELEPORT // flags2 }, { // MT_THRUSTFLOOR_UP 10091, // doomednum S_THRUSTINIT2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 128 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_THRUSTFLOOR_DOWN 10090, // doomednum S_THRUSTINIT1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 128 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound 0, // flags MF2_NOTELEPORT | MF2_FLOORCLIP | MF2_DONTDRAW // flags2 }, { // MT_TELEPORTOTHER 10040, // doomednum S_ARTI_TELOTHER1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_TELOTHER_FX1 -1, // doomednum S_TELO_FX1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_TELO_FX9, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 20 * FRACUNIT, // speed 16 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 10001, // damage SFX_NONE, // activesound MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_NOBLOCKMAP, // flags MF2_NOTELEPORT // flags2 }, { // MT_TELOTHER_FX2 -1, // doomednum S_TELO_FX2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_TELO_FX9, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 16 * FRACUNIT, // speed 16 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 10001, // damage SFX_NONE, // activesound MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_NOBLOCKMAP, // flags MF2_NOTELEPORT // flags2 }, { // MT_TELOTHER_FX3 -1, // doomednum S_TELO_FX3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_TELO_FX9, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 16 * FRACUNIT, // speed 16 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 10001, // damage SFX_NONE, // activesound MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_NOBLOCKMAP, // flags MF2_NOTELEPORT // flags2 }, { // MT_TELOTHER_FX4 -1, // doomednum S_TELO_FX4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_TELO_FX9, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 16 * FRACUNIT, // speed 16 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 10001, // damage SFX_NONE, // activesound MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_NOBLOCKMAP, // flags MF2_NOTELEPORT // flags2 }, { // MT_TELOTHER_FX5 -1, // doomednum S_TELO_FX5_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_TELO_FX9, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 16 * FRACUNIT, // speed 16 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 10001, // damage SFX_NONE, // activesound MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_NOBLOCKMAP, // flags MF2_NOTELEPORT // flags2 }, { // MT_DIRT1 -1, // doomednum S_DIRT1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DIRT1_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_DIRT2 -1, // doomednum S_DIRT2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DIRT2_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_DIRT3 -1, // doomednum S_DIRT3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DIRT3_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_DIRT4 -1, // doomednum S_DIRT4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DIRT4_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_LOGRAV // flags2 }, { // MT_DIRT5 -1, // doomednum S_DIRT5_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DIRT5_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_LOGRAV // flags2 }, { // MT_DIRT6 -1, // doomednum S_DIRT6_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DIRT6_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_LOGRAV // flags2 }, { // MT_DIRTCLUMP -1, // doomednum S_DIRTCLUMP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT // flags2 }, { // MT_ROCK1 -1, // doomednum S_ROCK1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ROCK1_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_ROCK2 -1, // doomednum S_ROCK2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ROCK2_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_ROCK3 -1, // doomednum S_ROCK3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ROCK3_D, // 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_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_FOGSPAWNER 10000, // doomednum S_SPAWNFOG1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_DONTDRAW | MF2_FLOATBOB // flags2 }, { // MT_FOGPATCHS 10001, // doomednum S_FOGPATCHS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FOGPATCHS0, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_FLOAT | MF_NOGRAVITY | MF_SHADOW | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_FOGPATCHM 10002, // doomednum S_FOGPATCHM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FOGPATCHM0, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_FLOAT | MF_NOGRAVITY | MF_SHADOW | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_FOGPATCHL 10003, // doomednum S_FOGPATCHL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FOGPATCHL0, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_FLOAT | MF_NOGRAVITY | MF_SHADOW | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_QUAKE_FOCUS -1, // doomednum S_QUAKE_ACTIVE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_DONTDRAW // flags2 }, { // MT_SGSHARD1 -1, // doomednum S_SGSHARD1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD1_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD2 -1, // doomednum S_SGSHARD2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD2_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD3 -1, // doomednum S_SGSHARD3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD3_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD4 -1, // doomednum S_SGSHARD4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD4_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD5 -1, // doomednum S_SGSHARD5_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD5_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD6 -1, // doomednum S_SGSHARD6_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD6_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD7 -1, // doomednum S_SGSHARD7_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD7_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD8 -1, // doomednum S_SGSHARD8_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD8_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD9 -1, // doomednum S_SGSHARD9_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD9_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SGSHARD0 -1, // doomednum S_SGSHARD0_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SGSHARD0_D, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_ARTIEGG 30, // doomednum S_ARTI_EGGC1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_EGGFX -1, // doomednum S_EGGFX1, // 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_NULL, // crashstate S_EGGFXI1_1, // deathstate S_NULL, // xdeathstate 0, // deathsound 18 * FRACUNIT, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 1, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_ARTISUPERHEAL 32, // doomednum S_ARTI_SPHL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ZWINGEDSTATUENOSKULL 9011, // doomednum S_ZWINGEDSTATUENOSKULL, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZGEMPEDESTAL 9012, // doomednum S_ZGEMPEDESTAL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 40 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ARTIPUZZSKULL 9002, // doomednum S_ARTIPUZZSKULL, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEMBIG 9003, // doomednum S_ARTIPUZZGEMBIG, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEMRED 9004, // doomednum S_ARTIPUZZGEMRED, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEMGREEN1 9005, // doomednum S_ARTIPUZZGEMGREEN1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEMGREEN2 9009, // doomednum S_ARTIPUZZGEMGREEN2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEMBLUE1 9006, // doomednum S_ARTIPUZZGEMBLUE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEMBLUE2 9010, // doomednum S_ARTIPUZZGEMBLUE2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZBOOK1 9007, // doomednum S_ARTIPUZZBOOK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZBOOK2 9008, // doomednum S_ARTIPUZZBOOK2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZSKULL2 9014, // doomednum S_ARTIPUZZSKULL2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZFWEAPON 9015, // doomednum S_ARTIPUZZFWEAPON, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZCWEAPON 9016, // doomednum S_ARTIPUZZCWEAPON, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZMWEAPON 9017, // doomednum S_ARTIPUZZMWEAPON, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEAR 9018, // doomednum S_ARTIPUZZGEAR_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEAR2 9019, // doomednum S_ARTIPUZZGEAR2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEAR3 9020, // doomednum S_ARTIPUZZGEAR3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTIPUZZGEAR4 9021, // doomednum S_ARTIPUZZGEAR4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARTITORCH 33, // doomednum S_ARTI_TRCH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_FIREBOMB -1, // doomednum S_FIREBOMB1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_FLECHETTE_EXPLODE, // deathsound 0, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOGRAVITY | MF_ALTSHADOW, // flags MF2_FIREDAMAGE // flags2 }, { // MT_ARTITELEPORT 36, // doomednum S_ARTI_ATLP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_ARTIPOISONBAG 8000, // doomednum S_ARTI_PSBG1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_POISONBAG -1, // doomednum S_POISONBAG1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOGRAVITY | MF_NOBLOCKMAP, // flags 0 // flags2 }, { // MT_POISONCLOUD -1, // doomednum S_POISONCLOUD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_POISONSHROOM_DEATH, // deathsound 0, // speed 1, // radius 1, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_NOGRAVITY | MF_NOBLOCKMAP | MF_SHADOW | MF_NOCLIP | MF_DROPOFF, // flags MF2_NODMGTHRUST // flags2 }, { // MT_THROWINGBOMB -1, // doomednum S_THROWINGBOMB1, // spawnstate 48, // spawnhealth S_NULL, // seestate SFX_FLECHETTE_BOUNCE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_THROWINGBOMB_X1, // deathstate S_NULL, // xdeathstate SFX_FLECHETTE_EXPLODE, // deathsound 12 * FRACUNIT, // speed 8 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_FLOORBOUNCE | MF2_FIREDAMAGE // flags2 }, { // MT_SPEEDBOOTS 8002, // doomednum S_ARTI_BOOTS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_BOOSTMANA 8003, // doomednum S_ARTI_MANA, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_BOOSTARMOR 8041, // doomednum S_ARTI_ARMOR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_BLASTRADIUS 10110, // doomednum S_ARTI_BLAST1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_HEALRADIUS 10120, // doomednum S_ARTI_HEALRAD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_SPLASH -1, // doomednum S_SPLASH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SPLASHX, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2 }, { // MT_SPLASHBASE -1, // doomednum S_SPLASHBASE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_LAVASPLASH -1, // doomednum S_LAVASPLASH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_LAVASMOKE -1, // doomednum S_LAVASMOKE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_SLUDGECHUNK -1, // doomednum S_SLUDGECHUNK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SLUDGECHUNKX, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2 }, { // MT_SLUDGESPLASH -1, // doomednum S_SLUDGESPLASH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC0 5, // doomednum S_ZWINGEDSTATUE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC1 6, // doomednum S_ZROCK1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC2 7, // doomednum S_ZROCK2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC3 9, // doomednum S_ZROCK3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC4 15, // doomednum S_ZROCK4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SOLID, // flags 0 // flags2 }, { // MT_MISC5 17, // doomednum S_ZCHANDELIER1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 60 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC6 8063, // doomednum S_ZCHANDELIER_U, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 60 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC7 24, // doomednum S_ZTREEDEAD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 96 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC8 25, // doomednum S_ZTREE, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 128 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_TREEDESTRUCTIBLE 8062, // doomednum S_ZTREEDESTRUCTIBLE1, // spawnstate 70, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZTREEDES_D1, // deathstate S_NULL, // xdeathstate SFX_TREE_BREAK, // deathsound 0, // speed 15 * FRACUNIT, // radius 180 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags 0 // flags2 }, { // MT_MISC9 26, // doomednum S_ZTREESWAMP182_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 150 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC10 27, // doomednum S_ZTREESWAMP172_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 120 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC11 28, // doomednum S_ZSTUMPBURNED1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 12 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC12 29, // doomednum S_ZSTUMPBARE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 12 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC13 37, // doomednum S_ZSTUMPSWAMP1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC14 38, // doomednum S_ZSTUMPSWAMP2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC15 39, // doomednum S_ZSHROOMLARGE1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC16 40, // doomednum S_ZSHROOMLARGE2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC17 41, // doomednum S_ZSHROOMLARGE3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC18 42, // doomednum S_ZSHROOMSMALL1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC19 44, // doomednum S_ZSHROOMSMALL2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC20 45, // doomednum S_ZSHROOMSMALL3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC21 46, // doomednum S_ZSHROOMSMALL4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC22 47, // doomednum S_ZSHROOMSMALL5_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC23 48, // doomednum S_ZSTALAGMITEPILLAR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 138 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC24 49, // doomednum S_ZSTALAGMITELARGE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 48 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC25 50, // doomednum S_ZSTALAGMITEMEDIUM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 6 * FRACUNIT, // radius 40 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC26 51, // doomednum S_ZSTALAGMITESMALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 36 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC27 52, // doomednum S_ZSTALACTITELARGE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 66 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC28 56, // doomednum S_ZSTALACTITEMEDIUM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 6 * FRACUNIT, // radius 50 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC29 57, // doomednum S_ZSTALACTITESMALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 40 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC30 58, // doomednum S_ZMOSSCEILING1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC31 59, // doomednum S_ZMOSSCEILING2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 24 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC32 60, // doomednum S_ZSWAMPVINE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 52 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC33 61, // doomednum S_ZCORPSEKABOB1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 92 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC34 62, // doomednum S_ZCORPSESLEEPING1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC35 63, // doomednum S_ZTOMBSTONERIP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 46 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC36 64, // doomednum S_ZTOMBSTONESHANE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 46 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC37 65, // doomednum S_ZTOMBSTONEBIGCROSS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 46 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC38 66, // doomednum S_ZTOMBSTONEBRIANR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 52 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC39 67, // doomednum S_ZTOMBSTONECROSSCIRCLE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 52 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC40 68, // doomednum S_ZTOMBSTONESMALLCROSS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 46 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC41 69, // doomednum S_ZTOMBSTONEBRIANP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 46 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC42 71, // doomednum S_CORPSEHANGING_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 6 * FRACUNIT, // radius 75 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC43 72, // doomednum S_ZSTATUEGARGOYLEGREENTALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 108 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC44 73, // doomednum S_ZSTATUEGARGOYLEBLUETALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 108 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC45 74, // doomednum S_ZSTATUEGARGOYLEGREENSHORT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC46 76, // doomednum S_ZSTATUEGARGOYLEBLUESHORT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC47 8044, // doomednum S_ZSTATUEGARGOYLESTRIPETALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 108 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC48 8045, // doomednum S_ZSTATUEGARGOYLEDARKREDTALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 108 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC49 8046, // doomednum S_ZSTATUEGARGOYLEREDTALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 108 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC50 8047, // doomednum S_ZSTATUEGARGOYLETANTALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 108 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC51 8048, // doomednum S_ZSTATUEGARGOYLERUSTTALL_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 108 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC52 8049, // doomednum S_ZSTATUEGARGOYLEDARKREDSHORT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC53 8050, // doomednum S_ZSTATUEGARGOYLEREDSHORT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC54 8051, // doomednum S_ZSTATUEGARGOYLETANSHORT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC55 8052, // doomednum S_ZSTATUEGARGOYLERUSTSHORT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 14 * FRACUNIT, // radius 62 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC56 77, // doomednum S_ZBANNERTATTERED_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 120 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC57 78, // doomednum S_ZTREELARGE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZTREELARGE1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 180 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC58 79, // doomednum S_ZTREELARGE2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZTREELARGE2, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 180 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC59 80, // doomednum S_ZTREEGNARLED1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 22 * FRACUNIT, // radius 100 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC60 87, // doomednum S_ZTREEGNARLED2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 22 * FRACUNIT, // radius 100 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC61 88, // doomednum S_ZLOG, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 25 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC62 89, // doomednum S_ZSTALACTITEICELARGE, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 66 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC63 90, // doomednum S_ZSTALACTITEICEMEDIUM, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 50 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC64 91, // doomednum S_ZSTALACTITEICESMALL, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC65 92, // doomednum S_ZSTALACTITEICETINY, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC66 93, // doomednum S_ZSTALAGMITEICELARGE, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 66 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC67 94, // doomednum S_ZSTALAGMITEICEMEDIUM, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 50 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC68 95, // doomednum S_ZSTALAGMITEICESMALL, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC69 96, // doomednum S_ZSTALAGMITEICETINY, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC70 97, // doomednum S_ZROCKBROWN1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 17 * FRACUNIT, // radius 72 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC71 98, // doomednum S_ZROCKBROWN2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 50 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC72 99, // doomednum S_ZROCKBLACK, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 40 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_MISC73 100, // doomednum S_ZRUBBLE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC74 101, // doomednum S_ZRUBBLE2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC75 102, // doomednum S_ZRUBBLE3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC76 103, // doomednum S_ZVASEPILLAR, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 12 * FRACUNIT, // radius 54 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_POTTERY1 104, // doomednum S_ZPOTTERY1, // spawnstate 15, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZPOTTERY_EXPLODE, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD | MF_DROPOFF, // flags MF2_SLIDE | MF2_PUSHABLE | MF2_TELESTOMP | MF2_PASSMOBJ // flags2 }, { // MT_POTTERY2 105, // doomednum S_ZPOTTERY2, // spawnstate 15, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZPOTTERY_EXPLODE, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 25 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD | MF_DROPOFF, // flags MF2_SLIDE | MF2_PUSHABLE | MF2_TELESTOMP | MF2_PASSMOBJ // flags2 }, { // MT_POTTERY3 106, // doomednum S_ZPOTTERY3, // spawnstate 15, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZPOTTERY_EXPLODE, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 25 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD | MF_DROPOFF, // flags MF2_SLIDE | MF2_PUSHABLE | MF2_TELESTOMP | MF2_PASSMOBJ // flags2 }, { // MT_POTTERYBIT1 -1, // doomednum S_POTTERYBIT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_POTTERYBIT_EX0, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_MISC77 108, // doomednum S_ZCORPSELYNCHED1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 11 * FRACUNIT, // radius 95 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_ZLYNCHED_NOHEART 109, // doomednum S_ZCORPSELYNCHED2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 100 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_MISC78 110, // doomednum S_ZCORPSESITTING, // spawnstate 30, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZCORPSESITTING_X, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 35 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags 0 // flags2 }, { // MT_CORPSEBIT -1, // doomednum S_CORPSEBIT_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP, // flags MF2_TELESTOMP // flags2 }, { // MT_CORPSEBLOODDRIP -1, // doomednum S_CORPSEBLOODDRIP, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CORPSEBLOODDRIP_X1, // deathstate S_NULL, // xdeathstate SFX_DRIP, // deathsound 0, // speed FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_MISSILE, // flags MF2_LOGRAV // flags2 }, { // MT_BLOODPOOL 111, // doomednum S_BLOODPOOL, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC79 119, // doomednum S_ZCANDLE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC80 113, // doomednum S_ZLEAFSPAWNER, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_DONTDRAW // flags2 }, { // MT_LEAF1 -1, // doomednum S_LEAF1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_LEAF_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_LOGRAV // flags2 }, { // MT_LEAF2 -1, // doomednum S_LEAF2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_LEAF_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_LOGRAV // flags2 }, { // MT_ZTWINEDTORCH 116, // doomednum S_ZTWINEDTORCH_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZTWINEDTORCH_UNLIT 117, // doomednum S_ZTWINEDTORCH_UNLIT, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 10 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_BRIDGE 118, // doomednum S_BRIDGE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 32 * FRACUNIT, // radius 2 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_NOGRAVITY, // flags MF2_DONTDRAW // flags2 }, { // MT_BRIDGEBALL -1, // doomednum S_BBALL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT // flags2 }, { // MT_ZWALLTORCH 54, // doomednum S_ZWALLTORCH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_ZWALLTORCH_UNLIT 55, // doomednum S_ZWALLTORCH_U, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_ZBARREL 8100, // doomednum S_ZBARREL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZSHRUB1 8101, // doomednum S_ZSHRUB1, // spawnstate 20, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_ZSHRUB1_X1, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZSHRUB1_DIE, // deathstate S_NULL, // xdeathstate SFX_TREE_EXPLODE, // deathsound 0, // speed 8 * FRACUNIT, // radius 24 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags 0 // flags2 }, { // MT_ZSHRUB2 8102, // doomednum S_ZSHRUB2, // spawnstate 10, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_ZSHRUB2_X1, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZSHRUB2_DIE, // deathstate S_NULL, // xdeathstate SFX_TREE_EXPLODE, // deathsound 0, // speed 16 * FRACUNIT, // radius 40 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags 0 // flags2 }, { // MT_ZBUCKET 8103, // doomednum S_ZBUCKET1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 72 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_ZPOISONSHROOM 8104, // doomednum S_ZPOISONSHROOM1, // spawnstate 30, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_ZPOISONSHROOM_P1, // painstate 255, // painchance SFX_POISONSHROOM_PAIN, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZPOISONSHROOM_X1, // deathstate S_NULL, // xdeathstate SFX_POISONSHROOM_DEATH, // deathsound 0, // speed 6 * FRACUNIT, // radius 20 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SHOOTABLE | MF_SOLID | MF_NOBLOOD, // flags 0 // flags2 }, { // MT_ZFIREBULL 8042, // doomednum S_ZFIREBULL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 80 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZFIREBULL_UNLIT 8043, // doomednum S_ZFIREBULL_U, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 80 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_FIRETHING 8060, // doomednum S_ZFIRETHING1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_BRASSTORCH 8061, // doomednum S_ZBRASSTORCH1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 6 * FRACUNIT, // radius 35 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZSUITOFARMOR 8064, // doomednum S_ZSUITOFARMOR, // spawnstate 60, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZSUITOFARMOR_X1, // deathstate S_NULL, // xdeathstate SFX_SUITOFARMOR_BREAK, // deathsound 0, // speed 16 * FRACUNIT, // radius 72 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags 0 // flags2 }, { // MT_ZARMORCHUNK -1, // doomednum S_ZARMORCHUNK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound 0, // flags 0 // flags2 }, { // MT_ZBELL 8065, // doomednum S_ZBELL, // spawnstate 5, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZBELL_X1, // deathstate S_NULL, // xdeathstate SFX_BELLRING, // deathsound 0, // speed 56 * FRACUNIT, // radius 120 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_ZBLUE_CANDLE 8066, // doomednum S_ZBLUE_CANDLE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_ZIRON_MAIDEN 8067, // doomednum S_ZIRON_MAIDEN, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 12 * FRACUNIT, // radius 60 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZXMAS_TREE 8068, // doomednum S_ZXMAS_TREE, // spawnstate 20, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_ZXMAS_TREE_X1, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ZXMAS_TREE_DIE, // deathstate S_NULL, // xdeathstate SFX_TREE_EXPLODE, // deathsound 0, // speed 11 * FRACUNIT, // radius 130 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, // flags 0 // flags2 }, { // MT_ZCAULDRON 8069, // doomednum S_ZCAULDRON1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 12 * FRACUNIT, // radius 26 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZCAULDRON_UNLIT 8070, // doomednum S_ZCAULDRON_U, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 12 * FRACUNIT, // radius 26 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID, // flags 0 // flags2 }, { // MT_ZCHAINBIT32 8071, // doomednum S_ZCHAINBIT32, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_ZCHAINBIT64 8072, // doomednum S_ZCHAINBIT64, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_ZCHAINEND_HEART 8073, // doomednum S_ZCHAINEND_HEART, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_ZCHAINEND_HOOK1 8074, // doomednum S_ZCHAINEND_HOOK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_ZCHAINEND_HOOK2 8075, // doomednum S_ZCHAINEND_HOOK2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_ZCHAINEND_SPIKE 8076, // doomednum S_ZCHAINEND_SPIKE, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_ZCHAINEND_SKULL 8077, // doomednum S_ZCHAINEND_SKULL, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 32 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SPAWNCEILING, // flags 0 // flags2 }, { // MT_TABLE_SHIT1 8500, // doomednum S_TABLE_SHIT1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT2 8501, // doomednum S_TABLE_SHIT2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT3 8502, // doomednum S_TABLE_SHIT3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT4 8503, // doomednum S_TABLE_SHIT4, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT5 8504, // doomednum S_TABLE_SHIT5, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT6 8505, // doomednum S_TABLE_SHIT6, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT7 8506, // doomednum S_TABLE_SHIT7, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT8 8507, // doomednum S_TABLE_SHIT8, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT9 8508, // doomednum S_TABLE_SHIT9, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TABLE_SHIT10 8509, // doomednum S_TABLE_SHIT10, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_TFOG -1, // doomednum S_TFOG1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_MISC81 140, // doomednum S_TELESMOKE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // 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, // crashstate 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 0 // flags2 }, { // MT_PUNCHPUFF -1, // doomednum S_PUNCHPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_FIGHTER_PUNCH_HITTHING, // seesound 8, // reactiontime SFX_FIGHTER_PUNCH_HITWALL, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_FW_AXE 8010, // doomednum S_AXE, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AXEPUFF -1, // doomednum S_HAMMERPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_FIGHTER_AXE_HITTHING, // seesound 8, // reactiontime SFX_FIGHTER_HAMMER_HITWALL, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_AXEPUFF_GLOW -1, // doomednum S_AXEPUFF_GLOW1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_FIGHTER_AXE_HITTHING, // seesound 8, // reactiontime SFX_FIGHTER_HAMMER_HITWALL, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_AXEBLOOD -1, // doomednum S_AXEBLOOD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_AXEBLOOD6, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2 }, { // MT_FW_HAMMER 123, // doomednum S_HAMM, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_HAMMER_MISSILE -1, // doomednum S_HAMMER_MISSILE_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_HAMMER_MISSILE_X1, // deathstate S_NULL, // xdeathstate SFX_FIGHTER_HAMMER_EXPLODE, // deathsound 25 * FRACUNIT, // speed 14 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 10, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS | MF2_FIREDAMAGE // flags2 }, { // MT_HAMMERPUFF -1, // doomednum S_HAMMERPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_FIGHTER_HAMMER_HITTHING, // seesound 8, // reactiontime SFX_FIGHTER_HAMMER_HITWALL, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_FSWORD_MISSILE -1, // doomednum S_FSWORD_MISSILE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FSWORD_MISSILE_X1, // deathstate S_NULL, // xdeathstate SFX_FIGHTER_SWORD_EXPLODE, // deathsound 30 * FRACUNIT, // speed 16 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 8, // damage SFX_NONE, // activesound MF_MISSILE | MF_NOBLOCKMAP | MF_NOGRAVITY | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS // flags2 }, { // MT_FSWORD_FLAME -1, // doomednum S_FSWORD_FLAME1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_CW_SERPSTAFF 10, // doomednum S_CSTAFF, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_CSTAFF_MISSILE -1, // doomednum S_CSTAFF_MISSILE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CSTAFF_MISSILE_X1, // deathstate S_NULL, // xdeathstate SFX_CLERIC_CSTAFF_EXPLODE, // deathsound 22 * FRACUNIT, // speed 12 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 5, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS // flags2 }, { // MT_CSTAFFPUFF -1, // doomednum S_CSTAFFPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_CLERIC_CSTAFF_HITTHING, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_CW_FLAME 8009, // doomednum S_CFLAME1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_CFLAMEFLOOR -1, // doomednum S_CFLAMEFLOOR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_FLAMEPUFF -1, // doomednum S_FLAMEPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_CLERIC_FLAME_EXPLODE, // seesound 8, // reactiontime SFX_CLERIC_FLAME_EXPLODE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed FRACUNIT, // radius FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_FLAMEPUFF2 -1, // doomednum S_FLAMEPUFF2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_CLERIC_FLAME_EXPLODE, // seesound 8, // reactiontime SFX_CLERIC_FLAME_EXPLODE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed FRACUNIT, // radius FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_CIRCLEFLAME -1, // doomednum S_CIRCLE_FLAME1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CIRCLE_FLAME_X1, // deathstate S_NULL, // xdeathstate SFX_CLERIC_FLAME_CIRCLE, // deathsound 0, // speed 6 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 2, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_CFLAME_MISSILE -1, // doomednum S_CFLAME_MISSILE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CFLAME_MISSILE_X, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 200 * FRACUNIT, // speed 14 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 8, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS | MF2_DONTDRAW | MF2_FIREDAMAGE // flags2 }, { // MT_HOLY_FX -1, // doomednum S_HOLY_FX1, // spawnstate 105, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_HOLY_FX_X1, // deathstate S_NULL, // xdeathstate SFX_SPIRIT_DIE, // deathsound 12 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 3, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_SEEKERMISSILE | MF2_RIP | MF2_IMPACT | MF2_PCROSS // flags2 }, { // MT_HOLY_TAIL -1, // doomednum S_HOLY_TAIL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed FRACUNIT, // radius FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_NOCLIP | MF_ALTSHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_HOLY_PUFF -1, // doomednum S_HOLY_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, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_HOLY_MISSILE -1, // doomednum S_HOLY_MISSILE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_HOLY_MISSILE_X, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 30 * FRACUNIT, // speed 15 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_HOLY_MISSILE_PUFF -1, // doomednum S_HOLY_MISSILE_P1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 4 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_MWANDPUFF -1, // doomednum S_MWANDPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_NOTELEPORT | MF2_CANNOTPUSH | MF2_NODMGTHRUST // flags2 }, { // MT_MWANDSMOKE -1, // doomednum S_MWANDSMOKE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags MF2_NOTELEPORT | MF2_CANNOTPUSH | MF2_NODMGTHRUST // flags2 }, { // MT_MWAND_MISSILE -1, // doomednum S_MWAND_MISSILE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_MWANDPUFF1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 184 * FRACUNIT, // speed 12 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 2, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_RIP | MF2_IMPACT | MF2_PCROSS | MF2_NODMGTHRUST | MF2_CANNOTPUSH // flags2 }, { // MT_MW_LIGHTNING 8040, // doomednum S_MW_LIGHTNING1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_LIGHTNING_CEILING -1, // doomednum S_LIGHTNING_CEILING1, // spawnstate 144, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_LIGHTNING_C_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 25 * FRACUNIT, // speed 16 * FRACUNIT, // radius 40 * FRACUNIT, // height 100, // mass 8, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS // flags2 }, { // MT_LIGHTNING_FLOOR -1, // doomednum S_LIGHTNING_FLOOR1, // spawnstate 144, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_LIGHTNING_F_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 25 * FRACUNIT, // speed 16 * FRACUNIT, // radius 40 * FRACUNIT, // height 100, // mass 8, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS // flags2 }, { // MT_LIGHTNING_ZAP -1, // doomednum S_LIGHTNING_ZAP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_LIGHTNING_ZAP_X8, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 35 * FRACUNIT, // height 100, // mass 2, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE | MF_DROPOFF, // flags 0 // flags2 }, { // MT_MSTAFF_FX -1, // doomednum S_MSTAFF_FX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_MSTAFF_FX_X1, // deathstate S_NULL, // xdeathstate SFX_MAGE_STAFF_EXPLODE, // deathsound 20 * FRACUNIT, // speed 16 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 6, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE | MF2_RIP | MF2_IMPACT | MF2_PCROSS // flags2 }, { // MT_MSTAFF_FX2 -1, // doomednum S_MSTAFF_FX2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_MSTAFF_FX2_X1, // deathstate S_NULL, // xdeathstate SFX_MAGE_STAFF_EXPLODE, // deathsound 17 * FRACUNIT, // speed 20 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE | MF2_IMPACT | MF2_PCROSS | MF2_SEEKERMISSILE // flags2 }, { // MT_FW_SWORD1 12, // doomednum S_FSWORD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_FW_SWORD2 13, // doomednum S_FSWORD2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_FW_SWORD3 16, // doomednum S_FSWORD3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_CW_HOLY1 18, // doomednum S_CHOLY1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_CW_HOLY2 19, // doomednum S_CHOLY2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_CW_HOLY3 20, // doomednum S_CHOLY3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_MW_STAFF1 21, // doomednum S_MSTAFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_MW_STAFF2 22, // doomednum S_MSTAFF2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_MW_STAFF3 23, // doomednum S_MSTAFF3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_FLOATBOB // flags2 }, { // MT_SNOUTPUFF -1, // doomednum S_PUNCHPUFF1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_MW_CONE 53, // doomednum S_COS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_SHARDFX1 -1, // doomednum S_SHARDFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SHARDFXE1_1, // deathstate S_NULL, // xdeathstate SFX_MAGE_SHARDS_EXPLODE, // deathsound 25 * FRACUNIT, // speed 13 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 1, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS | MF2_ICEDAMAGE // flags2 }, { // 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, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP, // flags 0 // flags2 }, { // MT_BLOODSPLATTER -1, // doomednum S_BLOODSPLATTER1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_BLOODSPLATTERX, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 2 * FRACUNIT, // radius 4 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2 }, { // MT_GIBS -1, // doomednum S_GIBS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT // flags2 }, { // MT_PLAYER_FIGHTER -1, // doomednum S_FPLAY, // spawnstate 100, // spawnhealth S_FPLAY_RUN1, // seestate SFX_NONE, // seesound 0, // reactiontime SFX_NONE, // attacksound S_FPLAY_PAIN, // painstate 255, // painchance SFX_PLAYER_FIGHTER_PAIN, // painsound S_NULL, // meleestate S_FPLAY_ATK1, // missilestate S_NULL, // crashstate S_FPLAY_DIE1, // deathstate S_FPLAY_XDIE1, // xdeathstate SFX_NONE, // deathsound 0, // speed 16 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_PICKUP | MF_NOTDMATCH, // flags MF2_WINDTHRUST | MF2_FLOORCLIP | MF2_SLIDE | MF2_PASSMOBJ | MF2_TELESTOMP | MF2_PUSHWALL // flags2 }, { // MT_BLOODYSKULL -1, // doomednum S_BLOODYSKULL1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 MF2_LOGRAV | MF2_CANNOTPUSH // flags2 }, { // MT_PLAYER_SPEED -1, // doomednum S_PLAYER_SPEED1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_ALTSHADOW, // flags 0 // flags2 }, { // MT_ICECHUNK -1, // doomednum S_ICECHUNK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF, // flags MF2_LOGRAV | MF2_CANNOTPUSH | MF2_FLOORCLIP // flags2 }, { // MT_PLAYER_CLERIC -1, // doomednum S_CPLAY, // spawnstate 100, // spawnhealth S_CPLAY_RUN1, // seestate SFX_NONE, // seesound 0, // reactiontime SFX_NONE, // attacksound S_CPLAY_PAIN, // painstate 255, // painchance SFX_PLAYER_CLERIC_PAIN, // painsound S_NULL, // meleestate S_CPLAY_ATK1, // missilestate S_NULL, // crashstate S_CPLAY_DIE1, // deathstate S_CPLAY_XDIE1, // xdeathstate SFX_NONE, // deathsound 0, // speed 16 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_PICKUP | MF_NOTDMATCH, // flags MF2_WINDTHRUST | MF2_FLOORCLIP | MF2_SLIDE | MF2_PASSMOBJ | MF2_TELESTOMP | MF2_PUSHWALL // flags2 }, { // MT_PLAYER_MAGE -1, // doomednum S_MPLAY, // spawnstate 100, // spawnhealth S_MPLAY_RUN1, // seestate SFX_NONE, // seesound 0, // reactiontime SFX_NONE, // attacksound S_MPLAY_PAIN, // painstate 255, // painchance SFX_PLAYER_MAGE_PAIN, // painsound S_NULL, // meleestate S_MPLAY_ATK1, // missilestate S_NULL, // crashstate S_MPLAY_DIE1, // deathstate S_MPLAY_XDIE1, // xdeathstate SFX_NONE, // deathsound 0, // speed 16 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_PICKUP | MF_NOTDMATCH, // flags MF2_WINDTHRUST | MF2_FLOORCLIP | MF2_SLIDE | MF2_PASSMOBJ | MF2_TELESTOMP | MF2_PUSHWALL // flags2 }, { // MT_PIGPLAYER -1, // doomednum S_PIGPLAY, // spawnstate 100, // spawnhealth S_PIGPLAY_RUN1, // seestate SFX_NONE, // seesound 0, // reactiontime SFX_NONE, // attacksound S_PIGPLAY_PAIN, // painstate 255, // painchance SFX_PIG_PAIN, // painsound S_NULL, // meleestate S_PIGPLAY_ATK1, // missilestate S_NULL, // crashstate S_PIG_DIE1, // deathstate S_NULL, // xdeathstate SFX_PIG_DEATH, // deathsound 0, // speed 16 * FRACUNIT, // radius 24 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_NOTDMATCH, // flags MF2_WINDTHRUST | MF2_SLIDE | MF2_PASSMOBJ | MF2_FLOORCLIP | MF2_TELESTOMP | MF2_PUSHWALL // flags2 }, { // MT_PIG -1, // doomednum S_PIG_LOOK1, // spawnstate 25, // spawnhealth S_PIG_WALK1, // seestate SFX_PIG_ACTIVE1, // seesound 8, // reactiontime SFX_NONE, // attacksound S_PIG_PAIN, // painstate 128, // painchance SFX_PIG_PAIN, // painsound S_PIG_ATK1, // meleestate 0, // missilestate S_NULL, // crashstate S_PIG_DIE1, // deathstate S_NULL, // xdeathstate SFX_PIG_DEATH, // deathsound 10, // speed 12 * FRACUNIT, // radius 22 * FRACUNIT, // height 60, // mass 0, // damage SFX_PIG_ACTIVE1, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_WINDTHRUST | MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_TELESTOMP // flags2 }, { // MT_CENTAUR 107, // doomednum S_CENTAUR_LOOK1, // spawnstate 200, // spawnhealth S_CENTAUR_WALK1, // seestate SFX_CENTAUR_SIGHT, // seesound 8, // reactiontime SFX_CENTAUR_ATTACK, // attacksound S_CENTAUR_PAIN1, // painstate 135, // painchance SFX_CENTAUR_PAIN, // painsound S_CENTAUR_ATK1, // meleestate 0, // missilestate S_NULL, // crashstate S_CENTAUR_DEATH1, // deathstate S_CENTAUR_DEATH_X1, // xdeathstate SFX_CENTAUR_DEATH, // deathsound 13, // speed 20 * FRACUNIT, // radius 64 * FRACUNIT, // height 120, // mass 0, // damage SFX_CENTAUR_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_MCROSS | MF2_TELESTOMP // flags2 }, { // MT_CENTAURLEADER 115, // doomednum S_CENTAUR_LOOK1, // spawnstate 250, // spawnhealth S_CENTAUR_WALK1, // seestate SFX_CENTAUR_SIGHT, // seesound 8, // reactiontime SFX_CENTAUR_ATTACK, // attacksound S_CENTAUR_PAIN1, // painstate 96, // painchance SFX_CENTAUR_PAIN, // painsound S_CENTAUR_ATK1, // meleestate S_CENTAUR_MISSILE1, // missilestate S_NULL, // crashstate S_CENTAUR_DEATH1, // deathstate S_CENTAUR_DEATH_X1, // xdeathstate SFX_CENTAUR_DEATH, // deathsound 10, // speed 20 * FRACUNIT, // radius 64 * FRACUNIT, // height 120, // mass 0, // damage SFX_CENTAUR_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_MCROSS | MF2_TELESTOMP // flags2 }, { // MT_CENTAUR_FX -1, // doomednum S_CENTAUR_FX1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CENTAUR_FX_X1, // deathstate S_NULL, // xdeathstate SFX_CENTAUR_MISSILE_EXPLODE, // deathsound 20 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_MISSILE | MF_NOBLOCKMAP | MF_NOGRAVITY | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS // flags2 }, { // MT_CENTAUR_SHIELD -1, // doomednum S_CENTAUR_SHIELD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CENTAUR_SHIELD_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT // flags2 }, { // MT_CENTAUR_SWORD -1, // doomednum S_CENTAUR_SWORD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_CENTAUR_SWORD_X1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT // flags2 }, { // MT_DEMON 31, // doomednum S_DEMN_LOOK1, // spawnstate 250, // spawnhealth S_DEMN_CHASE1, // seestate SFX_DEMON_SIGHT, // seesound 8, // reactiontime SFX_DEMON_ATTACK, // attacksound S_DEMN_PAIN1, // painstate 50, // painchance SFX_DEMON_PAIN, // painsound S_DEMN_ATK1_1, // meleestate S_DEMN_ATK2_1, // missilestate S_NULL, // crashstate S_DEMN_DEATH1, // deathstate S_DEMN_XDEATH1, // xdeathstate SFX_DEMON_DEATH, // deathsound 13, // speed 32 * FRACUNIT, // radius 64 * FRACUNIT, // height 220, // mass 0, // damage SFX_DEMON_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_MCROSS | MF2_TELESTOMP // flags2 }, { // MT_DEMONCHUNK1 -1, // doomednum S_DEMONCHUNK1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMONCHUNK1_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMONCHUNK2 -1, // doomednum S_DEMONCHUNK2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMONCHUNK2_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMONCHUNK3 -1, // doomednum S_DEMONCHUNK3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMONCHUNK3_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMONCHUNK4 -1, // doomednum S_DEMONCHUNK4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMONCHUNK4_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMONCHUNK5 -1, // doomednum S_DEMONCHUNK5_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMONCHUNK5_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMONFX1 -1, // doomednum S_DEMONFX_MOVE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMONFX_BOOM1, // deathstate S_NULL, // xdeathstate SFX_DEMON_MISSILE_EXPLODE, // deathsound 15 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 5, // damage SFX_NONE, // activesound MF_MISSILE | MF_NOBLOCKMAP | MF_NOGRAVITY | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS | MF2_FIREDAMAGE // flags2 }, { // MT_DEMON2 8080, // doomednum S_DEMN2_LOOK1, // spawnstate 250, // spawnhealth S_DEMN2_CHASE1, // seestate SFX_DEMON_SIGHT, // seesound 8, // reactiontime SFX_DEMON_ATTACK, // attacksound S_DEMN2_PAIN1, // painstate 50, // painchance SFX_DEMON_PAIN, // painsound S_DEMN2_ATK1_1, // meleestate S_DEMN2_ATK2_1, // missilestate S_NULL, // crashstate S_DEMN2_DEATH1, // deathstate S_DEMN2_XDEATH1, // xdeathstate SFX_DEMON_DEATH, // deathsound 13, // speed 32 * FRACUNIT, // radius 64 * FRACUNIT, // height 220, // mass 0, // damage SFX_DEMON_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_MCROSS | MF2_TELESTOMP // flags2 }, { // MT_DEMON2CHUNK1 -1, // doomednum S_DEMON2CHUNK1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMON2CHUNK1_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMON2CHUNK2 -1, // doomednum S_DEMON2CHUNK2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMON2CHUNK2_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMON2CHUNK3 -1, // doomednum S_DEMON2CHUNK3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMON2CHUNK3_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMON2CHUNK4 -1, // doomednum S_DEMON2CHUNK4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMON2CHUNK4_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMON2CHUNK5 -1, // doomednum S_DEMON2CHUNK5_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMON2CHUNK5_4, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_DEMON2FX1 -1, // doomednum S_DEMON2FX_MOVE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DEMON2FX_BOOM1, // deathstate S_NULL, // xdeathstate SFX_DEMON_MISSILE_EXPLODE, // deathsound 15 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 5, // damage SFX_NONE, // activesound MF_MISSILE | MF_NOBLOCKMAP | MF_NOGRAVITY | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS | MF2_FIREDAMAGE // flags2 }, { // MT_WRAITHB 10011, // doomednum S_WRAITH_LOOK1, // spawnstate 150, // spawnhealth S_WRAITH_RAISE1, // seestate SFX_WRAITH_SIGHT, // seesound 8, // reactiontime SFX_WRAITH_ATTACK, // attacksound S_WRAITH_PAIN1, // painstate 25, // painchance SFX_WRAITH_PAIN, // painsound S_WRAITH_ATK1_1, // meleestate S_WRAITH_ATK2_1, // missilestate S_NULL, // crashstate S_WRAITH_DEATH1_1, // deathstate S_WRAITH_DEATH2_1, // xdeathstate SFX_WRAITH_DEATH, // deathsound 11, // speed 20 * FRACUNIT, // radius 68 * FRACUNIT, // height 75, // mass 10, // damage SFX_WRAITH_ACTIVE, // activesound MF_DROPOFF | MF_NOGRAVITY | MF_FLOAT | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_TELESTOMP | MF2_DONTDRAW // flags2 }, { // MT_WRAITH 34, // doomednum S_WRAITH_INIT1, // spawnstate 150, // spawnhealth S_WRAITH_CHASE1, // seestate SFX_WRAITH_SIGHT, // seesound 8, // reactiontime SFX_WRAITH_ATTACK, // attacksound S_WRAITH_PAIN1, // painstate 25, // painchance SFX_WRAITH_PAIN, // painsound S_WRAITH_ATK1_1, // meleestate S_WRAITH_ATK2_1, // missilestate S_NULL, // crashstate S_WRAITH_DEATH1_1, // deathstate S_WRAITH_DEATH2_1, // xdeathstate SFX_WRAITH_DEATH, // deathsound 11, // speed 20 * FRACUNIT, // radius 55 * FRACUNIT, // height 75, // mass 10, // damage SFX_WRAITH_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF | MF_NOGRAVITY | MF_FLOAT, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_TELESTOMP // flags2 }, { // MT_WRAITHFX1 -1, // doomednum S_WRTHFX_MOVE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_WRTHFX_BOOM1, // deathstate S_NULL, // xdeathstate SFX_WRAITH_MISSILE_EXPLODE, // deathsound 14 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 5, // mass 5, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS | MF2_FLOORCLIP | MF2_FIREDAMAGE // flags2 }, { // MT_WRAITHFX2 -1, // doomednum S_WRTHFX_SIZZLE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 2 * FRACUNIT, // radius 5 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_WRAITHFX3 -1, // doomednum S_WRTHFX_DROP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_WRTHFX_DEAD1, // deathstate S_NULL, // xdeathstate SFX_DRIP, // deathsound 0, // speed 2 * FRACUNIT, // radius 5 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_WRAITHFX4 -1, // doomednum S_WRTHFX_ADROP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_WRTHFX_ADEAD1, // deathstate S_NULL, // xdeathstate SFX_DRIP, // deathsound 0, // speed 2 * FRACUNIT, // radius 5 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_WRAITHFX5 -1, // doomednum S_WRTHFX_BDROP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_WRTHFX_BDEAD1, // deathstate S_NULL, // xdeathstate SFX_DRIP, // deathsound 0, // speed 2 * FRACUNIT, // radius 5 * FRACUNIT, // height 5, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_MINOTAUR 9, // doomednum S_MNTR_SPAWN1, // spawnstate 2500, // spawnhealth S_MNTR_WALK1, // seestate SFX_MAULATOR_SIGHT, // seesound 8, // reactiontime SFX_MAULATOR_HAMMER_SWING, // attacksound S_MNTR_PAIN1, // painstate 25, // painchance SFX_MAULATOR_PAIN, // painsound S_MNTR_ATK1_1, // meleestate S_MNTR_ATK2_1, // missilestate S_NULL, // crashstate S_MNTR_DIE1, // deathstate S_NULL, // xdeathstate SFX_MAULATOR_DEATH, // deathsound 16, // speed 28 * FRACUNIT, // radius 100 * FRACUNIT, // height 800, // mass 7, // damage SFX_MAULATOR_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_SHADOW, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_TELESTOMP // flags2 }, { // MT_MNTRFX1 -1, // doomednum S_MNTRFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_MNTRFXI1_1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 20 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 3, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_MNTRFX2 -1, // doomednum S_MNTRFX2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_MNTRFXI2_1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 14 * FRACUNIT, // speed 5 * FRACUNIT, // radius 12 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_MNTRFX3 -1, // doomednum S_MNTRFX3_1, // 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_NULL, // crashstate S_MNTRFXI2_1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_MNTRSMOKE -1, // doomednum S_MINOSMOKE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_MNTRSMOKEEXIT -1, // doomednum S_MINOSMOKEX1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_SERPENT 121, // doomednum S_SERPENT_LOOK1, // spawnstate 90, // spawnhealth S_SERPENT_SWIM1, // seestate SFX_SERPENT_SIGHT, // seesound 8, // reactiontime SFX_SERPENT_ATTACK, // attacksound S_SERPENT_PAIN1, // painstate 96, // painchance SFX_SERPENT_PAIN, // painsound S_SERPENT_SURFACE1, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SERPENT_DIE1, // deathstate S_SERPENT_XDIE1, // xdeathstate SFX_SERPENT_DEATH, // deathsound 12, // speed 32 * FRACUNIT, // radius 70 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_COUNTKILL | MF_NOBLOOD, // flags MF2_PASSMOBJ | MF2_DONTDRAW | MF2_CANTLEAVEFLOORPIC | MF2_NONSHOOTABLE | MF2_MCROSS // flags2 }, { // MT_SERPENTLEADER 120, // doomednum S_SERPENT_LOOK1, // spawnstate 90, // spawnhealth S_SERPENT_SWIM1, // seestate SFX_SERPENT_SIGHT, // seesound 8, // reactiontime SFX_SERPENT_ATTACK, // attacksound S_SERPENT_PAIN1, // painstate 96, // painchance SFX_SERPENT_PAIN, // painsound S_SERPENT_SURFACE1, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SERPENT_DIE1, // deathstate S_SERPENT_XDIE1, // xdeathstate SFX_SERPENT_DEATH, // deathsound 12, // speed 32 * FRACUNIT, // radius 70 * FRACUNIT, // height 200, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_COUNTKILL | MF_NOBLOOD, // flags MF2_PASSMOBJ | MF2_DONTDRAW | MF2_CANTLEAVEFLOORPIC | MF2_NONSHOOTABLE | MF2_MCROSS // flags2 }, { // MT_SERPENTFX -1, // doomednum S_SERPENT_FX1, // 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_NULL, // crashstate S_SERPENT_FX_X1, // deathstate S_NULL, // xdeathstate SFX_SERPENTFX_HIT, // deathsound 15 * FRACUNIT, // speed 8 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 4, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_SERPENT_HEAD -1, // doomednum S_SERPENT_HEAD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP, // flags MF2_LOGRAV // flags2 }, { // MT_SERPENT_GIB1 -1, // doomednum S_SERPENT_GIB1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 3 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_SERPENT_GIB2 -1, // doomednum S_SERPENT_GIB2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 3 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_SERPENT_GIB3 -1, // doomednum S_SERPENT_GIB3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 3 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_BISHOP 114, // doomednum S_BISHOP_LOOK1, // spawnstate 130, // spawnhealth S_BISHOP_WALK1, // seestate SFX_BISHOP_SIGHT, // seesound 8, // reactiontime SFX_BISHOP_ATTACK, // attacksound S_BISHOP_PAIN1, // painstate 110, // painchance SFX_BISHOP_PAIN, // painsound 0, // meleestate S_BISHOP_ATK1, // missilestate S_NULL, // crashstate S_BISHOP_DEATH1, // deathstate S_NULL, // xdeathstate SFX_BISHOP_DEATH, // deathsound 10, // speed 22 * FRACUNIT, // radius 65 * FRACUNIT, // height 100, // mass 0, // damage SFX_BISHOP_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_FLOAT | MF_NOGRAVITY | MF_NOBLOOD, // flags MF2_PASSMOBJ | MF2_PUSHWALL | MF2_TELESTOMP // flags2 }, { // MT_BISHOP_PUFF -1, // doomednum S_BISHOP_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, // crashstate 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_SHADOW | MF_NOBLOCKMAP | MF_NOGRAVITY, // flags 0 // flags2 }, { // MT_BISHOPBLUR -1, // doomednum S_BISHOPBLUR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_BISHOPPAINBLUR -1, // doomednum S_BISHOPPAINBLUR1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_SHADOW, // flags 0 // flags2 }, { // MT_BISH_FX -1, // doomednum S_BISHFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_BISHFXI1_1, // deathstate S_NULL, // xdeathstate SFX_BISHOP_MISSILE_EXPLODE, // deathsound 10 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 100, // mass 1, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_SEEKERMISSILE // flags2 }, { // MT_DRAGON 254, // doomednum S_DRAGON_LOOK1, // spawnstate 640, // spawnhealth S_DRAGON_INIT, // seestate SFX_DRAGON_SIGHT, // seesound 8, // reactiontime SFX_DRAGON_ATTACK, // attacksound S_DRAGON_PAIN1, // painstate 128, // painchance SFX_DRAGON_PAIN, // painsound S_NULL, // meleestate S_DRAGON_ATK1, // missilestate S_NULL, // crashstate S_DRAGON_DEATH1, // deathstate S_NULL, // xdeathstate SFX_DRAGON_DEATH, // deathsound 10 * FRACUNIT, // speed 20 * FRACUNIT, // radius 65 * FRACUNIT, // height INT_MAX, // mass 0, // damage SFX_DRAGON_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_FLOAT | MF_NOGRAVITY | MF_NOBLOOD, // flags MF2_PASSMOBJ | MF2_BOSS // flags2 }, { // MT_DRAGON_FX -1, // doomednum S_DRAGON_FX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_DRAGON_FX1_X1, // deathstate S_NULL, // xdeathstate SFX_DRAGON_FIREBALL_EXPLODE, // deathsound 24 * FRACUNIT, // speed 12 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 6, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2 }, { // MT_DRAGON_FX2 -1, // doomednum S_DRAGON_FX2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_DRAGON_FIREBALL_EXPLODE, // deathsound 0, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP, // flags MF2_NOTELEPORT | MF2_FIREDAMAGE | MF2_DONTDRAW // flags2 }, { // MT_ARMOR_1 8005, // doomednum S_ARMOR_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARMOR_2 8006, // doomednum S_ARMOR_2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARMOR_3 8007, // doomednum S_ARMOR_3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_ARMOR_4 8008, // doomednum S_ARMOR_4, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOGRAVITY, // flags 0 // flags2 }, { // MT_MANA1 122, // doomednum S_MANA1_1, // spawnstate 10, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags MF2_FLOATBOB // flags2 }, { // MT_MANA2 124, // doomednum S_MANA2_1, // spawnstate 10, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags MF2_FLOATBOB // flags2 }, { // MT_MANA3 8004, // doomednum S_MANA3_1, // 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_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 8 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags MF2_FLOATBOB // flags2 }, { // MT_KEY1 8030, // doomednum S_KEY1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY2 8031, // doomednum S_KEY2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY3 8032, // doomednum S_KEY3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY4 8033, // doomednum S_KEY4, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY5 8034, // doomednum S_KEY5, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY6 8035, // doomednum S_KEY6, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY7 8036, // doomednum S_KEY7, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY8 8037, // doomednum S_KEY8, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEY9 8038, // doomednum S_KEY9, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEYA 8039, // doomednum S_KEYA, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_KEYB 8200, // doomednum S_KEYB, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 8 * FRACUNIT, // radius 20 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SPECIAL, // flags 0 // flags2 }, { // MT_SOUNDWIND 1410, // doomednum S_SND_WIND1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_SOUNDWATERFALL 41, // doomednum S_SND_WATERFALL, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 0 // flags2 }, { // MT_ETTIN 10030, // doomednum S_ETTIN_LOOK1, // spawnstate 175, // spawnhealth S_ETTIN_CHASE1, // seestate SFX_ETTIN_SIGHT, // seesound 8, // reactiontime SFX_ETTIN_ATTACK, // attacksound S_ETTIN_PAIN1, // painstate 60, // painchance SFX_ETTIN_PAIN, // painsound S_ETTIN_ATK1_1, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ETTIN_DEATH1_1, // deathstate S_ETTIN_DEATH2_1, // xdeathstate SFX_ETTIN_DEATH, // deathsound 13, // speed 25 * FRACUNIT, // radius 68 * FRACUNIT, // height 175, // mass 3, // damage SFX_ETTIN_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PUSHWALL | MF2_MCROSS | MF2_TELESTOMP // flags2 }, { // MT_ETTIN_MACE -1, // doomednum S_ETTIN_MACE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ETTIN_MACE5, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_FIREDEMON 10060, // doomednum S_FIRED_SPAWN1, // spawnstate 80, // spawnhealth S_FIRED_LOOK4, // seestate SFX_FIRED_SPAWN, // seesound 8, // reactiontime SFX_NONE, // attacksound S_FIRED_PAIN1, // painstate 1, // painchance SFX_FIRED_PAIN, // painsound S_NULL, // meleestate S_FIRED_ATTACK1, // missilestate S_FIRED_XDEATH1, // crashstate S_FIRED_DEATH1, // deathstate S_FIRED_XDEATH1, // xdeathstate SFX_FIRED_DEATH, // deathsound 13, // speed 20 * FRACUNIT, // radius 68 * FRACUNIT, // height 75, // mass 1, // damage SFX_FIRED_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF | MF_NOGRAVITY | MF_FLOAT, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_INVULNERABLE | MF2_MCROSS | MF2_TELESTOMP // flags2 }, { // MT_FIREDEMON_SPLOTCH1 -1, // doomednum S_FIRED_CORPSE1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_FIREDEMON_SPLOTCH2 -1, // doomednum S_FIRED_CORPSE4, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_DROPOFF | MF_CORPSE, // flags MF2_NOTELEPORT | MF2_FLOORCLIP // flags2 }, { // MT_FIREDEMON_FX1 -1, // doomednum S_FIRED_RDROP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FIRED_RDEAD1_1, // deathstate S_FIRED_RDEAD1_2, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 5 * FRACUNIT, // height 16, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_FIREDEMON_FX2 -1, // doomednum S_FIRED_RDROP2, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FIRED_RDEAD2_1, // deathstate S_FIRED_RDEAD2_2, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 5 * FRACUNIT, // height 16, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_FIREDEMON_FX3 -1, // doomednum S_FIRED_RDROP3, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FIRED_RDEAD3_1, // deathstate S_FIRED_RDEAD3_2, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 5 * FRACUNIT, // height 16, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_FIREDEMON_FX4 -1, // doomednum S_FIRED_RDROP4, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FIRED_RDEAD4_1, // deathstate S_FIRED_RDEAD4_2, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 5 * FRACUNIT, // height 16, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_FIREDEMON_FX5 -1, // doomednum S_FIRED_RDROP5, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FIRED_RDEAD5_1, // deathstate S_FIRED_RDEAD5_2, // xdeathstate SFX_NONE, // deathsound 0, // speed 3 * FRACUNIT, // radius 5 * FRACUNIT, // height 16, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_FIREDEMON_FX6 -1, // doomednum S_FIRED_FX6_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_FIRED_FX6_2, // deathstate S_NULL, // xdeathstate SFX_FIRED_MISSILE_HIT, // deathsound 10 * FRACUNIT, // speed 10 * FRACUNIT, // radius 6 * FRACUNIT, // height 15, // mass 1, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_IMPACT | MF2_PCROSS | MF2_FLOORCLIP | MF2_FIREDAMAGE // flags2 }, { // MT_ICEGUY 8020, // doomednum S_ICEGUY_LOOK, // spawnstate 120, // spawnhealth S_ICEGUY_WALK1, // seestate SFX_ICEGUY_SIGHT, // seesound 8, // reactiontime SFX_ICEGUY_ATTACK, // attacksound S_ICEGUY_PAIN1, // painstate 144, // painchance SFX_NONE, // painsound 0, // meleestate S_ICEGUY_ATK1, // missilestate S_NULL, // crashstate S_ICEGUY_DEATH, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 14, // speed 22 * FRACUNIT, // radius 75 * FRACUNIT, // height 150, // mass 0, // damage SFX_ICEGUY_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_NOBLOOD, // flags MF2_PASSMOBJ | MF2_PUSHWALL | MF2_ICEDAMAGE | MF2_MCROSS | MF2_TELESTOMP // flags2 }, { // MT_ICEGUY_FX -1, // doomednum S_ICEGUY_FX1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_ICEGUY_FX_X1, // deathstate S_NULL, // xdeathstate SFX_ICEGUY_FX_EXPLODE, // deathsound 14 * FRACUNIT, // speed 8 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 1, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags MF2_NOTELEPORT | MF2_ICEDAMAGE // flags2 }, { // MT_ICEFX_PUFF -1, // doomednum S_ICEFX_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, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed FRACUNIT, // radius FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_SHADOW | MF_DROPOFF, // flags MF2_NOTELEPORT // flags2 }, { // MT_ICEGUY_FX2 -1, // doomednum S_ICEGUY_FX2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 10 * FRACUNIT, // speed 4 * FRACUNIT, // radius 4 * FRACUNIT, // height 100, // mass 1, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_LOGRAV | MF2_ICEDAMAGE // flags2 }, { // MT_ICEGUY_BIT -1, // doomednum S_ICEGUY_BIT1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed FRACUNIT, // radius FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV // flags2 }, { // MT_ICEGUY_WISP1 -1, // doomednum S_ICEGUY_WISP1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_ICEGUY_WISP2 -1, // doomednum S_ICEGUY_WISP2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_FIGHTER_BOSS 10100, // doomednum S_FIGHTER, // spawnstate 800, // spawnhealth S_FIGHTER_RUN1, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_FIGHTER_PAIN, // painstate 50, // painchance SFX_PLAYER_FIGHTER_PAIN, // painsound S_FIGHTER_ATK1, // meleestate S_FIGHTER_ATK1, // missilestate S_NULL, // crashstate S_FIGHTER_DIE1, // deathstate S_FIGHTER_XDIE1, // xdeathstate SFX_PLAYER_FIGHTER_CRAZY_DEATH, // deathsound 25, // speed 16 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_TELESTOMP | MF2_PUSHWALL | MF2_MCROSS // flags2 }, { // MT_CLERIC_BOSS 10101, // doomednum S_CLERIC, // spawnstate 800, // spawnhealth S_CLERIC_RUN1, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_CLERIC_PAIN, // painstate 50, // painchance SFX_PLAYER_CLERIC_PAIN, // painsound S_CLERIC_ATK1, // meleestate S_CLERIC_ATK1, // missilestate S_NULL, // crashstate S_CLERIC_DIE1, // deathstate S_CLERIC_XDIE1, // xdeathstate SFX_PLAYER_CLERIC_CRAZY_DEATH, // deathsound 25, // speed 16 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_TELESTOMP | MF2_PUSHWALL | MF2_MCROSS // flags2 }, { // MT_MAGE_BOSS 10102, // doomednum S_MAGE, // spawnstate 800, // spawnhealth S_MAGE_RUN1, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_MAGE_PAIN, // painstate 50, // painchance SFX_PLAYER_MAGE_PAIN, // painsound S_MAGE_ATK1, // meleestate S_MAGE_ATK1, // missilestate S_NULL, // crashstate S_MAGE_DIE1, // deathstate S_MAGE_XDIE1, // xdeathstate SFX_PLAYER_MAGE_CRAZY_DEATH, // deathsound 25, // speed 16 * FRACUNIT, // radius 64 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_TELESTOMP | MF2_PUSHWALL | MF2_MCROSS // flags2 }, { // MT_SORCBOSS 10080, // doomednum S_SORC_SPAWN1, // spawnstate 5000, // spawnhealth S_SORC_WALK1, // seestate SFX_SORCERER_SIGHT, // seesound 8, // reactiontime SFX_NONE, // attacksound S_SORC_PAIN1, // painstate 10, // painchance SFX_SORCERER_PAIN, // painsound S_NULL, // meleestate S_SORC_ATK2_1, // missilestate S_NULL, // crashstate S_SORC_DIE1, // deathstate S_NULL, // xdeathstate SFX_SORCERER_DEATHSCREAM, // deathsound 16, // speed 40 * FRACUNIT, // radius 110 * FRACUNIT, // height 500, // mass 9, // damage SFX_SORCERER_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_NOBLOOD, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_PUSHWALL | MF2_BOSS | MF2_MCROSS // flags2 }, { // MT_SORCBALL1 -1, // doomednum S_SORCBALL1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_SORCERER_BALLBOUNCE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_SORCBALL1_D1, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SORCBALL1_D5, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 10 * FRACUNIT, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SORCBALL2 -1, // doomednum S_SORCBALL2_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_SORCERER_BALLBOUNCE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_SORCBALL2_D1, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SORCBALL2_D5, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 10 * FRACUNIT, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SORCBALL3 -1, // doomednum S_SORCBALL3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_SORCERER_BALLBOUNCE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_SORCBALL3_D1, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SORCBALL3_D5, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 10 * FRACUNIT, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SORCFX1 -1, // doomednum S_SORCFX1_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_SORCERER_BALLBOUNCE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SORCFX1_D1, // deathstate S_SORCFX1_D1, // xdeathstate SFX_NONE, // deathsound 7 * FRACUNIT, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE, // flags MF2_NOTELEPORT | MF2_FLOORBOUNCE // flags2 }, { // MT_SORCFX2 -1, // doomednum S_SORCFX2_SPLIT1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SORCFX2T1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 15 * FRACUNIT, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_SORCFX2_T1 -1, // doomednum S_SORCFX2T1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_ALTSHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_SORCFX3 -1, // doomednum S_SORCFX3_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_SORCERER_BISHOPSPAWN, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_BISHMORPH1, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 15 * FRACUNIT, // speed 22 * FRACUNIT, // radius 65 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE, // flags MF2_NOTELEPORT // flags2 }, { // MT_SORCFX3_EXPLOSION -1, // doomednum S_SORCFX3_EXP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_ALTSHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_SORCFX4 -1, // doomednum S_SORCFX4_1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_SORCFX4_D1, // deathstate S_NULL, // xdeathstate SFX_SORCERER_BALLEXPLODE, // deathsound 12 * FRACUNIT, // speed 10 * FRACUNIT, // radius 10 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_MISSILE | MF_NOGRAVITY, // flags MF2_NOTELEPORT // flags2 }, { // MT_SORCSPARK1 -1, // doomednum S_SORCSPARK1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 5 * FRACUNIT, // radius 5 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF, // flags MF2_NOTELEPORT | MF2_LOGRAV // flags2 }, { // MT_BLASTEFFECT -1, // doomednum S_BLASTEFFECT1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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_NOCLIP | MF_ALTSHADOW, // flags MF2_NOTELEPORT // flags2 }, { // MT_WATER_DRIP -1, // doomednum S_WATERDRIP1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_DRIP, // deathsound 0, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 1, // mass 0, // damage SFX_NONE, // activesound MF_MISSILE, // flags MF2_LOGRAV | MF2_NOTELEPORT // flags2 }, { // MT_KORAX 10200, // doomednum S_KORAX_LOOK1, // spawnstate 5000, // spawnhealth S_KORAX_CHASE2, // seestate SFX_KORAX_SIGHT, // seesound 8, // reactiontime SFX_KORAX_ATTACK, // attacksound S_KORAX_PAIN1, // painstate 20, // painchance SFX_KORAX_PAIN, // painsound S_NULL, // meleestate S_KORAX_ATTACK1, // missilestate S_NULL, // crashstate S_KORAX_DEATH1, // deathstate S_NULL, // xdeathstate SFX_KORAX_DEATH, // deathsound 10, // speed 65 * FRACUNIT, // radius 115 * FRACUNIT, // height 2000, // mass 15, // damage SFX_KORAX_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags MF2_FLOORCLIP | MF2_PUSHWALL | MF2_MCROSS | MF2_TELESTOMP | MF2_BOSS // flags2 }, { // MT_KORAX_SPIRIT1 -1, // doomednum S_KSPIRIT_ROAM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 8 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_KORAX_SPIRIT2 -1, // doomednum S_KSPIRIT_ROAM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 8 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_KORAX_SPIRIT3 -1, // doomednum S_KSPIRIT_ROAM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 8 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_KORAX_SPIRIT4 -1, // doomednum S_KSPIRIT_ROAM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 8 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_KORAX_SPIRIT5 -1, // doomednum S_KSPIRIT_ROAM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 8 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_KORAX_SPIRIT6 -1, // doomednum S_KSPIRIT_ROAM1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 8 * FRACUNIT, // speed 20 * FRACUNIT, // radius 16 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_DROPOFF | MF_NOGRAVITY | MF_ALTSHADOW | MF_MISSILE | MF_NOCLIP, // flags MF2_NOTELEPORT // flags2 }, { // MT_DEMON_MASH -1, // doomednum S_DEMN_LOOK1, // spawnstate 250, // spawnhealth S_DEMN_CHASE1, // seestate SFX_DEMON_SIGHT, // seesound 8, // reactiontime SFX_DEMON_ATTACK, // attacksound S_DEMN_PAIN1, // painstate 50, // painchance SFX_DEMON_PAIN, // painsound S_DEMN_ATK1_1, // meleestate S_DEMN_ATK2_1, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_DEMON_DEATH, // deathsound 13, // speed 32 * FRACUNIT, // radius 64 * FRACUNIT, // height 220, // mass 0, // damage SFX_DEMON_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_ALTSHADOW | MF_NOBLOOD, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_MCROSS | MF2_PUSHWALL | MF2_BLASTED // flags2 }, { // MT_DEMON2_MASH -1, // doomednum S_DEMN2_LOOK1, // spawnstate 250, // spawnhealth S_DEMN2_CHASE1, // seestate SFX_DEMON_SIGHT, // seesound 8, // reactiontime SFX_DEMON_ATTACK, // attacksound S_DEMN2_PAIN1, // painstate 50, // painchance SFX_DEMON_PAIN, // painsound S_DEMN2_ATK1_1, // meleestate S_DEMN2_ATK2_1, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_DEMON_DEATH, // deathsound 13, // speed 32 * FRACUNIT, // radius 64 * FRACUNIT, // height 220, // mass 0, // damage SFX_DEMON_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_ALTSHADOW | MF_NOBLOOD, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_MCROSS | MF2_PUSHWALL | MF2_BLASTED // flags2 }, { // MT_ETTIN_MASH -1, // doomednum S_ETTIN_LOOK1, // spawnstate 175, // spawnhealth S_ETTIN_CHASE1, // seestate SFX_ETTIN_SIGHT, // seesound 8, // reactiontime SFX_ETTIN_ATTACK, // attacksound S_ETTIN_PAIN1, // painstate 60, // painchance SFX_ETTIN_PAIN, // painsound S_ETTIN_ATK1_1, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_ETTIN_DEATH, // deathsound 13, // speed 25 * FRACUNIT, // radius 68 * FRACUNIT, // height 175, // mass 3, // damage SFX_ETTIN_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_ALTSHADOW | MF_NOBLOOD, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_MCROSS | MF2_PUSHWALL | MF2_BLASTED // flags2 }, { // MT_CENTAUR_MASH -1, // doomednum S_CENTAUR_LOOK1, // spawnstate 200, // spawnhealth S_CENTAUR_WALK1, // seestate SFX_CENTAUR_SIGHT, // seesound 8, // reactiontime SFX_CENTAUR_ATTACK, // attacksound S_CENTAUR_PAIN1, // painstate 135, // painchance SFX_CENTAUR_PAIN, // painsound S_CENTAUR_ATK1, // meleestate 0, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_CENTAUR_DEATH, // deathsound 13, // speed 20 * FRACUNIT, // radius 64 * FRACUNIT, // height 120, // mass 0, // damage SFX_CENTAUR_ACTIVE, // activesound MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_ALTSHADOW | MF_NOBLOOD, // flags MF2_FLOORCLIP | MF2_PASSMOBJ | MF2_MCROSS | MF2_PUSHWALL | MF2_BLASTED // flags2 }, { // MT_KORAX_BOLT -1, // doomednum S_KBOLT1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_NULL, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 0, // speed 15 * FRACUNIT, // radius 35 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE | MF_DROPOFF, // flags MF2_NOTELEPORT // flags2 }, { // MT_BAT_SPAWNER 10225, // doomednum S_SPAWNBATS1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate 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 | MF_NOGRAVITY, // flags MF2_DONTDRAW // flags2 }, { // MT_BAT -1, // doomednum S_BAT1, // spawnstate 1000, // spawnhealth S_NULL, // seestate SFX_NONE, // seesound 8, // reactiontime SFX_NONE, // attacksound S_NULL, // painstate 0, // painchance SFX_NONE, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // crashstate S_BAT_DEATH, // deathstate S_NULL, // xdeathstate SFX_NONE, // deathsound 5 * FRACUNIT, // speed 3 * FRACUNIT, // radius 3 * FRACUNIT, // height 100, // mass 0, // damage SFX_NONE, // activesound MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags MF2_PASSMOBJ | MF2_NOTELEPORT // flags2 } }; crispy-doom-crispy-doom-5.6.4/src/hexen/info.h000066400000000000000000002071631360717211000212470ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // generated by stateco typedef enum { SPR_MAN1, SPR_ACLO, SPR_TLGL, SPR_FBL1, SPR_XPL1, SPR_ARRW, SPR_DART, SPR_RIPP, SPR_CFCF, SPR_BLAD, SPR_SHRD, SPR_FFSM, SPR_FFLG, SPR_PTN1, SPR_PTN2, SPR_SOAR, SPR_INVU, SPR_SUMN, SPR_TSPK, SPR_TELO, SPR_TRNG, SPR_ROCK, SPR_FOGS, SPR_FOGM, SPR_FOGL, SPR_SGSA, SPR_SGSB, SPR_PORK, SPR_EGGM, SPR_FHFX, SPR_SPHL, SPR_STWN, SPR_GMPD, SPR_ASKU, SPR_ABGM, SPR_AGMR, SPR_AGMG, SPR_AGG2, SPR_AGMB, SPR_AGB2, SPR_ABK1, SPR_ABK2, SPR_ASK2, SPR_AFWP, SPR_ACWP, SPR_AMWP, SPR_AGER, SPR_AGR2, SPR_AGR3, SPR_AGR4, SPR_TRCH, SPR_PSBG, SPR_ATLP, SPR_THRW, SPR_SPED, SPR_BMAN, SPR_BRAC, SPR_BLST, SPR_HRAD, SPR_SPSH, SPR_LVAS, SPR_SLDG, SPR_STTW, SPR_RCK1, SPR_RCK2, SPR_RCK3, SPR_RCK4, SPR_CDLR, SPR_TRE1, SPR_TRDT, SPR_TRE2, SPR_TRE3, SPR_STM1, SPR_STM2, SPR_STM3, SPR_STM4, SPR_MSH1, SPR_MSH2, SPR_MSH3, SPR_MSH4, SPR_MSH5, SPR_MSH6, SPR_MSH7, SPR_MSH8, SPR_SGMP, SPR_SGM1, SPR_SGM2, SPR_SGM3, SPR_SLC1, SPR_SLC2, SPR_SLC3, SPR_MSS1, SPR_MSS2, SPR_SWMV, SPR_CPS1, SPR_CPS2, SPR_TMS1, SPR_TMS2, SPR_TMS3, SPR_TMS4, SPR_TMS5, SPR_TMS6, SPR_TMS7, SPR_CPS3, SPR_STT2, SPR_STT3, SPR_STT4, SPR_STT5, SPR_GAR1, SPR_GAR2, SPR_GAR3, SPR_GAR4, SPR_GAR5, SPR_GAR6, SPR_GAR7, SPR_GAR8, SPR_GAR9, SPR_BNR1, SPR_TRE4, SPR_TRE5, SPR_TRE6, SPR_TRE7, SPR_LOGG, SPR_ICT1, SPR_ICT2, SPR_ICT3, SPR_ICT4, SPR_ICM1, SPR_ICM2, SPR_ICM3, SPR_ICM4, SPR_RKBL, SPR_RKBS, SPR_RKBK, SPR_RBL1, SPR_RBL2, SPR_RBL3, SPR_VASE, SPR_POT1, SPR_POT2, SPR_POT3, SPR_PBIT, SPR_CPS4, SPR_CPS5, SPR_CPS6, SPR_CPB1, SPR_CPB2, SPR_CPB3, SPR_CPB4, SPR_BDRP, SPR_BDSH, SPR_BDPL, SPR_CNDL, SPR_LEF1, SPR_LEF3, SPR_LEF2, SPR_TWTR, SPR_WLTR, SPR_BARL, SPR_SHB1, SPR_SHB2, SPR_BCKT, SPR_SHRM, SPR_FBUL, SPR_FSKL, SPR_BRTR, SPR_SUIT, SPR_BBLL, SPR_CAND, SPR_IRON, SPR_XMAS, SPR_CDRN, SPR_CHNS, SPR_TST1, SPR_TST2, SPR_TST3, SPR_TST4, SPR_TST5, SPR_TST6, SPR_TST7, SPR_TST8, SPR_TST9, SPR_TST0, SPR_TELE, SPR_TSMK, SPR_FPCH, SPR_WFAX, SPR_FAXE, SPR_WFHM, SPR_FHMR, SPR_FSRD, SPR_FSFX, SPR_CMCE, SPR_WCSS, SPR_CSSF, SPR_WCFM, SPR_CFLM, SPR_CFFX, SPR_CHLY, SPR_SPIR, SPR_MWND, SPR_WMLG, SPR_MLNG, SPR_MLFX, SPR_MLF2, SPR_MSTF, SPR_MSP1, SPR_MSP2, SPR_WFR1, SPR_WFR2, SPR_WFR3, SPR_WCH1, SPR_WCH2, SPR_WCH3, SPR_WMS1, SPR_WMS2, SPR_WMS3, SPR_WPIG, SPR_WMCS, SPR_CONE, SPR_SHEX, SPR_BLOD, SPR_GIBS, SPR_PLAY, SPR_FDTH, SPR_BSKL, SPR_ICEC, SPR_CLER, SPR_MAGE, SPR_PIGY, SPR_CENT, SPR_CTXD, SPR_CTFX, SPR_CTDP, SPR_DEMN, SPR_DEMA, SPR_DEMB, SPR_DEMC, SPR_DEMD, SPR_DEME, SPR_DMFX, SPR_DEM2, SPR_DMBA, SPR_DMBB, SPR_DMBC, SPR_DMBD, SPR_DMBE, SPR_D2FX, SPR_WRTH, SPR_WRT2, SPR_WRBL, SPR_MNTR, SPR_FX12, SPR_FX13, SPR_MNSM, SPR_SSPT, SPR_SSDV, SPR_SSXD, SPR_SSFX, SPR_BISH, SPR_BPFX, SPR_DRAG, SPR_DRFX, SPR_ARM1, SPR_ARM2, SPR_ARM3, SPR_ARM4, SPR_MAN2, SPR_MAN3, SPR_KEY1, SPR_KEY2, SPR_KEY3, SPR_KEY4, SPR_KEY5, SPR_KEY6, SPR_KEY7, SPR_KEY8, SPR_KEY9, SPR_KEYA, SPR_KEYB, SPR_ETTN, SPR_ETTB, SPR_FDMN, SPR_FDMB, SPR_ICEY, SPR_ICPR, SPR_ICWS, SPR_SORC, SPR_SBMP, SPR_SBS4, SPR_SBMB, SPR_SBS3, SPR_SBMG, SPR_SBS1, SPR_SBS2, SPR_SBFX, SPR_RADE, SPR_WATR, SPR_KORX, SPR_ABAT, NUMSPRITES } spritenum_t; typedef enum { S_NULL, S_FREETARGMOBJ, S_MAPSPOT, S_FIREBALL1_1, S_FIREBALL1_2, S_FIREBALL1_X1, S_FIREBALL1_X2, S_FIREBALL1_X3, S_FIREBALL1_X4, S_FIREBALL1_X5, S_FIREBALL1_X6, S_ARROW_1, S_ARROW_X1, S_DART_1, S_DART_X1, S_POISONDART_1, S_POISONDART_X1, S_RIPPERBALL_1, S_RIPPERBALL_2, S_RIPPERBALL_3, S_RIPPERBALL_X1, S_RIPPERBALL_X2, S_RIPPERBALL_X3, S_RIPPERBALL_X4, S_RIPPERBALL_X5, S_RIPPERBALL_X6, S_RIPPERBALL_X7, S_RIPPERBALL_X8, S_RIPPERBALL_X9, S_RIPPERBALL_X10, S_PRJ_BLADE1, S_PRJ_BLADE_X1, S_ICESHARD1, S_ICESHARD2, S_ICESHARD3, S_FLAME_TSMALL1, S_FLAME_TSMALL2, S_FLAME_TSMALL3, S_FLAME_TSMALL4, S_FLAME_TSMALL5, S_FLAME_TSMALL6, S_FLAME_TLARGE1, S_FLAME_TLARGE2, S_FLAME_TLARGE3, S_FLAME_TLARGE4, S_FLAME_TLARGE5, S_FLAME_TLARGE6, S_FLAME_TLARGE7, S_FLAME_TLARGE8, S_FLAME_TLARGE9, S_FLAME_TLARGE10, S_FLAME_TLARGE11, S_FLAME_TLARGE12, S_FLAME_TLARGE13, S_FLAME_TLARGE14, S_FLAME_TLARGE15, S_FLAME_TLARGE16, S_FLAME_SDORM1, S_FLAME_SDORM2, S_FLAME_SDORM3, S_FLAME_SMALL1, S_FLAME_SMALL2, S_FLAME_SMALL3, S_FLAME_SMALL4, S_FLAME_SMALL5, S_FLAME_SMALL6, S_FLAME_SMALL7, S_FLAME_LDORM1, S_FLAME_LDORM2, S_FLAME_LDORM3, S_FLAME_LDORM4, S_FLAME_LDORM5, S_FLAME_LARGE1, S_FLAME_LARGE2, S_FLAME_LARGE3, S_FLAME_LARGE4, S_FLAME_LARGE5, S_FLAME_LARGE6, S_FLAME_LARGE7, S_FLAME_LARGE8, S_FLAME_LARGE9, S_FLAME_LARGE10, S_FLAME_LARGE11, S_FLAME_LARGE12, S_FLAME_LARGE13, S_FLAME_LARGE14, S_FLAME_LARGE15, S_FLAME_LARGE16, S_FLAME_LARGE17, S_FLAME_LARGE18, S_ITEM_PTN1_1, S_ITEM_PTN1_2, S_ITEM_PTN1_3, S_HIDESPECIAL1, S_HIDESPECIAL2, S_HIDESPECIAL3, S_HIDESPECIAL4, S_HIDESPECIAL5, S_HIDESPECIAL6, S_HIDESPECIAL7, S_HIDESPECIAL8, S_HIDESPECIAL9, S_HIDESPECIAL10, S_HIDESPECIAL11, S_DORMANTARTI1_1, S_DORMANTARTI1_2, S_DORMANTARTI1_3, S_DORMANTARTI1_4, S_DORMANTARTI1_5, S_DORMANTARTI1_6, S_DORMANTARTI1_7, S_DORMANTARTI1_8, S_DORMANTARTI1_9, S_DORMANTARTI1_10, S_DORMANTARTI1_11, S_DORMANTARTI1_12, S_DORMANTARTI1_13, S_DORMANTARTI1_14, S_DORMANTARTI1_15, S_DORMANTARTI1_16, S_DORMANTARTI1_17, S_DORMANTARTI1_18, S_DORMANTARTI1_19, S_DORMANTARTI1_20, S_DORMANTARTI1_21, S_DORMANTARTI2_1, S_DORMANTARTI2_2, S_DORMANTARTI2_3, S_DORMANTARTI2_4, S_DORMANTARTI2_5, S_DORMANTARTI2_6, S_DORMANTARTI2_7, S_DORMANTARTI2_8, S_DORMANTARTI2_9, S_DORMANTARTI2_10, S_DORMANTARTI2_11, S_DORMANTARTI2_12, S_DORMANTARTI2_13, S_DORMANTARTI2_14, S_DORMANTARTI2_15, S_DORMANTARTI2_16, S_DORMANTARTI2_17, S_DORMANTARTI2_18, S_DORMANTARTI2_19, S_DORMANTARTI2_20, S_DORMANTARTI2_21, S_DORMANTARTI3_1, S_DORMANTARTI3_2, S_DORMANTARTI3_3, S_DORMANTARTI3_4, S_DORMANTARTI3_5, S_DORMANTARTI3_6, S_DORMANTARTI3_7, S_DORMANTARTI3_8, S_DORMANTARTI3_9, S_DORMANTARTI3_10, S_DORMANTARTI3_11, S_DORMANTARTI3_12, S_DORMANTARTI3_13, S_DORMANTARTI3_14, S_DORMANTARTI3_15, S_DORMANTARTI3_16, S_DORMANTARTI3_17, S_DORMANTARTI3_18, S_DORMANTARTI3_19, S_DORMANTARTI3_20, S_DORMANTARTI3_21, S_DEADARTI1, S_DEADARTI2, S_DEADARTI3, S_DEADARTI4, S_DEADARTI5, S_DEADARTI6, S_DEADARTI7, S_DEADARTI8, S_DEADARTI9, S_DEADARTI10, S_ARTI_PTN2_1, S_ARTI_PTN2_2, S_ARTI_PTN2_3, S_ARTI_SOAR1, S_ARTI_SOAR2, S_ARTI_SOAR3, S_ARTI_SOAR4, S_ARTI_INVU1, S_ARTI_INVU2, S_ARTI_INVU3, S_ARTI_INVU4, S_ARTI_SUMMON, S_SUMMON_FX1_1, S_SUMMON_FX2_1, S_SUMMON_FX2_2, S_SUMMON_FX2_3, S_THRUSTINIT2_1, S_THRUSTINIT2_2, S_BTHRUSTINIT2_1, S_BTHRUSTINIT2_2, S_THRUSTINIT1_1, S_THRUSTINIT1_2, S_BTHRUSTINIT1_1, S_BTHRUSTINIT1_2, S_THRUSTRAISE1, S_THRUSTRAISE2, S_THRUSTRAISE3, S_THRUSTRAISE4, S_BTHRUSTRAISE1, S_BTHRUSTRAISE2, S_BTHRUSTRAISE3, S_BTHRUSTRAISE4, S_THRUSTIMPALE, S_BTHRUSTIMPALE, S_THRUSTRAISE, S_BTHRUSTRAISE, S_THRUSTBLOCK, S_BTHRUSTBLOCK, S_THRUSTLOWER, S_BTHRUSTLOWER, S_THRUSTSTAY, S_BTHRUSTSTAY, S_ARTI_TELOTHER1, S_ARTI_TELOTHER2, S_ARTI_TELOTHER3, S_ARTI_TELOTHER4, S_TELO_FX1, S_TELO_FX2, S_TELO_FX3, S_TELO_FX4, S_TELO_FX5, S_TELO_FX6, S_TELO_FX7, S_TELO_FX8, S_TELO_FX9, S_TELO_FX2_1, S_TELO_FX2_2, S_TELO_FX2_3, S_TELO_FX2_4, S_TELO_FX2_5, S_TELO_FX2_6, S_TELO_FX3_1, S_TELO_FX3_2, S_TELO_FX3_3, S_TELO_FX3_4, S_TELO_FX3_5, S_TELO_FX3_6, S_TELO_FX4_1, S_TELO_FX4_2, S_TELO_FX4_3, S_TELO_FX4_4, S_TELO_FX4_5, S_TELO_FX4_6, S_TELO_FX5_1, S_TELO_FX5_2, S_TELO_FX5_3, S_TELO_FX5_4, S_TELO_FX5_5, S_TELO_FX5_6, S_DIRT1_1, S_DIRT1_D, S_DIRT2_1, S_DIRT2_D, S_DIRT3_1, S_DIRT3_D, S_DIRT4_1, S_DIRT4_D, S_DIRT5_1, S_DIRT5_D, S_DIRT6_1, S_DIRT6_D, S_DIRTCLUMP1, S_ROCK1_1, S_ROCK1_D, S_ROCK2_1, S_ROCK2_D, S_ROCK3_1, S_ROCK3_D, S_SPAWNFOG1, S_FOGPATCHS1, S_FOGPATCHS2, S_FOGPATCHS3, S_FOGPATCHS4, S_FOGPATCHS5, S_FOGPATCHS0, S_FOGPATCHM1, S_FOGPATCHM2, S_FOGPATCHM3, S_FOGPATCHM4, S_FOGPATCHM5, S_FOGPATCHM0, S_FOGPATCHMA, S_FOGPATCHMB, S_FOGPATCHMC, S_FOGPATCHMD, S_FOGPATCHL1, S_FOGPATCHL2, S_FOGPATCHL3, S_FOGPATCHL4, S_FOGPATCHL5, S_FOGPATCHL0, S_FOGPATCHLA, S_FOGPATCHLB, S_FOGPATCHLC, S_FOGPATCHLD, S_QUAKE_ACTIVE1, S_QUAKE_ACTIVE2, S_QUAKE_ACTIVE3, S_QUAKE_ACTIVE4, S_QUAKE_ACTIVE5, S_QUAKE_ACTIVE6, S_QUAKE_ACTIVE7, S_QUAKE_ACTIVE8, S_QUAKE_ACTIVE9, S_QUAKE_ACTIVE0, S_QUAKE_ACTIVEA, S_QUAKE_ACTIVEB, S_QUAKE_ACTIVEC, S_QUAKE_ACTIVED, S_QUAKE_ACTIVEE, S_QUAKE_ACTIVEF, S_QUAKE_ACTIVEG, S_QUAKE_ACTIVEH, S_QUAKE_ACTIVEI, S_QUAKE_ACTIVEJ, S_QUAKE_ACTIVEK, S_QUAKE_ACTIVEL, S_QUAKE_ACTIVEM, S_QUAKE_ACTIVEN, S_QUAKE_ACTIVEO, S_QUAKE_ACTIVEP, S_QUAKE_ACTIVEQ, S_QUAKE_ACTIVER, S_QUAKE_ACTIVES, S_QUAKE_ACTIVET, S_QUAKE_ACTIVEU, S_QUAKE_ACTIVEV, S_QUAKE_ACTIVEW, S_QUAKE_ACTIVEX, S_QUAKE_ACTIVEY, S_QUAKE_ACTIVEZ, S_QUAKE_ACT1, S_QUAKE_ACT2, S_QUAKE_ACT3, S_QUAKE_ACT4, S_QUAKE_ACT5, S_QUAKE_ACT6, S_QUAKE_ACT7, S_QUAKE_ACT8, S_QUAKE_ACT9, S_QUAKE_ACT0, S_SGSHARD1_1, S_SGSHARD1_2, S_SGSHARD1_3, S_SGSHARD1_4, S_SGSHARD1_5, S_SGSHARD1_D, S_SGSHARD2_1, S_SGSHARD2_2, S_SGSHARD2_3, S_SGSHARD2_4, S_SGSHARD2_5, S_SGSHARD2_D, S_SGSHARD3_1, S_SGSHARD3_2, S_SGSHARD3_3, S_SGSHARD3_4, S_SGSHARD3_5, S_SGSHARD3_D, S_SGSHARD4_1, S_SGSHARD4_2, S_SGSHARD4_3, S_SGSHARD4_4, S_SGSHARD4_5, S_SGSHARD4_D, S_SGSHARD5_1, S_SGSHARD5_2, S_SGSHARD5_3, S_SGSHARD5_4, S_SGSHARD5_5, S_SGSHARD5_D, S_SGSHARD6_1, S_SGSHARD6_D, S_SGSHARD7_1, S_SGSHARD7_D, S_SGSHARD8_1, S_SGSHARD8_D, S_SGSHARD9_1, S_SGSHARD9_D, S_SGSHARD0_1, S_SGSHARD0_D, S_ARTI_EGGC1, S_ARTI_EGGC2, S_ARTI_EGGC3, S_ARTI_EGGC4, S_ARTI_EGGC5, S_ARTI_EGGC6, S_ARTI_EGGC7, S_ARTI_EGGC8, S_EGGFX1, S_EGGFX2, S_EGGFX3, S_EGGFX4, S_EGGFX5, S_EGGFXI1_1, S_EGGFXI1_2, S_EGGFXI1_3, S_EGGFXI1_4, S_ARTI_SPHL1, S_ZWINGEDSTATUENOSKULL, S_ZWINGEDSTATUENOSKULL2, S_ZGEMPEDESTAL1, S_ZGEMPEDESTAL2, S_ARTIPUZZSKULL, S_ARTIPUZZGEMBIG, S_ARTIPUZZGEMRED, S_ARTIPUZZGEMGREEN1, S_ARTIPUZZGEMGREEN2, S_ARTIPUZZGEMBLUE1, S_ARTIPUZZGEMBLUE2, S_ARTIPUZZBOOK1, S_ARTIPUZZBOOK2, S_ARTIPUZZSKULL2, S_ARTIPUZZFWEAPON, S_ARTIPUZZCWEAPON, S_ARTIPUZZMWEAPON, S_ARTIPUZZGEAR_1, S_ARTIPUZZGEAR_2, S_ARTIPUZZGEAR_3, S_ARTIPUZZGEAR_4, S_ARTIPUZZGEAR_5, S_ARTIPUZZGEAR_6, S_ARTIPUZZGEAR_7, S_ARTIPUZZGEAR_8, S_ARTIPUZZGEAR2_1, S_ARTIPUZZGEAR2_2, S_ARTIPUZZGEAR2_3, S_ARTIPUZZGEAR2_4, S_ARTIPUZZGEAR2_5, S_ARTIPUZZGEAR2_6, S_ARTIPUZZGEAR2_7, S_ARTIPUZZGEAR2_8, S_ARTIPUZZGEAR3_1, S_ARTIPUZZGEAR3_2, S_ARTIPUZZGEAR3_3, S_ARTIPUZZGEAR3_4, S_ARTIPUZZGEAR3_5, S_ARTIPUZZGEAR3_6, S_ARTIPUZZGEAR3_7, S_ARTIPUZZGEAR3_8, S_ARTIPUZZGEAR4_1, S_ARTIPUZZGEAR4_2, S_ARTIPUZZGEAR4_3, S_ARTIPUZZGEAR4_4, S_ARTIPUZZGEAR4_5, S_ARTIPUZZGEAR4_6, S_ARTIPUZZGEAR4_7, S_ARTIPUZZGEAR4_8, S_ARTI_TRCH1, S_ARTI_TRCH2, S_ARTI_TRCH3, S_FIREBOMB1, S_FIREBOMB2, S_FIREBOMB3, S_FIREBOMB4, S_FIREBOMB5, S_FIREBOMB6, S_FIREBOMB7, S_FIREBOMB8, S_FIREBOMB9, S_FIREBOMB10, S_FIREBOMB11, S_ARTI_ATLP1, S_ARTI_ATLP2, S_ARTI_ATLP3, S_ARTI_ATLP4, S_ARTI_PSBG1, S_POISONBAG1, S_POISONBAG2, S_POISONBAG3, S_POISONBAG4, S_POISONCLOUD1, S_POISONCLOUD2, S_POISONCLOUD3, S_POISONCLOUD4, S_POISONCLOUD5, S_POISONCLOUD6, S_POISONCLOUD7, S_POISONCLOUD8, S_POISONCLOUD9, S_POISONCLOUD10, S_POISONCLOUD11, S_POISONCLOUD12, S_POISONCLOUD13, S_POISONCLOUD14, S_POISONCLOUD15, S_POISONCLOUD16, S_POISONCLOUD17, S_POISONCLOUD18, S_POISONCLOUD_X1, S_POISONCLOUD_X2, S_POISONCLOUD_X3, S_POISONCLOUD_X4, S_THROWINGBOMB1, S_THROWINGBOMB2, S_THROWINGBOMB3, S_THROWINGBOMB4, S_THROWINGBOMB5, S_THROWINGBOMB6, S_THROWINGBOMB7, S_THROWINGBOMB8, S_THROWINGBOMB9, S_THROWINGBOMB10, S_THROWINGBOMB11, S_THROWINGBOMB12, S_THROWINGBOMB_X1, S_THROWINGBOMB_X2, S_THROWINGBOMB_X3, S_THROWINGBOMB_X4, S_THROWINGBOMB_X5, S_THROWINGBOMB_X6, S_THROWINGBOMB_X7, S_THROWINGBOMB_X8, S_ARTI_BOOTS1, S_ARTI_BOOTS2, S_ARTI_BOOTS3, S_ARTI_BOOTS4, S_ARTI_BOOTS5, S_ARTI_BOOTS6, S_ARTI_BOOTS7, S_ARTI_BOOTS8, S_ARTI_MANA, S_ARTI_ARMOR1, S_ARTI_ARMOR2, S_ARTI_ARMOR3, S_ARTI_ARMOR4, S_ARTI_ARMOR5, S_ARTI_ARMOR6, S_ARTI_ARMOR7, S_ARTI_ARMOR8, S_ARTI_BLAST1, S_ARTI_BLAST2, S_ARTI_BLAST3, S_ARTI_BLAST4, S_ARTI_BLAST5, S_ARTI_BLAST6, S_ARTI_BLAST7, S_ARTI_BLAST8, S_ARTI_HEALRAD1, S_ARTI_HEALRAD2, S_ARTI_HEALRAD3, S_ARTI_HEALRAD4, S_ARTI_HEALRAD5, S_ARTI_HEALRAD6, S_ARTI_HEALRAD7, S_ARTI_HEALRAD8, S_ARTI_HEALRAD9, S_ARTI_HEALRAD0, S_ARTI_HEALRADA, S_ARTI_HEALRADB, S_ARTI_HEALRADC, S_ARTI_HEALRADD, S_ARTI_HEALRADE, S_ARTI_HEALRADF, S_SPLASH1, S_SPLASH2, S_SPLASH3, S_SPLASH4, S_SPLASHX, S_SPLASHBASE1, S_SPLASHBASE2, S_SPLASHBASE3, S_SPLASHBASE4, S_SPLASHBASE5, S_SPLASHBASE6, S_SPLASHBASE7, S_LAVASPLASH1, S_LAVASPLASH2, S_LAVASPLASH3, S_LAVASPLASH4, S_LAVASPLASH5, S_LAVASPLASH6, S_LAVASMOKE1, S_LAVASMOKE2, S_LAVASMOKE3, S_LAVASMOKE4, S_LAVASMOKE5, S_SLUDGECHUNK1, S_SLUDGECHUNK2, S_SLUDGECHUNK3, S_SLUDGECHUNK4, S_SLUDGECHUNKX, S_SLUDGESPLASH1, S_SLUDGESPLASH2, S_SLUDGESPLASH3, S_SLUDGESPLASH4, S_ZWINGEDSTATUE1, S_ZROCK1_1, S_ZROCK2_1, S_ZROCK3_1, S_ZROCK4_1, S_ZCHANDELIER1, S_ZCHANDELIER2, S_ZCHANDELIER3, S_ZCHANDELIER_U, S_ZTREEDEAD1, S_ZTREE, S_ZTREEDESTRUCTIBLE1, S_ZTREEDES_D1, S_ZTREEDES_D2, S_ZTREEDES_D3, S_ZTREEDES_D4, S_ZTREEDES_D5, S_ZTREEDES_D6, S_ZTREEDES_X1, S_ZTREEDES_X2, S_ZTREEDES_X3, S_ZTREEDES_X4, S_ZTREEDES_X5, S_ZTREEDES_X6, S_ZTREEDES_X7, S_ZTREEDES_X8, S_ZTREEDES_X9, S_ZTREEDES_X10, S_ZTREESWAMP182_1, S_ZTREESWAMP172_1, S_ZSTUMPBURNED1, S_ZSTUMPBARE1, S_ZSTUMPSWAMP1_1, S_ZSTUMPSWAMP2_1, S_ZSHROOMLARGE1_1, S_ZSHROOMLARGE2_1, S_ZSHROOMLARGE3_1, S_ZSHROOMSMALL1_1, S_ZSHROOMSMALL2_1, S_ZSHROOMSMALL3_1, S_ZSHROOMSMALL4_1, S_ZSHROOMSMALL5_1, S_ZSTALAGMITEPILLAR1, S_ZSTALAGMITELARGE1, S_ZSTALAGMITEMEDIUM1, S_ZSTALAGMITESMALL1, S_ZSTALACTITELARGE1, S_ZSTALACTITEMEDIUM1, S_ZSTALACTITESMALL1, S_ZMOSSCEILING1_1, S_ZMOSSCEILING2_1, S_ZSWAMPVINE1, S_ZCORPSEKABOB1, S_ZCORPSESLEEPING1, S_ZTOMBSTONERIP1, S_ZTOMBSTONESHANE1, S_ZTOMBSTONEBIGCROSS1, S_ZTOMBSTONEBRIANR1, S_ZTOMBSTONECROSSCIRCLE1, S_ZTOMBSTONESMALLCROSS1, S_ZTOMBSTONEBRIANP1, S_CORPSEHANGING_1, S_ZSTATUEGARGOYLEGREENTALL_1, S_ZSTATUEGARGOYLEBLUETALL_1, S_ZSTATUEGARGOYLEGREENSHORT_1, S_ZSTATUEGARGOYLEBLUESHORT_1, S_ZSTATUEGARGOYLESTRIPETALL_1, S_ZSTATUEGARGOYLEDARKREDTALL_1, S_ZSTATUEGARGOYLEREDTALL_1, S_ZSTATUEGARGOYLETANTALL_1, S_ZSTATUEGARGOYLERUSTTALL_1, S_ZSTATUEGARGOYLEDARKREDSHORT_1, S_ZSTATUEGARGOYLEREDSHORT_1, S_ZSTATUEGARGOYLETANSHORT_1, S_ZSTATUEGARGOYLERUSTSHORT_1, S_ZBANNERTATTERED_1, S_ZTREELARGE1, S_ZTREELARGE2, S_ZTREEGNARLED1, S_ZTREEGNARLED2, S_ZLOG, S_ZSTALACTITEICELARGE, S_ZSTALACTITEICEMEDIUM, S_ZSTALACTITEICESMALL, S_ZSTALACTITEICETINY, S_ZSTALAGMITEICELARGE, S_ZSTALAGMITEICEMEDIUM, S_ZSTALAGMITEICESMALL, S_ZSTALAGMITEICETINY, S_ZROCKBROWN1, S_ZROCKBROWN2, S_ZROCKBLACK, S_ZRUBBLE1, S_ZRUBBLE2, S_ZRUBBLE3, S_ZVASEPILLAR, S_ZPOTTERY1, S_ZPOTTERY2, S_ZPOTTERY3, S_ZPOTTERY_EXPLODE, S_POTTERYBIT_1, S_POTTERYBIT_2, S_POTTERYBIT_3, S_POTTERYBIT_4, S_POTTERYBIT_5, S_POTTERYBIT_EX0, S_POTTERYBIT_EX1, S_POTTERYBIT_EX1_2, S_POTTERYBIT_EX2, S_POTTERYBIT_EX2_2, S_POTTERYBIT_EX3, S_POTTERYBIT_EX3_2, S_POTTERYBIT_EX4, S_POTTERYBIT_EX4_2, S_POTTERYBIT_EX5, S_POTTERYBIT_EX5_2, S_ZCORPSELYNCHED1, S_ZCORPSELYNCHED2, S_ZCORPSESITTING, S_ZCORPSESITTING_X, S_CORPSEBIT_1, S_CORPSEBIT_2, S_CORPSEBIT_3, S_CORPSEBIT_4, S_CORPSEBLOODDRIP, S_CORPSEBLOODDRIP_X1, S_CORPSEBLOODDRIP_X2, S_CORPSEBLOODDRIP_X3, S_CORPSEBLOODDRIP_X4, S_BLOODPOOL, S_ZCANDLE1, S_ZCANDLE2, S_ZCANDLE3, S_ZLEAFSPAWNER, S_LEAF1_1, S_LEAF1_2, S_LEAF1_3, S_LEAF1_4, S_LEAF1_5, S_LEAF1_6, S_LEAF1_7, S_LEAF1_8, S_LEAF1_9, S_LEAF1_10, S_LEAF1_11, S_LEAF1_12, S_LEAF1_13, S_LEAF1_14, S_LEAF1_15, S_LEAF1_16, S_LEAF1_17, S_LEAF1_18, S_LEAF_X1, S_LEAF2_1, S_LEAF2_2, S_LEAF2_3, S_LEAF2_4, S_LEAF2_5, S_LEAF2_6, S_LEAF2_7, S_LEAF2_8, S_LEAF2_9, S_LEAF2_10, S_LEAF2_11, S_LEAF2_12, S_LEAF2_13, S_LEAF2_14, S_LEAF2_15, S_LEAF2_16, S_LEAF2_17, S_LEAF2_18, S_ZTWINEDTORCH_1, S_ZTWINEDTORCH_2, S_ZTWINEDTORCH_3, S_ZTWINEDTORCH_4, S_ZTWINEDTORCH_5, S_ZTWINEDTORCH_6, S_ZTWINEDTORCH_7, S_ZTWINEDTORCH_8, S_ZTWINEDTORCH_UNLIT, S_BRIDGE1, S_BRIDGE2, S_BRIDGE3, S_FREE_BRIDGE1, S_FREE_BRIDGE2, S_BBALL1, S_BBALL2, S_ZWALLTORCH1, S_ZWALLTORCH2, S_ZWALLTORCH3, S_ZWALLTORCH4, S_ZWALLTORCH5, S_ZWALLTORCH6, S_ZWALLTORCH7, S_ZWALLTORCH8, S_ZWALLTORCH_U, S_ZBARREL1, S_ZSHRUB1, S_ZSHRUB1_DIE, S_ZSHRUB1_X1, S_ZSHRUB1_X2, S_ZSHRUB1_X3, S_ZSHRUB2, S_ZSHRUB2_DIE, S_ZSHRUB2_X1, S_ZSHRUB2_X2, S_ZSHRUB2_X3, S_ZSHRUB2_X4, S_ZBUCKET1, S_ZPOISONSHROOM1, S_ZPOISONSHROOM_P1, S_ZPOISONSHROOM_P2, S_ZPOISONSHROOM_X1, S_ZPOISONSHROOM_X2, S_ZPOISONSHROOM_X3, S_ZPOISONSHROOM_X4, S_ZFIREBULL1, S_ZFIREBULL2, S_ZFIREBULL3, S_ZFIREBULL4, S_ZFIREBULL5, S_ZFIREBULL6, S_ZFIREBULL7, S_ZFIREBULL_DEATH, S_ZFIREBULL_DEATH2, S_ZFIREBULL_U, S_ZFIREBULL_BIRTH, S_ZFIREBULL_BIRTH2, S_ZFIRETHING1, S_ZFIRETHING2, S_ZFIRETHING3, S_ZFIRETHING4, S_ZFIRETHING5, S_ZFIRETHING6, S_ZFIRETHING7, S_ZFIRETHING8, S_ZFIRETHING9, S_ZBRASSTORCH1, S_ZBRASSTORCH2, S_ZBRASSTORCH3, S_ZBRASSTORCH4, S_ZBRASSTORCH5, S_ZBRASSTORCH6, S_ZBRASSTORCH7, S_ZBRASSTORCH8, S_ZBRASSTORCH9, S_ZBRASSTORCH10, S_ZBRASSTORCH11, S_ZBRASSTORCH12, S_ZBRASSTORCH13, S_ZSUITOFARMOR, S_ZSUITOFARMOR_X1, S_ZARMORCHUNK1, S_ZARMORCHUNK2, S_ZARMORCHUNK3, S_ZARMORCHUNK4, S_ZARMORCHUNK5, S_ZARMORCHUNK6, S_ZARMORCHUNK7, S_ZARMORCHUNK8, S_ZARMORCHUNK9, S_ZARMORCHUNK10, S_ZBELL, S_ZBELL_X1, S_ZBELL_X2, S_ZBELL_X3, S_ZBELL_X4, S_ZBELL_X5, S_ZBELL_X6, S_ZBELL_X7, S_ZBELL_X8, S_ZBELL_X9, S_ZBELL_X10, S_ZBELL_X11, S_ZBELL_X12, S_ZBELL_X13, S_ZBELL_X14, S_ZBELL_X15, S_ZBELL_X16, S_ZBELL_X17, S_ZBELL_X18, S_ZBELL_X19, S_ZBELL_X20, S_ZBELL_X21, S_ZBELL_X22, S_ZBELL_X23, S_ZBELL_X24, S_ZBELL_X25, S_ZBELL_X26, S_ZBELL_X27, S_ZBELL_X28, S_ZBELL_X29, S_ZBELL_X30, S_ZBELL_X31, S_ZBELL_X32, S_ZBELL_X33, S_ZBELL_X34, S_ZBELL_X35, S_ZBELL_X36, S_ZBELL_X37, S_ZBELL_X38, S_ZBELL_X39, S_ZBELL_X40, S_ZBELL_X41, S_ZBELL_X42, S_ZBELL_X43, S_ZBELL_X44, S_ZBELL_X45, S_ZBELL_X46, S_ZBELL_X47, S_ZBLUE_CANDLE1, S_ZBLUE_CANDLE2, S_ZBLUE_CANDLE3, S_ZBLUE_CANDLE4, S_ZBLUE_CANDLE5, S_ZIRON_MAIDEN, S_ZXMAS_TREE, S_ZXMAS_TREE_DIE, S_ZXMAS_TREE_X1, S_ZXMAS_TREE_X2, S_ZXMAS_TREE_X3, S_ZXMAS_TREE_X4, S_ZXMAS_TREE_X5, S_ZXMAS_TREE_X6, S_ZXMAS_TREE_X7, S_ZXMAS_TREE_X8, S_ZXMAS_TREE_X9, S_ZXMAS_TREE_X10, S_ZCAULDRON1, S_ZCAULDRON2, S_ZCAULDRON3, S_ZCAULDRON4, S_ZCAULDRON5, S_ZCAULDRON6, S_ZCAULDRON7, S_ZCAULDRON_U, S_ZCHAINBIT32, S_ZCHAINBIT64, S_ZCHAINEND_HEART, S_ZCHAINEND_HOOK1, S_ZCHAINEND_HOOK2, S_ZCHAINEND_SPIKE, S_ZCHAINEND_SKULL, S_TABLE_SHIT1, S_TABLE_SHIT2, S_TABLE_SHIT3, S_TABLE_SHIT4, S_TABLE_SHIT5, S_TABLE_SHIT6, S_TABLE_SHIT7, S_TABLE_SHIT8, S_TABLE_SHIT9, S_TABLE_SHIT10, S_TFOG1, S_TFOG2, S_TFOG3, S_TFOG4, S_TFOG5, S_TFOG6, S_TFOG7, S_TFOG8, S_TFOG9, S_TFOG10, S_TFOG11, S_TFOG12, S_TFOG13, S_TELESMOKE1, S_TELESMOKE2, S_TELESMOKE3, S_TELESMOKE4, S_TELESMOKE5, S_TELESMOKE6, S_TELESMOKE7, S_TELESMOKE8, S_TELESMOKE9, S_TELESMOKE10, S_TELESMOKE11, S_TELESMOKE12, S_TELESMOKE13, S_TELESMOKE14, S_TELESMOKE15, S_TELESMOKE16, S_TELESMOKE17, S_TELESMOKE18, S_TELESMOKE19, S_TELESMOKE20, S_TELESMOKE21, S_TELESMOKE22, S_TELESMOKE23, S_TELESMOKE24, S_TELESMOKE25, S_TELESMOKE26, S_LIGHTDONE, S_PUNCHREADY, S_PUNCHDOWN, S_PUNCHUP, S_PUNCHATK1_1, S_PUNCHATK1_2, S_PUNCHATK1_3, S_PUNCHATK1_4, S_PUNCHATK1_5, S_PUNCHATK2_1, S_PUNCHATK2_2, S_PUNCHATK2_3, S_PUNCHATK2_4, S_PUNCHATK2_5, S_PUNCHATK2_6, S_PUNCHATK2_7, S_PUNCHATK2_8, S_PUNCHATK2_9, S_PUNCHPUFF1, S_PUNCHPUFF2, S_PUNCHPUFF3, S_PUNCHPUFF4, S_PUNCHPUFF5, S_AXE, S_FAXEREADY, S_FAXEDOWN, S_FAXEUP, S_FAXEATK_1, S_FAXEATK_2, S_FAXEATK_3, S_FAXEATK_4, S_FAXEATK_5, S_FAXEATK_6, S_FAXEATK_7, S_FAXEATK_8, S_FAXEATK_9, S_FAXEATK_10, S_FAXEATK_11, S_FAXEATK_12, S_FAXEATK_13, S_FAXEREADY_G, S_FAXEREADY_G1, S_FAXEREADY_G2, S_FAXEREADY_G3, S_FAXEREADY_G4, S_FAXEREADY_G5, S_FAXEDOWN_G, S_FAXEUP_G, S_FAXEATK_G1, S_FAXEATK_G2, S_FAXEATK_G3, S_FAXEATK_G4, S_FAXEATK_G5, S_FAXEATK_G6, S_FAXEATK_G7, S_FAXEATK_G8, S_FAXEATK_G9, S_FAXEATK_G10, S_FAXEATK_G11, S_FAXEATK_G12, S_FAXEATK_G13, S_AXEPUFF_GLOW1, S_AXEPUFF_GLOW2, S_AXEPUFF_GLOW3, S_AXEPUFF_GLOW4, S_AXEPUFF_GLOW5, S_AXEPUFF_GLOW6, S_AXEPUFF_GLOW7, S_AXEBLOOD1, S_AXEBLOOD2, S_AXEBLOOD3, S_AXEBLOOD4, S_AXEBLOOD5, S_AXEBLOOD6, S_HAMM, S_FHAMMERREADY, S_FHAMMERDOWN, S_FHAMMERUP, S_FHAMMERATK_1, S_FHAMMERATK_2, S_FHAMMERATK_3, S_FHAMMERATK_4, S_FHAMMERATK_5, S_FHAMMERATK_6, S_FHAMMERATK_7, S_FHAMMERATK_8, S_FHAMMERATK_9, S_FHAMMERATK_10, S_FHAMMERATK_11, S_FHAMMERATK_12, S_HAMMER_MISSILE_1, S_HAMMER_MISSILE_2, S_HAMMER_MISSILE_3, S_HAMMER_MISSILE_4, S_HAMMER_MISSILE_5, S_HAMMER_MISSILE_6, S_HAMMER_MISSILE_7, S_HAMMER_MISSILE_8, S_HAMMER_MISSILE_X1, S_HAMMER_MISSILE_X2, S_HAMMER_MISSILE_X3, S_HAMMER_MISSILE_X4, S_HAMMER_MISSILE_X5, S_HAMMER_MISSILE_X6, S_HAMMER_MISSILE_X7, S_HAMMER_MISSILE_X8, S_HAMMER_MISSILE_X9, S_HAMMER_MISSILE_X10, S_HAMMERPUFF1, S_HAMMERPUFF2, S_HAMMERPUFF3, S_HAMMERPUFF4, S_HAMMERPUFF5, S_FSWORDREADY, S_FSWORDREADY1, S_FSWORDREADY2, S_FSWORDREADY3, S_FSWORDREADY4, S_FSWORDREADY5, S_FSWORDREADY6, S_FSWORDREADY7, S_FSWORDREADY8, S_FSWORDREADY9, S_FSWORDREADY10, S_FSWORDREADY11, S_FSWORDDOWN, S_FSWORDUP, S_FSWORDATK_1, S_FSWORDATK_2, S_FSWORDATK_3, S_FSWORDATK_4, S_FSWORDATK_5, S_FSWORDATK_6, S_FSWORDATK_7, S_FSWORDATK_8, S_FSWORDATK_9, S_FSWORDATK_10, S_FSWORDATK_11, S_FSWORDATK_12, S_FSWORD_MISSILE1, S_FSWORD_MISSILE2, S_FSWORD_MISSILE3, S_FSWORD_MISSILE_X1, S_FSWORD_MISSILE_X2, S_FSWORD_MISSILE_X3, S_FSWORD_MISSILE_X4, S_FSWORD_MISSILE_X5, S_FSWORD_MISSILE_X6, S_FSWORD_MISSILE_X7, S_FSWORD_MISSILE_X8, S_FSWORD_MISSILE_X9, S_FSWORD_MISSILE_X10, S_FSWORD_FLAME1, S_FSWORD_FLAME2, S_FSWORD_FLAME3, S_FSWORD_FLAME4, S_FSWORD_FLAME5, S_FSWORD_FLAME6, S_FSWORD_FLAME7, S_FSWORD_FLAME8, S_FSWORD_FLAME9, S_FSWORD_FLAME10, S_CMACEREADY, S_CMACEDOWN, S_CMACEUP, S_CMACEATK_1, S_CMACEATK_2, S_CMACEATK_3, S_CMACEATK_4, S_CMACEATK_5, S_CMACEATK_6, S_CMACEATK_7, S_CMACEATK_8, S_CMACEATK_9, S_CMACEATK_10, S_CMACEATK_11, S_CMACEATK_12, S_CMACEATK_13, S_CMACEATK_14, S_CMACEATK_15, S_CMACEATK_16, S_CMACEATK_17, S_CSTAFF, S_CSTAFFREADY, S_CSTAFFREADY1, S_CSTAFFREADY2, S_CSTAFFREADY3, S_CSTAFFREADY4, S_CSTAFFREADY5, S_CSTAFFREADY6, S_CSTAFFREADY7, S_CSTAFFREADY8, S_CSTAFFREADY9, S_CSTAFFBLINK1, S_CSTAFFBLINK2, S_CSTAFFBLINK3, S_CSTAFFBLINK4, S_CSTAFFBLINK5, S_CSTAFFBLINK6, S_CSTAFFBLINK7, S_CSTAFFBLINK8, S_CSTAFFBLINK9, S_CSTAFFBLINK10, S_CSTAFFBLINK11, S_CSTAFFDOWN, S_CSTAFFDOWN2, S_CSTAFFDOWN3, S_CSTAFFUP, S_CSTAFFATK_1, S_CSTAFFATK_2, S_CSTAFFATK_3, S_CSTAFFATK_4, S_CSTAFFATK_5, S_CSTAFFATK_6, S_CSTAFFATK2_1, S_CSTAFF_MISSILE1, S_CSTAFF_MISSILE2, S_CSTAFF_MISSILE3, S_CSTAFF_MISSILE4, S_CSTAFF_MISSILE_X1, S_CSTAFF_MISSILE_X2, S_CSTAFF_MISSILE_X3, S_CSTAFF_MISSILE_X4, S_CSTAFFPUFF1, S_CSTAFFPUFF2, S_CSTAFFPUFF3, S_CSTAFFPUFF4, S_CSTAFFPUFF5, S_CFLAME1, S_CFLAME2, S_CFLAME3, S_CFLAME4, S_CFLAME5, S_CFLAME6, S_CFLAME7, S_CFLAME8, S_CFLAMEREADY1, S_CFLAMEREADY2, S_CFLAMEREADY3, S_CFLAMEREADY4, S_CFLAMEREADY5, S_CFLAMEREADY6, S_CFLAMEREADY7, S_CFLAMEREADY8, S_CFLAMEREADY9, S_CFLAMEREADY10, S_CFLAMEREADY11, S_CFLAMEREADY12, S_CFLAMEDOWN, S_CFLAMEUP, S_CFLAMEATK_1, S_CFLAMEATK_2, S_CFLAMEATK_3, S_CFLAMEATK_4, S_CFLAMEATK_5, S_CFLAMEATK_6, S_CFLAMEATK_7, S_CFLAMEATK_8, S_CFLAMEFLOOR1, S_CFLAMEFLOOR2, S_CFLAMEFLOOR3, S_FLAMEPUFF1, S_FLAMEPUFF2, S_FLAMEPUFF3, S_FLAMEPUFF4, S_FLAMEPUFF5, S_FLAMEPUFF6, S_FLAMEPUFF7, S_FLAMEPUFF8, S_FLAMEPUFF9, S_FLAMEPUFF10, S_FLAMEPUFF11, S_FLAMEPUFF12, S_FLAMEPUFF13, S_FLAMEPUFF2_1, S_FLAMEPUFF2_2, S_FLAMEPUFF2_3, S_FLAMEPUFF2_4, S_FLAMEPUFF2_5, S_FLAMEPUFF2_6, S_FLAMEPUFF2_7, S_FLAMEPUFF2_8, S_FLAMEPUFF2_9, S_FLAMEPUFF2_10, S_FLAMEPUFF2_11, S_FLAMEPUFF2_12, S_FLAMEPUFF2_13, S_FLAMEPUFF2_14, S_FLAMEPUFF2_15, S_FLAMEPUFF2_16, S_FLAMEPUFF2_17, S_FLAMEPUFF2_18, S_FLAMEPUFF2_19, S_FLAMEPUFF2_20, S_CIRCLE_FLAME1, S_CIRCLE_FLAME2, S_CIRCLE_FLAME3, S_CIRCLE_FLAME4, S_CIRCLE_FLAME5, S_CIRCLE_FLAME6, S_CIRCLE_FLAME7, S_CIRCLE_FLAME8, S_CIRCLE_FLAME9, S_CIRCLE_FLAME10, S_CIRCLE_FLAME11, S_CIRCLE_FLAME12, S_CIRCLE_FLAME13, S_CIRCLE_FLAME14, S_CIRCLE_FLAME15, S_CIRCLE_FLAME16, S_CIRCLE_FLAME_X1, S_CIRCLE_FLAME_X2, S_CIRCLE_FLAME_X3, S_CIRCLE_FLAME_X4, S_CIRCLE_FLAME_X5, S_CIRCLE_FLAME_X6, S_CIRCLE_FLAME_X7, S_CIRCLE_FLAME_X8, S_CIRCLE_FLAME_X9, S_CIRCLE_FLAME_X10, S_CFLAME_MISSILE1, S_CFLAME_MISSILE2, S_CFLAME_MISSILE_X, S_CHOLYREADY, S_CHOLYDOWN, S_CHOLYUP, S_CHOLYATK_1, S_CHOLYATK_2, S_CHOLYATK_3, S_CHOLYATK_4, S_CHOLYATK_5, S_CHOLYATK_6, S_CHOLYATK_7, S_CHOLYATK_8, S_CHOLYATK_9, S_HOLY_FX1, S_HOLY_FX2, S_HOLY_FX3, S_HOLY_FX4, S_HOLY_FX_X1, S_HOLY_FX_X2, S_HOLY_FX_X3, S_HOLY_FX_X4, S_HOLY_FX_X5, S_HOLY_FX_X6, S_HOLY_TAIL1, S_HOLY_TAIL2, S_HOLY_PUFF1, S_HOLY_PUFF2, S_HOLY_PUFF3, S_HOLY_PUFF4, S_HOLY_PUFF5, S_HOLY_MISSILE1, S_HOLY_MISSILE2, S_HOLY_MISSILE3, S_HOLY_MISSILE4, S_HOLY_MISSILE_X, S_HOLY_MISSILE_P1, S_HOLY_MISSILE_P2, S_HOLY_MISSILE_P3, S_HOLY_MISSILE_P4, S_HOLY_MISSILE_P5, S_MWANDREADY, S_MWANDDOWN, S_MWANDUP, S_MWANDATK_1, S_MWANDATK_2, S_MWANDATK_3, S_MWANDATK_4, S_MWANDPUFF1, S_MWANDPUFF2, S_MWANDPUFF3, S_MWANDPUFF4, S_MWANDPUFF5, S_MWANDSMOKE1, S_MWANDSMOKE2, S_MWANDSMOKE3, S_MWANDSMOKE4, S_MWAND_MISSILE1, S_MWAND_MISSILE2, S_MW_LIGHTNING1, S_MW_LIGHTNING2, S_MW_LIGHTNING3, S_MW_LIGHTNING4, S_MW_LIGHTNING5, S_MW_LIGHTNING6, S_MW_LIGHTNING7, S_MW_LIGHTNING8, S_MLIGHTNINGREADY, S_MLIGHTNINGREADY2, S_MLIGHTNINGREADY3, S_MLIGHTNINGREADY4, S_MLIGHTNINGREADY5, S_MLIGHTNINGREADY6, S_MLIGHTNINGREADY7, S_MLIGHTNINGREADY8, S_MLIGHTNINGREADY9, S_MLIGHTNINGREADY10, S_MLIGHTNINGREADY11, S_MLIGHTNINGREADY12, S_MLIGHTNINGREADY13, S_MLIGHTNINGREADY14, S_MLIGHTNINGREADY15, S_MLIGHTNINGREADY16, S_MLIGHTNINGREADY17, S_MLIGHTNINGREADY18, S_MLIGHTNINGREADY19, S_MLIGHTNINGREADY20, S_MLIGHTNINGREADY21, S_MLIGHTNINGREADY22, S_MLIGHTNINGREADY23, S_MLIGHTNINGREADY24, S_MLIGHTNINGDOWN, S_MLIGHTNINGUP, S_MLIGHTNINGATK_1, S_MLIGHTNINGATK_2, S_MLIGHTNINGATK_3, S_MLIGHTNINGATK_4, S_MLIGHTNINGATK_5, S_MLIGHTNINGATK_6, S_MLIGHTNINGATK_7, S_MLIGHTNINGATK_8, S_MLIGHTNINGATK_9, S_MLIGHTNINGATK_10, S_MLIGHTNINGATK_11, S_LIGHTNING_CEILING1, S_LIGHTNING_CEILING2, S_LIGHTNING_CEILING3, S_LIGHTNING_CEILING4, S_LIGHTNING_C_X1, S_LIGHTNING_C_X2, S_LIGHTNING_C_X3, S_LIGHTNING_C_X4, S_LIGHTNING_C_X5, S_LIGHTNING_C_X6, S_LIGHTNING_C_X7, S_LIGHTNING_C_X8, S_LIGHTNING_C_X9, S_LIGHTNING_C_X10, S_LIGHTNING_C_X11, S_LIGHTNING_C_X12, S_LIGHTNING_C_X13, S_LIGHTNING_C_X14, S_LIGHTNING_C_X15, S_LIGHTNING_C_X16, S_LIGHTNING_C_X17, S_LIGHTNING_C_X18, S_LIGHTNING_C_X19, S_LIGHTNING_FLOOR1, S_LIGHTNING_FLOOR2, S_LIGHTNING_FLOOR3, S_LIGHTNING_FLOOR4, S_LIGHTNING_F_X1, S_LIGHTNING_F_X2, S_LIGHTNING_F_X3, S_LIGHTNING_F_X4, S_LIGHTNING_F_X5, S_LIGHTNING_F_X6, S_LIGHTNING_F_X7, S_LIGHTNING_F_X8, S_LIGHTNING_F_X9, S_LIGHTNING_F_X10, S_LIGHTNING_F_X11, S_LIGHTNING_F_X12, S_LIGHTNING_F_X13, S_LIGHTNING_F_X14, S_LIGHTNING_F_X15, S_LIGHTNING_F_X16, S_LIGHTNING_F_X17, S_LIGHTNING_F_X18, S_LIGHTNING_F_X19, S_LIGHTNING_ZAP1, S_LIGHTNING_ZAP2, S_LIGHTNING_ZAP3, S_LIGHTNING_ZAP4, S_LIGHTNING_ZAP5, S_LIGHTNING_ZAP_X1, S_LIGHTNING_ZAP_X2, S_LIGHTNING_ZAP_X3, S_LIGHTNING_ZAP_X4, S_LIGHTNING_ZAP_X5, S_LIGHTNING_ZAP_X6, S_LIGHTNING_ZAP_X7, S_LIGHTNING_ZAP_X8, S_MSTAFFREADY, S_MSTAFFREADY2, S_MSTAFFREADY3, S_MSTAFFREADY4, S_MSTAFFREADY5, S_MSTAFFREADY6, S_MSTAFFREADY7, S_MSTAFFREADY8, S_MSTAFFREADY9, S_MSTAFFREADY10, S_MSTAFFREADY11, S_MSTAFFREADY12, S_MSTAFFREADY13, S_MSTAFFREADY14, S_MSTAFFREADY15, S_MSTAFFREADY16, S_MSTAFFREADY17, S_MSTAFFREADY18, S_MSTAFFREADY19, S_MSTAFFREADY20, S_MSTAFFREADY21, S_MSTAFFREADY22, S_MSTAFFREADY23, S_MSTAFFREADY24, S_MSTAFFREADY25, S_MSTAFFREADY26, S_MSTAFFREADY27, S_MSTAFFREADY28, S_MSTAFFREADY29, S_MSTAFFREADY30, S_MSTAFFREADY31, S_MSTAFFREADY32, S_MSTAFFREADY33, S_MSTAFFREADY34, S_MSTAFFREADY35, S_MSTAFFDOWN, S_MSTAFFUP, S_MSTAFFATK_1, S_MSTAFFATK_2, S_MSTAFFATK_3, S_MSTAFFATK_4, S_MSTAFFATK_5, S_MSTAFFATK_6, S_MSTAFFATK_7, S_MSTAFF_FX1_1, S_MSTAFF_FX1_2, S_MSTAFF_FX1_3, S_MSTAFF_FX1_4, S_MSTAFF_FX1_5, S_MSTAFF_FX1_6, S_MSTAFF_FX_X1, S_MSTAFF_FX_X2, S_MSTAFF_FX_X3, S_MSTAFF_FX_X4, S_MSTAFF_FX_X5, S_MSTAFF_FX_X6, S_MSTAFF_FX_X7, S_MSTAFF_FX_X8, S_MSTAFF_FX_X9, S_MSTAFF_FX_X10, S_MSTAFF_FX2_1, S_MSTAFF_FX2_2, S_MSTAFF_FX2_3, S_MSTAFF_FX2_4, S_MSTAFF_FX2_X1, S_MSTAFF_FX2_X2, S_MSTAFF_FX2_X3, S_MSTAFF_FX2_X4, S_MSTAFF_FX2_X5, S_FSWORD1, S_FSWORD2, S_FSWORD3, S_CHOLY1, S_CHOLY2, S_CHOLY3, S_MSTAFF1, S_MSTAFF2, S_MSTAFF3, S_SNOUTREADY, S_SNOUTDOWN, S_SNOUTUP, S_SNOUTATK1, S_SNOUTATK2, S_COS1, S_COS2, S_COS3, S_CONEREADY, S_CONEDOWN, S_CONEUP, S_CONEATK1_1, S_CONEATK1_2, S_CONEATK1_3, S_CONEATK1_4, S_CONEATK1_5, S_CONEATK1_6, S_CONEATK1_7, S_CONEATK1_8, S_SHARDFX1_1, S_SHARDFX1_2, S_SHARDFX1_3, S_SHARDFX1_4, S_SHARDFXE1_1, S_SHARDFXE1_2, S_SHARDFXE1_3, S_SHARDFXE1_4, S_SHARDFXE1_5, S_BLOOD1, S_BLOOD2, S_BLOOD3, S_BLOODSPLATTER1, S_BLOODSPLATTER2, S_BLOODSPLATTER3, S_BLOODSPLATTERX, S_GIBS1, S_FPLAY, S_FPLAY_RUN1, S_FPLAY_RUN2, S_FPLAY_RUN3, S_FPLAY_RUN4, S_FPLAY_ATK1, S_FPLAY_ATK2, S_FPLAY_PAIN, S_FPLAY_PAIN2, S_FPLAY_DIE1, S_FPLAY_DIE2, S_FPLAY_DIE3, S_FPLAY_DIE4, S_FPLAY_DIE5, S_FPLAY_DIE6, S_FPLAY_DIE7, S_FPLAY_XDIE1, S_FPLAY_XDIE2, S_FPLAY_XDIE3, S_FPLAY_XDIE4, S_FPLAY_XDIE5, S_FPLAY_XDIE6, S_FPLAY_XDIE7, S_FPLAY_XDIE8, S_FPLAY_ICE, S_FPLAY_ICE2, S_PLAY_F_FDTH1, S_PLAY_F_FDTH2, S_PLAY_C_FDTH1, S_PLAY_C_FDTH2, S_PLAY_M_FDTH1, S_PLAY_M_FDTH2, S_PLAY_FDTH3, S_PLAY_FDTH4, S_PLAY_FDTH5, S_PLAY_FDTH6, S_PLAY_FDTH7, S_PLAY_FDTH8, S_PLAY_FDTH9, S_PLAY_FDTH10, S_PLAY_FDTH11, S_PLAY_FDTH12, S_PLAY_FDTH13, S_PLAY_FDTH14, S_PLAY_FDTH15, S_PLAY_FDTH16, S_PLAY_FDTH17, S_PLAY_FDTH18, S_PLAY_FDTH19, S_PLAY_FDTH20, S_BLOODYSKULL1, S_BLOODYSKULL2, S_BLOODYSKULL3, S_BLOODYSKULL4, S_BLOODYSKULL5, S_BLOODYSKULL6, S_BLOODYSKULL7, S_BLOODYSKULLX1, S_BLOODYSKULLX2, S_PLAYER_SPEED1, S_PLAYER_SPEED2, S_ICECHUNK1, S_ICECHUNK2, S_ICECHUNK3, S_ICECHUNK4, S_ICECHUNK_HEAD, S_ICECHUNK_HEAD2, S_CPLAY, S_CPLAY_RUN1, S_CPLAY_RUN2, S_CPLAY_RUN3, S_CPLAY_RUN4, S_CPLAY_ATK1, S_CPLAY_ATK2, S_CPLAY_ATK3, S_CPLAY_PAIN, S_CPLAY_PAIN2, S_CPLAY_DIE1, S_CPLAY_DIE2, S_CPLAY_DIE3, S_CPLAY_DIE4, S_CPLAY_DIE5, S_CPLAY_DIE6, S_CPLAY_DIE7, S_CPLAY_DIE8, S_CPLAY_DIE9, S_CPLAY_XDIE1, S_CPLAY_XDIE2, S_CPLAY_XDIE3, S_CPLAY_XDIE4, S_CPLAY_XDIE5, S_CPLAY_XDIE6, S_CPLAY_XDIE7, S_CPLAY_XDIE8, S_CPLAY_XDIE9, S_CPLAY_XDIE10, S_CPLAY_ICE, S_CPLAY_ICE2, S_MPLAY, S_MPLAY_RUN1, S_MPLAY_RUN2, S_MPLAY_RUN3, S_MPLAY_RUN4, S_MPLAY_ATK1, S_MPLAY_ATK2, S_MPLAY_PAIN, S_MPLAY_PAIN2, S_MPLAY_DIE1, S_MPLAY_DIE2, S_MPLAY_DIE3, S_MPLAY_DIE4, S_MPLAY_DIE5, S_MPLAY_DIE6, S_MPLAY_DIE7, S_MPLAY_XDIE1, S_MPLAY_XDIE2, S_MPLAY_XDIE3, S_MPLAY_XDIE4, S_MPLAY_XDIE5, S_MPLAY_XDIE6, S_MPLAY_XDIE7, S_MPLAY_XDIE8, S_MPLAY_XDIE9, S_MPLAY_ICE, S_MPLAY_ICE2, S_PIGPLAY, S_PIGPLAY_RUN1, S_PIGPLAY_RUN2, S_PIGPLAY_RUN3, S_PIGPLAY_RUN4, S_PIGPLAY_ATK1, S_PIGPLAY_PAIN, S_PIG_LOOK1, S_PIG_WALK1, S_PIG_WALK2, S_PIG_WALK3, S_PIG_WALK4, S_PIG_PAIN, S_PIG_ATK1, S_PIG_ATK2, S_PIG_DIE1, S_PIG_DIE2, S_PIG_DIE3, S_PIG_DIE4, S_PIG_DIE5, S_PIG_DIE6, S_PIG_DIE7, S_PIG_DIE8, S_PIG_ICE, S_PIG_ICE2, S_CENTAUR_LOOK1, S_CENTAUR_LOOK2, S_CENTAUR_WALK1, S_CENTAUR_WALK2, S_CENTAUR_WALK3, S_CENTAUR_WALK4, S_CENTAUR_ATK1, S_CENTAUR_ATK2, S_CENTAUR_ATK3, S_CENTAUR_MISSILE1, S_CENTAUR_MISSILE2, S_CENTAUR_MISSILE3, S_CENTAUR_MISSILE4, S_CENTAUR_PAIN1, S_CENTAUR_PAIN2, S_CENTAUR_PAIN3, S_CENTAUR_PAIN4, S_CENTAUR_PAIN5, S_CENTAUR_PAIN6, S_CENTAUR_DEATH1, S_CENTAUR_DEATH2, S_CENTAUR_DEATH3, S_CENTAUR_DEATH4, S_CENTAUR_DEATH5, S_CENTAUR_DEATH6, S_CENTAUR_DEATH7, S_CENTAUR_DEATH8, S_CENTAUR_DEATH9, S_CENTAUR_DEATH0, S_CENTAUR_DEATH_X1, S_CENTAUR_DEATH_X2, S_CENTAUR_DEATH_X3, S_CENTAUR_DEATH_X4, S_CENTAUR_DEATH_X5, S_CENTAUR_DEATH_X6, S_CENTAUR_DEATH_X7, S_CENTAUR_DEATH_X8, S_CENTAUR_DEATH_X9, S_CENTAUR_DEATH_X10, S_CENTAUR_DEATH_X11, S_CENTAUR_ICE, S_CENTAUR_ICE2, S_CENTAUR_FX1, S_CENTAUR_FX_X1, S_CENTAUR_FX_X2, S_CENTAUR_FX_X3, S_CENTAUR_FX_X4, S_CENTAUR_FX_X5, S_CENTAUR_SHIELD1, S_CENTAUR_SHIELD2, S_CENTAUR_SHIELD3, S_CENTAUR_SHIELD4, S_CENTAUR_SHIELD5, S_CENTAUR_SHIELD6, S_CENTAUR_SHIELD_X1, S_CENTAUR_SHIELD_X2, S_CENTAUR_SHIELD_X3, S_CENTAUR_SHIELD_X4, S_CENTAUR_SWORD1, S_CENTAUR_SWORD2, S_CENTAUR_SWORD3, S_CENTAUR_SWORD4, S_CENTAUR_SWORD5, S_CENTAUR_SWORD6, S_CENTAUR_SWORD7, S_CENTAUR_SWORD_X1, S_CENTAUR_SWORD_X2, S_CENTAUR_SWORD_X3, S_DEMN_LOOK1, S_DEMN_LOOK2, S_DEMN_CHASE1, S_DEMN_CHASE2, S_DEMN_CHASE3, S_DEMN_CHASE4, S_DEMN_ATK1_1, S_DEMN_ATK1_2, S_DEMN_ATK1_3, S_DEMN_ATK2_1, S_DEMN_ATK2_2, S_DEMN_ATK2_3, S_DEMN_PAIN1, S_DEMN_PAIN2, S_DEMN_DEATH1, S_DEMN_DEATH2, S_DEMN_DEATH3, S_DEMN_DEATH4, S_DEMN_DEATH5, S_DEMN_DEATH6, S_DEMN_DEATH7, S_DEMN_DEATH8, S_DEMN_DEATH9, S_DEMN_XDEATH1, S_DEMN_XDEATH2, S_DEMN_XDEATH3, S_DEMN_XDEATH4, S_DEMN_XDEATH5, S_DEMN_XDEATH6, S_DEMN_XDEATH7, S_DEMN_XDEATH8, S_DEMN_XDEATH9, S_DEMON_ICE, S_DEMON_ICE2, S_DEMONCHUNK1_1, S_DEMONCHUNK1_2, S_DEMONCHUNK1_3, S_DEMONCHUNK1_4, S_DEMONCHUNK2_1, S_DEMONCHUNK2_2, S_DEMONCHUNK2_3, S_DEMONCHUNK2_4, S_DEMONCHUNK3_1, S_DEMONCHUNK3_2, S_DEMONCHUNK3_3, S_DEMONCHUNK3_4, S_DEMONCHUNK4_1, S_DEMONCHUNK4_2, S_DEMONCHUNK4_3, S_DEMONCHUNK4_4, S_DEMONCHUNK5_1, S_DEMONCHUNK5_2, S_DEMONCHUNK5_3, S_DEMONCHUNK5_4, S_DEMONFX_MOVE1, S_DEMONFX_MOVE2, S_DEMONFX_MOVE3, S_DEMONFX_BOOM1, S_DEMONFX_BOOM2, S_DEMONFX_BOOM3, S_DEMONFX_BOOM4, S_DEMONFX_BOOM5, S_DEMN2_LOOK1, S_DEMN2_LOOK2, S_DEMN2_CHASE1, S_DEMN2_CHASE2, S_DEMN2_CHASE3, S_DEMN2_CHASE4, S_DEMN2_ATK1_1, S_DEMN2_ATK1_2, S_DEMN2_ATK1_3, S_DEMN2_ATK2_1, S_DEMN2_ATK2_2, S_DEMN2_ATK2_3, S_DEMN2_PAIN1, S_DEMN2_PAIN2, S_DEMN2_DEATH1, S_DEMN2_DEATH2, S_DEMN2_DEATH3, S_DEMN2_DEATH4, S_DEMN2_DEATH5, S_DEMN2_DEATH6, S_DEMN2_DEATH7, S_DEMN2_DEATH8, S_DEMN2_DEATH9, S_DEMN2_XDEATH1, S_DEMN2_XDEATH2, S_DEMN2_XDEATH3, S_DEMN2_XDEATH4, S_DEMN2_XDEATH5, S_DEMN2_XDEATH6, S_DEMN2_XDEATH7, S_DEMN2_XDEATH8, S_DEMN2_XDEATH9, S_DEMON2CHUNK1_1, S_DEMON2CHUNK1_2, S_DEMON2CHUNK1_3, S_DEMON2CHUNK1_4, S_DEMON2CHUNK2_1, S_DEMON2CHUNK2_2, S_DEMON2CHUNK2_3, S_DEMON2CHUNK2_4, S_DEMON2CHUNK3_1, S_DEMON2CHUNK3_2, S_DEMON2CHUNK3_3, S_DEMON2CHUNK3_4, S_DEMON2CHUNK4_1, S_DEMON2CHUNK4_2, S_DEMON2CHUNK4_3, S_DEMON2CHUNK4_4, S_DEMON2CHUNK5_1, S_DEMON2CHUNK5_2, S_DEMON2CHUNK5_3, S_DEMON2CHUNK5_4, S_DEMON2FX_MOVE1, S_DEMON2FX_MOVE2, S_DEMON2FX_MOVE3, S_DEMON2FX_MOVE4, S_DEMON2FX_MOVE5, S_DEMON2FX_MOVE6, S_DEMON2FX_BOOM1, S_DEMON2FX_BOOM2, S_DEMON2FX_BOOM3, S_DEMON2FX_BOOM4, S_DEMON2FX_BOOM5, S_DEMON2FX_BOOM6, S_WRAITH_RAISE1, S_WRAITH_RAISE2, S_WRAITH_RAISE3, S_WRAITH_RAISE4, S_WRAITH_RAISE5, S_WRAITH_INIT1, S_WRAITH_INIT2, S_WRAITH_LOOK1, S_WRAITH_LOOK2, S_WRAITH_CHASE1, S_WRAITH_CHASE2, S_WRAITH_CHASE3, S_WRAITH_CHASE4, S_WRAITH_ATK1_1, S_WRAITH_ATK1_2, S_WRAITH_ATK1_3, S_WRAITH_ATK2_1, S_WRAITH_ATK2_2, S_WRAITH_ATK2_3, S_WRAITH_PAIN1, S_WRAITH_PAIN2, S_WRAITH_DEATH1_1, S_WRAITH_DEATH1_2, S_WRAITH_DEATH1_3, S_WRAITH_DEATH1_4, S_WRAITH_DEATH1_5, S_WRAITH_DEATH1_6, S_WRAITH_DEATH1_7, S_WRAITH_DEATH1_8, S_WRAITH_DEATH1_9, S_WRAITH_DEATH1_0, S_WRAITH_DEATH2_1, S_WRAITH_DEATH2_2, S_WRAITH_DEATH2_3, S_WRAITH_DEATH2_4, S_WRAITH_DEATH2_5, S_WRAITH_DEATH2_6, S_WRAITH_DEATH2_7, S_WRAITH_DEATH2_8, S_WRAITH_ICE, S_WRAITH_ICE2, S_WRTHFX_MOVE1, S_WRTHFX_MOVE2, S_WRTHFX_MOVE3, S_WRTHFX_BOOM1, S_WRTHFX_BOOM2, S_WRTHFX_BOOM3, S_WRTHFX_BOOM4, S_WRTHFX_BOOM5, S_WRTHFX_BOOM6, S_WRTHFX_SIZZLE1, S_WRTHFX_SIZZLE2, S_WRTHFX_SIZZLE3, S_WRTHFX_SIZZLE4, S_WRTHFX_SIZZLE5, S_WRTHFX_SIZZLE6, S_WRTHFX_SIZZLE7, S_WRTHFX_DROP1, S_WRTHFX_DROP2, S_WRTHFX_DROP3, S_WRTHFX_DEAD1, S_WRTHFX_ADROP1, S_WRTHFX_ADROP2, S_WRTHFX_ADROP3, S_WRTHFX_ADROP4, S_WRTHFX_ADEAD1, S_WRTHFX_BDROP1, S_WRTHFX_BDROP2, S_WRTHFX_BDROP3, S_WRTHFX_BDEAD1, S_MNTR_SPAWN1, S_MNTR_SPAWN2, S_MNTR_SPAWN3, S_MNTR_LOOK1, S_MNTR_LOOK2, S_MNTR_WALK1, S_MNTR_WALK2, S_MNTR_WALK3, S_MNTR_WALK4, S_MNTR_ROAM1, S_MNTR_ROAM2, S_MNTR_ROAM3, S_MNTR_ROAM4, S_MNTR_ATK1_1, S_MNTR_ATK1_2, S_MNTR_ATK1_3, S_MNTR_ATK2_1, S_MNTR_ATK2_2, S_MNTR_ATK2_3, S_MNTR_ATK3_1, S_MNTR_ATK3_2, S_MNTR_ATK3_3, S_MNTR_ATK3_4, S_MNTR_ATK4_1, S_MNTR_PAIN1, S_MNTR_PAIN2, S_MNTR_DIE1, S_MNTR_DIE2, S_MNTR_DIE3, S_MNTR_DIE4, S_MNTR_DIE5, S_MNTR_DIE6, S_MNTR_DIE7, S_MNTR_DIE8, S_MNTR_DIE9, S_MNTRFX1_1, S_MNTRFX1_2, S_MNTRFXI1_1, S_MNTRFXI1_2, S_MNTRFXI1_3, S_MNTRFXI1_4, S_MNTRFXI1_5, S_MNTRFXI1_6, S_MNTRFX2_1, S_MNTRFXI2_1, S_MNTRFXI2_2, S_MNTRFXI2_3, S_MNTRFXI2_4, S_MNTRFXI2_5, S_MNTRFX3_1, S_MNTRFX3_2, S_MNTRFX3_3, S_MNTRFX3_4, S_MNTRFX3_5, S_MNTRFX3_6, S_MNTRFX3_7, S_MNTRFX3_8, S_MNTRFX3_9, S_MINOSMOKE1, S_MINOSMOKE2, S_MINOSMOKE3, S_MINOSMOKE4, S_MINOSMOKE5, S_MINOSMOKE6, S_MINOSMOKE7, S_MINOSMOKE8, S_MINOSMOKE9, S_MINOSMOKE0, S_MINOSMOKEA, S_MINOSMOKEB, S_MINOSMOKEC, S_MINOSMOKED, S_MINOSMOKEE, S_MINOSMOKEF, S_MINOSMOKEG, S_MINOSMOKEX1, S_MINOSMOKEX2, S_MINOSMOKEX3, S_MINOSMOKEX4, S_MINOSMOKEX5, S_MINOSMOKEX6, S_MINOSMOKEX7, S_MINOSMOKEX8, S_MINOSMOKEX9, S_MINOSMOKEX0, S_MINOSMOKEXA, S_MINOSMOKEXB, S_MINOSMOKEXC, S_MINOSMOKEXD, S_MINOSMOKEXE, S_MINOSMOKEXF, S_MINOSMOKEXG, S_MINOSMOKEXH, S_MINOSMOKEXI, S_SERPENT_LOOK1, S_SERPENT_SWIM1, S_SERPENT_SWIM2, S_SERPENT_SWIM3, S_SERPENT_HUMP1, S_SERPENT_HUMP2, S_SERPENT_HUMP3, S_SERPENT_HUMP4, S_SERPENT_HUMP5, S_SERPENT_HUMP6, S_SERPENT_HUMP7, S_SERPENT_HUMP8, S_SERPENT_HUMP9, S_SERPENT_HUMP10, S_SERPENT_HUMP11, S_SERPENT_HUMP12, S_SERPENT_HUMP13, S_SERPENT_HUMP14, S_SERPENT_HUMP15, S_SERPENT_SURFACE1, S_SERPENT_SURFACE2, S_SERPENT_SURFACE3, S_SERPENT_SURFACE4, S_SERPENT_SURFACE5, S_SERPENT_DIVE1, S_SERPENT_DIVE2, S_SERPENT_DIVE3, S_SERPENT_DIVE4, S_SERPENT_DIVE5, S_SERPENT_DIVE6, S_SERPENT_DIVE7, S_SERPENT_DIVE8, S_SERPENT_DIVE9, S_SERPENT_DIVE10, S_SERPENT_WALK1, S_SERPENT_WALK2, S_SERPENT_WALK3, S_SERPENT_WALK4, S_SERPENT_PAIN1, S_SERPENT_PAIN2, S_SERPENT_ATK1, S_SERPENT_ATK2, S_SERPENT_MELEE1, S_SERPENT_MISSILE1, S_SERPENT_DIE1, S_SERPENT_DIE2, S_SERPENT_DIE3, S_SERPENT_DIE4, S_SERPENT_DIE5, S_SERPENT_DIE6, S_SERPENT_DIE7, S_SERPENT_DIE8, S_SERPENT_DIE9, S_SERPENT_DIE10, S_SERPENT_DIE11, S_SERPENT_DIE12, S_SERPENT_XDIE1, S_SERPENT_XDIE2, S_SERPENT_XDIE3, S_SERPENT_XDIE4, S_SERPENT_XDIE5, S_SERPENT_XDIE6, S_SERPENT_XDIE7, S_SERPENT_XDIE8, S_SERPENT_ICE, S_SERPENT_ICE2, S_SERPENT_FX1, S_SERPENT_FX2, S_SERPENT_FX3, S_SERPENT_FX4, S_SERPENT_FX_X1, S_SERPENT_FX_X2, S_SERPENT_FX_X3, S_SERPENT_FX_X4, S_SERPENT_FX_X5, S_SERPENT_FX_X6, S_SERPENT_HEAD1, S_SERPENT_HEAD2, S_SERPENT_HEAD3, S_SERPENT_HEAD4, S_SERPENT_HEAD5, S_SERPENT_HEAD6, S_SERPENT_HEAD7, S_SERPENT_HEAD8, S_SERPENT_HEAD_X1, S_SERPENT_GIB1_1, S_SERPENT_GIB1_2, S_SERPENT_GIB1_3, S_SERPENT_GIB1_4, S_SERPENT_GIB1_5, S_SERPENT_GIB1_6, S_SERPENT_GIB1_7, S_SERPENT_GIB1_8, S_SERPENT_GIB1_9, S_SERPENT_GIB1_10, S_SERPENT_GIB1_11, S_SERPENT_GIB1_12, S_SERPENT_GIB2_1, S_SERPENT_GIB2_2, S_SERPENT_GIB2_3, S_SERPENT_GIB2_4, S_SERPENT_GIB2_5, S_SERPENT_GIB2_6, S_SERPENT_GIB2_7, S_SERPENT_GIB2_8, S_SERPENT_GIB2_9, S_SERPENT_GIB2_10, S_SERPENT_GIB2_11, S_SERPENT_GIB2_12, S_SERPENT_GIB3_1, S_SERPENT_GIB3_2, S_SERPENT_GIB3_3, S_SERPENT_GIB3_4, S_SERPENT_GIB3_5, S_SERPENT_GIB3_6, S_SERPENT_GIB3_7, S_SERPENT_GIB3_8, S_SERPENT_GIB3_9, S_SERPENT_GIB3_10, S_SERPENT_GIB3_11, S_SERPENT_GIB3_12, S_BISHOP_LOOK1, S_BISHOP_DECIDE, S_BISHOP_BLUR1, S_BISHOP_BLUR2, S_BISHOP_WALK1, S_BISHOP_WALK2, S_BISHOP_WALK3, S_BISHOP_WALK4, S_BISHOP_WALK5, S_BISHOP_WALK6, S_BISHOP_ATK1, S_BISHOP_ATK2, S_BISHOP_ATK3, S_BISHOP_ATK4, S_BISHOP_ATK5, S_BISHOP_PAIN1, S_BISHOP_PAIN2, S_BISHOP_PAIN3, S_BISHOP_PAIN4, S_BISHOP_PAIN5, S_BISHOP_DEATH1, S_BISHOP_DEATH2, S_BISHOP_DEATH3, S_BISHOP_DEATH4, S_BISHOP_DEATH5, S_BISHOP_DEATH6, S_BISHOP_DEATH7, S_BISHOP_DEATH8, S_BISHOP_DEATH9, S_BISHOP_DEATH10, S_BISHOP_ICE, S_BISHOP_ICE2, S_BISHOP_PUFF1, S_BISHOP_PUFF2, S_BISHOP_PUFF3, S_BISHOP_PUFF4, S_BISHOP_PUFF5, S_BISHOP_PUFF6, S_BISHOP_PUFF7, S_BISHOPBLUR1, S_BISHOPBLUR2, S_BISHOPPAINBLUR1, S_BISHFX1_1, S_BISHFX1_2, S_BISHFX1_3, S_BISHFX1_4, S_BISHFX1_5, S_BISHFXI1_1, S_BISHFXI1_2, S_BISHFXI1_3, S_BISHFXI1_4, S_BISHFXI1_5, S_BISHFXI1_6, S_DRAGON_LOOK1, S_DRAGON_INIT, S_DRAGON_INIT2, S_DRAGON_INIT3, S_DRAGON_WALK1, S_DRAGON_WALK2, S_DRAGON_WALK3, S_DRAGON_WALK4, S_DRAGON_WALK5, S_DRAGON_WALK6, S_DRAGON_WALK7, S_DRAGON_WALK8, S_DRAGON_WALK9, S_DRAGON_WALK10, S_DRAGON_WALK11, S_DRAGON_WALK12, S_DRAGON_ATK1, S_DRAGON_PAIN1, S_DRAGON_DEATH1, S_DRAGON_DEATH2, S_DRAGON_DEATH3, S_DRAGON_DEATH4, S_DRAGON_CRASH1, S_DRAGON_CRASH2, S_DRAGON_CRASH3, S_DRAGON_FX1_1, S_DRAGON_FX1_2, S_DRAGON_FX1_3, S_DRAGON_FX1_4, S_DRAGON_FX1_5, S_DRAGON_FX1_6, S_DRAGON_FX1_X1, S_DRAGON_FX1_X2, S_DRAGON_FX1_X3, S_DRAGON_FX1_X4, S_DRAGON_FX1_X5, S_DRAGON_FX1_X6, S_DRAGON_FX2_1, S_DRAGON_FX2_2, S_DRAGON_FX2_3, S_DRAGON_FX2_4, S_DRAGON_FX2_5, S_DRAGON_FX2_6, S_DRAGON_FX2_7, S_DRAGON_FX2_8, S_DRAGON_FX2_9, S_DRAGON_FX2_10, S_DRAGON_FX2_11, S_ARMOR_1, S_ARMOR_2, S_ARMOR_3, S_ARMOR_4, S_MANA1_1, S_MANA1_2, S_MANA1_3, S_MANA1_4, S_MANA1_5, S_MANA1_6, S_MANA1_7, S_MANA1_8, S_MANA1_9, S_MANA2_1, S_MANA2_2, S_MANA2_3, S_MANA2_4, S_MANA2_5, S_MANA2_6, S_MANA2_7, S_MANA2_8, S_MANA2_9, S_MANA2_10, S_MANA2_11, S_MANA2_12, S_MANA2_13, S_MANA2_14, S_MANA2_15, S_MANA2_16, S_MANA3_1, S_MANA3_2, S_MANA3_3, S_MANA3_4, S_MANA3_5, S_MANA3_6, S_MANA3_7, S_MANA3_8, S_MANA3_9, S_MANA3_10, S_MANA3_11, S_MANA3_12, S_MANA3_13, S_MANA3_14, S_MANA3_15, S_MANA3_16, S_KEY1, S_KEY2, S_KEY3, S_KEY4, S_KEY5, S_KEY6, S_KEY7, S_KEY8, S_KEY9, S_KEYA, S_KEYB, S_SND_WIND1, S_SND_WIND2, S_SND_WATERFALL, S_ETTIN_LOOK1, S_ETTIN_LOOK2, S_ETTIN_CHASE1, S_ETTIN_CHASE2, S_ETTIN_CHASE3, S_ETTIN_CHASE4, S_ETTIN_PAIN1, S_ETTIN_ATK1_1, S_ETTIN_ATK1_2, S_ETTIN_ATK1_3, S_ETTIN_DEATH1_1, S_ETTIN_DEATH1_2, S_ETTIN_DEATH1_3, S_ETTIN_DEATH1_4, S_ETTIN_DEATH1_5, S_ETTIN_DEATH1_6, S_ETTIN_DEATH1_7, S_ETTIN_DEATH1_8, S_ETTIN_DEATH1_9, S_ETTIN_DEATH2_1, S_ETTIN_DEATH2_2, S_ETTIN_DEATH2_3, S_ETTIN_DEATH2_4, S_ETTIN_DEATH2_5, S_ETTIN_DEATH2_6, S_ETTIN_DEATH2_7, S_ETTIN_DEATH2_8, S_ETTIN_DEATH2_9, S_ETTIN_DEATH2_0, S_ETTIN_DEATH2_A, S_ETTIN_DEATH2_B, S_ETTIN_ICE1, S_ETTIN_ICE2, S_ETTIN_MACE1, S_ETTIN_MACE2, S_ETTIN_MACE3, S_ETTIN_MACE4, S_ETTIN_MACE5, S_ETTIN_MACE6, S_ETTIN_MACE7, S_FIRED_SPAWN1, S_FIRED_LOOK1, S_FIRED_LOOK2, S_FIRED_LOOK3, S_FIRED_LOOK4, S_FIRED_LOOK5, S_FIRED_LOOK6, S_FIRED_LOOK7, S_FIRED_LOOK8, S_FIRED_LOOK9, S_FIRED_LOOK0, S_FIRED_LOOKA, S_FIRED_LOOKB, S_FIRED_WALK1, S_FIRED_WALK2, S_FIRED_WALK3, S_FIRED_PAIN1, S_FIRED_ATTACK1, S_FIRED_ATTACK2, S_FIRED_ATTACK3, S_FIRED_ATTACK4, S_FIRED_DEATH1, S_FIRED_DEATH2, S_FIRED_DEATH3, S_FIRED_DEATH4, S_FIRED_XDEATH1, S_FIRED_XDEATH2, S_FIRED_XDEATH3, S_FIRED_ICE1, S_FIRED_ICE2, S_FIRED_CORPSE1, S_FIRED_CORPSE2, S_FIRED_CORPSE3, S_FIRED_CORPSE4, S_FIRED_CORPSE5, S_FIRED_CORPSE6, S_FIRED_RDROP1, S_FIRED_RDEAD1_1, S_FIRED_RDEAD1_2, S_FIRED_RDROP2, S_FIRED_RDEAD2_1, S_FIRED_RDEAD2_2, S_FIRED_RDROP3, S_FIRED_RDEAD3_1, S_FIRED_RDEAD3_2, S_FIRED_RDROP4, S_FIRED_RDEAD4_1, S_FIRED_RDEAD4_2, S_FIRED_RDROP5, S_FIRED_RDEAD5_1, S_FIRED_RDEAD5_2, S_FIRED_FX6_1, S_FIRED_FX6_2, S_FIRED_FX6_3, S_FIRED_FX6_4, S_FIRED_FX6_5, S_ICEGUY_LOOK, S_ICEGUY_DORMANT, S_ICEGUY_WALK1, S_ICEGUY_WALK2, S_ICEGUY_WALK3, S_ICEGUY_WALK4, S_ICEGUY_ATK1, S_ICEGUY_ATK2, S_ICEGUY_ATK3, S_ICEGUY_ATK4, S_ICEGUY_PAIN1, S_ICEGUY_DEATH, S_ICEGUY_FX1, S_ICEGUY_FX2, S_ICEGUY_FX3, S_ICEGUY_FX_X1, S_ICEGUY_FX_X2, S_ICEGUY_FX_X3, S_ICEGUY_FX_X4, S_ICEGUY_FX_X5, S_ICEFX_PUFF1, S_ICEFX_PUFF2, S_ICEFX_PUFF3, S_ICEFX_PUFF4, S_ICEFX_PUFF5, S_ICEGUY_FX2_1, S_ICEGUY_FX2_2, S_ICEGUY_FX2_3, S_ICEGUY_BIT1, S_ICEGUY_BIT2, S_ICEGUY_WISP1_1, S_ICEGUY_WISP1_2, S_ICEGUY_WISP1_3, S_ICEGUY_WISP1_4, S_ICEGUY_WISP1_5, S_ICEGUY_WISP1_6, S_ICEGUY_WISP1_7, S_ICEGUY_WISP1_8, S_ICEGUY_WISP1_9, S_ICEGUY_WISP2_1, S_ICEGUY_WISP2_2, S_ICEGUY_WISP2_3, S_ICEGUY_WISP2_4, S_ICEGUY_WISP2_5, S_ICEGUY_WISP2_6, S_ICEGUY_WISP2_7, S_ICEGUY_WISP2_8, S_ICEGUY_WISP2_9, S_FIGHTER, S_FIGHTER2, S_FIGHTERLOOK, S_FIGHTER_RUN1, S_FIGHTER_RUN2, S_FIGHTER_RUN3, S_FIGHTER_RUN4, S_FIGHTER_ATK1, S_FIGHTER_ATK2, S_FIGHTER_PAIN, S_FIGHTER_PAIN2, S_FIGHTER_DIE1, S_FIGHTER_DIE2, S_FIGHTER_DIE3, S_FIGHTER_DIE4, S_FIGHTER_DIE5, S_FIGHTER_DIE6, S_FIGHTER_DIE7, S_FIGHTER_XDIE1, S_FIGHTER_XDIE2, S_FIGHTER_XDIE3, S_FIGHTER_XDIE4, S_FIGHTER_XDIE5, S_FIGHTER_XDIE6, S_FIGHTER_XDIE7, S_FIGHTER_XDIE8, S_FIGHTER_ICE, S_FIGHTER_ICE2, S_CLERIC, S_CLERIC2, S_CLERICLOOK, S_CLERIC_RUN1, S_CLERIC_RUN2, S_CLERIC_RUN3, S_CLERIC_RUN4, S_CLERIC_ATK1, S_CLERIC_ATK2, S_CLERIC_ATK3, S_CLERIC_PAIN, S_CLERIC_PAIN2, S_CLERIC_DIE1, S_CLERIC_DIE2, S_CLERIC_DIE3, S_CLERIC_DIE4, S_CLERIC_DIE5, S_CLERIC_DIE6, S_CLERIC_DIE7, S_CLERIC_DIE8, S_CLERIC_DIE9, S_CLERIC_XDIE1, S_CLERIC_XDIE2, S_CLERIC_XDIE3, S_CLERIC_XDIE4, S_CLERIC_XDIE5, S_CLERIC_XDIE6, S_CLERIC_XDIE7, S_CLERIC_XDIE8, S_CLERIC_XDIE9, S_CLERIC_XDIE10, S_CLERIC_ICE, S_CLERIC_ICE2, S_MAGE, S_MAGE2, S_MAGELOOK, S_MAGE_RUN1, S_MAGE_RUN2, S_MAGE_RUN3, S_MAGE_RUN4, S_MAGE_ATK1, S_MAGE_ATK2, S_MAGE_PAIN, S_MAGE_PAIN2, S_MAGE_DIE1, S_MAGE_DIE2, S_MAGE_DIE3, S_MAGE_DIE4, S_MAGE_DIE5, S_MAGE_DIE6, S_MAGE_DIE7, S_MAGE_XDIE1, S_MAGE_XDIE2, S_MAGE_XDIE3, S_MAGE_XDIE4, S_MAGE_XDIE5, S_MAGE_XDIE6, S_MAGE_XDIE7, S_MAGE_XDIE8, S_MAGE_XDIE9, S_MAGE_ICE, S_MAGE_ICE2, S_SORC_SPAWN1, S_SORC_SPAWN2, S_SORC_LOOK1, S_SORC_WALK1, S_SORC_WALK2, S_SORC_WALK3, S_SORC_WALK4, S_SORC_PAIN1, S_SORC_PAIN2, S_SORC_ATK2_1, S_SORC_ATK2_2, S_SORC_ATK2_3, S_SORC_ATTACK1, S_SORC_ATTACK2, S_SORC_ATTACK3, S_SORC_ATTACK4, S_SORC_ATTACK5, S_SORC_DIE1, S_SORC_DIE2, S_SORC_DIE3, S_SORC_DIE4, S_SORC_DIE5, S_SORC_DIE6, S_SORC_DIE7, S_SORC_DIE8, S_SORC_DIE9, S_SORC_DIE0, S_SORC_DIEA, S_SORC_DIEB, S_SORC_DIEC, S_SORC_DIED, S_SORC_DIEE, S_SORC_DIEF, S_SORC_DIEG, S_SORC_DIEH, S_SORC_DIEI, S_SORCBALL1_1, S_SORCBALL1_2, S_SORCBALL1_3, S_SORCBALL1_4, S_SORCBALL1_5, S_SORCBALL1_6, S_SORCBALL1_7, S_SORCBALL1_8, S_SORCBALL1_9, S_SORCBALL1_0, S_SORCBALL1_A, S_SORCBALL1_B, S_SORCBALL1_C, S_SORCBALL1_D, S_SORCBALL1_E, S_SORCBALL1_F, S_SORCBALL1_D1, S_SORCBALL1_D2, S_SORCBALL1_D5, S_SORCBALL1_D6, S_SORCBALL1_D7, S_SORCBALL1_D8, S_SORCBALL1_D9, S_SORCBALL2_1, S_SORCBALL2_2, S_SORCBALL2_3, S_SORCBALL2_4, S_SORCBALL2_5, S_SORCBALL2_6, S_SORCBALL2_7, S_SORCBALL2_8, S_SORCBALL2_9, S_SORCBALL2_0, S_SORCBALL2_A, S_SORCBALL2_B, S_SORCBALL2_C, S_SORCBALL2_D, S_SORCBALL2_E, S_SORCBALL2_F, S_SORCBALL2_D1, S_SORCBALL2_D2, S_SORCBALL2_D5, S_SORCBALL2_D6, S_SORCBALL2_D7, S_SORCBALL2_D8, S_SORCBALL2_D9, S_SORCBALL3_1, S_SORCBALL3_2, S_SORCBALL3_3, S_SORCBALL3_4, S_SORCBALL3_5, S_SORCBALL3_6, S_SORCBALL3_7, S_SORCBALL3_8, S_SORCBALL3_9, S_SORCBALL3_0, S_SORCBALL3_A, S_SORCBALL3_B, S_SORCBALL3_C, S_SORCBALL3_D, S_SORCBALL3_E, S_SORCBALL3_F, S_SORCBALL3_D1, S_SORCBALL3_D2, S_SORCBALL3_D5, S_SORCBALL3_D6, S_SORCBALL3_D7, S_SORCBALL3_D8, S_SORCBALL3_D9, S_SORCFX1_1, S_SORCFX1_2, S_SORCFX1_3, S_SORCFX1_4, S_SORCFX1_D1, S_SORCFX1_D2, S_SORCFX1_D3, S_SORCFX2_SPLIT1, S_SORCFX2_ORBIT1, S_SORCFX2_ORBIT2, S_SORCFX2_ORBIT3, S_SORCFX2_ORBIT4, S_SORCFX2_ORBIT5, S_SORCFX2_ORBIT6, S_SORCFX2_ORBIT7, S_SORCFX2_ORBIT8, S_SORCFX2_ORBIT9, S_SORCFX2_ORBIT0, S_SORCFX2_ORBITA, S_SORCFX2_ORBITB, S_SORCFX2_ORBITC, S_SORCFX2_ORBITD, S_SORCFX2_ORBITE, S_SORCFX2_ORBITF, S_SORCFX2T1, S_SORCFX3_1, S_SORCFX3_2, S_SORCFX3_3, S_BISHMORPH1, S_BISHMORPHA, S_BISHMORPHB, S_BISHMORPHC, S_BISHMORPHD, S_BISHMORPHE, S_BISHMORPHF, S_BISHMORPHG, S_BISHMORPHH, S_BISHMORPHI, S_BISHMORPHJ, S_SORCFX3_EXP1, S_SORCFX3_EXP2, S_SORCFX3_EXP3, S_SORCFX3_EXP4, S_SORCFX3_EXP5, S_SORCFX4_1, S_SORCFX4_2, S_SORCFX4_3, S_SORCFX4_D1, S_SORCFX4_D2, S_SORCFX4_D3, S_SORCFX4_D4, S_SORCFX4_D5, S_SORCSPARK1, S_SORCSPARK2, S_SORCSPARK3, S_SORCSPARK4, S_SORCSPARK5, S_SORCSPARK6, S_SORCSPARK7, S_BLASTEFFECT1, S_BLASTEFFECT2, S_BLASTEFFECT3, S_BLASTEFFECT4, S_BLASTEFFECT5, S_BLASTEFFECT6, S_BLASTEFFECT7, S_BLASTEFFECT8, S_BLASTEFFECT9, S_WATERDRIP1, S_KORAX_LOOK1, S_KORAX_CHASE1, S_KORAX_CHASE2, S_KORAX_CHASE3, S_KORAX_CHASE4, S_KORAX_CHASE5, S_KORAX_CHASE6, S_KORAX_CHASE7, S_KORAX_CHASE8, S_KORAX_CHASE9, S_KORAX_CHASE0, S_KORAX_CHASEA, S_KORAX_CHASEB, S_KORAX_CHASEC, S_KORAX_CHASED, S_KORAX_CHASEE, S_KORAX_CHASEF, S_KORAX_PAIN1, S_KORAX_PAIN2, S_KORAX_ATTACK1, S_KORAX_ATTACK2, S_KORAX_MISSILE1, S_KORAX_MISSILE2, S_KORAX_MISSILE3, S_KORAX_COMMAND1, S_KORAX_COMMAND2, S_KORAX_COMMAND3, S_KORAX_COMMAND4, S_KORAX_COMMAND5, S_KORAX_DEATH1, S_KORAX_DEATH2, S_KORAX_DEATH3, S_KORAX_DEATH4, S_KORAX_DEATH5, S_KORAX_DEATH6, S_KORAX_DEATH7, S_KORAX_DEATH8, S_KORAX_DEATH9, S_KORAX_DEATH0, S_KORAX_DEATHA, S_KORAX_DEATHB, S_KORAX_DEATHC, S_KORAX_DEATHD, S_KSPIRIT_ROAM1, S_KSPIRIT_ROAM2, S_KSPIRIT_DEATH1, S_KSPIRIT_DEATH2, S_KSPIRIT_DEATH3, S_KSPIRIT_DEATH4, S_KSPIRIT_DEATH5, S_KSPIRIT_DEATH6, S_KBOLT1, S_KBOLT2, S_KBOLT3, S_KBOLT4, S_KBOLT5, S_KBOLT6, S_KBOLT7, S_SPAWNBATS1, S_SPAWNBATS2, S_SPAWNBATS3, S_SPAWNBATS_OFF, S_BAT1, S_BAT2, S_BAT3, S_BAT_DEATH, NUMSTATES } statenum_t; typedef struct { spritenum_t sprite; int frame; int tics; void (*action) (); statenum_t nextstate; int misc1, misc2; } state_t; extern state_t states[NUMSTATES]; extern const char *sprnames[]; typedef enum { MT_MAPSPOT, MT_MAPSPOTGRAVITY, MT_FIREBALL1, MT_ARROW, MT_DART, MT_POISONDART, MT_RIPPERBALL, MT_PROJECTILE_BLADE, MT_ICESHARD, MT_FLAME_SMALL_TEMP, MT_FLAME_LARGE_TEMP, MT_FLAME_SMALL, MT_FLAME_LARGE, MT_HEALINGBOTTLE, MT_HEALTHFLASK, MT_ARTIFLY, MT_ARTIINVULNERABILITY, MT_SUMMONMAULATOR, MT_SUMMON_FX, MT_THRUSTFLOOR_UP, MT_THRUSTFLOOR_DOWN, MT_TELEPORTOTHER, MT_TELOTHER_FX1, MT_TELOTHER_FX2, MT_TELOTHER_FX3, MT_TELOTHER_FX4, MT_TELOTHER_FX5, MT_DIRT1, MT_DIRT2, MT_DIRT3, MT_DIRT4, MT_DIRT5, MT_DIRT6, MT_DIRTCLUMP, MT_ROCK1, MT_ROCK2, MT_ROCK3, MT_FOGSPAWNER, MT_FOGPATCHS, MT_FOGPATCHM, MT_FOGPATCHL, MT_QUAKE_FOCUS, MT_SGSHARD1, MT_SGSHARD2, MT_SGSHARD3, MT_SGSHARD4, MT_SGSHARD5, MT_SGSHARD6, MT_SGSHARD7, MT_SGSHARD8, MT_SGSHARD9, MT_SGSHARD0, MT_ARTIEGG, MT_EGGFX, MT_ARTISUPERHEAL, MT_ZWINGEDSTATUENOSKULL, MT_ZGEMPEDESTAL, MT_ARTIPUZZSKULL, MT_ARTIPUZZGEMBIG, MT_ARTIPUZZGEMRED, MT_ARTIPUZZGEMGREEN1, MT_ARTIPUZZGEMGREEN2, MT_ARTIPUZZGEMBLUE1, MT_ARTIPUZZGEMBLUE2, MT_ARTIPUZZBOOK1, MT_ARTIPUZZBOOK2, MT_ARTIPUZZSKULL2, MT_ARTIPUZZFWEAPON, MT_ARTIPUZZCWEAPON, MT_ARTIPUZZMWEAPON, MT_ARTIPUZZGEAR, MT_ARTIPUZZGEAR2, MT_ARTIPUZZGEAR3, MT_ARTIPUZZGEAR4, MT_ARTITORCH, MT_FIREBOMB, MT_ARTITELEPORT, MT_ARTIPOISONBAG, MT_POISONBAG, MT_POISONCLOUD, MT_THROWINGBOMB, MT_SPEEDBOOTS, MT_BOOSTMANA, MT_BOOSTARMOR, MT_BLASTRADIUS, MT_HEALRADIUS, MT_SPLASH, MT_SPLASHBASE, MT_LAVASPLASH, MT_LAVASMOKE, MT_SLUDGECHUNK, MT_SLUDGESPLASH, MT_MISC0, MT_MISC1, MT_MISC2, MT_MISC3, MT_MISC4, MT_MISC5, MT_MISC6, MT_MISC7, MT_MISC8, MT_TREEDESTRUCTIBLE, MT_MISC9, MT_MISC10, MT_MISC11, MT_MISC12, MT_MISC13, MT_MISC14, MT_MISC15, MT_MISC16, MT_MISC17, MT_MISC18, MT_MISC19, MT_MISC20, MT_MISC21, MT_MISC22, MT_MISC23, MT_MISC24, MT_MISC25, MT_MISC26, MT_MISC27, MT_MISC28, 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_POTTERY1, MT_POTTERY2, MT_POTTERY3, MT_POTTERYBIT1, MT_MISC77, MT_ZLYNCHED_NOHEART, MT_MISC78, MT_CORPSEBIT, MT_CORPSEBLOODDRIP, MT_BLOODPOOL, MT_MISC79, MT_MISC80, MT_LEAF1, MT_LEAF2, MT_ZTWINEDTORCH, MT_ZTWINEDTORCH_UNLIT, MT_BRIDGE, MT_BRIDGEBALL, MT_ZWALLTORCH, MT_ZWALLTORCH_UNLIT, MT_ZBARREL, MT_ZSHRUB1, MT_ZSHRUB2, MT_ZBUCKET, MT_ZPOISONSHROOM, MT_ZFIREBULL, MT_ZFIREBULL_UNLIT, MT_FIRETHING, MT_BRASSTORCH, MT_ZSUITOFARMOR, MT_ZARMORCHUNK, MT_ZBELL, MT_ZBLUE_CANDLE, MT_ZIRON_MAIDEN, MT_ZXMAS_TREE, MT_ZCAULDRON, MT_ZCAULDRON_UNLIT, MT_ZCHAINBIT32, MT_ZCHAINBIT64, MT_ZCHAINEND_HEART, MT_ZCHAINEND_HOOK1, MT_ZCHAINEND_HOOK2, MT_ZCHAINEND_SPIKE, MT_ZCHAINEND_SKULL, MT_TABLE_SHIT1, MT_TABLE_SHIT2, MT_TABLE_SHIT3, MT_TABLE_SHIT4, MT_TABLE_SHIT5, MT_TABLE_SHIT6, MT_TABLE_SHIT7, MT_TABLE_SHIT8, MT_TABLE_SHIT9, MT_TABLE_SHIT10, MT_TFOG, MT_MISC81, MT_TELEPORTMAN, MT_PUNCHPUFF, MT_FW_AXE, MT_AXEPUFF, MT_AXEPUFF_GLOW, MT_AXEBLOOD, MT_FW_HAMMER, MT_HAMMER_MISSILE, MT_HAMMERPUFF, MT_FSWORD_MISSILE, MT_FSWORD_FLAME, MT_CW_SERPSTAFF, MT_CSTAFF_MISSILE, MT_CSTAFFPUFF, MT_CW_FLAME, MT_CFLAMEFLOOR, MT_FLAMEPUFF, MT_FLAMEPUFF2, MT_CIRCLEFLAME, MT_CFLAME_MISSILE, MT_HOLY_FX, MT_HOLY_TAIL, MT_HOLY_PUFF, MT_HOLY_MISSILE, MT_HOLY_MISSILE_PUFF, MT_MWANDPUFF, MT_MWANDSMOKE, MT_MWAND_MISSILE, MT_MW_LIGHTNING, MT_LIGHTNING_CEILING, MT_LIGHTNING_FLOOR, MT_LIGHTNING_ZAP, MT_MSTAFF_FX, MT_MSTAFF_FX2, MT_FW_SWORD1, MT_FW_SWORD2, MT_FW_SWORD3, MT_CW_HOLY1, MT_CW_HOLY2, MT_CW_HOLY3, MT_MW_STAFF1, MT_MW_STAFF2, MT_MW_STAFF3, MT_SNOUTPUFF, MT_MW_CONE, MT_SHARDFX1, MT_BLOOD, MT_BLOODSPLATTER, MT_GIBS, MT_PLAYER_FIGHTER, MT_BLOODYSKULL, MT_PLAYER_SPEED, MT_ICECHUNK, MT_PLAYER_CLERIC, MT_PLAYER_MAGE, MT_PIGPLAYER, MT_PIG, MT_CENTAUR, MT_CENTAURLEADER, MT_CENTAUR_FX, MT_CENTAUR_SHIELD, MT_CENTAUR_SWORD, MT_DEMON, MT_DEMONCHUNK1, MT_DEMONCHUNK2, MT_DEMONCHUNK3, MT_DEMONCHUNK4, MT_DEMONCHUNK5, MT_DEMONFX1, MT_DEMON2, MT_DEMON2CHUNK1, MT_DEMON2CHUNK2, MT_DEMON2CHUNK3, MT_DEMON2CHUNK4, MT_DEMON2CHUNK5, MT_DEMON2FX1, MT_WRAITHB, MT_WRAITH, MT_WRAITHFX1, MT_WRAITHFX2, MT_WRAITHFX3, MT_WRAITHFX4, MT_WRAITHFX5, MT_MINOTAUR, MT_MNTRFX1, MT_MNTRFX2, MT_MNTRFX3, MT_MNTRSMOKE, MT_MNTRSMOKEEXIT, MT_SERPENT, MT_SERPENTLEADER, MT_SERPENTFX, MT_SERPENT_HEAD, MT_SERPENT_GIB1, MT_SERPENT_GIB2, MT_SERPENT_GIB3, MT_BISHOP, MT_BISHOP_PUFF, MT_BISHOPBLUR, MT_BISHOPPAINBLUR, MT_BISH_FX, MT_DRAGON, MT_DRAGON_FX, MT_DRAGON_FX2, MT_ARMOR_1, MT_ARMOR_2, MT_ARMOR_3, MT_ARMOR_4, MT_MANA1, MT_MANA2, MT_MANA3, MT_KEY1, MT_KEY2, MT_KEY3, MT_KEY4, MT_KEY5, MT_KEY6, MT_KEY7, MT_KEY8, MT_KEY9, MT_KEYA, MT_KEYB, MT_SOUNDWIND, MT_SOUNDWATERFALL, MT_ETTIN, MT_ETTIN_MACE, MT_FIREDEMON, MT_FIREDEMON_SPLOTCH1, MT_FIREDEMON_SPLOTCH2, MT_FIREDEMON_FX1, MT_FIREDEMON_FX2, MT_FIREDEMON_FX3, MT_FIREDEMON_FX4, MT_FIREDEMON_FX5, MT_FIREDEMON_FX6, MT_ICEGUY, MT_ICEGUY_FX, MT_ICEFX_PUFF, MT_ICEGUY_FX2, MT_ICEGUY_BIT, MT_ICEGUY_WISP1, MT_ICEGUY_WISP2, MT_FIGHTER_BOSS, MT_CLERIC_BOSS, MT_MAGE_BOSS, MT_SORCBOSS, MT_SORCBALL1, MT_SORCBALL2, MT_SORCBALL3, MT_SORCFX1, MT_SORCFX2, MT_SORCFX2_T1, MT_SORCFX3, MT_SORCFX3_EXPLOSION, MT_SORCFX4, MT_SORCSPARK1, MT_BLASTEFFECT, MT_WATER_DRIP, MT_KORAX, MT_KORAX_SPIRIT1, MT_KORAX_SPIRIT2, MT_KORAX_SPIRIT3, MT_KORAX_SPIRIT4, MT_KORAX_SPIRIT5, MT_KORAX_SPIRIT6, MT_DEMON_MASH, MT_DEMON2_MASH, MT_ETTIN_MASH, MT_CENTAUR_MASH, MT_KORAX_BOLT, MT_BAT_SPAWNER, MT_BAT, NUMMOBJTYPES } mobjtype_t; typedef struct { int doomednum; int spawnstate; int spawnhealth; int seestate; int seesound; int reactiontime; int attacksound; int painstate; int painchance; int painsound; int meleestate; int missilestate; int crashstate; int deathstate; int xdeathstate; int deathsound; int speed; int radius; int height; int mass; int damage; int activesound; int flags; int flags2; } mobjinfo_t; extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; crispy-doom-crispy-doom-5.6.4/src/hexen/m_random.c000066400000000000000000000050131360717211000220710ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "m_random.h" // This is the new flat distribution table static const unsigned char rndtable[256] = { 201, 1, 243, 19, 18, 42, 183, 203, 101, 123, 154, 137, 34, 118, 10, 216, 135, 246, 0, 107, 133, 229, 35, 113, 177, 211, 110, 17, 139, 84, 251, 235, 182, 166, 161, 230, 143, 91, 24, 81, 22, 94, 7, 51, 232, 104, 122, 248, 175, 138, 127, 171, 222, 213, 44, 16, 9, 33, 88, 102, 170, 150, 136, 114, 62, 3, 142, 237, 6, 252, 249, 56, 74, 30, 13, 21, 180, 199, 32, 132, 187, 234, 78, 210, 46, 131, 197, 8, 206, 244, 73, 4, 236, 178, 195, 70, 121, 97, 167, 217, 103, 40, 247, 186, 105, 39, 95, 163, 99, 149, 253, 29, 119, 83, 254, 26, 202, 65, 130, 155, 60, 64, 184, 106, 221, 93, 164, 196, 112, 108, 179, 141, 54, 109, 11, 126, 75, 165, 191, 227, 87, 225, 156, 15, 98, 162, 116, 79, 169, 140, 190, 205, 168, 194, 41, 250, 27, 20, 14, 241, 50, 214, 72, 192, 220, 233, 67, 148, 96, 185, 176, 181, 215, 207, 172, 85, 89, 90, 209, 128, 124, 2, 55, 173, 66, 152, 47, 129, 59, 43, 159, 240, 239, 12, 189, 212, 144, 28, 200, 77, 219, 198, 134, 228, 45, 92, 125, 151, 5, 53, 255, 52, 68, 245, 160, 158, 61, 86, 58, 82, 117, 37, 242, 145, 69, 188, 115, 76, 63, 100, 49, 111, 153, 80, 38, 57, 174, 224, 71, 231, 23, 25, 48, 218, 120, 147, 208, 36, 226, 223, 193, 238, 157, 204, 146, 31 }; int rndindex = 0; int prndindex = 0; /* =============== = = M_Random = = Returns a 0-255 number = =============== */ int P_Random(void) { prndindex = (prndindex + 1) & 0xff; return rndtable[prndindex]; } int M_Random(void) { rndindex = (rndindex + 1) & 0xff; return rndtable[rndindex]; } void M_ClearRandom(void) { rndindex = prndindex = 0; } // inspired by the same routine in Eternity, thanks haleyjd int P_SubRandom (void) { int r = P_Random(); return r - P_Random(); } crispy-doom-crispy-doom-5.6.4/src/hexen/m_random.h000066400000000000000000000020531360717211000220770ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef HEXEN_M_RANDOM_H #define HEXEN_M_RANDOM_H // Most damage defined using HITDICE #define HITDICE(a) ((1+(P_Random()&7))*a) int M_Random(void); // returns a number from 0 to 255 int P_Random(void); // as M_Random, but used only by the play simulation void M_ClearRandom(void); // fix randoms for demos extern int rndindex; // Defined version of P_Random() - P_Random() int P_SubRandom (void); #endif // HEXEN_M_RANDOM_H crispy-doom-crispy-doom-5.6.4/src/hexen/mn_menu.c000066400000000000000000001361051360717211000217420ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include #include "h2def.h" #include "doomkeys.h" #include "i_input.h" #include "i_system.h" #include "i_swap.h" #include "i_video.h" #include "m_controls.h" #include "m_misc.h" #include "p_local.h" #include "r_local.h" #include "s_sound.h" #include "v_video.h" // MACROS ------------------------------------------------------------------ #define LEFT_DIR 0 #define RIGHT_DIR 1 #define ITEM_HEIGHT 20 #define SELECTOR_XOFFSET (-28) #define SELECTOR_YOFFSET (-1) #define SLOTTEXTLEN 16 #define ASCII_CURSOR '[' // TYPES ------------------------------------------------------------------- typedef enum { ITT_EMPTY, ITT_EFUNC, ITT_LRFUNC, ITT_SETMENU, ITT_INERT } ItemType_t; typedef enum { MENU_MAIN, MENU_CLASS, MENU_SKILL, MENU_OPTIONS, MENU_OPTIONS2, MENU_FILES, MENU_LOAD, MENU_SAVE, MENU_NONE } MenuType_t; typedef struct { ItemType_t type; const char *text; void (*func) (int option); int option; MenuType_t menu; } MenuItem_t; typedef struct { int x; int y; void (*drawFunc) (void); int itemCount; MenuItem_t *items; int oldItPos; MenuType_t prevMenu; } Menu_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void InitFonts(void); static void SetMenu(MenuType_t menu); static void SCQuitGame(int option); static void SCClass(int option); static void SCSkill(int option); static void SCMouseSensi(int option); static void SCSfxVolume(int option); static void SCMusicVolume(int option); static void SCScreenSize(int option); static boolean SCNetCheck(int option); static void SCNetCheck2(int option); static void SCLoadGame(int option); static void SCSaveGame(int option); static void SCMessages(int option); static void SCEndGame(int option); static void SCInfo(int option); static void DrawMainMenu(void); static void DrawClassMenu(void); static void DrawSkillMenu(void); static void DrawOptionsMenu(void); static void DrawOptions2Menu(void); static void DrawFileSlots(Menu_t * menu); static void DrawFilesMenu(void); static void MN_DrawInfo(void); static void DrawLoadMenu(void); static void DrawSaveMenu(void); static void DrawSlider(Menu_t * menu, int item, int width, int slot); void MN_LoadSlotText(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern int detailLevel; extern boolean gamekeydown[256]; // The NUMKEYS macro is local to g_game // PUBLIC DATA DEFINITIONS ------------------------------------------------- boolean MenuActive; int InfoType; int messageson = true; boolean mn_SuicideConsole; boolean demoextend; // from h2def.h // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int FontABaseLump; static int FontAYellowBaseLump; static int FontBBaseLump; static int MauloBaseLump; static Menu_t *CurrentMenu; static int CurrentItPos; static int MenuPClass; static int MenuTime; static boolean soundchanged; boolean askforquit; static int typeofask; static boolean FileMenuKeySteal; static boolean slottextloaded; static char SlotText[6][SLOTTEXTLEN + 2]; static char oldSlotText[SLOTTEXTLEN + 2]; static int SlotStatus[6]; static int slotptr; static int currentSlot; static int quicksave; static int quickload; static MenuItem_t MainItems[] = { {ITT_SETMENU, "NEW GAME", SCNetCheck2, 1, MENU_CLASS}, {ITT_SETMENU, "OPTIONS", NULL, 0, MENU_OPTIONS}, {ITT_SETMENU, "GAME FILES", NULL, 0, MENU_FILES}, {ITT_EFUNC, "INFO", SCInfo, 0, MENU_NONE}, {ITT_EFUNC, "QUIT GAME", SCQuitGame, 0, MENU_NONE} }; static Menu_t MainMenu = { 110, 56, DrawMainMenu, 5, MainItems, 0, MENU_NONE }; static MenuItem_t ClassItems[] = { {ITT_EFUNC, "FIGHTER", SCClass, 0, MENU_NONE}, {ITT_EFUNC, "CLERIC", SCClass, 1, MENU_NONE}, {ITT_EFUNC, "MAGE", SCClass, 2, MENU_NONE} }; static Menu_t ClassMenu = { 66, 66, DrawClassMenu, 3, ClassItems, 0, MENU_MAIN }; static MenuItem_t FilesItems[] = { {ITT_SETMENU, "LOAD GAME", SCNetCheck2, 2, MENU_LOAD}, {ITT_SETMENU, "SAVE GAME", NULL, 0, MENU_SAVE} }; static Menu_t FilesMenu = { 110, 60, DrawFilesMenu, 2, FilesItems, 0, MENU_MAIN }; static MenuItem_t LoadItems[] = { {ITT_EFUNC, NULL, SCLoadGame, 0, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 1, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 2, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 3, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 4, MENU_NONE}, {ITT_EFUNC, NULL, SCLoadGame, 5, MENU_NONE} }; static Menu_t LoadMenu = { 70, 30, DrawLoadMenu, 6, LoadItems, 0, MENU_FILES }; static MenuItem_t SaveItems[] = { {ITT_EFUNC, NULL, SCSaveGame, 0, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 1, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 2, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 3, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 4, MENU_NONE}, {ITT_EFUNC, NULL, SCSaveGame, 5, MENU_NONE} }; static Menu_t SaveMenu = { 70, 30, DrawSaveMenu, 6, SaveItems, 0, MENU_FILES }; static MenuItem_t SkillItems[] = { {ITT_EFUNC, NULL, SCSkill, sk_baby, MENU_NONE}, {ITT_EFUNC, NULL, SCSkill, sk_easy, MENU_NONE}, {ITT_EFUNC, NULL, SCSkill, sk_medium, MENU_NONE}, {ITT_EFUNC, NULL, SCSkill, sk_hard, MENU_NONE}, {ITT_EFUNC, NULL, SCSkill, sk_nightmare, MENU_NONE} }; static Menu_t SkillMenu = { 120, 44, DrawSkillMenu, 5, SkillItems, 2, MENU_CLASS }; static MenuItem_t OptionsItems[] = { {ITT_EFUNC, "END GAME", SCEndGame, 0, MENU_NONE}, {ITT_EFUNC, "MESSAGES : ", SCMessages, 0, MENU_NONE}, {ITT_LRFUNC, "MOUSE SENSITIVITY", SCMouseSensi, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE}, {ITT_SETMENU, "MORE...", NULL, 0, MENU_OPTIONS2} }; static Menu_t OptionsMenu = { 88, 30, DrawOptionsMenu, 5, OptionsItems, 0, MENU_MAIN }; static MenuItem_t Options2Items[] = { {ITT_LRFUNC, "SCREEN SIZE", SCScreenSize, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE}, {ITT_LRFUNC, "SFX VOLUME", SCSfxVolume, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE}, {ITT_LRFUNC, "MUSIC VOLUME", SCMusicVolume, 0, MENU_NONE}, {ITT_EMPTY, NULL, NULL, 0, MENU_NONE} }; static Menu_t Options2Menu = { 90, 20, DrawOptions2Menu, 6, Options2Items, 0, MENU_OPTIONS }; static Menu_t *Menus[] = { &MainMenu, &ClassMenu, &SkillMenu, &OptionsMenu, &Options2Menu, &FilesMenu, &LoadMenu, &SaveMenu }; // [crispy] intermediate gamma levels static const char *GammaText[] = { TXT_GAMMA_LEVEL_OFF, TXT_GAMMA_LEVEL_05, TXT_GAMMA_LEVEL_1, TXT_GAMMA_LEVEL_15, TXT_GAMMA_LEVEL_2, TXT_GAMMA_LEVEL_25, TXT_GAMMA_LEVEL_3, TXT_GAMMA_LEVEL_35, TXT_GAMMA_LEVEL_4 }; // CODE -------------------------------------------------------------------- //--------------------------------------------------------------------------- // // PROC MN_Init // //--------------------------------------------------------------------------- void MN_Init(void) { InitFonts(); MenuActive = false; // messageson = true; // Set by defaults in .CFG MauloBaseLump = W_GetNumForName("FBULA0"); // ("M_SKL00"); } //--------------------------------------------------------------------------- // // PROC InitFonts // //--------------------------------------------------------------------------- static void InitFonts(void) { FontABaseLump = W_GetNumForName("FONTA_S") + 1; FontAYellowBaseLump = W_GetNumForName("FONTAY_S") + 1; FontBBaseLump = W_GetNumForName("FONTB_S") + 1; } //--------------------------------------------------------------------------- // // PROC MN_DrTextA // // Draw text using font A. // //--------------------------------------------------------------------------- void MN_DrTextA(const char *text, int x, int y) { char c; patch_t *p; while ((c = *text++) != 0) { if (c < 33) { x += 5; } else { p = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); V_DrawPatch(x, y, p); x += SHORT(p->width) - 1; } } } //========================================================================== // // MN_DrTextAYellow // //========================================================================== void MN_DrTextAYellow(const char *text, int x, int y) { char c; patch_t *p; while ((c = *text++) != 0) { if (c < 33) { x += 5; } else { p = W_CacheLumpNum(FontAYellowBaseLump + c - 33, PU_CACHE); V_DrawPatch(x, y, p); x += SHORT(p->width) - 1; } } } //--------------------------------------------------------------------------- // // FUNC MN_TextAWidth // // Returns the pixel width of a string using font A. // //--------------------------------------------------------------------------- int MN_TextAWidth(const char *text) { char c; int width; patch_t *p; width = 0; while ((c = *text++) != 0) { if (c < 33) { width += 5; } else { p = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE); width += SHORT(p->width) - 1; } } return (width); } //--------------------------------------------------------------------------- // // PROC MN_DrTextB // // Draw text using font B. // //--------------------------------------------------------------------------- void MN_DrTextB(const char *text, int x, int y) { char c; patch_t *p; while ((c = *text++) != 0) { if (c < 33) { x += 8; } else { p = W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE); V_DrawPatch(x, y, p); x += SHORT(p->width) - 1; } } } //--------------------------------------------------------------------------- // // FUNC MN_TextBWidth // // Returns the pixel width of a string using font B. // //--------------------------------------------------------------------------- int MN_TextBWidth(const char *text) { char c; int width; patch_t *p; width = 0; while ((c = *text++) != 0) { if (c < 33) { width += 5; } else { p = W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE); width += SHORT(p->width) - 1; } } return (width); } //--------------------------------------------------------------------------- // // PROC MN_Ticker // //--------------------------------------------------------------------------- void MN_Ticker(void) { if (MenuActive == false) { return; } MenuTime++; } //--------------------------------------------------------------------------- // // PROC MN_Drawer // //--------------------------------------------------------------------------- const char *QuitEndMsg[] = { "ARE YOU SURE YOU WANT TO QUIT?", "ARE YOU SURE YOU WANT TO END THE GAME?", "DO YOU WANT TO QUICKSAVE THE GAME NAMED", "DO YOU WANT TO QUICKLOAD THE GAME NAMED", "ARE YOU SURE YOU WANT TO SUICIDE?" }; void MN_Drawer(void) { int i; int x; int y; MenuItem_t *item; const char *selName; if (MenuActive == false) { if (askforquit) { MN_DrTextA(QuitEndMsg[typeofask - 1], 160 - MN_TextAWidth(QuitEndMsg[typeofask - 1]) / 2, 80); if (typeofask == 3) { MN_DrTextA(SlotText[quicksave - 1], 160 - MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90); MN_DrTextA("?", 160 + MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90); } if (typeofask == 4) { MN_DrTextA(SlotText[quickload - 1], 160 - MN_TextAWidth(SlotText[quickload - 1]) / 2, 90); MN_DrTextA("?", 160 + MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90); } UpdateState |= I_FULLSCRN; } return; } else { UpdateState |= I_FULLSCRN; if (InfoType) { MN_DrawInfo(); return; } if (screenblocks < 10) { BorderNeedRefresh = true; } if (CurrentMenu->drawFunc != NULL) { CurrentMenu->drawFunc(); } x = CurrentMenu->x; y = CurrentMenu->y; item = CurrentMenu->items; for (i = 0; i < CurrentMenu->itemCount; i++) { if (item->type != ITT_EMPTY && item->text) { MN_DrTextB(item->text, x, y); } y += ITEM_HEIGHT; item++; } y = CurrentMenu->y + (CurrentItPos * ITEM_HEIGHT) + SELECTOR_YOFFSET; selName = MenuTime & 16 ? "M_SLCTR1" : "M_SLCTR2"; V_DrawPatch(x + SELECTOR_XOFFSET, y, W_CacheLumpName(selName, PU_CACHE)); } } //--------------------------------------------------------------------------- // // PROC DrawMainMenu // //--------------------------------------------------------------------------- static void DrawMainMenu(void) { int frame; frame = (MenuTime / 5) % 7; V_DrawPatch(88, 0, W_CacheLumpName("M_HTIC", PU_CACHE)); // Old Gold skull positions: (40, 10) and (232, 10) V_DrawPatch(37, 80, W_CacheLumpNum(MauloBaseLump + (frame + 2) % 7, PU_CACHE)); V_DrawPatch(278, 80, W_CacheLumpNum(MauloBaseLump + frame, PU_CACHE)); } //========================================================================== // // DrawClassMenu // //========================================================================== static void DrawClassMenu(void) { pclass_t class; static const char *boxLumpName[3] = { "m_fbox", "m_cbox", "m_mbox" }; static const char *walkLumpName[3] = { "m_fwalk1", "m_cwalk1", "m_mwalk1" }; MN_DrTextB("CHOOSE CLASS:", 34, 24); class = (pclass_t) CurrentMenu->items[CurrentItPos].option; V_DrawPatch(174, 8, W_CacheLumpName(boxLumpName[class], PU_CACHE)); V_DrawPatch(174 + 24, 8 + 12, W_CacheLumpNum(W_GetNumForName(walkLumpName[class]) + ((MenuTime >> 3) & 3), PU_CACHE)); } //--------------------------------------------------------------------------- // // PROC DrawSkillMenu // //--------------------------------------------------------------------------- static void DrawSkillMenu(void) { MN_DrTextB("CHOOSE SKILL LEVEL:", 74, 16); } //--------------------------------------------------------------------------- // // PROC DrawFilesMenu // //--------------------------------------------------------------------------- static void DrawFilesMenu(void) { // clear out the quicksave/quickload stuff quicksave = 0; quickload = 0; P_ClearMessage(&players[consoleplayer]); } //--------------------------------------------------------------------------- // // PROC DrawLoadMenu // //--------------------------------------------------------------------------- static void DrawLoadMenu(void) { MN_DrTextB("LOAD GAME", 160 - MN_TextBWidth("LOAD GAME") / 2, 10); if (!slottextloaded) { MN_LoadSlotText(); } DrawFileSlots(&LoadMenu); } //--------------------------------------------------------------------------- // // PROC DrawSaveMenu // //--------------------------------------------------------------------------- static void DrawSaveMenu(void) { MN_DrTextB("SAVE GAME", 160 - MN_TextBWidth("SAVE GAME") / 2, 10); if (!slottextloaded) { MN_LoadSlotText(); } DrawFileSlots(&SaveMenu); } static boolean ReadDescriptionForSlot(int slot, char *description) { FILE *fp; boolean found; char name[100]; char versionText[HXS_VERSION_TEXT_LENGTH]; M_snprintf(name, sizeof(name), "%shex%d.hxs", SavePath, slot); fp = fopen(name, "rb"); if (fp == NULL) { return false; } found = fread(description, HXS_DESCRIPTION_LENGTH, 1, fp) == 1 && fread(versionText, HXS_VERSION_TEXT_LENGTH, 1, fp) == 1; found = found && strcmp(versionText, HXS_VERSION_TEXT) == 0; fclose(fp); return found; } //=========================================================================== // // MN_LoadSlotText // // For each slot, looks for save games and reads the description field. // //=========================================================================== void MN_LoadSlotText(void) { char description[HXS_DESCRIPTION_LENGTH]; int slot; for (slot = 0; slot < 6; slot++) { if (ReadDescriptionForSlot(slot, description)) { memcpy(SlotText[slot], description, SLOTTEXTLEN); SlotStatus[slot] = 1; } else { memset(SlotText[slot], 0, SLOTTEXTLEN); SlotStatus[slot] = 0; } } slottextloaded = true; } //--------------------------------------------------------------------------- // // PROC DrawFileSlots // //--------------------------------------------------------------------------- static void DrawFileSlots(Menu_t * menu) { int i; int x; int y; x = menu->x; y = menu->y; for (i = 0; i < 6; i++) { V_DrawPatch(x, y, W_CacheLumpName("M_FSLOT", PU_CACHE)); if (SlotStatus[i]) { MN_DrTextA(SlotText[i], x + 5, y + 5); } y += ITEM_HEIGHT; } } //--------------------------------------------------------------------------- // // PROC DrawOptionsMenu // //--------------------------------------------------------------------------- static void DrawOptionsMenu(void) { if (messageson) { MN_DrTextB("ON", 196, 50); } else { MN_DrTextB("OFF", 196, 50); } DrawSlider(&OptionsMenu, 3, 10, mouseSensitivity); } //--------------------------------------------------------------------------- // // PROC DrawOptions2Menu // //--------------------------------------------------------------------------- static void DrawOptions2Menu(void) { DrawSlider(&Options2Menu, 1, 9, screenblocks - 3); DrawSlider(&Options2Menu, 3, 16, snd_MaxVolume); DrawSlider(&Options2Menu, 5, 16, snd_MusicVolume); } //--------------------------------------------------------------------------- // // PROC SCQuitGame // //--------------------------------------------------------------------------- static void SCQuitGame(int option) { MenuActive = false; askforquit = true; typeofask = 1; //quit game if (!netgame && !demoplayback) { paused = true; } } //--------------------------------------------------------------------------- // // PROC SCEndGame // //--------------------------------------------------------------------------- static void SCEndGame(int option) { if (demoplayback) { return; } if (SCNetCheck(3)) { MenuActive = false; askforquit = true; typeofask = 2; //endgame if (!netgame && !demoplayback) { paused = true; } } } //--------------------------------------------------------------------------- // // PROC SCMessages // //--------------------------------------------------------------------------- static void SCMessages(int option) { messageson ^= 1; if (messageson) { P_SetMessage(&players[consoleplayer], "MESSAGES ON", true); } else { P_SetMessage(&players[consoleplayer], "MESSAGES OFF", true); } S_StartSound(NULL, SFX_CHAT); } //=========================================================================== // // SCNetCheck // //=========================================================================== static boolean SCNetCheck(int option) { if (!netgame) { return true; } switch (option) { case 1: // new game P_SetMessage(&players[consoleplayer], "YOU CAN'T START A NEW GAME IN NETPLAY!", true); break; case 2: // load game P_SetMessage(&players[consoleplayer], "YOU CAN'T LOAD A GAME IN NETPLAY!", true); break; case 3: // end game P_SetMessage(&players[consoleplayer], "YOU CAN'T END A GAME IN NETPLAY!", true); break; } MenuActive = false; S_StartSound(NULL, SFX_CHAT); return false; } //=========================================================================== // // SCNetCheck2 // //=========================================================================== static void SCNetCheck2(int option) { SCNetCheck(option); return; } //--------------------------------------------------------------------------- // // PROC SCLoadGame // //--------------------------------------------------------------------------- static void SCLoadGame(int option) { if (demoplayback) { // deactivate playback, return control to player demoextend = false; } if (!SlotStatus[option]) { // Don't try to load from an empty slot return; } G_LoadGame(option); MN_DeactivateMenu(); BorderNeedRefresh = true; if (quickload == -1) { quickload = option + 1; P_ClearMessage(&players[consoleplayer]); } } //--------------------------------------------------------------------------- // // PROC SCSaveGame // //--------------------------------------------------------------------------- static void SCSaveGame(int option) { char *ptr; if (!FileMenuKeySteal) { int x, y; FileMenuKeySteal = true; // We need to activate the text input interface to type the save // game name: x = SaveMenu.x + 1; y = SaveMenu.y + 1 + option * ITEM_HEIGHT; I_StartTextInput(x, y, x + 190, y + ITEM_HEIGHT - 2); M_StringCopy(oldSlotText, SlotText[option], sizeof(oldSlotText)); ptr = SlotText[option]; while (*ptr) { ptr++; } *ptr = '['; *(ptr + 1) = 0; SlotStatus[option]++; currentSlot = option; slotptr = ptr - SlotText[option]; return; } else { G_SaveGame(option, SlotText[option]); FileMenuKeySteal = false; I_StopTextInput(); MN_DeactivateMenu(); } BorderNeedRefresh = true; if (quicksave == -1) { quicksave = option + 1; P_ClearMessage(&players[consoleplayer]); } } //========================================================================== // // SCClass // //========================================================================== static void SCClass(int option) { if (netgame) { P_SetMessage(&players[consoleplayer], "YOU CAN'T START A NEW GAME FROM WITHIN A NETGAME!", true); return; } MenuPClass = option; switch (MenuPClass) { case PCLASS_FIGHTER: SkillMenu.x = 120; SkillItems[0].text = "SQUIRE"; SkillItems[1].text = "KNIGHT"; SkillItems[2].text = "WARRIOR"; SkillItems[3].text = "BERSERKER"; SkillItems[4].text = "TITAN"; break; case PCLASS_CLERIC: SkillMenu.x = 116; SkillItems[0].text = "ALTAR BOY"; SkillItems[1].text = "ACOLYTE"; SkillItems[2].text = "PRIEST"; SkillItems[3].text = "CARDINAL"; SkillItems[4].text = "POPE"; break; case PCLASS_MAGE: SkillMenu.x = 112; SkillItems[0].text = "APPRENTICE"; SkillItems[1].text = "ENCHANTER"; SkillItems[2].text = "SORCERER"; SkillItems[3].text = "WARLOCK"; SkillItems[4].text = "ARCHIMAGE"; break; } SetMenu(MENU_SKILL); } //--------------------------------------------------------------------------- // // PROC SCSkill // //--------------------------------------------------------------------------- static void SCSkill(int option) { if (demoplayback) { // deactivate playback, return control to player demoextend = false; } PlayerClass[consoleplayer] = MenuPClass; G_DeferredNewGame(option); SB_SetClassData(); SB_state = -1; MN_DeactivateMenu(); } //--------------------------------------------------------------------------- // // PROC SCMouseSensi // //--------------------------------------------------------------------------- static void SCMouseSensi(int option) { if (option == RIGHT_DIR) { if (mouseSensitivity < 9) { mouseSensitivity++; } } else if (mouseSensitivity) { mouseSensitivity--; } } //--------------------------------------------------------------------------- // // PROC SCSfxVolume // //--------------------------------------------------------------------------- static void SCSfxVolume(int option) { if (option == RIGHT_DIR) { if (snd_MaxVolume < 15) { snd_MaxVolume++; } } else if (snd_MaxVolume) { snd_MaxVolume--; } soundchanged = true; // we'll set it when we leave the menu } //--------------------------------------------------------------------------- // // PROC SCMusicVolume // //--------------------------------------------------------------------------- static void SCMusicVolume(int option) { if (option == RIGHT_DIR) { if (snd_MusicVolume < 15) { snd_MusicVolume++; } } else if (snd_MusicVolume) { snd_MusicVolume--; } S_SetMusicVolume(); } //--------------------------------------------------------------------------- // // PROC SCScreenSize // //--------------------------------------------------------------------------- static void SCScreenSize(int option) { if (option == RIGHT_DIR) { if (screenblocks < 11) { screenblocks++; } } else if (screenblocks > 3) { screenblocks--; } R_SetViewSize(screenblocks, detailLevel); } //--------------------------------------------------------------------------- // // PROC SCInfo // //--------------------------------------------------------------------------- static void SCInfo(int option) { InfoType = 1; S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); if (!netgame && !demoplayback) { paused = true; } } //--------------------------------------------------------------------------- // // FUNC MN_Responder // //--------------------------------------------------------------------------- boolean MN_Responder(event_t * event) { int key; int charTyped; int i; MenuItem_t *item; extern boolean automapactive; extern void H2_StartTitle(void); extern void G_CheckDemoStatus(void); char *textBuffer; // In testcontrols mode, none of the function keys should do anything // - the only key is escape to quit. if (testcontrols) { if (event->type == ev_quit || (event->type == ev_keydown && (event->data1 == key_menu_activate || event->data1 == key_menu_quit))) { I_Quit(); return true; } return false; } // "close" button pressed on window? if (event->type == ev_quit) { // First click on close = bring up quit confirm message. // Second click = confirm quit. if (!MenuActive && askforquit && typeofask == 1) { G_CheckDemoStatus(); I_Quit(); } else { SCQuitGame(0); S_StartSound(NULL, SFX_CHAT); } return true; } // Allow the menu to be activated from a joystick button if a button // is bound for joybmenu. if (event->type == ev_joystick) { if (joybmenu >= 0 && (event->data1 & (1 << joybmenu)) != 0) { MN_ActivateMenu(); return true; } } // Only care about keypresses beyond this point. if (event->type != ev_keydown) { return false; } key = event->data1; charTyped = event->data2; if (InfoType) { /* The 4-Level Demo Version also has 3 Info pages if (gamemode == shareware) { InfoType = (InfoType + 1) % 5; } else */ { InfoType = (InfoType + 1) % 4; } if (key == KEY_ESCAPE) { InfoType = 0; } if (!InfoType) { if (!netgame && !demoplayback) { paused = false; } MN_DeactivateMenu(); SB_state = -1; //refresh the statbar BorderNeedRefresh = true; } S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); return (true); //make the info screen eat the keypress } if ((ravpic && key == KEY_F1) || (key != 0 && key == key_menu_screenshot)) { G_ScreenShot(); return (true); } if (askforquit) { if (key == key_menu_confirm) { switch (typeofask) { case 1: G_CheckDemoStatus(); I_Quit(); return false; case 2: P_ClearMessage(&players[consoleplayer]); askforquit = false; typeofask = 0; paused = false; I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); H2_StartTitle(); // go to intro/demo mode. return false; case 3: P_SetMessage(&players[consoleplayer], "QUICKSAVING....", false); FileMenuKeySteal = true; SCSaveGame(quicksave - 1); BorderNeedRefresh = true; break; case 4: P_SetMessage(&players[consoleplayer], "QUICKLOADING....", false); SCLoadGame(quickload - 1); BorderNeedRefresh = true; break; case 5: BorderNeedRefresh = true; mn_SuicideConsole = true; break; default: break; } askforquit = false; typeofask = 0; return true; } else if (key == key_menu_abort || key == KEY_ESCAPE) { players[consoleplayer].messageTics = 0; askforquit = false; typeofask = 0; paused = false; UpdateState |= I_FULLSCRN; BorderNeedRefresh = true; return true; } return false; // don't let the keys filter thru } if (!MenuActive && !chatmodeon) { if (key == key_menu_decscreen) { if (automapactive) { // Don't screen size in automap return (false); } SCScreenSize(LEFT_DIR); S_StartSound(NULL, SFX_PICKUP_KEY); BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; return (true); } else if (key == key_menu_incscreen) { if (automapactive) { // Don't screen size in automap return (false); } SCScreenSize(RIGHT_DIR); S_StartSound(NULL, SFX_PICKUP_KEY); BorderNeedRefresh = true; UpdateState |= I_FULLSCRN; return (true); } else if (key == key_menu_help) // F1 (help screen) { SCInfo(0); // start up info screens MenuActive = true; return (true); } else if (key == key_menu_save) // F2 (save game) { if (gamestate == GS_LEVEL && !demoplayback) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &SaveMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); slottextloaded = false; //reload the slot text, when needed } return true; } else if (key == key_menu_load) // F3 (load game) { if (SCNetCheck(2)) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &LoadMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); slottextloaded = false; //reload the slot text, when needed } return true; } else if (key == key_menu_volume) // F4 (volume) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &Options2Menu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); slottextloaded = false; //reload the slot text, when needed return true; } else if (key == key_menu_detail) // F5 (suicide) { MenuActive = false; askforquit = true; typeofask = 5; // suicide return true; } else if (key == key_menu_qsave) // F6 (quicksave) { if (gamestate == GS_LEVEL && !demoplayback) { if (!quicksave || quicksave == -1) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &SaveMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); slottextloaded = false; //reload the slot text quicksave = -1; P_SetMessage(&players[consoleplayer], "CHOOSE A QUICKSAVE SLOT", true); } else { askforquit = true; typeofask = 3; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, SFX_CHAT); } } return true; } else if (key == key_menu_endgame) // F7 (end game) { if (SCNetCheck(3)) { if (gamestate == GS_LEVEL && !demoplayback) { S_StartSound(NULL, SFX_CHAT); SCEndGame(0); } } return true; } else if (key == key_menu_messages) // F8 (toggle messages) { SCMessages(0); return true; } else if (key == key_menu_qload) // F9 (quickload) { if (SCNetCheck(2)) { if (!quickload || quickload == -1) { MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &LoadMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); slottextloaded = false; // reload the slot text quickload = -1; P_SetMessage(&players[consoleplayer], "CHOOSE A QUICKLOAD SLOT", true); } else { askforquit = true; if (!netgame && !demoplayback) { paused = true; } typeofask = 4; S_StartSound(NULL, SFX_CHAT); } } return true; } else if (key == key_menu_quit) // F10 (quit) { if (gamestate == GS_LEVEL || gamestate == GS_FINALE) { SCQuitGame(0); S_StartSound(NULL, SFX_CHAT); } return true; } else if (key == key_menu_gamma) // F11 (gamma correction) { usegamma++; if (usegamma > 4+4) // [crispy] intermediate gamma levels { usegamma = 0; } SB_PaletteFlash(true); // force change P_SetMessage(&players[consoleplayer], GammaText[usegamma], false); return true; } else if (key == KEY_F12) // F12 (???) { // F12 - reload current map (devmaps mode) if (netgame) { return false; } if (gamekeydown[key_speed]) { // Monsters ON nomonsters = false; } if (gamekeydown[key_strafe]) { // Monsters OFF nomonsters = true; } G_DeferedInitNew(gameskill, gameepisode, gamemap); P_SetMessage(&players[consoleplayer], TXT_CHEATWARP, false); return true; } } if (!MenuActive) { if (key == key_menu_activate || gamestate == GS_DEMOSCREEN || demoplayback) { MN_ActivateMenu(); return (true); } return (false); } if (!FileMenuKeySteal) { item = &CurrentMenu->items[CurrentItPos]; if (key == key_menu_down) // Next menu item { do { if (CurrentItPos + 1 > CurrentMenu->itemCount - 1) { CurrentItPos = 0; } else { CurrentItPos++; } } while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY); S_StartSound(NULL, SFX_FIGHTER_HAMMER_HITWALL); return (true); } else if (key == key_menu_up) // Previous menu item { do { if (CurrentItPos == 0) { CurrentItPos = CurrentMenu->itemCount - 1; } else { CurrentItPos--; } } while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY); S_StartSound(NULL, SFX_FIGHTER_HAMMER_HITWALL); return (true); } else if (key == key_menu_left) // Slider left { if (item->type == ITT_LRFUNC && item->func != NULL) { item->func(LEFT_DIR); S_StartSound(NULL, SFX_PICKUP_KEY); } return (true); } else if (key == key_menu_right) // Slider right { if (item->type == ITT_LRFUNC && item->func != NULL) { item->func(RIGHT_DIR); S_StartSound(NULL, SFX_PICKUP_KEY); } return (true); } else if (key == key_menu_forward) // Activate item (enter) { if (item->type == ITT_SETMENU) { if (item->func != NULL) { item->func(item->option); } SetMenu(item->menu); } else if (item->func != NULL) { CurrentMenu->oldItPos = CurrentItPos; if (item->type == ITT_LRFUNC) { item->func(RIGHT_DIR); } else if (item->type == ITT_EFUNC) { item->func(item->option); } } S_StartSound(NULL, SFX_DOOR_LIGHT_CLOSE); return (true); } else if (key == key_menu_activate) { MN_DeactivateMenu(); return (true); } else if (key == key_menu_back) { S_StartSound(NULL, SFX_PICKUP_KEY); if (CurrentMenu->prevMenu == MENU_NONE) { MN_DeactivateMenu(); } else { SetMenu(CurrentMenu->prevMenu); } return (true); } else if (charTyped != 0) { for (i = 0; i < CurrentMenu->itemCount; i++) { if (CurrentMenu->items[i].text) { if (toupper(charTyped) == toupper(CurrentMenu->items[i].text[0])) { CurrentItPos = i; return (true); } } } } return (false); } else { // Editing file names // When typing a savegame name, we use the fully shifted and // translated input value from event->data3. charTyped = event->data3; textBuffer = &SlotText[currentSlot][slotptr]; if (key == KEY_BACKSPACE) { if (slotptr) { *textBuffer-- = 0; *textBuffer = ASCII_CURSOR; slotptr--; } return (true); } if (key == KEY_ESCAPE) { M_StringCopy(SlotText[currentSlot], oldSlotText, sizeof(SlotText[currentSlot])); SlotStatus[currentSlot]--; MN_DeactivateMenu(); return (true); } if (key == KEY_ENTER) { SlotText[currentSlot][slotptr] = 0; // clear the cursor item = &CurrentMenu->items[CurrentItPos]; CurrentMenu->oldItPos = CurrentItPos; if (item->type == ITT_EFUNC) { item->func(item->option); if (item->menu != MENU_NONE) { SetMenu(item->menu); } } return (true); } if (slotptr < SLOTTEXTLEN && key != KEY_BACKSPACE) { if (isalpha(charTyped)) { *textBuffer++ = toupper(charTyped); *textBuffer = ASCII_CURSOR; slotptr++; return (true); } if (isdigit(charTyped) || charTyped == ' ' || charTyped == ',' || charTyped == '.' || charTyped == '-' || charTyped == '!') { *textBuffer++ = charTyped; *textBuffer = ASCII_CURSOR; slotptr++; return (true); } } return (true); } return (false); } //--------------------------------------------------------------------------- // // PROC MN_ActivateMenu // //--------------------------------------------------------------------------- void MN_ActivateMenu(void) { if (MenuActive) { return; } if (paused) { S_ResumeSound(); } MenuActive = true; FileMenuKeySteal = false; MenuTime = 0; CurrentMenu = &MainMenu; CurrentItPos = CurrentMenu->oldItPos; if (!netgame && !demoplayback) { paused = true; } S_StartSound(NULL, SFX_PLATFORM_STOP); slottextloaded = false; //reload the slot text, when needed } //--------------------------------------------------------------------------- // // PROC MN_DeactivateMenu // //--------------------------------------------------------------------------- void MN_DeactivateMenu(void) { if (CurrentMenu != NULL) { CurrentMenu->oldItPos = CurrentItPos; } MenuActive = false; if (FileMenuKeySteal) { I_StopTextInput(); } if (!netgame) { paused = false; } S_StartSound(NULL, SFX_PLATFORM_STOP); P_ClearMessage(&players[consoleplayer]); } //--------------------------------------------------------------------------- // // PROC MN_DrawInfo // //--------------------------------------------------------------------------- void MN_DrawInfo(void) { I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE)); V_CopyScaledBuffer(I_VideoBuffer, (byte *) W_CacheLumpNum(W_GetNumForName("TITLE") + InfoType, PU_CACHE), ORIGWIDTH * ORIGHEIGHT); // V_DrawPatch(0, 0, W_CacheLumpNum(W_GetNumForName("TITLE")+InfoType, // PU_CACHE)); } //--------------------------------------------------------------------------- // // PROC SetMenu // //--------------------------------------------------------------------------- static void SetMenu(MenuType_t menu) { CurrentMenu->oldItPos = CurrentItPos; CurrentMenu = Menus[menu]; CurrentItPos = CurrentMenu->oldItPos; } //--------------------------------------------------------------------------- // // PROC DrawSlider // //--------------------------------------------------------------------------- static void DrawSlider(Menu_t * menu, int item, int width, int slot) { int x; int y; int x2; int count; x = menu->x + 24; y = menu->y + 2 + (item * ITEM_HEIGHT); V_DrawPatch(x - 32, y, W_CacheLumpName("M_SLDLT", PU_CACHE)); for (x2 = x, count = width; count--; x2 += 8) { V_DrawPatch(x2, y, W_CacheLumpName(count & 1 ? "M_SLDMD1" : "M_SLDMD2", PU_CACHE)); } V_DrawPatch(x2, y, W_CacheLumpName("M_SLDRT", PU_CACHE)); V_DrawPatch(x + 4 + slot * 8, y + 7, W_CacheLumpName("M_SLDKB", PU_CACHE)); } crispy-doom-crispy-doom-5.6.4/src/hexen/p_acs.c000066400000000000000000001330331360717211000213660ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "m_misc.h" #include "m_random.h" #include "s_sound.h" #include "i_swap.h" #include "i_system.h" #include "p_local.h" // MACROS ------------------------------------------------------------------ #define MAX_SCRIPT_ARGS 3 #define SCRIPT_CONTINUE 0 #define SCRIPT_STOP 1 #define SCRIPT_TERMINATE 2 #define OPEN_SCRIPTS_BASE 1000 #define PRINT_BUFFER_SIZE 256 #define GAME_SINGLE_PLAYER 0 #define GAME_NET_COOPERATIVE 1 #define GAME_NET_DEATHMATCH 2 #define TEXTURE_TOP 0 #define TEXTURE_MIDDLE 1 #define TEXTURE_BOTTOM 2 // TYPES ------------------------------------------------------------------- typedef PACKED_STRUCT ( { int marker; int infoOffset; int code; }) acsHeader_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void StartOpenACS(int number, int infoIndex, int offset); static void ScriptFinished(int number); static boolean TagBusy(int tag); static boolean AddToACSStore(int map, int number, byte * args); static int GetACSIndex(int number); static void Push(int value); static int Pop(void); static int Top(void); static void Drop(void); static int CmdNOP(void); static int CmdTerminate(void); static int CmdSuspend(void); static int CmdPushNumber(void); static int CmdLSpec1(void); static int CmdLSpec2(void); static int CmdLSpec3(void); static int CmdLSpec4(void); static int CmdLSpec5(void); static int CmdLSpec1Direct(void); static int CmdLSpec2Direct(void); static int CmdLSpec3Direct(void); static int CmdLSpec4Direct(void); static int CmdLSpec5Direct(void); static int CmdAdd(void); static int CmdSubtract(void); static int CmdMultiply(void); static int CmdDivide(void); static int CmdModulus(void); static int CmdEQ(void); static int CmdNE(void); static int CmdLT(void); static int CmdGT(void); static int CmdLE(void); static int CmdGE(void); static int CmdAssignScriptVar(void); static int CmdAssignMapVar(void); static int CmdAssignWorldVar(void); static int CmdPushScriptVar(void); static int CmdPushMapVar(void); static int CmdPushWorldVar(void); static int CmdAddScriptVar(void); static int CmdAddMapVar(void); static int CmdAddWorldVar(void); static int CmdSubScriptVar(void); static int CmdSubMapVar(void); static int CmdSubWorldVar(void); static int CmdMulScriptVar(void); static int CmdMulMapVar(void); static int CmdMulWorldVar(void); static int CmdDivScriptVar(void); static int CmdDivMapVar(void); static int CmdDivWorldVar(void); static int CmdModScriptVar(void); static int CmdModMapVar(void); static int CmdModWorldVar(void); static int CmdIncScriptVar(void); static int CmdIncMapVar(void); static int CmdIncWorldVar(void); static int CmdDecScriptVar(void); static int CmdDecMapVar(void); static int CmdDecWorldVar(void); static int CmdGoto(void); static int CmdIfGoto(void); static int CmdDrop(void); static int CmdDelay(void); static int CmdDelayDirect(void); static int CmdRandom(void); static int CmdRandomDirect(void); static int CmdThingCount(void); static int CmdThingCountDirect(void); static int CmdTagWait(void); static int CmdTagWaitDirect(void); static int CmdPolyWait(void); static int CmdPolyWaitDirect(void); static int CmdChangeFloor(void); static int CmdChangeFloorDirect(void); static int CmdChangeCeiling(void); static int CmdChangeCeilingDirect(void); static int CmdRestart(void); static int CmdAndLogical(void); static int CmdOrLogical(void); static int CmdAndBitwise(void); static int CmdOrBitwise(void); static int CmdEorBitwise(void); static int CmdNegateLogical(void); static int CmdLShift(void); static int CmdRShift(void); static int CmdUnaryMinus(void); static int CmdIfNotGoto(void); static int CmdLineSide(void); static int CmdScriptWait(void); static int CmdScriptWaitDirect(void); static int CmdClearLineSpecial(void); static int CmdCaseGoto(void); static int CmdBeginPrint(void); static int CmdEndPrint(void); static int CmdPrintString(void); static int CmdPrintNumber(void); static int CmdPrintCharacter(void); static int CmdPlayerCount(void); static int CmdGameType(void); static int CmdGameSkill(void); static int CmdTimer(void); static int CmdSectorSound(void); static int CmdAmbientSound(void); static int CmdSoundSequence(void); static int CmdSetLineTexture(void); static int CmdSetLineBlocking(void); static int CmdSetLineSpecial(void); static int CmdThingSound(void); static int CmdEndPrintBold(void); static void ThingCount(int type, int tid); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- int ACScriptCount; byte *ActionCodeBase; static int ActionCodeSize; acsInfo_t *ACSInfo; int MapVars[MAX_ACS_MAP_VARS]; int WorldVars[MAX_ACS_WORLD_VARS]; acsstore_t ACSStore[MAX_ACS_STORE + 1]; // +1 for termination marker // PRIVATE DATA DEFINITIONS ------------------------------------------------ static char EvalContext[64]; static acs_t *ACScript; static unsigned int PCodeOffset; static byte SpecArgs[8]; static int ACStringCount; static char **ACStrings; static char PrintBuffer[PRINT_BUFFER_SIZE]; static acs_t *NewScript; static int (*PCodeCmds[]) (void) = { CmdNOP, CmdTerminate, CmdSuspend, CmdPushNumber, CmdLSpec1, CmdLSpec2, CmdLSpec3, CmdLSpec4, CmdLSpec5, CmdLSpec1Direct, CmdLSpec2Direct, CmdLSpec3Direct, CmdLSpec4Direct, CmdLSpec5Direct, CmdAdd, CmdSubtract, CmdMultiply, CmdDivide, CmdModulus, CmdEQ, CmdNE, CmdLT, CmdGT, CmdLE, CmdGE, CmdAssignScriptVar, CmdAssignMapVar, CmdAssignWorldVar, CmdPushScriptVar, CmdPushMapVar, CmdPushWorldVar, CmdAddScriptVar, CmdAddMapVar, CmdAddWorldVar, CmdSubScriptVar, CmdSubMapVar, CmdSubWorldVar, CmdMulScriptVar, CmdMulMapVar, CmdMulWorldVar, CmdDivScriptVar, CmdDivMapVar, CmdDivWorldVar, CmdModScriptVar, CmdModMapVar, CmdModWorldVar, CmdIncScriptVar, CmdIncMapVar, CmdIncWorldVar, CmdDecScriptVar, CmdDecMapVar, CmdDecWorldVar, CmdGoto, CmdIfGoto, CmdDrop, CmdDelay, CmdDelayDirect, CmdRandom, CmdRandomDirect, CmdThingCount, CmdThingCountDirect, CmdTagWait, CmdTagWaitDirect, CmdPolyWait, CmdPolyWaitDirect, CmdChangeFloor, CmdChangeFloorDirect, CmdChangeCeiling, CmdChangeCeilingDirect, CmdRestart, CmdAndLogical, CmdOrLogical, CmdAndBitwise, CmdOrBitwise, CmdEorBitwise, CmdNegateLogical, CmdLShift, CmdRShift, CmdUnaryMinus, CmdIfNotGoto, CmdLineSide, CmdScriptWait, CmdScriptWaitDirect, CmdClearLineSpecial, CmdCaseGoto, CmdBeginPrint, CmdEndPrint, CmdPrintString, CmdPrintNumber, CmdPrintCharacter, CmdPlayerCount, CmdGameType, CmdGameSkill, CmdTimer, CmdSectorSound, CmdAmbientSound, CmdSoundSequence, CmdSetLineTexture, CmdSetLineBlocking, CmdSetLineSpecial, CmdThingSound, CmdEndPrintBold, }; // CODE -------------------------------------------------------------------- //========================================================================== // // ACSAssert // // Check that the given condition evaluates to true. If it does not, exit // with an I_Error() printing the given message. // //========================================================================== static void ACSAssert(int condition, const char *fmt, ...) { char buf[128]; va_list args; if (condition) { return; } va_start(args, fmt); M_vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); I_Error("ACS assertion failure: in %s: %s", EvalContext, buf); } //========================================================================== // // ReadCodeInt // // Read a 32-bit value from the loaded ACS lump at the location pointed to // by PCodeOffset, advancing PCodeOffset to the next value in the process. // //========================================================================== static int ReadCodeInt(void) { int result; int *ptr; ACSAssert(PCodeOffset + 3 < ActionCodeSize, "unexpectedly reached end of ACS lump"); ptr = (int *) (ActionCodeBase + PCodeOffset); result = LONG(*ptr); PCodeOffset += 4; return result; } //========================================================================== // // ReadScriptVar // // Read a script variable index as an immediate value, validating the // result is a valid script variable number. // //========================================================================== static int ReadScriptVar(void) { int var = ReadCodeInt(); ACSAssert(var >= 0, "negative script variable: %d < 0", var); ACSAssert(var < MAX_ACS_SCRIPT_VARS, "invalid script variable: %d >= %d", var, MAX_ACS_SCRIPT_VARS); return var; } //========================================================================== // // ReadMapVar // // Read a map variable index as an immediate value, validating the // result is a valid map variable number. // //========================================================================== static int ReadMapVar(void) { int var = ReadCodeInt(); ACSAssert(var >= 0, "negative map variable: %d < 0", var); ACSAssert(var < MAX_ACS_MAP_VARS, "invalid map variable: %d >= %d", var, MAX_ACS_MAP_VARS); return var; } //========================================================================== // // ReadWorldVar // // Read a world variable index as an immediate value, validating the // result is a valid world variable number. // //========================================================================== static int ReadWorldVar(void) { int var = ReadCodeInt(); ACSAssert(var >= 0, "negative world variable: %d < 0", var); ACSAssert(var < MAX_ACS_WORLD_VARS, "invalid world variable: %d >= %d", var, MAX_ACS_WORLD_VARS); return var; } //========================================================================== // // StringLookup // // Look up the given string in the strings table by index, validating that // it is a valid string index. // //========================================================================== static char *StringLookup(int string_index) { ACSAssert(string_index >= 0, "negative string index: %d < 0", string_index); ACSAssert(string_index < ACStringCount, "invalid string index: %d >= %d", string_index, ACStringCount); return ACStrings[string_index]; } //========================================================================== // // ReadOffset // // Read a lump offset value, validating that it is an offset within the // range of the lump. // //========================================================================== static int ReadOffset(void) { int offset = ReadCodeInt(); ACSAssert(offset >= 0, "negative lump offset %d", offset); ACSAssert(offset < ActionCodeSize, "invalid lump offset: %d >= %d", offset, ActionCodeSize); return offset; } //========================================================================== // // P_LoadACScripts // //========================================================================== void P_LoadACScripts(int lump) { int i, offset; acsHeader_t *header; acsInfo_t *info; ActionCodeBase = W_CacheLumpNum(lump, PU_LEVEL); ActionCodeSize = W_LumpLength(lump); M_snprintf(EvalContext, sizeof(EvalContext), "header parsing of lump #%d", lump); header = (acsHeader_t *) ActionCodeBase; PCodeOffset = LONG(header->infoOffset); ACScriptCount = ReadCodeInt(); if (ACScriptCount == 0) { // Empty behavior lump return; } ACSInfo = Z_Malloc(ACScriptCount * sizeof(acsInfo_t), PU_LEVEL, 0); memset(ACSInfo, 0, ACScriptCount * sizeof(acsInfo_t)); for (i = 0, info = ACSInfo; i < ACScriptCount; i++, info++) { info->number = ReadCodeInt(); info->offset = ReadOffset(); info->argCount = ReadCodeInt(); if (info->argCount > MAX_SCRIPT_ARGS) { fprintf(stderr, "Warning: ACS script #%i has %i arguments, more " "than the maximum of %i. Enforcing limit.\n" "If you are seeing this message, please report " "the name of the WAD where you saw it.\n", i, info->argCount, MAX_SCRIPT_ARGS); info->argCount = MAX_SCRIPT_ARGS; } if (info->number >= OPEN_SCRIPTS_BASE) { // Auto-activate info->number -= OPEN_SCRIPTS_BASE; StartOpenACS(info->number, i, info->offset); info->state = ASTE_RUNNING; } else { info->state = ASTE_INACTIVE; } } ACStringCount = ReadCodeInt(); ACSAssert(ACStringCount >= 0, "negative string count %d", ACStringCount); ACStrings = Z_Malloc(ACStringCount * sizeof(char *), PU_LEVEL, NULL); for (i=0; inumber = number; // World objects are allotted 1 second for initialization script->delayCount = 35; script->infoIndex = infoIndex; script->ip = offset; script->thinker.function = T_InterpretACS; P_AddThinker(&script->thinker); } //========================================================================== // // P_CheckACSStore // // Scans the ACS store and executes all scripts belonging to the current // map. // //========================================================================== void P_CheckACSStore(void) { acsstore_t *store; for (store = ACSStore; store->map != 0; store++) { if (store->map == gamemap) { P_StartACS(store->script, 0, store->args, NULL, NULL, 0); if (NewScript) { NewScript->delayCount = 35; } store->map = -1; } } } //========================================================================== // // P_StartACS // // Start an ACS script. The 'args' array should be at least MAX_SCRIPT_ARGS // elements in length. // //========================================================================== static char ErrorMsg[128]; boolean P_StartACS(int number, int map, byte * args, mobj_t * activator, line_t * line, int side) { int i; acs_t *script; int infoIndex; aste_t *statePtr; NewScript = NULL; if (map && map != gamemap) { // Add to the script store return AddToACSStore(map, number, args); } infoIndex = GetACSIndex(number); if (infoIndex == -1) { // Script not found //I_Error("P_StartACS: Unknown script number %d", number); M_snprintf(ErrorMsg, sizeof(ErrorMsg), "P_STARTACS ERROR: UNKNOWN SCRIPT %d", number); P_SetMessage(&players[consoleplayer], ErrorMsg, true); } statePtr = &ACSInfo[infoIndex].state; if (*statePtr == ASTE_SUSPENDED) { // Resume a suspended script *statePtr = ASTE_RUNNING; return true; } if (*statePtr != ASTE_INACTIVE) { // Script is already executing return false; } script = Z_Malloc(sizeof(acs_t), PU_LEVSPEC, 0); memset(script, 0, sizeof(acs_t)); script->number = number; script->infoIndex = infoIndex; script->activator = activator; script->line = line; script->side = side; script->ip = ACSInfo[infoIndex].offset; script->thinker.function = T_InterpretACS; for (i = 0; i < MAX_SCRIPT_ARGS && i < ACSInfo[infoIndex].argCount; i++) { script->vars[i] = args[i]; } *statePtr = ASTE_RUNNING; P_AddThinker(&script->thinker); NewScript = script; return true; } //========================================================================== // // AddToACSStore // //========================================================================== static boolean AddToACSStore(int map, int number, byte * args) { int i; int index; index = -1; for (i = 0; ACSStore[i].map != 0; i++) { if (ACSStore[i].script == number && ACSStore[i].map == map) { // Don't allow duplicates return false; } if (index == -1 && ACSStore[i].map == -1) { // Remember first empty slot index = i; } } if (index == -1) { // Append required if (i == MAX_ACS_STORE) { I_Error("AddToACSStore: MAX_ACS_STORE (%d) exceeded.", MAX_ACS_STORE); } index = i; ACSStore[index + 1].map = 0; } ACSStore[index].map = map; ACSStore[index].script = number; memcpy(ACSStore[index].args, args, MAX_SCRIPT_ARGS); return true; } //========================================================================== // // P_StartLockedACS // //========================================================================== boolean P_StartLockedACS(line_t * line, byte * args, mobj_t * mo, int side) { int i; int lock; byte newArgs[5]; char LockedBuffer[80]; extern char *TextKeyMessages[11]; lock = args[4]; if (!mo->player) { return false; } if (lock) { if (!(mo->player->keys & (1 << (lock - 1)))) { M_snprintf(LockedBuffer, sizeof(LockedBuffer), "YOU NEED THE %s\n", TextKeyMessages[lock - 1]); P_SetMessage(mo->player, LockedBuffer, true); S_StartSound(mo, SFX_DOOR_LOCKED); return false; } } for (i = 0; i < 4; i++) { newArgs[i] = args[i]; } newArgs[4] = 0; return P_StartACS(newArgs[0], newArgs[1], &newArgs[2], mo, line, side); } //========================================================================== // // P_TerminateACS // //========================================================================== boolean P_TerminateACS(int number, int map) { int infoIndex; infoIndex = GetACSIndex(number); if (infoIndex == -1) { // Script not found return false; } if (ACSInfo[infoIndex].state == ASTE_INACTIVE || ACSInfo[infoIndex].state == ASTE_TERMINATING) { // States that disallow termination return false; } ACSInfo[infoIndex].state = ASTE_TERMINATING; return true; } //========================================================================== // // P_SuspendACS // //========================================================================== boolean P_SuspendACS(int number, int map) { int infoIndex; infoIndex = GetACSIndex(number); if (infoIndex == -1) { // Script not found return false; } if (ACSInfo[infoIndex].state == ASTE_INACTIVE || ACSInfo[infoIndex].state == ASTE_SUSPENDED || ACSInfo[infoIndex].state == ASTE_TERMINATING) { // States that disallow suspension return false; } ACSInfo[infoIndex].state = ASTE_SUSPENDED; return true; } //========================================================================== // // P_Init // //========================================================================== void P_ACSInitNewGame(void) { memset(WorldVars, 0, sizeof(WorldVars)); memset(ACSStore, 0, sizeof(ACSStore)); } //========================================================================== // // T_InterpretACS // //========================================================================== void T_InterpretACS(acs_t * script) { int cmd; int action; if (ACSInfo[script->infoIndex].state == ASTE_TERMINATING) { ACSInfo[script->infoIndex].state = ASTE_INACTIVE; ScriptFinished(ACScript->number); P_RemoveThinker(&ACScript->thinker); return; } if (ACSInfo[script->infoIndex].state != ASTE_RUNNING) { return; } if (script->delayCount) { script->delayCount--; return; } ACScript = script; PCodeOffset = ACScript->ip; do { M_snprintf(EvalContext, sizeof(EvalContext), "script %d @0x%x", ACSInfo[script->infoIndex].number, PCodeOffset); cmd = ReadCodeInt(); M_snprintf(EvalContext, sizeof(EvalContext), "script %d @0x%x, cmd=%d", ACSInfo[script->infoIndex].number, PCodeOffset, cmd); ACSAssert(cmd >= 0, "negative ACS instruction %d", cmd); ACSAssert(cmd < arrlen(PCodeCmds), "invalid ACS instruction %d (maybe this WAD is designed " "for an advanced source port and is not vanilla " "compatible)", cmd); action = PCodeCmds[cmd](); } while (action == SCRIPT_CONTINUE); ACScript->ip = PCodeOffset; if (action == SCRIPT_TERMINATE) { ACSInfo[script->infoIndex].state = ASTE_INACTIVE; ScriptFinished(ACScript->number); P_RemoveThinker(&ACScript->thinker); } } //========================================================================== // // P_TagFinished // //========================================================================== void P_TagFinished(int tag) { int i; if (TagBusy(tag) == true) { return; } for (i = 0; i < ACScriptCount; i++) { if (ACSInfo[i].state == ASTE_WAITINGFORTAG && ACSInfo[i].waitValue == tag) { ACSInfo[i].state = ASTE_RUNNING; } } } //========================================================================== // // P_PolyobjFinished // //========================================================================== void P_PolyobjFinished(int po) { int i; if (PO_Busy(po) == true) { return; } for (i = 0; i < ACScriptCount; i++) { if (ACSInfo[i].state == ASTE_WAITINGFORPOLY && ACSInfo[i].waitValue == po) { ACSInfo[i].state = ASTE_RUNNING; } } } //========================================================================== // // ScriptFinished // //========================================================================== static void ScriptFinished(int number) { int i; for (i = 0; i < ACScriptCount; i++) { if (ACSInfo[i].state == ASTE_WAITINGFORSCRIPT && ACSInfo[i].waitValue == number) { ACSInfo[i].state = ASTE_RUNNING; } } } //========================================================================== // // TagBusy // //========================================================================== static boolean TagBusy(int tag) { int sectorIndex; sectorIndex = -1; while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { if (sectors[sectorIndex].specialdata) { return true; } } return false; } //========================================================================== // // GetACSIndex // // Returns the index of a script number. Returns -1 if the script number // is not found. // //========================================================================== static int GetACSIndex(int number) { int i; for (i = 0; i < ACScriptCount; i++) { if (ACSInfo[i].number == number) { return i; } } return -1; } //========================================================================== // // CheckACSPresent // // Placing Korax in a PWAD without extra steps will result in a crash in // Vanilla because the relevant ACS scripts are not initialized // //========================================================================== void CheckACSPresent(int number) { if (GetACSIndex(number) == -1) { I_Error("Required ACS script %d not initialized", number); } } //========================================================================== // // Push // //========================================================================== static void Push(int value) { ACSAssert(ACScript->stackPtr < ACS_STACK_DEPTH, "maximum stack depth exceeded: %d >= %d", ACScript->stackPtr, ACS_STACK_DEPTH); ACScript->stack[ACScript->stackPtr++] = value; } //========================================================================== // // Pop // //========================================================================== static int Pop(void) { ACSAssert(ACScript->stackPtr > 0, "pop of empty stack"); return ACScript->stack[--ACScript->stackPtr]; } //========================================================================== // // Top // //========================================================================== static int Top(void) { ACSAssert(ACScript->stackPtr > 0, "read from top of empty stack"); return ACScript->stack[ACScript->stackPtr - 1]; } //========================================================================== // // Drop // //========================================================================== static void Drop(void) { ACSAssert(ACScript->stackPtr > 0, "drop on empty stack"); ACScript->stackPtr--; } //========================================================================== // // P-Code Commands // //========================================================================== static int CmdNOP(void) { return SCRIPT_CONTINUE; } static int CmdTerminate(void) { return SCRIPT_TERMINATE; } static int CmdSuspend(void) { ACSInfo[ACScript->infoIndex].state = ASTE_SUSPENDED; return SCRIPT_STOP; } static int CmdPushNumber(void) { Push(ReadCodeInt()); return SCRIPT_CONTINUE; } static int CmdLSpec1(void) { int special; special = ReadCodeInt(); SpecArgs[0] = Pop(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec2(void) { int special; special = ReadCodeInt(); SpecArgs[1] = Pop(); SpecArgs[0] = Pop(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec3(void) { int special; special = ReadCodeInt(); SpecArgs[2] = Pop(); SpecArgs[1] = Pop(); SpecArgs[0] = Pop(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec4(void) { int special; special = ReadCodeInt(); SpecArgs[3] = Pop(); SpecArgs[2] = Pop(); SpecArgs[1] = Pop(); SpecArgs[0] = Pop(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec5(void) { int special; special = ReadCodeInt(); SpecArgs[4] = Pop(); SpecArgs[3] = Pop(); SpecArgs[2] = Pop(); SpecArgs[1] = Pop(); SpecArgs[0] = Pop(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec1Direct(void) { int special; special = ReadCodeInt(); SpecArgs[0] = ReadCodeInt(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec2Direct(void) { int special; special = ReadCodeInt(); SpecArgs[0] = ReadCodeInt(); SpecArgs[1] = ReadCodeInt(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec3Direct(void) { int special; special = ReadCodeInt(); SpecArgs[0] = ReadCodeInt(); SpecArgs[1] = ReadCodeInt(); SpecArgs[2] = ReadCodeInt(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec4Direct(void) { int special; special = ReadCodeInt(); SpecArgs[0] = ReadCodeInt(); SpecArgs[1] = ReadCodeInt(); SpecArgs[2] = ReadCodeInt(); SpecArgs[3] = ReadCodeInt(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdLSpec5Direct(void) { int special; special = ReadCodeInt(); SpecArgs[0] = ReadCodeInt(); SpecArgs[1] = ReadCodeInt(); SpecArgs[2] = ReadCodeInt(); SpecArgs[3] = ReadCodeInt(); SpecArgs[4] = ReadCodeInt(); P_ExecuteLineSpecial(special, SpecArgs, ACScript->line, ACScript->side, ACScript->activator); return SCRIPT_CONTINUE; } static int CmdAdd(void) { Push(Pop() + Pop()); return SCRIPT_CONTINUE; } static int CmdSubtract(void) { int operand2; operand2 = Pop(); Push(Pop() - operand2); return SCRIPT_CONTINUE; } static int CmdMultiply(void) { Push(Pop() * Pop()); return SCRIPT_CONTINUE; } static int CmdDivide(void) { int operand2; operand2 = Pop(); Push(Pop() / operand2); return SCRIPT_CONTINUE; } static int CmdModulus(void) { int operand2; operand2 = Pop(); Push(Pop() % operand2); return SCRIPT_CONTINUE; } static int CmdEQ(void) { Push(Pop() == Pop()); return SCRIPT_CONTINUE; } static int CmdNE(void) { Push(Pop() != Pop()); return SCRIPT_CONTINUE; } static int CmdLT(void) { int operand2; operand2 = Pop(); Push(Pop() < operand2); return SCRIPT_CONTINUE; } static int CmdGT(void) { int operand2; operand2 = Pop(); Push(Pop() > operand2); return SCRIPT_CONTINUE; } static int CmdLE(void) { int operand2; operand2 = Pop(); Push(Pop() <= operand2); return SCRIPT_CONTINUE; } static int CmdGE(void) { int operand2; operand2 = Pop(); Push(Pop() >= operand2); return SCRIPT_CONTINUE; } static int CmdAssignScriptVar(void) { ACScript->vars[ReadScriptVar()] = Pop(); return SCRIPT_CONTINUE; } static int CmdAssignMapVar(void) { MapVars[ReadMapVar()] = Pop(); return SCRIPT_CONTINUE; } static int CmdAssignWorldVar(void) { WorldVars[ReadWorldVar()] = Pop(); return SCRIPT_CONTINUE; } static int CmdPushScriptVar(void) { Push(ACScript->vars[ReadScriptVar()]); return SCRIPT_CONTINUE; } static int CmdPushMapVar(void) { Push(MapVars[ReadMapVar()]); return SCRIPT_CONTINUE; } static int CmdPushWorldVar(void) { Push(WorldVars[ReadWorldVar()]); return SCRIPT_CONTINUE; } static int CmdAddScriptVar(void) { ACScript->vars[ReadScriptVar()] += Pop(); return SCRIPT_CONTINUE; } static int CmdAddMapVar(void) { MapVars[ReadMapVar()] += Pop(); return SCRIPT_CONTINUE; } static int CmdAddWorldVar(void) { WorldVars[ReadWorldVar()] += Pop(); return SCRIPT_CONTINUE; } static int CmdSubScriptVar(void) { ACScript->vars[ReadScriptVar()] -= Pop(); return SCRIPT_CONTINUE; } static int CmdSubMapVar(void) { MapVars[ReadMapVar()] -= Pop(); return SCRIPT_CONTINUE; } static int CmdSubWorldVar(void) { WorldVars[ReadWorldVar()] -= Pop(); return SCRIPT_CONTINUE; } static int CmdMulScriptVar(void) { ACScript->vars[ReadScriptVar()] *= Pop(); return SCRIPT_CONTINUE; } static int CmdMulMapVar(void) { MapVars[ReadMapVar()] *= Pop(); return SCRIPT_CONTINUE; } static int CmdMulWorldVar(void) { WorldVars[ReadWorldVar()] *= Pop(); return SCRIPT_CONTINUE; } static int CmdDivScriptVar(void) { ACScript->vars[ReadScriptVar()] /= Pop(); return SCRIPT_CONTINUE; } static int CmdDivMapVar(void) { MapVars[ReadMapVar()] /= Pop(); return SCRIPT_CONTINUE; } static int CmdDivWorldVar(void) { WorldVars[ReadWorldVar()] /= Pop(); return SCRIPT_CONTINUE; } static int CmdModScriptVar(void) { ACScript->vars[ReadScriptVar()] %= Pop(); return SCRIPT_CONTINUE; } static int CmdModMapVar(void) { MapVars[ReadMapVar()] %= Pop(); return SCRIPT_CONTINUE; } static int CmdModWorldVar(void) { WorldVars[ReadWorldVar()] %= Pop(); return SCRIPT_CONTINUE; } static int CmdIncScriptVar(void) { ++ACScript->vars[ReadScriptVar()]; return SCRIPT_CONTINUE; } static int CmdIncMapVar(void) { ++MapVars[ReadMapVar()]; return SCRIPT_CONTINUE; } static int CmdIncWorldVar(void) { ++WorldVars[ReadWorldVar()]; return SCRIPT_CONTINUE; } static int CmdDecScriptVar(void) { --ACScript->vars[ReadScriptVar()]; return SCRIPT_CONTINUE; } static int CmdDecMapVar(void) { --MapVars[ReadMapVar()]; return SCRIPT_CONTINUE; } static int CmdDecWorldVar(void) { --WorldVars[ReadWorldVar()]; return SCRIPT_CONTINUE; } static int CmdGoto(void) { PCodeOffset = ReadOffset(); return SCRIPT_CONTINUE; } static int CmdIfGoto(void) { int offset; offset = ReadOffset(); if (Pop() != 0) { PCodeOffset = offset; } return SCRIPT_CONTINUE; } static int CmdDrop(void) { Drop(); return SCRIPT_CONTINUE; } static int CmdDelay(void) { ACScript->delayCount = Pop(); return SCRIPT_STOP; } static int CmdDelayDirect(void) { ACScript->delayCount = ReadCodeInt(); return SCRIPT_STOP; } static int CmdRandom(void) { int low; int high; high = Pop(); low = Pop(); Push(low + (P_Random() % (high - low + 1))); return SCRIPT_CONTINUE; } static int CmdRandomDirect(void) { int low; int high; low = ReadCodeInt(); high = ReadCodeInt(); Push(low + (P_Random() % (high - low + 1))); return SCRIPT_CONTINUE; } static int CmdThingCount(void) { int tid; tid = Pop(); ThingCount(Pop(), tid); return SCRIPT_CONTINUE; } static int CmdThingCountDirect(void) { int type; type = ReadCodeInt(); ThingCount(type, ReadCodeInt()); return SCRIPT_CONTINUE; } static void ThingCount(int type, int tid) { int count; int searcher; mobj_t *mobj; mobjtype_t moType; thinker_t *think; if (!(type + tid)) { // Nothing to count return; } moType = TranslateThingType[type]; count = 0; searcher = -1; if (tid) { // Count TID things while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if (type == 0) { // Just count TIDs count++; } else if (moType == mobj->type) { if (mobj->flags & MF_COUNTKILL && mobj->health <= 0) { // Don't count dead monsters continue; } count++; } } } else { // Count only types for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mobj = (mobj_t *) think; if (mobj->type != moType) { // Doesn't match continue; } if (mobj->flags & MF_COUNTKILL && mobj->health <= 0) { // Don't count dead monsters continue; } count++; } } Push(count); } static int CmdTagWait(void) { ACSInfo[ACScript->infoIndex].waitValue = Pop(); ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG; return SCRIPT_STOP; } static int CmdTagWaitDirect(void) { ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt(); ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG; return SCRIPT_STOP; } static int CmdPolyWait(void) { ACSInfo[ACScript->infoIndex].waitValue = Pop(); ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY; return SCRIPT_STOP; } static int CmdPolyWaitDirect(void) { ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt(); ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY; return SCRIPT_STOP; } static int CmdChangeFloor(void) { int tag; int flat; int sectorIndex; flat = R_FlatNumForName(StringLookup(Pop())); tag = Pop(); sectorIndex = -1; while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { sectors[sectorIndex].floorpic = flat; } return SCRIPT_CONTINUE; } static int CmdChangeFloorDirect(void) { int tag; int flat; int sectorIndex; tag = ReadCodeInt(); flat = R_FlatNumForName(StringLookup(ReadCodeInt())); sectorIndex = -1; while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { sectors[sectorIndex].floorpic = flat; } return SCRIPT_CONTINUE; } static int CmdChangeCeiling(void) { int tag; int flat; int sectorIndex; flat = R_FlatNumForName(StringLookup(Pop())); tag = Pop(); sectorIndex = -1; while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { sectors[sectorIndex].ceilingpic = flat; } return SCRIPT_CONTINUE; } static int CmdChangeCeilingDirect(void) { int tag; int flat; int sectorIndex; tag = ReadCodeInt(); flat = R_FlatNumForName(StringLookup(ReadCodeInt())); sectorIndex = -1; while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { sectors[sectorIndex].ceilingpic = flat; } return SCRIPT_CONTINUE; } static int CmdRestart(void) { PCodeOffset = ACSInfo[ACScript->infoIndex].offset; return SCRIPT_CONTINUE; } static int CmdAndLogical(void) { Push(Pop() && Pop()); return SCRIPT_CONTINUE; } static int CmdOrLogical(void) { Push(Pop() || Pop()); return SCRIPT_CONTINUE; } static int CmdAndBitwise(void) { Push(Pop() & Pop()); return SCRIPT_CONTINUE; } static int CmdOrBitwise(void) { Push(Pop() | Pop()); return SCRIPT_CONTINUE; } static int CmdEorBitwise(void) { Push(Pop() ^ Pop()); return SCRIPT_CONTINUE; } static int CmdNegateLogical(void) { Push(!Pop()); return SCRIPT_CONTINUE; } static int CmdLShift(void) { int operand2; operand2 = Pop(); Push(Pop() << operand2); return SCRIPT_CONTINUE; } static int CmdRShift(void) { int operand2; operand2 = Pop(); Push(Pop() >> operand2); return SCRIPT_CONTINUE; } static int CmdUnaryMinus(void) { Push(-Pop()); return SCRIPT_CONTINUE; } static int CmdIfNotGoto(void) { int offset; offset = ReadOffset(); if (Pop() == 0) { PCodeOffset = offset; } return SCRIPT_CONTINUE; } static int CmdLineSide(void) { Push(ACScript->side); return SCRIPT_CONTINUE; } static int CmdScriptWait(void) { ACSInfo[ACScript->infoIndex].waitValue = Pop(); ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT; return SCRIPT_STOP; } static int CmdScriptWaitDirect(void) { ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt(); ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT; return SCRIPT_STOP; } static int CmdClearLineSpecial(void) { if (ACScript->line) { ACScript->line->special = 0; } return SCRIPT_CONTINUE; } static int CmdCaseGoto(void) { int value; int offset; value = ReadCodeInt(); offset = ReadOffset(); if (Top() == value) { PCodeOffset = offset; Drop(); } return SCRIPT_CONTINUE; } static int CmdBeginPrint(void) { *PrintBuffer = 0; return SCRIPT_CONTINUE; } static int CmdEndPrint(void) { player_t *player; if (ACScript->activator && ACScript->activator->player) { player = ACScript->activator->player; } else { player = &players[consoleplayer]; } P_SetMessage(player, PrintBuffer, true); return SCRIPT_CONTINUE; } static int CmdEndPrintBold(void) { int i; for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { P_SetYellowMessage(&players[i], PrintBuffer, true); } } return SCRIPT_CONTINUE; } static int CmdPrintString(void) { M_StringConcat(PrintBuffer, StringLookup(Pop()), sizeof(PrintBuffer)); return SCRIPT_CONTINUE; } static int CmdPrintNumber(void) { char tempStr[16]; M_snprintf(tempStr, sizeof(tempStr), "%d", Pop()); M_StringConcat(PrintBuffer, tempStr, sizeof(PrintBuffer)); return SCRIPT_CONTINUE; } static int CmdPrintCharacter(void) { char tempStr[2]; tempStr[0] = Pop(); tempStr[1] = '\0'; M_StringConcat(PrintBuffer, tempStr, sizeof(PrintBuffer)); return SCRIPT_CONTINUE; } static int CmdPlayerCount(void) { int i; int count; count = 0; for (i = 0; i < maxplayers; i++) { count += playeringame[i]; } Push(count); return SCRIPT_CONTINUE; } static int CmdGameType(void) { int gametype; if (netgame == false) { gametype = GAME_SINGLE_PLAYER; } else if (deathmatch) { gametype = GAME_NET_DEATHMATCH; } else { gametype = GAME_NET_COOPERATIVE; } Push(gametype); return SCRIPT_CONTINUE; } static int CmdGameSkill(void) { Push(gameskill); return SCRIPT_CONTINUE; } static int CmdTimer(void) { Push(leveltime); return SCRIPT_CONTINUE; } static int CmdSectorSound(void) { int volume; mobj_t *mobj; mobj = NULL; if (ACScript->line) { mobj = (mobj_t *) & ACScript->line->frontsector->soundorg; } volume = Pop(); S_StartSoundAtVolume(mobj, S_GetSoundID(StringLookup(Pop())), volume); return SCRIPT_CONTINUE; } static int CmdThingSound(void) { int tid; int sound; int volume; mobj_t *mobj; int searcher; volume = Pop(); sound = S_GetSoundID(StringLookup(Pop())); tid = Pop(); searcher = -1; while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { S_StartSoundAtVolume(mobj, sound, volume); } return SCRIPT_CONTINUE; } static int CmdAmbientSound(void) { int volume; volume = Pop(); S_StartSoundAtVolume(NULL, S_GetSoundID(StringLookup(Pop())), volume); return SCRIPT_CONTINUE; } static int CmdSoundSequence(void) { mobj_t *mobj; mobj = NULL; if (ACScript->line) { mobj = (mobj_t *) & ACScript->line->frontsector->soundorg; } SN_StartSequenceName(mobj, StringLookup(Pop())); return SCRIPT_CONTINUE; } static int CmdSetLineTexture(void) { line_t *line; int lineTag; int side; int position; int texture; int searcher; texture = R_TextureNumForName(StringLookup(Pop())); position = Pop(); side = Pop(); lineTag = Pop(); searcher = -1; while ((line = P_FindLine(lineTag, &searcher)) != NULL) { if (position == TEXTURE_MIDDLE) { sides[line->sidenum[side]].midtexture = texture; } else if (position == TEXTURE_BOTTOM) { sides[line->sidenum[side]].bottomtexture = texture; } else { // TEXTURE_TOP sides[line->sidenum[side]].toptexture = texture; } } return SCRIPT_CONTINUE; } static int CmdSetLineBlocking(void) { line_t *line; int lineTag; boolean blocking; int searcher; blocking = Pop()? ML_BLOCKING : 0; lineTag = Pop(); searcher = -1; while ((line = P_FindLine(lineTag, &searcher)) != NULL) { line->flags = (line->flags & ~ML_BLOCKING) | blocking; } return SCRIPT_CONTINUE; } static int CmdSetLineSpecial(void) { line_t *line; int lineTag; int special, arg1, arg2, arg3, arg4, arg5; int searcher; arg5 = Pop(); arg4 = Pop(); arg3 = Pop(); arg2 = Pop(); arg1 = Pop(); special = Pop(); lineTag = Pop(); searcher = -1; while ((line = P_FindLine(lineTag, &searcher)) != NULL) { line->special = special; line->arg1 = arg1; line->arg2 = arg2; line->arg3 = arg3; line->arg4 = arg4; line->arg5 = arg5; } return SCRIPT_CONTINUE; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_anim.c000066400000000000000000000321101360717211000215360ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "m_random.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ #define ANIM_SCRIPT_NAME "ANIMDEFS" #define MAX_ANIM_DEFS 20 #define MAX_FRAME_DEFS 96 #define ANIM_FLAT 0 #define ANIM_TEXTURE 1 #define SCI_FLAT "flat" #define SCI_TEXTURE "texture" #define SCI_PIC "pic" #define SCI_TICS "tics" #define SCI_RAND "rand" #define LIGHTNING_SPECIAL 198 #define LIGHTNING_SPECIAL2 199 #define SKYCHANGE_SPECIAL 200 // TYPES ------------------------------------------------------------------- typedef struct { int index; int tics; } frameDef_t; typedef struct { int type; int index; int tics; int currentFrameDef; int startFrameDef; int endFrameDef; } animDef_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void P_LightningFlash(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern fixed_t Sky1ColumnOffset; extern fixed_t Sky2ColumnOffset; extern boolean DoubleSky; // PUBLIC DATA DEFINITIONS ------------------------------------------------- fixed_t Sky1ScrollDelta; fixed_t Sky2ScrollDelta; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static animDef_t AnimDefs[MAX_ANIM_DEFS]; static frameDef_t FrameDefs[MAX_FRAME_DEFS]; static int AnimDefCount; static boolean LevelHasLightning; static int NextLightningFlash; static int LightningFlash; static int *LightningLightLevels; // CODE -------------------------------------------------------------------- //========================================================================== // // P_AnimateSurfaces // //========================================================================== void P_AnimateSurfaces(void) { int i; animDef_t *ad; line_t *line; // Animate flats and textures for (i = 0; i < AnimDefCount; i++) { ad = &AnimDefs[i]; ad->tics--; if (ad->tics == 0) { if (ad->currentFrameDef == ad->endFrameDef) { ad->currentFrameDef = ad->startFrameDef; } else { ad->currentFrameDef++; } ad->tics = FrameDefs[ad->currentFrameDef].tics; if (ad->tics > 255) { // Random tics ad->tics = (ad->tics >> 16) + P_Random() % ((ad->tics & 0xff00) >> 8); } if (ad->type == ANIM_FLAT) { flattranslation[ad->index] = FrameDefs[ad->currentFrameDef].index; } else { // Texture texturetranslation[ad->index] = FrameDefs[ad->currentFrameDef].index; } } } // Update scrolling textures for (i = 0; i < numlinespecials; i++) { line = linespeciallist[i]; switch (line->special) { case 100: // Scroll_Texture_Left sides[line->sidenum[0]].textureoffset += line->arg1 << 10; break; case 101: // Scroll_Texture_Right sides[line->sidenum[0]].textureoffset -= line->arg1 << 10; break; case 102: // Scroll_Texture_Up sides[line->sidenum[0]].rowoffset += line->arg1 << 10; break; case 103: // Scroll_Texture_Down sides[line->sidenum[0]].rowoffset -= line->arg1 << 10; break; } } // Update sky column offsets Sky1ColumnOffset += Sky1ScrollDelta; Sky2ColumnOffset += Sky2ScrollDelta; if (LevelHasLightning) { if (!NextLightningFlash || LightningFlash) { P_LightningFlash(); } else { NextLightningFlash--; } } } //========================================================================== // // P_LightningFlash // //========================================================================== static void P_LightningFlash(void) { int i; sector_t *tempSec; int *tempLight; boolean foundSec; int flashLight; if (LightningFlash) { LightningFlash--; if (LightningFlash) { tempLight = LightningLightLevels; tempSec = sectors; for (i = 0; i < numsectors; i++, tempSec++) { if (tempSec->ceilingpic == skyflatnum || tempSec->special == LIGHTNING_SPECIAL || tempSec->special == LIGHTNING_SPECIAL2) { if (*tempLight < tempSec->lightlevel - 4) { tempSec->lightlevel -= 4; } tempLight++; } } } else { // remove the alternate lightning flash special tempLight = LightningLightLevels; tempSec = sectors; for (i = 0; i < numsectors; i++, tempSec++) { if (tempSec->ceilingpic == skyflatnum || tempSec->special == LIGHTNING_SPECIAL || tempSec->special == LIGHTNING_SPECIAL2) { tempSec->lightlevel = *tempLight; tempLight++; } } Sky1Texture = P_GetMapSky1Texture(gamemap); } return; } LightningFlash = (P_Random() & 7) + 8; flashLight = 200 + (P_Random() & 31); tempSec = sectors; tempLight = LightningLightLevels; foundSec = false; for (i = 0; i < numsectors; i++, tempSec++) { if (tempSec->ceilingpic == skyflatnum || tempSec->special == LIGHTNING_SPECIAL || tempSec->special == LIGHTNING_SPECIAL2) { *tempLight = tempSec->lightlevel; if (tempSec->special == LIGHTNING_SPECIAL) { tempSec->lightlevel += 64; if (tempSec->lightlevel > flashLight) { tempSec->lightlevel = flashLight; } } else if (tempSec->special == LIGHTNING_SPECIAL2) { tempSec->lightlevel += 32; if (tempSec->lightlevel > flashLight) { tempSec->lightlevel = flashLight; } } else { tempSec->lightlevel = flashLight; } if (tempSec->lightlevel < *tempLight) { tempSec->lightlevel = *tempLight; } tempLight++; foundSec = true; } } if (foundSec) { Sky1Texture = P_GetMapSky2Texture(gamemap); // set alternate sky S_StartSound(NULL, SFX_THUNDER_CRASH); } // Calculate the next lighting flash if (!NextLightningFlash) { if (P_Random() < 50) { // Immediate Quick flash NextLightningFlash = (P_Random() & 15) + 16; } else { if (P_Random() < 128 && !(leveltime & 32)) { NextLightningFlash = ((P_Random() & 7) + 2) * 35; } else { NextLightningFlash = ((P_Random() & 15) + 5) * 35; } } } } //========================================================================== // // P_ForceLightning // //========================================================================== void P_ForceLightning(void) { NextLightningFlash = 0; } //========================================================================== // // P_InitLightning // //========================================================================== void P_InitLightning(void) { int i; int secCount; if (!P_GetMapLightning(gamemap)) { LevelHasLightning = false; LightningFlash = 0; return; } LightningFlash = 0; secCount = 0; for (i = 0; i < numsectors; i++) { if (sectors[i].ceilingpic == skyflatnum || sectors[i].special == LIGHTNING_SPECIAL || sectors[i].special == LIGHTNING_SPECIAL2) { secCount++; } } if (secCount) { LevelHasLightning = true; } else { LevelHasLightning = false; return; } LightningLightLevels = (int *) Z_Malloc(secCount * sizeof(int), PU_LEVEL, NULL); NextLightningFlash = ((P_Random() & 15) + 5) * 35; // don't flash at level start } //========================================================================== // // P_InitFTAnims // // Initialize flat and texture animation lists. // //========================================================================== void P_InitFTAnims(void) { int base; int mod; int fd; animDef_t *ad; boolean ignore; boolean done; fd = 0; ad = AnimDefs; AnimDefCount = 0; SC_Open(ANIM_SCRIPT_NAME); while (SC_GetString()) { if (AnimDefCount == MAX_ANIM_DEFS) { I_Error("P_InitFTAnims: too many AnimDefs."); } if (SC_Compare(SCI_FLAT)) { ad->type = ANIM_FLAT; } else if (SC_Compare(SCI_TEXTURE)) { ad->type = ANIM_TEXTURE; } else { SC_ScriptError(NULL); } SC_MustGetString(); // Name ignore = false; if (ad->type == ANIM_FLAT) { if (W_CheckNumForName(sc_String) == -1) { ignore = true; } else { ad->index = R_FlatNumForName(sc_String); } } else { // Texture if (R_CheckTextureNumForName(sc_String) == -1) { ignore = true; } else { ad->index = R_TextureNumForName(sc_String); } } ad->startFrameDef = fd; done = false; while (done == false) { if (SC_GetString()) { if (SC_Compare(SCI_PIC)) { if (fd == MAX_FRAME_DEFS) { I_Error("P_InitFTAnims: too many FrameDefs."); } SC_MustGetNumber(); if (ignore == false) { FrameDefs[fd].index = ad->index + sc_Number - 1; } SC_MustGetString(); if (SC_Compare(SCI_TICS)) { SC_MustGetNumber(); if (ignore == false) { FrameDefs[fd].tics = sc_Number; fd++; } } else if (SC_Compare(SCI_RAND)) { SC_MustGetNumber(); base = sc_Number; SC_MustGetNumber(); if (ignore == false) { mod = sc_Number - base + 1; FrameDefs[fd].tics = (base << 16) + (mod << 8); fd++; } } else { SC_ScriptError(NULL); } } else { SC_UnGet(); done = true; } } else { done = true; } } if ((ignore == false) && (fd - ad->startFrameDef < 2)) { I_Error("P_InitFTAnims: AnimDef has framecount < 2."); } if (ignore == false) { ad->endFrameDef = fd - 1; ad->currentFrameDef = ad->endFrameDef; ad->tics = 1; // Force 1st game tic to animate AnimDefCount++; ad++; } } SC_Close(); } crispy-doom-crispy-doom-5.6.4/src/hexen/p_ceilng.c000066400000000000000000000227571360717211000220730ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "p_local.h" //================================================================== //================================================================== // // CEILINGS // //================================================================== //================================================================== ceiling_t *activeceilings[MAXCEILINGS]; //================================================================== // // T_MoveCeiling // //================================================================== void T_MoveCeiling(ceiling_t * ceiling) { result_e res; switch (ceiling->direction) { // case 0: // IN STASIS // break; case 1: // UP res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false, 1, ceiling->direction); if (res == RES_PASTDEST) { SN_StopSequence((mobj_t *) & ceiling->sector->soundorg); switch (ceiling->type) { case CLEV_CRUSHANDRAISE: ceiling->direction = -1; ceiling->speed = ceiling->speed * 2; break; default: P_RemoveActiveCeiling(ceiling); break; } } break; case -1: // DOWN res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush, 1, ceiling->direction); if (res == RES_PASTDEST) { SN_StopSequence((mobj_t *) & ceiling->sector->soundorg); switch (ceiling->type) { case CLEV_CRUSHANDRAISE: case CLEV_CRUSHRAISEANDSTAY: ceiling->direction = 1; ceiling->speed = ceiling->speed / 2; break; default: P_RemoveActiveCeiling(ceiling); break; } } else if (res == RES_CRUSHED) { switch (ceiling->type) { case CLEV_CRUSHANDRAISE: case CLEV_LOWERANDCRUSH: case CLEV_CRUSHRAISEANDSTAY: //ceiling->speed = ceiling->speed/4; break; default: break; } } break; } } //================================================================== // // EV_DoCeiling // Move a ceiling up/down and all around! // //================================================================== int EV_DoCeiling(line_t * line, byte * arg, ceiling_e type) { int secnum, rtn; sector_t *sec; ceiling_t *ceiling; secnum = -1; rtn = 0; /* Old Ceiling stasis code // // Reactivate in-stasis ceilings...for certain types. // switch(type) { case CLEV_CRUSHANDRAISE: P_ActivateInStasisCeiling(line); default: break; } */ while ((secnum = P_FindSectorFromTag(arg[0], secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // // new door thinker // rtn = 1; ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVSPEC, 0); P_AddThinker(&ceiling->thinker); sec->specialdata = ceiling; ceiling->thinker.function = T_MoveCeiling; ceiling->sector = sec; ceiling->crush = 0; ceiling->speed = arg[1] * (FRACUNIT / 8); switch (type) { case CLEV_CRUSHRAISEANDSTAY: ceiling->crush = arg[2]; // arg[2] = crushing value ceiling->topheight = sec->ceilingheight; ceiling->bottomheight = sec->floorheight + (8 * FRACUNIT); ceiling->direction = -1; break; case CLEV_CRUSHANDRAISE: ceiling->topheight = sec->ceilingheight; case CLEV_LOWERANDCRUSH: ceiling->crush = arg[2]; // arg[2] = crushing value case CLEV_LOWERTOFLOOR: ceiling->bottomheight = sec->floorheight; if (type != CLEV_LOWERTOFLOOR) { ceiling->bottomheight += 8 * FRACUNIT; } ceiling->direction = -1; break; case CLEV_RAISETOHIGHEST: ceiling->topheight = P_FindHighestCeilingSurrounding(sec); ceiling->direction = 1; break; case CLEV_LOWERBYVALUE: ceiling->bottomheight = sec->ceilingheight - arg[2] * FRACUNIT; ceiling->direction = -1; break; case CLEV_RAISEBYVALUE: ceiling->topheight = sec->ceilingheight + arg[2] * FRACUNIT; ceiling->direction = 1; break; case CLEV_MOVETOVALUETIMES8: { int destHeight = arg[2] * FRACUNIT * 8; if (arg[3]) { destHeight = -destHeight; } if (sec->ceilingheight <= destHeight) { ceiling->direction = 1; ceiling->topheight = destHeight; if (sec->ceilingheight == destHeight) { rtn = 0; } } else if (sec->ceilingheight > destHeight) { ceiling->direction = -1; ceiling->bottomheight = destHeight; } break; } default: rtn = 0; break; } ceiling->tag = sec->tag; ceiling->type = type; P_AddActiveCeiling(ceiling); if (rtn) { SN_StartSequence((mobj_t *) & ceiling->sector->soundorg, SEQ_PLATFORM + ceiling->sector->seqType); } } return rtn; } //================================================================== // // Add an active ceiling // //================================================================== void P_AddActiveCeiling(ceiling_t * c) { int i; for (i = 0; i < MAXCEILINGS; i++) if (activeceilings[i] == NULL) { activeceilings[i] = c; return; } } //================================================================== // // Remove a ceiling's thinker // //================================================================== void P_RemoveActiveCeiling(ceiling_t * c) { int i; for (i = 0; i < MAXCEILINGS; i++) if (activeceilings[i] == c) { activeceilings[i]->sector->specialdata = NULL; P_RemoveThinker(&activeceilings[i]->thinker); P_TagFinished(activeceilings[i]->sector->tag); activeceilings[i] = NULL; break; } } #if 0 //================================================================== // // Restart a ceiling that's in-stasis // //================================================================== void P_ActivateInStasisCeiling(line_t * line) { int i; for (i = 0; i < MAXCEILINGS; i++) if (activeceilings[i] && (activeceilings[i]->tag == line->arg1) && (activeceilings[i]->direction == 0)) { activeceilings[i]->direction = activeceilings[i]->olddirection; activeceilings[i]->thinker.function = T_MoveCeiling; SN_StartSequence((mobj_t *) & activeceilings[i]->sector->soundorg, SEQ_PLATFORM + activeceilings[i]->sector->seqType); } } #endif //================================================================== // // EV_CeilingCrushStop // Stop a ceiling from crushing! // //================================================================== int EV_CeilingCrushStop(line_t * line, byte * args) { int i; int rtn; rtn = 0; for (i = 0; i < MAXCEILINGS; i++) { if (activeceilings[i] && activeceilings[i]->tag == args[0]) { rtn = 1; SN_StopSequence((mobj_t *) & activeceilings[i]->sector->soundorg); activeceilings[i]->sector->specialdata = NULL; P_RemoveThinker(&activeceilings[i]->thinker); P_TagFinished(activeceilings[i]->sector->tag); activeceilings[i] = NULL; break; } } return rtn; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_doors.c000066400000000000000000000226221360717211000217470ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "p_local.h" //================================================================== //================================================================== // // VERTICAL DOORS // //================================================================== //================================================================== //================================================================== // // T_VerticalDoor // //================================================================== void T_VerticalDoor(vldoor_t * door) { result_e res; switch (door->direction) { case 0: // WAITING if (!--door->topcountdown) switch (door->type) { case DREV_NORMAL: door->direction = -1; // time to go back down SN_StartSequence((mobj_t *) & door->sector->soundorg, SEQ_DOOR_STONE + door->sector->seqType); break; case DREV_CLOSE30THENOPEN: door->direction = 1; break; default: break; } break; case 2: // INITIAL WAIT if (!--door->topcountdown) { switch (door->type) { case DREV_RAISEIN5MINS: door->direction = 1; door->type = DREV_NORMAL; break; default: break; } } break; case -1: // DOWN res = T_MovePlane(door->sector, door->speed, door->sector->floorheight, false, 1, door->direction); if (res == RES_PASTDEST) { SN_StopSequence((mobj_t *) & door->sector->soundorg); switch (door->type) { case DREV_NORMAL: case DREV_CLOSE: door->sector->specialdata = NULL; P_TagFinished(door->sector->tag); P_RemoveThinker(&door->thinker); // unlink and free break; case DREV_CLOSE30THENOPEN: door->direction = 0; door->topcountdown = 35 * 30; break; default: break; } } else if (res == RES_CRUSHED) { switch (door->type) { case DREV_CLOSE: // DON'T GO BACK UP! break; default: door->direction = 1; break; } } break; case 1: // UP res = T_MovePlane(door->sector, door->speed, door->topheight, false, 1, door->direction); if (res == RES_PASTDEST) { SN_StopSequence((mobj_t *) & door->sector->soundorg); switch (door->type) { case DREV_NORMAL: door->direction = 0; // wait at top door->topcountdown = door->topwait; break; case DREV_CLOSE30THENOPEN: case DREV_OPEN: door->sector->specialdata = NULL; P_TagFinished(door->sector->tag); P_RemoveThinker(&door->thinker); // unlink and free break; default: break; } } break; } } //---------------------------------------------------------------------------- // // EV_DoDoor // // Move a door up/down // //---------------------------------------------------------------------------- int EV_DoDoor(line_t * line, byte * args, vldoor_e type) { int secnum; int retcode; sector_t *sec; vldoor_t *door; fixed_t speed; speed = args[1] * FRACUNIT / 8; secnum = -1; retcode = 0; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) { continue; } // Add new door thinker retcode = 1; door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; door->thinker.function = T_VerticalDoor; door->sector = sec; switch (type) { case DREV_CLOSE: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4 * FRACUNIT; door->direction = -1; break; case DREV_CLOSE30THENOPEN: door->topheight = sec->ceilingheight; door->direction = -1; break; case DREV_NORMAL: case DREV_OPEN: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4 * FRACUNIT; break; default: break; } door->type = type; door->speed = speed; door->topwait = args[2]; // line->arg3 SN_StartSequence((mobj_t *) & door->sector->soundorg, SEQ_DOOR_STONE + door->sector->seqType); } return (retcode); } //================================================================== // // EV_VerticalDoor : open a door manually, no tag value // //================================================================== boolean EV_VerticalDoor(line_t * line, mobj_t * thing) { sector_t *sec; vldoor_t *door; int side; side = 0; // only front sides can be used // if the sector has an active thinker, use it sec = sides[line->sidenum[side ^ 1]].sector; if (sec->specialdata) { return false; /* door = sec->specialdata; switch(line->special) { // only for raise doors case 12: if(door->direction == -1) { door->direction = 1; // go back up } else { if(!thing->player) { // Monsters don't close doors return; } door->direction = -1; // start going down immediately } return; } */ } // // new door thinker // door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 1; switch (line->special) { case 11: door->type = DREV_OPEN; line->special = 0; break; case 12: case 13: door->type = DREV_NORMAL; break; default: door->type = DREV_NORMAL; break; } door->speed = line->arg2 * (FRACUNIT / 8); door->topwait = line->arg3; // // find the top and bottom of the movement range // door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4 * FRACUNIT; SN_StartSequence((mobj_t *) & door->sector->soundorg, SEQ_DOOR_STONE + door->sector->seqType); return true; } //================================================================== // // Spawn a door that closes after 30 seconds // //================================================================== /* void P_SpawnDoorCloseIn30(sector_t *sec) { vldoor_t *door; door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 0; door->type = DREV_NORMAL; door->speed = VDOORSPEED; door->topcountdown = 30*35; } */ //================================================================== // // Spawn a door that opens after 5 minutes // //================================================================== /* void P_SpawnDoorRaiseIn5Mins(sector_t *sec, int secnum) { vldoor_t *door; door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0); P_AddThinker(&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 2; door->type = DREV_RAISEIN5MINS; door->speed = VDOORSPEED; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->topwait = VDOORWAIT; door->topcountdown = 5*60*35; } */ crispy-doom-crispy-doom-5.6.4/src/hexen/p_enemy.c000066400000000000000000004272021360717211000217410ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "m_random.h" #include "i_system.h" #include "i_swap.h" #include "p_local.h" #include "s_sound.h" // Macros // Types // Private Data // External Data extern fixed_t FloatBobOffsets[64]; //---------------------------------------------------------------------------- // // PROC P_RecursiveSound // //---------------------------------------------------------------------------- mobj_t *soundtarget; void P_RecursiveSound(sector_t * sec, int soundblocks) { int i; line_t *check; sector_t *other; // Wake up all monsters in this sector if (sec->validcount == validcount && sec->soundtraversed <= soundblocks + 1) { // Already flooded return; } sec->validcount = validcount; sec->soundtraversed = soundblocks + 1; sec->soundtarget = soundtarget; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; if (!(check->flags & ML_TWOSIDED)) { continue; } P_LineOpening(check); if (openrange <= 0) { // Closed door continue; } if (sides[check->sidenum[0]].sector == sec) { other = sides[check->sidenum[1]].sector; } else { other = sides[check->sidenum[0]].sector; } if (check->flags & ML_SOUNDBLOCK) { if (!soundblocks) { P_RecursiveSound(other, 1); } } else { P_RecursiveSound(other, soundblocks); } } } //---------------------------------------------------------------------------- // // PROC P_NoiseAlert // // If a monster yells at a player, it will alert other monsters to the // player. // //---------------------------------------------------------------------------- void P_NoiseAlert(mobj_t * target, mobj_t * emmiter) { soundtarget = target; validcount++; P_RecursiveSound(emmiter->subsector->sector, 0); } //---------------------------------------------------------------------------- // // FUNC P_CheckMeleeRange // //---------------------------------------------------------------------------- boolean P_CheckMeleeRange(mobj_t * actor) { mobj_t *mo; fixed_t dist; if (!actor->target) { return (false); } mo = actor->target; dist = P_AproxDistance(mo->x - actor->x, mo->y - actor->y); if (dist >= MELEERANGE) { return (false); } if (!P_CheckSight(actor, mo)) { return (false); } if (mo->z > actor->z + actor->height) { // Target is higher than the attacker return (false); } else if (actor->z > mo->z + mo->height) { // Attacker is higher return (false); } return (true); } //---------------------------------------------------------------------------- // // FUNC P_CheckMeleeRange2 // //---------------------------------------------------------------------------- boolean P_CheckMeleeRange2(mobj_t * actor) { mobj_t *mo; fixed_t dist; if (!actor->target) { return (false); } mo = actor->target; dist = P_AproxDistance(mo->x - actor->x, mo->y - actor->y); if (dist >= MELEERANGE * 2 || dist < MELEERANGE) { return (false); } if (!P_CheckSight(actor, mo)) { return (false); } if (mo->z > actor->z + actor->height) { // Target is higher than the attacker return (false); } else if (actor->z > mo->z + mo->height) { // Attacker is higher return (false); } return (true); } //---------------------------------------------------------------------------- // // FUNC P_CheckMissileRange // //---------------------------------------------------------------------------- boolean P_CheckMissileRange(mobj_t * actor) { fixed_t dist; if (!P_CheckSight(actor, actor->target)) { return (false); } if (actor->flags & MF_JUSTHIT) { // The target just hit the enemy, so fight back! actor->flags &= ~MF_JUSTHIT; return (true); } if (actor->reactiontime) { // Don't attack yet return (false); } dist = (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) >> FRACBITS) - 64; if (!actor->info->meleestate) { // No melee attack, so fire more frequently dist -= 128; } if (dist > 200) { dist = 200; } if (P_Random() < dist) { return (false); } return (true); } /* ================ = = P_Move = = Move in the current direction = returns false if the move is blocked ================ */ fixed_t xspeed[8] = { FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000, 0, 47000 }; fixed_t yspeed[8] = { 0, 47000, FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000 }; #define MAXSPECIALCROSS 8 extern line_t *spechit[MAXSPECIALCROSS]; extern int numspechit; boolean P_Move(mobj_t * actor) { fixed_t tryx, tryy; line_t *ld; boolean good; if (actor->flags2 & MF2_BLASTED) return (true); if (actor->movedir == DI_NODIR) { return (false); } tryx = actor->x + actor->info->speed * xspeed[actor->movedir]; tryy = actor->y + actor->info->speed * yspeed[actor->movedir]; if (!P_TryMove(actor, tryx, tryy)) { // open any specials if (actor->flags & MF_FLOAT && floatok) { // must adjust height if (actor->z < tmfloorz) { actor->z += FLOATSPEED; } else { actor->z -= FLOATSPEED; } actor->flags |= MF_INFLOAT; return (true); } if (!numspechit) { return false; } actor->movedir = DI_NODIR; good = false; while (numspechit--) { ld = spechit[numspechit]; // if the special isn't a door that can be opened, return false if (P_ActivateLine(ld, actor, 0, SPAC_USE)) { good = true; } /* Old version before use/cross/impact specials were combined if(P_UseSpecialLine(actor, ld)) { good = true; } */ } return (good); } else { actor->flags &= ~MF_INFLOAT; } if (!(actor->flags & MF_FLOAT)) { if (actor->z > actor->floorz) { P_HitFloor(actor); } actor->z = actor->floorz; } return (true); } //---------------------------------------------------------------------------- // // FUNC P_TryWalk // // Attempts to move actor in its current (ob->moveangle) direction. // If blocked by either a wall or an actor returns FALSE. // If move is either clear of block only by a door, returns TRUE and sets. // If a door is in the way, an OpenDoor call is made to start it opening. // //---------------------------------------------------------------------------- boolean P_TryWalk(mobj_t * actor) { if (!P_Move(actor)) { return (false); } actor->movecount = P_Random() & 15; return (true); } /* ================ = = P_NewChaseDir = ================ */ dirtype_t opposite[] = { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR }; dirtype_t diags[] = { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST }; void P_NewChaseDir(mobj_t * actor) { fixed_t deltax, deltay; dirtype_t d[3]; dirtype_t tdir, olddir, turnaround; if (!actor->target) I_Error("P_NewChaseDir: called with no target"); olddir = actor->movedir; turnaround = opposite[olddir]; deltax = actor->target->x - actor->x; deltay = actor->target->y - actor->y; if (deltax > 10 * FRACUNIT) d[1] = DI_EAST; else if (deltax < -10 * FRACUNIT) d[1] = DI_WEST; else d[1] = DI_NODIR; if (deltay < -10 * FRACUNIT) d[2] = DI_SOUTH; else if (deltay > 10 * FRACUNIT) d[2] = DI_NORTH; else d[2] = DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { actor->movedir = diags[((deltay < 0) << 1) + (deltax > 0)]; if (actor->movedir != turnaround && P_TryWalk(actor)) return; } // try other directions if (P_Random() > 200 || abs(deltay) > abs(deltax)) { tdir = d[1]; d[1] = d[2]; d[2] = tdir; } if (d[1] == turnaround) d[1] = DI_NODIR; if (d[2] == turnaround) d[2] = DI_NODIR; if (d[1] != DI_NODIR) { actor->movedir = d[1]; if (P_TryWalk(actor)) return; /*either moved forward or attacked */ } if (d[2] != DI_NODIR) { actor->movedir = d[2]; if (P_TryWalk(actor)) return; } /* there is no direct path to the player, so pick another direction */ if (olddir != DI_NODIR) { actor->movedir = olddir; if (P_TryWalk(actor)) return; } if (P_Random() & 1) /*randomly determine direction of search */ { for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) { if (tdir != turnaround) { actor->movedir = tdir; if (P_TryWalk(actor)) return; } } } else { tdir = DI_SOUTHEAST; for (;;) { if (tdir != turnaround) { actor->movedir = tdir; if (P_TryWalk(actor)) return; } if (tdir == DI_EAST) { break; } --tdir; } } if (turnaround != DI_NODIR) { actor->movedir = turnaround; if (P_TryWalk(actor)) return; } actor->movedir = DI_NODIR; // can't move } //--------------------------------------------------------------------------- // // FUNC P_LookForMonsters // //--------------------------------------------------------------------------- #define MONS_LOOK_RANGE (16*64*FRACUNIT) #define MONS_LOOK_LIMIT 64 boolean P_LookForMonsters(mobj_t * actor) { int count; mobj_t *mo; thinker_t *think; if (!P_CheckSight(players[0].mo, actor)) { // Player can't see monster return (false); } count = 0; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *) think; if (!(mo->flags & MF_COUNTKILL) || (mo == actor) || (mo->health <= 0)) { // Not a valid monster continue; } if (P_AproxDistance(actor->x - mo->x, actor->y - mo->y) > MONS_LOOK_RANGE) { // Out of range continue; } if (P_Random() < 16) { // Skip continue; } if (count++ > MONS_LOOK_LIMIT) { // Stop searching return (false); } if (!P_CheckSight(actor, mo)) { // Out of sight continue; } if (actor->type == MT_MINOTAUR) { if ((mo->type == MT_MINOTAUR) && (mo->target != actor->special1.p->mo)) { continue; } } // Found a target monster actor->target = mo; return (true); } return (false); } /* ================ = = P_LookForPlayers = = If allaround is false, only look 180 degrees in front = returns true if a player is targeted ================ */ boolean P_LookForPlayers(mobj_t * actor, boolean allaround) { int c; int stop; player_t *player; angle_t an; fixed_t dist; if (!netgame && players[0].health <= 0) { // Single player game and player is dead, look for monsters return (P_LookForMonsters(actor)); } c = 0; // NOTE: This behavior has been changed from the Vanilla behavior, where // an infinite loop can occur if players 0-3 all quit the game. Although // technically this is not what Vanilla does, fixing this is highly // desirable, and having the game simply lock up is not acceptable. // stop = (actor->lastlook - 1) & 3; // for (;; actor->lastlook = (actor->lastlook + 1) & 3) stop = (actor->lastlook + maxplayers - 1) % maxplayers; for (;; actor->lastlook = (actor->lastlook + 1) % maxplayers) { if (!playeringame[actor->lastlook]) continue; if (c++ == 2 || actor->lastlook == stop) return false; // done looking player = &players[actor->lastlook]; if (player->health <= 0) continue; // dead if (!P_CheckSight(actor, player->mo)) continue; // out of sight if (!allaround) { an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; if (an > ANG90 && an < ANG270) { dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y); // if real close, react anyway if (dist > MELEERANGE) continue; // behind back } } if (player->mo->flags & MF_SHADOW) { // Player is invisible if ((P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) > 2 * MELEERANGE) && P_AproxDistance(player->mo->momx, player->mo->momy) < 5 * FRACUNIT) { // Player is sneaking - can't detect return (false); } if (P_Random() < 225) { // Player isn't sneaking, but still didn't detect return (false); } } if (actor->type == MT_MINOTAUR) { if (actor->special1.p == player) { continue; // Don't target master } } actor->target = player->mo; return (true); } return (false); } /* =============================================================================== ACTION ROUTINES =============================================================================== */ /* ============== = = A_Look = = Stay in state until a player is sighted = ============== */ void A_Look(mobj_t * actor) { mobj_t *targ; actor->threshold = 0; // any shot will wake up targ = actor->subsector->sector->soundtarget; if (targ && (targ->flags & MF_SHOOTABLE)) { actor->target = targ; if (actor->flags & MF_AMBUSH) { if (P_CheckSight(actor, actor->target)) goto seeyou; } else goto seeyou; } if (!P_LookForPlayers(actor, false)) return; // go into chase state seeyou: if (actor->info->seesound) { int sound; sound = actor->info->seesound; if (actor->flags2 & MF2_BOSS) { // Full volume S_StartSound(NULL, sound); } else { S_StartSound(actor, sound); } } P_SetMobjState(actor, actor->info->seestate); } /* ============== = = A_Chase = = Actor has a melee attack, so it tries to close as fast as possible = ============== */ void A_Chase(mobj_t * actor) { int delta; if (actor->reactiontime) { actor->reactiontime--; } // Modify target threshold if (actor->threshold) { actor->threshold--; } if (gameskill == sk_nightmare) { // Monsters move faster in nightmare mode actor->tics -= actor->tics / 2; if (actor->tics < 3) { actor->tics = 3; } } // // turn towards movement direction if not there yet // if (actor->movedir < 8) { actor->angle &= (7 << 29); delta = actor->angle - (actor->movedir << 29); if (delta > 0) { actor->angle -= ANG90 / 2; } else if (delta < 0) { actor->angle += ANG90 / 2; } } if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) { // look for a new target if (P_LookForPlayers(actor, true)) { // got a new target return; } P_SetMobjState(actor, actor->info->spawnstate); return; } // // don't attack twice in a row // if (actor->flags & MF_JUSTATTACKED) { actor->flags &= ~MF_JUSTATTACKED; if (gameskill != sk_nightmare) P_NewChaseDir(actor); return; } // // check for melee attack // if (actor->info->meleestate && P_CheckMeleeRange(actor)) { if (actor->info->attacksound) { S_StartSound(actor, actor->info->attacksound); } P_SetMobjState(actor, actor->info->meleestate); return; } // // check for missile attack // if (actor->info->missilestate) { if (gameskill < sk_nightmare && actor->movecount) goto nomissile; if (!P_CheckMissileRange(actor)) goto nomissile; P_SetMobjState(actor, actor->info->missilestate); actor->flags |= MF_JUSTATTACKED; return; } nomissile: // // possibly choose another target // if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target)) { if (P_LookForPlayers(actor, true)) return; // got a new target } // // chase towards player // if (--actor->movecount < 0 || !P_Move(actor)) { P_NewChaseDir(actor); } // // make active sound // if (actor->info->activesound && P_Random() < 3) { if (actor->type == MT_BISHOP && P_Random() < 128) { S_StartSound(actor, actor->info->seesound); } else if (actor->type == MT_PIG) { S_StartSound(actor, SFX_PIG_ACTIVE1 + (P_Random() & 1)); } else if (actor->flags2 & MF2_BOSS) { S_StartSound(NULL, actor->info->activesound); } else { S_StartSound(actor, actor->info->activesound); } } } //---------------------------------------------------------------------------- // // PROC 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) { // Target is a ghost actor->angle += P_SubRandom() << 21; } } //---------------------------------------------------------------------------- // // PROC A_Pain // //---------------------------------------------------------------------------- void A_Pain(mobj_t * actor) { if (actor->info->painsound) { S_StartSound(actor, actor->info->painsound); } } //============================================================================ // // A_SetInvulnerable // //============================================================================ void A_SetInvulnerable(mobj_t * actor) { actor->flags2 |= MF2_INVULNERABLE; } //============================================================================ // // A_UnSetInvulnerable // //============================================================================ void A_UnSetInvulnerable(mobj_t * actor) { actor->flags2 &= ~MF2_INVULNERABLE; } //============================================================================ // // A_SetReflective // //============================================================================ void A_SetReflective(mobj_t * actor) { actor->flags2 |= MF2_REFLECTIVE; if ((actor->type == MT_CENTAUR) || (actor->type == MT_CENTAURLEADER)) { A_SetInvulnerable(actor); } } //============================================================================ // // A_UnSetReflective // //============================================================================ void A_UnSetReflective(mobj_t * actor) { actor->flags2 &= ~MF2_REFLECTIVE; if ((actor->type == MT_CENTAUR) || (actor->type == MT_CENTAURLEADER)) { A_UnSetInvulnerable(actor); } } //---------------------------------------------------------------------------- // // FUNC P_UpdateMorphedMonster // // Returns true if the pig morphs. // //---------------------------------------------------------------------------- boolean P_UpdateMorphedMonster(mobj_t * actor, int tics) { mobj_t *fog; fixed_t x; fixed_t y; fixed_t z; mobjtype_t moType; mobj_t *mo; mobj_t oldMonster; actor->special1.i -= tics; if (actor->special1.i > 0) { return (false); } moType = actor->special2.i; switch (moType) { case MT_WRAITHB: // These must remain morphed case MT_SERPENT: case MT_SERPENTLEADER: case MT_MINOTAUR: return (false); default: break; } x = actor->x; y = actor->y; z = actor->z; oldMonster = *actor; // Save pig vars P_RemoveMobjFromTIDList(actor); P_SetMobjState(actor, S_FREETARGMOBJ); mo = P_SpawnMobj(x, y, z, moType); if (P_TestMobjLocation(mo) == false) { // Didn't fit P_RemoveMobj(mo); mo = P_SpawnMobj(x, y, z, oldMonster.type); mo->angle = oldMonster.angle; mo->flags = oldMonster.flags; mo->health = oldMonster.health; mo->target = oldMonster.target; mo->special = oldMonster.special; mo->special1.i = 5 * 35; // Next try in 5 seconds mo->special2.i = moType; mo->tid = oldMonster.tid; memcpy(mo->args, oldMonster.args, 5); P_InsertMobjIntoTIDList(mo, oldMonster.tid); return (false); } mo->angle = oldMonster.angle; mo->target = oldMonster.target; mo->tid = oldMonster.tid; mo->special = oldMonster.special; memcpy(mo->args, oldMonster.args, 5); P_InsertMobjIntoTIDList(mo, oldMonster.tid); fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, SFX_TELEPORT); return (true); } //---------------------------------------------------------------------------- // // PROC A_PigLook // //---------------------------------------------------------------------------- void A_PigLook(mobj_t * actor) { if (P_UpdateMorphedMonster(actor, 10)) { return; } A_Look(actor); } //---------------------------------------------------------------------------- // // PROC A_PigChase // //---------------------------------------------------------------------------- void A_PigChase(mobj_t * actor) { if (P_UpdateMorphedMonster(actor, 3)) { return; } A_Chase(actor); } //============================================================================ // // A_PigAttack // //============================================================================ void A_PigAttack(mobj_t * actor) { if (P_UpdateMorphedMonster(actor, 18)) { return; } if (!actor->target) { return; } if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, 2 + (P_Random() & 1)); S_StartSound(actor, SFX_PIG_ATTACK); } } //============================================================================ // // A_PigPain // //============================================================================ void A_PigPain(mobj_t * actor) { A_Pain(actor); if (actor->z <= actor->floorz) { actor->momz = 3.5 * FRACUNIT; } } void FaceMovementDirection(mobj_t * actor) { switch (actor->movedir) { case DI_EAST: actor->angle = 0 << 24; break; case DI_NORTHEAST: actor->angle = 32 << 24; break; case DI_NORTH: actor->angle = 64 << 24; break; case DI_NORTHWEST: actor->angle = 96 << 24; break; case DI_WEST: actor->angle = 128 << 24; break; case DI_SOUTHWEST: actor->angle = 160 << 24; break; case DI_SOUTH: actor->angle = 192 << 24; break; case DI_SOUTHEAST: actor->angle = 224 << 24; break; } } //---------------------------------------------------------------------------- // // Minotaur variables // // special1 pointer to player that spawned it (mobj_t) // special2 internal to minotaur AI // args[0] args[0]-args[3] together make up minotaur start time // args[1] | // args[2] | // args[3] V // args[4] charge duration countdown //---------------------------------------------------------------------------- void A_MinotaurFade0(mobj_t * actor) { actor->flags &= ~MF_ALTSHADOW; actor->flags |= MF_SHADOW; } void A_MinotaurFade1(mobj_t * actor) { // Second level of transparency actor->flags &= ~MF_SHADOW; actor->flags |= MF_ALTSHADOW; } void A_MinotaurFade2(mobj_t * actor) { // Make fully visible actor->flags &= ~MF_SHADOW; actor->flags &= ~MF_ALTSHADOW; } //---------------------------------------------------------------------------- // // A_MinotaurRoam - // // //---------------------------------------------------------------------------- void A_MinotaurLook(mobj_t * actor); // Check the age of the minotaur and stomp it after MAULATORTICS of time // have passed. Returns false if killed. static boolean CheckMinotaurAge(mobj_t *mo) { unsigned int starttime; // The start time is stored in the mobj_t structure, but it is stored // in little endian format. For Vanilla savegame compatibility we must // swap it to the native endianness. memcpy(&starttime, mo->args, sizeof(unsigned int)); if (leveltime - LONG(starttime) >= MAULATORTICS) { P_DamageMobj(mo, NULL, NULL, 10000); return false; } return true; } void A_MinotaurRoam(mobj_t * actor) { actor->flags &= ~MF_SHADOW; // In case pain caused him to actor->flags &= ~MF_ALTSHADOW; // skip his fade in. if (!CheckMinotaurAge(actor)) { return; } if (P_Random() < 30) A_MinotaurLook(actor); // adjust to closest target if (P_Random() < 6) { //Choose new direction actor->movedir = P_Random() % 8; FaceMovementDirection(actor); } if (!P_Move(actor)) { // Turn if (P_Random() & 1) actor->movedir = (actor->movedir + 1) % 8; else actor->movedir = (actor->movedir + 7) % 8; FaceMovementDirection(actor); } } //---------------------------------------------------------------------------- // // PROC A_MinotaurLook // // Look for enemy of player //---------------------------------------------------------------------------- #define MINOTAUR_LOOK_DIST (16*54*FRACUNIT) void A_MinotaurLook(mobj_t * actor) { mobj_t *mo = NULL; player_t *player; thinker_t *think; fixed_t dist; int i; mobj_t *master = actor->special1.m; actor->target = NULL; if (deathmatch) // Quick search for players { for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) continue; player = &players[i]; mo = player->mo; if (mo == master) continue; if (mo->health <= 0) continue; dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y); if (dist > MINOTAUR_LOOK_DIST) continue; actor->target = mo; break; } } if (!actor->target) // Near player monster search { if (master && (master->health > 0) && (master->player)) mo = P_RoughMonsterSearch(master, 20); else mo = P_RoughMonsterSearch(actor, 20); actor->target = mo; } if (!actor->target) // Normal monster search { for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) continue; mo = (mobj_t *) think; if (!(mo->flags & MF_COUNTKILL)) continue; if (mo->health <= 0) continue; if (!(mo->flags & MF_SHOOTABLE)) continue; dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y); if (dist > MINOTAUR_LOOK_DIST) continue; if ((mo == master) || (mo == actor)) continue; if ((mo->type == MT_MINOTAUR) && (mo->special1.m == actor->special1.m)) continue; actor->target = mo; break; // Found mobj to attack } } if (actor->target) { P_SetMobjStateNF(actor, S_MNTR_WALK1); } else { P_SetMobjStateNF(actor, S_MNTR_ROAM1); } } void A_MinotaurChase(mobj_t * actor) { actor->flags &= ~MF_SHADOW; // In case pain caused him to actor->flags &= ~MF_ALTSHADOW; // skip his fade in. if (!CheckMinotaurAge(actor)) { return; } if (P_Random() < 30) A_MinotaurLook(actor); // adjust to closest target if (!actor->target || (actor->target->health <= 0) || !(actor->target->flags & MF_SHOOTABLE)) { // look for a new target P_SetMobjState(actor, S_MNTR_LOOK1); return; } FaceMovementDirection(actor); actor->reactiontime = 0; // Melee attack if (actor->info->meleestate && P_CheckMeleeRange(actor)) { if (actor->info->attacksound) { S_StartSound(actor, actor->info->attacksound); } P_SetMobjState(actor, actor->info->meleestate); return; } // Missile attack if (actor->info->missilestate && P_CheckMissileRange(actor)) { P_SetMobjState(actor, actor->info->missilestate); return; } // chase towards target if (!P_Move(actor)) { P_NewChaseDir(actor); } // Active sound if (actor->info->activesound && P_Random() < 6) { S_StartSound(actor, actor->info->activesound); } } //---------------------------------------------------------------------------- // // PROC A_MinotaurAtk1 // // Melee attack. // //---------------------------------------------------------------------------- void A_MinotaurAtk1(mobj_t * actor) { if (!actor->target) return; S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(4)); } } //---------------------------------------------------------------------------- // // PROC A_MinotaurDecide // // Choose a missile attack. // //---------------------------------------------------------------------------- #define MNTR_CHARGE_SPEED (23*FRACUNIT) void A_MinotaurDecide(mobj_t * actor) { angle_t angle; mobj_t *target = actor->target; int dist; if (!target) return; dist = P_AproxDistance(actor->x - target->x, actor->y - target->y); if (target->z + target->height > actor->z && target->z + target->height < actor->z + actor->height && dist < 16 * 64 * FRACUNIT && dist > 1 * 64 * FRACUNIT && P_Random() < 230) { // Charge attack // Don't call the state function right away P_SetMobjStateNF(actor, S_MNTR_ATK4_1); actor->flags |= MF_SKULLFLY; A_FaceTarget(actor); angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]); actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]); actor->args[4] = 35 / 2; // Charge duration } else if (target->z == target->floorz && dist < 9 * 64 * FRACUNIT && P_Random() < 100) { // Floor fire attack P_SetMobjState(actor, S_MNTR_ATK3_1); actor->special2.i = 0; } else { // Swing attack A_FaceTarget(actor); // Don't need to call P_SetMobjState because the current state // falls through to the swing attack } } //---------------------------------------------------------------------------- // // PROC A_MinotaurCharge // //---------------------------------------------------------------------------- void A_MinotaurCharge(mobj_t * actor) { mobj_t *puff; if (!actor->target) return; if (actor->args[4] > 0) { puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PUNCHPUFF); puff->momz = 2 * FRACUNIT; actor->args[4]--; } else { actor->flags &= ~MF_SKULLFLY; P_SetMobjState(actor, actor->info->seestate); } } //---------------------------------------------------------------------------- // // PROC A_MinotaurAtk2 // // Swing attack. // //---------------------------------------------------------------------------- void A_MinotaurAtk2(mobj_t * actor) { mobj_t *mo; angle_t angle; fixed_t momz; if (!actor->target) return; S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(3)); return; } mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1); if (mo) { //S_StartSound(mo, sfx_minat2); momz = mo->momz; angle = mo->angle; P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 8), momz); P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 8), momz); P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 16), momz); P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 16), momz); } } //---------------------------------------------------------------------------- // // PROC A_MinotaurAtk3 // // Floor fire attack. // //---------------------------------------------------------------------------- void A_MinotaurAtk3(mobj_t * actor) { mobj_t *mo; player_t *player; if (!actor->target) { return; } if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(3)); if ((player = actor->target->player) != NULL) { // Squish the player player->deltaviewheight = -16 * FRACUNIT; } } else { mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2); if (mo != NULL) { S_StartSound(mo, SFX_MAULATOR_HAMMER_HIT); } } if (P_Random() < 192 && actor->special2.i == 0) { P_SetMobjState(actor, S_MNTR_ATK3_4); actor->special2.i = 1; } } //---------------------------------------------------------------------------- // // PROC A_MntrFloorFire // //---------------------------------------------------------------------------- void A_MntrFloorFire(mobj_t * actor) { mobj_t *mo; int r1, r2; r1 = P_SubRandom(); r2 = P_SubRandom(); actor->z = actor->floorz; mo = P_SpawnMobj(actor->x + (r2 << 10), actor->y + (r1 << 10), ONFLOORZ, MT_MNTRFX3); mo->target = actor->target; mo->momx = 1; // Force block checking P_CheckMissileSpawn(mo); } //---------------------------------------------------------------------------- // // PROC A_Scream // //---------------------------------------------------------------------------- void A_Scream(mobj_t * actor) { int sound; S_StopSound(actor); if (actor->player) { if (actor->player->morphTics) { S_StartSound(actor, actor->info->deathsound); } else { // Handle the different player death screams if (actor->momz <= -39 * FRACUNIT) { // Falling splat sound = SFX_PLAYER_FALLING_SPLAT; } else if (actor->health > -50) { // Normal death sound switch (actor->player->class) { case PCLASS_FIGHTER: sound = SFX_PLAYER_FIGHTER_NORMAL_DEATH; break; case PCLASS_CLERIC: sound = SFX_PLAYER_CLERIC_NORMAL_DEATH; break; case PCLASS_MAGE: sound = SFX_PLAYER_MAGE_NORMAL_DEATH; break; default: sound = SFX_NONE; break; } } else if (actor->health > -100) { // Crazy death sound switch (actor->player->class) { case PCLASS_FIGHTER: sound = SFX_PLAYER_FIGHTER_CRAZY_DEATH; break; case PCLASS_CLERIC: sound = SFX_PLAYER_CLERIC_CRAZY_DEATH; break; case PCLASS_MAGE: sound = SFX_PLAYER_MAGE_CRAZY_DEATH; break; default: sound = SFX_NONE; break; } } else { // Extreme death sound switch (actor->player->class) { case PCLASS_FIGHTER: sound = SFX_PLAYER_FIGHTER_EXTREME1_DEATH; break; case PCLASS_CLERIC: sound = SFX_PLAYER_CLERIC_EXTREME1_DEATH; break; case PCLASS_MAGE: sound = SFX_PLAYER_MAGE_EXTREME1_DEATH; break; default: sound = SFX_NONE; break; } sound += P_Random() % 3; // Three different extreme deaths } S_StartSound(actor, sound); } } else { S_StartSound(actor, actor->info->deathsound); } } //--------------------------------------------------------------------------- // // PROC P_DropItem // //--------------------------------------------------------------------------- /* void P_DropItem(mobj_t *source, mobjtype_t type, int special, int chance) { mobj_t *mo; if(P_Random() > chance) { return; } mo = P_SpawnMobj(source->x, source->y, source->z+(source->height>>1), type); mo->momx = P_SubRandom()<<8; mo->momy = P_SubRandom()<<8; mo->momz = FRACUNIT*5+(P_Random()<<10); mo->flags2 |= MF2_DROPPED; mo->health = special; } */ //---------------------------------------------------------------------------- // // PROC A_NoBlocking // //---------------------------------------------------------------------------- void A_NoBlocking(mobj_t * actor) { actor->flags &= ~MF_SOLID; // Check for monsters dropping things /* switch(actor->type) { // Add the monster dropped items here case MT_MUMMYLEADERGHOST: P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84); break; default: break; } */ } //---------------------------------------------------------------------------- // // PROC A_Explode // // Handles a bunch of exploding things. // //---------------------------------------------------------------------------- void A_Explode(mobj_t * actor) { int damage; int distance; boolean damageSelf; damage = 128; distance = 128; damageSelf = true; switch (actor->type) { case MT_FIREBOMB: // Time Bombs actor->z += 32 * FRACUNIT; actor->flags &= ~MF_SHADOW; break; case MT_MNTRFX2: // Minotaur floor fire damage = 24; break; case MT_BISHOP: // Bishop radius death damage = 25 + (P_Random() & 15); break; case MT_HAMMER_MISSILE: // Fighter Hammer damage = 128; damageSelf = false; break; case MT_FSWORD_MISSILE: // Fighter Runesword damage = 64; damageSelf = false; break; case MT_CIRCLEFLAME: // Cleric Flame secondary flames damage = 20; damageSelf = false; break; case MT_SORCBALL1: // Sorcerer balls case MT_SORCBALL2: case MT_SORCBALL3: distance = 255; damage = 255; actor->args[0] = 1; // don't play bounce break; case MT_SORCFX1: // Sorcerer spell 1 damage = 30; break; case MT_SORCFX4: // Sorcerer spell 4 damage = 20; break; case MT_TREEDESTRUCTIBLE: damage = 10; break; case MT_DRAGON_FX2: damage = 80; damageSelf = false; break; case MT_MSTAFF_FX: damage = 64; distance = 192; damageSelf = false; break; case MT_MSTAFF_FX2: damage = 80; distance = 192; damageSelf = false; break; case MT_POISONCLOUD: damage = 4; distance = 40; break; case MT_ZXMAS_TREE: case MT_ZSHRUB2: damage = 30; distance = 64; break; default: break; } P_RadiusAttack(actor, actor->target, damage, distance, damageSelf); if (actor->z <= actor->floorz + (distance << FRACBITS) && actor->type != MT_POISONCLOUD) { P_HitFloor(actor); } } //---------------------------------------------------------------------------- // // PROC P_Massacre // // Kills all monsters. // //---------------------------------------------------------------------------- int P_Massacre(void) { int count; mobj_t *mo; thinker_t *think; count = 0; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *) think; if ((mo->flags & MF_COUNTKILL) && (mo->health > 0)) { mo->flags2 &= ~(MF2_NONSHOOTABLE + MF2_INVULNERABLE); mo->flags |= MF_SHOOTABLE; P_DamageMobj(mo, NULL, NULL, 10000); count++; } } return count; } //---------------------------------------------------------------------------- // // PROC A_SkullPop // //---------------------------------------------------------------------------- void A_SkullPop(mobj_t * actor) { mobj_t *mo; player_t *player; if (!actor->player) { return; } actor->flags &= ~MF_SOLID; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT, MT_BLOODYSKULL); //mo->target = actor; mo->momx = P_SubRandom() << 9; mo->momy = P_SubRandom() << 9; mo->momz = FRACUNIT * 2 + (P_Random() << 6); // Attach player mobj to bloody skull player = actor->player; actor->player = NULL; actor->special1.i = player->class; mo->player = player; mo->health = actor->health; mo->angle = actor->angle; player->mo = mo; player->lookdir = 0; player->damagecount = 32; } //---------------------------------------------------------------------------- // // PROC A_CheckSkullFloor // //---------------------------------------------------------------------------- void A_CheckSkullFloor(mobj_t * actor) { if (actor->z <= actor->floorz) { P_SetMobjState(actor, S_BLOODYSKULLX1); S_StartSound(actor, SFX_DRIP); } } //---------------------------------------------------------------------------- // // PROC A_CheckSkullDone // //---------------------------------------------------------------------------- void A_CheckSkullDone(mobj_t * actor) { if (actor->special2.i == 666) { P_SetMobjState(actor, S_BLOODYSKULLX2); } } //---------------------------------------------------------------------------- // // PROC A_CheckBurnGone // //---------------------------------------------------------------------------- void A_CheckBurnGone(mobj_t * actor) { if (actor->special2.i == 666) { P_SetMobjState(actor, S_PLAY_FDTH20); } } //---------------------------------------------------------------------------- // // PROC A_FreeTargMobj // //---------------------------------------------------------------------------- void A_FreeTargMobj(mobj_t * mo) { mo->momx = mo->momy = mo->momz = 0; mo->z = mo->ceilingz + 4 * FRACUNIT; mo->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_SOLID | MF_COUNTKILL); mo->flags |= MF_CORPSE | MF_DROPOFF | MF_NOGRAVITY; mo->flags2 &= ~(MF2_PASSMOBJ | MF2_LOGRAV); mo->flags2 |= MF2_DONTDRAW; mo->player = NULL; mo->health = -1000; // Don't resurrect } //---------------------------------------------------------------------------- // // CorpseQueue Routines // //---------------------------------------------------------------------------- // Corpse queue for monsters - this should be saved out #define CORPSEQUEUESIZE 64 mobj_t *corpseQueue[CORPSEQUEUESIZE]; int corpseQueueSlot; // throw another corpse on the queue void A_QueueCorpse(mobj_t * actor) { mobj_t *corpse; if (corpseQueueSlot >= CORPSEQUEUESIZE) { // Too many corpses - remove an old one corpse = corpseQueue[corpseQueueSlot % CORPSEQUEUESIZE]; if (corpse) P_RemoveMobj(corpse); } corpseQueue[corpseQueueSlot % CORPSEQUEUESIZE] = actor; corpseQueueSlot++; } // Remove a mobj from the queue (for resurrection) void A_DeQueueCorpse(mobj_t * actor) { int slot; for (slot = 0; slot < CORPSEQUEUESIZE; slot++) { if (corpseQueue[slot] == actor) { corpseQueue[slot] = NULL; break; } } } void P_InitCreatureCorpseQueue(boolean corpseScan) { thinker_t *think; mobj_t *mo; // Initialize queue corpseQueueSlot = 0; memset(corpseQueue, 0, sizeof(mobj_t *) * CORPSEQUEUESIZE); if (!corpseScan) return; // Search mobj list for corpses and place them in this queue for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) continue; mo = (mobj_t *) think; if (!(mo->flags & MF_CORPSE)) continue; // Must be a corpse if (mo->flags & MF_ICECORPSE) continue; // Not ice corpses // Only corpses that call A_QueueCorpse from death routine switch (mo->type) { case MT_CENTAUR: case MT_CENTAURLEADER: case MT_DEMON: case MT_DEMON2: case MT_WRAITH: case MT_WRAITHB: case MT_BISHOP: case MT_ETTIN: case MT_PIG: case MT_CENTAUR_SHIELD: case MT_CENTAUR_SWORD: case MT_DEMONCHUNK1: case MT_DEMONCHUNK2: case MT_DEMONCHUNK3: case MT_DEMONCHUNK4: case MT_DEMONCHUNK5: case MT_DEMON2CHUNK1: case MT_DEMON2CHUNK2: case MT_DEMON2CHUNK3: case MT_DEMON2CHUNK4: case MT_DEMON2CHUNK5: case MT_FIREDEMON_SPLOTCH1: case MT_FIREDEMON_SPLOTCH2: A_QueueCorpse(mo); // Add corpse to queue break; default: break; } } } //---------------------------------------------------------------------------- // // PROC A_AddPlayerCorpse // //---------------------------------------------------------------------------- #define BODYQUESIZE 32 mobj_t *bodyque[BODYQUESIZE]; int bodyqueslot; void A_AddPlayerCorpse(mobj_t * actor) { if (bodyqueslot >= BODYQUESIZE) { // Too many player corpses - remove an old one P_RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]); } bodyque[bodyqueslot % BODYQUESIZE] = actor; bodyqueslot++; } //============================================================================ // // A_SerpentUnHide // //============================================================================ void A_SerpentUnHide(mobj_t * actor) { actor->flags2 &= ~MF2_DONTDRAW; actor->floorclip = 24 * FRACUNIT; } //============================================================================ // // A_SerpentHide // //============================================================================ void A_SerpentHide(mobj_t * actor) { actor->flags2 |= MF2_DONTDRAW; actor->floorclip = 0; } //============================================================================ // // A_SerpentChase // //============================================================================ void A_SerpentChase(mobj_t * actor) { int delta; int oldX, oldY, oldFloor; if (actor->reactiontime) { actor->reactiontime--; } // Modify target threshold if (actor->threshold) { actor->threshold--; } if (gameskill == sk_nightmare) { // Monsters move faster in nightmare mode actor->tics -= actor->tics / 2; if (actor->tics < 3) { actor->tics = 3; } } // // turn towards movement direction if not there yet // if (actor->movedir < 8) { actor->angle &= (7 << 29); delta = actor->angle - (actor->movedir << 29); if (delta > 0) { actor->angle -= ANG90 / 2; } else if (delta < 0) { actor->angle += ANG90 / 2; } } if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) { // look for a new target if (P_LookForPlayers(actor, true)) { // got a new target return; } P_SetMobjState(actor, actor->info->spawnstate); return; } // // don't attack twice in a row // if (actor->flags & MF_JUSTATTACKED) { actor->flags &= ~MF_JUSTATTACKED; if (gameskill != sk_nightmare) P_NewChaseDir(actor); return; } // // check for melee attack // if (actor->info->meleestate && P_CheckMeleeRange(actor)) { if (actor->info->attacksound) { S_StartSound(actor, actor->info->attacksound); } P_SetMobjState(actor, actor->info->meleestate); return; } // // possibly choose another target // if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target)) { if (P_LookForPlayers(actor, true)) return; // got a new target } // // chase towards player // oldX = actor->x; oldY = actor->y; oldFloor = actor->subsector->sector->floorpic; if (--actor->movecount < 0 || !P_Move(actor)) { P_NewChaseDir(actor); } if (actor->subsector->sector->floorpic != oldFloor) { P_TryMove(actor, oldX, oldY); P_NewChaseDir(actor); } // // make active sound // if (actor->info->activesound && P_Random() < 3) { S_StartSound(actor, actor->info->activesound); } } //============================================================================ // // A_SerpentRaiseHump // // Raises the hump above the surface by raising the floorclip level //============================================================================ void A_SerpentRaiseHump(mobj_t * actor) { actor->floorclip -= 4 * FRACUNIT; } //============================================================================ // // A_SerpentLowerHump // //============================================================================ void A_SerpentLowerHump(mobj_t * actor) { actor->floorclip += 4 * FRACUNIT; } //============================================================================ // // A_SerpentHumpDecide // // Decided whether to hump up, or if the mobj is a serpent leader, // to missile attack //============================================================================ void A_SerpentHumpDecide(mobj_t * actor) { if (actor->type == MT_SERPENTLEADER) { if (P_Random() > 30) { return; } else if (P_Random() < 40) { // Missile attack P_SetMobjState(actor, S_SERPENT_SURFACE1); return; } } else if (P_Random() > 3) { return; } if (!P_CheckMeleeRange(actor)) { // The hump shouldn't occur when within melee range if (actor->type == MT_SERPENTLEADER && P_Random() < 128) { P_SetMobjState(actor, S_SERPENT_SURFACE1); } else { P_SetMobjState(actor, S_SERPENT_HUMP1); S_StartSound(actor, SFX_SERPENT_ACTIVE); } } } //============================================================================ // // A_SerpentBirthScream // //============================================================================ void A_SerpentBirthScream(mobj_t * actor) { S_StartSound(actor, SFX_SERPENT_BIRTH); } //============================================================================ // // A_SerpentDiveSound // //============================================================================ void A_SerpentDiveSound(mobj_t * actor) { S_StartSound(actor, SFX_SERPENT_ACTIVE); } //============================================================================ // // A_SerpentWalk // // Similar to A_Chase, only has a hardcoded entering of meleestate //============================================================================ void A_SerpentWalk(mobj_t * actor) { int delta; if (actor->reactiontime) { actor->reactiontime--; } // Modify target threshold if (actor->threshold) { actor->threshold--; } if (gameskill == sk_nightmare) { // Monsters move faster in nightmare mode actor->tics -= actor->tics / 2; if (actor->tics < 3) { actor->tics = 3; } } // // turn towards movement direction if not there yet // if (actor->movedir < 8) { actor->angle &= (7 << 29); delta = actor->angle - (actor->movedir << 29); if (delta > 0) { actor->angle -= ANG90 / 2; } else if (delta < 0) { actor->angle += ANG90 / 2; } } if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) { // look for a new target if (P_LookForPlayers(actor, true)) { // got a new target return; } P_SetMobjState(actor, actor->info->spawnstate); return; } // // don't attack twice in a row // if (actor->flags & MF_JUSTATTACKED) { actor->flags &= ~MF_JUSTATTACKED; if (gameskill != sk_nightmare) 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, S_SERPENT_ATK1); return; } // // possibly choose another target // if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target)) { if (P_LookForPlayers(actor, true)) return; // got a new target } // // chase towards player // if (--actor->movecount < 0 || !P_Move(actor)) { P_NewChaseDir(actor); } } //============================================================================ // // A_SerpentCheckForAttack // //============================================================================ void A_SerpentCheckForAttack(mobj_t * actor) { if (!actor->target) { return; } if (actor->type == MT_SERPENTLEADER) { if (!P_CheckMeleeRange(actor)) { P_SetMobjState(actor, S_SERPENT_ATK1); return; } } if (P_CheckMeleeRange2(actor)) { P_SetMobjState(actor, S_SERPENT_WALK1); } else if (P_CheckMeleeRange(actor)) { if (P_Random() < 32) { P_SetMobjState(actor, S_SERPENT_WALK1); } else { P_SetMobjState(actor, S_SERPENT_ATK1); } } } //============================================================================ // // A_SerpentChooseAttack // //============================================================================ void A_SerpentChooseAttack(mobj_t * actor) { if (!actor->target || P_CheckMeleeRange(actor)) { return; } if (actor->type == MT_SERPENTLEADER) { P_SetMobjState(actor, S_SERPENT_MISSILE1); } } //============================================================================ // // A_SerpentMeleeAttack // //============================================================================ void A_SerpentMeleeAttack(mobj_t * actor) { if (!actor->target) { return; } if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(5)); S_StartSound(actor, SFX_SERPENT_MELEEHIT); } if (P_Random() < 96) { A_SerpentCheckForAttack(actor); } } //============================================================================ // // A_SerpentMissileAttack // //============================================================================ void A_SerpentMissileAttack(mobj_t * actor) { if (!actor->target) { return; } P_SpawnMissile(actor, actor->target, MT_SERPENTFX); } //============================================================================ // // A_SerpentHeadPop // //============================================================================ void A_SerpentHeadPop(mobj_t * actor) { P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_SERPENT_HEAD); } //============================================================================ // // A_SerpentSpawnGibs // //============================================================================ void A_SerpentSpawnGibs(mobj_t * actor) { mobj_t *mo; int r1, r2; r1 = P_Random(); r2 = P_Random(); mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12), actor->y + ((r1 - 128) << 12), actor->floorz + FRACUNIT, MT_SERPENT_GIB1); if (mo) { mo->momx = (P_Random() - 128) << 6; mo->momy = (P_Random() - 128) << 6; mo->floorclip = 6 * FRACUNIT; } r1 = P_Random(); r2 = P_Random(); mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12), actor->y + ((r1 - 128) << 12), actor->floorz + FRACUNIT, MT_SERPENT_GIB2); if (mo) { mo->momx = (P_Random() - 128) << 6; mo->momy = (P_Random() - 128) << 6; mo->floorclip = 6 * FRACUNIT; } r1 = P_Random(); r2 = P_Random(); mo = P_SpawnMobj(actor->x + ((r2 - 128) << 12), actor->y + ((r1 - 128) << 12), actor->floorz + FRACUNIT, MT_SERPENT_GIB3); if (mo) { mo->momx = (P_Random() - 128) << 6; mo->momy = (P_Random() - 128) << 6; mo->floorclip = 6 * FRACUNIT; } } //============================================================================ // // A_FloatGib // //============================================================================ void A_FloatGib(mobj_t * actor) { actor->floorclip -= FRACUNIT; } //============================================================================ // // A_SinkGib // //============================================================================ void A_SinkGib(mobj_t * actor) { actor->floorclip += FRACUNIT; } //============================================================================ // // A_DelayGib // //============================================================================ void A_DelayGib(mobj_t * actor) { actor->tics -= P_Random() >> 2; } //============================================================================ // // A_SerpentHeadCheck // //============================================================================ void A_SerpentHeadCheck(mobj_t * actor) { if (actor->z <= actor->floorz) { if (P_GetThingFloorType(actor) >= FLOOR_LIQUID) { P_HitFloor(actor); P_SetMobjState(actor, S_NULL); } else { P_SetMobjState(actor, S_SERPENT_HEAD_X1); } } } //============================================================================ // // A_CentaurAttack // //============================================================================ void A_CentaurAttack(mobj_t * actor) { if (!actor->target) { return; } if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, P_Random() % 7 + 3); } } //============================================================================ // // A_CentaurAttack2 // //============================================================================ void A_CentaurAttack2(mobj_t * actor) { if (!actor->target) { return; } P_SpawnMissile(actor, actor->target, MT_CENTAUR_FX); S_StartSound(actor, SFX_CENTAURLEADER_ATTACK); } //============================================================================ // // A_CentaurDropStuff // // Spawn shield/sword sprites when the centaur pulps //============================================================================ void A_CentaurDropStuff(mobj_t * actor) { mobj_t *mo; angle_t angle; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_CENTAUR_SHIELD); if (mo) { angle = actor->angle + ANG90; mo->momz = FRACUNIT * 8 + (P_Random() << 10); mo->momx = FixedMul(((P_Random() - 128) << 11) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul(((P_Random() - 128) << 11) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_CENTAUR_SWORD); if (mo) { angle = actor->angle - ANG90; mo->momz = FRACUNIT * 8 + (P_Random() << 10); mo->momx = FixedMul(((P_Random() - 128) << 11) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul(((P_Random() - 128) << 11) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } } //============================================================================ // // A_CentaurDefend // //============================================================================ void A_CentaurDefend(mobj_t * actor) { A_FaceTarget(actor); if (P_CheckMeleeRange(actor) && P_Random() < 32) { A_UnSetInvulnerable(actor); P_SetMobjState(actor, actor->info->meleestate); } } //============================================================================ // // A_BishopAttack // //============================================================================ void A_BishopAttack(mobj_t * actor) { if (!actor->target) { return; } S_StartSound(actor, actor->info->attacksound); if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(4)); return; } actor->special1.i = (P_Random() & 3) + 5; } //============================================================================ // // A_BishopAttack2 // // Spawns one of a string of bishop missiles //============================================================================ void A_BishopAttack2(mobj_t * actor) { mobj_t *mo; if (!actor->target || !actor->special1.i) { actor->special1.i = 0; P_SetMobjState(actor, S_BISHOP_WALK1); return; } mo = P_SpawnMissile(actor, actor->target, MT_BISH_FX); if (mo) { mo->special1.m = actor->target; mo->special2.i = 16; // High word == x/y, Low word == z } actor->special1.i--; } //============================================================================ // // A_BishopMissileWeave // //============================================================================ void A_BishopMissileWeave(mobj_t * actor) { fixed_t newX, newY; int weaveXY, weaveZ; int angle; weaveXY = actor->special2.i >> 16; weaveZ = actor->special2.i & 0xFFFF; angle = (actor->angle + ANG90) >> ANGLETOFINESHIFT; newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 1); newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 1); weaveXY = (weaveXY + 2) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 1); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 1); P_TryMove(actor, newX, newY); actor->z -= FloatBobOffsets[weaveZ]; weaveZ = (weaveZ + 2) & 63; actor->z += FloatBobOffsets[weaveZ]; actor->special2.i = weaveZ + (weaveXY << 16); } //============================================================================ // // A_BishopMissileSeek // //============================================================================ void A_BishopMissileSeek(mobj_t * actor) { P_SeekerMissile(actor, ANG1 * 2, ANG1 * 3); } //============================================================================ // // A_BishopDecide // //============================================================================ void A_BishopDecide(mobj_t * actor) { if (P_Random() < 220) { return; } else { P_SetMobjState(actor, S_BISHOP_BLUR1); } } //============================================================================ // // A_BishopDoBlur // //============================================================================ void A_BishopDoBlur(mobj_t * actor) { actor->special1.i = (P_Random() & 3) + 3; // Random number of blurs if (P_Random() < 120) { P_ThrustMobj(actor, actor->angle + ANG90, 11 * FRACUNIT); } else if (P_Random() > 125) { P_ThrustMobj(actor, actor->angle - ANG90, 11 * FRACUNIT); } else { // Thrust forward P_ThrustMobj(actor, actor->angle, 11 * FRACUNIT); } S_StartSound(actor, SFX_BISHOP_BLUR); } //============================================================================ // // A_BishopSpawnBlur // //============================================================================ void A_BishopSpawnBlur(mobj_t * actor) { mobj_t *mo; if (!--actor->special1.i) { actor->momx = 0; actor->momy = 0; if (P_Random() > 96) { P_SetMobjState(actor, S_BISHOP_WALK1); } else { P_SetMobjState(actor, S_BISHOP_ATK1); } } mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOPBLUR); if (mo) { mo->angle = actor->angle; } } //============================================================================ // // A_BishopChase // //============================================================================ void A_BishopChase(mobj_t * actor) { actor->z -= FloatBobOffsets[actor->special2.i] >> 1; actor->special2.i = (actor->special2.i + 4) & 63; actor->z += FloatBobOffsets[actor->special2.i] >> 1; } //============================================================================ // // A_BishopPuff // //============================================================================ void A_BishopPuff(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 40 * FRACUNIT, MT_BISHOP_PUFF); if (mo) { mo->momz = FRACUNIT / 2; } } //============================================================================ // // A_BishopPainBlur // //============================================================================ void A_BishopPainBlur(mobj_t * actor) { mobj_t *mo; int r1,r2,r3; if (P_Random() < 64) { P_SetMobjState(actor, S_BISHOP_BLUR1); return; } r1 = P_SubRandom(); r2 = P_SubRandom(); r3 = P_SubRandom(); mo = P_SpawnMobj(actor->x + (r3 << 12), actor->y + (r2 << 12), actor->z + (r1 << 11), MT_BISHOPPAINBLUR); if (mo) { mo->angle = actor->angle; } } //============================================================================ // // DragonSeek // //============================================================================ static void DragonSeek(mobj_t * actor, angle_t thresh, angle_t turnMax) { int dir; int dist; angle_t delta; angle_t angle; mobj_t *target; int search; int i; int bestArg; angle_t bestAngle; angle_t angleToSpot, angleToTarget; mobj_t *mo; target = actor->special1.m; if (target == NULL) { return; } dir = P_FaceMobj(actor, target, &delta); if (delta > thresh) { delta >>= 1; if (delta > turnMax) { delta = turnMax; } } if (dir) { // Turn clockwise actor->angle += delta; } else { // Turn counter clockwise actor->angle -= delta; } angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(actor->info->speed, finecosine[angle]); actor->momy = FixedMul(actor->info->speed, finesine[angle]); if (actor->z + actor->height < target->z || target->z + target->height < actor->z) { dist = P_AproxDistance(target->x - actor->x, target->y - actor->y); dist = dist / actor->info->speed; if (dist < 1) { dist = 1; } actor->momz = (target->z - actor->z) / dist; } else { dist = P_AproxDistance(target->x - actor->x, target->y - actor->y); dist = dist / actor->info->speed; } if (target->flags & MF_SHOOTABLE && P_Random() < 64) { // attack the destination mobj if it's attackable mobj_t *oldTarget; if (abs(actor->angle - R_PointToAngle2(actor->x, actor->y, target->x, target->y)) < ANG45 / 2) { oldTarget = actor->target; actor->target = target; if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(10)); S_StartSound(actor, SFX_DRAGON_ATTACK); } else if (P_Random() < 128 && P_CheckMissileRange(actor)) { P_SpawnMissile(actor, target, MT_DRAGON_FX); S_StartSound(actor, SFX_DRAGON_ATTACK); } actor->target = oldTarget; } } if (dist < 4) { // Hit the target thing if (actor->target && P_Random() < 200) { bestArg = -1; bestAngle = ANG_MAX; angleToTarget = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); for (i = 0; i < 5; i++) { if (!target->args[i]) { continue; } search = -1; mo = P_FindMobjFromTID(target->args[i], &search); angleToSpot = R_PointToAngle2(actor->x, actor->y, mo->x, mo->y); if (abs(angleToSpot - angleToTarget) < bestAngle) { bestAngle = abs(angleToSpot - angleToTarget); bestArg = i; } } if (bestArg != -1) { search = -1; actor->special1.m = P_FindMobjFromTID(target->args[bestArg], &search); } } else { do { i = (P_Random() >> 2) % 5; } while (!target->args[i]); search = -1; actor->special1.m = P_FindMobjFromTID(target->args[i], &search); } } } //============================================================================ // // A_DragonInitFlight // //============================================================================ void A_DragonInitFlight(mobj_t * actor) { int search; search = -1; do { // find the first tid identical to the dragon's tid actor->special1.m = P_FindMobjFromTID(actor->tid, &search); if (search == -1) { P_SetMobjState(actor, actor->info->spawnstate); return; } } while (actor->special1.m == actor); P_RemoveMobjFromTIDList(actor); } //============================================================================ // // A_DragonFlight // //============================================================================ void A_DragonFlight(mobj_t * actor) { angle_t angle; DragonSeek(actor, 4 * ANG1, 8 * ANG1); if (actor->target) { if (!(actor->target->flags & MF_SHOOTABLE)) { // target died actor->target = NULL; return; } angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); if (abs(actor->angle - angle) < ANG45 / 2 && P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(8)); S_StartSound(actor, SFX_DRAGON_ATTACK); } else if (abs(actor->angle - angle) <= ANG1 * 20) { P_SetMobjState(actor, actor->info->missilestate); S_StartSound(actor, SFX_DRAGON_ATTACK); } } else { P_LookForPlayers(actor, true); } } //============================================================================ // // A_DragonFlap // //============================================================================ void A_DragonFlap(mobj_t * actor) { A_DragonFlight(actor); if (P_Random() < 240) { S_StartSound(actor, SFX_DRAGON_WINGFLAP); } else { S_StartSound(actor, actor->info->activesound); } } //============================================================================ // // A_DragonAttack // //============================================================================ void A_DragonAttack(mobj_t * actor) { P_SpawnMissile(actor, actor->target, MT_DRAGON_FX); } //============================================================================ // // A_DragonFX2 // //============================================================================ void A_DragonFX2(mobj_t * actor) { mobj_t *mo; int i; int r1,r2,r3; int delay; delay = 16 + (P_Random() >> 3); for (i = 1 + (P_Random() & 3); i; i--) { r1 = P_Random(); r2 = P_Random(); r3 = P_Random(); mo = P_SpawnMobj(actor->x + ((r3 - 128) << 14), actor->y + ((r2 - 128) << 14), actor->z + ((r1 - 128) << 12), MT_DRAGON_FX2); if (mo) { mo->tics = delay + (P_Random() & 3) * i * 2; mo->target = actor->target; } } } //============================================================================ // // A_DragonPain // //============================================================================ void A_DragonPain(mobj_t * actor) { A_Pain(actor); if (!actor->special1.i) { // no destination spot yet P_SetMobjState(actor, S_DRAGON_INIT); } } //============================================================================ // // A_DragonCheckCrash // //============================================================================ void A_DragonCheckCrash(mobj_t * actor) { if (actor->z <= actor->floorz) { P_SetMobjState(actor, S_DRAGON_CRASH1); } } //============================================================================ // Demon AI //============================================================================ // // A_DemonAttack1 (melee) // void A_DemonAttack1(mobj_t * actor) { if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(2)); } } // // A_DemonAttack2 (missile) // void A_DemonAttack2(mobj_t * actor) { mobj_t *mo; int fireBall; if (actor->type == MT_DEMON) { fireBall = MT_DEMONFX1; } else { fireBall = MT_DEMON2FX1; } mo = P_SpawnMissile(actor, actor->target, fireBall); if (mo) { mo->z += 30 * FRACUNIT; S_StartSound(actor, SFX_DEMON_MISSILE_FIRE); } } // // A_DemonDeath // void A_DemonDeath(mobj_t * actor) { mobj_t *mo; angle_t angle; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMONCHUNK1); if (mo) { angle = actor->angle + ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMONCHUNK2); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMONCHUNK3); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMONCHUNK4); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMONCHUNK5); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } } //=========================================================================== // // A_Demon2Death // //=========================================================================== void A_Demon2Death(mobj_t * actor) { mobj_t *mo; angle_t angle; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMON2CHUNK1); if (mo) { angle = actor->angle + ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMON2CHUNK2); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMON2CHUNK3); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMON2CHUNK4); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } mo = P_SpawnMobj(actor->x, actor->y, actor->z + 45 * FRACUNIT, MT_DEMON2CHUNK5); if (mo) { angle = actor->angle - ANG90; mo->momz = 8 * FRACUNIT; mo->momx = FixedMul((P_Random() << 10) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 10) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; } } // // A_SinkMobj // Sink a mobj incrementally into the floor // boolean A_SinkMobj(mobj_t * actor) { if (actor->floorclip < actor->info->height) { switch (actor->type) { case MT_THRUSTFLOOR_DOWN: case MT_THRUSTFLOOR_UP: actor->floorclip += 6 * FRACUNIT; break; default: actor->floorclip += FRACUNIT; break; } return false; } return true; } // // A_RaiseMobj // Raise a mobj incrementally from the floor to // boolean A_RaiseMobj(mobj_t * actor) { int done = true; // Raise a mobj from the ground if (actor->floorclip > 0) { switch (actor->type) { case MT_WRAITHB: actor->floorclip -= 2 * FRACUNIT; break; case MT_THRUSTFLOOR_DOWN: case MT_THRUSTFLOOR_UP: actor->floorclip -= actor->special2.i * FRACUNIT; break; default: actor->floorclip -= 2 * FRACUNIT; break; } if (actor->floorclip <= 0) { actor->floorclip = 0; done = true; } else { done = false; } } return done; // Reached target height } //============================================================================ // Wraith Variables // // special1 Internal index into floatbob // special2 //============================================================================ // // A_WraithInit // void A_WraithInit(mobj_t * actor) { actor->z += 48 << FRACBITS; actor->special1.i = 0; // index into floatbob } void A_WraithRaiseInit(mobj_t * actor) { actor->flags2 &= ~MF2_DONTDRAW; actor->flags2 &= ~MF2_NONSHOOTABLE; actor->flags |= MF_SHOOTABLE | MF_SOLID; actor->floorclip = actor->info->height; } void A_WraithRaise(mobj_t * actor) { if (A_RaiseMobj(actor)) { // Reached it's target height P_SetMobjState(actor, S_WRAITH_CHASE1); } P_SpawnDirt(actor, actor->radius); } void A_WraithMelee(mobj_t * actor) { int amount; // Steal health from target and give to player if (P_CheckMeleeRange(actor) && (P_Random() < 220)) { amount = HITDICE(2); P_DamageMobj(actor->target, actor, actor, amount); actor->health += amount; } } void A_WraithMissile(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMissile(actor, actor->target, MT_WRAITHFX1); if (mo) { S_StartSound(actor, SFX_WRAITH_MISSILE_FIRE); } } // // A_WraithFX2 - spawns sparkle tail of missile // void A_WraithFX2(mobj_t * actor) { mobj_t *mo; angle_t angle; int i; for (i = 0; i < 2; i++) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX2); if (mo) { if (P_Random() < 128) { angle = actor->angle + (P_Random() << 22); } else { angle = actor->angle - (P_Random() << 22); } mo->momz = 0; mo->momx = FixedMul((P_Random() << 7) + FRACUNIT, finecosine[angle >> ANGLETOFINESHIFT]); mo->momy = FixedMul((P_Random() << 7) + FRACUNIT, finesine[angle >> ANGLETOFINESHIFT]); mo->target = actor; mo->floorclip = 10 * FRACUNIT; } } } // Spawn an FX3 around the actor during attacks void A_WraithFX3(mobj_t * actor) { mobj_t *mo; int numdropped = P_Random() % 15; int i; for (i = 0; i < numdropped; i++) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX3); if (mo) { mo->x += (P_Random() - 128) << 11; mo->y += (P_Random() - 128) << 11; mo->z += (P_Random() << 10); mo->target = actor; } } } // Spawn an FX4 during movement void A_WraithFX4(mobj_t * actor) { mobj_t *mo; int chance = P_Random(); int spawn4, spawn5; if (chance < 10) { spawn4 = true; spawn5 = false; } else if (chance < 20) { spawn4 = false; spawn5 = true; } else if (chance < 25) { spawn4 = true; spawn5 = true; } else { spawn4 = false; spawn5 = false; } if (spawn4) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX4); if (mo) { mo->x += (P_Random() - 128) << 12; mo->y += (P_Random() - 128) << 12; mo->z += (P_Random() << 10); mo->target = actor; } } if (spawn5) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX5); if (mo) { mo->x += (P_Random() - 128) << 11; mo->y += (P_Random() - 128) << 11; mo->z += (P_Random() << 10); mo->target = actor; } } } void A_WraithLook(mobj_t * actor) { // A_WraithFX4(actor); // too expensive A_Look(actor); } void A_WraithChase(mobj_t * actor) { int weaveindex = actor->special1.i; actor->z += FloatBobOffsets[weaveindex]; actor->special1.i = (weaveindex + 2) & 63; // if (actor->floorclip > 0) // { // P_SetMobjState(actor, S_WRAITH_RAISE2); // return; // } A_Chase(actor); A_WraithFX4(actor); } //============================================================================ // Ettin AI //============================================================================ void A_EttinAttack(mobj_t * actor) { if (P_CheckMeleeRange(actor)) { P_DamageMobj(actor->target, actor, actor, HITDICE(2)); } } void A_DropMace(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height >> 1), MT_ETTIN_MACE); if (mo) { mo->momx = (P_Random() - 128) << 11; mo->momy = (P_Random() - 128) << 11; mo->momz = FRACUNIT * 10 + (P_Random() << 10); mo->target = actor; } } //============================================================================ // Fire Demon AI // // special1 index into floatbob // special2 whether strafing or not //============================================================================ void A_FiredSpawnRock(mobj_t * actor) { mobj_t *mo; int x, y, z; int rtype = 0; switch (P_Random() % 5) { case 0: rtype = MT_FIREDEMON_FX1; break; case 1: rtype = MT_FIREDEMON_FX2; break; case 2: rtype = MT_FIREDEMON_FX3; break; case 3: rtype = MT_FIREDEMON_FX4; break; case 4: rtype = MT_FIREDEMON_FX5; break; } x = actor->x + ((P_Random() - 128) << 12); y = actor->y + ((P_Random() - 128) << 12); z = actor->z + ((P_Random()) << 11); mo = P_SpawnMobj(x, y, z, rtype); if (mo) { mo->target = actor; mo->momx = (P_Random() - 128) << 10; mo->momy = (P_Random() - 128) << 10; mo->momz = (P_Random() << 10); mo->special1.i = 2; // Number bounces } // Initialize fire demon actor->special2.i = 0; actor->flags &= ~MF_JUSTATTACKED; } void A_FiredRocks(mobj_t * actor) { A_FiredSpawnRock(actor); A_FiredSpawnRock(actor); A_FiredSpawnRock(actor); A_FiredSpawnRock(actor); A_FiredSpawnRock(actor); } void A_FiredAttack(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMissile(actor, actor->target, MT_FIREDEMON_FX6); if (mo) S_StartSound(actor, SFX_FIRED_ATTACK); } void A_SmBounce(mobj_t * actor) { // give some more momentum (x,y,&z) actor->z = actor->floorz + FRACUNIT; actor->momz = (2 * FRACUNIT) + (P_Random() << 10); actor->momx = P_Random() % 3 << FRACBITS; actor->momy = P_Random() % 3 << FRACBITS; } #define FIREDEMON_ATTACK_RANGE 64*8*FRACUNIT void A_FiredChase(mobj_t * actor) { int weaveindex = actor->special1.i; mobj_t *target = actor->target; angle_t ang; fixed_t dist; if (actor->reactiontime) actor->reactiontime--; if (actor->threshold) actor->threshold--; // Float up and down actor->z += FloatBobOffsets[weaveindex]; actor->special1.i = (weaveindex + 2) & 63; // Insure it stays above certain height if (actor->z < actor->floorz + (64 * FRACUNIT)) { actor->z += 2 * FRACUNIT; } if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) { // Invalid target P_LookForPlayers(actor, true); return; } // Strafe if (actor->special2.i > 0) { actor->special2.i--; } else { actor->special2.i = 0; actor->momx = actor->momy = 0; dist = P_AproxDistance(actor->x - target->x, actor->y - target->y); if (dist < FIREDEMON_ATTACK_RANGE) { if (P_Random() < 30) { ang = R_PointToAngle2(actor->x, actor->y, target->x, target->y); if (P_Random() < 128) ang += ANG90; else ang -= ANG90; ang >>= ANGLETOFINESHIFT; actor->momx = FixedMul(8 * FRACUNIT, finecosine[ang]); actor->momy = FixedMul(8 * FRACUNIT, finesine[ang]); actor->special2.i = 3; // strafe time } } } FaceMovementDirection(actor); // Normal movement if (!actor->special2.i) { if (--actor->movecount < 0 || !P_Move(actor)) { P_NewChaseDir(actor); } } // Do missile attack if (!(actor->flags & MF_JUSTATTACKED)) { if (P_CheckMissileRange(actor) && (P_Random() < 20)) { P_SetMobjState(actor, actor->info->missilestate); actor->flags |= MF_JUSTATTACKED; return; } } else { actor->flags &= ~MF_JUSTATTACKED; } // make active sound if (actor->info->activesound && P_Random() < 3) { S_StartSound(actor, actor->info->activesound); } } void A_FiredSplotch(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FIREDEMON_SPLOTCH1); if (mo) { mo->momx = (P_Random() - 128) << 11; mo->momy = (P_Random() - 128) << 11; mo->momz = FRACUNIT * 3 + (P_Random() << 10); } mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FIREDEMON_SPLOTCH2); if (mo) { mo->momx = (P_Random() - 128) << 11; mo->momy = (P_Random() - 128) << 11; mo->momz = FRACUNIT * 3 + (P_Random() << 10); } } //============================================================================ // // A_IceGuyLook // //============================================================================ void A_IceGuyLook(mobj_t * actor) { fixed_t dist; fixed_t an; A_Look(actor); if (P_Random() < 64) { dist = ((P_Random() - 128) * actor->radius) >> 7; an = (actor->angle + ANG90) >> ANGLETOFINESHIFT; P_SpawnMobj(actor->x + FixedMul(dist, finecosine[an]), actor->y + FixedMul(dist, finesine[an]), actor->z + 60 * FRACUNIT, MT_ICEGUY_WISP1 + (P_Random() & 1)); } } //============================================================================ // // A_IceGuyChase // //============================================================================ void A_IceGuyChase(mobj_t * actor) { fixed_t dist; fixed_t an; mobj_t *mo; A_Chase(actor); if (P_Random() < 128) { dist = ((P_Random() - 128) * actor->radius) >> 7; an = (actor->angle + ANG90) >> ANGLETOFINESHIFT; mo = P_SpawnMobj(actor->x + FixedMul(dist, finecosine[an]), actor->y + FixedMul(dist, finesine[an]), actor->z + 60 * FRACUNIT, MT_ICEGUY_WISP1 + (P_Random() & 1)); if (mo) { mo->momx = actor->momx; mo->momy = actor->momy; mo->momz = actor->momz; mo->target = actor; } } } //============================================================================ // // A_IceGuyAttack // //============================================================================ void A_IceGuyAttack(mobj_t * actor) { fixed_t an; if (!actor->target) { return; } an = (actor->angle + ANG90) >> ANGLETOFINESHIFT; P_SpawnMissileXYZ(actor->x + FixedMul(actor->radius >> 1, finecosine[an]), actor->y + FixedMul(actor->radius >> 1, finesine[an]), actor->z + 40 * FRACUNIT, actor, actor->target, MT_ICEGUY_FX); an = (actor->angle - ANG90) >> ANGLETOFINESHIFT; P_SpawnMissileXYZ(actor->x + FixedMul(actor->radius >> 1, finecosine[an]), actor->y + FixedMul(actor->radius >> 1, finesine[an]), actor->z + 40 * FRACUNIT, actor, actor->target, MT_ICEGUY_FX); S_StartSound(actor, actor->info->attacksound); } //============================================================================ // // A_IceGuyMissilePuff // //============================================================================ void A_IceGuyMissilePuff(mobj_t * actor) { P_SpawnMobj(actor->x, actor->y, actor->z + 2 * FRACUNIT, MT_ICEFX_PUFF); } //============================================================================ // // A_IceGuyDie // //============================================================================ void A_IceGuyDie(mobj_t * actor) { void A_FreezeDeathChunks(mobj_t * actor); actor->momx = 0; actor->momy = 0; actor->momz = 0; actor->height <<= 2; A_FreezeDeathChunks(actor); } //============================================================================ // // A_IceGuyMissileExplode // //============================================================================ void A_IceGuyMissileExplode(mobj_t * actor) { mobj_t *mo; unsigned int i; for (i = 0; i < 8; i++) { mo = P_SpawnMissileAngle(actor, MT_ICEGUY_FX2, i * ANG45, -0.3 * FRACUNIT); if (mo) { mo->target = actor->target; } } } //============================================================================ // // Sorcerer stuff // // Sorcerer Variables // special1 Angle of ball 1 (all others relative to that) // special2 which ball to stop at in stop mode (MT_???) // args[0] Denfense time // args[1] Number of full rotations since stopping mode // args[2] Target orbit speed for acceleration/deceleration // args[3] Movement mode (see SORC_ macros) // args[4] Current ball orbit speed // Sorcerer Ball Variables // special1 Previous angle of ball (for woosh) // special2 Countdown of rapid fire (FX4) // args[0] If set, don't play the bounce sound when bouncing //============================================================================ #define SORCBALL_INITIAL_SPEED 7 #define SORCBALL_TERMINAL_SPEED 25 #define SORCBALL_SPEED_ROTATIONS 5 #define SORC_DEFENSE_TIME 255 #define SORC_DEFENSE_HEIGHT 45 #define BOUNCE_TIME_UNIT (35/2) #define SORCFX4_RAPIDFIRE_TIME (6*3) // 3 seconds #define SORCFX4_SPREAD_ANGLE 20 #define SORC_DECELERATE 0 #define SORC_ACCELERATE 1 #define SORC_STOPPING 2 #define SORC_FIRESPELL 3 #define SORC_STOPPED 4 #define SORC_NORMAL 5 #define SORC_FIRING_SPELL 6 #define BALL1_ANGLEOFFSET 0 #define BALL2_ANGLEOFFSET (ANG_MAX/3) #define BALL3_ANGLEOFFSET ((ANG_MAX/3)*2) void A_SorcBallOrbit(mobj_t * actor); void A_SorcSpinBalls(mobj_t * actor); void A_SpeedBalls(mobj_t * actor); void A_SlowBalls(mobj_t * actor); void A_StopBalls(mobj_t * actor); void A_AccelBalls(mobj_t * actor); void A_DecelBalls(mobj_t * actor); void A_SorcBossAttack(mobj_t * actor); void A_SpawnFizzle(mobj_t * actor); void A_CastSorcererSpell(mobj_t * actor); void A_SorcUpdateBallAngle(mobj_t * actor); void A_BounceCheck(mobj_t * actor); void A_SorcFX1Seek(mobj_t * actor); void A_SorcOffense1(mobj_t * actor); void A_SorcOffense2(mobj_t * actor); // Spawn spinning balls above head - actor is sorcerer void A_SorcSpinBalls(mobj_t * actor) { mobj_t *mo; fixed_t z; A_SlowBalls(actor); actor->args[0] = 0; // Currently no defense actor->args[3] = SORC_NORMAL; actor->args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed actor->special1.i = ANG1; z = actor->z - actor->floorclip + actor->info->height; mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL1); if (mo) { mo->target = actor; mo->special2.i = SORCFX4_RAPIDFIRE_TIME; } mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL2); if (mo) mo->target = actor; mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL3); if (mo) mo->target = actor; } // // A_SorcBallOrbit() ========================================== // void A_SorcBallOrbit(mobj_t * actor) { int x, y; angle_t angle, baseangle; int mode = actor->target->args[3]; mobj_t *parent = (mobj_t *) actor->target; int dist = parent->radius - (actor->radius << 1); angle_t prevangle = actor->special1.i; if (actor->target->health <= 0) P_SetMobjState(actor, actor->info->painstate); baseangle = (angle_t) parent->special1.i; switch (actor->type) { case MT_SORCBALL1: angle = baseangle + BALL1_ANGLEOFFSET; break; case MT_SORCBALL2: angle = baseangle + BALL2_ANGLEOFFSET; break; case MT_SORCBALL3: angle = baseangle + BALL3_ANGLEOFFSET; break; default: I_Error("corrupted sorcerer"); return; } actor->angle = angle; angle >>= ANGLETOFINESHIFT; switch (mode) { case SORC_NORMAL: // Balls rotating normally A_SorcUpdateBallAngle(actor); break; case SORC_DECELERATE: // Balls decelerating A_DecelBalls(actor); A_SorcUpdateBallAngle(actor); break; case SORC_ACCELERATE: // Balls accelerating A_AccelBalls(actor); A_SorcUpdateBallAngle(actor); break; case SORC_STOPPING: // Balls stopping if ((parent->special2.i == actor->type) && (parent->args[1] > SORCBALL_SPEED_ROTATIONS) && (abs(angle - (parent->angle >> ANGLETOFINESHIFT)) < (30 << 5))) { // Can stop now actor->target->args[3] = SORC_FIRESPELL; actor->target->args[4] = 0; // Set angle so ball angle == sorcerer angle switch (actor->type) { case MT_SORCBALL1: parent->special1.i = (int) (parent->angle - BALL1_ANGLEOFFSET); break; case MT_SORCBALL2: parent->special1.i = (int) (parent->angle - BALL2_ANGLEOFFSET); break; case MT_SORCBALL3: parent->special1.i = (int) (parent->angle - BALL3_ANGLEOFFSET); break; default: break; } } else { A_SorcUpdateBallAngle(actor); } break; case SORC_FIRESPELL: // Casting spell if (parent->special2.i == actor->type) { // Put sorcerer into special throw spell anim if (parent->health > 0) P_SetMobjStateNF(parent, S_SORC_ATTACK1); if (actor->type == MT_SORCBALL1 && P_Random() < 200) { S_StartSound(NULL, SFX_SORCERER_SPELLCAST); actor->special2.i = SORCFX4_RAPIDFIRE_TIME; actor->args[4] = 128; parent->args[3] = SORC_FIRING_SPELL; } else { A_CastSorcererSpell(actor); parent->args[3] = SORC_STOPPED; } } break; case SORC_FIRING_SPELL: if (parent->special2.i == actor->type) { if (actor->special2.i-- <= 0) { // Done rapid firing parent->args[3] = SORC_STOPPED; // Back to orbit balls if (parent->health > 0) P_SetMobjStateNF(parent, S_SORC_ATTACK4); } else { // Do rapid fire spell A_SorcOffense2(actor); } } break; case SORC_STOPPED: // Balls stopped default: break; } if ((angle < prevangle) && (parent->args[4] == SORCBALL_TERMINAL_SPEED)) { parent->args[1]++; // Bump rotation counter // Completed full rotation - make woosh sound S_StartSound(actor, SFX_SORCERER_BALLWOOSH); } actor->special1.i = angle; // Set previous angle x = parent->x + FixedMul(dist, finecosine[angle]); y = parent->y + FixedMul(dist, finesine[angle]); actor->x = x; actor->y = y; actor->z = parent->z - parent->floorclip + parent->info->height; } // // Set balls to speed mode - actor is sorcerer // void A_SpeedBalls(mobj_t * actor) { actor->args[3] = SORC_ACCELERATE; // speed mode actor->args[2] = SORCBALL_TERMINAL_SPEED; // target speed } // // Set balls to slow mode - actor is sorcerer // void A_SlowBalls(mobj_t * actor) { actor->args[3] = SORC_DECELERATE; // slow mode actor->args[2] = SORCBALL_INITIAL_SPEED; // target speed } // // Instant stop when rotation gets to ball in special2 // actor is sorcerer // void A_StopBalls(mobj_t * actor) { int chance = P_Random(); actor->args[3] = SORC_STOPPING; // stopping mode actor->args[1] = 0; // Reset rotation counter if ((actor->args[0] <= 0) && (chance < 200)) { actor->special2.i = MT_SORCBALL2; // Blue } else if ((actor->health < (actor->info->spawnhealth >> 1)) && (chance < 200)) { actor->special2.i = MT_SORCBALL3; // Green } else { actor->special2.i = MT_SORCBALL1; // Yellow } } // // Increase ball orbit speed - actor is ball // void A_AccelBalls(mobj_t * actor) { mobj_t *sorc = actor->target; if (sorc->args[4] < sorc->args[2]) { sorc->args[4]++; } else { sorc->args[3] = SORC_NORMAL; if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED) { // Reached terminal velocity - stop balls A_StopBalls(sorc); } } } // Decrease ball orbit speed - actor is ball void A_DecelBalls(mobj_t * actor) { mobj_t *sorc = actor->target; if (sorc->args[4] > sorc->args[2]) { sorc->args[4]--; } else { sorc->args[3] = SORC_NORMAL; } } // Update angle if first ball - actor is ball void A_SorcUpdateBallAngle(mobj_t * actor) { if (actor->type == MT_SORCBALL1) { actor->target->special1.i += ANG1 * actor->target->args[4]; } } // actor is ball void A_CastSorcererSpell(mobj_t * actor) { mobj_t *mo; int spell = actor->type; angle_t ang1, ang2; fixed_t z; mobj_t *parent = actor->target; S_StartSound(NULL, SFX_SORCERER_SPELLCAST); // Put sorcerer into throw spell animation if (parent->health > 0) P_SetMobjStateNF(parent, S_SORC_ATTACK4); switch (spell) { case MT_SORCBALL1: // Offensive A_SorcOffense1(actor); break; case MT_SORCBALL2: // Defensive z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT * FRACUNIT; mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCFX2); parent->flags2 |= MF2_REFLECTIVE | MF2_INVULNERABLE; parent->args[0] = SORC_DEFENSE_TIME; if (mo) mo->target = parent; break; case MT_SORCBALL3: // Reinforcements ang1 = actor->angle - ANG45; ang2 = actor->angle + ANG45; if (actor->health < (actor->info->spawnhealth / 3)) { // Spawn 2 at a time mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4 * FRACUNIT); if (mo) mo->target = parent; mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang2, 4 * FRACUNIT); if (mo) mo->target = parent; } else { if (P_Random() < 128) ang1 = ang2; mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4 * FRACUNIT); if (mo) mo->target = parent; } break; default: break; } } /* void A_SpawnReinforcements(mobj_t *actor) { mobj_t *parent = actor->target; mobj_t *mo; angle_t ang; ang = ANG1 * P_Random(); mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5*FRACUNIT); if (mo) mo->target = parent; } */ // actor is ball void A_SorcOffense1(mobj_t * actor) { mobj_t *mo; angle_t ang1, ang2; mobj_t *parent = (mobj_t *) actor->target; ang1 = actor->angle + ANG1 * 70; ang2 = actor->angle - ANG1 * 70; mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang1, 0); if (mo) { mo->target = parent; mo->special1.m = parent->target; mo->args[4] = BOUNCE_TIME_UNIT; mo->args[3] = 15; // Bounce time in seconds } mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang2, 0); if (mo) { mo->target = parent; mo->special1.m = parent->target; mo->args[4] = BOUNCE_TIME_UNIT; mo->args[3] = 15; // Bounce time in seconds } } // Actor is ball void A_SorcOffense2(mobj_t * actor) { angle_t ang1; mobj_t *mo; int delta, index; mobj_t *parent = actor->target; mobj_t *dest = parent->target; int dist; index = actor->args[4] << 5; actor->args[4] += 15; delta = (finesine[index]) * SORCFX4_SPREAD_ANGLE; delta = (delta >> FRACBITS) * ANG1; ang1 = actor->angle + delta; mo = P_SpawnMissileAngle(parent, MT_SORCFX4, ang1, 0); if (mo) { mo->special2.i = 35 * 5 / 2; // 5 seconds dist = P_AproxDistance(dest->x - mo->x, dest->y - mo->y); dist = dist / mo->info->speed; if (dist < 1) dist = 1; mo->momz = (dest->z - mo->z) / dist; } } // Resume ball spinning void A_SorcBossAttack(mobj_t * actor) { actor->args[3] = SORC_ACCELERATE; actor->args[2] = SORCBALL_INITIAL_SPEED; } // spell cast magic fizzle void A_SpawnFizzle(mobj_t * actor) { fixed_t x, y, z; fixed_t dist = 5 * FRACUNIT; angle_t angle = actor->angle >> ANGLETOFINESHIFT; fixed_t speed = actor->info->speed; angle_t rangle; mobj_t *mo; int ix; x = actor->x + FixedMul(dist, finecosine[angle]); y = actor->y + FixedMul(dist, finesine[angle]); z = actor->z - actor->floorclip + (actor->height >> 1); for (ix = 0; ix < 5; ix++) { mo = P_SpawnMobj(x, y, z, MT_SORCSPARK1); if (mo) { rangle = angle + ((P_Random() % 5) << 1); mo->momx = FixedMul(P_Random() % speed, finecosine[rangle]); mo->momy = FixedMul(P_Random() % speed, finesine[rangle]); mo->momz = FRACUNIT * 2; } } } //============================================================================ // Yellow spell - offense //============================================================================ void A_SorcFX1Seek(mobj_t * actor) { A_BounceCheck(actor); P_SeekerMissile(actor, ANG1 * 2, ANG1 * 6); } //============================================================================ // Blue spell - defense //============================================================================ // // FX2 Variables // special1 current angle // special2 // args[0] 0 = CW, 1 = CCW // args[1] //============================================================================ // Split ball in two void A_SorcFX2Split(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2); if (mo) { mo->target = actor->target; mo->args[0] = 0; // CW mo->special1.i = actor->angle; // Set angle P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1); } mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2); if (mo) { mo->target = actor->target; mo->args[0] = 1; // CCW mo->special1.i = actor->angle; // Set angle P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1); } P_SetMobjStateNF(actor, S_NULL); } // Orbit FX2 about sorcerer void A_SorcFX2Orbit(mobj_t * actor) { angle_t angle; fixed_t x, y, z; mobj_t *parent = actor->target; fixed_t dist = parent->info->radius; if ((parent->health <= 0) || // Sorcerer is dead (!parent->args[0])) // Time expired { P_SetMobjStateNF(actor, actor->info->deathstate); parent->args[0] = 0; parent->flags2 &= ~MF2_REFLECTIVE; parent->flags2 &= ~MF2_INVULNERABLE; } if (actor->args[0] && (parent->args[0]-- <= 0)) // Time expired { P_SetMobjStateNF(actor, actor->info->deathstate); parent->args[0] = 0; parent->flags2 &= ~MF2_REFLECTIVE; } // Move to new position based on angle if (actor->args[0]) // Counter clock-wise { actor->special1.i += ANG1 * 10; angle = ((angle_t) actor->special1.i) >> ANGLETOFINESHIFT; x = parent->x + FixedMul(dist, finecosine[angle]); y = parent->y + FixedMul(dist, finesine[angle]); z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT * FRACUNIT; z += FixedMul(15 * FRACUNIT, finecosine[angle]); // Spawn trailer P_SpawnMobj(x, y, z, MT_SORCFX2_T1); } else // Clock wise { actor->special1.i -= ANG1 * 10; angle = ((angle_t) actor->special1.i) >> ANGLETOFINESHIFT; x = parent->x + FixedMul(dist, finecosine[angle]); y = parent->y + FixedMul(dist, finesine[angle]); z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT * FRACUNIT; z += FixedMul(20 * FRACUNIT, finesine[angle]); // Spawn trailer P_SpawnMobj(x, y, z, MT_SORCFX2_T1); } actor->x = x; actor->y = y; actor->z = z; } //============================================================================ // Green spell - spawn bishops //============================================================================ void A_SpawnBishop(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOP); if (mo) { if (!P_TestMobjLocation(mo)) { P_SetMobjState(mo, S_NULL); } } P_SetMobjState(actor, S_NULL); } /* void A_SmokePuffEntry(mobj_t *actor) { P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE); } */ void A_SmokePuffExit(mobj_t * actor) { P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKEEXIT); } void A_SorcererBishopEntry(mobj_t * actor) { P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX3_EXPLOSION); S_StartSound(actor, actor->info->seesound); } //============================================================================ // FX4 - rapid fire balls //============================================================================ void A_SorcFX4Check(mobj_t * actor) { if (actor->special2.i-- <= 0) { P_SetMobjStateNF(actor, actor->info->deathstate); } } //============================================================================ // Ball death - spawn stuff //============================================================================ void A_SorcBallPop(mobj_t * actor) { S_StartSound(NULL, SFX_SORCERER_BALLPOP); actor->flags &= ~MF_NOGRAVITY; actor->flags2 |= MF2_LOGRAV; actor->momx = ((P_Random() % 10) - 5) << FRACBITS; actor->momy = ((P_Random() % 10) - 5) << FRACBITS; actor->momz = (2 + (P_Random() % 3)) << FRACBITS; actor->special2.i = 4 * FRACUNIT; // Initial bounce factor actor->args[4] = BOUNCE_TIME_UNIT; // Bounce time unit actor->args[3] = 5; // Bounce time in seconds } void A_BounceCheck(mobj_t * actor) { if (actor->args[4]-- <= 0) { if (actor->args[3]-- <= 0) { P_SetMobjState(actor, actor->info->deathstate); switch (actor->type) { case MT_SORCBALL1: case MT_SORCBALL2: case MT_SORCBALL3: S_StartSound(NULL, SFX_SORCERER_BIGBALLEXPLODE); break; case MT_SORCFX1: S_StartSound(NULL, SFX_SORCERER_HEADSCREAM); break; default: break; } } else { actor->args[4] = BOUNCE_TIME_UNIT; } } } //============================================================================ // Class Bosses //============================================================================ #define CLASS_BOSS_STRAFE_RANGE 64*10*FRACUNIT void A_FastChase(mobj_t * actor) { int delta; fixed_t dist; angle_t ang; mobj_t *target; if (actor->reactiontime) { actor->reactiontime--; } // Modify target threshold if (actor->threshold) { actor->threshold--; } if (gameskill == sk_nightmare) { // Monsters move faster in nightmare mode actor->tics -= actor->tics / 2; if (actor->tics < 3) { actor->tics = 3; } } // // turn towards movement direction if not there yet // if (actor->movedir < 8) { actor->angle &= (7 << 29); delta = actor->angle - (actor->movedir << 29); if (delta > 0) { actor->angle -= ANG90 / 2; } else if (delta < 0) { actor->angle += ANG90 / 2; } } if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) { // look for a new target if (P_LookForPlayers(actor, true)) { // got a new target return; } P_SetMobjState(actor, actor->info->spawnstate); return; } // // don't attack twice in a row // if (actor->flags & MF_JUSTATTACKED) { actor->flags &= ~MF_JUSTATTACKED; if (gameskill != sk_nightmare) P_NewChaseDir(actor); return; } // Strafe if (actor->special2.i > 0) { actor->special2.i--; } else { target = actor->target; actor->special2.i = 0; actor->momx = actor->momy = 0; dist = P_AproxDistance(actor->x - target->x, actor->y - target->y); if (dist < CLASS_BOSS_STRAFE_RANGE) { if (P_Random() < 100) { ang = R_PointToAngle2(actor->x, actor->y, target->x, target->y); if (P_Random() < 128) ang += ANG90; else ang -= ANG90; ang >>= ANGLETOFINESHIFT; actor->momx = FixedMul(13 * FRACUNIT, finecosine[ang]); actor->momy = FixedMul(13 * FRACUNIT, finesine[ang]); actor->special2.i = 3; // strafe time } } } // // check for missile attack // if (actor->info->missilestate) { if (gameskill < sk_nightmare && actor->movecount) goto nomissile; if (!P_CheckMissileRange(actor)) goto nomissile; P_SetMobjState(actor, actor->info->missilestate); actor->flags |= MF_JUSTATTACKED; return; } nomissile: // // possibly choose another target // if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target)) { if (P_LookForPlayers(actor, true)) return; // got a new target } // // chase towards player // if (!actor->special2.i) { if (--actor->movecount < 0 || !P_Move(actor)) { P_NewChaseDir(actor); } } } void A_FighterAttack(mobj_t * actor) { extern void A_FSwordAttack2(mobj_t * actor); if (!actor->target) return; A_FSwordAttack2(actor); } void A_ClericAttack(mobj_t * actor) { extern void A_CHolyAttack3(mobj_t * actor); if (!actor->target) return; A_CHolyAttack3(actor); } void A_MageAttack(mobj_t * actor) { extern void A_MStaffAttack2(mobj_t * actor); if (!actor->target) return; A_MStaffAttack2(actor); } void A_ClassBossHealth(mobj_t * actor) { if (netgame && !deathmatch) // co-op only { if (!actor->special1.i) { actor->health *= 5; actor->special1.i = true; // has been initialized } } } //=========================================================================== // // A_CheckFloor - Checks if an object hit the floor // //=========================================================================== void A_CheckFloor(mobj_t * actor) { if (actor->z <= actor->floorz) { actor->z = actor->floorz; actor->flags2 &= ~MF2_LOGRAV; P_SetMobjState(actor, actor->info->deathstate); } } //============================================================================ // // A_FreezeDeath // //============================================================================ void A_FreezeDeath(mobj_t * actor) { int r = P_Random(); actor->tics = 75 + r + P_Random(); actor->flags |= MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD; actor->flags2 |= MF2_PUSHABLE | MF2_TELESTOMP | MF2_PASSMOBJ | MF2_SLIDE; actor->height <<= 2; S_StartSound(actor, SFX_FREEZE_DEATH); if (actor->player) { actor->player->damagecount = 0; actor->player->poisoncount = 0; actor->player->bonuscount = 0; if (actor->player == &players[consoleplayer]) { SB_PaletteFlash(false); } } else if (actor->flags & MF_COUNTKILL && actor->special) { // Initiate monster death actions. P_ExecuteLineSpecial(actor->special, actor->args, NULL, 0, actor); } } //============================================================================ // // A_IceSetTics // //============================================================================ void A_IceSetTics(mobj_t * actor) { int floor; actor->tics = 70 + (P_Random() & 63); floor = P_GetThingFloorType(actor); if (floor == FLOOR_LAVA) { actor->tics >>= 2; } else if (floor == FLOOR_ICE) { actor->tics <<= 1; } } //============================================================================ // // A_IceCheckHeadDone // //============================================================================ void A_IceCheckHeadDone(mobj_t * actor) { if (actor->special2.i == 666) { P_SetMobjState(actor, S_ICECHUNK_HEAD2); } } //============================================================================ // // A_FreezeDeathChunks // //============================================================================ void A_FreezeDeathChunks(mobj_t * actor) { int i; int r1,r2,r3; mobj_t *mo; if (actor->momx || actor->momy || actor->momz) { actor->tics = 105; return; } S_StartSound(actor, SFX_FREEZE_SHATTER); for (i = 12 + (P_Random() & 15); i >= 0; i--) { r1 = P_Random(); r2 = P_Random(); r3 = P_Random(); mo = P_SpawnMobj(actor->x + (((r3 - 128) * actor->radius) >> 7), actor->y + (((r2 - 128) * actor->radius) >> 7), actor->z + (r1 * actor->height / 255), MT_ICECHUNK); P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3)); mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2; mo->momx = P_SubRandom() << (FRACBITS - 7); mo->momy = P_SubRandom() << (FRACBITS - 7); A_IceSetTics(mo); // set a random tic wait } for (i = 12 + (P_Random() & 15); i >= 0; i--) { r1 = P_Random(); r2 = P_Random(); r3 = P_Random(); mo = P_SpawnMobj(actor->x + (((r3 - 128) * actor->radius) >> 7), actor->y + (((r2 - 128) * actor->radius) >> 7), actor->z + (r1 * actor->height / 255), MT_ICECHUNK); P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 3)); mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2; mo->momx = P_SubRandom() << (FRACBITS - 7); mo->momy = P_SubRandom() << (FRACBITS - 7); A_IceSetTics(mo); // set a random tic wait } if (actor->player) { // attach the player's view to a chunk of ice mo = P_SpawnMobj(actor->x, actor->y, actor->z + VIEWHEIGHT, MT_ICECHUNK); P_SetMobjState(mo, S_ICECHUNK_HEAD); mo->momz = FixedDiv(mo->z - actor->z, actor->height) << 2; mo->momx = P_SubRandom() << (FRACBITS - 7); mo->momy = P_SubRandom() << (FRACBITS - 7); mo->flags2 |= MF2_ICEDAMAGE; // used to force blue palette mo->flags2 &= ~MF2_FLOORCLIP; mo->player = actor->player; actor->player = NULL; mo->health = actor->health; mo->angle = actor->angle; mo->player->mo = mo; mo->player->lookdir = 0; } P_RemoveMobjFromTIDList(actor); P_SetMobjState(actor, S_FREETARGMOBJ); actor->flags2 |= MF2_DONTDRAW; } //=========================================================================== // Korax Variables // special1 last teleport destination // special2 set if "below half" script not yet run // // Korax Scripts (reserved) // 249 Tell scripts that we are below half health // 250-254 Control scripts // 255 Death script // // Korax TIDs (reserved) // 245 Reserved for Korax himself // 248 Initial teleport destination // 249 Teleport destination // 250-254 For use in respective control scripts // 255 For use in death script (spawn spots) //=========================================================================== #define KORAX_SPIRIT_LIFETIME (5*(35/5)) // 5 seconds #define KORAX_COMMAND_HEIGHT (120*FRACUNIT) #define KORAX_COMMAND_OFFSET (27*FRACUNIT) void KoraxFire1(mobj_t * actor, int type); void KoraxFire2(mobj_t * actor, int type); void KoraxFire3(mobj_t * actor, int type); void KoraxFire4(mobj_t * actor, int type); void KoraxFire5(mobj_t * actor, int type); void KoraxFire6(mobj_t * actor, int type); void KSpiritInit(mobj_t * spirit, mobj_t * korax); #define KORAX_TID (245) #define KORAX_FIRST_TELEPORT_TID (248) #define KORAX_TELEPORT_TID (249) void A_KoraxChase(mobj_t * actor) { mobj_t *spot; int lastfound; byte args[3] = {0, 0, 0}; if ((!actor->special2.i) && (actor->health <= (actor->info->spawnhealth / 2))) { lastfound = 0; spot = P_FindMobjFromTID(KORAX_FIRST_TELEPORT_TID, &lastfound); if (spot) { P_Teleport(actor, spot->x, spot->y, spot->angle, true); } CheckACSPresent(249); P_StartACS(249, 0, args, actor, NULL, 0); actor->special2.i = 1; // Don't run again return; } if (!actor->target) return; if (P_Random() < 30) { P_SetMobjState(actor, actor->info->missilestate); } else if (P_Random() < 30) { S_StartSound(NULL, SFX_KORAX_ACTIVE); } // Teleport away if (actor->health < (actor->info->spawnhealth >> 1)) { if (P_Random() < 10) { lastfound = actor->special1.i; spot = P_FindMobjFromTID(KORAX_TELEPORT_TID, &lastfound); actor->special1.i = lastfound; if (spot) { P_Teleport(actor, spot->x, spot->y, spot->angle, true); } } } } void A_KoraxStep(mobj_t * actor) { A_Chase(actor); } void A_KoraxStep2(mobj_t * actor) { S_StartSound(NULL, SFX_KORAX_STEP); A_Chase(actor); } void A_KoraxBonePop(mobj_t * actor) { mobj_t *mo; byte args[5]; args[0] = args[1] = args[2] = args[3] = args[4] = 0; // Spawn 6 spirits equalangularly mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT1, ANG60 * 0, 5 * FRACUNIT); if (mo) KSpiritInit(mo, actor); mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT2, ANG60 * 1, 5 * FRACUNIT); if (mo) KSpiritInit(mo, actor); mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT3, ANG60 * 2, 5 * FRACUNIT); if (mo) KSpiritInit(mo, actor); mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT4, ANG60 * 3, 5 * FRACUNIT); if (mo) KSpiritInit(mo, actor); mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT5, ANG60 * 4, 5 * FRACUNIT); if (mo) KSpiritInit(mo, actor); mo = P_SpawnMissileAngle(actor, MT_KORAX_SPIRIT6, ANG60 * 5, 5 * FRACUNIT); if (mo) KSpiritInit(mo, actor); CheckACSPresent(255); P_StartACS(255, 0, args, actor, NULL, 0); // Death script } void KSpiritInit(mobj_t * spirit, mobj_t * korax) { int i; mobj_t *tail, *next; spirit->health = KORAX_SPIRIT_LIFETIME; spirit->special1.m = korax; // Swarm around korax spirit->special2.i = 32 + (P_Random() & 7); // Float bob index spirit->args[0] = 10; // initial turn value spirit->args[1] = 0; // initial look angle // Spawn a tail for spirit tail = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL); tail->special2.m = spirit; // parent for (i = 1; i < 3; i++) { next = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL); P_SetMobjState(next, next->info->spawnstate + 1); tail->special1.m = next; tail = next; } tail->special1.m = NULL; // last tail bit } void A_KoraxDecide(mobj_t * actor) { if (P_Random() < 220) { P_SetMobjState(actor, S_KORAX_MISSILE1); } else { P_SetMobjState(actor, S_KORAX_COMMAND1); } } void A_KoraxMissile(mobj_t * actor) { int type = P_Random() % 6; int sound = 0; S_StartSound(actor, SFX_KORAX_ATTACK); switch (type) { case 0: type = MT_WRAITHFX1; sound = SFX_WRAITH_MISSILE_FIRE; break; case 1: type = MT_DEMONFX1; sound = SFX_DEMON_MISSILE_FIRE; break; case 2: type = MT_DEMON2FX1; sound = SFX_DEMON_MISSILE_FIRE; break; case 3: type = MT_FIREDEMON_FX6; sound = SFX_FIRED_ATTACK; break; case 4: type = MT_CENTAUR_FX; sound = SFX_CENTAURLEADER_ATTACK; break; case 5: type = MT_SERPENTFX; sound = SFX_CENTAURLEADER_ATTACK; break; } // Fire all 6 missiles at once S_StartSound(NULL, sound); KoraxFire1(actor, type); KoraxFire2(actor, type); KoraxFire3(actor, type); KoraxFire4(actor, type); KoraxFire5(actor, type); KoraxFire6(actor, type); } // Call action code scripts (250-254) void A_KoraxCommand(mobj_t * actor) { byte args[5]; fixed_t x, y, z; angle_t ang; int numcommands; S_StartSound(actor, SFX_KORAX_COMMAND); // Shoot stream of lightning to ceiling ang = (actor->angle - ANG90) >> ANGLETOFINESHIFT; x = actor->x + FixedMul(KORAX_COMMAND_OFFSET, finecosine[ang]); y = actor->y + FixedMul(KORAX_COMMAND_OFFSET, finesine[ang]); z = actor->z + KORAX_COMMAND_HEIGHT; P_SpawnMobj(x, y, z, MT_KORAX_BOLT); args[0] = args[1] = args[2] = args[3] = args[4] = 0; if (actor->health <= (actor->info->spawnhealth >> 1)) { numcommands = 5; } else { numcommands = 4; } switch (P_Random() % numcommands) { case 0: CheckACSPresent(250); P_StartACS(250, 0, args, actor, NULL, 0); break; case 1: CheckACSPresent(251); P_StartACS(251, 0, args, actor, NULL, 0); break; case 2: CheckACSPresent(252); P_StartACS(252, 0, args, actor, NULL, 0); break; case 3: CheckACSPresent(253); P_StartACS(253, 0, args, actor, NULL, 0); break; case 4: CheckACSPresent(254); P_StartACS(254, 0, args, actor, NULL, 0); break; } } #define KORAX_DELTAANGLE (85*ANG1) #define KORAX_ARM_EXTENSION_SHORT (40*FRACUNIT) #define KORAX_ARM_EXTENSION_LONG (55*FRACUNIT) #define KORAX_ARM1_HEIGHT (108*FRACUNIT) #define KORAX_ARM2_HEIGHT (82*FRACUNIT) #define KORAX_ARM3_HEIGHT (54*FRACUNIT) #define KORAX_ARM4_HEIGHT (104*FRACUNIT) #define KORAX_ARM5_HEIGHT (86*FRACUNIT) #define KORAX_ARM6_HEIGHT (53*FRACUNIT) // Arm projectiles // arm positions numbered: // 1 top left // 2 middle left // 3 lower left // 4 top right // 5 middle right // 6 lower right // Arm 1 projectile void KoraxFire1(mobj_t * actor, int type) { angle_t ang; fixed_t x, y, z; ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]); y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]); z = actor->z - actor->floorclip + KORAX_ARM1_HEIGHT; P_SpawnKoraxMissile(x, y, z, actor, actor->target, type); } // Arm 2 projectile void KoraxFire2(mobj_t * actor, int type) { angle_t ang; fixed_t x, y, z; ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); z = actor->z - actor->floorclip + KORAX_ARM2_HEIGHT; P_SpawnKoraxMissile(x, y, z, actor, actor->target, type); } // Arm 3 projectile void KoraxFire3(mobj_t * actor, int type) { angle_t ang; fixed_t x, y, z; ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); z = actor->z - actor->floorclip + KORAX_ARM3_HEIGHT; P_SpawnKoraxMissile(x, y, z, actor, actor->target, type); } // Arm 4 projectile void KoraxFire4(mobj_t * actor, int type) { angle_t ang; fixed_t x, y, z; ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]); y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]); z = actor->z - actor->floorclip + KORAX_ARM4_HEIGHT; P_SpawnKoraxMissile(x, y, z, actor, actor->target, type); } // Arm 5 projectile void KoraxFire5(mobj_t * actor, int type) { angle_t ang; fixed_t x, y, z; ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); z = actor->z - actor->floorclip + KORAX_ARM5_HEIGHT; P_SpawnKoraxMissile(x, y, z, actor, actor->target, type); } // Arm 6 projectile void KoraxFire6(mobj_t * actor, int type) { angle_t ang; fixed_t x, y, z; ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT; x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]); y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]); z = actor->z - actor->floorclip + KORAX_ARM6_HEIGHT; P_SpawnKoraxMissile(x, y, z, actor, actor->target, type); } void A_KSpiritWeave(mobj_t * actor) { fixed_t newX, newY; int weaveXY, weaveZ; int angle; weaveXY = actor->special2.i >> 16; weaveZ = actor->special2.i & 0xFFFF; angle = (actor->angle + ANG90) >> ANGLETOFINESHIFT; newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 2); newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 2); weaveXY = (weaveXY + (P_Random() % 5)) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 2); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 2); P_TryMove(actor, newX, newY); actor->z -= FloatBobOffsets[weaveZ] << 1; weaveZ = (weaveZ + (P_Random() % 5)) & 63; actor->z += FloatBobOffsets[weaveZ] << 1; actor->special2.i = weaveZ + (weaveXY << 16); } void A_KSpiritSeeker(mobj_t * actor, angle_t thresh, angle_t turnMax) { int dir; int dist; angle_t delta; angle_t angle; mobj_t *target; fixed_t newZ; fixed_t deltaZ; target = actor->special1.m; if (target == NULL) { return; } dir = P_FaceMobj(actor, target, &delta); if (delta > thresh) { delta >>= 1; if (delta > turnMax) { delta = turnMax; } } if (dir) { // Turn clockwise actor->angle += delta; } else { // Turn counter clockwise actor->angle -= delta; } angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(actor->info->speed, finecosine[angle]); actor->momy = FixedMul(actor->info->speed, finesine[angle]); if (!(leveltime & 15) || actor->z > target->z + (target->info->height) || actor->z + actor->height < target->z) { newZ = target->z + ((P_Random() * target->info->height) >> 8); deltaZ = newZ - actor->z; if (abs(deltaZ) > 15 * FRACUNIT) { if (deltaZ > 0) { deltaZ = 15 * FRACUNIT; } else { deltaZ = -15 * FRACUNIT; } } dist = P_AproxDistance(target->x - actor->x, target->y - actor->y); dist = dist / actor->info->speed; if (dist < 1) { dist = 1; } actor->momz = deltaZ / dist; } return; } void A_KSpiritRoam(mobj_t * actor) { if (actor->health-- <= 0) { S_StartSound(actor, SFX_SPIRIT_DIE); P_SetMobjState(actor, S_KSPIRIT_DEATH1); } else { if (actor->special1.m) { A_KSpiritSeeker(actor, actor->args[0] * ANG1, actor->args[0] * ANG1 * 2); } A_KSpiritWeave(actor); if (P_Random() < 50) { S_StartSound(NULL, SFX_SPIRIT_ACTIVE); } } } void A_KBolt(mobj_t * actor) { // Countdown lifetime if (actor->special1.i-- <= 0) { P_SetMobjState(actor, S_NULL); } } #define KORAX_BOLT_HEIGHT 48*FRACUNIT #define KORAX_BOLT_LIFETIME 3 void A_KBoltRaise(mobj_t * actor) { mobj_t *mo; fixed_t z; // Spawn a child upward z = actor->z + KORAX_BOLT_HEIGHT; if ((z + KORAX_BOLT_HEIGHT) < actor->ceilingz) { mo = P_SpawnMobj(actor->x, actor->y, z, MT_KORAX_BOLT); if (mo) { mo->special1.i = KORAX_BOLT_LIFETIME; } } else { // Maybe cap it off here } } crispy-doom-crispy-doom-5.6.4/src/hexen/p_floor.c000066400000000000000000000735541360717211000217540ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_system.h" #include "p_local.h" extern fixed_t FloatBobOffsets[64]; //================================================================== //================================================================== // // FLOORS // //================================================================== //================================================================== //================================================================== // // Move a plane (floor or ceiling) and check for crushing // //================================================================== result_e T_MovePlane(sector_t * sector, fixed_t speed, fixed_t dest, int crush, int floorOrCeiling, int direction) { boolean flag; fixed_t lastpos; switch (floorOrCeiling) { case 0: // FLOOR switch (direction) { case -1: // DOWN if (sector->floorheight - speed < dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else { lastpos = sector->floorheight; sector->floorheight -= speed; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } } break; case 1: // UP if (sector->floorheight + speed > dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else // COULD GET CRUSHED { lastpos = sector->floorheight; sector->floorheight += speed; flag = P_ChangeSector(sector, crush); if (flag == true) { //if (crush == true) //{ // return RES_CRUSHED; //} sector->floorheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } } break; } break; case 1: // CEILING switch (direction) { case -1: // DOWN if (sector->ceilingheight - speed < dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else // COULD GET CRUSHED { lastpos = sector->ceilingheight; sector->ceilingheight -= speed; flag = P_ChangeSector(sector, crush); if (flag == true) { //if (crush == true) //{ // return RES_CRUSHED; //} sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } } break; case 1: // UP if (sector->ceilingheight + speed > dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else { lastpos = sector->ceilingheight; sector->ceilingheight += speed; flag = P_ChangeSector(sector, crush); #if 0 if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } #endif } break; } break; } return RES_OK; } //================================================================== // // MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) // //================================================================== void T_MoveFloor(floormove_t * floor) { result_e res; if (floor->resetDelayCount) { floor->resetDelayCount--; if (!floor->resetDelayCount) { floor->floordestheight = floor->resetHeight; floor->direction = -floor->direction; floor->resetDelay = 0; floor->delayCount = 0; floor->delayTotal = 0; } } if (floor->delayCount) { floor->delayCount--; if (!floor->delayCount && floor->textureChange) { floor->sector->floorpic += floor->textureChange; } return; } res = T_MovePlane(floor->sector, floor->speed, floor->floordestheight, floor->crush, 0, floor->direction); if (floor->type == FLEV_RAISEBUILDSTEP) { if ((floor->direction == 1 && floor->sector->floorheight >= floor->stairsDelayHeight) || (floor->direction == -1 && floor->sector->floorheight <= floor->stairsDelayHeight)) { floor->delayCount = floor->delayTotal; floor->stairsDelayHeight += floor->stairsDelayHeightDelta; } } if (res == RES_PASTDEST) { SN_StopSequence((mobj_t *) & floor->sector->soundorg); if (floor->delayTotal) { floor->delayTotal = 0; } if (floor->resetDelay) { // floor->resetDelayCount = floor->resetDelay; // floor->resetDelay = 0; return; } floor->sector->specialdata = NULL; /* if (floor->direction == 1) switch(floor->type) { case donutRaise: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } else if (floor->direction == -1) switch(floor->type) { case lowerAndChange: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } */ if (floor->textureChange) { floor->sector->floorpic -= floor->textureChange; } P_TagFinished(floor->sector->tag); P_RemoveThinker(&floor->thinker); } } //================================================================== // // HANDLE FLOOR TYPES // //================================================================== int EV_DoFloor(line_t * line, byte * args, floor_e floortype) { int secnum; int rtn; sector_t *sec; floormove_t *floor = NULL; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // // new floor thinker // rtn = 1; floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); memset(floor, 0, sizeof(*floor)); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = floortype; floor->crush = 0; floor->speed = args[1] * (FRACUNIT / 8); if (floortype == FLEV_LOWERTIMES8INSTANT || floortype == FLEV_RAISETIMES8INSTANT) { floor->speed = 2000 << FRACBITS; } switch (floortype) { case FLEV_LOWERFLOOR: floor->direction = -1; floor->sector = sec; floor->floordestheight = P_FindHighestFloorSurrounding(sec); break; case FLEV_LOWERFLOORTOLOWEST: floor->direction = -1; floor->sector = sec; floor->floordestheight = P_FindLowestFloorSurrounding(sec); break; case FLEV_LOWERFLOORBYVALUE: floor->direction = -1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight - args[2] * FRACUNIT; break; case FLEV_LOWERTIMES8INSTANT: case FLEV_LOWERBYVALUETIMES8: floor->direction = -1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight - args[2] * FRACUNIT * 8; break; case FLEV_RAISEFLOORCRUSH: floor->crush = args[2]; // arg[2] = crushing value floor->direction = 1; floor->sector = sec; floor->floordestheight = sec->ceilingheight - 8 * FRACUNIT; break; case FLEV_RAISEFLOOR: floor->direction = 1; floor->sector = sec; floor->floordestheight = P_FindLowestCeilingSurrounding(sec); if (floor->floordestheight > sec->ceilingheight) floor->floordestheight = sec->ceilingheight; break; case FLEV_RAISEFLOORTONEAREST: floor->direction = 1; floor->sector = sec; floor->floordestheight = P_FindNextHighestFloor(sec, sec->floorheight); break; case FLEV_RAISEFLOORBYVALUE: floor->direction = 1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight + args[2] * FRACUNIT; break; case FLEV_RAISETIMES8INSTANT: case FLEV_RAISEBYVALUETIMES8: floor->direction = 1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight + args[2] * FRACUNIT * 8; break; case FLEV_MOVETOVALUETIMES8: floor->sector = sec; floor->floordestheight = args[2] * FRACUNIT * 8; if (args[3]) { floor->floordestheight = -floor->floordestheight; } if (floor->floordestheight > floor->sector->floorheight) { floor->direction = 1; } else if (floor->floordestheight < floor->sector->floorheight) { floor->direction = -1; } else { // already at lowest position rtn = 0; } break; default: rtn = 0; break; } } if (rtn) { SN_StartSequence((mobj_t *) & floor->sector->soundorg, SEQ_PLATFORM + floor->sector->seqType); } return rtn; } //============================================================================ // // EV_DoFloorAndCeiling // //============================================================================ int EV_DoFloorAndCeiling(line_t * line, byte * args, boolean raise) { boolean floor, ceiling; int secnum; sector_t *sec; if (raise) { floor = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE); secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; sec->specialdata = NULL; } ceiling = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE); } else { floor = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE); secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; sec->specialdata = NULL; } ceiling = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE); } return (floor | ceiling); } // ===== Build Stairs Private Data ===== #define STAIR_SECTOR_TYPE 26 #define STAIR_QUEUE_SIZE 32 struct { sector_t *sector; int type; int height; } StairQueue[STAIR_QUEUE_SIZE]; static int QueueHead; static int QueueTail; static int StepDelta; static int Direction; static int Speed; static int Texture; static int StartDelay; static int StartDelayDelta; static int TextureChange; static int StartHeight; //========================================================================== // // QueueStairSector // //========================================================================== static void QueueStairSector(sector_t * sec, int type, int height) { if ((QueueTail + 1) % STAIR_QUEUE_SIZE == QueueHead) { I_Error("BuildStairs: Too many branches located.\n"); } StairQueue[QueueTail].sector = sec; StairQueue[QueueTail].type = type; StairQueue[QueueTail].height = height; QueueTail = (QueueTail + 1) % STAIR_QUEUE_SIZE; } //========================================================================== // // DequeueStairSector // //========================================================================== static sector_t *DequeueStairSector(int *type, int *height) { sector_t *sec; if (QueueHead == QueueTail) { // queue is empty return NULL; } *type = StairQueue[QueueHead].type; *height = StairQueue[QueueHead].height; sec = StairQueue[QueueHead].sector; QueueHead = (QueueHead + 1) % STAIR_QUEUE_SIZE; return sec; } //========================================================================== // // ProcessStairSector // //========================================================================== static void ProcessStairSector(sector_t * sec, int type, int height, stairs_e stairsType, int delay, int resetDelay) { int i; sector_t *tsec; floormove_t *floor; // // new floor thinker // height += StepDelta; floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); memset(floor, 0, sizeof(*floor)); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = FLEV_RAISEBUILDSTEP; floor->direction = Direction; floor->sector = sec; floor->floordestheight = height; switch (stairsType) { case STAIRS_NORMAL: floor->speed = Speed; if (delay) { floor->delayTotal = delay; floor->stairsDelayHeight = sec->floorheight + StepDelta; floor->stairsDelayHeightDelta = StepDelta; } floor->resetDelay = resetDelay; floor->resetDelayCount = resetDelay; floor->resetHeight = sec->floorheight; break; case STAIRS_SYNC: floor->speed = FixedMul(Speed, FixedDiv(height - StartHeight, StepDelta)); floor->resetDelay = delay; //arg4 floor->resetDelayCount = delay; floor->resetHeight = sec->floorheight; break; /* case STAIRS_PHASED: floor->floordestheight = sec->floorheight+StepDelta; floor->speed = Speed; floor->delayCount = StartDelay; StartDelay += StartDelayDelta; floor->textureChange = TextureChange; floor->resetDelayCount = StartDelay; break; */ default: break; } SN_StartSequence((mobj_t *) & sec->soundorg, SEQ_PLATFORM + sec->seqType); // // Find next sector to raise // Find nearby sector with sector special equal to type // for (i = 0; i < sec->linecount; i++) { if (!((sec->lines[i])->flags & ML_TWOSIDED)) { continue; } tsec = (sec->lines[i])->frontsector; if (tsec->special == type + STAIR_SECTOR_TYPE && !tsec->specialdata && tsec->floorpic == Texture && tsec->validcount != validcount) { QueueStairSector(tsec, type ^ 1, height); tsec->validcount = validcount; //tsec->special = 0; } tsec = (sec->lines[i])->backsector; if (tsec->special == type + STAIR_SECTOR_TYPE && !tsec->specialdata && tsec->floorpic == Texture && tsec->validcount != validcount) { QueueStairSector(tsec, type ^ 1, height); tsec->validcount = validcount; //tsec->special = 0; } } } //================================================================== // // BUILD A STAIRCASE! // // Direction is either positive or negative, denoting build stairs // up or down. //================================================================== int EV_BuildStairs(line_t * line, byte * args, int direction, stairs_e stairsType) { int secnum; int height; int delay; int resetDelay; sector_t *sec; sector_t *qSec; int type; // Set global stairs variables TextureChange = 0; Direction = direction; StepDelta = Direction * (args[2] * FRACUNIT); Speed = args[1] * (FRACUNIT / 8); resetDelay = args[4]; delay = args[3]; if (stairsType == STAIRS_PHASED) { StartDelayDelta = args[3]; StartDelay = StartDelayDelta; resetDelay = StartDelayDelta; delay = 0; TextureChange = args[4]; } secnum = -1; validcount++; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; Texture = sec->floorpic; StartHeight = sec->floorheight; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; QueueStairSector(sec, 0, sec->floorheight); sec->special = 0; } while ((qSec = DequeueStairSector(&type, &height)) != NULL) { ProcessStairSector(qSec, type, height, stairsType, delay, resetDelay); } return (1); } //========================================================================= // // T_BuildPillar // //========================================================================= void T_BuildPillar(pillar_t * pillar) { result_e res1; result_e res2; // First, raise the floor res1 = T_MovePlane(pillar->sector, pillar->floorSpeed, pillar->floordest, pillar->crush, 0, pillar->direction); // floorOrCeiling, direction // Then, lower the ceiling res2 = T_MovePlane(pillar->sector, pillar->ceilingSpeed, pillar->ceilingdest, pillar->crush, 1, -pillar->direction); if (res1 == RES_PASTDEST && res2 == RES_PASTDEST) { pillar->sector->specialdata = NULL; SN_StopSequence((mobj_t *) & pillar->sector->soundorg); P_TagFinished(pillar->sector->tag); P_RemoveThinker(&pillar->thinker); } } //========================================================================= // // EV_BuildPillar // //========================================================================= int EV_BuildPillar(line_t * line, byte * args, boolean crush) { int secnum; sector_t *sec; pillar_t *pillar; int newHeight; int rtn; rtn = 0; secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // already moving if (sec->floorheight == sec->ceilingheight) { // pillar is already closed continue; } rtn = 1; if (!args[2]) { newHeight = sec->floorheight + ((sec->ceilingheight - sec->floorheight) / 2); } else { newHeight = sec->floorheight + (args[2] << FRACBITS); } pillar = Z_Malloc(sizeof(*pillar), PU_LEVSPEC, 0); sec->specialdata = pillar; P_AddThinker(&pillar->thinker); pillar->thinker.function = T_BuildPillar; pillar->sector = sec; if (!args[2]) { pillar->ceilingSpeed = pillar->floorSpeed = args[1] * (FRACUNIT / 8); } else if (newHeight - sec->floorheight > sec->ceilingheight - newHeight) { pillar->floorSpeed = args[1] * (FRACUNIT / 8); pillar->ceilingSpeed = FixedMul(sec->ceilingheight - newHeight, FixedDiv(pillar->floorSpeed, newHeight - sec->floorheight)); } else { pillar->ceilingSpeed = args[1] * (FRACUNIT / 8); pillar->floorSpeed = FixedMul(newHeight - sec->floorheight, FixedDiv(pillar->ceilingSpeed, sec->ceilingheight - newHeight)); } pillar->floordest = newHeight; pillar->ceilingdest = newHeight; pillar->direction = 1; pillar->crush = crush * args[3]; SN_StartSequence((mobj_t *) & pillar->sector->soundorg, SEQ_PLATFORM + pillar->sector->seqType); } return rtn; } //========================================================================= // // EV_OpenPillar // //========================================================================= int EV_OpenPillar(line_t * line, byte * args) { int secnum; sector_t *sec; pillar_t *pillar; int rtn; rtn = 0; secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // already moving if (sec->floorheight != sec->ceilingheight) { // pillar isn't closed continue; } rtn = 1; pillar = Z_Malloc(sizeof(*pillar), PU_LEVSPEC, 0); sec->specialdata = pillar; P_AddThinker(&pillar->thinker); pillar->thinker.function = T_BuildPillar; pillar->sector = sec; if (!args[2]) { pillar->floordest = P_FindLowestFloorSurrounding(sec); } else { pillar->floordest = sec->floorheight - (args[2] << FRACBITS); } if (!args[3]) { pillar->ceilingdest = P_FindHighestCeilingSurrounding(sec); } else { pillar->ceilingdest = sec->ceilingheight + (args[3] << FRACBITS); } if (sec->floorheight - pillar->floordest >= pillar->ceilingdest - sec->ceilingheight) { pillar->floorSpeed = args[1] * (FRACUNIT / 8); pillar->ceilingSpeed = FixedMul(sec->ceilingheight - pillar->ceilingdest, FixedDiv(pillar->floorSpeed, pillar->floordest - sec->floorheight)); } else { pillar->ceilingSpeed = args[1] * (FRACUNIT / 8); pillar->floorSpeed = FixedMul(pillar->floordest - sec->floorheight, FixedDiv(pillar->ceilingSpeed, sec->ceilingheight - pillar->ceilingdest)); } pillar->direction = -1; // open the pillar SN_StartSequence((mobj_t *) & pillar->sector->soundorg, SEQ_PLATFORM + pillar->sector->seqType); } return rtn; } //========================================================================= // // EV_FloorCrushStop // //========================================================================= int EV_FloorCrushStop(line_t * line, byte * args) { thinker_t *think; floormove_t *floor; boolean rtn; rtn = 0; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != T_MoveFloor) { continue; } floor = (floormove_t *) think; if (floor->type != FLEV_RAISEFLOORCRUSH) { continue; } // Completely remove the crushing floor SN_StopSequence((mobj_t *) & floor->sector->soundorg); floor->sector->specialdata = NULL; P_TagFinished(floor->sector->tag); P_RemoveThinker(&floor->thinker); rtn = 1; } return rtn; } //========================================================================== // // T_FloorWaggle // //========================================================================== #define WGLSTATE_EXPAND 1 #define WGLSTATE_STABLE 2 #define WGLSTATE_REDUCE 3 void T_FloorWaggle(floorWaggle_t * waggle) { switch (waggle->state) { case WGLSTATE_EXPAND: if ((waggle->scale += waggle->scaleDelta) >= waggle->targetScale) { waggle->scale = waggle->targetScale; waggle->state = WGLSTATE_STABLE; } break; case WGLSTATE_REDUCE: if ((waggle->scale -= waggle->scaleDelta) <= 0) { // Remove waggle->sector->floorheight = waggle->originalHeight; P_ChangeSector(waggle->sector, true); waggle->sector->specialdata = NULL; P_TagFinished(waggle->sector->tag); P_RemoveThinker(&waggle->thinker); return; } break; case WGLSTATE_STABLE: if (waggle->ticker != -1) { if (!--waggle->ticker) { waggle->state = WGLSTATE_REDUCE; } } break; } waggle->accumulator += waggle->accDelta; waggle->sector->floorheight = waggle->originalHeight + FixedMul(FloatBobOffsets[(waggle->accumulator >> FRACBITS) & 63], waggle->scale); P_ChangeSector(waggle->sector, true); } //========================================================================== // // EV_StartFloorWaggle // //========================================================================== boolean EV_StartFloorWaggle(int tag, int height, int speed, int offset, int timer) { int sectorIndex; sector_t *sector; floorWaggle_t *waggle; boolean retCode; retCode = false; sectorIndex = -1; while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { sector = §ors[sectorIndex]; if (sector->specialdata) { // Already busy with another thinker continue; } retCode = true; waggle = Z_Malloc(sizeof(*waggle), PU_LEVSPEC, 0); sector->specialdata = waggle; waggle->thinker.function = T_FloorWaggle; waggle->sector = sector; waggle->originalHeight = sector->floorheight; waggle->accumulator = offset * FRACUNIT; waggle->accDelta = speed << 10; waggle->scale = 0; waggle->targetScale = height << 10; waggle->scaleDelta = waggle->targetScale / (35 + ((3 * 35) * height) / 255); waggle->ticker = timer ? timer * 35 : -1; waggle->state = WGLSTATE_EXPAND; P_AddThinker(&waggle->thinker); } return retCode; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_inter.c000066400000000000000000002010771360717211000217450ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "m_misc.h" #include "m_random.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" #define BONUSADD 6 int ArmorIncrement[NUMCLASSES][NUMARMOR] = { {25 * FRACUNIT, 20 * FRACUNIT, 15 * FRACUNIT, 5 * FRACUNIT}, {10 * FRACUNIT, 25 * FRACUNIT, 5 * FRACUNIT, 20 * FRACUNIT}, {5 * FRACUNIT, 15 * FRACUNIT, 10 * FRACUNIT, 25 * FRACUNIT}, {0, 0, 0, 0} }; int AutoArmorSave[NUMCLASSES] = { 15 * FRACUNIT, 10 * FRACUNIT, 5 * FRACUNIT, 0 }; const char *TextKeyMessages[] = { TXT_KEY_STEEL, TXT_KEY_CAVE, TXT_KEY_AXE, TXT_KEY_FIRE, TXT_KEY_EMERALD, TXT_KEY_DUNGEON, TXT_KEY_SILVER, TXT_KEY_RUSTED, TXT_KEY_HORN, TXT_KEY_SWAMP, TXT_KEY_CASTLE }; static void SetDormantArtifact(mobj_t * arti); static void TryPickupArtifact(player_t * player, artitype_t artifactType, mobj_t * artifact); static void TryPickupWeapon(player_t * player, pclass_t weaponClass, weapontype_t weaponType, mobj_t * weapon, const char *message); static void TryPickupWeaponPiece(player_t * player, pclass_t matchClass, int pieceValue, mobj_t * pieceMobj); //-------------------------------------------------------------------------- // // PROC P_SetMessage // //-------------------------------------------------------------------------- void P_SetMessage(player_t * player, const char *message, boolean ultmsg) { if ((player->ultimateMessage || !messageson) && !ultmsg) { return; } M_StringCopy(player->message, message, sizeof(player->message)); // strupr(player->message); player->messageTics = MESSAGETICS; player->yellowMessage = false; if (ultmsg) { player->ultimateMessage = true; } if (player == &players[consoleplayer]) { BorderTopRefresh = true; } } //========================================================================== // // P_SetYellowMessage // //========================================================================== void P_SetYellowMessage(player_t * player, const char *message, boolean ultmsg) { if ((player->ultimateMessage || !messageson) && !ultmsg) { return; } M_StringCopy(player->message, message, sizeof(player->message)); player->messageTics = 5 * MESSAGETICS; // Bold messages last longer player->yellowMessage = true; if (ultmsg) { player->ultimateMessage = true; } if (player == &players[consoleplayer]) { BorderTopRefresh = true; } } //========================================================================== // // P_ClearMessage // //========================================================================== void P_ClearMessage(player_t * player) { player->messageTics = 0; if (player == &players[consoleplayer]) { BorderTopRefresh = true; } } //---------------------------------------------------------------------------- // // PROC P_HideSpecialThing // //---------------------------------------------------------------------------- void P_HideSpecialThing(mobj_t * thing) { thing->flags &= ~MF_SPECIAL; thing->flags2 |= MF2_DONTDRAW; P_SetMobjState(thing, S_HIDESPECIAL1); } //-------------------------------------------------------------------------- // // FUNC P_GiveMana // // Returns true if the player accepted the mana, false if it was // refused (player has MAX_MANA). // //-------------------------------------------------------------------------- boolean P_GiveMana(player_t * player, manatype_t mana, int count) { int prevMana; //weapontype_t changeWeapon; if (mana == MANA_NONE || mana == MANA_BOTH) { return (false); } if ((unsigned int) mana > NUMMANA) { I_Error("P_GiveMana: bad type %i", mana); } if (player->mana[mana] == MAX_MANA) { return (false); } if (gameskill == sk_baby || gameskill == sk_nightmare) { // extra mana in baby mode and nightmare mode count += count >> 1; } prevMana = player->mana[mana]; player->mana[mana] += count; if (player->mana[mana] > MAX_MANA) { player->mana[mana] = MAX_MANA; } if (player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND && mana == MANA_1 && prevMana <= 0) { P_SetPsprite(player, ps_weapon, S_FAXEREADY_G); } return (true); } //========================================================================== // // TryPickupWeapon // //========================================================================== static void TryPickupWeapon(player_t * player, pclass_t weaponClass, weapontype_t weaponType, mobj_t * weapon, const char *message) { boolean remove; boolean gaveMana; boolean gaveWeapon; remove = true; if (player->class != weaponClass) { // Wrong class, but try to pick up for mana if (netgame && !deathmatch) { // Can't pick up weapons for other classes in coop netplay return; } if (weaponType == WP_SECOND) { if (!P_GiveMana(player, MANA_1, 25)) { return; } } else { if (!P_GiveMana(player, MANA_2, 25)) { return; } } } else if (netgame && !deathmatch) { // Cooperative net-game if (player->weaponowned[weaponType]) { return; } player->weaponowned[weaponType] = true; if (weaponType == WP_SECOND) { P_GiveMana(player, MANA_1, 25); } else { P_GiveMana(player, MANA_2, 25); } player->pendingweapon = weaponType; remove = false; } else { // Deathmatch or single player game if (weaponType == WP_SECOND) { gaveMana = P_GiveMana(player, MANA_1, 25); } else { gaveMana = P_GiveMana(player, MANA_2, 25); } if (player->weaponowned[weaponType]) { gaveWeapon = false; } else { gaveWeapon = true; player->weaponowned[weaponType] = true; if (weaponType > player->readyweapon) { // Only switch to more powerful weapons player->pendingweapon = weaponType; } } if (!(gaveWeapon || gaveMana)) { // Player didn't need the weapon or any mana return; } } P_SetMessage(player, message, false); if (weapon->special) { P_ExecuteLineSpecial(weapon->special, weapon->args, NULL, 0, player->mo); weapon->special = 0; } if (remove) { if (deathmatch && !(weapon->flags2 & MF2_DROPPED)) { P_HideSpecialThing(weapon); } else { P_RemoveMobj(weapon); } } player->bonuscount += BONUSADD; if (player == &players[consoleplayer]) { S_StartSound(NULL, SFX_PICKUP_WEAPON); SB_PaletteFlash(false); } } //-------------------------------------------------------------------------- // // FUNC P_GiveWeapon // // Returns true if the weapon or its mana was accepted. // //-------------------------------------------------------------------------- /* boolean P_GiveWeapon(player_t *player, pclass_t class, weapontype_t weapon) { boolean gaveMana; boolean gaveWeapon; if(player->class != class) { // player cannot use this weapon, take it anyway, and get mana if(netgame && !deathmatch) { // Can't pick up weapons for other classes in coop netplay return false; } if(weapon == WP_SECOND) { return P_GiveMana(player, MANA_1, 25); } else { return P_GiveMana(player, MANA_2, 25); } } if(netgame && !deathmatch) { // Cooperative net-game if(player->weaponowned[weapon]) { return(false); } player->bonuscount += BONUSADD; player->weaponowned[weapon] = true; if(weapon == WP_SECOND) { P_GiveMana(player, MANA_1, 25); } else { P_GiveMana(player, MANA_2, 25); } player->pendingweapon = weapon; if(player == &players[consoleplayer]) { S_StartSound(NULL, SFX_PICKUP_WEAPON); } return(false); } if(weapon == WP_SECOND) { gaveMana = P_GiveMana(player, MANA_1, 25); } else { gaveMana = P_GiveMana(player, MANA_2, 25); } if(player->weaponowned[weapon]) { gaveWeapon = false; } else { gaveWeapon = true; player->weaponowned[weapon] = true; if(weapon > player->readyweapon) { // Only switch to more powerful weapons player->pendingweapon = weapon; } } return(gaveWeapon || gaveMana); } */ //=========================================================================== // // P_GiveWeaponPiece // //=========================================================================== /* boolean P_GiveWeaponPiece(player_t *player, pclass_t class, int piece) { P_GiveMana(player, MANA_1, 20); P_GiveMana(player, MANA_2, 20); if(player->class != class) { return true; } else if(player->pieces&piece) { // player already has that weapon piece return true; } player->pieces |= piece; if(player->pieces == 7) { // player has built the fourth weapon! P_GiveWeapon(player, class, WP_FOURTH); S_StartSound(player->mo, SFX_WEAPON_BUILD); } return true; } */ //========================================================================== // // TryPickupWeaponPiece // //========================================================================== static void TryPickupWeaponPiece(player_t * player, pclass_t matchClass, int pieceValue, mobj_t * pieceMobj) { boolean remove; boolean checkAssembled; boolean gaveWeapon; int gaveMana; static const char *fourthWeaponText[] = { TXT_WEAPON_F4, TXT_WEAPON_C4, TXT_WEAPON_M4 }; static const char *weaponPieceText[] = { TXT_QUIETUS_PIECE, TXT_WRAITHVERGE_PIECE, TXT_BLOODSCOURGE_PIECE }; static int pieceValueTrans[] = { 0, // 0: never WPIECE1 | WPIECE2 | WPIECE3, // WPIECE1 (1) WPIECE2 | WPIECE3, // WPIECE2 (2) 0, // 3: never WPIECE3 // WPIECE3 (4) }; remove = true; checkAssembled = true; gaveWeapon = false; if (player->class != matchClass) { // Wrong class, but try to pick up for mana if (netgame && !deathmatch) { // Can't pick up wrong-class weapons in coop netplay return; } checkAssembled = false; gaveMana = P_GiveMana(player, MANA_1, 20) + P_GiveMana(player, MANA_2, 20); if (!gaveMana) { // Didn't need the mana, so don't pick it up return; } } else if (netgame && !deathmatch) { // Cooperative net-game if (player->pieces & pieceValue) { // Already has the piece return; } pieceValue = pieceValueTrans[pieceValue]; P_GiveMana(player, MANA_1, 20); P_GiveMana(player, MANA_2, 20); remove = false; } else { // Deathmatch or single player game gaveMana = P_GiveMana(player, MANA_1, 20) + P_GiveMana(player, MANA_2, 20); if (player->pieces & pieceValue) { // Already has the piece, check if mana needed if (!gaveMana) { // Didn't need the mana, so don't pick it up return; } checkAssembled = false; } } // Pick up the weapon piece if (pieceMobj->special) { P_ExecuteLineSpecial(pieceMobj->special, pieceMobj->args, NULL, 0, player->mo); pieceMobj->special = 0; } if (remove) { if (deathmatch && !(pieceMobj->flags2 & MF2_DROPPED)) { P_HideSpecialThing(pieceMobj); } else { P_RemoveMobj(pieceMobj); } } player->bonuscount += BONUSADD; if (player == &players[consoleplayer]) { SB_PaletteFlash(false); } // Check if fourth weapon assembled if (checkAssembled) { player->pieces |= pieceValue; if (player->pieces == (WPIECE1 | WPIECE2 | WPIECE3)) { gaveWeapon = true; player->weaponowned[WP_FOURTH] = true; player->pendingweapon = WP_FOURTH; } } if (gaveWeapon) { P_SetMessage(player, fourthWeaponText[matchClass], false); // Play the build-sound full volume for all players S_StartSound(NULL, SFX_WEAPON_BUILD); } else { P_SetMessage(player, weaponPieceText[matchClass], false); if (player == &players[consoleplayer]) { S_StartSound(NULL, SFX_PICKUP_WEAPON); } } } //--------------------------------------------------------------------------- // // FUNC P_GiveBody // // Returns false if the body isn't needed at all. // //--------------------------------------------------------------------------- boolean P_GiveBody(player_t * player, int num) { int max; max = MAXHEALTH; if (player->morphTics) { max = MAXMORPHHEALTH; } if (player->health >= max) { return (false); } player->health += num; if (player->health > max) { player->health = max; } player->mo->health = player->health; return (true); } //--------------------------------------------------------------------------- // // FUNC P_GiveArmor // // Returns false if the armor is worse than the current armor. // //--------------------------------------------------------------------------- boolean P_GiveArmor(player_t * player, armortype_t armortype, int amount) { int hits; int totalArmor; extern int ArmorMax[NUMCLASSES]; if (amount == -1) { hits = ArmorIncrement[player->class][armortype]; if (player->armorpoints[armortype] >= hits) { return false; } else { player->armorpoints[armortype] = hits; } } else { hits = amount * 5 * FRACUNIT; totalArmor = player->armorpoints[ARMOR_ARMOR] + player->armorpoints[ARMOR_SHIELD] + player->armorpoints[ARMOR_HELMET] + player->armorpoints[ARMOR_AMULET] + AutoArmorSave[player->class]; if (totalArmor < ArmorMax[player->class] * 5 * FRACUNIT) { player->armorpoints[armortype] += hits; } else { return false; } } return true; } //--------------------------------------------------------------------------- // // PROC P_GiveKey // //--------------------------------------------------------------------------- int P_GiveKey(player_t * player, keytype_t key) { if (player->keys & (1 << key)) { return false; } player->bonuscount += BONUSADD; player->keys |= 1 << key; return true; } //--------------------------------------------------------------------------- // // FUNC P_GivePower // // Returns true if power accepted. // //--------------------------------------------------------------------------- boolean P_GivePower(player_t * player, powertype_t power) { if (power == pw_invulnerability) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = INVULNTICS; player->mo->flags2 |= MF2_INVULNERABLE; if (player->class == PCLASS_MAGE) { player->mo->flags2 |= MF2_REFLECTIVE; } return (true); } if (power == pw_flight) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = FLIGHTTICS; player->mo->flags2 |= MF2_FLY; player->mo->flags |= MF_NOGRAVITY; if (player->mo->z <= player->mo->floorz) { player->flyheight = 10; // thrust the player in the air a bit } return (true); } if (power == pw_infrared) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = INFRATICS; return (true); } if (power == pw_speed) { if (player->powers[power] > BLINKTHRESHOLD) { // Already have it return (false); } player->powers[power] = SPEEDTICS; return (true); } if (power == pw_minotaur) { // Doesn't matter if already have power, renew ticker player->powers[power] = MAULATORTICS; return (true); } /* if(power == pw_ironfeet) { player->powers[power] = IRONTICS; return(true); } if(power == pw_strength) { P_GiveBody(player, 100); player->powers[power] = 1; return(true); } */ if (player->powers[power]) { return (false); // already got it } player->powers[power] = 1; return (true); } //========================================================================== // // TryPickupArtifact // //========================================================================== static void TryPickupArtifact(player_t * player, artitype_t artifactType, mobj_t * artifact) { static const char *artifactMessages[NUMARTIFACTS] = { NULL, TXT_ARTIINVULNERABILITY, TXT_ARTIHEALTH, TXT_ARTISUPERHEALTH, TXT_ARTIHEALINGRADIUS, TXT_ARTISUMMON, TXT_ARTITORCH, TXT_ARTIEGG, TXT_ARTIFLY, TXT_ARTIBLASTRADIUS, TXT_ARTIPOISONBAG, TXT_ARTITELEPORTOTHER, TXT_ARTISPEED, TXT_ARTIBOOSTMANA, TXT_ARTIBOOSTARMOR, TXT_ARTITELEPORT, TXT_ARTIPUZZSKULL, TXT_ARTIPUZZGEMBIG, TXT_ARTIPUZZGEMRED, TXT_ARTIPUZZGEMGREEN1, TXT_ARTIPUZZGEMGREEN2, TXT_ARTIPUZZGEMBLUE1, TXT_ARTIPUZZGEMBLUE2, TXT_ARTIPUZZBOOK1, TXT_ARTIPUZZBOOK2, TXT_ARTIPUZZSKULL2, TXT_ARTIPUZZFWEAPON, TXT_ARTIPUZZCWEAPON, TXT_ARTIPUZZMWEAPON, TXT_ARTIPUZZGEAR, // All gear pickups use the same text TXT_ARTIPUZZGEAR, TXT_ARTIPUZZGEAR, TXT_ARTIPUZZGEAR }; if (gamemode == shareware) { artifactMessages[arti_blastradius] = TXT_ARTITELEPORT; artifactMessages[arti_teleport] = TXT_ARTIBLASTRADIUS; } if (P_GiveArtifact(player, artifactType, artifact)) { if (artifact->special) { P_ExecuteLineSpecial(artifact->special, artifact->args, NULL, 0, NULL); artifact->special = 0; } player->bonuscount += BONUSADD; if (artifactType < arti_firstpuzzitem) { SetDormantArtifact(artifact); S_StartSound(artifact, SFX_PICKUP_ARTIFACT); P_SetMessage(player, artifactMessages[artifactType], false); } else { // Puzzle item S_StartSound(NULL, SFX_PICKUP_ITEM); P_SetMessage(player, artifactMessages[artifactType], true); if (!netgame || deathmatch) { // Remove puzzle items if not cooperative netplay P_RemoveMobj(artifact); } } } } //--------------------------------------------------------------------------- // // FUNC P_GiveArtifact // // Returns true if artifact accepted. // //--------------------------------------------------------------------------- boolean P_GiveArtifact(player_t * player, artitype_t arti, mobj_t * mo) { int i; int j; boolean slidePointer; slidePointer = false; i = 0; while (player->inventory[i].type != arti && i < player->inventorySlotNum) { i++; } if (i == player->inventorySlotNum) { if (arti < arti_firstpuzzitem) { i = 0; while (player->inventory[i].type < arti_firstpuzzitem && i < player->inventorySlotNum) { i++; } if (i != player->inventorySlotNum) { for (j = player->inventorySlotNum; j > i; j--) { player->inventory[j].count = player->inventory[j - 1].count; player->inventory[j].type = player->inventory[j - 1].type; slidePointer = true; } } } player->inventory[i].count = 1; player->inventory[i].type = arti; player->inventorySlotNum++; } else { if (arti >= arti_firstpuzzitem && netgame && !deathmatch) { // Can't carry more than 1 puzzle item in coop netplay return false; } if (player->inventory[i].count >= 25) { // Player already has 25 of this item return false; } player->inventory[i].count++; } if (!player->artifactCount) { player->readyArtifact = arti; } else if (player == &players[consoleplayer] && slidePointer && i <= inv_ptr) { inv_ptr++; curpos++; if (curpos > 6) { curpos = 6; } } player->artifactCount++; return (true); } //========================================================================== // // SetDormantArtifact // // Removes the MF_SPECIAL flag and initiates the artifact pickup // animation. // //========================================================================== static void SetDormantArtifact(mobj_t * arti) { arti->flags &= ~MF_SPECIAL; if (deathmatch && !(arti->flags2 & MF2_DROPPED)) { if (arti->type == MT_ARTIINVULNERABILITY) { P_SetMobjState(arti, S_DORMANTARTI3_1); } else if (arti->type == MT_SUMMONMAULATOR || arti->type == MT_ARTIFLY) { P_SetMobjState(arti, S_DORMANTARTI2_1); } else { P_SetMobjState(arti, S_DORMANTARTI1_1); } } else { // Don't respawn P_SetMobjState(arti, S_DEADARTI1); } } //--------------------------------------------------------------------------- // // PROC A_RestoreArtifact // //--------------------------------------------------------------------------- void A_RestoreArtifact(mobj_t * arti) { arti->flags |= MF_SPECIAL; P_SetMobjState(arti, arti->info->spawnstate); S_StartSound(arti, SFX_RESPAWN); } //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing1 // // Make a special thing visible again. // //--------------------------------------------------------------------------- void A_RestoreSpecialThing1(mobj_t * thing) { thing->flags2 &= ~MF2_DONTDRAW; S_StartSound(thing, SFX_RESPAWN); } //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing2 // //--------------------------------------------------------------------------- void A_RestoreSpecialThing2(mobj_t * thing) { thing->flags |= MF_SPECIAL; P_SetMobjState(thing, thing->info->spawnstate); } //--------------------------------------------------------------------------- // // PROC P_TouchSpecialThing // //--------------------------------------------------------------------------- void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher) { player_t *player; fixed_t delta; int sound; boolean respawn; delta = special->z - toucher->z; if (delta > toucher->height || delta < -32 * FRACUNIT) { // Out of reach return; } if (toucher->health <= 0) { // Toucher is dead return; } sound = SFX_PICKUP_ITEM; player = toucher->player; respawn = true; switch (special->sprite) { // Items case SPR_PTN1: // Item_HealingPotion if (!P_GiveBody(player, 10)) { return; } P_SetMessage(player, TXT_ITEMHEALTH, false); break; case SPR_ARM1: if (!P_GiveArmor(player, ARMOR_ARMOR, -1)) { return; } P_SetMessage(player, TXT_ARMOR1, false); break; case SPR_ARM2: if (!P_GiveArmor(player, ARMOR_SHIELD, -1)) { return; } P_SetMessage(player, TXT_ARMOR2, false); break; case SPR_ARM3: if (!P_GiveArmor(player, ARMOR_HELMET, -1)) { return; } P_SetMessage(player, TXT_ARMOR3, false); break; case SPR_ARM4: if (!P_GiveArmor(player, ARMOR_AMULET, -1)) { return; } P_SetMessage(player, TXT_ARMOR4, false); break; // Keys case SPR_KEY1: case SPR_KEY2: case SPR_KEY3: case SPR_KEY4: case SPR_KEY5: case SPR_KEY6: case SPR_KEY7: case SPR_KEY8: case SPR_KEY9: case SPR_KEYA: case SPR_KEYB: if (!P_GiveKey(player, special->sprite - SPR_KEY1)) { return; } P_SetMessage(player, TextKeyMessages[special->sprite - SPR_KEY1], true); sound = SFX_PICKUP_KEY; // Check and process the special now in case the key doesn't // get removed for coop netplay if (special->special) { P_ExecuteLineSpecial(special->special, special->args, NULL, 0, toucher); special->special = 0; } if (!netgame) { // Only remove keys in single player game break; } player->bonuscount += BONUSADD; if (player == &players[consoleplayer]) { S_StartSound(NULL, sound); SB_PaletteFlash(false); } return; // Artifacts case SPR_PTN2: TryPickupArtifact(player, arti_health, special); return; case SPR_SOAR: TryPickupArtifact(player, arti_fly, special); return; case SPR_INVU: TryPickupArtifact(player, arti_invulnerability, special); return; case SPR_SUMN: TryPickupArtifact(player, arti_summon, special); return; case SPR_PORK: TryPickupArtifact(player, arti_egg, special); return; case SPR_SPHL: TryPickupArtifact(player, arti_superhealth, special); return; case SPR_HRAD: TryPickupArtifact(player, arti_healingradius, special); return; case SPR_TRCH: TryPickupArtifact(player, arti_torch, special); return; case SPR_ATLP: TryPickupArtifact(player, arti_teleport, special); return; case SPR_TELO: TryPickupArtifact(player, arti_teleportother, special); return; case SPR_PSBG: TryPickupArtifact(player, arti_poisonbag, special); return; case SPR_SPED: TryPickupArtifact(player, arti_speed, special); return; case SPR_BMAN: TryPickupArtifact(player, arti_boostmana, special); return; case SPR_BRAC: TryPickupArtifact(player, arti_boostarmor, special); return; case SPR_BLST: TryPickupArtifact(player, arti_blastradius, special); return; // Puzzle artifacts case SPR_ASKU: TryPickupArtifact(player, arti_puzzskull, special); return; case SPR_ABGM: TryPickupArtifact(player, arti_puzzgembig, special); return; case SPR_AGMR: TryPickupArtifact(player, arti_puzzgemred, special); return; case SPR_AGMG: TryPickupArtifact(player, arti_puzzgemgreen1, special); return; case SPR_AGG2: TryPickupArtifact(player, arti_puzzgemgreen2, special); return; case SPR_AGMB: TryPickupArtifact(player, arti_puzzgemblue1, special); return; case SPR_AGB2: TryPickupArtifact(player, arti_puzzgemblue2, special); return; case SPR_ABK1: TryPickupArtifact(player, arti_puzzbook1, special); return; case SPR_ABK2: TryPickupArtifact(player, arti_puzzbook2, special); return; case SPR_ASK2: TryPickupArtifact(player, arti_puzzskull2, special); return; case SPR_AFWP: TryPickupArtifact(player, arti_puzzfweapon, special); return; case SPR_ACWP: TryPickupArtifact(player, arti_puzzcweapon, special); return; case SPR_AMWP: TryPickupArtifact(player, arti_puzzmweapon, special); return; case SPR_AGER: TryPickupArtifact(player, arti_puzzgear1, special); return; case SPR_AGR2: TryPickupArtifact(player, arti_puzzgear2, special); return; case SPR_AGR3: TryPickupArtifact(player, arti_puzzgear3, special); return; case SPR_AGR4: TryPickupArtifact(player, arti_puzzgear4, special); return; // Mana case SPR_MAN1: if (!P_GiveMana(player, MANA_1, 15)) { return; } P_SetMessage(player, TXT_MANA_1, false); break; case SPR_MAN2: if (!P_GiveMana(player, MANA_2, 15)) { return; } P_SetMessage(player, TXT_MANA_2, false); break; case SPR_MAN3: // Double Mana Dodecahedron if (!P_GiveMana(player, MANA_1, 20)) { if (!P_GiveMana(player, MANA_2, 20)) { return; } } else { P_GiveMana(player, MANA_2, 20); } P_SetMessage(player, TXT_MANA_BOTH, false); break; // 2nd and 3rd Mage Weapons case SPR_WMCS: // Frost Shards TryPickupWeapon(player, PCLASS_MAGE, WP_SECOND, special, TXT_WEAPON_M2); return; case SPR_WMLG: // Arc of Death TryPickupWeapon(player, PCLASS_MAGE, WP_THIRD, special, TXT_WEAPON_M3); return; // 2nd and 3rd Fighter Weapons case SPR_WFAX: // Timon's Axe TryPickupWeapon(player, PCLASS_FIGHTER, WP_SECOND, special, TXT_WEAPON_F2); return; case SPR_WFHM: // Hammer of Retribution TryPickupWeapon(player, PCLASS_FIGHTER, WP_THIRD, special, TXT_WEAPON_F3); return; // 2nd and 3rd Cleric Weapons case SPR_WCSS: // Serpent Staff TryPickupWeapon(player, PCLASS_CLERIC, WP_SECOND, special, TXT_WEAPON_C2); return; case SPR_WCFM: // Firestorm TryPickupWeapon(player, PCLASS_CLERIC, WP_THIRD, special, TXT_WEAPON_C3); return; // Fourth Weapon Pieces case SPR_WFR1: TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE1, special); return; case SPR_WFR2: TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE2, special); return; case SPR_WFR3: TryPickupWeaponPiece(player, PCLASS_FIGHTER, WPIECE3, special); return; case SPR_WCH1: TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE1, special); return; case SPR_WCH2: TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE2, special); return; case SPR_WCH3: TryPickupWeaponPiece(player, PCLASS_CLERIC, WPIECE3, special); return; case SPR_WMS1: TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE1, special); return; case SPR_WMS2: TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE2, special); return; case SPR_WMS3: TryPickupWeaponPiece(player, PCLASS_MAGE, WPIECE3, special); return; default: I_Error("P_SpecialThing: Unknown gettable thing"); } if (special->special) { P_ExecuteLineSpecial(special->special, special->args, NULL, 0, toucher); special->special = 0; } if (deathmatch && respawn && !(special->flags2 & MF2_DROPPED)) { P_HideSpecialThing(special); } else { P_RemoveMobj(special); } player->bonuscount += BONUSADD; if (player == &players[consoleplayer]) { S_StartSound(NULL, sound); SB_PaletteFlash(false); } } // Search thinker list for minotaur mobj_t *ActiveMinotaur(player_t * master) { mobj_t *mo; player_t *plr; thinker_t *think; unsigned int *starttime; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) continue; mo = (mobj_t *) think; if (mo->type != MT_MINOTAUR) continue; if (mo->health <= 0) continue; if (!(mo->flags & MF_COUNTKILL)) continue; // for morphed minotaurs if (mo->flags & MF_CORPSE) continue; starttime = (unsigned int *) mo->args; if ((leveltime - *starttime) >= MAULATORTICS) continue; plr = mo->special1.m->player; if (plr == master) return (mo); } return (NULL); } //--------------------------------------------------------------------------- // // PROC P_KillMobj // //--------------------------------------------------------------------------- void P_KillMobj(mobj_t * source, mobj_t * target) { byte dummyArgs[3] = {0, 0, 0}; mobj_t *master; target->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_NOGRAVITY); target->flags |= MF_CORPSE | MF_DROPOFF; target->flags2 &= ~MF2_PASSMOBJ; target->height >>= 2; if ((target->flags & MF_COUNTKILL || target->type == MT_ZBELL) && target->special) { // Initiate monster death actions if (target->type == MT_SORCBOSS) { P_StartACS(target->special, 0, dummyArgs, target, NULL, 0); } else { P_ExecuteLineSpecial(target->special, target->args, NULL, 0, target); } } if (source && source->player) { // Check for frag changes if (target->player) { if (target == source) { // Self-frag target->player->frags[target->player - players]--; if (cmdfrag && netgame && source->player == &players[consoleplayer]) { // Send out a frag count packet NET_SendFrags(source->player); } } else { source->player->frags[target->player - players]++; if (cmdfrag && netgame && source->player == &players[consoleplayer]) { // Send out a frag count packet NET_SendFrags(source->player); } } } } if (target->player) { // Player death if (!source) { // Self-frag target->player->frags[target->player - players]--; if (cmdfrag && netgame && target->player == &players[consoleplayer]) { // Send out a frag count packet NET_SendFrags(target->player); } } target->flags &= ~MF_SOLID; target->flags2 &= ~MF2_FLY; target->player->powers[pw_flight] = 0; target->player->playerstate = PST_DEAD; P_DropWeapon(target->player); if (target->flags2 & MF2_FIREDAMAGE) { // Player flame death switch (target->player->class) { case PCLASS_FIGHTER: S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH); P_SetMobjState(target, S_PLAY_F_FDTH1); return; case PCLASS_CLERIC: S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH); P_SetMobjState(target, S_PLAY_C_FDTH1); return; case PCLASS_MAGE: S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH); P_SetMobjState(target, S_PLAY_M_FDTH1); return; default: break; } } if (target->flags2 & MF2_ICEDAMAGE) { // Player ice death target->flags &= ~(7 << MF_TRANSSHIFT); //no translation target->flags |= MF_ICECORPSE; switch (target->player->class) { case PCLASS_FIGHTER: P_SetMobjState(target, S_FPLAY_ICE); return; case PCLASS_CLERIC: P_SetMobjState(target, S_CPLAY_ICE); return; case PCLASS_MAGE: P_SetMobjState(target, S_MPLAY_ICE); return; case PCLASS_PIG: P_SetMobjState(target, S_PIG_ICE); return; default: break; } } } if (target->flags2 & MF2_FIREDAMAGE) { if (target->type == MT_FIGHTER_BOSS || target->type == MT_CLERIC_BOSS || target->type == MT_MAGE_BOSS) { switch (target->type) { case MT_FIGHTER_BOSS: S_StartSound(target, SFX_PLAYER_FIGHTER_BURN_DEATH); P_SetMobjState(target, S_PLAY_F_FDTH1); return; case MT_CLERIC_BOSS: S_StartSound(target, SFX_PLAYER_CLERIC_BURN_DEATH); P_SetMobjState(target, S_PLAY_C_FDTH1); return; case MT_MAGE_BOSS: S_StartSound(target, SFX_PLAYER_MAGE_BURN_DEATH); P_SetMobjState(target, S_PLAY_M_FDTH1); return; default: break; } } else if (target->type == MT_TREEDESTRUCTIBLE) { P_SetMobjState(target, S_ZTREEDES_X1); target->height = 24 * FRACUNIT; S_StartSound(target, SFX_TREE_EXPLODE); return; } } if (target->flags2 & MF2_ICEDAMAGE) { target->flags |= MF_ICECORPSE; switch (target->type) { case MT_BISHOP: P_SetMobjState(target, S_BISHOP_ICE); return; case MT_CENTAUR: case MT_CENTAURLEADER: P_SetMobjState(target, S_CENTAUR_ICE); return; case MT_DEMON: case MT_DEMON2: P_SetMobjState(target, S_DEMON_ICE); return; case MT_SERPENT: case MT_SERPENTLEADER: P_SetMobjState(target, S_SERPENT_ICE); return; case MT_WRAITH: case MT_WRAITHB: P_SetMobjState(target, S_WRAITH_ICE); return; case MT_ETTIN: P_SetMobjState(target, S_ETTIN_ICE1); return; case MT_FIREDEMON: P_SetMobjState(target, S_FIRED_ICE1); return; case MT_FIGHTER_BOSS: P_SetMobjState(target, S_FIGHTER_ICE); return; case MT_CLERIC_BOSS: P_SetMobjState(target, S_CLERIC_ICE); return; case MT_MAGE_BOSS: P_SetMobjState(target, S_MAGE_ICE); return; case MT_PIG: P_SetMobjState(target, S_PIG_ICE); return; default: target->flags &= ~MF_ICECORPSE; break; } } if (target->type == MT_MINOTAUR) { master = target->special1.m; if (master->health > 0) { if (!ActiveMinotaur(master->player)) { master->player->powers[pw_minotaur] = 0; } } } else if (target->type == MT_TREEDESTRUCTIBLE) { target->height = 24 * FRACUNIT; } if (target->health < -(target->info->spawnhealth >> 1) && target->info->xdeathstate) { // Extreme death P_SetMobjState(target, target->info->xdeathstate); } else { // Normal death if ((target->type == MT_FIREDEMON) && (target->z <= target->floorz + 2 * FRACUNIT) && (target->info->xdeathstate)) { // This is to fix the imps' staying in fall state P_SetMobjState(target, target->info->xdeathstate); } else { P_SetMobjState(target, target->info->deathstate); } } target->tics -= P_Random() & 3; // I_StartSound(&actor->r, actor->info->deathsound); } //--------------------------------------------------------------------------- // // FUNC P_MinotaurSlam // //--------------------------------------------------------------------------- void P_MinotaurSlam(mobj_t * source, mobj_t * target) { angle_t angle; fixed_t thrust; angle = R_PointToAngle2(source->x, source->y, target->x, target->y); angle >>= ANGLETOFINESHIFT; thrust = 16 * FRACUNIT + (P_Random() << 10); target->momx += FixedMul(thrust, finecosine[angle]); target->momy += FixedMul(thrust, finesine[angle]); P_DamageMobj(target, NULL, source, HITDICE(4)); if (target->player) { target->reactiontime = 14 + (P_Random() & 7); } source->args[0] = 0; // Stop charging } //--------------------------------------------------------------------------- // // FUNC P_MorphPlayer // // Returns true if the player gets turned into a pig // //--------------------------------------------------------------------------- boolean P_MorphPlayer(player_t * player) { mobj_t *pmo; mobj_t *fog; mobj_t *beastMo; fixed_t x; fixed_t y; fixed_t z; angle_t angle; int oldFlags2; if (player->powers[pw_invulnerability]) { // Immune when invulnerable return (false); } if (player->morphTics) { // Player is already a beast return false; } pmo = player->mo; x = pmo->x; y = pmo->y; z = pmo->z; angle = pmo->angle; oldFlags2 = pmo->flags2; P_SetMobjState(pmo, S_FREETARGMOBJ); fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, SFX_TELEPORT); beastMo = P_SpawnMobj(x, y, z, MT_PIGPLAYER); beastMo->special1.i = player->readyweapon; beastMo->angle = angle; beastMo->player = player; player->health = beastMo->health = MAXMORPHHEALTH; player->mo = beastMo; memset(&player->armorpoints[0], 0, NUMARMOR * sizeof(int)); player->class = PCLASS_PIG; if (oldFlags2 & MF2_FLY) { beastMo->flags2 |= MF2_FLY; } player->morphTics = MORPHTICS; P_ActivateMorphWeapon(player); return (true); } //--------------------------------------------------------------------------- // // FUNC P_MorphMonster // //--------------------------------------------------------------------------- boolean P_MorphMonster(mobj_t * actor) { mobj_t *master, *monster, *fog; mobjtype_t moType; fixed_t x; fixed_t y; fixed_t z; mobj_t oldMonster; if (actor->player) return (false); if (!(actor->flags & MF_COUNTKILL)) return false; if (actor->flags2 & MF2_BOSS) return false; moType = actor->type; switch (moType) { case MT_PIG: return (false); case MT_FIGHTER_BOSS: case MT_CLERIC_BOSS: case MT_MAGE_BOSS: return (false); default: break; } oldMonster = *actor; x = oldMonster.x; y = oldMonster.y; z = oldMonster.z; P_RemoveMobjFromTIDList(actor); P_SetMobjState(actor, S_FREETARGMOBJ); fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, SFX_TELEPORT); monster = P_SpawnMobj(x, y, z, MT_PIG); monster->special2.i = moType; monster->special1.i = MORPHTICS + P_Random(); monster->flags |= (oldMonster.flags & MF_SHADOW); monster->target = oldMonster.target; monster->angle = oldMonster.angle; monster->tid = oldMonster.tid; monster->special = oldMonster.special; P_InsertMobjIntoTIDList(monster, oldMonster.tid); memcpy(monster->args, oldMonster.args, 5); // check for turning off minotaur power for active icon if (moType == MT_MINOTAUR) { master = oldMonster.special1.m; if (master->health > 0) { if (!ActiveMinotaur(master->player)) { master->player->powers[pw_minotaur] = 0; } } } return (true); } //--------------------------------------------------------------------------- // // PROC P_AutoUseHealth // //--------------------------------------------------------------------------- void P_AutoUseHealth(player_t * player, int saveHealth) { int i; int count; int normalCount; int normalSlot = 0; int superCount; int superSlot = 0; normalCount = superCount = 0; for (i = 0; i < player->inventorySlotNum; i++) { if (player->inventory[i].type == arti_health) { normalSlot = i; normalCount = player->inventory[i].count; } else if (player->inventory[i].type == arti_superhealth) { superSlot = i; superCount = player->inventory[i].count; } } if ((gameskill == sk_baby) && (normalCount * 25 >= saveHealth)) { // Use quartz flasks count = (saveHealth + 24) / 25; for (i = 0; i < count; i++) { player->health += 25; P_PlayerRemoveArtifact(player, normalSlot); } } else if (superCount * 100 >= saveHealth) { // Use mystic urns count = (saveHealth + 99) / 100; for (i = 0; i < count; i++) { player->health += 100; P_PlayerRemoveArtifact(player, superSlot); } } else if ((gameskill == sk_baby) && (superCount * 100 + normalCount * 25 >= saveHealth)) { // Use mystic urns and quartz flasks count = (saveHealth + 24) / 25; saveHealth -= count * 25; for (i = 0; i < count; i++) { player->health += 25; P_PlayerRemoveArtifact(player, normalSlot); } count = (saveHealth + 99) / 100; for (i = 0; i < count; i++) { player->health += 100; P_PlayerRemoveArtifact(player, normalSlot); } } player->mo->health = player->health; } /* ================= = = 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 barrel explosions and other environmental stuff ================== */ void P_DamageMobj (mobj_t * target, mobj_t * inflictor, mobj_t * source, int damage) { unsigned ang; int saved; fixed_t savedPercent; player_t *player; mobj_t *master; fixed_t thrust; int temp; int i; if (!(target->flags & MF_SHOOTABLE)) { // Shouldn't happen return; } if (target->health <= 0) { if (inflictor && inflictor->flags2 & MF2_ICEDAMAGE) { return; } else if (target->flags & MF_ICECORPSE) // frozen { target->tics = 1; target->momx = target->momy = 0; } return; } if ((target->flags2 & MF2_INVULNERABLE) && damage < 10000) { // mobj is invulnerable if (target->player) return; // for player, no exceptions if (inflictor) { switch (inflictor->type) { // These inflictors aren't foiled by invulnerability case MT_HOLY_FX: case MT_POISONCLOUD: case MT_FIREBOMB: break; default: return; } } else { return; } } if (target->player) { if (damage < 1000 && ((target->player->cheats & CF_GODMODE) || target->player->powers[pw_invulnerability])) { return; } } if (target->flags & MF_SKULLFLY) { target->momx = target->momy = target->momz = 0; } if (target->flags2 & MF2_DORMANT) { // Invulnerable, and won't wake up return; } player = target->player; if (player && gameskill == sk_baby) { // Take half damage in trainer mode damage >>= 1; } // Special damage types if (inflictor) { switch (inflictor->type) { case MT_EGGFX: if (player) { P_MorphPlayer(player); } else { P_MorphMonster(target); } return; // Always return case MT_TELOTHER_FX1: case MT_TELOTHER_FX2: case MT_TELOTHER_FX3: case MT_TELOTHER_FX4: case MT_TELOTHER_FX5: if ((target->flags & MF_COUNTKILL) && (target->type != MT_SERPENT) && (target->type != MT_SERPENTLEADER) && (!(target->flags2 & MF2_BOSS))) { P_TeleportOther(target); } return; case MT_MINOTAUR: if (inflictor->flags & MF_SKULLFLY) { // Slam only when in charge mode P_MinotaurSlam(inflictor, target); return; } break; case MT_BISH_FX: // Bishops are just too nasty damage >>= 1; break; case MT_SHARDFX1: switch (inflictor->special2.i) { case 3: damage <<= 3; break; case 2: damage <<= 2; break; case 1: damage <<= 1; break; default: break; } break; case MT_CSTAFF_MISSILE: // Cleric Serpent Staff does poison damage if (target->player) { P_PoisonPlayer(target->player, source, 20); damage >>= 1; } break; case MT_ICEGUY_FX2: damage >>= 1; break; case MT_POISONDART: if (target->player) { P_PoisonPlayer(target->player, source, 20); damage >>= 1; } break; case MT_POISONCLOUD: if (target->player) { if (target->player->poisoncount < 4) { P_PoisonDamage(target->player, source, 15 + (P_Random() & 15), false); // Don't play painsound P_PoisonPlayer(target->player, source, 50); S_StartSound(target, SFX_PLAYER_POISONCOUGH); } return; } else if (!(target->flags & MF_COUNTKILL)) { // only damage monsters/players with the poison cloud return; } break; case MT_FSWORD_MISSILE: if (target->player) { damage -= damage >> 2; } break; default: break; } } // Push the target unless source is using the gauntlets if (inflictor && (!source || !source->player) && !(inflictor->flags2 & MF2_NODMGTHRUST)) { ang = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y); //thrust = damage*(FRACUNIT>>3)*100/target->info->mass; thrust = damage * (FRACUNIT >> 3) * 150 / target->info->mass; // make fall forwards sometimes if ((damage < 40) && (damage > target->health) && (target->z - inflictor->z > 64 * FRACUNIT) && (P_Random() & 1)) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; target->momx += FixedMul(thrust, finecosine[ang]); target->momy += FixedMul(thrust, finesine[ang]); } // // player specific // if (player) { savedPercent = AutoArmorSave[player->class] + player->armorpoints[ARMOR_ARMOR] + player->armorpoints[ARMOR_SHIELD] + player->armorpoints[ARMOR_HELMET] + player->armorpoints[ARMOR_AMULET]; if (savedPercent) { // armor absorbed some damage if (savedPercent > 100 * FRACUNIT) { savedPercent = 100 * FRACUNIT; } for (i = 0; i < NUMARMOR; i++) { if (player->armorpoints[i]) { player->armorpoints[i] -= FixedDiv(FixedMul(damage << FRACBITS, ArmorIncrement[player->class][i]), 300 * FRACUNIT); if (player->armorpoints[i] < 2 * FRACUNIT) { player->armorpoints[i] = 0; } } } saved = FixedDiv(FixedMul(damage << FRACBITS, savedPercent), 100 * FRACUNIT); if (saved > savedPercent * 2) { saved = savedPercent * 2; } damage -= saved >> FRACBITS; } if (damage >= player->health && ((gameskill == sk_baby) || deathmatch) && !player->morphTics) { // Try to use some inventory health P_AutoUseHealth(player, damage - player->health + 1); } player->health -= damage; // mirror mobj health here for Dave if (player->health < 0) { player->health = 0; } player->attacker = source; player->damagecount += damage; // add damage after armor / invuln if (player->damagecount > 100) { player->damagecount = 100; // teleport stomp does 10k points... } temp = damage < 100 ? damage : 100; if (player == &players[consoleplayer]) { I_Tactile(40, 10, 40 + temp * 2); SB_PaletteFlash(false); } } // // do the damage // target->health -= damage; if (target->health <= 0) { // Death if (inflictor) { // check for special fire damage or ice damage deaths if (inflictor->flags2 & MF2_FIREDAMAGE) { if (player && !player->morphTics) { // Check for flame death if (target->health > -50 && damage > 25) { target->flags2 |= MF2_FIREDAMAGE; } } else { target->flags2 |= MF2_FIREDAMAGE; } } else if (inflictor->flags2 & MF2_ICEDAMAGE) { target->flags2 |= MF2_ICEDAMAGE; } } if (source && (source->type == MT_MINOTAUR)) { // Minotaur's kills go to his master master = source->special1.m; // Make sure still alive and not a pointer to fighter head if (master->player && (master->player->mo == master)) { source = master; } } if (source && (source->player) && (source->player->readyweapon == WP_FOURTH)) { // Always extreme death from fourth weapon target->health = -5000; } P_KillMobj(source, target); return; } if ((P_Random() < target->info->painchance) && !(target->flags & MF_SKULLFLY)) { if (inflictor && (inflictor->type >= MT_LIGHTNING_FLOOR && inflictor->type <= MT_LIGHTNING_ZAP)) { if (P_Random() < 96) { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState(target, target->info->painstate); } else { // "electrocute" the target target->frame |= FF_FULLBRIGHT; if (target->flags & MF_COUNTKILL && P_Random() < 128 && !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT)) { if ((target->type == MT_CENTAUR) || (target->type == MT_CENTAURLEADER) || (target->type == MT_ETTIN)) { S_StartSound(target, SFX_PUPPYBEAT); } } } } else { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState(target, target->info->painstate); if (inflictor && inflictor->type == MT_POISONCLOUD) { if (target->flags & MF_COUNTKILL && P_Random() < 128 && !S_GetSoundPlayingInfo(target, SFX_PUPPYBEAT)) { if ((target->type == MT_CENTAUR) || (target->type == MT_CENTAURLEADER) || (target->type == MT_ETTIN)) { S_StartSound(target, SFX_PUPPYBEAT); } } } } } target->reactiontime = 0; // we're awake now... if (!target->threshold && source && !(source->flags2 & MF2_BOSS) && !(target->type == MT_BISHOP) && !(target->type == MT_MINOTAUR)) { // Target actor is not intent on another actor, // so make him chase after source if ((target->type == MT_CENTAUR && source->type == MT_CENTAURLEADER) || (target->type == MT_CENTAURLEADER && source->type == MT_CENTAUR)) { return; } target->target = source; target->threshold = BASETHRESHOLD; if (target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL) { P_SetMobjState(target, target->info->seestate); } } } //========================================================================== // // P_FallingDamage // //========================================================================== void P_FallingDamage(player_t * player) { int damage; int mom; int dist; mom = abs(player->mo->momz); dist = FixedMul(mom, 16 * FRACUNIT / 23); if (mom >= 63 * FRACUNIT) { // automatic death P_DamageMobj(player->mo, NULL, NULL, 10000); return; } damage = ((FixedMul(dist, dist) / 10) >> FRACBITS) - 24; if (player->mo->momz > -39 * FRACUNIT && damage > player->mo->health && player->mo->health != 1) { // No-death threshold damage = player->mo->health - 1; } S_StartSound(player->mo, SFX_PLAYER_LAND); P_DamageMobj(player->mo, NULL, NULL, damage); } //========================================================================== // // P_PoisonPlayer - Sets up all data concerning poisoning // //========================================================================== void P_PoisonPlayer(player_t * player, mobj_t * poisoner, int poison) { if ((player->cheats & CF_GODMODE) || player->powers[pw_invulnerability]) { return; } player->poisoncount += poison; player->poisoner = poisoner; if (player->poisoncount > 100) { player->poisoncount = 100; } } //========================================================================== // // P_PoisonDamage - Similar to P_DamageMobj // //========================================================================== void P_PoisonDamage(player_t * player, mobj_t * source, int damage, boolean playPainSound) { mobj_t *target; mobj_t *inflictor; target = player->mo; inflictor = source; if (target->health <= 0) { return; } if (target->flags2 & MF2_INVULNERABLE && damage < 10000) { // mobj is invulnerable return; } if (gameskill == sk_baby) { // Take half damage in trainer mode damage >>= 1; } if (damage < 1000 && ((player->cheats & CF_GODMODE) || player->powers[pw_invulnerability])) { return; } if (damage >= player->health && ((gameskill == sk_baby) || deathmatch) && !player->morphTics) { // Try to use some inventory health P_AutoUseHealth(player, damage - player->health + 1); } player->health -= damage; // mirror mobj health here for Dave if (player->health < 0) { player->health = 0; } player->attacker = source; // // do the damage // target->health -= damage; if (target->health <= 0) { // Death target->special1.i = damage; if (inflictor && !player->morphTics) { // Check for flame death if ((inflictor->flags2 & MF2_FIREDAMAGE) && (target->health > -50) && (damage > 25)) { target->flags2 |= MF2_FIREDAMAGE; } if (inflictor->flags2 & MF2_ICEDAMAGE) { target->flags2 |= MF2_ICEDAMAGE; } } P_KillMobj(source, target); return; } if (!(leveltime & 63) && playPainSound) { P_SetMobjState(target, target->info->painstate); } /* if((P_Random() < target->info->painchance) && !(target->flags&MF_SKULLFLY)) { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState(target, target->info->painstate); } */ } crispy-doom-crispy-doom-5.6.4/src/hexen/p_lights.c000066400000000000000000000250041360717211000221100ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "m_random.h" #include "p_local.h" //============================================================================ // // T_Light // //============================================================================ void T_Light(light_t * light) { if (light->count) { light->count--; return; } switch (light->type) { case LITE_FADE: light->sector->lightlevel = ((light->sector->lightlevel << FRACBITS) + light->value2) >> FRACBITS; if (light->tics2 == 1) { if (light->sector->lightlevel >= light->value1) { light->sector->lightlevel = light->value1; P_RemoveThinker(&light->thinker); } } else if (light->sector->lightlevel <= light->value1) { light->sector->lightlevel = light->value1; P_RemoveThinker(&light->thinker); } break; case LITE_GLOW: light->sector->lightlevel = ((light->sector->lightlevel << FRACBITS) + light->tics1) >> FRACBITS; if (light->tics2 == 1) { if (light->sector->lightlevel >= light->value1) { light->sector->lightlevel = light->value1; light->tics1 = -light->tics1; light->tics2 = -1; // reverse direction } } else if (light->sector->lightlevel <= light->value2) { light->sector->lightlevel = light->value2; light->tics1 = -light->tics1; light->tics2 = 1; // reverse direction } break; case LITE_FLICKER: if (light->sector->lightlevel == light->value1) { light->sector->lightlevel = light->value2; light->count = (P_Random() & 7) + 1; } else { light->sector->lightlevel = light->value1; light->count = (P_Random() & 31) + 1; } break; case LITE_STROBE: if (light->sector->lightlevel == light->value1) { light->sector->lightlevel = light->value2; light->count = light->tics2; } else { light->sector->lightlevel = light->value1; light->count = light->tics1; } break; default: break; } } //============================================================================ // // EV_SpawnLight // //============================================================================ boolean EV_SpawnLight(line_t * line, byte * arg, lighttype_t type) { light_t *light; sector_t *sec; int secNum; int arg1, arg2, arg3, arg4; boolean think; boolean rtn; /* Original code; redundant considering that a byte value is always in the range 0-255: arg1 = arg[1] > 255 ? 255 : arg[1]; arg1 = arg1 < 0 ? 0 : arg1; arg2 = arg[2] > 255 ? 255 : arg[2]; arg2 = arg2 < 0 ? 0 : arg2; arg3 = arg[3] > 255 ? 255 : arg[3]; arg3 = arg3 < 0 ? 0 : arg3; arg4 = arg[4] > 255 ? 255 : arg[4]; arg4 = arg4 < 0 ? 0 : arg4; */ arg1 = arg[1]; arg2 = arg[2]; arg3 = arg[3]; arg4 = arg[4]; secNum = -1; rtn = false; think = false; while ((secNum = P_FindSectorFromTag(arg[0], secNum)) >= 0) { think = false; sec = §ors[secNum]; light = (light_t *) Z_Malloc(sizeof(light_t), PU_LEVSPEC, 0); light->type = type; light->sector = sec; light->count = 0; rtn = true; switch (type) { case LITE_RAISEBYVALUE: sec->lightlevel += arg1; if (sec->lightlevel > 255) { sec->lightlevel = 255; } break; case LITE_LOWERBYVALUE: sec->lightlevel -= arg1; if (sec->lightlevel < 0) { sec->lightlevel = 0; } break; case LITE_CHANGETOVALUE: sec->lightlevel = arg1; if (sec->lightlevel < 0) { sec->lightlevel = 0; } else if (sec->lightlevel > 255) { sec->lightlevel = 255; } break; case LITE_FADE: think = true; light->value1 = arg1; // destination lightlevel light->value2 = FixedDiv((arg1 - sec->lightlevel) << FRACBITS, arg2 << FRACBITS); // delta lightlevel if (sec->lightlevel <= arg1) { light->tics2 = 1; // get brighter } else { light->tics2 = -1; } break; case LITE_GLOW: think = true; light->value1 = arg1; // upper lightlevel light->value2 = arg2; // lower lightlevel light->tics1 = FixedDiv((arg1 - sec->lightlevel) << FRACBITS, arg3 << FRACBITS); // lightlevel delta if (sec->lightlevel <= arg1) { light->tics2 = 1; // get brighter } else { light->tics2 = -1; } break; case LITE_FLICKER: think = true; light->value1 = arg1; // upper lightlevel light->value2 = arg2; // lower lightlevel sec->lightlevel = light->value1; light->count = (P_Random() & 64) + 1; break; case LITE_STROBE: think = true; light->value1 = arg1; // upper lightlevel light->value2 = arg2; // lower lightlevel light->tics1 = arg3; // upper tics light->tics2 = arg4; // lower tics light->count = arg3; sec->lightlevel = light->value1; break; default: rtn = false; break; } if (think) { P_AddThinker(&light->thinker); light->thinker.function = T_Light; } else { Z_Free(light); } } return rtn; } //============================================================================ // // T_Phase // //============================================================================ int PhaseTable[64] = { 128, 112, 96, 80, 64, 48, 32, 32, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 32, 32, 48, 64, 80, 96, 112, 128 }; void T_Phase(phase_t * phase) { phase->index = (phase->index + 1) & 63; phase->sector->lightlevel = phase->base + PhaseTable[phase->index]; } //========================================================================== // // P_SpawnPhasedLight // //========================================================================== void P_SpawnPhasedLight(sector_t * sector, int base, int index) { phase_t *phase; phase = Z_Malloc(sizeof(*phase), PU_LEVSPEC, 0); P_AddThinker(&phase->thinker); phase->sector = sector; if (index == -1) { // sector->lightlevel as the index phase->index = sector->lightlevel & 63; } else { phase->index = index & 63; } phase->base = base & 255; sector->lightlevel = phase->base + PhaseTable[phase->index]; phase->thinker.function = T_Phase; sector->special = 0; } //========================================================================== // // P_SpawnLightSequence // //========================================================================== void P_SpawnLightSequence(sector_t * sector, int indexStep) { sector_t *sec; sector_t *nextSec; sector_t *tempSec; int seqSpecial; int i; int count; fixed_t index; fixed_t indexDelta; int base; seqSpecial = LIGHT_SEQUENCE; // look for Light_Sequence, first sec = sector; count = 1; do { nextSec = NULL; sec->special = LIGHT_SEQUENCE_START; // make sure that the search doesn't back up. for (i = 0; i < sec->linecount; i++) { tempSec = getNextSector(sec->lines[i], sec); if (!tempSec) { continue; } if (tempSec->special == seqSpecial) { if (seqSpecial == LIGHT_SEQUENCE) { seqSpecial = LIGHT_SEQUENCE_ALT; } else { seqSpecial = LIGHT_SEQUENCE; } nextSec = tempSec; count++; } } sec = nextSec; } while (sec); sec = sector; count *= indexStep; index = 0; indexDelta = FixedDiv(64 * FRACUNIT, count * FRACUNIT); base = sector->lightlevel; do { nextSec = NULL; if (sec->lightlevel) { base = sec->lightlevel; } P_SpawnPhasedLight(sec, base, index >> FRACBITS); index += indexDelta; for (i = 0; i < sec->linecount; i++) { tempSec = getNextSector(sec->lines[i], sec); if (!tempSec) { continue; } if (tempSec->special == LIGHT_SEQUENCE_START) { nextSec = tempSec; } } sec = nextSec; } while (sec); } crispy-doom-crispy-doom-5.6.4/src/hexen/p_local.h000066400000000000000000000270211360717211000217160ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __P_LOCAL__ #define __P_LOCAL__ #ifndef __R_LOCAL__ #include "r_local.h" #endif #define STARTREDPALS 1 #define STARTBONUSPALS 9 #define STARTPOISONPALS 13 #define STARTICEPAL 21 #define STARTHOLYPAL 22 #define STARTSCOURGEPAL 25 #define NUMREDPALS 8 #define NUMBONUSPALS 4 #define NUMPOISONPALS 8 #define TOCENTER -8 #define FLOATSPEED (FRACUNIT*4) #define MAXHEALTH 100 #define MAXMORPHHEALTH 30 #define VIEWHEIGHT (48*FRACUNIT) // mapblocks are used to check movement against lines and things #define MAPBLOCKUNITS 128 #define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) #define MAPBLOCKSHIFT (FRACBITS+7) #define MAPBMASK (MAPBLOCKSIZE-1) #define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) // player radius for movement checking #define PLAYERRADIUS 16*FRACUNIT // MAXRADIUS is for precalculated sector block boxes // the spider demon is larger, but we don't have any moving sectors // nearby #define MAXRADIUS 32*FRACUNIT #define GRAVITY FRACUNIT #define MAXMOVE (30*FRACUNIT) #define USERANGE (64*FRACUNIT) #define MELEERANGE (64*FRACUNIT) #define MISSILERANGE (32*64*FRACUNIT) typedef enum { DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_NODIR, NUMDIRS } dirtype_t; #define BASETHRESHOLD 100 // follow a player exlusively for 3 seconds // ***** P_TICK ***** extern thinker_t thinkercap; // both the head and tail of the thinker list extern int TimerGame; // tic countdown for deathmatch void P_InitThinkers(void); void P_AddThinker(thinker_t * thinker); void P_RemoveThinker(thinker_t * thinker); // ***** P_PSPR ***** #define USE_MANA1 1 #define USE_MANA2 1 void P_SetPsprite(player_t * player, int position, statenum_t stnum); void P_SetPspriteNF(player_t * player, int position, statenum_t stnum); void P_SetupPsprites(player_t * curplayer); void P_MovePsprites(player_t * curplayer); void P_DropWeapon(player_t * player); void P_ActivateMorphWeapon(player_t * player); void P_PostMorphWeapon(player_t * player, weapontype_t weapon); // ***** P_USER ***** extern int PStateNormal[NUMCLASSES]; extern int PStateRun[NUMCLASSES]; extern int PStateAttack[NUMCLASSES]; extern int PStateAttackEnd[NUMCLASSES]; void P_PlayerThink(player_t * player); void P_Thrust(player_t * player, angle_t angle, fixed_t move); void P_PlayerRemoveArtifact(player_t * player, int slot); void P_PlayerUseArtifact(player_t * player, artitype_t arti); boolean P_UseArtifact(player_t * player, artitype_t arti); int P_GetPlayerNum(player_t * player); void P_TeleportOther(mobj_t * victim); void ResetBlasted(mobj_t * mo); // ***** P_MOBJ ***** // Any floor type >= FLOOR_LIQUID will floorclip sprites enum { FLOOR_SOLID, FLOOR_ICE, FLOOR_LIQUID, FLOOR_WATER, FLOOR_LAVA, FLOOR_SLUDGE }; #define ONFLOORZ INT_MIN #define ONCEILINGZ INT_MAX #define FLOATRANDZ (INT_MAX-1) #define FROMCEILINGZ128 (INT_MAX-2) extern mobjtype_t PuffType; extern mobj_t *MissileMobj; mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); void P_RemoveMobj(mobj_t * th); boolean P_SetMobjState(mobj_t * mobj, statenum_t state); boolean P_SetMobjStateNF(mobj_t * mobj, statenum_t state); void P_ThrustMobj(mobj_t * mo, angle_t angle, fixed_t move); int P_FaceMobj(mobj_t * source, mobj_t * target, angle_t * delta); boolean P_SeekerMissile(mobj_t * actor, angle_t thresh, angle_t turnMax); void P_MobjThinker(mobj_t * mobj); void P_BlasterMobjThinker(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); void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator); void P_BloodSplatter2(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator); void P_RipperBlood(mobj_t * mo); int P_GetThingFloorType(mobj_t * thing); int P_HitFloor(mobj_t * thing); boolean P_CheckMissileSpawn(mobj_t * missile); mobj_t *P_SpawnMissile(mobj_t * source, mobj_t * dest, mobjtype_t type); mobj_t *P_SpawnMissileXYZ(fixed_t x, fixed_t y, fixed_t z, mobj_t * source, mobj_t * dest, mobjtype_t type); mobj_t *P_SpawnMissileAngle(mobj_t * source, mobjtype_t type, angle_t angle, fixed_t momz); mobj_t *P_SpawnMissileAngleSpeed(mobj_t * source, mobjtype_t type, angle_t angle, fixed_t momz, fixed_t speed); mobj_t *P_SpawnPlayerMissile(mobj_t * source, mobjtype_t type); mobj_t *P_SPMAngle(mobj_t * source, mobjtype_t type, angle_t angle); mobj_t *P_SPMAngleXYZ(mobj_t * source, fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, angle_t angle); void P_CreateTIDList(void); void P_RemoveMobjFromTIDList(mobj_t * mobj); void P_InsertMobjIntoTIDList(mobj_t * mobj, int tid); mobj_t *P_FindMobjFromTID(int tid, int *searchPosition); mobj_t *P_SpawnKoraxMissile(fixed_t x, fixed_t y, fixed_t z, mobj_t * source, mobj_t * dest, mobjtype_t type); // ***** P_ENEMY ***** void P_NoiseAlert(mobj_t * target, mobj_t * emmiter); int P_Massacre(void); boolean A_RaiseMobj(mobj_t * actor); boolean A_SinkMobj(mobj_t * actor); void A_NoBlocking(mobj_t * actor); boolean P_LookForMonsters(mobj_t * actor); void P_InitCreatureCorpseQueue(boolean corpseScan); void A_DeQueueCorpse(mobj_t * actor); // ***** P_MAPUTL ***** typedef struct { fixed_t x, y, dx, dy; } divline_t; typedef struct { fixed_t frac; // along trace line boolean isaline; union { mobj_t *thing; line_t *line; } d; } intercept_t; #define MAXINTERCEPTS 128 extern intercept_t intercepts[MAXINTERCEPTS], *intercept_p; typedef boolean(*traverser_t) (intercept_t * in); fixed_t P_AproxDistance(fixed_t dx, fixed_t dy); int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line); int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line); void P_MakeDivline(line_t * li, divline_t * dl); fixed_t P_InterceptVector(divline_t * v2, divline_t * v1); int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld); extern fixed_t opentop, openbottom, openrange; extern fixed_t lowfloor; void P_LineOpening(line_t * linedef); boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *)); boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *)); #define PT_ADDLINES 1 #define PT_ADDTHINGS 2 #define PT_EARLYOUT 4 extern divline_t trace; boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean(*trav) (intercept_t *)); void P_UnsetThingPosition(mobj_t * thing); void P_SetThingPosition(mobj_t * thing); mobj_t *P_RoughMonsterSearch(mobj_t * mo, int distance); // ***** P_MAP ***** extern boolean floatok; // if true, move would be ok if extern fixed_t tmfloorz, tmceilingz; // within tmfloorz - tmceilingz extern int tmfloorpic; extern mobj_t *BlockingMobj; extern line_t *ceilingline; boolean P_TestMobjLocation(mobj_t * mobj); boolean P_CheckPosition(mobj_t * thing, fixed_t x, fixed_t y); mobj_t *P_CheckOnmobj(mobj_t * thing); void P_FakeZMovement(mobj_t * mo); boolean P_TryMove(mobj_t * thing, fixed_t x, fixed_t y); boolean P_TeleportMove(mobj_t * thing, fixed_t x, fixed_t y); void P_SlideMove(mobj_t * mo); void P_BounceWall(mobj_t * mo); boolean P_CheckSight(mobj_t * t1, mobj_t * t2); void P_UseLines(player_t * player); boolean P_UsePuzzleItem(player_t * player, int itemType); void PIT_ThrustSpike(mobj_t * actor); boolean P_ChangeSector(sector_t * sector, int crunch); extern mobj_t *PuffSpawned; // true if a puff was spawned extern mobj_t *linetarget; // who got hit (or NULL) fixed_t P_AimLineAttack(mobj_t * t1, angle_t angle, fixed_t distance); void P_LineAttack(mobj_t * t1, angle_t angle, fixed_t distance, fixed_t slope, int damage); void P_RadiusAttack(mobj_t * spot, mobj_t * source, int damage, int distance, boolean damageSource); // ***** P_SETUP ***** extern byte *rejectmatrix; // for fast sight rejection extern short *blockmaplump; // offsets in blockmap are from here extern short *blockmap; extern int bmapwidth, bmapheight; // in mapblocks extern fixed_t bmaporgx, bmaporgy; // origin of block map extern mobj_t **blocklinks; // for thing chains // ***** P_INTER ***** extern int clipmana[NUMMANA]; void P_SetMessage(player_t * player, const char *message, boolean ultmsg); void P_SetYellowMessage(player_t * player, const char *message, boolean ultmsg); void P_ClearMessage(player_t * player); void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher); void P_DamageMobj(mobj_t * target, mobj_t * inflictor, mobj_t * source, int damage); void P_FallingDamage(player_t * player); void P_PoisonPlayer(player_t * player, mobj_t * poisoner, int poison); void P_PoisonDamage(player_t * player, mobj_t * source, int damage, boolean playPainSound); boolean P_GiveMana(player_t * player, manatype_t mana, int count); boolean P_GiveArtifact(player_t * player, artitype_t arti, mobj_t * mo); boolean P_GiveArmor(player_t * player, armortype_t armortype, int amount); boolean P_GiveBody(player_t * player, int num); boolean P_GivePower(player_t * player, powertype_t power); boolean P_MorphPlayer(player_t * player); // ***** AM_MAP ***** boolean AM_Responder(event_t * ev); void AM_Ticker(void); void AM_Drawer(void); // ***** A_ACTION ***** boolean A_LocalQuake(byte * args, mobj_t * victim); void P_SpawnDirt(mobj_t * actor, fixed_t radius); void A_BridgeRemove(mobj_t * actor); // ***** SB_BAR ***** extern int SB_state; extern int ArtifactFlash; void SB_PaletteFlash(boolean forceChange); // ===== PO_MAN ===== typedef enum { PODOOR_NONE, PODOOR_SLIDE, PODOOR_SWING, } podoortype_t; typedef struct { thinker_t thinker; int polyobj; int speed; unsigned int dist; int angle; fixed_t xSpeed; // for sliding walls fixed_t ySpeed; } polyevent_t; typedef struct { thinker_t thinker; int polyobj; int speed; int dist; int totalDist; int direction; fixed_t xSpeed, ySpeed; int tics; int waitTics; podoortype_t type; boolean close; } polydoor_t; enum { PO_ANCHOR_TYPE = 3000, PO_SPAWN_TYPE, PO_SPAWNCRUSH_TYPE }; #define PO_LINE_START 1 // polyobj line start special #define PO_LINE_EXPLICIT 5 extern polyobj_t *polyobjs; // list of all poly-objects on the level extern int po_NumPolyobjs; void T_PolyDoor(polydoor_t * pd); void T_RotatePoly(polyevent_t * pe); boolean EV_RotatePoly(line_t * line, byte * args, int direction, boolean overRide); void T_MovePoly(polyevent_t * pe); boolean EV_MovePoly(line_t * line, byte * args, boolean timesEight, boolean overRide); boolean EV_OpenPolyDoor(line_t * line, byte * args, podoortype_t type); boolean PO_MovePolyobj(int num, int x, int y); boolean PO_RotatePolyobj(int num, angle_t angle); void PO_Init(int lump); boolean PO_Busy(int polyobj); #include "p_spec.h" #endif // __P_LOCAL__ crispy-doom-crispy-doom-5.6.4/src/hexen/p_map.c000066400000000000000000002041011360717211000213700ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "m_random.h" #include "i_system.h" #include "m_bbox.h" #include "p_local.h" #include "s_sound.h" static void CheckForPushSpecial(line_t * line, int side, mobj_t * mobj); /* =============================================================================== NOTES: =============================================================================== */ /* =============================================================================== mobj_t NOTES 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 it's 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 be 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 it's origin contained. A valid mobj_t is a mobj_t that has the proper subsector_t filled in for it's xy coordinates and is linked into the subsector's sector 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. =============================================================================== */ fixed_t tmbbox[4]; mobj_t *tmthing; mobj_t *tsthing; int tmflags; fixed_t tmx, tmy; boolean floatok; // if true, move would be ok if // within tmfloorz - tmceilingz fixed_t tmfloorz, tmceilingz, tmdropoffz; int tmfloorpic; // keep track of the line that lowers the ceiling, so missiles don't explode // against sky hack walls line_t *ceilingline; // keep track of special lines as they are hit, but don't process them // until the move is proven valid #define MAXSPECIALCROSS 8 line_t *spechit[MAXSPECIALCROSS]; int numspechit; mobj_t *onmobj; // generic global onmobj...used for landing on pods/players mobj_t *BlockingMobj; /* =============================================================================== TELEPORT MOVE =============================================================================== */ /* ================== = = PIT_StompThing = ================== */ boolean PIT_StompThing(mobj_t * thing) { fixed_t blockdist; if (!(thing->flags & MF_SHOOTABLE)) return true; blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) return true; // didn't hit it if (thing == tmthing) return true; // don't clip against self if (!(tmthing->flags2 & MF2_TELESTOMP)) { // Not allowed to stomp things return (false); } P_DamageMobj(thing, tmthing, tmthing, 10000); return true; } /* =================== = = P_TeleportMove = =================== */ boolean P_TeleportMove(mobj_t * thing, fixed_t x, fixed_t y) { int xl, xh, yl, yh, bx, by; subsector_t *newsubsec; // // kill anything occupying the position // tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector(x, y); ceilingline = 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; tmfloorpic = newsubsec->sector->floorpic; validcount++; numspechit = 0; // // stomp on any things contacted // xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockThingsIterator(bx, by, PIT_StompThing)) return false; // // the move is ok, so link the thing into its new position // P_UnsetThingPosition(thing); thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; P_SetThingPosition(thing); return true; } boolean PIT_ThrustStompThing(mobj_t * thing) { fixed_t blockdist; if (!(thing->flags & MF_SHOOTABLE)) return true; blockdist = thing->radius + tsthing->radius; if (abs(thing->x - tsthing->x) >= blockdist || abs(thing->y - tsthing->y) >= blockdist || (thing->z > tsthing->z + tsthing->height)) return true; // didn't hit it if (thing == tsthing) return true; // don't clip against self P_DamageMobj(thing, tsthing, tsthing, 10001); tsthing->args[1] = 1; // Mark thrust thing as bloody return true; } void PIT_ThrustSpike(mobj_t * actor) { int xl, xh, yl, yh, bx, by; int x0, x2, y0, y2; tsthing = actor; x0 = actor->x - actor->info->radius; x2 = actor->x + actor->info->radius; y0 = actor->y - actor->info->radius; y2 = actor->y + actor->info->radius; xl = (x0 - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; xh = (x2 - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; yl = (y0 - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; yh = (y2 - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; // stomp on any things contacted for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) P_BlockThingsIterator(bx, by, PIT_ThrustStompThing); } /* =============================================================================== MOVEMENT ITERATOR FUNCTIONS =============================================================================== */ /* ================== = = PIT_CheckLine = = Adjusts tmfloorz and tmceilingz as lines are contacted ================== */ boolean PIT_CheckLine(line_t * ld) { if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) { return (true); } if (P_BoxOnLineSide(tmbbox, ld) != -1) { return (true); } // a line has been hit /* = = The moving thing's destination position will cross the given line. = If this should not be allowed, return false. = If the line is special, keep track of it to process later if the move = is proven ok. NOTE: specials are NOT sorted by order, so two special lines = that are only 8 pixels apart could be crossed in either order. */ if (!ld->backsector) { // One sided line if (tmthing->flags2 & MF2_BLASTED) { P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass >> 5); } CheckForPushSpecial(ld, 0, tmthing); return (false); } if (!(tmthing->flags & MF_MISSILE)) { if (ld->flags & ML_BLOCKING) { // Explicitly blocking everything if (tmthing->flags2 & MF2_BLASTED) { P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass >> 5); } CheckForPushSpecial(ld, 0, tmthing); return (false); } if (!tmthing->player && ld->flags & ML_BLOCKMONSTERS) { // Block monsters only if (tmthing->flags2 & MF2_BLASTED) { P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass >> 5); } return (false); } } P_LineOpening(ld); // set openrange, opentop, openbottom // adjust floor / ceiling heights if (opentop < tmceilingz) { tmceilingz = opentop; ceilingline = ld; } if (openbottom > tmfloorz) { tmfloorz = openbottom; } if (lowfloor < tmdropoffz) { tmdropoffz = lowfloor; } if (ld->special) { // Contacted a special line, add it to the list spechit[numspechit] = ld; numspechit++; } return (true); } //--------------------------------------------------------------------------- // // FUNC PIT_CheckThing // //--------------------------------------------------------------------------- boolean PIT_CheckThing(mobj_t * thing) { fixed_t blockdist; boolean solid; int damage; if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) { // Can't hit thing return (true); } blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) { // Didn't hit thing return (true); } if (thing == tmthing) { // Don't clip against self return (true); } BlockingMobj = thing; if (tmthing->flags2 & MF2_PASSMOBJ) { // check if a mobj passed over/under another object if (tmthing->type == MT_BISHOP && thing->type == MT_BISHOP) { // don't let bishops fly over other bishops return false; } if (tmthing->z >= thing->z + thing->height && !(thing->flags & MF_SPECIAL)) { return (true); } else if (tmthing->z + tmthing->height < thing->z && !(thing->flags & MF_SPECIAL)) { // under thing return (true); } } // Check for skulls slamming into things if (tmthing->flags & MF_SKULLFLY) { if (tmthing->type == MT_MINOTAUR) { // Slamming minotaurs shouldn't move non-creatures if (!(thing->flags & MF_COUNTKILL)) { return (false); } } else if (tmthing->type == MT_HOLY_FX) { if (thing->flags & MF_SHOOTABLE && thing != tmthing->target) { if (netgame && !deathmatch && thing->player) { // don't attack other co-op players return true; } if (thing->flags2 & MF2_REFLECTIVE && (thing->player || thing->flags2 & MF2_BOSS)) { tmthing->special1.m = tmthing->target; tmthing->target = thing; return true; } if (thing->flags & MF_COUNTKILL || thing->player) { tmthing->special1.m = thing; } if (P_Random() < 96) { damage = 12; if (thing->player || thing->flags2 & MF2_BOSS) { damage = 3; // ghost burns out faster when attacking players/bosses tmthing->health -= 6; } P_DamageMobj(thing, tmthing, tmthing->target, damage); if (P_Random() < 128) { P_SpawnMobj(tmthing->x, tmthing->y, tmthing->z, MT_HOLY_PUFF); S_StartSound(tmthing, SFX_SPIRIT_ATTACK); if (thing->flags & MF_COUNTKILL && P_Random() < 128 && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT)) { if ((thing->type == MT_CENTAUR) || (thing->type == MT_CENTAURLEADER) || (thing->type == MT_ETTIN)) { S_StartSound(thing, SFX_PUPPYBEAT); } } } } if (thing->health <= 0) { tmthing->special1.i = 0; } } return true; } damage = ((P_Random() % 8) + 1) * tmthing->damage; P_DamageMobj(thing, tmthing, tmthing, damage); tmthing->flags &= ~MF_SKULLFLY; tmthing->momx = tmthing->momy = tmthing->momz = 0; P_SetMobjState(tmthing, tmthing->info->seestate); return (false); } // Check for blasted thing running into another if (tmthing->flags2 & MF2_BLASTED && thing->flags & MF_SHOOTABLE) { if (!(thing->flags2 & MF2_BOSS) && (thing->flags & MF_COUNTKILL)) { thing->momx += tmthing->momx; thing->momy += tmthing->momy; if ((thing->momx + thing->momy) > 3 * FRACUNIT) { damage = (tmthing->info->mass / 100) + 1; P_DamageMobj(thing, tmthing, tmthing, damage); damage = (thing->info->mass / 100) + 1; P_DamageMobj(tmthing, thing, thing, damage >> 2); } return (false); } } // Check for missile if (tmthing->flags & MF_MISSILE) { // Check for a non-shootable mobj if (thing->flags2 & MF2_NONSHOOTABLE) { return true; } // Check if it went over / under if (tmthing->z > thing->z + thing->height) { // Over thing return (true); } if (tmthing->z + tmthing->height < thing->z) { // Under thing return (true); } if (tmthing->flags2 & MF2_FLOORBOUNCE) { if (tmthing->target == thing || !(thing->flags & MF_SOLID)) { return true; } else { return false; } } if (tmthing->type == MT_LIGHTNING_FLOOR || tmthing->type == MT_LIGHTNING_CEILING) { if (thing->flags & MF_SHOOTABLE && thing != tmthing->target) { if (thing->info->mass != INT_MAX) { thing->momx += tmthing->momx >> 4; thing->momy += tmthing->momy >> 4; } if ((!thing->player && !(thing->flags2 & MF2_BOSS)) || !(leveltime & 1)) { if (thing->type == MT_CENTAUR || thing->type == MT_CENTAURLEADER) { // Lightning does more damage to centaurs P_DamageMobj(thing, tmthing, tmthing->target, 9); } else { P_DamageMobj(thing, tmthing, tmthing->target, 3); } if (!(S_GetSoundPlayingInfo(tmthing, SFX_MAGE_LIGHTNING_ZAP))) { S_StartSound(tmthing, SFX_MAGE_LIGHTNING_ZAP); } if (thing->flags & MF_COUNTKILL && P_Random() < 64 && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT)) { if ((thing->type == MT_CENTAUR) || (thing->type == MT_CENTAURLEADER) || (thing->type == MT_ETTIN)) { S_StartSound(thing, SFX_PUPPYBEAT); } } } tmthing->health--; if (tmthing->health <= 0 || thing->health <= 0) { return false; } if (tmthing->type == MT_LIGHTNING_FLOOR) { if (tmthing->special2.m && !tmthing->special2.m->special1.m) { tmthing->special2.m->special1.m = thing; } } else if (!tmthing->special1.m) { tmthing->special1.m = thing; } } return true; // lightning zaps through all sprites } else if (tmthing->type == MT_LIGHTNING_ZAP) { mobj_t *lmo; if (thing->flags & MF_SHOOTABLE && thing != tmthing->target) { lmo = tmthing->special2.m; if (lmo) { if (lmo->type == MT_LIGHTNING_FLOOR) { if (lmo->special2.m && !lmo->special2.m->special1.m) { lmo->special2.m->special1.m = thing; } } else if (!lmo->special1.m) { lmo->special1.m = thing; } if (!(leveltime & 3)) { lmo->health--; } } } } else if (tmthing->type == MT_MSTAFF_FX2 && thing != tmthing->target) { if (!thing->player && !(thing->flags2 & MF2_BOSS)) { switch (thing->type) { case MT_FIGHTER_BOSS: // these not flagged boss case MT_CLERIC_BOSS: // so they can be blasted case MT_MAGE_BOSS: break; default: P_DamageMobj(thing, tmthing, tmthing->target, 10); return true; break; } } } if (tmthing->target && tmthing->target->type == thing->type) { // Don't hit same species as originator if (thing == tmthing->target) { // Don't missile self return (true); } if (!thing->player) { // Hit same species as originator, explode, no damage return (false); } } if (!(thing->flags & MF_SHOOTABLE)) { // Didn't do any damage return !(thing->flags & MF_SOLID); } if (tmthing->flags2 & MF2_RIP) { if (!(thing->flags & MF_NOBLOOD) && !(thing->flags2 & MF2_REFLECTIVE) && !(thing->flags2 & MF2_INVULNERABLE)) { // Ok to spawn some blood P_RipperBlood(tmthing); } //S_StartSound(tmthing, sfx_ripslop); damage = ((P_Random() & 3) + 2) * tmthing->damage; P_DamageMobj(thing, tmthing, tmthing->target, damage); if (thing->flags2 & MF2_PUSHABLE && !(tmthing->flags2 & MF2_CANNOTPUSH)) { // Push thing thing->momx += tmthing->momx >> 2; thing->momy += tmthing->momy >> 2; } numspechit = 0; return (true); } // Do damage damage = ((P_Random() % 8) + 1) * tmthing->damage; if (damage) { if (!(thing->flags & MF_NOBLOOD) && !(thing->flags2 & MF2_REFLECTIVE) && !(thing->flags2 & MF2_INVULNERABLE) && !(tmthing->type == MT_TELOTHER_FX1) && !(tmthing->type == MT_TELOTHER_FX2) && !(tmthing->type == MT_TELOTHER_FX3) && !(tmthing->type == MT_TELOTHER_FX4) && !(tmthing->type == MT_TELOTHER_FX5) && (P_Random() < 192)) { P_BloodSplatter(tmthing->x, tmthing->y, tmthing->z, thing); } P_DamageMobj(thing, tmthing, tmthing->target, damage); } return (false); } if (thing->flags2 & MF2_PUSHABLE && !(tmthing->flags2 & MF2_CANNOTPUSH)) { // Push thing thing->momx += tmthing->momx >> 2; thing->momy += tmthing->momy >> 2; } // Check for special thing if (thing->flags & MF_SPECIAL) { solid = (thing->flags & MF_SOLID) != 0; if (tmflags & MF_PICKUP) { // Can be picked up by tmthing P_TouchSpecialThing(thing, tmthing); // Can remove thing } return (!solid); } return (!(thing->flags & MF_SOLID)); } //--------------------------------------------------------------------------- // // PIT_CheckOnmobjZ // //--------------------------------------------------------------------------- boolean PIT_CheckOnmobjZ(mobj_t * thing) { fixed_t blockdist; if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) { // Can't hit thing return (true); } blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) { // Didn't hit thing return (true); } if (thing == tmthing) { // Don't clip against self return (true); } if (tmthing->z > thing->z + thing->height) { return (true); } else if (tmthing->z + tmthing->height < thing->z) { // under thing return (true); } if (thing->flags & MF_SOLID) { onmobj = thing; } return (!(thing->flags & MF_SOLID)); } /* =============================================================================== MOVEMENT CLIPPING =============================================================================== */ //---------------------------------------------------------------------------- // // FUNC P_TestMobjLocation // // Returns true if the mobj is not blocked by anything at its current // location, otherwise returns false. // //---------------------------------------------------------------------------- boolean P_TestMobjLocation(mobj_t * mobj) { int flags; flags = mobj->flags; mobj->flags &= ~MF_PICKUP; if (P_CheckPosition(mobj, mobj->x, mobj->y)) { // XY is ok, now check Z mobj->flags = flags; if ((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz)) { // Bad Z return (false); } return (true); } mobj->flags = flags; return (false); } /* ================== = = 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 mobj_t *BlockingMobj = pointer to thing that blocked position (NULL if not blocked, or blocked by a line). ================== */ boolean P_CheckPosition(mobj_t * thing, fixed_t x, fixed_t y) { int xl, xh, yl, yh, bx, by; subsector_t *newsubsec; tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector(x, y); ceilingline = 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; tmfloorpic = newsubsec->sector->floorpic; validcount++; numspechit = 0; if (tmflags & MF_NOCLIP && !(tmflags & MF_SKULLFLY)) { return true; } // // check things first, possibly picking things up // the bounding box is extended by MAXRADIUS because mobj_ts are grouped // into mapblocks based on their origin point, and can overlap into adjacent // blocks by up to MAXRADIUS units // xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; BlockingMobj = NULL; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockThingsIterator(bx, by, PIT_CheckThing)) return false; // // check lines // if (tmflags & MF_NOCLIP) { return true; } BlockingMobj = NULL; xl = (tmbbox[BOXLEFT] - bmaporgx) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy) >> MAPBLOCKSHIFT; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockLinesIterator(bx, by, PIT_CheckLine)) return false; return true; } //============================================================================= // // P_CheckOnmobj(mobj_t *thing) // // Checks if the new Z position is legal //============================================================================= mobj_t *P_CheckOnmobj(mobj_t * thing) { int xl, xh, yl, yh, bx, by; subsector_t *newsubsec; fixed_t x; fixed_t y; mobj_t oldmo; x = thing->x; y = thing->y; tmthing = thing; tmflags = thing->flags; oldmo = *thing; // save the old mobj before the fake zmovement P_FakeZMovement(tmthing); 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; tmfloorpic = newsubsec->sector->floorpic; validcount++; numspechit = 0; if (tmflags & MF_NOCLIP) return NULL; // // check things first, possibly picking things up // the bounding box is extended by MAXRADIUS because mobj_ts are grouped // into mapblocks based on their origin point, and can overlap into adjacent // blocks by up to MAXRADIUS units // xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; for (bx = xl; bx <= xh; bx++) for (by = yl; by <= yh; by++) if (!P_BlockThingsIterator(bx, by, PIT_CheckOnmobjZ)) { *tmthing = oldmo; return onmobj; } *tmthing = oldmo; return NULL; } //============================================================================= // // P_FakeZMovement // // Fake the zmovement so that we can check if a move is legal //============================================================================= void P_FakeZMovement(mobj_t * mo) { int dist; int delta; // // adjust height // mo->z += mo->momz; if (mo->flags & MF_FLOAT && mo->target) { // float down towards target if too close if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT)) { dist = P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y); delta = (mo->target->z + (mo->height >> 1)) - mo->z; if (delta < 0 && dist < -(delta * 3)) mo->z -= FLOATSPEED; else if (delta > 0 && dist < (delta * 3)) mo->z += FLOATSPEED; } } if (mo->player && mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz) && leveltime & 2) { mo->z += finesine[(FINEANGLES / 20 * leveltime >> 2) & FINEMASK]; } // // clip movement // if (mo->z <= mo->floorz) { // Hit the floor mo->z = mo->floorz; if (mo->momz < 0) { mo->momz = 0; } if (mo->flags & MF_SKULLFLY) { // The skull slammed into something mo->momz = -mo->momz; } if (mo->info->crashstate && (mo->flags & MF_CORPSE)) { return; } } else if (mo->flags2 & MF2_LOGRAV) { if (mo->momz == 0) mo->momz = -(GRAVITY >> 3) * 2; else mo->momz -= GRAVITY >> 3; } else if (!(mo->flags & MF_NOGRAVITY)) { if (mo->momz == 0) mo->momz = -GRAVITY * 2; else mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // hit the ceiling if (mo->momz > 0) mo->momz = 0; mo->z = mo->ceilingz - mo->height; if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } } } //=========================================================================== // // CheckForPushSpecial // //=========================================================================== static void CheckForPushSpecial(line_t * line, int side, mobj_t * mobj) { if (line->special) { if (mobj->flags2 & MF2_PUSHWALL) { P_ActivateLine(line, mobj, side, SPAC_PUSH); } else if (mobj->flags2 & MF2_IMPACT) { P_ActivateLine(line, mobj, side, SPAC_IMPACT); } } } /* =================== = = P_TryMove = = Attempt to move to a new position, crossing special lines unless MF_TELEPORT = is set = =================== */ boolean P_TryMove(mobj_t * thing, fixed_t x, fixed_t y) { fixed_t oldx, oldy; int side, oldside; line_t *ld; floatok = false; if (!P_CheckPosition(thing, x, y)) { // Solid wall or thing if (!BlockingMobj || BlockingMobj->player || !thing->player) { goto pushline; } else if (BlockingMobj->z + BlockingMobj->height - thing->z > 24 * FRACUNIT || (BlockingMobj->subsector->sector->ceilingheight - (BlockingMobj->z + BlockingMobj->height) < thing->height) || (tmceilingz - (BlockingMobj->z + BlockingMobj->height) < thing->height)) { goto pushline; } } if (!(thing->flags & MF_NOCLIP)) { if (tmceilingz - tmfloorz < thing->height) { // Doesn't fit goto pushline; } floatok = true; if (!(thing->flags & MF_TELEPORT) && tmceilingz - thing->z < thing->height && thing->type != MT_LIGHTNING_CEILING && !(thing->flags2 & MF2_FLY)) { // mobj must lower itself to fit goto pushline; } if (thing->flags2 & MF2_FLY) { if (thing->z + thing->height > tmceilingz) { thing->momz = -8 * FRACUNIT; goto pushline; } else if (thing->z < tmfloorz && tmfloorz - tmdropoffz > 24 * FRACUNIT) { thing->momz = 8 * FRACUNIT; goto pushline; } } if (!(thing->flags & MF_TELEPORT) // The Minotaur floor fire (MT_MNTRFX2) can step up any amount && thing->type != MT_MNTRFX2 && thing->type != MT_LIGHTNING_FLOOR && tmfloorz - thing->z > 24 * FRACUNIT) { goto pushline; } if (!(thing->flags & (MF_DROPOFF | MF_FLOAT)) && (tmfloorz - tmdropoffz > 24 * FRACUNIT) && !(thing->flags2 & MF2_BLASTED)) { // Can't move over a dropoff unless it's been blasted return (false); } if (thing->flags2 & MF2_CANTLEAVEFLOORPIC && (tmfloorpic != thing->subsector->sector->floorpic || tmfloorz - thing->z != 0)) { // must stay within a sector of a certain floor type return false; } } // // the move is ok, so link the thing into its new position // P_UnsetThingPosition(thing); oldx = thing->x; oldy = thing->y; thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->floorpic = tmfloorpic; thing->x = x; thing->y = y; P_SetThingPosition(thing); if (thing->flags2 & MF2_FLOORCLIP) { if (thing->z == thing->subsector->sector->floorheight && P_GetThingFloorType(thing) >= FLOOR_LIQUID) { thing->floorclip = 10 * FRACUNIT; } else { thing->floorclip = 0; } } // // if any special lines were hit, do the effect // if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) { while (numspechit > 0) { numspechit--; // see if the line was crossed ld = spechit[numspechit]; side = P_PointOnLineSide(thing->x, thing->y, ld); oldside = P_PointOnLineSide(oldx, oldy, ld); if (side != oldside) { if (ld->special) { if (thing->player) { P_ActivateLine(ld, thing, oldside, SPAC_CROSS); } else if (thing->flags2 & MF2_MCROSS) { P_ActivateLine(ld, thing, oldside, SPAC_MCROSS); } else if (thing->flags2 & MF2_PCROSS) { P_ActivateLine(ld, thing, oldside, SPAC_PCROSS); } } } } } return true; pushline: if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) { int numSpecHitTemp; if (tmthing->flags2 & MF2_BLASTED) { P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass >> 5); } numSpecHitTemp = numspechit; while (numSpecHitTemp > 0) { numSpecHitTemp--; // see if the line was crossed ld = spechit[numSpecHitTemp]; side = P_PointOnLineSide(thing->x, thing->y, ld); CheckForPushSpecial(ld, side, thing); } } return false; } /* ================== = = P_ThingHeightClip = = Takes a valid thing and adjusts the thing->floorz, thing->ceilingz, = anf 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 ================== */ boolean P_ThingHeightClip(mobj_t * thing) { boolean onfloor; onfloor = (thing->z == thing->floorz); P_CheckPosition(thing, thing->x, thing->y); // what about stranding a monster partially off an edge? thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->floorpic = tmfloorpic; if (onfloor) { // walking monsters rise and fall with the floor if ((thing->z - thing->floorz < 9 * FRACUNIT) || (thing->flags & MF_NOGRAVITY)) { thing->z = thing->floorz; } } else { // don't adjust a floating monster unless forced to if (thing->z + thing->height > thing->ceilingz) thing->z = thing->ceilingz - thing->height; } if (thing->ceilingz - thing->floorz < thing->height) return false; return true; } /* ============================================================================== SLIDE MOVE Allows the player to slide along any angled walls ============================================================================== */ fixed_t bestslidefrac, secondslidefrac; line_t *bestslideline, *secondslideline; mobj_t *slidemo; fixed_t tmxmove, tmymove; /* ================== = = P_HitSlideLine = = Adjusts the xmove / ymove so that the next move will slide along the wall ================== */ void P_HitSlideLine(line_t * ld) { int side; angle_t lineangle, moveangle, deltaangle; fixed_t movelen, newlen; if (ld->slopetype == ST_HORIZONTAL) { tmymove = 0; return; } if (ld->slopetype == ST_VERTICAL) { tmxmove = 0; return; } side = P_PointOnLineSide(slidemo->x, slidemo->y, ld); lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy); if (side == 1) lineangle += ANG180; moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove); deltaangle = moveangle - lineangle; if (deltaangle > ANG180) deltaangle += ANG180; // I_Error ("SlideLine: ang>ANG180"); lineangle >>= ANGLETOFINESHIFT; deltaangle >>= ANGLETOFINESHIFT; movelen = P_AproxDistance(tmxmove, tmymove); newlen = FixedMul(movelen, finecosine[deltaangle]); tmxmove = FixedMul(newlen, finecosine[lineangle]); tmymove = FixedMul(newlen, finesine[lineangle]); } /* ============== = = PTR_SlideTraverse = ============== */ boolean 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; } P_LineOpening(li); // set openrange, opentop, openbottom 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 return true; // this line doesn't block movement // the line does block movement, see if it is closer than best so far isblocking: if (in->frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in->frac; bestslideline = li; } return false; // stop } /* ================== = = P_SlideMove = = The momx / momy move is bad, so try to slide along a wall = = Find the first line hit, move flush to it, and slide along it = = This is a kludgy mess. ================== */ void P_SlideMove(mobj_t * mo) { fixed_t leadx, leady; fixed_t trailx, traily; fixed_t newx, newy; int hitcount; slidemo = mo; hitcount = 0; retry: if (++hitcount == 3) goto stairstep; // don't loop forever // // trace along the three leading corners // if (mo->momx > 0) { leadx = mo->x + mo->radius; trailx = mo->x - mo->radius; } else { leadx = mo->x - mo->radius; trailx = mo->x + mo->radius; } if (mo->momy > 0) { leady = mo->y + mo->radius; traily = mo->y - mo->radius; } else { leady = mo->y - mo->radius; traily = mo->y + mo->radius; } bestslidefrac = FRACUNIT + 1; P_PathTraverse(leadx, leady, leadx + mo->momx, leady + mo->momy, PT_ADDLINES, PTR_SlideTraverse); P_PathTraverse(trailx, leady, trailx + mo->momx, leady + mo->momy, PT_ADDLINES, PTR_SlideTraverse); P_PathTraverse(leadx, traily, leadx + mo->momx, traily + mo->momy, PT_ADDLINES, PTR_SlideTraverse); // // move up to the wall // if (bestslidefrac == FRACUNIT + 1) { // the move must have hit the middle, so stairstep stairstep: if (!P_TryMove(mo, mo->x, mo->y + mo->momy)) { P_TryMove(mo, mo->x + mo->momx, mo->y); } return; } bestslidefrac -= 0x800; // fudge a bit to make sure it doesn't hit if (bestslidefrac > 0) { newx = FixedMul(mo->momx, bestslidefrac); newy = FixedMul(mo->momy, bestslidefrac); if (!P_TryMove(mo, mo->x + newx, mo->y + newy)) goto stairstep; } // // now continue along the wall // bestslidefrac = FRACUNIT - (bestslidefrac + 0x800); // remainder if (bestslidefrac > FRACUNIT) bestslidefrac = FRACUNIT; if (bestslidefrac <= 0) return; tmxmove = FixedMul(mo->momx, bestslidefrac); tmymove = FixedMul(mo->momy, bestslidefrac); P_HitSlideLine(bestslideline); // clip the moves mo->momx = tmxmove; mo->momy = tmymove; if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove)) { goto retry; } } //============================================================================ // // PTR_BounceTraverse // //============================================================================ boolean PTR_BounceTraverse(intercept_t * in) { line_t *li; if (!in->isaline) I_Error("PTR_BounceTraverse: 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 bounceblocking; } P_LineOpening(li); // set openrange, opentop, openbottom if (openrange < slidemo->height) goto bounceblocking; // doesn't fit if (opentop - slidemo->z < slidemo->height) goto bounceblocking; // mobj is too high return true; // this line doesn't block movement // the line does block movement, see if it is closer than best so far bounceblocking: if (in->frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in->frac; bestslideline = li; } return false; // stop } //============================================================================ // // P_BounceWall // //============================================================================ void P_BounceWall(mobj_t * mo) { fixed_t leadx, leady; int side; angle_t lineangle, moveangle, deltaangle; fixed_t movelen; slidemo = mo; // // trace along the three leading corners // if (mo->momx > 0) { leadx = mo->x + mo->radius; } else { leadx = mo->x - mo->radius; } if (mo->momy > 0) { leady = mo->y + mo->radius; } else { leady = mo->y - mo->radius; } bestslidefrac = FRACUNIT + 1; P_PathTraverse(leadx, leady, leadx + mo->momx, leady + mo->momy, PT_ADDLINES, PTR_BounceTraverse); side = P_PointOnLineSide(mo->x, mo->y, bestslideline); lineangle = R_PointToAngle2(0, 0, bestslideline->dx, bestslideline->dy); if (side == 1) lineangle += ANG180; moveangle = R_PointToAngle2(0, 0, mo->momx, mo->momy); deltaangle = (2 * lineangle) - moveangle; // if (deltaangle > ANG180) // deltaangle += ANG180; // I_Error ("SlideLine: ang>ANG180"); lineangle >>= ANGLETOFINESHIFT; deltaangle >>= ANGLETOFINESHIFT; movelen = P_AproxDistance(mo->momx, mo->momy); movelen = FixedMul(movelen, 0.75 * FRACUNIT); // friction if (movelen < FRACUNIT) movelen = 2 * FRACUNIT; mo->momx = FixedMul(movelen, finecosine[deltaangle]); mo->momy = FixedMul(movelen, finesine[deltaangle]); } /* ============================================================================== P_LineAttack ============================================================================== */ mobj_t *PuffSpawned; mobj_t *linetarget; // who got hit (or NULL) mobj_t *shootthing; fixed_t shootz; // height if not aiming up or down // ???: use slope for monsters? int la_damage; fixed_t attackrange; fixed_t aimslope; extern fixed_t topslope, bottomslope; // slopes to top and bottom of target /* =============================================================================== = = PTR_AimTraverse = = Sets linetaget and aimslope when a target is aimed at =============================================================================== */ boolean PTR_AimTraverse(intercept_t * in) { line_t *li; mobj_t *th; fixed_t slope, thingtopslope, thingbottomslope; fixed_t dist; if (in->isaline) { li = in->d.line; if (!(li->flags & ML_TWOSIDED)) return false; // stop // // crosses a two sided line // a two sided line will restrict the possible target ranges P_LineOpening(li); if (openbottom >= opentop) return false; // stop dist = FixedMul(attackrange, in->frac); if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv(openbottom - shootz, dist); if (slope > bottomslope) bottomslope = slope; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv(opentop - shootz, dist); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop return true; // shot continues } // // shoot a thing // th = in->d.thing; if (th == shootthing) return true; // can't shoot self if (!(th->flags & MF_SHOOTABLE)) { // corpse or something return true; } if (th->player && netgame && !deathmatch) { // don't aim at fellow co-op players 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 = ============================================================================== */ boolean PTR_ShootTraverse(intercept_t * in) { fixed_t x, y, z; fixed_t frac; line_t *li; mobj_t *th; fixed_t slope; fixed_t dist; fixed_t thingtopslope, thingbottomslope; extern mobj_t LavaInflictor; if (in->isaline) { li = in->d.line; if (li->special) { P_ActivateLine(li, shootthing, 0, SPAC_IMPACT); // P_ShootSpecialLine (shootthing, li); } if (!(li->flags & ML_TWOSIDED)) goto hitline; // // crosses a two sided line // P_LineOpening(li); dist = FixedMul(attackrange, in->frac); if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv(openbottom - shootz, dist); if (slope > aimslope) goto hitline; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv(opentop - shootz, dist); if (slope < aimslope) goto hitline; } return true; // shot continues // // hit line // hitline: // position a bit closer frac = in->frac - FixedDiv(4 * FRACUNIT, attackrange); x = trace.x + FixedMul(trace.dx, frac); y = trace.y + FixedMul(trace.dy, frac); z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange)); if (li->frontsector->ceilingpic == skyflatnum) { if (z > li->frontsector->ceilingheight) return false; // don't shoot the sky! if (li->backsector && li->backsector->ceilingpic == skyflatnum) return false; // it's a sky hack wall } P_SpawnPuff(x, y, z); return false; // don't go any farther } // // 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 for physical attacks on a ghost // /* FIX: Impliment Heretic 2 weapons here if(th->flags&MF_SHADOW && shootthing->player->readyweapon == wp_staff) { 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 < 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)); P_SpawnPuff(x, y, z); if (la_damage) { if (!(in->d.thing->flags & MF_NOBLOOD) && !(in->d.thing->flags2 & MF2_INVULNERABLE)) { if (PuffType == MT_AXEPUFF || PuffType == MT_AXEPUFF_GLOW) { P_BloodSplatter2(x, y, z, in->d.thing); } if (P_Random() < 192) { P_BloodSplatter(x, y, z, in->d.thing); } } if (PuffType == MT_FLAMEPUFF2) { // Cleric FlameStrike does fire damage P_DamageMobj(th, &LavaInflictor, shootthing, la_damage); } else { P_DamageMobj(th, shootthing, shootthing, la_damage); } } return (false); // don't go any farther } /* ================= = = P_AimLineAttack = ================= */ fixed_t P_AimLineAttack(mobj_t * t1, angle_t angle, fixed_t distance) { fixed_t x2, y2; angle >>= ANGLETOFINESHIFT; shootthing = t1; x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; topslope = 100 * FRACUNIT / 160; // can't shoot outside view angles bottomslope = -100 * FRACUNIT / 160; attackrange = distance; linetarget = NULL; 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, 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; shootz -= t1->floorclip; attackrange = distance; aimslope = slope; if (P_PathTraverse(t1->x, t1->y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, PTR_ShootTraverse)) { switch (PuffType) { case MT_PUNCHPUFF: S_StartSound(t1, SFX_FIGHTER_PUNCH_MISS); break; case MT_HAMMERPUFF: case MT_AXEPUFF: case MT_AXEPUFF_GLOW: S_StartSound(t1, SFX_FIGHTER_HAMMER_MISS); break; case MT_FLAMEPUFF: P_SpawnPuff(x2, y2, shootz + FixedMul(slope, distance)); break; default: break; } } } /* ============================================================================== USE LINES ============================================================================== */ mobj_t *usething; boolean PTR_UseTraverse(intercept_t * in) { int sound; fixed_t pheight; if (!in->d.line->special) { P_LineOpening(in->d.line); if (openrange <= 0) { if (usething->player) { switch (usething->player->class) { case PCLASS_FIGHTER: sound = SFX_PLAYER_FIGHTER_FAILED_USE; break; case PCLASS_CLERIC: sound = SFX_PLAYER_CLERIC_FAILED_USE; break; case PCLASS_MAGE: sound = SFX_PLAYER_MAGE_FAILED_USE; break; case PCLASS_PIG: sound = SFX_PIG_ACTIVE1; break; default: sound = SFX_NONE; break; } S_StartSound(usething, sound); } return false; // can't use through a wall } if (usething->player) { pheight = usething->z + (usething->height / 2); if ((opentop < pheight) || (openbottom > pheight)) { switch (usething->player->class) { case PCLASS_FIGHTER: sound = SFX_PLAYER_FIGHTER_FAILED_USE; break; case PCLASS_CLERIC: sound = SFX_PLAYER_CLERIC_FAILED_USE; break; case PCLASS_MAGE: sound = SFX_PLAYER_MAGE_FAILED_USE; break; case PCLASS_PIG: sound = SFX_PIG_ACTIVE1; break; default: sound = SFX_NONE; break; } S_StartSound(usething, sound); } } return true; // not a special line, but keep checking } if (P_PointOnLineSide(usething->x, usething->y, in->d.line) == 1) return false; // don't use back sides // P_UseSpecialLine (usething, in->d.line); P_ActivateLine(in->d.line, usething, 0, SPAC_USE); return false; // can't use for than one special line in a row } /* ================ = = P_UseLines = = Looks for special lines in front of the player to activate ================ */ void P_UseLines(player_t * player) { int angle; fixed_t x1, y1, x2, y2; usething = player->mo; angle = player->mo->angle >> ANGLETOFINESHIFT; x1 = player->mo->x; y1 = player->mo->y; x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse); } //========================================================================== // // PTR_PuzzleItemTraverse // //========================================================================== #define USE_PUZZLE_ITEM_SPECIAL 129 static mobj_t *PuzzleItemUser; static int PuzzleItemType; static boolean PuzzleActivated; boolean PTR_PuzzleItemTraverse(intercept_t * in) { mobj_t *mobj; byte args[3]; int sound; if (in->isaline) { // Check line if (in->d.line->special != USE_PUZZLE_ITEM_SPECIAL) { P_LineOpening(in->d.line); if (openrange <= 0) { sound = SFX_NONE; if (PuzzleItemUser->player) { switch (PuzzleItemUser->player->class) { case PCLASS_FIGHTER: sound = SFX_PUZZLE_FAIL_FIGHTER; break; case PCLASS_CLERIC: sound = SFX_PUZZLE_FAIL_CLERIC; break; case PCLASS_MAGE: sound = SFX_PUZZLE_FAIL_MAGE; break; default: sound = SFX_NONE; break; } } S_StartSound(PuzzleItemUser, sound); return false; // can't use through a wall } return true; // Continue searching } if (P_PointOnLineSide(PuzzleItemUser->x, PuzzleItemUser->y, in->d.line) == 1) { // Don't use back sides return false; } if (PuzzleItemType != in->d.line->arg1) { // Item type doesn't match return false; } // Construct an args[] array that would contain the values from // the line that would be passed by Vanilla Hexen. args[0] = in->d.line->arg3; args[1] = in->d.line->arg4; args[2] = in->d.line->arg5; P_StartACS(in->d.line->arg2, 0, args, PuzzleItemUser, in->d.line, 0); in->d.line->special = 0; PuzzleActivated = true; return false; // Stop searching } // Check thing mobj = in->d.thing; if (mobj->special != USE_PUZZLE_ITEM_SPECIAL) { // Wrong special return true; } if (PuzzleItemType != mobj->args[0]) { // Item type doesn't match return true; } P_StartACS(mobj->args[1], 0, &mobj->args[2], PuzzleItemUser, NULL, 0); mobj->special = 0; PuzzleActivated = true; return false; // Stop searching } //========================================================================== // // P_UsePuzzleItem // // Returns true if the puzzle item was used on a line or a thing. // //========================================================================== boolean P_UsePuzzleItem(player_t * player, int itemType) { int angle; fixed_t x1, y1, x2, y2; PuzzleItemType = itemType; PuzzleItemUser = player->mo; PuzzleActivated = false; angle = player->mo->angle >> ANGLETOFINESHIFT; x1 = player->mo->x; y1 = player->mo->y; x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES | PT_ADDTHINGS, PTR_PuzzleItemTraverse); return PuzzleActivated; } /* ============================================================================== RADIUS ATTACK ============================================================================== */ mobj_t *bombsource; mobj_t *bombspot; int bombdamage; int bombdistance; boolean DamageSource; /* ================= = = PIT_RadiusAttack = = Source is the creature that casued the explosion at spot ================= */ boolean PIT_RadiusAttack(mobj_t * thing) { fixed_t dx, dy, dist; int damage; if (!(thing->flags & MF_SHOOTABLE)) { return true; } // if(thing->flags2&MF2_BOSS) // { // Bosses take no damage from PIT_RadiusAttack // return(true); // } if (!DamageSource && thing == bombsource) { // don't damage the source of the explosion return true; } if (abs((thing->z - bombspot->z) >> FRACBITS) > 2 * bombdistance) { // too high/low return true; } dx = abs(thing->x - bombspot->x); dy = abs(thing->y - bombspot->y); dist = dx > dy ? dx : dy; dist = (dist - thing->radius) >> FRACBITS; if (dist < 0) { dist = 0; } if (dist >= bombdistance) { // Out of range return true; } if (P_CheckSight(thing, bombspot)) { // OK to damage, target is in direct path damage = (bombdamage * (bombdistance - dist) / bombdistance) + 1; if (thing->player) { damage >>= 2; } P_DamageMobj(thing, bombspot, bombsource, damage); } 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 distance, boolean damageSource) { int x, y, xl, xh, yl, yh; fixed_t dist; dist = (distance + MAXRADIUS) << FRACBITS; yh = (spot->y + dist - bmaporgy) >> MAPBLOCKSHIFT; yl = (spot->y - dist - bmaporgy) >> MAPBLOCKSHIFT; xh = (spot->x + dist - bmaporgx) >> MAPBLOCKSHIFT; xl = (spot->x - dist - bmaporgx) >> MAPBLOCKSHIFT; bombspot = spot; bombsource = source; bombdamage = damage; bombdistance = distance; DamageSource = damageSource; for (y = yl; y <= yh; y++) { for (x = xl; x <= xh; x++) { P_BlockThingsIterator(x, y, PIT_RadiusAttack); } } } /* ============================================================================== SECTOR HEIGHT CHANGING = After modifying a sectors floor or ceiling height, call this = routine to adjust the positions of all things that touch the = sector. = = If anything doesn't fit anymore, true will be returned. = If crunch is true, they will take damage as they are being crushed = If Crunch is false, you should set the sector height back the way it = was and call P_ChangeSector again to undo the changes ============================================================================== */ int crushchange; boolean nofit; /* =============== = = PIT_ChangeSector = =============== */ boolean PIT_ChangeSector(mobj_t * thing) { mobj_t *mo; if (P_ThingHeightClip(thing)) return true; // keep checking // crunch bodies to giblets if ((thing->flags & MF_CORPSE) && (thing->health <= 0)) { if (thing->flags & MF_NOBLOOD) { P_RemoveMobj(thing); } else { if (thing->state != &states[S_GIBS1]) { P_SetMobjState(thing, S_GIBS1); thing->height = 0; thing->radius = 0; S_StartSound(thing, SFX_PLAYER_FALLING_SPLAT); } } return true; // keep checking } // crunch dropped items if (thing->flags2 & MF2_DROPPED) { P_RemoveMobj(thing); return true; // keep checking } if (!(thing->flags & MF_SHOOTABLE)) return true; // assume it is bloody gibs or something nofit = true; if (crushchange && !(leveltime & 3)) { P_DamageMobj(thing, NULL, NULL, crushchange); // spray blood in a random direction if ((!(thing->flags & MF_NOBLOOD)) && (!(thing->flags2 & MF2_INVULNERABLE))) { mo = P_SpawnMobj(thing->x, thing->y, thing->z + thing->height / 2, MT_BLOOD); mo->momx = P_SubRandom() << 12; mo->momy = P_SubRandom() << 12; } } return true; // keep checking (crush other things) } /* =============== = = P_ChangeSector = =============== */ boolean P_ChangeSector(sector_t * sector, int crunch) { int x, y; nofit = false; crushchange = crunch; // recheck 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; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_maputl.c000066400000000000000000000637101360717211000221260ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_system.h" #include "m_bbox.h" #include "p_local.h" static mobj_t *RoughBlockCheck(mobj_t * mo, int index); /* =================== = = P_AproxDistance = = Gives an estimation of distance (not exact) = =================== */ fixed_t P_AproxDistance(fixed_t dx, fixed_t dy) { dx = abs(dx); dy = abs(dy); if (dx < dy) return dx + dy - (dx >> 1); return dx + dy - (dy >> 1); } /* ================== = = P_PointOnLineSide = = Returns 0 or 1 ================== */ int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line) { fixed_t dx, dy; fixed_t left, right; if (!line->dx) { if (x <= line->v1->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->v1->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->v1->x); dy = (y - line->v1->y); left = FixedMul(line->dy >> FRACBITS, dx); right = FixedMul(dy, line->dx >> FRACBITS); if (right < left) return 0; // front side return 1; // back side } /* ================= = = P_BoxOnLineSide = = Considers the line to be infinite = Returns side 0 or 1, -1 if box crosses the line ================= */ int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld) { int p1 = 0, p2 = 0; switch (ld->slopetype) { case ST_HORIZONTAL: p1 = tmbox[BOXTOP] > ld->v1->y; p2 = tmbox[BOXBOTTOM] > ld->v1->y; if (ld->dx < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_VERTICAL: p1 = tmbox[BOXRIGHT] < ld->v1->x; p2 = tmbox[BOXLEFT] < ld->v1->x; if (ld->dy < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_POSITIVE: p1 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); break; case ST_NEGATIVE: p1 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); break; } if (p1 == p2) return p1; return -1; } /* ================== = = P_PointOnDivlineSide = = Returns 0 or 1 ================== */ int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line) { fixed_t dx, dy; fixed_t left, right; if (!line->dx) { if (x <= line->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->x); dy = (y - line->y); // try to quickly decide by looking at sign bits if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000) { if ((line->dy ^ dx) & 0x80000000) return 1; // (left is negative) return 0; } left = FixedMul(line->dy >> 8, dx >> 8); right = FixedMul(dy >> 8, line->dx >> 8); if (right < left) return 0; // front side return 1; // back side } /* ============== = = P_MakeDivline = ============== */ void P_MakeDivline(line_t * li, divline_t * dl) { dl->x = li->v1->x; dl->y = li->v1->y; dl->dx = li->dx; dl->dy = li->dy; } /* =============== = = P_InterceptVector = = Returns the fractional intercept point along the first divline = = This is only called by the addthings and addlines traversers =============== */ fixed_t P_InterceptVector(divline_t * v2, divline_t * v1) { #if 1 fixed_t frac, num, den; den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul((v1->x - v2->x) >> 8, v1->dy) + FixedMul((v2->y - v1->y) >> 8, v1->dx); frac = FixedDiv(num, den); return frac; #else float frac, num, den, v1x, v1y, v1dx, v1dy, v2x, v2y, v2dx, v2dy; v1x = (float) v1->x / FRACUNIT; v1y = (float) v1->y / FRACUNIT; v1dx = (float) v1->dx / FRACUNIT; v1dy = (float) v1->dy / FRACUNIT; v2x = (float) v2->x / FRACUNIT; v2y = (float) v2->y / FRACUNIT; v2dx = (float) v2->dx / FRACUNIT; v2dy = (float) v2->dy / FRACUNIT; den = v1dy * v2dx - v1dx * v2dy; if (den == 0) return 0; // parallel num = (v1x - v2x) * v1dy + (v2y - v1y) * v1dx; frac = num / den; return frac * FRACUNIT; #endif } /* ================== = = P_LineOpening = = Sets opentop and openbottom to the window through a two sided line = OPTIMIZE: keep this precalculated ================== */ fixed_t opentop, openbottom, openrange; fixed_t lowfloor; void P_LineOpening(line_t * linedef) { sector_t *front, *back; if (linedef->sidenum[1] == -1) { // single sided line openrange = 0; return; } front = linedef->frontsector; back = linedef->backsector; if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; if (front->floorheight > back->floorheight) { openbottom = front->floorheight; lowfloor = back->floorheight; tmfloorpic = front->floorpic; } else { openbottom = back->floorheight; lowfloor = front->floorheight; tmfloorpic = back->floorpic; } openrange = opentop - openbottom; } /* =============================================================================== THING POSITION SETTING =============================================================================== */ /* =================== = = P_UnsetThingPosition = = Unlinks a thing from block map and sectors = =================== */ void P_UnsetThingPosition(mobj_t * thing) { int blockx, blocky; if (!(thing->flags & MF_NOSECTOR)) { // inert things don't need to be in blockmap // unlink from subsector if (thing->snext) thing->snext->sprev = thing->sprev; if (thing->sprev) thing->sprev->snext = thing->snext; else thing->subsector->sector->thinglist = thing->snext; } if (!(thing->flags & MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap // unlink from block map if (thing->bnext) thing->bnext->bprev = thing->bprev; if (thing->bprev) thing->bprev->bnext = thing->bnext; else { blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; if (blockx >= 0 && blockx < bmapwidth && blocky >= 0 && blocky < bmapheight) blocklinks[blocky * bmapwidth + blockx] = thing->bnext; } } } /* =================== = = P_SetThingPosition = = Links a thing into both a block and a subsector based on it's x y = Sets thing->subsector properly = =================== */ void P_SetThingPosition(mobj_t * thing) { subsector_t *ss; sector_t *sec; int blockx, blocky; mobj_t **link; // // link into subsector // ss = R_PointInSubsector(thing->x, thing->y); thing->subsector = ss; if (!(thing->flags & MF_NOSECTOR)) { // invisible things don't go into the sector links sec = ss->sector; thing->sprev = NULL; thing->snext = sec->thinglist; if (sec->thinglist) sec->thinglist->sprev = thing; sec->thinglist = thing; } // // link into blockmap // if (!(thing->flags & MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; if (blockx >= 0 && blockx < bmapwidth && blocky >= 0 && blocky < bmapheight) { link = &blocklinks[blocky * bmapwidth + blockx]; thing->bprev = NULL; thing->bnext = *link; if (*link) (*link)->bprev = thing; *link = thing; } else { // thing is off the map thing->bnext = thing->bprev = NULL; } } } /* =============================================================================== BLOCK MAP ITERATORS For each line/thing in the given mapblock, call the passed 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 =================== */ boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *)) { int offset; short *list; line_t *ld; int i; polyblock_t *polyLink; seg_t **tempSeg; extern polyblock_t **PolyBlockMap; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) return true; offset = y * bmapwidth + x; polyLink = PolyBlockMap[offset]; while (polyLink) { if (polyLink->polyobj) { if (polyLink->polyobj->validcount != validcount) { polyLink->polyobj->validcount = validcount; tempSeg = polyLink->polyobj->segs; for (i = 0; i < polyLink->polyobj->numsegs; i++, tempSeg++) { if ((*tempSeg)->linedef->validcount == validcount) { continue; } (*tempSeg)->linedef->validcount = validcount; if (!func((*tempSeg)->linedef)) { return false; } } } } polyLink = polyLink->next; } offset = *(blockmap + offset); for (list = blockmaplump + offset; *list != -1; list++) { ld = &lines[*list]; if (ld->validcount == validcount) continue; // line has already been checked ld->validcount = validcount; if (!func(ld)) return false; } return true; // everything was checked } /* ================== = = P_BlockThingsIterator = ================== */ boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *)) { mobj_t *mobj; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) return true; for (mobj = blocklinks[y * bmapwidth + x]; mobj; mobj = mobj->bnext) if (!func(mobj)) return false; return true; } /* =============================================================================== INTERCEPT ROUTINES =============================================================================== */ intercept_t intercepts[MAXINTERCEPTS], *intercept_p; divline_t trace; boolean earlyout; int ptflags; /* ================== = = PIT_AddLineIntercepts = = Looks for lines in the given block that intercept the given trace = to add to the intercepts list = A line is crossed if its endpoints are on opposite sides of the trace = Returns true if earlyout and a solid line hit ================== */ boolean PIT_AddLineIntercepts(line_t * ld) { int s1, s2; fixed_t frac; divline_t dl; // avoid precision problems with two routines if (trace.dx > FRACUNIT * 16 || trace.dy > FRACUNIT * 16 || trace.dx < -FRACUNIT * 16 || trace.dy < -FRACUNIT * 16) { s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace); s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace); } else { s1 = P_PointOnLineSide(trace.x, trace.y, ld); s2 = P_PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy, ld); } if (s1 == s2) return true; // line isn't crossed // // hit the line // P_MakeDivline(ld, &dl); frac = P_InterceptVector(&trace, &dl); if (frac < 0) return true; // behind source // try to early out the check if (earlyout && frac < FRACUNIT && !ld->backsector) return false; // stop checking intercept_p->frac = frac; intercept_p->isaline = true; intercept_p->d.line = ld; intercept_p++; return true; // continue } /* ================== = = PIT_AddThingIntercepts = ================== */ boolean PIT_AddThingIntercepts(mobj_t * thing) { fixed_t x1, y1, x2, y2; int s1, s2; boolean tracepositive; divline_t dl; fixed_t frac; tracepositive = (trace.dx ^ trace.dy) > 0; // check a corner to corner crossection for hit if (tracepositive) { x1 = thing->x - thing->radius; y1 = thing->y + thing->radius; x2 = thing->x + thing->radius; y2 = thing->y - thing->radius; } else { x1 = thing->x - thing->radius; y1 = thing->y - thing->radius; x2 = thing->x + thing->radius; y2 = thing->y + thing->radius; } s1 = P_PointOnDivlineSide(x1, y1, &trace); s2 = P_PointOnDivlineSide(x2, y2, &trace); if (s1 == s2) return true; // line isn't crossed dl.x = x1; dl.y = y1; dl.dx = x2 - x1; dl.dy = y2 - y1; frac = P_InterceptVector(&trace, &dl); if (frac < 0) return true; // behind source intercept_p->frac = frac; intercept_p->isaline = false; intercept_p->d.thing = thing; intercept_p++; return true; // keep going } /* ==================== = = P_TraverseIntercepts = = Returns true if the traverser function returns true for all lines ==================== */ boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac) { int count; fixed_t dist; intercept_t *scan, *in; count = intercept_p - intercepts; in = 0; // shut up compiler warning while (count--) { dist = INT_MAX; for (scan = intercepts; scan < intercept_p; scan++) if (scan->frac < dist) { dist = scan->frac; in = scan; } if (dist > maxfrac) return true; // checked everything in range #if 0 { // don't check these yet, ther may be others inserted in = scan = intercepts; for (scan = intercepts; scan < intercept_p; scan++) if (scan->frac > maxfrac) *in++ = *scan; intercept_p = in; return false; } #endif if (!func(in)) return false; // don't bother going farther in->frac = INT_MAX; } return true; // everything was traversed } /* ================== = = 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 ================== */ boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean(*trav) (intercept_t *)) { fixed_t xt1, yt1, xt2, yt2; fixed_t xstep, ystep; fixed_t partial; fixed_t xintercept, yintercept; int mapx, mapy, mapxstep, mapystep; int count; earlyout = (flags & PT_EARLYOUT) != 0; validcount++; intercept_p = intercepts; if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) y1 += FRACUNIT; // don't side exactly on a line trace.x = x1; trace.y = y1; trace.dx = x2 - x1; trace.dy = y2 - y1; x1 -= bmaporgx; y1 -= bmaporgy; xt1 = x1 >> MAPBLOCKSHIFT; yt1 = y1 >> MAPBLOCKSHIFT; x2 -= bmaporgx; y2 -= bmaporgy; xt2 = x2 >> MAPBLOCKSHIFT; yt2 = y2 >> MAPBLOCKSHIFT; if (xt2 > xt1) { mapxstep = 1; partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1)); ystep = 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 (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); } //=========================================================================== // // P_RoughMonsterSearch // // Searches though the surrounding mapblocks for monsters/players // distance is in MAPBLOCKUNITS //=========================================================================== mobj_t *P_RoughMonsterSearch(mobj_t * mo, int distance) { int blockX; int blockY; int startX, startY; int blockIndex; int firstStop; int secondStop; int thirdStop; int finalStop; int count; mobj_t *target; startX = (mo->x - bmaporgx) >> MAPBLOCKSHIFT; startY = (mo->y - bmaporgy) >> MAPBLOCKSHIFT; if (startX >= 0 && startX < bmapwidth && startY >= 0 && startY < bmapheight) { target = RoughBlockCheck(mo, startY * bmapwidth + startX); if (target != NULL) { // found a target right away return target; } } for (count = 1; count <= distance; count++) { blockX = startX - count; blockY = startY - count; if (blockY < 0) { blockY = 0; } else if (blockY >= bmapheight) { blockY = bmapheight - 1; } if (blockX < 0) { blockX = 0; } else if (blockX >= bmapwidth) { blockX = bmapwidth - 1; } blockIndex = blockY * bmapwidth + blockX; firstStop = startX + count; if (firstStop < 0) { continue; } if (firstStop >= bmapwidth) { firstStop = bmapwidth - 1; } secondStop = startY + count; if (secondStop < 0) { continue; } if (secondStop >= bmapheight) { secondStop = bmapheight - 1; } thirdStop = secondStop * bmapwidth + blockX; secondStop = secondStop * bmapwidth + firstStop; firstStop += blockY * bmapwidth; finalStop = blockIndex; // Trace the first block section (along the top) for (; blockIndex <= firstStop; blockIndex++) { target = RoughBlockCheck(mo, blockIndex); if (target != NULL) { return target; } } // Trace the second block section (right edge) for (blockIndex--; blockIndex <= secondStop; blockIndex += bmapwidth) { target = RoughBlockCheck(mo, blockIndex); if (target != NULL) { return target; } } // Trace the third block section (bottom edge) for (blockIndex -= bmapwidth; blockIndex >= thirdStop; blockIndex--) { target = RoughBlockCheck(mo, blockIndex); if (target != NULL) { return target; } } // Trace the final block section (left edge) for (blockIndex++; blockIndex > finalStop; blockIndex -= bmapwidth) { target = RoughBlockCheck(mo, blockIndex); if (target != NULL) { return target; } } } return NULL; } //=========================================================================== // // RoughBlockCheck // //=========================================================================== static mobj_t *RoughBlockCheck(mobj_t * mo, int index) { mobj_t *link; mobj_t *master; angle_t angle; link = blocklinks[index]; while (link) { if (mo->player) // Minotaur looking around player { if ((link->flags & MF_COUNTKILL) || (link->player && (link != mo))) { if (!(link->flags & MF_SHOOTABLE)) { link = link->bnext; continue; } if (link->flags2 & MF2_DORMANT) { link = link->bnext; continue; } if ((link->type == MT_MINOTAUR) && (link->special1.m == mo)) { link = link->bnext; continue; } if (netgame && !deathmatch && link->player) { link = link->bnext; continue; } if (P_CheckSight(mo, link)) { return link; } } link = link->bnext; } else if (mo->type == MT_MINOTAUR) // looking around minotaur { master = mo->special1.m; if ((link->flags & MF_COUNTKILL) || (link->player && (link != master))) { if (!(link->flags & MF_SHOOTABLE)) { link = link->bnext; continue; } if (link->flags2 & MF2_DORMANT) { link = link->bnext; continue; } if ((link->type == MT_MINOTAUR) && (link->special1.m == mo->special1.m)) { link = link->bnext; continue; } if (netgame && !deathmatch && link->player) { link = link->bnext; continue; } if (P_CheckSight(mo, link)) { return link; } } link = link->bnext; } else if (mo->type == MT_MSTAFF_FX2) // bloodscourge { if ((link->flags & MF_COUNTKILL || (link->player && link != mo->target)) && !(link->flags2 & MF2_DORMANT)) { if (!(link->flags & MF_SHOOTABLE)) { link = link->bnext; continue; } if (netgame && !deathmatch && link->player) { link = link->bnext; continue; } else if (P_CheckSight(mo, link)) { master = mo->target; angle = R_PointToAngle2(master->x, master->y, link->x, link->y) - master->angle; angle >>= 24; if (angle > 226 || angle < 30) { return link; } } } link = link->bnext; } else // spirits { if ((link->flags & MF_COUNTKILL || (link->player && link != mo->target)) && !(link->flags2 & MF2_DORMANT)) { if (!(link->flags & MF_SHOOTABLE)) { link = link->bnext; continue; } if (netgame && !deathmatch && link->player) { link = link->bnext; continue; } if (link == mo->target) { link = link->bnext; continue; } else if (P_CheckSight(mo, link)) { return link; } } link = link->bnext; } } return NULL; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_mobj.c000066400000000000000000002122741360717211000215540ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "m_random.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" #include "sounds.h" // MACROS ------------------------------------------------------------------ #define MAX_TID_COUNT 200 // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void G_PlayerReborn(int player); void P_MarkAsLeaving(mobj_t * corpse); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void P_SpawnMapThing(mapthing_t * mthing); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void PlayerLandedOnThing(mobj_t * mo, mobj_t * onmobj); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern mobj_t LavaInflictor; // PUBLIC DATA DEFINITIONS ------------------------------------------------- mobjtype_t PuffType; mobj_t *MissileMobj; fixed_t FloatBobOffsets[64] = { 0, 51389, 102283, 152192, 200636, 247147, 291278, 332604, 370727, 405280, 435929, 462380, 484378, 501712, 514213, 521763, 524287, 521763, 514213, 501712, 484378, 462380, 435929, 405280, 370727, 332604, 291278, 247147, 200636, 152192, 102283, 51389, -1, -51390, -102284, -152193, -200637, -247148, -291279, -332605, -370728, -405281, -435930, -462381, -484380, -501713, -514215, -521764, -524288, -521764, -514214, -501713, -484379, -462381, -435930, -405280, -370728, -332605, -291279, -247148, -200637, -152193, -102284, -51389 }; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int TIDList[MAX_TID_COUNT + 1]; // +1 for termination marker static mobj_t *TIDMobj[MAX_TID_COUNT]; // CODE -------------------------------------------------------------------- //========================================================================== // // P_SetMobjState // // Returns true if the mobj is still present. // //========================================================================== boolean P_SetMobjState(mobj_t * mobj, statenum_t state) { state_t *st; if (state == S_NULL) { // Remove mobj mobj->state = (state_t *) S_NULL; P_RemoveMobj(mobj); return (false); } st = &states[state]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; if (st->action) { // Call action function st->action(mobj); } return (true); } //========================================================================== // // P_SetMobjStateNF // // Same as P_SetMobjState, but does not call the state function. // //========================================================================== boolean P_SetMobjStateNF(mobj_t * mobj, statenum_t state) { state_t *st; if (state == S_NULL) { // Remove mobj mobj->state = (state_t *) S_NULL; P_RemoveMobj(mobj); return (false); } st = &states[state]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; return (true); } //---------------------------------------------------------------------------- // // PROC P_ExplodeMissile // //---------------------------------------------------------------------------- void P_ExplodeMissile(mobj_t * mo) { mo->momx = mo->momy = mo->momz = 0; P_SetMobjState(mo, mobjinfo[mo->type].deathstate); //mo->tics -= P_Random()&3; mo->flags &= ~MF_MISSILE; switch (mo->type) { case MT_SORCBALL1: case MT_SORCBALL2: case MT_SORCBALL3: S_StartSound(NULL, SFX_SORCERER_BIGBALLEXPLODE); break; case MT_SORCFX1: S_StartSound(NULL, SFX_SORCERER_HEADSCREAM); break; default: if (mo->info->deathsound) { S_StartSound(mo, mo->info->deathsound); } break; } } //---------------------------------------------------------------------------- // // PROC P_FloorBounceMissile // //---------------------------------------------------------------------------- void P_FloorBounceMissile(mobj_t * mo) { if (P_HitFloor(mo) >= FLOOR_LIQUID) { switch (mo->type) { case MT_SORCFX1: case MT_SORCBALL1: case MT_SORCBALL2: case MT_SORCBALL3: break; default: P_RemoveMobj(mo); return; } } switch (mo->type) { case MT_SORCFX1: mo->momz = -mo->momz; // no energy absorbed break; case MT_SGSHARD1: case MT_SGSHARD2: case MT_SGSHARD3: case MT_SGSHARD4: case MT_SGSHARD5: case MT_SGSHARD6: case MT_SGSHARD7: case MT_SGSHARD8: case MT_SGSHARD9: case MT_SGSHARD0: mo->momz = FixedMul(mo->momz, -0.3 * FRACUNIT); if (abs(mo->momz) < (FRACUNIT / 2)) { P_SetMobjState(mo, S_NULL); return; } break; default: mo->momz = FixedMul(mo->momz, -0.7 * FRACUNIT); break; } mo->momx = 2 * mo->momx / 3; mo->momy = 2 * mo->momy / 3; if (mo->info->seesound) { switch (mo->type) { case MT_SORCBALL1: case MT_SORCBALL2: case MT_SORCBALL3: if (!mo->args[0]) S_StartSound(mo, mo->info->seesound); break; default: S_StartSound(mo, mo->info->seesound); break; } S_StartSound(mo, mo->info->seesound); } // P_SetMobjState(mo, mobjinfo[mo->type].deathstate); } //---------------------------------------------------------------------------- // // PROC P_ThrustMobj // //---------------------------------------------------------------------------- void P_ThrustMobj(mobj_t * mo, angle_t angle, fixed_t move) { angle >>= ANGLETOFINESHIFT; mo->momx += FixedMul(move, finecosine[angle]); mo->momy += FixedMul(move, finesine[angle]); } //---------------------------------------------------------------------------- // // FUNC P_FaceMobj // // Returns 1 if 'source' needs to turn clockwise, or 0 if 'source' needs // to turn counter clockwise. 'delta' is set to the amount 'source' // needs to turn. // //---------------------------------------------------------------------------- int P_FaceMobj(mobj_t * source, mobj_t * target, angle_t * delta) { angle_t diff; angle_t angle1; angle_t angle2; angle1 = source->angle; angle2 = R_PointToAngle2(source->x, source->y, target->x, target->y); if (angle2 > angle1) { diff = angle2 - angle1; if (diff > ANG180) { *delta = ANG_MAX - diff; return (0); } else { *delta = diff; return (1); } } else { diff = angle1 - angle2; if (diff > ANG180) { *delta = ANG_MAX - diff; return (1); } else { *delta = diff; return (0); } } } //---------------------------------------------------------------------------- // // // The missile special1 field must be mobj_t *target. Returns true if // target was tracked, false if not. // //---------------------------------------------------------------------------- boolean P_SeekerMissile(mobj_t * actor, angle_t thresh, angle_t turnMax) { int dir; int dist; angle_t delta; angle_t angle; mobj_t *target; target = actor->special1.m; if (target == NULL) { return (false); } if (!(target->flags & MF_SHOOTABLE)) { // Target died actor->special1.m = NULL; return (false); } dir = P_FaceMobj(actor, target, &delta); if (delta > thresh) { delta >>= 1; if (delta > turnMax) { delta = turnMax; } } if (dir) { // Turn clockwise actor->angle += delta; } else { // Turn counter clockwise actor->angle -= delta; } angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(actor->info->speed, finecosine[angle]); actor->momy = FixedMul(actor->info->speed, finesine[angle]); if (actor->z + actor->height < target->z || target->z + target->height < actor->z) { // Need to seek vertically dist = P_AproxDistance(target->x - actor->x, target->y - actor->y); dist = dist / actor->info->speed; if (dist < 1) { dist = 1; } actor->momz = (target->z + (target->height >> 1) - (actor->z + (actor->height >> 1))) / dist; } return (true); } //---------------------------------------------------------------------------- // // PROC P_XYMovement // //---------------------------------------------------------------------------- #define STOPSPEED 0x1000 #define FRICTION_NORMAL 0xe800 #define FRICTION_LOW 0xf900 #define FRICTION_FLY 0xeb00 void P_XYMovement(mobj_t * mo) { fixed_t ptryx, ptryy; player_t *player; fixed_t xmove, ymove; int special; angle_t angle; static int windTab[3] = { 2048 * 5, 2048 * 10, 2048 * 25 }; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // A flying mobj slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState(mo, mo->info->seestate); } return; } special = mo->subsector->sector->special; if (mo->flags2 & MF2_WINDTHRUST) { switch (special) { case 40: case 41: case 42: // Wind_East P_ThrustMobj(mo, 0, windTab[special - 40]); break; case 43: case 44: case 45: // Wind_North P_ThrustMobj(mo, ANG90, windTab[special - 43]); break; case 46: case 47: case 48: // Wind_South P_ThrustMobj(mo, ANG270, windTab[special - 46]); break; case 49: case 50: case 51: // Wind_West P_ThrustMobj(mo, ANG180, windTab[special - 49]); break; } } player = mo->player; if (mo->momx > MAXMOVE) { mo->momx = MAXMOVE; } else if (mo->momx < -MAXMOVE) { mo->momx = -MAXMOVE; } if (mo->momy > MAXMOVE) { mo->momy = MAXMOVE; } else if (mo->momy < -MAXMOVE) { mo->momy = -MAXMOVE; } xmove = mo->momx; ymove = mo->momy; do { if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2) { ptryx = mo->x + xmove / 2; ptryy = mo->y + ymove / 2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } if (!P_TryMove(mo, ptryx, ptryy)) { // Blocked move if (mo->flags2 & MF2_SLIDE) { // Try to slide along it if (BlockingMobj == NULL) { // Slide against wall P_SlideMove(mo); } else { // Slide against mobj //if(P_TryMove(mo, mo->x, mo->y+mo->momy)) if (P_TryMove(mo, mo->x, ptryy)) { mo->momx = 0; } //else if(P_TryMove(mo, mo->x+mo->momx, mo->y)) else if (P_TryMove(mo, ptryx, mo->y)) { mo->momy = 0; } else { mo->momx = mo->momy = 0; } } } else if (mo->flags & MF_MISSILE) { if (mo->flags2 & MF2_FLOORBOUNCE) { if (BlockingMobj) { if ((BlockingMobj->flags2 & MF2_REFLECTIVE) || ((!BlockingMobj->player) && (!(BlockingMobj->flags & MF_COUNTKILL)))) { fixed_t speed; angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y) + ANG1 * ((P_Random() % 16) - 8); speed = P_AproxDistance(mo->momx, mo->momy); speed = FixedMul(speed, 0.75 * FRACUNIT); mo->angle = angle; angle >>= ANGLETOFINESHIFT; mo->momx = FixedMul(speed, finecosine[angle]); mo->momy = FixedMul(speed, finesine[angle]); if (mo->info->seesound) { S_StartSound(mo, mo->info->seesound); } return; } else { // Struck a player/creature P_ExplodeMissile(mo); } } else { // Struck a wall P_BounceWall(mo); switch (mo->type) { case MT_SORCBALL1: case MT_SORCBALL2: case MT_SORCBALL3: case MT_SORCFX1: break; default: if (mo->info->seesound) { S_StartSound(mo, mo->info->seesound); } break; } return; } } if (BlockingMobj && (BlockingMobj->flags2 & MF2_REFLECTIVE)) { angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); // Change angle for delflection/reflection switch (BlockingMobj->type) { case MT_CENTAUR: case MT_CENTAURLEADER: if (abs(angle - BlockingMobj->angle) >> 24 > 45) goto explode; if (mo->type == MT_HOLY_FX) goto explode; // Drop through to sorcerer full reflection case MT_SORCBOSS: // Deflection if (P_Random() < 128) angle += ANG45; else angle -= ANG45; break; default: // Reflection angle += ANG1 * ((P_Random() % 16) - 8); break; } // Reflect the missile along angle mo->angle = angle; angle >>= ANGLETOFINESHIFT; mo->momx = FixedMul(mo->info->speed >> 1, finecosine[angle]); mo->momy = FixedMul(mo->info->speed >> 1, finesine[angle]); // mo->momz = -mo->momz; if (mo->flags2 & MF2_SEEKERMISSILE) { mo->special1.m = mo->target; } mo->target = BlockingMobj; return; } explode: // Explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) { // Hack to prevent missiles exploding against the sky if (mo->type == MT_BLOODYSKULL) { mo->momx = mo->momy = 0; mo->momz = -FRACUNIT; } else if (mo->type == MT_HOLY_FX) { P_ExplodeMissile(mo); } else { P_RemoveMobj(mo); } return; } P_ExplodeMissile(mo); } //else if(mo->info->crashstate) //{ // mo->momx = mo->momy = 0; // P_SetMobjState(mo, mo->info->crashstate); // return; //} else { mo->momx = mo->momy = 0; } } } while (xmove || ymove); // Friction if (player && player->cheats & CF_NOMOMENTUM) { // Debug option for no sliding at all mo->momx = mo->momy = 0; return; } if (mo->flags & (MF_MISSILE | MF_SKULLFLY)) { // No friction for missiles return; } if (mo->z > mo->floorz && !(mo->flags2 & MF2_FLY) && !(mo->flags2 & MF2_ONMOBJ)) { // No friction when falling if (mo->type != MT_BLASTEFFECT) return; } if (mo->flags & MF_CORPSE) { // Don't stop sliding if halfway off a step with some momentum if (mo->momx > FRACUNIT / 4 || mo->momx < -FRACUNIT / 4 || mo->momy > FRACUNIT / 4 || mo->momy < -FRACUNIT / 4) { if (mo->floorz != mo->subsector->sector->floorheight) { return; } } } if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && mo->momy > -STOPSPEED && mo->momy < STOPSPEED && (!player || (player->cmd.forwardmove == 0 && player->cmd.sidemove == 0))) { // If in a walking frame, stop moving if (player) { if ((unsigned) ((player->mo->state - states) - PStateRun[player->class]) < 4) { P_SetMobjState(player->mo, PStateNormal[player->class]); } } mo->momx = 0; mo->momy = 0; } else { if (mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz) && !(mo->flags2 & MF2_ONMOBJ)) { mo->momx = FixedMul(mo->momx, FRICTION_FLY); mo->momy = FixedMul(mo->momy, FRICTION_FLY); } else if (P_GetThingFloorType(mo) == FLOOR_ICE) { mo->momx = FixedMul(mo->momx, FRICTION_LOW); mo->momy = FixedMul(mo->momy, FRICTION_LOW); } else { mo->momx = FixedMul(mo->momx, FRICTION_NORMAL); mo->momy = FixedMul(mo->momy, FRICTION_NORMAL); } } } // Move this to p_inter *** void P_MonsterFallingDamage(mobj_t * mo) { int damage; int mom; mom = abs(mo->momz); if (mom > 35 * FRACUNIT) { // automatic death damage = 10000; } else { damage = ((mom - (23 * FRACUNIT)) * 6) >> FRACBITS; } damage = 10000; // always kill 'em P_DamageMobj(mo, NULL, NULL, damage); } /* =============== = = P_ZMovement = =============== */ void P_ZMovement(mobj_t * mo) { int dist; int delta; // // check for smooth step up // if (mo->player && mo->z < mo->floorz) { mo->player->viewheight -= mo->floorz - mo->z; mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight) >> 3; } // // adjust height // mo->z += mo->momz; if (mo->flags & MF_FLOAT && mo->target) { // float down towards target if too close if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT)) { dist = P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y); delta = (mo->target->z + (mo->height >> 1)) - mo->z; if (delta < 0 && dist < -(delta * 3)) mo->z -= FLOATSPEED; else if (delta > 0 && dist < (delta * 3)) mo->z += FLOATSPEED; } } if (mo->player && mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz) && leveltime & 2) { mo->z += finesine[(FINEANGLES / 20 * leveltime >> 2) & FINEMASK]; } // // clip movement // if (mo->z <= mo->floorz) { // Hit the floor if (mo->flags & MF_MISSILE) { mo->z = mo->floorz; if (mo->flags2 & MF2_FLOORBOUNCE) { P_FloorBounceMissile(mo); return; } else if (mo->type == MT_HOLY_FX) { // The spirit struck the ground mo->momz = 0; P_HitFloor(mo); return; } else if (mo->type == MT_MNTRFX2 || mo->type == MT_LIGHTNING_FLOOR) { // Minotaur floor fire can go up steps return; } else { P_HitFloor(mo); P_ExplodeMissile(mo); return; } } if (mo->flags & MF_COUNTKILL) // Blasted mobj falling { if (mo->momz < -(23 * FRACUNIT)) { P_MonsterFallingDamage(mo); } } if (mo->z - mo->momz > mo->floorz) { // Spawn splashes, etc. P_HitFloor(mo); } mo->z = mo->floorz; if (mo->momz < 0) { if (mo->flags2 & MF2_ICEDAMAGE && mo->momz < -GRAVITY * 8) { mo->tics = 1; mo->momx = 0; mo->momy = 0; mo->momz = 0; return; } if (mo->player) { mo->player->jumpTics = 7; // delay any jumping for a short time if (mo->momz < -GRAVITY * 8 && !(mo->flags2 & MF2_FLY)) { // squat down mo->player->deltaviewheight = mo->momz >> 3; if (mo->momz < -23 * FRACUNIT) { P_FallingDamage(mo->player); P_NoiseAlert(mo, mo); } else if (mo->momz < -GRAVITY * 12 && !mo->player->morphTics) { S_StartSound(mo, SFX_PLAYER_LAND); switch (mo->player->class) { case PCLASS_FIGHTER: S_StartSound(mo, SFX_PLAYER_FIGHTER_GRUNT); break; case PCLASS_CLERIC: S_StartSound(mo, SFX_PLAYER_CLERIC_GRUNT); break; case PCLASS_MAGE: S_StartSound(mo, SFX_PLAYER_MAGE_GRUNT); break; default: break; } } else if ((P_GetThingFloorType(mo) < FLOOR_LIQUID) && (!mo->player->morphTics)) { S_StartSound(mo, SFX_PLAYER_LAND); } // haleyjd: removed externdriver crap mo->player->centering = true; } } else if (mo->type >= MT_POTTERY1 && mo->type <= MT_POTTERY3) { P_DamageMobj(mo, NULL, NULL, 25); } else if (mo->flags & MF_COUNTKILL) { if (mo->momz < -23 * FRACUNIT) { // Doesn't get here } } mo->momz = 0; } if (mo->flags & MF_SKULLFLY) { // The skull slammed into something mo->momz = -mo->momz; } if (mo->info->crashstate && (mo->flags & MF_CORPSE) && !(mo->flags2 & MF2_ICEDAMAGE)) { P_SetMobjState(mo, mo->info->crashstate); return; } } else if (mo->flags2 & MF2_LOGRAV) { if (mo->momz == 0) mo->momz = -(GRAVITY >> 3) * 2; else mo->momz -= GRAVITY >> 3; } else if (!(mo->flags & MF_NOGRAVITY)) { if (mo->momz == 0) mo->momz = -GRAVITY * 2; else mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // hit the ceiling if (mo->momz > 0) mo->momz = 0; mo->z = mo->ceilingz - mo->height; if (mo->flags2 & MF2_FLOORBOUNCE) { // Maybe reverse momentum here for ceiling bounce // Currently won't happen if (mo->info->seesound) { S_StartSound(mo, mo->info->seesound); } return; } if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } if (mo->flags & MF_MISSILE) { if (mo->type == MT_LIGHTNING_CEILING) { return; } if (mo->subsector->sector->ceilingpic == skyflatnum) { if (mo->type == MT_BLOODYSKULL) { mo->momx = mo->momy = 0; mo->momz = -FRACUNIT; } else if (mo->type == MT_HOLY_FX) { P_ExplodeMissile(mo); } else { P_RemoveMobj(mo); } return; } P_ExplodeMissile(mo); return; } } } //---------------------------------------------------------------------------- // // PROC P_BlasterMobjThinker // // //---------------------------------------------------------------------------- void P_BlasterMobjThinker(mobj_t * mobj) { int i; fixed_t xfrac; fixed_t yfrac; fixed_t zfrac; fixed_t z; boolean changexy; mobj_t *mo; // Handle movement if (mobj->momx || mobj->momy || (mobj->z != mobj->floorz) || mobj->momz) { xfrac = mobj->momx >> 3; yfrac = mobj->momy >> 3; zfrac = mobj->momz >> 3; changexy = xfrac || yfrac; for (i = 0; i < 8; i++) { if (changexy) { if (!P_TryMove(mobj, mobj->x + xfrac, mobj->y + yfrac)) { // Blocked move P_ExplodeMissile(mobj); return; } } mobj->z += zfrac; if (mobj->z <= mobj->floorz) { // Hit the floor mobj->z = mobj->floorz; P_HitFloor(mobj); P_ExplodeMissile(mobj); return; } if (mobj->z + mobj->height > mobj->ceilingz) { // Hit the ceiling mobj->z = mobj->ceilingz - mobj->height; P_ExplodeMissile(mobj); return; } if (changexy) { if (mobj->type == MT_MWAND_MISSILE && (P_Random() < 128)) { z = mobj->z - 8 * FRACUNIT; if (z < mobj->floorz) { z = mobj->floorz; } P_SpawnMobj(mobj->x, mobj->y, z, MT_MWANDSMOKE); } else if (!--mobj->special1.i) { mobj->special1.i = 4; z = mobj->z - 12 * FRACUNIT; if (z < mobj->floorz) { z = mobj->floorz; } mo = P_SpawnMobj(mobj->x, mobj->y, z, MT_CFLAMEFLOOR); if (mo) { mo->angle = mobj->angle; } } } } } // Advance the state if (mobj->tics != -1) { mobj->tics--; while (!mobj->tics) { if (!P_SetMobjState(mobj, mobj->state->nextstate)) { // mobj was removed return; } } } } //=========================================================================== // // PlayerLandedOnThing // //=========================================================================== static void PlayerLandedOnThing(mobj_t * mo, mobj_t * onmobj) { mo->player->deltaviewheight = mo->momz >> 3; if (mo->momz < -23 * FRACUNIT) { P_FallingDamage(mo->player); P_NoiseAlert(mo, mo); } else if (mo->momz < -GRAVITY * 12 && !mo->player->morphTics) { S_StartSound(mo, SFX_PLAYER_LAND); switch (mo->player->class) { case PCLASS_FIGHTER: S_StartSound(mo, SFX_PLAYER_FIGHTER_GRUNT); break; case PCLASS_CLERIC: S_StartSound(mo, SFX_PLAYER_CLERIC_GRUNT); break; case PCLASS_MAGE: S_StartSound(mo, SFX_PLAYER_MAGE_GRUNT); break; default: break; } } else if (!mo->player->morphTics) { S_StartSound(mo, SFX_PLAYER_LAND); } // haleyjd: removed externdriver crap mo->player->centering = true; } //---------------------------------------------------------------------------- // // PROC P_MobjThinker // //---------------------------------------------------------------------------- void P_MobjThinker(mobj_t * mobj) { mobj_t *onmo; /* // Reset to not blasted when momentums are gone if((mobj->flags2&MF2_BLASTED) && (!(mobj->momx)) && (!(mobj->momy))) ResetBlasted(mobj); */ // Handle X and Y momentums BlockingMobj = NULL; if (mobj->momx || mobj->momy || (mobj->flags & MF_SKULLFLY)) { P_XYMovement(mobj); if (mobj->thinker.function == (think_t) - 1) { // mobj was removed return; } } else if (mobj->flags2 & MF2_BLASTED) { // Reset to not blasted when momentums are gone ResetBlasted(mobj); } if (mobj->flags2 & MF2_FLOATBOB) { // Floating item bobbing motion (special1 is height) mobj->z = mobj->floorz + mobj->special1.i + FloatBobOffsets[(mobj->health++) & 63]; } else if ((mobj->z != mobj->floorz) || mobj->momz || BlockingMobj) { // Handle Z momentum and gravity if (mobj->flags2 & MF2_PASSMOBJ) { if (!(onmo = P_CheckOnmobj(mobj))) { P_ZMovement(mobj); if (mobj->player && mobj->flags & MF2_ONMOBJ) { mobj->flags2 &= ~MF2_ONMOBJ; } } else { if (mobj->player) { if (mobj->momz < -GRAVITY * 8 && !(mobj->flags2 & MF2_FLY)) { PlayerLandedOnThing(mobj, onmo); } if (onmo->z + onmo->height - mobj->z <= 24 * FRACUNIT) { mobj->player->viewheight -= onmo->z + onmo->height - mobj->z; mobj->player->deltaviewheight = (VIEWHEIGHT - mobj->player->viewheight) >> 3; mobj->z = onmo->z + onmo->height; mobj->flags2 |= MF2_ONMOBJ; mobj->momz = 0; } else { // hit the bottom of the blocking mobj mobj->momz = 0; } } /* Landing on another player, and mimicking his movements if(mobj->player && onmo->player) { mobj->momx = onmo->momx; mobj->momy = onmo->momy; if(onmo->z < onmo->floorz) { mobj->z += onmo->floorz-onmo->z; if(onmo->player) { onmo->player->viewheight -= onmo->floorz-onmo->z; onmo->player->deltaviewheight = (VIEWHEIGHT- onmo->player->viewheight)>>3; } onmo->z = onmo->floorz; } } */ } } else { P_ZMovement(mobj); } if (mobj->thinker.function == (think_t) - 1) { // mobj was removed return; } } // Cycle through states, calling action functions at transitions if (mobj->tics != -1) { mobj->tics--; // you can cycle through multiple states in a tic while (!mobj->tics) { if (!P_SetMobjState(mobj, mobj->state->nextstate)) { // mobj was removed return; } } } } //========================================================================== // // 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; fixed_t space; mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, NULL); memset(mobj, 0, sizeof(*mobj)); info = &mobjinfo[type]; mobj->type = type; mobj->info = info; mobj->x = x; mobj->y = y; mobj->radius = info->radius; mobj->height = info->height; mobj->flags = info->flags; mobj->flags2 = info->flags2; mobj->damage = info->damage; mobj->health = info->spawnhealth; if (gameskill != sk_nightmare) { mobj->reactiontime = info->reactiontime; } mobj->lastlook = P_Random() % maxplayers; // Set the state, but do not use P_SetMobjState, because action // routines can't be called yet. If the spawnstate has an action // routine, it will not be called. st = &states[info->spawnstate]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // Set subsector and/or block links. P_SetThingPosition(mobj); mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; if (z == ONFLOORZ) { mobj->z = mobj->floorz; } else if (z == ONCEILINGZ) { mobj->z = mobj->ceilingz - mobj->info->height; } else if (z == FLOATRANDZ) { space = ((mobj->ceilingz) - (mobj->info->height)) - mobj->floorz; if (space > 48 * FRACUNIT) { space -= 40 * FRACUNIT; mobj->z = ((space * P_Random()) >> 8) + mobj->floorz + 40 * FRACUNIT; } else { mobj->z = mobj->floorz; } } else if (mobj->flags2 & MF2_FLOATBOB) { mobj->z = mobj->floorz + z; // artifact z passed in as height } else { mobj->z = z; } if (mobj->flags2 & MF2_FLOORCLIP && P_GetThingFloorType(mobj) >= FLOOR_LIQUID && mobj->z == mobj->subsector->sector->floorheight) { mobj->floorclip = 10 * FRACUNIT; } else { mobj->floorclip = 0; } mobj->thinker.function = P_MobjThinker; P_AddThinker(&mobj->thinker); return (mobj); } //========================================================================== // // P_RemoveMobj // //========================================================================== void P_RemoveMobj(mobj_t * mobj) { // Remove from creature queue if (mobj->flags & MF_COUNTKILL && mobj->flags & MF_CORPSE) { A_DeQueueCorpse(mobj); } if (mobj->tid) { // Remove from TID list P_RemoveMobjFromTIDList(mobj); } // Unlink from sector and block lists P_UnsetThingPosition(mobj); // Stop any playing sound S_StopSound(mobj); // Free block P_RemoveThinker((thinker_t *) mobj); } //========================================================================== // // P_SpawnPlayer // // Called when a player is spawned on the level. Most of the player // structure stays unchanged between levels. // //========================================================================== void P_SpawnPlayer(mapthing_t * mthing) { player_t *p; fixed_t x, y, z; mobj_t *mobj; if (mthing->type - 1 >= maxplayers || !playeringame[mthing->type - 1]) { // Not playing return; } p = &players[mthing->type - 1]; if (p->playerstate == PST_REBORN) { G_PlayerReborn(mthing->type - 1); } x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; z = ONFLOORZ; if (randomclass && deathmatch) { p->class = P_Random() % 3; if (p->class == PlayerClass[mthing->type - 1]) { p->class = (p->class + 1) % 3; } PlayerClass[mthing->type - 1] = p->class; SB_SetClassData(); } else { p->class = PlayerClass[mthing->type - 1]; } switch (p->class) { case PCLASS_FIGHTER: mobj = P_SpawnMobj(x, y, z, MT_PLAYER_FIGHTER); break; case PCLASS_CLERIC: mobj = P_SpawnMobj(x, y, z, MT_PLAYER_CLERIC); break; case PCLASS_MAGE: mobj = P_SpawnMobj(x, y, z, MT_PLAYER_MAGE); break; default: I_Error("P_SpawnPlayer: Unknown class type"); return; } // Set translation table data if (p->class == PCLASS_FIGHTER && (mthing->type == 1 || mthing->type == 3)) { // The first type should be blue, and the third should be the // Fighter's original gold color if (mthing->type == 1) { mobj->flags |= 2 << MF_TRANSSHIFT; } } else if (mthing->type > 1) { // Set color translation bits for player sprites mobj->flags |= (mthing->type - 1) << MF_TRANSSHIFT; } mobj->angle = ANG45 * (mthing->angle / 45); mobj->player = p; mobj->health = p->health; p->mo = mobj; p->playerstate = PST_LIVE; p->refire = 0; P_ClearMessage(p); p->damagecount = 0; p->bonuscount = 0; p->poisoncount = 0; p->morphTics = 0; p->extralight = 0; p->fixedcolormap = 0; p->viewheight = VIEWHEIGHT; P_SetupPsprites(p); if (deathmatch) { // Give all keys in death match mode p->keys = 2047; } } //========================================================================== // // P_SpawnMapThing // // The fields of the mapthing should already be in host byte order. // //========================================================================== void P_SpawnMapThing(mapthing_t * mthing) { int i; unsigned int spawnMask; mobj_t *mobj; fixed_t x, y, z; static unsigned int classFlags[] = { MTF_FIGHTER, MTF_CLERIC, MTF_MAGE }; // Count deathmatch start positions if (mthing->type == 11) { if (deathmatch_p < &deathmatchstarts[MAXDEATHMATCHSTARTS]) { memcpy(deathmatch_p, mthing, sizeof(*mthing)); deathmatch_p++; } return; } if (mthing->type == PO_ANCHOR_TYPE) { // Polyobj Anchor Pt. return; } else if (mthing->type == PO_SPAWN_TYPE || mthing->type == PO_SPAWNCRUSH_TYPE) { // Polyobj Anchor Pt. po_NumPolyobjs++; return; } // Check for player starts 1 to 4 if (mthing->type <= 4) { playerstarts[mthing->arg1][mthing->type - 1] = *mthing; if (!deathmatch && !mthing->arg1) { P_SpawnPlayer(mthing); } return; } // Check for player starts 5 to 8 if (mthing->type >= 9100 && mthing->type <= 9103) { mapthing_t *player_start; int player; player = 4 + mthing->type - 9100; player_start = &playerstarts[mthing->arg1][player]; memcpy(player_start, mthing, sizeof(mapthing_t)); player_start->type = player + 1; if (!deathmatch && !player_start->arg1) { P_SpawnPlayer(player_start); } return; } if (mthing->type >= 1400 && mthing->type < 1410) { R_PointInSubsector(mthing->x << FRACBITS, mthing->y << FRACBITS)->sector->seqType = mthing->type - 1400; return; } // Check current game type with spawn flags if (netgame == false) { spawnMask = MTF_GSINGLE; } else if (deathmatch) { spawnMask = MTF_GDEATHMATCH; } else { spawnMask = MTF_GCOOP; } if (!(mthing->options & spawnMask)) { return; } // Check current skill with spawn flags if (gameskill == sk_baby || gameskill == sk_easy) { spawnMask = MTF_EASY; } else if (gameskill == sk_hard || gameskill == sk_nightmare) { spawnMask = MTF_HARD; } else { spawnMask = MTF_NORMAL; } if (!(mthing->options & spawnMask)) { return; } // Check current character classes with spawn flags if (netgame == false) { // Single player if ((mthing->options & classFlags[PlayerClass[0]]) == 0) { // Not for current class return; } } else if (deathmatch == false) { // Cooperative spawnMask = 0; for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { spawnMask |= classFlags[PlayerClass[i]]; } } if ((mthing->options & spawnMask) == 0) { return; } } // Find which type to spawn for (i = 0; i < NUMMOBJTYPES; i++) { if (mthing->type == mobjinfo[i].doomednum) { break; } } if (i == NUMMOBJTYPES) { // Can't find thing type I_Error("P_SpawnMapThing: Unknown type %i at (%i, %i)", mthing->type, mthing->x, mthing->y); } // Don't spawn keys and players in deathmatch if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) { return; } // Don't spawn monsters if -nomonsters if (nomonsters && (mobjinfo[i].flags & MF_COUNTKILL)) { return; } x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; if (mobjinfo[i].flags & MF_SPAWNCEILING) { z = ONCEILINGZ; } else if (mobjinfo[i].flags2 & MF2_SPAWNFLOAT) { z = FLOATRANDZ; } else if (mobjinfo[i].flags2 & MF2_FLOATBOB) { z = mthing->height << FRACBITS; } else { z = ONFLOORZ; } switch (i) { // Special stuff case MT_ZLYNCHED_NOHEART: P_SpawnMobj(x, y, ONFLOORZ, MT_BLOODPOOL); break; default: break; } mobj = P_SpawnMobj(x, y, z, i); if (z == ONFLOORZ) { mobj->z += mthing->height << FRACBITS; } else if (z == ONCEILINGZ) { mobj->z -= mthing->height << FRACBITS; } mobj->tid = mthing->tid; mobj->special = mthing->special; mobj->args[0] = mthing->arg1; mobj->args[1] = mthing->arg2; mobj->args[2] = mthing->arg3; mobj->args[3] = mthing->arg4; mobj->args[4] = mthing->arg5; if (mobj->flags2 & MF2_FLOATBOB) { // Seed random starting index for bobbing motion mobj->health = P_Random(); mobj->special1.i = mthing->height << FRACBITS; } if (mobj->tics > 0) { mobj->tics = 1 + (P_Random() % mobj->tics); } // if(mobj->flags&MF_COUNTITEM) // { // totalitems++; // } if (mobj->flags & MF_COUNTKILL) { // Quantize angle to 45 degree increments mobj->angle = ANG45 * (mthing->angle / 45); } else { // Scale angle correctly (source is 0..359) mobj->angle = ((mthing->angle << 8) / 360) << 24; } if (mthing->options & MTF_AMBUSH) { mobj->flags |= MF_AMBUSH; } if (mthing->options & MTF_DORMANT) { mobj->flags2 |= MF2_DORMANT; if (mobj->type == MT_ICEGUY) { P_SetMobjState(mobj, S_ICEGUY_DORMANT); } mobj->tics = -1; } } //========================================================================== // // P_CreateTIDList // //========================================================================== void P_CreateTIDList(void) { int i; mobj_t *mobj; thinker_t *t; i = 0; for (t = thinkercap.next; t != &thinkercap; t = t->next) { // Search all current thinkers if (t->function != P_MobjThinker) { // Not a mobj thinker continue; } mobj = (mobj_t *) t; if (mobj->tid != 0) { // Add to list if (i == MAX_TID_COUNT) { I_Error("P_CreateTIDList: MAX_TID_COUNT (%d) exceeded.", MAX_TID_COUNT); } TIDList[i] = mobj->tid; TIDMobj[i++] = mobj; } } // Add termination marker TIDList[i] = 0; } //========================================================================== // // P_InsertMobjIntoTIDList // //========================================================================== void P_InsertMobjIntoTIDList(mobj_t * mobj, int tid) { int i; int index; index = -1; for (i = 0; TIDList[i] != 0; i++) { if (TIDList[i] == -1) { // Found empty slot index = i; break; } } if (index == -1) { // Append required if (i == MAX_TID_COUNT) { I_Error("P_InsertMobjIntoTIDList: MAX_TID_COUNT (%d)" "exceeded.", MAX_TID_COUNT); } index = i; TIDList[index + 1] = 0; } mobj->tid = tid; TIDList[index] = tid; TIDMobj[index] = mobj; } //========================================================================== // // P_RemoveMobjFromTIDList // //========================================================================== void P_RemoveMobjFromTIDList(mobj_t * mobj) { int i; for (i = 0; TIDList[i] != 0; i++) { if (TIDMobj[i] == mobj) { TIDList[i] = -1; TIDMobj[i] = NULL; mobj->tid = 0; return; } } mobj->tid = 0; } //========================================================================== // // P_FindMobjFromTID // //========================================================================== mobj_t *P_FindMobjFromTID(int tid, int *searchPosition) { int i; for (i = *searchPosition + 1; TIDList[i] != 0; i++) { if (TIDList[i] == tid) { *searchPosition = i; return TIDMobj[i]; } } *searchPosition = -1; return NULL; } /* =============================================================================== GAME SPAWN FUNCTIONS =============================================================================== */ //--------------------------------------------------------------------------- // // PROC P_SpawnPuff // //--------------------------------------------------------------------------- extern fixed_t attackrange; void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z) { mobj_t *puff; z += (P_SubRandom() << 10); puff = P_SpawnMobj(x, y, z, PuffType); if (linetarget && puff->info->seesound) { // Hit thing sound S_StartSound(puff, puff->info->seesound); } else if (puff->info->attacksound) { S_StartSound(puff, puff->info->attacksound); } switch (PuffType) { case MT_PUNCHPUFF: puff->momz = FRACUNIT; break; case MT_HAMMERPUFF: puff->momz = .8 * FRACUNIT; break; default: break; } PuffSpawned = puff; } /* ================ = = P_SpawnBlood = ================ */ /* void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage) { mobj_t *th; z += (P_SubRandom()<<10); th = P_SpawnMobj (x,y,z, MT_BLOOD); th->momz = FRACUNIT*2; th->tics -= P_Random()&3; if (damage <= 12 && damage >= 9) P_SetMobjState (th,S_BLOOD2); else if (damage < 9) P_SetMobjState (th,S_BLOOD3); } */ //--------------------------------------------------------------------------- // // PROC P_BloodSplatter // //--------------------------------------------------------------------------- void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator) { mobj_t *mo; mo = P_SpawnMobj(x, y, z, MT_BLOODSPLATTER); mo->target = originator; mo->momx = P_SubRandom() << 10; mo->momy = P_SubRandom() << 10; mo->momz = 3 * FRACUNIT; } //=========================================================================== // // P_BloodSplatter2 // //=========================================================================== void P_BloodSplatter2(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator) { mobj_t *mo; int r1, r2; r1 = P_Random(); r2 = P_Random(); mo = P_SpawnMobj(x + ((r2 - 128) << 11), y + ((r1 - 128) << 11), z, MT_AXEBLOOD); mo->target = originator; } //--------------------------------------------------------------------------- // // PROC P_RipperBlood // //--------------------------------------------------------------------------- void P_RipperBlood(mobj_t * mo) { mobj_t *th; fixed_t x, y, z; x = mo->x + (P_SubRandom() << 12); y = mo->y + (P_SubRandom() << 12); z = mo->z + (P_SubRandom() << 12); th = P_SpawnMobj(x, y, z, MT_BLOOD); // th->flags |= MF_NOGRAVITY; th->momx = mo->momx >> 1; th->momy = mo->momy >> 1; th->tics += P_Random() & 3; } //--------------------------------------------------------------------------- // // FUNC P_GetThingFloorType // //--------------------------------------------------------------------------- int P_GetThingFloorType(mobj_t * thing) { if (thing->floorpic) { return (TerrainTypes[thing->floorpic]); } else { return (TerrainTypes[thing->subsector->sector->floorpic]); } /* if(thing->subsector->sector->floorpic == W_GetNumForName("FLTWAWA1")-firstflat) { return(FLOOR_WATER); } else { return(FLOOR_SOLID); } */ } //--------------------------------------------------------------------------- // // FUNC P_HitFloor // //--------------------------------------------------------------------------- #define SMALLSPLASHCLIP 12<floorz != thing->subsector->sector->floorheight) { // don't splash if landing on the edge above water/lava/etc.... return (FLOOR_SOLID); } // Things that don't splash go here switch (thing->type) { case MT_LEAF1: case MT_LEAF2: // case MT_BLOOD: // I set these to low mass -- pm // case MT_BLOODSPLATTER: case MT_SPLASH: case MT_SLUDGECHUNK: return (FLOOR_SOLID); default: break; } // Small splash for small masses if (thing->info->mass < 10) smallsplash = true; switch (P_GetThingFloorType(thing)) { case FLOOR_WATER: if (smallsplash) { mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE); if (mo) mo->floorclip += SMALLSPLASHCLIP; S_StartSound(mo, SFX_AMBIENT10); // small drip } else { mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASH); mo->target = thing; mo->momx = P_SubRandom() << 8; mo->momy = P_SubRandom() << 8; mo->momz = 2 * FRACUNIT + (P_Random() << 8); mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE); if (thing->player) P_NoiseAlert(thing, thing); S_StartSound(mo, SFX_WATER_SPLASH); } return (FLOOR_WATER); case FLOOR_LAVA: if (smallsplash) { mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH); if (mo) mo->floorclip += SMALLSPLASHCLIP; } else { mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASMOKE); mo->momz = FRACUNIT + (P_Random() << 7); mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH); if (thing->player) P_NoiseAlert(thing, thing); } S_StartSound(mo, SFX_LAVA_SIZZLE); if (thing->player && leveltime & 31) { P_DamageMobj(thing, &LavaInflictor, NULL, 5); } return (FLOOR_LAVA); case FLOOR_SLUDGE: if (smallsplash) { mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGESPLASH); if (mo) mo->floorclip += SMALLSPLASHCLIP; } else { mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGECHUNK); mo->target = thing; mo->momx = P_SubRandom() << 8; mo->momy = P_SubRandom() << 8; mo->momz = FRACUNIT + (P_Random() << 8); mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGESPLASH); if (thing->player) P_NoiseAlert(thing, thing); } S_StartSound(mo, SFX_SLUDGE_GLOOP); return (FLOOR_SLUDGE); } return (FLOOR_SOLID); } //--------------------------------------------------------------------------- // // FUNC P_CheckMissileSpawn // // Returns true if the missile is at a valid spawn point, otherwise // explodes it and returns false. // //--------------------------------------------------------------------------- boolean P_CheckMissileSpawn(mobj_t * missile) { //missile->tics -= P_Random()&3; // move a little forward so an angle can be computed if it // immediately explodes missile->x += (missile->momx >> 1); missile->y += (missile->momy >> 1); missile->z += (missile->momz >> 1); if (!P_TryMove(missile, missile->x, missile->y)) { P_ExplodeMissile(missile); return (false); } return (true); } //--------------------------------------------------------------------------- // // FUNC P_SpawnMissile // // Returns NULL if the missile exploded immediately, otherwise returns // a mobj_t pointer to the missile. // //--------------------------------------------------------------------------- mobj_t *P_SpawnMissile(mobj_t * source, mobj_t * dest, mobjtype_t type) { fixed_t z; mobj_t *th; angle_t an; int dist; switch (type) { case MT_MNTRFX1: // Minotaur swing attack missile z = source->z + 40 * FRACUNIT; break; case MT_MNTRFX2: // Minotaur floor fire missile z = ONFLOORZ + source->floorclip; break; case MT_CENTAUR_FX: z = source->z + 45 * FRACUNIT; break; case MT_ICEGUY_FX: z = source->z + 40 * FRACUNIT; break; case MT_HOLY_MISSILE: z = source->z + 40 * FRACUNIT; break; default: z = source->z + 32 * FRACUNIT; break; } z -= source->floorclip; th = P_SpawnMobj(source->x, source->y, z, type); if (th->info->seesound) { S_StartSound(th, th->info->seesound); } th->target = source; // Originator an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); if (dest->flags & MF_SHADOW) { // Invisible target an += P_SubRandom() << 21; } 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; return (P_CheckMissileSpawn(th) ? th : NULL); } //--------------------------------------------------------------------------- // // FUNC P_SpawnMissileXYZ // // Returns NULL if the missile exploded immediately, otherwise returns // a mobj_t pointer to the missile. // //--------------------------------------------------------------------------- mobj_t *P_SpawnMissileXYZ(fixed_t x, fixed_t y, fixed_t z, mobj_t * source, mobj_t * dest, mobjtype_t type) { mobj_t *th; angle_t an; int dist; z -= source->floorclip; th = P_SpawnMobj(x, y, z, type); if (th->info->seesound) { S_StartSound(th, th->info->seesound); } th->target = source; // Originator an = R_PointToAngle2(source->x, source->y, dest->x, dest->y); if (dest->flags & MF_SHADOW) { // Invisible target an += P_SubRandom() << 21; } 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; return (P_CheckMissileSpawn(th) ? th : NULL); } //--------------------------------------------------------------------------- // // FUNC P_SpawnMissileAngle // // Returns NULL if the missile exploded immediately, otherwise returns // a mobj_t pointer to the missile. // //--------------------------------------------------------------------------- mobj_t *P_SpawnMissileAngle(mobj_t * source, mobjtype_t type, angle_t angle, fixed_t momz) { fixed_t z; mobj_t *mo; switch (type) { case MT_MNTRFX1: // Minotaur swing attack missile z = source->z + 40 * FRACUNIT; break; case MT_MNTRFX2: // Minotaur floor fire missile z = ONFLOORZ + source->floorclip; break; case MT_ICEGUY_FX2: // Secondary Projectiles of the Ice Guy z = source->z + 3 * FRACUNIT; break; case MT_MSTAFF_FX2: z = source->z + 40 * FRACUNIT; break; default: z = source->z + 32 * FRACUNIT; break; } z -= source->floorclip; mo = P_SpawnMobj(source->x, source->y, z, type); if (mo->info->seesound) { S_StartSound(mo, mo->info->seesound); } mo->target = source; // Originator mo->angle = angle; angle >>= ANGLETOFINESHIFT; mo->momx = FixedMul(mo->info->speed, finecosine[angle]); mo->momy = FixedMul(mo->info->speed, finesine[angle]); mo->momz = momz; return (P_CheckMissileSpawn(mo) ? mo : NULL); } //--------------------------------------------------------------------------- // // FUNC P_SpawnMissileAngleSpeed // // Returns NULL if the missile exploded immediately, otherwise returns // a mobj_t pointer to the missile. // //--------------------------------------------------------------------------- mobj_t *P_SpawnMissileAngleSpeed(mobj_t * source, mobjtype_t type, angle_t angle, fixed_t momz, fixed_t speed) { fixed_t z; mobj_t *mo; z = source->z; z -= source->floorclip; mo = P_SpawnMobj(source->x, source->y, z, type); if (mo->info->seesound) { //S_StartSound(mo, mo->info->seesound); } mo->target = source; // Originator mo->angle = angle; angle >>= ANGLETOFINESHIFT; mo->momx = FixedMul(speed, finecosine[angle]); mo->momy = FixedMul(speed, finesine[angle]); mo->momz = momz; return (P_CheckMissileSpawn(mo) ? mo : NULL); } /* ================ = = P_SpawnPlayerMissile = = Tries to aim at a nearby monster ================ */ mobj_t *P_SpawnPlayerMissile(mobj_t * source, mobjtype_t type) { angle_t an; fixed_t x, y, z, slope; // Try to find a target an = source->angle; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an += 1 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an -= 2 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); } if (!linetarget) { an = source->angle; slope = ((source->player->lookdir) << FRACBITS) / 173; } } x = source->x; y = source->y; if (type == MT_LIGHTNING_FLOOR) { z = ONFLOORZ; slope = 0; } else if (type == MT_LIGHTNING_CEILING) { z = ONCEILINGZ; slope = 0; } else { z = source->z + 4 * 8 * FRACUNIT + ((source->player->lookdir) << FRACBITS) / 173; z -= source->floorclip; } MissileMobj = P_SpawnMobj(x, y, z, type); if (MissileMobj->info->seesound) { //S_StartSound(MissileMobj, MissileMobj->info->seesound); } MissileMobj->target = source; MissileMobj->angle = an; MissileMobj->momx = FixedMul(MissileMobj->info->speed, finecosine[an >> ANGLETOFINESHIFT]); MissileMobj->momy = FixedMul(MissileMobj->info->speed, finesine[an >> ANGLETOFINESHIFT]); MissileMobj->momz = FixedMul(MissileMobj->info->speed, slope); if (MissileMobj->type == MT_MWAND_MISSILE || MissileMobj->type == MT_CFLAME_MISSILE) { // Ultra-fast ripper spawning missile MissileMobj->x += (MissileMobj->momx >> 3); MissileMobj->y += (MissileMobj->momy >> 3); MissileMobj->z += (MissileMobj->momz >> 3); } else { // Normal missile MissileMobj->x += (MissileMobj->momx >> 1); MissileMobj->y += (MissileMobj->momy >> 1); MissileMobj->z += (MissileMobj->momz >> 1); } if (!P_TryMove(MissileMobj, MissileMobj->x, MissileMobj->y)) { // Exploded immediately P_ExplodeMissile(MissileMobj); return (NULL); } return (MissileMobj); } //---------------------------------------------------------------------------- // // P_SpawnPlayerMinotaur - // // Special missile that has larger blocking than player //---------------------------------------------------------------------------- /* mobj_t *P_SpawnPlayerMinotaur(mobj_t *source, mobjtype_t type) { angle_t an; fixed_t x, y, z; fixed_t dist=0 *FRACUNIT; an = source->angle; x = source->x + FixedMul(dist, finecosine[an>>ANGLETOFINESHIFT]); y = source->y + FixedMul(dist, finesine[an>>ANGLETOFINESHIFT]); z = source->z + 4*8*FRACUNIT+((source->player->lookdir)<floorclip; MissileMobj = P_SpawnMobj(x, y, z, type); if(MissileMobj->info->seesound) { //S_StartSound(MissileMobj, MissileMobj->info->seesound); } MissileMobj->target = source; MissileMobj->angle = an; MissileMobj->momx = FixedMul(MissileMobj->info->speed, finecosine[an>>ANGLETOFINESHIFT]); MissileMobj->momy = FixedMul(MissileMobj->info->speed, finesine[an>>ANGLETOFINESHIFT]); MissileMobj->momz = 0; // MissileMobj->x += (MissileMobj->momx>>3); // MissileMobj->y += (MissileMobj->momy>>3); // MissileMobj->z += (MissileMobj->momz>>3); if(!P_TryMove(MissileMobj, MissileMobj->x, MissileMobj->y)) { // Wouln't fit return(NULL); } return(MissileMobj); } */ //--------------------------------------------------------------------------- // // PROC P_SPMAngle // //--------------------------------------------------------------------------- mobj_t *P_SPMAngle(mobj_t * source, mobjtype_t type, angle_t angle) { mobj_t *th; angle_t an; fixed_t x, y, z, slope; // // see which target is to be aimed at // an = angle; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an += 1 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an -= 2 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); } if (!linetarget) { an = angle; slope = ((source->player->lookdir) << FRACBITS) / 173; } } x = source->x; y = source->y; z = source->z + 4 * 8 * FRACUNIT + ((source->player->lookdir) << FRACBITS) / 173; z -= source->floorclip; th = P_SpawnMobj(x, y, z, type); // if(th->info->seesound) // { // S_StartSound(th, th->info->seesound); // } th->target = source; th->angle = an; th->momx = FixedMul(th->info->speed, finecosine[an >> ANGLETOFINESHIFT]); th->momy = FixedMul(th->info->speed, finesine[an >> ANGLETOFINESHIFT]); th->momz = FixedMul(th->info->speed, slope); return (P_CheckMissileSpawn(th) ? th : NULL); } //=========================================================================== // // P_SPMAngleXYZ // //=========================================================================== mobj_t *P_SPMAngleXYZ(mobj_t * source, fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, angle_t angle) { mobj_t *th; angle_t an; fixed_t slope; // // see which target is to be aimed at // an = angle; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an += 1 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (!linetarget) { an -= 2 << 26; slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT); } if (!linetarget) { an = angle; slope = ((source->player->lookdir) << FRACBITS) / 173; } } z += 4 * 8 * FRACUNIT + ((source->player->lookdir) << FRACBITS) / 173; z -= source->floorclip; th = P_SpawnMobj(x, y, z, type); // if(th->info->seesound) // { // S_StartSound(th, th->info->seesound); // } th->target = source; th->angle = an; th->momx = FixedMul(th->info->speed, finecosine[an >> ANGLETOFINESHIFT]); th->momy = FixedMul(th->info->speed, finesine[an >> ANGLETOFINESHIFT]); th->momz = FixedMul(th->info->speed, slope); return (P_CheckMissileSpawn(th) ? th : NULL); } mobj_t *P_SpawnKoraxMissile(fixed_t x, fixed_t y, fixed_t z, mobj_t * source, mobj_t * dest, mobjtype_t type) { mobj_t *th; angle_t an; int dist; z -= source->floorclip; th = P_SpawnMobj(x, y, z, type); if (th->info->seesound) { S_StartSound(th, th->info->seesound); } th->target = source; // Originator an = R_PointToAngle2(x, y, dest->x, dest->y); if (dest->flags & MF_SHADOW) { // Invisible target an += P_SubRandom() << 21; } 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 - x, dest->y - y); dist = dist / th->info->speed; if (dist < 1) { dist = 1; } th->momz = (dest->z - z + (30 * FRACUNIT)) / dist; return (P_CheckMissileSpawn(th) ? th : NULL); } crispy-doom-crispy-doom-5.6.4/src/hexen/p_plats.c000066400000000000000000000202701360717211000217410ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "m_random.h" #include "i_system.h" #include "p_local.h" plat_t *activeplats[MAXPLATS]; //================================================================== // // Move a plat up and down // //================================================================== void T_PlatRaise(plat_t * plat) { result_e res; switch (plat->status) { case PLAT_UP: res = T_MovePlane(plat->sector, plat->speed, plat->high, plat->crush, 0, 1); if (res == RES_CRUSHED && (!plat->crush)) { plat->count = plat->wait; plat->status = PLAT_DOWN; SN_StartSequence((mobj_t *) & plat->sector->soundorg, SEQ_PLATFORM + plat->sector->seqType); } else if (res == RES_PASTDEST) { plat->count = plat->wait; plat->status = PLAT_WAITING; SN_StopSequence((mobj_t *) & plat->sector->soundorg); switch (plat->type) { case PLAT_DOWNWAITUPSTAY: case PLAT_DOWNBYVALUEWAITUPSTAY: P_RemoveActivePlat(plat); break; default: break; } } break; case PLAT_DOWN: res = T_MovePlane(plat->sector, plat->speed, plat->low, false, 0, -1); if (res == RES_PASTDEST) { plat->count = plat->wait; plat->status = PLAT_WAITING; switch (plat->type) { case PLAT_UPWAITDOWNSTAY: case PLAT_UPBYVALUEWAITDOWNSTAY: P_RemoveActivePlat(plat); break; default: break; } SN_StopSequence((mobj_t *) & plat->sector->soundorg); } break; case PLAT_WAITING: if (!--plat->count) { if (plat->sector->floorheight == plat->low) plat->status = PLAT_UP; else plat->status = PLAT_DOWN; SN_StartSequence((mobj_t *) & plat->sector->soundorg, SEQ_PLATFORM + plat->sector->seqType); } // case PLAT_IN_STASIS: // break; } } //================================================================== // // Do Platforms // "amount" is only used for SOME platforms. // //================================================================== int EV_DoPlat(line_t * line, byte * args, plattype_e type, int amount) { plat_t *plat; int secnum; int rtn; sector_t *sec; secnum = -1; rtn = 0; /* // // Activate all plats that are in_stasis // switch(type) { case PLAT_PERPETUALRAISE: P_ActivateInStasis(args[0]); break; default: break; } */ while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // // Find lowest & highest floors around sector // rtn = 1; plat = Z_Malloc(sizeof(*plat), PU_LEVSPEC, 0); P_AddThinker(&plat->thinker); plat->type = type; plat->sector = sec; plat->sector->specialdata = plat; plat->thinker.function = T_PlatRaise; plat->crush = false; plat->tag = args[0]; plat->speed = args[1] * (FRACUNIT / 8); switch (type) { case PLAT_DOWNWAITUPSTAY: plat->low = P_FindLowestFloorSurrounding(sec) + 8 * FRACUNIT; if (plat->low > sec->floorheight) plat->low = sec->floorheight; plat->high = sec->floorheight; plat->wait = args[2]; plat->status = PLAT_DOWN; break; case PLAT_DOWNBYVALUEWAITUPSTAY: plat->low = sec->floorheight - args[3] * 8 * FRACUNIT; if (plat->low > sec->floorheight) plat->low = sec->floorheight; plat->high = sec->floorheight; plat->wait = args[2]; plat->status = PLAT_DOWN; break; case PLAT_UPWAITDOWNSTAY: plat->high = P_FindHighestFloorSurrounding(sec); if (plat->high < sec->floorheight) plat->high = sec->floorheight; plat->low = sec->floorheight; plat->wait = args[2]; plat->status = PLAT_UP; break; case PLAT_UPBYVALUEWAITDOWNSTAY: plat->high = sec->floorheight + args[3] * 8 * FRACUNIT; if (plat->high < sec->floorheight) plat->high = sec->floorheight; plat->low = sec->floorheight; plat->wait = args[2]; plat->status = PLAT_UP; break; case PLAT_PERPETUALRAISE: plat->low = P_FindLowestFloorSurrounding(sec) + 8 * FRACUNIT; 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 = args[2]; plat->status = P_Random() & 1; break; } P_AddActivePlat(plat); SN_StartSequence((mobj_t *) & sec->soundorg, SEQ_PLATFORM + sec->seqType); } return rtn; } #if 0 void P_ActivateInStasis(int tag) { int i; for (i = 0; i < MAXPLATS; i++) if (activeplats[i] && (activeplats[i])->tag == tag && (activeplats[i])->status == PLAT_IN_STASIS) { (activeplats[i])->status = (activeplats[i])->oldstatus; (activeplats[i])->thinker.function = T_PlatRaise; } } #endif void EV_StopPlat(line_t * line, byte * args) { int i; for (i = 0; i < MAXPLATS; i++) { activeplats[i]->tag = args[0]; if (activeplats[i]->tag != 0) { activeplats[i]->sector->specialdata = NULL; P_TagFinished(activeplats[i]->sector->tag); P_RemoveThinker(&activeplats[i]->thinker); activeplats[i] = NULL; return; } } /* int j; for (j = 0;j < MAXPLATS;j++) { if (activeplats[j] && ((activeplats[j])->status != PLAT_IN_STASIS) && ((activeplats[j])->tag == args[0])) { (activeplats[j])->oldstatus = (activeplats[j])->status; (activeplats[j])->status = PLAT_IN_STASIS; (activeplats[j])->thinker.function = NULL; SN_StopSequence((mobj_t *)&(activeplats[j])->sector->soundorg); } } */ } void P_AddActivePlat(plat_t * plat) { int i; for (i = 0; i < MAXPLATS; i++) if (activeplats[i] == NULL) { activeplats[i] = plat; return; } I_Error("P_AddActivePlat: no more plats!"); } void P_RemoveActivePlat(plat_t * plat) { int i; for (i = 0; i < MAXPLATS; i++) if (plat == activeplats[i]) { (activeplats[i])->sector->specialdata = NULL; P_TagFinished(plat->sector->tag); P_RemoveThinker(&(activeplats[i])->thinker); activeplats[i] = NULL; return; } I_Error("P_RemoveActivePlat: can't find plat!"); } crispy-doom-crispy-doom-5.6.4/src/hexen/p_pspr.c000066400000000000000000002140641360717211000216100ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ #define LOWERSPEED FRACUNIT*6 #define RAISESPEED FRACUNIT*6 #define WEAPONBOTTOM 128*FRACUNIT #define WEAPONTOP 32*FRACUNIT // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- extern void P_ExplodeMissile(mobj_t * mo); extern void A_UnHideThing(mobj_t * actor); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern fixed_t FloatBobOffsets[64]; // PUBLIC DATA DEFINITIONS ------------------------------------------------- fixed_t bulletslope; weaponinfo_t WeaponInfo[NUMWEAPONS][NUMCLASSES] = { { // First Weapons { // Fighter First Weapon - Punch MANA_NONE, // mana S_PUNCHUP, // upstate S_PUNCHDOWN, // downstate S_PUNCHREADY, // readystate S_PUNCHATK1_1, // atkstate S_PUNCHATK1_1, // holdatkstate S_NULL // flashstate }, { // Cleric First Weapon - Mace MANA_NONE, // mana S_CMACEUP, // upstate S_CMACEDOWN, // downstate S_CMACEREADY, // readystate S_CMACEATK_1, // atkstate S_CMACEATK_1, // holdatkstate S_NULL // flashstate }, { // Mage First Weapon - Wand MANA_NONE, S_MWANDUP, S_MWANDDOWN, S_MWANDREADY, S_MWANDATK_1, S_MWANDATK_1, S_NULL}, { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } }, { // Second Weapons { // Fighter - Axe MANA_NONE, // mana S_FAXEUP, // upstate S_FAXEDOWN, // downstate S_FAXEREADY, // readystate S_FAXEATK_1, // atkstate S_FAXEATK_1, // holdatkstate S_NULL // flashstate }, { // Cleric - Serpent Staff MANA_1, // mana S_CSTAFFUP, // upstate S_CSTAFFDOWN, // downstate S_CSTAFFREADY, // readystate S_CSTAFFATK_1, // atkstate S_CSTAFFATK_1, // holdatkstate S_NULL // flashstate }, { // Mage - Cone of shards MANA_1, // mana S_CONEUP, // upstate S_CONEDOWN, // downstate S_CONEREADY, // readystate S_CONEATK1_1, // atkstate S_CONEATK1_3, // holdatkstate S_NULL // flashstate }, { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } }, { // Third Weapons { // Fighter - Hammer MANA_NONE, // mana S_FHAMMERUP, // upstate S_FHAMMERDOWN, // downstate S_FHAMMERREADY, // readystate S_FHAMMERATK_1, // atkstate S_FHAMMERATK_1, // holdatkstate S_NULL // flashstate }, { // Cleric - Flame Strike MANA_2, // mana S_CFLAMEUP, // upstate S_CFLAMEDOWN, // downstate S_CFLAMEREADY1, // readystate S_CFLAMEATK_1, // atkstate S_CFLAMEATK_1, // holdatkstate S_NULL // flashstate }, { // Mage - Lightning MANA_2, // mana S_MLIGHTNINGUP, // upstate S_MLIGHTNINGDOWN, // downstate S_MLIGHTNINGREADY, // readystate S_MLIGHTNINGATK_1, // atkstate S_MLIGHTNINGATK_1, // holdatkstate S_NULL // flashstate }, { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } }, { // Fourth Weapons { // Fighter - Rune Sword MANA_BOTH, // mana S_FSWORDUP, // upstate S_FSWORDDOWN, // downstate S_FSWORDREADY, // readystate S_FSWORDATK_1, // atkstate S_FSWORDATK_1, // holdatkstate S_NULL // flashstate }, { // Cleric - Holy Symbol MANA_BOTH, // mana S_CHOLYUP, // upstate S_CHOLYDOWN, // downstate S_CHOLYREADY, // readystate S_CHOLYATK_1, // atkstate S_CHOLYATK_1, // holdatkstate S_NULL // flashstate }, { // Mage - Staff MANA_BOTH, // mana S_MSTAFFUP, // upstate S_MSTAFFDOWN, // downstate S_MSTAFFREADY, // readystate S_MSTAFFATK_1, // atkstate S_MSTAFFATK_1, // holdatkstate S_NULL // flashstate }, { // Pig - Snout MANA_NONE, // mana S_SNOUTUP, // upstate S_SNOUTDOWN, // downstate S_SNOUTREADY, // readystate S_SNOUTATK1, // atkstate S_SNOUTATK1, // holdatkstate S_NULL // flashstate } } }; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int WeaponManaUse[NUMCLASSES][NUMWEAPONS] = { {0, 2, 3, 14}, {0, 1, 4, 18}, {0, 3, 5, 15}, {0, 0, 0, 0} }; // CODE -------------------------------------------------------------------- //--------------------------------------------------------------------------- // // PROC P_SetPsprite // //--------------------------------------------------------------------------- void P_SetPsprite(player_t * player, int position, statenum_t stnum) { pspdef_t *psp; state_t *state; psp = &player->psprites[position]; do { if (!stnum) { // Object removed itself. psp->state = NULL; break; } state = &states[stnum]; psp->state = state; psp->tics = state->tics; // could be 0 if (state->misc1) { // Set coordinates. psp->sx = state->misc1 << FRACBITS; } if (state->misc2) { psp->sy = state->misc2 << FRACBITS; } if (state->action) { // Call action routine. state->action(player, psp); if (!psp->state) { break; } } stnum = psp->state->nextstate; } while (!psp->tics); // An initial state of 0 could cycle through. } //--------------------------------------------------------------------------- // // PROC P_SetPspriteNF // // Identical to P_SetPsprite, without calling the action function //--------------------------------------------------------------------------- void P_SetPspriteNF(player_t * player, int position, statenum_t stnum) { pspdef_t *psp; state_t *state; psp = &player->psprites[position]; do { if (!stnum) { // Object removed itself. psp->state = NULL; break; } state = &states[stnum]; psp->state = state; psp->tics = state->tics; // could be 0 if (state->misc1) { // Set coordinates. psp->sx = state->misc1 << FRACBITS; } if (state->misc2) { psp->sy = state->misc2 << FRACBITS; } stnum = psp->state->nextstate; } while (!psp->tics); // An initial state of 0 could cycle through. } /* ================= = = P_CalcSwing = ================= */ /* fixed_t swingx, swingy; void P_CalcSwing (player_t *player) { fixed_t swing; int angle; // OPTIMIZE: tablify this swing = player->bob; angle = (FINEANGLES/70*leveltime)&FINEMASK; swingx = FixedMul ( swing, finesine[angle]); angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK; swingy = -FixedMul ( swingx, finesine[angle]); } */ //--------------------------------------------------------------------------- // // PROC P_ActivateMorphWeapon // //--------------------------------------------------------------------------- void P_ActivateMorphWeapon(player_t * player) { player->pendingweapon = WP_NOCHANGE; player->psprites[ps_weapon].sy = WEAPONTOP; player->readyweapon = WP_FIRST; // Snout is the first weapon P_SetPsprite(player, ps_weapon, S_SNOUTREADY); } //--------------------------------------------------------------------------- // // PROC P_PostMorphWeapon // //--------------------------------------------------------------------------- void P_PostMorphWeapon(player_t * player, weapontype_t weapon) { player->pendingweapon = WP_NOCHANGE; player->readyweapon = weapon; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite(player, ps_weapon, WeaponInfo[weapon][player->class].upstate); } //--------------------------------------------------------------------------- // // PROC P_BringUpWeapon // // Starts bringing the pending weapon up from the bottom of the screen. // //--------------------------------------------------------------------------- void P_BringUpWeapon(player_t * player) { statenum_t new; if (player->pendingweapon == WP_NOCHANGE) { player->pendingweapon = player->readyweapon; } if (player->class == PCLASS_FIGHTER && player->pendingweapon == WP_SECOND && player->mana[MANA_1]) { new = S_FAXEUP_G; } else { new = WeaponInfo[player->pendingweapon][player->class].upstate; } player->pendingweapon = WP_NOCHANGE; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite(player, ps_weapon, new); } //--------------------------------------------------------------------------- // // FUNC P_CheckMana // // Returns true if there is enough mana to shoot. If not, selects the // next weapon to use. // //--------------------------------------------------------------------------- boolean P_CheckMana(player_t * player) { manatype_t mana; int count; mana = WeaponInfo[player->readyweapon][player->class].mana; count = WeaponManaUse[player->class][player->readyweapon]; if (mana == MANA_BOTH) { if (player->mana[MANA_1] >= count && player->mana[MANA_2] >= count) { return true; } } else if (mana == MANA_NONE || player->mana[mana] >= count) { return (true); } // out of mana, pick a weapon to change to do { if (player->weaponowned[WP_THIRD] && player->mana[MANA_2] >= WeaponManaUse[player->class][WP_THIRD]) { player->pendingweapon = WP_THIRD; } else if (player->weaponowned[WP_SECOND] && player->mana[MANA_1] >= WeaponManaUse[player->class][WP_SECOND]) { player->pendingweapon = WP_SECOND; } else if (player->weaponowned[WP_FOURTH] && player->mana[MANA_1] >= WeaponManaUse[player->class][WP_FOURTH] && player->mana[MANA_2] >= WeaponManaUse[player->class][WP_FOURTH]) { player->pendingweapon = WP_FOURTH; } else { player->pendingweapon = WP_FIRST; } } while (player->pendingweapon == WP_NOCHANGE); P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->class].downstate); return (false); } //--------------------------------------------------------------------------- // // PROC P_FireWeapon // //--------------------------------------------------------------------------- void P_FireWeapon(player_t * player) { statenum_t attackState; if (!P_CheckMana(player)) { return; } P_SetMobjState(player->mo, PStateAttack[player->class]); // S_PLAY_ATK1); if (player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND && player->mana[MANA_1] > 0) { // Glowing axe attackState = S_FAXEATK_G1; } else { attackState = player->refire ? WeaponInfo[player->readyweapon][player->class].holdatkstate : WeaponInfo[player->readyweapon][player->class].atkstate; } P_SetPsprite(player, ps_weapon, attackState); P_NoiseAlert(player->mo, player->mo); } //--------------------------------------------------------------------------- // // PROC P_DropWeapon // // The player died, so put the weapon away. // //--------------------------------------------------------------------------- void P_DropWeapon(player_t * player) { P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->class].downstate); } //--------------------------------------------------------------------------- // // PROC A_WeaponReady // // The player can fire the weapon or change to another weapon at this time. // //--------------------------------------------------------------------------- void A_WeaponReady(player_t * player, pspdef_t * psp) { int angle; // Change player from attack state if (player->mo->state >= &states[PStateAttack[player->class]] && player->mo->state <= &states[PStateAttackEnd[player->class]]) { P_SetMobjState(player->mo, PStateNormal[player->class]); } // Put the weapon away if the player has a pending weapon or has // died. if (player->pendingweapon != WP_NOCHANGE || !player->health) { P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->class]. downstate); return; } // Check for fire. if (player->cmd.buttons & BT_ATTACK) { player->attackdown = true; P_FireWeapon(player); return; } else { player->attackdown = false; } if (!player->morphTics) { // Bob the weapon based on movement speed. angle = (128 * leveltime) & FINEMASK; psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); angle &= FINEANGLES / 2 - 1; psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); } } //--------------------------------------------------------------------------- // // PROC A_ReFire // // The player can re fire the weapon without lowering it entirely. // //--------------------------------------------------------------------------- void A_ReFire(player_t * player, pspdef_t * psp) { if ((player->cmd.buttons & BT_ATTACK) && player->pendingweapon == WP_NOCHANGE && player->health) { player->refire++; P_FireWeapon(player); } else { player->refire = 0; P_CheckMana(player); } } //--------------------------------------------------------------------------- // // PROC A_Lower // //--------------------------------------------------------------------------- void A_Lower(player_t * player, pspdef_t * psp) { if (player->morphTics) { psp->sy = WEAPONBOTTOM; } else { psp->sy += LOWERSPEED; } if (psp->sy < WEAPONBOTTOM) { // Not lowered all the way yet return; } if (player->playerstate == PST_DEAD) { // Player is dead, so don't bring up a pending weapon psp->sy = WEAPONBOTTOM; return; } if (!player->health) { // Player is dead, so keep the weapon off screen P_SetPsprite(player, ps_weapon, S_NULL); return; } player->readyweapon = player->pendingweapon; P_BringUpWeapon(player); } //--------------------------------------------------------------------------- // // PROC A_Raise // //--------------------------------------------------------------------------- void A_Raise(player_t * player, pspdef_t * psp) { psp->sy -= RAISESPEED; if (psp->sy > WEAPONTOP) { // Not raised all the way yet return; } psp->sy = WEAPONTOP; if (player->class == PCLASS_FIGHTER && player->readyweapon == WP_SECOND && player->mana[MANA_1]) { P_SetPsprite(player, ps_weapon, S_FAXEREADY_G); } else { P_SetPsprite(player, ps_weapon, WeaponInfo[player->readyweapon][player->class]. readystate); } } /* =============== = = P_BulletSlope = = Sets a slope so a near miss is at aproximately the height of the = intended target = =============== */ /* void P_BulletSlope (mobj_t *mo) { angle_t an; // // see which target is to be aimed at // an = mo->angle; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); } if (!linetarget) { an += 1<<26; bulletslope = (mo->player->lookdir<x, pmo->y, linetarget->x, linetarget->y); difference = (int) angle - (int) pmo->angle; if (abs(difference) > MAX_ANGADJUST) { pmo->angle += difference > 0 ? MAX_ANGADJUST : -MAX_ANGADJUST; } else { pmo->angle = angle; } } //============================================================================ // // A_SnoutAttack // //============================================================================ void A_SnoutAttack(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; damage = 3 + (P_Random() & 3); angle = player->mo->angle; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); PuffType = MT_SNOUTPUFF; PuffSpawned = NULL; P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); S_StartSound(player->mo, SFX_PIG_ACTIVE1 + (P_Random() & 1)); if (linetarget) { AdjustPlayerAngle(player->mo); // player->mo->angle = R_PointToAngle2(player->mo->x, // player->mo->y, linetarget->x, linetarget->y); if (PuffSpawned) { // Bit something S_StartSound(player->mo, SFX_PIG_ATTACK); } } } //============================================================================ // // A_FHammerAttack // //============================================================================ #define HAMMER_RANGE (MELEERANGE+MELEERANGE/2) void A_FHammerAttack(player_t * player, pspdef_t * psp) { angle_t angle; mobj_t *pmo = player->mo; int damage; fixed_t power; int slope; int i; damage = 60 + (P_Random() & 63); power = 10 * FRACUNIT; PuffType = MT_HAMMERPUFF; for (i = 0; i < 16; i++) { angle = pmo->angle + i * (ANG45 / 32); slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); if (linetarget) { P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); AdjustPlayerAngle(pmo); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } pmo->special1.i = false; // Don't throw a hammer goto hammerdone; } angle = pmo->angle - i * (ANG45 / 32); slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); if (linetarget) { P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); AdjustPlayerAngle(pmo); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } pmo->special1.i = false; // Don't throw a hammer goto hammerdone; } } // didn't find any targets in meleerange, so set to throw out a hammer PuffSpawned = NULL; angle = pmo->angle; slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE); P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage); if (PuffSpawned) { pmo->special1.i = false; } else { pmo->special1.i = true; } hammerdone: if (player->mana[MANA_2] < WeaponManaUse[player->class][player->readyweapon]) { // Don't spawn a hammer if the player doesn't have enough mana pmo->special1.i = false; } return; } //============================================================================ // // A_FHammerThrow // //============================================================================ void A_FHammerThrow(player_t * player, pspdef_t * psp) { mobj_t *mo; if (!player->mo->special1.i) { return; } player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; mo = P_SpawnPlayerMissile(player->mo, MT_HAMMER_MISSILE); if (mo) { mo->special1.i = 0; } } //============================================================================ // // A_FSwordAttack // //============================================================================ void A_FSwordAttack(player_t * player, pspdef_t * psp) { mobj_t *pmo; player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; pmo = player->mo; P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z - 10 * FRACUNIT, MT_FSWORD_MISSILE, pmo->angle + ANG45 / 4); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z - 5 * FRACUNIT, MT_FSWORD_MISSILE, pmo->angle + ANG45 / 8); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z, MT_FSWORD_MISSILE, pmo->angle); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z + 5 * FRACUNIT, MT_FSWORD_MISSILE, pmo->angle - ANG45 / 8); P_SPMAngleXYZ(pmo, pmo->x, pmo->y, pmo->z + 10 * FRACUNIT, MT_FSWORD_MISSILE, pmo->angle - ANG45 / 4); S_StartSound(pmo, SFX_FIGHTER_SWORD_FIRE); } //============================================================================ // // A_FSwordAttack2 // //============================================================================ void A_FSwordAttack2(mobj_t * actor) { angle_t angle = actor->angle; P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE, angle + ANG45 / 4, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE, angle + ANG45 / 8, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE, angle, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE, angle - ANG45 / 8, 0); P_SpawnMissileAngle(actor, MT_FSWORD_MISSILE, angle - ANG45 / 4, 0); S_StartSound(actor, SFX_FIGHTER_SWORD_FIRE); } //============================================================================ // // A_FSwordFlames // //============================================================================ void A_FSwordFlames(mobj_t * actor) { int i; int r1,r2,r3; for (i = 1 + (P_Random() & 3); i; i--) { r1 = P_Random(); r2 = P_Random(); r3 = P_Random(); P_SpawnMobj(actor->x + ((r3 - 128) << 12), actor->y + ((r2 - 128) << 12), actor->z + ((r1 - 128) << 11), MT_FSWORD_FLAME); } } //============================================================================ // // A_MWandAttack // //============================================================================ void A_MWandAttack(player_t * player, pspdef_t * psp) { mobj_t *mo; mo = P_SpawnPlayerMissile(player->mo, MT_MWAND_MISSILE); if (mo) { mo->thinker.function = P_BlasterMobjThinker; } S_StartSound(player->mo, SFX_MAGE_WAND_FIRE); } // ===== Mage Lightning Weapon ===== //============================================================================ // // A_LightningReady // //============================================================================ void A_LightningReady(player_t * player, pspdef_t * psp) { A_WeaponReady(player, psp); if (P_Random() < 160) { S_StartSound(player->mo, SFX_MAGE_LIGHTNING_READY); } } //============================================================================ // // A_LightningClip // //============================================================================ #define ZAGSPEED FRACUNIT void A_LightningClip(mobj_t * actor) { mobj_t *cMo; mobj_t *target = NULL; int zigZag; if (actor->type == MT_LIGHTNING_FLOOR) { actor->z = actor->floorz; target = actor->special2.m->special1.m; } else if (actor->type == MT_LIGHTNING_CEILING) { actor->z = actor->ceilingz - actor->height; target = actor->special1.m; } if (actor->type == MT_LIGHTNING_FLOOR) { // floor lightning zig-zags, and forces the ceiling lightning to mimic cMo = actor->special2.m; zigZag = P_Random(); if ((zigZag > 128 && actor->special1.i < 2) || actor->special1.i < -2) { P_ThrustMobj(actor, actor->angle + ANG90, ZAGSPEED); if (cMo) { P_ThrustMobj(cMo, actor->angle + ANG90, ZAGSPEED); } actor->special1.i++; } else { P_ThrustMobj(actor, actor->angle - ANG90, ZAGSPEED); if (cMo) { P_ThrustMobj(cMo, cMo->angle - ANG90, ZAGSPEED); } actor->special1.i--; } } if (target) { if (target->health <= 0) { P_ExplodeMissile(actor); } else { actor->angle = R_PointToAngle2(actor->x, actor->y, target->x, target->y); actor->momx = 0; actor->momy = 0; P_ThrustMobj(actor, actor->angle, actor->info->speed >> 1); } } } //============================================================================ // // A_LightningZap // //============================================================================ void A_LightningZap(mobj_t * actor) { mobj_t *mo; fixed_t deltaZ; int r1,r2; A_LightningClip(actor); actor->health -= 8; if (actor->health <= 0) { P_SetMobjState(actor, actor->info->deathstate); return; } if (actor->type == MT_LIGHTNING_FLOOR) { deltaZ = 10 * FRACUNIT; } else { deltaZ = -10 * FRACUNIT; } r1 = P_Random(); r2 = P_Random(); mo = P_SpawnMobj(actor->x + ((r2 - 128) * actor->radius / 256), actor->y + ((r1 - 128) * actor->radius / 256), actor->z + deltaZ, MT_LIGHTNING_ZAP); if (mo) { mo->special2.m = actor; mo->momx = actor->momx; mo->momy = actor->momy; mo->target = actor->target; if (actor->type == MT_LIGHTNING_FLOOR) { mo->momz = 20 * FRACUNIT; } else { mo->momz = -20 * FRACUNIT; } } /* mo = P_SpawnMobj(actor->x+((P_Random()-128)*actor->radius/256), actor->y+((P_Random()-128)*actor->radius/256), actor->z+deltaZ, MT_LIGHTNING_ZAP); if(mo) { mo->special2.m = actor; mo->momx = actor->momx; mo->momy = actor->momy; mo->target = actor->target; if(actor->type == MT_LIGHTNING_FLOOR) { mo->momz = 16*FRACUNIT; } else { mo->momz = -16*FRACUNIT; } } */ if (actor->type == MT_LIGHTNING_FLOOR && P_Random() < 160) { S_StartSound(actor, SFX_MAGE_LIGHTNING_CONTINUOUS); } } //============================================================================ // // A_MLightningAttack2 // //============================================================================ void A_MLightningAttack2(mobj_t * actor) { mobj_t *fmo, *cmo; fmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_FLOOR); cmo = P_SpawnPlayerMissile(actor, MT_LIGHTNING_CEILING); if (fmo) { fmo->special1.m = NULL; fmo->special2.m = cmo; A_LightningZap(fmo); } if (cmo) { cmo->special1.m = NULL; // mobj that it will track cmo->special2.m = fmo; A_LightningZap(cmo); } S_StartSound(actor, SFX_MAGE_LIGHTNING_FIRE); } //============================================================================ // // A_MLightningAttack // //============================================================================ void A_MLightningAttack(player_t * player, pspdef_t * psp) { A_MLightningAttack2(player->mo); player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; } //============================================================================ // // A_ZapMimic // //============================================================================ void A_ZapMimic(mobj_t * actor) { mobj_t *mo; mo = actor->special2.m; if (mo) { if (mo->state >= &states[mo->info->deathstate] || mo->state == &states[S_FREETARGMOBJ]) { P_ExplodeMissile(actor); } else { actor->momx = mo->momx; actor->momy = mo->momy; } } } //============================================================================ // // A_LastZap // //============================================================================ void A_LastZap(mobj_t * actor) { mobj_t *mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_LIGHTNING_ZAP); if (mo) { P_SetMobjState(mo, S_LIGHTNING_ZAP_X1); mo->momz = 40 * FRACUNIT; } } //============================================================================ // // A_LightningRemove // //============================================================================ void A_LightningRemove(mobj_t * actor) { mobj_t *mo; mo = actor->special2.m; if (mo) { mo->special2.m = NULL; P_ExplodeMissile(mo); } } //============================================================================ // // MStaffSpawn // //============================================================================ void MStaffSpawn(mobj_t * pmo, angle_t angle) { mobj_t *mo; mo = P_SPMAngle(pmo, MT_MSTAFF_FX2, angle); if (mo) { mo->target = pmo; mo->special1.m = P_RoughMonsterSearch(mo, 10); } } //============================================================================ // // A_MStaffAttack // //============================================================================ void A_MStaffAttack(player_t * player, pspdef_t * psp) { angle_t angle; mobj_t *pmo; player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; pmo = player->mo; angle = pmo->angle; MStaffSpawn(pmo, angle); MStaffSpawn(pmo, angle - ANG1 * 5); MStaffSpawn(pmo, angle + ANG1 * 5); S_StartSound(player->mo, SFX_MAGE_STAFF_FIRE); if (player == &players[consoleplayer]) { player->damagecount = 0; player->bonuscount = 0; I_SetPalette((byte *) W_CacheLumpNum(W_GetNumForName("playpal"), PU_CACHE) + STARTSCOURGEPAL * 768); } } //============================================================================ // // A_MStaffPalette // //============================================================================ void A_MStaffPalette(player_t * player, pspdef_t * psp) { int pal; if (player == &players[consoleplayer]) { pal = STARTSCOURGEPAL + psp->state - (&states[S_MSTAFFATK_2]); if (pal == STARTSCOURGEPAL + 3) { // reset back to original playpal pal = 0; } I_SetPalette((byte *) W_CacheLumpNum(W_GetNumForName("playpal"), PU_CACHE) + pal * 768); } } //============================================================================ // // A_MStaffWeave // //============================================================================ void A_MStaffWeave(mobj_t * actor) { fixed_t newX, newY; int weaveXY, weaveZ; int angle; weaveXY = actor->special2.i >> 16; weaveZ = actor->special2.i & 0xFFFF; angle = (actor->angle + ANG90) >> ANGLETOFINESHIFT; newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 2); newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 2); weaveXY = (weaveXY + 6) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 2); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 2); P_TryMove(actor, newX, newY); actor->z -= FloatBobOffsets[weaveZ] << 1; weaveZ = (weaveZ + 3) & 63; actor->z += FloatBobOffsets[weaveZ] << 1; if (actor->z <= actor->floorz) { actor->z = actor->floorz + FRACUNIT; } actor->special2.i = weaveZ + (weaveXY << 16); } //============================================================================ // // A_MStaffTrack // //============================================================================ void A_MStaffTrack(mobj_t * actor) { if ((actor->special1.m == NULL) && (P_Random() < 50)) { actor->special1.m = P_RoughMonsterSearch(actor, 10); } P_SeekerMissile(actor, ANG1 * 2, ANG1 * 10); } //============================================================================ // // MStaffSpawn2 - for use by mage class boss // //============================================================================ void MStaffSpawn2(mobj_t * actor, angle_t angle) { mobj_t *mo; mo = P_SpawnMissileAngle(actor, MT_MSTAFF_FX2, angle, 0); if (mo) { mo->target = actor; mo->special1.m = P_RoughMonsterSearch(mo, 10); } } //============================================================================ // // A_MStaffAttack2 - for use by mage class boss // //============================================================================ void A_MStaffAttack2(mobj_t * actor) { angle_t angle; angle = actor->angle; MStaffSpawn2(actor, angle); MStaffSpawn2(actor, angle - ANG1 * 5); MStaffSpawn2(actor, angle + ANG1 * 5); S_StartSound(actor, SFX_MAGE_STAFF_FIRE); } //============================================================================ // // A_FPunchAttack // //============================================================================ void A_FPunchAttack(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; mobj_t *pmo = player->mo; fixed_t power; int i; damage = 40 + (P_Random() & 15); power = 2 * FRACUNIT; PuffType = MT_PUNCHPUFF; for (i = 0; i < 16; i++) { angle = pmo->angle + i * (ANG45 / 16); slope = P_AimLineAttack(pmo, angle, 2 * MELEERANGE); if (linetarget) { player->mo->special1.i++; if (pmo->special1.i == 3) { damage <<= 1; power = 6 * FRACUNIT; PuffType = MT_HAMMERPUFF; } P_LineAttack(pmo, angle, 2 * MELEERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); goto punchdone; } angle = pmo->angle - i * (ANG45 / 16); slope = P_AimLineAttack(pmo, angle, 2 * MELEERANGE); if (linetarget) { pmo->special1.i++; if (pmo->special1.i == 3) { damage <<= 1; power = 6 * FRACUNIT; PuffType = MT_HAMMERPUFF; } P_LineAttack(pmo, angle, 2 * MELEERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); goto punchdone; } } // didn't find any creatures, so try to strike any walls pmo->special1.i = 0; angle = pmo->angle; slope = P_AimLineAttack(pmo, angle, MELEERANGE); P_LineAttack(pmo, angle, MELEERANGE, slope, damage); punchdone: if (pmo->special1.i == 3) { pmo->special1.i = 0; P_SetPsprite(player, ps_weapon, S_PUNCHATK2_1); S_StartSound(pmo, SFX_FIGHTER_GRUNT); } return; } //============================================================================ // // A_FAxeAttack // //============================================================================ #define AXERANGE 2.25*MELEERANGE void A_FAxeAttack(player_t * player, pspdef_t * psp) { angle_t angle; mobj_t *pmo = player->mo; fixed_t power; int damage; int slope; int i; int useMana; int r; r = P_Random(); damage = 40 + (r & 15) + (P_Random() & 7); power = 0; if (player->mana[MANA_1] > 0) { damage <<= 1; power = 6 * FRACUNIT; PuffType = MT_AXEPUFF_GLOW; useMana = 1; } else { PuffType = MT_AXEPUFF; useMana = 0; } for (i = 0; i < 16; i++) { angle = pmo->angle + i * (ANG45 / 16); slope = P_AimLineAttack(pmo, angle, AXERANGE); if (linetarget) { P_LineAttack(pmo, angle, AXERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL || linetarget->player) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); useMana++; goto axedone; } angle = pmo->angle - i * (ANG45 / 16); slope = P_AimLineAttack(pmo, angle, AXERANGE); if (linetarget) { P_LineAttack(pmo, angle, AXERANGE, slope, damage); if (linetarget->flags & MF_COUNTKILL) { P_ThrustMobj(linetarget, angle, power); } AdjustPlayerAngle(pmo); useMana++; goto axedone; } } // didn't find any creatures, so try to strike any walls pmo->special1.m = NULL; angle = pmo->angle; slope = P_AimLineAttack(pmo, angle, MELEERANGE); P_LineAttack(pmo, angle, MELEERANGE, slope, damage); axedone: if (useMana == 2) { player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; if (player->mana[MANA_1] <= 0) { P_SetPsprite(player, ps_weapon, S_FAXEATK_5); } } return; } //=========================================================================== // // A_CMaceAttack // //=========================================================================== void A_CMaceAttack(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int slope; int i; damage = 25 + (P_Random() & 15); PuffType = MT_HAMMERPUFF; for (i = 0; i < 16; i++) { angle = player->mo->angle + i * (ANG45 / 16); slope = P_AimLineAttack(player->mo, angle, 2 * MELEERANGE); if (linetarget) { P_LineAttack(player->mo, angle, 2 * MELEERANGE, slope, damage); AdjustPlayerAngle(player->mo); // player->mo->angle = R_PointToAngle2(player->mo->x, // player->mo->y, linetarget->x, linetarget->y); goto macedone; } angle = player->mo->angle - i * (ANG45 / 16); slope = P_AimLineAttack(player->mo, angle, 2 * MELEERANGE); if (linetarget) { P_LineAttack(player->mo, angle, 2 * MELEERANGE, slope, damage); AdjustPlayerAngle(player->mo); // player->mo->angle = R_PointToAngle2(player->mo->x, // player->mo->y, linetarget->x, linetarget->y); goto macedone; } } // didn't find any creatures, so try to strike any walls player->mo->special1.m = NULL; angle = player->mo->angle; slope = P_AimLineAttack(player->mo, angle, MELEERANGE); P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); macedone: return; } //============================================================================ // // A_CStaffCheck // //============================================================================ void A_CStaffCheck(player_t * player, pspdef_t * psp) { mobj_t *pmo; int damage; int newLife; angle_t angle; int slope; int i; pmo = player->mo; damage = 20 + (P_Random() & 15); PuffType = MT_CSTAFFPUFF; for (i = 0; i < 3; i++) { angle = pmo->angle + i * (ANG45 / 16); slope = P_AimLineAttack(pmo, angle, 1.5 * MELEERANGE); if (linetarget) { P_LineAttack(pmo, angle, 1.5 * MELEERANGE, slope, damage); pmo->angle = R_PointToAngle2(pmo->x, pmo->y, linetarget->x, linetarget->y); if ((linetarget->player || linetarget->flags & MF_COUNTKILL) && (!(linetarget->flags2 & (MF2_DORMANT + MF2_INVULNERABLE)))) { newLife = player->health + (damage >> 3); newLife = newLife > 100 ? 100 : newLife; pmo->health = player->health = newLife; P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1); } player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; break; } angle = pmo->angle - i * (ANG45 / 16); slope = P_AimLineAttack(player->mo, angle, 1.5 * MELEERANGE); if (linetarget) { P_LineAttack(pmo, angle, 1.5 * MELEERANGE, slope, damage); pmo->angle = R_PointToAngle2(pmo->x, pmo->y, linetarget->x, linetarget->y); if (linetarget->player || linetarget->flags & MF_COUNTKILL) { newLife = player->health + (damage >> 4); newLife = newLife > 100 ? 100 : newLife; pmo->health = player->health = newLife; P_SetPsprite(player, ps_weapon, S_CSTAFFATK2_1); } player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; break; } } } //============================================================================ // // A_CStaffAttack // //============================================================================ void A_CStaffAttack(player_t * player, pspdef_t * psp) { mobj_t *mo; mobj_t *pmo; player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; pmo = player->mo; mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle - (ANG45 / 15)); if (mo) { mo->special2.i = 32; } mo = P_SPMAngle(pmo, MT_CSTAFF_MISSILE, pmo->angle + (ANG45 / 15)); if (mo) { mo->special2.i = 0; } S_StartSound(player->mo, SFX_CLERIC_CSTAFF_FIRE); } //============================================================================ // // A_CStaffMissileSlither // //============================================================================ void A_CStaffMissileSlither(mobj_t * actor) { fixed_t newX, newY; int weaveXY; int angle; weaveXY = actor->special2.i; angle = (actor->angle + ANG90) >> ANGLETOFINESHIFT; newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]); newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY]); weaveXY = (weaveXY + 3) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]); P_TryMove(actor, newX, newY); actor->special2.i = weaveXY; } //============================================================================ // // A_CStaffInitBlink // //============================================================================ void A_CStaffInitBlink(player_t * player, pspdef_t * psp) { player->mo->special1.i = (P_Random() >> 1) + 20; } //============================================================================ // // A_CStaffCheckBlink // //============================================================================ void A_CStaffCheckBlink(player_t * player, pspdef_t * psp) { if (!--player->mo->special1.i) { P_SetPsprite(player, ps_weapon, S_CSTAFFBLINK1); player->mo->special1.i = (P_Random() + 50) >> 2; } } //============================================================================ // // A_CFlameAttack // //============================================================================ #define FLAMESPEED (0.45*FRACUNIT) #define CFLAMERANGE (12*64*FRACUNIT) void A_CFlameAttack(player_t * player, pspdef_t * psp) { mobj_t *mo; mo = P_SpawnPlayerMissile(player->mo, MT_CFLAME_MISSILE); if (mo) { mo->thinker.function = P_BlasterMobjThinker; mo->special1.i = 2; } player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE); } //============================================================================ // // A_CFlamePuff // //============================================================================ void A_CFlamePuff(mobj_t * actor) { A_UnHideThing(actor); actor->momx = 0; actor->momy = 0; actor->momz = 0; S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE); } //============================================================================ // // A_CFlameMissile // //============================================================================ void A_CFlameMissile(mobj_t * actor) { int i; int an; fixed_t dist; mobj_t *mo; A_UnHideThing(actor); S_StartSound(actor, SFX_CLERIC_FLAME_EXPLODE); if (BlockingMobj && BlockingMobj->flags & MF_SHOOTABLE) { // Hit something, so spawn the flame circle around the thing dist = BlockingMobj->radius + 18 * FRACUNIT; for (i = 0; i < 4; i++) { an = (i * ANG45) >> ANGLETOFINESHIFT; mo = P_SpawnMobj(BlockingMobj->x + FixedMul(dist, finecosine[an]), BlockingMobj->y + FixedMul(dist, finesine[an]), BlockingMobj->z + 5 * FRACUNIT, MT_CIRCLEFLAME); if (mo) { mo->angle = an << ANGLETOFINESHIFT; mo->target = actor->target; mo->momx = mo->special1.i = FixedMul(FLAMESPEED, finecosine[an]); mo->momy = mo->special2.i = FixedMul(FLAMESPEED, finesine[an]); mo->tics -= P_Random() & 3; } mo = P_SpawnMobj(BlockingMobj->x - FixedMul(dist, finecosine[an]), BlockingMobj->y - FixedMul(dist, finesine[an]), BlockingMobj->z + 5 * FRACUNIT, MT_CIRCLEFLAME); if (mo) { mo->angle = ANG180 + (an << ANGLETOFINESHIFT); mo->target = actor->target; mo->momx = mo->special1.i = FixedMul(-FLAMESPEED, finecosine[an]); mo->momy = mo->special2.i = FixedMul(-FLAMESPEED, finesine[an]); mo->tics -= P_Random() & 3; } } P_SetMobjState(actor, S_FLAMEPUFF2_1); } } /* void A_CFlameAttack(player_t *player, pspdef_t *psp) { mobj_t *pmo; angle_t angle; int damage; int i; int an, an90; fixed_t dist; mobj_t *mo; pmo = player->mo; P_BulletSlope(pmo); damage = 25+HITDICE(3); angle = pmo->angle; if(player->refire) { angle += P_SubRandom()<<17; } P_AimLineAttack(pmo, angle, CFLAMERANGE); // Correctly set linetarget if(!linetarget) { angle += ANG1*2; P_AimLineAttack(pmo, angle, CFLAMERANGE); if(!linetarget) { angle -= ANG1*4; P_AimLineAttack(pmo, angle, CFLAMERANGE); if(!linetarget) { angle += ANG1*2; } } } if(linetarget) { PuffType = MT_FLAMEPUFF2; } else { PuffType = MT_FLAMEPUFF; } P_LineAttack(pmo, angle, CFLAMERANGE, bulletslope, damage); if(linetarget) { // Hit something, so spawn the flame circle around the thing dist = linetarget->radius+18*FRACUNIT; for(i = 0; i < 4; i++) { an = (i*ANG45)>>ANGLETOFINESHIFT; an90 = (i*ANG45+ANG90)>>ANGLETOFINESHIFT; mo = P_SpawnMobj(linetarget->x+FixedMul(dist, finecosine[an]), linetarget->y+FixedMul(dist, finesine[an]), linetarget->z+5*FRACUNIT, MT_CIRCLEFLAME); if(mo) { mo->angle = an<target = pmo; mo->momx = mo->special1.i = FixedMul(FLAMESPEED, finecosine[an]); mo->momy = mo->special2.i = FixedMul(FLAMESPEED, finesine[an]); mo->tics -= P_Random()&3; } mo = P_SpawnMobj(linetarget->x-FixedMul(dist, finecosine[an]), linetarget->y-FixedMul(dist, finesine[an]), linetarget->z+5*FRACUNIT, MT_CIRCLEFLAME); if(mo) { mo->angle = ANG180+(an<target = pmo; mo->momx = mo->special1.i = FixedMul(-FLAMESPEED, finecosine[an]); mo->momy = mo->special2.i = FixedMul(-FLAMESPEED, finesine[an]); mo->tics -= P_Random()&3; } } } // Create a line of flames from the player to the flame puff CFlameCreateFlames(player->mo); player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; S_StartSound(player->mo, SFX_CLERIC_FLAME_FIRE); } */ //============================================================================ // // A_CFlameRotate // //============================================================================ #define FLAMEROTSPEED 2*FRACUNIT void A_CFlameRotate(mobj_t * actor) { int an; an = (actor->angle + ANG90) >> ANGLETOFINESHIFT; actor->momx = actor->special1.i + FixedMul(FLAMEROTSPEED, finecosine[an]); actor->momy = actor->special2.i + FixedMul(FLAMEROTSPEED, finesine[an]); actor->angle += ANG90 / 15; } //============================================================================ // // A_CHolyAttack3 // // Spawns the spirits //============================================================================ void A_CHolyAttack3(mobj_t * actor) { P_SpawnMissile(actor, actor->target, MT_HOLY_MISSILE); S_StartSound(actor, SFX_CHOLY_FIRE); } //============================================================================ // // A_CHolyAttack2 // // Spawns the spirits //============================================================================ void A_CHolyAttack2(mobj_t * actor) { int j; int i; int r; mobj_t *mo; mobj_t *tail, *next; for (j = 0; j < 4; j++) { mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_FX); if (!mo) { continue; } switch (j) { // float bob index case 0: mo->special2.i = P_Random() & 7; // upper-left break; case 1: mo->special2.i = 32 + (P_Random() & 7); // upper-right break; case 2: mo->special2.i = (32 + (P_Random() & 7)) << 16; // lower-left break; case 3: r = P_Random(); mo->special2.i = ((32 + (r & 7)) << 16) + 32 + (P_Random() & 7); break; } mo->z = actor->z; mo->angle = actor->angle + (ANG45 + ANG45 / 2) - ANG45 * j; P_ThrustMobj(mo, mo->angle, mo->info->speed); mo->target = actor->target; mo->args[0] = 10; // initial turn value mo->args[1] = 0; // initial look angle if (deathmatch) { // Ghosts last slightly less longer in DeathMatch mo->health = 85; } if (linetarget) { mo->special1.m = linetarget; mo->flags |= MF_NOCLIP | MF_SKULLFLY; mo->flags &= ~MF_MISSILE; } tail = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL); tail->special2.m = mo; // parent for (i = 1; i < 3; i++) { next = P_SpawnMobj(mo->x, mo->y, mo->z, MT_HOLY_TAIL); P_SetMobjState(next, next->info->spawnstate + 1); tail->special1.m = next; tail = next; } tail->special1.m = NULL; // last tail bit } } //============================================================================ // // A_CHolyAttack // //============================================================================ void A_CHolyAttack(player_t * player, pspdef_t * psp) { player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; player->mana[MANA_2] -= WeaponManaUse[player->class][player->readyweapon]; P_SpawnPlayerMissile(player->mo, MT_HOLY_MISSILE); if (player == &players[consoleplayer]) { player->damagecount = 0; player->bonuscount = 0; I_SetPalette((byte *) W_CacheLumpNum(W_GetNumForName("playpal"), PU_CACHE) + STARTHOLYPAL * 768); } S_StartSound(player->mo, SFX_CHOLY_FIRE); } //============================================================================ // // A_CHolyPalette // //============================================================================ void A_CHolyPalette(player_t * player, pspdef_t * psp) { int pal; if (player == &players[consoleplayer]) { pal = STARTHOLYPAL + psp->state - (&states[S_CHOLYATK_6]); if (pal == STARTHOLYPAL + 3) { // reset back to original playpal pal = 0; } I_SetPalette((byte *) W_CacheLumpNum(W_GetNumForName("playpal"), PU_CACHE) + pal * 768); } } //============================================================================ // // CHolyFindTarget // //============================================================================ static void CHolyFindTarget(mobj_t * actor) { mobj_t *target; target = P_RoughMonsterSearch(actor, 6); if (target != NULL) { actor->special1.m = target; actor->flags |= MF_NOCLIP | MF_SKULLFLY; actor->flags &= ~MF_MISSILE; } } //============================================================================ // // CHolySeekerMissile // // Similar to P_SeekerMissile, but seeks to a random Z on the target //============================================================================ static void CHolySeekerMissile(mobj_t * actor, angle_t thresh, angle_t turnMax) { int dir; int dist; angle_t delta; angle_t angle; mobj_t *target; fixed_t newZ; fixed_t deltaZ; target = actor->special1.m; if (target == NULL) { return; } if (!(target->flags & MF_SHOOTABLE) || (!(target->flags & MF_COUNTKILL) && !target->player)) { // Target died/target isn't a player or creature actor->special1.m = NULL; actor->flags &= ~(MF_NOCLIP | MF_SKULLFLY); actor->flags |= MF_MISSILE; CHolyFindTarget(actor); return; } dir = P_FaceMobj(actor, target, &delta); if (delta > thresh) { delta >>= 1; if (delta > turnMax) { delta = turnMax; } } if (dir) { // Turn clockwise actor->angle += delta; } else { // Turn counter clockwise actor->angle -= delta; } angle = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(actor->info->speed, finecosine[angle]); actor->momy = FixedMul(actor->info->speed, finesine[angle]); if (!(leveltime & 15) || actor->z > target->z + (target->height) || actor->z + actor->height < target->z) { newZ = target->z + ((P_Random() * target->height) >> 8); deltaZ = newZ - actor->z; if (abs(deltaZ) > 15 * FRACUNIT) { if (deltaZ > 0) { deltaZ = 15 * FRACUNIT; } else { deltaZ = -15 * FRACUNIT; } } dist = P_AproxDistance(target->x - actor->x, target->y - actor->y); dist = dist / actor->info->speed; if (dist < 1) { dist = 1; } actor->momz = deltaZ / dist; } return; } //============================================================================ // // A_CHolyWeave // //============================================================================ static void CHolyWeave(mobj_t * actor) { fixed_t newX, newY; int weaveXY, weaveZ; int angle; weaveXY = actor->special2.i >> 16; weaveZ = actor->special2.i & 0xFFFF; angle = (actor->angle + ANG90) >> ANGLETOFINESHIFT; newX = actor->x - FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 2); newY = actor->y - FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 2); weaveXY = (weaveXY + (P_Random() % 5)) & 63; newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY] << 2); newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY] << 2); P_TryMove(actor, newX, newY); actor->z -= FloatBobOffsets[weaveZ] << 1; weaveZ = (weaveZ + (P_Random() % 5)) & 63; actor->z += FloatBobOffsets[weaveZ] << 1; actor->special2.i = weaveZ + (weaveXY << 16); } //============================================================================ // // A_CHolySeek // //============================================================================ void A_CHolySeek(mobj_t * actor) { actor->health--; if (actor->health <= 0) { actor->momx >>= 2; actor->momy >>= 2; actor->momz = 0; P_SetMobjState(actor, actor->info->deathstate); actor->tics -= P_Random() & 3; return; } if (actor->special1.m) { CHolySeekerMissile(actor, actor->args[0] * ANG1, actor->args[0] * ANG1 * 2); if (!((leveltime + 7) & 15)) { actor->args[0] = 5 + (P_Random() / 20); } } CHolyWeave(actor); } //============================================================================ // // CHolyTailFollow // //============================================================================ static void CHolyTailFollow(mobj_t * actor, fixed_t dist) { mobj_t *child; int an; fixed_t oldDistance, newDistance; child = actor->special1.m; if (child) { an = R_PointToAngle2(actor->x, actor->y, child->x, child->y) >> ANGLETOFINESHIFT; oldDistance = P_AproxDistance(child->x - actor->x, child->y - actor->y); if (P_TryMove (child, actor->x + FixedMul(dist, finecosine[an]), actor->y + FixedMul(dist, finesine[an]))) { newDistance = P_AproxDistance(child->x - actor->x, child->y - actor->y) - FRACUNIT; if (oldDistance < FRACUNIT) { if (child->z < actor->z) { child->z = actor->z - dist; } else { child->z = actor->z + dist; } } else { child->z = actor->z + FixedMul(FixedDiv(newDistance, oldDistance), child->z - actor->z); } } CHolyTailFollow(child, dist - FRACUNIT); } } //============================================================================ // // CHolyTailRemove // //============================================================================ static void CHolyTailRemove(mobj_t * actor) { mobj_t *child; child = actor->special1.m; if (child) { CHolyTailRemove(child); } P_RemoveMobj(actor); } //============================================================================ // // A_CHolyTail // //============================================================================ void A_CHolyTail(mobj_t * actor) { mobj_t *parent; parent = actor->special2.m; if (parent) { if (parent->state >= &states[parent->info->deathstate]) { // Ghost removed, so remove all tail parts CHolyTailRemove(actor); return; } else if (P_TryMove(actor, parent->x - FixedMul(14 * FRACUNIT, finecosine[parent-> angle >> ANGLETOFINESHIFT]), parent->y - FixedMul(14 * FRACUNIT, finesine[parent-> angle >> ANGLETOFINESHIFT]))) { actor->z = parent->z - 5 * FRACUNIT; } CHolyTailFollow(actor, 10 * FRACUNIT); } } //============================================================================ // // A_CHolyCheckScream // //============================================================================ void A_CHolyCheckScream(mobj_t * actor) { A_CHolySeek(actor); if (P_Random() < 20) { S_StartSound(actor, SFX_SPIRIT_ACTIVE); } if (!actor->special1.m) { CHolyFindTarget(actor); } } //============================================================================ // // A_CHolySpawnPuff // //============================================================================ void A_CHolySpawnPuff(mobj_t * actor) { P_SpawnMobj(actor->x, actor->y, actor->z, MT_HOLY_MISSILE_PUFF); } //---------------------------------------------------------------------------- // // PROC A_FireConePL1 // //---------------------------------------------------------------------------- #define SHARDSPAWN_LEFT 1 #define SHARDSPAWN_RIGHT 2 #define SHARDSPAWN_UP 4 #define SHARDSPAWN_DOWN 8 void A_FireConePL1(player_t * player, pspdef_t * psp) { angle_t angle; int damage; int i; mobj_t *pmo, *mo; int conedone = false; pmo = player->mo; player->mana[MANA_1] -= WeaponManaUse[player->class][player->readyweapon]; S_StartSound(pmo, SFX_MAGE_SHARDS_FIRE); damage = 90 + (P_Random() & 15); for (i = 0; i < 16; i++) { angle = pmo->angle + i * (ANG45 / 16); P_AimLineAttack(pmo, angle, MELEERANGE); if (linetarget) { pmo->flags2 |= MF2_ICEDAMAGE; P_DamageMobj(linetarget, pmo, pmo, damage); pmo->flags2 &= ~MF2_ICEDAMAGE; conedone = true; break; } } // didn't find any creatures, so fire projectiles if (!conedone) { mo = P_SpawnPlayerMissile(pmo, MT_SHARDFX1); if (mo) { mo->special1.i = SHARDSPAWN_LEFT | SHARDSPAWN_DOWN | SHARDSPAWN_UP | SHARDSPAWN_RIGHT; mo->special2.i = 3; // Set sperm count (levels of reproductivity) mo->target = pmo; mo->args[0] = 3; // Mark Initial shard as super damage } } } void A_ShedShard(mobj_t * actor) { mobj_t *mo; int spawndir = actor->special1.i; int spermcount = actor->special2.i; if (spermcount <= 0) return; // No sperm left actor->special2.i = 0; spermcount--; // every so many calls, spawn a new missile in it's set directions if (spawndir & SHARDSPAWN_LEFT) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle + (ANG45 / 9), 0, (20 + 2 * spermcount) << FRACBITS); if (mo) { mo->special1.i = SHARDSPAWN_LEFT; mo->special2.i = spermcount; mo->momz = actor->momz; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } if (spawndir & SHARDSPAWN_RIGHT) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle - (ANG45 / 9), 0, (20 + 2 * spermcount) << FRACBITS); if (mo) { mo->special1.i = SHARDSPAWN_RIGHT; mo->special2.i = spermcount; mo->momz = actor->momz; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } if (spawndir & SHARDSPAWN_UP) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle, 0, (15 + 2 * spermcount) << FRACBITS); if (mo) { mo->momz = actor->momz; mo->z += 8 * FRACUNIT; if (spermcount & 1) // Every other reproduction mo->special1.i = SHARDSPAWN_UP | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; else mo->special1.i = SHARDSPAWN_UP; mo->special2.i = spermcount; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } if (spawndir & SHARDSPAWN_DOWN) { mo = P_SpawnMissileAngleSpeed(actor, MT_SHARDFX1, actor->angle, 0, (15 + 2 * spermcount) << FRACBITS); if (mo) { mo->momz = actor->momz; mo->z -= 4 * FRACUNIT; if (spermcount & 1) // Every other reproduction mo->special1.i = SHARDSPAWN_DOWN | SHARDSPAWN_LEFT | SHARDSPAWN_RIGHT; else mo->special1.i = SHARDSPAWN_DOWN; mo->special2.i = spermcount; mo->target = actor->target; mo->args[0] = (spermcount == 3) ? 2 : 0; } } } //---------------------------------------------------------------------------- // // PROC A_HideInCeiling // //---------------------------------------------------------------------------- /* void A_HideInCeiling(mobj_t *actor) { actor->z = actor->ceilingz+4*FRACUNIT; } */ //---------------------------------------------------------------------------- // // PROC A_FloatPuff // //---------------------------------------------------------------------------- /* void A_FloatPuff(mobj_t *puff) { puff->momz += 1.8*FRACUNIT; } */ void A_Light0(player_t * player, pspdef_t * psp) { player->extralight = 0; } /* void A_Light1(player_t *player, pspdef_t *psp) { player->extralight = 1; } */ /* void A_Light2(player_t *player, pspdef_t *psp) { player->extralight = 2; } */ //------------------------------------------------------------------------ // // PROC P_SetupPsprites // // Called at start of level for each player // //------------------------------------------------------------------------ void P_SetupPsprites(player_t * player) { int i; // Remove all psprites for (i = 0; i < NUMPSPRITES; i++) { player->psprites[i].state = NULL; } // Spawn the ready weapon player->pendingweapon = player->readyweapon; P_BringUpWeapon(player); } //------------------------------------------------------------------------ // // PROC P_MovePsprites // // Called every tic by player thinking routine // //------------------------------------------------------------------------ void P_MovePsprites(player_t * player) { int i; pspdef_t *psp; state_t *state; psp = &player->psprites[0]; for (i = 0; i < NUMPSPRITES; i++, psp++) { if ((state = psp->state) != 0) // a null state means not active { // drop tic count and possibly change state if (psp->tics != -1) // a -1 tic count never changes { psp->tics--; if (!psp->tics) { P_SetPsprite(player, i, psp->state->nextstate); } } } } player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_setup.c000066400000000000000000000752521360717211000217700ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include #include #include "h2def.h" #include "i_system.h" #include "m_argv.h" #include "m_bbox.h" #include "m_misc.h" #include "i_swap.h" #include "s_sound.h" #include "p_local.h" // MACROS ------------------------------------------------------------------ #define MAPINFO_SCRIPT_NAME "MAPINFO" #define MCMD_SKY1 1 #define MCMD_SKY2 2 #define MCMD_LIGHTNING 3 #define MCMD_FADETABLE 4 #define MCMD_DOUBLESKY 5 #define MCMD_CLUSTER 6 #define MCMD_WARPTRANS 7 #define MCMD_NEXT 8 #define MCMD_CDTRACK 9 #define MCMD_CD_STARTTRACK 10 #define MCMD_CD_END1TRACK 11 #define MCMD_CD_END2TRACK 12 #define MCMD_CD_END3TRACK 13 #define MCMD_CD_INTERTRACK 14 #define MCMD_CD_TITLETRACK 15 #define UNKNOWN_MAP_NAME "DEVELOPMENT MAP" #define DEFAULT_SKY_NAME "SKY1" #define DEFAULT_SONG_LUMP "DEFSONG" #define DEFAULT_FADE_TABLE "COLORMAP" // TYPES ------------------------------------------------------------------- typedef struct mapInfo_s mapInfo_t; struct mapInfo_s { short cluster; short warpTrans; short nextMap; short cdTrack; char name[32]; short sky1Texture; short sky2Texture; fixed_t sky1ScrollDelta; fixed_t sky2ScrollDelta; boolean doubleSky; boolean lightning; int fadetable; char songLump[10]; }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void P_SpawnMapThing(mapthing_t * mthing); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static int QualifyMap(int map); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- int MapCount; mapthing_t deathmatchstarts[MAXDEATHMATCHSTARTS], *deathmatch_p; mapthing_t playerstarts[MAX_PLAYER_STARTS][MAXPLAYERS]; 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; short *blockmaplump; // offsets in blockmap are from here short *blockmap; int bmapwidth, bmapheight; // in mapblocks fixed_t bmaporgx, bmaporgy; // origin of block map mobj_t **blocklinks; // for thing chains byte *rejectmatrix; // for fast sight rejection // PRIVATE DATA DEFINITIONS ------------------------------------------------ static mapInfo_t MapInfo[99]; static const char *MapCmdNames[] = { "SKY1", "SKY2", "DOUBLESKY", "LIGHTNING", "FADETABLE", "CLUSTER", "WARPTRANS", "NEXT", "CDTRACK", "CD_START_TRACK", "CD_END1_TRACK", "CD_END2_TRACK", "CD_END3_TRACK", "CD_INTERMISSION_TRACK", "CD_TITLE_TRACK", NULL }; static int MapCmdIDs[] = { MCMD_SKY1, MCMD_SKY2, MCMD_DOUBLESKY, MCMD_LIGHTNING, MCMD_FADETABLE, MCMD_CLUSTER, MCMD_WARPTRANS, MCMD_NEXT, MCMD_CDTRACK, MCMD_CD_STARTTRACK, MCMD_CD_END1TRACK, MCMD_CD_END2TRACK, MCMD_CD_END3TRACK, MCMD_CD_INTERTRACK, MCMD_CD_TITLETRACK }; static int cd_NonLevelTracks[6]; // Non-level specific song cd track numbers // CODE -------------------------------------------------------------------- /* ================= = = P_LoadVertexes = ================= */ void P_LoadVertexes(int lump) { byte *data; int i; mapvertex_t *ml; vertex_t *li; numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t); vertexes = Z_Malloc(numvertexes * sizeof(vertex_t), PU_LEVEL, 0); data = W_CacheLumpNum(lump, PU_STATIC); ml = (mapvertex_t *) data; li = vertexes; for (i = 0; i < numvertexes; i++, li++, ml++) { li->x = SHORT(ml->x) << FRACBITS; li->y = SHORT(ml->y) << FRACBITS; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSegs = ================= */ void P_LoadSegs(int lump) { byte *data; int i; mapseg_t *ml; seg_t *li; line_t *ldef; int linedef, side; numsegs = W_LumpLength(lump) / sizeof(mapseg_t); segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); memset(segs, 0, numsegs * sizeof(seg_t)); data = W_CacheLumpNum(lump, PU_STATIC); ml = (mapseg_t *) data; li = segs; for (i = 0; i < numsegs; i++, li++, ml++) { li->v1 = &vertexes[SHORT(ml->v1)]; li->v2 = &vertexes[SHORT(ml->v2)]; li->angle = (SHORT(ml->angle)) << 16; li->offset = (SHORT(ml->offset)) << 16; linedef = SHORT(ml->linedef); ldef = &lines[linedef]; li->linedef = ldef; side = SHORT(ml->side); li->sidedef = &sides[ldef->sidenum[side]]; li->frontsector = sides[ldef->sidenum[side]].sector; if (ldef->flags & ML_TWOSIDED) li->backsector = sides[ldef->sidenum[side ^ 1]].sector; else li->backsector = 0; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSubsectors = ================= */ void P_LoadSubsectors(int lump) { byte *data; int i; mapsubsector_t *ms; subsector_t *ss; numsubsectors = W_LumpLength(lump) / sizeof(mapsubsector_t); subsectors = Z_Malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); data = W_CacheLumpNum(lump, PU_STATIC); ms = (mapsubsector_t *) data; memset(subsectors, 0, numsubsectors * sizeof(subsector_t)); ss = subsectors; for (i = 0; i < numsubsectors; i++, ss++, ms++) { ss->numlines = SHORT(ms->numsegs); ss->firstline = SHORT(ms->firstseg); } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSectors = ================= */ void P_LoadSectors(int lump) { byte *data; int i; mapsector_t *ms; sector_t *ss; numsectors = W_LumpLength(lump) / sizeof(mapsector_t); sectors = Z_Malloc(numsectors * sizeof(sector_t), PU_LEVEL, 0); memset(sectors, 0, numsectors * sizeof(sector_t)); data = W_CacheLumpNum(lump, PU_STATIC); ms = (mapsector_t *) data; ss = sectors; for (i = 0; i < numsectors; i++, ss++, ms++) { ss->floorheight = SHORT(ms->floorheight) << FRACBITS; ss->ceilingheight = SHORT(ms->ceilingheight) << FRACBITS; ss->floorpic = R_FlatNumForName(ms->floorpic); ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); ss->lightlevel = SHORT(ms->lightlevel); ss->special = SHORT(ms->special); ss->tag = SHORT(ms->tag); ss->thinglist = NULL; ss->seqType = SEQTYPE_STONE; // default seqType } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadNodes = ================= */ void P_LoadNodes(int lump) { byte *data; int i, j, k; mapnode_t *mn; node_t *no; numnodes = W_LumpLength(lump) / sizeof(mapnode_t); nodes = Z_Malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); data = W_CacheLumpNum(lump, PU_STATIC); mn = (mapnode_t *) data; no = nodes; for (i = 0; i < numnodes; i++, no++, mn++) { no->x = SHORT(mn->x) << FRACBITS; no->y = SHORT(mn->y) << FRACBITS; no->dx = SHORT(mn->dx) << FRACBITS; no->dy = SHORT(mn->dy) << FRACBITS; for (j = 0; j < 2; j++) { no->children[j] = SHORT(mn->children[j]); for (k = 0; k < 4; k++) no->bbox[j][k] = SHORT(mn->bbox[j][k]) << FRACBITS; } } W_ReleaseLumpNum(lump); } //========================================================================== // // P_LoadThings // //========================================================================== void P_LoadThings(int lump) { byte *data; int i; mapthing_t spawnthing; mapthing_t *mt; int numthings; int playerCount; int deathSpotsCount; data = W_CacheLumpNum(lump, PU_STATIC); numthings = W_LumpLength(lump) / sizeof(mapthing_t); mt = (mapthing_t *) data; for (i = 0; i < numthings; i++, mt++) { spawnthing.tid = SHORT(mt->tid); spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); spawnthing.height = SHORT(mt->height); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); spawnthing.options = SHORT(mt->options); spawnthing.special = mt->special; spawnthing.arg1 = mt->arg1; spawnthing.arg2 = mt->arg2; spawnthing.arg3 = mt->arg3; spawnthing.arg4 = mt->arg4; spawnthing.arg5 = mt->arg5; P_SpawnMapThing(&spawnthing); } P_CreateTIDList(); P_InitCreatureCorpseQueue(false); // false = do NOT scan for corpses W_ReleaseLumpNum(lump); if (!deathmatch) { // Don't need to check deathmatch spots return; } playerCount = 0; for (i = 0; i < maxplayers; i++) { playerCount += playeringame[i]; } deathSpotsCount = deathmatch_p - deathmatchstarts; if (deathSpotsCount < playerCount) { I_Error("P_LoadThings: Player count (%d) exceeds deathmatch " "spots (%d)", playerCount, deathSpotsCount); } } /* ================= = = P_LoadLineDefs = ================= */ void P_LoadLineDefs(int lump) { byte *data; int i; maplinedef_t *mld; line_t *ld; vertex_t *v1, *v2; numlines = W_LumpLength(lump) / sizeof(maplinedef_t); lines = Z_Malloc(numlines * sizeof(line_t), PU_LEVEL, 0); memset(lines, 0, numlines * sizeof(line_t)); data = W_CacheLumpNum(lump, PU_STATIC); mld = (maplinedef_t *) data; ld = lines; for (i = 0; i < numlines; i++, mld++, ld++) { ld->flags = SHORT(mld->flags); // Old line special info ... //ld->special = SHORT(mld->special); //ld->tag = SHORT(mld->tag); // New line special info ... ld->special = mld->special; ld->arg1 = mld->arg1; ld->arg2 = mld->arg2; ld->arg3 = mld->arg3; ld->arg4 = mld->arg4; ld->arg5 = mld->arg5; v1 = ld->v1 = &vertexes[SHORT(mld->v1)]; v2 = ld->v2 = &vertexes[SHORT(mld->v2)]; ld->dx = v2->x - v1->x; ld->dy = v2->y - v1->y; if (!ld->dx) ld->slopetype = ST_VERTICAL; else if (!ld->dy) ld->slopetype = ST_HORIZONTAL; else { if (FixedDiv(ld->dy, ld->dx) > 0) ld->slopetype = ST_POSITIVE; else ld->slopetype = ST_NEGATIVE; } if (v1->x < v2->x) { ld->bbox[BOXLEFT] = v1->x; ld->bbox[BOXRIGHT] = v2->x; } else { ld->bbox[BOXLEFT] = v2->x; ld->bbox[BOXRIGHT] = v1->x; } if (v1->y < v2->y) { ld->bbox[BOXBOTTOM] = v1->y; ld->bbox[BOXTOP] = v2->y; } else { ld->bbox[BOXBOTTOM] = v2->y; ld->bbox[BOXTOP] = v1->y; } ld->sidenum[0] = SHORT(mld->sidenum[0]); ld->sidenum[1] = SHORT(mld->sidenum[1]); if (ld->sidenum[0] != -1) ld->frontsector = sides[ld->sidenum[0]].sector; else ld->frontsector = 0; if (ld->sidenum[1] != -1) ld->backsector = sides[ld->sidenum[1]].sector; else ld->backsector = 0; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadSideDefs = ================= */ void P_LoadSideDefs(int lump) { byte *data; int i; mapsidedef_t *msd; side_t *sd; numsides = W_LumpLength(lump) / sizeof(mapsidedef_t); sides = Z_Malloc(numsides * sizeof(side_t), PU_LEVEL, 0); memset(sides, 0, numsides * sizeof(side_t)); data = W_CacheLumpNum(lump, PU_STATIC); msd = (mapsidedef_t *) data; sd = sides; for (i = 0; i < numsides; i++, msd++, sd++) { sd->textureoffset = SHORT(msd->textureoffset) << FRACBITS; sd->rowoffset = SHORT(msd->rowoffset) << FRACBITS; sd->toptexture = R_TextureNumForName(msd->toptexture); sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); sd->midtexture = R_TextureNumForName(msd->midtexture); sd->sector = §ors[SHORT(msd->sector)]; } W_ReleaseLumpNum(lump); } /* ================= = = P_LoadBlockMap = ================= */ void P_LoadBlockMap(int lump) { int i, count; int lumplen; lumplen = W_LumpLength(lump); blockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL); W_ReadLump(lump, blockmaplump); blockmap = blockmaplump + 4; // Swap all short integers to native byte ordering: count = lumplen / 2; for (i = 0; i < count; i++) blockmaplump[i] = SHORT(blockmaplump[i]); bmaporgx = blockmaplump[0] << FRACBITS; bmaporgy = blockmaplump[1] << FRACBITS; bmapwidth = blockmaplump[2]; bmapheight = blockmaplump[3]; // clear out mobj chains count = sizeof(*blocklinks) * bmapwidth * bmapheight; blocklinks = Z_Malloc(count, PU_LEVEL, 0); memset(blocklinks, 0, count); } /* ================= = = P_GroupLines = = Builds sector line lists and subsector sector numbers = Finds block bounding boxes for sectors ================= */ void P_GroupLines(void) { line_t **linebuffer; int i, j, total; line_t *li; sector_t *sector; subsector_t *ss; seg_t *seg; fixed_t bbox[4]; int block; // look up sector number for each subsector ss = subsectors; for (i = 0; i < numsubsectors; i++, ss++) { seg = &segs[ss->firstline]; ss->sector = seg->sidedef->sector; } // count number of lines in each sector li = lines; total = 0; for (i = 0; i < numlines; i++, li++) { total++; li->frontsector->linecount++; if (li->backsector && li->backsector != li->frontsector) { li->backsector->linecount++; total++; } } // build line tables for each sector linebuffer = Z_Malloc(total * sizeof(line_t *), PU_LEVEL, 0); sector = sectors; for (i = 0; i < numsectors; i++, sector++) { M_ClearBox(bbox); sector->lines = linebuffer; li = lines; for (j = 0; j < numlines; j++, li++) { if (li->frontsector == sector || li->backsector == sector) { *linebuffer++ = li; M_AddToBox(bbox, li->v1->x, li->v1->y); M_AddToBox(bbox, li->v2->x, li->v2->y); } } if (linebuffer - sector->lines != sector->linecount) I_Error("P_GroupLines: miscounted"); // set the degenmobj_t to the middle of the bounding box sector->soundorg.x = (bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2; sector->soundorg.y = (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2; // adjust bounding box to map blocks block = (bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; block = block >= bmapheight ? bmapheight - 1 : block; sector->blockbox[BOXTOP] = block; block = (bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXBOTTOM] = block; block = (bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; block = block >= bmapwidth ? bmapwidth - 1 : block; sector->blockbox[BOXRIGHT] = block; block = (bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXLEFT] = block; } } //============================================================================= /* ================= = = P_SetupLevel = ================= */ void P_SetupLevel(int episode, int map, int playermask, skill_t skill) { int i; int parm; char lumpname[9]; int lumpnum; mobj_t *mobj; for (i = 0; i < maxplayers; i++) { players[i].killcount = players[i].secretcount = players[i].itemcount = 0; } players[consoleplayer].viewz = 1; // will be set by player think // Waiting-for-level-load song; not played if playing music from CD // (the seek time will be so long it will just make loading take // longer) if (!cdmusic) { S_StartSongName("chess", true); } Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); P_InitThinkers(); leveltime = 0; M_snprintf(lumpname, sizeof(lumpname), "MAP%02d", map); lumpnum = W_GetNumForName(lumpname); // // Begin processing map lumps // Note: most of this ordering is important // P_LoadBlockMap(lumpnum + ML_BLOCKMAP); P_LoadVertexes(lumpnum + ML_VERTEXES); P_LoadSectors(lumpnum + ML_SECTORS); P_LoadSideDefs(lumpnum + ML_SIDEDEFS); P_LoadLineDefs(lumpnum + ML_LINEDEFS); P_LoadSubsectors(lumpnum + ML_SSECTORS); P_LoadNodes(lumpnum + ML_NODES); P_LoadSegs(lumpnum + ML_SEGS); rejectmatrix = W_CacheLumpNum(lumpnum + ML_REJECT, PU_LEVEL); P_GroupLines(); bodyqueslot = 0; po_NumPolyobjs = 0; deathmatch_p = deathmatchstarts; P_LoadThings(lumpnum + ML_THINGS); PO_Init(lumpnum + ML_THINGS); // Initialize the polyobjs P_LoadACScripts(lumpnum + ML_BEHAVIOR); // ACS object code // // End of map lump processing // // If deathmatch, randomly spawn the active players TimerGame = 0; if (deathmatch) { for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { // must give a player spot before deathmatchspawn mobj = P_SpawnMobj(playerstarts[0][i].x << 16, playerstarts[0][i].y << 16, 0, MT_PLAYER_FIGHTER); players[i].mo = mobj; G_DeathMatchSpawnPlayer(i); P_RemoveMobj(mobj); } } //! // @arg // @category net // @vanilla // // For multiplayer games: exit each level after n minutes. // parm = M_CheckParmWithArgs("-timer", 1); if (parm) { TimerGame = atoi(myargv[parm + 1]) * 35 * 60; } } // set up world state P_SpawnSpecials(); // build subsector connect matrix // P_ConnectSubsectors (); // Load colormap and set the fullbright flag i = P_GetMapFadeTable(gamemap); W_ReadLump(i, colormaps); if (i == W_GetNumForName("COLORMAP")) { LevelUseFullBright = true; } else { // Probably fog ... don't use fullbright sprites LevelUseFullBright = false; } // preload graphics if (precache) R_PrecacheLevel(); // Check if the level is a lightning level P_InitLightning(); S_StopAllSound(); SN_StopAllSequences(); S_StartSong(gamemap, true); //printf ("free memory: 0x%x\n", Z_FreeMemory()); } //========================================================================== // // InitMapInfo // //========================================================================== static void InitMapInfo(void) { int map; int mapMax; int mcmdValue; mapInfo_t *info; char songMulch[10]; char *default_sky_name = DEFAULT_SKY_NAME; mapMax = 1; if (gamemode == shareware) { default_sky_name = "SKY2"; } // Put defaults into MapInfo[0] info = MapInfo; info->cluster = 0; info->warpTrans = 0; info->nextMap = 1; // Always go to map 1 if not specified info->cdTrack = 1; info->sky1Texture = R_TextureNumForName(default_sky_name); info->sky2Texture = info->sky1Texture; info->sky1ScrollDelta = 0; info->sky2ScrollDelta = 0; info->doubleSky = false; info->lightning = false; info->fadetable = W_GetNumForName(DEFAULT_FADE_TABLE); M_StringCopy(info->name, UNKNOWN_MAP_NAME, sizeof(info->name)); // M_StringCopy(info->songLump, DEFAULT_SONG_LUMP, sizeof(info->songLump)); SC_Open(MAPINFO_SCRIPT_NAME); while (SC_GetString()) { if (SC_Compare("MAP") == false) { SC_ScriptError(NULL); } SC_MustGetNumber(); if (sc_Number < 1 || sc_Number > 99) { // SC_ScriptError(NULL); } map = sc_Number; info = &MapInfo[map]; // Save song lump name M_StringCopy(songMulch, info->songLump, sizeof(songMulch)); // Copy defaults to current map definition memcpy(info, &MapInfo[0], sizeof(*info)); // Restore song lump name M_StringCopy(info->songLump, songMulch, sizeof(info->songLump)); // The warp translation defaults to the map number info->warpTrans = map; // Map name must follow the number SC_MustGetString(); M_StringCopy(info->name, sc_String, sizeof(info->name)); // Process optional tokens while (SC_GetString()) { if (SC_Compare("MAP")) { // Start next map definition SC_UnGet(); break; } mcmdValue = MapCmdIDs[SC_MustMatchString(MapCmdNames)]; switch (mcmdValue) { case MCMD_CLUSTER: SC_MustGetNumber(); info->cluster = sc_Number; break; case MCMD_WARPTRANS: SC_MustGetNumber(); info->warpTrans = sc_Number; break; case MCMD_NEXT: SC_MustGetNumber(); info->nextMap = sc_Number; break; case MCMD_CDTRACK: SC_MustGetNumber(); info->cdTrack = sc_Number; break; case MCMD_SKY1: SC_MustGetString(); info->sky1Texture = R_TextureNumForName(sc_String); SC_MustGetNumber(); info->sky1ScrollDelta = sc_Number << 8; break; case MCMD_SKY2: SC_MustGetString(); info->sky2Texture = R_TextureNumForName(sc_String); SC_MustGetNumber(); info->sky2ScrollDelta = sc_Number << 8; break; case MCMD_DOUBLESKY: info->doubleSky = true; break; case MCMD_LIGHTNING: info->lightning = true; break; case MCMD_FADETABLE: SC_MustGetString(); info->fadetable = W_GetNumForName(sc_String); break; case MCMD_CD_STARTTRACK: case MCMD_CD_END1TRACK: case MCMD_CD_END2TRACK: case MCMD_CD_END3TRACK: case MCMD_CD_INTERTRACK: case MCMD_CD_TITLETRACK: SC_MustGetNumber(); cd_NonLevelTracks[mcmdValue - MCMD_CD_STARTTRACK] = sc_Number; break; } } mapMax = map > mapMax ? map : mapMax; } SC_Close(); MapCount = mapMax; } //========================================================================== // // P_GetMapCluster // //========================================================================== int P_GetMapCluster(int map) { return MapInfo[QualifyMap(map)].cluster; } //========================================================================== // // P_GetMapCDTrack // //========================================================================== int P_GetMapCDTrack(int map) { return MapInfo[QualifyMap(map)].cdTrack; } //========================================================================== // // P_GetMapWarpTrans // //========================================================================== int P_GetMapWarpTrans(int map) { return MapInfo[QualifyMap(map)].warpTrans; } //========================================================================== // // P_GetMapNextMap // //========================================================================== int P_GetMapNextMap(int map) { return MapInfo[QualifyMap(map)].nextMap; } //========================================================================== // // P_TranslateMap // // Returns the actual map number given a warp map number. // //========================================================================== int P_TranslateMap(int map) { int i; for (i = 1; i < 99; i++) // Make this a macro { if (MapInfo[i].warpTrans == map) { return i; } } // Not found return -1; } //========================================================================== // // P_GetMapSky1Texture // //========================================================================== int P_GetMapSky1Texture(int map) { return MapInfo[QualifyMap(map)].sky1Texture; } //========================================================================== // // P_GetMapSky2Texture // //========================================================================== int P_GetMapSky2Texture(int map) { return MapInfo[QualifyMap(map)].sky2Texture; } //========================================================================== // // P_GetMapName // //========================================================================== char *P_GetMapName(int map) { return MapInfo[QualifyMap(map)].name; } //========================================================================== // // P_GetMapSky1ScrollDelta // //========================================================================== fixed_t P_GetMapSky1ScrollDelta(int map) { return MapInfo[QualifyMap(map)].sky1ScrollDelta; } //========================================================================== // // P_GetMapSky2ScrollDelta // //========================================================================== fixed_t P_GetMapSky2ScrollDelta(int map) { return MapInfo[QualifyMap(map)].sky2ScrollDelta; } //========================================================================== // // P_GetMapDoubleSky // //========================================================================== boolean P_GetMapDoubleSky(int map) { return MapInfo[QualifyMap(map)].doubleSky; } //========================================================================== // // P_GetMapLightning // //========================================================================== boolean P_GetMapLightning(int map) { return MapInfo[QualifyMap(map)].lightning; } //========================================================================== // // P_GetMapFadeTable // //========================================================================== boolean P_GetMapFadeTable(int map) { return MapInfo[QualifyMap(map)].fadetable; } //========================================================================== // // P_GetMapSongLump // //========================================================================== char *P_GetMapSongLump(int map) { if (!strcasecmp(MapInfo[QualifyMap(map)].songLump, DEFAULT_SONG_LUMP)) { return NULL; } else { return MapInfo[QualifyMap(map)].songLump; } } //========================================================================== // // P_PutMapSongLump // //========================================================================== void P_PutMapSongLump(int map, char *lumpName) { if (map < 1 || map > MapCount) { return; } M_StringCopy(MapInfo[map].songLump, lumpName, sizeof(MapInfo[map].songLump)); } //========================================================================== // // P_GetCDStartTrack // //========================================================================== int P_GetCDStartTrack(void) { return cd_NonLevelTracks[MCMD_CD_STARTTRACK - MCMD_CD_STARTTRACK]; } //========================================================================== // // P_GetCDEnd1Track // //========================================================================== int P_GetCDEnd1Track(void) { return cd_NonLevelTracks[MCMD_CD_END1TRACK - MCMD_CD_STARTTRACK]; } //========================================================================== // // P_GetCDEnd2Track // //========================================================================== int P_GetCDEnd2Track(void) { return cd_NonLevelTracks[MCMD_CD_END2TRACK - MCMD_CD_STARTTRACK]; } //========================================================================== // // P_GetCDEnd3Track // //========================================================================== int P_GetCDEnd3Track(void) { return cd_NonLevelTracks[MCMD_CD_END3TRACK - MCMD_CD_STARTTRACK]; } //========================================================================== // // P_GetCDIntermissionTrack // //========================================================================== int P_GetCDIntermissionTrack(void) { return cd_NonLevelTracks[MCMD_CD_INTERTRACK - MCMD_CD_STARTTRACK]; } //========================================================================== // // P_GetCDTitleTrack // //========================================================================== int P_GetCDTitleTrack(void) { return cd_NonLevelTracks[MCMD_CD_TITLETRACK - MCMD_CD_STARTTRACK]; } //========================================================================== // // QualifyMap // //========================================================================== static int QualifyMap(int map) { return (map < 1 || map > MapCount) ? 0 : map; } //========================================================================== // // P_Init // //========================================================================== void P_Init(void) { InitMapInfo(); P_InitSwitchList(); P_InitFTAnims(); // Init flat and texture animations P_InitTerrainTypes(); P_InitLava(); R_InitSprites(sprnames); } // Special early initializer needed to start sound before R_Init() void InitMapMusicInfo(void) { int i; for (i = 0; i < 99; i++) { M_StringCopy(MapInfo[i].songLump, DEFAULT_SONG_LUMP, sizeof(MapInfo[i].songLump)); } MapCount = 98; } /* void My_Debug(void) { int i; printf("My debug stuff ----------------------\n"); printf("gamemap=%d\n",gamemap); for (i=0; i<10; i++) { printf("i=%d songlump=%s\n",i,MapInfo[i].songLump); } } */ crispy-doom-crispy-doom-5.6.4/src/hexen/p_sight.c000066400000000000000000000234171360717211000217420ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "p_local.h" /* ============================================================================== 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]; /* ============== = = PTR_SightTraverse = ============== */ boolean 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 = =================== */ boolean P_SightBlockLinesIterator(int x, int y) { int offset; short *list; line_t *ld; int s1, s2; divline_t dl; polyblock_t *polyLink; seg_t **segList; int i; extern polyblock_t **PolyBlockMap; offset = y * bmapwidth + x; polyLink = PolyBlockMap[offset]; while (polyLink) { if (polyLink->polyobj) { // only check non-empty links if (polyLink->polyobj->validcount != validcount) { segList = polyLink->polyobj->segs; for (i = 0; i < polyLink->polyobj->numsegs; i++, segList++) { ld = (*segList)->linedef; if (ld->validcount == validcount) { continue; } 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 // store the line for later intersection testing intercept_p->d.line = ld; intercept_p++; } polyLink->polyobj->validcount = validcount; } } polyLink = polyLink->next; } 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 // 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 ==================== */ boolean 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; scan < intercept_p; scan++) { P_MakeDivline(scan->d.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; scan < intercept_p; scan++) if (scan->frac < 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 ================== */ boolean 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 = x1 >> MAPBLOCKSHIFT; yt1 = y1 >> MAPBLOCKSHIFT; x2 -= bmaporgx; y2 -= bmaporgy; xt2 = x2 >> MAPBLOCKSHIFT; yt2 = y2 >> MAPBLOCKSHIFT; // 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 = ===================== */ boolean P_CheckSight(mobj_t * t1, mobj_t * t2) { int s1, s2; int pnum, bytenum, bitnum; // // check for trivial rejection // s1 = (t1->subsector->sector - sectors); s2 = (t2->subsector->sector - sectors); 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); } crispy-doom-crispy-doom-5.6.4/src/hexen/p_spec.c000066400000000000000000001121151360717211000215500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "i_system.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ #define MAX_TAGGED_LINES 64 // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static boolean CheckedLockedDoor(mobj_t * mo, byte lock); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- int *TerrainTypes; struct { const char *name; int type; } TerrainTypeDefs[] = { { "X_005", FLOOR_WATER}, { "X_001", FLOOR_LAVA}, { "X_009", FLOOR_SLUDGE}, { "F_033", FLOOR_ICE}, { "END", -1} }; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static struct { line_t *line; int lineTag; } TaggedLines[MAX_TAGGED_LINES]; static int TaggedLineCount; mobj_t LavaInflictor; // CODE -------------------------------------------------------------------- //========================================================================== // // P_InitLava // //========================================================================== void P_InitLava(void) { memset(&LavaInflictor, 0, sizeof(mobj_t)); LavaInflictor.type = MT_CIRCLEFLAME; LavaInflictor.flags2 = MF2_FIREDAMAGE | MF2_NODMGTHRUST; } //========================================================================== // // P_InitTerrainTypes // //========================================================================== void P_InitTerrainTypes(void) { int i; int lump; int size; size = (numflats + 1) * sizeof(int); TerrainTypes = Z_Malloc(size, PU_STATIC, 0); memset(TerrainTypes, 0, size); for (i = 0; TerrainTypeDefs[i].type != -1; i++) { lump = W_CheckNumForName(TerrainTypeDefs[i].name); if (lump != -1) { TerrainTypes[lump - firstflat] = TerrainTypeDefs[i].type; } } } //========================================================================== // // getSide // // Will return a side_t* given the number of the current sector, the // line number, and the side (0/1) that you want. // //========================================================================== /* side_t *getSide(int currentSector, int line, int side) { return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ]; } */ //========================================================================== // // getSector // // Will return a sector_t* given the number of the current sector, the // line number, and the side (0/1) that you want. // //========================================================================== /* sector_t *getSector(int currentSector, int line, int side) { return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector; } */ //========================================================================== // // twoSided // // Given the sector number and the line number, will tell you whether // the line is two-sided or not. // //========================================================================== /* int twoSided(int sector, int line) { return (sectors[sector].lines[line])->flags & ML_TWOSIDED; } */ //================================================================== // // Return sector_t * of sector next to current. NULL if not two-sided line // //================================================================== sector_t *getNextSector(line_t * line, sector_t * sec) { if (!(line->flags & ML_TWOSIDED)) return NULL; if (line->frontsector == sec) return line->backsector; return line->frontsector; } //================================================================== // // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS // //================================================================== fixed_t P_FindLowestFloorSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t floor = sec->floorheight; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->floorheight < floor) floor = other->floorheight; } return floor; } //================================================================== // // FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS // //================================================================== fixed_t P_FindHighestFloorSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t floor = -500 * FRACUNIT; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->floorheight > floor) floor = other->floorheight; } return floor; } //================================================================== // // FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS // //================================================================== fixed_t P_FindNextHighestFloor(sector_t * sec, int currentheight) { int i; int h; int min; line_t *check; sector_t *other; fixed_t height = currentheight; fixed_t heightlist[20]; // 20 adjoining sectors max! heightlist[0] = 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) heightlist[h++] = other->floorheight; } // // Find lowest height in list // min = heightlist[0]; for (i = 1; i < h; i++) if (heightlist[i] < min) min = heightlist[i]; return min; } //================================================================== // // FIND LOWEST CEILING IN THE SURROUNDING SECTORS // //================================================================== fixed_t P_FindLowestCeilingSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t height = INT_MAX; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->ceilingheight < height) height = other->ceilingheight; } return height; } //================================================================== // // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS // //================================================================== fixed_t P_FindHighestCeilingSurrounding(sector_t * sec) { int i; line_t *check; sector_t *other; fixed_t height = 0; for (i = 0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check, sec); if (!other) continue; if (other->ceilingheight > height) height = other->ceilingheight; } return height; } //================================================================== // // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO // //================================================================== /* int P_FindSectorFromLineTag(line_t *line,int start) { int i; for (i=start+1;iarg1) return i; return -1; } */ //========================================================================= // // P_FindSectorFromTag // //========================================================================= int P_FindSectorFromTag(int tag, int start) { int i; for (i = start + 1; i < numsectors; i++) { if (sectors[i].tag == tag) { return i; } } return -1; } //================================================================== // // Find minimum light from an adjacent sector // //================================================================== /* int P_FindMinSurroundingLight(sector_t *sector,int max) { int i; int min; line_t *line; sector_t *check; min = max; for (i=0 ; i < sector->linecount ; i++) { line = sector->lines[i]; check = getNextSector(line,sector); if (!check) continue; if (check->lightlevel < min) min = check->lightlevel; } return min; } */ //========================================================================= // // EV_SectorSoundChange // //========================================================================= boolean EV_SectorSoundChange(byte * args) { int secNum; boolean rtn; if (!args[0]) { return false; } secNum = -1; rtn = false; while ((secNum = P_FindSectorFromTag(args[0], secNum)) >= 0) { sectors[secNum].seqType = args[1]; rtn = true; } return rtn; } //============================================================================ // // CheckedLockedDoor // //============================================================================ static boolean CheckedLockedDoor(mobj_t * mo, byte lock) { extern char *TextKeyMessages[11]; char LockedBuffer[80]; if (!mo->player) { return false; } if (!lock) { return true; } if (!(mo->player->keys & (1 << (lock - 1)))) { M_snprintf(LockedBuffer, sizeof(LockedBuffer), "YOU NEED THE %s\n", TextKeyMessages[lock - 1]); P_SetMessage(mo->player, LockedBuffer, true); S_StartSound(mo, SFX_DOOR_LOCKED); return false; } return true; } //========================================================================== // // EV_LineSearchForPuzzleItem // //========================================================================== boolean EV_LineSearchForPuzzleItem(line_t * line, byte * args, mobj_t * mo) { player_t *player; int i; int type; artitype_t arti; if (!mo) return false; player = mo->player; if (!player) return false; // Search player's inventory for puzzle items for (i = 0; i < player->artifactCount; i++) { arti = player->inventory[i].type; type = arti - arti_firstpuzzitem; if (type < 0) continue; if (type == line->arg1) { // A puzzle item was found for the line if (P_UseArtifact(player, arti)) { // A puzzle item was found for the line P_PlayerRemoveArtifact(player, i); if (player == &players[consoleplayer]) { if (arti < arti_firstpuzzitem) { S_StartSound(NULL, SFX_ARTIFACT_USE); } else { S_StartSound(NULL, SFX_PUZZLE_SUCCESS); } ArtifactFlash = 4; } return true; } } } return false; } /* ============================================================================== EVENTS Events are operations triggered by using, crossing, or shooting special lines, or by timed thinkers ============================================================================== */ //============================================================================ // // P_ExecuteLineSpecial // // Invoked when crossing a linedef. The args[] array should be at least // 5 elements in length. // //============================================================================ boolean P_ExecuteLineSpecial(int special, byte * args, line_t * line, int side, mobj_t * mo) { boolean buttonSuccess; buttonSuccess = false; switch (special) { case 1: // Poly Start Line break; case 2: // Poly Rotate Left buttonSuccess = EV_RotatePoly(line, args, 1, false); break; case 3: // Poly Rotate Right buttonSuccess = EV_RotatePoly(line, args, -1, false); break; case 4: // Poly Move buttonSuccess = EV_MovePoly(line, args, false, false); break; case 5: // Poly Explicit Line: Only used in initialization break; case 6: // Poly Move Times 8 buttonSuccess = EV_MovePoly(line, args, true, false); break; case 7: // Poly Door Swing buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SWING); break; case 8: // Poly Door Slide buttonSuccess = EV_OpenPolyDoor(line, args, PODOOR_SLIDE); break; case 10: // Door Close buttonSuccess = EV_DoDoor(line, args, DREV_CLOSE); break; case 11: // Door Open if (!args[0]) { buttonSuccess = EV_VerticalDoor(line, mo); } else { buttonSuccess = EV_DoDoor(line, args, DREV_OPEN); } break; case 12: // Door Raise if (!args[0]) { buttonSuccess = EV_VerticalDoor(line, mo); } else { buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL); } break; case 13: // Door Locked_Raise if (CheckedLockedDoor(mo, args[3])) { if (!args[0]) { buttonSuccess = EV_VerticalDoor(line, mo); } else { buttonSuccess = EV_DoDoor(line, args, DREV_NORMAL); } } break; case 20: // Floor Lower by Value buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE); break; case 21: // Floor Lower to Lowest buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOORTOLOWEST); break; case 22: // Floor Lower to Nearest buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERFLOOR); break; case 23: // Floor Raise by Value buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE); break; case 24: // Floor Raise to Highest buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOOR); break; case 25: // Floor Raise to Nearest buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORTONEAREST); break; case 26: // Stairs Build Down Normal buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_NORMAL); break; case 27: // Build Stairs Up Normal buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_NORMAL); break; case 28: // Floor Raise and Crush buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEFLOORCRUSH); break; case 29: // Build Pillar (no crushing) buttonSuccess = EV_BuildPillar(line, args, false); break; case 30: // Open Pillar buttonSuccess = EV_OpenPillar(line, args); break; case 31: // Stairs Build Down Sync buttonSuccess = EV_BuildStairs(line, args, -1, STAIRS_SYNC); break; case 32: // Build Stairs Up Sync buttonSuccess = EV_BuildStairs(line, args, 1, STAIRS_SYNC); break; case 35: // Raise Floor by Value Times 8 buttonSuccess = EV_DoFloor(line, args, FLEV_RAISEBYVALUETIMES8); break; case 36: // Lower Floor by Value Times 8 buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERBYVALUETIMES8); break; case 40: // Ceiling Lower by Value buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE); break; case 41: // Ceiling Raise by Value buttonSuccess = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE); break; case 42: // Ceiling Crush and Raise buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHANDRAISE); break; case 43: // Ceiling Lower and Crush buttonSuccess = EV_DoCeiling(line, args, CLEV_LOWERANDCRUSH); break; case 44: // Ceiling Crush Stop buttonSuccess = EV_CeilingCrushStop(line, args); break; case 45: // Ceiling Crush Raise and Stay buttonSuccess = EV_DoCeiling(line, args, CLEV_CRUSHRAISEANDSTAY); break; case 46: // Floor Crush Stop buttonSuccess = EV_FloorCrushStop(line, args); break; case 60: // Plat Perpetual Raise buttonSuccess = EV_DoPlat(line, args, PLAT_PERPETUALRAISE, 0); break; case 61: // Plat Stop EV_StopPlat(line, args); break; case 62: // Plat Down-Wait-Up-Stay buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNWAITUPSTAY, 0); break; case 63: // Plat Down-by-Value*8-Wait-Up-Stay buttonSuccess = EV_DoPlat(line, args, PLAT_DOWNBYVALUEWAITUPSTAY, 0); break; case 64: // Plat Up-Wait-Down-Stay buttonSuccess = EV_DoPlat(line, args, PLAT_UPWAITDOWNSTAY, 0); break; case 65: // Plat Up-by-Value*8-Wait-Down-Stay buttonSuccess = EV_DoPlat(line, args, PLAT_UPBYVALUEWAITDOWNSTAY, 0); break; case 66: // Floor Lower Instant * 8 buttonSuccess = EV_DoFloor(line, args, FLEV_LOWERTIMES8INSTANT); break; case 67: // Floor Raise Instant * 8 buttonSuccess = EV_DoFloor(line, args, FLEV_RAISETIMES8INSTANT); break; case 68: // Floor Move to Value * 8 buttonSuccess = EV_DoFloor(line, args, FLEV_MOVETOVALUETIMES8); break; case 69: // Ceiling Move to Value * 8 buttonSuccess = EV_DoCeiling(line, args, CLEV_MOVETOVALUETIMES8); break; case 70: // Teleport if (side == 0) { // Only teleport when crossing the front side of a line buttonSuccess = EV_Teleport(args[0], mo, true); } break; case 71: // Teleport, no fog if (side == 0) { // Only teleport when crossing the front side of a line buttonSuccess = EV_Teleport(args[0], mo, false); } break; case 72: // Thrust Mobj if (!side) // Only thrust on side 0 { P_ThrustMobj(mo, args[0] * (ANG90 / 64), args[1] << FRACBITS); buttonSuccess = 1; } break; case 73: // Damage Mobj if (args[0]) { P_DamageMobj(mo, NULL, NULL, args[0]); } else { // If arg1 is zero, then guarantee a kill P_DamageMobj(mo, NULL, NULL, 10000); } buttonSuccess = 1; break; case 74: // Teleport_NewMap if (side == 0) { // Only teleport when crossing the front side of a line if (!(mo && mo->player && mo->player->playerstate == PST_DEAD)) // Players must be alive to teleport { G_Completed(args[0], args[1]); buttonSuccess = true; } } break; case 75: // Teleport_EndGame if (side == 0) { // Only teleport when crossing the front side of a line if (!(mo && mo->player && mo->player->playerstate == PST_DEAD)) // Players must be alive to teleport { buttonSuccess = true; if (deathmatch) { // Winning in deathmatch just goes back to map 1 G_Completed(1, 0); } else { // Passing -1, -1 to G_Completed() starts the Finale G_Completed(-1, -1); } } } break; case 80: // ACS_Execute buttonSuccess = P_StartACS(args[0], args[1], &args[2], mo, line, side); break; case 81: // ACS_Suspend buttonSuccess = P_SuspendACS(args[0], args[1]); break; case 82: // ACS_Terminate buttonSuccess = P_TerminateACS(args[0], args[1]); break; case 83: // ACS_LockedExecute buttonSuccess = P_StartLockedACS(line, args, mo, side); break; case 90: // Poly Rotate Left Override buttonSuccess = EV_RotatePoly(line, args, 1, true); break; case 91: // Poly Rotate Right Override buttonSuccess = EV_RotatePoly(line, args, -1, true); break; case 92: // Poly Move Override buttonSuccess = EV_MovePoly(line, args, false, true); break; case 93: // Poly Move Times 8 Override buttonSuccess = EV_MovePoly(line, args, true, true); break; case 94: // Build Pillar Crush buttonSuccess = EV_BuildPillar(line, args, true); break; case 95: // Lower Floor and Ceiling buttonSuccess = EV_DoFloorAndCeiling(line, args, false); break; case 96: // Raise Floor and Ceiling buttonSuccess = EV_DoFloorAndCeiling(line, args, true); break; case 109: // Force Lightning buttonSuccess = true; P_ForceLightning(); break; case 110: // Light Raise by Value buttonSuccess = EV_SpawnLight(line, args, LITE_RAISEBYVALUE); break; case 111: // Light Lower by Value buttonSuccess = EV_SpawnLight(line, args, LITE_LOWERBYVALUE); break; case 112: // Light Change to Value buttonSuccess = EV_SpawnLight(line, args, LITE_CHANGETOVALUE); break; case 113: // Light Fade buttonSuccess = EV_SpawnLight(line, args, LITE_FADE); break; case 114: // Light Glow buttonSuccess = EV_SpawnLight(line, args, LITE_GLOW); break; case 115: // Light Flicker buttonSuccess = EV_SpawnLight(line, args, LITE_FLICKER); break; case 116: // Light Strobe buttonSuccess = EV_SpawnLight(line, args, LITE_STROBE); break; case 120: // Quake Tremor buttonSuccess = A_LocalQuake(args, mo); break; case 129: // UsePuzzleItem buttonSuccess = EV_LineSearchForPuzzleItem(line, args, mo); break; case 130: // Thing_Activate buttonSuccess = EV_ThingActivate(args[0]); break; case 131: // Thing_Deactivate buttonSuccess = EV_ThingDeactivate(args[0]); break; case 132: // Thing_Remove buttonSuccess = EV_ThingRemove(args[0]); break; case 133: // Thing_Destroy buttonSuccess = EV_ThingDestroy(args[0]); break; case 134: // Thing_Projectile buttonSuccess = EV_ThingProjectile(args, 0); break; case 135: // Thing_Spawn buttonSuccess = EV_ThingSpawn(args, 1); break; case 136: // Thing_ProjectileGravity buttonSuccess = EV_ThingProjectile(args, 1); break; case 137: // Thing_SpawnNoFog buttonSuccess = EV_ThingSpawn(args, 0); break; case 138: // Floor_Waggle buttonSuccess = EV_StartFloorWaggle(args[0], args[1], args[2], args[3], args[4]); break; case 140: // Sector_SoundChange buttonSuccess = EV_SectorSoundChange(args); break; // Line specials only processed during level initialization // 100: Scroll_Texture_Left // 101: Scroll_Texture_Right // 102: Scroll_Texture_Up // 103: Scroll_Texture_Down // 121: Line_SetIdentification // Inert Line specials default: break; } return buttonSuccess; } //============================================================================ // // P_ActivateLine // //============================================================================ boolean P_ActivateLine(line_t * line, mobj_t * mo, int side, int activationType) { byte args[5]; int lineActivation; boolean repeat; boolean buttonSuccess; lineActivation = GET_SPAC(line->flags); if (lineActivation != activationType) { return false; } if (!mo->player && !(mo->flags & MF_MISSILE)) { if (lineActivation != SPAC_MCROSS) { // currently, monsters can only activate the MCROSS activation type return false; } if (line->flags & ML_SECRET) return false; // never open secret doors } repeat = (line->flags & ML_REPEAT_SPECIAL) != 0; buttonSuccess = false; // Construct args[] array to contain the arguments from the line, as we // cannot rely on struct field ordering and layout. args[0] = line->arg1; args[1] = line->arg2; args[2] = line->arg3; args[3] = line->arg4; args[4] = line->arg5; buttonSuccess = P_ExecuteLineSpecial(line->special, args, line, side, mo); if (!repeat && buttonSuccess) { // clear the special on non-retriggerable lines line->special = 0; } if ((lineActivation == SPAC_USE || lineActivation == SPAC_IMPACT) && buttonSuccess) { P_ChangeSwitchTexture(line, repeat); } return true; } //---------------------------------------------------------------------------- // // PROC P_PlayerInSpecialSector // // Called every tic frame that the player origin is in a special sector. // //---------------------------------------------------------------------------- void P_PlayerInSpecialSector(player_t * player) { sector_t *sector; static int pushTab[3] = { 2048 * 5, 2048 * 10, 2048 * 25 }; sector = player->mo->subsector->sector; if (player->mo->z != sector->floorheight) { // Player is not touching the floor return; } switch (sector->special) { case 9: // SecretArea player->secretcount++; sector->special = 0; break; case 201: case 202: case 203: // Scroll_North_xxx P_Thrust(player, ANG90, pushTab[sector->special - 201]); break; case 204: case 205: case 206: // Scroll_East_xxx P_Thrust(player, 0, pushTab[sector->special - 204]); break; case 207: case 208: case 209: // Scroll_South_xxx P_Thrust(player, ANG270, pushTab[sector->special - 207]); break; case 210: case 211: case 212: // Scroll_West_xxx P_Thrust(player, ANG180, pushTab[sector->special - 210]); break; case 213: case 214: case 215: // Scroll_NorthWest_xxx P_Thrust(player, ANG90 + ANG45, pushTab[sector->special - 213]); break; case 216: case 217: case 218: // Scroll_NorthEast_xxx P_Thrust(player, ANG45, pushTab[sector->special - 216]); break; case 219: case 220: case 221: // Scroll_SouthEast_xxx P_Thrust(player, ANG270 + ANG45, pushTab[sector->special - 219]); break; case 222: case 223: case 224: // Scroll_SouthWest_xxx P_Thrust(player, ANG180 + ANG45, pushTab[sector->special - 222]); break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: // Wind specials are handled in (P_mobj):P_XYMovement break; case 26: // Stairs_Special1 case 27: // Stairs_Special2 // Used in (P_floor):ProcessStairSector break; case 198: // Lightning Special case 199: // Lightning Flash special case 200: // Sky2 // Used in (R_plane):R_Drawplanes break; default: I_Error("P_PlayerInSpecialSector: " "unknown special %i", sector->special); } } //============================================================================ // // P_PlayerOnSpecialFlat // //============================================================================ void P_PlayerOnSpecialFlat(player_t * player, int floorType) { if (player->mo->z != player->mo->floorz) { // Player is not touching the floor return; } switch (floorType) { case FLOOR_LAVA: if (!(leveltime & 31)) { P_DamageMobj(player->mo, &LavaInflictor, NULL, 10); S_StartSound(player->mo, SFX_LAVA_SIZZLE); } break; default: break; } } //---------------------------------------------------------------------------- // // PROC P_UpdateSpecials // //---------------------------------------------------------------------------- void P_UpdateSpecials(void) { int i; // Handle buttons for (i = 0; i < MAXBUTTONS; i++) { if (buttonlist[i].btimer) { buttonlist[i].btimer--; if (!buttonlist[i].btimer) { switch (buttonlist[i].where) { case SWTCH_TOP: sides[buttonlist[i].line->sidenum[0]].toptexture = buttonlist[i].btexture; break; case SWTCH_MIDDLE: sides[buttonlist[i].line->sidenum[0]].midtexture = buttonlist[i].btexture; break; case SWTCH_BOTTOM: sides[buttonlist[i].line->sidenum[0]].bottomtexture = buttonlist[i].btexture; break; } //S_StartSound((mobj_t *)&buttonlist[i].soundorg, sfx_switch); memset(&buttonlist[i], 0, sizeof(button_t)); } } } } /* ============================================================================== SPECIAL SPAWNING ============================================================================== */ /* ================================================================================ = P_SpawnSpecials = = After the map has been loaded, scan for specials that = spawn thinkers = =============================================================================== */ short numlinespecials; line_t *linespeciallist[MAXLINEANIMS]; void P_SpawnSpecials(void) { sector_t *sector; int i; // // Init special SECTORs // sector = sectors; for (i = 0; i < numsectors; i++, sector++) { if (!sector->special) continue; switch (sector->special) { case 1: // Phased light // Hardcoded base, use sector->lightlevel as the index P_SpawnPhasedLight(sector, 80, -1); break; case 2: // Phased light sequence start P_SpawnLightSequence(sector, 1); break; // Specials 3 & 4 are used by the phased light sequences /* case 1: // FLICKERING LIGHTS P_SpawnLightFlash (sector); break; case 2: // STROBE FAST P_SpawnStrobeFlash(sector,FASTDARK,0); break; case 3: // STROBE SLOW P_SpawnStrobeFlash(sector,SLOWDARK,0); break; case 4: // STROBE FAST/DEATH SLIME P_SpawnStrobeFlash(sector,FASTDARK,0); sector->special = 4; break; case 8: // GLOWING LIGHT P_SpawnGlowingLight(sector); break; case 9: // SECRET SECTOR totalsecret++; break; case 10: // DOOR CLOSE IN 30 SECONDS P_SpawnDoorCloseIn30 (sector); break; case 12: // SYNC STROBE SLOW P_SpawnStrobeFlash (sector, SLOWDARK, 1); break; case 13: // SYNC STROBE FAST P_SpawnStrobeFlash (sector, FASTDARK, 1); break; case 14: // DOOR RAISE IN 5 MINUTES P_SpawnDoorRaiseIn5Mins (sector, i); break; */ } } // // Init line EFFECTs // numlinespecials = 0; TaggedLineCount = 0; for (i = 0; i < numlines; i++) { switch (lines[i].special) { case 100: // Scroll_Texture_Left case 101: // Scroll_Texture_Right case 102: // Scroll_Texture_Up case 103: // Scroll_Texture_Down linespeciallist[numlinespecials] = &lines[i]; numlinespecials++; break; case 121: // Line_SetIdentification if (lines[i].arg1) { if (TaggedLineCount == MAX_TAGGED_LINES) { I_Error("P_SpawnSpecials: MAX_TAGGED_LINES " "(%d) exceeded.", MAX_TAGGED_LINES); } TaggedLines[TaggedLineCount].line = &lines[i]; TaggedLines[TaggedLineCount++].lineTag = lines[i].arg1; } lines[i].special = 0; break; } } // // Init other misc stuff // for (i = 0; i < MAXCEILINGS; i++) activeceilings[i] = NULL; for (i = 0; i < MAXPLATS; i++) activeplats[i] = NULL; for (i = 0; i < MAXBUTTONS; i++) memset(&buttonlist[i], 0, sizeof(button_t)); // Initialize flat and texture animations P_InitFTAnims(); } //========================================================================== // // P_FindLine // //========================================================================== line_t *P_FindLine(int lineTag, int *searchPosition) { int i; for (i = *searchPosition + 1; i < TaggedLineCount; i++) { if (TaggedLines[i].lineTag == lineTag) { *searchPosition = i; return TaggedLines[i].line; } } *searchPosition = -1; return NULL; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_spec.h000066400000000000000000000333761360717211000215700ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // extern int *TerrainTypes; // // scrolling line specials // #define MAXLINEANIMS 64*256 extern short numlinespecials; extern line_t *linespeciallist[MAXLINEANIMS]; // Define values for map objects #define MO_TELEPORTMAN 14 // at game start void P_InitTerrainTypes(void); void P_InitLava(void); // at map load void P_SpawnSpecials(void); // every tic void P_UpdateSpecials(void); // when needed boolean P_ExecuteLineSpecial(int special, byte * args, line_t * line, int side, mobj_t * mo); boolean P_ActivateLine(line_t * ld, mobj_t * mo, int side, int activationType); //boolean P_UseSpecialLine ( mobj_t *thing, line_t *line); //void P_ShootSpecialLine ( mobj_t *thing, line_t *line); //void P_CrossSpecialLine (int linenum, int side, mobj_t *thing); void P_PlayerInSpecialSector(player_t * player); void P_PlayerOnSpecialFlat(player_t * player, int floorType); //int twoSided(int sector,int line); //sector_t *getSector(int currentSector,int line,int side); //side_t *getSide(int currentSector,int line, int side); fixed_t P_FindLowestFloorSurrounding(sector_t * sec); fixed_t P_FindHighestFloorSurrounding(sector_t * sec); fixed_t P_FindNextHighestFloor(sector_t * sec, int currentheight); fixed_t P_FindLowestCeilingSurrounding(sector_t * sec); fixed_t P_FindHighestCeilingSurrounding(sector_t * sec); //int P_FindSectorFromLineTag(line_t *line,int start); int P_FindSectorFromTag(int tag, int start); //int P_FindMinSurroundingLight(sector_t *sector,int max); sector_t *getNextSector(line_t * line, sector_t * sec); line_t *P_FindLine(int lineTag, int *searchPosition); // // SPECIAL // //int EV_DoDonut(line_t *line); //------------------------------- // P_anim.c //------------------------------- void P_AnimateSurfaces(void); void P_InitFTAnims(void); void P_InitLightning(void); void P_ForceLightning(void); /* =============================================================================== P_LIGHTS =============================================================================== */ typedef enum { LITE_RAISEBYVALUE, LITE_LOWERBYVALUE, LITE_CHANGETOVALUE, LITE_FADE, LITE_GLOW, LITE_FLICKER, LITE_STROBE } lighttype_t; typedef struct { thinker_t thinker; sector_t *sector; lighttype_t type; int value1; int value2; int tics1; int tics2; int count; } light_t; typedef struct { thinker_t thinker; sector_t *sector; int index; int base; } phase_t; #define LIGHT_SEQUENCE_START 2 #define LIGHT_SEQUENCE 3 #define LIGHT_SEQUENCE_ALT 4 void T_Phase(phase_t * phase); void T_Light(light_t * light); void P_SpawnPhasedLight(sector_t * sector, int base, int index); void P_SpawnLightSequence(sector_t * sector, int indexStep); boolean EV_SpawnLight(line_t * line, byte * arg, lighttype_t type); #if 0 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; typedef struct { thinker_t thinker; sector_t *sector; int index; int base; } phase_t; #define GLOWSPEED 8 #define STROBEBRIGHT 5 #define FASTDARK 15 #define SLOWDARK 35 #define LIGHT_SEQUENCE_START 2 #define LIGHT_SEQUENCE 3 #define LIGHT_SEQUENCE_ALT 4 void T_LightFlash(lightflash_t * flash); void P_SpawnLightFlash(sector_t * sector); void T_StrobeFlash(strobe_t * flash); void P_SpawnStrobeFlash(sector_t * sector, int fastOrSlow, int inSync); void EV_StartLightStrobing(line_t * line); void EV_TurnTagLightsOff(line_t * line); void EV_LightTurnOn(line_t * line, int bright); void T_Glow(glow_t * g); void P_SpawnGlowingLight(sector_t * sector); void T_Phase(phase_t * phase); void P_SpawnPhasedLight(sector_t * sector, int base, int index); void P_SpawnLightSequence(sector_t * sector, int indexStep); #endif /* =============================================================================== P_SWITCH =============================================================================== */ typedef struct { char name1[9]; char name2[9]; int soundID; } switchlist_t; typedef enum { SWTCH_TOP, SWTCH_MIDDLE, SWTCH_BOTTOM } bwhere_e; typedef struct { line_t *line; bwhere_e where; int btexture; int btimer; mobj_t *soundorg; } button_t; #define MAXSWITCHES 50 // max # of wall switches in a level #define MAXBUTTONS 16 // 4 players, 4 buttons each at once, max. #define BUTTONTIME 35 // 1 second extern button_t buttonlist[MAXBUTTONS]; void P_ChangeSwitchTexture(line_t * line, int useAgain); void P_InitSwitchList(void); /* =============================================================================== P_PLATS =============================================================================== */ typedef enum { PLAT_UP, PLAT_DOWN, PLAT_WAITING, // PLAT_IN_STASIS } plat_e; typedef enum { PLAT_PERPETUALRAISE, PLAT_DOWNWAITUPSTAY, PLAT_DOWNBYVALUEWAITUPSTAY, PLAT_UPWAITDOWNSTAY, PLAT_UPBYVALUEWAITDOWNSTAY, //PLAT_RAISEANDCHANGE, //PLAT_RAISETONEARESTANDCHANGE } plattype_e; typedef struct { thinker_t thinker; sector_t *sector; fixed_t speed; fixed_t low; fixed_t high; int wait; int count; plat_e status; plat_e oldstatus; int crush; int tag; plattype_e type; } plat_t; #define PLATWAIT 3 #define PLATSPEED FRACUNIT #define MAXPLATS 30*256 extern plat_t *activeplats[MAXPLATS]; void T_PlatRaise(plat_t * plat); int EV_DoPlat(line_t * line, byte * args, plattype_e type, int amount); void P_AddActivePlat(plat_t * plat); void P_RemoveActivePlat(plat_t * plat); void EV_StopPlat(line_t * line, byte * args); /* =============================================================================== P_DOORS =============================================================================== */ typedef enum { DREV_NORMAL, DREV_CLOSE30THENOPEN, DREV_CLOSE, DREV_OPEN, DREV_RAISEIN5MINS, } vldoor_e; typedef struct { thinker_t thinker; sector_t *sector; vldoor_e type; fixed_t topheight; fixed_t speed; int direction; // 1 = up, 0 = waiting at top, -1 = down int topwait; // tics to wait at the top (keep in case a door going down is reset) int topcountdown; // when it reaches 0, start going down } vldoor_t; #define VDOORSPEED FRACUNIT*2 #define VDOORWAIT 150 boolean EV_VerticalDoor(line_t * line, mobj_t * thing); int EV_DoDoor(line_t * line, byte * args, vldoor_e type); void T_VerticalDoor(vldoor_t * door); //void P_SpawnDoorCloseIn30(sector_t *sec); //void P_SpawnDoorRaiseIn5Mins(sector_t *sec, int secnum); /* =============================================================================== P_CEILNG =============================================================================== */ typedef enum { CLEV_LOWERTOFLOOR, CLEV_RAISETOHIGHEST, CLEV_LOWERANDCRUSH, CLEV_CRUSHANDRAISE, CLEV_LOWERBYVALUE, CLEV_RAISEBYVALUE, CLEV_CRUSHRAISEANDSTAY, CLEV_MOVETOVALUETIMES8 } ceiling_e; typedef struct { thinker_t thinker; sector_t *sector; ceiling_e type; fixed_t bottomheight, topheight; fixed_t speed; int crush; int direction; // 1 = up, 0 = waiting, -1 = down int tag; // ID int olddirection; } ceiling_t; #define CEILSPEED FRACUNIT #define CEILWAIT 150 #define MAXCEILINGS 30 extern ceiling_t *activeceilings[MAXCEILINGS]; int EV_DoCeiling(line_t * line, byte * args, ceiling_e type); void T_MoveCeiling(ceiling_t * ceiling); void P_AddActiveCeiling(ceiling_t * c); void P_RemoveActiveCeiling(ceiling_t * c); int EV_CeilingCrushStop(line_t * line, byte * args); /* =============================================================================== P_FLOOR =============================================================================== */ typedef enum { FLEV_LOWERFLOOR, // lower floor to highest surrounding floor FLEV_LOWERFLOORTOLOWEST, // lower floor to lowest surrounding floor FLEV_LOWERFLOORBYVALUE, FLEV_RAISEFLOOR, // raise floor to lowest surrounding CEILING FLEV_RAISEFLOORTONEAREST, // raise floor to next highest surrounding floor FLEV_RAISEFLOORBYVALUE, FLEV_RAISEFLOORCRUSH, FLEV_RAISEBUILDSTEP, // One step of a staircase FLEV_RAISEBYVALUETIMES8, FLEV_LOWERBYVALUETIMES8, FLEV_LOWERTIMES8INSTANT, FLEV_RAISETIMES8INSTANT, FLEV_MOVETOVALUETIMES8 } floor_e; typedef struct { thinker_t thinker; sector_t *sector; floor_e type; int crush; int direction; int newspecial; short texture; fixed_t floordestheight; fixed_t speed; int delayCount; int delayTotal; fixed_t stairsDelayHeight; fixed_t stairsDelayHeightDelta; fixed_t resetHeight; short resetDelay; short resetDelayCount; byte textureChange; } floormove_t; typedef struct { thinker_t thinker; sector_t *sector; int ceilingSpeed; int floorSpeed; int floordest; int ceilingdest; int direction; int crush; } pillar_t; typedef struct { thinker_t thinker; sector_t *sector; fixed_t originalHeight; fixed_t accumulator; fixed_t accDelta; fixed_t targetScale; fixed_t scale; fixed_t scaleDelta; int ticker; int state; } floorWaggle_t; #define FLOORSPEED FRACUNIT typedef enum { RES_OK, RES_CRUSHED, RES_PASTDEST } result_e; typedef enum { STAIRS_NORMAL, STAIRS_SYNC, STAIRS_PHASED } stairs_e; result_e T_MovePlane(sector_t * sector, fixed_t speed, fixed_t dest, int crush, int floorOrCeiling, int direction); int EV_BuildStairs(line_t * line, byte * args, int direction, stairs_e type); int EV_DoFloor(line_t * line, byte * args, floor_e floortype); void T_MoveFloor(floormove_t * floor); void T_BuildPillar(pillar_t * pillar); void T_FloorWaggle(floorWaggle_t * waggle); int EV_BuildPillar(line_t * line, byte * args, boolean crush); int EV_OpenPillar(line_t * line, byte * args); int EV_DoFloorAndCeiling(line_t * line, byte * args, boolean raise); int EV_FloorCrushStop(line_t * line, byte * args); boolean EV_StartFloorWaggle(int tag, int height, int speed, int offset, int timer); //-------------------------------------------------------------------------- // // p_telept // //-------------------------------------------------------------------------- boolean P_Teleport(mobj_t * thing, fixed_t x, fixed_t y, angle_t angle, boolean useFog); boolean EV_Teleport(int tid, mobj_t * thing, boolean fog); //-------------------------------------------------------------------------- // // p_acs // //-------------------------------------------------------------------------- #define MAX_ACS_SCRIPT_VARS 10 #define MAX_ACS_MAP_VARS 32 #define MAX_ACS_WORLD_VARS 64 #define ACS_STACK_DEPTH 32 #define MAX_ACS_STORE 20 typedef enum { ASTE_INACTIVE, ASTE_RUNNING, ASTE_SUSPENDED, ASTE_WAITINGFORTAG, ASTE_WAITINGFORPOLY, ASTE_WAITINGFORSCRIPT, ASTE_TERMINATING } aste_t; typedef struct acs_s acs_t; typedef struct acsInfo_s acsInfo_t; struct acsInfo_s { int number; int offset; int argCount; aste_t state; int waitValue; }; struct acs_s { thinker_t thinker; mobj_t *activator; line_t *line; int side; int number; int infoIndex; int delayCount; int stack[ACS_STACK_DEPTH]; int stackPtr; int vars[MAX_ACS_SCRIPT_VARS]; int ip; }; typedef struct { int map; // Target map int script; // Script number on target map byte args[4]; // Padded to 4 for alignment } acsstore_t; void P_LoadACScripts(int lump); boolean P_StartACS(int number, int map, byte * args, mobj_t * activator, line_t * line, int side); boolean P_StartLockedACS(line_t * line, byte * args, mobj_t * mo, int side); boolean P_TerminateACS(int number, int map); boolean P_SuspendACS(int number, int map); void T_InterpretACS(acs_t * script); void P_TagFinished(int tag); void P_PolyobjFinished(int po); void P_ACSInitNewGame(void); void P_CheckACSStore(void); void CheckACSPresent(int number); extern int ACScriptCount; extern byte *ActionCodeBase; extern acsInfo_t *ACSInfo; extern int MapVars[MAX_ACS_MAP_VARS]; extern int WorldVars[MAX_ACS_WORLD_VARS]; extern acsstore_t ACSStore[MAX_ACS_STORE + 1]; // +1 for termination marker //-------------------------------------------------------------------------- // // p_things // //-------------------------------------------------------------------------- extern mobjtype_t TranslateThingType[]; boolean EV_ThingProjectile(byte * args, boolean gravity); boolean EV_ThingSpawn(byte * args, boolean fog); boolean EV_ThingActivate(int tid); boolean EV_ThingDeactivate(int tid); boolean EV_ThingRemove(int tid); boolean EV_ThingDestroy(int tid); crispy-doom-crispy-doom-5.6.4/src/hexen/p_switch.c000066400000000000000000000115421360717211000221210ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" //================================================================== // // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE // //================================================================== switchlist_t alphSwitchListDemo[] = { {"SW_1_UP", "SW_1_DN", SFX_SWITCH1}, {"SW_2_UP", "SW_2_DN", SFX_SWITCH1}, {"SW52_OFF", "SW52_ON", SFX_SWITCH2}, {"\0", "\0", 0} }; switchlist_t alphSwitchListFull[] = { {"SW_1_UP", "SW_1_DN", SFX_SWITCH1}, {"SW_2_UP", "SW_2_DN", SFX_SWITCH1}, {"VALVE1", "VALVE2", SFX_VALVE_TURN}, {"SW51_OFF", "SW51_ON", SFX_SWITCH2}, {"SW52_OFF", "SW52_ON", SFX_SWITCH2}, {"SW53_UP", "SW53_DN", SFX_ROPE_PULL}, {"PUZZLE5", "PUZZLE9", SFX_SWITCH1}, {"PUZZLE6", "PUZZLE10", SFX_SWITCH1}, {"PUZZLE7", "PUZZLE11", SFX_SWITCH1}, {"PUZZLE8", "PUZZLE12", SFX_SWITCH1}, {"\0", "\0", 0} }; switchlist_t *alphSwitchList = NULL; int switchlist[MAXSWITCHES * 2]; int numswitches; button_t buttonlist[MAXBUTTONS]; /* =============== = = P_InitSwitchList = = Only called at game initialization = =============== */ void P_InitSwitchList(void) { int i; int index; if (!alphSwitchList) { if (gamemode == shareware) { alphSwitchList = alphSwitchListDemo; } else { alphSwitchList = alphSwitchListFull; } } for (index = 0, i = 0; i < MAXSWITCHES; i++) { if (!alphSwitchList[i].soundID) { numswitches = index / 2; switchlist[index] = -1; break; } switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name1); switchlist[index++] = R_TextureNumForName(alphSwitchList[i].name2); } } //================================================================== // // Start a button counting down till it turns off. // //================================================================== void P_StartButton(line_t * line, bwhere_e w, int texture, int time) { int i; for (i = 0; i < MAXBUTTONS; i++) { if (!buttonlist[i].btimer) { buttonlist[i].line = line; buttonlist[i].where = w; buttonlist[i].btexture = texture; buttonlist[i].btimer = time; buttonlist[i].soundorg = (mobj_t *) & line->frontsector->soundorg; return; } } I_Error("P_StartButton: no button slots left!"); } //================================================================== // // Function that changes wall texture. // Tell it if switch is ok to use again (1=yes, it's a button). // //================================================================== void P_ChangeSwitchTexture(line_t * line, int useAgain) { int texTop; int texMid; int texBot; int i; texTop = sides[line->sidenum[0]].toptexture; texMid = sides[line->sidenum[0]].midtexture; texBot = sides[line->sidenum[0]].bottomtexture; for (i = 0; i < numswitches * 2; i++) { if (switchlist[i] == texTop) { S_StartSound((mobj_t *) & line->frontsector->soundorg, alphSwitchList[i / 2].soundID); sides[line->sidenum[0]].toptexture = switchlist[i ^ 1]; if (useAgain) { P_StartButton(line, SWTCH_TOP, switchlist[i], BUTTONTIME); } return; } else if (switchlist[i] == texMid) { S_StartSound((mobj_t *) & line->frontsector->soundorg, alphSwitchList[i / 2].soundID); sides[line->sidenum[0]].midtexture = switchlist[i ^ 1]; if (useAgain) { P_StartButton(line, SWTCH_MIDDLE, switchlist[i], BUTTONTIME); } return; } else if (switchlist[i] == texBot) { S_StartSound((mobj_t *) & line->frontsector->soundorg, alphSwitchList[i / 2].soundID); sides[line->sidenum[0]].bottomtexture = switchlist[i ^ 1]; if (useAgain) { P_StartButton(line, SWTCH_BOTTOM, switchlist[i], BUTTONTIME); } return; } } } crispy-doom-crispy-doom-5.6.4/src/hexen/p_telept.c000066400000000000000000000124371360717211000221210ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "m_random.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- //========================================================================== // // P_Teleport // //========================================================================== boolean P_Teleport(mobj_t * thing, fixed_t x, fixed_t y, angle_t angle, boolean useFog) { fixed_t oldx; fixed_t oldy; fixed_t oldz; fixed_t aboveFloor; fixed_t fogDelta; player_t *player; unsigned an; mobj_t *fog; oldx = thing->x; oldy = thing->y; oldz = thing->z; aboveFloor = thing->z - thing->floorz; if (!P_TeleportMove(thing, x, y)) { return false; } if (thing->player) { player = thing->player; if (player->powers[pw_flight] && aboveFloor) { thing->z = thing->floorz + aboveFloor; if (thing->z + thing->height > thing->ceilingz) { thing->z = thing->ceilingz - thing->height; } player->viewz = thing->z + player->viewheight; } else { thing->z = thing->floorz; player->viewz = thing->z + player->viewheight; if (useFog) { player->lookdir = 0; } } } else if (thing->flags & MF_MISSILE) { thing->z = thing->floorz + aboveFloor; if (thing->z + thing->height > thing->ceilingz) { thing->z = thing->ceilingz - thing->height; } } else { thing->z = thing->floorz; } // Spawn teleport fog at source and destination if (useFog) { fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT; fog = P_SpawnMobj(oldx, oldy, oldz + fogDelta, MT_TFOG); S_StartSound(fog, SFX_TELEPORT); an = angle >> ANGLETOFINESHIFT; fog = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an], thing->z + fogDelta, MT_TFOG); S_StartSound(fog, SFX_TELEPORT); if (thing->player && !thing->player->powers[pw_speed]) { // Freeze player for about .5 sec thing->reactiontime = 18; } thing->angle = angle; } if (thing->flags2 & MF2_FLOORCLIP) { if (thing->z == thing->subsector->sector->floorheight && P_GetThingFloorType(thing) > FLOOR_SOLID) { thing->floorclip = 10 * FRACUNIT; } else { thing->floorclip = 0; } } if (thing->flags & MF_MISSILE) { angle >>= ANGLETOFINESHIFT; thing->momx = FixedMul(thing->info->speed, finecosine[angle]); thing->momy = FixedMul(thing->info->speed, finesine[angle]); } else if (useFog) // no fog doesn't alter the player's momentums { thing->momx = thing->momy = thing->momz = 0; } return true; } //========================================================================== // // EV_Teleport // //========================================================================== boolean EV_Teleport(int tid, mobj_t * thing, boolean fog) { int i; int count; mobj_t *mo; int searcher; if (!thing) { // Teleport function called with an invalid mobj return false; } if (thing->flags2 & MF2_NOTELEPORT) { return false; } count = 0; searcher = -1; while (P_FindMobjFromTID(tid, &searcher) != NULL) { count++; } if (count == 0) { return false; } count = 1 + (P_Random() % count); searcher = -1; mo = NULL; for (i = 0; i < count; i++) { mo = P_FindMobjFromTID(tid, &searcher); } if (mo == NULL) { I_Error("Can't find teleport mapspot\n"); } return P_Teleport(thing, mo->x, mo->y, mo->angle, fog); } crispy-doom-crispy-doom-5.6.4/src/hexen/p_things.c000066400000000000000000000411721360717211000221160ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "p_local.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static boolean ActivateThing(mobj_t * mobj); static boolean DeactivateThing(mobj_t * mobj); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- mobjtype_t TranslateThingType[] = { MT_MAPSPOT, // T_NONE MT_CENTAUR, // T_CENTAUR MT_CENTAURLEADER, // T_CENTAURLEADER MT_DEMON, // T_DEMON MT_ETTIN, // T_ETTIN MT_FIREDEMON, // T_FIREGARGOYLE MT_SERPENT, // T_WATERLURKER MT_SERPENTLEADER, // T_WATERLURKERLEADER MT_WRAITH, // T_WRAITH MT_WRAITHB, // T_WRAITHBURIED MT_FIREBALL1, // T_FIREBALL1 MT_MANA1, // T_MANA1 MT_MANA2, // T_MANA2 MT_SPEEDBOOTS, // T_ITEMBOOTS MT_ARTIEGG, // T_ITEMEGG MT_ARTIFLY, // T_ITEMFLIGHT MT_SUMMONMAULATOR, // T_ITEMSUMMON MT_TELEPORTOTHER, // T_ITEMTPORTOTHER MT_ARTITELEPORT, // T_ITEMTELEPORT MT_BISHOP, // T_BISHOP MT_ICEGUY, // T_ICEGOLEM MT_BRIDGE, // T_BRIDGE MT_BOOSTARMOR, // T_DRAGONSKINBRACERS MT_HEALINGBOTTLE, // T_ITEMHEALTHPOTION MT_HEALTHFLASK, // T_ITEMHEALTHFLASK MT_ARTISUPERHEAL, // T_ITEMHEALTHFULL MT_BOOSTMANA, // T_ITEMBOOSTMANA MT_FW_AXE, // T_FIGHTERAXE MT_FW_HAMMER, // T_FIGHTERHAMMER MT_FW_SWORD1, // T_FIGHTERSWORD1 MT_FW_SWORD2, // T_FIGHTERSWORD2 MT_FW_SWORD3, // T_FIGHTERSWORD3 MT_CW_SERPSTAFF, // T_CLERICSTAFF MT_CW_HOLY1, // T_CLERICHOLY1 MT_CW_HOLY2, // T_CLERICHOLY2 MT_CW_HOLY3, // T_CLERICHOLY3 MT_MW_CONE, // T_MAGESHARDS MT_MW_STAFF1, // T_MAGESTAFF1 MT_MW_STAFF2, // T_MAGESTAFF2 MT_MW_STAFF3, // T_MAGESTAFF3 MT_EGGFX, // T_MORPHBLAST MT_ROCK1, // T_ROCK1 MT_ROCK2, // T_ROCK2 MT_ROCK3, // T_ROCK3 MT_DIRT1, // T_DIRT1 MT_DIRT2, // T_DIRT2 MT_DIRT3, // T_DIRT3 MT_DIRT4, // T_DIRT4 MT_DIRT5, // T_DIRT5 MT_DIRT6, // T_DIRT6 MT_ARROW, // T_ARROW MT_DART, // T_DART MT_POISONDART, // T_POISONDART MT_RIPPERBALL, // T_RIPPERBALL MT_SGSHARD1, // T_STAINEDGLASS1 MT_SGSHARD2, // T_STAINEDGLASS2 MT_SGSHARD3, // T_STAINEDGLASS3 MT_SGSHARD4, // T_STAINEDGLASS4 MT_SGSHARD5, // T_STAINEDGLASS5 MT_SGSHARD6, // T_STAINEDGLASS6 MT_SGSHARD7, // T_STAINEDGLASS7 MT_SGSHARD8, // T_STAINEDGLASS8 MT_SGSHARD9, // T_STAINEDGLASS9 MT_SGSHARD0, // T_STAINEDGLASS0 MT_PROJECTILE_BLADE, // T_BLADE MT_ICESHARD, // T_ICESHARD MT_FLAME_SMALL, // T_FLAME_SMALL MT_FLAME_LARGE, // T_FLAME_LARGE MT_ARMOR_1, // T_MESHARMOR MT_ARMOR_2, // T_FALCONSHIELD MT_ARMOR_3, // T_PLATINUMHELM MT_ARMOR_4, // T_AMULETOFWARDING MT_ARTIPOISONBAG, // T_ITEMFLECHETTE MT_ARTITORCH, // T_ITEMTORCH MT_BLASTRADIUS, // T_ITEMREPULSION MT_MANA3, // T_MANA3 MT_ARTIPUZZSKULL, // T_PUZZSKULL MT_ARTIPUZZGEMBIG, // T_PUZZGEMBIG MT_ARTIPUZZGEMRED, // T_PUZZGEMRED MT_ARTIPUZZGEMGREEN1, // T_PUZZGEMGREEN1 MT_ARTIPUZZGEMGREEN2, // T_PUZZGEMGREEN2 MT_ARTIPUZZGEMBLUE1, // T_PUZZGEMBLUE1 MT_ARTIPUZZGEMBLUE2, // T_PUZZGEMBLUE2 MT_ARTIPUZZBOOK1, // T_PUZZBOOK1 MT_ARTIPUZZBOOK2, // T_PUZZBOOK2 MT_KEY1, // T_METALKEY MT_KEY2, // T_SMALLMETALKEY MT_KEY3, // T_AXEKEY MT_KEY4, // T_FIREKEY MT_KEY5, // T_GREENKEY MT_KEY6, // T_MACEKEY MT_KEY7, // T_SILVERKEY MT_KEY8, // T_RUSTYKEY MT_KEY9, // T_HORNKEY MT_KEYA, // T_SERPENTKEY MT_WATER_DRIP, // T_WATERDRIP MT_FLAME_SMALL_TEMP, // T_TEMPSMALLFLAME MT_FLAME_SMALL, // T_PERMSMALLFLAME MT_FLAME_LARGE_TEMP, // T_TEMPLARGEFLAME MT_FLAME_LARGE, // T_PERMLARGEFLAME MT_DEMON_MASH, // T_DEMON_MASH MT_DEMON2_MASH, // T_DEMON2_MASH MT_ETTIN_MASH, // T_ETTIN_MASH MT_CENTAUR_MASH, // T_CENTAUR_MASH MT_THRUSTFLOOR_UP, // T_THRUSTSPIKEUP MT_THRUSTFLOOR_DOWN, // T_THRUSTSPIKEDOWN MT_WRAITHFX4, // T_FLESH_DRIP1 MT_WRAITHFX5, // T_FLESH_DRIP2 MT_WRAITHFX2 // T_SPARK_DRIP }; // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- //========================================================================== // // EV_ThingProjectile // //========================================================================== boolean EV_ThingProjectile(byte * args, boolean gravity) { int tid; angle_t angle; int fineAngle; fixed_t speed; fixed_t vspeed; mobjtype_t moType; mobj_t *mobj; mobj_t *newMobj; int searcher; boolean success; success = false; searcher = -1; tid = args[0]; moType = TranslateThingType[args[1]]; if (nomonsters && (mobjinfo[moType].flags & MF_COUNTKILL)) { // Don't spawn monsters if -nomonsters return false; } angle = (int) args[2] << 24; fineAngle = angle >> ANGLETOFINESHIFT; speed = (int) args[3] << 13; vspeed = (int) args[4] << 13; while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { newMobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, moType); if (newMobj->info->seesound) { S_StartSound(newMobj, newMobj->info->seesound); } newMobj->target = mobj; // Originator newMobj->angle = angle; newMobj->momx = FixedMul(speed, finecosine[fineAngle]); newMobj->momy = FixedMul(speed, finesine[fineAngle]); newMobj->momz = vspeed; newMobj->flags2 |= MF2_DROPPED; // Don't respawn if (gravity == true) { newMobj->flags &= ~MF_NOGRAVITY; newMobj->flags2 |= MF2_LOGRAV; } if (P_CheckMissileSpawn(newMobj) == true) { success = true; } } return success; } //========================================================================== // // EV_ThingSpawn // //========================================================================== boolean EV_ThingSpawn(byte * args, boolean fog) { int tid; angle_t angle; mobj_t *mobj; mobj_t *newMobj; mobj_t *fogMobj; mobjtype_t moType; int searcher; boolean success; fixed_t z; success = false; searcher = -1; tid = args[0]; moType = TranslateThingType[args[1]]; if (nomonsters && (mobjinfo[moType].flags & MF_COUNTKILL)) { // Don't spawn monsters if -nomonsters return false; } angle = (int) args[2] << 24; while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if (mobjinfo[moType].flags2 & MF2_FLOATBOB) { z = mobj->z - mobj->floorz; } else { z = mobj->z; } newMobj = P_SpawnMobj(mobj->x, mobj->y, z, moType); if (P_TestMobjLocation(newMobj) == false) { // Didn't fit P_RemoveMobj(newMobj); } else { newMobj->angle = angle; if (fog == true) { fogMobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fogMobj, SFX_TELEPORT); } newMobj->flags2 |= MF2_DROPPED; // Don't respawn if (newMobj->flags2 & MF2_FLOATBOB) { newMobj->special1.i = newMobj->z - newMobj->floorz; } success = true; } } return success; } //========================================================================== // // EV_ThingActivate // //========================================================================== boolean EV_ThingActivate(int tid) { mobj_t *mobj; int searcher; boolean success; success = false; searcher = -1; while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if (ActivateThing(mobj) == true) { success = true; } } return success; } //========================================================================== // // EV_ThingDeactivate // //========================================================================== boolean EV_ThingDeactivate(int tid) { mobj_t *mobj; int searcher; boolean success; success = false; searcher = -1; while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if (DeactivateThing(mobj) == true) { success = true; } } return success; } //========================================================================== // // EV_ThingRemove // //========================================================================== boolean EV_ThingRemove(int tid) { mobj_t *mobj; int searcher; boolean success; success = false; searcher = -1; while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if (mobj->type == MT_BRIDGE) { A_BridgeRemove(mobj); return true; } P_RemoveMobj(mobj); success = true; } return success; } //========================================================================== // // EV_ThingDestroy // //========================================================================== boolean EV_ThingDestroy(int tid) { mobj_t *mobj; int searcher; boolean success; success = false; searcher = -1; while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL) { if (mobj->flags & MF_SHOOTABLE) { P_DamageMobj(mobj, NULL, NULL, 10000); success = true; } } return success; } //========================================================================== // // EV_ThingMove // // arg[0] = tid // arg[1] = speed // arg[2] = angle (255 = use mobj angle) // arg[3] = distance (pixels>>2) // //========================================================================== /* boolean EV_ThingMove(byte *args) { return false; } */ //========================================================================== // // ActivateThing // //========================================================================== static boolean ActivateThing(mobj_t * mobj) { if (mobj->flags & MF_COUNTKILL) { // Monster if (mobj->flags2 & MF2_DORMANT) { mobj->flags2 &= ~MF2_DORMANT; mobj->tics = 1; return true; } return false; } switch (mobj->type) { case MT_ZTWINEDTORCH: case MT_ZTWINEDTORCH_UNLIT: P_SetMobjState(mobj, S_ZTWINEDTORCH_1); S_StartSound(mobj, SFX_IGNITE); break; case MT_ZWALLTORCH: case MT_ZWALLTORCH_UNLIT: P_SetMobjState(mobj, S_ZWALLTORCH1); S_StartSound(mobj, SFX_IGNITE); break; case MT_ZGEMPEDESTAL: P_SetMobjState(mobj, S_ZGEMPEDESTAL2); break; case MT_ZWINGEDSTATUENOSKULL: P_SetMobjState(mobj, S_ZWINGEDSTATUENOSKULL2); break; case MT_THRUSTFLOOR_UP: case MT_THRUSTFLOOR_DOWN: if (mobj->args[0] == 0) { S_StartSound(mobj, SFX_THRUSTSPIKE_LOWER); mobj->flags2 &= ~MF2_DONTDRAW; if (mobj->args[1]) P_SetMobjState(mobj, S_BTHRUSTRAISE1); else P_SetMobjState(mobj, S_THRUSTRAISE1); } break; case MT_ZFIREBULL: case MT_ZFIREBULL_UNLIT: P_SetMobjState(mobj, S_ZFIREBULL_BIRTH); S_StartSound(mobj, SFX_IGNITE); break; case MT_ZBELL: if (mobj->health > 0) { P_DamageMobj(mobj, NULL, NULL, 10); // 'ring' the bell } break; case MT_ZCAULDRON: case MT_ZCAULDRON_UNLIT: P_SetMobjState(mobj, S_ZCAULDRON1); S_StartSound(mobj, SFX_IGNITE); break; case MT_FLAME_SMALL: S_StartSound(mobj, SFX_IGNITE); P_SetMobjState(mobj, S_FLAME_SMALL1); break; case MT_FLAME_LARGE: S_StartSound(mobj, SFX_IGNITE); P_SetMobjState(mobj, S_FLAME_LARGE1); break; case MT_BAT_SPAWNER: P_SetMobjState(mobj, S_SPAWNBATS1); break; default: return false; break; } return true; } //========================================================================== // // DeactivateThing // //========================================================================== static boolean DeactivateThing(mobj_t * mobj) { if (mobj->flags & MF_COUNTKILL) { // Monster if (!(mobj->flags2 & MF2_DORMANT)) { mobj->flags2 |= MF2_DORMANT; mobj->tics = -1; return true; } return false; } switch (mobj->type) { case MT_ZTWINEDTORCH: case MT_ZTWINEDTORCH_UNLIT: P_SetMobjState(mobj, S_ZTWINEDTORCH_UNLIT); break; case MT_ZWALLTORCH: case MT_ZWALLTORCH_UNLIT: P_SetMobjState(mobj, S_ZWALLTORCH_U); break; case MT_THRUSTFLOOR_UP: case MT_THRUSTFLOOR_DOWN: if (mobj->args[0] == 1) { S_StartSound(mobj, SFX_THRUSTSPIKE_RAISE); if (mobj->args[1]) P_SetMobjState(mobj, S_BTHRUSTLOWER); else P_SetMobjState(mobj, S_THRUSTLOWER); } break; case MT_ZFIREBULL: case MT_ZFIREBULL_UNLIT: P_SetMobjState(mobj, S_ZFIREBULL_DEATH); break; case MT_ZCAULDRON: case MT_ZCAULDRON_UNLIT: P_SetMobjState(mobj, S_ZCAULDRON_U); break; case MT_FLAME_SMALL: P_SetMobjState(mobj, S_FLAME_SDORM1); break; case MT_FLAME_LARGE: P_SetMobjState(mobj, S_FLAME_LDORM1); break; case MT_BAT_SPAWNER: P_SetMobjState(mobj, S_SPAWNBATS_OFF); break; default: return false; break; } return true; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_tick.c000066400000000000000000000100631360717211000215470ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "p_local.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void RunThinkers(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- int leveltime; int TimerGame; thinker_t thinkercap; // The head and tail of the thinker list // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- //========================================================================== // // P_Ticker // //========================================================================== void P_Ticker(void) { int i; if (paused) { return; } for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { P_PlayerThink(&players[i]); } } if (TimerGame) { if (!--TimerGame) { G_Completed(P_TranslateMap(P_GetMapNextMap(gamemap)), 0); } } RunThinkers(); P_UpdateSpecials(); P_AnimateSurfaces(); leveltime++; } //========================================================================== // // RunThinkers // //========================================================================== static void RunThinkers(void) { thinker_t *currentthinker, *nextthinker; currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { if (currentthinker->function == (think_t) - 1) { // Time to remove it nextthinker = currentthinker->next; currentthinker->next->prev = currentthinker->prev; currentthinker->prev->next = currentthinker->next; Z_Free(currentthinker); } else { if (currentthinker->function) currentthinker->function(currentthinker); nextthinker = currentthinker->next; } currentthinker = nextthinker; } } //========================================================================== // // P_InitThinkers // //========================================================================== void P_InitThinkers(void) { thinkercap.prev = thinkercap.next = &thinkercap; } //========================================================================== // // P_AddThinker // // Adds a new thinker at the end of the list. // //========================================================================== void P_AddThinker(thinker_t * thinker) { thinkercap.prev->next = thinker; thinker->next = &thinkercap; thinker->prev = thinkercap.prev; thinkercap.prev = thinker; } //========================================================================== // // P_RemoveThinker // // Deallocation is lazy -- it will not actually be freed until its // thinking turn comes up. // //========================================================================== void P_RemoveThinker(thinker_t * thinker) { thinker->function = (think_t) - 1; } crispy-doom-crispy-doom-5.6.4/src/hexen/p_user.c000066400000000000000000001323451360717211000216030ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "m_random.h" #include "i_system.h" #include "p_local.h" #include "s_sound.h" void P_PlayerNextArtifact(player_t * player); // Macros #define MAXBOB 0x100000 // 16 pixels of bob // Data boolean onground; int newtorch; // used in the torch flicker effect. int newtorchdelta; int PStateNormal[NUMCLASSES] = { S_FPLAY, S_CPLAY, S_MPLAY, S_PIGPLAY }; int PStateRun[NUMCLASSES] = { S_FPLAY_RUN1, S_CPLAY_RUN1, S_MPLAY_RUN1, S_PIGPLAY_RUN1 }; int PStateAttack[NUMCLASSES] = { S_FPLAY_ATK1, S_CPLAY_ATK1, S_MPLAY_ATK1, S_PIGPLAY_ATK1 }; int PStateAttackEnd[NUMCLASSES] = { S_FPLAY_ATK2, S_CPLAY_ATK3, S_MPLAY_ATK2, S_PIGPLAY_ATK1 }; int ArmorMax[NUMCLASSES] = { 20, 18, 16, 1 }; /* ================== = = P_Thrust = = moves the given origin along a given angle = ================== */ void P_Thrust(player_t * player, angle_t angle, fixed_t move) { angle >>= ANGLETOFINESHIFT; if (player->powers[pw_flight] && !(player->mo->z <= player->mo->floorz)) { player->mo->momx += FixedMul(move, finecosine[angle]); player->mo->momy += FixedMul(move, finesine[angle]); } else if (P_GetThingFloorType(player->mo) == FLOOR_ICE) // Friction_Low { player->mo->momx += FixedMul(move >> 1, finecosine[angle]); player->mo->momy += FixedMul(move >> 1, finesine[angle]); } else { player->mo->momx += FixedMul(move, finecosine[angle]); player->mo->momy += FixedMul(move, finesine[angle]); } } /* ================== = = P_CalcHeight = =Calculate the walking / running height adjustment = ================== */ void P_CalcHeight(player_t * player) { int angle; fixed_t bob; // // regular movement bobbing (needs to be calculated for gun swing even // if not on ground) // OPTIMIZE: tablify angle player->bob = FixedMul(player->mo->momx, player->mo->momx) + FixedMul(player->mo->momy, player->mo->momy); player->bob >>= 2; if (player->bob > MAXBOB) player->bob = MAXBOB; if (player->mo->flags2 & MF2_FLY && !onground) { player->bob = FRACUNIT / 2; } if ((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; 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; } } if (player->morphTics) { player->viewz = player->mo->z + player->viewheight - (20 * FRACUNIT); } else { player->viewz = player->mo->z + player->viewheight + bob; } if (player->mo->floorclip && player->playerstate != PST_DEAD && player->mo->z <= player->mo->floorz) { player->viewz -= player->mo->floorclip; } if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) { player->viewz = player->mo->ceilingz - 4 * FRACUNIT; } if (player->viewz < player->mo->floorz + 4 * FRACUNIT) { player->viewz = player->mo->floorz + 4 * FRACUNIT; } } /* ================= = = P_MovePlayer = ================= */ void P_MovePlayer(player_t * player) { int look; int fly; ticcmd_t *cmd; cmd = &player->cmd; player->mo->angle += (cmd->angleturn << 16); onground = (player->mo->z <= player->mo->floorz || (player->mo->flags2 & MF2_ONMOBJ)); if (cmd->forwardmove) { if (onground || player->mo->flags2 & MF2_FLY) { P_Thrust(player, player->mo->angle, cmd->forwardmove * 2048); } else { P_Thrust(player, player->mo->angle, FRACUNIT >> 8); } } if (cmd->sidemove) { if (onground || player->mo->flags2 & MF2_FLY) { P_Thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2048); } else { P_Thrust(player, player->mo->angle, FRACUNIT >> 8); } } if (cmd->forwardmove || cmd->sidemove) { if (player->mo->state == &states[PStateNormal[player->class]]) { P_SetMobjState(player->mo, PStateRun[player->class]); } } look = cmd->lookfly & 15; if (look > 7) { look -= 16; } if (look) { if (look == TOCENTER) { player->centering = true; } else { player->lookdir += 5 * look; if (player->lookdir > 90 || player->lookdir < -110) { player->lookdir -= 5 * look; } } } if (player->centering) { if (player->lookdir > 0) { player->lookdir -= 8; } else if (player->lookdir < 0) { player->lookdir += 8; } if (abs(player->lookdir) < 8) { player->lookdir = 0; player->centering = false; } } fly = cmd->lookfly >> 4; if (fly > 7) { fly -= 16; } if (fly && player->powers[pw_flight]) { if (fly != TOCENTER) { player->flyheight = fly * 2; if (!(player->mo->flags2 & MF2_FLY)) { player->mo->flags2 |= MF2_FLY; player->mo->flags |= MF_NOGRAVITY; if (player->mo->momz <= -39 * FRACUNIT) { // stop falling scream S_StopSound(player->mo); } } } else { player->mo->flags2 &= ~MF2_FLY; player->mo->flags &= ~MF_NOGRAVITY; } } else if (fly > 0) { P_PlayerUseArtifact(player, arti_fly); } if (player->mo->flags2 & MF2_FLY) { player->mo->momz = player->flyheight * FRACUNIT; if (player->flyheight) { player->flyheight /= 2; } } } //========================================================================== // // P_DeathThink // //========================================================================== void P_DeathThink(player_t * player) { int dir; angle_t delta; int lookDelta; P_MovePsprites(player); onground = (player->mo->z <= player->mo->floorz); if (player->mo->type == MT_BLOODYSKULL || player->mo->type == MT_ICECHUNK) { // Flying bloody skull or flying ice chunk player->viewheight = 6 * FRACUNIT; player->deltaviewheight = 0; //player->damagecount = 20; if (onground) { if (player->lookdir < 60) { lookDelta = (60 - player->lookdir) / 8; if (lookDelta < 1 && (leveltime & 1)) { lookDelta = 1; } else if (lookDelta > 6) { lookDelta = 6; } player->lookdir += lookDelta; } } } else if (!(player->mo->flags2 & MF2_ICEDAMAGE)) { // Fall to ground (if not frozen) player->deltaviewheight = 0; if (player->viewheight > 6 * FRACUNIT) { player->viewheight -= FRACUNIT; } if (player->viewheight < 6 * FRACUNIT) { player->viewheight = 6 * FRACUNIT; } if (player->lookdir > 0) { player->lookdir -= 6; } else if (player->lookdir < 0) { player->lookdir += 6; } if (abs(player->lookdir) < 6) { player->lookdir = 0; } } P_CalcHeight(player); if (player->attacker && player->attacker != player->mo) { // Watch killer dir = P_FaceMobj(player->mo, player->attacker, &delta); if (delta < ANG1 * 10) { // Looking at killer, so fade damage and poison counters if (player->damagecount) { player->damagecount--; } if (player->poisoncount) { player->poisoncount--; } } delta = delta / 8; if (delta > ANG1 * 5) { delta = ANG1 * 5; } if (dir) { // Turn clockwise player->mo->angle += delta; } else { // Turn counter clockwise player->mo->angle -= delta; } } else if (player->damagecount || player->poisoncount) { if (player->damagecount) { player->damagecount--; } else { player->poisoncount--; } } if (player->cmd.buttons & BT_USE) { if (player == &players[consoleplayer]) { I_SetPalette((byte *) W_CacheLumpName("PLAYPAL", PU_CACHE)); inv_ptr = 0; curpos = 0; newtorch = 0; newtorchdelta = 0; } player->playerstate = PST_REBORN; player->mo->special1.i = player->class; if (player->mo->special1.i > 2) { player->mo->special1.i = 0; } // Let the mobj know the player has entered the reborn state. Some // mobjs need to know when it's ok to remove themselves. player->mo->special2.i = 666; } } //---------------------------------------------------------------------------- // // PROC P_MorphPlayerThink // //---------------------------------------------------------------------------- void P_MorphPlayerThink(player_t * player) { mobj_t *pmo; if (player->morphTics & 15) { return; } pmo = player->mo; if (!(pmo->momx + pmo->momy) && P_Random() < 64) { // Snout sniff P_SetPspriteNF(player, ps_weapon, S_SNOUTATK2); S_StartSound(pmo, SFX_PIG_ACTIVE1); // snort return; } if (P_Random() < 48) { if (P_Random() < 128) { S_StartSound(pmo, SFX_PIG_ACTIVE1); } else { S_StartSound(pmo, SFX_PIG_ACTIVE2); } } } //---------------------------------------------------------------------------- // // FUNC P_GetPlayerNum // //---------------------------------------------------------------------------- int P_GetPlayerNum(player_t * player) { int i; for (i = 0; i < maxplayers; i++) { if (player == &players[i]) { return (i); } } return (0); } //---------------------------------------------------------------------------- // // FUNC P_UndoPlayerMorph // //---------------------------------------------------------------------------- boolean P_UndoPlayerMorph(player_t * player) { mobj_t *fog; mobj_t *mo; mobj_t *pmo; fixed_t x; fixed_t y; fixed_t z; angle_t angle; int playerNum; weapontype_t weapon; int oldFlags; int oldFlags2; int oldBeast; pmo = player->mo; x = pmo->x; y = pmo->y; z = pmo->z; angle = pmo->angle; weapon = pmo->special1.i; oldFlags = pmo->flags; oldFlags2 = pmo->flags2; oldBeast = pmo->type; P_SetMobjState(pmo, S_FREETARGMOBJ); playerNum = P_GetPlayerNum(player); switch (PlayerClass[playerNum]) { case PCLASS_FIGHTER: mo = P_SpawnMobj(x, y, z, MT_PLAYER_FIGHTER); break; case PCLASS_CLERIC: mo = P_SpawnMobj(x, y, z, MT_PLAYER_CLERIC); break; case PCLASS_MAGE: mo = P_SpawnMobj(x, y, z, MT_PLAYER_MAGE); break; default: I_Error("P_UndoPlayerMorph: Unknown player class %d\n", player->class); return false; } if (P_TestMobjLocation(mo) == false) { // Didn't fit P_RemoveMobj(mo); mo = P_SpawnMobj(x, y, z, oldBeast); mo->angle = angle; mo->health = player->health; mo->special1.i = weapon; mo->player = player; mo->flags = oldFlags; mo->flags2 = oldFlags2; player->mo = mo; player->morphTics = 2 * 35; return (false); } if (player->class == PCLASS_FIGHTER) { // The first type should be blue, and the third should be the // Fighter's original gold color if (playerNum == 0) { mo->flags |= 2 << MF_TRANSSHIFT; } else if (playerNum != 2) { mo->flags |= playerNum << MF_TRANSSHIFT; } } else if (playerNum) { // Set color translation bits for player sprites mo->flags |= playerNum << MF_TRANSSHIFT; } mo->angle = angle; mo->player = player; mo->reactiontime = 18; if (oldFlags2 & MF2_FLY) { mo->flags2 |= MF2_FLY; mo->flags |= MF_NOGRAVITY; } player->morphTics = 0; player->health = mo->health = MAXHEALTH; player->mo = mo; player->class = PlayerClass[playerNum]; angle >>= ANGLETOFINESHIFT; fog = P_SpawnMobj(x + 20 * finecosine[angle], y + 20 * finesine[angle], z + TELEFOGHEIGHT, MT_TFOG); S_StartSound(fog, SFX_TELEPORT); P_PostMorphWeapon(player, weapon); return (true); } //---------------------------------------------------------------------------- // // PROC P_PlayerThink // //---------------------------------------------------------------------------- void P_PlayerThink(player_t * player) { ticcmd_t *cmd; weapontype_t newweapon; int floorType; mobj_t *pmo; // No-clip cheat if (player->cheats & CF_NOCLIP) { player->mo->flags |= MF_NOCLIP; } else { player->mo->flags &= ~MF_NOCLIP; } cmd = &player->cmd; if (player->mo->flags & MF_JUSTATTACKED) { // Gauntlets attack auto forward motion cmd->angleturn = 0; cmd->forwardmove = 0xc800 / 512; cmd->sidemove = 0; player->mo->flags &= ~MF_JUSTATTACKED; } // messageTics is above the rest of the counters so that messages will // go away, even in death. player->messageTics--; // Can go negative if (!player->messageTics || player->messageTics == -1) { // Refresh the screen when a message goes away player->ultimateMessage = false; // clear out any chat messages. player->yellowMessage = false; if (player == &players[consoleplayer]) { BorderTopRefresh = true; } } player->worldTimer++; if (player->playerstate == PST_DEAD) { P_DeathThink(player); return; } if (player->jumpTics) { player->jumpTics--; } if (player->morphTics) { P_MorphPlayerThink(player); } // Handle movement if (player->mo->reactiontime) { // Player is frozen player->mo->reactiontime--; } else { P_MovePlayer(player); pmo = player->mo; if (player->powers[pw_speed] && !(leveltime & 1) && P_AproxDistance(pmo->momx, pmo->momy) > 12 * FRACUNIT) { mobj_t *speedMo; int playerNum; speedMo = P_SpawnMobj(pmo->x, pmo->y, pmo->z, MT_PLAYER_SPEED); if (speedMo) { speedMo->angle = pmo->angle; playerNum = P_GetPlayerNum(player); if (player->class == PCLASS_FIGHTER) { // The first type should be blue, and the // third should be the Fighter's original gold color if (playerNum == 0) { speedMo->flags |= 2 << MF_TRANSSHIFT; } else if (playerNum != 2) { speedMo->flags |= playerNum << MF_TRANSSHIFT; } } else if (playerNum) { // Set color translation bits for player sprites speedMo->flags |= playerNum << MF_TRANSSHIFT; } speedMo->target = pmo; speedMo->special1.i = player->class; if (speedMo->special1.i > 2) { speedMo->special1.i = 0; } speedMo->sprite = pmo->sprite; speedMo->floorclip = pmo->floorclip; if (player == &players[consoleplayer]) { speedMo->flags2 |= MF2_DONTDRAW; } } } } P_CalcHeight(player); if (player->mo->subsector->sector->special) { P_PlayerInSpecialSector(player); } if ((floorType = P_GetThingFloorType(player->mo)) != FLOOR_SOLID) { P_PlayerOnSpecialFlat(player, floorType); } switch (player->class) { case PCLASS_FIGHTER: if (player->mo->momz <= -35 * FRACUNIT && player->mo->momz >= -40 * FRACUNIT && !player->morphTics && !S_GetSoundPlayingInfo(player->mo, SFX_PLAYER_FIGHTER_FALLING_SCREAM)) { S_StartSound(player->mo, SFX_PLAYER_FIGHTER_FALLING_SCREAM); } break; case PCLASS_CLERIC: if (player->mo->momz <= -35 * FRACUNIT && player->mo->momz >= -40 * FRACUNIT && !player->morphTics && !S_GetSoundPlayingInfo(player->mo, SFX_PLAYER_CLERIC_FALLING_SCREAM)) { S_StartSound(player->mo, SFX_PLAYER_CLERIC_FALLING_SCREAM); } break; case PCLASS_MAGE: if (player->mo->momz <= -35 * FRACUNIT && player->mo->momz >= -40 * FRACUNIT && !player->morphTics && !S_GetSoundPlayingInfo(player->mo, SFX_PLAYER_MAGE_FALLING_SCREAM)) { S_StartSound(player->mo, SFX_PLAYER_MAGE_FALLING_SCREAM); } break; default: break; } if (cmd->arti) { // Use an artifact if ((cmd->arti & AFLAG_JUMP) && onground && !player->jumpTics) { if (player->morphTics) { player->mo->momz = 6 * FRACUNIT; } else { player->mo->momz = 9 * FRACUNIT; } player->mo->flags2 &= ~MF2_ONMOBJ; player->jumpTics = 18; } else if (cmd->arti & AFLAG_SUICIDE) { P_DamageMobj(player->mo, NULL, NULL, 10000); } if (cmd->arti == NUMARTIFACTS) { // use one of each artifact (except puzzle artifacts) int i; for (i = 1; i < arti_firstpuzzitem; i++) { P_PlayerUseArtifact(player, i); } } else { P_PlayerUseArtifact(player, cmd->arti & AFLAG_MASK); } } // Check for weapon change if (cmd->buttons & BT_SPECIAL) { // A special event has no other buttons cmd->buttons = 0; } if (cmd->buttons & BT_CHANGE && !player->morphTics) { // The actual changing of the weapon is done when the weapon // psprite can do it (A_WeaponReady), so it doesn't happen in // the middle of an attack. newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT; if (player->weaponowned[newweapon] && newweapon != player->readyweapon) { player->pendingweapon = newweapon; } } // Check for use if (cmd->buttons & BT_USE) { if (!player->usedown) { P_UseLines(player); player->usedown = true; } } else { player->usedown = false; } // Morph counter if (player->morphTics) { if (!--player->morphTics) { // Attempt to undo the pig P_UndoPlayerMorph(player); } } // Cycle psprites P_MovePsprites(player); // Other Counters if (player->powers[pw_invulnerability]) { if (player->class == PCLASS_CLERIC) { if (!(leveltime & 7) && player->mo->flags & MF_SHADOW && !(player->mo->flags2 & MF2_DONTDRAW)) { player->mo->flags &= ~MF_SHADOW; if (!(player->mo->flags & MF_ALTSHADOW)) { player->mo->flags2 |= MF2_DONTDRAW | MF2_NONSHOOTABLE; } } if (!(leveltime & 31)) { if (player->mo->flags2 & MF2_DONTDRAW) { if (!(player->mo->flags & MF_SHADOW)) { player->mo->flags |= MF_SHADOW | MF_ALTSHADOW; } else { player->mo->flags2 &= ~(MF2_DONTDRAW | MF2_NONSHOOTABLE); } } else { player->mo->flags |= MF_SHADOW; player->mo->flags &= ~MF_ALTSHADOW; } } } if (!(--player->powers[pw_invulnerability])) { player->mo->flags2 &= ~(MF2_INVULNERABLE | MF2_REFLECTIVE); if (player->class == PCLASS_CLERIC) { player->mo->flags2 &= ~(MF2_DONTDRAW | MF2_NONSHOOTABLE); player->mo->flags &= ~(MF_SHADOW | MF_ALTSHADOW); } } } if (player->powers[pw_minotaur]) { player->powers[pw_minotaur]--; } if (player->powers[pw_infrared]) { player->powers[pw_infrared]--; } if (player->powers[pw_flight] && netgame) { if (!--player->powers[pw_flight]) { if (player->mo->z != player->mo->floorz) { // haleyjd: removed externdriver crap player->centering = true; } player->mo->flags2 &= ~MF2_FLY; player->mo->flags &= ~MF_NOGRAVITY; BorderTopRefresh = true; //make sure the sprite's cleared out } } if (player->powers[pw_speed]) { player->powers[pw_speed]--; } if (player->damagecount) { player->damagecount--; } if (player->bonuscount) { player->bonuscount--; } if (player->poisoncount && !(leveltime & 15)) { player->poisoncount -= 5; if (player->poisoncount < 0) { player->poisoncount = 0; } P_PoisonDamage(player, player->poisoner, 1, true); } // Colormaps // if(player->powers[pw_invulnerability]) // { // if(player->powers[pw_invulnerability] > BLINKTHRESHOLD // || (player->powers[pw_invulnerability]&8)) // { // player->fixedcolormap = INVERSECOLORMAP; // } // else // { // player->fixedcolormap = 0; // } // } // else if (player->powers[pw_infrared]) { if (player->powers[pw_infrared] <= BLINKTHRESHOLD) { if (player->powers[pw_infrared] & 8) { player->fixedcolormap = 0; } else { player->fixedcolormap = 1; } } else if (!(leveltime & 16) && player == &players[consoleplayer]) { if (newtorch) { if (player->fixedcolormap + newtorchdelta > 7 || player->fixedcolormap + newtorchdelta < 1 || newtorch == player->fixedcolormap) { newtorch = 0; } else { player->fixedcolormap += newtorchdelta; } } else { newtorch = (M_Random() & 7) + 1; newtorchdelta = (newtorch == player->fixedcolormap) ? 0 : ((newtorch > player->fixedcolormap) ? 1 : -1); } } } else { player->fixedcolormap = 0; } } //---------------------------------------------------------------------------- // // PROC P_ArtiTele // //---------------------------------------------------------------------------- void P_ArtiTele(player_t * player) { int i; int selections; fixed_t destX; fixed_t destY; angle_t destAngle; if (deathmatch) { selections = deathmatch_p - deathmatchstarts; i = P_Random() % selections; destX = deathmatchstarts[i].x << FRACBITS; destY = deathmatchstarts[i].y << FRACBITS; destAngle = ANG45 * (deathmatchstarts[i].angle / 45); } else { destX = playerstarts[0][0].x << FRACBITS; destY = playerstarts[0][0].y << FRACBITS; destAngle = ANG45 * (playerstarts[0][0].angle / 45); } P_Teleport(player->mo, destX, destY, destAngle, true); if (player->morphTics) { // Teleporting away will undo any morph effects (pig) P_UndoPlayerMorph(player); } //S_StartSound(NULL, sfx_wpnup); // Full volume laugh } //---------------------------------------------------------------------------- // // PROC P_ArtiTeleportOther // //---------------------------------------------------------------------------- void P_ArtiTeleportOther(player_t * player) { mobj_t *mo; mo = P_SpawnPlayerMissile(player->mo, MT_TELOTHER_FX1); if (mo) { mo->target = player->mo; } } void P_TeleportToPlayerStarts(mobj_t * victim) { int i, selections = 0; fixed_t destX, destY; angle_t destAngle; for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) continue; selections++; } i = P_Random() % selections; destX = playerstarts[0][i].x << FRACBITS; destY = playerstarts[0][i].y << FRACBITS; destAngle = ANG45 * (playerstarts[0][i].angle / 45); P_Teleport(victim, destX, destY, destAngle, true); //S_StartSound(NULL, sfx_wpnup); // Full volume laugh } void P_TeleportToDeathmatchStarts(mobj_t * victim) { int i, selections; fixed_t destX, destY; angle_t destAngle; selections = deathmatch_p - deathmatchstarts; if (selections) { i = P_Random() % selections; destX = deathmatchstarts[i].x << FRACBITS; destY = deathmatchstarts[i].y << FRACBITS; destAngle = ANG45 * (deathmatchstarts[i].angle / 45); P_Teleport(victim, destX, destY, destAngle, true); //S_StartSound(NULL, sfx_wpnup); // Full volume laugh } else { P_TeleportToPlayerStarts(victim); } } //---------------------------------------------------------------------------- // // PROC P_TeleportOther // //---------------------------------------------------------------------------- void P_TeleportOther(mobj_t * victim) { if (victim->player) { if (deathmatch) P_TeleportToDeathmatchStarts(victim); else P_TeleportToPlayerStarts(victim); } else { // If death action, run it upon teleport if (victim->flags & MF_COUNTKILL && victim->special) { P_RemoveMobjFromTIDList(victim); P_ExecuteLineSpecial(victim->special, victim->args, NULL, 0, victim); victim->special = 0; } // Send all monsters to deathmatch spots P_TeleportToDeathmatchStarts(victim); } } #define BLAST_RADIUS_DIST 255*FRACUNIT #define BLAST_SPEED 20*FRACUNIT #define BLAST_FULLSTRENGTH 255 void ResetBlasted(mobj_t * mo) { mo->flags2 &= ~MF2_BLASTED; if (!(mo->flags & MF_ICECORPSE)) { mo->flags2 &= ~MF2_SLIDE; } } void P_BlastMobj(mobj_t * source, mobj_t * victim, fixed_t strength) { angle_t angle, ang; mobj_t *mo; fixed_t x, y, z; angle = R_PointToAngle2(source->x, source->y, victim->x, victim->y); angle >>= ANGLETOFINESHIFT; if (strength < BLAST_FULLSTRENGTH) { victim->momx = FixedMul(strength, finecosine[angle]); victim->momy = FixedMul(strength, finesine[angle]); if (victim->player) { // Players handled automatically } else { victim->flags2 |= MF2_SLIDE; victim->flags2 |= MF2_BLASTED; } } else // full strength blast from artifact { if (victim->flags & MF_MISSILE) { switch (victim->type) { case MT_SORCBALL1: // don't blast sorcerer balls case MT_SORCBALL2: case MT_SORCBALL3: return; break; case MT_MSTAFF_FX2: // Reflect to originator victim->special1.m = victim->target; victim->target = source; break; default: break; } } if (victim->type == MT_HOLY_FX) { if (victim->special1.m == source) { victim->special1.m = victim->target; victim->target = source; } } victim->momx = FixedMul(BLAST_SPEED, finecosine[angle]); victim->momy = FixedMul(BLAST_SPEED, finesine[angle]); // Spawn blast puff ang = R_PointToAngle2(victim->x, victim->y, source->x, source->y); ang >>= ANGLETOFINESHIFT; x = victim->x + FixedMul(victim->radius + FRACUNIT, finecosine[ang]); y = victim->y + FixedMul(victim->radius + FRACUNIT, finesine[ang]); z = victim->z - victim->floorclip + (victim->height >> 1); mo = P_SpawnMobj(x, y, z, MT_BLASTEFFECT); if (mo) { mo->momx = victim->momx; mo->momy = victim->momy; } if (victim->flags & MF_MISSILE) { victim->momz = 8 * FRACUNIT; mo->momz = victim->momz; } else { victim->momz = (1000 / victim->info->mass) << FRACBITS; } if (victim->player) { // Players handled automatically } else { victim->flags2 |= MF2_SLIDE; victim->flags2 |= MF2_BLASTED; } } } // Blast all mobj things away void P_BlastRadius(player_t * player) { mobj_t *mo; mobj_t *pmo = player->mo; thinker_t *think; fixed_t dist; S_StartSound(pmo, SFX_ARTIFACT_BLAST); P_NoiseAlert(player->mo, player->mo); for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *) think; if ((mo == pmo) || (mo->flags2 & MF2_BOSS)) { // Not a valid monster continue; } if ((mo->type == MT_POISONCLOUD) || // poison cloud (mo->type == MT_HOLY_FX) || // holy fx (mo->flags & MF_ICECORPSE)) // frozen corpse { // Let these special cases go } else if ((mo->flags & MF_COUNTKILL) && (mo->health <= 0)) { continue; } else if (!(mo->flags & MF_COUNTKILL) && !(mo->player) && !(mo->flags & MF_MISSILE)) { // Must be monster, player, or missile continue; } if (mo->flags2 & MF2_DORMANT) { continue; // no dormant creatures } if ((mo->type == MT_WRAITHB) && (mo->flags2 & MF2_DONTDRAW)) { continue; // no underground wraiths } if ((mo->type == MT_SPLASHBASE) || (mo->type == MT_SPLASH)) { continue; } if (mo->type == MT_SERPENT || mo->type == MT_SERPENTLEADER) { continue; } dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y); if (dist > BLAST_RADIUS_DIST) { // Out of range continue; } P_BlastMobj(pmo, mo, BLAST_FULLSTRENGTH); } } #define HEAL_RADIUS_DIST 255*FRACUNIT // Do class specific effect for everyone in radius boolean P_HealRadius(player_t * player) { mobj_t *mo; mobj_t *pmo = player->mo; thinker_t *think; fixed_t dist; int effective = false; int amount; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != P_MobjThinker) { // Not a mobj thinker continue; } mo = (mobj_t *) think; if (!mo->player) continue; if (mo->health <= 0) continue; dist = P_AproxDistance(pmo->x - mo->x, pmo->y - mo->y); if (dist > HEAL_RADIUS_DIST) { // Out of range continue; } switch (player->class) { case PCLASS_FIGHTER: // Radius armor boost if ((P_GiveArmor(mo->player, ARMOR_ARMOR, 1)) || (P_GiveArmor(mo->player, ARMOR_SHIELD, 1)) || (P_GiveArmor(mo->player, ARMOR_HELMET, 1)) || (P_GiveArmor(mo->player, ARMOR_AMULET, 1))) { effective = true; S_StartSound(mo, SFX_MYSTICINCANT); } break; case PCLASS_CLERIC: // Radius heal amount = 50 + (P_Random() % 50); if (P_GiveBody(mo->player, amount)) { effective = true; S_StartSound(mo, SFX_MYSTICINCANT); } break; case PCLASS_MAGE: // Radius mana boost amount = 50 + (P_Random() % 50); if ((P_GiveMana(mo->player, MANA_1, amount)) || (P_GiveMana(mo->player, MANA_2, amount))) { effective = true; S_StartSound(mo, SFX_MYSTICINCANT); } break; case PCLASS_PIG: default: break; } } return (effective); } //---------------------------------------------------------------------------- // // PROC P_PlayerNextArtifact // //---------------------------------------------------------------------------- void P_PlayerNextArtifact(player_t * player) { if (player == &players[consoleplayer]) { inv_ptr--; if (inv_ptr < 6) { curpos--; if (curpos < 0) { curpos = 0; } } if (inv_ptr < 0) { inv_ptr = player->inventorySlotNum - 1; if (inv_ptr < 6) { curpos = inv_ptr; } else { curpos = 6; } } player->readyArtifact = player->inventory[inv_ptr].type; } } //---------------------------------------------------------------------------- // // PROC P_PlayerRemoveArtifact // //---------------------------------------------------------------------------- void P_PlayerRemoveArtifact(player_t * player, int slot) { int i; player->artifactCount--; if (!(--player->inventory[slot].count)) { // Used last of a type - compact the artifact list player->readyArtifact = arti_none; player->inventory[slot].type = arti_none; for (i = slot + 1; i < player->inventorySlotNum; i++) { player->inventory[i - 1] = player->inventory[i]; } player->inventorySlotNum--; if (player == &players[consoleplayer]) { // Set position markers and get next readyArtifact inv_ptr--; if (inv_ptr < 6) { curpos--; if (curpos < 0) { curpos = 0; } } if (inv_ptr >= player->inventorySlotNum) { inv_ptr = player->inventorySlotNum - 1; } if (inv_ptr < 0) { inv_ptr = 0; } player->readyArtifact = player->inventory[inv_ptr].type; } } } //---------------------------------------------------------------------------- // // PROC P_PlayerUseArtifact // //---------------------------------------------------------------------------- void P_PlayerUseArtifact(player_t * player, artitype_t arti) { int i; for (i = 0; i < player->inventorySlotNum; i++) { if (player->inventory[i].type == arti) { // Found match - try to use if (P_UseArtifact(player, arti)) { // Artifact was used - remove it from inventory P_PlayerRemoveArtifact(player, i); if (player == &players[consoleplayer]) { if (arti < arti_firstpuzzitem) { S_StartSound(NULL, SFX_ARTIFACT_USE); } else { S_StartSound(NULL, SFX_PUZZLE_SUCCESS); } ArtifactFlash = 4; } } else if (arti < arti_firstpuzzitem) { // Unable to use artifact, advance pointer P_PlayerNextArtifact(player); } break; } } } //========================================================================== // // P_UseArtifact // // Returns true if the artifact was used. // //========================================================================== boolean P_UseArtifact(player_t * player, artitype_t arti) { mobj_t *mo; angle_t angle; int i; int count; switch (arti) { case arti_invulnerability: if (!P_GivePower(player, pw_invulnerability)) { return (false); } break; case arti_health: if (!P_GiveBody(player, 25)) { return (false); } break; case arti_superhealth: if (!P_GiveBody(player, 100)) { return (false); } break; case arti_healingradius: if (!P_HealRadius(player)) { return (false); } break; case arti_torch: if (!P_GivePower(player, pw_infrared)) { return (false); } break; case arti_egg: mo = player->mo; P_SpawnPlayerMissile(mo, MT_EGGFX); P_SPMAngle(mo, MT_EGGFX, mo->angle - (ANG45 / 6)); P_SPMAngle(mo, MT_EGGFX, mo->angle + (ANG45 / 6)); P_SPMAngle(mo, MT_EGGFX, mo->angle - (ANG45 / 3)); P_SPMAngle(mo, MT_EGGFX, mo->angle + (ANG45 / 3)); break; case arti_fly: if (!P_GivePower(player, pw_flight)) { return (false); } if (player->mo->momz <= -35 * FRACUNIT) { // stop falling scream S_StopSound(player->mo); } break; case arti_summon: mo = P_SpawnPlayerMissile(player->mo, MT_SUMMON_FX); if (mo) { mo->target = player->mo; mo->special1.m = (player->mo); mo->momz = 5 * FRACUNIT; } break; case arti_teleport: P_ArtiTele(player); break; case arti_teleportother: P_ArtiTeleportOther(player); break; case arti_poisonbag: angle = player->mo->angle >> ANGLETOFINESHIFT; if (player->class == PCLASS_CLERIC) { mo = P_SpawnMobj(player->mo->x + 16 * finecosine[angle], player->mo->y + 24 * finesine[angle], player->mo->z - player->mo->floorclip + 8 * FRACUNIT, MT_POISONBAG); if (mo) { mo->target = player->mo; } } else if (player->class == PCLASS_MAGE) { mo = P_SpawnMobj(player->mo->x + 16 * finecosine[angle], player->mo->y + 24 * finesine[angle], player->mo->z - player->mo->floorclip + 8 * FRACUNIT, MT_FIREBOMB); if (mo) { mo->target = player->mo; } } else // PCLASS_FIGHTER, obviously (also pig, not so obviously) { mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z - player->mo->floorclip + 35 * FRACUNIT, MT_THROWINGBOMB); if (mo) { mo->angle = player->mo->angle + (((P_Random() & 7) - 4) << 24); mo->momz = 4 * FRACUNIT + ((player->lookdir) << (FRACBITS - 4)); mo->z += player->lookdir << (FRACBITS - 4); P_ThrustMobj(mo, mo->angle, mo->info->speed); mo->momx += player->mo->momx >> 1; mo->momy += player->mo->momy >> 1; mo->target = player->mo; mo->tics -= P_Random() & 3; P_CheckMissileSpawn(mo); } } break; case arti_speed: if (!P_GivePower(player, pw_speed)) { return (false); } break; case arti_boostmana: if (!P_GiveMana(player, MANA_1, MAX_MANA)) { if (!P_GiveMana(player, MANA_2, MAX_MANA)) { return false; } } else { P_GiveMana(player, MANA_2, MAX_MANA); } break; case arti_boostarmor: count = 0; for (i = 0; i < NUMARMOR; i++) { count += P_GiveArmor(player, i, 1); // 1 point per armor type } if (!count) { return false; } break; case arti_blastradius: P_BlastRadius(player); break; case arti_puzzskull: case arti_puzzgembig: case arti_puzzgemred: case arti_puzzgemgreen1: case arti_puzzgemgreen2: case arti_puzzgemblue1: case arti_puzzgemblue2: case arti_puzzbook1: case arti_puzzbook2: case arti_puzzskull2: case arti_puzzfweapon: case arti_puzzcweapon: case arti_puzzmweapon: case arti_puzzgear1: case arti_puzzgear2: case arti_puzzgear3: case arti_puzzgear4: if (P_UsePuzzleItem(player, arti - arti_firstpuzzitem)) { return true; } else { P_SetYellowMessage(player, TXT_USEPUZZLEFAILED, false); return false; } break; default: return false; } return true; } //============================================================================ // // A_SpeedFade // //============================================================================ void A_SpeedFade(mobj_t * actor) { actor->flags |= MF_SHADOW; actor->flags &= ~MF_ALTSHADOW; actor->sprite = actor->target->sprite; } crispy-doom-crispy-doom-5.6.4/src/hexen/po_man.c000066400000000000000000001300761360717211000215560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "i_system.h" #include "m_bbox.h" #include "i_swap.h" #include "p_local.h" #include "r_local.h" // MACROS ------------------------------------------------------------------ #define PO_MAXPOLYSEGS 64 // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static polyobj_t *GetPolyobj(int polyNum); static int GetPolyobjMirror(int poly); static void ThrustMobj(mobj_t * mobj, seg_t * seg, polyobj_t * po); static void UpdateSegBBox(seg_t * seg); static void RotatePt(int an, fixed_t * x, fixed_t * y, fixed_t startSpotX, fixed_t startSpotY); static void UnLinkPolyobj(polyobj_t * po); static void LinkPolyobj(polyobj_t * po); static boolean CheckMobjBlocking(seg_t * seg, polyobj_t * po); static void InitBlockMap(void); static void IterFindPolySegs(int x, int y, seg_t ** segList); static void SpawnPolyobj(int index, int tag, boolean crush); static void TranslateToStartSpot(int tag, int originX, int originY); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- polyblock_t **PolyBlockMap; polyobj_t *polyobjs; // list of all poly-objects on the level int po_NumPolyobjs; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int PolySegCount; static fixed_t PolyStartX; static fixed_t PolyStartY; // CODE -------------------------------------------------------------------- // ===== Polyobj Event Code ===== //========================================================================== // // T_RotatePoly // //========================================================================== void T_RotatePoly(polyevent_t * pe) { int absSpeed; polyobj_t *poly; if (PO_RotatePolyobj(pe->polyobj, pe->speed)) { absSpeed = abs(pe->speed); if (pe->dist == -1) { // perpetual polyobj return; } pe->dist -= absSpeed; if (pe->dist <= 0) { poly = GetPolyobj(pe->polyobj); if (poly->specialdata == pe) { poly->specialdata = NULL; } SN_StopSequence((mobj_t *) & poly->startSpot); P_PolyobjFinished(poly->tag); P_RemoveThinker(&pe->thinker); } if (pe->dist < absSpeed) { pe->speed = pe->dist * (pe->speed < 0 ? -1 : 1); } } } //========================================================================== // // EV_RotatePoly // //========================================================================== boolean EV_RotatePoly(line_t * line, byte * args, int direction, boolean overRide) { int mirror; int polyNum; polyevent_t *pe; polyobj_t *poly; polyNum = args[0]; poly = GetPolyobj(polyNum); if (poly != NULL) { if (poly->specialdata && !overRide) { // poly is already moving return false; } } else { I_Error("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum); } pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); P_AddThinker(&pe->thinker); pe->thinker.function = T_RotatePoly; pe->polyobj = polyNum; if (args[2]) { if (args[2] == 255) { pe->dist = -1; } else { pe->dist = args[2] * (ANG90 / 64); // Angle } } else { pe->dist = ANG_MAX - 1; } pe->speed = (args[1] * direction * (ANG90 / 64)) >> 3; poly->specialdata = pe; SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); while ((mirror = GetPolyobjMirror(polyNum)) != 0) { poly = GetPolyobj(mirror); if (poly && poly->specialdata && !overRide) { // mirroring poly is already in motion break; } pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); P_AddThinker(&pe->thinker); pe->thinker.function = T_RotatePoly; poly->specialdata = pe; pe->polyobj = mirror; if (args[2]) { if (args[2] == 255) { pe->dist = -1; } else { pe->dist = args[2] * (ANG90 / 64); // Angle } } else { pe->dist = ANG_MAX - 1; } poly = GetPolyobj(polyNum); if (poly != NULL) { poly->specialdata = pe; } else { I_Error("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum); } direction = -direction; pe->speed = (args[1] * direction * (ANG90 / 64)) >> 3; polyNum = mirror; SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } return true; } //========================================================================== // // T_MovePoly // //========================================================================== void T_MovePoly(polyevent_t * pe) { int absSpeed; polyobj_t *poly; if (PO_MovePolyobj(pe->polyobj, pe->xSpeed, pe->ySpeed)) { absSpeed = abs(pe->speed); pe->dist -= absSpeed; if (pe->dist <= 0) { poly = GetPolyobj(pe->polyobj); if (poly->specialdata == pe) { poly->specialdata = NULL; } SN_StopSequence((mobj_t *) & poly->startSpot); P_PolyobjFinished(poly->tag); P_RemoveThinker(&pe->thinker); } if (pe->dist < absSpeed) { pe->speed = pe->dist * (pe->speed < 0 ? -1 : 1); pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); } } } //========================================================================== // // EV_MovePoly // //========================================================================== boolean EV_MovePoly(line_t * line, byte * args, boolean timesEight, boolean overRide) { int mirror; int polyNum; polyevent_t *pe; polyobj_t *poly; angle_t an; polyNum = args[0]; poly = GetPolyobj(polyNum); if (poly != NULL) { if (poly->specialdata && !overRide) { // poly is already moving return false; } } else { I_Error("EV_MovePoly: Invalid polyobj num: %d\n", polyNum); } pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); P_AddThinker(&pe->thinker); pe->thinker.function = T_MovePoly; pe->polyobj = polyNum; if (timesEight) { pe->dist = args[3] * 8 * FRACUNIT; } else { pe->dist = args[3] * FRACUNIT; // Distance } pe->speed = args[1] * (FRACUNIT / 8); poly->specialdata = pe; an = args[2] * (ANG90 / 64); pe->angle = an >> ANGLETOFINESHIFT; pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); while ((mirror = GetPolyobjMirror(polyNum)) != 0) { poly = GetPolyobj(mirror); if (poly && poly->specialdata && !overRide) { // mirroring poly is already in motion break; } pe = Z_Malloc(sizeof(polyevent_t), PU_LEVSPEC, 0); P_AddThinker(&pe->thinker); pe->thinker.function = T_MovePoly; pe->polyobj = mirror; poly->specialdata = pe; if (timesEight) { pe->dist = args[3] * 8 * FRACUNIT; } else { pe->dist = args[3] * FRACUNIT; // Distance } pe->speed = args[1] * (FRACUNIT / 8); an = an + ANG180; // reverse the angle pe->angle = an >> ANGLETOFINESHIFT; pe->xSpeed = FixedMul(pe->speed, finecosine[pe->angle]); pe->ySpeed = FixedMul(pe->speed, finesine[pe->angle]); polyNum = mirror; SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } return true; } //========================================================================== // // T_PolyDoor // //========================================================================== void T_PolyDoor(polydoor_t * pd) { int absSpeed; polyobj_t *poly; if (pd->tics) { if (!--pd->tics) { poly = GetPolyobj(pd->polyobj); SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } return; } switch (pd->type) { case PODOOR_SLIDE: if (PO_MovePolyobj(pd->polyobj, pd->xSpeed, pd->ySpeed)) { absSpeed = abs(pd->speed); pd->dist -= absSpeed; if (pd->dist <= 0) { poly = GetPolyobj(pd->polyobj); SN_StopSequence((mobj_t *) & poly->startSpot); if (!pd->close) { pd->dist = pd->totalDist; pd->close = true; pd->tics = pd->waitTics; pd->direction = (ANG_MAX >> ANGLETOFINESHIFT) - pd->direction; pd->xSpeed = -pd->xSpeed; pd->ySpeed = -pd->ySpeed; } else { if (poly->specialdata == pd) { poly->specialdata = NULL; } P_PolyobjFinished(poly->tag); P_RemoveThinker(&pd->thinker); } } } else { poly = GetPolyobj(pd->polyobj); if (poly->crush || !pd->close) { // continue moving if the poly is a crusher, or is opening return; } else { // open back up pd->dist = pd->totalDist - pd->dist; pd->direction = (ANG_MAX >> ANGLETOFINESHIFT) - pd->direction; pd->xSpeed = -pd->xSpeed; pd->ySpeed = -pd->ySpeed; pd->close = false; SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } } break; case PODOOR_SWING: if (PO_RotatePolyobj(pd->polyobj, pd->speed)) { absSpeed = abs(pd->speed); if (pd->dist == -1) { // perpetual polyobj return; } pd->dist -= absSpeed; if (pd->dist <= 0) { poly = GetPolyobj(pd->polyobj); SN_StopSequence((mobj_t *) & poly->startSpot); if (!pd->close) { pd->dist = pd->totalDist; pd->close = true; pd->tics = pd->waitTics; pd->speed = -pd->speed; } else { if (poly->specialdata == pd) { poly->specialdata = NULL; } P_PolyobjFinished(poly->tag); P_RemoveThinker(&pd->thinker); } } } else { poly = GetPolyobj(pd->polyobj); if (poly->crush || !pd->close) { // continue moving if the poly is a crusher, or is opening return; } else { // open back up and rewait pd->dist = pd->totalDist - pd->dist; pd->speed = -pd->speed; pd->close = false; SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } } break; default: break; } } //========================================================================== // // EV_OpenPolyDoor // //========================================================================== boolean EV_OpenPolyDoor(line_t * line, byte * args, podoortype_t type) { int mirror; int polyNum; polydoor_t *pd; polyobj_t *poly; angle_t an = 0; polyNum = args[0]; poly = GetPolyobj(polyNum); if (poly != NULL) { if (poly->specialdata) { // poly is already moving return false; } } else { I_Error("EV_OpenPolyDoor: Invalid polyobj num: %d\n", polyNum); } pd = Z_Malloc(sizeof(polydoor_t), PU_LEVSPEC, 0); memset(pd, 0, sizeof(polydoor_t)); P_AddThinker(&pd->thinker); pd->thinker.function = T_PolyDoor; pd->type = type; pd->polyobj = polyNum; if (type == PODOOR_SLIDE) { pd->waitTics = args[4]; pd->speed = args[1] * (FRACUNIT / 8); pd->totalDist = args[3] * FRACUNIT; // Distance pd->dist = pd->totalDist; an = args[2] * (ANG90 / 64); pd->direction = an >> ANGLETOFINESHIFT; pd->xSpeed = FixedMul(pd->speed, finecosine[pd->direction]); pd->ySpeed = FixedMul(pd->speed, finesine[pd->direction]); SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } else if (type == PODOOR_SWING) { pd->waitTics = args[3]; pd->direction = 1; // ADD: PODOOR_SWINGL, PODOOR_SWINGR pd->speed = (args[1] * pd->direction * (ANG90 / 64)) >> 3; pd->totalDist = args[2] * (ANG90 / 64); pd->dist = pd->totalDist; SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } poly->specialdata = pd; while ((mirror = GetPolyobjMirror(polyNum)) != 0) { poly = GetPolyobj(mirror); if (poly && poly->specialdata) { // mirroring poly is already in motion break; } pd = Z_Malloc(sizeof(polydoor_t), PU_LEVSPEC, 0); memset(pd, 0, sizeof(polydoor_t)); P_AddThinker(&pd->thinker); pd->thinker.function = T_PolyDoor; pd->polyobj = mirror; pd->type = type; poly->specialdata = pd; if (type == PODOOR_SLIDE) { pd->waitTics = args[4]; pd->speed = args[1] * (FRACUNIT / 8); pd->totalDist = args[3] * FRACUNIT; // Distance pd->dist = pd->totalDist; an = an + ANG180; // reverse the angle pd->direction = an >> ANGLETOFINESHIFT; pd->xSpeed = FixedMul(pd->speed, finecosine[pd->direction]); pd->ySpeed = FixedMul(pd->speed, finesine[pd->direction]); SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } else if (type == PODOOR_SWING) { pd->waitTics = args[3]; pd->direction = -1; // ADD: same as above pd->speed = (args[1] * pd->direction * (ANG90 / 64)) >> 3; pd->totalDist = args[2] * (ANG90 / 64); pd->dist = pd->totalDist; SN_StartSequence((mobj_t *) & poly->startSpot, SEQ_DOOR_STONE + poly->seqType); } polyNum = mirror; } return true; } // ===== Higher Level Poly Interface code ===== //========================================================================== // // GetPolyobj // //========================================================================== static polyobj_t *GetPolyobj(int polyNum) { int i; for (i = 0; i < po_NumPolyobjs; i++) { if (polyobjs[i].tag == polyNum) { return &polyobjs[i]; } } return NULL; } //========================================================================== // // GetPolyobjMirror // //========================================================================== static int GetPolyobjMirror(int poly) { int i; for (i = 0; i < po_NumPolyobjs; i++) { if (polyobjs[i].tag == poly) { return ((*polyobjs[i].segs)->linedef->arg2); } } return 0; } //========================================================================== // // ThrustMobj // //========================================================================== static void ThrustMobj(mobj_t * mobj, seg_t * seg, polyobj_t * po) { int thrustAngle; int thrustX; int thrustY; polyevent_t *pe; int force; if (!(mobj->flags & MF_SHOOTABLE) && !mobj->player) { return; } thrustAngle = (seg->angle - ANG90) >> ANGLETOFINESHIFT; pe = po->specialdata; if (pe) { if (pe->thinker.function == T_RotatePoly) { force = pe->speed >> 8; } else { force = pe->speed >> 3; } if (force < FRACUNIT) { force = FRACUNIT; } else if (force > 4 * FRACUNIT) { force = 4 * FRACUNIT; } } else { force = FRACUNIT; } thrustX = FixedMul(force, finecosine[thrustAngle]); thrustY = FixedMul(force, finesine[thrustAngle]); mobj->momx += thrustX; mobj->momy += thrustY; if (po->crush) { if (!P_CheckPosition(mobj, mobj->x + thrustX, mobj->y + thrustY)) { P_DamageMobj(mobj, NULL, NULL, 3); } } } //========================================================================== // // UpdateSegBBox // //========================================================================== static void UpdateSegBBox(seg_t * seg) { line_t *line; line = seg->linedef; if (seg->v1->x < seg->v2->x) { line->bbox[BOXLEFT] = seg->v1->x; line->bbox[BOXRIGHT] = seg->v2->x; } else { line->bbox[BOXLEFT] = seg->v2->x; line->bbox[BOXRIGHT] = seg->v1->x; } if (seg->v1->y < seg->v2->y) { line->bbox[BOXBOTTOM] = seg->v1->y; line->bbox[BOXTOP] = seg->v2->y; } else { line->bbox[BOXBOTTOM] = seg->v2->y; line->bbox[BOXTOP] = seg->v1->y; } // Update the line's slopetype line->dx = line->v2->x - line->v1->x; line->dy = line->v2->y - line->v1->y; if (!line->dx) { line->slopetype = ST_VERTICAL; } else if (!line->dy) { line->slopetype = ST_HORIZONTAL; } else { if (FixedDiv(line->dy, line->dx) > 0) { line->slopetype = ST_POSITIVE; } else { line->slopetype = ST_NEGATIVE; } } } //========================================================================== // // PO_MovePolyobj // //========================================================================== boolean PO_MovePolyobj(int num, int x, int y) { int count; seg_t **segList; seg_t **veryTempSeg; polyobj_t *po; vertex_t *prevPts; boolean blocked; if (!(po = GetPolyobj(num))) { I_Error("PO_MovePolyobj: Invalid polyobj number: %d\n", num); } UnLinkPolyobj(po); segList = po->segs; prevPts = po->prevPts; blocked = false; validcount++; for (count = po->numsegs; count; count--, segList++, prevPts++) { if ((*segList)->linedef->validcount != validcount) { (*segList)->linedef->bbox[BOXTOP] += y; (*segList)->linedef->bbox[BOXBOTTOM] += y; (*segList)->linedef->bbox[BOXLEFT] += x; (*segList)->linedef->bbox[BOXRIGHT] += x; (*segList)->linedef->validcount = validcount; } for (veryTempSeg = po->segs; veryTempSeg != segList; veryTempSeg++) { if ((*veryTempSeg)->v1 == (*segList)->v1) { break; } } if (veryTempSeg == segList) { (*segList)->v1->x += x; (*segList)->v1->y += y; } (*prevPts).x += x; // previous points are unique for each seg (*prevPts).y += y; } segList = po->segs; for (count = po->numsegs; count; count--, segList++) { if (CheckMobjBlocking(*segList, po)) { blocked = true; } } if (blocked) { count = po->numsegs; segList = po->segs; prevPts = po->prevPts; validcount++; while (count--) { if ((*segList)->linedef->validcount != validcount) { (*segList)->linedef->bbox[BOXTOP] -= y; (*segList)->linedef->bbox[BOXBOTTOM] -= y; (*segList)->linedef->bbox[BOXLEFT] -= x; (*segList)->linedef->bbox[BOXRIGHT] -= x; (*segList)->linedef->validcount = validcount; } for (veryTempSeg = po->segs; veryTempSeg != segList; veryTempSeg++) { if ((*veryTempSeg)->v1 == (*segList)->v1) { break; } } if (veryTempSeg == segList) { (*segList)->v1->x -= x; (*segList)->v1->y -= y; } (*prevPts).x -= x; (*prevPts).y -= y; segList++; prevPts++; } LinkPolyobj(po); return false; } po->startSpot.x += x; po->startSpot.y += y; LinkPolyobj(po); return true; } //========================================================================== // // RotatePt // //========================================================================== static void RotatePt(int an, fixed_t * x, fixed_t * y, fixed_t startSpotX, fixed_t startSpotY) { fixed_t trx, try; fixed_t gxt, gyt; trx = *x; try = *y; gxt = FixedMul(trx, finecosine[an]); gyt = FixedMul(try, finesine[an]); *x = (gxt - gyt) + startSpotX; gxt = FixedMul(trx, finesine[an]); gyt = FixedMul(try, finecosine[an]); *y = (gyt + gxt) + startSpotY; } //========================================================================== // // PO_RotatePolyobj // //========================================================================== boolean PO_RotatePolyobj(int num, angle_t angle) { int count; seg_t **segList; vertex_t *originalPts; vertex_t *prevPts; int an; polyobj_t *po; boolean blocked; if (!(po = GetPolyobj(num))) { I_Error("PO_RotatePolyobj: Invalid polyobj number: %d\n", num); } an = (po->angle + angle) >> ANGLETOFINESHIFT; UnLinkPolyobj(po); segList = po->segs; originalPts = po->originalPts; prevPts = po->prevPts; for (count = po->numsegs; count; count--, segList++, originalPts++, prevPts++) { prevPts->x = (*segList)->v1->x; prevPts->y = (*segList)->v1->y; (*segList)->v1->x = originalPts->x; (*segList)->v1->y = originalPts->y; RotatePt(an, &(*segList)->v1->x, &(*segList)->v1->y, po->startSpot.x, po->startSpot.y); } segList = po->segs; blocked = false; validcount++; for (count = po->numsegs; count; count--, segList++) { if (CheckMobjBlocking(*segList, po)) { blocked = true; } if ((*segList)->linedef->validcount != validcount) { UpdateSegBBox(*segList); (*segList)->linedef->validcount = validcount; } (*segList)->angle += angle; } if (blocked) { segList = po->segs; prevPts = po->prevPts; for (count = po->numsegs; count; count--, segList++, prevPts++) { (*segList)->v1->x = prevPts->x; (*segList)->v1->y = prevPts->y; } segList = po->segs; validcount++; for (count = po->numsegs; count; count--, segList++, prevPts++) { if ((*segList)->linedef->validcount != validcount) { UpdateSegBBox(*segList); (*segList)->linedef->validcount = validcount; } (*segList)->angle -= angle; } LinkPolyobj(po); return false; } po->angle += angle; LinkPolyobj(po); return true; } //========================================================================== // // UnLinkPolyobj // //========================================================================== static void UnLinkPolyobj(polyobj_t * po) { polyblock_t *link; int i, j; int index; // remove the polyobj from each blockmap section for (j = po->bbox[BOXBOTTOM]; j <= po->bbox[BOXTOP]; j++) { index = j * bmapwidth; for (i = po->bbox[BOXLEFT]; i <= po->bbox[BOXRIGHT]; i++) { if (i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight) { link = PolyBlockMap[index + i]; while (link != NULL && link->polyobj != po) { link = link->next; } if (link == NULL) { // polyobj not located in the link cell continue; } link->polyobj = NULL; } } } } //========================================================================== // // LinkPolyobj // //========================================================================== static void LinkPolyobj(polyobj_t * po) { int leftX, rightX; int topY, bottomY; seg_t **tempSeg; polyblock_t **link; polyblock_t *tempLink; int i, j; // calculate the polyobj bbox tempSeg = po->segs; rightX = leftX = (*tempSeg)->v1->x; topY = bottomY = (*tempSeg)->v1->y; for (i = 0; i < po->numsegs; i++, tempSeg++) { if ((*tempSeg)->v1->x > rightX) { rightX = (*tempSeg)->v1->x; } if ((*tempSeg)->v1->x < leftX) { leftX = (*tempSeg)->v1->x; } if ((*tempSeg)->v1->y > topY) { topY = (*tempSeg)->v1->y; } if ((*tempSeg)->v1->y < bottomY) { bottomY = (*tempSeg)->v1->y; } } po->bbox[BOXRIGHT] = (rightX - bmaporgx) >> MAPBLOCKSHIFT; po->bbox[BOXLEFT] = (leftX - bmaporgx) >> MAPBLOCKSHIFT; po->bbox[BOXTOP] = (topY - bmaporgy) >> MAPBLOCKSHIFT; po->bbox[BOXBOTTOM] = (bottomY - bmaporgy) >> MAPBLOCKSHIFT; // add the polyobj to each blockmap section for (j = po->bbox[BOXBOTTOM] * bmapwidth; j <= po->bbox[BOXTOP] * bmapwidth; j += bmapwidth) { for (i = po->bbox[BOXLEFT]; i <= po->bbox[BOXRIGHT]; i++) { if (i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight * bmapwidth) { link = &PolyBlockMap[j + i]; if (!(*link)) { // Create a new link at the current block cell *link = Z_Malloc(sizeof(polyblock_t), PU_LEVEL, 0); (*link)->next = NULL; (*link)->prev = NULL; (*link)->polyobj = po; continue; } else { tempLink = *link; while (tempLink->next != NULL && tempLink->polyobj != NULL) { tempLink = tempLink->next; } } if (tempLink->polyobj == NULL) { tempLink->polyobj = po; continue; } else { tempLink->next = Z_Malloc(sizeof(polyblock_t), PU_LEVEL, 0); tempLink->next->next = NULL; tempLink->next->prev = tempLink; tempLink->next->polyobj = po; } } // else, don't link the polyobj, since it's off the map } } } //========================================================================== // // CheckMobjBlocking // //========================================================================== static boolean CheckMobjBlocking(seg_t * seg, polyobj_t * po) { mobj_t *mobj; int i, j; int left, right, top, bottom; int tmbbox[4]; line_t *ld; boolean blocked; ld = seg->linedef; top = (ld->bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; bottom = (ld->bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; left = (ld->bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; right = (ld->bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; blocked = false; bottom = bottom < 0 ? 0 : bottom; bottom = bottom >= bmapheight ? bmapheight - 1 : bottom; top = top < 0 ? 0 : top; top = top >= bmapheight ? bmapheight - 1 : top; left = left < 0 ? 0 : left; left = left >= bmapwidth ? bmapwidth - 1 : left; right = right < 0 ? 0 : right; right = right >= bmapwidth ? bmapwidth - 1 : right; for (j = bottom * bmapwidth; j <= top * bmapwidth; j += bmapwidth) { for (i = left; i <= right; i++) { for (mobj = blocklinks[j + i]; mobj; mobj = mobj->bnext) { if (mobj->flags & MF_SOLID || mobj->player) { tmbbox[BOXTOP] = mobj->y + mobj->radius; tmbbox[BOXBOTTOM] = mobj->y - mobj->radius; tmbbox[BOXLEFT] = mobj->x - mobj->radius; tmbbox[BOXRIGHT] = mobj->x + mobj->radius; if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) { continue; } if (P_BoxOnLineSide(tmbbox, ld) != -1) { continue; } ThrustMobj(mobj, seg, po); blocked = true; } } } } return blocked; } //========================================================================== // // InitBlockMap // //========================================================================== static void InitBlockMap(void) { int i; int j; seg_t **segList; int leftX, rightX; int topY, bottomY; PolyBlockMap = Z_Malloc(bmapwidth * bmapheight * sizeof(polyblock_t *), PU_LEVEL, 0); memset(PolyBlockMap, 0, bmapwidth * bmapheight * sizeof(polyblock_t *)); for (i = 0; i < po_NumPolyobjs; i++) { LinkPolyobj(&polyobjs[i]); // calculate a rough area // right now, working like shit...gotta fix this... segList = polyobjs[i].segs; leftX = rightX = (*segList)->v1->x; topY = bottomY = (*segList)->v1->y; for (j = 0; j < polyobjs[i].numsegs; j++, segList++) { if ((*segList)->v1->x < leftX) { leftX = (*segList)->v1->x; } if ((*segList)->v1->x > rightX) { rightX = (*segList)->v1->x; } if ((*segList)->v1->y < bottomY) { bottomY = (*segList)->v1->y; } if ((*segList)->v1->y > topY) { topY = (*segList)->v1->y; } } // area = ((rightX >> FRACBITS) - (leftX >> FRACBITS)) * // ((topY >> FRACBITS) - (bottomY >> FRACBITS)); // fprintf(stdaux, "Area of Polyobj[%d]: %d\n", polyobjs[i].tag, area); // fprintf(stdaux, "\t[%d]\n[%d]\t\t[%d]\n\t[%d]\n", topY>>FRACBITS, // leftX>>FRACBITS, // rightX>>FRACBITS, bottomY>>FRACBITS); } } //========================================================================== // // IterFindPolySegs // // Passing NULL for segList will cause IterFindPolySegs to // count the number of segs in the polyobj //========================================================================== static void IterFindPolySegs(int x, int y, seg_t ** segList) { int i; if (x == PolyStartX && y == PolyStartY) { return; } for (i = 0; i < numsegs; i++) { if (segs[i].v1->x == x && segs[i].v1->y == y) { if (!segList) { PolySegCount++; } else { *segList++ = &segs[i]; } IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, segList); return; } } I_Error("IterFindPolySegs: Non-closed Polyobj located.\n"); } //========================================================================== // // SpawnPolyobj // //========================================================================== static void SpawnPolyobj(int index, int tag, boolean crush) { int i; int j; int psIndex; int psIndexOld; seg_t *polySegList[PO_MAXPOLYSEGS]; for (i = 0; i < numsegs; i++) { if (segs[i].linedef->special == PO_LINE_START && segs[i].linedef->arg1 == tag) { if (polyobjs[index].segs) { I_Error("SpawnPolyobj: Polyobj %d already spawned.\n", tag); } segs[i].linedef->special = 0; segs[i].linedef->arg1 = 0; PolySegCount = 1; PolyStartX = segs[i].v1->x; PolyStartY = segs[i].v1->y; IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, NULL); polyobjs[index].numsegs = PolySegCount; polyobjs[index].segs = Z_Malloc(PolySegCount * sizeof(seg_t *), PU_LEVEL, 0); *(polyobjs[index].segs) = &segs[i]; // insert the first seg IterFindPolySegs(segs[i].v2->x, segs[i].v2->y, polyobjs[index].segs + 1); polyobjs[index].crush = crush; polyobjs[index].tag = tag; polyobjs[index].seqType = segs[i].linedef->arg3; if (polyobjs[index].seqType < 0 || polyobjs[index].seqType >= SEQTYPE_NUMSEQ) { polyobjs[index].seqType = 0; } break; } } if (!polyobjs[index].segs) { // didn't find a polyobj through PO_LINE_START psIndex = 0; polyobjs[index].numsegs = 0; for (j = 1; j < PO_MAXPOLYSEGS; j++) { psIndexOld = psIndex; for (i = 0; i < numsegs; i++) { if (segs[i].linedef->special == PO_LINE_EXPLICIT && segs[i].linedef->arg1 == tag) { if (!segs[i].linedef->arg2) { I_Error ("SpawnPolyobj: Explicit line missing order number (probably %d) in poly %d.\n", j + 1, tag); } if (segs[i].linedef->arg2 == j) { polySegList[psIndex] = &segs[i]; polyobjs[index].numsegs++; psIndex++; if (psIndex > PO_MAXPOLYSEGS) { I_Error ("SpawnPolyobj: psIndex > PO_MAXPOLYSEGS\n"); } } } } // Clear out any specials for these segs...we cannot clear them out // in the above loop, since we aren't guaranteed one seg per // linedef. for (i = 0; i < numsegs; i++) { if (segs[i].linedef->special == PO_LINE_EXPLICIT && segs[i].linedef->arg1 == tag && segs[i].linedef->arg2 == j) { segs[i].linedef->special = 0; segs[i].linedef->arg1 = 0; } } if (psIndex == psIndexOld) { // Check if an explicit line order has been skipped // A line has been skipped if there are any more explicit // lines with the current tag value for (i = 0; i < numsegs; i++) { if (segs[i].linedef->special == PO_LINE_EXPLICIT && segs[i].linedef->arg1 == tag) { I_Error ("SpawnPolyobj: Missing explicit line %d for poly %d\n", j, tag); } } } } if (polyobjs[index].numsegs) { PolySegCount = polyobjs[index].numsegs; // PolySegCount used globally polyobjs[index].crush = crush; polyobjs[index].tag = tag; polyobjs[index].segs = Z_Malloc(polyobjs[index].numsegs * sizeof(seg_t *), PU_LEVEL, 0); for (i = 0; i < polyobjs[index].numsegs; i++) { polyobjs[index].segs[i] = polySegList[i]; } polyobjs[index].seqType = (*polyobjs[index].segs)->linedef->arg4; } // Next, change the polyobjs first line to point to a mirror // if it exists (*polyobjs[index].segs)->linedef->arg2 = (*polyobjs[index].segs)->linedef->arg3; } } //========================================================================== // // TranslateToStartSpot // //========================================================================== static void TranslateToStartSpot(int tag, int originX, int originY) { seg_t **tempSeg; seg_t **veryTempSeg; vertex_t *tempPt; subsector_t *sub; polyobj_t *po; int deltaX; int deltaY; vertex_t avg; // used to find a polyobj's center, and hence subsector int i; po = NULL; for (i = 0; i < po_NumPolyobjs; i++) { if (polyobjs[i].tag == tag) { po = &polyobjs[i]; break; } } if (!po) { // didn't match the tag with a polyobj tag I_Error("TranslateToStartSpot: Unable to match polyobj tag: %d\n", tag); } if (po->segs == NULL) { I_Error ("TranslateToStartSpot: Anchor point located without a StartSpot point: %d\n", tag); } po->originalPts = Z_Malloc(po->numsegs * sizeof(vertex_t), PU_LEVEL, 0); po->prevPts = Z_Malloc(po->numsegs * sizeof(vertex_t), PU_LEVEL, 0); deltaX = originX - po->startSpot.x; deltaY = originY - po->startSpot.y; tempSeg = po->segs; tempPt = po->originalPts; avg.x = 0; avg.y = 0; validcount++; for (i = 0; i < po->numsegs; i++, tempSeg++, tempPt++) { if ((*tempSeg)->linedef->validcount != validcount) { (*tempSeg)->linedef->bbox[BOXTOP] -= deltaY; (*tempSeg)->linedef->bbox[BOXBOTTOM] -= deltaY; (*tempSeg)->linedef->bbox[BOXLEFT] -= deltaX; (*tempSeg)->linedef->bbox[BOXRIGHT] -= deltaX; (*tempSeg)->linedef->validcount = validcount; } for (veryTempSeg = po->segs; veryTempSeg != tempSeg; veryTempSeg++) { if ((*veryTempSeg)->v1 == (*tempSeg)->v1) { break; } } if (veryTempSeg == tempSeg) { // the point hasn't been translated, yet (*tempSeg)->v1->x -= deltaX; (*tempSeg)->v1->y -= deltaY; } avg.x += (*tempSeg)->v1->x >> FRACBITS; avg.y += (*tempSeg)->v1->y >> FRACBITS; // the original Pts are based off the startSpot Pt, and are // unique to each seg, not each linedef tempPt->x = (*tempSeg)->v1->x - po->startSpot.x; tempPt->y = (*tempSeg)->v1->y - po->startSpot.y; } avg.x /= po->numsegs; avg.y /= po->numsegs; sub = R_PointInSubsector(avg.x << FRACBITS, avg.y << FRACBITS); if (sub->poly != NULL) { I_Error ("PO_TranslateToStartSpot: Multiple polyobjs in a single subsector.\n"); } sub->poly = po; } //========================================================================== // // PO_Init // //========================================================================== void PO_Init(int lump) { byte *data; int i; mapthing_t spawnthing; mapthing_t *mt; int numthings; int polyIndex; polyobjs = Z_Malloc(po_NumPolyobjs * sizeof(polyobj_t), PU_LEVEL, 0); memset(polyobjs, 0, po_NumPolyobjs * sizeof(polyobj_t)); data = W_CacheLumpNum(lump, PU_STATIC); numthings = W_LumpLength(lump) / sizeof(mapthing_t); mt = (mapthing_t *) data; polyIndex = 0; // index polyobj number // Find the startSpot points, and spawn each polyobj for (i = 0; i < numthings; i++, mt++) { spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); // 3001 = no crush, 3002 = crushing if (spawnthing.type == PO_SPAWN_TYPE || spawnthing.type == PO_SPAWNCRUSH_TYPE) { // Polyobj StartSpot Pt. polyobjs[polyIndex].startSpot.x = spawnthing.x << FRACBITS; polyobjs[polyIndex].startSpot.y = spawnthing.y << FRACBITS; SpawnPolyobj(polyIndex, spawnthing.angle, (spawnthing.type == PO_SPAWNCRUSH_TYPE)); polyIndex++; } } mt = (mapthing_t *) data; for (i = 0; i < numthings; i++, mt++) { spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); if (spawnthing.type == PO_ANCHOR_TYPE) { // Polyobj Anchor Pt. TranslateToStartSpot(spawnthing.angle, spawnthing.x << FRACBITS, spawnthing.y << FRACBITS); } } W_ReleaseLumpNum(lump); // check for a startspot without an anchor point for (i = 0; i < po_NumPolyobjs; i++) { if (!polyobjs[i].originalPts) { I_Error ("PO_Init: StartSpot located without an Anchor point: %d\n", polyobjs[i].tag); } } InitBlockMap(); } //========================================================================== // // PO_Busy // //========================================================================== boolean PO_Busy(int polyobj) { polyobj_t *poly; poly = GetPolyobj(polyobj); if (!poly->specialdata) { return false; } else { return true; } } crispy-doom-crispy-doom-5.6.4/src/hexen/r_bsp.c000066400000000000000000000310211360717211000214000ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_system.h" #include "m_bbox.h" #include "r_local.h" seg_t *curline; side_t *sidedef; line_t *linedef; sector_t *frontsector, *backsector; drawseg_t drawsegs[MAXDRAWSEGS], *ds_p; void R_StoreWallRange(int start, int stop); /* ==================== = = R_ClearDrawSegs = ==================== */ void R_ClearDrawSegs(void) { ds_p = drawsegs; } //============================================================================= /* =============================================================================== = = ClipWallSegment = = Clips the given range of columns and includes it in the new clip list =============================================================================== */ typedef struct { int first, last; } cliprange_t; // We must expand MAXSEGS to the theoretical limit of the number of solidsegs // that can be generated in a scene by the DOOM engine. This was determined by // Lee Killough during BOOM development to be a function of the screensize. // The simplest thing we can do, other than fix this bug, is to let the game // render overage and then bomb out by detecting the overflow after the // fact. -haleyjd //#define MAXSEGS 32 #define MAXSEGS (MAXWIDTH / 2 + 1) cliprange_t solidsegs[MAXSEGS], *newend; // newend is one past the last valid seg void R_ClipSolidWallSegment(int first, int last) { cliprange_t *next, *start; // find the first range that touches the range (adjacent pixels are touching) start = solidsegs; while (start->last < first - 1) start++; if (first < start->first) { if (last < start->first - 1) { // post is entirely visible (above start), so insert a new clippost R_StoreWallRange(first, last); next = newend; newend++; while (next != start) { *next = *(next - 1); next--; } next->first = first; next->last = last; return; } // there is a fragment above *start R_StoreWallRange(first, start->first - 1); start->first = first; // adjust the clip size } if (last <= start->last) return; // bottom contained in start next = start; while (last >= (next + 1)->first - 1) { // there is a fragment between two posts R_StoreWallRange(next->last + 1, (next + 1)->first - 1); next++; if (last <= next->last) { // bottom is contained in next start->last = next->last; // adjust the clip size goto crunch; } } // there is a fragment after *next R_StoreWallRange(next->last + 1, last); start->last = last; // adjust the clip size // remove start+1 to next from the clip list, // because start now covers their area crunch: if (next == start) return; // post just extended past the bottom of one post while (next++ != newend) // remove a post *++start = *next; newend = start + 1; } /* =============================================================================== = = R_ClipPassWallSegment = = Clips the given range of columns, but does not includes it in the clip list =============================================================================== */ void R_ClipPassWallSegment(int first, int last) { cliprange_t *start; // find the first range that touches the range (adjacent pixels are touching) start = solidsegs; while (start->last < first - 1) start++; if (first < start->first) { if (last < start->first - 1) { // post is entirely visible (above start) R_StoreWallRange(first, last); return; } // there is a fragment above *start R_StoreWallRange(first, start->first - 1); } if (last <= start->last) return; // bottom contained in start while (last >= (start + 1)->first - 1) { // there is a fragment between two posts R_StoreWallRange(start->last + 1, (start + 1)->first - 1); start++; if (last <= start->last) return; } // there is a fragment after *next R_StoreWallRange(start->last + 1, last); } /* ==================== = = R_ClearClipSegs = ==================== */ void R_ClearClipSegs(void) { solidsegs[0].first = -0x7fffffff; solidsegs[0].last = -1; solidsegs[1].first = viewwidth; solidsegs[1].last = 0x7fffffff; newend = solidsegs + 2; } //============================================================================= /* ====================== = = R_AddLine = = Clips the given segment and adds any visible pieces to the line list = ====================== */ void R_AddLine(seg_t * line) { int x1, x2; angle_t angle1, angle2, span, tspan; curline = line; // OPTIMIZE: quickly reject orthogonal back sides angle1 = R_PointToAngle(line->v1->x, line->v1->y); angle2 = R_PointToAngle(line->v2->x, line->v2->y); // // clip to view edges // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW) span = angle1 - angle2; if (span >= ANG180) return; // back side rw_angle1 = angle1; // global angle needed by segcalc angle1 -= viewangle; angle2 -= viewangle; tspan = angle1 + clipangle; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return; // totally off the left edge angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return; // totally off the left edge angle2 = -clipangle; } // // the seg is in the view range, but not necessarily visible // angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; x1 = viewangletox[angle1]; x2 = viewangletox[angle2]; if (x1 == x2) return; // does not cross a pixel backsector = line->backsector; if (!backsector) goto clipsolid; // single sided line if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) goto clipsolid; // closed door if (backsector->ceilingheight != frontsector->ceilingheight || backsector->floorheight != frontsector->floorheight) goto clippass; // window // reject empty lines used for triggers and special events if (backsector->ceilingpic == frontsector->ceilingpic && backsector->floorpic == frontsector->floorpic && backsector->lightlevel == frontsector->lightlevel && backsector->special == frontsector->special && curline->sidedef->midtexture == 0) return; clippass: R_ClipPassWallSegment(x1, x2 - 1); return; clipsolid: R_ClipSolidWallSegment(x1, x2 - 1); } //============================================================================ /* =============================================================================== = = R_CheckBBox = = Returns true if some part of the bbox might be visible = =============================================================================== */ int checkcoord[12][4] = { {3, 0, 2, 1}, {3, 0, 2, 0}, {3, 1, 2, 0}, {0}, {2, 0, 2, 1}, {0, 0, 0, 0}, {3, 1, 3, 0}, {0}, {2, 0, 3, 1}, {2, 1, 3, 1}, {2, 1, 3, 0} }; boolean R_CheckBBox(fixed_t * bspcoord) { int boxx, boxy, boxpos; fixed_t x1, y1, x2, y2; angle_t angle1, angle2, span, tspan; cliprange_t *start; int sx1, sx2; // find the corners of the box that define the edges from current viewpoint if (viewx <= bspcoord[BOXLEFT]) boxx = 0; else if (viewx < bspcoord[BOXRIGHT]) boxx = 1; else boxx = 2; if (viewy >= bspcoord[BOXTOP]) boxy = 0; else if (viewy > bspcoord[BOXBOTTOM]) boxy = 1; else boxy = 2; boxpos = (boxy << 2) + boxx; if (boxpos == 5) return true; x1 = bspcoord[checkcoord[boxpos][0]]; y1 = bspcoord[checkcoord[boxpos][1]]; x2 = bspcoord[checkcoord[boxpos][2]]; y2 = bspcoord[checkcoord[boxpos][3]]; // // check clip list for an open space // angle1 = R_PointToAngle(x1, y1) - viewangle; angle2 = R_PointToAngle(x2, y2) - viewangle; span = angle1 - angle2; if (span >= ANG180) return true; // sitting on a line tspan = angle1 + clipangle; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return false; // totally off the left edge angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2 * clipangle) { tspan -= 2 * clipangle; if (tspan >= span) return false; // totally off the left edge angle2 = -clipangle; } // find the first clippost that touches the source post (adjacent pixels are touching) angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; sx1 = viewangletox[angle1]; sx2 = viewangletox[angle2]; if (sx1 == sx2) return false; // does not cross a pixel sx2--; start = solidsegs; while (start->last < sx2) start++; if (sx1 >= start->first && sx2 <= start->last) return false; // the clippost contains the new span return true; } /* ================ = = R_Subsector = = Draw one or more segments ================ */ void R_Subsector(int num) { int count; seg_t *line; subsector_t *sub; int polyCount; seg_t **polySeg; #ifdef RANGECHECK if (num >= numsubsectors) I_Error("R_Subsector: ss %i with numss = %i", num, numsubsectors); #endif sscount++; sub = &subsectors[num]; frontsector = sub->sector; count = sub->numlines; line = &segs[sub->firstline]; if (frontsector->floorheight < viewz) { floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, frontsector->lightlevel, frontsector->special); } else { floorplane = NULL; } if (frontsector->ceilingheight > viewz || frontsector->ceilingpic == skyflatnum) { ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, frontsector->lightlevel, 0); } else { ceilingplane = NULL; } R_AddSprites(frontsector); if (sub->poly) { // Render the polyobj in the subsector first polyCount = sub->poly->numsegs; polySeg = sub->poly->segs; while (polyCount--) { R_AddLine(*polySeg++); } } while (count--) { R_AddLine(line); line++; } // check for solidsegs overflow - extremely unsatisfactory! if(newend > &solidsegs[32] && false) I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n"); } /* =============================================================================== = = RenderBSPNode = =============================================================================== */ void R_RenderBSPNode(int bspnum) { node_t *bsp; int side; if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) R_Subsector(0); else R_Subsector(bspnum & (~NF_SUBSECTOR)); return; } bsp = &nodes[bspnum]; // // decide which side the view point is on // side = R_PointOnSide(viewx, viewy, bsp); R_RenderBSPNode(bsp->children[side]); // recursively divide front space if (R_CheckBBox(bsp->bbox[side ^ 1])) // possibly divide back space R_RenderBSPNode(bsp->children[side ^ 1]); } crispy-doom-crispy-doom-5.6.4/src/hexen/r_data.c000066400000000000000000000417021360717211000215340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_system.h" #include "i_swap.h" #include "m_misc.h" #include "r_local.h" #include "p_local.h" typedef struct { int originx; // block origin (allways UL), which has allready int originy; // accounted for the patch's internal origin int patch; } texpatch_t; // a maptexturedef_t describes a rectangular texture, which is composed of one // or more mappatch_t structures that arrange graphic patches typedef struct { char name[8]; // for switch changing, etc short width; short height; short patchcount; texpatch_t patches[1]; // [patchcount] drawn back to front // into the cached texture } texture_t; int firstflat, lastflat, numflats; int firstpatch, lastpatch, numpatches; int firstspritelump, lastspritelump, numspritelumps; int numtextures; texture_t **textures; int *texturewidthmask; fixed_t *textureheight; // needed for texture pegging int *texturecompositesize; short **texturecolumnlump; unsigned short **texturecolumnofs; byte **texturecomposite; int *flattranslation; // for global animation int *texturetranslation; // for global animation fixed_t *spritewidth; // needed for pre rendering fixed_t *spriteoffset; fixed_t *spritetopoffset; lighttable_t *colormaps; /* ============================================================================== MAPTEXTURE_T CACHING when a texture is first needed, it counts the number of composite columns required in the texture and allocates space for a column directory and any new columns. The directory will simply point inside other patches if there is only one patch in a given column, but any columns with multiple patches will have new column_ts generated. ============================================================================== */ /* =================== = = R_DrawColumnInCache = = Clip and draw a column from a patch into a cached post = =================== */ void R_DrawColumnInCache(column_t * patch, byte * cache, int originy, int cacheheight) { int count, position; byte *source; while (patch->topdelta != 0xff) { source = (byte *) patch + 3; count = patch->length; position = originy + patch->topdelta; if (position < 0) { count += position; position = 0; } if (position + count > cacheheight) count = cacheheight - position; if (count > 0) memcpy(cache + position, source, count); patch = (column_t *) ((byte *) patch + patch->length + 4); } } /* =================== = = R_GenerateComposite = =================== */ void R_GenerateComposite(int texnum) { byte *block; texture_t *texture; texpatch_t *patch; patch_t *realpatch; int x, x1, x2; int i; column_t *patchcol; short *collump; unsigned short *colofs; texture = textures[texnum]; block = Z_Malloc(texturecompositesize[texnum], PU_STATIC, &texturecomposite[texnum]); collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // // composite the columns together // patch = texture->patches; for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for (; x < x2; x++) { if (collump[x] >= 0) continue; // column does not have multiple patches patchcol = (column_t *) ((byte *) realpatch + LONG(realpatch->columnofs[x - x1])); R_DrawColumnInCache(patchcol, block + colofs[x], patch->originy, texture->height); } } // now that the texture has been built, it is purgable Z_ChangeTag(block, PU_CACHE); } /* =================== = = R_GenerateLookup = =================== */ void R_GenerateLookup(int texnum) { texture_t *texture; byte *patchcount; // [texture->width] texpatch_t *patch; patch_t *realpatch; int x, x1, x2; int i; short *collump; unsigned short *colofs; texture = textures[texnum]; texturecomposite[texnum] = 0; // composited not created yet texturecompositesize[texnum] = 0; collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // // count the number of columns that are covered by more than one patch // fill in the lump / offset, so columns with only a single patch are // all done // patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount); memset(patchcount, 0, texture->width); patch = texture->patches; for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++) { realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for (; x < x2; x++) { patchcount[x]++; collump[x] = patch->patch; colofs[x] = LONG(realpatch->columnofs[x - x1]) + 3; } } for (x = 0; x < texture->width; x++) { if (!patchcount[x]) { ST_Message("R_GenerateLookup: column without a patch (%s)\n", texture->name); return; } // I_Error ("R_GenerateLookup: column without a patch"); if (patchcount[x] > 1) { collump[x] = -1; // use the cached block colofs[x] = texturecompositesize[texnum]; if (texturecompositesize[texnum] > 0x10000 - texture->height) I_Error("R_GenerateLookup: texture %i is >64k", texnum); texturecompositesize[texnum] += texture->height; } } Z_Free(patchcount); } /* ================ = = R_GetColumn = ================ */ byte *R_GetColumn(int tex, int col) { int lump, ofs; col &= texturewidthmask[tex]; lump = texturecolumnlump[tex][col]; ofs = texturecolumnofs[tex][col]; if (lump > 0) return (byte *) W_CacheLumpNum(lump, PU_CACHE) + ofs; if (!texturecomposite[tex]) R_GenerateComposite(tex); return texturecomposite[tex] + ofs; } /* ================== = = R_InitTextures = = Initializes the texture list with the textures from the world map = ================== */ void R_InitTextures(void) { maptexture_t *mtexture; texture_t *texture; mappatch_t *mpatch; texpatch_t *patch; int i, j; int *maptex, *maptex2, *maptex1; char name[9], *names, *name_p; int *patchlookup; int totalwidth; int nummappatches; int offset, maxoff, maxoff2; int numtextures1, numtextures2; int *directory; // // load the patch names from pnames.lmp // names = W_CacheLumpName("PNAMES", PU_STATIC); nummappatches = LONG(*((int *) names)); name_p = names + 4; patchlookup = Z_Malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL); for (i = 0; i < nummappatches; i++) { M_StringCopy(name, name_p + i * 8, sizeof(name)); patchlookup[i] = W_CheckNumForName(name); } W_ReleaseLumpName("PNAMES"); // // load the map texture definitions from textures.lmp // maptex = maptex1 = W_CacheLumpName("TEXTURE1", PU_STATIC); numtextures1 = LONG(*maptex); maxoff = W_LumpLength(W_GetNumForName("TEXTURE1")); directory = maptex + 1; if (W_CheckNumForName("TEXTURE2") != -1) { maptex2 = W_CacheLumpName("TEXTURE2", PU_STATIC); numtextures2 = LONG(*maptex2); maxoff2 = W_LumpLength(W_GetNumForName("TEXTURE2")); } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; } numtextures = numtextures1 + numtextures2; textures = Z_Malloc(numtextures * sizeof(texture_t *), PU_STATIC, 0); texturecolumnlump = Z_Malloc(numtextures * sizeof(short *), PU_STATIC, 0); texturecolumnofs = Z_Malloc(numtextures * sizeof(short *), PU_STATIC, 0); texturecomposite = Z_Malloc(numtextures * sizeof(byte *), PU_STATIC, 0); texturecompositesize = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); texturewidthmask = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); textureheight = Z_Malloc(numtextures * sizeof(fixed_t), PU_STATIC, 0); totalwidth = 0; for (i = 0; i < numtextures; i++, directory++) { if (i == numtextures1) { // start looking in second texture file maptex = maptex2; maxoff = maxoff2; directory = maptex + 1; } offset = LONG(*directory); if (offset > maxoff) I_Error("R_InitTextures: bad texture directory"); mtexture = (maptexture_t *) ((byte *) maptex + offset); texture = textures[i] = Z_Malloc(sizeof(texture_t) + sizeof(texpatch_t) * (SHORT(mtexture->patchcount) - 1), PU_STATIC, 0); texture->width = SHORT(mtexture->width); texture->height = SHORT(mtexture->height); texture->patchcount = SHORT(mtexture->patchcount); memcpy(texture->name, mtexture->name, sizeof(texture->name)); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; for (j = 0; j < texture->patchcount; j++, mpatch++, patch++) { patch->originx = SHORT(mpatch->originx); patch->originy = SHORT(mpatch->originy); patch->patch = patchlookup[SHORT(mpatch->patch)]; if (patch->patch == -1) I_Error("R_InitTextures: Missing patch in texture %s", texture->name); } texturecolumnlump[i] = Z_Malloc(texture->width * sizeof(short), PU_STATIC, 0); texturecolumnofs[i] = Z_Malloc(texture->width * sizeof(short), PU_STATIC, 0); j = 1; while (j * 2 <= texture->width) j <<= 1; texturewidthmask[i] = j - 1; textureheight[i] = texture->height << FRACBITS; totalwidth += texture->width; } Z_Free(patchlookup); W_ReleaseLumpName("TEXTURE1"); if (maptex2) W_ReleaseLumpName("TEXTURE2"); // // precalculate whatever possible // for (i = 0; i < numtextures; i++) { R_GenerateLookup(i); if (!(i & 31)) ST_Progress(); } // // translation table for global animation // texturetranslation = Z_Malloc((numtextures + 1) * sizeof(int), PU_STATIC, 0); for (i = 0; i < numtextures; i++) texturetranslation[i] = i; } /* ================ = = R_InitFlats = ================= */ void R_InitFlats(void) { int i; firstflat = W_GetNumForName("F_START") + 1; lastflat = W_GetNumForName("F_END") - 1; numflats = lastflat - firstflat + 1; // translation table for global animation flattranslation = Z_Malloc((numflats + 1) * sizeof(int), PU_STATIC, 0); for (i = 0; i < numflats; i++) flattranslation[i] = i; } /* ================ = = R_InitSpriteLumps = = Finds the width and hoffset of all sprites in the wad, so the sprite doesn't = need to be cached just for the header during rendering ================= */ void R_InitSpriteLumps(void) { int i; patch_t *patch; firstspritelump = W_GetNumForName("S_START") + 1; lastspritelump = W_GetNumForName("S_END") - 1; numspritelumps = lastspritelump - firstspritelump + 1; spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); spritetopoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); for (i = 0; i < numspritelumps; i++) { if (!(i & 127)) ST_Progress(); patch = W_CacheLumpNum(firstspritelump + i, PU_CACHE); spritewidth[i] = SHORT(patch->width) << FRACBITS; spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS; spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS; } } /* ================ = = R_InitColormaps = ================= */ void R_InitColormaps(void) { int lump, length; // // load in the light tables // 256 byte align tables // lump = W_GetNumForName("COLORMAP"); length = W_LumpLength(lump); colormaps = Z_Malloc(length, PU_STATIC, 0); W_ReadLump(lump, colormaps); } /* ================ = = R_InitData = = Locates all the lumps that will be used by all views = Must be called after W_Init ================= */ void R_InitData(void) { R_InitTextures(); R_InitFlats(); R_InitSpriteLumps(); R_InitColormaps(); } //============================================================================= /* ================ = = R_FlatNumForName = ================ */ int R_FlatNumForName(const char *name) { int i; char namet[9]; i = W_CheckNumForName(name); if (i == -1) { namet[8] = 0; memcpy(namet, name, 8); I_Error("R_FlatNumForName: %s not found", namet); } return i - firstflat; } /* ================ = = R_CheckTextureNumForName = ================ */ int R_CheckTextureNumForName(const char *name) { int i; if (name[0] == '-') // no texture marker return 0; for (i = 0; i < numtextures; i++) if (!strncasecmp(textures[i]->name, name, 8)) return i; return -1; } /* ================ = = R_TextureNumForName = ================ */ int R_TextureNumForName(const char *name) { int i; //char namet[9]; i = R_CheckTextureNumForName(name); if (i == -1) I_Error("R_TextureNumForName: %s not found", name); return i; } /* ================= = = R_PrecacheLevel = = Preloads all relevent graphics for the level ================= */ int flatmemory, texturememory, spritememory; void R_PrecacheLevel(void) { char *flatpresent; char *texturepresent; char *spritepresent; int i, j, k, lump; texture_t *texture; thinker_t *th; spriteframe_t *sf; if (demoplayback) return; // // precache flats // flatpresent = Z_Malloc(numflats, PU_STATIC, NULL); memset(flatpresent, 0, numflats); for (i = 0; i < numsectors; i++) { flatpresent[sectors[i].floorpic] = 1; flatpresent[sectors[i].ceilingpic] = 1; } flatmemory = 0; for (i = 0; i < numflats; i++) if (flatpresent[i]) { lump = firstflat + i; flatmemory += lumpinfo[lump]->size; W_CacheLumpNum(lump, PU_CACHE); } Z_Free(flatpresent); // // precache textures // texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL); memset(texturepresent, 0, numtextures); for (i = 0; i < numsides; i++) { texturepresent[sides[i].toptexture] = 1; texturepresent[sides[i].midtexture] = 1; texturepresent[sides[i].bottomtexture] = 1; } texturepresent[Sky1Texture] = 1; texturepresent[Sky2Texture] = 1; texturememory = 0; for (i = 0; i < numtextures; i++) { if (!texturepresent[i]) continue; texture = textures[i]; for (j = 0; j < texture->patchcount; j++) { lump = texture->patches[j].patch; texturememory += lumpinfo[lump]->size; W_CacheLumpNum(lump, PU_CACHE); } } Z_Free(texturepresent); // // precache sprites // spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL); memset(spritepresent, 0, numsprites); for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (th->function == P_MobjThinker) spritepresent[((mobj_t *) th)->sprite] = 1; } spritememory = 0; for (i = 0; i < numsprites; i++) { if (!spritepresent[i]) continue; for (j = 0; j < sprites[i].numframes; j++) { sf = &sprites[i].spriteframes[j]; for (k = 0; k < 8; k++) { lump = firstspritelump + sf->lump[k]; spritememory += lumpinfo[lump]->size; W_CacheLumpNum(lump, PU_CACHE); } } } Z_Free(spritepresent); } crispy-doom-crispy-doom-5.6.4/src/hexen/r_draw.c000066400000000000000000000335551360717211000215670ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_system.h" #include "i_video.h" #include "r_local.h" #include "v_video.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. */ byte *viewimage; int viewwidth, scaledviewwidth, viewheight, viewwindowx, viewwindowy; byte *ylookup[MAXHEIGHT]; int columnofs[MAXWIDTH]; //byte translations[3][256]; // color tables for different players /* ================== = = R_DrawColumn = = Source is the top of the column to scale = ================== */ lighttable_t *dc_colormap; int dc_x; int dc_yl; int dc_yh; fixed_t dc_iscale; fixed_t dc_texturemid; byte *dc_source; // first pixel in a column (possibly virtual) int dccount; // just for profiling void R_DrawColumn(void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } void R_DrawColumnLow(void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); // dccount++; #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } void R_DrawTLColumn(void) { int count; byte *dest; fixed_t frac, fracstep; if (!dc_yl) dc_yl = 1; if (dc_yh == viewheight - 1) dc_yh = viewheight - 2; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawTLColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = tinttable[*dest + (dc_colormap[dc_source[(frac >> FRACBITS) & 127]] << 8)]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } //============================================================================ // // R_DrawAltTLColumn // //============================================================================ void R_DrawAltTLColumn(void) { int count; byte *dest; fixed_t frac, fracstep; if (!dc_yl) dc_yl = 1; if (dc_yh == viewheight - 1) dc_yh = viewheight - 2; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawAltTLColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = tinttable[((*dest) << 8) + dc_colormap[dc_source[(frac >> FRACBITS) & 127]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } /* ======================== = = R_DrawTranslatedColumn = ======================== */ byte *dc_translation; byte *translationtables; void R_DrawTranslatedColumn(void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } //============================================================================ // // R_DrawTranslatedTLColumn // //============================================================================ void R_DrawTranslatedTLColumn(void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl - centery) * fracstep; do { *dest = tinttable[((*dest) << 8) + dc_colormap[dc_translation [dc_source[frac >> FRACBITS]]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } //============================================================================ // // R_DrawTranslatedAltTLColumn // //============================================================================ /* void R_DrawTranslatedAltTLColumn (void) { int count; byte *dest; fixed_t frac, fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; do { *dest = tinttable[*dest +(dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]<<8)]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } */ //-------------------------------------------------------------------------- // // PROC R_InitTranslationTables // //-------------------------------------------------------------------------- void R_InitTranslationTables(void) { int i; byte *transLump; int lumpnum; V_LoadTintTable(); // Allocate translation tables translationtables = Z_Malloc(256 * 3 * (maxplayers - 1), PU_STATIC, 0); for (i = 0; i < 3 * (maxplayers - 1); i++) { lumpnum = W_GetNumForName("trantbl0") + i; transLump = W_CacheLumpNum(lumpnum, PU_STATIC); memcpy(translationtables + i * 256, transLump, 256); W_ReleaseLumpNum(lumpnum); } } /* ================ = = R_DrawSpan = ================ */ int ds_y; int ds_x1; int ds_x2; lighttable_t *ds_colormap; fixed_t ds_xfrac; fixed_t ds_yfrac; fixed_t ds_xstep; fixed_t ds_ystep; byte *ds_source; // start of a 64*64 tile image int dscount; // just for profiling void R_DrawSpan(void) { fixed_t xfrac, yfrac; byte *dest; int count, spot; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || (unsigned) ds_y > SCREENHEIGHT) I_Error("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y); // dscount++; #endif xfrac = ds_xfrac; yfrac = ds_yfrac; dest = ylookup[ds_y] + columnofs[ds_x1]; count = ds_x2 - ds_x1; do { spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); *dest++ = ds_colormap[ds_source[spot]]; xfrac += ds_xstep; yfrac += ds_ystep; } while (count--); } void R_DrawSpanLow(void) { fixed_t xfrac, yfrac; byte *dest; int count, spot; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || (unsigned) ds_y > SCREENHEIGHT) I_Error("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y); // dscount++; #endif xfrac = ds_xfrac; yfrac = ds_yfrac; dest = ylookup[ds_y] + columnofs[ds_x1]; count = ds_x2 - ds_x1; do { spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63); *dest++ = ds_colormap[ds_source[spot]]; xfrac += ds_xstep; yfrac += ds_ystep; } while (count--); } /* ================ = = R_InitBuffer = ================= */ void R_InitBuffer(int width, int height) { int i; viewwindowx = (SCREENWIDTH - width) >> 1; for (i = 0; i < width; i++) columnofs[i] = viewwindowx + i; if (width == SCREENWIDTH) viewwindowy = 0; else viewwindowy = (SCREENHEIGHT - SBARHEIGHT - height) >> 1; for (i = 0; i < height; i++) ylookup[i] = I_VideoBuffer + (i + viewwindowy) * SCREENWIDTH; } /* ================== = = R_DrawViewBorder = = Draws the border around the view for different size windows ================== */ boolean BorderNeedRefresh; void R_DrawViewBorder(void) { byte *src, *dest; int x, y; if (scaledviewwidth == SCREENWIDTH) return; src = W_CacheLumpName("F_022", PU_CACHE); dest = I_VideoBuffer; for (y = 0; y < SCREENHEIGHT - SBARHEIGHT; y++) { for (x = 0; x < SCREENWIDTH / 64; x++) { memcpy(dest, src + ((y & 63) << 6), 64); dest += 64; } if (SCREENWIDTH & 63) { memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); dest += (SCREENWIDTH & 63); } } for (x = (viewwindowx >> crispy->hires); x < (viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires); x += 16) { V_DrawPatch(x, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName("bordt", PU_CACHE)); V_DrawPatch(x, (viewwindowy >> crispy->hires) + (viewheight >> crispy->hires), W_CacheLumpName("bordb", PU_CACHE)); } for (y = (viewwindowy >> crispy->hires); y < (viewwindowy >> crispy->hires) + (viewheight >> crispy->hires); y += 16) { V_DrawPatch((viewwindowx >> crispy->hires) - 4, y, W_CacheLumpName("bordl", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), y, W_CacheLumpName("bordr", PU_CACHE)); } V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName("bordtl", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) - 4, W_CacheLumpName("bordtr", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) + (viewheight >> crispy->hires), W_CacheLumpName("bordbr", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) + (viewheight >> crispy->hires), W_CacheLumpName("bordbl", PU_CACHE)); } /* ================== = = R_DrawTopBorder = = Draws the top border around the view for different size windows ================== */ boolean BorderTopRefresh; void R_DrawTopBorder(void) { byte *src, *dest; int x, y; if (scaledviewwidth == SCREENWIDTH) return; /* if(gamemode == shareware) { src = W_CacheLumpName ("FLOOR04", PU_CACHE); } else { src = W_CacheLumpName ("FLAT513", PU_CACHE); } */ src = W_CacheLumpName("F_022", PU_CACHE); dest = I_VideoBuffer; for (y = 0; y < 34; y++) { for (x = 0; x < SCREENWIDTH / 64; x++) { memcpy(dest, src + ((y & 63) << 6), 64); dest += 64; } if (SCREENWIDTH & 63) { memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); dest += (SCREENWIDTH & 63); } } if (viewwindowy < 35) { for (x = (viewwindowx >> crispy->hires); x < (viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires); x += 16) { V_DrawPatch(x, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName("bordt", PU_CACHE)); } V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires), W_CacheLumpName("bordl", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires), W_CacheLumpName("bordr", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) + 16, W_CacheLumpName("bordl", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) + 16, W_CacheLumpName("bordr", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) - 4, (viewwindowy >> crispy->hires) - 4, W_CacheLumpName("bordtl", PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires) + (viewwidth >> crispy->hires), (viewwindowy >> crispy->hires) - 4, W_CacheLumpName("bordtr", PU_CACHE)); } } crispy-doom-crispy-doom-5.6.4/src/hexen/r_local.h000066400000000000000000000342211360717211000217200ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __R_LOCAL__ #define __R_LOCAL__ #include "i_video.h" #define ANGLETOSKYSHIFT 22 // sky map is 256*128*4 maps #define BASEYCENTER 100 //#define MAXWIDTH 1120 //#define MAXHEIGHT 832 #define PI 3.141592657 #define CENTERY (SCREENHEIGHT/2) #define MINZ (FRACUNIT*4) #define FIELDOFVIEW 2048 // fineangles in the SCREENWIDTH wide window // // lighting constants // #define LIGHTLEVELS 16 #define LIGHTSEGSHIFT 4 #define MAXLIGHTSCALE 48 #define LIGHTSCALESHIFT 12 #define MAXLIGHTZ 128 #define LIGHTZSHIFT 20 #define NUMCOLORMAPS 32 // number of diminishing #define INVERSECOLORMAP 32 /* ============================================================================== INTERNAL MAP TYPES ============================================================================== */ //================ used by play and refresh typedef struct { fixed_t x, y; } vertex_t; struct line_s; typedef struct { fixed_t floorheight, ceilingheight; short floorpic, ceilingpic; short lightlevel; short special, tag; int soundtraversed; // 0 = untraversed, 1,2 = sndlines -1 mobj_t *soundtarget; // thing that made a sound (or null) seqtype_t seqType; // stone, metal, heavy, etc... int blockbox[4]; // mapblock bounding box for height changes degenmobj_t soundorg; // for any sounds played by the sector int validcount; // if == validcount, already checked mobj_t *thinglist; // list of mobjs in sector void *specialdata; // thinker_t for reversable actions int linecount; struct line_s **lines; // [linecount] size } sector_t; typedef struct { fixed_t textureoffset; // add this to the calculated texture col fixed_t rowoffset; // add this to the calculated texture top short toptexture, bottomtexture, midtexture; sector_t *sector; } side_t; typedef enum { ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE } slopetype_t; /* typedef struct line_s { vertex_t *v1, *v2; fixed_t dx,dy; // v2 - v1 for side checking short flags; short special, tag; short sidenum[2]; // sidenum[1] will be -1 if one sided fixed_t bbox[4]; slopetype_t slopetype; // to aid move clipping sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked void *specialdata; // thinker_t for reversable actions } line_t; */ typedef struct line_s { vertex_t *v1; vertex_t *v2; fixed_t dx; fixed_t dy; short flags; byte special; byte arg1; byte arg2; byte arg3; byte arg4; byte arg5; short sidenum[2]; fixed_t bbox[4]; slopetype_t slopetype; sector_t *frontsector; sector_t *backsector; int validcount; void *specialdata; } line_t; typedef struct { vertex_t *v1, *v2; fixed_t offset; angle_t angle; side_t *sidedef; line_t *linedef; sector_t *frontsector; sector_t *backsector; // NULL for one sided lines } seg_t; // ===== Polyobj data ===== typedef struct { int numsegs; seg_t **segs; degenmobj_t startSpot; vertex_t *originalPts; // used as the base for the rotations vertex_t *prevPts; // use to restore the old point values angle_t angle; int tag; // reference tag assigned in HereticEd int bbox[4]; int validcount; boolean crush; // should the polyobj attempt to crush mobjs? int seqType; fixed_t size; // polyobj size (area of POLY_AREAUNIT == size of FRACUNIT) void *specialdata; // pointer a thinker, if the poly is moving } polyobj_t; typedef struct polyblock_s { polyobj_t *polyobj; struct polyblock_s *prev; struct polyblock_s *next; } polyblock_t; typedef struct subsector_s { sector_t *sector; short numlines; short firstline; polyobj_t *poly; } subsector_t; 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 } node_t; /* ============================================================================== OTHER TYPES ============================================================================== */ typedef byte lighttable_t; // this could be wider for >8 bit display #define MAXVISPLANES 160*8 #define MAXOPENINGS MAXWIDTH*64*4 typedef struct { fixed_t height; int picnum; int lightlevel; int special; int minx, maxx; unsigned short pad1; // leave pads for [minx-1]/[maxx+1] unsigned short top[MAXWIDTH]; unsigned short pad2; unsigned short pad3; unsigned short bottom[MAXWIDTH]; unsigned short pad4; } visplane_t; typedef struct drawseg_s { seg_t *curline; int x1, x2; fixed_t scale1, scale2, scalestep; int silhouette; // 0=none, 1=bottom, 2=top, 3=both fixed_t bsilheight; // don't clip sprites above this fixed_t tsilheight; // don't clip sprites below this // pointers to lists for sprite clipping short *sprtopclip; // adjusted so [x1] is first value short *sprbottomclip; // adjusted so [x1] is first value short *maskedtexturecol; // adjusted so [x1] is first value } drawseg_t; #define SIL_NONE 0 #define SIL_BOTTOM 1 #define SIL_TOP 2 #define SIL_BOTH 3 #define MAXDRAWSEGS 256*8 // A vissprite_t is a thing that will be drawn during a refresh typedef struct vissprite_s { struct vissprite_s *prev, *next; int 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; lighttable_t *colormap; int mobjflags; // for color translation and shadow draw boolean psprite; // true if psprite int class; // player class (used in translation) fixed_t floorclip; } vissprite_t; extern visplane_t *floorplane, *ceilingplane; // Sprites are patches with a special naming convention so they can be // recognized by R_InitSprites. 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. Some sprites will only have one picture used // for all views. typedef struct { boolean rotate; // if false use 0 for any position short lump[8]; // lump to use for view angles 0-7 byte flip[8]; // flip (1 = flip) to use for view angles 0-7 } spriteframe_t; typedef struct { int numframes; spriteframe_t *spriteframes; } spritedef_t; extern spritedef_t *sprites; extern int numsprites; //============================================================================= 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 fixed_t viewx, viewy, viewz; extern angle_t viewangle; extern player_t *viewplayer; extern angle_t clipangle; extern int viewangletox[FINEANGLES / 2]; extern angle_t xtoviewangle[MAXWIDTH + 1]; extern fixed_t rw_distance; extern angle_t rw_normalangle; // // R_main.c // extern int screenblocks; extern int viewwidth, viewheight, viewwindowx, viewwindowy; extern int scaledviewwidth; extern int centerx, centery; extern int flyheight; extern fixed_t centerxfrac; extern fixed_t centeryfrac; extern fixed_t projection; extern int validcount; extern int sscount, linecount, loopcount; extern lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; extern lighttable_t *scalelightfixed[MAXLIGHTSCALE]; extern lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; extern int extralight; extern lighttable_t *fixedcolormap; extern fixed_t viewcos, viewsin; extern int detailshift; // 0 = high, 1 = low extern void (*colfunc) (void); extern void (*basecolfunc) (void); extern void (*tlcolfunc) (void); extern void (*spanfunc) (void); int R_PointOnSide(fixed_t x, fixed_t y, node_t * node); int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t * line); angle_t R_PointToAngle(fixed_t x, fixed_t y); angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); fixed_t R_PointToDist(fixed_t x, fixed_t y); fixed_t R_ScaleFromGlobalAngle(angle_t visangle); subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); //void R_AddPointToBox (int x, int y, fixed_t *box); // // R_bsp.c // extern seg_t *curline; extern side_t *sidedef; extern line_t *linedef; extern sector_t *frontsector, *backsector; extern int rw_x; extern int rw_stopx; extern boolean segtextured; extern boolean markfloor; // false if the back side is the same plane extern boolean markceiling; extern boolean skymap; extern drawseg_t drawsegs[MAXDRAWSEGS], *ds_p; extern lighttable_t **hscalelight, **vscalelight, **dscalelight; typedef void (*drawfunc_t) (int start, int stop); void R_ClearClipSegs(void); void R_ClearDrawSegs(void); void R_InitSkyMap(void); void R_RenderBSPNode(int bspnum); // // R_segs.c // extern int rw_angle1; // angle to line origin extern int TransTextureStart; extern int TransTextureEnd; void R_RenderMaskedSegRange(drawseg_t * ds, int x1, int x2); // // R_plane.c // typedef void (*planefunction_t) (int top, int bottom); extern planefunction_t floorfunc, ceilingfunc; extern int skyflatnum; extern short openings[MAXOPENINGS], *lastopening; extern short floorclip[MAXWIDTH]; extern short ceilingclip[MAXWIDTH]; extern fixed_t yslope[MAXHEIGHT]; extern fixed_t distscale[MAXWIDTH]; void R_InitPlanes(void); void R_ClearPlanes(void); void R_MapPlane(int y, int x1, int x2); void R_MakeSpans(int x, int t1, int b1, int t2, int b2); void R_DrawPlanes(void); visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel, int special); visplane_t *R_CheckPlane(visplane_t * pl, int start, int stop); // // R_debug.m // extern int drawbsp; void RD_OpenMapWindow(void); void RD_ClearMapWindow(void); void RD_DisplayLine(int x1, int y1, int x2, int y2, float gray); void RD_DrawNodeLine(node_t * node); void RD_DrawLineCheck(seg_t * line); void RD_DrawLine(seg_t * line); void RD_DrawBBox(fixed_t * bbox); // // R_data.c // extern fixed_t *textureheight; // needed for texture pegging extern fixed_t *spritewidth; // needed for pre rendering (fracs) extern fixed_t *spriteoffset; extern fixed_t *spritetopoffset; extern lighttable_t *colormaps; extern int firstflat; extern int numflats; extern int *flattranslation; // for global animation extern int *texturetranslation; // for global animation extern int firstspritelump, lastspritelump, numspritelumps; extern boolean LevelUseFullBright; byte *R_GetColumn(int tex, int col); void R_InitData(void); void R_PrecacheLevel(void); // // R_things.c // #define MAXVISSPRITES 192*8 extern vissprite_t vissprites[MAXVISSPRITES], *vissprite_p; extern vissprite_t vsprsortedhead; // constant arrays used for psprite clipping and initializing clipping extern short negonearray[MAXWIDTH]; extern short screenheightarray[MAXWIDTH]; // vars for R_DrawMaskedColumn extern short *mfloorclip; extern short *mceilingclip; extern fixed_t spryscale; extern fixed_t sprtopscreen; extern fixed_t sprbotscreen; extern fixed_t pspritescale, pspriteiscale; void R_DrawMaskedColumn(column_t * column, signed int baseclip); void R_SortVisSprites(void); void R_AddSprites(sector_t * sec); void R_AddPSprites(void); void R_DrawSprites(void); void R_InitSprites(const char **namelist); void R_ClearSprites(void); void R_DrawMasked(void); void R_ClipVisSprite(vissprite_t * vis, int xl, int xh); //============================================================================= // // R_draw.c // //============================================================================= extern lighttable_t *dc_colormap; extern int dc_x; extern int dc_yl; extern int dc_yh; extern fixed_t dc_iscale; extern fixed_t dc_texturemid; extern byte *dc_source; // first pixel in a column void R_DrawColumn(void); void R_DrawColumnLow(void); void R_DrawTLColumn(void); void R_DrawTLColumnLow(void); void R_DrawTranslatedColumn(void); void R_DrawTranslatedTLColumn(void); void R_DrawTranslatedColumnLow(void); void R_DrawAltTLColumn(void); //void R_DrawTranslatedAltTLColumn(void); extern int ds_y; extern int ds_x1; extern int ds_x2; extern lighttable_t *ds_colormap; extern fixed_t ds_xfrac; extern fixed_t ds_yfrac; extern fixed_t ds_xstep; extern fixed_t ds_ystep; extern byte *ds_source; // start of a 64*64 tile image extern byte *translationtables; extern byte *dc_translation; void R_DrawSpan(void); void R_DrawSpanLow(void); void R_InitBuffer(int width, int height); void R_InitTranslationTables(void); #endif // __R_LOCAL__ crispy-doom-crispy-doom-5.6.4/src/hexen/r_main.c000066400000000000000000000441751360717211000215560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "m_random.h" #include "h2def.h" #include "m_bbox.h" #include "r_local.h" int viewangleoffset; // haleyjd: removed WATCOMC int validcount = 1; // increment every time a check is made lighttable_t *fixedcolormap; extern lighttable_t **walllights; int centerx, centery; fixed_t centerxfrac, centeryfrac; fixed_t projection; int framecount; // just for profiling purposes int sscount, linecount, loopcount; fixed_t viewx, viewy, viewz; angle_t viewangle; fixed_t viewcos, viewsin; player_t *viewplayer; int detailshift; // 0 = high, 1 = low // // precalculated math tables // angle_t clipangle; // The viewangletox[viewangle + FINEANGLES/4] lookup maps the visible view // angles to screen X coordinates, flattening the arc to a flat projection // plane. There will be many angles mapped to the same X. int viewangletox[FINEANGLES / 2]; // The xtoviewangleangle[] table maps a screen pixel to the lowest viewangle // that maps back to x ranges from clipangle to -clipangle angle_t xtoviewangle[MAXWIDTH + 1]; lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; lighttable_t *scalelightfixed[MAXLIGHTSCALE]; lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; int extralight; // bumped light from gun blasts void (*colfunc) (void); void (*basecolfunc) (void); void (*tlcolfunc) (void); void (*transcolfunc) (void); void (*spanfunc) (void); /* =================== = = R_AddPointToBox = =================== */ /* void R_AddPointToBox (int x, int y, fixed_t *box) { if (x< box[BOXLEFT]) box[BOXLEFT] = x; if (x> box[BOXRIGHT]) box[BOXRIGHT] = x; if (y< box[BOXBOTTOM]) box[BOXBOTTOM] = y; if (y> box[BOXTOP]) box[BOXTOP] = y; } */ /* =============================================================================== = = R_PointOnSide = = Returns side 0 (front) or 1 (back) =============================================================================== */ int R_PointOnSide(fixed_t x, fixed_t y, node_t * node) { fixed_t dx, dy; fixed_t left, right; if (!node->dx) { if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); // try to quickly decide by looking at sign bits if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000) { if ((node->dy ^ dx) & 0x80000000) return 1; // (left is negative) return 0; } left = FixedMul(node->dy >> FRACBITS, dx); right = FixedMul(dy, node->dx >> FRACBITS); if (right < left) return 0; // front side return 1; // back side } int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t * line) { fixed_t lx, ly; fixed_t ldx, ldy; fixed_t dx, dy; fixed_t left, right; lx = line->v1->x; ly = line->v1->y; ldx = line->v2->x - lx; ldy = line->v2->y - ly; if (!ldx) { if (x <= lx) return ldy > 0; return ldy < 0; } if (!ldy) { if (y <= ly) return ldx < 0; return ldx > 0; } dx = (x - lx); dy = (y - ly); // try to quickly decide by looking at sign bits if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000) { if ((ldy ^ dx) & 0x80000000) return 1; // (left is negative) return 0; } left = FixedMul(ldy >> FRACBITS, dx); right = FixedMul(dy, ldx >> FRACBITS); if (right < left) return 0; // front side return 1; // back side } /* =============================================================================== = = R_PointToAngle = =============================================================================== */ #define DBITS (FRACBITS-SLOPEBITS) angle_t R_PointToAngle(fixed_t x, fixed_t y) { x -= viewx; y -= viewy; if ((!x) && (!y)) return 0; if (x >= 0) { // x >=0 if (y >= 0) { // y>= 0 if (x > y) return tantoangle[SlopeDiv(y, x)]; // octant 0 else return ANG90 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 1 } else { // y<0 y = -y; if (x > y) return -tantoangle[SlopeDiv(y, x)]; // octant 8 else return ANG270 + tantoangle[SlopeDiv(x, y)]; // octant 7 } } else { // x<0 x = -x; if (y >= 0) { // y>= 0 if (x > y) return ANG180 - 1 - tantoangle[SlopeDiv(y, x)]; // octant 3 else return ANG90 + tantoangle[SlopeDiv(x, y)]; // octant 2 } else { // y<0 y = -y; if (x > y) return ANG180 + tantoangle[SlopeDiv(y, x)]; // octant 4 else return ANG270 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 5 } } return 0; } angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) { viewx = x1; viewy = y1; return R_PointToAngle(x2, y2); } fixed_t R_PointToDist(fixed_t x, fixed_t y) { int angle; fixed_t dx, dy, temp; fixed_t dist; dx = abs(x - viewx); dy = abs(y - viewy); if (dy > dx) { temp = dx; dx = dy; dy = temp; } angle = (tantoangle[FixedDiv(dy, dx) >> DBITS] + ANG90) >> ANGLETOFINESHIFT; dist = FixedDiv(dx, finesine[angle]); // use as cosine return dist; } /* ================= = = R_InitPointToAngle = ================= */ void R_InitPointToAngle(void) { // now getting from tables.c #if 0 int i; long t; float f; // // slope (tangent) to angle lookup // for (i = 0; i <= SLOPERANGE; i++) { f = atan((float) i / SLOPERANGE) / (3.141592657 * 2); t = 0xffffffff * f; tantoangle[i] = t; } #endif } //============================================================================= /* ================ = = R_ScaleFromGlobalAngle = = Returns the texture mapping scale for the current line at the given angle = rw_distance must be calculated first ================ */ fixed_t R_ScaleFromGlobalAngle(angle_t visangle) { fixed_t scale; int anglea, angleb; int sinea, sineb; fixed_t num, den; #if 0 { fixed_t dist, z; fixed_t sinv, cosv; sinv = finesine[(visangle - rw_normalangle) >> ANGLETOFINESHIFT]; dist = FixedDiv(rw_distance, sinv); cosv = finecosine[(viewangle - visangle) >> ANGLETOFINESHIFT]; z = abs(FixedMul(dist, cosv)); scale = FixedDiv(projection, z); return scale; } #endif anglea = ANG90 + (visangle - viewangle); angleb = ANG90 + (visangle - rw_normalangle); // bothe sines are allways positive sinea = finesine[anglea >> ANGLETOFINESHIFT]; sineb = finesine[angleb >> ANGLETOFINESHIFT]; num = FixedMul(projection, sineb) << detailshift; den = FixedMul(rw_distance, sinea); if (den > num >> 16) { scale = FixedDiv(num, den); if (scale > 64 * FRACUNIT) scale = 64 * FRACUNIT; else if (scale < 256) scale = 256; } else scale = 64 * FRACUNIT; return scale; } /* ================= = = R_InitTables = ================= */ void R_InitTables(void) { // now getting from tables.c #if 0 int i; float a, fv; int t; // // viewangle tangent table // for (i = 0; i < FINEANGLES / 2; i++) { a = (i - FINEANGLES / 4 + 0.5) * PI * 2 / FINEANGLES; fv = FRACUNIT * tan(a); t = fv; finetangent[i] = t; } // // finesine table // for (i = 0; i < 5 * FINEANGLES / 4; i++) { // OPTIMIZE: mirror... a = (i + 0.5) * PI * 2 / FINEANGLES; t = FRACUNIT * sin(a); finesine[i] = t; } #endif } /* ================= = = R_InitTextureMapping = ================= */ void R_InitTextureMapping(void) { int i; int x; int t; fixed_t focallength; // // use tangent table to generate viewangletox // viewangletox will give the next greatest x after the view angle // // calc focallength so FIELDOFVIEW angles covers SCREENWIDTH focallength = FixedDiv(centerxfrac, finetangent[FINEANGLES / 4 + FIELDOFVIEW / 2]); for (i = 0; i < FINEANGLES / 2; i++) { if (finetangent[i] > FRACUNIT * 2) t = -1; else if (finetangent[i] < -FRACUNIT * 2) t = viewwidth + 1; else { t = FixedMul(finetangent[i], focallength); t = (centerxfrac - t + FRACUNIT - 1) >> FRACBITS; if (t < -1) t = -1; else if (t > viewwidth + 1) t = viewwidth + 1; } viewangletox[i] = t; } // // scan viewangletox[] to generate xtoviewangleangle[] // // xtoviewangle will give the smallest view angle that maps to x for (x = 0; x <= viewwidth; x++) { i = 0; while (viewangletox[i] > x) i++; xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANG90; } // // take out the fencepost cases from viewangletox // for (i = 0; i < FINEANGLES / 2; i++) { t = FixedMul(finetangent[i], focallength); t = centerx - t; if (viewangletox[i] == -1) viewangletox[i] = 0; else if (viewangletox[i] == viewwidth + 1) viewangletox[i] = viewwidth; } clipangle = xtoviewangle[0]; } //============================================================================= /* ==================== = = R_InitLightTables = = Only inits the zlight table, because the scalelight table changes = with view size = ==================== */ #define DISTMAP 2 void R_InitLightTables(void) { int i, j, level, startmap; int scale; // // Calculate the light levels to use for each level / distance combination // for (i = 0; i < LIGHTLEVELS; i++) { startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; for (j = 0; j < MAXLIGHTZ; j++) { scale = FixedDiv((ORIGWIDTH / 2 * FRACUNIT), (j + 1) << LIGHTZSHIFT); scale >>= LIGHTSCALESHIFT; level = startmap - scale / DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; zlight[i][j] = colormaps + level * 256; } } } /* ============== = = R_SetViewSize = = Don't really change anything here, because i might be in the middle of = a refresh. The change will take effect next refresh. = ============== */ boolean setsizeneeded; int setblocks, setdetail; void R_SetViewSize(int blocks, int detail) { setsizeneeded = true; setblocks = blocks; setdetail = detail; } /* ============== = = R_ExecuteSetViewSize = ============== */ void R_ExecuteSetViewSize(void) { fixed_t cosadj, dy; int i, j, level, startmap; setsizeneeded = false; if (setblocks == 11) { scaledviewwidth = SCREENWIDTH; viewheight = SCREENHEIGHT; } else { scaledviewwidth = (setblocks * 32) << crispy->hires; viewheight = (setblocks * 161 / 10) << crispy->hires; } detailshift = setdetail; viewwidth = scaledviewwidth >> detailshift; centery = viewheight / 2; centerx = viewwidth / 2; centerxfrac = centerx << FRACBITS; centeryfrac = centery << FRACBITS; projection = centerxfrac; if (!detailshift) { colfunc = basecolfunc = R_DrawColumn; tlcolfunc = R_DrawTLColumn; transcolfunc = R_DrawTranslatedColumn; spanfunc = R_DrawSpan; } else { colfunc = basecolfunc = R_DrawColumnLow; tlcolfunc = R_DrawTLColumn; transcolfunc = R_DrawTranslatedColumn; spanfunc = R_DrawSpanLow; } R_InitBuffer(scaledviewwidth, viewheight); R_InitTextureMapping(); // // psprite scales // pspritescale = FRACUNIT * viewwidth / ORIGWIDTH; pspriteiscale = FRACUNIT * ORIGWIDTH / viewwidth; // // thing clipping // for (i = 0; i < viewwidth; i++) screenheightarray[i] = viewheight; // // planes // for (i = 0; i < viewheight; i++) { dy = ((i - viewheight / 2) << FRACBITS) + FRACUNIT / 2; dy = abs(dy); yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, dy); } for (i = 0; i < viewwidth; i++) { cosadj = abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]); distscale[i] = FixedDiv(FRACUNIT, cosadj); } // // Calculate the light levels to use for each level / scale combination // for (i = 0; i < LIGHTLEVELS; i++) { startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; for (j = 0; j < MAXLIGHTSCALE; j++) { level = startmap - j * SCREENWIDTH / (viewwidth << detailshift) / DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; scalelight[i][j] = colormaps + level * 256; } } // // draw the border // R_DrawViewBorder(); // erase old menu stuff } /* ============== = = R_Init = ============== */ int detailLevel; int screenblocks = 10; void R_Init(void) { R_InitData(); R_InitPointToAngle(); R_InitTables(); // viewwidth / viewheight / detailLevel are set by the defaults R_SetViewSize(screenblocks, detailLevel); R_InitPlanes(); R_InitLightTables(); R_InitSkyMap(); R_InitTranslationTables(); framecount = 0; } /* ============== = = R_PointInSubsector = ============== */ subsector_t *R_PointInSubsector(fixed_t x, fixed_t y) { node_t *node; int side, nodenum; if (!numnodes) // single subsector is a special case return subsectors; nodenum = numnodes - 1; while (!(nodenum & NF_SUBSECTOR)) { node = &nodes[nodenum]; side = R_PointOnSide(x, y, node); nodenum = node->children[side]; } return &subsectors[nodenum & ~NF_SUBSECTOR]; } //---------------------------------------------------------------------------- // // PROC R_SetupFrame // //---------------------------------------------------------------------------- void R_SetupFrame(player_t * player) { int i; int tableAngle; int tempCentery; int intensity; //drawbsp = 1; viewplayer = player; // haleyjd: removed WATCOMC // haleyjd FIXME: viewangleoffset handling? viewangle = player->mo->angle + viewangleoffset; tableAngle = viewangle >> ANGLETOFINESHIFT; viewx = player->mo->x; viewy = player->mo->y; if (localQuakeHappening[displayplayer] && !paused) { intensity = localQuakeHappening[displayplayer]; viewx += ((M_Random() % (intensity << 2)) - (intensity << 1)) << FRACBITS; viewy += ((M_Random() % (intensity << 2)) - (intensity << 1)) << FRACBITS; } extralight = player->extralight; viewz = player->viewz; tempCentery = viewheight / 2 + ((player->lookdir) << crispy->hires) * screenblocks / 10; if (centery != tempCentery) { centery = tempCentery; centeryfrac = centery << FRACBITS; for (i = 0; i < viewheight; i++) { yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, abs(((i - centery) << FRACBITS) + FRACUNIT / 2)); } } viewsin = finesine[tableAngle]; viewcos = finecosine[tableAngle]; sscount = 0; if (player->fixedcolormap) { fixedcolormap = colormaps + player->fixedcolormap * 256 * sizeof(lighttable_t); walllights = scalelightfixed; for (i = 0; i < MAXLIGHTSCALE; i++) { scalelightfixed[i] = fixedcolormap; } } else { fixedcolormap = 0; } framecount++; validcount++; if (BorderNeedRefresh) { if (setblocks < 10) { R_DrawViewBorder(); } BorderNeedRefresh = false; BorderTopRefresh = false; UpdateState |= I_FULLSCRN; } if (BorderTopRefresh) { if (setblocks < 10) { R_DrawTopBorder(); } BorderTopRefresh = false; UpdateState |= I_MESSAGES; } #if 0 { static int frame; memset(screen, frame, SCREENWIDTH * SCREENHEIGHT); frame++; } #endif } /* ============== = = R_RenderView = ============== */ void R_RenderPlayerView(player_t * player) { R_SetupFrame(player); R_ClearClipSegs(); R_ClearDrawSegs(); R_ClearPlanes(); R_ClearSprites(); NetUpdate(); // check for new console commands // Make displayed player invisible locally if (localQuakeHappening[displayplayer] && gamestate == GS_LEVEL) { players[displayplayer].mo->flags2 |= MF2_DONTDRAW; R_RenderBSPNode(numnodes - 1); // head node is the last node output players[displayplayer].mo->flags2 &= ~MF2_DONTDRAW; } else { R_RenderBSPNode(numnodes - 1); // head node is the last node output } NetUpdate(); // check for new console commands R_DrawPlanes(); NetUpdate(); // check for new console commands R_DrawMasked(); NetUpdate(); // check for new console commands } crispy-doom-crispy-doom-5.6.4/src/hexen/r_plane.c000066400000000000000000000415231360717211000217230ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "i_system.h" #include "r_local.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern fixed_t Sky1ScrollDelta; extern fixed_t Sky2ScrollDelta; // PUBLIC DATA DEFINITIONS ------------------------------------------------- int Sky1Texture; int Sky2Texture; fixed_t Sky1ColumnOffset; fixed_t Sky2ColumnOffset; int skyflatnum; int skytexturemid; fixed_t skyiscale; boolean DoubleSky; planefunction_t floorfunc, ceilingfunc; // Opening visplane_t visplanes[MAXVISPLANES], *lastvisplane; visplane_t *floorplane, *ceilingplane; short openings[MAXOPENINGS], *lastopening; // Clip values are the solid pixel bounding the range. // floorclip start out SCREENHEIGHT // ceilingclip starts out -1 short floorclip[MAXWIDTH]; short ceilingclip[MAXWIDTH]; // spanstart holds the start of a plane span, initialized to 0 int spanstart[MAXHEIGHT]; int spanstop[MAXHEIGHT]; // Texture mapping lighttable_t **planezlight; fixed_t planeheight; fixed_t yslope[MAXHEIGHT]; fixed_t distscale[MAXWIDTH]; fixed_t basexscale, baseyscale; fixed_t cachedheight[MAXHEIGHT]; fixed_t cacheddistance[MAXHEIGHT]; fixed_t cachedxstep[MAXHEIGHT]; fixed_t cachedystep[MAXHEIGHT]; // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- //========================================================================== // // R_InitSky // // Called at level load. // //========================================================================== void R_InitSky(int map) { Sky1Texture = P_GetMapSky1Texture(map); Sky2Texture = P_GetMapSky2Texture(map); Sky1ScrollDelta = P_GetMapSky1ScrollDelta(map); Sky2ScrollDelta = P_GetMapSky2ScrollDelta(map); Sky1ColumnOffset = 0; Sky2ColumnOffset = 0; DoubleSky = P_GetMapDoubleSky(map); } //========================================================================== // // R_InitSkyMap // // Called whenever the view size changes. // //========================================================================== void R_InitSkyMap(void) { skyflatnum = R_FlatNumForName("F_SKY"); skytexturemid = 200 * FRACUNIT; skyiscale = FRACUNIT; } //========================================================================== // // R_InitPlanes // // Called at game startup. // //========================================================================== void R_InitPlanes(void) { } //========================================================================== // // R_MapPlane // // Globals used: planeheight, ds_source, basexscale, baseyscale, // viewx, viewy. // //========================================================================== void R_MapPlane(int y, int x1, int x2) { angle_t angle; fixed_t distance, length; unsigned index; #ifdef RANGECHECK if (x2 < x1 || x1 < 0 || x2 >= viewwidth || (unsigned) y > viewheight) { I_Error("R_MapPlane: %i, %i at %i", x1, x2, y); } #endif if (planeheight != cachedheight[y]) { cachedheight[y] = planeheight; distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]); ds_xstep = cachedxstep[y] = FixedMul(distance, basexscale); ds_ystep = cachedystep[y] = FixedMul(distance, baseyscale); } else { distance = cacheddistance[y]; ds_xstep = cachedxstep[y]; ds_ystep = cachedystep[y]; } length = FixedMul(distance, distscale[x1]); angle = (viewangle + xtoviewangle[x1]) >> ANGLETOFINESHIFT; ds_xfrac = viewx + FixedMul(finecosine[angle], length); ds_yfrac = -viewy - FixedMul(finesine[angle], length); if (fixedcolormap) { ds_colormap = fixedcolormap; } else { index = distance >> LIGHTZSHIFT; if (index >= MAXLIGHTZ) { index = MAXLIGHTZ - 1; } ds_colormap = planezlight[index]; } ds_y = y; ds_x1 = x1; ds_x2 = x2; spanfunc(); // High or low detail } //========================================================================== // // R_ClearPlanes // // Called at the beginning of each frame. // //========================================================================== void R_ClearPlanes(void) { int i; angle_t angle; // Opening / clipping determination for (i = 0; i < viewwidth; i++) { floorclip[i] = viewheight; ceilingclip[i] = -1; } lastvisplane = visplanes; lastopening = openings; // Texture calculation memset(cachedheight, 0, sizeof(cachedheight)); angle = (viewangle - ANG90) >> ANGLETOFINESHIFT; // left to right mapping // Scale will be unit scale at SCREENWIDTH/2 distance basexscale = FixedDiv(finecosine[angle], centerxfrac); baseyscale = -FixedDiv(finesine[angle], centerxfrac); } //========================================================================== // // R_FindPlane // //========================================================================== visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel, int special) { visplane_t *check; if (special < 150) { // Don't let low specials affect search special = 0; } if (picnum == skyflatnum) { // All skies map together height = 0; lightlevel = 0; } for (check = visplanes; check < lastvisplane; check++) { if (height == check->height && picnum == check->picnum && lightlevel == check->lightlevel && special == check->special) break; } if (check < lastvisplane) { return (check); } if (lastvisplane - visplanes == MAXVISPLANES) { I_Error("R_FindPlane: no more visplanes"); } lastvisplane++; check->height = height; check->picnum = picnum; check->lightlevel = lightlevel; check->special = special; check->minx = SCREENWIDTH; check->maxx = -1; memset(check->top, 0xff, sizeof(check->top)); return (check); } //========================================================================== // // R_CheckPlane // //========================================================================== visplane_t *R_CheckPlane(visplane_t * pl, int start, int stop) { int intrl, intrh; int unionl, unionh; int x; if (start < pl->minx) { intrl = pl->minx; unionl = start; } else { unionl = pl->minx; intrl = start; } if (stop > pl->maxx) { intrh = pl->maxx; unionh = stop; } else { unionh = pl->maxx; intrh = stop; } for (x = intrl; x <= intrh; x++) { if (pl->top[x] != 0xffff) { break; } } if (x > intrh) { pl->minx = unionl; pl->maxx = unionh; return pl; // use the same visplane } // Make a new visplane lastvisplane->height = pl->height; lastvisplane->picnum = pl->picnum; lastvisplane->lightlevel = pl->lightlevel; lastvisplane->special = pl->special; pl = lastvisplane++; pl->minx = start; pl->maxx = stop; memset(pl->top, 0xff, sizeof(pl->top)); return pl; } //========================================================================== // // R_MakeSpans // //========================================================================== void R_MakeSpans(int x, int t1, int b1, int t2, int b2) { while (t1 < t2 && t1 <= b1) { R_MapPlane(t1, spanstart[t1], x - 1); t1++; } while (b1 > b2 && b1 >= t1) { R_MapPlane(b1, spanstart[b1], x - 1); b1--; } while (t2 < t1 && t2 <= b2) { spanstart[t2] = x; t2++; } while (b2 > b1 && b2 >= t2) { spanstart[b2] = x; b2--; } } //========================================================================== // // R_DrawPlanes // //========================================================================== #define SKYTEXTUREMIDSHIFTED 200 void R_DrawPlanes(void) { visplane_t *pl; int light; int x, stop; int angle; byte *tempSource; byte *source; byte *source2; byte *dest; int count; int offset; int skyTexture; int offset2; int skyTexture2; int scrollOffset; int frac; int fracstep = FRACUNIT >> crispy->hires; extern byte *ylookup[MAXHEIGHT]; extern int columnofs[MAXWIDTH]; #ifdef RANGECHECK if (ds_p - drawsegs > MAXDRAWSEGS) { I_Error("R_DrawPlanes: drawsegs overflow (%" PRIiPTR ")", ds_p - drawsegs); } if (lastvisplane - visplanes > MAXVISPLANES) { I_Error("R_DrawPlanes: visplane overflow (%" PRIiPTR ")", lastvisplane - visplanes); } if (lastopening - openings > MAXOPENINGS) { I_Error("R_DrawPlanes: opening overflow (%" PRIiPTR ")", lastopening - openings); } #endif for (pl = visplanes; pl < lastvisplane; pl++) { if (pl->minx > pl->maxx) { continue; } if (pl->picnum == skyflatnum) { // Sky flat if (DoubleSky) { // Render 2 layers, sky 1 in front offset = Sky1ColumnOffset >> 16; skyTexture = texturetranslation[Sky1Texture]; offset2 = Sky2ColumnOffset >> 16; skyTexture2 = texturetranslation[Sky2Texture]; for (x = pl->minx; x <= pl->maxx; x++) { dc_yl = pl->top[x]; dc_yh = pl->bottom[x]; if (dc_yl <= dc_yh) { count = dc_yh - dc_yl; if (count < 0) { return; } angle = (viewangle + xtoviewangle[x]) >> ANGLETOSKYSHIFT; source = R_GetColumn(skyTexture, angle + offset); source2 = R_GetColumn(skyTexture2, angle + offset2); dest = ylookup[dc_yl] + columnofs[x]; frac = SKYTEXTUREMIDSHIFTED * FRACUNIT + (dc_yl - centery) * fracstep; do { if (source[frac >> FRACBITS]) { *dest = source[frac >> FRACBITS]; frac += fracstep; } else { *dest = source2[frac >> FRACBITS]; frac += fracstep; } dest += SCREENWIDTH; } while (count--); } } continue; // Next visplane } else { // Render single layer if (pl->special == 200) { // Use sky 2 offset = Sky2ColumnOffset >> 16; skyTexture = texturetranslation[Sky2Texture]; } else { // Use sky 1 offset = Sky1ColumnOffset >> 16; skyTexture = texturetranslation[Sky1Texture]; } for (x = pl->minx; x <= pl->maxx; x++) { dc_yl = pl->top[x]; dc_yh = pl->bottom[x]; if (dc_yl <= dc_yh) { count = dc_yh - dc_yl; if (count < 0) { return; } angle = (viewangle + xtoviewangle[x]) >> ANGLETOSKYSHIFT; source = R_GetColumn(skyTexture, angle + offset); dest = ylookup[dc_yl] + columnofs[x]; frac = SKYTEXTUREMIDSHIFTED * FRACUNIT + (dc_yl - centery) * fracstep; do { *dest = source[frac >> FRACBITS]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } } continue; // Next visplane } } // Regular flat tempSource = W_CacheLumpNum(firstflat + flattranslation[pl->picnum], PU_STATIC); scrollOffset = leveltime >> 1 & 63; switch (pl->special) { // Handle scrolling flats case 201: case 202: case 203: // Scroll_North_xxx ds_source = tempSource + ((scrollOffset << (pl->special - 201) & 63) << 6); break; case 204: case 205: case 206: // Scroll_East_xxx ds_source = tempSource + ((63 - scrollOffset) << (pl->special - 204) & 63); break; case 207: case 208: case 209: // Scroll_South_xxx ds_source = tempSource + (((63 - scrollOffset) << (pl->special - 207) & 63) << 6); break; case 210: case 211: case 212: // Scroll_West_xxx ds_source = tempSource + (scrollOffset << (pl->special - 210) & 63); break; case 213: case 214: case 215: // Scroll_NorthWest_xxx ds_source = tempSource + (scrollOffset << (pl->special - 213) & 63) + ((scrollOffset << (pl->special - 213) & 63) << 6); break; case 216: case 217: case 218: // Scroll_NorthEast_xxx ds_source = tempSource + ((63 - scrollOffset) << (pl->special - 216) & 63) + ((scrollOffset << (pl->special - 216) & 63) << 6); break; case 219: case 220: case 221: // Scroll_SouthEast_xxx ds_source = tempSource + ((63 - scrollOffset) << (pl->special - 219) & 63) + (((63 - scrollOffset) << (pl->special - 219) & 63) << 6); break; case 222: case 223: case 224: // Scroll_SouthWest_xxx ds_source = tempSource + (scrollOffset << (pl->special - 222) & 63) + (((63 - scrollOffset) << (pl->special - 222) & 63) << 6); break; default: ds_source = tempSource; break; } planeheight = abs(pl->height - viewz); light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight; if (light >= LIGHTLEVELS) { light = LIGHTLEVELS - 1; } if (light < 0) { light = 0; } planezlight = zlight[light]; pl->top[pl->maxx + 1] = 0xffff; pl->top[pl->minx - 1] = 0xffff; stop = pl->maxx + 1; for (x = pl->minx; x <= stop; x++) { R_MakeSpans(x, pl->top[x - 1], pl->bottom[x - 1], pl->top[x], pl->bottom[x]); } W_ReleaseLumpNum(firstflat + flattranslation[pl->picnum]); } } crispy-doom-crispy-doom-5.6.4/src/hexen/r_segs.c000066400000000000000000000462751360717211000215760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "i_system.h" #include "r_local.h" // OPTIMIZE: closed two sided lines as single sided boolean segtextured; // true if any of the segs textures might be vis boolean markfloor; // false if the back side is the same plane boolean markceiling; boolean maskedtexture; int toptexture, bottomtexture, midtexture; angle_t rw_normalangle; int rw_angle1; // angle to line origin // // wall // int rw_x; int rw_stopx; angle_t rw_centerangle; fixed_t rw_offset; fixed_t rw_distance; fixed_t rw_scale; fixed_t rw_scalestep; fixed_t rw_midtexturemid; fixed_t rw_toptexturemid; fixed_t rw_bottomtexturemid; int worldtop, worldbottom, worldhigh, worldlow; fixed_t pixhigh, pixlow; fixed_t pixhighstep, pixlowstep; fixed_t topfrac, topstep; fixed_t bottomfrac, bottomstep; lighttable_t **walllights; short *maskedtexturecol; /* ================ = = R_RenderMaskedSegRange = ================ */ void R_RenderMaskedSegRange(drawseg_t * ds, int x1, int x2) { unsigned index; column_t *col; int lightnum; int texnum; // // calculate light table // use different light tables for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally curline = ds->curline; frontsector = curline->frontsector; backsector = curline->backsector; texnum = texturetranslation[curline->sidedef->midtexture]; lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; //if (curline->v1->y == curline->v2->y) // lightnum--; //else if (curline->v1->x == curline->v2->x) // lightnum++; //if (lightnum < 0) // walllights = scalelight[0]; if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS - 1]; else walllights = scalelight[lightnum]; maskedtexturecol = ds->maskedtexturecol; rw_scalestep = ds->scalestep; spryscale = ds->scale1 + (x1 - ds->x1) * rw_scalestep; mfloorclip = ds->sprbottomclip; mceilingclip = ds->sprtopclip; // // find positioning // if (curline->linedef->flags & ML_DONTPEGBOTTOM) { dc_texturemid = frontsector->floorheight > backsector->floorheight ? frontsector->floorheight : backsector->floorheight; dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; } else { dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight ? frontsector->ceilingheight : backsector->ceilingheight; dc_texturemid = dc_texturemid - viewz; } dc_texturemid += curline->sidedef->rowoffset; if (fixedcolormap) dc_colormap = fixedcolormap; // // draw the columns // for (dc_x = x1; dc_x <= x2; dc_x++) { // calculate lighting if (maskedtexturecol[dc_x] != SHRT_MAX) { if (!fixedcolormap) { index = spryscale >> (LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; dc_colormap = walllights[index]; } sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); dc_iscale = 0xffffffffu / (unsigned) spryscale; // // draw the texture // col = (column_t *) ((byte *) R_GetColumn(texnum, maskedtexturecol[dc_x]) - 3); R_DrawMaskedColumn(col, -1); maskedtexturecol[dc_x] = SHRT_MAX; } spryscale += rw_scalestep; } } /* ================ = = R_RenderSegLoop = = Draws zero, one, or two textures (and possibly a masked texture) for walls = Can draw or mark the starting pixel of floor and ceiling textures = = CALLED: CORE LOOPING ROUTINE ================ */ #define HEIGHTBITS 12 #define HEIGHTUNIT (1<> HEIGHTBITS; if (yl < ceilingclip[rw_x] + 1) yl = ceilingclip[rw_x] + 1; // no space above wall if (markceiling) { top = ceilingclip[rw_x] + 1; bottom = yl - 1; if (bottom >= floorclip[rw_x]) bottom = floorclip[rw_x] - 1; if (top <= bottom) { ceilingplane->top[rw_x] = top; ceilingplane->bottom[rw_x] = bottom; } } yh = bottomfrac >> HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x] - 1; if (markfloor) { top = yh + 1; bottom = floorclip[rw_x] - 1; if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x] + 1; if (top <= bottom) { floorplane->top[rw_x] = top; floorplane->bottom[rw_x] = bottom; } } // // texturecolumn and lighting are independent of wall tiers // if (segtextured) { // calculate texture offset angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT; texturecolumn = rw_offset - FixedMul(finetangent[angle], rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale >> (LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; dc_colormap = walllights[index]; dc_x = rw_x; dc_iscale = 0xffffffffu / (unsigned) rw_scale; } // // draw the wall tiers // if (midtexture) { // single sided line dc_yl = yl; dc_yh = yh; dc_texturemid = rw_midtexturemid; dc_source = R_GetColumn(midtexture, texturecolumn); colfunc(); ceilingclip[rw_x] = viewheight; floorclip[rw_x] = -1; } else { // two sided line if (toptexture) { // top wall mid = pixhigh >> HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x] - 1; if (mid >= yl) { dc_yl = yl; dc_yh = mid; dc_texturemid = rw_toptexturemid; dc_source = R_GetColumn(toptexture, texturecolumn); colfunc(); ceilingclip[rw_x] = mid; } else ceilingclip[rw_x] = yl - 1; } else { // no top wall if (markceiling) ceilingclip[rw_x] = yl - 1; } if (bottomtexture) { // bottom wall mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; pixlow += pixlowstep; if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x] + 1; // no space above wall if (mid <= yh) { dc_yl = mid; dc_yh = yh; dc_texturemid = rw_bottomtexturemid; dc_source = R_GetColumn(bottomtexture, texturecolumn); colfunc(); floorclip[rw_x] = mid; } else floorclip[rw_x] = yh + 1; } else { // no bottom wall if (markfloor) floorclip[rw_x] = yh + 1; } if (maskedtexture) { // save texturecol for backdrawing of masked mid texture maskedtexturecol[rw_x] = texturecolumn; } } rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } } /* ===================== = = R_StoreWallRange = = A wall segment will be drawn between start and stop pixels (inclusive) = ====================== */ void R_StoreWallRange(int start, int stop) { fixed_t hyp; fixed_t sineval; angle_t distangle, offsetangle; fixed_t vtop; int lightnum; if (ds_p == &drawsegs[MAXDRAWSEGS]) return; // don't overflow and crash #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->angle + ANG90; offsetangle = abs(rw_normalangle - rw_angle1); if (offsetangle > ANG90) offsetangle = ANG90; distangle = ANG90 - offsetangle; hyp = R_PointToDist(curline->v1->x, curline->v1->y); sineval = finesine[distangle >> ANGLETOFINESHIFT]; rw_distance = FixedMul(hyp, sineval); ds_p->x1 = rw_x = start; ds_p->x2 = stop; ds_p->curline = curline; rw_stopx = stop + 1; // // calculate scale at both ends and step // ds_p->scale1 = rw_scale = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[start]); if (stop > start) { ds_p->scale2 = R_ScaleFromGlobalAngle(viewangle + xtoviewangle[stop]); ds_p->scalestep = rw_scalestep = (ds_p->scale2 - rw_scale) / (stop - start); } else { // // try to fix the stretched line bug // #if 0 if (rw_distance < FRACUNIT / 2) { fixed_t trx, try; fixed_t gxt, gyt; trx = curline->v1->x - viewx; try = curline->v1->y - viewy; gxt = FixedMul(trx, viewcos); gyt = -FixedMul(try, viewsin); ds_p->scale1 = FixedDiv(projection, gxt - gyt); } #endif ds_p->scale2 = ds_p->scale1; } // // calculate texture boundaries and decide if floor / ceiling marks // are needed // worldtop = frontsector->ceilingheight - viewz; worldbottom = frontsector->floorheight - viewz; midtexture = toptexture = bottomtexture = maskedtexture = 0; ds_p->maskedtexturecol = NULL; if (!backsector) { // // single sided line // midtexture = texturetranslation[sidedef->midtexture]; // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; if (linedef->flags & ML_DONTPEGBOTTOM) { vtop = frontsector->floorheight + textureheight[sidedef->midtexture]; rw_midtexturemid = vtop - viewz; // bottom of texture at bottom } else rw_midtexturemid = worldtop; // top of texture at top rw_midtexturemid += sidedef->rowoffset; ds_p->silhouette = SIL_BOTH; ds_p->sprtopclip = screenheightarray; ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->tsilheight = INT_MIN; } else { // // two sided line // ds_p->sprtopclip = ds_p->sprbottomclip = NULL; ds_p->silhouette = 0; if (frontsector->floorheight > backsector->floorheight) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = frontsector->floorheight; } else if (backsector->floorheight > viewz) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = INT_MAX; // ds_p->sprbottomclip = negonearray; } if (frontsector->ceilingheight < backsector->ceilingheight) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = frontsector->ceilingheight; } else if (backsector->ceilingheight < viewz) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT_MIN; // ds_p->sprtopclip = screenheightarray; } if (backsector->ceilingheight <= frontsector->floorheight) { ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->silhouette |= SIL_BOTTOM; } if (backsector->floorheight >= frontsector->ceilingheight) { ds_p->sprtopclip = screenheightarray; ds_p->tsilheight = INT_MIN; ds_p->silhouette |= SIL_TOP; } worldhigh = backsector->ceilingheight - viewz; worldlow = backsector->floorheight - viewz; // hack to allow height changes in outdoor areas if (frontsector->ceilingpic == skyflatnum && backsector->ceilingpic == skyflatnum) worldtop = worldhigh; if (worldlow != worldbottom || backsector->floorpic != frontsector->floorpic || backsector->lightlevel != frontsector->lightlevel || backsector->special != frontsector->special) markfloor = true; else markfloor = false; // same plane on both sides if (worldhigh != worldtop || backsector->ceilingpic != frontsector->ceilingpic || backsector->lightlevel != frontsector->lightlevel) markceiling = true; else markceiling = false; // same plane on both sides if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) markceiling = markfloor = true; // closed door if (worldhigh < worldtop) { // top texture toptexture = texturetranslation[sidedef->toptexture]; if (linedef->flags & ML_DONTPEGTOP) rw_toptexturemid = worldtop; // top of texture at top else { vtop = backsector->ceilingheight + textureheight[sidedef->toptexture]; rw_toptexturemid = vtop - viewz; // bottom of texture } } if (worldlow > worldbottom) { // bottom texture bottomtexture = texturetranslation[sidedef->bottomtexture]; if (linedef->flags & ML_DONTPEGBOTTOM) { // bottom of texture at bottom rw_bottomtexturemid = worldtop; // top of texture at top } else // top of texture at top rw_bottomtexturemid = worldlow; } rw_toptexturemid += sidedef->rowoffset; rw_bottomtexturemid += sidedef->rowoffset; // // allocate space for masked texture tables // if (sidedef->midtexture) { // masked midtexture maskedtexture = true; ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; lastopening += rw_stopx - rw_x; } } // // calculate rw_offset (only needed for textured lines) // segtextured = midtexture | toptexture | bottomtexture | maskedtexture; if (segtextured) { offsetangle = rw_normalangle - rw_angle1; if (offsetangle > ANG180) offsetangle = -offsetangle; if (offsetangle > ANG90) offsetangle = ANG90; sineval = finesine[offsetangle >> ANGLETOFINESHIFT]; rw_offset = FixedMul(hyp, sineval); if (rw_normalangle - rw_angle1 < ANG180) rw_offset = -rw_offset; rw_offset += sidedef->textureoffset + curline->offset; rw_centerangle = ANG90 + viewangle - rw_normalangle; // // calculate light table // use different light tables for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally if (!fixedcolormap) { lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; //if (curline->v1->y == curline->v2->y) // lightnum--; //else if (curline->v1->x == curline->v2->x) // lightnum++; //if (lightnum < 0) // walllights = scalelight[0]; if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS - 1]; else walllights = scalelight[lightnum]; } } // // if a floor / ceiling plane is on the wrong side of the view plane // it is definately invisible and doesn't need to be marked // if (frontsector->floorheight >= viewz) markfloor = false; // above view plane if (frontsector->ceilingheight <= viewz && frontsector->ceilingpic != skyflatnum) markceiling = false; // below view plane // // calculate incremental stepping values for texture edges // worldtop >>= 4; worldbottom >>= 4; topstep = -FixedMul(rw_scalestep, worldtop); topfrac = (centeryfrac >> 4) - FixedMul(worldtop, rw_scale); bottomstep = -FixedMul(rw_scalestep, worldbottom); bottomfrac = (centeryfrac >> 4) - FixedMul(worldbottom, rw_scale); if (backsector) { worldhigh >>= 4; worldlow >>= 4; if (worldhigh < worldtop) { pixhigh = (centeryfrac >> 4) - FixedMul(worldhigh, rw_scale); pixhighstep = -FixedMul(rw_scalestep, worldhigh); } if (worldlow > worldbottom) { pixlow = (centeryfrac >> 4) - FixedMul(worldlow, rw_scale); pixlowstep = -FixedMul(rw_scalestep, worldlow); } } // // render it // if (markceiling) ceilingplane = R_CheckPlane(ceilingplane, rw_x, rw_stopx - 1); if (markfloor) floorplane = R_CheckPlane(floorplane, rw_x, rw_stopx - 1); R_RenderSegLoop(); // // save sprite clipping info // if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) { memcpy(lastopening, ceilingclip + start, 2 * (rw_stopx - start)); ds_p->sprtopclip = lastopening - start; lastopening += rw_stopx - start; } if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && !ds_p->sprbottomclip) { memcpy(lastopening, floorclip + start, 2 * (rw_stopx - start)); ds_p->sprbottomclip = lastopening - start; lastopening += rw_stopx - start; } if (maskedtexture && !(ds_p->silhouette & SIL_TOP)) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT_MIN; } if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) { ds_p->silhouette |= SIL_BOTTOM; ds_p->bsilheight = INT_MAX; } ds_p++; } crispy-doom-crispy-doom-5.6.4/src/hexen/r_things.c000066400000000000000000000636331360717211000221260ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include "h2def.h" #include "i_system.h" #include "i_swap.h" #include "r_local.h" //void R_DrawTranslatedAltTLColumn(void); typedef struct { int x1, x2; int column; int topclip; int bottomclip; } maskdraw_t; /* Sprite rotation 0 is facing the viewer, rotation 1 is one angle turn CLOCKWISE around the axis. This is not the same as the angle, which increases counter clockwise (protractor). There was a lot of stuff grabbed wrong, so I changed it... */ fixed_t pspritescale, pspriteiscale; lighttable_t **spritelights; // constant arrays used for psprite clipping and initializing clipping short negonearray[MAXWIDTH]; short screenheightarray[MAXWIDTH]; boolean LevelUseFullBright; /* =============================================================================== INITIALIZATION FUNCTIONS =============================================================================== */ // variables used to look up and range check thing_t sprites patches spritedef_t *sprites; int numsprites; spriteframe_t sprtemp[30]; int maxframe; static const char *spritename; /* ================= = = R_InstallSpriteLump = = Local function for R_InitSprites ================= */ void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation, boolean flipped) { int r; if (frame >= 30 || rotation > 8) I_Error("R_InstallSpriteLump: Bad frame characters in lump %i", lump); if ((int) frame > maxframe) maxframe = frame; if (rotation == 0) { // the lump should be used for all rotations if (sprtemp[frame].rotate == false) I_Error("R_InitSprites: Sprite %s frame %c has multip rot=0 lump", spritename, 'A' + frame); if (sprtemp[frame].rotate == true) I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump", spritename, 'A' + frame); sprtemp[frame].rotate = false; for (r = 0; r < 8; r++) { sprtemp[frame].lump[r] = lump - firstspritelump; sprtemp[frame].flip[r] = (byte) flipped; } return; } // the lump is only used for one rotation if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump", spritename, 'A' + frame); sprtemp[frame].rotate = true; rotation--; // make 0 based if (sprtemp[frame].lump[rotation] != -1) I_Error ("R_InitSprites: Sprite %s : %c : %c has two lumps mapped to it", spritename, 'A' + frame, '1' + rotation); sprtemp[frame].lump[rotation] = lump - firstspritelump; sprtemp[frame].flip[rotation] = (byte) flipped; } /* ================= = = R_InitSpriteDefs = = Pass a null terminated list of sprite names (4 chars exactly) to be used = Builds the sprite rotation matrixes to account for horizontally flipped = sprites. Will report an error if the lumps are inconsistant =Only called at startup = = Sprite lump names are 4 characters for the actor, a letter for the frame, = and a number for the rotation, A sprite that is flippable will have an = additional letter/number appended. The rotation character can be 0 to = signify no rotations ================= */ void R_InitSpriteDefs(const char **namelist) { const char **check; int i, l, frame, rotation; int start, end; // count the number of sprite names check = namelist; while (*check != NULL) check++; numsprites = check - namelist; if (!numsprites) return; sprites = Z_Malloc(numsprites * sizeof(*sprites), PU_STATIC, NULL); start = firstspritelump - 1; end = lastspritelump + 1; // scan all the lump names for each of the names, noting the highest // frame letter // Just compare 4 characters as ints for (i = 0; i < numsprites; i++) { spritename = namelist[i]; memset(sprtemp, -1, sizeof(sprtemp)); maxframe = -1; // // scan the lumps, filling in the frames for whatever is found // for (l = start + 1; l < end; l++) if (!strncmp(lumpinfo[l]->name, namelist[i], 4)) { frame = lumpinfo[l]->name[4] - 'A'; rotation = lumpinfo[l]->name[5] - '0'; R_InstallSpriteLump(l, frame, rotation, false); if (lumpinfo[l]->name[6]) { frame = lumpinfo[l]->name[6] - 'A'; rotation = lumpinfo[l]->name[7] - '0'; R_InstallSpriteLump(l, frame, rotation, true); } } // // check the frames that were found for completeness // if (maxframe == -1) { //continue; sprites[i].numframes = 0; if (gamemode == shareware) continue; I_Error("R_InitSprites: No lumps found for sprite %s", namelist[i]); } maxframe++; for (frame = 0; frame < maxframe; frame++) { switch ((int) sprtemp[frame].rotate) { case -1: // no rotations were found for that frame at all I_Error("R_InitSprites: No patches found for %s frame %c", namelist[i], frame + 'A'); case 0: // only the first rotation is needed break; case 1: // must have all 8 frames for (rotation = 0; rotation < 8; rotation++) if (sprtemp[frame].lump[rotation] == -1) I_Error ("R_InitSprites: Sprite %s frame %c is missing rotations", namelist[i], frame + 'A'); } } // // allocate space for the frames present and copy sprtemp to it // sprites[i].numframes = maxframe; sprites[i].spriteframes = Z_Malloc(maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); memcpy(sprites[i].spriteframes, sprtemp, maxframe * sizeof(spriteframe_t)); } } /* =============================================================================== GAME FUNCTIONS =============================================================================== */ vissprite_t vissprites[MAXVISSPRITES], *vissprite_p; int newvissprite; /* =================== = = R_InitSprites = = Called at program start =================== */ void R_InitSprites(const char **namelist) { int i; for (i = 0; i < SCREENWIDTH; i++) { negonearray[i] = -1; } R_InitSpriteDefs(namelist); } /* =================== = = R_ClearSprites = = Called at frame start =================== */ void R_ClearSprites(void) { vissprite_p = vissprites; } /* =================== = = R_NewVisSprite = =================== */ vissprite_t overflowsprite; vissprite_t *R_NewVisSprite(void) { if (vissprite_p == &vissprites[MAXVISSPRITES]) return &overflowsprite; vissprite_p++; return vissprite_p - 1; } /* ================ = = R_DrawMaskedColumn = = Used for sprites and masked mid textures ================ */ short *mfloorclip; short *mceilingclip; fixed_t spryscale; fixed_t sprtopscreen; fixed_t sprbotscreen; void R_DrawMaskedColumn(column_t * column, signed int baseclip) { int topscreen, bottomscreen; fixed_t basetexturemid; basetexturemid = dc_texturemid; for (; column->topdelta != 0xff;) { // calculate unclipped screen coordinates for post topscreen = sprtopscreen + spryscale * column->topdelta; bottomscreen = topscreen + spryscale * column->length; dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS; dc_yh = (bottomscreen - 1) >> FRACBITS; if (dc_yh >= mfloorclip[dc_x]) dc_yh = mfloorclip[dc_x] - 1; if (dc_yl <= mceilingclip[dc_x]) dc_yl = mceilingclip[dc_x] + 1; if (dc_yh >= baseclip && baseclip != -1) dc_yh = baseclip; if (dc_yl <= dc_yh) { dc_source = (byte *) column + 3; dc_texturemid = basetexturemid - (column->topdelta << FRACBITS); // dc_source = (byte *)column + 3 - column->topdelta; colfunc(); // either R_DrawColumn or R_DrawTLColumn } column = (column_t *) ((byte *) column + column->length + 4); } dc_texturemid = basetexturemid; } /* ================ = = R_DrawVisSprite = = mfloorclip and mceilingclip should also be set ================ */ void R_DrawVisSprite(vissprite_t * vis, int x1, int x2) { column_t *column; int texturecolumn; fixed_t frac; patch_t *patch; fixed_t baseclip; patch = W_CacheLumpNum(vis->patch + firstspritelump, PU_CACHE); dc_colormap = vis->colormap; // if(!dc_colormap) // colfunc = tlcolfunc; // NULL colormap = shadow draw if (vis->mobjflags & (MF_SHADOW | MF_ALTSHADOW)) { if (vis->mobjflags & MF_TRANSLATION) { colfunc = R_DrawTranslatedTLColumn; dc_translation = translationtables - 256 + vis->class * ((maxplayers - 1) * 256) + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); } else if (vis->mobjflags & MF_SHADOW) { // Draw using shadow column function colfunc = tlcolfunc; } else { colfunc = R_DrawAltTLColumn; } } else if (vis->mobjflags & MF_TRANSLATION) { // Draw using translated column function colfunc = R_DrawTranslatedColumn; dc_translation = translationtables - 256 + vis->class * ((maxplayers - 1) * 256) + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); } dc_iscale = abs(vis->xiscale) >> detailshift; dc_texturemid = vis->texturemid; frac = vis->startfrac; spryscale = vis->scale; sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); // check to see if vissprite is a weapon if (vis->psprite) { dc_texturemid += FixedMul(((centery - viewheight / 2) << FRACBITS), vis->xiscale); sprtopscreen += (viewheight / 2 - centery) << FRACBITS; } if (vis->floorclip && !vis->psprite) { sprbotscreen = sprtopscreen + FixedMul(SHORT(patch->height) << FRACBITS, spryscale); baseclip = (sprbotscreen - FixedMul(vis->floorclip, spryscale)) >> FRACBITS; } else { baseclip = -1; } for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) { texturecolumn = frac >> FRACBITS; #ifdef RANGECHECK if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) I_Error("R_DrawSpriteRange: bad texturecolumn"); #endif column = (column_t *) ((byte *) patch + LONG(patch->columnofs[texturecolumn])); R_DrawMaskedColumn(column, baseclip); } colfunc = basecolfunc; } /* =================== = = R_ProjectSprite = = Generates a vissprite for a thing if it might be visible = =================== */ void R_ProjectSprite(mobj_t * thing) { fixed_t trx, try; fixed_t gxt, gyt; fixed_t tx, tz; fixed_t xscale; int x1, x2; spritedef_t *sprdef; spriteframe_t *sprframe; int lump; unsigned rot; boolean flip; int index; vissprite_t *vis; angle_t ang; fixed_t iscale; if (thing->flags2 & MF2_DONTDRAW) { // Never make a vissprite when MF2_DONTDRAW is flagged. return; } // // transform the origin point // trx = thing->x - viewx; try = thing->y - viewy; gxt = FixedMul(trx, viewcos); gyt = -FixedMul(try, viewsin); tz = gxt - gyt; if (tz < MINZ) return; // thing is behind view plane xscale = FixedDiv(projection, tz); gxt = -FixedMul(trx, viewsin); gyt = FixedMul(try, viewcos); tx = -(gyt + gxt); if (abs(tx) > (tz << 2)) return; // too far off the side // // decide which patch to use for sprite reletive to player // #ifdef RANGECHECK if ((unsigned) thing->sprite >= 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 sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; if (sprframe->rotate) { // choose a different rotation based on player view ang = R_PointToAngle(thing->x, thing->y); rot = (ang - thing->angle + (unsigned) (ANG45 / 2) * 9) >> 29; lump = sprframe->lump[rot]; flip = (boolean) sprframe->flip[rot]; } else { // use single rotation for all views lump = sprframe->lump[0]; flip = (boolean) sprframe->flip[0]; } // // calculate edges of the shape // tx -= spriteoffset[lump]; x1 = (centerxfrac + FixedMul(tx, xscale)) >> FRACBITS; if (x1 > viewwidth) return; // off the right side tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul(tx, xscale)) >> FRACBITS) - 1; if (x2 < 0) return; // off the left side // // store information in a vissprite // vis = R_NewVisSprite(); vis->mobjflags = thing->flags; vis->psprite = false; vis->scale = xscale << detailshift; vis->gx = thing->x; vis->gy = thing->y; vis->gz = thing->z; vis->gzt = thing->z + spritetopoffset[lump]; if (thing->flags & MF_TRANSLATION) { if (thing->player) { vis->class = thing->player->class; } else { vis->class = thing->special1.i; } if (vis->class > 2) { vis->class = 0; } } // foot clipping vis->floorclip = thing->floorclip; vis->texturemid = vis->gzt - viewz - vis->floorclip; vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; iscale = FixedDiv(FRACUNIT, xscale); if (flip) { vis->startfrac = spritewidth[lump] - 1; vis->xiscale = -iscale; } else { vis->startfrac = 0; vis->xiscale = iscale; } if (vis->x1 > x1) vis->startfrac += vis->xiscale * (vis->x1 - x1); vis->patch = lump; // // get light level // // if (thing->flags & MF_SHADOW) // vis->colormap = NULL; // shadow draw // else ... if (fixedcolormap) vis->colormap = fixedcolormap; // fixed map else if (LevelUseFullBright && thing->frame & FF_FULLBRIGHT) vis->colormap = colormaps; // full bright else { // diminished light index = xscale >> (LIGHTSCALESHIFT - detailshift + crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; vis->colormap = spritelights[index]; } } /* ======================== = = R_AddSprites = ======================== */ void R_AddSprites(sector_t * sec) { mobj_t *thing; int lightnum; if (sec->validcount == validcount) return; // already added sec->validcount = validcount; lightnum = (sec->lightlevel >> LIGHTSEGSHIFT) + extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS - 1]; else spritelights = scalelight[lightnum]; for (thing = sec->thinglist; thing; thing = thing->snext) R_ProjectSprite(thing); } /* ======================== = = R_DrawPSprite = ======================== */ // Y-adjustment values for full screen (4 weapons) int PSpriteSY[NUMCLASSES][NUMWEAPONS] = { {0, -12 * FRACUNIT, -10 * FRACUNIT, 10 * FRACUNIT}, // Fighter {-8 * FRACUNIT, 10 * FRACUNIT, 10 * FRACUNIT, 0}, // Cleric {9 * FRACUNIT, 20 * FRACUNIT, 20 * FRACUNIT, 20 * FRACUNIT}, // Mage {10 * FRACUNIT, 10 * FRACUNIT, 10 * FRACUNIT, 10 * FRACUNIT} // Pig }; void R_DrawPSprite(pspdef_t * psp) { fixed_t tx; int x1, x2; spritedef_t *sprdef; spriteframe_t *sprframe; int lump; boolean flip; vissprite_t *vis, avis; int tempangle; // // decide which patch to use // #ifdef RANGECHECK if ((unsigned) psp->state->sprite >= 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 : %i ", psp->state->sprite, psp->state->frame); #endif sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; lump = sprframe->lump[0]; flip = (boolean) sprframe->flip[0]; // // calculate edges of the shape // tx = psp->sx - 160 * FRACUNIT; tx -= spriteoffset[lump]; if (viewangleoffset) { tempangle = ((centerxfrac / 1024) * (viewangleoffset >> ANGLETOFINESHIFT)); } else { tempangle = 0; } x1 = (centerxfrac + FixedMul(tx, pspritescale) + tempangle) >> FRACBITS; if (x1 > viewwidth) return; // off the right side tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul(tx, pspritescale) + tempangle) >> FRACBITS) - 1; if (x2 < 0) return; // off the left side // // store information in a vissprite // vis = &avis; vis->mobjflags = 0; vis->class = 0; vis->psprite = true; vis->texturemid = (BASEYCENTER << FRACBITS) /* + FRACUNIT / 2 */ - (psp->sy - spritetopoffset[lump]); if (viewheight == SCREENHEIGHT) { vis->texturemid -= PSpriteSY[viewplayer->class] [players[consoleplayer].readyweapon]; } vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; vis->scale = pspritescale << detailshift; if (flip) { vis->xiscale = -pspriteiscale; vis->startfrac = spritewidth[lump] - 1; } else { vis->xiscale = pspriteiscale; vis->startfrac = 0; } if (vis->x1 > x1) vis->startfrac += vis->xiscale * (vis->x1 - x1); vis->patch = lump; if (viewplayer->powers[pw_invulnerability] && viewplayer->class == PCLASS_CLERIC) { vis->colormap = spritelights[MAXLIGHTSCALE - 1]; if (viewplayer->powers[pw_invulnerability] > 4 * 32) { if (viewplayer->mo->flags2 & MF2_DONTDRAW) { // don't draw the psprite vis->mobjflags |= MF_SHADOW; } else if (viewplayer->mo->flags & MF_SHADOW) { vis->mobjflags |= MF_ALTSHADOW; } } else if (viewplayer->powers[pw_invulnerability] & 8) { vis->mobjflags |= MF_SHADOW; } } else if (fixedcolormap) { // Fixed color vis->colormap = fixedcolormap; } else if (psp->state->frame & FF_FULLBRIGHT) { // Full bright vis->colormap = colormaps; } else { // local light vis->colormap = spritelights[MAXLIGHTSCALE - 1]; } R_DrawVisSprite(vis, vis->x1, vis->x2); } /* ======================== = = R_DrawPlayerSprites = ======================== */ void R_DrawPlayerSprites(void) { int i, lightnum; pspdef_t *psp; // // get light level // lightnum = (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) + extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS - 1]; else spritelights = scalelight[lightnum]; // // clip to screen bounds // mfloorclip = screenheightarray; mceilingclip = negonearray; // // add all active psprites // for (i = 0, psp = viewplayer->psprites; i < NUMPSPRITES; i++, psp++) if (psp->state) R_DrawPSprite(psp); } /* ======================== = = R_SortVisSprites = ======================== */ vissprite_t vsprsortedhead; void R_SortVisSprites(void) { int i, count; vissprite_t *ds, *best; vissprite_t unsorted; fixed_t bestscale; count = vissprite_p - vissprites; unsorted.next = unsorted.prev = &unsorted; if (!count) return; for (ds = vissprites; ds < vissprite_p; ds++) { ds->next = ds + 1; ds->prev = ds - 1; } vissprites[0].prev = &unsorted; unsorted.next = &vissprites[0]; (vissprite_p - 1)->next = &unsorted; unsorted.prev = vissprite_p - 1; // // pull the vissprites out by scale // best = 0; // shut up the compiler warning vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; for (i = 0; i < count; i++) { bestscale = INT_MAX; for (ds = unsorted.next; ds != &unsorted; ds = ds->next) { if (ds->scale < bestscale) { bestscale = ds->scale; best = ds; } } best->next->prev = best->prev; best->prev->next = best->next; best->next = &vsprsortedhead; best->prev = vsprsortedhead.prev; vsprsortedhead.prev->next = best; vsprsortedhead.prev = best; } } /* ======================== = = R_DrawSprite = ======================== */ void R_DrawSprite(vissprite_t * spr) { drawseg_t *ds; short clipbot[MAXWIDTH], cliptop[MAXWIDTH]; int x, r1, r2; fixed_t scale, lowscale; int silhouette; for (x = spr->x1; x <= spr->x2; x++) clipbot[x] = cliptop[x] = -2; // // scan drawsegs from end to start for obscuring segs // the first drawseg that has a greater scale is the clip seg // for (ds = ds_p - 1; ds >= drawsegs; ds--) { // // determine if the drawseg obscures the sprite // if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || (!ds->silhouette && !ds->maskedtexturecol)) continue; // doesn't cover sprite r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; if (ds->scale1 > ds->scale2) { lowscale = ds->scale2; scale = ds->scale1; } else { lowscale = ds->scale1; scale = ds->scale2; } if (scale < spr->scale || (lowscale < spr->scale && !R_PointOnSegSide(spr->gx, spr->gy, ds->curline))) { if (ds->maskedtexturecol) // masked mid texture R_RenderMaskedSegRange(ds, r1, r2); continue; // seg is behind sprite } // // clip this piece of the sprite // silhouette = ds->silhouette; if (spr->gz >= ds->bsilheight) silhouette &= ~SIL_BOTTOM; if (spr->gzt <= ds->tsilheight) silhouette &= ~SIL_TOP; if (silhouette == 1) { // bottom sil for (x = r1; x <= r2; x++) if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; } else if (silhouette == 2) { // top sil for (x = r1; x <= r2; x++) if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } else if (silhouette == 3) { // both for (x = r1; x <= r2; x++) { if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } } } // // all clipping has been performed, so draw the sprite // // check for unclipped columns for (x = spr->x1; x <= spr->x2; x++) { if (clipbot[x] == -2) clipbot[x] = viewheight; if (cliptop[x] == -2) cliptop[x] = -1; } mfloorclip = clipbot; mceilingclip = cliptop; R_DrawVisSprite(spr, spr->x1, spr->x2); } /* ======================== = = R_DrawMasked = ======================== */ void R_DrawMasked(void) { vissprite_t *spr; drawseg_t *ds; R_SortVisSprites(); if (vissprite_p > vissprites) { // draw all vissprites back to front for (spr = vsprsortedhead.next; spr != &vsprsortedhead; spr = spr->next) R_DrawSprite(spr); } // // render any remaining masked mid textures // for (ds = ds_p - 1; ds >= drawsegs; ds--) if (ds->maskedtexturecol) R_RenderMaskedSegRange(ds, ds->x1, ds->x2); // // draw the psprites on top of everything // // Added for the sideviewing with an external device if (viewangleoffset <= 1024 << ANGLETOFINESHIFT || viewangleoffset >= -(1024 << ANGLETOFINESHIFT)) { // don't draw on side views R_DrawPlayerSprites(); } // if (!viewangleoffset) // don't draw on side views // R_DrawPlayerSprites (); } crispy-doom-crispy-doom-5.6.4/src/hexen/s_sound.c000066400000000000000000000600021360717211000217460ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "m_random.h" #include "i_cdmus.h" #include "i_sound.h" #include "i_system.h" #include "i_timer.h" #include "m_argv.h" #include "m_misc.h" #include "r_local.h" #include "p_local.h" // for P_AproxDistance #include "sounds.h" #include "s_sound.h" #define PRIORITY_MAX_ADJUST 10 #define DIST_ADJUST (MAX_SND_DIST/PRIORITY_MAX_ADJUST) #define DEFAULT_ARCHIVEPATH "o:\\sound\\archive\\" void S_ShutDown(void); // If true, CD music playback is enabled (snd_musicdevice == SNDDEVICE_CD // and CD initialization succeeded). boolean cdmusic; // Track number of a track to play explicitly chosen by the // player using cheats. A value of zero means no track chosen: static int cd_custom_track = 0; // Currently playing track: static int cd_current_track = 0; // Time (MS) at which the currently-playing CD track will finish playing // and should be looped. Zero if we are not currently playing a track: static int cd_track_end_time = 0; /* =============================================================================== MUSIC & SFX API =============================================================================== */ //static channel_t channel[MAX_CHANNELS]; //static int rs; //the current registered song. //int mus_song = -1; //int mus_lumpnum; //void *mus_sndptr; //byte *soundCurve; extern sfxinfo_t S_sfx[]; extern musicinfo_t S_music[]; static channel_t Channel[MAX_CHANNELS]; static void *RegisteredSong; //the current registered song. static boolean MusicPaused; static int Mus_Song = -1; static byte *Mus_SndPtr; static byte *SoundCurve; int snd_MaxVolume = 10; // maximum volume for sound int snd_MusicVolume = 10; // maximum volume for music int snd_Channels = 16; // int AmbChan; //========================================================================== // // S_Start // //========================================================================== void S_Start(void) { S_StopAllSound(); S_StartSong(gamemap, true); } //========================================================================== // // Returns true if we are playing a looping CD track and it is time to // restart it. // //========================================================================== static boolean ShouldRestartCDTrack(void) { return cd_track_end_time != 0 && I_GetTimeMS() > cd_track_end_time; } //========================================================================== // // Start playing a CD track. Returns true for success. // //========================================================================== static boolean StartCDTrack(int track, boolean loop) { // Already playing? If so, don't bother. if (track == cd_current_track && !ShouldRestartCDTrack()) { return true; } if (I_CDMusPlay(track)) { return false; } cd_current_track = track; if (loop) { cd_track_end_time = I_GetTimeMS() + 1000 * I_CDMusTrackLength(track); } else { cd_track_end_time = 0; } return true; } //========================================================================== // // S_StartSong // //========================================================================== void S_StartSong(int song, boolean loop) { char *songLump; int lumpnum; int length; int track; // If we're in CD music mode, play a CD track, instead: if (cdmusic) { // Default to the player-chosen track if (cd_custom_track != 0) { track = cd_custom_track; } else { track = P_GetMapCDTrack(gamemap); } StartCDTrack(track, loop); } else { if (song == Mus_Song) { // don't replay an old song return; } if (RegisteredSong) { I_StopSong(); I_UnRegisterSong(RegisteredSong); RegisteredSong = 0; } songLump = P_GetMapSongLump(song); if (!songLump) { return; } lumpnum = W_GetNumForName(songLump); Mus_SndPtr = W_CacheLumpNum(lumpnum, PU_STATIC); length = W_LumpLength(lumpnum); RegisteredSong = I_RegisterSong(Mus_SndPtr, length); I_PlaySong(RegisteredSong, loop); Mus_Song = song; W_ReleaseLumpNum(lumpnum); } } //========================================================================== // // Play a custom-chosen music track selected by the player. // // Returns true for success. // //========================================================================== boolean S_StartCustomCDTrack(int tracknum) { boolean result; result = StartCDTrack(tracknum, true); if (result) { cd_custom_track = tracknum; } return result; } //========================================================================== // // Get the currently-playing CD track; returns -1 if not playing. // //========================================================================== int S_GetCurrentCDTrack(void) { if (!cdmusic || cd_current_track == 0) { return -1; } return cd_current_track; } //========================================================================== // // S_StartSongName // //========================================================================== void S_StartSongName(const char *songLump, boolean loop) { int lumpnum; int cdTrack; int length; if (!songLump) { return; } if (cdmusic) { cdTrack = 0; if (!strcmp(songLump, "hexen")) { cdTrack = P_GetCDTitleTrack(); } else if (!strcmp(songLump, "hub")) { cdTrack = P_GetCDIntermissionTrack(); } else if (!strcmp(songLump, "hall")) { cdTrack = P_GetCDEnd1Track(); } else if (!strcmp(songLump, "orb")) { cdTrack = P_GetCDEnd2Track(); } else if (!strcmp(songLump, "chess") && cd_custom_track == 0) { cdTrack = P_GetCDEnd3Track(); } /* Uncomment this, if Kevin writes a specific song for startup else if(!strcmp(songLump, "start")) { cdTrack = P_GetCDStartTrack(); } */ if (cdTrack != 0) { cd_custom_track = 0; StartCDTrack(cdTrack, loop); } } else { if (RegisteredSong) { I_StopSong(); I_UnRegisterSong(RegisteredSong); RegisteredSong = NULL; } lumpnum = W_GetNumForName(songLump); Mus_SndPtr = W_CacheLumpNum(lumpnum, PU_MUSIC); length = W_LumpLength(lumpnum); RegisteredSong = I_RegisterSong(Mus_SndPtr, length); I_PlaySong(RegisteredSong, loop); W_ReleaseLumpNum(lumpnum); Mus_Song = -1; } } //========================================================================== // // S_GetSoundID // //========================================================================== int S_GetSoundID(char *name) { int i; for (i = 0; i < NUMSFX; i++) { if (!strcmp(S_sfx[i].tagname, name)) { return i; } } return 0; } //========================================================================== // // S_StartSound // //========================================================================== void S_StartSound(mobj_t * origin, int sound_id) { S_StartSoundAtVolume(origin, sound_id, 127); } static mobj_t *GetSoundListener(void) { static degenmobj_t dummy_listener; // If we are at the title screen, the console player doesn't have an // object yet, so return a pointer to a static dummy listener instead. if (players[displayplayer].mo != NULL) { return players[displayplayer].mo; } else { dummy_listener.x = 0; dummy_listener.y = 0; dummy_listener.z = 0; return (mobj_t *) &dummy_listener; } } //========================================================================== // // S_StartSoundAtVolume // //========================================================================== void S_StartSoundAtVolume(mobj_t * origin, int sound_id, int volume) { mobj_t *listener; int dist, vol; int i; int priority; int sep; int angle; int absx; int absy; static int sndcount = 0; int chan; if (sound_id == 0 || snd_MaxVolume == 0) return; listener = GetSoundListener(); if (origin == NULL) { origin = listener; } if (volume == 0) { return; } // calculate the distance before other stuff so that we can throw out // sounds that are beyond the hearing range. absx = abs(origin->x - listener->x); absy = abs(origin->y - listener->y); dist = absx + absy - (absx > absy ? absy >> 1 : absx >> 1); dist >>= FRACBITS; if (dist >= MAX_SND_DIST) { return; // sound is beyond the hearing range... } if (dist < 0) { dist = 0; } priority = S_sfx[sound_id].priority; priority *= (PRIORITY_MAX_ADJUST - (dist / DIST_ADJUST)); #if 0 // TODO if (!S_StopSoundID(sound_id, priority)) { return; // other sounds have greater priority } #endif for (i = 0; i < snd_Channels; i++) { if (origin->player) { i = snd_Channels; break; // let the player have more than one sound. } if (origin == Channel[i].mo) { // only allow other mobjs one sound S_StopSound(Channel[i].mo); break; } } if (i >= snd_Channels) { for (i = 0; i < snd_Channels; i++) { if (Channel[i].mo == NULL) { break; } } if (i >= snd_Channels) { // look for a lower priority sound to replace. sndcount++; if (sndcount >= snd_Channels) { sndcount = 0; } for (chan = 0; chan < snd_Channels; chan++) { i = (sndcount + chan) % snd_Channels; if (priority >= Channel[i].priority) { chan = -1; //denote that sound should be replaced. break; } } if (chan != -1) { return; //no free channels. } else //replace the lower priority sound. { if (Channel[i].handle) { if (I_SoundIsPlaying(Channel[i].handle)) { I_StopSound(Channel[i].handle); } if (S_sfx[Channel[i].sound_id].usefulness > 0) { S_sfx[Channel[i].sound_id].usefulness--; } } } } } Channel[i].mo = origin; vol = (SoundCurve[dist] * (snd_MaxVolume * 8) * volume) >> 14; if (origin == listener) { sep = 128; // vol = (volume*(snd_MaxVolume+1)*8)>>7; } else { angle = R_PointToAngle2(listener->x, listener->y, Channel[i].mo->x, Channel[i].mo->y); angle = (angle - viewangle) >> 24; sep = angle * 2 - 128; if (sep < 64) sep = -sep; if (sep > 192) sep = 512 - sep; // vol = SoundCurve[dist]; } // if the sfxinfo_t is marked as 'can be pitch shifted' if (S_sfx[sound_id].pitch) { Channel[i].pitch = (byte) (NORM_PITCH + (M_Random() & 7) - (M_Random() & 7)); } else { Channel[i].pitch = NORM_PITCH; } if (S_sfx[sound_id].lumpnum == 0) { S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]); } Channel[i].handle = I_StartSound(&S_sfx[sound_id], i, vol, sep, Channel[i].pitch); Channel[i].sound_id = sound_id; Channel[i].priority = priority; Channel[i].volume = volume; if (S_sfx[sound_id].usefulness < 0) { S_sfx[sound_id].usefulness = 1; } else { S_sfx[sound_id].usefulness++; } } //========================================================================== // // S_StopSoundID // //========================================================================== boolean S_StopSoundID(int sound_id, int priority) { int i; int lp; //least priority int found; if (S_sfx[sound_id].numchannels == -1) { return (true); } lp = -1; //denote the argument sound_id found = 0; for (i = 0; i < snd_Channels; i++) { if (Channel[i].sound_id == sound_id && Channel[i].mo) { found++; //found one. Now, should we replace it?? if (priority >= Channel[i].priority) { // if we're gonna kill one, then this'll be it lp = i; priority = Channel[i].priority; } } } if (found < S_sfx[sound_id].numchannels) { return (true); } else if (lp == -1) { return (false); // don't replace any sounds } if (Channel[lp].handle) { if (I_SoundIsPlaying(Channel[lp].handle)) { I_StopSound(Channel[lp].handle); } if (S_sfx[Channel[lp].sound_id].usefulness > 0) { S_sfx[Channel[lp].sound_id].usefulness--; } Channel[lp].mo = NULL; } return (true); } //========================================================================== // // S_StopSound // //========================================================================== void S_StopSound(mobj_t * origin) { int i; for (i = 0; i < snd_Channels; i++) { if (Channel[i].mo == origin) { I_StopSound(Channel[i].handle); if (S_sfx[Channel[i].sound_id].usefulness > 0) { S_sfx[Channel[i].sound_id].usefulness--; } Channel[i].handle = 0; Channel[i].mo = NULL; } } } //========================================================================== // // S_StopAllSound // //========================================================================== void S_StopAllSound(void) { int i; //stop all sounds for (i = 0; i < snd_Channels; i++) { if (Channel[i].handle) { S_StopSound(Channel[i].mo); } } memset(Channel, 0, 8 * sizeof(channel_t)); } //========================================================================== // // S_SoundLink // //========================================================================== void S_SoundLink(mobj_t * oldactor, mobj_t * newactor) { int i; for (i = 0; i < snd_Channels; i++) { if (Channel[i].mo == oldactor) Channel[i].mo = newactor; } } //========================================================================== // // S_PauseSound // //========================================================================== void S_PauseSound(void) { if (cdmusic) { I_CDMusStop(); } else { I_PauseSong(); } } //========================================================================== // // S_ResumeSound // //========================================================================== void S_ResumeSound(void) { if (cdmusic) { I_CDMusResume(); } else { I_ResumeSong(); } } //========================================================================== // // S_UpdateSounds // //========================================================================== void S_UpdateSounds(mobj_t * listener) { int i, dist, vol; int angle; int sep; int priority; int absx; int absy; I_UpdateSound(); // If we are looping a CD track, we need to check if it has // finished playing and needs to restart. if (cdmusic && ShouldRestartCDTrack()) { StartCDTrack(cd_current_track, true); } if (snd_MaxVolume == 0) { return; } // Update any Sequences SN_UpdateActiveSequences(); for (i = 0; i < snd_Channels; i++) { if (!Channel[i].handle || S_sfx[Channel[i].sound_id].usefulness == -1) { continue; } if (!I_SoundIsPlaying(Channel[i].handle)) { if (S_sfx[Channel[i].sound_id].usefulness > 0) { S_sfx[Channel[i].sound_id].usefulness--; } Channel[i].handle = 0; Channel[i].mo = NULL; Channel[i].sound_id = 0; } if (Channel[i].mo == NULL || Channel[i].sound_id == 0 || Channel[i].mo == listener || listener == NULL) { continue; } else { absx = abs(Channel[i].mo->x - listener->x); absy = abs(Channel[i].mo->y - listener->y); dist = absx + absy - (absx > absy ? absy >> 1 : absx >> 1); dist >>= FRACBITS; if (dist >= MAX_SND_DIST) { S_StopSound(Channel[i].mo); continue; } if (dist < 0) { dist = 0; } //vol = SoundCurve[dist]; vol = (SoundCurve[dist] * (snd_MaxVolume * 8) * Channel[i].volume) >> 14; if (Channel[i].mo == listener) { sep = 128; } else { angle = R_PointToAngle2(listener->x, listener->y, Channel[i].mo->x, Channel[i].mo->y); angle = (angle - viewangle) >> 24; sep = angle * 2 - 128; if (sep < 64) sep = -sep; if (sep > 192) sep = 512 - sep; } I_UpdateSoundParams(i, vol, sep); priority = S_sfx[Channel[i].sound_id].priority; priority *= PRIORITY_MAX_ADJUST - (dist / DIST_ADJUST); Channel[i].priority = priority; } } } //========================================================================== // // S_Init // //========================================================================== void S_Init(void) { I_SetOPLDriverVer(opl_doom2_1_666); SoundCurve = W_CacheLumpName("SNDCURVE", PU_STATIC); // SoundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL); if (snd_Channels > 8) { snd_Channels = 8; } I_SetMusicVolume(snd_MusicVolume * 8); I_AtExit(S_ShutDown, true); // Hexen defaults to pitch-shifting on if (snd_pitchshift == -1) { snd_pitchshift = 1; } I_PrecacheSounds(S_sfx, NUMSFX); // Attempt to setup CD music if (snd_musicdevice == SNDDEVICE_CD) { ST_Message(" Attempting to initialize CD Music: "); if (!cdrom) { cdmusic = (I_CDMusInit() != -1); } else { // The user is trying to use the cdrom for both game and music cdmusic = false; } if (cdmusic) { ST_Message("initialized.\n"); } else { ST_Message("failed.\n"); } I_CDMusPrintStartup(); } } //========================================================================== // // S_GetChannelInfo // //========================================================================== void S_GetChannelInfo(SoundInfo_t * s) { int i; ChanInfo_t *c; s->channelCount = snd_Channels; s->musicVolume = snd_MusicVolume; s->soundVolume = snd_MaxVolume; for (i = 0; i < snd_Channels; i++) { c = &s->chan[i]; c->id = Channel[i].sound_id; c->priority = Channel[i].priority; c->name = S_sfx[c->id].name; c->mo = Channel[i].mo; if (c->mo != NULL) { c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy) >> FRACBITS; } else { c->distance = 0; } } } //========================================================================== // // S_GetSoundPlayingInfo // //========================================================================== boolean S_GetSoundPlayingInfo(mobj_t * mobj, int sound_id) { int i; for (i = 0; i < snd_Channels; i++) { if (Channel[i].sound_id == sound_id && Channel[i].mo == mobj) { if (I_SoundIsPlaying(Channel[i].handle)) { return true; } } } return false; } //========================================================================== // // S_SetMusicVolume // //========================================================================== void S_SetMusicVolume(void) { if (cdmusic) { I_CDMusSetVolume(snd_MusicVolume * 16); // 0-255 } else { I_SetMusicVolume(snd_MusicVolume * 8); } if (snd_MusicVolume == 0) { if (!cdmusic) { I_PauseSong(); } MusicPaused = true; } else if (MusicPaused) { if (!cdmusic) { I_ResumeSong(); } MusicPaused = false; } } //========================================================================== // // S_ShutDown // //========================================================================== void S_ShutDown(void) { I_StopSong(); I_UnRegisterSong(RegisteredSong); I_ShutdownSound(); if (cdmusic) { I_CDMusStop(); } } //========================================================================== // // S_InitScript // //========================================================================== void S_InitScript(void) { int i; SC_OpenLump("sndinfo"); while (SC_GetString()) { if (*sc_String == '$') { if (!strcasecmp(sc_String, "$ARCHIVEPATH")) { SC_MustGetString(); } else if (!strcasecmp(sc_String, "$MAP")) { SC_MustGetNumber(); SC_MustGetString(); if (sc_Number) { P_PutMapSongLump(sc_Number, sc_String); } } continue; } else { for (i = 0; i < NUMSFX; i++) { if (!strcmp(S_sfx[i].tagname, sc_String)) { SC_MustGetString(); if (*sc_String != '?') { M_StringCopy(S_sfx[i].name, sc_String, sizeof(S_sfx[i].name)); } else { M_StringCopy(S_sfx[i].name, "default", sizeof(S_sfx[i].name)); } break; } } if (i == NUMSFX) { SC_MustGetString(); } } } SC_Close(); for (i = 0; i < NUMSFX; i++) { if (!strcmp(S_sfx[i].name, "")) { M_StringCopy(S_sfx[i].name, "default", sizeof(S_sfx[i].name)); } } } crispy-doom-crispy-doom-5.6.4/src/hexen/s_sound.h000066400000000000000000000045301360717211000217570ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __S_SOUND__ #define __S_SOUND__ /* typedef struct { char name[8]; int p1; } musicinfo_t; */ /* typedef struct sfxinfo_s { char tagName[32]; char lumpname[12]; // Only need 9 bytes, but padded out to be dword aligned //struct sfxinfo_s *link; // Make alias for another sound int priority; // Higher priority takes precendence int usefulness; // Determines when a sound should be cached out void *snd_ptr; int lumpnum; int numchannels; // total number of channels a sound type may occupy boolean changePitch; } sfxinfo_t; */ typedef struct { mobj_t *mo; int sound_id; int handle; int volume; int pitch; int priority; } channel_t; typedef struct { int id; unsigned short priority; char *name; mobj_t *mo; int distance; } ChanInfo_t; typedef struct { int channelCount; int musicVolume; int soundVolume; ChanInfo_t chan[8]; } SoundInfo_t; extern int snd_MaxVolume; extern int snd_MusicVolume; extern int snd_Channels; extern boolean cdmusic; void S_Start(void); void S_StartSound(mobj_t * origin, int sound_id); int S_GetSoundID(char *name); void S_StartSoundAtVolume(mobj_t * origin, int sound_id, int volume); void S_StopSound(mobj_t * origin); void S_StopAllSound(void); void S_PauseSound(void); void S_ResumeSound(void); void S_UpdateSounds(mobj_t * listener); void S_StartSong(int song, boolean loop); void S_StartSongName(const char *songLump, boolean loop); void S_Init(void); void S_GetChannelInfo(SoundInfo_t * s); void S_SetMusicVolume(void); boolean S_GetSoundPlayingInfo(mobj_t * mobj, int sound_id); boolean S_StartCustomCDTrack(int tracknum); int S_GetCurrentCDTrack(void); #endif crispy-doom-crispy-doom-5.6.4/src/hexen/sb_bar.c000066400000000000000000001574301360717211000215400ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "i_cdmus.h" #include "i_video.h" #include "m_bbox.h" #include "m_cheat.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" #include "v_video.h" #include "i_swap.h" // TYPES ------------------------------------------------------------------- typedef struct Cheat_s { void (*func) (player_t * player, struct Cheat_s * cheat); cheatseq_t *seq; } Cheat_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void DrawSoundInfo(void); static void DrINumber(signed int val, int x, int y); static void DrRedINumber(signed int val, int x, int y); static void DrBNumber(signed int val, int x, int y); static void DrawCommonBar(void); static void DrawMainBar(void); static void DrawInventoryBar(void); static void DrawKeyBar(void); static void DrawWeaponPieces(void); static void DrawFullScreenStuff(void); static void DrawAnimatedIcons(void); static boolean HandleCheats(byte key); static boolean CheatAddKey(Cheat_t * cheat, byte key, boolean * eat); static void CheatGodFunc(player_t * player, Cheat_t * cheat); static void CheatNoClipFunc(player_t * player, Cheat_t * cheat); static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat); static void CheatHealthFunc(player_t * player, Cheat_t * cheat); static void CheatKeysFunc(player_t * player, Cheat_t * cheat); static void CheatSoundFunc(player_t * player, Cheat_t * cheat); static void CheatTickerFunc(player_t * player, Cheat_t * cheat); static void CheatArtifactAllFunc(player_t * player, Cheat_t * cheat); static void CheatPuzzleFunc(player_t * player, Cheat_t * cheat); static void CheatWarpFunc(player_t * player, Cheat_t * cheat); static void CheatPigFunc(player_t * player, Cheat_t * cheat); static void CheatMassacreFunc(player_t * player, Cheat_t * cheat); static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat); static void CheatQuickenFunc1(player_t * player, Cheat_t * cheat); static void CheatQuickenFunc2(player_t * player, Cheat_t * cheat); static void CheatQuickenFunc3(player_t * player, Cheat_t * cheat); static void CheatClassFunc1(player_t * player, Cheat_t * cheat); static void CheatClassFunc2(player_t * player, Cheat_t * cheat); static void CheatInitFunc(player_t * player, Cheat_t * cheat); static void CheatVersionFunc(player_t * player, Cheat_t * cheat); static void CheatDebugFunc(player_t * player, Cheat_t * cheat); static void CheatScriptFunc1(player_t * player, Cheat_t * cheat); static void CheatScriptFunc2(player_t * player, Cheat_t * cheat); static void CheatScriptFunc3(player_t * player, Cheat_t * cheat); static void CheatRevealFunc(player_t * player, Cheat_t * cheat); static void CheatTrackFunc1(player_t * player, Cheat_t * cheat); static void CheatTrackFunc2(player_t * player, Cheat_t * cheat); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern int ArmorIncrement[NUMCLASSES][NUMARMOR]; extern int AutoArmorSave[NUMCLASSES]; // PUBLIC DATA DECLARATIONS ------------------------------------------------ boolean DebugSound; // Debug flag for displaying sound info boolean inventory; int curpos; int inv_ptr; int ArtifactFlash; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int DisplayTicker = 0; static int HealthMarker; //static int ChainWiggle; static player_t *CPlayer; static int SpinFlylump; static int SpinMinotaurLump; static int SpinSpeedLump; static int SpinDefenseLump; static int FontBNumBase; static int PlayPalette; static patch_t *PatchH2BAR; static patch_t *PatchH2TOP; static patch_t *PatchLFEDGE; static patch_t *PatchRTEDGE; static patch_t *PatchARMCLEAR; static patch_t *PatchARTICLEAR; static patch_t *PatchMANACLEAR; static patch_t *PatchKILLS; static patch_t *PatchMANAVIAL1; static patch_t *PatchMANAVIAL2; static patch_t *PatchMANAVIALDIM1; static patch_t *PatchMANAVIALDIM2; static patch_t *PatchMANADIM1; static patch_t *PatchMANADIM2; static patch_t *PatchMANABRIGHT1; static patch_t *PatchMANABRIGHT2; static patch_t *PatchCHAIN; static patch_t *PatchSTATBAR; static patch_t *PatchKEYBAR; static patch_t *PatchLIFEGEM; static patch_t *PatchSELECTBOX; static patch_t *PatchINumbers[10]; static patch_t *PatchNEGATIVE; static patch_t *PatchSmNumbers[10]; static patch_t *PatchINVBAR; static patch_t *PatchWEAPONSLOT; static patch_t *PatchWEAPONFULL; static patch_t *PatchPIECE1; static patch_t *PatchPIECE2; static patch_t *PatchPIECE3; static patch_t *PatchINVLFGEM1; static patch_t *PatchINVLFGEM2; static patch_t *PatchINVRTGEM1; static patch_t *PatchINVRTGEM2; // Toggle god mode cheatseq_t CheatGodSeq = CHEAT("satan", 0); // Toggle no clipping mode cheatseq_t CheatNoClipSeq = CHEAT("casper", 0); // Get all weapons and mana cheatseq_t CheatWeaponsSeq = CHEAT("nra", 0); // Get full health cheatseq_t CheatHealthSeq = CHEAT("clubmed", 0); // Get all keys cheatseq_t CheatKeysSeq = CHEAT("locksmith", 0); // Toggle sound debug info cheatseq_t CheatSoundSeq = CHEAT("noise", 0); // Toggle ticker cheatseq_t CheatTickerSeq = CHEAT("ticker", 0); // Get all artifacts cheatseq_t CheatArtifactAllSeq = CHEAT("indiana", 0); // Get all puzzle pieces cheatseq_t CheatPuzzleSeq = CHEAT("sherlock", 0); // Warp to new level cheatseq_t CheatWarpSeq = CHEAT("visit", 2); // Become a pig cheatseq_t CheatPigSeq = CHEAT("deliverance", 0); // Kill all monsters cheatseq_t CheatMassacreSeq = CHEAT("butcher", 0); cheatseq_t CheatIDKFASeq = CHEAT("conan", 0); cheatseq_t CheatQuickenSeq1 = CHEAT("martek", 0); cheatseq_t CheatQuickenSeq2 = CHEAT("martekmartek", 0); cheatseq_t CheatQuickenSeq3 = CHEAT("martekmartekmartek", 0); // New class cheatseq_t CheatClass1Seq = CHEAT("shadowcaster", 0); cheatseq_t CheatClass2Seq = CHEAT("shadowcaster", 1); cheatseq_t CheatInitSeq = CHEAT("init", 0); cheatseq_t CheatVersionSeq = CHEAT("mrjones", 0); cheatseq_t CheatDebugSeq = CHEAT("where", 0); cheatseq_t CheatScriptSeq1 = CHEAT("puke", 0); cheatseq_t CheatScriptSeq2 = CHEAT("puke", 1); cheatseq_t CheatScriptSeq3 = CHEAT("puke", 2); cheatseq_t CheatRevealSeq = CHEAT("mapsco", 0); cheatseq_t CheatTrackSeq1 = CHEAT("`", 0); cheatseq_t CheatTrackSeq2 = CHEAT("`", 2); static Cheat_t Cheats[] = { {CheatTrackFunc1, &CheatTrackSeq1}, {CheatTrackFunc2, &CheatTrackSeq2}, {CheatGodFunc, &CheatGodSeq}, {CheatNoClipFunc, &CheatNoClipSeq}, {CheatWeaponsFunc, &CheatWeaponsSeq}, {CheatHealthFunc, &CheatHealthSeq}, {CheatKeysFunc, &CheatKeysSeq}, {CheatSoundFunc, &CheatSoundSeq}, {CheatTickerFunc, &CheatTickerSeq}, {CheatArtifactAllFunc, &CheatArtifactAllSeq}, {CheatPuzzleFunc, &CheatPuzzleSeq}, {CheatWarpFunc, &CheatWarpSeq}, {CheatPigFunc, &CheatPigSeq}, {CheatMassacreFunc, &CheatMassacreSeq}, {CheatIDKFAFunc, &CheatIDKFASeq}, {CheatQuickenFunc1, &CheatQuickenSeq1}, {CheatQuickenFunc2, &CheatQuickenSeq2}, {CheatQuickenFunc3, &CheatQuickenSeq3}, {CheatClassFunc1, &CheatClass1Seq}, {CheatClassFunc2, &CheatClass2Seq}, {CheatInitFunc, &CheatInitSeq}, {CheatVersionFunc, &CheatVersionSeq}, {CheatDebugFunc, &CheatDebugSeq}, {CheatScriptFunc1, &CheatScriptSeq1}, {CheatScriptFunc2, &CheatScriptSeq2}, {CheatScriptFunc3, &CheatScriptSeq3}, {CheatRevealFunc, &CheatRevealSeq}, }; #define SET_CHEAT(cheat, seq) \ { memcpy(cheat.sequence, seq, sizeof(seq)); \ cheat.sequence_len = sizeof(seq) - 1; } // CODE -------------------------------------------------------------------- //========================================================================== // // SB_Init // //========================================================================== void SB_Init(void) { int i; int startLump; PatchH2BAR = W_CacheLumpName("H2BAR", PU_STATIC); PatchH2TOP = W_CacheLumpName("H2TOP", PU_STATIC); PatchINVBAR = W_CacheLumpName("INVBAR", PU_STATIC); PatchLFEDGE = W_CacheLumpName("LFEDGE", PU_STATIC); PatchRTEDGE = W_CacheLumpName("RTEDGE", PU_STATIC); PatchSTATBAR = W_CacheLumpName("STATBAR", PU_STATIC); PatchKEYBAR = W_CacheLumpName("KEYBAR", PU_STATIC); PatchSELECTBOX = W_CacheLumpName("SELECTBOX", PU_STATIC); PatchARTICLEAR = W_CacheLumpName("ARTICLS", PU_STATIC); PatchARMCLEAR = W_CacheLumpName("ARMCLS", PU_STATIC); PatchMANACLEAR = W_CacheLumpName("MANACLS", PU_STATIC); PatchMANAVIAL1 = W_CacheLumpName("MANAVL1", PU_STATIC); PatchMANAVIAL2 = W_CacheLumpName("MANAVL2", PU_STATIC); PatchMANAVIALDIM1 = W_CacheLumpName("MANAVL1D", PU_STATIC); PatchMANAVIALDIM2 = W_CacheLumpName("MANAVL2D", PU_STATIC); PatchMANADIM1 = W_CacheLumpName("MANADIM1", PU_STATIC); PatchMANADIM2 = W_CacheLumpName("MANADIM2", PU_STATIC); PatchMANABRIGHT1 = W_CacheLumpName("MANABRT1", PU_STATIC); PatchMANABRIGHT2 = W_CacheLumpName("MANABRT2", PU_STATIC); PatchINVLFGEM1 = W_CacheLumpName("invgeml1", PU_STATIC); PatchINVLFGEM2 = W_CacheLumpName("invgeml2", PU_STATIC); PatchINVRTGEM1 = W_CacheLumpName("invgemr1", PU_STATIC); PatchINVRTGEM2 = W_CacheLumpName("invgemr2", PU_STATIC); // PatchCHAINBACK = W_CacheLumpName("CHAINBACK", PU_STATIC); startLump = W_GetNumForName("IN0"); for (i = 0; i < 10; i++) { PatchINumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC); } PatchNEGATIVE = W_CacheLumpName("NEGNUM", PU_STATIC); FontBNumBase = W_GetNumForName("FONTB16"); startLump = W_GetNumForName("SMALLIN0"); for (i = 0; i < 10; i++) { PatchSmNumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC); } PlayPalette = W_GetNumForName("PLAYPAL"); SpinFlylump = W_GetNumForName("SPFLY0"); SpinMinotaurLump = W_GetNumForName("SPMINO0"); SpinSpeedLump = W_GetNumForName("SPBOOT0"); SpinDefenseLump = W_GetNumForName("SPSHLD0"); if (deathmatch) { PatchKILLS = W_CacheLumpName("KILLS", PU_STATIC); } SB_SetClassData(); if (gamemode == shareware) { SET_CHEAT(CheatGodSeq, "bgokey"); SET_CHEAT(CheatNoClipSeq, "rjohnson"); SET_CHEAT(CheatWeaponsSeq, "crhinehart"); SET_CHEAT(CheatHealthSeq,"sgurno"); SET_CHEAT(CheatKeysSeq, "mraymondjudy"); SET_CHEAT(CheatSoundSeq, "kschilder"); SET_CHEAT(CheatTickerSeq, "rrettenmund"); SET_CHEAT(CheatArtifactAllSeq, "braffel"); SET_CHEAT(CheatPuzzleSeq, "tmoore"); SET_CHEAT(CheatWarpSeq, "bpelletier"); SET_CHEAT(CheatPigSeq, "ebiessman"); SET_CHEAT(CheatMassacreSeq, "cstika"); SET_CHEAT(CheatIDKFASeq, "rambo"); SET_CHEAT(CheatQuickenSeq1, "quicken"); SET_CHEAT(CheatQuickenSeq2, "quickenquicken"); SET_CHEAT(CheatQuickenSeq3, "quickenquickenquicken"); SET_CHEAT(CheatClass1Seq, "plipo"); SET_CHEAT(CheatClass2Seq, "plipo"); SET_CHEAT(CheatVersionSeq, "pmacarther"); SET_CHEAT(CheatDebugSeq, "jsumwalt"); SET_CHEAT(CheatScriptSeq1, "mwagabaza"); SET_CHEAT(CheatScriptSeq2, "mwagabaza"); SET_CHEAT(CheatScriptSeq3, "mwagabaza"); SET_CHEAT(CheatRevealSeq, "reveal"); } } //========================================================================== // // SB_SetClassData // //========================================================================== void SB_SetClassData(void) { int class; class = PlayerClass[consoleplayer]; // original player class (not pig) PatchWEAPONSLOT = W_CacheLumpNum(W_GetNumForName("wpslot0") + class, PU_STATIC); PatchWEAPONFULL = W_CacheLumpNum(W_GetNumForName("wpfull0") + class, PU_STATIC); PatchPIECE1 = W_CacheLumpNum(W_GetNumForName("wpiecef1") + class, PU_STATIC); PatchPIECE2 = W_CacheLumpNum(W_GetNumForName("wpiecef2") + class, PU_STATIC); PatchPIECE3 = W_CacheLumpNum(W_GetNumForName("wpiecef3") + class, PU_STATIC); PatchCHAIN = W_CacheLumpNum(W_GetNumForName("chain") + class, PU_STATIC); if (!netgame) { // single player game uses red life gem (the second gem) PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName("lifegem") + maxplayers * class + 1, PU_STATIC); } else { PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName("lifegem") + maxplayers * class + consoleplayer, PU_STATIC); } SB_state = -1; UpdateState |= I_FULLSCRN; } //========================================================================== // // SB_Ticker // //========================================================================== void SB_Ticker(void) { int delta; int curHealth; curHealth = players[consoleplayer].mo->health; if (curHealth < 0) { curHealth = 0; } if (curHealth < HealthMarker) { delta = (HealthMarker - curHealth) >> 2; if (delta < 1) { delta = 1; } else if (delta > 6) { delta = 6; } HealthMarker -= delta; } else if (curHealth > HealthMarker) { delta = (curHealth - HealthMarker) >> 2; if (delta < 1) { delta = 1; } else if (delta > 6) { delta = 6; } HealthMarker += delta; } } //========================================================================== // // DrINumber // // Draws a three digit number. // //========================================================================== static void DrINumber(signed int val, int x, int y) { patch_t *patch; int oldval; oldval = val; if (val < 0) { val = -val; if (val > 99) { val = 99; } if (val > 9) { patch = PatchINumbers[val / 10]; V_DrawPatch(x + 8, y, patch); V_DrawPatch(x, y, PatchNEGATIVE); } else { V_DrawPatch(x + 8, y, PatchNEGATIVE); } val = val % 10; patch = PatchINumbers[val]; V_DrawPatch(x + 16, y, patch); return; } if (val > 99) { patch = PatchINumbers[val / 100]; V_DrawPatch(x, y, patch); } val = val % 100; if (val > 9 || oldval > 99) { patch = PatchINumbers[val / 10]; V_DrawPatch(x + 8, y, patch); } val = val % 10; patch = PatchINumbers[val]; V_DrawPatch(x + 16, y, patch); } //========================================================================== // // DrRedINumber // // Draws a three digit number using the red font // //========================================================================== static void DrRedINumber(signed int val, int x, int y) { patch_t *patch; int oldval; oldval = val; if (val < 0) { val = 0; } if (val > 99) { patch = W_CacheLumpNum(W_GetNumForName("inred0") + val / 100, PU_CACHE); V_DrawPatch(x, y, patch); } val = val % 100; if (val > 9 || oldval > 99) { patch = W_CacheLumpNum(W_GetNumForName("inred0") + val / 10, PU_CACHE); V_DrawPatch(x + 8, y, patch); } val = val % 10; patch = W_CacheLumpNum(W_GetNumForName("inred0") + val, PU_CACHE); V_DrawPatch(x + 16, y, patch); } //========================================================================== // // DrBNumber // // Draws a three digit number using FontB // //========================================================================== static void DrBNumber(signed int val, int x, int y) { patch_t *patch; int xpos; int oldval; oldval = val; xpos = x; if (val < 0) { val = 0; } if (val > 99) { patch = W_CacheLumpNum(FontBNumBase + val / 100, PU_CACHE); V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } val = val % 100; xpos += 12; if (val > 9 || oldval > 99) { patch = W_CacheLumpNum(FontBNumBase + val / 10, PU_CACHE); V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } val = val % 10; xpos += 12; patch = W_CacheLumpNum(FontBNumBase + val, PU_CACHE); V_DrawShadowedPatch(xpos + 6 - SHORT(patch->width) / 2, y, patch); } //========================================================================== // // DrSmallNumber // // Draws a small two digit number. // //========================================================================== static void DrSmallNumber(int val, int x, int y) { patch_t *patch; if (val <= 0) { return; } if (val > 999) { val %= 1000; } if (val > 99) { patch = PatchSmNumbers[val / 100]; V_DrawPatch(x, y, patch); patch = PatchSmNumbers[(val % 100) / 10]; V_DrawPatch(x + 4, y, patch); } else if (val > 9) { patch = PatchSmNumbers[val / 10]; V_DrawPatch(x + 4, y, patch); } val %= 10; patch = PatchSmNumbers[val]; V_DrawPatch(x + 8, y, patch); } /* //========================================================================== // // ShadeLine // //========================================================================== static void ShadeLine(int x, int y, int height, int shade) { byte *dest; byte *shades; shades = colormaps+9*256+shade*2*256; dest = I_VideoBuffer+y*SCREENWIDTH+x; while(height--) { *(dest) = *(shades+*dest); dest += SCREENWIDTH; } } //========================================================================== // // ShadeChain // //========================================================================== static void ShadeChain(void) { int i; for(i = 0; i < 16; i++) { ShadeLine(277+i, 190, 10, i/2); ShadeLine(19+i, 190, 10, 7-(i/2)); } } */ //========================================================================== // // DrawSoundInfo // // Displays sound debugging information. // //========================================================================== static void DrawSoundInfo(void) { int i; SoundInfo_t s; ChanInfo_t *c; char text[32]; int x; int y; int xPos[7] = { 1, 75, 112, 156, 200, 230, 260 }; if (leveltime & 16) { MN_DrTextA("*** SOUND DEBUG INFO ***", xPos[0], 20); } S_GetChannelInfo(&s); if (s.channelCount == 0) { return; } x = 0; MN_DrTextA("NAME", xPos[x++], 30); MN_DrTextA("MO.T", xPos[x++], 30); MN_DrTextA("MO.X", xPos[x++], 30); MN_DrTextA("MO.Y", xPos[x++], 30); MN_DrTextA("ID", xPos[x++], 30); MN_DrTextA("PRI", xPos[x++], 30); MN_DrTextA("DIST", xPos[x++], 30); for (i = 0; i < s.channelCount; i++) { c = &s.chan[i]; x = 0; y = 40 + i * 10; if (c->mo == NULL) { // Channel is unused MN_DrTextA("------", xPos[0], y); continue; } M_snprintf(text, sizeof(text), "%s", c->name); M_ForceUppercase(text); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->mo->type); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->mo->x >> FRACBITS); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->mo->y >> FRACBITS); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", (int) c->id); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->priority); MN_DrTextA(text, xPos[x++], y); M_snprintf(text, sizeof(text), "%d", c->distance); MN_DrTextA(text, xPos[x++], y); } UpdateState |= I_FULLSCRN; BorderNeedRefresh = true; } //========================================================================== // // SB_Drawer // //========================================================================== char patcharti[][10] = { {"ARTIBOX"}, // none {"ARTIINVU"}, // invulnerability {"ARTIPTN2"}, // health {"ARTISPHL"}, // superhealth {"ARTIHRAD"}, // healing radius {"ARTISUMN"}, // summon maulator {"ARTITRCH"}, // torch {"ARTIPORK"}, // egg {"ARTISOAR"}, // fly {"ARTIBLST"}, // blast radius {"ARTIPSBG"}, // poison bag {"ARTITELO"}, // teleport other {"ARTISPED"}, // speed {"ARTIBMAN"}, // boost mana {"ARTIBRAC"}, // boost armor {"ARTIATLP"}, // teleport {"ARTISKLL"}, // arti_puzzskull {"ARTIBGEM"}, // arti_puzzgembig {"ARTIGEMR"}, // arti_puzzgemred {"ARTIGEMG"}, // arti_puzzgemgreen1 {"ARTIGMG2"}, // arti_puzzgemgreen2 {"ARTIGEMB"}, // arti_puzzgemblue1 {"ARTIGMB2"}, // arti_puzzgemblue2 {"ARTIBOK1"}, // arti_puzzbook1 {"ARTIBOK2"}, // arti_puzzbook2 {"ARTISKL2"}, // arti_puzzskull2 {"ARTIFWEP"}, // arti_puzzfweapon {"ARTICWEP"}, // arti_puzzcweapon {"ARTIMWEP"}, // arti_puzzmweapon {"ARTIGEAR"}, // arti_puzzgear1 {"ARTIGER2"}, // arti_puzzgear2 {"ARTIGER3"}, // arti_puzzgear3 {"ARTIGER4"}, // arti_puzzgear4 }; int SB_state = -1; static int oldarti = 0; static int oldartiCount = 0; static int oldfrags = -9999; static int oldmana1 = -1; static int oldmana2 = -1; static int oldarmor = -1; static int oldhealth = -1; static int oldlife = -1; static int oldpieces = -1; static int oldweapon = -1; static int oldkeys = -1; extern boolean automapactive; void SB_Drawer(void) { // Sound info debug stuff if (DebugSound == true) { DrawSoundInfo(); } CPlayer = &players[consoleplayer]; if (viewheight == SCREENHEIGHT && !automapactive) { DrawFullScreenStuff(); SB_state = -1; } else { if (SB_state == -1) { V_DrawPatch(0, 134, PatchH2BAR); oldhealth = -1; } DrawCommonBar(); if (!inventory) { if (SB_state != 0) { // Main interface if (!automapactive) { V_DrawPatch(38, 162, PatchSTATBAR); } else { V_DrawPatch(38, 162, PatchKEYBAR); } oldarti = 0; oldmana1 = -1; oldmana2 = -1; oldarmor = -1; oldpieces = -1; oldfrags = -9999; //can't use -1, 'cuz of negative frags oldlife = -1; oldweapon = -1; oldkeys = -1; } if (!automapactive) { DrawMainBar(); } else { DrawKeyBar(); } SB_state = 0; } else { DrawInventoryBar(); SB_state = 1; } } SB_PaletteFlash(false); DrawAnimatedIcons(); } //========================================================================== // // DrawAnimatedIcons // //========================================================================== static void DrawAnimatedIcons(void) { int frame; static boolean hitCenterFrame; // Wings of wrath if (CPlayer->powers[pw_flight]) { if (CPlayer->powers[pw_flight] > BLINKTHRESHOLD || !(CPlayer->powers[pw_flight] & 16)) { frame = (leveltime / 3) & 15; if (CPlayer->mo->flags2 & MF2_FLY) { if (hitCenterFrame && (frame != 15 && frame != 0)) { V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump + 15, PU_CACHE)); } else { V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump + frame, PU_CACHE)); hitCenterFrame = false; } } else { if (!hitCenterFrame && (frame != 15 && frame != 0)) { V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump + frame, PU_CACHE)); hitCenterFrame = false; } else { V_DrawPatch(20, 19, W_CacheLumpNum(SpinFlylump + 15, PU_CACHE)); hitCenterFrame = true; } } } BorderTopRefresh = true; UpdateState |= I_MESSAGES; } // Speed Boots if (CPlayer->powers[pw_speed]) { if (CPlayer->powers[pw_speed] > BLINKTHRESHOLD || !(CPlayer->powers[pw_speed] & 16)) { frame = (leveltime / 3) & 15; V_DrawPatch(60, 19, W_CacheLumpNum(SpinSpeedLump + frame, PU_CACHE)); } BorderTopRefresh = true; UpdateState |= I_MESSAGES; } // Defensive power if (CPlayer->powers[pw_invulnerability]) { if (CPlayer->powers[pw_invulnerability] > BLINKTHRESHOLD || !(CPlayer->powers[pw_invulnerability] & 16)) { frame = (leveltime / 3) & 15; V_DrawPatch(260, 19, W_CacheLumpNum(SpinDefenseLump + frame, PU_CACHE)); } BorderTopRefresh = true; UpdateState |= I_MESSAGES; } // Minotaur Active if (CPlayer->powers[pw_minotaur]) { if (CPlayer->powers[pw_minotaur] > BLINKTHRESHOLD || !(CPlayer->powers[pw_minotaur] & 16)) { frame = (leveltime / 3) & 15; V_DrawPatch(300, 19, W_CacheLumpNum(SpinMinotaurLump + frame, PU_CACHE)); } BorderTopRefresh = true; UpdateState |= I_MESSAGES; } } //========================================================================== // // SB_PaletteFlash // // Sets the new palette based upon the current values of // consoleplayer->damagecount and consoleplayer->bonuscount. // //========================================================================== void SB_PaletteFlash(boolean forceChange) { static int sb_palette = 0; int palette; byte *pal; if (forceChange) { sb_palette = -1; } if (gamestate == GS_LEVEL) { CPlayer = &players[consoleplayer]; if (CPlayer->poisoncount) { palette = 0; palette = (CPlayer->poisoncount + 7) >> 3; if (palette >= NUMPOISONPALS) { palette = NUMPOISONPALS - 1; } palette += STARTPOISONPALS; } else if (CPlayer->damagecount) { palette = (CPlayer->damagecount + 7) >> 3; if (palette >= NUMREDPALS) { palette = NUMREDPALS - 1; } palette += STARTREDPALS; } else if (CPlayer->bonuscount) { palette = (CPlayer->bonuscount + 7) >> 3; if (palette >= NUMBONUSPALS) { palette = NUMBONUSPALS - 1; } palette += STARTBONUSPALS; } else if (CPlayer->mo->flags2 & MF2_ICEDAMAGE) { // Frozen player palette = STARTICEPAL; } else { palette = 0; } } else { palette = 0; } if (palette != sb_palette) { sb_palette = palette; pal = (byte *) W_CacheLumpNum(PlayPalette, PU_CACHE) + palette * 768; I_SetPalette(pal); } } //========================================================================== // // DrawCommonBar // //========================================================================== void DrawCommonBar(void) { int healthPos; V_DrawPatch(0, 134, PatchH2TOP); if (oldhealth != HealthMarker) { oldhealth = HealthMarker; healthPos = HealthMarker; if (healthPos < 0) { healthPos = 0; } if (healthPos > 100) { healthPos = 100; } V_DrawPatch(28 + (((healthPos * 196) / 100) % 9), 193, PatchCHAIN); V_DrawPatch(7 + ((healthPos * 11) / 5), 193, PatchLIFEGEM); V_DrawPatch(0, 193, PatchLFEDGE); V_DrawPatch(277, 193, PatchRTEDGE); // ShadeChain(); UpdateState |= I_STATBAR; } } //========================================================================== // // DrawMainBar // //========================================================================== void DrawMainBar(void) { int i, j, k; int temp; patch_t *manaPatch1, *manaPatch2; patch_t *manaVialPatch1, *manaVialPatch2; manaPatch1 = NULL; manaPatch2 = NULL; manaVialPatch1 = NULL; manaVialPatch2 = NULL; // Ready artifact if (ArtifactFlash) { V_DrawPatch(144, 160, PatchARTICLEAR); V_DrawPatch(148, 164, W_CacheLumpNum(W_GetNumForName("useartia") + ArtifactFlash - 1, PU_CACHE)); ArtifactFlash--; oldarti = -1; // so that the correct artifact fills in after the flash UpdateState |= I_STATBAR; } else if (oldarti != CPlayer->readyArtifact || oldartiCount != CPlayer->inventory[inv_ptr].count) { V_DrawPatch(144, 160, PatchARTICLEAR); if (CPlayer->readyArtifact > 0) { V_DrawPatch(143, 163, W_CacheLumpName(patcharti[CPlayer->readyArtifact], PU_CACHE)); if (CPlayer->inventory[inv_ptr].count > 1) { DrSmallNumber(CPlayer->inventory[inv_ptr].count, 162, 184); } } oldarti = CPlayer->readyArtifact; oldartiCount = CPlayer->inventory[inv_ptr].count; UpdateState |= I_STATBAR; } // Frags if (deathmatch) { temp = 0; for (i = 0; i < maxplayers; i++) { temp += CPlayer->frags[i]; } if (temp != oldfrags) { V_DrawPatch(38, 162, PatchKILLS); DrINumber(temp, 40, 176); oldfrags = temp; UpdateState |= I_STATBAR; } } else { temp = HealthMarker; if (temp < 0) { temp = 0; } else if (temp > 100) { temp = 100; } if (oldlife != temp) { oldlife = temp; V_DrawPatch(41, 178, PatchARMCLEAR); if (temp >= 25) { DrINumber(temp, 40, 176); } else { DrRedINumber(temp, 40, 176); } UpdateState |= I_STATBAR; } } // Mana temp = CPlayer->mana[0]; if (oldmana1 != temp) { V_DrawPatch(77, 178, PatchMANACLEAR); DrSmallNumber(temp, 79, 181); manaVialPatch1 = (patch_t *) 1; // force a vial update if (temp == 0) { // Draw Dim Mana icon manaPatch1 = PatchMANADIM1; } else if (oldmana1 == 0) { manaPatch1 = PatchMANABRIGHT1; } oldmana1 = temp; UpdateState |= I_STATBAR; } temp = CPlayer->mana[1]; if (oldmana2 != temp) { V_DrawPatch(109, 178, PatchMANACLEAR); DrSmallNumber(temp, 111, 181); manaVialPatch1 = (patch_t *) 1; // force a vial update if (temp == 0) { // Draw Dim Mana icon manaPatch2 = PatchMANADIM2; } else if (oldmana2 == 0) { manaPatch2 = PatchMANABRIGHT2; } oldmana2 = temp; UpdateState |= I_STATBAR; } if (oldweapon != CPlayer->readyweapon || manaPatch1 || manaPatch2 || manaVialPatch1) { // Update mana graphics based upon mana count/weapon type if (CPlayer->readyweapon == WP_FIRST) { manaPatch1 = PatchMANADIM1; manaPatch2 = PatchMANADIM2; manaVialPatch1 = PatchMANAVIALDIM1; manaVialPatch2 = PatchMANAVIALDIM2; } else if (CPlayer->readyweapon == WP_SECOND) { if (!manaPatch1) { manaPatch1 = PatchMANABRIGHT1; } manaVialPatch1 = PatchMANAVIAL1; manaPatch2 = PatchMANADIM2; manaVialPatch2 = PatchMANAVIALDIM2; } else if (CPlayer->readyweapon == WP_THIRD) { manaPatch1 = PatchMANADIM1; manaVialPatch1 = PatchMANAVIALDIM1; if (!manaPatch2) { manaPatch2 = PatchMANABRIGHT2; } manaVialPatch2 = PatchMANAVIAL2; } else { manaVialPatch1 = PatchMANAVIAL1; manaVialPatch2 = PatchMANAVIAL2; if (!manaPatch1) { manaPatch1 = PatchMANABRIGHT1; } if (!manaPatch2) { manaPatch2 = PatchMANABRIGHT2; } } V_DrawPatch(77, 164, manaPatch1); V_DrawPatch(110, 164, manaPatch2); V_DrawPatch(94, 164, manaVialPatch1); for (i = 165; i < 187 - (22 * CPlayer->mana[0]) / MAX_MANA; i++) { for (j = 0; j <= crispy->hires; j++) for (k = 0; k <= crispy->hires; k++) { I_VideoBuffer[((i << crispy->hires) + j) * SCREENWIDTH + ((95 << crispy->hires) + k)] = 0; I_VideoBuffer[((i << crispy->hires) + j) * SCREENWIDTH + ((96 << crispy->hires) + k)] = 0; I_VideoBuffer[((i << crispy->hires) + j) * SCREENWIDTH + ((97 << crispy->hires) + k)] = 0; } } V_DrawPatch(102, 164, manaVialPatch2); for (i = 165; i < 187 - (22 * CPlayer->mana[1]) / MAX_MANA; i++) { for (j = 0; j <= crispy->hires; j++) for (k = 0; k <= crispy->hires; k++) { I_VideoBuffer[((i << crispy->hires) + j) * SCREENWIDTH + ((103 << crispy->hires) + k)] = 0; I_VideoBuffer[((i << crispy->hires) + j) * SCREENWIDTH + ((104 << crispy->hires) + k)] = 0; I_VideoBuffer[((i << crispy->hires) + j) * SCREENWIDTH + ((105 << crispy->hires) + k)] = 0; } } oldweapon = CPlayer->readyweapon; UpdateState |= I_STATBAR; } // Armor temp = AutoArmorSave[CPlayer->class] + CPlayer->armorpoints[ARMOR_ARMOR] + CPlayer->armorpoints[ARMOR_SHIELD] + CPlayer->armorpoints[ARMOR_HELMET] + CPlayer->armorpoints[ARMOR_AMULET]; if (oldarmor != temp) { oldarmor = temp; V_DrawPatch(255, 178, PatchARMCLEAR); DrINumber(FixedDiv(temp, 5 * FRACUNIT) >> FRACBITS, 250, 176); UpdateState |= I_STATBAR; } // Weapon Pieces if (oldpieces != CPlayer->pieces) { DrawWeaponPieces(); oldpieces = CPlayer->pieces; UpdateState |= I_STATBAR; } } //========================================================================== // // DrawInventoryBar // //========================================================================== void DrawInventoryBar(void) { int i; int x; x = inv_ptr - curpos; UpdateState |= I_STATBAR; V_DrawPatch(38, 162, PatchINVBAR); for (i = 0; i < 7; i++) { //V_DrawPatch(50+i*31, 160, W_CacheLumpName("ARTIBOX", PU_CACHE)); if (CPlayer->inventorySlotNum > x + i && CPlayer->inventory[x + i].type != arti_none) { V_DrawPatch(50 + i * 31, 163, W_CacheLumpName(patcharti [CPlayer->inventory[x + i].type], PU_CACHE)); if (CPlayer->inventory[x + i].count > 1) { DrSmallNumber(CPlayer->inventory[x + i].count, 68 + i * 31, 185); } } } V_DrawPatch(50 + curpos * 31, 163, PatchSELECTBOX); if (x != 0) { V_DrawPatch(42, 163, !(leveltime & 4) ? PatchINVLFGEM1 : PatchINVLFGEM2); } if (CPlayer->inventorySlotNum - x > 7) { V_DrawPatch(269, 163, !(leveltime & 4) ? PatchINVRTGEM1 : PatchINVRTGEM2); } } //========================================================================== // // DrawKeyBar // //========================================================================== void DrawKeyBar(void) { int i; int xPosition; int temp; if (oldkeys != CPlayer->keys) { xPosition = 46; for (i = 0; i < NUMKEYS && xPosition <= 126; i++) { if (CPlayer->keys & (1 << i)) { V_DrawPatch(xPosition, 164, W_CacheLumpNum(W_GetNumForName("keyslot1") + i, PU_CACHE)); xPosition += 20; } } oldkeys = CPlayer->keys; UpdateState |= I_STATBAR; } temp = AutoArmorSave[CPlayer->class] + CPlayer->armorpoints[ARMOR_ARMOR] + CPlayer->armorpoints[ARMOR_SHIELD] + CPlayer->armorpoints[ARMOR_HELMET] + CPlayer->armorpoints[ARMOR_AMULET]; if (oldarmor != temp) { for (i = 0; i < NUMARMOR; i++) { if (!CPlayer->armorpoints[i]) { continue; } if (CPlayer->armorpoints[i] <= (ArmorIncrement[CPlayer->class][i] >> 2)) { V_DrawTLPatch(150 + 31 * i, 164, W_CacheLumpNum(W_GetNumForName("armslot1") + i, PU_CACHE)); } else if (CPlayer->armorpoints[i] <= (ArmorIncrement[CPlayer->class][i] >> 1)) { V_DrawAltTLPatch(150 + 31 * i, 164, W_CacheLumpNum(W_GetNumForName("armslot1") + i, PU_CACHE)); } else { V_DrawPatch(150 + 31 * i, 164, W_CacheLumpNum(W_GetNumForName("armslot1") + i, PU_CACHE)); } } oldarmor = temp; UpdateState |= I_STATBAR; } } //========================================================================== // // DrawWeaponPieces // //========================================================================== static int PieceX[NUMCLASSES][3] = { {190, 225, 234}, {190, 212, 225}, {190, 205, 224}, {0, 0, 0} // Pig is never used }; static void DrawWeaponPieces(void) { if (CPlayer->pieces == 7) { V_DrawPatch(190, 162, PatchWEAPONFULL); return; } V_DrawPatch(190, 162, PatchWEAPONSLOT); if (CPlayer->pieces & WPIECE1) { V_DrawPatch(PieceX[PlayerClass[consoleplayer]][0], 162, PatchPIECE1); } if (CPlayer->pieces & WPIECE2) { V_DrawPatch(PieceX[PlayerClass[consoleplayer]][1], 162, PatchPIECE2); } if (CPlayer->pieces & WPIECE3) { V_DrawPatch(PieceX[PlayerClass[consoleplayer]][2], 162, PatchPIECE3); } } //========================================================================== // // DrawFullScreenStuff // //========================================================================== void DrawFullScreenStuff(void) { int i; int x; int temp; UpdateState |= I_FULLSCRN; if (CPlayer->mo->health > 0) { DrBNumber(CPlayer->mo->health, 5, 180); } else { DrBNumber(0, 5, 180); } if (deathmatch) { temp = 0; for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { temp += CPlayer->frags[i]; } } DrINumber(temp, 45, 185); } if (!inventory) { if (CPlayer->readyArtifact > 0) { V_DrawTLPatch(286, 170, W_CacheLumpName("ARTIBOX", PU_CACHE)); V_DrawPatch(284, 169, W_CacheLumpName(patcharti[CPlayer->readyArtifact], PU_CACHE)); if (CPlayer->inventory[inv_ptr].count > 1) { DrSmallNumber(CPlayer->inventory[inv_ptr].count, 302, 192); } } } else { x = inv_ptr - curpos; for (i = 0; i < 7; i++) { V_DrawTLPatch(50 + i * 31, 168, W_CacheLumpName("ARTIBOX", PU_CACHE)); if (CPlayer->inventorySlotNum > x + i && CPlayer->inventory[x + i].type != arti_none) { V_DrawPatch(49 + i * 31, 167, W_CacheLumpName(patcharti [CPlayer->inventory[x + i].type], PU_CACHE)); if (CPlayer->inventory[x + i].count > 1) { DrSmallNumber(CPlayer->inventory[x + i].count, 66 + i * 31, 188); } } } V_DrawPatch(50 + curpos * 31, 167, PatchSELECTBOX); if (x != 0) { V_DrawPatch(40, 167, !(leveltime & 4) ? PatchINVLFGEM1 : PatchINVLFGEM2); } if (CPlayer->inventorySlotNum - x > 7) { V_DrawPatch(268, 167, !(leveltime & 4) ? PatchINVRTGEM1 : PatchINVRTGEM2); } } } //========================================================================== // // Draw_TeleportIcon // //========================================================================== void Draw_TeleportIcon(void) { patch_t *patch; patch = W_CacheLumpNum(W_GetNumForName("teleicon"), PU_CACHE); V_DrawPatch(100, 68, patch); UpdateState |= I_FULLSCRN; I_FinishUpdate(); UpdateState |= I_FULLSCRN; } //========================================================================== // // Draw_SaveIcon // //========================================================================== void Draw_SaveIcon(void) { patch_t *patch; patch = W_CacheLumpNum(W_GetNumForName("saveicon"), PU_CACHE); V_DrawPatch(100, 68, patch); UpdateState |= I_FULLSCRN; I_FinishUpdate(); UpdateState |= I_FULLSCRN; } //========================================================================== // // Draw_LoadIcon // //========================================================================== void Draw_LoadIcon(void) { patch_t *patch; patch = W_CacheLumpNum(W_GetNumForName("loadicon"), PU_CACHE); V_DrawPatch(100, 68, patch); UpdateState |= I_FULLSCRN; I_FinishUpdate(); UpdateState |= I_FULLSCRN; } //========================================================================== // // SB_Responder // //========================================================================== boolean SB_Responder(event_t * event) { if (event->type == ev_keydown) { if (HandleCheats(event->data1)) { // Need to eat the key return (true); } } return (false); } //========================================================================== // // HandleCheats // // Returns true if the caller should eat the key. // //========================================================================== static boolean HandleCheats(byte key) { int i; boolean eat; if (gameskill == sk_nightmare) { // Can't cheat in nightmare mode return (false); } else if (netgame) { // change CD track is the only cheat available in deathmatch eat = false; if (cdmusic) { if (CheatAddKey(&Cheats[0], key, &eat)) { Cheats[0].func(&players[consoleplayer], &Cheats[0]); S_StartSound(NULL, SFX_PLATFORM_STOP); } if (CheatAddKey(&Cheats[1], key, &eat)) { Cheats[1].func(&players[consoleplayer], &Cheats[1]); S_StartSound(NULL, SFX_PLATFORM_STOP); } } return eat; } if (players[consoleplayer].health <= 0) { // Dead players can't cheat return (false); } eat = false; for (i = 0; ipos) { cheat->pos = cheat->sequence; cheat->currentArg = 0; } if (*cheat->pos == 0) { *eat = true; cheat->args[cheat->currentArg++] = key; cheat->pos++; } else if (CheatLookup[key] == *cheat->pos) { cheat->pos++; } else { cheat->pos = cheat->sequence; cheat->currentArg = 0; } if (*cheat->pos == 0xff) { cheat->pos = cheat->sequence; cheat->currentArg = 0; return (true); } return (false); */ *eat = cht_CheckCheat(cheat->seq, key); return *eat; } //========================================================================== // // CHEAT FUNCTIONS // //========================================================================== static void CheatGodFunc(player_t * player, Cheat_t * cheat) { player->cheats ^= CF_GODMODE; if (player->cheats & CF_GODMODE) { P_SetMessage(player, TXT_CHEATGODON, true); } else { P_SetMessage(player, TXT_CHEATGODOFF, true); } SB_state = -1; } static void CheatNoClipFunc(player_t * player, Cheat_t * cheat) { player->cheats ^= CF_NOCLIP; if (player->cheats & CF_NOCLIP) { P_SetMessage(player, TXT_CHEATNOCLIPON, true); } else { P_SetMessage(player, TXT_CHEATNOCLIPOFF, true); } } static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat) { int i; //extern boolean *WeaponInShareware; for (i = 0; i < NUMARMOR; i++) { player->armorpoints[i] = ArmorIncrement[player->class][i]; } for (i = 0; i < NUMWEAPONS; i++) { player->weaponowned[i] = true; } for (i = 0; i < NUMMANA; i++) { player->mana[i] = MAX_MANA; } P_SetMessage(player, TXT_CHEATWEAPONS, true); } static void CheatHealthFunc(player_t * player, Cheat_t * cheat) { if (player->morphTics) { player->health = player->mo->health = MAXMORPHHEALTH; } else { player->health = player->mo->health = MAXHEALTH; } P_SetMessage(player, TXT_CHEATHEALTH, true); } static void CheatKeysFunc(player_t * player, Cheat_t * cheat) { player->keys = 2047; P_SetMessage(player, TXT_CHEATKEYS, true); } static void CheatSoundFunc(player_t * player, Cheat_t * cheat) { DebugSound = !DebugSound; if (DebugSound) { P_SetMessage(player, TXT_CHEATSOUNDON, true); } else { P_SetMessage(player, TXT_CHEATSOUNDOFF, true); } } static void CheatTickerFunc(player_t * player, Cheat_t * cheat) { DisplayTicker = !DisplayTicker; if (DisplayTicker) { P_SetMessage(player, TXT_CHEATTICKERON, true); } else { P_SetMessage(player, TXT_CHEATTICKEROFF, true); } I_DisplayFPSDots(DisplayTicker); } static void CheatArtifactAllFunc(player_t * player, Cheat_t * cheat) { int i; int j; for (i = arti_none + 1; i < arti_firstpuzzitem; i++) { for (j = 0; j < 25; j++) { P_GiveArtifact(player, i, NULL); } } P_SetMessage(player, TXT_CHEATARTIFACTS3, true); } static void CheatPuzzleFunc(player_t * player, Cheat_t * cheat) { int i; for (i = arti_firstpuzzitem; i < NUMARTIFACTS; i++) { P_GiveArtifact(player, i, NULL); } P_SetMessage(player, TXT_CHEATARTIFACTS3, true); } static void CheatInitFunc(player_t * player, Cheat_t * cheat) { G_DeferedInitNew(gameskill, gameepisode, gamemap); P_SetMessage(player, TXT_CHEATWARP, true); } static void CheatWarpFunc(player_t * player, Cheat_t * cheat) { int tens; int ones; int map; char mapName[9]; char args[2]; cht_GetParam(cheat->seq, args); tens = args[0] - '0'; ones = args[1] - '0'; if (tens < 0 || tens > 9 || ones < 0 || ones > 9) { // Bad map P_SetMessage(player, TXT_CHEATBADINPUT, true); return; } map = P_TranslateMap((args[0] - '0') * 10 + args[1] - '0'); if (map == -1) { // Not found P_SetMessage(player, TXT_CHEATNOMAP, true); return; } if (map == gamemap) { // Don't try to teleport to current map P_SetMessage(player, TXT_CHEATBADINPUT, true); return; } M_snprintf(mapName, sizeof(mapName), "MAP%02d", map); if (W_CheckNumForName(mapName) == -1) { // Can't find P_SetMessage(player, TXT_CHEATNOMAP, true); return; } P_SetMessage(player, TXT_CHEATWARP, true); G_TeleportNewMap(map, 0); } static void CheatPigFunc(player_t * player, Cheat_t * cheat) { extern boolean P_UndoPlayerMorph(player_t * player); if (player->morphTics) { P_UndoPlayerMorph(player); } else { P_MorphPlayer(player); } P_SetMessage(player, "SQUEAL!!", true); } static void CheatMassacreFunc(player_t * player, Cheat_t * cheat) { int count; char buffer[80]; count = P_Massacre(); M_snprintf(buffer, sizeof(buffer), "%d MONSTERS KILLED\n", count); P_SetMessage(player, buffer, true); } static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat) { int i; if (player->morphTics) { return; } for (i = 1; i < NUMWEAPONS; i++) { player->weaponowned[i] = false; } // In the original code, NUMWEAPONS was 8. So the writes to weaponowned // overflowed the array. We must set the following fields to zero as // well: player->mana[0] = 0; player->mana[1] = 0; player->attackdown = 0; player->usedown = 0; player->pendingweapon = WP_FIRST; P_SetMessage(player, TXT_CHEATIDKFA, true); } static void CheatQuickenFunc1(player_t * player, Cheat_t * cheat) { P_SetMessage(player, "TRYING TO CHEAT? THAT'S ONE....", true); } static void CheatQuickenFunc2(player_t * player, Cheat_t * cheat) { P_SetMessage(player, "THAT'S TWO....", true); } static void CheatQuickenFunc3(player_t * player, Cheat_t * cheat) { P_DamageMobj(player->mo, NULL, player->mo, 10000); P_SetMessage(player, "THAT'S THREE! TIME TO DIE.", true); } static void CheatClassFunc1(player_t * player, Cheat_t * cheat) { P_SetMessage(player, "ENTER NEW PLAYER CLASS (0 - 2)", true); } static void CheatClassFunc2(player_t * player, Cheat_t * cheat) { int i; int class; char args[2]; cht_GetParam(cheat->seq, args); if (player->morphTics) { // don't change class if the player is morphed return; } class = args[0] - '0'; if (class > 2 || class < 0) { P_SetMessage(player, "INVALID PLAYER CLASS", true); return; } player->class = class; for (i = 0; i < NUMARMOR; i++) { player->armorpoints[i] = 0; } PlayerClass[consoleplayer] = class; P_PostMorphWeapon(player, WP_FIRST); SB_SetClassData(); SB_state = -1; UpdateState |= I_FULLSCRN; } static void CheatVersionFunc(player_t * player, Cheat_t * cheat) { P_SetMessage(player, HEXEN_VERSIONTEXT, true); } static void CheatDebugFunc(player_t * player, Cheat_t * cheat) { char textBuffer[50]; M_snprintf(textBuffer, sizeof(textBuffer), "MAP %d (%d) X:%5d Y:%5d Z:%5d", P_GetMapWarpTrans(gamemap), gamemap, player->mo->x >> FRACBITS, player->mo->y >> FRACBITS, player->mo->z >> FRACBITS); P_SetMessage(player, textBuffer, true); } static void CheatScriptFunc1(player_t * player, Cheat_t * cheat) { P_SetMessage(player, "RUN WHICH SCRIPT(01-99)?", true); } static void CheatScriptFunc2(player_t * player, Cheat_t * cheat) { P_SetMessage(player, "RUN WHICH SCRIPT(01-99)?", true); } static void CheatScriptFunc3(player_t * player, Cheat_t * cheat) { int script; byte script_args[3]; int tens, ones; char textBuffer[40]; char args[2]; cht_GetParam(cheat->seq, args); tens = args[0] - '0'; ones = args[1] - '0'; script = tens * 10 + ones; if (script < 1) return; if (script > 99) return; script_args[0] = script_args[1] = script_args[2] = 0; if (P_StartACS(script, 0, script_args, player->mo, NULL, 0)) { M_snprintf(textBuffer, sizeof(textBuffer), "RUNNING SCRIPT %.2d", script); P_SetMessage(player, textBuffer, true); } } extern int cheating; static void CheatRevealFunc(player_t * player, Cheat_t * cheat) { cheating = (cheating + 1) % 3; } //=========================================================================== // // CheatTrackFunc1 // //=========================================================================== static void CheatTrackFunc1(player_t * player, Cheat_t * cheat) { char buffer[80]; if (!cdmusic) { return; } if (I_CDMusInit() == -1) { P_SetMessage(player, "ERROR INITIALIZING CD", true); } M_snprintf(buffer, sizeof(buffer), "ENTER DESIRED CD TRACK (%.2d - %.2d):\n", I_CDMusFirstTrack(), I_CDMusLastTrack()); P_SetMessage(player, buffer, true); } //=========================================================================== // // CheatTrackFunc2 // //=========================================================================== static void CheatTrackFunc2(player_t * player, Cheat_t * cheat) { char buffer[80]; int track; char args[2]; cht_GetParam(cheat->seq, args); if (!cdmusic) { return; } track = (args[0] - '0') * 10 + (args[1] - '0'); if (track < I_CDMusFirstTrack() || track > I_CDMusLastTrack()) { P_SetMessage(player, "INVALID TRACK NUMBER\n", true); return; } if (track == S_GetCurrentCDTrack()) { return; } if (!S_StartCustomCDTrack(track)) { M_snprintf(buffer, sizeof(buffer), "ERROR WHILE TRYING TO PLAY CD TRACK: %.2d\n", track); P_SetMessage(player, buffer, true); } else { // No error encountered while attempting to play the track M_snprintf(buffer, sizeof(buffer), "PLAYING TRACK: %.2d\n", track); P_SetMessage(player, buffer, true); } } crispy-doom-crispy-doom-5.6.4/src/hexen/sc_man.c000066400000000000000000000253551360717211000215500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include #include #include "h2def.h" #include "i_system.h" #include "m_misc.h" // MACROS ------------------------------------------------------------------ #define MAX_STRING_SIZE 64 #define ASCII_COMMENT (';') #define ASCII_QUOTE (34) #define LUMP_SCRIPT 1 #define FILE_ZONE_SCRIPT 2 // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void CheckOpen(void); static void OpenScript(const char *name, int type); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- char *sc_String; int sc_Number; int sc_Line; boolean sc_End; boolean sc_Crossed; boolean sc_FileScripts = false; const char *sc_ScriptsDir = ""; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static char ScriptName[16]; static char *ScriptBuffer; static char *ScriptPtr; static char *ScriptEndPtr; static char StringBuffer[MAX_STRING_SIZE]; static int ScriptLumpNum; static boolean ScriptOpen = false; static int ScriptSize; static boolean AlreadyGot = false; // CODE -------------------------------------------------------------------- //========================================================================== // // SC_Open // //========================================================================== void SC_Open(const char *name) { char fileName[128]; if (sc_FileScripts == true) { M_snprintf(fileName, sizeof(fileName), "%s%s.txt", sc_ScriptsDir, name); SC_OpenFile(fileName); } else { SC_OpenLump(name); } } //========================================================================== // // SC_OpenLump // // Loads a script (from the WAD files) and prepares it for parsing. // //========================================================================== void SC_OpenLump(const char *name) { OpenScript(name, LUMP_SCRIPT); } //========================================================================== // // SC_OpenFile // // Loads a script (from a file) and prepares it for parsing. Uses the // zone memory allocator for memory allocation and de-allocation. // //========================================================================== void SC_OpenFile(const char *name) { OpenScript(name, FILE_ZONE_SCRIPT); } //========================================================================== // // OpenScript // //========================================================================== static void OpenScript(const char *name, int type) { SC_Close(); if (type == LUMP_SCRIPT) { // Lump script ScriptLumpNum = W_GetNumForName(name); ScriptBuffer = (char *) W_CacheLumpNum(ScriptLumpNum, PU_STATIC); ScriptSize = W_LumpLength(ScriptLumpNum); M_StringCopy(ScriptName, name, sizeof(ScriptName)); } else if (type == FILE_ZONE_SCRIPT) { // File script - zone ScriptLumpNum = -1; ScriptSize = M_ReadFile(name, (byte **) & ScriptBuffer); M_ExtractFileBase(name, ScriptName); } ScriptPtr = ScriptBuffer; ScriptEndPtr = ScriptPtr + ScriptSize; sc_Line = 1; sc_End = false; ScriptOpen = true; sc_String = StringBuffer; AlreadyGot = false; } //========================================================================== // // SC_Close // //========================================================================== void SC_Close(void) { if (ScriptOpen) { if (ScriptLumpNum >= 0) { W_ReleaseLumpNum(ScriptLumpNum); } else { Z_Free(ScriptBuffer); } ScriptOpen = false; } } //========================================================================== // // SC_GetString // //========================================================================== boolean SC_GetString(void) { char *text; boolean 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 < ScriptEndPtr && *ScriptPtr <= 32) { 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(char *name) { SC_MustGetString(); if (SC_Compare(name) == false) { SC_ScriptError(NULL); } } //========================================================================== // // SC_GetNumber // //========================================================================== boolean 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. // //========================================================================== /* boolean SC_Check(void) { 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 // //========================================================================== boolean 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()."); } } crispy-doom-crispy-doom-5.6.4/src/hexen/sn_sonix.c000066400000000000000000000354321360717211000221450ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include #include "m_random.h" #include "h2def.h" #include "i_system.h" #include "i_sound.h" #include "s_sound.h" // MACROS ------------------------------------------------------------------ #define SS_MAX_SCRIPTS 64 #define SS_TEMPBUFFER_SIZE 1024 #define SS_SEQUENCE_NAME_LENGTH 32 #define SS_SCRIPT_NAME "SNDSEQ" #define SS_STRING_PLAY "play" #define SS_STRING_PLAYUNTILDONE "playuntildone" #define SS_STRING_PLAYTIME "playtime" #define SS_STRING_PLAYREPEAT "playrepeat" #define SS_STRING_DELAY "delay" #define SS_STRING_DELAYRAND "delayrand" #define SS_STRING_VOLUME "volume" #define SS_STRING_END "end" #define SS_STRING_STOPSOUND "stopsound" // TYPES ------------------------------------------------------------------- typedef enum { SS_CMD_NONE, SS_CMD_PLAY, SS_CMD_WAITUNTILDONE, // used by PLAYUNTILDONE SS_CMD_PLAYTIME, SS_CMD_PLAYREPEAT, SS_CMD_DELAY, SS_CMD_DELAYRAND, SS_CMD_VOLUME, SS_CMD_STOPSOUND, SS_CMD_END } sscmds_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void VerifySequencePtr(int *base, int *ptr); static int GetSoundOffset(char *name); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern sfxinfo_t S_sfx[]; // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ static struct { char name[SS_SEQUENCE_NAME_LENGTH]; int scriptNum; int stopSound; } SequenceTranslate[SEQ_NUMSEQ] = { { "Platform", 0, 0}, { "Platform", 0, 0}, // a 'heavy' platform is just a platform { "PlatformMetal", 0, 0}, { "Platform", 0, 0}, // same with a 'creak' platform { "Silence", 0, 0}, { "Lava", 0, 0}, { "Water", 0, 0}, { "Ice", 0, 0}, { "Earth", 0, 0}, { "PlatformMetal2", 0, 0}, { "DoorNormal", 0, 0}, { "DoorHeavy", 0, 0}, { "DoorMetal", 0, 0}, { "DoorCreak", 0, 0}, { "Silence", 0, 0}, { "Lava", 0, 0}, { "Water", 0, 0}, { "Ice", 0, 0}, { "Earth", 0, 0}, { "DoorMetal2", 0, 0}, { "Wind", 0, 0} }; static int *SequenceData[SS_MAX_SCRIPTS]; int ActiveSequences; seqnode_t *SequenceListHead; // CODE -------------------------------------------------------------------- //========================================================================== // // VerifySequencePtr // // Verifies the integrity of the temporary ptr, and ensures that the ptr // isn't exceeding the size of the temporary buffer //========================================================================== static void VerifySequencePtr(int *base, int *ptr) { if (ptr - base > SS_TEMPBUFFER_SIZE) { I_Error("VerifySequencePtr: tempPtr >= %d\n", SS_TEMPBUFFER_SIZE); } } //========================================================================== // // GetSoundOffset // //========================================================================== static int GetSoundOffset(char *name) { int i; for (i = 0; i < NUMSFX; i++) { if (!strcasecmp(name, S_sfx[i].tagname)) { return i; } } SC_ScriptError("GetSoundOffset: Unknown sound name\n"); return 0; } //========================================================================== // // SN_InitSequenceScript // //========================================================================== void SN_InitSequenceScript(void) { int i, j; int inSequence; int *tempDataStart = NULL; int *tempDataPtr = NULL; inSequence = -1; ActiveSequences = 0; for (i = 0; i < SS_MAX_SCRIPTS; i++) { SequenceData[i] = NULL; } SC_Open(SS_SCRIPT_NAME); while (SC_GetString()) { if (*sc_String == ':') { if (inSequence != -1) { SC_ScriptError("SN_InitSequenceScript: Nested Script Error"); } tempDataStart = (int *) Z_Malloc(SS_TEMPBUFFER_SIZE, PU_STATIC, NULL); memset(tempDataStart, 0, SS_TEMPBUFFER_SIZE); tempDataPtr = tempDataStart; for (i = 0; i < SS_MAX_SCRIPTS; i++) { if (SequenceData[i] == NULL) { break; } } if (i == SS_MAX_SCRIPTS) { I_Error("Number of SS Scripts >= SS_MAX_SCRIPTS"); } for (j = 0; j < SEQ_NUMSEQ; j++) { if (!strcasecmp(SequenceTranslate[j].name, sc_String + 1)) { SequenceTranslate[j].scriptNum = i; inSequence = j; break; } } continue; // parse the next command } if (inSequence == -1) { continue; } if (SC_Compare(SS_STRING_PLAYUNTILDONE)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAY; *tempDataPtr++ = GetSoundOffset(sc_String); *tempDataPtr++ = SS_CMD_WAITUNTILDONE; } else if (SC_Compare(SS_STRING_PLAY)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAY; *tempDataPtr++ = GetSoundOffset(sc_String); } else if (SC_Compare(SS_STRING_PLAYTIME)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAY; *tempDataPtr++ = GetSoundOffset(sc_String); SC_MustGetNumber(); *tempDataPtr++ = SS_CMD_DELAY; *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_PLAYREPEAT)) { VerifySequencePtr(tempDataStart, tempDataPtr); SC_MustGetString(); *tempDataPtr++ = SS_CMD_PLAYREPEAT; *tempDataPtr++ = GetSoundOffset(sc_String); } else if (SC_Compare(SS_STRING_DELAY)) { VerifySequencePtr(tempDataStart, tempDataPtr); *tempDataPtr++ = SS_CMD_DELAY; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_DELAYRAND)) { VerifySequencePtr(tempDataStart, tempDataPtr); *tempDataPtr++ = SS_CMD_DELAYRAND; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_VOLUME)) { VerifySequencePtr(tempDataStart, tempDataPtr); *tempDataPtr++ = SS_CMD_VOLUME; SC_MustGetNumber(); *tempDataPtr++ = sc_Number; } else if (SC_Compare(SS_STRING_END)) { int dataSize; *tempDataPtr++ = SS_CMD_END; dataSize = (tempDataPtr - tempDataStart) * sizeof(int); SequenceData[i] = (int *) Z_Malloc(dataSize, PU_STATIC, NULL); memcpy(SequenceData[i], tempDataStart, dataSize); Z_Free(tempDataStart); inSequence = -1; } else if (SC_Compare(SS_STRING_STOPSOUND)) { SC_MustGetString(); SequenceTranslate[inSequence].stopSound = GetSoundOffset(sc_String); *tempDataPtr++ = SS_CMD_STOPSOUND; } else { SC_ScriptError("SN_InitSequenceScript: Unknown commmand.\n"); } } } //========================================================================== // // SN_StartSequence // //========================================================================== void SN_StartSequence(mobj_t * mobj, int sequence) { seqnode_t *node; SN_StopSequence(mobj); // Stop any previous sequence node = (seqnode_t *) Z_Malloc(sizeof(seqnode_t), PU_STATIC, NULL); node->sequencePtr = SequenceData[SequenceTranslate[sequence].scriptNum]; node->sequence = sequence; node->mobj = mobj; node->delayTics = 0; node->stopSound = SequenceTranslate[sequence].stopSound; node->volume = 127; // Start at max volume if (!SequenceListHead) { SequenceListHead = node; node->next = node->prev = NULL; } else { SequenceListHead->prev = node; node->next = SequenceListHead; node->prev = NULL; SequenceListHead = node; } ActiveSequences++; return; } //========================================================================== // // SN_StartSequenceName // //========================================================================== void SN_StartSequenceName(mobj_t * mobj, char *name) { int i; for (i = 0; i < SEQ_NUMSEQ; i++) { if (!strcmp(name, SequenceTranslate[i].name)) { SN_StartSequence(mobj, i); return; } } } //========================================================================== // // SN_StopSequence // //========================================================================== void SN_StopSequence(mobj_t * mobj) { seqnode_t *node; for (node = SequenceListHead; node; node = node->next) { if (node->mobj == mobj) { S_StopSound(mobj); if (node->stopSound) { S_StartSoundAtVolume(mobj, node->stopSound, node->volume); } if (SequenceListHead == node) { SequenceListHead = node->next; } if (node->prev) { node->prev->next = node->next; } if (node->next) { node->next->prev = node->prev; } Z_Free(node); ActiveSequences--; } } } //========================================================================== // // SN_UpdateActiveSequences // //========================================================================== void SN_UpdateActiveSequences(void) { seqnode_t *node; boolean sndPlaying; if (!ActiveSequences || paused) { // No sequences currently playing/game is paused return; } for (node = SequenceListHead; node; node = node->next) { if (node->delayTics) { node->delayTics--; continue; } sndPlaying = S_GetSoundPlayingInfo(node->mobj, node->currentSoundID); switch (*node->sequencePtr) { case SS_CMD_PLAY: if (!sndPlaying) { node->currentSoundID = *(node->sequencePtr + 1); S_StartSoundAtVolume(node->mobj, node->currentSoundID, node->volume); } node->sequencePtr += 2; break; case SS_CMD_WAITUNTILDONE: if (!sndPlaying) { node->sequencePtr++; node->currentSoundID = 0; } break; case SS_CMD_PLAYREPEAT: if (!sndPlaying) { node->currentSoundID = *(node->sequencePtr + 1); S_StartSoundAtVolume(node->mobj, node->currentSoundID, node->volume); } break; case SS_CMD_DELAY: node->delayTics = *(node->sequencePtr + 1); node->sequencePtr += 2; node->currentSoundID = 0; break; case SS_CMD_DELAYRAND: node->delayTics = *(node->sequencePtr + 1) + M_Random() % (*(node->sequencePtr + 2) - *(node->sequencePtr + 1)); node->sequencePtr += 2; node->currentSoundID = 0; break; case SS_CMD_VOLUME: node->volume = (127 * (*(node->sequencePtr + 1))) / 100; node->sequencePtr += 2; break; case SS_CMD_STOPSOUND: // Wait until something else stops the sequence break; case SS_CMD_END: SN_StopSequence(node->mobj); break; default: break; } } } //========================================================================== // // SN_StopAllSequences // //========================================================================== void SN_StopAllSequences(void) { seqnode_t *node; for (node = SequenceListHead; node; node = node->next) { node->stopSound = 0; // don't play any stop sounds SN_StopSequence(node->mobj); } } //========================================================================== // // SN_GetSequenceOffset // //========================================================================== int SN_GetSequenceOffset(int sequence, int *sequencePtr) { return (sequencePtr - SequenceData[SequenceTranslate[sequence].scriptNum]); } //========================================================================== // // SN_ChangeNodeData // // nodeNum zero is the first node //========================================================================== void SN_ChangeNodeData(int nodeNum, int seqOffset, int delayTics, int volume, int currentSoundID) { int i; seqnode_t *node; i = 0; node = SequenceListHead; while (node && i < nodeNum) { node = node->next; i++; } if (!node) { // reach the end of the list before finding the nodeNum-th node return; } node->delayTics = delayTics; node->volume = volume; node->sequencePtr += seqOffset; node->currentSoundID = currentSoundID; } crispy-doom-crispy-doom-5.6.4/src/hexen/sounds.c000066400000000000000000000257341360717211000216240ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "h2def.h" #include "sounds.h" // Music info /* musicinfo_t S_music[] = { { "MUS_E1M1", 0 }, // 1-1 { "MUS_E1M2", 0 }, { "MUS_E1M3", 0 }, { "MUS_E1M4", 0 }, { "MUS_E1M5", 0 }, { "MUS_E1M6", 0 }, { "MUS_E1M7", 0 }, { "MUS_E1M8", 0 }, { "MUS_E1M9", 0 }, { "MUS_E2M1", 0 }, // 2-1 { "MUS_E2M2", 0 }, { "MUS_E2M3", 0 }, { "MUS_E2M4", 0 }, { "MUS_E1M4", 0 }, { "MUS_E2M6", 0 }, { "MUS_E2M7", 0 }, { "MUS_E2M8", 0 }, { "MUS_E2M9", 0 }, { "MUS_E1M1", 0 }, // 3-1 { "MUS_E3M2", 0 }, { "MUS_E3M3", 0 }, { "MUS_E1M6", 0 }, { "MUS_E1M3", 0 }, { "MUS_E1M2", 0 }, { "MUS_E1M5", 0 }, { "MUS_E1M9", 0 }, { "MUS_E2M6", 0 }, { "MUS_E1M6", 0 }, // 4-1 { "MUS_TITL", 0 }, { "MUS_INTR", 0 }, { "MUS_CPTD", 0 } }; */ // Sound info #define SOUND(name, priority, numchannels, pitchshift) \ { name, "", priority, NULL, pitchshift, 0, -1, 0, numchannels, NULL } sfxinfo_t S_sfx[] = { // tagname, lumpname, priority, usefulness, snd_ptr, lumpnum, numchannels, // pitchshift SOUND("", 0, 0, 0), SOUND("PlayerFighterNormalDeath", 256, 2, 1), SOUND("PlayerFighterCrazyDeath", 256, 2, 1), SOUND("PlayerFighterExtreme1Death", 256, 2, 1), SOUND("PlayerFighterExtreme2Death", 256, 2, 1), SOUND("PlayerFighterExtreme3Death", 256, 2, 1), SOUND("PlayerFighterBurnDeath", 256, 2, 1), SOUND("PlayerClericNormalDeath", 256, 2, 1), SOUND("PlayerClericCrazyDeath", 256, 2, 1), SOUND("PlayerClericExtreme1Death", 256, 2, 1), SOUND("PlayerClericExtreme2Death", 256, 2, 1), SOUND("PlayerClericExtreme3Death", 256, 2, 1), SOUND("PlayerClericBurnDeath", 256, 2, 1), SOUND("PlayerMageNormalDeath", 256, 2, 0), SOUND("PlayerMageCrazyDeath", 256, 2, 0), SOUND("PlayerMageExtreme1Death", 256, 2, 0), SOUND("PlayerMageExtreme2Death", 256, 2, 0), SOUND("PlayerMageExtreme3Death", 256, 2, 0), SOUND("PlayerMageBurnDeath", 256, 2, 0), SOUND("PlayerFighterPain", 256, 2, 1), SOUND("PlayerClericPain", 256, 2, 1), SOUND("PlayerMagePain", 256, 2, 0), SOUND("PlayerFighterGrunt", 256, 2, 1), SOUND("PlayerClericGrunt", 256, 2, 1), SOUND("PlayerMageGrunt", 256, 2, 0), SOUND("PlayerLand", 32, 2, 1), SOUND("PlayerPoisonCough", 256, 2, 1), SOUND("PlayerFighterFallingScream", 256, 2, 1), SOUND("PlayerClericFallingScream", 256, 2, 1), SOUND("PlayerMageFallingScream", 256, 2, 0), SOUND("PlayerFallingSplat", 256, 2, 1), SOUND("PlayerFighterFailedUse", 256, 1, 1), SOUND("PlayerClericFailedUse", 256, 1, 1), SOUND("PlayerMageFailedUse", 256, 1, 0), SOUND("PlatformStart", 36, 2, 1), SOUND("PlatformStartMetal", 36, 2, 1), SOUND("PlatformStop", 40, 2, 1), SOUND("StoneMove", 32, 2, 1), SOUND("MetalMove", 32, 2, 1), SOUND("DoorOpen", 36, 2, 1), SOUND("DoorLocked", 36, 2, 1), SOUND("DoorOpenMetal", 36, 2, 1), SOUND("DoorCloseMetal", 36, 2, 1), SOUND("DoorCloseLight", 36, 2, 1), SOUND("DoorCloseHeavy", 36, 2, 1), SOUND("DoorCreak", 36, 2, 1), SOUND("PickupWeapon", 36, 2, 0), SOUND("PickupArtifact", 36, 2, 1), SOUND("PickupKey", 36, 2, 1), SOUND("PickupItem", 36, 2, 1), SOUND("PickupPiece", 36, 2, 0), SOUND("WeaponBuild", 36, 2, 0), SOUND("UseArtifact", 36, 2, 1), SOUND("BlastRadius", 36, 2, 1), SOUND("Teleport", 256, 2, 1), SOUND("ThunderCrash", 30, 2, 1), SOUND("FighterPunchMiss", 80, 2, 1), SOUND("FighterPunchHitThing", 80, 2, 1), SOUND("FighterPunchHitWall", 80, 2, 1), SOUND("FighterGrunt", 80, 2, 1), SOUND("FighterAxeHitThing", 80, 2, 1), SOUND("FighterHammerMiss", 80, 2, 1), SOUND("FighterHammerHitThing", 80, 2, 1), SOUND("FighterHammerHitWall", 80, 2, 1), SOUND("FighterHammerContinuous", 32, 2, 1), SOUND("FighterHammerExplode", 80, 2, 1), SOUND("FighterSwordFire", 80, 2, 1), SOUND("FighterSwordExplode", 80, 2, 1), SOUND("ClericCStaffFire", 80, 2, 1), SOUND("ClericCStaffExplode", 40, 2, 1), SOUND("ClericCStaffHitThing", 80, 2, 1), SOUND("ClericFlameFire", 80, 2, 1), SOUND("ClericFlameExplode", 80, 2, 1), SOUND("ClericFlameCircle", 80, 2, 1), SOUND("MageWandFire", 80, 2, 1), SOUND("MageLightningFire", 80, 2, 1), SOUND("MageLightningZap", 32, 2, 1), SOUND("MageLightningContinuous", 32, 2, 1), SOUND("MageLightningReady", 30, 2, 1), SOUND("MageShardsFire", 80, 2, 1), SOUND("MageShardsExplode", 36, 2, 1), SOUND("MageStaffFire", 80, 2, 1), SOUND("MageStaffExplode", 40, 2, 1), SOUND("Switch1", 32, 2, 1), SOUND("Switch2", 32, 2, 1), SOUND("SerpentSight", 32, 2, 1), SOUND("SerpentActive", 32, 2, 1), SOUND("SerpentPain", 32, 2, 1), SOUND("SerpentAttack", 32, 2, 1), SOUND("SerpentMeleeHit", 32, 2, 1), SOUND("SerpentDeath", 40, 2, 1), SOUND("SerpentBirth", 32, 2, 1), SOUND("SerpentFXContinuous", 32, 2, 1), SOUND("SerpentFXHit", 32, 2, 1), SOUND("PotteryExplode", 32, 2, 1), SOUND("Drip", 32, 2, 1), SOUND("CentaurSight", 32, 2, 1), SOUND("CentaurActive", 32, 2, 1), SOUND("CentaurPain", 32, 2, 1), SOUND("CentaurAttack", 32, 2, 1), SOUND("CentaurDeath", 40, 2, 1), SOUND("CentaurLeaderAttack", 32, 2, 1), SOUND("CentaurMissileExplode", 32, 2, 1), SOUND("Wind", 1, 2, 1), SOUND("BishopSight", 32, 2, 1), SOUND("BishopActive", 32, 2, 1), SOUND("BishopPain", 32, 2, 1), SOUND("BishopAttack", 32, 2, 1), SOUND("BishopDeath", 40, 2, 1), SOUND("BishopMissileExplode", 32, 2, 1), SOUND("BishopBlur", 32, 2, 1), SOUND("DemonSight", 32, 2, 1), SOUND("DemonActive", 32, 2, 1), SOUND("DemonPain", 32, 2, 1), SOUND("DemonAttack", 32, 2, 1), SOUND("DemonMissileFire", 32, 2, 1), SOUND("DemonMissileExplode", 32, 2, 1), SOUND("DemonDeath", 40, 2, 1), SOUND("WraithSight", 32, 2, 1), SOUND("WraithActive", 32, 2, 1), SOUND("WraithPain", 32, 2, 1), SOUND("WraithAttack", 32, 2, 1), SOUND("WraithMissileFire", 32, 2, 1), SOUND("WraithMissileExplode", 32, 2, 1), SOUND("WraithDeath", 40, 2, 1), SOUND("PigActive1", 32, 2, 1), SOUND("PigActive2", 32, 2, 1), SOUND("PigPain", 32, 2, 1), SOUND("PigAttack", 32, 2, 1), SOUND("PigDeath", 40, 2, 1), SOUND("MaulatorSight", 32, 2, 1), SOUND("MaulatorActive", 32, 2, 1), SOUND("MaulatorPain", 32, 2, 1), SOUND("MaulatorHamSwing", 32, 2, 1), SOUND("MaulatorHamHit", 32, 2, 1), SOUND("MaulatorMissileHit", 32, 2, 1), SOUND("MaulatorDeath", 40, 2, 1), SOUND("FreezeDeath", 40, 2, 1), SOUND("FreezeShatter", 40, 2, 1), SOUND("EttinSight", 32, 2, 1), SOUND("EttinActive", 32, 2, 1), SOUND("EttinPain", 32, 2, 1), SOUND("EttinAttack", 32, 2, 1), SOUND("EttinDeath", 40, 2, 1), SOUND("FireDemonSpawn", 32, 2, 1), SOUND("FireDemonActive", 32, 2, 1), SOUND("FireDemonPain", 32, 2, 1), SOUND("FireDemonAttack", 32, 2, 1), SOUND("FireDemonMissileHit", 32, 2, 1), SOUND("FireDemonDeath", 40, 2, 1), SOUND("IceGuySight", 32, 2, 1), SOUND("IceGuyActive", 32, 2, 1), SOUND("IceGuyAttack", 32, 2, 1), SOUND("IceGuyMissileExplode", 32, 2, 1), SOUND("SorcererSight", 256, 2, 1), SOUND("SorcererActive", 256, 2, 1), SOUND("SorcererPain", 256, 2, 1), SOUND("SorcererSpellCast", 256, 2, 1), SOUND("SorcererBallWoosh", 256, 4, 1), SOUND("SorcererDeathScream", 256, 2, 1), SOUND("SorcererBishopSpawn", 80, 2, 1), SOUND("SorcererBallPop", 80, 2, 1), SOUND("SorcererBallBounce", 80, 3, 1), SOUND("SorcererBallExplode", 80, 3, 1), SOUND("SorcererBigBallExplode", 80, 3, 1), SOUND("SorcererHeadScream", 256, 2, 1), SOUND("DragonSight", 64, 2, 1), SOUND("DragonActive", 64, 2, 1), SOUND("DragonWingflap", 64, 2, 1), SOUND("DragonAttack", 64, 2, 1), SOUND("DragonPain", 64, 2, 1), SOUND("DragonDeath", 64, 2, 1), SOUND("DragonFireballExplode", 32, 2, 1), SOUND("KoraxSight", 256, 2, 1), SOUND("KoraxActive", 256, 2, 1), SOUND("KoraxPain", 256, 2, 1), SOUND("KoraxAttack", 256, 2, 1), SOUND("KoraxCommand", 256, 2, 1), SOUND("KoraxDeath", 256, 2, 1), SOUND("KoraxStep", 128, 2, 1), SOUND("ThrustSpikeRaise", 32, 2, 1), SOUND("ThrustSpikeLower", 32, 2, 1), SOUND("GlassShatter", 32, 2, 1), SOUND("FlechetteBounce", 32, 2, 1), SOUND("FlechetteExplode", 32, 2, 1), SOUND("LavaMove", 36, 2, 1), SOUND("WaterMove", 36, 2, 1), SOUND("IceStartMove", 36, 2, 1), SOUND("EarthStartMove", 36, 2, 1), SOUND("WaterSplash", 32, 2, 1), SOUND("LavaSizzle", 32, 2, 1), SOUND("SludgeGloop", 32, 2, 1), SOUND("HolySymbolFire", 64, 2, 1), SOUND("SpiritActive", 32, 2, 1), SOUND("SpiritAttack", 32, 2, 1), SOUND("SpiritDie", 32, 2, 1), SOUND("ValveTurn", 36, 2, 1), SOUND("RopePull", 36, 2, 1), SOUND("FlyBuzz", 20, 2, 1), SOUND("Ignite", 32, 2, 1), SOUND("PuzzleSuccess", 256, 2, 1), SOUND("PuzzleFailFighter", 256, 2, 1), SOUND("PuzzleFailCleric", 256, 2, 1), SOUND("PuzzleFailMage", 256, 2, 1), SOUND("Earthquake", 32, 2, 1), SOUND("BellRing", 32, 2, 0), SOUND("TreeBreak", 32, 2, 1), SOUND("TreeExplode", 32, 2, 1), SOUND("SuitofArmorBreak", 32, 2, 1), SOUND("PoisonShroomPain", 20, 2, 1), SOUND("PoisonShroomDeath", 32, 2, 1), SOUND("Ambient1", 1, 1, 1), SOUND("Ambient2", 1, 1, 1), SOUND("Ambient3", 1, 1, 1), SOUND("Ambient4", 1, 1, 1), SOUND("Ambient5", 1, 1, 1), SOUND("Ambient6", 1, 1, 1), SOUND("Ambient7", 1, 1, 1), SOUND("Ambient8", 1, 1, 1), SOUND("Ambient9", 1, 1, 1), SOUND("Ambient10", 1, 1, 1), SOUND("Ambient11", 1, 1, 1), SOUND("Ambient12", 1, 1, 1), SOUND("Ambient13", 1, 1, 1), SOUND("Ambient14", 1, 1, 1), SOUND("Ambient15", 1, 1, 1), SOUND("StartupTick", 32, 2, 1), SOUND("SwitchOtherLevel", 32, 2, 1), SOUND("Respawn", 32, 2, 1), SOUND("KoraxVoiceGreetings", 512, 2, 1), SOUND("KoraxVoiceReady", 512, 2, 1), SOUND("KoraxVoiceBlood", 512, 2, 1), SOUND("KoraxVoiceGame", 512, 2, 1), SOUND("KoraxVoiceBoard", 512, 2, 1), SOUND("KoraxVoiceWorship", 512, 2, 1), SOUND("KoraxVoiceMaybe", 512, 2, 1), SOUND("KoraxVoiceStrong", 512, 2, 1), SOUND("KoraxVoiceFace", 512, 2, 1), SOUND("BatScream", 32, 2, 1), SOUND("Chat", 512, 2, 1), SOUND("MenuMove", 32, 2, 1), SOUND("ClockTick", 32, 2, 1), SOUND("Fireball", 32, 2, 1), SOUND("PuppyBeat", 30, 2, 1), SOUND("MysticIncant", 32, 4, 1), }; crispy-doom-crispy-doom-5.6.4/src/hexen/sounds.h000066400000000000000000000165351360717211000216300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __SOUNDSH__ #define __SOUNDSH__ #include "i_sound.h" #define MAX_SND_DIST 2025 #define MAX_CHANNELS 16 // Music identifiers typedef enum { 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_e4m1, mus_titl, mus_intr, mus_cptd, NUMMUSIC } musicenum_t; // Sound identifiers typedef enum { SFX_NONE, SFX_PLAYER_FIGHTER_NORMAL_DEATH, // class specific death screams SFX_PLAYER_FIGHTER_CRAZY_DEATH, SFX_PLAYER_FIGHTER_EXTREME1_DEATH, SFX_PLAYER_FIGHTER_EXTREME2_DEATH, SFX_PLAYER_FIGHTER_EXTREME3_DEATH, SFX_PLAYER_FIGHTER_BURN_DEATH, SFX_PLAYER_CLERIC_NORMAL_DEATH, SFX_PLAYER_CLERIC_CRAZY_DEATH, SFX_PLAYER_CLERIC_EXTREME1_DEATH, SFX_PLAYER_CLERIC_EXTREME2_DEATH, SFX_PLAYER_CLERIC_EXTREME3_DEATH, SFX_PLAYER_CLERIC_BURN_DEATH, SFX_PLAYER_MAGE_NORMAL_DEATH, SFX_PLAYER_MAGE_CRAZY_DEATH, SFX_PLAYER_MAGE_EXTREME1_DEATH, SFX_PLAYER_MAGE_EXTREME2_DEATH, SFX_PLAYER_MAGE_EXTREME3_DEATH, SFX_PLAYER_MAGE_BURN_DEATH, SFX_PLAYER_FIGHTER_PAIN, SFX_PLAYER_CLERIC_PAIN, SFX_PLAYER_MAGE_PAIN, SFX_PLAYER_FIGHTER_GRUNT, SFX_PLAYER_CLERIC_GRUNT, SFX_PLAYER_MAGE_GRUNT, SFX_PLAYER_LAND, SFX_PLAYER_POISONCOUGH, SFX_PLAYER_FIGHTER_FALLING_SCREAM, // class specific falling screams SFX_PLAYER_CLERIC_FALLING_SCREAM, SFX_PLAYER_MAGE_FALLING_SCREAM, SFX_PLAYER_FALLING_SPLAT, SFX_PLAYER_FIGHTER_FAILED_USE, SFX_PLAYER_CLERIC_FAILED_USE, SFX_PLAYER_MAGE_FAILED_USE, SFX_PLATFORM_START, SFX_PLATFORM_STARTMETAL, SFX_PLATFORM_STOP, SFX_STONE_MOVE, SFX_METAL_MOVE, SFX_DOOR_OPEN, SFX_DOOR_LOCKED, SFX_DOOR_METAL_OPEN, SFX_DOOR_METAL_CLOSE, SFX_DOOR_LIGHT_CLOSE, SFX_DOOR_HEAVY_CLOSE, SFX_DOOR_CREAK, SFX_PICKUP_WEAPON, SFX_PICKUP_ARTIFACT, SFX_PICKUP_KEY, SFX_PICKUP_ITEM, SFX_PICKUP_PIECE, SFX_WEAPON_BUILD, SFX_ARTIFACT_USE, SFX_ARTIFACT_BLAST, SFX_TELEPORT, SFX_THUNDER_CRASH, SFX_FIGHTER_PUNCH_MISS, SFX_FIGHTER_PUNCH_HITTHING, SFX_FIGHTER_PUNCH_HITWALL, SFX_FIGHTER_GRUNT, SFX_FIGHTER_AXE_HITTHING, SFX_FIGHTER_HAMMER_MISS, SFX_FIGHTER_HAMMER_HITTHING, SFX_FIGHTER_HAMMER_HITWALL, SFX_FIGHTER_HAMMER_CONTINUOUS, SFX_FIGHTER_HAMMER_EXPLODE, SFX_FIGHTER_SWORD_FIRE, SFX_FIGHTER_SWORD_EXPLODE, SFX_CLERIC_CSTAFF_FIRE, SFX_CLERIC_CSTAFF_EXPLODE, SFX_CLERIC_CSTAFF_HITTHING, SFX_CLERIC_FLAME_FIRE, SFX_CLERIC_FLAME_EXPLODE, SFX_CLERIC_FLAME_CIRCLE, SFX_MAGE_WAND_FIRE, SFX_MAGE_LIGHTNING_FIRE, SFX_MAGE_LIGHTNING_ZAP, SFX_MAGE_LIGHTNING_CONTINUOUS, SFX_MAGE_LIGHTNING_READY, SFX_MAGE_SHARDS_FIRE, SFX_MAGE_SHARDS_EXPLODE, SFX_MAGE_STAFF_FIRE, SFX_MAGE_STAFF_EXPLODE, SFX_SWITCH1, SFX_SWITCH2, SFX_SERPENT_SIGHT, SFX_SERPENT_ACTIVE, SFX_SERPENT_PAIN, SFX_SERPENT_ATTACK, SFX_SERPENT_MELEEHIT, SFX_SERPENT_DEATH, SFX_SERPENT_BIRTH, SFX_SERPENTFX_CONTINUOUS, SFX_SERPENTFX_HIT, SFX_POTTERY_EXPLODE, SFX_DRIP, SFX_CENTAUR_SIGHT, SFX_CENTAUR_ACTIVE, SFX_CENTAUR_PAIN, SFX_CENTAUR_ATTACK, SFX_CENTAUR_DEATH, SFX_CENTAURLEADER_ATTACK, SFX_CENTAUR_MISSILE_EXPLODE, SFX_WIND, SFX_BISHOP_SIGHT, SFX_BISHOP_ACTIVE, SFX_BISHOP_PAIN, SFX_BISHOP_ATTACK, SFX_BISHOP_DEATH, SFX_BISHOP_MISSILE_EXPLODE, SFX_BISHOP_BLUR, SFX_DEMON_SIGHT, SFX_DEMON_ACTIVE, SFX_DEMON_PAIN, SFX_DEMON_ATTACK, SFX_DEMON_MISSILE_FIRE, SFX_DEMON_MISSILE_EXPLODE, SFX_DEMON_DEATH, SFX_WRAITH_SIGHT, SFX_WRAITH_ACTIVE, SFX_WRAITH_PAIN, SFX_WRAITH_ATTACK, SFX_WRAITH_MISSILE_FIRE, SFX_WRAITH_MISSILE_EXPLODE, SFX_WRAITH_DEATH, SFX_PIG_ACTIVE1, SFX_PIG_ACTIVE2, SFX_PIG_PAIN, SFX_PIG_ATTACK, SFX_PIG_DEATH, SFX_MAULATOR_SIGHT, SFX_MAULATOR_ACTIVE, SFX_MAULATOR_PAIN, SFX_MAULATOR_HAMMER_SWING, SFX_MAULATOR_HAMMER_HIT, SFX_MAULATOR_MISSILE_HIT, SFX_MAULATOR_DEATH, SFX_FREEZE_DEATH, SFX_FREEZE_SHATTER, SFX_ETTIN_SIGHT, SFX_ETTIN_ACTIVE, SFX_ETTIN_PAIN, SFX_ETTIN_ATTACK, SFX_ETTIN_DEATH, SFX_FIRED_SPAWN, SFX_FIRED_ACTIVE, SFX_FIRED_PAIN, SFX_FIRED_ATTACK, SFX_FIRED_MISSILE_HIT, SFX_FIRED_DEATH, SFX_ICEGUY_SIGHT, SFX_ICEGUY_ACTIVE, SFX_ICEGUY_ATTACK, SFX_ICEGUY_FX_EXPLODE, SFX_SORCERER_SIGHT, SFX_SORCERER_ACTIVE, SFX_SORCERER_PAIN, SFX_SORCERER_SPELLCAST, SFX_SORCERER_BALLWOOSH, SFX_SORCERER_DEATHSCREAM, SFX_SORCERER_BISHOPSPAWN, SFX_SORCERER_BALLPOP, SFX_SORCERER_BALLBOUNCE, SFX_SORCERER_BALLEXPLODE, SFX_SORCERER_BIGBALLEXPLODE, SFX_SORCERER_HEADSCREAM, SFX_DRAGON_SIGHT, SFX_DRAGON_ACTIVE, SFX_DRAGON_WINGFLAP, SFX_DRAGON_ATTACK, SFX_DRAGON_PAIN, SFX_DRAGON_DEATH, SFX_DRAGON_FIREBALL_EXPLODE, SFX_KORAX_SIGHT, SFX_KORAX_ACTIVE, SFX_KORAX_PAIN, SFX_KORAX_ATTACK, SFX_KORAX_COMMAND, SFX_KORAX_DEATH, SFX_KORAX_STEP, SFX_THRUSTSPIKE_RAISE, SFX_THRUSTSPIKE_LOWER, SFX_STAINEDGLASS_SHATTER, SFX_FLECHETTE_BOUNCE, SFX_FLECHETTE_EXPLODE, SFX_LAVA_MOVE, SFX_WATER_MOVE, SFX_ICE_STARTMOVE, SFX_EARTH_STARTMOVE, SFX_WATER_SPLASH, SFX_LAVA_SIZZLE, SFX_SLUDGE_GLOOP, SFX_CHOLY_FIRE, SFX_SPIRIT_ACTIVE, SFX_SPIRIT_ATTACK, SFX_SPIRIT_DIE, SFX_VALVE_TURN, SFX_ROPE_PULL, SFX_FLY_BUZZ, SFX_IGNITE, SFX_PUZZLE_SUCCESS, SFX_PUZZLE_FAIL_FIGHTER, SFX_PUZZLE_FAIL_CLERIC, SFX_PUZZLE_FAIL_MAGE, SFX_EARTHQUAKE, SFX_BELLRING, SFX_TREE_BREAK, SFX_TREE_EXPLODE, SFX_SUITOFARMOR_BREAK, SFX_POISONSHROOM_PAIN, SFX_POISONSHROOM_DEATH, SFX_AMBIENT1, SFX_AMBIENT2, SFX_AMBIENT3, SFX_AMBIENT4, SFX_AMBIENT5, SFX_AMBIENT6, SFX_AMBIENT7, SFX_AMBIENT8, SFX_AMBIENT9, SFX_AMBIENT10, SFX_AMBIENT11, SFX_AMBIENT12, SFX_AMBIENT13, SFX_AMBIENT14, SFX_AMBIENT15, SFX_STARTUP_TICK, SFX_SWITCH_OTHERLEVEL, SFX_RESPAWN, SFX_KORAX_VOICE_1, SFX_KORAX_VOICE_2, SFX_KORAX_VOICE_3, SFX_KORAX_VOICE_4, SFX_KORAX_VOICE_5, SFX_KORAX_VOICE_6, SFX_KORAX_VOICE_7, SFX_KORAX_VOICE_8, SFX_KORAX_VOICE_9, SFX_BAT_SCREAM, SFX_CHAT, SFX_MENU_MOVE, SFX_CLOCK_TICK, SFX_FIREBALL, SFX_PUPPYBEAT, SFX_MYSTICINCANT, NUMSFX } sfxenum_t; #endif crispy-doom-crispy-doom-5.6.4/src/hexen/st_start.c000066400000000000000000000201651360717211000221450ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include #include "config.h" #include "h2def.h" #include "i_system.h" #include "i_video.h" #include "i_videohr.h" #include "s_sound.h" #include "st_start.h" // MACROS ------------------------------------------------------------------ #define ST_MAX_NOTCHES 32 #define ST_NOTCH_WIDTH 16 #define ST_NOTCH_HEIGHT 23 #define ST_PROGRESS_X 64 // Start of notches x screen pos. #define ST_PROGRESS_Y 441 // Start of notches y screen pos. #define ST_NETPROGRESS_X 288 #define ST_NETPROGRESS_Y 32 #define ST_NETNOTCH_WIDTH 8 #define ST_NETNOTCH_HEIGHT 16 #define ST_MAX_NETNOTCHES 8 byte *ST_LoadScreen(void); void ST_UpdateNotches(int notchPosition); void ST_UpdateNetNotches(int notchPosition); // PRIVATE DATA DEFINITIONS ------------------------------------------------ static const byte *bitmap = NULL; int graphical_startup = 0; static boolean using_graphical_startup; static const byte notchTable[] = { // plane 0 0x00, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x03, 0xC0, 0x0F, 0x90, 0x1B, 0x68, 0x3D, 0xBC, 0x3F, 0xFC, 0x20, 0x08, 0x20, 0x08, 0x2F, 0xD8, 0x37, 0xD8, 0x37, 0xF8, 0x1F, 0xF8, 0x1C, 0x50, // plane 1 0x00, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0xA0, 0x30, 0x6C, 0x24, 0x94, 0x42, 0x4A, 0x60, 0x0E, 0x60, 0x06, 0x7F, 0xF6, 0x7F, 0xF6, 0x7F, 0xF6, 0x5E, 0xF6, 0x38, 0x16, 0x23, 0xAC, // plane 2 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x03, 0xE0, 0x30, 0x6C, 0x24, 0x94, 0x52, 0x6A, 0x7F, 0xFE, 0x60, 0x0E, 0x60, 0x0E, 0x6F, 0xD6, 0x77, 0xD6, 0x56, 0xF6, 0x38, 0x36, 0x23, 0xAC, // plane 3 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0x80, 0x02, 0x40, 0x0F, 0x90, 0x1B, 0x68, 0x3D, 0xB4, 0x1F, 0xF0, 0x1F, 0xF8, 0x1F, 0xF8, 0x10, 0x28, 0x08, 0x28, 0x29, 0x08, 0x07, 0xE8, 0x1C, 0x50 }; // Red Network Progress notches static const byte netnotchTable[] = { // plane 0 0x80, 0x50, 0xD0, 0xf0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xD0, 0xF0, 0xC0, 0x70, 0x50, 0x80, 0x60, // plane 1 0x60, 0xE0, 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, 0xA0, 0xA0, 0xE0, 0x60, 0x00, // plane 2 0x80, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x80, 0x60, // plane 3 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // CODE -------------------------------------------------------------------- //-------------------------------------------------------------------------- // // Startup Screen Functions // //-------------------------------------------------------------------------- //========================================================================== // // ST_Init - Do the startup screen // //========================================================================== void ST_Init(void) { byte *pal; byte *buffer; using_graphical_startup = false; if (graphical_startup && !debugmode && !testcontrols) { I_SetWindowTitleHR("Hexen startup - " PACKAGE_STRING); // Set 640x480x16 mode if (I_SetVideoModeHR()) { using_graphical_startup = true; I_InitWindowIcon(); S_StartSongName("orb", true); I_ClearScreenHR(); I_InitPaletteHR(); I_BlackPaletteHR(); // Load graphic buffer = ST_LoadScreen(); pal = buffer; bitmap = buffer + 16 * 3; I_SlamHR(bitmap); I_FadeToPaletteHR(pal); Z_Free(buffer); } } } void ST_Done(void) { if (using_graphical_startup) { I_ClearScreenHR(); I_UnsetVideoModeHR(); } } //========================================================================== // // ST_UpdateNotches // //========================================================================== void ST_UpdateNotches(int notchPosition) { int x = ST_PROGRESS_X + notchPosition * ST_NOTCH_WIDTH; int y = ST_PROGRESS_Y; I_SlamBlockHR(x, y, ST_NOTCH_WIDTH, ST_NOTCH_HEIGHT, notchTable); } //========================================================================== // // ST_UpdateNetNotches - indicates network progress // //========================================================================== void ST_UpdateNetNotches(int notchPosition) { int x = ST_NETPROGRESS_X + notchPosition * ST_NETNOTCH_WIDTH; int y = ST_NETPROGRESS_Y; I_SlamBlockHR(x, y, ST_NETNOTCH_WIDTH, ST_NETNOTCH_HEIGHT, netnotchTable); } //========================================================================== // // ST_Progress - increments progress indicator // //========================================================================== void ST_Progress(void) { // Check for ESC press -- during startup all events eaten here if (I_CheckAbortHR()) { I_Quit(); } if (using_graphical_startup) { static int notchPosition = 0; if (notchPosition < ST_MAX_NOTCHES) { ST_UpdateNotches(notchPosition); S_StartSound(NULL, SFX_STARTUP_TICK); //I_Sleep(1000); notchPosition++; } } printf("."); } //========================================================================== // // ST_NetProgress - indicates network progress // //========================================================================== void ST_NetProgress(void) { printf("*"); if (using_graphical_startup) { static int netnotchPosition = 0; if (netnotchPosition < ST_MAX_NETNOTCHES) { ST_UpdateNetNotches(netnotchPosition); S_StartSound(NULL, SFX_DRIP); netnotchPosition++; } } } //========================================================================== // // ST_NetDone - net progress complete // //========================================================================== void ST_NetDone(void) { if (using_graphical_startup) { S_StartSound(NULL, SFX_PICKUP_WEAPON); } } //========================================================================== // // ST_Message - gives debug message // //========================================================================== void ST_Message(const char *message, ...) { va_list argptr; va_start(argptr, message); vprintf(message, argptr); va_end(argptr); } //========================================================================== // // ST_RealMessage - gives user message // //========================================================================== void ST_RealMessage(const char *message, ...) { va_list argptr; va_start(argptr, message); vprintf(message, argptr); va_end(argptr); } //========================================================================== // // ST_LoadScreen - loads startup graphic // //========================================================================== byte *ST_LoadScreen(void) { int length, lump; byte *buffer; lump = W_GetNumForName("STARTUP"); length = W_LumpLength(lump); buffer = (byte *) Z_Malloc(length, PU_STATIC, NULL); W_ReadLump(lump, buffer); return (buffer); } crispy-doom-crispy-doom-5.6.4/src/hexen/st_start.h000066400000000000000000000026051360717211000221510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef STSTART_H #define STSTART_H #include "doomtype.h" // HEADER FILES ------------------------------------------------------------ // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- extern void ST_Init(void); extern void ST_Done(void); extern void ST_Message(const char *message, ...) PRINTF_ATTR(1, 2); extern void ST_RealMessage(const char *message, ...) PRINTF_ATTR(1, 2); extern void ST_Progress(void); extern void ST_NetProgress(void); extern void ST_NetDone(void); // PUBLIC DATA DECLARATIONS ------------------------------------------------ extern int graphical_startup; #endif crispy-doom-crispy-doom-5.6.4/src/hexen/sv_save.c000066400000000000000000002245151360717211000217550ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // HEADER FILES ------------------------------------------------------------ #include "h2def.h" #include "i_system.h" #include "m_misc.h" #include "i_swap.h" #include "p_local.h" // MACROS ------------------------------------------------------------------ #define MAX_TARGET_PLAYERS 512 #define MOBJ_NULL -1 #define MOBJ_XX_PLAYER -2 #define MAX_MAPS 99 #define BASE_SLOT 6 #define REBORN_SLOT 7 #define REBORN_DESCRIPTION "TEMP GAME" #define MAX_THINKER_SIZE 256 // TYPES ------------------------------------------------------------------- typedef enum { ASEG_GAME_HEADER = 101, ASEG_MAP_HEADER, ASEG_WORLD, ASEG_POLYOBJS, ASEG_MOBJS, ASEG_THINKERS, ASEG_SCRIPTS, ASEG_PLAYERS, ASEG_SOUNDS, ASEG_MISC, ASEG_END } gameArchiveSegment_t; typedef enum { TC_NULL, TC_MOVE_CEILING, TC_VERTICAL_DOOR, TC_MOVE_FLOOR, TC_PLAT_RAISE, TC_INTERPRET_ACS, TC_FLOOR_WAGGLE, TC_LIGHT, TC_PHASE, TC_BUILD_PILLAR, TC_ROTATE_POLY, TC_MOVE_POLY, TC_POLY_DOOR } thinkClass_t; typedef struct { thinkClass_t tClass; think_t thinkerFunc; void (*writeFunc)(); void (*readFunc)(); void (*restoreFunc) (); size_t size; } thinkInfo_t; typedef struct { thinker_t thinker; sector_t *sector; } ssthinker_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- void P_SpawnPlayer(mapthing_t * mthing); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void ArchiveWorld(void); static void UnarchiveWorld(void); static void ArchivePolyobjs(void); static void UnarchivePolyobjs(void); static void ArchiveMobjs(void); static void UnarchiveMobjs(void); static void ArchiveThinkers(void); static void UnarchiveThinkers(void); static void ArchiveScripts(void); static void UnarchiveScripts(void); static void ArchivePlayers(void); static void UnarchivePlayers(void); static void ArchiveSounds(void); static void UnarchiveSounds(void); static void ArchiveMisc(void); static void UnarchiveMisc(void); static void SetMobjArchiveNums(void); static void RemoveAllThinkers(void); static int GetMobjNum(mobj_t * mobj); static void SetMobjPtr(mobj_t **ptr, unsigned int archiveNum); static void RestoreSSThinker(ssthinker_t * sst); static void RestorePlatRaise(plat_t * plat); static void RestoreMoveCeiling(ceiling_t * ceiling); static void AssertSegment(gameArchiveSegment_t segType); static void ClearSaveSlot(int slot); static void CopySaveSlot(int sourceSlot, int destSlot); static void CopyFile(char *sourceName, char *destName); static boolean ExistingFile(char *name); static void SV_OpenRead(char *fileName); static void SV_OpenWrite(char *fileName); static void SV_Close(void); static void SV_Read(void *buffer, int size); static byte SV_ReadByte(void); static uint16_t SV_ReadWord(void); static uint32_t SV_ReadLong(void); static void *SV_ReadPtr(void); static void SV_Write(const void *buffer, int size); static void SV_WriteByte(byte val); static void SV_WriteWord(unsigned short val); static void SV_WriteLong(unsigned int val); static void SV_WritePtr(void *ptr); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- #define DEFAULT_SAVEPATH "hexndata/" char *SavePath = DEFAULT_SAVEPATH; int vanilla_savegame_limit = 1; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int MobjCount; static mobj_t **MobjList; static mobj_t ***TargetPlayerAddrs; static int TargetPlayerCount; static boolean SavingPlayers; static FILE *SavingFP; // CODE -------------------------------------------------------------------- // Autogenerated functions for reading/writing structs: // // acsstore_t // static void StreamIn_acsstore_t(acsstore_t *str) { int i; // int map; str->map = SV_ReadLong(); // int script; str->script = SV_ReadLong(); // byte args[4]; for (i=0; i<4; ++i) { str->args[i] = SV_ReadByte(); } } static void StreamOut_acsstore_t(acsstore_t *str) { int i; // int map; SV_WriteLong(str->map); // int script; SV_WriteLong(str->script); // byte args[4]; for (i=0; i<4; ++i) { SV_WriteByte(str->args[i]); } } // // ticcmd_t // (this is based on the Vanilla definition of the struct) // static void StreamIn_ticcmd_t(ticcmd_t *str) { // char forwardmove; str->forwardmove = SV_ReadByte(); // char sidemove; str->sidemove = SV_ReadByte(); // short angleturn; str->angleturn = SV_ReadWord(); // short consistancy; str->consistancy = SV_ReadWord(); // byte chatchar; str->chatchar = SV_ReadByte(); // byte buttons; str->buttons = SV_ReadByte(); // byte lookfly; str->lookfly = SV_ReadByte(); // byte arti; str->arti = SV_ReadByte(); } static void StreamOut_ticcmd_t(ticcmd_t *str) { // char forwardmove; SV_WriteByte(str->forwardmove); // char sidemove; SV_WriteByte(str->sidemove); // short angleturn; SV_WriteWord(str->angleturn); // short consistancy; SV_WriteWord(str->consistancy); // byte chatchar; SV_WriteByte(str->chatchar); // byte buttons; SV_WriteByte(str->buttons); // byte lookfly; SV_WriteByte(str->lookfly); // byte arti; SV_WriteByte(str->arti); } // // inventory_t // static void StreamIn_inventory_t(inventory_t *str) { // int type; str->type = SV_ReadLong(); // int count; str->count = SV_ReadLong(); } static void StreamOut_inventory_t(inventory_t *str) { // int type; SV_WriteLong(str->type); // int count; SV_WriteLong(str->count); } // // pspdef_t // static void StreamIn_pspdef_t(pspdef_t *str) { int state_num; // state_t *state; // This is a pointer; it is stored as an index into the states table. state_num = SV_ReadLong(); if (state_num != 0) { str->state = states + state_num; } else { str->state = NULL; } // int tics; str->tics = SV_ReadLong(); // fixed_t sx, sy; str->sx = SV_ReadLong(); str->sy = SV_ReadLong(); } static void StreamOut_pspdef_t(pspdef_t *str) { // state_t *state; // This is a pointer; store the index in the states table, // rather than the pointer itself. if (str->state != NULL) { SV_WriteLong(str->state - states); } else { SV_WriteLong(0); } // int tics; SV_WriteLong(str->tics); // fixed_t sx, sy; SV_WriteLong(str->sx); SV_WriteLong(str->sy); } // // player_t // static void StreamIn_player_t(player_t *str) { int i; // mobj_t *mo; // Pointer value is reset on load. str->mo = SV_ReadPtr(); str->mo = NULL; // playerstate_t playerstate; str->playerstate = SV_ReadLong(); // ticcmd_t cmd; StreamIn_ticcmd_t(&str->cmd); // pclass_t class; str->class = SV_ReadLong(); // fixed_t viewz; str->viewz = SV_ReadLong(); // fixed_t viewheight; str->viewheight = SV_ReadLong(); // fixed_t deltaviewheight; str->deltaviewheight = SV_ReadLong(); // fixed_t bob; str->bob = SV_ReadLong(); // int flyheight; str->flyheight = SV_ReadLong(); // int lookdir; str->lookdir = SV_ReadLong(); // boolean centering; str->centering = SV_ReadLong(); // int health; str->health = SV_ReadLong(); // int armorpoints[NUMARMOR]; for (i=0; iarmorpoints[i] = SV_ReadLong(); } // inventory_t inventory[NUMINVENTORYSLOTS]; for (i=0; iinventory[i]); } // artitype_t readyArtifact; str->readyArtifact = SV_ReadLong(); // int artifactCount; str->artifactCount = SV_ReadLong(); // int inventorySlotNum; str->inventorySlotNum = SV_ReadLong(); // int powers[NUMPOWERS]; for (i=0; ipowers[i] = SV_ReadLong(); } // int keys; str->keys = SV_ReadLong(); // int pieces; str->pieces = SV_ReadLong(); // signed int frags[MAXPLAYERS]; for (i=0; ifrags[i] = SV_ReadLong(); } // weapontype_t readyweapon; str->readyweapon = SV_ReadLong(); // weapontype_t pendingweapon; str->pendingweapon = SV_ReadLong(); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i] = SV_ReadLong(); } // int mana[NUMMANA]; for (i=0; imana[i] = SV_ReadLong(); } // int attackdown, usedown; str->attackdown = SV_ReadLong(); str->usedown = SV_ReadLong(); // int cheats; str->cheats = SV_ReadLong(); // int refire; str->refire = SV_ReadLong(); // int killcount, itemcount, secretcount; str->killcount = SV_ReadLong(); str->itemcount = SV_ReadLong(); str->secretcount = SV_ReadLong(); // char message[80]; for (i=0; i<80; ++i) { str->message[i] = SV_ReadByte(); } // int messageTics; str->messageTics = SV_ReadLong(); // short ultimateMessage; str->ultimateMessage = SV_ReadWord(); // short yellowMessage; str->yellowMessage = SV_ReadWord(); // int damagecount, bonuscount; str->damagecount = SV_ReadLong(); str->bonuscount = SV_ReadLong(); // int poisoncount; str->poisoncount = SV_ReadLong(); // mobj_t *poisoner; // Pointer value is reset. str->poisoner = SV_ReadPtr(); str->poisoner = NULL; // mobj_t *attacker; // Pointer value is reset. str->attacker = SV_ReadPtr(); str->attacker = NULL; // int extralight; str->extralight = SV_ReadLong(); // int fixedcolormap; str->fixedcolormap = SV_ReadLong(); // int colormap; str->colormap = SV_ReadLong(); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // int morphTics; str->morphTics = SV_ReadLong(); // unsigned int jumpTics; str->jumpTics = SV_ReadLong(); // unsigned int worldTimer; str->worldTimer = SV_ReadLong(); } static void StreamOut_player_t(player_t *str) { int i; // mobj_t *mo; SV_WritePtr(str->mo); // playerstate_t playerstate; SV_WriteLong(str->playerstate); // ticcmd_t cmd; StreamOut_ticcmd_t(&str->cmd); // pclass_t class; SV_WriteLong(str->class); // fixed_t viewz; SV_WriteLong(str->viewz); // fixed_t viewheight; SV_WriteLong(str->viewheight); // fixed_t deltaviewheight; SV_WriteLong(str->deltaviewheight); // fixed_t bob; SV_WriteLong(str->bob); // int flyheight; SV_WriteLong(str->flyheight); // int lookdir; SV_WriteLong(str->lookdir); // boolean centering; SV_WriteLong(str->centering); // int health; SV_WriteLong(str->health); // int armorpoints[NUMARMOR]; for (i=0; iarmorpoints[i]); } // inventory_t inventory[NUMINVENTORYSLOTS]; for (i=0; iinventory[i]); } // artitype_t readyArtifact; SV_WriteLong(str->readyArtifact); // int artifactCount; SV_WriteLong(str->artifactCount); // int inventorySlotNum; SV_WriteLong(str->inventorySlotNum); // int powers[NUMPOWERS]; for (i=0; ipowers[i]); } // int keys; SV_WriteLong(str->keys); // int pieces; SV_WriteLong(str->pieces); // signed int frags[MAXPLAYERS]; for (i=0; ifrags[i]); } // weapontype_t readyweapon; SV_WriteLong(str->readyweapon); // weapontype_t pendingweapon; SV_WriteLong(str->pendingweapon); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i]); } // int mana[NUMMANA]; for (i=0; imana[i]); } // int attackdown, usedown; SV_WriteLong(str->attackdown); SV_WriteLong(str->usedown); // int cheats; SV_WriteLong(str->cheats); // int refire; SV_WriteLong(str->refire); // int killcount, itemcount, secretcount; SV_WriteLong(str->killcount); SV_WriteLong(str->itemcount); SV_WriteLong(str->secretcount); // char message[80]; for (i=0; i<80; ++i) { SV_WriteByte(str->message[i]); } // int messageTics; SV_WriteLong(str->messageTics); // short ultimateMessage; SV_WriteWord(str->ultimateMessage); // short yellowMessage; SV_WriteWord(str->yellowMessage); // int damagecount, bonuscount; SV_WriteLong(str->damagecount); SV_WriteLong(str->bonuscount); // int poisoncount; SV_WriteLong(str->poisoncount); // mobj_t *poisoner; SV_WritePtr(str->poisoner); // mobj_t *attacker; SV_WritePtr(str->attacker); // int extralight; SV_WriteLong(str->extralight); // int fixedcolormap; SV_WriteLong(str->fixedcolormap); // int colormap; SV_WriteLong(str->colormap); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // int morphTics; SV_WriteLong(str->morphTics); // unsigned int jumpTics; SV_WriteLong(str->jumpTics); // unsigned int worldTimer; SV_WriteLong(str->worldTimer); } // // thinker_t // static void StreamIn_thinker_t(thinker_t *str) { // struct thinker_s *prev, *next; // Pointers are discarded: str->prev = SV_ReadPtr(); str->prev = NULL; str->next = SV_ReadPtr(); str->next = NULL; // think_t function; // Function pointer is discarded: str->function = SV_ReadPtr(); str->function = NULL; } static void StreamOut_thinker_t(thinker_t *str) { // struct thinker_s *prev, *next; SV_WritePtr(str->prev); SV_WritePtr(str->next); // think_t function; SV_WritePtr(&str->function); } // // mobj_t // static void StreamInMobjSpecials(mobj_t *mobj) { unsigned int special1, special2; special1 = SV_ReadLong(); special2 = SV_ReadLong(); mobj->special1.i = special1; mobj->special2.i = special2; switch (mobj->type) { // Just special1 case MT_BISH_FX: case MT_HOLY_FX: case MT_DRAGON: case MT_THRUSTFLOOR_UP: case MT_THRUSTFLOOR_DOWN: case MT_MINOTAUR: case MT_SORCFX1: SetMobjPtr(&mobj->special1.m, special1); break; // Just special2 case MT_LIGHTNING_FLOOR: case MT_LIGHTNING_ZAP: SetMobjPtr(&mobj->special2.m, special2); break; // Both special1 and special2 case MT_HOLY_TAIL: case MT_LIGHTNING_CEILING: SetMobjPtr(&mobj->special1.m, special1); SetMobjPtr(&mobj->special2.m, special2); break; default: break; } } static void StreamIn_mobj_t(mobj_t *str) { unsigned int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // fixed_t x, y, z; str->x = SV_ReadLong(); str->y = SV_ReadLong(); str->z = SV_ReadLong(); // struct mobj_s *snext, *sprev; // Pointer values are discarded: str->snext = SV_ReadPtr(); str->snext = NULL; str->sprev = SV_ReadPtr(); str->sprev = NULL; // angle_t angle; str->angle = SV_ReadLong(); // spritenum_t sprite; str->sprite = SV_ReadLong(); // int frame; str->frame = SV_ReadLong(); // struct mobj_s *bnext, *bprev; // Values are read but discarded; this will be restored when the thing's // position is set. str->bnext = SV_ReadPtr(); str->bnext = NULL; str->bprev = SV_ReadPtr(); str->bprev = NULL; // struct subsector_s *subsector; // Read but discard: pointer will be restored when thing position is set. str->subsector = SV_ReadPtr(); str->subsector = NULL; // fixed_t floorz, ceilingz; str->floorz = SV_ReadLong(); str->ceilingz = SV_ReadLong(); // fixed_t floorpic; str->floorpic = SV_ReadLong(); // fixed_t radius, height; str->radius = SV_ReadLong(); str->height = SV_ReadLong(); // fixed_t momx, momy, momz; str->momx = SV_ReadLong(); str->momy = SV_ReadLong(); str->momz = SV_ReadLong(); // int validcount; str->validcount = SV_ReadLong(); // mobjtype_t type; str->type = SV_ReadLong(); // mobjinfo_t *info; // Pointer value is read but discarded. str->info = SV_ReadPtr(); str->info = NULL; // int tics; str->tics = SV_ReadLong(); // state_t *state; // Restore as index into states table. i = SV_ReadLong(); str->state = &states[i]; // int damage; str->damage = SV_ReadLong(); // int flags; str->flags = SV_ReadLong(); // int flags2; str->flags2 = SV_ReadLong(); // specialval_t special1; // specialval_t special2; // Read in special values: there are special cases to deal with with // mobj pointers. StreamInMobjSpecials(str); // int health; str->health = SV_ReadLong(); // int movedir; str->movedir = SV_ReadLong(); // int movecount; str->movecount = SV_ReadLong(); // struct mobj_s *target; i = SV_ReadLong(); SetMobjPtr(&str->target, i); // int reactiontime; str->reactiontime = SV_ReadLong(); // int threshold; str->threshold = SV_ReadLong(); // struct player_s *player; // Saved as player number. i = SV_ReadLong(); if (i == 0) { str->player = NULL; } else { str->player = &players[i - 1]; str->player->mo = str; } // int lastlook; str->lastlook = SV_ReadLong(); // fixed_t floorclip; str->floorclip = SV_ReadLong(); // int archiveNum; str->archiveNum = SV_ReadLong(); // short tid; str->tid = SV_ReadWord(); // byte special; str->special = SV_ReadByte(); // byte args[5]; for (i=0; i<5; ++i) { str->args[i] = SV_ReadByte(); } } static void StreamOutMobjSpecials(mobj_t *mobj) { unsigned int special1, special2; boolean corpse; corpse = (mobj->flags & MF_CORPSE) != 0; special1 = mobj->special1.i; special2 = mobj->special2.i; switch (mobj->type) { // Just special1 case MT_BISH_FX: case MT_HOLY_FX: case MT_DRAGON: case MT_THRUSTFLOOR_UP: case MT_THRUSTFLOOR_DOWN: case MT_MINOTAUR: case MT_SORCFX1: case MT_MSTAFF_FX2: if (corpse) { special1 = MOBJ_NULL; } else { special1 = GetMobjNum(mobj->special1.m); } break; // Just special2 case MT_LIGHTNING_FLOOR: case MT_LIGHTNING_ZAP: if (corpse) { special2 = MOBJ_NULL; } else { special2 = GetMobjNum(mobj->special2.m); } break; // Both special1 and special2 case MT_HOLY_TAIL: case MT_LIGHTNING_CEILING: if (corpse) { special1 = MOBJ_NULL; special2 = MOBJ_NULL; } else { special1 = GetMobjNum(mobj->special1.m); special2 = GetMobjNum(mobj->special2.m); } break; // Miscellaneous case MT_KORAX: special1 = 0; // Searching index break; default: break; } // Write special values to savegame file. SV_WriteLong(special1); SV_WriteLong(special2); } static void StreamOut_mobj_t(mobj_t *str) { int i; // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // fixed_t x, y, z; SV_WriteLong(str->x); SV_WriteLong(str->y); SV_WriteLong(str->z); // struct mobj_s *snext, *sprev; SV_WritePtr(str->snext); SV_WritePtr(str->sprev); // angle_t angle; SV_WriteLong(str->angle); // spritenum_t sprite; SV_WriteLong(str->sprite); // int frame; SV_WriteLong(str->frame); // struct mobj_s *bnext, *bprev; SV_WritePtr(str->bnext); SV_WritePtr(str->bprev); // struct subsector_s *subsector; SV_WritePtr(str->subsector); // fixed_t floorz, ceilingz; SV_WriteLong(str->floorz); SV_WriteLong(str->ceilingz); // fixed_t floorpic; SV_WriteLong(str->floorpic); // fixed_t radius, height; SV_WriteLong(str->radius); SV_WriteLong(str->height); // fixed_t momx, momy, momz; SV_WriteLong(str->momx); SV_WriteLong(str->momy); SV_WriteLong(str->momz); // int validcount; SV_WriteLong(str->validcount); // mobjtype_t type; SV_WriteLong(str->type); // mobjinfo_t *info; SV_WritePtr(str->info); // int tics; SV_WriteLong(str->tics); // state_t *state; // Save as index into the states table. SV_WriteLong(str->state - states); // int damage; SV_WriteLong(str->damage); // int flags; SV_WriteLong(str->flags); // int flags2; SV_WriteLong(str->flags2); // specialval_t special1; // specialval_t special2; // There are lots of special cases for the special values: StreamOutMobjSpecials(str); // int health; SV_WriteLong(str->health); // int movedir; SV_WriteLong(str->movedir); // int movecount; SV_WriteLong(str->movecount); // struct mobj_s *target; if ((str->flags & MF_CORPSE) != 0) { SV_WriteLong(MOBJ_NULL); } else { SV_WriteLong(GetMobjNum(str->target)); } // int reactiontime; SV_WriteLong(str->reactiontime); // int threshold; SV_WriteLong(str->threshold); // struct player_s *player; // Stored as index into players[] array, if there is a player pointer. if (str->player != NULL) { SV_WriteLong(str->player - players + 1); } else { SV_WriteLong(0); } // int lastlook; SV_WriteLong(str->lastlook); // fixed_t floorclip; SV_WriteLong(str->floorclip); // int archiveNum; SV_WriteLong(str->archiveNum); // short tid; SV_WriteWord(str->tid); // byte special; SV_WriteByte(str->special); // byte args[5]; for (i=0; i<5; ++i) { SV_WriteByte(str->args[i]); } } // // floormove_t // static void StreamIn_floormove_t(floormove_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = sectors + i; // floor_e type; str->type = SV_ReadLong(); // int crush; str->crush = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); // int newspecial; str->newspecial = SV_ReadLong(); // short texture; str->texture = SV_ReadWord(); // fixed_t floordestheight; str->floordestheight = SV_ReadLong(); // fixed_t speed; str->speed = SV_ReadLong(); // int delayCount; str->delayCount = SV_ReadLong(); // int delayTotal; str->delayTotal = SV_ReadLong(); // fixed_t stairsDelayHeight; str->stairsDelayHeight = SV_ReadLong(); // fixed_t stairsDelayHeightDelta; str->stairsDelayHeightDelta = SV_ReadLong(); // fixed_t resetHeight; str->resetHeight = SV_ReadLong(); // short resetDelay; str->resetDelay = SV_ReadWord(); // short resetDelayCount; str->resetDelayCount = SV_ReadWord(); // byte textureChange; str->textureChange = SV_ReadByte(); } static void StreamOut_floormove_t(floormove_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // floor_e type; SV_WriteLong(str->type); // int crush; SV_WriteLong(str->crush); // int direction; SV_WriteLong(str->direction); // int newspecial; SV_WriteLong(str->newspecial); // short texture; SV_WriteWord(str->texture); // fixed_t floordestheight; SV_WriteLong(str->floordestheight); // fixed_t speed; SV_WriteLong(str->speed); // int delayCount; SV_WriteLong(str->delayCount); // int delayTotal; SV_WriteLong(str->delayTotal); // fixed_t stairsDelayHeight; SV_WriteLong(str->stairsDelayHeight); // fixed_t stairsDelayHeightDelta; SV_WriteLong(str->stairsDelayHeightDelta); // fixed_t resetHeight; SV_WriteLong(str->resetHeight); // short resetDelay; SV_WriteWord(str->resetDelay); // short resetDelayCount; SV_WriteWord(str->resetDelayCount); // byte textureChange; SV_WriteByte(str->textureChange); } // // plat_t // static void StreamIn_plat_t(plat_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = sectors + i; // fixed_t speed; str->speed = SV_ReadLong(); // fixed_t low; str->low = SV_ReadLong(); // fixed_t high; str->high = SV_ReadLong(); // int wait; str->wait = SV_ReadLong(); // int count; str->count = SV_ReadLong(); // plat_e status; str->status = SV_ReadLong(); // plat_e oldstatus; str->oldstatus = SV_ReadLong(); // int crush; str->crush = SV_ReadLong(); // int tag; str->tag = SV_ReadLong(); // plattype_e type; str->type = SV_ReadLong(); } static void StreamOut_plat_t(plat_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // fixed_t speed; SV_WriteLong(str->speed); // fixed_t low; SV_WriteLong(str->low); // fixed_t high; SV_WriteLong(str->high); // int wait; SV_WriteLong(str->wait); // int count; SV_WriteLong(str->count); // plat_e status; SV_WriteLong(str->status); // plat_e oldstatus; SV_WriteLong(str->oldstatus); // int crush; SV_WriteLong(str->crush); // int tag; SV_WriteLong(str->tag); // plattype_e type; SV_WriteLong(str->type); } // // ceiling_t // static void StreamIn_ceiling_t(ceiling_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = sectors + i; // ceiling_e type; str->type = SV_ReadLong(); // fixed_t bottomheight, topheight; str->bottomheight = SV_ReadLong(); str->topheight = SV_ReadLong(); // fixed_t speed; str->speed = SV_ReadLong(); // int crush; str->crush = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); // int tag; str->tag = SV_ReadLong(); // int olddirection; str->olddirection = SV_ReadLong(); } static void StreamOut_ceiling_t(ceiling_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // ceiling_e type; SV_WriteLong(str->type); // fixed_t bottomheight, topheight; SV_WriteLong(str->bottomheight); SV_WriteLong(str->topheight); // fixed_t speed; SV_WriteLong(str->speed); // int crush; SV_WriteLong(str->crush); // int direction; SV_WriteLong(str->direction); // int tag; SV_WriteLong(str->tag); // int olddirection; SV_WriteLong(str->olddirection); } // // light_t // static void StreamIn_light_t(light_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = sectors + i; // lighttype_t type; str->type = SV_ReadLong(); // int value1; str->value1 = SV_ReadLong(); // int value2; str->value2 = SV_ReadLong(); // int tics1; str->tics1 = SV_ReadLong(); // int tics2; str->tics2 = SV_ReadLong(); // int count; str->count = SV_ReadLong(); } static void StreamOut_light_t(light_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // lighttype_t type; SV_WriteLong(str->type); // int value1; SV_WriteLong(str->value1); // int value2; SV_WriteLong(str->value2); // int tics1; SV_WriteLong(str->tics1); // int tics2; SV_WriteLong(str->tics2); // int count; SV_WriteLong(str->count); } // // vldoor_t // static void StreamIn_vldoor_t(vldoor_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // vldoor_e type; str->type = SV_ReadLong(); // fixed_t topheight; str->topheight = SV_ReadLong(); // fixed_t speed; str->speed = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); // int topwait; str->topwait = SV_ReadLong(); // int topcountdown; str->topcountdown = SV_ReadLong(); } static void StreamOut_vldoor_t(vldoor_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // vldoor_e type; SV_WriteLong(str->type); // fixed_t topheight; SV_WriteLong(str->topheight); // fixed_t speed; SV_WriteLong(str->speed); // int direction; SV_WriteLong(str->direction); // int topwait; SV_WriteLong(str->topwait); // int topcountdown; SV_WriteLong(str->topcountdown); } // // phase_t // static void StreamIn_phase_t(phase_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // int index; str->index = SV_ReadLong(); // int base; str->base = SV_ReadLong(); } static void StreamOut_phase_t(phase_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // int index; SV_WriteLong(str->index); // int base; SV_WriteLong(str->base); } // // acs_t // static void StreamIn_acs_t(acs_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // mobj_t *activator; i = SV_ReadLong(); SetMobjPtr(&str->activator, i); // line_t *line; i = SV_ReadLong(); if (i != -1) { str->line = &lines[i]; } else { str->line = NULL; } // int side; str->side = SV_ReadLong(); // int number; str->number = SV_ReadLong(); // int infoIndex; str->infoIndex = SV_ReadLong(); // int delayCount; str->delayCount = SV_ReadLong(); // int stack[ACS_STACK_DEPTH]; for (i=0; istack[i] = SV_ReadLong(); } // int stackPtr; str->stackPtr = SV_ReadLong(); // int vars[MAX_ACS_SCRIPT_VARS]; for (i=0; ivars[i] = SV_ReadLong(); } // int *ip; str->ip = SV_ReadLong(); } static void StreamOut_acs_t(acs_t *str) { int i; // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // mobj_t *activator; SV_WriteLong(GetMobjNum(str->activator)); // line_t *line; if (str->line != NULL) { SV_WriteLong(str->line - lines); } else { SV_WriteLong(-1); } // int side; SV_WriteLong(str->side); // int number; SV_WriteLong(str->number); // int infoIndex; SV_WriteLong(str->infoIndex); // int delayCount; SV_WriteLong(str->delayCount); // int stack[ACS_STACK_DEPTH]; for (i=0; istack[i]); } // int stackPtr; SV_WriteLong(str->stackPtr); // int vars[MAX_ACS_SCRIPT_VARS]; for (i=0; ivars[i]); } // int *ip; SV_WriteLong(str->ip); } // // polyevent_t // static void StreamIn_polyevent_t(polyevent_t *str) { // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // int polyobj; str->polyobj = SV_ReadLong(); // int speed; str->speed = SV_ReadLong(); // unsigned int dist; str->dist = SV_ReadLong(); // int angle; str->angle = SV_ReadLong(); // fixed_t xSpeed; str->xSpeed = SV_ReadLong(); // fixed_t ySpeed; str->ySpeed = SV_ReadLong(); } static void StreamOut_polyevent_t(polyevent_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // int polyobj; SV_WriteLong(str->polyobj); // int speed; SV_WriteLong(str->speed); // unsigned int dist; SV_WriteLong(str->dist); // int angle; SV_WriteLong(str->angle); // fixed_t xSpeed; SV_WriteLong(str->xSpeed); // fixed_t ySpeed; SV_WriteLong(str->ySpeed); } // // pillar_t // static void StreamIn_pillar_t(pillar_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // int ceilingSpeed; str->ceilingSpeed = SV_ReadLong(); // int floorSpeed; str->floorSpeed = SV_ReadLong(); // int floordest; str->floordest = SV_ReadLong(); // int ceilingdest; str->ceilingdest = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); // int crush; str->crush = SV_ReadLong(); } static void StreamOut_pillar_t(pillar_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // int ceilingSpeed; SV_WriteLong(str->ceilingSpeed); // int floorSpeed; SV_WriteLong(str->floorSpeed); // int floordest; SV_WriteLong(str->floordest); // int ceilingdest; SV_WriteLong(str->ceilingdest); // int direction; SV_WriteLong(str->direction); // int crush; SV_WriteLong(str->crush); } // // polydoor_t // static void StreamIn_polydoor_t(polydoor_t *str) { // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // int polyobj; str->polyobj = SV_ReadLong(); // int speed; str->speed = SV_ReadLong(); // int dist; str->dist = SV_ReadLong(); // int totalDist; str->totalDist = SV_ReadLong(); // int direction; str->direction = SV_ReadLong(); // fixed_t xSpeed, ySpeed; str->xSpeed = SV_ReadLong(); str->ySpeed = SV_ReadLong(); // int tics; str->tics = SV_ReadLong(); // int waitTics; str->waitTics = SV_ReadLong(); // podoortype_t type; str->type = SV_ReadLong(); // boolean close; str->close = SV_ReadLong(); } static void StreamOut_polydoor_t(polydoor_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // int polyobj; SV_WriteLong(str->polyobj); // int speed; SV_WriteLong(str->speed); // int dist; SV_WriteLong(str->dist); // int totalDist; SV_WriteLong(str->totalDist); // int direction; SV_WriteLong(str->direction); // fixed_t xSpeed, ySpeed; SV_WriteLong(str->xSpeed); SV_WriteLong(str->ySpeed); // int tics; SV_WriteLong(str->tics); // int waitTics; SV_WriteLong(str->waitTics); // podoortype_t type; SV_WriteLong(str->type); // boolean close; SV_WriteLong(str->close); } // // floorWaggle_t // static void StreamIn_floorWaggle_t(floorWaggle_t *str) { int i; // thinker_t thinker; StreamIn_thinker_t(&str->thinker); // sector_t *sector; i = SV_ReadLong(); str->sector = §ors[i]; // fixed_t originalHeight; str->originalHeight = SV_ReadLong(); // fixed_t accumulator; str->accumulator = SV_ReadLong(); // fixed_t accDelta; str->accDelta = SV_ReadLong(); // fixed_t targetScale; str->targetScale = SV_ReadLong(); // fixed_t scale; str->scale = SV_ReadLong(); // fixed_t scaleDelta; str->scaleDelta = SV_ReadLong(); // int ticker; str->ticker = SV_ReadLong(); // int state; str->state = SV_ReadLong(); } static void StreamOut_floorWaggle_t(floorWaggle_t *str) { // thinker_t thinker; StreamOut_thinker_t(&str->thinker); // sector_t *sector; SV_WriteLong(str->sector - sectors); // fixed_t originalHeight; SV_WriteLong(str->originalHeight); // fixed_t accumulator; SV_WriteLong(str->accumulator); // fixed_t accDelta; SV_WriteLong(str->accDelta); // fixed_t targetScale; SV_WriteLong(str->targetScale); // fixed_t scale; SV_WriteLong(str->scale); // fixed_t scaleDelta; SV_WriteLong(str->scaleDelta); // int ticker; SV_WriteLong(str->ticker); // int state; SV_WriteLong(str->state); } //========================================================================== // // SV_SaveGame // //========================================================================== void SV_SaveGame(int slot, const char *description) { char fileName[100]; char versionText[HXS_VERSION_TEXT_LENGTH]; unsigned int i; // Open the output file M_snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath); SV_OpenWrite(fileName); // Write game save description SV_Write(description, HXS_DESCRIPTION_LENGTH); // Write version info memset(versionText, 0, HXS_VERSION_TEXT_LENGTH); M_StringCopy(versionText, HXS_VERSION_TEXT, HXS_VERSION_TEXT_LENGTH); SV_Write(versionText, HXS_VERSION_TEXT_LENGTH); // Place a header marker SV_WriteLong(ASEG_GAME_HEADER); // Write current map and difficulty SV_WriteByte(gamemap); SV_WriteByte(gameskill); // Write global script info for (i = 0; i < MAX_ACS_WORLD_VARS; ++i) { SV_WriteLong(WorldVars[i]); } for (i = 0; i < MAX_ACS_STORE + 1; ++i) { StreamOut_acsstore_t(&ACSStore[i]); } ArchivePlayers(); // Place a termination marker SV_WriteLong(ASEG_END); // Close the output file SV_Close(); // Save out the current map SV_SaveMap(true); // true = save player info // Clear all save files at destination slot ClearSaveSlot(slot); // Copy base slot to destination slot CopySaveSlot(BASE_SLOT, slot); } //========================================================================== // // SV_SaveMap // //========================================================================== void SV_SaveMap(boolean savePlayers) { char fileName[100]; SavingPlayers = savePlayers; // Open the output file M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap); SV_OpenWrite(fileName); // Place a header marker SV_WriteLong(ASEG_MAP_HEADER); // Write the level timer SV_WriteLong(leveltime); // Set the mobj archive numbers SetMobjArchiveNums(); ArchiveWorld(); ArchivePolyobjs(); ArchiveMobjs(); ArchiveThinkers(); ArchiveScripts(); ArchiveSounds(); ArchiveMisc(); // Place a termination marker SV_WriteLong(ASEG_END); // Close the output file SV_Close(); } //========================================================================== // // SV_LoadGame // //========================================================================== void SV_LoadGame(int slot) { int i; char fileName[100]; char version_text[HXS_VERSION_TEXT_LENGTH]; player_t playerBackup[MAXPLAYERS]; mobj_t *mobj; // Copy all needed save files to the base slot if (slot != BASE_SLOT) { ClearSaveSlot(BASE_SLOT); CopySaveSlot(slot, BASE_SLOT); } // Create the name M_snprintf(fileName, sizeof(fileName), "%shex6.hxs", SavePath); // Load the file SV_OpenRead(fileName); // Set the save pointer and skip the description field fseek(SavingFP, HXS_DESCRIPTION_LENGTH, SEEK_CUR); // Check the version text for (i = 0; i < sizeof(version_text); ++i) { version_text[i] = SV_ReadByte(); } if (strncmp(version_text, HXS_VERSION_TEXT, HXS_VERSION_TEXT_LENGTH) != 0) { // Bad version return; } AssertSegment(ASEG_GAME_HEADER); gameepisode = 1; gamemap = SV_ReadByte(); gameskill = SV_ReadByte(); // Read global script info for (i = 0; i < MAX_ACS_WORLD_VARS; ++i) { WorldVars[i] = SV_ReadLong(); } for (i = 0; i < MAX_ACS_STORE + 1; ++i) { StreamIn_acsstore_t(&ACSStore[i]); } // Read the player structures UnarchivePlayers(); AssertSegment(ASEG_END); // Save player structs for (i = 0; i < maxplayers; i++) { playerBackup[i] = players[i]; } SV_Close(); // Load the current map SV_LoadMap(); // Don't need the player mobj relocation info for load game Z_Free(TargetPlayerAddrs); // Restore player structs inv_ptr = 0; curpos = 0; for (i = 0; i < maxplayers; i++) { mobj = players[i].mo; players[i] = playerBackup[i]; players[i].mo = mobj; if (i == consoleplayer) { players[i].readyArtifact = players[i].inventory[inv_ptr].type; } } } //========================================================================== // // SV_UpdateRebornSlot // // Copies the base slot to the reborn slot. // //========================================================================== void SV_UpdateRebornSlot(void) { ClearSaveSlot(REBORN_SLOT); CopySaveSlot(BASE_SLOT, REBORN_SLOT); } //========================================================================== // // SV_ClearRebornSlot // //========================================================================== void SV_ClearRebornSlot(void) { ClearSaveSlot(REBORN_SLOT); } //========================================================================== // // SV_MapTeleport // //========================================================================== void SV_MapTeleport(int map, int position) { int i; int j; char fileName[100]; player_t playerBackup[MAXPLAYERS]; mobj_t *targetPlayerMobj; mobj_t *mobj; int inventoryPtr; int currentInvPos; boolean rClass; boolean playerWasReborn; boolean oldWeaponowned[NUMWEAPONS]; int oldKeys = 0; int oldPieces = 0; int bestWeapon; if (!deathmatch) { if (P_GetMapCluster(gamemap) == P_GetMapCluster(map)) { // Same cluster - save map without saving player mobjs SV_SaveMap(false); } else { // Entering new cluster - clear base slot ClearSaveSlot(BASE_SLOT); } } // Store player structs for later rClass = randomclass; randomclass = false; for (i = 0; i < maxplayers; i++) { playerBackup[i] = players[i]; } // Save some globals that get trashed during the load inventoryPtr = inv_ptr; currentInvPos = curpos; // Only SV_LoadMap() uses TargetPlayerAddrs, so it's NULLed here // for the following check (player mobj redirection) TargetPlayerAddrs = NULL; gamemap = map; M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap); if (!deathmatch && ExistingFile(fileName)) { // Unarchive map SV_LoadMap(); } else { // New map G_InitNew(gameskill, gameepisode, gamemap); // Destroy all freshly spawned players for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { P_RemoveMobj(players[i].mo); } } } // Restore player structs targetPlayerMobj = NULL; for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) { continue; } players[i] = playerBackup[i]; P_ClearMessage(&players[i]); players[i].attacker = NULL; players[i].poisoner = NULL; if (netgame) { if (players[i].playerstate == PST_DEAD) { // In a network game, force all players to be alive players[i].playerstate = PST_REBORN; } if (!deathmatch) { // Cooperative net-play, retain keys and weapons oldKeys = players[i].keys; oldPieces = players[i].pieces; for (j = 0; j < NUMWEAPONS; j++) { oldWeaponowned[j] = players[i].weaponowned[j]; } } } playerWasReborn = (players[i].playerstate == PST_REBORN); if (deathmatch) { memset(players[i].frags, 0, sizeof(players[i].frags)); mobj = P_SpawnMobj(playerstarts[0][i].x << 16, playerstarts[0][i].y << 16, 0, MT_PLAYER_FIGHTER); players[i].mo = mobj; G_DeathMatchSpawnPlayer(i); P_RemoveMobj(mobj); } else { P_SpawnPlayer(&playerstarts[position][i]); } if (playerWasReborn && netgame && !deathmatch) { // Restore keys and weapons when reborn in co-op players[i].keys = oldKeys; players[i].pieces = oldPieces; for (bestWeapon = 0, j = 0; j < NUMWEAPONS; j++) { if (oldWeaponowned[j]) { bestWeapon = j; players[i].weaponowned[j] = true; } } players[i].mana[MANA_1] = 25; players[i].mana[MANA_2] = 25; if (bestWeapon) { // Bring up the best weapon players[i].pendingweapon = bestWeapon; } } if (targetPlayerMobj == NULL) { // The poor sap targetPlayerMobj = players[i].mo; } } randomclass = rClass; // Redirect anything targeting a player mobj if (TargetPlayerAddrs) { for (i = 0; i < TargetPlayerCount; i++) { *TargetPlayerAddrs[i] = targetPlayerMobj; } Z_Free(TargetPlayerAddrs); } // Destroy all things touching players for (i = 0; i < maxplayers; i++) { if (playeringame[i]) { P_TeleportMove(players[i].mo, players[i].mo->x, players[i].mo->y); } } // Restore trashed globals inv_ptr = inventoryPtr; curpos = currentInvPos; // Launch waiting scripts if (!deathmatch) { P_CheckACSStore(); } // For single play, save immediately into the reborn slot if (!netgame) { SV_SaveGame(REBORN_SLOT, REBORN_DESCRIPTION); } } //========================================================================== // // SV_GetRebornSlot // //========================================================================== int SV_GetRebornSlot(void) { return (REBORN_SLOT); } //========================================================================== // // SV_RebornSlotAvailable // // Returns true if the reborn slot is available. // //========================================================================== boolean SV_RebornSlotAvailable(void) { char fileName[100]; M_snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, REBORN_SLOT); return ExistingFile(fileName); } //========================================================================== // // SV_LoadMap // //========================================================================== void SV_LoadMap(void) { char fileName[100]; // Load a base level G_InitNew(gameskill, gameepisode, gamemap); // Remove all thinkers RemoveAllThinkers(); // Create the name M_snprintf(fileName, sizeof(fileName), "%shex6%02d.hxs", SavePath, gamemap); // Load the file SV_OpenRead(fileName); AssertSegment(ASEG_MAP_HEADER); // Read the level timer leveltime = SV_ReadLong(); UnarchiveWorld(); UnarchivePolyobjs(); UnarchiveMobjs(); UnarchiveThinkers(); UnarchiveScripts(); UnarchiveSounds(); UnarchiveMisc(); AssertSegment(ASEG_END); // Free mobj list and save buffer Z_Free(MobjList); SV_Close(); } //========================================================================== // // SV_InitBaseSlot // //========================================================================== void SV_InitBaseSlot(void) { ClearSaveSlot(BASE_SLOT); } //========================================================================== // // ArchivePlayers // //========================================================================== static void ArchivePlayers(void) { int i; SV_WriteLong(ASEG_PLAYERS); for (i = 0; i < maxplayers; i++) { SV_WriteByte(playeringame[i]); } for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) { continue; } SV_WriteByte(PlayerClass[i]); StreamOut_player_t(&players[i]); } } //========================================================================== // // UnarchivePlayers // //========================================================================== static void UnarchivePlayers(void) { int i; AssertSegment(ASEG_PLAYERS); for (i = 0; i < maxplayers; i++) { playeringame[i] = SV_ReadByte(); } for (i = 0; i < maxplayers; i++) { if (!playeringame[i]) { continue; } PlayerClass[i] = SV_ReadByte(); StreamIn_player_t(&players[i]); P_ClearMessage(&players[i]); } } //========================================================================== // // ArchiveWorld // //========================================================================== static void ArchiveWorld(void) { int i; int j; sector_t *sec; line_t *li; side_t *si; SV_WriteLong(ASEG_WORLD); for (i = 0, sec = sectors; i < numsectors; i++, sec++) { SV_WriteWord(sec->floorheight >> FRACBITS); SV_WriteWord(sec->ceilingheight >> FRACBITS); SV_WriteWord(sec->floorpic); SV_WriteWord(sec->ceilingpic); SV_WriteWord(sec->lightlevel); SV_WriteWord(sec->special); SV_WriteWord(sec->tag); SV_WriteWord(sec->seqType); } for (i = 0, li = lines; i < numlines; i++, li++) { SV_WriteWord(li->flags); SV_WriteByte(li->special); SV_WriteByte(li->arg1); SV_WriteByte(li->arg2); SV_WriteByte(li->arg3); SV_WriteByte(li->arg4); SV_WriteByte(li->arg5); for (j = 0; j < 2; j++) { if (li->sidenum[j] == -1) { continue; } si = &sides[li->sidenum[j]]; SV_WriteWord(si->textureoffset >> FRACBITS); SV_WriteWord(si->rowoffset >> FRACBITS); SV_WriteWord(si->toptexture); SV_WriteWord(si->bottomtexture); SV_WriteWord(si->midtexture); } } } //========================================================================== // // UnarchiveWorld // //========================================================================== static void UnarchiveWorld(void) { int i; int j; sector_t *sec; line_t *li; side_t *si; AssertSegment(ASEG_WORLD); for (i = 0, sec = sectors; i < numsectors; i++, sec++) { sec->floorheight = SV_ReadWord() << FRACBITS; sec->ceilingheight = SV_ReadWord() << FRACBITS; sec->floorpic = SV_ReadWord(); sec->ceilingpic = SV_ReadWord(); sec->lightlevel = SV_ReadWord(); sec->special = SV_ReadWord(); sec->tag = SV_ReadWord(); sec->seqType = SV_ReadWord(); sec->specialdata = 0; sec->soundtarget = 0; } for (i = 0, li = lines; i < numlines; i++, li++) { li->flags = SV_ReadWord(); li->special = SV_ReadByte(); li->arg1 = SV_ReadByte(); li->arg2 = SV_ReadByte(); li->arg3 = SV_ReadByte(); li->arg4 = SV_ReadByte(); li->arg5 = SV_ReadByte(); for (j = 0; j < 2; j++) { if (li->sidenum[j] == -1) { continue; } si = &sides[li->sidenum[j]]; si->textureoffset = SV_ReadWord() << FRACBITS; si->rowoffset = SV_ReadWord() << FRACBITS; si->toptexture = SV_ReadWord(); si->bottomtexture = SV_ReadWord(); si->midtexture = SV_ReadWord(); } } } //========================================================================== // // SetMobjArchiveNums // // Sets the archive numbers in all mobj structs. Also sets the MobjCount // global. Ignores player mobjs if SavingPlayers is false. // //========================================================================== static void SetMobjArchiveNums(void) { mobj_t *mobj; thinker_t *thinker; MobjCount = 0; for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { if (thinker->function == P_MobjThinker) { mobj = (mobj_t *) thinker; if (mobj->player && !SavingPlayers) { // Skipping player mobjs continue; } mobj->archiveNum = MobjCount++; } } } //========================================================================== // // ArchiveMobjs // //========================================================================== static void ArchiveMobjs(void) { int count; thinker_t *thinker; SV_WriteLong(ASEG_MOBJS); SV_WriteLong(MobjCount); count = 0; for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { if (thinker->function != P_MobjThinker) { // Not a mobj thinker continue; } if (((mobj_t *) thinker)->player && !SavingPlayers) { // Skipping player mobjs continue; } count++; StreamOut_mobj_t((mobj_t *) thinker); } if (count != MobjCount) { I_Error("ArchiveMobjs: bad mobj count"); } } //========================================================================== // // UnarchiveMobjs // //========================================================================== static void UnarchiveMobjs(void) { int i; mobj_t *mobj; AssertSegment(ASEG_MOBJS); TargetPlayerAddrs = Z_Malloc(MAX_TARGET_PLAYERS * sizeof(mobj_t **), PU_STATIC, NULL); TargetPlayerCount = 0; MobjCount = SV_ReadLong(); MobjList = Z_Malloc(MobjCount * sizeof(mobj_t *), PU_STATIC, NULL); for (i = 0; i < MobjCount; i++) { MobjList[i] = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); } for (i = 0; i < MobjCount; i++) { mobj = MobjList[i]; StreamIn_mobj_t(mobj); // Restore broken pointers. mobj->info = &mobjinfo[mobj->type]; P_SetThingPosition(mobj); mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->thinker.function = P_MobjThinker; P_AddThinker(&mobj->thinker); } P_CreateTIDList(); P_InitCreatureCorpseQueue(true); // true = scan for corpses } //========================================================================== // // GetMobjNum // //========================================================================== static int GetMobjNum(mobj_t * mobj) { if (mobj == NULL) { return MOBJ_NULL; } if (mobj->player && !SavingPlayers) { return MOBJ_XX_PLAYER; } return mobj->archiveNum; } //========================================================================== // // SetMobjPtr // //========================================================================== static void SetMobjPtr(mobj_t **ptr, unsigned int archiveNum) { if (archiveNum == MOBJ_NULL) { *ptr = NULL; } else if (archiveNum == MOBJ_XX_PLAYER) { if (TargetPlayerCount == MAX_TARGET_PLAYERS) { I_Error("RestoreMobj: exceeded MAX_TARGET_PLAYERS"); } TargetPlayerAddrs[TargetPlayerCount++] = ptr; *ptr = NULL; } else { *ptr = MobjList[archiveNum]; } } //========================================================================== // // Thinker types list. // // This is used by ArchiveThinkers and UnarchiveThinkers, below. // // Original comment: // "This list has been prioritized using frequency estimates" // //========================================================================== static thinkInfo_t ThinkerInfo[] = { { TC_MOVE_FLOOR, T_MoveFloor, StreamOut_floormove_t, StreamIn_floormove_t, RestoreSSThinker, sizeof(floormove_t) }, { TC_PLAT_RAISE, T_PlatRaise, StreamOut_plat_t, StreamIn_plat_t, RestorePlatRaise, sizeof(plat_t) }, { TC_MOVE_CEILING, T_MoveCeiling, StreamOut_ceiling_t, StreamIn_ceiling_t, RestoreMoveCeiling, sizeof(ceiling_t) }, { TC_LIGHT, T_Light, StreamOut_light_t, StreamIn_light_t, NULL, sizeof(light_t) }, { TC_VERTICAL_DOOR, T_VerticalDoor, StreamOut_vldoor_t, StreamIn_vldoor_t, RestoreSSThinker, sizeof(vldoor_t) }, { TC_PHASE, T_Phase, StreamOut_phase_t, StreamIn_phase_t, NULL, sizeof(phase_t) }, { TC_INTERPRET_ACS, T_InterpretACS, StreamOut_acs_t, StreamIn_acs_t, NULL, sizeof(acs_t) }, { TC_ROTATE_POLY, T_RotatePoly, StreamOut_polyevent_t, StreamIn_polyevent_t, NULL, sizeof(polyevent_t) }, { TC_BUILD_PILLAR, T_BuildPillar, StreamOut_pillar_t, StreamIn_pillar_t, RestoreSSThinker, sizeof(pillar_t) }, { TC_MOVE_POLY, T_MovePoly, StreamOut_polyevent_t, StreamIn_polyevent_t, NULL, sizeof(polyevent_t) }, { TC_POLY_DOOR, T_PolyDoor, StreamOut_polydoor_t, StreamIn_polydoor_t, NULL, sizeof(polydoor_t) }, { TC_FLOOR_WAGGLE, T_FloorWaggle, StreamOut_floorWaggle_t, StreamIn_floorWaggle_t, RestoreSSThinker, sizeof(floorWaggle_t) }, { TC_NULL, NULL, NULL, NULL, NULL, 0}, }; //========================================================================== // // ArchiveThinkers // //========================================================================== static void ArchiveThinkers(void) { thinker_t *thinker; thinkInfo_t *info; SV_WriteLong(ASEG_THINKERS); for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { for (info = ThinkerInfo; info->tClass != TC_NULL; info++) { if (thinker->function == info->thinkerFunc) { SV_WriteByte(info->tClass); info->writeFunc(thinker); break; } } } // Add a termination marker SV_WriteByte(TC_NULL); } //========================================================================== // // UnarchiveThinkers // //========================================================================== static void UnarchiveThinkers(void) { int tClass; thinker_t *thinker; thinkInfo_t *info; AssertSegment(ASEG_THINKERS); while ((tClass = SV_ReadByte()) != TC_NULL) { for (info = ThinkerInfo; info->tClass != TC_NULL; info++) { if (tClass == info->tClass) { thinker = Z_Malloc(info->size, PU_LEVEL, NULL); info->readFunc(thinker); thinker->function = info->thinkerFunc; if (info->restoreFunc) { info->restoreFunc(thinker); } P_AddThinker(thinker); break; } } if (info->tClass == TC_NULL) { I_Error("UnarchiveThinkers: Unknown tClass %d in " "savegame", tClass); } } } //========================================================================== // // RestoreSSThinker // //========================================================================== static void RestoreSSThinker(ssthinker_t *sst) { sst->sector->specialdata = sst->thinker.function; } //========================================================================== // // RestorePlatRaise // //========================================================================== static void RestorePlatRaise(plat_t *plat) { plat->sector->specialdata = T_PlatRaise; P_AddActivePlat(plat); } //========================================================================== // // RestoreMoveCeiling // //========================================================================== static void RestoreMoveCeiling(ceiling_t *ceiling) { ceiling->sector->specialdata = T_MoveCeiling; P_AddActiveCeiling(ceiling); } //========================================================================== // // ArchiveScripts // //========================================================================== static void ArchiveScripts(void) { int i; SV_WriteLong(ASEG_SCRIPTS); for (i = 0; i < ACScriptCount; i++) { SV_WriteWord(ACSInfo[i].state); SV_WriteWord(ACSInfo[i].waitValue); } for (i = 0; i< MAX_ACS_MAP_VARS; ++i) { SV_WriteLong(MapVars[i]); } } //========================================================================== // // UnarchiveScripts // //========================================================================== static void UnarchiveScripts(void) { int i; AssertSegment(ASEG_SCRIPTS); for (i = 0; i < ACScriptCount; i++) { ACSInfo[i].state = SV_ReadWord(); ACSInfo[i].waitValue = SV_ReadWord(); } for (i = 0; i < MAX_ACS_MAP_VARS; ++i) { MapVars[i] = SV_ReadLong(); } } //========================================================================== // // ArchiveMisc // //========================================================================== static void ArchiveMisc(void) { int ix; SV_WriteLong(ASEG_MISC); for (ix = 0; ix < maxplayers; ix++) { SV_WriteLong(localQuakeHappening[ix]); } } //========================================================================== // // UnarchiveMisc // //========================================================================== static void UnarchiveMisc(void) { int ix; AssertSegment(ASEG_MISC); for (ix = 0; ix < maxplayers; ix++) { localQuakeHappening[ix] = SV_ReadLong(); } } //========================================================================== // // RemoveAllThinkers // //========================================================================== static void RemoveAllThinkers(void) { thinker_t *thinker; thinker_t *nextThinker; thinker = thinkercap.next; while (thinker != &thinkercap) { nextThinker = thinker->next; if (thinker->function == P_MobjThinker) { P_RemoveMobj((mobj_t *) thinker); } else { Z_Free(thinker); } thinker = nextThinker; } P_InitThinkers(); } //========================================================================== // // ArchiveSounds // //========================================================================== static void ArchiveSounds(void) { seqnode_t *node; sector_t *sec; int difference; int i; SV_WriteLong(ASEG_SOUNDS); // Save the sound sequences SV_WriteLong(ActiveSequences); for (node = SequenceListHead; node; node = node->next) { SV_WriteLong(node->sequence); SV_WriteLong(node->delayTics); SV_WriteLong(node->volume); SV_WriteLong(SN_GetSequenceOffset(node->sequence, node->sequencePtr)); SV_WriteLong(node->currentSoundID); for (i = 0; i < po_NumPolyobjs; i++) { if (node->mobj == (mobj_t *) & polyobjs[i].startSpot) { break; } } if (i == po_NumPolyobjs) { // Sound is attached to a sector, not a polyobj sec = R_PointInSubsector(node->mobj->x, node->mobj->y)->sector; difference = (int) ((byte *) sec - (byte *) & sectors[0]) / sizeof(sector_t); SV_WriteLong(0); // 0 -- sector sound origin } else { SV_WriteLong(1); // 1 -- polyobj sound origin difference = i; } SV_WriteLong(difference); } } //========================================================================== // // UnarchiveSounds // //========================================================================== static void UnarchiveSounds(void) { int i; int numSequences; int sequence; int delayTics; int volume; int seqOffset; int soundID; int polySnd; int secNum; mobj_t *sndMobj; AssertSegment(ASEG_SOUNDS); // Reload and restart all sound sequences numSequences = SV_ReadLong(); i = 0; while (i < numSequences) { sequence = SV_ReadLong(); delayTics = SV_ReadLong(); volume = SV_ReadLong(); seqOffset = SV_ReadLong(); soundID = SV_ReadLong(); polySnd = SV_ReadLong(); secNum = SV_ReadLong(); if (!polySnd) { sndMobj = (mobj_t *) & sectors[secNum].soundorg; } else { sndMobj = (mobj_t *) & polyobjs[secNum].startSpot; } SN_StartSequence(sndMobj, sequence); SN_ChangeNodeData(i, seqOffset, delayTics, volume, soundID); i++; } } //========================================================================== // // ArchivePolyobjs // //========================================================================== static void ArchivePolyobjs(void) { int i; SV_WriteLong(ASEG_POLYOBJS); SV_WriteLong(po_NumPolyobjs); for (i = 0; i < po_NumPolyobjs; i++) { SV_WriteLong(polyobjs[i].tag); SV_WriteLong(polyobjs[i].angle); SV_WriteLong(polyobjs[i].startSpot.x); SV_WriteLong(polyobjs[i].startSpot.y); } } //========================================================================== // // UnarchivePolyobjs // //========================================================================== static void UnarchivePolyobjs(void) { int i; fixed_t deltaX; fixed_t deltaY; AssertSegment(ASEG_POLYOBJS); if (SV_ReadLong() != po_NumPolyobjs) { I_Error("UnarchivePolyobjs: Bad polyobj count"); } for (i = 0; i < po_NumPolyobjs; i++) { if (SV_ReadLong() != polyobjs[i].tag) { I_Error("UnarchivePolyobjs: Invalid polyobj tag"); } PO_RotatePolyobj(polyobjs[i].tag, (angle_t) SV_ReadLong()); deltaX = SV_ReadLong() - polyobjs[i].startSpot.x; deltaY = SV_ReadLong() - polyobjs[i].startSpot.y; PO_MovePolyobj(polyobjs[i].tag, deltaX, deltaY); } } //========================================================================== // // AssertSegment // //========================================================================== static void AssertSegment(gameArchiveSegment_t segType) { if (SV_ReadLong() != segType) { I_Error("Corrupt save game: Segment [%d] failed alignment check", segType); } } //========================================================================== // // ClearSaveSlot // // Deletes all save game files associated with a slot number. // //========================================================================== static void ClearSaveSlot(int slot) { int i; char fileName[100]; for (i = 0; i < MAX_MAPS; i++) { M_snprintf(fileName, sizeof(fileName), "%shex%d%02d.hxs", SavePath, slot, i); remove(fileName); } M_snprintf(fileName, sizeof(fileName), "%shex%d.hxs", SavePath, slot); remove(fileName); } //========================================================================== // // CopySaveSlot // // Copies all the save game files from one slot to another. // //========================================================================== static void CopySaveSlot(int sourceSlot, int destSlot) { int i; char sourceName[100]; char destName[100]; for (i = 0; i < MAX_MAPS; i++) { M_snprintf(sourceName, sizeof(sourceName), "%shex%d%02d.hxs", SavePath, sourceSlot, i); if (ExistingFile(sourceName)) { M_snprintf(destName, sizeof(destName), "%shex%d%02d.hxs", SavePath, destSlot, i); CopyFile(sourceName, destName); } } M_snprintf(sourceName, sizeof(sourceName), "%shex%d.hxs", SavePath, sourceSlot); if (ExistingFile(sourceName)) { M_snprintf(destName, sizeof(destName), "%shex%d.hxs", SavePath, destSlot); CopyFile(sourceName, destName); } else { I_Error("Could not load savegame %s", sourceName); } } //========================================================================== // // CopyFile // // This function was rewritten to copy files with minimal strain on zone // allocation and allow for big maps that technically work in vanilla to // save without error. //========================================================================== static void CopyFile(char *source_name, char *dest_name) { const int BUFFER_CHUNK_SIZE = 0x10000; byte *buffer; int file_length, file_remaining; FILE *read_handle, *write_handle; int buf_count, read_count, write_count; read_handle = fopen(source_name, "rb"); if (read_handle == NULL) { I_Error ("Couldn't read file %s", source_name); } file_length = file_remaining = M_FileLength(read_handle); // Vanilla savegame emulation. // // CopyFile() typically calls M_ReadFile() which stores the entire file // in memory: Chocolate Hexen should force an allocation error here // whenever it's appropriate. if (vanilla_savegame_limit) { buffer = Z_Malloc(file_length, PU_STATIC, NULL); Z_Free(buffer); } write_handle = fopen(dest_name, "wb"); if (write_handle == NULL) { I_Error ("Couldn't read file %s", dest_name); } buffer = Z_Malloc (BUFFER_CHUNK_SIZE, PU_STATIC, NULL); do { buf_count = BUFFER_CHUNK_SIZE; if( file_remaining < BUFFER_CHUNK_SIZE) { buf_count = file_remaining; } read_count = fread(buffer, 1, buf_count, read_handle); if (read_count < buf_count) { I_Error ("Couldn't read file %s", source_name); } write_count = fwrite(buffer, 1, buf_count, write_handle); if (write_count < buf_count) { I_Error ("Couldn't write to file %s", dest_name); } file_remaining -= buf_count; } while (file_remaining > 0); Z_Free(buffer); fclose(read_handle); fclose(write_handle); } //========================================================================== // // ExistingFile // //========================================================================== static boolean ExistingFile(char *name) { FILE *fp; if ((fp = fopen(name, "rb")) != NULL) { fclose(fp); return true; } else { return false; } } //========================================================================== // // SV_Open // //========================================================================== static void SV_OpenRead(char *fileName) { SavingFP = fopen(fileName, "rb"); // Should never happen, only if hex6.hxs cannot ever be created. if (SavingFP == NULL) { I_Error("Could not load savegame %s", fileName); } } static void SV_OpenWrite(char *fileName) { SavingFP = fopen(fileName, "wb"); } //========================================================================== // // SV_Close // //========================================================================== static void SV_Close(void) { if (SavingFP) { fclose(SavingFP); } } //========================================================================== // // SV_Read // //========================================================================== static void SV_Read(void *buffer, int size) { int retval = fread(buffer, 1, size, SavingFP); if (retval != size) { I_Error("Incomplete read in SV_Read: Expected %d, got %d bytes", size, retval); } } static byte SV_ReadByte(void) { byte result; SV_Read(&result, sizeof(byte)); return result; } static uint16_t SV_ReadWord(void) { uint16_t result; SV_Read(&result, sizeof(unsigned short)); return SHORT(result); } static uint32_t SV_ReadLong(void) { uint32_t result; SV_Read(&result, sizeof(int)); return LONG(result); } static void *SV_ReadPtr(void) { return (void *) (intptr_t) SV_ReadLong(); } //========================================================================== // // SV_Write // //========================================================================== static void SV_Write(const void *buffer, int size) { fwrite(buffer, size, 1, SavingFP); } static void SV_WriteByte(byte val) { fwrite(&val, sizeof(byte), 1, SavingFP); } static void SV_WriteWord(unsigned short val) { val = SHORT(val); fwrite(&val, sizeof(unsigned short), 1, SavingFP); } static void SV_WriteLong(unsigned int val) { val = LONG(val); fwrite(&val, sizeof(int), 1, SavingFP); } static void SV_WritePtr(void *val) { long ptr; // Write a pointer value. In Vanilla Hexen pointers are 32-bit but // nowadays they might be larger. Whatever value we write here isn't // going to be much use when we reload the game. ptr = (long)(intptr_t) val; SV_WriteLong((unsigned int) (ptr & 0xffffffff)); } crispy-doom-crispy-doom-5.6.4/src/hexen/textdefs.h000066400000000000000000000152011360717211000221300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // MN_menu.c --------------------------------------------------------------- #define PRESSKEY "press a key." #define PRESSYN "press y or n." #define TXT_PAUSED "PAUSED" #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 MSGOFF "Messages OFF" #define MSGON "Messages ON" #define NETEND "you can't end a netgame!\n\n"PRESSKEY #define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN #define DOSY "(press y to quit to dos.)" #define TXT_GAMMA_LEVEL_OFF "GAMMA CORRECTION OFF" #define TXT_GAMMA_LEVEL_1 "GAMMA CORRECTION LEVEL 1" #define TXT_GAMMA_LEVEL_2 "GAMMA CORRECTION LEVEL 2" #define TXT_GAMMA_LEVEL_3 "GAMMA CORRECTION LEVEL 3" #define TXT_GAMMA_LEVEL_4 "GAMMA CORRECTION LEVEL 4" // [crispy] intermediate gamma levels #define TXT_GAMMA_LEVEL_05 "GAMMA CORRECTION LEVEL 0.5" #define TXT_GAMMA_LEVEL_15 "GAMMA CORRECTION LEVEL 1.5" #define TXT_GAMMA_LEVEL_25 "GAMMA CORRECTION LEVEL 2.5" #define TXT_GAMMA_LEVEL_35 "GAMMA CORRECTION LEVEL 3.5" #define EMPTYSTRING "empty slot" // P_inter.c --------------------------------------------------------------- // Mana #define TXT_MANA_1 "BLUE MANA" #define TXT_MANA_2 "GREEN MANA" #define TXT_MANA_BOTH "COMBINED MANA" // Keys #define TXT_KEY_STEEL "STEEL KEY" #define TXT_KEY_CAVE "CAVE KEY" #define TXT_KEY_AXE "AXE KEY" #define TXT_KEY_FIRE "FIRE KEY" #define TXT_KEY_EMERALD "EMERALD KEY" #define TXT_KEY_DUNGEON "DUNGEON KEY" #define TXT_KEY_SILVER "SILVER KEY" #define TXT_KEY_RUSTED "RUSTED KEY" #define TXT_KEY_HORN "HORN KEY" #define TXT_KEY_SWAMP "SWAMP KEY" #define TXT_KEY_CASTLE "CASTLE KEY" // Artifacts #define TXT_ARTIINVULNERABILITY "ICON OF THE DEFENDER" #define TXT_ARTIHEALTH "QUARTZ FLASK" #define TXT_ARTISUPERHEALTH "MYSTIC URN" #define TXT_ARTISUMMON "DARK SERVANT" #define TXT_ARTITORCH "TORCH" #define TXT_ARTIEGG "PORKALATOR" #define TXT_ARTIFLY "WINGS OF WRATH" #define TXT_ARTITELEPORT "CHAOS DEVICE" #define TXT_ARTIPOISONBAG "FLECHETTE" #define TXT_ARTITELEPORTOTHER "BANISHMENT DEVICE" #define TXT_ARTISPEED "BOOTS OF SPEED" #define TXT_ARTIBOOSTMANA "KRATER OF MIGHT" #define TXT_ARTIBOOSTARMOR "DRAGONSKIN BRACERS" #define TXT_ARTIBLASTRADIUS "DISC OF REPULSION" #define TXT_ARTIHEALINGRADIUS "MYSTIC AMBIT INCANT" // Puzzle artifacts #define TXT_ARTIPUZZSKULL "YORICK'S SKULL" #define TXT_ARTIPUZZGEMBIG "HEART OF D'SPARIL" #define TXT_ARTIPUZZGEMRED "RUBY PLANET" #define TXT_ARTIPUZZGEMGREEN1 "EMERALD PLANET" #define TXT_ARTIPUZZGEMGREEN2 "EMERALD PLANET" #define TXT_ARTIPUZZGEMBLUE1 "SAPPHIRE PLANET" #define TXT_ARTIPUZZGEMBLUE2 "SAPPHIRE PLANET" #define TXT_ARTIPUZZBOOK1 "DAEMON CODEX" #define TXT_ARTIPUZZBOOK2 "LIBER OSCURA" #define TXT_ARTIPUZZSKULL2 "FLAME MASK" #define TXT_ARTIPUZZFWEAPON "GLAIVE SEAL" #define TXT_ARTIPUZZCWEAPON "HOLY RELIC" #define TXT_ARTIPUZZMWEAPON "SIGIL OF THE MAGUS" #define TXT_ARTIPUZZGEAR "CLOCK GEAR" #define TXT_USEPUZZLEFAILED "YOU CANNOT USE THIS HERE" // Items #define TXT_ITEMHEALTH "CRYSTAL VIAL" #define TXT_ITEMBAGOFHOLDING "BAG OF HOLDING" #define TXT_ITEMSHIELD1 "SILVER SHIELD" #define TXT_ITEMSHIELD2 "ENCHANTED SHIELD" #define TXT_ITEMSUPERMAP "MAP SCROLL" #define TXT_ARMOR1 "MESH ARMOR" #define TXT_ARMOR2 "FALCON SHIELD" #define TXT_ARMOR3 "PLATINUM HELMET" #define TXT_ARMOR4 "AMULET OF WARDING" // Weapons #define TXT_WEAPON_F2 "TIMON'S AXE" #define TXT_WEAPON_F3 "HAMMER OF RETRIBUTION" #define TXT_WEAPON_F4 "QUIETUS ASSEMBLED" #define TXT_WEAPON_C2 "SERPENT STAFF" #define TXT_WEAPON_C3 "FIRESTORM" #define TXT_WEAPON_C4 "WRAITHVERGE ASSEMBLED" #define TXT_WEAPON_M2 "FROST SHARDS" #define TXT_WEAPON_M3 "ARC OF DEATH" #define TXT_WEAPON_M4 "BLOODSCOURGE ASSEMBLED" #define TXT_QUIETUS_PIECE "SEGMENT OF QUIETUS" #define TXT_WRAITHVERGE_PIECE "SEGMENT OF WRAITHVERGE" #define TXT_BLOODSCOURGE_PIECE "SEGMENT OF BLOODSCOURGE" // SB_bar.c ---------------------------------------------------------------- #define TXT_CHEATGODON "GOD MODE ON" #define TXT_CHEATGODOFF "GOD MODE OFF" #define TXT_CHEATNOCLIPON "NO CLIPPING ON" #define TXT_CHEATNOCLIPOFF "NO CLIPPING OFF" #define TXT_CHEATWEAPONS "ALL WEAPONS" #define TXT_CHEATHEALTH "FULL HEALTH" #define TXT_CHEATKEYS "ALL KEYS" #define TXT_CHEATSOUNDON "SOUND DEBUG ON" #define TXT_CHEATSOUNDOFF "SOUND DEBUG OFF" #define TXT_CHEATTICKERON "TICKER ON" #define TXT_CHEATTICKEROFF "TICKER OFF" #define TXT_CHEATARTIFACTS3 "ALL ARTIFACTS" #define TXT_CHEATARTIFACTSFAIL "BAD INPUT" #define TXT_CHEATWARP "LEVEL WARP" #define TXT_CHEATSCREENSHOT "SCREENSHOT" #define TXT_CHEATIDDQD "TRYING TO CHEAT, EH? NOW YOU DIE!" #define TXT_CHEATIDKFA "CHEATER - YOU DON'T DESERVE WEAPONS" #define TXT_CHEATBADINPUT "BAD INPUT" #define TXT_CHEATNOMAP "CAN'T FIND MAP" // G_game.c ---------------------------------------------------------------- #define TXT_GAMESAVED "GAME SAVED" // M_misc.c ---------------------------------------------------------------- #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" // AM_map.c ---------------------------------------------------------------- #define AMSTR_FOLLOWON "FOLLOW MODE ON" #define AMSTR_FOLLOWOFF "FOLLOW MODE OFF" crispy-doom-crispy-doom-5.6.4/src/hexen/xddefs.h000066400000000000000000000104131360717211000215570ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef __XDDEFS__ #define __XDDEFS__ #include "doomtype.h" #include "v_patch.h" //-------------------------------------------------------------------------- // // Map level types // //-------------------------------------------------------------------------- // lump order in a map wad enum { ML_LABEL, ML_THINGS, ML_LINEDEFS, ML_SIDEDEFS, ML_VERTEXES, ML_SEGS, ML_SSECTORS, ML_NODES, ML_SECTORS, ML_REJECT, ML_BLOCKMAP, ML_BEHAVIOR }; typedef PACKED_STRUCT ( { short x; short y; }) mapvertex_t; typedef PACKED_STRUCT ( { short textureoffset; short rowoffset; char toptexture[8]; char bottomtexture[8]; char midtexture[8]; short sector; // on viewer's side }) mapsidedef_t; typedef PACKED_STRUCT ( { short v1; short v2; short flags; byte special; byte arg1; byte arg2; byte arg3; byte arg4; byte arg5; short sidenum[2]; // sidenum[1] will be -1 if one sided }) maplinedef_t; #define ML_BLOCKING 0x0001 #define ML_BLOCKMONSTERS 0x0002 #define ML_TWOSIDED 0x0004 #define ML_DONTPEGTOP 0x0008 #define ML_DONTPEGBOTTOM 0x0010 #define ML_SECRET 0x0020 // don't map as two sided: IT'S A SECRET! #define ML_SOUNDBLOCK 0x0040 // don't let sound cross two of these #define ML_DONTDRAW 0x0080 // don't draw on the automap #define ML_MAPPED 0x0100 // set if already drawn in automap #define ML_REPEAT_SPECIAL 0x0200 // special is repeatable #define ML_SPAC_SHIFT 10 #define ML_SPAC_MASK 0x1c00 #define GET_SPAC(flags) ((flags&ML_SPAC_MASK)>>ML_SPAC_SHIFT) // Special activation types #define SPAC_CROSS 0 // when player crosses line #define SPAC_USE 1 // when player uses line #define SPAC_MCROSS 2 // when monster crosses line #define SPAC_IMPACT 3 // when projectile hits line #define SPAC_PUSH 4 // when player/monster pushes line #define SPAC_PCROSS 5 // when projectile crosses line typedef PACKED_STRUCT ( { short floorheight; short ceilingheight; char floorpic[8]; char ceilingpic[8]; short lightlevel; short special; short tag; }) mapsector_t; typedef PACKED_STRUCT ( { short numsegs; short firstseg; // segs are stored sequentially }) mapsubsector_t; typedef PACKED_STRUCT ( { short v1; short v2; short angle; short linedef; short side; short offset; }) mapseg_t; #define NF_SUBSECTOR 0x8000 typedef PACKED_STRUCT ( { short x, y, dx, dy; // partition line short bbox[2][4]; // bounding box for each child unsigned short children[2]; // if NF_SUBSECTOR its a subsector }) mapnode_t; typedef PACKED_STRUCT ( { short tid; short x; short y; short height; short angle; short type; short options; byte special; byte arg1; byte arg2; byte arg3; byte arg4; byte arg5; }) mapthing_t; #define MTF_EASY 1 #define MTF_NORMAL 2 #define MTF_HARD 4 #define MTF_AMBUSH 8 #define MTF_DORMANT 16 #define MTF_FIGHTER 32 #define MTF_CLERIC 64 #define MTF_MAGE 128 #define MTF_GSINGLE 256 #define MTF_GCOOP 512 #define MTF_GDEATHMATCH 1024 //-------------------------------------------------------------------------- // // Texture definition // //-------------------------------------------------------------------------- typedef PACKED_STRUCT ( { short originx; short originy; short patch; short stepdir; short colormap; }) mappatch_t; typedef PACKED_STRUCT ( { char name[8]; boolean masked; short width; short height; int obsolete; short patchcount; mappatch_t patches[1]; }) maptexture_t; #endif // __XDDEFS__ crispy-doom-crispy-doom-5.6.4/src/i_cdmus.c000066400000000000000000000027011360717211000206120ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Hexen CD interface. // #include #include "SDL.h" #include "doomtype.h" #include "i_cdmus.h" int cd_Error; int I_CDMusInit(void) { fprintf(stderr, "I_CDMusInit: CD music playback is no longer supported! " "Please use digital music packs instead:\n" "https://www.chocolate-doom.org/wiki/index.php/Digital_music_packs\n"); return -1; } // We cannot print status messages inline during startup, they must // be deferred until after I_CDMusInit has returned. void I_CDMusPrintStartup(void) { } int I_CDMusPlay(int track) { return 0; } int I_CDMusStop(void) { return 0; } int I_CDMusResume(void) { return 0; } int I_CDMusSetVolume(int volume) { return 0; } int I_CDMusFirstTrack(void) { return 0; } int I_CDMusLastTrack(void) { return 0; } int I_CDMusTrackLength(int track_num) { return 0; } crispy-doom-crispy-doom-5.6.4/src/i_cdmus.h000066400000000000000000000025071360717211000206230ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // i_cdmus.h #ifndef __ICDMUS__ #define __ICDMUS__ #define CDERR_NOTINSTALLED 10 // MSCDEX not installed #define CDERR_NOAUDIOSUPPORT 11 // CD-ROM Doesn't support audio #define CDERR_NOAUDIOTRACKS 12 // Current CD has no audio tracks #define CDERR_BADDRIVE 20 // Bad drive number #define CDERR_BADTRACK 21 // Bad track number #define CDERR_IOCTLBUFFMEM 22 // Not enough low memory for IOCTL #define CDERR_DEVREQBASE 100 // DevReq errors extern int cd_Error; int I_CDMusInit(void); void I_CDMusPrintStartup(void); int I_CDMusPlay(int track); int I_CDMusStop(void); int I_CDMusResume(void); int I_CDMusSetVolume(int volume); int I_CDMusFirstTrack(void); int I_CDMusLastTrack(void); int I_CDMusTrackLength(int track); #endif crispy-doom-crispy-doom-5.6.4/src/i_endoom.c000066400000000000000000000031731360717211000207640ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Exit text-mode ENDOOM screen. // #include #include #include "config.h" #include "doomtype.h" #include "i_video.h" #include "txt_main.h" #define ENDOOM_W 80 #define ENDOOM_H 25 // // Displays the text mode ending screen after the game quits // void I_Endoom(byte *endoom_data) { unsigned char *screendata; int y; int indent; // Set up text mode screen TXT_Init(); TXT_SetWindowTitle(PACKAGE_STRING); // SDL2-TODO I_InitWindowTitle(); // SDL2-TODO I_InitWindowIcon(); // Write the data to the screen memory screendata = TXT_GetScreenData(); indent = (ENDOOM_W - TXT_SCREEN_W) / 2; for (y=0; y 0) { break; } TXT_Sleep(0); } // Shut down text mode screen TXT_Shutdown(); } crispy-doom-crispy-doom-5.6.4/src/i_endoom.h000066400000000000000000000014531360717211000207700ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Exit text-mode ENDOOM screen. // #ifndef __I_ENDOOM__ #define __I_ENDOOM__ // Display the Endoom screen on shutdown. Pass a pointer to the // ENDOOM lump. void I_Endoom(byte *data); #endif crispy-doom-crispy-doom-5.6.4/src/i_glob.c000066400000000000000000000205171360717211000204270ustar00rootroot00000000000000// // 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 "i_glob.h" #include "m_misc.h" #include "config.h" #if defined(_MSC_VER) // For Visual C++, we need to include the win_opendir module. #include #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 #ifndef NO_DIRENT_IMPLEMENTATION // Only the fields d_name and (as an XSI extension) d_ino are specified // in POSIX.1. Other than Linux, the d_type field is available mainly // only on BSD systems. The remaining fields are available on many, but // not all systems. static boolean 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; filename = M_StringJoin(dir, DIR_SEPARATOR_S, de->d_name, NULL); result = 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; globs = malloc(sizeof(char *)); if (globs == NULL) { return NULL; } globs[0] = M_StringDuplicate(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] = M_StringDuplicate(arg); ++num_globs; } va_end(args); result = malloc(sizeof(glob_t)); if (result == NULL) { FreeStringList(globs, num_globs); return NULL; } result->dir = opendir(directory); if (result->dir == NULL) { FreeStringList(globs, num_globs); free(result); return NULL; } result->directory = M_StringDuplicate(directory); result->globs = globs; result->num_globs = num_globs; result->flags = flags; result->last_filename = NULL; result->filenames = NULL; result->filenames_len = 0; result->next_index = -1; return result; } glob_t *I_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 boolean 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 boolean 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; 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. return M_StringJoin(glob->directory, DIR_SEPARATOR_S, de->d_name, NULL); } 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 */ crispy-doom-crispy-doom-5.6.4/src/i_glob.h000066400000000000000000000025601360717211000204320ustar00rootroot00000000000000// // 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 crispy-doom-crispy-doom-5.6.4/src/i_input.c000066400000000000000000000326321360717211000206440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // SDL implementation of system-specific input interface. // #include "SDL.h" #include "SDL_keycode.h" #include "doomkeys.h" #include "doomtype.h" #include "d_event.h" #include "i_input.h" #include "m_argv.h" #include "m_config.h" static const int scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; // Lookup table for mapping ASCII characters to their equivalent when // shift is pressed on a US layout keyboard. This is the original table // as found in the Doom sources, comments and all. static const char shiftxform[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, ' ', '!', '"', '#', '$', '%', '&', '"', // shift-' '(', ')', '*', '+', '<', // shift-, '_', // shift-- '>', // shift-. '?', // shift-/ ')', // shift-0 '!', // shift-1 '@', // shift-2 '#', // shift-3 '$', // shift-4 '%', // shift-5 '^', // shift-6 '&', // shift-7 '*', // shift-8 '(', // shift-9 ':', ':', // shift-; '<', '+', // shift-= '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', // shift-[ '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK ']', // shift-] '"', '_', '\'', // shift-` 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127 }; // If true, I_StartTextInput() has been called, and we are populating // the data3 field of ev_keydown events. static boolean text_input_enabled = true; // Bit mask of mouse button state. static unsigned int mouse_button_state = 0; // Disallow mouse and joystick movement to cause forward/backward // motion. Specified with the '-novert' command line parameter. // This is an int to allow saving to config file int novert = 1; // [crispy] // If true, keyboard mapping is ignored, like in Vanilla Doom. // The sensible thing to do is to disable this if you have a non-US // keyboard. int vanilla_keyboard_mapping = true; // Mouse acceleration // // This emulates some of the behavior of DOS mouse drivers by increasing // the speed when the mouse is moved fast. // // The mouse input values are input directly to the game, but when // the values exceed the value of mouse_threshold, they are multiplied // by mouse_acceleration to increase the speed. float mouse_acceleration = 2.0; int mouse_threshold = 10; // [crispy] float mouse_acceleration_y = 1.0; int mouse_threshold_y = 0; int mouse_y_invert = 0; // Translates the SDL key to a value of the type found in doomkeys.h static int TranslateKey(SDL_Keysym *sym) { int scancode = sym->scancode; switch (scancode) { case SDL_SCANCODE_LCTRL: case SDL_SCANCODE_RCTRL: return KEY_RCTRL; case SDL_SCANCODE_LSHIFT: case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT; case SDL_SCANCODE_LALT: return KEY_LALT; case SDL_SCANCODE_RALT: return KEY_RALT; default: if (scancode >= 0 && scancode < arrlen(scancode_translate_table)) { return scancode_translate_table[scancode]; } else { return 0; } } } // Get the localized version of the key press. This takes into account the // keyboard layout, but does not apply any changes due to modifiers, (eg. // shift-, alt-, etc.) static int GetLocalizedKey(SDL_Keysym *sym) { // When using Vanilla mapping, we just base everything off the scancode // and always pretend the user is using a US layout keyboard. if (vanilla_keyboard_mapping) { return TranslateKey(sym); } else { int result = sym->sym; if (result < 0 || result >= 128) { result = 0; } return result; } } // Get the equivalent ASCII (Unicode?) character for a keypress. static int GetTypedChar(SDL_Keysym *sym) { // We only return typed characters when entering text, after // I_StartTextInput() has been called. Otherwise we return nothing. if (!text_input_enabled) { return 0; } // If we're strictly emulating Vanilla, we should always act like // we're using a US layout keyboard (in ev_keydown, data1=data2). // Otherwise we should use the native key mapping. if (vanilla_keyboard_mapping) { int result = TranslateKey(sym); // If shift is held down, apply the original uppercase // translation table used under DOS. if ((SDL_GetModState() & KMOD_SHIFT) != 0 && result >= 0 && result < arrlen(shiftxform)) { result = shiftxform[result]; } return result; } else { SDL_Event next_event; // Special cases, where we always return a fixed value. switch (sym->sym) { case SDLK_BACKSPACE: return KEY_BACKSPACE; case SDLK_RETURN: return KEY_ENTER; default: break; } // The following is a gross hack, but I don't see an easier way // of doing this within the SDL2 API (in SDL1 it was easier). // We want to get the fully transformed input character associated // with this keypress - correct keyboard layout, appropriately // transformed by any modifier keys, etc. So peek ahead in the SDL // event queue and see if the key press is immediately followed by // an SDL_TEXTINPUT event. If it is, it's reasonable to assume the // key press and the text input are connected. Technically the SDL // API does not guarantee anything of the sort, but in practice this // is what happens and I've verified it through manual inspect of // the SDL source code. // // In an ideal world we'd split out ev_keydown into a separate // ev_textinput event, as SDL2 has done. But this doesn't work // (I experimented with the idea), because lots of Doom's code is // based around different responders "eating" events to stop them // being passed on to another responder. If code is listening for // a text input, it cannot block the corresponding keydown events // which can affect other responders. // // So we're stuck with this as a rather fragile alternative. if (SDL_PeepEvents(&next_event, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1 && next_event.type == SDL_TEXTINPUT) { // If an SDL_TEXTINPUT event is found, we always assume it // matches the key press. The input text must be a single // ASCII character - if it isn't, it's possible the input // char is a Unicode value instead; better to send a null // character than the unshifted key. if (strlen(next_event.text.text) == 1 && (next_event.text.text[0] & 0x80) == 0) { return next_event.text.text[0]; } } // Failed to find anything :/ return 0; } } void I_HandleKeyboardEvent(SDL_Event *sdlevent) { // XXX: passing pointers to event for access after this function // has terminated is undefined behaviour event_t event; switch (sdlevent->type) { case SDL_KEYDOWN: event.type = ev_keydown; event.data1 = TranslateKey(&sdlevent->key.keysym); event.data2 = GetLocalizedKey(&sdlevent->key.keysym); event.data3 = GetTypedChar(&sdlevent->key.keysym); if (event.data1 != 0) { D_PostEvent(&event); } break; case SDL_KEYUP: event.type = ev_keyup; event.data1 = TranslateKey(&sdlevent->key.keysym); // data2/data3 are initialized to zero for ev_keyup. // For ev_keydown it's the shifted Unicode character // that was typed, but if something wants to detect // key releases it should do so based on data1 // (key ID), not the printable char. event.data2 = 0; event.data3 = 0; if (event.data1 != 0) { D_PostEvent(&event); } break; default: break; } } void I_StartTextInput(int x1, int y1, int x2, int y2) { text_input_enabled = true; if (!vanilla_keyboard_mapping) { // SDL2-TODO: SDL_SetTextInputRect(...); SDL_StartTextInput(); } } void I_StopTextInput(void) { text_input_enabled = false; if (!vanilla_keyboard_mapping) { SDL_StopTextInput(); } } static void UpdateMouseButtonState(unsigned int button, boolean on) { static event_t event; if (button < SDL_BUTTON_LEFT || button > MAX_MOUSE_BUTTONS) { return; } // Note: button "0" is left, button "1" is right, // button "2" is middle for Doom. This is different // to how SDL sees things. switch (button) { case SDL_BUTTON_LEFT: button = 0; break; case SDL_BUTTON_RIGHT: button = 1; break; case SDL_BUTTON_MIDDLE: button = 2; break; default: // SDL buttons are indexed from 1. --button; break; } // Turn bit representing this button on or off. if (on) { mouse_button_state |= (1 << button); } else { mouse_button_state &= ~(1 << button); } // Post an event with the new button state. event.type = ev_mouse; event.data1 = mouse_button_state; event.data2 = event.data3 = 0; D_PostEvent(&event); } static void MapMouseWheelToButtons(SDL_MouseWheelEvent *wheel) { // SDL2 distinguishes button events from mouse wheel events. // We want to treat the mouse wheel as two buttons, as per // SDL1 static event_t up, down; int button; if (wheel->y <= 0) { // scroll down button = 4; } else { // scroll up button = 3; } // post a button down event mouse_button_state |= (1 << button); down.type = ev_mouse; down.data1 = mouse_button_state; down.data2 = down.data3 = 0; D_PostEvent(&down); // post a button up event mouse_button_state &= ~(1 << button); up.type = ev_mouse; up.data1 = mouse_button_state; up.data2 = up.data3 = 0; D_PostEvent(&up); } void I_HandleMouseEvent(SDL_Event *sdlevent) { switch (sdlevent->type) { case SDL_MOUSEBUTTONDOWN: UpdateMouseButtonState(sdlevent->button.button, true); break; case SDL_MOUSEBUTTONUP: UpdateMouseButtonState(sdlevent->button.button, false); break; case SDL_MOUSEWHEEL: MapMouseWheelToButtons(&(sdlevent->wheel)); break; default: break; } } static int AccelerateMouse(int val) { if (val < 0) return -AccelerateMouse(-val); if (val > mouse_threshold) { return (int)((val - mouse_threshold) * mouse_acceleration + mouse_threshold); } else { return val; } } // [crispy] static int AccelerateMouseY(int val) { if (val < 0) return -AccelerateMouseY(-val); if (val > mouse_threshold_y) { return (int)((val - mouse_threshold_y) * mouse_acceleration_y + mouse_threshold_y); } else { return val; } } // // Read the change in mouse state to generate mouse motion events // // This is to combine all mouse movement for a tic into one mouse // motion event. void I_ReadMouse(void) { int x, y; event_t ev; SDL_GetRelativeMouseState(&x, &y); if (x != 0 || y != 0) { ev.type = ev_mouse; ev.data1 = mouse_button_state; ev.data2 = AccelerateMouse(x); if (true || !novert) // [crispy] moved to src/*/g_game.c { ev.data3 = -AccelerateMouseY(y); // [crispy] } else { ev.data3 = 0; } // XXX: undefined behaviour since event is scoped to // this function D_PostEvent(&ev); } } // Bind all variables controlling input options. void I_BindInputVariables(void) { M_BindFloatVariable("mouse_acceleration", &mouse_acceleration); M_BindIntVariable("mouse_threshold", &mouse_threshold); M_BindIntVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping); M_BindIntVariable("novert", &novert); M_BindFloatVariable("mouse_acceleration_y", &mouse_acceleration_y); // [crispy] M_BindIntVariable("mouse_threshold_y", &mouse_threshold_y); // [crispy] M_BindIntVariable("mouse_y_invert", &mouse_y_invert); // [crispy] } crispy-doom-crispy-doom-5.6.4/src/i_input.h000066400000000000000000000026341360717211000206500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System-specific keyboard/mouse input. // #ifndef __I_INPUT__ #define __I_INPUT__ #include "doomtype.h" #define MAX_MOUSE_BUTTONS 8 extern float mouse_acceleration; extern int mouse_threshold; extern float mouse_acceleration_y; // [crispy] extern int mouse_threshold_y; // [crispy] extern int mouse_y_invert; // [crispy] extern int novert; // [crispy] void I_BindInputVariables(void); void I_ReadMouse(void); // I_StartTextInput begins text input, activating the on-screen keyboard // (if one is used). The caller indicates that any entered text will be // displayed in the rectangle given by the provided set of coordinates. void I_StartTextInput(int x1, int y1, int x2, int y2); // I_StopTextInput finishes text input, deactivating the on-screen keyboard // (if one is used). void I_StopTextInput(void); #endif crispy-doom-crispy-doom-5.6.4/src/i_joystick.c000066400000000000000000000226741360717211000213510ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // SDL Joystick code. // #include "SDL.h" #include "SDL_joystick.h" #include #include #include #include "doomtype.h" #include "d_event.h" #include "i_joystick.h" #include "i_system.h" #include "m_config.h" #include "m_misc.h" // When an axis is within the dead zone, it is set to zero. // This is 5% of the full range: #define DEAD_ZONE (32768 / 3) static SDL_Joystick *joystick = NULL; // Configuration variables: // Standard default.cfg Joystick enable/disable static int usejoystick = 0; // SDL GUID and index of the joystick to use. static char *joystick_guid = ""; static int joystick_index = -1; // Which joystick axis to use for horizontal movement, and whether to // invert the direction: static int joystick_x_axis = 0; static int joystick_x_invert = 0; // Which joystick axis to use for vertical movement, and whether to // invert the direction: static int joystick_y_axis = 1; static int joystick_y_invert = 0; // Which joystick axis to use for strafing? static int joystick_strafe_axis = -1; static int joystick_strafe_invert = 0; // Which joystick axis to use for looking? static int joystick_look_axis = -1; static int joystick_look_invert = 0; // Virtual to physical button joystick button mapping. By default this // is a straight mapping. static int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; void I_ShutdownJoystick(void) { if (joystick != NULL) { SDL_JoystickClose(joystick); joystick = NULL; SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } } static boolean IsValidAxis(int axis) { int num_axes; if (axis < 0) { return true; } if (IS_BUTTON_AXIS(axis)) { return true; } if (IS_HAT_AXIS(axis)) { return HAT_AXIS_HAT(axis) < SDL_JoystickNumHats(joystick); } num_axes = SDL_JoystickNumAxes(joystick); return axis < num_axes; } static int DeviceIndex(void) { SDL_JoystickGUID guid, dev_guid; int i; guid = SDL_JoystickGetGUIDFromString(joystick_guid); // GUID identifies a class of device rather than a specific device. // Check if joystick_index has the expected GUID, as this can act // as a tie-breaker in case there are multiple identical devices. if (joystick_index >= 0 && joystick_index < SDL_NumJoysticks()) { dev_guid = SDL_JoystickGetDeviceGUID(joystick_index); if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID))) { return joystick_index; } } // Check all devices to look for one with the expected GUID. for (i = 0; i < SDL_NumJoysticks(); ++i) { dev_guid = SDL_JoystickGetDeviceGUID(i); if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID))) { printf("I_InitJoystick: Joystick moved to index %d.\n", i); return i; } } // No joystick found with the expected GUID. return -1; } void I_InitJoystick(void) { int index; if (!usejoystick || !strcmp(joystick_guid, "")) { return; } if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { return; } index = DeviceIndex(); if (index < 0) { printf("I_InitJoystick: Couldn't find joystick with GUID \"%s\": " "device not found or not connected?\n", joystick_guid); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); return; } // Open the joystick joystick = SDL_JoystickOpen(index); if (joystick == NULL) { printf("I_InitJoystick: Failed to open joystick #%i\n", index); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); return; } if (!IsValidAxis(joystick_x_axis) || !IsValidAxis(joystick_y_axis) || !IsValidAxis(joystick_strafe_axis) || !IsValidAxis(joystick_look_axis)) { printf("I_InitJoystick: Invalid joystick axis for configured joystick " "(run joystick setup again)\n"); SDL_JoystickClose(joystick); joystick = NULL; SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } SDL_JoystickEventState(SDL_ENABLE); // Initialized okay! printf("I_InitJoystick: %s\n", SDL_JoystickName(joystick)); I_AtExit(I_ShutdownJoystick, true); } static boolean IsAxisButton(int physbutton) { if (IS_BUTTON_AXIS(joystick_x_axis)) { if (physbutton == BUTTON_AXIS_NEG(joystick_x_axis) || physbutton == BUTTON_AXIS_POS(joystick_x_axis)) { return true; } } if (IS_BUTTON_AXIS(joystick_y_axis)) { if (physbutton == BUTTON_AXIS_NEG(joystick_y_axis) || physbutton == BUTTON_AXIS_POS(joystick_y_axis)) { return true; } } if (IS_BUTTON_AXIS(joystick_strafe_axis)) { if (physbutton == BUTTON_AXIS_NEG(joystick_strafe_axis) || physbutton == BUTTON_AXIS_POS(joystick_strafe_axis)) { return true; } } if (IS_BUTTON_AXIS(joystick_look_axis)) { if (physbutton == BUTTON_AXIS_NEG(joystick_look_axis) || physbutton == BUTTON_AXIS_POS(joystick_look_axis)) { return true; } } return false; } // Get the state of the given virtual button. static int ReadButtonState(int vbutton) { int physbutton; // Map from virtual button to physical (SDL) button. if (vbutton < NUM_VIRTUAL_BUTTONS) { physbutton = joystick_physical_buttons[vbutton]; } else { physbutton = vbutton; } // Never read axis buttons as buttons. if (IsAxisButton(physbutton)) { return 0; } return SDL_JoystickGetButton(joystick, physbutton); } // Get a bitmask of all currently-pressed buttons static int GetButtonsState(void) { int i; int result; result = 0; for (i = 0; i < 20; ++i) { if (ReadButtonState(i)) { result |= 1 << i; } } return result; } // Read the state of an axis, inverting if necessary. static int GetAxisState(int axis, int invert) { int result; // Axis -1 means disabled. if (axis < 0) { return 0; } // Is this a button axis, or a hat axis? // If so, we need to handle it specially. result = 0; if (IS_BUTTON_AXIS(axis)) { if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_NEG(axis))) { result -= 32767; } if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_POS(axis))) { result += 32767; } } else if (IS_HAT_AXIS(axis)) { int direction = HAT_AXIS_DIRECTION(axis); int hatval = SDL_JoystickGetHat(joystick, HAT_AXIS_HAT(axis)); if (direction == HAT_AXIS_HORIZONTAL) { if ((hatval & SDL_HAT_LEFT) != 0) { result -= 32767; } else if ((hatval & SDL_HAT_RIGHT) != 0) { result += 32767; } } else if (direction == HAT_AXIS_VERTICAL) { if ((hatval & SDL_HAT_UP) != 0) { result -= 32767; } else if ((hatval & SDL_HAT_DOWN) != 0) { result += 32767; } } } else { result = SDL_JoystickGetAxis(joystick, axis); if (result < DEAD_ZONE && result > -DEAD_ZONE) { result = 0; } } if (invert) { result = -result; } return result; } void I_UpdateJoystick(void) { if (joystick != NULL) { event_t ev; ev.type = ev_joystick; ev.data1 = GetButtonsState(); ev.data2 = GetAxisState(joystick_x_axis, joystick_x_invert); ev.data3 = GetAxisState(joystick_y_axis, joystick_y_invert); ev.data4 = GetAxisState(joystick_strafe_axis, joystick_strafe_invert); ev.data5 = GetAxisState(joystick_look_axis, joystick_look_invert); D_PostEvent(&ev); } } void I_BindJoystickVariables(void) { int i; M_BindIntVariable("use_joystick", &usejoystick); M_BindStringVariable("joystick_guid", &joystick_guid); M_BindIntVariable("joystick_index", &joystick_index); M_BindIntVariable("joystick_x_axis", &joystick_x_axis); M_BindIntVariable("joystick_y_axis", &joystick_y_axis); M_BindIntVariable("joystick_strafe_axis", &joystick_strafe_axis); M_BindIntVariable("joystick_x_invert", &joystick_x_invert); M_BindIntVariable("joystick_y_invert", &joystick_y_invert); M_BindIntVariable("joystick_strafe_invert",&joystick_strafe_invert); M_BindIntVariable("joystick_look_axis", &joystick_look_axis); M_BindIntVariable("joystick_look_invert", &joystick_look_invert); for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i) { char name[32]; M_snprintf(name, sizeof(name), "joystick_physical_button%i", i); M_BindIntVariable(name, &joystick_physical_buttons[i]); } } crispy-doom-crispy-doom-5.6.4/src/i_joystick.h000066400000000000000000000047171360717211000213540ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System-specific joystick interface. // #ifndef __I_JOYSTICK__ #define __I_JOYSTICK__ // Number of "virtual" joystick buttons defined in configuration files. // This needs to be at least as large as the number of different key // bindings supported by the higher-level game code (joyb* variables). #define NUM_VIRTUAL_BUTTONS 11 // If this bit is set in a configuration file axis value, the axis is // not actually a joystick axis, but instead is a "button axis". This // means that instead of reading an SDL joystick axis, we read the // state of two buttons to get the axis value. This is needed for eg. // the PS3 SIXAXIS controller, where the D-pad buttons register as // buttons, not as two axes. #define BUTTON_AXIS 0x10000 // Query whether a given axis value describes a button axis. #define IS_BUTTON_AXIS(axis) ((axis) >= 0 && ((axis) & BUTTON_AXIS) != 0) // Get the individual buttons from a button axis value. #define BUTTON_AXIS_NEG(axis) ((axis) & 0xff) #define BUTTON_AXIS_POS(axis) (((axis) >> 8) & 0xff) // Create a button axis value from two button values. #define CREATE_BUTTON_AXIS(neg, pos) (BUTTON_AXIS | (neg) | ((pos) << 8)) // If this bit is set in an axis value, the axis is not actually a // joystick axis, but is a "hat" axis. This means that we read (one of) // the hats on the joystick. #define HAT_AXIS 0x20000 #define IS_HAT_AXIS(axis) ((axis) >= 0 && ((axis) & HAT_AXIS) != 0) // Get the hat number from a hat axis value. #define HAT_AXIS_HAT(axis) ((axis) & 0xff) // Which axis of the hat? (horizonal or vertical) #define HAT_AXIS_DIRECTION(axis) (((axis) >> 8) & 0xff) #define CREATE_HAT_AXIS(hat, direction) \ (HAT_AXIS | (hat) | ((direction) << 8)) #define HAT_AXIS_HORIZONTAL 1 #define HAT_AXIS_VERTICAL 2 void I_InitJoystick(void); void I_ShutdownJoystick(void); void I_UpdateJoystick(void); void I_BindJoystickVariables(void); #endif /* #ifndef __I_JOYSTICK__ */ crispy-doom-crispy-doom-5.6.4/src/i_main.c000066400000000000000000000037031360717211000204260ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Main program, simply calls D_DoomMain high level loop. // #include "config.h" #include "crispy.h" #include #include "SDL.h" #include "doomtype.h" #include "i_system.h" #include "m_argv.h" #include "m_misc.h" // [crispy] M_snprintf() // // D_DoomMain() // Not a globally visible function, just included for source reference, // calls all startup code, parses command line options. // void D_DoomMain (void); int main(int argc, char **argv) { // save arguments myargc = argc; myargv = argv; //! // Print the program version and exit. // if (M_ParmExists("-version") || M_ParmExists("--version")) { puts(PACKAGE_STRING); exit(0); } { char buf[16]; SDL_version version; SDL_GetVersion(&version); M_snprintf(buf, sizeof(buf), "%d.%d.%d", version.major, version.minor, version.patch); crispy->sdlversion = M_StringDuplicate(buf); crispy->platform = SDL_GetPlatform(); } #if defined(_WIN32) // compose a proper command line from loose file paths passed as arguments // to allow for loading WADs and DEHACKED patches by drag-and-drop M_AddLooseFiles(); #endif M_FindResponseFile(); #ifdef SDL_HINT_NO_SIGNAL_HANDLERS SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); #endif // start doom D_DoomMain (); return 0; } crispy-doom-crispy-doom-5.6.4/src/i_midipipe.c000066400000000000000000000273261360717211000213110ustar00rootroot00000000000000// // Copyright(C) 2013 James Haley et al. // Copyright(C) 2017 Alex Mayfield // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // Client Interface to Midi Server // #if _WIN32 #include #include #define WIN32_LEAN_AND_MEAN #include #include "i_midipipe.h" #include "config.h" #include "i_sound.h" #include "i_timer.h" #include "m_misc.h" #include "net_packet.h" #include "../midiproc/proto.h" #if defined(_DEBUG) #define DEBUGOUT(s) puts(s) #else #define DEBUGOUT(s) #endif //============================================================================= // // Public Data // // True if the midi proces was initialized at least once and has not been // explicitly shut down. This remains true if the server is momentarily // unreachable. boolean midi_server_initialized = false; // True if the current track is being handled via the MIDI server. boolean midi_server_registered = false; //============================================================================= // // Data // #define MIDIPIPE_MAX_WAIT 1000 // Max amount of ms to wait for expected data. static HANDLE midi_process_in_reader; // Input stream for midi process. static HANDLE midi_process_in_writer; static HANDLE midi_process_out_reader; // Output stream for midi process. static HANDLE midi_process_out_writer; //============================================================================= // // Private functions // // // FreePipes // // Free all pipes in use by this module. // static void FreePipes() { if (midi_process_in_reader != NULL) { CloseHandle(midi_process_in_reader); midi_process_in_reader = NULL; } if (midi_process_in_writer != NULL) { CloseHandle(midi_process_in_writer); midi_process_in_writer = NULL; } if (midi_process_out_reader != NULL) { CloseHandle(midi_process_out_reader); midi_process_in_reader = NULL; } if (midi_process_out_writer != NULL) { CloseHandle(midi_process_out_writer); midi_process_out_writer = NULL; } } // // UsingNativeMidi // // Enumerate all music decoders and return true if NATIVEMIDI is one of them. // // If this is the case, using the MIDI server is probably necessary. If not, // we're likely using Timidity and thus don't need to start the server. // static boolean UsingNativeMidi() { int i; int decoders = Mix_GetNumMusicDecoders(); for (i = 0; i < decoders; i++) { if (strcmp(Mix_GetMusicDecoder(i), "NATIVEMIDI") == 0) { return true; } } return false; } // // WritePipe // // Writes packet data to the subprocess' standard in. // static boolean WritePipe(net_packet_t *packet) { DWORD bytes_written; BOOL ok = WriteFile(midi_process_in_writer, packet->data, packet->len, &bytes_written, NULL); return ok; } // // ExpectPipe // // Expect the contents of a packet off of the subprocess' stdout. If the // response is unexpected, or doesn't arrive within a specific amuont of time, // assume the subprocess is in an unknown state. // static boolean ExpectPipe(net_packet_t *packet) { int start; BOOL ok; CHAR pipe_buffer[8192]; DWORD pipe_buffer_read = 0; if (packet->len > sizeof(pipe_buffer)) { // The size of the packet we're expecting is larger than our buffer // size, so bail out now. return false; } start = I_GetTimeMS(); do { // Wait until we see exactly the amount of data we expect on the pipe. ok = PeekNamedPipe(midi_process_out_reader, NULL, 0, NULL, &pipe_buffer_read, NULL); if (!ok) { break; } else if (pipe_buffer_read < packet->len) { I_Sleep(1); continue; } // Read precisely the number of bytes we're expecting, and no more. ok = ReadFile(midi_process_out_reader, pipe_buffer, packet->len, &pipe_buffer_read, NULL); if (!ok || pipe_buffer_read != packet->len) { break; } // Compare our data buffer to the packet. if (memcmp(packet->data, pipe_buffer, packet->len) != 0) { break; } return true; // Continue looping as long as we don't exceed our maximum wait time. } while (I_GetTimeMS() - start <= MIDIPIPE_MAX_WAIT); // TODO: Deal with the wedged process? return false; } // // RemoveFileSpec // // A reimplementation of PathRemoveFileSpec that doesn't bring in Shlwapi // void RemoveFileSpec(TCHAR *path, size_t size) { TCHAR *fp = NULL; fp = &path[size]; while (path <= fp && *fp != DIR_SEPARATOR) { fp--; } *(fp + 1) = '\0'; } static boolean BlockForAck(void) { boolean ok; net_packet_t *packet; packet = NET_NewPacket(2); NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_ACK); ok = ExpectPipe(packet); NET_FreePacket(packet); return ok; } //============================================================================= // // Protocol Commands // // // I_MidiPipe_RegisterSong // // Tells the MIDI subprocess to load a specific filename for playing. This // function blocks until there is an acknowledgement from the server. // boolean I_MidiPipe_RegisterSong(char *filename) { boolean ok; net_packet_t *packet; packet = NET_NewPacket(64); NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_REGISTER_SONG); NET_WriteString(packet, filename); ok = WritePipe(packet); NET_FreePacket(packet); midi_server_registered = false; ok = ok && BlockForAck(); if (!ok) { DEBUGOUT("I_MidiPipe_RegisterSong failed"); return false; } midi_server_registered = true; DEBUGOUT("I_MidiPipe_RegisterSong succeeded"); return true; } // // I_MidiPipe_UnregisterSong // // Tells the MIDI subprocess to unload the current song. // void I_MidiPipe_UnregisterSong(void) { boolean ok; net_packet_t *packet; packet = NET_NewPacket(64); NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_UNREGISTER_SONG); ok = WritePipe(packet); NET_FreePacket(packet); ok = ok && BlockForAck(); if (!ok) { DEBUGOUT("I_MidiPipe_UnregisterSong failed"); return; } midi_server_registered = false; DEBUGOUT("I_MidiPipe_UnregisterSong succeeded"); } // // I_MidiPipe_SetVolume // // Tells the MIDI subprocess to set a specific volume for the song. // void I_MidiPipe_SetVolume(int vol) { boolean ok; net_packet_t *packet; packet = NET_NewPacket(6); NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_SET_VOLUME); NET_WriteInt32(packet, vol); ok = WritePipe(packet); NET_FreePacket(packet); ok = ok && BlockForAck(); if (!ok) { DEBUGOUT("I_MidiPipe_SetVolume failed"); return; } DEBUGOUT("I_MidiPipe_SetVolume succeeded"); } // // I_MidiPipe_PlaySong // // Tells the MIDI subprocess to play the currently loaded song. // void I_MidiPipe_PlaySong(int loops) { boolean ok; net_packet_t *packet; packet = NET_NewPacket(6); NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_PLAY_SONG); NET_WriteInt32(packet, loops); ok = WritePipe(packet); NET_FreePacket(packet); ok = ok && BlockForAck(); if (!ok) { DEBUGOUT("I_MidiPipe_PlaySong failed"); return; } DEBUGOUT("I_MidiPipe_PlaySong succeeded"); } // // I_MidiPipe_StopSong // // Tells the MIDI subprocess to stop playing the currently loaded song. // void I_MidiPipe_StopSong() { boolean ok; net_packet_t *packet; packet = NET_NewPacket(2); NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_STOP_SONG); ok = WritePipe(packet); NET_FreePacket(packet); ok = ok && BlockForAck(); if (!ok) { DEBUGOUT("I_MidiPipe_StopSong failed"); return; } DEBUGOUT("I_MidiPipe_StopSong succeeded"); } // // I_MidiPipe_ShutdownServer // // Tells the MIDI subprocess to shutdown. // void I_MidiPipe_ShutdownServer() { boolean ok; net_packet_t *packet; packet = NET_NewPacket(2); NET_WriteInt16(packet, MIDIPIPE_PACKET_TYPE_SHUTDOWN); ok = WritePipe(packet); NET_FreePacket(packet); ok = ok && BlockForAck(); FreePipes(); midi_server_initialized = false; if (!ok) { DEBUGOUT("I_MidiPipe_ShutdownServer failed"); return; } DEBUGOUT("I_MidiPipe_ShutdownServer succeeded"); } //============================================================================= // // Public Interface // // // I_MidiPipeInitServer // // Start up the MIDI server. // boolean I_MidiPipe_InitServer() { TCHAR dirname[MAX_PATH + 1]; DWORD dirname_len; char *module = NULL; char *cmdline = NULL; char params_buf[128]; SECURITY_ATTRIBUTES sec_attrs; PROCESS_INFORMATION proc_info; STARTUPINFO startup_info; BOOL ok; if (!UsingNativeMidi() || strlen(snd_musiccmd) > 0) { // If we're not using native MIDI, or if we're playing music through // an exteranl program, we don't need to start the server. return false; } // Get directory name memset(dirname, 0, sizeof(dirname)); dirname_len = GetModuleFileName(NULL, dirname, MAX_PATH); if (dirname_len == 0) { return false; } RemoveFileSpec(dirname, dirname_len); // Define the module. module = PROGRAM_PREFIX "midiproc.exe"; // Set up pipes memset(&sec_attrs, 0, sizeof(SECURITY_ATTRIBUTES)); sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES); sec_attrs.bInheritHandle = TRUE; sec_attrs.lpSecurityDescriptor = NULL; if (!CreatePipe(&midi_process_in_reader, &midi_process_in_writer, &sec_attrs, 0)) { DEBUGOUT("Could not initialize midiproc stdin"); return false; } if (!SetHandleInformation(midi_process_in_writer, HANDLE_FLAG_INHERIT, 0)) { DEBUGOUT("Could not disinherit midiproc stdin"); return false; } if (!CreatePipe(&midi_process_out_reader, &midi_process_out_writer, &sec_attrs, 0)) { DEBUGOUT("Could not initialize midiproc stdout/stderr"); return false; } if (!SetHandleInformation(midi_process_out_reader, HANDLE_FLAG_INHERIT, 0)) { DEBUGOUT("Could not disinherit midiproc stdin"); return false; } // Define the command line. Version, Sample Rate, and handles follow // the executable name. M_snprintf(params_buf, sizeof(params_buf), "%d %Iu %Iu", snd_samplerate, (size_t) midi_process_in_reader, (size_t) midi_process_out_writer); cmdline = M_StringJoin(module, " \"" PACKAGE_STRING "\"", " ", params_buf, NULL); // Launch the subprocess memset(&proc_info, 0, sizeof(proc_info)); memset(&startup_info, 0, sizeof(startup_info)); startup_info.cb = sizeof(startup_info); ok = CreateProcess(TEXT(module), TEXT(cmdline), NULL, NULL, TRUE, 0, NULL, dirname, &startup_info, &proc_info); if (!ok) { FreePipes(); free(cmdline); return false; } // Since the server has these handles, we don't need them anymore. CloseHandle(midi_process_in_reader); midi_process_in_reader = NULL; CloseHandle(midi_process_out_writer); midi_process_out_writer = NULL; midi_server_initialized = true; return true; } #endif crispy-doom-crispy-doom-5.6.4/src/i_midipipe.h000066400000000000000000000022431360717211000213050ustar00rootroot00000000000000// // Copyright(C) 2013 James Haley et al. // Copyright(C) 2017 Alex Mayfield // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // Client Interface to Midi Server // #ifndef __I_MIDIPIPE__ #define __I_MIDIPIPE__ #if _WIN32 #include "SDL_mixer.h" #include "doomtype.h" extern boolean midi_server_initialized; extern boolean midi_server_registered; boolean I_MidiPipe_RegisterSong(char *filename); void I_MidiPipe_UnregisterSong(void); void I_MidiPipe_SetVolume(int vol); void I_MidiPipe_PlaySong(int loops); void I_MidiPipe_StopSong(); void I_MidiPipe_ShutdownServer(); boolean I_MidiPipe_InitServer(); #else #include "doomtype.h" static const boolean midi_server_registered = false; #endif #endif crispy-doom-crispy-doom-5.6.4/src/i_musicpack.c000066400000000000000000001126621360717211000214660ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System interface for music. // #include #include #include #include #include "SDL.h" #include "SDL_mixer.h" #include "i_glob.h" #include "i_midipipe.h" #include "config.h" #include "doomtype.h" #include "memio.h" #include "mus2mid.h" #include "deh_str.h" #include "gusconf.h" #include "i_sound.h" #include "i_system.h" #include "i_swap.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "sha1.h" #include "w_wad.h" #include "z_zone.h" #define MID_HEADER_MAGIC "MThd" #define MUS_HEADER_MAGIC "MUS\x1a" #define FLAC_HEADER "fLaC" #define OGG_HEADER "OggS" // Looping Vorbis metadata tag names. These have been defined by ZDoom // for specifying the start and end positions for looping music tracks // in .ogg and .flac files. // More information is here: http://zdoom.org/wiki/Audio_loop #define LOOP_START_TAG "LOOP_START" #define LOOP_END_TAG "LOOP_END" // FLAC metadata headers that we care about. #define FLAC_STREAMINFO 0 #define FLAC_VORBIS_COMMENT 4 // Ogg metadata headers that we care about. #define OGG_ID_HEADER 1 #define OGG_COMMENT_HEADER 3 // Structure for music substitution. // We store a mapping based on SHA1 checksum -> filename of substitute music // file to play, so that substitution occurs based on content rather than // lump name. This has some inherent advantages: // * Music for Plutonia (reused from Doom 1) works automatically. // * If a PWAD replaces music, the replacement music is used rather than // the substitute music for the IWAD. // * If a PWAD reuses music from an IWAD (even from a different game), we get // the high quality version of the music automatically (neat!) typedef struct { const char *hash_prefix; const char *filename; } subst_music_t; // Structure containing parsed metadata read from a digital music track: typedef struct { boolean valid; unsigned int samplerate_hz; int start_time, end_time; } file_metadata_t; static subst_music_t *subst_music = NULL; static unsigned int subst_music_len = 0; static boolean music_initialized = false; // If this is true, this module initialized SDL sound and has the // responsibility to shut it down static boolean sdl_was_initialized = false; char *music_pack_path = ""; // If true, we are playing a substitute digital track rather than in-WAD // MIDI/MUS track, and file_metadata contains loop metadata. static file_metadata_t file_metadata; // Position (in samples) that we have reached in the current track. // This is updated by the TrackPositionCallback function. static unsigned int current_track_pos; // Currently playing music track. static Mix_Music *current_track_music = NULL; // If true, the currently playing track is being played on loop. static boolean current_track_loop; // Table of known hashes and filenames to look up for them. This allows // users to drop in a set of files without having to also provide a // configuration file. static const subst_music_t known_filenames[] = { // Doom 1 music files. {"b2e05b4e8dff8d76f8f4", "d_inter.{ext}"}, {"0c0acce45130bab935d2", "d_intro.{ext}"}, {"fca4086939a68ae4ed84", "d_victor.{ext}"}, {"5971e5e20554f47ca065", "d_intro.{ext}"}, {"99767e32769229897f77", "d_e1m1.{ext}"}, {"b5e7dfb4efe9e688bf2a", "d_e1m2.{ext}"}, {"fda8fa73e4d30a6b961c", "d_e1m3.{ext}"}, {"3805f9bf3f1702f7e7f5", "d_e1m4.{ext}"}, {"f546ed823b234fe39165", "d_e1m5.{ext}"}, {"4450811b5a6748cfd83e", "d_e1m6.{ext}"}, {"73edb50d96b0ac03be34", "d_e1m7.{ext}"}, {"47d711a6fd32f5047879", "d_e1m8.{ext}"}, {"62c631c2fdaa5ecd9a8d", "d_e1m9.{ext}"}, {"7702a6449585428e7185", "d_e2m1.{ext}"}, {"1cb1810989cbfae2b29b", "d_e2m2.{ext}"}, {"7d740f3c881a22945e47", "d_e2m4.{ext}"}, {"ae9c3dc2f9aeea002327", "d_e2m6.{ext}"}, {"b26aad3caa420e9a2c76", "d_e2m7.{ext}"}, {"90f06251a2a90bfaefd4", "d_e2m8.{ext}"}, {"b2fb439f23c08c8e2577", "d_e3m1.{ext}"}, {"b6c07bb249526b864208", "d_e3m2.{ext}"}, {"ce3587ee503ffe707b2d", "d_e3m3.{ext}"}, {"d746ea2aa16b3237422c", "d_e3m8.{ext}"}, {"3da3b1335560a92912e6", "d_bunny.{ext}"}, // Duplicates that don't have identical hashes: {"4a5badc4f10a7d4ed021", "d_inter.{ext}"}, // E2M3 {"36b14bf165b3fdd3958e", "d_e1m7.{ext}"}, // E3M5 {"e77c3d42f2ea87f04607", "d_e1m6.{ext}"}, // E3M6 {"3d85ec9c10b5ea465568", "d_e2m7.{ext}"}, // E3M7 {"4d42e2ce1c1ff192500e", "d_e1m9.{ext}"}, // E3M9 // These tracks are reused in Alien Vendetta, but are MIDs: {"a05e45f67e1b64733fe3", "d_e2m1.{ext}"}, // MAP02 {"8024ae1616ddd97ce330", "d_e1m4.{ext}"}, // MAP03 {"3af8d79ddba49edaf9eb", "d_victor.{ext}"}, // MAP05 {"a55352c96c025b6bd08a", "d_inter.{ext}"}, // MAP07 {"76d1fc25ab7b1b4a58d6", "d_e1m8.{ext}"}, // MAP11 {"497777f0863eca7cea87", "d_e1m2.{ext}"}, // MAP12 {"0228fd87f8762f112fb6", "d_e2m2.{ext}"}, // MAP13 {"db94e8e1d7c02092eab5", "d_e1m6.{ext}"}, // MAP14 {"5a8d7a307eebc952795c", "d_e2m7.{ext}"}, // MAP16 {"1a36b692bf26d94a72cc", "d_e1m7.{ext}"}, // MAP23 {"37c6cefa351b06995152", "d_e1m5.{ext}"}, // MAP27 {"36b97b87fe98348d44b6", "d_e2m6.{ext}"}, // MAP28 // Doom II music files. {"79080e9681a2d7bec3fb", "d_runnin.{ext}"}, // MAP01,15 {"868b3aae73c7b12e92c0", "d_stalks.{ext}"}, // MAP02,11,17 {"19237754d2eb85f41d84", "d_countd.{ext}"}, // MAP03,21 {"00abff3b61b25a6855d2", "d_betwee.{ext}"}, // MAP04 {"954636c7ee09edf5d98f", "d_doom.{ext}"}, // MAP05,13 {"8d32b2b7aa3b806474c1", "d_the_da.{ext}"}, // MAP06,12,24 {"41efc3c84bb321af2b6b", "d_shawn.{ext}"}, // MAP07,19,29 // Assuming single D_DDTBLU: http://doomwiki.org/wiki/Doom_II_music#Trivia {"51c0872fec9f43259318", "d_ddtblu.{ext}"}, // MAP08 {"acb7ad85494d18235df8", "d_ddtblu.{ext}"}, // MAP14,22 {"4b7ceccbf47e78e2fa0b", "d_in_cit.{ext}"}, // MAP09 {"1d1f4a9edba174584e11", "d_dead.{ext}"}, // MAP10,16 {"1736c81aac77f9bffd3d", "d_romero.{ext}"}, // MAP18,27 {"a55d400570ad255a576b", "d_messag.{ext}"}, // MAP20,26 {"29d30c3fbd712016f2e5", "d_ampie.{ext}"}, // MAP23 {"bcfe9786afdcfb704afa", "d_adrian.{ext}"}, // MAP25 {"e05c10389e71836834ae", "d_tense.{ext}"}, // MAP28 {"b779022b1d0f0010b8f0", "d_openin.{ext}"}, // MAP30 {"a9a5f7b0ab3be0f4fc24", "d_evil.{ext}"}, // MAP31 {"4503d155aafec0296689", "d_ultima.{ext}"}, // MAP32 {"56f2363f01df38908c77", "d_dm2ttl.{ext}"}, {"71e58baf9e9dea4dd24a", "d_dm2int.{ext}"}, {"e632318629869811f7dc", "d_read_m.{ext}"}, // Duplicate filenames: the above filenames are the "canonical" files // for the given SHA1 hashes, but we can also look for these filenames // corresponding to the duplicated music tracks too. {"868b3aae73c7b12e92c0", "d_stlks2.{ext}"}, {"868b3aae73c7b12e92c0", "d_stlks3.{ext}"}, {"8d32b2b7aa3b806474c1", "d_theda2.{ext}"}, {"8d32b2b7aa3b806474c1", "d_theda3.{ext}"}, {"954636c7ee09edf5d98f", "d_doom2.{ext}"}, {"acb7ad85494d18235df8", "d_ddtbl2.{ext}"}, {"acb7ad85494d18235df8", "d_ddtbl3.{ext}"}, {"79080e9681a2d7bec3fb", "d_runni2.{ext}"}, {"1d1f4a9edba174584e11", "d_dead2.{ext}"}, {"41efc3c84bb321af2b6b", "d_shawn2.{ext}"}, {"41efc3c84bb321af2b6b", "d_shawn3.{ext}"}, {"19237754d2eb85f41d84", "d_count2.{ext}"}, {"a55d400570ad255a576b", "d_messg2.{ext}"}, {"1736c81aac77f9bffd3d", "d_romer2.{ext}"}, // These tracks are reused in Alien Vendetta, but are MIDs: {"9433604c098b7b1119a4", "d_in_cit.{ext}"}, // MAP26 // Heretic tracks. {"12818ca0d3c957e7d57e", "mus_titl.{ext}"}, {"5cb988538ce1b1857349", "mus_intr.{ext}"}, {"6f126abe35a78b61b930", "mus_cptd.{ext}"}, {"62557250f0427c067dc9", "mus_e1m1.{ext}"}, {"1e8d5fd814490b9ae166", "mus_e1m2.{ext}"}, {"f0f31e8834e85035d434", "mus_e1m3.{ext}"}, {"054d6997405cc5a32b46", "mus_e1m4.{ext}"}, {"31950ab062cc1e5ca49d", "mus_e1m5.{ext}"}, {"7389024fbab0dff47211", "mus_e1m6.{ext}"}, {"f2aa312dddd0a294a095", "mus_e1m7.{ext}"}, {"cd6856731d1ae1f3aa4e", "mus_e1m8.{ext}"}, {"d7fe793f266733d92e61", "mus_e1m9.{ext}"}, {"933545b48fad8c66f042", "mus_e2m1.{ext}"}, {"bf88ecd4ae1621222592", "mus_e2m2.{ext}"}, {"4f619f87a828c2ca4801", "mus_e2m3.{ext}"}, {"13033a83c49424b2f2ab", "mus_e2m4.{ext}"}, {"b3851f9351ae411d9de3", "mus_e2m6.{ext}"}, {"82539791159fbbc02a23", "mus_e2m7.{ext}"}, {"fd9e53a49cfa62c463a0", "mus_e2m8.{ext}"}, {"29503959324d2ca67958", "mus_e2m9.{ext}"}, {"3aa632257c5be375b97b", "mus_e3m2.{ext}"}, {"69ba0dce7913d53b67a8", "mus_e3m3.{ext}"}, // These Heretic tracks are reused in Alien Vendetta, but are MIDs: {"51344131e8d260753ce7", "mus_e2m3.{ext}"}, // MAP15 {"78b570b2397570440aff", "mus_e1m1.{ext}"}, // MAP19 {"ee21ba9fad4de3dfaef0", "mus_e1m4.{ext}"}, // MAP29 {"d2bb643a60696ccbca03", "mus_e1m9.{ext}"}, // MAP32 // Hexen tracks: {"fbf55fc1ee26bd01266b", "winnowr.{ext}"}, {"71776e2da2b7ba607d81", "jachr.{ext}"}, {"c5c8630608b8132b33cd", "simonr.{ext}"}, {"43683b3f55a031de88d4", "wutzitr.{ext}"}, {"a6062883f29436ef73db", "falconr.{ext}"}, {"512cb6cc9b558d5f0fef", "levelr.{ext}"}, {"d31226ae75fce6a24208", "chartr.{ext}"}, {"bf1f1e561bbdba4e699f", "swampr.{ext}"}, {"b303193f756ca0e2de0f", "deepr.{ext}"}, {"f0635f0386d883b00186", "fubasr.{ext}"}, {"18f2a01f83df6e3abedc", "grover.{ext}"}, {"b2527eb0522f08b2cf5f", "fortr.{ext}"}, {"343addba8ba53a20a160", "foojar.{ext}"}, {"c13109045b06b5a63386", "sixater.{ext}"}, {"693525aaf69eac5429ab", "wobabyr.{ext}"}, {"8f884223811c2bb8311d", "cryptr.{ext}"}, {"de540e6826e62b32c01c", "fantar.{ext}"}, {"efdff548df918934f71f", "blechr.{ext}"}, {"de91f150f6a127e72e35", "voidr.{ext}"}, {"e0497fe27289fe18515b", "chap_1r.{ext}"}, {"f2ef1abdc3f672a3519a", "chap_2r.{ext}"}, {"78cd9882f61cc441bef4", "chap_3r.{ext}"}, {"97b2b575d9d096c1f89f", "chap_4r.{ext}"}, {"ad0197a0f6c52ac30915", "chippyr.{ext}"}, {"30506c62e9f0989ffe09", "percr.{ext}"}, {"3542803beaa43bf1de1a", "secretr.{ext}"}, {"81067721f40c611d09fb", "bonesr.{ext}"}, {"4822af2e1a2eb7faf660", "octor.{ext}"}, {"26bb3cec902ed8008fc2", "rithmr.{ext}"}, {"94ab641c7aa93caac77a", "stalkr.{ext}"}, {"d0a3f337c54b0703b4d3", "borkr.{ext}"}, {"79e7781ec7eb9b9434b5", "crucibr.{ext}"}, {"c2786e5581a7f8801969", "hexen.{ext}"}, {"97fae9a084c0efda5151", "hub.{ext}"}, {"c5da52d5c2ec4803ef8f", "hall.{ext}"}, {"1e71bc0e2feafb06214e", "orb.{ext}"}, {"bc9dcfa6632e847e03af", "chess.{ext}"}, // Hexen CD tracks: alternate filenames for a ripped copy of // the CD soundtrack. {"71776e2da2b7ba607d81", "hexen02.{ext}"}, // level 2 (jachr) {"efdff548df918934f71f", "hexen03.{ext}"}, // level 26 (blechr) {"c2786e5581a7f8801969", "hexen04.{ext}"}, // (hexen) {"1e71bc0e2feafb06214e", "hexen05.{ext}"}, // (orb) {"f0635f0386d883b00186", "hexen06.{ext}"}, // level 10 (fubasr) {"bc9dcfa6632e847e03af", "hexen07.{ext}"}, // (chess) {"8f884223811c2bb8311d", "hexen08.{ext}"}, // level 24 (cryptr) {"a6062883f29436ef73db", "hexen09.{ext}"}, // level 5 (falconr) {"4822af2e1a2eb7faf660", "hexen10.{ext}"}, // level 36 (octor) {"26bb3cec902ed8008fc2", "hexen11.{ext}"}, // level 37 (rithmr) {"c13109045b06b5a63386", "hexen12.{ext}"}, // level 22 (sixater) {"fbf55fc1ee26bd01266b", "hexen13.{ext}"}, // level 1 (winnowr) {"bf1f1e561bbdba4e699f", "hexen14.{ext}"}, // level 8 (swampr) {"43683b3f55a031de88d4", "hexen15.{ext}"}, // level 4 (wutzitr) {"81067721f40c611d09fb", "hexen16.{ext}"}, // level 35 (bonesr) {"e0497fe27289fe18515b", "hexen17.{ext}"}, // level 28 (chap_1r) {"97b2b575d9d096c1f89f", "hexen18.{ext}"}, // level 31 (chap_4r) {"de540e6826e62b32c01c", "hexen19.{ext}"}, // level 25 (fantar) {"343addba8ba53a20a160", "hexen20.{ext}"}, // level 21 (foojar) {"512cb6cc9b558d5f0fef", "hexen21.{ext}"}, // level 6 (levelr) {"c5c8630608b8132b33cd", "hexen22.{ext}"}, // level 3 (simonr) // Strife: {"8ac2b2b47707f0fdf8f6", "d_logo.{ext}"}, // Title {"62e1c58054a1f1bc39b2", "d_action.{ext}"}, // 1,15,28 {"12fa000f3fa1edac5c4f", "d_tavern.{ext}"}, // 2 {"695e56ab3251792d20e5", "d_danger.{ext}"}, // 3,11 {"96fe30e8712217b60dd7", "d_fast.{ext}"}, // 4 {"61345598a3de04aad508", "d_darker.{ext}"}, // 6,14 {"52353e9a435b7b1cb268", "d_strike.{ext}"}, // 7,19 {"061164504907bffc9c22", "d_slide.{ext}"}, // 8,18,22 {"3dbb4b703ce69aafcdd5", "d_tribal.{ext}"}, // 9 {"393773688eba050c3548", "d_march.{ext}"}, // 10 {"3cba3c627de065a667dd", "d_mood.{ext}"}, // 12 {"b1f65a333e5c70255784", "d_castle.{ext}"}, // 13 {"e1455a83a04c9ac4a09f", "d_fight.{ext}"}, // 16,31 {"17f822b7374b1f069b89", "d_spense.{ext}"}, // 17 {"e66c5a1a7d05f021f4ae", "d_dark.{ext}"}, // 20 {"1c92bd0625026af30dad", "d_tech.{ext}"}, // 21,27 {"7ae280713d078de7933a", "d_drone.{ext}"}, // 23,30 {"4a664afd0d7eae79c97a", "d_panthr.{ext}"}, // 24 {"4a7d62beeac5601ccf21", "d_sad.{ext}"}, // 25 {"e60e109779400f2855d7", "d_instry.{ext}"}, // 26,29 {"b7d36878faeb291d6df5", "d_happy.{ext}"}, // Better ending {"ff4a342c8c5ec51b06c3", "d_end.{ext}"}, // Worse ending // This conflicts with Doom's d_intro: //{"ec8fa484c4e85adbf700", "d_intro.{ext}"}, // 5 }; // Given a time string (for LOOP_START/LOOP_END), parse it and return // the time (in # samples since start of track) it represents. static unsigned int ParseVorbisTime(unsigned int samplerate_hz, char *value) { char *num_start, *p; unsigned int result = 0; char c; if (strchr(value, ':') == NULL) { return atoi(value); } result = 0; num_start = value; for (p = value; *p != '\0'; ++p) { if (*p == '.' || *p == ':') { c = *p; *p = '\0'; result = result * 60 + atoi(num_start); num_start = p + 1; *p = c; } if (*p == '.') { return result * samplerate_hz + (unsigned int) (atof(p) * samplerate_hz); } } return (result * 60 + atoi(num_start)) * samplerate_hz; } // Given a vorbis comment string (eg. "LOOP_START=12345"), set fields // in the metadata structure as appropriate. static void ParseVorbisComment(file_metadata_t *metadata, char *comment) { char *eq, *key, *value; eq = strchr(comment, '='); if (eq == NULL) { return; } key = comment; *eq = '\0'; value = eq + 1; if (!strcmp(key, LOOP_START_TAG)) { metadata->start_time = ParseVorbisTime(metadata->samplerate_hz, value); } else if (!strcmp(key, LOOP_END_TAG)) { metadata->end_time = ParseVorbisTime(metadata->samplerate_hz, value); } } // Parse a vorbis comments structure, reading from the given file. static void ParseVorbisComments(file_metadata_t *metadata, FILE *fs) { uint32_t buf; unsigned int num_comments, i, comment_len; char *comment; // We must have read the sample rate already from an earlier header. if (metadata->samplerate_hz == 0) { return; } // Skip the starting part we don't care about. if (fread(&buf, 4, 1, fs) < 1) { return; } if (fseek(fs, LONG(buf), SEEK_CUR) != 0) { return; } // Read count field for number of comments. if (fread(&buf, 4, 1, fs) < 1) { return; } num_comments = LONG(buf); // Read each individual comment. for (i = 0; i < num_comments; ++i) { // Read length of comment. if (fread(&buf, 4, 1, fs) < 1) { return; } comment_len = LONG(buf); // Read actual comment data into string buffer. comment = calloc(1, comment_len + 1); if (comment == NULL || fread(comment, 1, comment_len, fs) < comment_len) { free(comment); break; } // Parse comment string. ParseVorbisComment(metadata, comment); free(comment); } } static void ParseFlacStreaminfo(file_metadata_t *metadata, FILE *fs) { byte buf[34]; // Read block data. if (fread(buf, sizeof(buf), 1, fs) < 1) { return; } // We only care about sample rate and song length. metadata->samplerate_hz = (buf[10] << 12) | (buf[11] << 4) | (buf[12] >> 4); // Song length is actually a 36 bit field, but 32 bits should be // enough for everybody. //metadata->song_length = (buf[14] << 24) | (buf[15] << 16) // | (buf[16] << 8) | buf[17]; } static void ParseFlacFile(file_metadata_t *metadata, FILE *fs) { byte header[4]; unsigned int block_type; size_t block_len; boolean last_block; for (;;) { long pos = -1; // Read METADATA_BLOCK_HEADER: if (fread(header, 4, 1, fs) < 1) { return; } block_type = header[0] & ~0x80; last_block = (header[0] & 0x80) != 0; block_len = (header[1] << 16) | (header[2] << 8) | header[3]; pos = ftell(fs); if (pos < 0) { return; } if (block_type == FLAC_STREAMINFO) { ParseFlacStreaminfo(metadata, fs); } else if (block_type == FLAC_VORBIS_COMMENT) { ParseVorbisComments(metadata, fs); } if (last_block) { break; } // Seek to start of next block. if (fseek(fs, pos + block_len, SEEK_SET) != 0) { return; } } } static void ParseOggIdHeader(file_metadata_t *metadata, FILE *fs) { byte buf[21]; if (fread(buf, sizeof(buf), 1, fs) < 1) { return; } metadata->samplerate_hz = (buf[8] << 24) | (buf[7] << 16) | (buf[6] << 8) | buf[5]; } static void ParseOggFile(file_metadata_t *metadata, FILE *fs) { byte buf[7]; unsigned int offset; // Scan through the start of the file looking for headers. They // begin '[byte]vorbis' where the byte value indicates header type. memset(buf, 0, sizeof(buf)); for (offset = 0; offset < 100 * 1024; ++offset) { // buf[] is used as a sliding window. Each iteration, we // move the buffer one byte to the left and read an extra // byte onto the end. memmove(buf, buf + 1, sizeof(buf) - 1); if (fread(&buf[6], 1, 1, fs) < 1) { return; } if (!memcmp(buf + 1, "vorbis", 6)) { switch (buf[0]) { case OGG_ID_HEADER: ParseOggIdHeader(metadata, fs); break; case OGG_COMMENT_HEADER: ParseVorbisComments(metadata, fs); break; default: break; } } } } static void ReadLoopPoints(const char *filename, file_metadata_t *metadata) { FILE *fs; char header[4]; metadata->valid = false; metadata->samplerate_hz = 0; metadata->start_time = 0; metadata->end_time = -1; fs = fopen(filename, "rb"); if (fs == NULL) { return; } // Check for a recognized file format; use the first four bytes // of the file. if (fread(header, 4, 1, fs) < 1) { fclose(fs); return; } if (memcmp(header, FLAC_HEADER, 4) == 0) { ParseFlacFile(metadata, fs); } else if (memcmp(header, OGG_HEADER, 4) == 0) { ParseOggFile(metadata, fs); } fclose(fs); // Only valid if at the very least we read the sample rate. metadata->valid = metadata->samplerate_hz > 0; // If start and end time are both zero, ignore the loop tags. // This is consistent with other source ports. if (metadata->start_time == 0 && metadata->end_time == 0) { metadata->valid = false; } } // Given a MUS lump, look up a substitute MUS file to play instead // (or NULL to just use normal MIDI playback). static const char *GetSubstituteMusicFile(void *data, size_t data_len) { sha1_context_t context; sha1_digest_t hash; const char *filename; char hash_str[sizeof(sha1_digest_t) * 2 + 1]; unsigned int i; // Don't bother doing a hash if we're never going to find anything. if (subst_music_len == 0) { return NULL; } SHA1_Init(&context); SHA1_Update(&context, data, data_len); SHA1_Final(hash, &context); // Build a string representation of the hash. for (i = 0; i < sizeof(sha1_digest_t); ++i) { M_snprintf(hash_str + i * 2, sizeof(hash_str) - i * 2, "%02x", hash[i]); } // Look for a hash that matches. // The substitute mapping list can (intentionally) contain multiple // filename mappings for the same hash. This allows us to try // different files and fall back if our first choice isn't found. filename = NULL; for (i = 0; i < subst_music_len; ++i) { if (M_StringStartsWith(hash_str, subst_music[i].hash_prefix)) { filename = subst_music[i].filename; // If the file exists, then use this file in preference to // any fallbacks. But we always return a filename if it's // in the list, even if it's just so we can print an error // message to the user saying it doesn't exist. if (M_FileExists(filename)) { break; } } } return filename; } static char *GetFullPath(const char *musicdir, const char *path) { char *result; char *systemized_path; // Starting with directory separator means we have an absolute path, // so just return it. if (path[0] == DIR_SEPARATOR) { return M_StringDuplicate(path); } #ifdef _WIN32 // d:\path\... if (isalpha(path[0]) && path[1] == ':' && path[2] == DIR_SEPARATOR) { return M_StringDuplicate(path); } #endif // Paths in the substitute filenames can contain Unix-style / // path separators, but we should convert this to the separator // for the native platform. systemized_path = M_StringReplace(path, "/", DIR_SEPARATOR_S); // Copy config filename and cut off the filename to just get the // parent dir. result = M_StringJoin(musicdir, systemized_path, NULL); free(systemized_path); return result; } // If filename ends with .{ext}, check if a .ogg, .flac or .mp3 exists with // that name, returning it if found. If none exist, NULL is returned. If the // filename doesn't end with .{ext} then it just acts as a wrapper around // GetFullPath(). static char *ExpandFileExtension(const char *musicdir, const char *filename) { static const char *extns[] = {".flac", ".ogg", ".mp3"}; char *replaced, *result; int i; if (!M_StringEndsWith(filename, ".{ext}")) { return GetFullPath(musicdir, filename); } for (i = 0; i < arrlen(extns); ++i) { replaced = M_StringReplace(filename, ".{ext}", extns[i]); result = GetFullPath(musicdir, replaced); free(replaced); if (M_FileExists(result)) { return result; } free(result); } return NULL; } // Add a substitute music file to the lookup list. static void AddSubstituteMusic(const char *musicdir, const char *hash_prefix, const char *filename) { subst_music_t *s; char *path; path = ExpandFileExtension(musicdir, filename); if (path == NULL) { return; } ++subst_music_len; subst_music = I_Realloc(subst_music, sizeof(subst_music_t) * subst_music_len); s = &subst_music[subst_music_len - 1]; s->hash_prefix = hash_prefix; s->filename = path; } static const char *ReadHashPrefix(char *line) { char *result; char *p; int i, len; for (p = line; *p != '\0' && !isspace(*p) && *p != '='; ++p) { if (!isxdigit(*p)) { return NULL; } } len = p - line; if (len == 0 || len > sizeof(sha1_digest_t) * 2) { return NULL; } result = malloc(len + 1); if (result == NULL) { return NULL; } for (i = 0; i < len; ++i) { result[i] = tolower(line[i]); } result[len] = '\0'; return result; } // Parse a line from substitute music configuration file; returns error // message or NULL for no error. static const char *ParseSubstituteLine(char *musicdir, char *line) { const char *hash_prefix; char *filename; char *p; // Strip out comments if present. p = strchr(line, '#'); if (p != NULL) { while (p > line && isspace(*(p - 1))) { --p; } *p = '\0'; } // Skip leading spaces. for (p = line; *p != '\0' && isspace(*p); ++p); // Empty line? This includes comment lines now that comments have // been stripped. if (*p == '\0') { return NULL; } hash_prefix = ReadHashPrefix(p); if (hash_prefix == NULL) { return "Invalid hash prefix"; } p += strlen(hash_prefix); // Skip spaces. for (; *p != '\0' && isspace(*p); ++p); if (*p != '=') { return "Expected '='"; } ++p; // Skip spaces. for (; *p != '\0' && isspace(*p); ++p); filename = p; // We're now at the filename. Cut off trailing space characters. while (strlen(p) > 0 && isspace(p[strlen(p) - 1])) { p[strlen(p) - 1] = '\0'; } if (strlen(p) == 0) { return "No filename specified for music substitution"; } // Expand full path and add to our database of substitutes. AddSubstituteMusic(musicdir, hash_prefix, filename); return NULL; } // Read a substitute music configuration file. static boolean ReadSubstituteConfig(char *musicdir, const char *filename) { char *buffer; char *line; int linenum = 1; // This unnecessarily opens the file twice... if (!M_FileExists(filename)) { return false; } M_ReadFile(filename, (byte **) &buffer); line = buffer; while (line != NULL) { const char *error; char *next; // find end of line char *eol = strchr(line, '\n'); if (eol != NULL) { // change the newline into NUL *eol = '\0'; next = eol + 1; } else { // end of buffer next = NULL; } error = ParseSubstituteLine(musicdir, line); if (error != NULL) { fprintf(stderr, "%s:%i: Error: %s\n", filename, linenum, error); } ++linenum; line = next; } Z_Free(buffer); return true; } // Find substitute configs and try to load them. static void LoadSubstituteConfigs(void) { glob_t *glob; char *musicdir; const char *path; unsigned int old_music_len; unsigned int i; // We can configure the path to music packs using the music_pack_path // configuration variable. Otherwise we use the current directory, or // $configdir/music to look for .cfg files. if (strcmp(music_pack_path, "") != 0) { musicdir = M_StringJoin(music_pack_path, DIR_SEPARATOR_S, NULL); } else if (!strcmp(configdir, "")) { musicdir = M_StringDuplicate(""); } else { musicdir = M_StringJoin(configdir, "music", DIR_SEPARATOR_S, NULL); } // Load all music packs, by searching for .cfg files. glob = I_StartGlob(musicdir, "*.cfg", GLOB_FLAG_SORTED|GLOB_FLAG_NOCASE); for (;;) { path = I_NextGlob(glob); if (path == NULL) { break; } ReadSubstituteConfig(musicdir, path); } I_EndGlob(glob); if (subst_music_len > 0) { printf("Loaded %u music substitutions from config files.\n", subst_music_len); } old_music_len = subst_music_len; // Add entries from known filenames list. We add this after those from the // configuration files, so that the entries here can be overridden. for (i = 0; i < arrlen(known_filenames); ++i) { AddSubstituteMusic(musicdir, known_filenames[i].hash_prefix, known_filenames[i].filename); } if (subst_music_len > old_music_len) { printf("Configured %u music substitutions based on filename.\n", subst_music_len - old_music_len); } free(musicdir); } // Returns true if the given lump number is a music lump that should // be included in substitute configs. // Identifying music lumps by name is not feasible; some games (eg. // Heretic, Hexen) don't have a common naming pattern for music lumps. static boolean IsMusicLump(int lumpnum) { byte *data; boolean result; if (W_LumpLength(lumpnum) < 4) { return false; } data = W_CacheLumpNum(lumpnum, PU_STATIC); result = memcmp(data, MUS_HEADER_MAGIC, 4) == 0 || memcmp(data, MID_HEADER_MAGIC, 4) == 0; W_ReleaseLumpNum(lumpnum); return result; } // Dump an example config file containing checksums for all MIDI music // found in the WAD directory. static void DumpSubstituteConfig(char *filename) { sha1_context_t context; sha1_digest_t digest; char name[9]; byte *data; FILE *fs; unsigned int lumpnum; size_t h; fs = fopen(filename, "w"); if (fs == NULL) { I_Error("Failed to open %s for writing", filename); return; } fprintf(fs, "# Example %s substitute MIDI file.\n\n", PACKAGE_NAME); fprintf(fs, "# SHA1 hash = filename\n"); for (lumpnum = 0; lumpnum < numlumps; ++lumpnum) { strncpy(name, lumpinfo[lumpnum]->name, 8); name[8] = '\0'; if (!IsMusicLump(lumpnum)) { continue; } // Calculate hash. data = W_CacheLumpNum(lumpnum, PU_STATIC); SHA1_Init(&context); SHA1_Update(&context, data, W_LumpLength(lumpnum)); SHA1_Final(digest, &context); W_ReleaseLumpNum(lumpnum); // Print line. for (h = 0; h < sizeof(sha1_digest_t); ++h) { fprintf(fs, "%02x", digest[h]); } fprintf(fs, " = %s.ogg\n", name); } fprintf(fs, "\n"); fclose(fs); printf("Substitute MIDI config file written to %s.\n", filename); I_Quit(); } // Shutdown music static void I_MP_ShutdownMusic(void) { if (music_initialized) { Mix_HaltMusic(); music_initialized = false; if (sdl_was_initialized) { Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); sdl_was_initialized = false; } } } static boolean SDLIsInitialized(void) { int freq, channels; Uint16 format; return Mix_QuerySpec(&freq, &format, &channels) != 0; } // Callback function that is invoked to track current track position. void TrackPositionCallback(int chan, void *stream, int len, void *udata) { // Position is doubled up twice: for 16-bit samples and for stereo. current_track_pos += len / 4; } // Initialize music subsystem static boolean I_MP_InitMusic(void) { int i; //! // @category obscure // @arg // // Read all MIDI files from loaded WAD files, dump an example substitution // music config file to the specified filename and quit. // i = M_CheckParmWithArgs("-dumpsubstconfig", 1); if (i > 0) { DumpSubstituteConfig(myargv[i + 1]); } // If we're in GENMIDI mode, try to load sound packs. LoadSubstituteConfigs(); // We can't initialize if we don't have any substitute files to work with. // If so, don't bother with SDL initialization etc. if (subst_music_len == 0) { return false; } // If SDL_mixer is not initialized, we have to initialize it // and have the responsibility to shut it down later on. if (SDLIsInitialized()) { music_initialized = true; } else if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); } else if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0) { fprintf(stderr, "Error initializing SDL_mixer: %s\n", Mix_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); } else { SDL_PauseAudio(0); sdl_was_initialized = true; music_initialized = true; } // Initialize SDL_Mixer for digital music playback Mix_Init(MIX_INIT_FLAC | MIX_INIT_OGG | MIX_INIT_MP3); // Register an effect function to track the music position. Mix_RegisterEffect(MIX_CHANNEL_POST, TrackPositionCallback, NULL, NULL); return music_initialized; } // Set music volume (0 - 127) static void I_MP_SetMusicVolume(int volume) { Mix_VolumeMusic((volume * MIX_MAX_VOLUME) / 127); } // Start playing a mid static void I_MP_PlaySong(void *handle, boolean looping) { int loops; if (!music_initialized) { return; } if (handle == NULL) { return; } current_track_music = (Mix_Music *) handle; current_track_loop = looping; if (looping) { loops = -1; } else { loops = 1; } // Don't loop when playing substitute music, as we do it // ourselves instead. if (file_metadata.valid) { loops = 1; SDL_LockAudio(); current_track_pos = 0; // start of track SDL_UnlockAudio(); } if (Mix_PlayMusic(current_track_music, loops) == -1) { fprintf(stderr, "I_MP_PlaySong: Error starting track: %s\n", Mix_GetError()); } } static void I_MP_PauseSong(void) { if (!music_initialized) { return; } Mix_PauseMusic(); } static void I_MP_ResumeSong(void) { if (!music_initialized) { return; } Mix_ResumeMusic(); } static void I_MP_StopSong(void) { if (!music_initialized) { return; } Mix_HaltMusic(); current_track_music = NULL; } static void I_MP_UnRegisterSong(void *handle) { Mix_Music *music = (Mix_Music *) handle; if (!music_initialized) { return; } if (handle == NULL) { return; } Mix_FreeMusic(music); } static void *I_MP_RegisterSong(void *data, int len) { const char *filename; Mix_Music *music; if (!music_initialized) { return NULL; } // See if we're substituting this MUS for a high-quality replacement. filename = GetSubstituteMusicFile(data, len); if (filename == NULL) { return NULL; } music = Mix_LoadMUS(filename); if (music == NULL) { // Fall through and play MIDI normally, but print an error // message. fprintf(stderr, "Failed to load substitute music file: %s: %s\n", filename, Mix_GetError()); return NULL; } // Read loop point metadata from the file so that we know where // to loop the music. ReadLoopPoints(filename, &file_metadata); return music; } // Is the song playing? static boolean I_MP_MusicIsPlaying(void) { if (!music_initialized) { return false; } return Mix_PlayingMusic(); } // Get position in substitute music track, in seconds since start of track. static double GetMusicPosition(void) { unsigned int music_pos; int freq; Mix_QuerySpec(&freq, NULL, NULL); SDL_LockAudio(); music_pos = current_track_pos; SDL_UnlockAudio(); return (double) music_pos / freq; } static void RestartCurrentTrack(void) { double start = (double) file_metadata.start_time / file_metadata.samplerate_hz; // If the track finished we need to restart it. if (current_track_music != NULL) { Mix_PlayMusic(current_track_music, 1); } Mix_SetMusicPosition(start); SDL_LockAudio(); current_track_pos = file_metadata.start_time; SDL_UnlockAudio(); } // Poll music position; if we have passed the loop point end position // then we need to go back. static void I_MP_PollMusic(void) { // When playing substitute tracks, loop tags only apply if we're playing // a looping track. Tracks like the title screen music have the loop // tags ignored. if (current_track_loop && file_metadata.valid) { double end = (double) file_metadata.end_time / file_metadata.samplerate_hz; // If we have reached the loop end point then we have to take action. if (file_metadata.end_time >= 0 && GetMusicPosition() >= end) { RestartCurrentTrack(); } // Have we reached the actual end of track (not loop end)? if (!Mix_PlayingMusic()) { RestartCurrentTrack(); } } } music_module_t music_pack_module = { NULL, 0, I_MP_InitMusic, I_MP_ShutdownMusic, I_MP_SetMusicVolume, I_MP_PauseSong, I_MP_ResumeSong, I_MP_RegisterSong, I_MP_UnRegisterSong, I_MP_PlaySong, I_MP_StopSong, I_MP_MusicIsPlaying, I_MP_PollMusic, }; crispy-doom-crispy-doom-5.6.4/src/i_oplmusic.c000066400000000000000000001356471360717211000213520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System interface for music. // #include #include #include #include "memio.h" #include "mus2mid.h" #include "deh_main.h" #include "i_sound.h" #include "i_swap.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "opl.h" #include "midifile.h" // #define OPL_MIDI_DEBUG #define MAXMIDLENGTH (96 * 1024) #define GENMIDI_NUM_INSTRS 128 #define GENMIDI_NUM_PERCUSSION 47 #define GENMIDI_HEADER "#OPL_II#" #define GENMIDI_FLAG_FIXED 0x0001 /* fixed pitch */ #define GENMIDI_FLAG_2VOICE 0x0004 /* double voice (OPL3) */ #define PERCUSSION_LOG_LEN 16 typedef PACKED_STRUCT ( { byte tremolo; byte attack; byte sustain; byte waveform; byte scale; byte level; }) genmidi_op_t; typedef PACKED_STRUCT ( { genmidi_op_t modulator; byte feedback; genmidi_op_t carrier; byte unused; short base_note_offset; }) genmidi_voice_t; typedef PACKED_STRUCT ( { unsigned short flags; byte fine_tuning; byte fixed_note; genmidi_voice_t voices[2]; }) genmidi_instr_t; // Data associated with a channel of a track that is currently playing. typedef struct { // The instrument currently used for this track. genmidi_instr_t *instrument; // Volume level int volume; int volume_base; // Pan int pan; // Pitch bend value: int bend; } opl_channel_data_t; // Data associated with a track that is currently playing. typedef struct { // Track iterator used to read new events. midi_track_iter_t *iter; } 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; // Array used by voice: int array; // Currently-loaded instrument data 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 car_volume; unsigned int mod_volume; // Pan. unsigned int reg_pan; // Priority. unsigned int priority; }; // 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 opl_driver_ver_t opl_drv_ver = opl_doom_1_9; static boolean music_initialized = false; //static boolean musicpaused = false; static int start_music_volume; static int current_music_volume; // GENMIDI lump instrument data: static genmidi_instr_t *main_instrs; static genmidi_instr_t *percussion_instrs; static char (*main_instr_names)[32]; static char (*percussion_names)[32]; // Voices: static opl_voice_t voices[OPL_NUM_VOICES * 2]; static opl_voice_t *voice_free_list[OPL_NUM_VOICES * 2]; static opl_voice_t *voice_alloced_list[OPL_NUM_VOICES * 2]; static int voice_free_num; static int voice_alloced_num; static int opl_opl3mode; static int num_opl_voices; // Data for each channel. static opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK]; // Track data for playing tracks: static opl_track_data_t *tracks; static unsigned int num_tracks = 0; static unsigned int running_tracks = 0; static boolean song_looping; // Tempo control variables static unsigned int ticks_per_beat; static unsigned int us_per_beat; // Mini-log of recently played percussion instruments: static uint8_t last_perc[PERCUSSION_LOG_LEN]; static unsigned int last_perc_count; // Configuration file variable, containing the port number for the // adlib chip. char *snd_dmxoption = "-opl3"; // [crispy] default to OPL3 emulation int opl_io_port = 0x388; // If true, OPL sound channels are reversed to their correct arrangement // (as intended by the MIDI standard) rather than the backwards one // used by DMX due to a bug. static boolean opl_stereo_correct = false; // Load instrument table from GENMIDI lump: static boolean LoadInstrumentTable(void) { byte *lump; lump = W_CacheLumpName(DEH_String("genmidi"), PU_STATIC); // DMX does not check header main_instrs = (genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER)); percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS; main_instr_names = (char (*)[32]) (percussion_instrs + GENMIDI_NUM_PERCUSSION); percussion_names = main_instr_names + GENMIDI_NUM_INSTRS; return true; } // Get the next available voice from the freelist. static opl_voice_t *GetFreeVoice(void) { opl_voice_t *result; int i; // None available? if (voice_free_num == 0) { return NULL; } // Remove from free list result = voice_free_list[0]; voice_free_num--; for (i = 0; i < voice_free_num; i++) { voice_free_list[i] = voice_free_list[i + 1]; } // Add to allocated list voice_alloced_list[voice_alloced_num++] = result; return result; } // Release a voice back to the freelist. static void VoiceKeyOff(opl_voice_t *voice); static void ReleaseVoice(int index) { opl_voice_t *voice; boolean double_voice; int i; // Doom 2 1.666 OPL crash emulation. if (index >= voice_alloced_num) { voice_alloced_num = 0; voice_free_num = 0; return; } voice = voice_alloced_list[index]; VoiceKeyOff(voice); voice->channel = NULL; voice->note = 0; double_voice = voice->current_instr_voice != 0; // Remove from alloced list. voice_alloced_num--; for (i = index; i < voice_alloced_num; i++) { voice_alloced_list[i] = voice_alloced_list[i + 1]; } // Search to the end of the freelist (This is how Doom behaves!) voice_free_list[voice_free_num++] = voice; if (double_voice && opl_drv_ver < opl_doom_1_9) { ReleaseVoice(index); } } // Load data to the specified operator static void LoadOperatorData(int operator, genmidi_op_t *data, boolean max_level, unsigned int *volume) { 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; if (max_level) { level |= 0x3f; } else { level |= data->level; } *volume = level; 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, genmidi_instr_t *instr, unsigned int instr_voice) { 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 | voice->array, &data->carrier, true, &voice->car_volume); LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating, &voice->mod_volume); // 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) | voice->array, data->feedback | voice->reg_pan); // Calculate voice priority. voice->priority = 0x0f - (data->carrier.attack >> 4) + 0x0f - (data->carrier.sustain & 0x0f); } static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) { genmidi_voice_t *opl_voice; unsigned int midi_volume; unsigned int full_volume; unsigned int car_volume; unsigned int mod_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. midi_volume = 2 * (volume_mapping_table[voice->channel->volume] + 1); full_volume = (volume_mapping_table[voice->note_volume] * midi_volume) >> 9; // The volume value to use in the register: car_volume = 0x3f - full_volume; // Update the volume register(s) if necessary. if (car_volume != (voice->car_volume & 0x3f)) { voice->car_volume = car_volume | (voice->car_volume & 0xc0); OPL_WriteRegister((OPL_REGS_LEVEL + voice->op2) | voice->array, voice->car_volume); // If we are using non-modulated feedback mode, we must set the // volume for both voices. if ((opl_voice->feedback & 0x01) != 0 && opl_voice->modulator.level != 0x3f) { mod_volume = opl_voice->modulator.level; if (mod_volume < car_volume) { mod_volume = car_volume; } mod_volume |= voice->mod_volume & 0xc0; if(mod_volume != voice->mod_volume) { voice->mod_volume = mod_volume; OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array, mod_volume | (opl_voice->modulator.scale & 0xc0)); } } } } static void SetVoicePan(opl_voice_t *voice, unsigned int pan) { genmidi_voice_t *opl_voice; voice->reg_pan = pan; opl_voice = &voice->current_instr->voices[voice->current_instr_voice];; OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array, opl_voice->feedback | pan); } // Initialize the voice table and freelist static void InitVoices(void) { int i; // Start with an empty free list. voice_free_num = num_opl_voices; voice_alloced_num = 0; // Initialize each voice. for (i = 0; i < num_opl_voices; ++i) { voices[i].index = i % OPL_NUM_VOICES; voices[i].op1 = voice_operators[0][i % OPL_NUM_VOICES]; voices[i].op2 = voice_operators[1][i % OPL_NUM_VOICES]; voices[i].array = (i / OPL_NUM_VOICES) << 8; voices[i].current_instr = NULL; // Add this voice to the freelist. voice_free_list[i] = &voices[i]; } } static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume, boolean clip_start); // Set music volume (0 - 127) static void I_OPL_SetMusicVolume(int volume) { unsigned int i; if (current_music_volume == volume) { return; } // Internal state variable. current_music_volume = volume; // Update the volume of all voices. for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) { if (i == 15) { SetChannelVolume(&channels[i], volume, false); } else { SetChannelVolume(&channels[i], channels[i].volume_base, false); } } } static void VoiceKeyOff(opl_voice_t *voice) { OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, voice->freq >> 8); } static opl_channel_data_t *TrackChannelForEvent(opl_track_data_t *track, midi_event_t *event) { unsigned int channel_num = event->data.channel.channel; // MIDI uses track #9 for percussion, but for MUS it's track #15 // instead. Because DMX works on MUS data internally, we need to // swap back to the MUS version of the channel number. if (channel_num == 9) { channel_num = 15; } else if (channel_num == 15) { channel_num = 9; } return &channels[channel_num]; } // 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; int i; unsigned int key; /* printf("note off: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); */ channel = TrackChannelForEvent(track, event); 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; i < voice_alloced_num; i++) { if (voice_alloced_list[i]->channel == channel && voice_alloced_list[i]->key == key) { // Finished with this voice now. ReleaseVoice(i); i--; } } } // When all voices are in use, we must discard an existing voice to // play a new note. Find and free an existing voice. The channel // passed to the function is the channel for the new note to be // played. static void ReplaceExistingVoice(void) { int i; int result; // Check the allocated voices, if we find an instrument that is // of a lower priority to the new instrument, discard it. // If a voice is being used to play the second voice of an instrument, // use that, as second voices are non-essential. // Lower numbered MIDI channels implicitly have a higher priority // than higher-numbered channels, eg. MIDI channel 1 is never // discarded for MIDI channel 2. result = 0; for (i = 0; i < voice_alloced_num; i++) { if (voice_alloced_list[i]->current_instr_voice != 0 || voice_alloced_list[i]->channel >= voice_alloced_list[result]->channel) { result = i; } } ReleaseVoice(result); } // Alternate versions of ReplaceExistingVoice() used when emulating old // versions of the DMX library used in Doom 1.666, Heretic and Hexen. static void ReplaceExistingVoiceDoom1(void) { int i; int result; result = 0; for (i = 0; i < voice_alloced_num; i++) { if (voice_alloced_list[i]->channel > voice_alloced_list[result]->channel) { result = i; } } ReleaseVoice(result); } static void ReplaceExistingVoiceDoom2(opl_channel_data_t *channel) { int i; int result; int priority; result = 0; priority = 0x8000; for (i = 0; i < voice_alloced_num - 3; i++) { if (voice_alloced_list[i]->priority < priority && voice_alloced_list[i]->channel >= channel) { priority = voice_alloced_list[i]->priority; result = i; } } ReleaseVoice(result); } static unsigned int FrequencyForVoice(opl_voice_t *voice) { genmidi_voice_t *gm_voice; signed int freq_index; unsigned int octave; unsigned int sub_index; signed 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 ((SHORT(voice->current_instr->flags) & GENMIDI_FLAG_FIXED) == 0) { note += (signed short) SHORT(gm_voice->base_note_offset); } // Avoid possible overflow due to base note offset: while (note < 0) { note += 12; } while (note > 95) { note -= 12; } 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; } if (freq_index < 0) { freq_index = 0; } // 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) { octave = 7; } // 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) | voice->array, freq & 0xff); OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, (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, genmidi_instr_t *instrument, unsigned int instrument_voice, unsigned int note, unsigned int key, unsigned int volume) { opl_voice_t *voice; if (!opl_opl3mode && opl_drv_ver == opl_doom1_1_666) { instrument_voice = 0; } // Find a voice to use for this new note. voice = GetFreeVoice(); if (voice == NULL) { 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 ((SHORT(instrument->flags) & GENMIDI_FLAG_FIXED) != 0) { voice->note = instrument->fixed_note; } else { voice->note = note; } voice->reg_pan = channel->pan; // 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) { genmidi_instr_t *instrument; opl_channel_data_t *channel; unsigned int note, key, volume, voicenum; boolean double_voice; /* printf("note on: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); */ note = event->data.channel.param1; key = event->data.channel.param1; volume = event->data.channel.param2; // A volume of zero means key off. Some MIDI tracks, eg. the ones // in AV.wad, use a second key on with a volume of zero to mean // key off. if (volume <= 0) { KeyOffEvent(track, event); return; } // The channel. channel = TrackChannelForEvent(track, event); // Percussion channel is treated differently. if (event->data.channel.channel == 9) { if (key < 35 || key > 81) { return; } instrument = &percussion_instrs[key - 35]; last_perc[last_perc_count] = key; last_perc_count = (last_perc_count + 1) % PERCUSSION_LOG_LEN; note = 60; } else { instrument = channel->instrument; } double_voice = (SHORT(instrument->flags) & GENMIDI_FLAG_2VOICE) != 0; switch (opl_drv_ver) { case opl_doom1_1_666: voicenum = double_voice + 1; if (!opl_opl3mode) { voicenum = 1; } while (voice_alloced_num > num_opl_voices - voicenum) { ReplaceExistingVoiceDoom1(); } // Find and program a voice for this instrument. If this // is a double voice instrument, we must do this twice. if (double_voice) { VoiceKeyOn(channel, instrument, 1, note, key, volume); } VoiceKeyOn(channel, instrument, 0, note, key, volume); break; case opl_doom2_1_666: if (voice_alloced_num == num_opl_voices) { ReplaceExistingVoiceDoom2(channel); } if (voice_alloced_num == num_opl_voices - 1 && double_voice) { ReplaceExistingVoiceDoom2(channel); } // Find and program a voice for this instrument. If this // is a double voice instrument, we must do this twice. if (double_voice) { VoiceKeyOn(channel, instrument, 1, note, key, volume); } VoiceKeyOn(channel, instrument, 0, note, key, volume); break; default: case opl_doom_1_9: if (voice_free_num == 0) { ReplaceExistingVoice(); } // Find and program a voice for this instrument. If this // is a double voice instrument, we must do this twice. VoiceKeyOn(channel, instrument, 0, note, key, volume); if (double_voice) { VoiceKeyOn(channel, instrument, 1, note, key, volume); } break; } } static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) { opl_channel_data_t *channel; int instrument; // Set the instrument used on this channel. channel = TrackChannelForEvent(track, event); instrument = event->data.channel.param1; 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, boolean clip_start) { unsigned int i; channel->volume_base = volume; if (volume > current_music_volume) { volume = current_music_volume; } if (clip_start && volume > start_music_volume) { volume = start_music_volume; } channel->volume = volume; // Update all voices that this channel is using. for (i = 0; i < num_opl_voices; ++i) { if (voices[i].channel == channel) { SetVoiceVolume(&voices[i], voices[i].note_volume); } } } static void SetChannelPan(opl_channel_data_t *channel, unsigned int pan) { unsigned int reg_pan; unsigned int i; // The DMX library has the stereo channels backwards, maybe because // Paul Radek had a Soundblaster card with the channels reversed, or // perhaps it was just a bug in the OPL3 support that was never // finished. By default we preserve this bug, but we also provide a // secret DMXOPTION to fix it. // if (opl_stereo_correct) // [crispy] unconditionally enable correctly reversed stereo { pan = 144 - pan; } if (opl_opl3mode) { if (pan >= 96) { reg_pan = 0x10; } else if (pan <= 48) { reg_pan = 0x20; } else { reg_pan = 0x30; } if (channel->pan != reg_pan) { channel->pan = reg_pan; for (i = 0; i < num_opl_voices; i++) { if (voices[i].channel == channel) { SetVoicePan(&voices[i], reg_pan); } } } } } // Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event. static void AllNotesOff(opl_channel_data_t *channel, unsigned int param) { int i; for (i = 0; i < voice_alloced_num; i++) { if (voice_alloced_list[i]->channel == channel) { // Finished with this voice now. ReleaseVoice(i); i--; } } } static void ControllerEvent(opl_track_data_t *track, midi_event_t *event) { opl_channel_data_t *channel; unsigned int controller; unsigned int param; /* printf("change controller: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); */ channel = TrackChannelForEvent(track, event); controller = event->data.channel.param1; param = event->data.channel.param2; switch (controller) { case MIDI_CONTROLLER_MAIN_VOLUME: SetChannelVolume(channel, param, true); break; case MIDI_CONTROLLER_PAN: SetChannelPan(channel, param); break; case MIDI_CONTROLLER_ALL_NOTES_OFF: AllNotesOff(channel, param); break; default: #ifdef OPL_MIDI_DEBUG fprintf(stderr, "Unknown MIDI controller type: %u\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; int i; opl_voice_t *voice_updated_list[OPL_NUM_VOICES * 2]; unsigned int voice_updated_num = 0; opl_voice_t *voice_not_updated_list[OPL_NUM_VOICES * 2]; unsigned int voice_not_updated_num = 0; // Update the channel bend value. Only the MSB of the pitch bend // value is considered: this is what Doom does. channel = TrackChannelForEvent(track, event); channel->bend = event->data.channel.param2 - 64; // Update all voices for this channel. for (i = 0; i < voice_alloced_num; ++i) { if (voice_alloced_list[i]->channel == channel) { UpdateVoiceFrequency(voice_alloced_list[i]); voice_updated_list[voice_updated_num++] = voice_alloced_list[i]; } else { voice_not_updated_list[voice_not_updated_num++] = voice_alloced_list[i]; } } for (i = 0; i < voice_not_updated_num; i++) { voice_alloced_list[i] = voice_not_updated_list[i]; } for (i = 0; i < voice_updated_num; i++) { voice_alloced_list[i + voice_not_updated_num] = voice_updated_list[i]; } } static void MetaSetTempo(unsigned int tempo) { OPL_AdjustCallbacks((float) us_per_beat / tempo); us_per_beat = tempo; } // Process a meta event. static void MetaEvent(opl_track_data_t *track, midi_event_t *event) { byte *data = event->data.meta.data; unsigned int data_len = event->data.meta.length; switch (event->data.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; case MIDI_META_SET_TEMPO: if (data_len == 3) { MetaSetTempo((data[0] << 16) | (data[1] << 8) | data[2]); } 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 fprintf(stderr, "Unknown MIDI meta event type: %u\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 fprintf(stderr, "Unknown MIDI event type %i\n", event->event_type); #endif break; } } static void ScheduleTrack(opl_track_data_t *track); static void InitChannel(opl_channel_data_t *channel); // Restart a song from the beginning. static void RestartSong(void *unused) { unsigned int i; running_tracks = num_tracks; start_music_volume = current_music_volume; for (i = 0; i < num_tracks; ++i) { MIDI_RestartIterator(tracks[i].iter); ScheduleTrack(&tracks[i]); } for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) { InitChannel(&channels[i]); } } // Callback function invoked when another event needs to be read from // a track. static void TrackTimerCallback(void *arg) { opl_track_data_t *track = arg; midi_event_t *event; // Get the next event and process it. if (!MIDI_GetNextEvent(track->iter, &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. // Don't restart the song immediately, but wait for 5ms // before triggering a restart. Otherwise it is possible // to construct an empty MIDI file that causes the game // to lock up in an infinite loop. (5ms should be short // enough not to be noticeable by the listener). if (running_tracks <= 0 && song_looping) { OPL_SetCallback(5000, RestartSong, NULL); } return; } // Reschedule the callback for the next event in the track. ScheduleTrack(track); } static void ScheduleTrack(opl_track_data_t *track) { unsigned int nticks; uint64_t us; // Get the number of microseconds until the next event. nticks = MIDI_GetDeltaTime(track->iter); us = ((uint64_t) nticks * us_per_beat) / ticks_per_beat; // Set a timer to be invoked when the next event is // ready to play. OPL_SetCallback(us, TrackTimerCallback, track); } // Initialize a channel. static void InitChannel(opl_channel_data_t *channel) { // TODO: Work out sensible defaults? channel->instrument = &main_instrs[0]; channel->volume = current_music_volume; channel->volume_base = 100; if (channel->volume > channel->volume_base) { channel->volume = channel->volume_base; } channel->pan = 0x30; channel->bend = 0; } // Start a MIDI track playing: static void StartTrack(midi_file_t *file, unsigned int track_num) { opl_track_data_t *track; track = &tracks[track_num]; track->iter = MIDI_IterateTrack(file, track_num); // Schedule the first event. ScheduleTrack(track); } // Start playing a mid static void I_OPL_PlaySong(void *handle, boolean looping) { midi_file_t *file; unsigned int i; if (!music_initialized || handle == NULL) { return; } file = handle; // Allocate track data. tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t)); num_tracks = MIDI_NumTracks(file); running_tracks = num_tracks; song_looping = looping; ticks_per_beat = MIDI_GetFileTimeDivision(file); // Default is 120 bpm. // TODO: this is wrong us_per_beat = 500 * 1000; start_music_volume = current_music_volume; for (i = 0; i < num_tracks; ++i) { StartTrack(file, i); } for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) { InitChannel(&channels[i]); } // If the music was previously paused, it needs to be unpaused; playing // a new song implies that we turn off pause. This matches vanilla // behavior of the DMX library, and some of the higher-level code in // s_sound.c relies on this. OPL_SetPaused(0); } static void I_OPL_PauseSong(void) { unsigned int i; if (!music_initialized) { return; } // Pause OPL callbacks. OPL_SetPaused(1); // Turn off all main instrument voices (not percussion). // This is what Vanilla does. for (i = 0; i < num_opl_voices; ++i) { if (voices[i].channel != NULL && voices[i].current_instr < percussion_instrs) { VoiceKeyOff(&voices[i]); } } } static void I_OPL_ResumeSong(void) { if (!music_initialized) { return; } OPL_SetPaused(0); } static void I_OPL_StopSong(void) { unsigned int i; if (!music_initialized) { return; } OPL_Lock(); // Stop all playback. OPL_ClearCallbacks(); // Free all voices. for (i = 0; i < MIDI_CHANNELS_PER_TRACK; ++i) { AllNotesOff(&channels[i], 0); } // Free all track data. for (i = 0; i < num_tracks; ++i) { MIDI_FreeIterator(tracks[i].iter); } free(tracks); tracks = NULL; num_tracks = 0; OPL_Unlock(); } static void I_OPL_UnRegisterSong(void *handle) { if (!music_initialized) { return; } if (handle != NULL) { MIDI_FreeFile(handle); } } // Determine whether memory block is a .mid file static boolean IsMid(byte *mem, int len) { return len > 4 && !memcmp(mem, "MThd", 4); } static boolean ConvertMus(byte *musdata, int len, char *filename) { MEMFILE *instream; MEMFILE *outstream; void *outbuf; size_t outbuf_len; int result; instream = mem_fopen_read(musdata, len); outstream = mem_fopen_write(); result = mus2mid(instream, outstream); if (result == 0) { mem_get_buf(outstream, &outbuf, &outbuf_len); M_WriteFile(filename, outbuf, outbuf_len); } mem_fclose(instream); mem_fclose(outstream); return result; } static void *I_OPL_RegisterSong(void *data, int len) { midi_file_t *result; char *filename; if (!music_initialized) { return NULL; } // MUS files begin with "MUS" // Reject anything which doesnt have this signature filename = M_TempFile("doom.mid"); // [crispy] remove MID file size limit if (IsMid(data, len) /* && len < MAXMIDLENGTH */) { M_WriteFile(filename, data, len); } else { // Assume a MUS file and try to convert ConvertMus(data, len, filename); } result = MIDI_LoadFile(filename); if (result == NULL) { fprintf(stderr, "I_OPL_RegisterSong: Failed to load MID.\n"); } // remove file now remove(filename); free(filename); return result; } // Is the song playing? static boolean I_OPL_MusicIsPlaying(void) { if (!music_initialized) { return false; } return num_tracks > 0; } // Shutdown music 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_ReleaseLumpName(DEH_String("genmidi")); music_initialized = false; } } // Initialize music subsystem static boolean I_OPL_InitMusic(void) { char *dmxoption; opl_init_result_t chip_type; OPL_SetSampleRate(snd_samplerate); chip_type = OPL_Init(opl_io_port); if (chip_type == OPL_INIT_NONE) { printf("Dude. The Adlib isn't responding.\n"); return false; } // The DMXOPTION variable must be set to enable OPL3 support. // As an extension, we also allow it to be set from the config file. dmxoption = getenv("DMXOPTION"); if (dmxoption == NULL) { dmxoption = snd_dmxoption != NULL ? snd_dmxoption : ""; } if (chip_type == OPL_INIT_OPL3 && strstr(dmxoption, "-opl3") != NULL) { opl_opl3mode = 1; num_opl_voices = OPL_NUM_VOICES * 2; } else { opl_opl3mode = 0; num_opl_voices = OPL_NUM_VOICES; } // Secret, undocumented DMXOPTION that reverses the stereo channels // into their correct orientation. opl_stereo_correct = strstr(dmxoption, "-reverse") != NULL; // Initialize all registers. OPL_InitRegisters(opl_opl3mode); // Load instruments from GENMIDI lump: if (!LoadInstrumentTable()) { OPL_Shutdown(); return false; } InitVoices(); tracks = NULL; num_tracks = 0; music_initialized = true; return true; } static snddevice_t music_opl_devices[] = { SNDDEVICE_ADLIB, SNDDEVICE_SB, }; music_module_t music_opl_module = { music_opl_devices, arrlen(music_opl_devices), 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_MusicIsPlaying, NULL, // Poll }; void I_SetOPLDriverVer(opl_driver_ver_t ver) { opl_drv_ver = ver; } //---------------------------------------------------------------------- // // Development / debug message generation, to help developing GENMIDI // lumps. // //---------------------------------------------------------------------- static int NumActiveChannels(void) { int i; for (i = MIDI_CHANNELS_PER_TRACK - 1; i >= 0; --i) { if (channels[i].instrument != &main_instrs[0]) { return i + 1; } } return 0; } static int ChannelInUse(opl_channel_data_t *channel) { int i; for (i = 0; i < voice_alloced_num; i++) { if (voice_alloced_list[i]->channel == channel) { return 1; } } return 0; } void I_OPL_DevMessages(char *result, size_t result_len) { char tmp[80]; int instr_num; int lines; int i; if (num_tracks == 0) { M_snprintf(result, result_len, "No OPL track!"); return; } M_snprintf(result, result_len, "Tracks:\n"); lines = 1; for (i = 0; i < NumActiveChannels(); ++i) { if (channels[i].instrument == NULL) { continue; } instr_num = channels[i].instrument - main_instrs; M_snprintf(tmp, sizeof(tmp), "chan %i: %c i#%i (%s)\n", i, ChannelInUse(&channels[i]) ? '\'' : ' ', instr_num + 1, main_instr_names[instr_num]); M_StringConcat(result, tmp, result_len); ++lines; } M_snprintf(tmp, sizeof(tmp), "\nLast percussion:\n"); M_StringConcat(result, tmp, result_len); lines += 2; i = (last_perc_count + PERCUSSION_LOG_LEN - 1) % PERCUSSION_LOG_LEN; do { if (last_perc[i] == 0) { break; } M_snprintf(tmp, sizeof(tmp), "%cp#%i (%s)\n", i == 0 ? '\'' : ' ', last_perc[i], percussion_names[last_perc[i] - 35]); M_StringConcat(result, tmp, result_len); ++lines; i = (i + PERCUSSION_LOG_LEN - 1) % PERCUSSION_LOG_LEN; } while (lines < 25 && i != last_perc_count); } crispy-doom-crispy-doom-5.6.4/src/i_pcsound.c000066400000000000000000000155571360717211000211670ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System interface for PC speaker sound. // #include "SDL.h" #include #include "doomtype.h" #include "deh_str.h" #include "i_sound.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "pcsound.h" #define TIMER_FREQ 1193181 /* hz */ static boolean pcs_initialized = false; static SDL_mutex *sound_lock; static boolean use_sfx_prefix; static uint8_t *current_sound_lump = NULL; static uint8_t *current_sound_pos = NULL; static unsigned int current_sound_remaining = 0; static int current_sound_handle = 0; static int current_sound_lump_num = -1; static const uint16_t divisors[] = { 0, 6818, 6628, 6449, 6279, 6087, 5906, 5736, 5575, 5423, 5279, 5120, 4971, 4830, 4697, 4554, 4435, 4307, 4186, 4058, 3950, 3836, 3728, 3615, 3519, 3418, 3323, 3224, 3131, 3043, 2960, 2875, 2794, 2711, 2633, 2560, 2485, 2415, 2348, 2281, 2213, 2153, 2089, 2032, 1975, 1918, 1864, 1810, 1757, 1709, 1659, 1612, 1565, 1521, 1478, 1435, 1395, 1355, 1316, 1280, 1242, 1207, 1173, 1140, 1107, 1075, 1045, 1015, 986, 959, 931, 905, 879, 854, 829, 806, 783, 760, 739, 718, 697, 677, 658, 640, 621, 604, 586, 570, 553, 538, 522, 507, 493, 479, 465, 452, 439, 427, 415, 403, 391, 380, 369, 359, 348, 339, 329, 319, 310, 302, 293, 285, 276, 269, 261, 253, 246, 239, 232, 226, 219, 213, 207, 201, 195, 190, 184, 179, }; static void PCSCallbackFunc(int *duration, int *freq) { unsigned 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 < arrlen(divisors) && divisors[tone] != 0) { *freq = (int) (TIMER_FREQ / divisors[tone]); } else { *freq = 0; } ++current_sound_pos; --current_sound_remaining; } else { *freq = 0; } SDL_UnlockMutex(sound_lock); } static boolean CachePCSLump(sfxinfo_t *sfxinfo) { int lumplen; int headerlen; // Free the current sound lump back to the cache if (current_sound_lump != NULL) { W_ReleaseLumpNum(current_sound_lump_num); current_sound_lump = NULL; } // Load from WAD current_sound_lump = W_CacheLumpNum(sfxinfo->lumpnum, PU_STATIC); lumplen = W_LumpLength(sfxinfo->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; current_sound_lump_num = sfxinfo->lumpnum; return true; } // These Doom PC speaker sounds are not played - this can be seen in the // Heretic source code, where there are remnants of this left over // from Doom. static boolean IsDisabledSound(sfxinfo_t *sfxinfo) { int i; const char *disabled_sounds[] = { "posact", "bgact", "dmact", "dmpain", "popain", "sawidl", }; for (i=0; iname, disabled_sounds[i])) { return true; } } return false; } static int I_PCS_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch) { int result; if (!pcs_initialized) { return -1; } if (IsDisabledSound(sfxinfo)) { return -1; } if (SDL_LockMutex(sound_lock) < 0) { return -1; } result = CachePCSLump(sfxinfo); if (result) { current_sound_handle = channel; } SDL_UnlockMutex(sound_lock); if (result) { return channel; } else { return -1; } } static void I_PCS_StopSound(int handle) { if (!pcs_initialized) { 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); } // // Retrieve the raw data lump index // for a given SFX name. // static int I_PCS_GetSfxLumpNum(sfxinfo_t* sfx) { char namebuf[9]; if (use_sfx_prefix) { M_snprintf(namebuf, sizeof(namebuf), "dp%s", DEH_String(sfx->name)); } else { M_StringCopy(namebuf, DEH_String(sfx->name), sizeof(namebuf)); } // [crispy] make missing sounds non-fatal return W_CheckNumForName(namebuf); } static boolean I_PCS_SoundIsPlaying(int handle) { if (!pcs_initialized) { return false; } if (handle != current_sound_handle) { return false; } return current_sound_lump != NULL && current_sound_remaining > 0; } static boolean I_PCS_InitSound(boolean _use_sfx_prefix) { use_sfx_prefix = _use_sfx_prefix; // Use the sample rate from the configuration file PCSound_SetSampleRate(snd_samplerate); // Initialize the PC speaker subsystem. pcs_initialized = PCSound_Init(PCSCallbackFunc); if (pcs_initialized) { sound_lock = SDL_CreateMutex(); } return pcs_initialized; } static void I_PCS_ShutdownSound(void) { if (pcs_initialized) { PCSound_Shutdown(); } } static void I_PCS_UpdateSound(void) { // no-op. } void I_PCS_UpdateSoundParams(int channel, int vol, int sep) { // no-op. } static snddevice_t sound_pcsound_devices[] = { SNDDEVICE_PCSPEAKER, }; sound_module_t sound_pcsound_module = { sound_pcsound_devices, arrlen(sound_pcsound_devices), I_PCS_InitSound, I_PCS_ShutdownSound, I_PCS_GetSfxLumpNum, I_PCS_UpdateSound, I_PCS_UpdateSoundParams, I_PCS_StartSound, I_PCS_StopSound, I_PCS_SoundIsPlaying, }; crispy-doom-crispy-doom-5.6.4/src/i_sdlmusic.c000066400000000000000000000244021360717211000213240ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System interface for music. // #include #include #include #include #include "SDL.h" #include "SDL_mixer.h" #include "i_midipipe.h" #include "config.h" #include "doomtype.h" #include "memio.h" #include "mus2mid.h" #include "deh_str.h" #include "gusconf.h" #include "i_sound.h" #include "i_system.h" #include "i_swap.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "sha1.h" #include "w_wad.h" #include "z_zone.h" #define MAXMIDLENGTH (96 * 1024) static boolean music_initialized = false; // If this is true, this module initialized SDL sound and has the // responsibility to shut it down static boolean sdl_was_initialized = false; static boolean musicpaused = false; static int current_music_volume; char *timidity_cfg_path = ""; static char *temp_timidity_cfg = NULL; // If the temp_timidity_cfg config variable is set, generate a "wrapper" // config file for Timidity to point to the actual config file. This // is needed to inject a "dir" command so that the patches are read // relative to the actual config file. static boolean WriteWrapperTimidityConfig(char *write_path) { char *path; FILE *fstream; if (!strcmp(timidity_cfg_path, "")) { return false; } fstream = fopen(write_path, "w"); if (fstream == NULL) { return false; } path = M_DirName(timidity_cfg_path); fprintf(fstream, "dir %s\n", path); free(path); fprintf(fstream, "source %s\n", timidity_cfg_path); fclose(fstream); return true; } void I_InitTimidityConfig(void) { char *env_string; boolean success; temp_timidity_cfg = M_TempFile("timidity.cfg"); if (snd_musicdevice == SNDDEVICE_GUS) { success = GUS_WriteConfig(temp_timidity_cfg); } else { success = WriteWrapperTimidityConfig(temp_timidity_cfg); } // Set the TIMIDITY_CFG environment variable to point to the temporary // config file. if (success) { env_string = M_StringJoin("TIMIDITY_CFG=", temp_timidity_cfg, NULL); putenv(env_string); } else { free(temp_timidity_cfg); temp_timidity_cfg = NULL; } } // Remove the temporary config file generated by I_InitTimidityConfig(). static void RemoveTimidityConfig(void) { if (temp_timidity_cfg != NULL) { remove(temp_timidity_cfg); free(temp_timidity_cfg); } } // Shutdown music static void I_SDL_ShutdownMusic(void) { if (music_initialized) { #if defined(_WIN32) I_MidiPipe_ShutdownServer(); #endif Mix_HaltMusic(); music_initialized = false; if (sdl_was_initialized) { Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); sdl_was_initialized = false; } } } static boolean SDLIsInitialized(void) { int freq, channels; Uint16 format; return Mix_QuerySpec(&freq, &format, &channels) != 0; } // Initialize music subsystem static boolean I_SDL_InitMusic(void) { // If SDL_mixer is not initialized, we have to initialize it // and have the responsibility to shut it down later on. if (SDLIsInitialized()) { music_initialized = true; } else { if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); } else if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0) { fprintf(stderr, "Error initializing SDL_mixer: %s\n", Mix_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); } else { SDL_PauseAudio(0); sdl_was_initialized = true; music_initialized = true; } } #if defined(SDL_MIXER_VERSION_ATLEAST) #if SDL_MIXER_VERSION_ATLEAST(2,0,2) // Initialize SDL_Mixer for MIDI music playback Mix_Init(MIX_INIT_MID | MIX_INIT_FLAC | MIX_INIT_OGG | MIX_INIT_MP3); // [crispy] initialize some more audio formats #endif #endif // Once initialization is complete, the temporary Timidity config // file can be removed. RemoveTimidityConfig(); // If snd_musiccmd is set, we need to call Mix_SetMusicCMD to // configure an external music playback program. if (strlen(snd_musiccmd) > 0) { Mix_SetMusicCMD(snd_musiccmd); } #if defined(_WIN32) // [AM] Start up midiproc to handle playing MIDI music. I_MidiPipe_InitServer(); #endif return music_initialized; } // // SDL_mixer's native MIDI music playing does not pause properly. // As a workaround, set the volume to 0 when paused. // static void UpdateMusicVolume(void) { int vol; if (musicpaused) { vol = 0; } else { vol = (current_music_volume * MIX_MAX_VOLUME) / 127; } #if defined(_WIN32) I_MidiPipe_SetVolume(vol); #endif Mix_VolumeMusic(vol); } // Set music volume (0 - 127) static void I_SDL_SetMusicVolume(int volume) { // Internal state variable. current_music_volume = volume; UpdateMusicVolume(); } // Start playing a mid static void I_SDL_PlaySong(void *handle, boolean looping) { int loops; if (!music_initialized) { return; } if (handle == NULL && !midi_server_registered) { return; } if (looping) { loops = -1; } else { loops = 1; } #if defined(_WIN32) if (midi_server_registered) { I_MidiPipe_PlaySong(loops); } else #endif { Mix_PlayMusic((Mix_Music *) handle, loops); } } static void I_SDL_PauseSong(void) { if (!music_initialized) { return; } musicpaused = true; UpdateMusicVolume(); } static void I_SDL_ResumeSong(void) { if (!music_initialized) { return; } musicpaused = false; UpdateMusicVolume(); } static void I_SDL_StopSong(void) { if (!music_initialized) { return; } #if defined(_WIN32) if (midi_server_registered) { I_MidiPipe_StopSong(); } else #endif { Mix_HaltMusic(); } } static void I_SDL_UnRegisterSong(void *handle) { Mix_Music *music = (Mix_Music *) handle; if (!music_initialized) { return; } #if defined(_WIN32) if (midi_server_registered) { I_MidiPipe_UnregisterSong(); } else #endif { if (handle != NULL) { Mix_FreeMusic(music); } } } // Determine whether memory block is a .mid file // [crispy] Reverse Choco's logic from "if (MIDI)" to "if (not MUS)" /* static boolean IsMid(byte *mem, int len) { return len > 4 && !memcmp(mem, "MThd", 4); } */ static boolean ConvertMus(byte *musdata, int len, const char *filename) { MEMFILE *instream; MEMFILE *outstream; void *outbuf; size_t outbuf_len; int result; instream = mem_fopen_read(musdata, len); outstream = mem_fopen_write(); result = mus2mid(instream, outstream); if (result == 0) { mem_get_buf(outstream, &outbuf, &outbuf_len); M_WriteFile(filename, outbuf, outbuf_len); } mem_fclose(instream); mem_fclose(outstream); return result; } static void *I_SDL_RegisterSong(void *data, int len) { char *filename; Mix_Music *music; if (!music_initialized) { return NULL; } // MUS files begin with "MUS" // Reject anything which doesnt have this signature filename = M_TempFile("doom"); // [crispy] generic filename // [crispy] Reverse Choco's logic from "if (MIDI)" to "if (not MUS)" // MUS is the only format that requires conversion, // let SDL_Mixer figure out the others /* if (IsMid(data, len) && len < MAXMIDLENGTH) */ if (len < 4 || memcmp(data, "MUS\x1a", 4)) // [crispy] MUS_HEADER_MAGIC { M_WriteFile(filename, data, len); } else { // Assume a MUS file and try to convert ConvertMus(data, len, filename); } // Load the MIDI. In an ideal world we'd be using Mix_LoadMUS_RW() // by now, but Mix_SetMusicCMD() only works with Mix_LoadMUS(), so // we have to generate a temporary file. #if defined(_WIN32) // [AM] If we do not have an external music command defined, play // music with the MIDI server. if (midi_server_initialized) { music = NULL; if (!I_MidiPipe_RegisterSong(filename)) { fprintf(stderr, "Error loading midi: %s\n", "Could not communicate with midiproc."); } } else #endif { music = Mix_LoadMUS(filename); if (music == NULL) { // Failed to load fprintf(stderr, "Error loading midi: %s\n", Mix_GetError()); } // Remove the temporary MIDI file; however, when using an external // MIDI program we can't delete the file. Otherwise, the program // won't find the file to play. This means we leave a mess on // disk :( if (strlen(snd_musiccmd) == 0) { remove(filename); } } free(filename); return music; } // Is the song playing? static boolean I_SDL_MusicIsPlaying(void) { if (!music_initialized) { return false; } return Mix_PlayingMusic(); } static snddevice_t music_sdl_devices[] = { SNDDEVICE_PAS, SNDDEVICE_GUS, SNDDEVICE_WAVEBLASTER, SNDDEVICE_SOUNDCANVAS, SNDDEVICE_GENMIDI, SNDDEVICE_AWE32, }; music_module_t music_sdl_module = { music_sdl_devices, arrlen(music_sdl_devices), I_SDL_InitMusic, I_SDL_ShutdownMusic, I_SDL_SetMusicVolume, I_SDL_PauseSong, I_SDL_ResumeSong, I_SDL_RegisterSong, I_SDL_UnRegisterSong, I_SDL_PlaySong, I_SDL_StopSong, I_SDL_MusicIsPlaying, NULL, // Poll }; crispy-doom-crispy-doom-5.6.4/src/i_sdlsound.c000066400000000000000000000676721360717211000213540ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2008 David Flater // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System interface for sound. // #include "config.h" #include #include #include #include #include "SDL.h" #include "SDL_mixer.h" #ifdef HAVE_LIBSAMPLERATE #include #endif #include "deh_str.h" #include "i_sound.h" #include "i_system.h" #include "i_swap.h" #include "m_argv.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "doomtype.h" #define LOW_PASS_FILTER //#define DEBUG_DUMP_WAVS #define NUM_CHANNELS 16*2 // [crispy] support up to 32 sound channels typedef struct allocated_sound_s allocated_sound_t; struct allocated_sound_s { sfxinfo_t *sfxinfo; Mix_Chunk chunk; int use_count; int pitch; allocated_sound_t *prev, *next; }; static boolean sound_initialized = false; static allocated_sound_t *channels_playing[NUM_CHANNELS]; static int mixer_freq; static Uint16 mixer_format; static int mixer_channels; static boolean use_sfx_prefix; static boolean (*ExpandSoundData)(sfxinfo_t *sfxinfo, byte *data, int samplerate, int length) = NULL; // Doubly-linked list of allocated sounds. // When a sound is played, it is moved to the head, so that the oldest // sounds not used recently are at the tail. static allocated_sound_t *allocated_sounds_head = NULL; static allocated_sound_t *allocated_sounds_tail = NULL; static int allocated_sounds_size = 0; // [crispy] values 3 and higher might reproduce DOOM.EXE more accurately, // but 1 is closer to "use_libsamplerate = 0" which is the default in Choco // and causes only a short delay at startup int use_libsamplerate = 1; // Scale factor used when converting libsamplerate floating point numbers // to integers. Too high means the sounds can clip; too low means they // will be too quiet. This is an amount that should avoid clipping most // of the time: with all the Doom IWAD sound effects, at least. If a PWAD // is used, clipping might occur. float libsamplerate_scale = 0.65f; // Hook a sound into the linked list at the head. static void AllocatedSoundLink(allocated_sound_t *snd) { snd->prev = NULL; snd->next = allocated_sounds_head; allocated_sounds_head = snd; if (allocated_sounds_tail == NULL) { allocated_sounds_tail = snd; } else { snd->next->prev = snd; } } // Unlink a sound from the linked list. static void AllocatedSoundUnlink(allocated_sound_t *snd) { if (snd->prev == NULL) { allocated_sounds_head = snd->next; } else { snd->prev->next = snd->next; } if (snd->next == NULL) { allocated_sounds_tail = snd->prev; } else { snd->next->prev = snd->prev; } } static void FreeAllocatedSound(allocated_sound_t *snd) { // Unlink from linked list. AllocatedSoundUnlink(snd); // Keep track of the amount of allocated sound data: allocated_sounds_size -= snd->chunk.alen; free(snd); } // Search from the tail backwards along the allocated sounds list, find // and free a sound that is not in use, to free up memory. Return true // for success. static boolean FindAndFreeSound(void) { allocated_sound_t *snd; snd = allocated_sounds_tail; while (snd != NULL) { if (snd->use_count == 0) { FreeAllocatedSound(snd); return true; } snd = snd->prev; } // No available sounds to free... return false; } // Enforce SFX cache size limit. We are just about to allocate "len" // bytes on the heap for a new sound effect, so free up some space // so that we keep allocated_sounds_size < snd_cachesize static void ReserveCacheSpace(size_t len) { if (snd_cachesize <= 0) { return; } // Keep freeing sound effects that aren't currently being played, // until there is enough space for the new sound. while (allocated_sounds_size + len > snd_cachesize) { // Free a sound. If there is nothing more to free, stop. if (!FindAndFreeSound()) { break; } } } // Allocate a block for a new sound effect. static allocated_sound_t *AllocateSound(sfxinfo_t *sfxinfo, size_t len) { allocated_sound_t *snd; // Keep allocated sounds within the cache size. ReserveCacheSpace(len); // Allocate the sound structure and data. The data will immediately // follow the structure, which acts as a header. do { snd = malloc(sizeof(allocated_sound_t) + len); // Out of memory? Try to free an old sound, then loop round // and try again. if (snd == NULL && !FindAndFreeSound()) { return NULL; } } while (snd == NULL); // Skip past the chunk structure for the audio buffer snd->chunk.abuf = (byte *) (snd + 1); snd->chunk.alen = len; snd->chunk.allocated = 1; snd->chunk.volume = MIX_MAX_VOLUME; snd->pitch = NORM_PITCH; snd->sfxinfo = sfxinfo; snd->use_count = 0; // Keep track of how much memory all these cached sounds are using... allocated_sounds_size += len; AllocatedSoundLink(snd); return snd; } // Lock a sound, to indicate that it may not be freed. static void LockAllocatedSound(allocated_sound_t *snd) { // Increase use count, to stop the sound being freed. ++snd->use_count; //printf("++ %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count); // When we use a sound, re-link it into the list at the head, so // that the oldest sounds fall to the end of the list for freeing. AllocatedSoundUnlink(snd); AllocatedSoundLink(snd); } // Unlock a sound to indicate that it may now be freed. static void UnlockAllocatedSound(allocated_sound_t *snd) { if (snd->use_count <= 0) { I_Error("Sound effect released more times than it was locked..."); } --snd->use_count; //printf("-- %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count); } // Search through the list of allocated sounds and return the one that matches // the supplied sfxinfo entry and pitch level. static allocated_sound_t * GetAllocatedSoundBySfxInfoAndPitch(sfxinfo_t *sfxinfo, int pitch) { allocated_sound_t * p = allocated_sounds_head; while (p != NULL) { if (p->sfxinfo == sfxinfo && p->pitch == pitch) { return p; } p = p->next; } return NULL; } // Allocate a new sound chunk and pitch-shift an existing sound up-or-down // into it. static allocated_sound_t * PitchShift(allocated_sound_t *insnd, int pitch) { allocated_sound_t * outsnd; Sint16 *inp, *outp; Sint16 *srcbuf, *dstbuf; Uint32 srclen, dstlen; srcbuf = (Sint16 *)insnd->chunk.abuf; srclen = insnd->chunk.alen; // determine ratio pitch:NORM_PITCH and apply to srclen, then invert. // This is an approximation of vanilla behaviour based on measurements dstlen = (int)((1 + (1 - (float)pitch / NORM_PITCH)) * srclen); // ensure that the new buffer is an even length if ((dstlen % 2) == 0) { dstlen++; } outsnd = AllocateSound(insnd->sfxinfo, dstlen); if (!outsnd) { return NULL; } outsnd->pitch = pitch; dstbuf = (Sint16 *)outsnd->chunk.abuf; // loop over output buffer. find corresponding input cell, copy over for (outp = dstbuf; outp < dstbuf + dstlen/2; ++outp) { inp = srcbuf + (int)((float)(outp - dstbuf) / dstlen * srclen); *outp = *inp; } return outsnd; } // When a sound stops, check if it is still playing. If it is not, // we can mark the sound data as CACHE to be freed back for other // means. static void ReleaseSoundOnChannel(int channel) { allocated_sound_t *snd = channels_playing[channel]; Mix_HaltChannel(channel); if (snd == NULL) { return; } channels_playing[channel] = NULL; UnlockAllocatedSound(snd); // if the sound is a pitch-shift and it's not in use, immediately // free it if (snd->pitch != NORM_PITCH && snd->use_count <= 0) { FreeAllocatedSound(snd); } } #ifdef HAVE_LIBSAMPLERATE // Returns the conversion mode for libsamplerate to use. static int SRC_ConversionMode(void) { switch (use_libsamplerate) { // 0 = disabled default: case 0: return -1; // Ascending numbers give higher quality case 1: return SRC_LINEAR; case 2: return SRC_ZERO_ORDER_HOLD; case 3: return SRC_SINC_FASTEST; case 4: return SRC_SINC_MEDIUM_QUALITY; case 5: return SRC_SINC_BEST_QUALITY; } } // libsamplerate-based generic sound expansion function for any sample rate // unsigned 8 bits --> signed 16 bits // mono --> stereo // samplerate --> mixer_freq // Returns number of clipped samples. // DWF 2008-02-10 with cleanups by Simon Howard. static boolean ExpandSoundData_SRC(sfxinfo_t *sfxinfo, byte *data, int samplerate, int length) { SRC_DATA src_data; float *data_in; uint32_t i, abuf_index=0, clipped=0; // uint32_t alen; int retn; int16_t *expanded; allocated_sound_t *snd; Mix_Chunk *chunk; src_data.input_frames = length; data_in = malloc(length * sizeof(float)); src_data.data_in = data_in; src_data.src_ratio = (double)mixer_freq / samplerate; // We include some extra space here in case of rounding-up. src_data.output_frames = src_data.src_ratio * length + (mixer_freq / 4); src_data.data_out = malloc(src_data.output_frames * sizeof(float)); assert(src_data.data_in != NULL && src_data.data_out != NULL); // Convert input data to floats for (i=0; ichunk; expanded = (int16_t *) chunk->abuf; // Convert the result back into 16-bit integers. for (i=0; i INT16_MAX) { cvtval_i = INT16_MAX; ++clipped; } // Left and right channels expanded[abuf_index++] = cvtval_i; expanded[abuf_index++] = cvtval_i; } free(data_in); free(src_data.data_out); if (clipped > 0) { fprintf(stderr, "Sound '%s': clipped %u samples (%0.2f %%)\n", sfxinfo->name, clipped, 400.0 * clipped / chunk->alen); } return true; } #endif static boolean ConvertibleRatio(int freq1, int freq2) { int ratio; if (freq1 > freq2) { return ConvertibleRatio(freq2, freq1); } else if ((freq2 % freq1) != 0) { // Not in a direct ratio return false; } else { // Check the ratio is a power of 2 ratio = freq2 / freq1; while ((ratio & 1) == 0) { ratio = ratio >> 1; } return ratio == 1; } } #ifdef DEBUG_DUMP_WAVS // Debug code to dump resampled sound effects to WAV files for analysis. static void WriteWAV(char *filename, byte *data, uint32_t length, int samplerate) { FILE *wav; unsigned int i; unsigned short s; wav = fopen(filename, "wb"); // Header fwrite("RIFF", 1, 4, wav); i = LONG(36 + samplerate); fwrite(&i, 4, 1, wav); fwrite("WAVE", 1, 4, wav); // Subchunk 1 fwrite("fmt ", 1, 4, wav); i = LONG(16); fwrite(&i, 4, 1, wav); // Length s = SHORT(1); fwrite(&s, 2, 1, wav); // Format (PCM) s = SHORT(2); fwrite(&s, 2, 1, wav); // Channels (2=stereo) i = LONG(samplerate); fwrite(&i, 4, 1, wav); // Sample rate i = LONG(samplerate * 2 * 2); fwrite(&i, 4, 1, wav); // Byte rate (samplerate * stereo * 16 bit) s = SHORT(2 * 2); fwrite(&s, 2, 1, wav); // Block align (stereo * 16 bit) s = SHORT(16); fwrite(&s, 2, 1, wav); // Bits per sample (16 bit) // Data subchunk fwrite("data", 1, 4, wav); i = LONG(length); fwrite(&i, 4, 1, wav); // Data length fwrite(data, 1, length, wav); // Data fclose(wav); } #endif // Generic sound expansion function for any sample rate. // Returns number of clipped samples (always 0). static boolean ExpandSoundData_SDL(sfxinfo_t *sfxinfo, byte *data, int samplerate, int length) { SDL_AudioCVT convertor; allocated_sound_t *snd; Mix_Chunk *chunk; uint32_t expanded_length; // Calculate the length of the expanded version of the sample. expanded_length = (uint32_t) ((((uint64_t) length) * mixer_freq) / samplerate); // Double up twice: 8 -> 16 bit and mono -> stereo expanded_length *= 4; // Allocate a chunk in which to expand the sound snd = AllocateSound(sfxinfo, expanded_length); if (snd == NULL) { return false; } chunk = &snd->chunk; // If we can, use the standard / optimized SDL conversion routines. if (samplerate <= mixer_freq && ConvertibleRatio(samplerate, mixer_freq) && SDL_BuildAudioCVT(&convertor, AUDIO_U8, 1, samplerate, mixer_format, mixer_channels, mixer_freq)) { convertor.len = length; convertor.buf = malloc(convertor.len * convertor.len_mult); assert(convertor.buf != NULL); memcpy(convertor.buf, data, length); SDL_ConvertAudio(&convertor); memcpy(chunk->abuf, convertor.buf, chunk->alen); free(convertor.buf); } else { Sint16 *expanded = (Sint16 *) chunk->abuf; int expanded_length; int expand_ratio; int i; // Generic expansion if conversion does not work: // // SDL's audio conversion only works for rate conversions that are // powers of 2; if the two formats are not in a direct power of 2 // ratio, do this naive conversion instead. // number of samples in the converted sound expanded_length = ((uint64_t) length * mixer_freq) / samplerate; expand_ratio = (length << 8) / expanded_length; for (i=0; i> 8; sample = data[src] | (data[src] << 8); sample -= 32768; // expand 8->16 bits, mono->stereo expanded[i * 2] = expanded[i * 2 + 1] = sample; } #ifdef LOW_PASS_FILTER // Perform a low-pass filter on the upscaled sound to filter // out high-frequency noise from the conversion process. { float rc, dt, alpha; // 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) dt = 1.0f / mixer_freq; rc = 1.0f / (3.14f * samplerate); alpha = dt / (rc + dt); // Both channels are processed in parallel, hence [i-2]: for (i=2; ilumpnum; data = W_CacheLumpNum(lumpnum, PU_STATIC); lumplen = W_LumpLength(lumpnum); // Check the header, and ensure this is a valid sound if (lumplen < 8 || data[0] != 0x03 || data[1] != 0x00) { // Invalid sound return false; } // 16 bit sample rate field, 32 bit length field samplerate = (data[3] << 8) | data[2]; length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4]; // If the header specifies that the length of the sound is greater than // the length of the lump itself, this is an invalid sound lump // We also discard sound lumps that are less than 49 samples long, // as this is how DMX behaves - although the actual cut-off length // seems to vary slightly depending on the sample rate. This needs // further investigation to better understand the correct // behavior. if (length > lumplen - 8 || length <= 48) { return false; } // The DMX sound library seems to skip the first 16 and last 16 // bytes of the lump - reason unknown. data += 16; length -= 32; // Sample rate conversion if (!ExpandSoundData(sfxinfo, data + 8, samplerate, length)) { return false; } #ifdef DEBUG_DUMP_WAVS { char filename[16]; allocated_sound_t * snd; M_snprintf(filename, sizeof(filename), "%s.wav", DEH_String(sfxinfo->name)); snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH); WriteWAV(filename, snd->chunk.abuf, snd->chunk.alen,mixer_freq); } #endif // don't need the original lump any more W_ReleaseLumpNum(lumpnum); return true; } static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len) { // Linked sfx lumps? Get the lump number for the sound linked to. if (sfx->link != NULL) { sfx = sfx->link; } // Doom adds a DS* prefix to sound lumps; Heretic and Hexen don't // do this. if (use_sfx_prefix) { M_snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name)); } else { M_StringCopy(buf, DEH_String(sfx->name), buf_len); } } #ifdef HAVE_LIBSAMPLERATE // Preload all the sound effects - stops nasty ingame freezes static void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) { char namebuf[9]; int i; // Don't need to precache the sounds unless we are using libsamplerate. if (use_libsamplerate == 0) { return; } printf("I_SDL_PrecacheSounds: Precaching all sound effects.."); for (i=0; i= NUM_CHANNELS) { return; } left = ((254 - sep) * vol) / 127; right = ((sep) * vol) / 127; if (left < 0) left = 0; else if ( left > 255) left = 255; if (right < 0) right = 0; else if (right > 255) right = 255; Mix_SetPanning(handle, left, right); } // // 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. // static int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch) { allocated_sound_t *snd; if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS) { return -1; } // Release a sound effect if there is already one playing // on this channel ReleaseSoundOnChannel(channel); // Get the sound data if (!LockSound(sfxinfo)) { return -1; } snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, pitch); if (snd == NULL) { allocated_sound_t *newsnd; // fetch the base sound effect, un-pitch-shifted snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH); if (snd == NULL) { return -1; } if (snd_pitchshift) { newsnd = PitchShift(snd, pitch); if (newsnd) { LockAllocatedSound(newsnd); UnlockAllocatedSound(snd); snd = newsnd; } } } else { LockAllocatedSound(snd); } // play sound Mix_PlayChannel(channel, &snd->chunk, 0); channels_playing[channel] = snd; // set separation, etc. I_SDL_UpdateSoundParams(channel, vol, sep); return channel; } static void I_SDL_StopSound(int handle) { if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS) { return; } // Sound data is no longer needed; release the // sound data being used for this channel ReleaseSoundOnChannel(handle); } static boolean I_SDL_SoundIsPlaying(int handle) { if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS) { return false; } return Mix_Playing(handle); } // // Periodically called to update the sound system // static void I_SDL_UpdateSound(void) { int i; // Check all channels to see if a sound has finished for (i=0; i limit) { return (1 << n); } } // Should never happen? return 1024; } static boolean I_SDL_InitSound(boolean _use_sfx_prefix) { int i; // SDL 2.0.6 has a bug that makes it unusable. if (SDL_COMPILEDVERSION == SDL_VERSIONNUM(2, 0, 6)) { I_Error( "I_SDL_InitSound: " "You are trying to launch with SDL 2.0.6 which has a known bug " "that makes the game crash. Please either downgrade to " "SDL 2.0.5 or upgrade to 2.0.7. See the following bug for some " "additional context:\n" ""); } use_sfx_prefix = _use_sfx_prefix; // No sounds yet for (i=0; i #include #include "SDL_mixer.h" #include "config.h" #include "doomtype.h" #include "gusconf.h" #include "i_sound.h" #include "i_video.h" #include "m_argv.h" #include "m_config.h" // Sound sample rate to use for digital output (Hz) int snd_samplerate = 44100; // Maximum number of bytes to dedicate to allocated sound effects. // (Default: 64MB) int snd_cachesize = 64 * 1024 * 1024; // Config variable that controls the sound buffer size. // We default to 28ms (1000 / 35fps = 1 buffer per tic). int snd_maxslicetime_ms = 28; // External command to invoke to play back music. char *snd_musiccmd = ""; // Whether to vary the pitch of sound effects // Each game will set the default differently int snd_pitchshift = -1; int snd_musicdevice = SNDDEVICE_SB; int snd_sfxdevice = SNDDEVICE_SB; // Low-level sound and music modules we are using static sound_module_t *sound_module; static music_module_t *music_module; // If true, the music pack module was successfully initialized. static boolean music_packs_active = false; // This is either equal to music_module or &music_pack_module, // depending on whether the current track is substituted. static music_module_t *active_music_module; // Sound modules extern void I_InitTimidityConfig(void); extern sound_module_t sound_sdl_module; extern sound_module_t sound_pcsound_module; extern music_module_t music_sdl_module; extern music_module_t music_opl_module; extern music_module_t music_pack_module; // For OPL module: extern opl_driver_ver_t opl_drv_ver; extern int opl_io_port; // For native music module: extern char *music_pack_path; extern char *timidity_cfg_path; // DOS-specific options: These are unused but should be maintained // so that the config file can be shared between chocolate // doom and doom.exe static int snd_sbport = 0; static int snd_sbirq = 0; static int snd_sbdma = 0; static int snd_mport = 0; // Compiled-in sound modules: static sound_module_t *sound_modules[] = { &sound_sdl_module, &sound_pcsound_module, NULL, }; // Compiled-in music modules: static music_module_t *music_modules[] = { &music_sdl_module, &music_opl_module, NULL, }; // Check if a sound device is in the given list of devices static boolean SndDeviceInList(snddevice_t device, snddevice_t *list, int len) { int i; for (i=0; isound_devices, sound_modules[i]->num_sound_devices)) { // Initialize the module if (sound_modules[i]->Init(use_sfx_prefix)) { sound_module = sound_modules[i]; return; } } } } // Initialize music according to snd_musicdevice. static void InitMusicModule(void) { int i; music_module = NULL; for (i=0; music_modules[i] != NULL; ++i) { // Is the music device in the list of devices supported // by this module? if (SndDeviceInList(snd_musicdevice, music_modules[i]->sound_devices, music_modules[i]->num_sound_devices)) { // Initialize the module if (music_modules[i]->Init()) { music_module = music_modules[i]; return; } } } } // // Initializes sound stuff, including volume // Sets channels, SFX and music volume, // allocates channel buffer, sets S_sfx lookup. // void I_InitSound(boolean use_sfx_prefix) { boolean nosound, nosfx, nomusic, nomusicpacks; //! // @vanilla // // Disable all sound output. // nosound = M_CheckParm("-nosound") > 0; //! // @vanilla // // Disable sound effects. // nosfx = M_CheckParm("-nosfx") > 0; //! // @vanilla // // Disable music. // nomusic = M_CheckParm("-nomusic") > 0; //! // // Disable substitution music packs. // nomusicpacks = M_ParmExists("-nomusicpacks"); // Auto configure the music pack directory. M_SetMusicPackDir(); // Initialize the sound and music subsystems. if (!nosound && !screensaver_mode) { // This is kind of a hack. If native MIDI is enabled, set up // the TIMIDITY_CFG environment variable here before SDL_mixer // is opened. if (!nomusic && (snd_musicdevice == SNDDEVICE_GENMIDI || snd_musicdevice == SNDDEVICE_GUS)) { I_InitTimidityConfig(); } if (!nosfx) { InitSfxModule(use_sfx_prefix); } if (!nomusic) { InitMusicModule(); active_music_module = music_module; } // We may also have substitute MIDIs we can load. if (!nomusicpacks && music_module != NULL) { music_packs_active = music_pack_module.Init(); } } // [crispy] print the SDL audio backend { const char *driver_name = SDL_GetCurrentAudioDriver(); fprintf(stderr, "I_InitSound: SDL audio driver is %s\n", driver_name ? driver_name : "none"); } } void I_ShutdownSound(void) { if (sound_module != NULL) { sound_module->Shutdown(); } if (music_packs_active) { music_pack_module.Shutdown(); } if (music_module != NULL) { music_module->Shutdown(); } } int I_GetSfxLumpNum(sfxinfo_t *sfxinfo) { if (sound_module != NULL) { return sound_module->GetSfxLumpNum(sfxinfo); } else { return 0; } } void I_UpdateSound(void) { if (sound_module != NULL) { sound_module->Update(); } if (active_music_module != NULL && active_music_module->Poll != NULL) { active_music_module->Poll(); } } static void CheckVolumeSeparation(int *vol, int *sep) { if (*sep < 0) { *sep = 0; } else if (*sep > 254) { *sep = 254; } if (*vol < 0) { *vol = 0; } else if (*vol > 127) { *vol = 127; } } void I_UpdateSoundParams(int channel, int vol, int sep) { if (sound_module != NULL) { CheckVolumeSeparation(&vol, &sep); sound_module->UpdateSoundParams(channel, vol, sep); } } int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch) { if (sound_module != NULL) { CheckVolumeSeparation(&vol, &sep); return sound_module->StartSound(sfxinfo, channel, vol, sep, pitch); } else { return 0; } } void I_StopSound(int channel) { if (sound_module != NULL) { sound_module->StopSound(channel); } } boolean I_SoundIsPlaying(int channel) { if (sound_module != NULL) { return sound_module->SoundIsPlaying(channel); } else { return false; } } void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) { if (sound_module != NULL && sound_module->CacheSounds != NULL) { sound_module->CacheSounds(sounds, num_sounds); } } void I_InitMusic(void) { } void I_ShutdownMusic(void) { } void I_SetMusicVolume(int volume) { if (active_music_module != NULL) { active_music_module->SetMusicVolume(volume); } } void I_PauseSong(void) { if (active_music_module != NULL) { active_music_module->PauseMusic(); } } void I_ResumeSong(void) { if (active_music_module != NULL) { active_music_module->ResumeMusic(); } } void *I_RegisterSong(void *data, int len) { // If the music pack module is active, check to see if there is a // valid substitution for this track. If there is, we set the // active_music_module pointer to the music pack module for the // duration of this particular track. if (music_packs_active) { void *handle; handle = music_pack_module.RegisterSong(data, len); if (handle != NULL) { active_music_module = &music_pack_module; return handle; } } // No substitution for this track, so use the main module. active_music_module = music_module; if (active_music_module != NULL) { return active_music_module->RegisterSong(data, len); } else { return NULL; } } void I_UnRegisterSong(void *handle) { if (active_music_module != NULL) { active_music_module->UnRegisterSong(handle); } } void I_PlaySong(void *handle, boolean looping) { if (active_music_module != NULL) { active_music_module->PlaySong(handle, looping); } } void I_StopSong(void) { if (active_music_module != NULL) { active_music_module->StopSong(); } } boolean I_MusicIsPlaying(void) { if (active_music_module != NULL) { return active_music_module->MusicIsPlaying(); } else { return false; } } void I_BindSoundVariables(void) { extern char *snd_dmxoption; extern int use_libsamplerate; extern float libsamplerate_scale; M_BindIntVariable("snd_musicdevice", &snd_musicdevice); M_BindIntVariable("snd_sfxdevice", &snd_sfxdevice); M_BindIntVariable("snd_sbport", &snd_sbport); M_BindIntVariable("snd_sbirq", &snd_sbirq); M_BindIntVariable("snd_sbdma", &snd_sbdma); M_BindIntVariable("snd_mport", &snd_mport); M_BindIntVariable("snd_maxslicetime_ms", &snd_maxslicetime_ms); M_BindStringVariable("snd_musiccmd", &snd_musiccmd); M_BindStringVariable("snd_dmxoption", &snd_dmxoption); M_BindIntVariable("snd_samplerate", &snd_samplerate); M_BindIntVariable("snd_cachesize", &snd_cachesize); M_BindIntVariable("opl_io_port", &opl_io_port); M_BindIntVariable("snd_pitchshift", &snd_pitchshift); M_BindStringVariable("music_pack_path", &music_pack_path); M_BindStringVariable("timidity_cfg_path", &timidity_cfg_path); M_BindStringVariable("gus_patch_path", &gus_patch_path); M_BindIntVariable("gus_ram_kb", &gus_ram_kb); M_BindIntVariable("use_libsamplerate", &use_libsamplerate); M_BindFloatVariable("libsamplerate_scale", &libsamplerate_scale); } crispy-doom-crispy-doom-5.6.4/src/i_sound.h000066400000000000000000000131161360717211000206360ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The not so system specific sound interface. // #ifndef __I_SOUND__ #define __I_SOUND__ #include "doomtype.h" // so that the individual game logic and sound driver code agree #define NORM_PITCH 127 // // SoundFX struct. // typedef struct sfxinfo_struct sfxinfo_t; struct sfxinfo_struct { // tag name, used for hexen. const char *tagname; // lump name. If we are running with use_sfx_prefix=true, a // 'DS' (or 'DP' for PC speaker sounds) is prepended to this. char name[9]; // Sfx priority int priority; // referenced sound if a link sfxinfo_t *link; // pitch if a link (Doom), whether to pitch-shift (Hexen) int pitch; // volume if a link int volume; // this is checked every second to see if sound // can be thrown out (if 0, then decrement, if -1, // then throw out, if > 0, then it is in use) int usefulness; // lump number of sfx int lumpnum; // Maximum number of channels that the sound can be played on // (Heretic) int numchannels; // data used by the low level code void *driver_data; }; // // MusicInfo struct. // typedef struct { // up to 6-character name const char *name; // lump number of music int lumpnum; // music data void *data; // music handle once registered void *handle; } musicinfo_t; typedef enum { SNDDEVICE_NONE = 0, SNDDEVICE_PCSPEAKER = 1, SNDDEVICE_ADLIB = 2, SNDDEVICE_SB = 3, SNDDEVICE_PAS = 4, SNDDEVICE_GUS = 5, SNDDEVICE_WAVEBLASTER = 6, SNDDEVICE_SOUNDCANVAS = 7, SNDDEVICE_GENMIDI = 8, SNDDEVICE_AWE32 = 9, SNDDEVICE_CD = 10, } snddevice_t; // Interface for sound modules typedef struct { // List of sound devices that this sound module is used for. snddevice_t *sound_devices; int num_sound_devices; // Initialise sound module // Returns true if successfully initialised boolean (*Init)(boolean use_sfx_prefix); // Shutdown sound module void (*Shutdown)(void); // Returns the lump index of the given sound. int (*GetSfxLumpNum)(sfxinfo_t *sfxinfo); // Called periodically to update the subsystem. void (*Update)(void); // Update the sound settings on the given channel. void (*UpdateSoundParams)(int channel, int vol, int sep); // Start a sound on a given channel. Returns the channel id // or -1 on failure. int (*StartSound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch); // Stop the sound playing on the given channel. void (*StopSound)(int channel); // Query if a sound is playing on the given channel boolean (*SoundIsPlaying)(int channel); // Called on startup to precache sound effects (if necessary) void (*CacheSounds)(sfxinfo_t *sounds, int num_sounds); } sound_module_t; void I_InitSound(boolean use_sfx_prefix); void I_ShutdownSound(void); int I_GetSfxLumpNum(sfxinfo_t *sfxinfo); void I_UpdateSound(void); void I_UpdateSoundParams(int channel, int vol, int sep); int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch); void I_StopSound(int channel); boolean I_SoundIsPlaying(int channel); void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds); // Interface for music modules typedef struct { // List of sound devices that this music module is used for. snddevice_t *sound_devices; int num_sound_devices; // Initialise the music subsystem boolean (*Init)(void); // Shutdown the music subsystem void (*Shutdown)(void); // Set music volume - range 0-127 void (*SetMusicVolume)(int volume); // Pause music void (*PauseMusic)(void); // Un-pause music void (*ResumeMusic)(void); // Register a song handle from data // Returns a handle that can be used to play the song void *(*RegisterSong)(void *data, int len); // Un-register (free) song data void (*UnRegisterSong)(void *handle); // Play the song void (*PlaySong)(void *handle, boolean looping); // Stop playing the current song. void (*StopSong)(void); // Query if music is playing. boolean (*MusicIsPlaying)(void); // Invoked periodically to poll. void (*Poll)(void); } music_module_t; void I_InitMusic(void); void I_ShutdownMusic(void); void I_SetMusicVolume(int volume); void I_PauseSong(void); void I_ResumeSong(void); void *I_RegisterSong(void *data, int len); void I_UnRegisterSong(void *handle); void I_PlaySong(void *handle, boolean looping); void I_StopSong(void); boolean I_MusicIsPlaying(void); extern int snd_sfxdevice; extern int snd_musicdevice; extern int snd_samplerate; extern int snd_cachesize; extern int snd_maxslicetime_ms; extern char *snd_musiccmd; extern int snd_pitchshift; void I_BindSoundVariables(void); // DMX version to emulate for OPL emulation: typedef enum { opl_doom1_1_666, // Doom 1 v1.666 opl_doom2_1_666, // Doom 2 v1.666, Hexen, Heretic opl_doom_1_9 // Doom v1.9, Strife } opl_driver_ver_t; void I_SetOPLDriverVer(opl_driver_ver_t ver); #endif crispy-doom-crispy-doom-5.6.4/src/i_swap.h000066400000000000000000000022731360717211000204620ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Endianess handling, swapping 16bit and 32bit. // #ifndef __I_SWAP__ #define __I_SWAP__ #include "SDL_endian.h" // Endianess handling. // WAD files are stored little endian. // Just use SDL's endianness swapping functions. // These are deliberately cast to signed values; this is the behaviour // of the macros in the original source and some code relies on it. #define SHORT(x) ((signed short) SDL_SwapLE16(x)) #define LONG(x) ((signed int) SDL_SwapLE32(x)) // Defines for checking the endianness of the system. #if SDL_BYTEORDER == SDL_BIG_ENDIAN #define SYS_BIG_ENDIAN #endif #endif crispy-doom-crispy-doom-5.6.4/src/i_system.c000066400000000000000000000235161360717211000210320ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #endif #include "SDL.h" #include "config.h" #include "deh_str.h" #include "doomtype.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "i_joystick.h" #include "i_sound.h" #include "i_timer.h" #include "i_video.h" #include "i_system.h" #include "w_wad.h" #include "z_zone.h" #define DEFAULT_RAM 16*2 /* MiB [crispy] */ #define MIN_RAM 4*4 /* MiB [crispy] */ typedef struct atexit_listentry_s atexit_listentry_t; struct atexit_listentry_s { atexit_func_t func; boolean run_on_error; atexit_listentry_t *next; }; static atexit_listentry_t *exit_funcs = NULL; void I_AtExit(atexit_func_t func, boolean run_on_error) { atexit_listentry_t *entry; entry = malloc(sizeof(*entry)); entry->func = func; entry->run_on_error = run_on_error; entry->next = exit_funcs; exit_funcs = entry; } // Tactile feedback function, probably used for the Logitech Cyberman void I_Tactile(int on, int off, int total) { } // Zone memory auto-allocation function that allocates the zone size // by trying progressively smaller zone sizes until one is found that // works. static byte *AutoAllocMemory(int *size, int default_ram, int min_ram) { byte *zonemem; // Allocate the zone memory. This loop tries progressively smaller // zone sizes until a size is found that can be allocated. // If we used the -mb command line parameter, only the parameter // provided is accepted. zonemem = NULL; while (zonemem == NULL) { // We need a reasonable minimum amount of RAM to start. if (default_ram < min_ram) { I_Error("Unable to allocate %i MiB of RAM for zone", default_ram); } // Try to allocate the zone memory. *size = default_ram * 1024 * 1024; zonemem = malloc(*size); // Failed to allocate? Reduce zone size until we reach a size // that is acceptable. if (zonemem == NULL) { default_ram -= 1; } } return zonemem; } byte *I_ZoneBase (int *size) { byte *zonemem; int min_ram, default_ram; int p; static int i = 1; //! // @category obscure // @arg // // Specify the heap size, in MiB (default 16). // p = M_CheckParmWithArgs("-mb", 1); if (p > 0) { default_ram = atoi(myargv[p+1]); min_ram = default_ram; } else { default_ram = DEFAULT_RAM; min_ram = MIN_RAM; } // [crispy] do not allocate new zones ad infinitum if (i > 8) { min_ram = default_ram + 1; } zonemem = AutoAllocMemory(size, default_ram * i, min_ram * i); // [crispy] if called again, allocate another zone twice as big i *= 2; printf("zone memory: %p, %d MiB allocated for zone\n", zonemem, *size >> 20); // [crispy] human-understandable zone heap size return zonemem; } void I_PrintBanner(const char *msg) { int i; int spaces = 35 - (strlen(msg) / 2); for (i=0; ifunc(); entry = entry->next; } SDL_Quit(); exit(0); } // // I_Error // static boolean already_quitting = false; void I_Error (const char *error, ...) { char msgbuf[512]; va_list argptr; atexit_listentry_t *entry; boolean exit_gui_popup; if (already_quitting) { fprintf(stderr, "Warning: recursive call to I_Error detected.\n"); exit(-1); } else { already_quitting = true; } // Message first. va_start(argptr, error); //fprintf(stderr, "\nError: "); vfprintf(stderr, error, argptr); fprintf(stderr, "\n\n"); va_end(argptr); fflush(stderr); // Write a copy of the message into buffer. va_start(argptr, error); memset(msgbuf, 0, sizeof(msgbuf)); M_vsnprintf(msgbuf, sizeof(msgbuf), error, argptr); va_end(argptr); // Shutdown. Here might be other errors. entry = exit_funcs; while (entry != NULL) { if (entry->run_on_error) { entry->func(); } entry = entry->next; } //! // @category obscure // // If specified, don't show a GUI window for error messages when the // game exits with an error. // exit_gui_popup = !M_ParmExists("-nogui"); // Pop up a GUI dialog box to show the error message, if the // game was not run from the console (and the user will // therefore be unable to otherwise see the message). if (exit_gui_popup && !I_ConsoleStdout()) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, PACKAGE_STRING, msgbuf, NULL); } // abort(); SDL_Quit(); exit(-1); } // // I_Realloc // void *I_Realloc(void *ptr, size_t size) { void *new_ptr; new_ptr = realloc(ptr, size); if (size != 0 && new_ptr == NULL) { I_Error ("I_Realloc: failed on reallocation of %" PRIuPTR " bytes", size); } return new_ptr; } // // Read Access Violation emulation. // // From PrBoom+, by entryway. // // C:\>debug // -d 0:0 // // DOS 6.22: // 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) // DOS 7.1: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // Win98: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // DOSBox under XP: // 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) #define DOS_MEM_DUMP_SIZE 10 static const unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = { 0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00}; static const unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = { 0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00}; static const unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = { 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00}; static unsigned char mem_dump_custom[DOS_MEM_DUMP_SIZE]; static const unsigned char *dos_mem_dump = mem_dump_dos622; boolean I_GetMemoryValue(unsigned int offset, void *value, int size) { static boolean firsttime = true; if (firsttime) { int p, i, val; firsttime = false; i = 0; //! // @category compat // @arg // // Specify DOS version to emulate for NULL pointer dereference // emulation. Supported versions are: dos622, dos71, dosbox. // The default is to emulate DOS 7.1 (Windows 98). // p = M_CheckParmWithArgs("-setmem", 1); if (p > 0) { if (!strcasecmp(myargv[p + 1], "dos622")) { dos_mem_dump = mem_dump_dos622; } if (!strcasecmp(myargv[p + 1], "dos71")) { dos_mem_dump = mem_dump_win98; } else if (!strcasecmp(myargv[p + 1], "dosbox")) { dos_mem_dump = mem_dump_dosbox; } else { for (i = 0; i < DOS_MEM_DUMP_SIZE; ++i) { ++p; if (p >= myargc || myargv[p][0] == '-') { break; } M_StrToInt(myargv[p], &val); mem_dump_custom[i++] = (unsigned char) val; } dos_mem_dump = mem_dump_custom; } } } switch (size) { case 1: *((unsigned char *) value) = dos_mem_dump[offset]; return true; case 2: *((unsigned short *) value) = dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8); return true; case 4: *((unsigned int *) value) = dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8) | (dos_mem_dump[offset + 2] << 16) | (dos_mem_dump[offset + 3] << 24); return true; } return false; } crispy-doom-crispy-doom-5.6.4/src/i_system.h000066400000000000000000000042741360717211000210370ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System specific interface stuff. // #ifndef __I_SYSTEM__ #define __I_SYSTEM__ #include "d_ticcmd.h" #include "d_event.h" typedef void (*atexit_func_t)(void); // Called by DoomMain. void I_Init (void); // Called by startup code // to get the ammount of memory to malloc // for the zone management. byte* I_ZoneBase (int *size); boolean I_ConsoleStdout(void); // Asynchronous interrupt functions should maintain private queues // that are read by the synchronous functions // to be converted into events. // Either returns a null ticcmd, // or calls a loadable driver to build it. // This ticcmd will then be modified by the gameloop // for normal input. ticcmd_t* I_BaseTiccmd (void); // Called by M_Responder when quit is selected. // Clean exit, displays sell blurb. void I_Quit (void) NORETURN; void I_Error (const char *error, ...) NORETURN PRINTF_ATTR(1, 2); void I_Tactile (int on, int off, int total); void *I_Realloc(void *ptr, size_t size); boolean I_GetMemoryValue(unsigned int offset, void *value, int size); // Schedule a function to be called when the program exits. // If run_if_error is true, the function is called if the exit // is due to an error (I_Error) void I_AtExit(atexit_func_t func, boolean run_if_error); // Add all system-specific config file variable bindings. void I_BindVariables(void); // Print startup banner copyright message. void I_PrintStartupBanner(const char *gamedescription); // Print a centered text banner displaying the given string. void I_PrintBanner(const char *text); // Print a dividing line for startup banners. void I_PrintDivider(void); #endif crispy-doom-crispy-doom-5.6.4/src/i_timer.c000066400000000000000000000027701360717211000206250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Timer functions. // #include "SDL.h" #include "i_timer.h" #include "doomtype.h" // // I_GetTime // returns time in 1/35th second tics // static Uint32 basetime = 0; int I_GetTime (void) { Uint32 ticks; ticks = SDL_GetTicks(); if (basetime == 0) basetime = ticks; ticks -= basetime; return (ticks * TICRATE) / 1000; } // // Same as I_GetTime, but returns time in milliseconds // int I_GetTimeMS(void) { Uint32 ticks; ticks = SDL_GetTicks(); if (basetime == 0) basetime = ticks; return ticks - basetime; } // Sleep for a specified number of ms void I_Sleep(int ms) { SDL_Delay(ms); } void I_WaitVBL(int count) { I_Sleep((count * 1000) / 70); } void I_InitTimer(void) { // initialize timer #if SDL_VERSION_ATLEAST(2, 0, 5) SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1"); #endif SDL_Init(SDL_INIT_TIMER); } crispy-doom-crispy-doom-5.6.4/src/i_timer.h000066400000000000000000000020161360717211000206230ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System-specific timer interface // #ifndef __I_TIMER__ #define __I_TIMER__ #define TICRATE 35 // Called by D_DoomLoop, // returns current time in tics. int I_GetTime (void); // returns current time in ms int I_GetTimeMS (void); // Pause for a specified number of ms void I_Sleep(int ms); // Initialize timer void I_InitTimer(void); // Wait for vertical retrace or pause a bit. void I_WaitVBL(int count); #endif crispy-doom-crispy-doom-5.6.4/src/i_video.c000066400000000000000000001400751360717211000206140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM graphics stuff for SDL. // #include "SDL.h" #include "SDL_opengl.h" #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #endif #include "icon.c" #include "crispy.h" #include "config.h" #include "d_loop.h" #include "deh_str.h" #include "doomtype.h" #include "i_input.h" #include "i_joystick.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "tables.h" #include "v_diskicon.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" int SCREENWIDTH, SCREENHEIGHT, SCREENHEIGHT_4_3; // These are (1) the window (or the full screen) that our game is rendered to // and (2) the renderer that scales the texture (see below) into this window. static SDL_Window *screen; static SDL_Renderer *renderer; // Window title static const char *window_title = ""; // These are (1) the 320x200x8 paletted buffer that we draw to (i.e. the one // that holds I_VideoBuffer), (2) the 320x200x32 RGBA intermediate buffer that // we blit the former buffer to, (3) the intermediate 320x200 texture that we // load the RGBA buffer to and that we render into another texture (4) which // is upscaled by an integer factor UPSCALE using "nearest" scaling and which // in turn is finally rendered to screen using "linear" scaling. #ifndef CRISPY_TRUECOLOR static SDL_Surface *screenbuffer = NULL; #endif static SDL_Surface *argbbuffer = NULL; static SDL_Texture *texture = NULL; static SDL_Texture *texture_upscaled = NULL; #ifndef CRISPY_TRUECOLOR static SDL_Rect blit_rect = { 0, 0, MAXWIDTH, MAXHEIGHT }; #endif static uint32_t pixel_format; // palette #ifdef CRISPY_TRUECOLOR static SDL_Texture *curpane = NULL; static SDL_Texture *redpane = NULL; static SDL_Texture *yelpane = NULL; static SDL_Texture *grnpane = NULL; static int pane_alpha; static unsigned int rmask, gmask, bmask, amask; // [crispy] moved up here static const uint8_t blend_alpha = 0xa8; extern pixel_t* colormaps; // [crispy] evil hack to get FPS dots working as in Vanilla #else static SDL_Color palette[256]; #endif static boolean palette_to_set; // display has been set up? static boolean initialized = false; // disable mouse? static boolean nomouse = false; int usemouse = 1; // Save screenshots in PNG format. int png_screenshots = 1; // [crispy] // SDL video driver name char *video_driver = ""; // Window position: char *window_position = "center"; // SDL display number on which to run. int video_display = 0; // Screen width and height, from configuration file. int window_width = 800; int window_height = 600; // Fullscreen mode, 0x0 for SDL_WINDOW_FULLSCREEN_DESKTOP. int fullscreen_width = 0, fullscreen_height = 0; // Maximum number of pixels to use for intermediate scale buffer. static int max_scaling_buffer_pixels = 16000000; // Run in full screen mode? (int type for config code) int fullscreen = true; // Aspect ratio correction mode int aspect_ratio_correct = true; static int actualheight; // Force integer scales for resolution-independent rendering int integer_scaling = false; // VGA Porch palette change emulation int vga_porch_flash = false; // Force software rendering, for systems which lack effective hardware // acceleration int force_software_renderer = false; // Time to wait for the screen to settle on startup before starting the // game (ms) static int startup_delay = 1000; // Grab the mouse? (int type for config code). nograbmouse_override allows // this to be temporarily disabled via the command line. static int grabmouse = true; static boolean nograbmouse_override = false; // The screen buffer; this is modified to draw things to the screen pixel_t *I_VideoBuffer = NULL; // If true, game is running as a screensaver boolean screensaver_mode = false; // Flag indicating whether the screen is currently visible: // when the screen isnt visible, don't render the screen boolean screenvisible = true; // If true, we display dots at the bottom of the screen to // indicate FPS. static boolean display_fps_dots; // If this is true, the screen is rendered but not blitted to the // video buffer. static boolean noblit; // Callback function to invoke to determine whether to grab the // mouse pointer. static grabmouse_callback_t grabmouse_callback = NULL; // Does the window currently have focus? static boolean window_focused = true; // Window resize state. static boolean need_resize = false; static unsigned int last_resize_time; #define RESIZE_DELAY 500 // Gamma correction level to use int usegamma = 0; // Joystick/gamepad hysteresis unsigned int joywait = 0; static boolean MouseShouldBeGrabbed() { // never grab the mouse when in screensaver mode if (screensaver_mode) return false; // if the window doesn't 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 (fullscreen) return true; // Don't grab the mouse if mouse input is disabled if (!usemouse || nomouse) return false; // if we specify not to grab the mouse, never grab if (nograbmouse_override || !grabmouse) return false; // Invoke the grabmouse callback function to determine whether // the mouse should be grabbed if (grabmouse_callback != NULL) { return grabmouse_callback(); } else { return true; } } void I_SetGrabMouseCallback(grabmouse_callback_t func) { grabmouse_callback = func; } // Set the variable controlling FPS dots. void I_DisplayFPSDots(boolean dots_on) { display_fps_dots = dots_on; } static void SetShowCursor(boolean show) { if (!screensaver_mode) { // When the cursor is hidden, grab the input. // Relative mode implicitly hides the cursor. SDL_SetRelativeMouseMode(!show); SDL_GetRelativeMouseState(NULL, NULL); } } void I_ShutdownGraphics(void) { if (initialized) { SetShowCursor(true); SDL_QuitSubSystem(SDL_INIT_VIDEO); initialized = false; } } // // I_StartFrame // void I_StartFrame (void) { // er? } // Adjust window_width / window_height variables to be an an aspect // ratio consistent with the aspect_ratio_correct variable. static void AdjustWindowSize(void) { if (aspect_ratio_correct || integer_scaling) { if (window_width * actualheight <= window_height * SCREENWIDTH) { // We round up window_height if the ratio is not exact; this leaves // the result stable. window_height = (window_width * actualheight + SCREENWIDTH - 1) / SCREENWIDTH; } else { window_width = window_height * SCREENWIDTH / actualheight; } } } static void HandleWindowEvent(SDL_WindowEvent *event) { int i; switch (event->event) { #if 0 // SDL2-TODO case SDL_ACTIVEEVENT: // need to update our focus state UpdateFocus(); break; #endif case SDL_WINDOWEVENT_EXPOSED: palette_to_set = true; break; case SDL_WINDOWEVENT_RESIZED: need_resize = true; last_resize_time = SDL_GetTicks(); break; // Don't render the screen when the window is minimized: case SDL_WINDOWEVENT_MINIMIZED: screenvisible = false; break; case SDL_WINDOWEVENT_MAXIMIZED: case SDL_WINDOWEVENT_RESTORED: screenvisible = true; break; // 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. case SDL_WINDOWEVENT_FOCUS_GAINED: window_focused = true; break; case SDL_WINDOWEVENT_FOCUS_LOST: window_focused = false; break; // We want to save the user's preferred monitor to use for running the // game, so that next time we're run we start on the same display. So // every time the window is moved, find which display we're now on and // update the video_display config variable. case SDL_WINDOWEVENT_MOVED: i = SDL_GetWindowDisplayIndex(screen); if (i >= 0) { video_display = i; } break; default: break; } } static boolean ToggleFullScreenKeyShortcut(SDL_Keysym *sym) { Uint16 flags = (KMOD_LALT | KMOD_RALT); #if defined(__MACOSX__) flags |= (KMOD_LGUI | KMOD_RGUI); #endif return (sym->scancode == SDL_SCANCODE_RETURN || sym->scancode == SDL_SCANCODE_KP_ENTER) && (sym->mod & flags) != 0; } static void I_ToggleFullScreen(void) { unsigned int flags = 0; // TODO: Consider implementing fullscreen toggle for SDL_WINDOW_FULLSCREEN // (mode-changing) setup. This is hard because we have to shut down and // restart again. if (fullscreen_width != 0 || fullscreen_height != 0) { return; } fullscreen = !fullscreen; if (fullscreen) { flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } SDL_SetWindowFullscreen(screen, flags); if (!fullscreen) { AdjustWindowSize(); SDL_SetWindowSize(screen, window_width, window_height); } } void I_GetEvent(void) { extern void I_HandleKeyboardEvent(SDL_Event *sdlevent); extern void I_HandleMouseEvent(SDL_Event *sdlevent); SDL_Event sdlevent; SDL_PumpEvents(); while (SDL_PollEvent(&sdlevent)) { switch (sdlevent.type) { case SDL_KEYDOWN: if (ToggleFullScreenKeyShortcut(&sdlevent.key.keysym)) { I_ToggleFullScreen(); break; } // deliberate fall-though case SDL_KEYUP: I_HandleKeyboardEvent(&sdlevent); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: case SDL_MOUSEWHEEL: if (usemouse && !nomouse && window_focused) { I_HandleMouseEvent(&sdlevent); } break; case SDL_QUIT: if (screensaver_mode) { I_Quit(); } else { event_t event; event.type = ev_quit; D_PostEvent(&event); } break; case SDL_WINDOWEVENT: if (sdlevent.window.windowID == SDL_GetWindowID(screen)) { HandleWindowEvent(&sdlevent.window); } break; default: break; } } } // // I_StartTic // void I_StartTic (void) { if (!initialized) { return; } I_GetEvent(); if (usemouse && !nomouse && window_focused) { I_ReadMouse(); } if (joywait < I_GetTime()) { I_UpdateJoystick(); } } // // I_UpdateNoBlit // void I_UpdateNoBlit (void) { // what is this? } static void UpdateGrab(void) { static boolean currently_grabbed = false; boolean grab; grab = MouseShouldBeGrabbed(); if (screensaver_mode) { // Hide the cursor in screensaver mode SetShowCursor(false); } else if (grab && !currently_grabbed) { SetShowCursor(false); } else if (!grab && currently_grabbed) { int screen_w, screen_h; SetShowCursor(true); // When releasing the mouse from grab, warp the mouse cursor to // the bottom-right of the screen. This is a minimally distracting // place for it to appear - we may only have released the grab // because we're at an end of level intermission screen, for // example. SDL_GetWindowSize(screen, &screen_w, &screen_h); SDL_WarpMouseInWindow(screen, screen_w - 16, screen_h - 16); SDL_GetRelativeMouseState(NULL, NULL); } currently_grabbed = grab; } static void LimitTextureSize(int *w_upscale, int *h_upscale) { SDL_RendererInfo rinfo; int orig_w, orig_h; orig_w = *w_upscale; orig_h = *h_upscale; // Query renderer and limit to maximum texture dimensions of hardware: if (SDL_GetRendererInfo(renderer, &rinfo) != 0) { I_Error("CreateUpscaledTexture: SDL_GetRendererInfo() call failed: %s", SDL_GetError()); } while (*w_upscale * SCREENWIDTH > rinfo.max_texture_width) { --*w_upscale; } while (*h_upscale * SCREENHEIGHT > rinfo.max_texture_height) { --*h_upscale; } if ((*w_upscale < 1 && rinfo.max_texture_width > 0) || (*h_upscale < 1 && rinfo.max_texture_height > 0)) { I_Error("CreateUpscaledTexture: Can't create a texture big enough for " "the whole screen! Maximum texture size %dx%d", rinfo.max_texture_width, rinfo.max_texture_height); } // We limit the amount of texture memory used for the intermediate buffer, // since beyond a certain point there are diminishing returns. Also, // depending on the hardware there may be performance problems with very // huge textures, so the user can use this to reduce the maximum texture // size if desired. if (max_scaling_buffer_pixels < SCREENWIDTH * SCREENHEIGHT) { I_Error("CreateUpscaledTexture: max_scaling_buffer_pixels too small " "to create a texture buffer: %d < %d", max_scaling_buffer_pixels, SCREENWIDTH * SCREENHEIGHT); } while (*w_upscale * *h_upscale * SCREENWIDTH * SCREENHEIGHT > max_scaling_buffer_pixels) { if (*w_upscale > *h_upscale) { --*w_upscale; } else { --*h_upscale; } } if (*w_upscale != orig_w || *h_upscale != orig_h) { printf("CreateUpscaledTexture: Limited texture size to %dx%d " "(max %d pixels, max texture size %dx%d)\n", *w_upscale * SCREENWIDTH, *h_upscale * SCREENHEIGHT, max_scaling_buffer_pixels, rinfo.max_texture_width, rinfo.max_texture_height); } } static void CreateUpscaledTexture(boolean force) { int w, h; int h_upscale, w_upscale; static int h_upscale_old, w_upscale_old; SDL_Texture *new_texture, *old_texture; // Get the size of the renderer output. The units this gives us will be // real world pixels, which are not necessarily equivalent to the screen's // window size (because of highdpi). if (SDL_GetRendererOutputSize(renderer, &w, &h) != 0) { I_Error("Failed to get renderer output size: %s", SDL_GetError()); } // When the screen or window dimensions do not match the aspect ratio // of the texture, the rendered area is scaled down to fit. Calculate // the actual dimensions of the rendered area. if (w * actualheight < h * SCREENWIDTH) { // Tall window. h = w * actualheight / SCREENWIDTH; } else { // Wide window. w = h * SCREENWIDTH / actualheight; } // Pick texture size the next integer multiple of the screen dimensions. // If one screen dimension matches an integer multiple of the original // resolution, there is no need to overscale in this direction. w_upscale = (w + SCREENWIDTH - 1) / SCREENWIDTH; h_upscale = (h + SCREENHEIGHT - 1) / SCREENHEIGHT; // Minimum texture dimensions of 320x200. if (w_upscale < 1) { w_upscale = 1; } if (h_upscale < 1) { h_upscale = 1; } LimitTextureSize(&w_upscale, &h_upscale); // Create a new texture only if the upscale factors have actually changed. if (h_upscale == h_upscale_old && w_upscale == w_upscale_old && !force) { return; } h_upscale_old = h_upscale; w_upscale_old = w_upscale; // Set the scaling quality for rendering the upscaled texture to "linear", // which looks much softer and smoother than "nearest" but does a better // job at downscaling from the upscaled texture to screen. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); new_texture = SDL_CreateTexture(renderer, pixel_format, SDL_TEXTUREACCESS_TARGET, w_upscale*SCREENWIDTH, h_upscale*SCREENHEIGHT); old_texture = texture_upscaled; texture_upscaled = new_texture; if (old_texture != NULL) { SDL_DestroyTexture(old_texture); } } // [AM] Fractional part of the current tic, in the half-open // range of [0.0, 1.0). Used for interpolation. fixed_t fractionaltic; // // I_FinishUpdate // void I_FinishUpdate (void) { static int lasttic; int tics; int i; if (!initialized) return; if (noblit) return; if (need_resize) { if (SDL_GetTicks() > last_resize_time + RESIZE_DELAY) { int flags; // When the window is resized (we're not in fullscreen mode), // save the new window size. flags = SDL_GetWindowFlags(screen); if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { SDL_GetWindowSize(screen, &window_width, &window_height); // Adjust the window by resizing again so that the window // is the right aspect ratio. AdjustWindowSize(); SDL_SetWindowSize(screen, window_width, window_height); } CreateUpscaledTexture(false); need_resize = false; palette_to_set = true; } else { return; } } UpdateGrab(); #if 0 // SDL2-TODO // Don't update the screen if the window isn't visible. // Not doing this breaks under Windows when we alt-tab away // while fullscreen. if (!(SDL_GetAppState() & SDL_APPACTIVE)) return; #endif // draws little dots on the bottom of the screen if (display_fps_dots) { i = I_GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; for (i=0 ; i= 1000) { crispy->fps = (fpscount * 1000) / mili; fpscount = 0; lastmili = i; } } // Draw disk icon before blit, if necessary. V_DrawDiskIcon(); #ifndef CRISPY_TRUECOLOR if (palette_to_set) { SDL_SetPaletteColors(screenbuffer->format->palette, palette, 0, 256); palette_to_set = false; if (vga_porch_flash) { // "flash" the pillars/letterboxes with palette changes, emulating // VGA "porch" behaviour (GitHub issue #832) SDL_SetRenderDrawColor(renderer, palette[0].r, palette[0].g, palette[0].b, SDL_ALPHA_OPAQUE); } } // Blit from the paletted 8-bit screen buffer to the intermediate // 32-bit RGBA buffer that we can load into the texture. SDL_LowerBlit(screenbuffer, &blit_rect, argbbuffer, &blit_rect); #endif // Update the intermediate texture with the contents of the RGBA buffer. SDL_UpdateTexture(texture, NULL, argbbuffer->pixels, argbbuffer->pitch); // Make sure the pillarboxes are kept clear each frame. SDL_RenderClear(renderer); if (crispy->smoothscaling) { // Render this intermediate texture into the upscaled texture // using "nearest" integer scaling. SDL_SetRenderTarget(renderer, texture_upscaled); SDL_RenderCopy(renderer, texture, NULL, NULL); // Finally, render this upscaled texture to screen using linear scaling. SDL_SetRenderTarget(renderer, NULL); SDL_RenderCopy(renderer, texture_upscaled, NULL, NULL); } else { SDL_SetRenderTarget(renderer, NULL); SDL_RenderCopy(renderer, texture, NULL, NULL); } #ifdef CRISPY_TRUECOLOR if (curpane) { SDL_SetTextureAlphaMod(curpane, pane_alpha); SDL_RenderCopy(renderer, curpane, NULL, NULL); } #endif // Draw! SDL_RenderPresent(renderer); // [AM] Figure out how far into the current tic we're in as a fixed_t. if (crispy->uncapped) { fractionaltic = I_GetTimeMS() * TICRATE % 1000 * FRACUNIT / 1000; } // Restore background and undo the disk indicator, if it was drawn. V_RestoreDiskBackground(); } // // I_ReadScreen // void I_ReadScreen (pixel_t* scr) { memcpy(scr, I_VideoBuffer, SCREENWIDTH*SCREENHEIGHT*sizeof(*scr)); } // // I_SetPalette // // [crispy] intermediate gamma levels byte **gamma2table = NULL; void I_SetGammaTable (void) { int i; gamma2table = malloc(9 * sizeof(*gamma2table)); // [crispy] 5 original gamma levels for (i = 0; i < 5; i++) { gamma2table[2*i] = (byte *)gammatable[i]; } // [crispy] 4 intermediate gamma levels for (i = 0; i < 4; i++) { int j; gamma2table[2*i+1] = malloc(256 * sizeof(**gamma2table)); for (j = 0; j < 256; j++) { gamma2table[2*i+1][j] = (gamma2table[2*i][j] + gamma2table[2*i+2][j]) / 2; } } } #ifndef CRISPY_TRUECOLOR void I_SetPalette (byte *doompalette) { int i; // [crispy] intermediate gamma levels if (!gamma2table) { I_SetGammaTable(); } for (i=0; i<256; ++i) { // Zero out the bottom two bits of each channel - the PC VGA // controller only supports 6 bits of accuracy. // [crispy] intermediate gamma levels palette[i].r = gamma2table[usegamma][*doompalette++] & ~3; palette[i].g = gamma2table[usegamma][*doompalette++] & ~3; palette[i].b = gamma2table[usegamma][*doompalette++] & ~3; } palette_to_set = true; } // Given an RGB value, find the closest matching palette index. int I_GetPaletteIndex(int r, int g, int b) { int best, best_diff, diff; int i; best = 0; best_diff = INT_MAX; for (i = 0; i < 256; ++i) { diff = (r - palette[i].r) * (r - palette[i].r) + (g - palette[i].g) * (g - palette[i].g) + (b - palette[i].b) * (b - palette[i].b); if (diff < best_diff) { best = i; best_diff = diff; } if (diff == 0) { break; } } return best; } #else void I_SetPalette (int palette) { switch (palette) { case 0: curpane = NULL; break; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: curpane = redpane; pane_alpha = 0xff * palette / 9; break; case 9: case 10: case 11: case 12: curpane = yelpane; pane_alpha = 0xff * (palette - 8) / 8; break; case 13: curpane = grnpane; pane_alpha = 0xff * 125 / 1000; break; default: I_Error("Unknown palette: %d!\n", palette); break; } } #endif // // Set the window title // void I_SetWindowTitle(const char *title) { window_title = title; } // // Call the SDL function to set the window title, based on // the title set with I_SetWindowTitle. // void I_InitWindowTitle(void) { char *buf; buf = M_StringJoin(window_title, " - ", PACKAGE_STRING, NULL); SDL_SetWindowTitle(screen, buf); free(buf); } // Set the application icon void I_InitWindowIcon(void) { SDL_Surface *surface; surface = SDL_CreateRGBSurfaceFrom((void *) icon_data, icon_w, icon_h, 32, icon_w * 4, 0xff << 24, 0xff << 16, 0xff << 8, 0xff << 0); SDL_SetWindowIcon(screen, surface); SDL_FreeSurface(surface); } // Set video size to a particular scale factor (1x, 2x, 3x, etc.) static void SetScaleFactor(int factor) { // Pick 320x200 or 320x240, depending on aspect ratio correct window_width = factor * SCREENWIDTH; window_height = factor * actualheight; fullscreen = false; } void I_GraphicsCheckCommandLine(void) { int i; //! // @category video // @vanilla // // Disable blitting the screen. // noblit = M_CheckParm ("-noblit"); //! // @category video // // Don't grab the mouse when running in windowed mode. // nograbmouse_override = M_ParmExists("-nograbmouse"); // default to fullscreen mode, allow override with command line // nofullscreen because we love prboom //! // @category video // // Run in a window. // if (M_CheckParm("-window") || M_CheckParm("-nofullscreen")) { fullscreen = false; } //! // @category video // // Run in fullscreen mode. // if (M_CheckParm("-fullscreen")) { fullscreen = true; } //! // @category video // // Disable the mouse. // nomouse = M_CheckParm("-nomouse") > 0; //! // @category video // @arg // // Specify the screen width, in pixels. Implies -window. // i = M_CheckParmWithArgs("-width", 1); if (i > 0) { window_width = atoi(myargv[i + 1]); fullscreen = false; } //! // @category video // @arg // // Specify the screen height, in pixels. Implies -window. // i = M_CheckParmWithArgs("-height", 1); if (i > 0) { window_height = atoi(myargv[i + 1]); fullscreen = false; } //! // @category video // @arg // // Specify the dimensions of the window. Implies -window. // i = M_CheckParmWithArgs("-geometry", 1); if (i > 0) { int w, h, s; s = sscanf(myargv[i + 1], "%ix%i", &w, &h); if (s == 2) { window_width = w; window_height = h; fullscreen = false; } } //! // @category video // // Don't scale up the screen. Implies -window. // if (M_CheckParm("-1")) { SetScaleFactor(1); } //! // @category video // // Double up the screen to 2x its normal size. Implies -window. // if (M_CheckParm("-2")) { SetScaleFactor(2); } //! // @category video // // Double up the screen to 3x its normal size. Implies -window. // if (M_CheckParm("-3")) { SetScaleFactor(3); } } // Check if we have been invoked as a screensaver by xscreensaver. void I_CheckIsScreensaver(void) { char *env; env = getenv("XSCREENSAVER_WINDOW"); if (env != NULL) { screensaver_mode = true; } } static void SetSDLVideoDriver(void) { // Allow a default value for the SDL video driver to be specified // in the configuration file. if (strcmp(video_driver, "") != 0) { char *env_string; env_string = M_StringJoin("SDL_VIDEODRIVER=", video_driver, NULL); putenv(env_string); free(env_string); } } // Check the display bounds of the display referred to by 'video_display' and // set x and y to a location that places the window in the center of that // display. static void CenterWindow(int *x, int *y, int w, int h) { SDL_Rect bounds; if (SDL_GetDisplayBounds(video_display, &bounds) < 0) { fprintf(stderr, "CenterWindow: Failed to read display bounds " "for display #%d!\n", video_display); return; } *x = bounds.x + SDL_max((bounds.w - w) / 2, 0); *y = bounds.y + SDL_max((bounds.h - h) / 2, 0); } void I_GetWindowPosition(int *x, int *y, int w, int h) { // Check that video_display corresponds to a display that really exists, // and if it doesn't, reset it. if (video_display < 0 || video_display >= SDL_GetNumVideoDisplays()) { fprintf(stderr, "I_GetWindowPosition: We were configured to run on display #%d, " "but it no longer exists (max %d). Moving to display 0.\n", video_display, SDL_GetNumVideoDisplays() - 1); video_display = 0; } // in fullscreen mode, the window "position" still matters, because // we use it to control which display we run fullscreen on. if (fullscreen) { CenterWindow(x, y, w, h); return; } // in windowed mode, the desired window position can be specified // in the configuration file. if (window_position == NULL || !strcmp(window_position, "")) { *x = *y = SDL_WINDOWPOS_UNDEFINED; } else if (!strcmp(window_position, "center")) { // Note: SDL has a SDL_WINDOWPOS_CENTER, but this is useless for our // purposes, since we also want to control which display we appear on. // So we have to do this ourselves. CenterWindow(x, y, w, h); } else if (sscanf(window_position, "%i,%i", x, y) != 2) { // invalid format: revert to default fprintf(stderr, "I_GetWindowPosition: invalid window_position setting\n"); *x = *y = SDL_WINDOWPOS_UNDEFINED; } } static void SetVideoMode(void) { int w, h; int x, y; #ifndef CRISPY_TRUECOLOR unsigned int rmask, gmask, bmask, amask; #endif int unused_bpp; int window_flags = 0, renderer_flags = 0; SDL_DisplayMode mode; w = window_width; h = window_height; // In windowed mode, the window can be resized while the game is // running. window_flags = SDL_WINDOW_RESIZABLE; // Set the highdpi flag - this makes a big difference on Macs with // retina displays, especially when using small window sizes. window_flags |= SDL_WINDOW_ALLOW_HIGHDPI; if (fullscreen) { if (fullscreen_width == 0 && fullscreen_height == 0) { // This window_flags means "Never change the screen resolution! // Instead, draw to the entire screen by scaling the texture // appropriately". window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } else { w = fullscreen_width; h = fullscreen_height; window_flags |= SDL_WINDOW_FULLSCREEN; } } // Running without window decorations is potentially useful if you're // playing in three window mode and want to line up three game windows // next to each other on a single desktop. // Deliberately not documented because I'm not sure how useful this is yet. if (M_ParmExists("-borderless")) { window_flags |= SDL_WINDOW_BORDERLESS; } I_GetWindowPosition(&x, &y, w, h); // Create window and renderer contexts. We set the window title // later anyway and leave the window position "undefined". If // "window_flags" contains the fullscreen flag (see above), then // w and h are ignored. if (screen == NULL) { screen = SDL_CreateWindow(NULL, x, y, w, h, window_flags); if (screen == NULL) { I_Error("Error creating window for video startup: %s", SDL_GetError()); } pixel_format = SDL_GetWindowPixelFormat(screen); SDL_SetWindowMinimumSize(screen, SCREENWIDTH, actualheight); I_InitWindowTitle(); I_InitWindowIcon(); } // The SDL_RENDERER_TARGETTEXTURE flag is required to render the // intermediate texture into the upscaled texture. renderer_flags = SDL_RENDERER_TARGETTEXTURE; if (SDL_GetCurrentDisplayMode(video_display, &mode) != 0) { I_Error("Could not get display mode for video display #%d: %s", video_display, SDL_GetError()); } // Turn on vsync if we aren't in a -timedemo if (!singletics && mode.refresh_rate > 0) { if (crispy->vsync) // [crispy] uncapped vsync { renderer_flags |= SDL_RENDERER_PRESENTVSYNC; } } if (force_software_renderer) { renderer_flags |= SDL_RENDERER_SOFTWARE; renderer_flags &= ~SDL_RENDERER_PRESENTVSYNC; crispy->vsync = false; } if (renderer != NULL) { SDL_DestroyRenderer(renderer); // all associated textures get destroyed texture = NULL; texture_upscaled = NULL; } renderer = SDL_CreateRenderer(screen, -1, renderer_flags); // If we could not find a matching render driver, // try again without hardware acceleration. if (renderer == NULL && !force_software_renderer) { renderer_flags |= SDL_RENDERER_SOFTWARE; renderer_flags &= ~SDL_RENDERER_PRESENTVSYNC; renderer = SDL_CreateRenderer(screen, -1, renderer_flags); // If this helped, save the setting for later. if (renderer != NULL) { force_software_renderer = 1; } } if (renderer == NULL) { I_Error("Error creating renderer for screen window: %s", SDL_GetError()); } // Important: Set the "logical size" of the rendering context. At the same // time this also defines the aspect ratio that is preserved while scaling // and stretching the texture into the window. if (aspect_ratio_correct || integer_scaling) { SDL_RenderSetLogicalSize(renderer, SCREENWIDTH, actualheight); } // Force integer scales for resolution-independent rendering. #if SDL_VERSION_ATLEAST(2, 0, 5) SDL_RenderSetIntegerScale(renderer, integer_scaling); #endif // Blank out the full screen area in case there is any junk in // the borders that won't otherwise be overwritten. SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_RenderPresent(renderer); #ifndef CRISPY_TRUECOLOR // Create the 8-bit paletted and the 32-bit RGBA screenbuffer surfaces. if (screenbuffer != NULL) { SDL_FreeSurface(screenbuffer); screenbuffer = NULL; } if (screenbuffer == NULL) { screenbuffer = SDL_CreateRGBSurface(0, SCREENWIDTH, SCREENHEIGHT, 8, 0, 0, 0, 0); SDL_FillRect(screenbuffer, NULL, 0); } #endif // Format of argbbuffer must match the screen pixel format because we // import the surface data into the texture. if (argbbuffer != NULL) { SDL_FreeSurface(argbbuffer); argbbuffer = NULL; } if (argbbuffer == NULL) { SDL_PixelFormatEnumToMasks(pixel_format, &unused_bpp, &rmask, &gmask, &bmask, &amask); argbbuffer = SDL_CreateRGBSurface(0, SCREENWIDTH, SCREENHEIGHT, 32, rmask, gmask, bmask, amask); #ifdef CRISPY_TRUECOLOR SDL_FillRect(argbbuffer, NULL, I_MapRGB(0xff, 0x0, 0x0)); redpane = SDL_CreateTextureFromSurface(renderer, argbbuffer); SDL_SetTextureBlendMode(redpane, SDL_BLENDMODE_BLEND); SDL_FillRect(argbbuffer, NULL, I_MapRGB(0xd7, 0xba, 0x45)); yelpane = SDL_CreateTextureFromSurface(renderer, argbbuffer); SDL_SetTextureBlendMode(yelpane, SDL_BLENDMODE_BLEND); SDL_FillRect(argbbuffer, NULL, I_MapRGB(0x0, 0xff, 0x0)); grnpane = SDL_CreateTextureFromSurface(renderer, argbbuffer); SDL_SetTextureBlendMode(grnpane, SDL_BLENDMODE_BLEND); #endif SDL_FillRect(argbbuffer, NULL, 0); } if (texture != NULL) { SDL_DestroyTexture(texture); } // Set the scaling quality for rendering the intermediate texture into // the upscaled texture to "nearest", which is gritty and pixelated and // resembles software scaling pretty well. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); // Create the intermediate texture that the RGBA surface gets loaded into. // The SDL_TEXTUREACCESS_STREAMING flag means that this texture's content // is going to change frequently. texture = SDL_CreateTexture(renderer, pixel_format, SDL_TEXTUREACCESS_STREAMING, SCREENWIDTH, SCREENHEIGHT); // Initially create the upscaled texture for rendering to screen CreateUpscaledTexture(true); } void I_InitGraphics(void) { SDL_Event dummy; #ifndef CRISPY_TRUECOLOR byte *doompal; #endif char *env; // Pass through the XSCREENSAVER_WINDOW environment variable to // SDL_WINDOWID, to embed the SDL window into the Xscreensaver // window. env = getenv("XSCREENSAVER_WINDOW"); if (env != NULL) { char winenv[30]; int winid; sscanf(env, "0x%x", &winid); M_snprintf(winenv, sizeof(winenv), "SDL_WINDOWID=%i", winid); putenv(winenv); } SetSDLVideoDriver(); if (SDL_Init(SDL_INIT_VIDEO) < 0) { I_Error("Failed to initialize video: %s", SDL_GetError()); } // When in screensaver mode, run full screen and auto detect // screen dimensions (don't change video mode) if (screensaver_mode) { fullscreen = true; } // [crispy] run-time variable high-resolution rendering if (crispy->hires) { SCREENWIDTH = MAXWIDTH; SCREENHEIGHT = MAXHEIGHT; SCREENHEIGHT_4_3 = MAXHEIGHT_4_3; } else { SCREENWIDTH = ORIGWIDTH; SCREENHEIGHT = ORIGHEIGHT; SCREENHEIGHT_4_3 = ORIGHEIGHT_4_3; } #ifndef CRISPY_TRUECOLOR blit_rect.w = SCREENWIDTH; blit_rect.h = SCREENHEIGHT; #endif // [crispy] (re-)initialize resolution-agnostic patch drawing V_Init(); if (aspect_ratio_correct == 1) { actualheight = SCREENHEIGHT_4_3; } else { actualheight = SCREENHEIGHT; } // Create the game window; this may switch graphic modes depending // on configuration. AdjustWindowSize(); SetVideoMode(); #ifndef CRISPY_TRUECOLOR // Start with a clear black screen // (screen will be flipped after we set the palette) SDL_FillRect(screenbuffer, NULL, 0); // Set the palette doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE); I_SetPalette(doompal); SDL_SetPaletteColors(screenbuffer->format->palette, palette, 0, 256); #endif // SDL2-TODO UpdateFocus(); UpdateGrab(); // On some systems, it takes a second or so for the screen to settle // after changing modes. We include the option to add a delay when // setting the screen mode, so that the game doesn't start immediately // with the player unable to see anything. if (fullscreen && !screensaver_mode) { SDL_Delay(startup_delay); } // The actual 320x200 canvas that we draw to. This is the pixel buffer of // the 8-bit paletted screen buffer that gets blit on an intermediate // 32-bit RGBA screen buffer that gets loaded into a texture that gets // finally rendered into our window or full screen in I_FinishUpdate(). #ifndef CRISPY_TRUECOLOR I_VideoBuffer = screenbuffer->pixels; #else I_VideoBuffer = argbbuffer->pixels; #endif V_RestoreBuffer(); // Clear the screen to black. memset(I_VideoBuffer, 0, SCREENWIDTH * SCREENHEIGHT * sizeof(*I_VideoBuffer)); // clear out any events waiting at the start and center the mouse while (SDL_PollEvent(&dummy)); initialized = true; // Call I_ShutdownGraphics on quit I_AtExit(I_ShutdownGraphics, true); } // [crispy] re-initialize only the parts of the rendering stack that are really necessary void I_ReInitGraphics (int reinit) { // [crispy] re-set rendering resolution and re-create framebuffers if (reinit & REINIT_FRAMEBUFFERS) { unsigned int rmask, gmask, bmask, amask; int unused_bpp; if (crispy->hires) { SCREENWIDTH = MAXWIDTH; SCREENHEIGHT = MAXHEIGHT; SCREENHEIGHT_4_3 = MAXHEIGHT_4_3; } else { SCREENWIDTH = ORIGWIDTH; SCREENHEIGHT = ORIGHEIGHT; SCREENHEIGHT_4_3 = ORIGHEIGHT_4_3; } #ifndef CRISPY_TRUECOLOR blit_rect.w = SCREENWIDTH; blit_rect.h = SCREENHEIGHT; #endif // [crispy] re-initialize resolution-agnostic patch drawing V_Init(); #ifndef CRISPY_TRUECOLOR SDL_FreeSurface(screenbuffer); screenbuffer = SDL_CreateRGBSurface(0, SCREENWIDTH, SCREENHEIGHT, 8, 0, 0, 0, 0); #endif SDL_FreeSurface(argbbuffer); SDL_PixelFormatEnumToMasks(pixel_format, &unused_bpp, &rmask, &gmask, &bmask, &amask); argbbuffer = SDL_CreateRGBSurface(0, SCREENWIDTH, SCREENHEIGHT, 32, rmask, gmask, bmask, amask); #ifndef CRISPY_TRUECOLOR // [crispy] re-set the framebuffer pointer I_VideoBuffer = screenbuffer->pixels; #else I_VideoBuffer = argbbuffer->pixels; #endif V_RestoreBuffer(); // [crispy] it will get re-created below with the new resolution SDL_DestroyTexture(texture); } // [crispy] re-create renderer if (reinit & REINIT_RENDERER) { SDL_RendererInfo info = {0}; int flags; SDL_GetRendererInfo(renderer, &info); flags = info.flags; if (crispy->vsync && !(flags & SDL_RENDERER_SOFTWARE)) { flags |= SDL_RENDERER_PRESENTVSYNC; } else { flags &= ~SDL_RENDERER_PRESENTVSYNC; } SDL_DestroyRenderer(renderer); renderer = SDL_CreateRenderer(screen, -1, flags); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // [crispy] the texture gets destroyed in SDL_DestroyRenderer(), force its re-creation texture_upscaled = NULL; } // [crispy] re-create textures if (reinit & REINIT_TEXTURES) { SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); texture = SDL_CreateTexture(renderer, pixel_format, SDL_TEXTUREACCESS_STREAMING, SCREENWIDTH, SCREENHEIGHT); // [crispy] force its re-creation CreateUpscaledTexture(true); } // [crispy] re-set logical rendering resolution if (reinit & REINIT_ASPECTRATIO) { if (aspect_ratio_correct == 1) { actualheight = SCREENHEIGHT_4_3; } else { actualheight = SCREENHEIGHT; } if (aspect_ratio_correct || integer_scaling) { SDL_RenderSetLogicalSize(renderer, SCREENWIDTH, actualheight); } else { SDL_RenderSetLogicalSize(renderer, 0, 0); } #if SDL_VERSION_ATLEAST(2, 0, 5) SDL_RenderSetIntegerScale(renderer, integer_scaling); #endif } // [crispy] adjust the window size and re-set the palette need_resize = true; } // [crispy] take screenshot of the rendered image void I_RenderReadPixels(byte **data, int *w, int *h, int *p) { SDL_Rect rect; SDL_PixelFormat *format; int temp; uint32_t png_format; byte *pixels; // [crispy] adjust cropping rectangle if necessary rect.x = rect.y = 0; SDL_GetRendererOutputSize(renderer, &rect.w, &rect.h); if (aspect_ratio_correct || integer_scaling) { if (integer_scaling) { int temp1, temp2, scale; temp1 = rect.w; temp2 = rect.h; scale = MIN(rect.w / SCREENWIDTH, rect.h / actualheight); rect.w = SCREENWIDTH * scale; rect.h = actualheight * scale; rect.x = (temp1 - rect.w) / 2; rect.y = (temp2 - rect.h) / 2; } else if (rect.w * actualheight > rect.h * SCREENWIDTH) { temp = rect.w; rect.w = rect.h * SCREENWIDTH / actualheight; rect.x = (temp - rect.w) / 2; } else if (rect.h * SCREENWIDTH > rect.w * actualheight) { temp = rect.h; rect.h = rect.w * actualheight / SCREENWIDTH; rect.y = (temp - rect.h) / 2; } } // [crispy] native PNG pixel format #if SDL_VERSION_ATLEAST(2, 0, 5) png_format = SDL_PIXELFORMAT_RGB24; #else #if SDL_BYTEORDER == SDL_LIL_ENDIAN png_format = SDL_PIXELFORMAT_ABGR8888; #else png_format = SDL_PIXELFORMAT_RGBA8888; #endif #endif format = SDL_AllocFormat(png_format); temp = rect.w * format->BytesPerPixel; // [crispy] pitch // [crispy] As far as I understand the issue, SDL_RenderPresent() // may return early, i.e. before it has actually finished rendering the // current texture to screen -- from where we want to capture it. // However, it does never return before it has finished rendering the // *previous* texture. // Thus, we add a second call to SDL_RenderPresent() here to make sure // that it has at least finished rendering the previous texture, which // already contains the scene that we actually want to capture. if (crispy->post_rendering_hook) { SDL_RenderCopy(renderer, crispy->smoothscaling ? texture_upscaled : texture, NULL, NULL); SDL_RenderPresent(renderer); } // [crispy] allocate memory for screenshot image pixels = malloc(rect.h * temp); SDL_RenderReadPixels(renderer, &rect, format->format, pixels, temp); *data = pixels; *w = rect.w; *h = rect.h; *p = temp; SDL_FreeFormat(format); } // Bind all variables controlling video options into the configuration // file system. void I_BindVideoVariables(void) { M_BindIntVariable("use_mouse", &usemouse); M_BindIntVariable("fullscreen", &fullscreen); M_BindIntVariable("video_display", &video_display); M_BindIntVariable("aspect_ratio_correct", &aspect_ratio_correct); M_BindIntVariable("integer_scaling", &integer_scaling); M_BindIntVariable("vga_porch_flash", &vga_porch_flash); M_BindIntVariable("startup_delay", &startup_delay); M_BindIntVariable("fullscreen_width", &fullscreen_width); M_BindIntVariable("fullscreen_height", &fullscreen_height); M_BindIntVariable("force_software_renderer", &force_software_renderer); M_BindIntVariable("max_scaling_buffer_pixels", &max_scaling_buffer_pixels); M_BindIntVariable("window_width", &window_width); M_BindIntVariable("window_height", &window_height); M_BindIntVariable("grabmouse", &grabmouse); M_BindStringVariable("video_driver", &video_driver); M_BindStringVariable("window_position", &window_position); M_BindIntVariable("usegamma", &usegamma); M_BindIntVariable("png_screenshots", &png_screenshots); } #ifdef CRISPY_TRUECOLOR const pixel_t I_BlendAdd (const pixel_t bg, const pixel_t fg) { uint32_t r, g, b; if ((r = (fg & rmask) + (bg & rmask)) > rmask) r = rmask; if ((g = (fg & gmask) + (bg & gmask)) > gmask) g = gmask; if ((b = (fg & bmask) + (bg & bmask)) > bmask) b = bmask; return amask | r | g | b; } // [crispy] http://stereopsis.com/doubleblend.html const pixel_t I_BlendDark (const pixel_t bg, const int d) { const uint32_t ag = (bg & 0xff00ff00) >> 8; const uint32_t rb = bg & 0x00ff00ff; uint32_t sag = d * ag; uint32_t srb = d * rb; sag = sag & 0xff00ff00; srb = (srb >> 8) & 0x00ff00ff; return amask | sag | srb; } const pixel_t I_BlendOver (const pixel_t bg, const pixel_t fg) { const uint32_t r = ((blend_alpha * (fg & rmask) + (0xff - blend_alpha) * (bg & rmask)) >> 8) & rmask; const uint32_t g = ((blend_alpha * (fg & gmask) + (0xff - blend_alpha) * (bg & gmask)) >> 8) & gmask; const uint32_t b = ((blend_alpha * (fg & bmask) + (0xff - blend_alpha) * (bg & bmask)) >> 8) & bmask; return amask | r | g | b; } const pixel_t (*blendfunc) (const pixel_t fg, const pixel_t bg) = I_BlendOver; const pixel_t I_MapRGB (const uint8_t r, const uint8_t g, const uint8_t b) { /* return amask | (((r * rmask) >> 8) & rmask) | (((g * gmask) >> 8) & gmask) | (((b * bmask) >> 8) & bmask); */ return SDL_MapRGB(argbbuffer->format, r, g, b); } #endif crispy-doom-crispy-doom-5.6.4/src/i_video.h000066400000000000000000000057131360717211000206200ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System specific interface stuff. // #ifndef __I_VIDEO__ #define __I_VIDEO__ #include "doomtype.h" #include "crispy.h" // Screen width and height. #define ORIGWIDTH 320 // [crispy] #define ORIGHEIGHT 200 // [crispy] #define MAXWIDTH (ORIGWIDTH << 1) // [crispy] #define MAXHEIGHT (ORIGHEIGHT << 1) // [crispy] extern int SCREENWIDTH; extern int SCREENHEIGHT; // Screen height used when aspect_ratio_correct=true. #define ORIGHEIGHT_4_3 240 // [crispy] #define MAXHEIGHT_4_3 (ORIGHEIGHT_4_3 << 1) // [crispy] extern int SCREENHEIGHT_4_3; typedef boolean (*grabmouse_callback_t)(void); // Called by D_DoomMain, // determines the hardware configuration // and sets up the video mode void I_InitGraphics (void); void I_GraphicsCheckCommandLine(void); void I_ShutdownGraphics(void); // Takes full 8 bit values. #ifndef CRISPY_TRUECOLOR void I_SetPalette (byte* palette); int I_GetPaletteIndex(int r, int g, int b); #else void I_SetPalette (int palette); extern const pixel_t I_MapRGB (const uint8_t r, const uint8_t g, const uint8_t b); #endif void I_UpdateNoBlit (void); void I_FinishUpdate (void); void I_ReadScreen (pixel_t* scr); void I_BeginRead (void); void I_SetWindowTitle(const char *title); void I_CheckIsScreensaver(void); void I_SetGrabMouseCallback(grabmouse_callback_t func); void I_DisplayFPSDots(boolean dots_on); void I_BindVideoVariables(void); void I_InitWindowTitle(void); void I_InitWindowIcon(void); // Called before processing any tics in a frame (just after displaying a frame). // Time consuming syncronous operations are performed here (joystick reading). void I_StartFrame (void); // Called before processing each tic in a frame. // Quick syncronous operations are performed here. void I_StartTic (void); // Enable the loading disk image displayed when reading from disk. void I_EnableLoadingDisk(int xoffs, int yoffs); extern char *video_driver; extern boolean screenvisible; extern int vanilla_keyboard_mapping; extern boolean screensaver_mode; extern int usegamma; extern pixel_t *I_VideoBuffer; extern int screen_width; extern int screen_height; extern int fullscreen; extern int aspect_ratio_correct; extern int integer_scaling; extern int vga_porch_flash; extern int force_software_renderer; extern char *window_position; void I_GetWindowPosition(int *x, int *y, int w, int h); // Joystic/gamepad hysteresis extern unsigned int joywait; #endif crispy-doom-crispy-doom-5.6.4/src/i_videohr.c000066400000000000000000000131001360717211000211320ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // SDL emulation of VGA 640x480x4 planar video mode, // for Hexen startup loading screen. // #include "SDL.h" #include "string.h" #include "doomtype.h" #include "i_timer.h" #include "i_video.h" // Palette fade-in takes two seconds #define FADE_TIME 2000 #define HR_SCREENWIDTH 640 #define HR_SCREENHEIGHT 480 static SDL_Window *hr_screen = NULL; static SDL_Surface *hr_surface = NULL; static const char *window_title = ""; boolean I_SetVideoModeHR(void) { int x, y; if (SDL_Init(SDL_INIT_VIDEO) < 0) { return false; } I_GetWindowPosition(&x, &y, HR_SCREENWIDTH, HR_SCREENHEIGHT); // Create screen surface at the native desktop pixel depth (bpp=0), // as we cannot trust true 8-bit to reliably work nowadays. hr_screen = SDL_CreateWindow(window_title, x, y, HR_SCREENWIDTH, HR_SCREENHEIGHT, 0); if (hr_screen == NULL) { SDL_QuitSubSystem(SDL_INIT_VIDEO); return false; } // We do all actual drawing into an intermediate surface. hr_surface = SDL_CreateRGBSurface(0, HR_SCREENWIDTH, HR_SCREENHEIGHT, 8, 0, 0, 0, 0); return true; } void I_SetWindowTitleHR(const char *title) { window_title = title; } void I_UnsetVideoModeHR(void) { if (hr_screen != NULL) { SDL_QuitSubSystem(SDL_INIT_VIDEO); hr_screen = NULL; SDL_FreeSurface(hr_surface); hr_surface = NULL; } } void I_ClearScreenHR(void) { SDL_Rect area = { 0, 0, HR_SCREENWIDTH, HR_SCREENHEIGHT }; SDL_FillRect(hr_surface, &area, 0); } void I_SlamBlockHR(int x, int y, int w, int h, const byte *src) { SDL_Rect blit_rect; const byte *srcptrs[4]; byte srcbits[4]; byte *dest; int x1, y1; int i; int bit; // Set up source pointers to read from source buffer - each 4-bit // pixel has its bits split into four sub-buffers for (i=0; i<4; ++i) { srcptrs[i] = src + (i * w * h / 8); } if (SDL_LockSurface(hr_surface) < 0) { return; } // Draw each pixel bit = 0; for (y1=y; y1pixels) + y1 * hr_surface->pitch + x; for (x1=x; x1> (7 - (bit % 8))) & 0x1; } // Reassemble the pixel value *dest = (srcbits[0] << 0) | (srcbits[1] << 1) | (srcbits[2] << 2) | (srcbits[3] << 3); // Next pixel! ++dest; ++bit; } } SDL_UnlockSurface(hr_surface); // Update the region we drew. blit_rect.x = x; blit_rect.y = y; blit_rect.w = w; blit_rect.h = h; SDL_BlitSurface(hr_surface, &blit_rect, SDL_GetWindowSurface(hr_screen), &blit_rect); SDL_UpdateWindowSurfaceRects(hr_screen, &blit_rect, 1); } void I_SlamHR(const byte *buffer) { I_SlamBlockHR(0, 0, HR_SCREENWIDTH, HR_SCREENHEIGHT, buffer); } void I_InitPaletteHR(void) { // ... } void I_SetPaletteHR(const byte *palette) { SDL_Rect screen_rect = {0, 0, HR_SCREENWIDTH, HR_SCREENHEIGHT}; SDL_Color sdlpal[16]; int i; for (i=0; i<16; ++i) { sdlpal[i].r = palette[i * 3 + 0] * 4; sdlpal[i].g = palette[i * 3 + 1] * 4; sdlpal[i].b = palette[i * 3 + 2] * 4; } // After setting colors, update the screen. SDL_SetPaletteColors(hr_surface->format->palette, sdlpal, 0, 16); SDL_BlitSurface(hr_surface, &screen_rect, SDL_GetWindowSurface(hr_screen), &screen_rect); SDL_UpdateWindowSurfaceRects(hr_screen, &screen_rect, 1); } void I_FadeToPaletteHR(const byte *palette) { byte tmppal[16 * 3]; int starttime; int elapsed; int i; starttime = I_GetTimeMS(); for (;;) { elapsed = I_GetTimeMS() - starttime; if (elapsed >= FADE_TIME) { break; } // Generate the fake palette for (i=0; i<16 * 3; ++i) { tmppal[i] = (palette[i] * elapsed) / FADE_TIME; } I_SetPaletteHR(tmppal); SDL_UpdateWindowSurface(hr_screen); // Sleep a bit I_Sleep(10); } // Set the final palette I_SetPaletteHR(palette); } void I_BlackPaletteHR(void) { byte blackpal[16 * 3]; memset(blackpal, 0, sizeof(blackpal)); I_SetPaletteHR(blackpal); } // Check if the user has hit the escape key to abort startup. boolean I_CheckAbortHR(void) { SDL_Event ev; boolean result = false; // Not initialized? if (hr_surface == NULL) { return false; } while (SDL_PollEvent(&ev)) { if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_ESCAPE) { result = true; } } return result; } crispy-doom-crispy-doom-5.6.4/src/i_videohr.h000066400000000000000000000022001360717211000211360ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // SDL emulation of VGA 640x480x4 planar video mode, // for Hexen startup loading screen. // #ifndef I_VIDEOHR_H #define I_VIDEOHR_H boolean I_SetVideoModeHR(void); void I_UnsetVideoModeHR(void); void I_SetWindowTitleHR(const char *title); void I_ClearScreenHR(void); void I_SlamBlockHR(int x, int y, int w, int h, const byte *src); void I_SlamHR(const byte *buffer); void I_InitPaletteHR(void); void I_SetPaletteHR(const byte *palette); void I_FadeToPaletteHR(const byte *palette); void I_BlackPaletteHR(void); boolean I_CheckAbortHR(void); #endif /* #ifndef I_VIDEOHR_H */ crispy-doom-crispy-doom-5.6.4/src/icon.c000066400000000000000000006326671360717211000201430ustar00rootroot00000000000000static int icon_w = 128; static int icon_h = 128; static const unsigned int icon_data[] = { 0xce9b35ff, 0xca982eff, 0xcb972bff, 0xd4a648ff, 0xd7ab5bff, 0xd7ac5cff, 0xd7ac5eff, 0xd5a448ff, 0xc18a20ff, 0xb37a0cff, 0xb37a0cff, 0xb37606ff, 0xb97609ff, 0xdca946ff, 0xe9c16dff, 0xedc97bff, 0xf3d78fff, 0xf7db92ff, 0xeac068ff, 0xe0ae4eff, 0xd99a27ff, 0xd99617ff, 0xdea12bff, 0xe1a735ff, 0xe6b142ff, 0xe8b444ff, 0xeab647ff, 0xebb746ff, 0xebb849ff, 0xecb84aff, 0xecb94aff, 0xebb948ff, 0xeab843ff, 0xeab946ff, 0xecb63eff, 0xecb63cff, 0xecbe5dff, 0xecc571ff, 0xedc776ff, 0xeac060ff, 0xebc268ff, 0xebc679ff, 0xecc882ff, 0xe9c579ff, 0xe7bf68ff, 0xebc26eff, 0xebc374ff, 0xebc579ff, 0xeac376ff, 0xe9bd66ff, 0xe9be64ff, 0xebc47aff, 0xecc479ff, 0xeac06dff, 0xeabe66ff, 0xecc26cff, 0xebc26dff, 0xedc675ff, 0xeec67bff, 0xeec77cff, 0xeec97eff, 0xf1d391ff, 0xf0cf89ff, 0xefcb7fff, 0xeec36aff, 0xedc36fff, 0xecc36dff, 0xf3d495ff, 0xf3d69dff, 0xf1d194ff, 0xedc880ff, 0xf3d295ff, 0xf6d69dff, 0xe8b96aff, 0xe1ab48ff, 0xe3b157ff, 0xe6bd75ff, 0xe7c485ff, 0xe8c585ff, 0xe9c684ff, 0xeac98bff, 0xecc990ff, 0xeac98eff, 0xe6c688ff, 0xe8c78bff, 0xe9c98dff, 0xe6c37eff, 0xe8c686ff, 0xebc995ff, 0xedcc95ff, 0xedcc93ff, 0xefd2a2ff, 0xebcd9dff, 0xe9c98fff, 0xecd1a5ff, 0xebd2abff, 0xe9cea2ff, 0xeacfa0ff, 0xe8cea0ff, 0xe4c28aff, 0xe1ba77ff, 0xdcb268ff, 0xd3a556ff, 0xc48f44ff, 0xc68f43ff, 0xe3ae5bff, 0xeabd7bff, 0xeecd99ff, 0xe8c892ff, 0xe2c080ff, 0xe0bd78ff, 0xe3c27fff, 0xe6c792ff, 0xddba7cff, 0xd7b169ff, 0xd3ac61ff, 0xd1b070ff, 0xcead6eff, 0xc5a35eff, 0xb98b33ff, 0xa9710eff, 0xac7417ff, 0xb38124ff, 0xb58424ff, 0xb88930ff, 0xbb8e36ff, 0xbc8d2fff, 0xbd8c2bff, 0xcb972dff, 0xc9982eff, 0xcd9a2eff, 0xd4a649ff, 0xd7ab57ff, 0xd8ab5aff, 0xd8aa59ff, 0xd8a851ff, 0xca952eff, 0xb3790bff, 0xb27607ff, 0xb17305ff, 0xb46d07ff, 0xd7a13dff, 0xe3b967ff, 0xe7c075ff, 0xeac578ff, 0xf2d388ff, 0xfae1a4ff, 0xfbe7b1ff, 0xf1d28bff, 0xdfa73fff, 0xda9c26ff, 0xdc9f29ff, 0xdfa32cff, 0xe1a72fff, 0xe3ab37ff, 0xe6b13dff, 0xe8b344ff, 0xe7b44aff, 0xe9b547ff, 0xe7b544ff, 0xe7b23eff, 0xe7b23fff, 0xe8b23aff, 0xe7b034ff, 0xe8b74dff, 0xecc571ff, 0xecc87dff, 0xebc269ff, 0xeac065ff, 0xeac372ff, 0xeac372ff, 0xe7bc62ff, 0xe8bc61ff, 0xeac06dff, 0xe8bc65ff, 0xe9bd64ff, 0xe8bd65ff, 0xe7b95bff, 0xe9c06bff, 0xe9c173ff, 0xe9be69ff, 0xe8bb65ff, 0xe7b958ff, 0xeabf67ff, 0xedc473ff, 0xeec676ff, 0xefc87bff, 0xefcc84ff, 0xeec77eff, 0xefcc8aff, 0xefce85ff, 0xf0cb81ff, 0xefc978ff, 0xf1ce85ff, 0xefc97aff, 0xf1d18aff, 0xf2d28eff, 0xf2d18bff, 0xf4d694ff, 0xf9e0aaff, 0xfbe3b2ff, 0xf2cd8fff, 0xe4b053ff, 0xe1af4fff, 0xe4b864ff, 0xe7c17dff, 0xebc98fff, 0xebc991ff, 0xe9c788ff, 0xeaca8bff, 0xeaca8fff, 0xe7c589ff, 0xe6c486ff, 0xe6c481ff, 0xe5c27cff, 0xe9c785ff, 0xeac98dff, 0xeacc8fff, 0xefd099ff, 0xeccd98ff, 0xe8c78bff, 0xeaca8fff, 0xebd1a3ff, 0xead0a7ff, 0xe9cc9eff, 0xe9cea1ff, 0xe8cfa5ff, 0xe7c896ff, 0xe3c189ff, 0xdfb97fff, 0xd5a75fff, 0xc1893dff, 0xa0652bff, 0xc79044ff, 0xe8b667ff, 0xe9c080ff, 0xe7c385ff, 0xe2bf7eff, 0xdeba72ff, 0xdebb75ff, 0xe0c28bff, 0xdcbb7eff, 0xd9b777ff, 0xd3b16cff, 0xd0af6fff, 0xcead70ff, 0xc5a35eff, 0xba913dff, 0xab7413ff, 0xa5680aff, 0xac761bff, 0xaf7d1eff, 0xb27f21ff, 0xb17f1fff, 0xb8882cff, 0xbb8c30ff, 0xce9d36ff, 0xcb9a31ff, 0xce992cff, 0xd8a951ff, 0xd9ad5bff, 0xd8ac5dff, 0xd5a855ff, 0xd6aa53ff, 0xd5a447ff, 0xbf861dff, 0xae7107ff, 0xad6c05ff, 0xb36905ff, 0xce922bff, 0xdba949ff, 0xdeb155ff, 0xe4b862ff, 0xe9c277ff, 0xf1d493ff, 0xf7e1aaff, 0xfceab4ff, 0xfae2a4ff, 0xedc772ff, 0xdea537ff, 0xda9b21ff, 0xdc9e21ff, 0xdea227ff, 0xe0a529ff, 0xe3aa32ff, 0xe3ad38ff, 0xe3ab37ff, 0xe4ab34ff, 0xe3a92fff, 0xe3a931ff, 0xe2ab35ff, 0xe2a92fff, 0xe7b750ff, 0xebc470ff, 0xeac673ff, 0xebc472ff, 0xeac06cff, 0xe8bb5eff, 0xe6b857ff, 0xe6b854ff, 0xe7b857ff, 0xe9bf69ff, 0xe7bb63ff, 0xe7ba5eff, 0xe7ba5dff, 0xe7ba5bff, 0xe8bd65ff, 0xeac379ff, 0xe7bc66ff, 0xe5b759ff, 0xe7ba5cff, 0xecc473ff, 0xecc26dff, 0xecc36fff, 0xeec97dff, 0xefcd88ff, 0xefca81ff, 0xeec87bff, 0xefc875ff, 0xefc977ff, 0xf1ca7aff, 0xf6db9fff, 0xf6d89aff, 0xf5d795ff, 0xf9dfa2ff, 0xf9dfa0ff, 0xf8dd9bff, 0xfce6b2ff, 0xfce7b7ff, 0xf7d9a1ff, 0xe9bd6dff, 0xe4b358ff, 0xe7bd6fff, 0xeac483ff, 0xe9c486ff, 0xe9c486ff, 0xe7c481ff, 0xe8c681ff, 0xe8c98aff, 0xe7c786ff, 0xe4c079ff, 0xe6c380ff, 0xe9c98aff, 0xedcc92ff, 0xe8c88aff, 0xe9c989ff, 0xebcb91ff, 0xeacb91ff, 0xe9ca8fff, 0xe9c890ff, 0xe9cb96ff, 0xe8c994ff, 0xe9ca9aff, 0xe9cda1ff, 0xe7ca9eff, 0xe3c088ff, 0xdeb671ff, 0xddb475ff, 0xd6ab68ff, 0xc8954cff, 0xa76b30ff, 0xac7533ff, 0xe4b15eff, 0xeabf7aff, 0xebc58dff, 0xe8c692ff, 0xe3c186ff, 0xdeb977ff, 0xdab877ff, 0xdebc80ff, 0xdcba7fff, 0xd2b06cff, 0xcead6cff, 0xcbaa6eff, 0xc09c53ff, 0xba9140ff, 0xae7f28ff, 0xa36d14ff, 0xa36a11ff, 0xa97415ff, 0xac7719ff, 0xaa7614ff, 0xb07f20ff, 0xb38220ff, 0xcd9c37ff, 0xce9d39ff, 0xd3a242ff, 0xd7aa57ff, 0xd7ab5bff, 0xd5aa57ff, 0xd7ab58ff, 0xd9ad59ff, 0xd7a751ff, 0xcd9a35ff, 0xb07414ff, 0xa56004ff, 0xb26904ff, 0xc17e11ff, 0xd5a13dff, 0xdaa748ff, 0xddad51ff, 0xe1b65fff, 0xe9c173ff, 0xeecc83ff, 0xf4da96ff, 0xfae4a5ff, 0xfce7a5ff, 0xf8df97ff, 0xe6ba58ff, 0xd8981cff, 0xd99a19ff, 0xdc9d20ff, 0xdea025ff, 0xdfa223ff, 0xe0a328ff, 0xe1a52dff, 0xe0a42bff, 0xdea123ff, 0xdea122ff, 0xdfa62bff, 0xe8bb58ff, 0xecc57aff, 0xeac372ff, 0xeac274ff, 0xeac275ff, 0xe9bd66ff, 0xe8bc62ff, 0xe7bb60ff, 0xe6b95cff, 0xe6bb60ff, 0xe7ba60ff, 0xe7ba5dff, 0xe7b95bff, 0xe7ba63ff, 0xe7ba62ff, 0xe8bb66ff, 0xe6b656ff, 0xe5b654ff, 0xe8be66ff, 0xecc475ff, 0xebc36dff, 0xedc97eff, 0xeeca81ff, 0xedc87bff, 0xf0cf89ff, 0xefcc7eff, 0xeec876ff, 0xefc97bff, 0xf4d48fff, 0xf6db9fff, 0xf6d893ff, 0xfbe2a8ff, 0xfce7b1ff, 0xfceab3ff, 0xfce9b2ff, 0xfce8b9ff, 0xfce5aeff, 0xfbe3abff, 0xefc981ff, 0xe3b258ff, 0xe6ba69ff, 0xe8bf75ff, 0xe6bb6cff, 0xe7c079ff, 0xe8c37eff, 0xe6be72ff, 0xe4bd73ff, 0xe5bf76ff, 0xe8c37cff, 0xeecf93ff, 0xf0d4a0ff, 0xeed19cff, 0xebcd95ff, 0xebcc92ff, 0xeed39fff, 0xedd4a4ff, 0xe7c78cff, 0xe9cb95ff, 0xe9ce9cff, 0xe8cb97ff, 0xe9ca99ff, 0xe9cca0ff, 0xe5c492ff, 0xdfba7aff, 0xddb46dff, 0xdbaf67ff, 0xd6aa60ff, 0xcd9b4eff, 0xb87e39ff, 0xa46727ff, 0xd99d42ff, 0xe7b769ff, 0xe8be7dff, 0xe6bd7fff, 0xe4bf86ff, 0xe3c189ff, 0xdcb879ff, 0xdcbb7fff, 0xd8b77cff, 0xd4b273ff, 0xcaa866ff, 0xc5a561ff, 0xbe9848ff, 0xbb9142ff, 0xab7f31ff, 0x9b722aff, 0x9b691aff, 0x9d640aff, 0x9f6a0dff, 0xa67416ff, 0xaa7617ff, 0xaf7c1eff, 0xd3a54fff, 0xd4a74eff, 0xd6ab57ff, 0xd8ac5bff, 0xd5aa57ff, 0xd5aa56ff, 0xd8ad5cff, 0xd8ae5aff, 0xd6a851ff, 0xd3a142ff, 0xba8420ff, 0x9d5b04ff, 0xac6404ff, 0xbd7d12ff, 0xd09d3fff, 0xd5a347ff, 0xd6a541ff, 0xddad51ff, 0xe3b967ff, 0xe8bf6dff, 0xecc776ff, 0xf1d58eff, 0xf6de9cff, 0xfae6acff, 0xfae4a3ff, 0xe8bf63ff, 0xd99c26ff, 0xd79718ff, 0xd99818ff, 0xdb9c1bff, 0xdc9e1dff, 0xdc9e1eff, 0xdc9e1dff, 0xdb9c1aff, 0xdb9b16ff, 0xdea327ff, 0xe6b750ff, 0xebc274ff, 0xeac376ff, 0xe7bf6cff, 0xe8bd65ff, 0xe9be69ff, 0xe7ba61ff, 0xe5b552ff, 0xe6b85bff, 0xe7ba5eff, 0xe7ba5fff, 0xe7bb60ff, 0xe8bc63ff, 0xe7bc67ff, 0xe5b85eff, 0xe4b351ff, 0xe5b658ff, 0xe4b24eff, 0xe7b95eff, 0xeac06aff, 0xebc270ff, 0xecc77aff, 0xecc778ff, 0xedc776ff, 0xf0d18bff, 0xeec97cff, 0xeec775ff, 0xf0ce82ff, 0xf7dda0ff, 0xf5d894ff, 0xf7dd98ff, 0xfce6acff, 0xfdeab6ff, 0xfdecbbff, 0xfdebbbff, 0xfdefc4ff, 0xfdecbaff, 0xfdeab3ff, 0xf7dda0ff, 0xe7b860ff, 0xe3b459ff, 0xe4b55dff, 0xe4b660ff, 0xe5b869ff, 0xe5bc6fff, 0xe2b561ff, 0xe4b967ff, 0xe8bf71ff, 0xefcf8dff, 0xf3daa4ff, 0xf2dcabff, 0xf1d8a9ff, 0xeed5a2ff, 0xefd29cff, 0xefd8acff, 0xecd3a4ff, 0xe6c485ff, 0xe9cb93ff, 0xebd0a0ff, 0xe9cf9dff, 0xeacfa2ff, 0xe9cea2ff, 0xe6c492ff, 0xe3c08aff, 0xe2bc85ff, 0xddb677ff, 0xd8aa63ff, 0xd19b49ff, 0xc58939ff, 0xbb7a32ff, 0xcf8920ff, 0xe1a94bff, 0xe7ba6dff, 0xeac17fff, 0xe6bf80ff, 0xdfbc81ff, 0xd9b573ff, 0xd9b776ff, 0xdab980ff, 0xd0ac68ff, 0xc69e51ff, 0xc29d4fff, 0xbd9645ff, 0xb58b3cff, 0xa57930ff, 0x9d7840ff, 0x9c763eff, 0x96661cff, 0x98630fff, 0xa06b12ff, 0xa6751bff, 0xa8781bff, 0xd8ab59ff, 0xd7aa55ff, 0xd5a952ff, 0xd7ac55ff, 0xd9b062ff, 0xd6ad5cff, 0xd7ac5bff, 0xd8ac5aff, 0xd6a952ff, 0xd2a245ff, 0xca9533ff, 0xaf7112ff, 0xa96004ff, 0xba7a11ff, 0xcd9835ff, 0xd3a246ff, 0xd6a345ff, 0xdbaa51ff, 0xddb057ff, 0xe0b459ff, 0xe5bb63ff, 0xeac476ff, 0xefce83ff, 0xf2d892ff, 0xf8e09fff, 0xfae39dff, 0xf6da91ff, 0xebc56fff, 0xdfa63aff, 0xd7971aff, 0xd59413ff, 0xd69411ff, 0xd49410ff, 0xd49411ff, 0xd5940fff, 0xdda225ff, 0xe3b247ff, 0xe5b85cff, 0xe8bc65ff, 0xe7bb61ff, 0xe5b758ff, 0xe7bc63ff, 0xe5b759ff, 0xe3b34bff, 0xe4b554ff, 0xe5b657ff, 0xe6b95fff, 0xe7bd68ff, 0xe6b85bff, 0xe5b658ff, 0xe4b65aff, 0xe1b14aff, 0xe3b252ff, 0xe2b04cff, 0xe4b451ff, 0xe9bc60ff, 0xebc06bff, 0xecc26dff, 0xebc370ff, 0xecc36dff, 0xefcc80ff, 0xf1d38dff, 0xf3d48dff, 0xf5d894ff, 0xf6da96ff, 0xf8dd9aff, 0xfbe5a8ff, 0xfce8aeff, 0xfcebb9ff, 0xfdeec0ff, 0xfdf0bfff, 0xfdf4c8ff, 0xfdf2c8ff, 0xfdefc0ff, 0xfbe6b0ff, 0xefc87cff, 0xe1b151ff, 0xe3b458ff, 0xe4b75fff, 0xe4b760ff, 0xe3b55dff, 0xe2b55fff, 0xe7be70ff, 0xe9c274ff, 0xedcd8bff, 0xf1d497ff, 0xf1d9a2ff, 0xf0d7a3ff, 0xeed199ff, 0xeecf96ff, 0xeccf9aff, 0xe8ca91ff, 0xebcd94ff, 0xecd29eff, 0xeacd98ff, 0xe8ca93ff, 0xe8cc98ff, 0xe8ca97ff, 0xe5c08aff, 0xe6c290ff, 0xe4bf8dff, 0xe0b87fff, 0xdbaf6cff, 0xd4a04fff, 0xd0963eff, 0xc27e2eff, 0xca8420ff, 0xdc9d33ff, 0xe4b055ff, 0xe7ba6dff, 0xe7be74ff, 0xe3bd7bff, 0xdab574ff, 0xdbb87aff, 0xd9b77cff, 0xcea559ff, 0xc29742ff, 0xbe9542ff, 0xb48831ff, 0xaa7720ff, 0xa06e1dff, 0x9e7e4fff, 0x9f835bff, 0x9d7e4cff, 0x976a24ff, 0x955e09ff, 0xa16f19ff, 0xaa7c26ff, 0xd6aa54ff, 0xd4a74dff, 0xd4a646ff, 0xd6a94cff, 0xd7ad58ff, 0xd6ab5aff, 0xd8ad61ff, 0xd4a954ff, 0xd4a64dff, 0xd1a24aff, 0xd0a049ff, 0xc6902bff, 0xac6406ff, 0xb36c08ff, 0xc89029ff, 0xcd9937ff, 0xd1a041ff, 0xd3a13eff, 0xd9a948ff, 0xdcae49ff, 0xe2b65dff, 0xe4ba5eff, 0xe9c064ff, 0xedcc7fff, 0xf1d695ff, 0xf6df9cff, 0xfae6a4ff, 0xfdebb1ff, 0xf9e4aaff, 0xeecc81ff, 0xd8a137ff, 0xcc8a0dff, 0xc78505ff, 0xc68607ff, 0xc8890dff, 0xd29a25ff, 0xdbaa3dff, 0xe0b04dff, 0xe4b85fff, 0xe6bb68ff, 0xe4b65aff, 0xe5b75bff, 0xe5b75cff, 0xe2b248ff, 0xe4b654ff, 0xe5b657ff, 0xe4b453ff, 0xe5b960ff, 0xe4b557ff, 0xe2b351ff, 0xe4b65cff, 0xe4b659ff, 0xe3b251ff, 0xe2b04aff, 0xe2b046ff, 0xe7b959ff, 0xebc171ff, 0xedc779ff, 0xecc571ff, 0xecc369ff, 0xf0cd81ff, 0xf1d38eff, 0xf4d691ff, 0xf6d993ff, 0xf7d992ff, 0xfae2a2ff, 0xfceab5ff, 0xfdeab0ff, 0xfce9acff, 0xfdebb1ff, 0xfdf2bfff, 0xfdf5c8ff, 0xfdf7d1ff, 0xfdf9d6ff, 0xfdf4cfff, 0xf6d99bff, 0xe3b254ff, 0xe4b35aff, 0xe5b75eff, 0xe2b354ff, 0xe2b355ff, 0xe2b357ff, 0xe2b459ff, 0xe4b85eff, 0xeac578ff, 0xeccd8bff, 0xeed197ff, 0xf0d39cff, 0xeccd90ff, 0xe8c683ff, 0xe6c583ff, 0xe8c68cff, 0xebcc94ff, 0xeacb92ff, 0xeacd94ff, 0xe9ca93ff, 0xe8c890ff, 0xe8c790ff, 0xe5c089ff, 0xeac79aff, 0xe7c497ff, 0xe3bb86ff, 0xddb06cff, 0xd9a558ff, 0xd5a051ff, 0xcf9852ff, 0xcf9548ff, 0xd99b35ff, 0xdfa642ff, 0xe5b25bff, 0xe6b964ff, 0xe8c37aff, 0xdfb879ff, 0xd6b06cff, 0xcfa658ff, 0xcb9e4aff, 0xc1963eff, 0xbd9241ff, 0xb2822cff, 0xa56e12ff, 0x9f6b17ff, 0x9e8057ff, 0xa18d6fff, 0xa18a68ff, 0x9f7f52ff, 0x986a22ff, 0x9d6d19ff, 0xac8237ff, 0xd6a84fff, 0xcfa041ff, 0xd4a443ff, 0xd7a94fff, 0xd6a952ff, 0xd6ab59ff, 0xd6ab5bff, 0xd1a449ff, 0xd1a144ff, 0xd5a752ff, 0xd2a553ff, 0xcb9633ff, 0xb4710cff, 0xb56f08ff, 0xc2881cff, 0xc79129ff, 0xc99228ff, 0xcf9b35ff, 0xd8a84bff, 0xdcad51ff, 0xe0b45cff, 0xe4ba63ff, 0xe7c06cff, 0xeac575ff, 0xeecd88ff, 0xf3d9a0ff, 0xf5dea5ff, 0xf7e1a5ff, 0xfae6b0ff, 0xf9e8b4ff, 0xebd38fff, 0xca9938ff, 0xc4661eff, 0xbf6615ff, 0xb7740dff, 0xbb8922ff, 0xc79a3bff, 0xd1a343ff, 0xdaac52ff, 0xdfb360ff, 0xe1b561ff, 0xe1b155ff, 0xe0ae48ff, 0xe0ad3fff, 0xe3b24fff, 0xe3b251ff, 0xe2b14eff, 0xe3b556ff, 0xe2b350ff, 0xe2b452ff, 0xe4b65dff, 0xe3b457ff, 0xe3b252ff, 0xe3b24eff, 0xe5b655ff, 0xe7b95bff, 0xeabf6cff, 0xebc36fff, 0xebc370ff, 0xeec876ff, 0xf1d086ff, 0xf3d58fff, 0xf6db98ff, 0xf9e09fff, 0xf9df96ff, 0xfbe6a5ff, 0xfceab1ff, 0xfceaadff, 0xfdecb1ff, 0xfdf1bdff, 0xfdf4c7ff, 0xfdf7cbff, 0xfdf9d2ff, 0xfdf7cdff, 0xfdf0c1ff, 0xf6d993ff, 0xe5b658ff, 0xe1b154ff, 0xe2b050ff, 0xe0b14aff, 0xe2b556ff, 0xe3b659ff, 0xe1b257ff, 0xe1b355ff, 0xe7bf6bff, 0xe9c77fff, 0xebcb8bff, 0xedcf94ff, 0xeaca8aff, 0xe8c783ff, 0xe9c888ff, 0xe8c68bff, 0xe9c88eff, 0xeccf9bff, 0xecd29fff, 0xe9c991ff, 0xe9cb94ff, 0xe9c995ff, 0xe7c48fff, 0xe9c89aff, 0xe9c89fff, 0xe5be8fff, 0xe0b373ff, 0xdfb170ff, 0xdcad6aff, 0xd4a15cff, 0xd09c56ff, 0xd89c3fff, 0xe1a74fff, 0xe3b05eff, 0xe6b869ff, 0xe8c480ff, 0xe6c17dff, 0xd7ac5fff, 0xcc9c44ff, 0xca9d46ff, 0xc79c48ff, 0xc49d52ff, 0xba8e41ff, 0xa46b10ff, 0x9d6710ff, 0x9d783fff, 0xa58759ff, 0xa38c65ff, 0xa28b68ff, 0x9f8354ff, 0x9f7936ff, 0xa77f35ff, 0xe4bd6aff, 0xd8ab4fff, 0xd2a243ff, 0xd3a341ff, 0xd3a443ff, 0xd2a548ff, 0xd3a64cff, 0xd1a349ff, 0xd0a148ff, 0xcf9e41ff, 0xd1a047ff, 0xcb9733ff, 0xb97811ff, 0xae6704ff, 0xbe8114ff, 0xc58c23ff, 0xc79026ff, 0xcd9a3aff, 0xd4a750ff, 0xd9ac54ff, 0xdbae51ff, 0xe1b966ff, 0xe6c178ff, 0xe8c57bff, 0xeac77eff, 0xeecd8eff, 0xf2d49aff, 0xf3d89aff, 0xf3da99ff, 0xf1d99cff, 0xe6d190ff, 0xd3ba78ff, 0xcd6144ff, 0xcf3f2eff, 0xc9482aff, 0xbe5825ff, 0xb6722dff, 0xb88938ff, 0xc19744ff, 0xcca250ff, 0xd6aa5bff, 0xd8a952ff, 0xdba949ff, 0xdaa838ff, 0xddaa3eff, 0xdfae47ff, 0xe2b04bff, 0xe3b250ff, 0xe4b45aff, 0xe3b456ff, 0xe3b65aff, 0xe4b355ff, 0xe2b250ff, 0xe2b450ff, 0xe4b54fff, 0xe4b44dff, 0xe8bb5eff, 0xeabf62ff, 0xecc267ff, 0xf0ca79ff, 0xf3d389ff, 0xf4d489ff, 0xf6d88fff, 0xfae29dff, 0xfbe6a2ff, 0xfce7a4ff, 0xfce8a6ff, 0xfce8a4ff, 0xfceaa9ff, 0xfdefbaff, 0xfdf0bfff, 0xfdf5c9ff, 0xfdf9d2ff, 0xfdf9d2ff, 0xfdf0c0ff, 0xf1d082ff, 0xdfb04cff, 0xdfaf4cff, 0xe0af4dff, 0xe1b14dff, 0xe4b453ff, 0xe4b455ff, 0xe3b354ff, 0xe6bb67ff, 0xe8bf6eff, 0xe6be6aff, 0xe9c57bff, 0xe9c885ff, 0xe8c884ff, 0xe9c987ff, 0xebca8fff, 0xe9c88eff, 0xeacb90ff, 0xedd19dff, 0xeacd99ff, 0xe7c68cff, 0xe9c790ff, 0xe9c791ff, 0xe8c690ff, 0xe9c798ff, 0xe8c79cff, 0xe7c295ff, 0xe4bc89ff, 0xe7c395ff, 0xe2be8bff, 0xd8aa68ff, 0xcf994eff, 0xd89a40ff, 0xe5af63ff, 0xe6b771ff, 0xe5b86eff, 0xe6be75ff, 0xe7c683ff, 0xdeb66eff, 0xc99439ff, 0xc59236ff, 0xc69643ff, 0xc09140ff, 0xb28028ff, 0xa26f15ff, 0xa37424ff, 0xa77e38ff, 0xaa833fff, 0xa8833dff, 0xa9884dff, 0xa68857ff, 0xa18457ff, 0xa07d44ff, 0xe7c377ff, 0xe2b762ff, 0xd9ab52ff, 0xd7a74cff, 0xd7ab50ff, 0xd4a749ff, 0xd2a447ff, 0xd1a34aff, 0xd1a148ff, 0xd09e40ff, 0xcf9d3fff, 0xce9a38ff, 0xc08620ff, 0xaf6704ff, 0xb77609ff, 0xbd8116ff, 0xc08719ff, 0xc58e25ff, 0xcf9e45ff, 0xd3a54cff, 0xd7a745ff, 0xddaf56ff, 0xe3bc71ff, 0xe6c27dff, 0xe8c378ff, 0xeac578ff, 0xecc572ff, 0xedca7bff, 0xeed18bff, 0xe9cf8dff, 0xd9c284ff, 0xbfaa73ff, 0xc45d42ff, 0xd13e2fff, 0xcc3f30ff, 0xcb3e30ff, 0xd0432fff, 0xc74f30ff, 0xbc6434ff, 0xb87c3aff, 0xbb8f45ff, 0xc29946ff, 0xca9c40ff, 0xd09f3bff, 0xd5a132ff, 0xd8a73cff, 0xddae4cff, 0xe1b152ff, 0xe5b862ff, 0xe4b55aff, 0xe3b353ff, 0xe4b456ff, 0xe3b352ff, 0xe6b95eff, 0xe4b54fff, 0xe4b347ff, 0xe5b54bff, 0xe6ba55ff, 0xecc265ff, 0xefc874ff, 0xf1d184ff, 0xf4d88dff, 0xf7de98ff, 0xfae3a0ff, 0xf8df96ff, 0xfae49eff, 0xfce9abff, 0xfcecb5ff, 0xfcecb2ff, 0xfcedb4ff, 0xfdf1baff, 0xfdf5caff, 0xfdf5cbff, 0xfdefbfff, 0xf8e1a3ff, 0xe7bb5dff, 0xddaa3fff, 0xe2b454ff, 0xe5b75cff, 0xe5b456ff, 0xe6b759ff, 0xe3b151ff, 0xe4b354ff, 0xe9be6aff, 0xe8bd6cff, 0xe5ba64ff, 0xe9c579ff, 0xe8c47eff, 0xe6c27cff, 0xebca8eff, 0xedce96ff, 0xeac98eff, 0xeac989ff, 0xeccc92ff, 0xedcd96ff, 0xeecf99ff, 0xeed3a3ff, 0xebcc9eff, 0xe6c189ff, 0xe7c491ff, 0xe6c191ff, 0xe5bf8eff, 0xe4bc89ff, 0xe5be8dff, 0xe7c598ff, 0xe6c495ff, 0xdfb67fff, 0xdda75cff, 0xe4af62ff, 0xe6bb7bff, 0xe5bb75ff, 0xe5bb70ff, 0xe9ca8fff, 0xe7c88dff, 0xca953dff, 0xb97e1dff, 0xbd872eff, 0xaf7714ff, 0xad7d25ff, 0xb0883eff, 0xb79557ff, 0xb69554ff, 0xb3904dff, 0xb08c41ff, 0xb09148ff, 0xae8b44ff, 0xa88443ff, 0xa78344ff, 0xe7c27cff, 0xe4bb6bff, 0xe5bf76ff, 0xe5c17fff, 0xe2bc72ff, 0xe2ba6eff, 0xdeb363ff, 0xd9ac5bff, 0xd09e41ff, 0xcc9834ff, 0xd1a248ff, 0xcf9e46ff, 0xc48f2bff, 0xad6706ff, 0xb16b04ff, 0xb77609ff, 0xb97b0aff, 0xbd7e0bff, 0xc58c21ff, 0xce9b3dff, 0xd3a241ff, 0xd8a94aff, 0xddb15cff, 0xe4bd72ff, 0xe7be6eff, 0xe8c06dff, 0xe8c06cff, 0xebc269ff, 0xecc877ff, 0xe4c680ff, 0xd1b97dff, 0xb19c6bff, 0xbc5940ff, 0xd33f2fff, 0xa53630ff, 0x492130ff, 0x812e30ff, 0xb63930ff, 0xcf3f30ff, 0xcf4530ff, 0xc45431ff, 0xb86930ff, 0xb47d2dff, 0xb88b2eff, 0xc29530ff, 0xcc9e3dff, 0xd4a445ff, 0xdbac4dff, 0xe0b35bff, 0xe1b355ff, 0xe2b455ff, 0xe4b75bff, 0xe4b659ff, 0xe3b350ff, 0xe3b245ff, 0xe4b448ff, 0xe6b750ff, 0xe6b84eff, 0xe9bd57ff, 0xeec66aff, 0xf1d07eff, 0xf5d990ff, 0xf9e09dff, 0xf9df98ff, 0xf9e197ff, 0xfbe5a1ff, 0xfce8a7ff, 0xfdecb4ff, 0xfdedb3ff, 0xfdeeb0ff, 0xfdedb3ff, 0xfdeeb8ff, 0xfce9adff, 0xf9e19dff, 0xeecc78ff, 0xdcab3eff, 0xdfac42ff, 0xe3b459ff, 0xe3b457ff, 0xe6b757ff, 0xe8ba5eff, 0xe4b353ff, 0xe7b95eff, 0xe8bb66ff, 0xe7bb67ff, 0xe5ba63ff, 0xe6bd69ff, 0xe7be6fff, 0xe6be73ff, 0xeac98cff, 0xebcc93ff, 0xeaca8eff, 0xeccc8fff, 0xefd19bff, 0xf1d6a4ff, 0xf1dbafff, 0xf1dcb8ff, 0xeed4adff, 0xebcb99ff, 0xe9c796ff, 0xe8c799ff, 0xe9c99eff, 0xeacaa1ff, 0xe9c699ff, 0xe3bd85ff, 0xe2bb82ff, 0xe1bb86ff, 0xd9a047ff, 0xdea748ff, 0xe4b871ff, 0xe5bb77ff, 0xe4bd73ff, 0xe5c587ff, 0xe8cb93ff, 0xd9b370ff, 0xb27b1dff, 0xac7b1dff, 0xad812cff, 0xb28b3eff, 0xb7944fff, 0xc1a26cff, 0xc1a369ff, 0xc2a468ff, 0xbf9f59ff, 0xc1a058ff, 0xc6a65fff, 0xc8a45aff, 0xc8a156ff, 0xe3bd6bff, 0xe2b963ff, 0xe4bd72ff, 0xe3be7aff, 0xe5c27fff, 0xe8c688ff, 0xe5c17cff, 0xe6c487ff, 0xddb366ff, 0xcb952cff, 0xcd9936ff, 0xcc9739ff, 0xc38920ff, 0xae6605ff, 0xa95c04ff, 0xb16904ff, 0xb77305ff, 0xba7a07ff, 0xbf8411ff, 0xc79126ff, 0xd09e3dff, 0xd8a850ff, 0xdaa94dff, 0xdfb564ff, 0xe3b868ff, 0xe5bc6bff, 0xe6be6fff, 0xe9c072ff, 0xeac370ff, 0xe1ba68ff, 0xcbaa64ff, 0xa78e57ff, 0xb6543aff, 0xd4402fff, 0x9f3530ff, 0x0a1230ff, 0x021030ff, 0x1d1730ff, 0x532330ff, 0x923130ff, 0xc03b30ff, 0xd14030ff, 0xcc482fff, 0xc0572cff, 0xb76e2fff, 0xb68437ff, 0xbf9441ff, 0xc89e49ff, 0xd3a756ff, 0xdbae5aff, 0xdeb157ff, 0xe1b256ff, 0xe3b75aff, 0xe3b455ff, 0xe2b146ff, 0xe4b347ff, 0xe4b345ff, 0xe5b546ff, 0xe8bc53ff, 0xeec76fff, 0xefcc72ff, 0xf5da8fff, 0xf7dc93ff, 0xfae19aff, 0xfce6a3ff, 0xfce8a4ff, 0xfce7a2ff, 0xfceaadff, 0xfcebaeff, 0xfdedb3ff, 0xfcefbaff, 0xfce7a7ff, 0xfae093ff, 0xf2cf7aff, 0xddaa3cff, 0xdaa42cff, 0xdeab3dff, 0xe1b14dff, 0xe5b658ff, 0xe5b653ff, 0xe7b959ff, 0xe6b654ff, 0xe8bb61ff, 0xebc172ff, 0xe9be6eff, 0xe8bd68ff, 0xe4b75bff, 0xe4b75dff, 0xe8c278ff, 0xe9c382ff, 0xe6c27dff, 0xe7c480ff, 0xeccc8dff, 0xf0d19aff, 0xf1d8a9ff, 0xf1d6abff, 0xefd3abff, 0xeed5adff, 0xebcd99ff, 0xebc999ff, 0xeacca1ff, 0xebcca7ff, 0xeacaa3ff, 0xe9c99cff, 0xe3bc83ff, 0xe4bf87ff, 0xe5c28cff, 0xdaa249ff, 0xdca343ff, 0xe0b05cff, 0xe4bb72ff, 0xe3bb70ff, 0xe5c384ff, 0xe6c88fff, 0xe4c792ff, 0xbc8c3bff, 0xad7f25ff, 0xb18a39ff, 0xb6934aff, 0xba9b55ff, 0xc0a466ff, 0xc5a664ff, 0xcba657ff, 0xd4ae61ff, 0xdcbd77ff, 0xdfc385ff, 0xdfc387ff, 0xe1c48dff, 0xe1b760ff, 0xdfb359ff, 0xe3b969ff, 0xe3bd75ff, 0xe6c58aff, 0xe8c792ff, 0xe3bf7eff, 0xe6c68dff, 0xe4c181ff, 0xdeb25eff, 0xcf9c36ff, 0xc79022ff, 0xc48a18ff, 0xb77309ff, 0xa75b04ff, 0xa35804ff, 0xb16a04ff, 0xb77507ff, 0xbb7b09ff, 0xc0830eff, 0xcf9b3cff, 0xd8a958ff, 0xd8a84cff, 0xdcad57ff, 0xdfb25dff, 0xe1b562ff, 0xe5ba65ff, 0xe7bb64ff, 0xe7bd64ff, 0xe0b866ff, 0xc9a75fff, 0xa4884cff, 0xb25234ff, 0xd44030ff, 0xa33630ff, 0x0c1330ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x071230ff, 0x291a30ff, 0x642730ff, 0xa13530ff, 0xc73d30ff, 0xd14230ff, 0xc94c30ff, 0xbe5f34ff, 0xb67534ff, 0xb98b40ff, 0xc29a48ff, 0xcca24bff, 0xd4a84eff, 0xdaac4fff, 0xddad4aff, 0xdfae43ff, 0xe1b146ff, 0xe4b241ff, 0xe4b440ff, 0xe7ba50ff, 0xedc261ff, 0xefc96cff, 0xf1d07cff, 0xf4d78aff, 0xf7dc90ff, 0xf8e29cff, 0xfbe7a3ff, 0xfce7a1ff, 0xfceaa7ff, 0xfcebaaff, 0xfdf0bbff, 0xfdefbaff, 0xf9e098ff, 0xf1ce76ff, 0xe1b24aff, 0xd8a22bff, 0xdba42dff, 0xdfab39ff, 0xe8b857ff, 0xefc677ff, 0xeec471ff, 0xecc16aff, 0xe9ba59ff, 0xe9ba5aff, 0xe9bd68ff, 0xebc273ff, 0xebc476ff, 0xe9bf6eff, 0xe6b964ff, 0xe9c177ff, 0xe7bf74ff, 0xe9c37eff, 0xebca8bff, 0xebcd8cff, 0xf0d29bff, 0xf0d4a2ff, 0xf0d4a8ff, 0xedcea1ff, 0xedd3a9ff, 0xedd1a2ff, 0xeccc9bff, 0xebcda1ff, 0xeacca5ff, 0xeacba1ff, 0xe9cea1ff, 0xe7c490ff, 0xe7c38eff, 0xe6c48fff, 0xdeae64ff, 0xdda64dff, 0xe0b05aff, 0xe4bd78ff, 0xe5c07eff, 0xead09eff, 0xecd6aaff, 0xe9d0a1ff, 0xcba35eff, 0xb28631ff, 0xb68f3fff, 0xb9984cff, 0xbd9d55ff, 0xc49e4cff, 0xcfa34dff, 0xd9b161ff, 0xe0c282ff, 0xe1ca93ff, 0xe3ca96ff, 0xe2cb99ff, 0xe2cd9eff, 0xe3b862ff, 0xe2b75cff, 0xe1b762ff, 0xdfb55fff, 0xe3bc73ff, 0xe5c180ff, 0xdfb768ff, 0xe1bc77ff, 0xe0b970ff, 0xdfb76dff, 0xdcb264ff, 0xd2a042ff, 0xc89125ff, 0xbe8111ff, 0xa75f05ff, 0x9a4c04ff, 0xae6504ff, 0xb46f04ff, 0xb67305ff, 0xbe800eff, 0xcd9b3dff, 0xd6a959ff, 0xdaac5cff, 0xd9a94fff, 0xdaa847ff, 0xddad51ff, 0xe3b761ff, 0xe6bd68ff, 0xe6bc63ff, 0xdfb665ff, 0xc8a458ff, 0xa28547ff, 0xb05335ff, 0xd44030ff, 0xa73730ff, 0x0f1330ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x001030ff, 0x0d1330ff, 0x371d30ff, 0x762b30ff, 0xae3730ff, 0xcc3e30ff, 0xd0442fff, 0xc65031ff, 0xbb6531ff, 0xb77d35ff, 0xba8e38ff, 0xc39a3fff, 0xcca03eff, 0xd3a337ff, 0xd9a83bff, 0xdfae40ff, 0xe2b242ff, 0xe6b84bff, 0xedc364ff, 0xefc667ff, 0xf1ce77ff, 0xf4d88aff, 0xf8df96ff, 0xf7dd90ff, 0xf9e3a0ff, 0xfcedb8ff, 0xfcefbaff, 0xfdf2c2ff, 0xfdf2c5ff, 0xfceaaeff, 0xf8df98ff, 0xdfb14cff, 0xd8a22bff, 0xdaa52dff, 0xdea933ff, 0xe8b953ff, 0xf0c771ff, 0xf1cc7eff, 0xf1ce83ff, 0xf1ce85ff, 0xedc269ff, 0xe8b855ff, 0xe9bb5eff, 0xebc16cff, 0xecc473ff, 0xecc680ff, 0xeac277ff, 0xe9be6eff, 0xe9c071ff, 0xebc47dff, 0xe9c57dff, 0xebc987ff, 0xedcd96ff, 0xedd19eff, 0xedcf9dff, 0xeccc9aff, 0xedcd9eff, 0xebcb9cff, 0xe7c48dff, 0xe7c38fff, 0xe8c594ff, 0xe9c897ff, 0xebcd9dff, 0xebcd9aff, 0xeac995ff, 0xe6c690ff, 0xddac5eff, 0xdfab57ff, 0xe4bd7cff, 0xe6c58dff, 0xe1bd7fff, 0xe2c285ff, 0xead6acff, 0xead6acff, 0xd2ad6cff, 0xb88e3eff, 0xba9445ff, 0xbd9a4cff, 0xc8a049ff, 0xd3a240ff, 0xd8ad5aff, 0xddbb75ff, 0xe0c48cff, 0xe0c897ff, 0xe0cb9aff, 0xe1cda0ff, 0xe0ca9eff, 0xe6bb68ff, 0xe3b964ff, 0xe0b459ff, 0xdfb050ff, 0xe4bb6dff, 0xe4bd73ff, 0xe4ba6bff, 0xe0b665ff, 0xddb25dff, 0xdeb667ff, 0xe1bc7aff, 0xdcb264ff, 0xd29f41ff, 0xc89029ff, 0xa8630eff, 0x954504ff, 0xab5f04ff, 0xaf6905ff, 0xb57104ff, 0xbb7a07ff, 0xc6902aff, 0xd1a24dff, 0xd7ab5cff, 0xd8ab55ff, 0xdbaa4bff, 0xddad51ff, 0xe0b564ff, 0xe2b766ff, 0xe5ba64ff, 0xddb563ff, 0xc7a254ff, 0xa08240ff, 0xad5233ff, 0xd34030ff, 0xab3730ff, 0x111430ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x011030ff, 0x161530ff, 0x472030ff, 0x872f30ff, 0xba3a30ff, 0xd04030ff, 0xce4630ff, 0xc3552fff, 0xb86b2fff, 0xb67f2aff, 0xbc9030ff, 0xc69a36ff, 0xd0a33bff, 0xd9aa40ff, 0xe0b24bff, 0xeac065ff, 0xefc972ff, 0xf0ca72ff, 0xf2d280ff, 0xf8e19bff, 0xfae49fff, 0xfbe7a8ff, 0xfdedb9ff, 0xfcedb8ff, 0xfcedb8ff, 0xfbe6aaff, 0xf8dd93ff, 0xe8c46cff, 0xd7a129ff, 0xdaa329ff, 0xdea832ff, 0xe6b64bff, 0xeec366ff, 0xf0cc7dff, 0xf2d087ff, 0xf3d38cff, 0xf1cf8aff, 0xedc26dff, 0xe9b959ff, 0xedc16bff, 0xecc16eff, 0xeabe66ff, 0xecc575ff, 0xedc880ff, 0xecc174ff, 0xebc57dff, 0xe8c075ff, 0xe8c174ff, 0xe7c37aff, 0xe7c483ff, 0xe9c68eff, 0xe8c890ff, 0xe9c892ff, 0xe8c58fff, 0xe7c48eff, 0xe6c289ff, 0xe9c995ff, 0xeaca98ff, 0xe9c790ff, 0xeaca96ff, 0xeacd9aff, 0xead09eff, 0xe8ca98ff, 0xddac61ff, 0xe0b369ff, 0xe5c187ff, 0xe0bc81ff, 0xdbb56fff, 0xddb972ff, 0xe7cfa0ff, 0xe7cf9fff, 0xd6b16fff, 0xb88e3fff, 0xb48d3bff, 0xc39b44ff, 0xd6ab4eff, 0xdcb35cff, 0xe1bf80ff, 0xe2c68fff, 0xdfc28aff, 0xe0c694ff, 0xdec792ff, 0xdfc895ff, 0xe1c896ff, 0xe5b960ff, 0xe5bc69ff, 0xe4b960ff, 0xe4b85eff, 0xe6bd71ff, 0xe5bf76ff, 0xe7c582ff, 0xe5c17aff, 0xdfb466ff, 0xd9aa55ff, 0xdbae60ff, 0xddb165ff, 0xdaad5aff, 0xd8aa55ff, 0xc48e32ff, 0x8f4308ff, 0xa45704ff, 0xae6605ff, 0xb26c04ff, 0xb97907ff, 0xc08517ff, 0xca973bff, 0xd3a451ff, 0xd5a74dff, 0xdbaf59ff, 0xdcae57ff, 0xdbae54ff, 0xddb15aff, 0xe1b55eff, 0xdab05dff, 0xc49c4aff, 0x9f7f3bff, 0xab5331ff, 0xd34030ff, 0xae3830ff, 0x131430ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x041130ff, 0x211830ff, 0x582430ff, 0x973230ff, 0xc23c30ff, 0xd14130ff, 0xcb492dff, 0xc05a2cff, 0xb8722fff, 0xb98831ff, 0xc29839ff, 0xcea547ff, 0xdab154ff, 0xe3bc5fff, 0xeac66eff, 0xf1d183ff, 0xf3d98dff, 0xf8e19aff, 0xfbe6a7ff, 0xfae7a8ff, 0xf9e4a0ff, 0xf8de95ff, 0xf6d582ff, 0xebc467ff, 0xdaa537ff, 0xd9a127ff, 0xdda52bff, 0xe4b445ff, 0xeabc57ff, 0xefc66cff, 0xf0cb7bff, 0xf2d086ff, 0xf3d48dff, 0xf2d494ff, 0xf0ca80ff, 0xecbe62ff, 0xecbf65ff, 0xecbf6aff, 0xeabd65ff, 0xecc370ff, 0xecc475ff, 0xecc576ff, 0xecc880ff, 0xebc784ff, 0xe8c17aff, 0xe7c077ff, 0xe6bf78ff, 0xe5bd73ff, 0xe4be77ff, 0xe3bd78ff, 0xe2bd77ff, 0xe2bc7aff, 0xe6c38aff, 0xe9c894ff, 0xeacb9aff, 0xeac793ff, 0xe7c893ff, 0xe8c793ff, 0xeace9cff, 0xe9cd9fff, 0xdfb36dff, 0xe0b36bff, 0xe5c188ff, 0xe5c28cff, 0xe3c189ff, 0xe4c68dff, 0xe7cb96ff, 0xe6cb91ff, 0xdab775ff, 0xbd9242ff, 0xbc903aff, 0xd7ae5aff, 0xe3bf79ff, 0xe3c48aff, 0xe3c99aff, 0xe2c89aff, 0xe0c388ff, 0xe1c895ff, 0xdcc28bff, 0xdec187ff, 0xdcbe7cff, 0xe8bf70ff, 0xe7c278ff, 0xe4bb67ff, 0xe5bd6fff, 0xe7c278ff, 0xe9c785ff, 0xe8c586ff, 0xe6c383ff, 0xe3bd78ff, 0xe0b873ff, 0xd39f49ff, 0xd3a14eff, 0xdbb167ff, 0xdcb46cff, 0xd8ad5aff, 0xac7027ff, 0x9f5104ff, 0xad6304ff, 0xb06c04ff, 0xb67305ff, 0xbc7c0aff, 0xc58d27ff, 0xce9a3eff, 0xd1a242ff, 0xd6a951ff, 0xdaac56ff, 0xd9a849ff, 0xdbad53ff, 0xddb057ff, 0xd6a84dff, 0xc2963cff, 0x9c7b31ff, 0xa8522cff, 0xd34030ff, 0xb23930ff, 0x151530ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x091230ff, 0x2e1b30ff, 0x6a2830ff, 0xa63630ff, 0xc93d30ff, 0xd14330ff, 0xc94e30ff, 0xbf6332ff, 0xba7f3aff, 0xc09745ff, 0xcba84eff, 0xd9b761ff, 0xe6cd8aff, 0xecd48cff, 0xf1d88eff, 0xf5dd98ff, 0xfae6aaff, 0xf8e19dff, 0xf4d480ff, 0xeac160ff, 0xdeb358ff, 0xdaa73cff, 0xdda730ff, 0xe2ad35ff, 0xe6b74aff, 0xebbd5aff, 0xefc770ff, 0xf0ce83ff, 0xf3d592ff, 0xf4d794ff, 0xf2d393ff, 0xefc87cff, 0xedc166ff, 0xebbd61ff, 0xedc26cff, 0xefc673ff, 0xeec879ff, 0xedc470ff, 0xeec779ff, 0xedc983ff, 0xecc986ff, 0xeac782ff, 0xe8c47eff, 0xe8c27bff, 0xe5be75ff, 0xe6c27fff, 0xe2b86dff, 0xe3b76bff, 0xe5c07fff, 0xe8c58bff, 0xebc995ff, 0xebca9aff, 0xe9c894ff, 0xe5c28bff, 0xe6c28cff, 0xe6c895ff, 0xe5c694ff, 0xdcaf64ff, 0xdeb064ff, 0xe3be83ff, 0xe4c28aff, 0xe6c892ff, 0xe5c890ff, 0xe6c88cff, 0xe8cc90ff, 0xdfbd79ff, 0xc59745ff, 0xd0a756ff, 0xe5c485ff, 0xe7c999ff, 0xe7cfa5ff, 0xe5cea1ff, 0xe0c48aff, 0xe2c68aff, 0xe0c793ff, 0xddc392ff, 0xdcc18cff, 0xdec283ff, 0xe7c37cff, 0xe6bf70ff, 0xe6bd68ff, 0xe6bb68ff, 0xe8c277ff, 0xe9c785ff, 0xe7c587ff, 0xe4c180ff, 0xe1b96fff, 0xe2bb78ff, 0xdaae62ff, 0xd29b40ff, 0xdaae62ff, 0xdab26bff, 0xdab164ff, 0xd2a450ff, 0xa96410ff, 0xa95f04ff, 0xad6604ff, 0xb26c04ff, 0xb87706ff, 0xc48b24ff, 0xce993aff, 0xd1a243ff, 0xd3a54bff, 0xd6a547ff, 0xd7a642ff, 0xd7a63eff, 0xd8a843ff, 0xd4a344ff, 0xc0943cff, 0x9b792dff, 0xa55029ff, 0xd34030ff, 0xb63a30ff, 0x181530ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x001030ff, 0x101430ff, 0x3c1e30ff, 0x7b2c30ff, 0xb23830ff, 0xce3f30ff, 0xd04530ff, 0xc85636ff, 0xc0713eff, 0xc18f4bff, 0xc9ac67ff, 0xd7c282ff, 0xe1cb87ff, 0xecd89eff, 0xf2dfadff, 0xefd286ff, 0xeeca70ff, 0xdcab43ff, 0xd59d28ff, 0xdba431ff, 0xe0ab35ff, 0xe3b242ff, 0xe6b74dff, 0xeabe5bff, 0xeec56cff, 0xf0d18bff, 0xf1d38eff, 0xf4d798ff, 0xf4d898ff, 0xf1ce87ff, 0xf1ca78ff, 0xf2cb78ff, 0xf2cf83ff, 0xf1cb80ff, 0xf0ca7aff, 0xefc979ff, 0xefcb80ff, 0xedc882ff, 0xecc782ff, 0xedca88ff, 0xedcb88ff, 0xefd098ff, 0xeed09cff, 0xebca92ff, 0xe4ba71ff, 0xe3b96eff, 0xe7c382ff, 0xeccc93ff, 0xebcc93ff, 0xe7c68bff, 0xe2bb79ff, 0xe8c48fff, 0xe8c894ff, 0xe3c288ff, 0xdfbb7bff, 0xd9a650ff, 0xdcaa50ff, 0xe0b871ff, 0xe0bd7cff, 0xe3c388ff, 0xdfbf7eff, 0xe3c684ff, 0xe4c684ff, 0xd9b061ff, 0xcd9b3fff, 0xdfb771ff, 0xe5c48bff, 0xe5c896ff, 0xe7cfa3ff, 0xe5ca94ff, 0xe1c383ff, 0xe4ca95ff, 0xe0c68fff, 0xd9b97dff, 0xdbbc81ff, 0xdebf86ff, 0xe4b964ff, 0xe4b861ff, 0xe7c06fff, 0xe5be6bff, 0xe6bf71ff, 0xe6c17bff, 0xe5c383ff, 0xe2bc74ff, 0xe1b86dff, 0xe0b870ff, 0xdeb66cff, 0xdbaf5aff, 0xdaaf60ff, 0xd9b167ff, 0xd9ad63ff, 0xd6ab5bff, 0xbe872dff, 0xa85f05ff, 0xab6304ff, 0xaf6904ff, 0xb46f04ff, 0xc0851eff, 0xcc9a3bff, 0xd2a64eff, 0xd3a652ff, 0xd5a751ff, 0xd5a543ff, 0xd7a43aff, 0xd9a743ff, 0xd2a242ff, 0xbf933eff, 0x9a782dff, 0xa35127ff, 0xd24030ff, 0xb93b30ff, 0x1b1630ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x021030ff, 0x191630ff, 0x4d2230ff, 0x8c3030ff, 0xbd3a30ff, 0xd04030ff, 0xcf4933ff, 0xc65f3cff, 0xc5855cff, 0xc8a571ff, 0xd0bb84ff, 0xdac68aff, 0xe0c171ff, 0xdbb053ff, 0xd0992aff, 0xd49e2aff, 0xdaa42fff, 0xe0ab34ff, 0xe2b344ff, 0xe6b953ff, 0xe9bc59ff, 0xecc369ff, 0xeecc80ff, 0xf0d18eff, 0xefcf8dff, 0xf2d393ff, 0xf0cf86ff, 0xf2ce7dff, 0xf4d187ff, 0xf1ce83ff, 0xf1c875ff, 0xf0cb7aff, 0xefc87cff, 0xeecc85ff, 0xefd08eff, 0xf1d49aff, 0xf4daa2ff, 0xf3d79fff, 0xf0d49dff, 0xeed199ff, 0xeac684ff, 0xe9c583ff, 0xe8c381ff, 0xeaca8bff, 0xebc98bff, 0xe9c686ff, 0xe5c07cff, 0xe1b66cff, 0xe7c48eff, 0xe7c895ff, 0xe3c084ff, 0xdcb36aff, 0xd8a343ff, 0xdeaf5cff, 0xe0b976ff, 0xdeb566ff, 0xdfbc70ff, 0xe0bf77ff, 0xe2c27eff, 0xddba70ff, 0xd2a146ff, 0xd39c35ff, 0xdaa854ff, 0xe7c489ff, 0xe1c288ff, 0xe4c893ff, 0xe5ca91ff, 0xe5c98cff, 0xe2c487ff, 0xddbf83ff, 0xdfc288ff, 0xe1c590ff, 0xdfbe80ff, 0xe4b75eff, 0xe5ba65ff, 0xe5be6dff, 0xe6be6cff, 0xe8c788ff, 0xe7c487ff, 0xe5c282ff, 0xe1ba70ff, 0xdfb565ff, 0xddb260ff, 0xddb464ff, 0xddb466ff, 0xdbb368ff, 0xdab26aff, 0xd9af66ff, 0xd5aa5cff, 0xca9a3cff, 0xac680aff, 0xa96104ff, 0xae6804ff, 0xb16d04ff, 0xbd831cff, 0xc89432ff, 0xd0a24aff, 0xd6ab5fff, 0xd2a557ff, 0xd1a248ff, 0xd7a64aff, 0xd8a84aff, 0xd0a141ff, 0xbd9139ff, 0x9b782eff, 0xa15127ff, 0xd14030ff, 0xbb3b30ff, 0x1e1730ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x051130ff, 0x251830ff, 0x5e2630ff, 0x9c3330ff, 0xc53c2fff, 0xd14231ff, 0xcd5038ff, 0xc56b49ff, 0xc18855ff, 0xbf974dff, 0xbb9139ff, 0xbf8d23ff, 0xcc992eff, 0xd39d27ff, 0xdcaa3aff, 0xe2b34cff, 0xe5b653ff, 0xe8bb5aff, 0xedc66fff, 0xedcb7fff, 0xefce87ff, 0xf1d18cff, 0xf0cd85ff, 0xf1cc7eff, 0xf3cc76ff, 0xf2cc79ff, 0xf2ce7fff, 0xf2cc7bff, 0xf1cd7eff, 0xf1ce83ff, 0xf3d593ff, 0xf5daa0ff, 0xe3ba76ff, 0xeac584ff, 0xf5dba3ff, 0xf3d79eff, 0xefcf8eff, 0xf2d7a0ff, 0xf3dcabff, 0xf0d5a2ff, 0xefd39cff, 0xeccc8cff, 0xefd7a8ff, 0xeaca91ff, 0xe3ba70ff, 0xe7c48aff, 0xe6c58cff, 0xe0ba75ff, 0xd7a952ff, 0xd7a242ff, 0xdcac58ff, 0xdeb266ff, 0xd7a744ff, 0xd9b04dff, 0xe1be70ff, 0xe4c687ff, 0xdab36bff, 0xd49c3eff, 0xdaa23bff, 0xdfb056ff, 0xe7c485ff, 0xe9c993ff, 0xe6c78cff, 0xe4c68bff, 0xe5c88eff, 0xe4c68dff, 0xd8b879ff, 0xd8b776ff, 0xd9b879ff, 0xdbbb7aff, 0xe6c072ff, 0xe6c279ff, 0xe4ba67ff, 0xe5bf72ff, 0xe5c383ff, 0xe2bc73ff, 0xe1b969ff, 0xe2bc72ff, 0xe0b769ff, 0xdbaf56ff, 0xdaad55ff, 0xdeb466ff, 0xdeb670ff, 0xdab066ff, 0xd6ac5eff, 0xd4a959ff, 0xc79435ff, 0xa7610cff, 0xa55a04ff, 0xac6304ff, 0xb16d04ff, 0xb7790dff, 0xc38c23ff, 0xcd9c3eff, 0xd5aa5aff, 0xd3a857ff, 0xcf9f47ff, 0xd3a54eff, 0xd8a74aff, 0xd1a13fff, 0xbe923cff, 0x9b7830ff, 0x9f5127ff, 0xd14030ff, 0xbd3c30ff, 0x221830ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x000f30ff, 0x0b1230ff, 0x321c30ff, 0x6f2a30ff, 0xaa362fff, 0xcb3d2fff, 0xd14331ff, 0xc64e30ff, 0xba5f2cff, 0xb4762dff, 0xb68628ff, 0xbf9326ff, 0xcda139ff, 0xd5a945ff, 0xdeb254ff, 0xe5bb62ff, 0xe9c066ff, 0xecc878ff, 0xf0cf86ff, 0xf2d38eff, 0xf1d188ff, 0xf2d288ff, 0xf4d080ff, 0xf5d181ff, 0xf3ce7fff, 0xf1cb79ff, 0xf1cb7aff, 0xf7dd9bff, 0xf8e1aaff, 0xf7dea6ff, 0xedcb88ff, 0xedcc89ff, 0xf3d99eff, 0xf7dfa9ff, 0xf7e0a9ff, 0xf5deacff, 0xf2dbabff, 0xf4ddadff, 0xf1d79cff, 0xedcf8dff, 0xeed29bff, 0xecd19cff, 0xe8c78aff, 0xe7c489ff, 0xe4c184ff, 0xdfbb75ff, 0xd6a74eff, 0xd5a244ff, 0xd8a850ff, 0xdbac56ff, 0xdaac4cff, 0xdfb865ff, 0xe5c784ff, 0xe9cf97ff, 0xe0bb7dff, 0xdba751ff, 0xe2b15dff, 0xe5bd6dff, 0xecca8dff, 0xefd19cff, 0xeacc93ff, 0xe1c283ff, 0xdfc080ff, 0xdfc081ff, 0xdebf7eff, 0xdab976ff, 0xdab875ff, 0xdcbb77ff, 0xe5be72ff, 0xe5be71ff, 0xe3ba65ff, 0xe4be72ff, 0xe2bb70ff, 0xe0b664ff, 0xdfb561ff, 0xdfb564ff, 0xdcaf58ff, 0xdcaf57ff, 0xe0ba74ff, 0xe1bc7dff, 0xdeb670ff, 0xdab064ff, 0xd8ad62ff, 0xd3a855ff, 0xce9d40ff, 0xc58d2eff, 0xb17012ff, 0xa96205ff, 0xaf6c05ff, 0xb27104ff, 0xbe8315ff, 0xcc9a3aff, 0xd1a34bff, 0xd3a653ff, 0xd0a34fff, 0xd1a452ff, 0xd2a348ff, 0xd0a043ff, 0xbd8f37ff, 0x997421ff, 0x9c5122ff, 0xd04030ff, 0xbf3c30ff, 0x261930ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x011030ff, 0x121430ff, 0x411f30ff, 0x812d30ff, 0xb63930ff, 0xcf3f30ff, 0xcf4530ff, 0xc4522cff, 0xb96829ff, 0xb7822eff, 0xbe953bff, 0xcba54eff, 0xd7b05dff, 0xe1bd6dff, 0xe7c77fff, 0xebcc85ff, 0xefcd85ff, 0xf0ca78ff, 0xf0ca76ff, 0xf4d080ff, 0xf1ce80ff, 0xf1ca77ff, 0xf1cb75ff, 0xf1cc78ff, 0xf5da94ff, 0xf8e2a4ff, 0xf8e2a6ff, 0xf6dea5ff, 0xf5dda4ff, 0xf5dda6ff, 0xf5dfaaff, 0xf5dfacff, 0xf5e0aeff, 0xf4dfaeff, 0xf4e0b0ff, 0xf4dfaaff, 0xf2d99eff, 0xf2dca8ff, 0xf2dfb5ff, 0xefdbb0ff, 0xeccf9eff, 0xeacc9aff, 0xe2be7eff, 0xd8a852ff, 0xd19d37ff, 0xd5a341ff, 0xdaa84bff, 0xdbad50ff, 0xe2bf71ff, 0xe7c889ff, 0xebd39dff, 0xe3c07cff, 0xdda74dff, 0xe2b25dff, 0xe7c27eff, 0xeacb8fff, 0xefd29fff, 0xedd09bff, 0xe1bf79ff, 0xddbc76ff, 0xe3c584ff, 0xe5c88bff, 0xe4c78dff, 0xdfbe7bff, 0xdcb86eff, 0xe5bd70ff, 0xe2b966ff, 0xe2b660ff, 0xe2b869ff, 0xe1b566ff, 0xdfb462ff, 0xe1b769ff, 0xddae56ff, 0xdaab50ff, 0xdeb568ff, 0xdfbc79ff, 0xddb872ff, 0xdab265ff, 0xd8af5fff, 0xdab46dff, 0xd6ad5fff, 0xcd9e43ff, 0xcc9839ff, 0xc38d21ff, 0xb2700cff, 0xaa6304ff, 0xb06c04ff, 0xb87a0cff, 0xc79530ff, 0xcea042ff, 0xd2a755ff, 0xd1a558ff, 0xd3a85dff, 0xd0a24dff, 0xcc9d41ff, 0xba8c2cff, 0x997320ff, 0x9a5122ff, 0xd0402fff, 0xc23d30ff, 0x2a1a30ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x031030ff, 0x1c1730ff, 0x522330ff, 0x913130ff, 0xc03b30ff, 0xd14030ff, 0xcd492fff, 0xc35c33ff, 0xbd783fff, 0xc19552ff, 0xc9a962ff, 0xd6ba78ff, 0xe0c381ff, 0xe9cc85ff, 0xedc977ff, 0xf1cb75ff, 0xf3cd7bff, 0xf2ce7dff, 0xf4ce7cff, 0xf4cf7cff, 0xf4d183ff, 0xf9e099ff, 0xfbe9aaff, 0xfbe9acff, 0xfae7aeff, 0xf8e2a9ff, 0xf3d89cff, 0xf6dfa8ff, 0xf6e1b1ff, 0xf4dfadff, 0xf2dda8ff, 0xf1dba4ff, 0xf4e0abff, 0xf5e2b2ff, 0xf5e6beff, 0xf3e3c4ff, 0xf2e1bfff, 0xeed7aeff, 0xedd7afff, 0xe2be82ff, 0xd39f42ff, 0xd19b39ff, 0xd2a042ff, 0xdbae54ff, 0xe0b868ff, 0xe4c47fff, 0xe4c47fff, 0xe5c784ff, 0xe3bc6fff, 0xe1b056ff, 0xe4ba6cff, 0xeccc90ff, 0xedcd93ff, 0xeacb93ff, 0xe8c98fff, 0xe6c684ff, 0xe6c684ff, 0xe7c88bff, 0xe4c887ff, 0xe1c381ff, 0xdeba6fff, 0xdeb96dff, 0xe5bc6fff, 0xe2b96aff, 0xe3b868ff, 0xe2b868ff, 0xe1b562ff, 0xe0b665ff, 0xe0b665ff, 0xe0b669ff, 0xdfb975ff, 0xddb871ff, 0xddbb75ff, 0xdcb770ff, 0xdab366ff, 0xd8af60ff, 0xdab269ff, 0xd9b068ff, 0xd2a653ff, 0xcb9838ff, 0xc48e21ff, 0xb97e10ff, 0xa86005ff, 0xab6304ff, 0xb57508ff, 0xc28c23ff, 0xcd9d3fff, 0xd2a856ff, 0xd5ac63ff, 0xd4aa61ff, 0xd0a354ff, 0xc89a40ff, 0xba8d30ff, 0x9a7320ff, 0x97501dff, 0xcf402fff, 0xc43d30ff, 0x2d1a30ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x071130ff, 0x291930ff, 0x632730ff, 0xa03430ff, 0xc73d30ff, 0xd14230ff, 0xcc4f36ff, 0xc46a47ff, 0xc28c5bff, 0xc8a86eff, 0xd4bb7bff, 0xddc078ff, 0xe6c77eff, 0xecca7eff, 0xf0ce83ff, 0xf3cf81ff, 0xf5d17dff, 0xf9dc91ff, 0xfbe4a0ff, 0xfbe9abff, 0xfdf2c3ff, 0xfdeebeff, 0xfceab9ff, 0xf9e4b0ff, 0xf5dfa8ff, 0xf3dfabff, 0xf1daa1ff, 0xf0d499ff, 0xf1d9a0ff, 0xf7e7beff, 0xf5e5bdff, 0xf4e3beff, 0xf2e3c4ff, 0xf0dfbbff, 0xeedab0ff, 0xedd8b0ff, 0xe2c085ff, 0xd6a451ff, 0xd5a249ff, 0xd8a74dff, 0xdfb569ff, 0xe6c58aff, 0xe4c17dff, 0xe1b969ff, 0xe0ba6aff, 0xe5ba67ff, 0xe3b864ff, 0xe6c07bff, 0xe7c589ff, 0xe7c889ff, 0xe4c280ff, 0xe0be75ff, 0xe1be76ff, 0xe4c37dff, 0xe8c885ff, 0xe7c782ff, 0xe5c681ff, 0xdeba70ff, 0xe1bc72ff, 0xe5bf78ff, 0xe1b561ff, 0xdfb25aff, 0xe2b869ff, 0xe0b668ff, 0xdeb25bff, 0xdcaf55ff, 0xe2bd7dff, 0xe0bc7fff, 0xdcb366ff, 0xdcb266ff, 0xdcb46bff, 0xdab36dff, 0xd8b067ff, 0xd7af68ff, 0xd4ac5fff, 0xd2a750ff, 0xca9835ff, 0xc58f26ff, 0xbe8718ff, 0xab6506ff, 0xa86004ff, 0xb16f05ff, 0xbd831aff, 0xcb993cff, 0xcea047ff, 0xd3a858ff, 0xd2a654ff, 0xcea24eff, 0xc79841ff, 0xba8c31ff, 0x9a7421ff, 0x96521fff, 0xcf4030ff, 0xc63e30ff, 0x311b30ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x001030ff, 0x0d1330ff, 0x371d30ff, 0x752b30ff, 0xae3730ff, 0xcc3e30ff, 0xd14633ff, 0xcb583dff, 0xc67751ff, 0xc5965fff, 0xceaf72ff, 0xd8be7dff, 0xe0c37dff, 0xeacb82ff, 0xf1d68dff, 0xf6de97ff, 0xf9e3a1ff, 0xfbe8acff, 0xfcf0c3ff, 0xfcefc0ff, 0xfceebfff, 0xfcefc3ff, 0xf7e4b2ff, 0xefd292ff, 0xefd596ff, 0xf3dca6ff, 0xf5e2b2ff, 0xf6e8c1ff, 0xf7e7c4ff, 0xf6e8c8ff, 0xf4e7c8ff, 0xf2e2beff, 0xeeddb4ff, 0xe6cb9bff, 0xd8ad68ff, 0xd8aa63ff, 0xdcb068ff, 0xddb365ff, 0xdeb86cff, 0xe0bb76ff, 0xe2c181ff, 0xe1bd75ff, 0xdfb462ff, 0xe4b869ff, 0xe5bf75ff, 0xe5c179ff, 0xe1bb73ff, 0xdeb96dff, 0xe0b968ff, 0xe4bf71ff, 0xe4bf72ff, 0xe5c27cff, 0xe3c075ff, 0xe6c278ff, 0xe7c680ff, 0xe1bc6fff, 0xdeb663ff, 0xe4c07cff, 0xe1b768ff, 0xe0b15cff, 0xe2b870ff, 0xdfb464ff, 0xdeb364ff, 0xdeb56fff, 0xe0bc83ff, 0xe1bb80ff, 0xdbb060ff, 0xd9af5eff, 0xdab065ff, 0xd7ad60ff, 0xd5ab5fff, 0xd3a85cff, 0xd4ab5eff, 0xd0a34bff, 0xc99839ff, 0xc5912aff, 0xbe8618ff, 0xae6b05ff, 0xa75e05ff, 0xad6903ff, 0xb87c12ff, 0xc89534ff, 0xd0a249ff, 0xd3a853ff, 0xd2a752ff, 0xd2a756ff, 0xc99c4cff, 0xb88c3aff, 0x9a7525ff, 0x93521eff, 0xcf4231ff, 0xc94131ff, 0x351d30ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x011030ff, 0x151530ff, 0x472130ff, 0x863030ff, 0xba3b30ff, 0xd04231ff, 0xd14c36ff, 0xcb6345ff, 0xc7845aff, 0xc8a16aff, 0xd2b876ff, 0xdfcb88ff, 0xe8d48fff, 0xefdb9eff, 0xf6e3afff, 0xf8e6b2ff, 0xfae9b9ff, 0xfceab8ff, 0xfae9b6ff, 0xf8e4afff, 0xf3d89aff, 0xf6e1afff, 0xf7e6bbff, 0xf8ebc9ff, 0xf7e8c3ff, 0xf7eacbff, 0xf6e8cbff, 0xf8ebd2ff, 0xf5e7c8ff, 0xefdbb1ff, 0xdfbb7fff, 0xd2a151ff, 0xdab16fff, 0xdfba80ff, 0xdfbb7dff, 0xdebb77ff, 0xdaaf5bff, 0xdcb15bff, 0xddb562ff, 0xe2bc70ff, 0xe8c381ff, 0xe7c17fff, 0xe6c27dff, 0xe3bb6cff, 0xe1b965ff, 0xe3bc6aff, 0xe7c57eff, 0xe8c67cff, 0xeac881ff, 0xebcd87ff, 0xe7c679ff, 0xe6bf6eff, 0xdfb35bff, 0xd8a948ff, 0xdeaf55ff, 0xdfb057ff, 0xe2b86bff, 0xe1bb79ff, 0xe0b66aff, 0xdeb369ff, 0xdbb16bff, 0xdcb473ff, 0xdab16bff, 0xd8ac5aff, 0xd8ac5aff, 0xd8ad60ff, 0xd5aa5aff, 0xd4a856ff, 0xd2a552ff, 0xd1a653ff, 0xce9f45ff, 0xc7932eff, 0xc5912aff, 0xbf881dff, 0xb87b0bff, 0xad6805ff, 0xa75d04ff, 0xb3720bff, 0xc38e29ff, 0xcc9e44ff, 0xd4a959ff, 0xd4aa57ff, 0xd5aa5aff, 0xcb9f4fff, 0xb78a3aff, 0x9b762dff, 0x935728ff, 0xcf4531ff, 0xcc4432ff, 0x391e31ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x041130ff, 0x201830ff, 0x582631ff, 0x973531ff, 0xc34031ff, 0xd24633ff, 0xd0543bff, 0xca7151ff, 0xc89467ff, 0xcdb17bff, 0xd7c592ff, 0xe2d5a9ff, 0xebdbadff, 0xf0deadff, 0xf5e3b3ff, 0xf8e7b4ff, 0xf9e9b8ff, 0xf8e5b1ff, 0xfbedc8ff, 0xfbeed1ff, 0xf5e8c8ff, 0xf5e6c5ff, 0xf5e8ccff, 0xf7ead1ff, 0xf7ecd4ff, 0xf6e8caff, 0xf2e0baff, 0xe3c38dff, 0xd2a152ff, 0xdbb16aff, 0xd9b066ff, 0xdbb468ff, 0xddb66cff, 0xdbb25eff, 0xdaad51ff, 0xddb35eff, 0xe7c680ff, 0xeaca88ff, 0xeccc8bff, 0xeccc8bff, 0xe7c47dff, 0xe4be74ff, 0xe7c27bff, 0xe8c377ff, 0xebca80ff, 0xedcd87ff, 0xedcb83ff, 0xe5bd66ff, 0xe3b85eff, 0xdfb250ff, 0xd7a236ff, 0xdfb154ff, 0xe0b35bff, 0xe0b566ff, 0xe0b66eff, 0xddb165ff, 0xdbae5dff, 0xdcb165ff, 0xdbb164ff, 0xdaaf63ff, 0xd6a852ff, 0xd2a449ff, 0xd0a249ff, 0xd0a44eff, 0xd1a34eff, 0xce9f43ff, 0xc9983cff, 0xc89637ff, 0xc89637ff, 0xc5912eff, 0xc28c26ff, 0xba7f10ff, 0xb27309ff, 0xa55c04ff, 0xac6705ff, 0xbd871dff, 0xc89739ff, 0xd1a54fff, 0xd5ab59ff, 0xd6aa5aff, 0xc99c43ff, 0xb88834ff, 0x9d772eff, 0x935828ff, 0xce4732ff, 0xce4633ff, 0x3d2031ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x091230ff, 0x2d1c31ff, 0x6a2c31ff, 0xa63b32ff, 0xca4432ff, 0xd34b36ff, 0xcf5c41ff, 0xc97b5aff, 0xc9a07eff, 0xcfbe9bff, 0xd8cba1ff, 0xe4d9b2ff, 0xecddafff, 0xf1e2b0ff, 0xf7eac1ff, 0xf9eed1ff, 0xf8ebcfff, 0xf5e6c6ff, 0xf4e5c5ff, 0xf4e7cbff, 0xf2e4c7ff, 0xf1e1c0ff, 0xf1e0bcff, 0xedd8afff, 0xdab36eff, 0xd3a452ff, 0xdbaf65ff, 0xd9ae5fff, 0xd7aa53ff, 0xdab160ff, 0xddb86dff, 0xdcb361ff, 0xdeb462ff, 0xe1ba6eff, 0xe2bf74ff, 0xe7c579ff, 0xefd190ff, 0xeed090ff, 0xe7c176ff, 0xe6be71ff, 0xe8c378ff, 0xebca80ff, 0xedce87ff, 0xebc679ff, 0xe8bf68ff, 0xe0b358ff, 0xd9a941ff, 0xd7a232ff, 0xe3b767ff, 0xe1b562ff, 0xdeb15eff, 0xdcae5bff, 0xdcaf64ff, 0xd9aa5bff, 0xd8ab57ff, 0xd7a958ff, 0xd4a550ff, 0xd3a24bff, 0xd0a149ff, 0xd0a24dff, 0xce9f4aff, 0xcf9f49ff, 0xcd9d47ff, 0xca9a42ff, 0xc99a41ff, 0xc8973eff, 0xc48f30ff, 0xc08a23ff, 0xb5780aff, 0xb06c05ff, 0xa86204ff, 0xaa6605ff, 0xba8016ff, 0xc79333ff, 0xcea148ff, 0xd1a64fff, 0xd2a54fff, 0xd4a74fff, 0xd1ab61ff, 0xab8b47ff, 0x986234ff, 0xce4a34ff, 0xcf4934ff, 0x422231ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x001030ff, 0x0f1430ff, 0x3c2031ff, 0x7c3132ff, 0xb34033ff, 0xcf4834ff, 0xd3513aff, 0xcd654cff, 0xc78669ff, 0xc9aa8eff, 0xd3c7aaff, 0xdfd4b0ff, 0xe8deb9ff, 0xefe2bfff, 0xf3e6c7ff, 0xf6ead1ff, 0xf6ead3ff, 0xf3e5c9ff, 0xf1e1bfff, 0xf1deb7ff, 0xefd9abff, 0xe3c188ff, 0xd09f4bff, 0xd0a14cff, 0xd2a352ff, 0xd8ad60ff, 0xd3a44bff, 0xd4a84cff, 0xd4a74bff, 0xd5a648ff, 0xdaaf59ff, 0xe0b86bff, 0xe4bf77ff, 0xe6c376ff, 0xeac87aff, 0xedd08aff, 0xebcb83ff, 0xe8c375ff, 0xe7c071ff, 0xe7c070ff, 0xeac77dff, 0xe8bf6cff, 0xe6ba61ff, 0xe0b353ff, 0xd8a535ff, 0xd59f29ff, 0xe1b86bff, 0xe0b460ff, 0xddb15fff, 0xdaaa53ff, 0xdaac56ff, 0xdaab5aff, 0xd8aa5bff, 0xd6a859ff, 0xcf9f47ff, 0xcf993dff, 0xce9b42ff, 0xcb993cff, 0xcb9738ff, 0xc9973dff, 0xc8953aff, 0xc6933aff, 0xc59239ff, 0xc49139ff, 0xc0892aff, 0xbc841bff, 0xb37306ff, 0xaf6a04ff, 0xa96404ff, 0xa96704ff, 0xb17307ff, 0xc28b26ff, 0xd0a148ff, 0xd7ac57ff, 0xe2b966ff, 0xe9c577ff, 0xdcc07dff, 0xb9a46cff, 0xa1764fff, 0xce4e37ff, 0xd14c36ff, 0x472432ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x021030ff, 0x191731ff, 0x4d2632ff, 0x8d3833ff, 0xbf4534ff, 0xd34c35ff, 0xd2573fff, 0xcc7159ff, 0xcc977cff, 0xd4bc9bff, 0xe1d4b0ff, 0xecdebeff, 0xf2e6cbff, 0xf2e4c6ff, 0xf0debbff, 0xf2e2beff, 0xf2e0b4ff, 0xecd5a2ff, 0xd3a75bff, 0xcc973fff, 0xd2a355ff, 0xd6a963ff, 0xd3a653ff, 0xcf9c3dff, 0xca9630ff, 0xc1881aff, 0xd7a94cff, 0xe1b969ff, 0xe7c57fff, 0xeed49bff, 0xf0d79fff, 0xedcd84ff, 0xebca7dff, 0xedcf85ff, 0xebca7cff, 0xe9c473ff, 0xe7c06dff, 0xe9c273ff, 0xe5b85cff, 0xe1b44fff, 0xddaf45ff, 0xdaa93dff, 0xd6a12cff, 0xe0b563ff, 0xdfb15dff, 0xddb061ff, 0xdcaf60ff, 0xdbad5bff, 0xd9ab5cff, 0xd6a657ff, 0xd5a859ff, 0xd2a34eff, 0xd09e44ff, 0xcc983bff, 0xca953aff, 0xcb973bff, 0xca9840ff, 0xc59032ff, 0xc28d2fff, 0xc38e36ff, 0xc0892fff, 0xbc831fff, 0xb87d11ff, 0xb27207ff, 0xba7c19ff, 0xb5761aff, 0xae6e0eff, 0xbe8625ff, 0xdaad56ff, 0xeccc82ff, 0xf1d692ff, 0xf4da9aff, 0xedd190ff, 0xdabe7bff, 0xb8a169ff, 0x9f754dff, 0xcd5037ff, 0xd24e37ff, 0x4c2732ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x051130ff, 0x241b31ff, 0x5e2c33ff, 0x9d3e34ff, 0xc84a35ff, 0xd35038ff, 0xd0694fff, 0xd3bda3ff, 0xe6dcc4ff, 0xf0e3c5ff, 0xefdeb8ff, 0xf0dcb4ff, 0xf4e7c6ff, 0xf3e5c1ff, 0xeacf9cff, 0xd3a553ff, 0xce9c43ff, 0xd2a453ff, 0xd2a455ff, 0xd0a047ff, 0xce9e42ff, 0xc38d2dff, 0xb3700bff, 0xc58e31ff, 0xd7ac53ff, 0xe5c06eff, 0xeed190ff, 0xf0d89cff, 0xf2d997ff, 0xf0d48bff, 0xeecd81ff, 0xedcd81ff, 0xedce81ff, 0xeac778ff, 0xe8c16eff, 0xe5b95dff, 0xe3b756ff, 0xdcad45ff, 0xd8a739ff, 0xd6a130ff, 0xe0b262ff, 0xddb05bff, 0xdbaa4fff, 0xdcae5eff, 0xdcaf65ff, 0xdbad65ff, 0xd7a859ff, 0xd2a04aff, 0xd3a34eff, 0xd29f48ff, 0xcf9d45ff, 0xcb9535ff, 0xc99636ff, 0xca963eff, 0xc58f31ff, 0xc28d2eff, 0xc08a2eff, 0xc28b2eff, 0xc79231ff, 0xd6a74dff, 0xdaae5dff, 0xe5bb68ff, 0xe5bb67ff, 0xe4c075ff, 0xefd38fff, 0xf2d690ff, 0xf2d58fff, 0xf0d189ff, 0xeece85ff, 0xe9c67bff, 0xd9b666ff, 0xb79e62ff, 0x9d7349ff, 0xcd5338ff, 0xd45138ff, 0x522a33ff, 0x000f30ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x001030ff, 0x000f30ff, 0x000f30ff, 0x091330ff, 0x703334ff, 0xd55137ff, 0xd05940ff, 0xc1a891ff, 0xdad1b9ff, 0xeee4cbff, 0xf5e7c6ff, 0xf5e4c1ff, 0xf2e2c0ff, 0xf0dfb7ff, 0xe5c48dff, 0xd1a14cff, 0xcd9c45ff, 0xcfa254ff, 0xcfa14fff, 0xc89335ff, 0xb7781bff, 0xad6814ff, 0xac6006ff, 0xab6104ff, 0xaf670aff, 0xbe8226ff, 0xdab25fff, 0xebcd84ff, 0xf1da9aff, 0xf4dc9aff, 0xf2d68dff, 0xeece7fff, 0xebcc7cff, 0xecca7aff, 0xeac574ff, 0xe9c36dff, 0xe5bd64ff, 0xdcad47ff, 0xd6a534ff, 0xd29d25ff, 0xdda948ff, 0xdca94dff, 0xdbaa55ff, 0xd9ab59ff, 0xdbae64ff, 0xdaae64ff, 0xd6a857ff, 0xd4a452ff, 0xd4a453ff, 0xd5a658ff, 0xd0a149ff, 0xcd9838ff, 0xcc9b44ff, 0xca953dff, 0xc38b28ff, 0xbb8118ff, 0xc08724ff, 0xd8ab5bff, 0xe7c174ff, 0xefd18cff, 0xf0d594ff, 0xeed28aff, 0xefd087ff, 0xf2d692ff, 0xf1d597ff, 0xedcd82ff, 0xe9c369ff, 0xeac36dff, 0xeac46fff, 0xe7be68ff, 0xd6b263ff, 0xb5995aff, 0x9b7143ff, 0xcc5539ff, 0xd55439ff, 0x582d35ff, 0x001233ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x0a1533ff, 0x4e2331ff, 0x351e32ff, 0x0e1532ff, 0x011233ff, 0x001233ff, 0x011333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021333ff, 0x021232ff, 0x021232ff, 0x021232ff, 0x021232ff, 0x021232ff, 0x021232ff, 0x021232ff, 0x011232ff, 0x021232ff, 0x011232ff, 0x001132ff, 0x5a2e35ff, 0xd55439ff, 0xce593fff, 0xb39983ff, 0xd1cab3ff, 0xe7dab7ff, 0xf4e6c3ff, 0xf6e8c4ff, 0xf1deb6ff, 0xe9cd99ff, 0xd5a95dff, 0xcb983bff, 0xca973fff, 0xcd9c46ff, 0xcc993fff, 0xb37011ff, 0xa75b03ff, 0xa85b04ff, 0xaa5d04ff, 0xa95b05ff, 0xa95b04ff, 0xa85903ff, 0xab630eff, 0xbc802aff, 0xd1a555ff, 0xe3c072ff, 0xeecc76ff, 0xf0cd79ff, 0xedcd7aff, 0xebc877ff, 0xebc877ff, 0xe9c673ff, 0xe2b95bff, 0xdcae4bff, 0xd7a739ff, 0xcf981aff, 0xd8a33fff, 0xdaa74eff, 0xd9a853ff, 0xdbad5eff, 0xd8ab5fff, 0xd7a95bff, 0xd8a85cff, 0xd5a754ff, 0xd09d47ff, 0xd3a45aff, 0xd4a755ff, 0xd5a554ff, 0xd09e4cff, 0xc99237ff, 0xc69035ff, 0xc0851fff, 0xd39e3cff, 0xe6bd71ff, 0xeac982ff, 0xe9c87eff, 0xe9c77dff, 0xe7c373ff, 0xe6c06dff, 0xe4bb5fff, 0xe4bb66ff, 0xe5bc64ff, 0xe4ba5dff, 0xe5bb64ff, 0xe5bc65ff, 0xe2ba67ff, 0xcfa853ff, 0xb18f45ff, 0x976d3aff, 0xcb5739ff, 0xd6573aff, 0x5e3238ff, 0x011536ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x031636ff, 0x101836ff, 0xa43731ff, 0xc83e30ff, 0xa23631ff, 0x6b2b33ff, 0x342034ff, 0x101836ff, 0x021636ff, 0x011636ff, 0x031636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x041636ff, 0x011435ff, 0x593037ff, 0xd6563aff, 0xce593dff, 0xa78a6fff, 0xc7bd9fff, 0xe6d8b4ff, 0xf2e3baff, 0xf1e0b5ff, 0xefd9abff, 0xddb571ff, 0xcc9a43ff, 0xca9a3fff, 0xca963aff, 0xc89233ff, 0xbc8222ff, 0xab6607ff, 0xa85d04ff, 0xa85d04ff, 0xaa6205ff, 0xa65b04ff, 0xa85e04ff, 0xa95f05ff, 0xa75804ff, 0xa65803ff, 0xaa5f07ff, 0xb17110ff, 0xc6922cff, 0xe0b54eff, 0xedcb76ff, 0xebc771ff, 0xe9c774ff, 0xe9c673ff, 0xe1b859ff, 0xdbad46ff, 0xd5a534ff, 0xd09b1eff, 0xd69f3fff, 0xd39c3bff, 0xd6a048ff, 0xdcad62ff, 0xd9ab5eff, 0xd4a14aff, 0xd5a24cff, 0xd3a24bff, 0xd29f4aff, 0xd2a152ff, 0xd5a75aff, 0xd3a556ff, 0xcc983bff, 0xc99130ff, 0xc99136ff, 0xd6a243ff, 0xe5ba65ff, 0xe7c276ff, 0xe5bf6fff, 0xe4bc69ff, 0xe3b861ff, 0xddb151ff, 0xdbae4eff, 0xd9aa47ff, 0xd8a744ff, 0xdaab4cff, 0xdeb156ff, 0xddb04eff, 0xddb04cff, 0xd9ab4fff, 0xc79b41ff, 0xaa8434ff, 0x906429ff, 0xca5838ff, 0xd85a3bff, 0x65363aff, 0x031839ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x051939ff, 0x101b39ff, 0xa13732ff, 0xd44130ff, 0xc43c2cff, 0xc73d2dff, 0xc63e31ff, 0xa33732ff, 0x6a2c34ff, 0x342237ff, 0x111b38ff, 0x041939ff, 0x031939ff, 0x051939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x061939ff, 0x031839ff, 0x58323aff, 0xd6593bff, 0xce5b3eff, 0xa38568ff, 0xc0b38fff, 0xe4d6b1ff, 0xf2e3bbff, 0xf1ddafff, 0xe8ca94ff, 0xcc9b4dff, 0xc28d35ff, 0xbd8525ff, 0xb97f1cff, 0xaf6d0eff, 0xa35c04ff, 0xa65f03ff, 0xa65b03ff, 0xa45703ff, 0xa65a03ff, 0xa35903ff, 0xa45b04ff, 0xa45b04ff, 0xac6204ff, 0xae6704ff, 0xad6605ff, 0xac6504ff, 0xa86204ff, 0xb37511ff, 0xcf9f38ff, 0xe7bf5fff, 0xe9c369ff, 0xe5be64ff, 0xdfb553ff, 0xdbae47ff, 0xd5a432ff, 0xd09a1cff, 0xd29835ff, 0xd39a36ff, 0xd7a34fff, 0xd6a048ff, 0xd8a450ff, 0xd5a14cff, 0xd29c3fff, 0xd49f45ff, 0xd29d42ff, 0xd09d41ff, 0xce993cff, 0xcb9535ff, 0xcd9738ff, 0xc99233ff, 0xd29c3cff, 0xe4ba63ff, 0xe6bf71ff, 0xe4bd6fff, 0xe0b761ff, 0xddb256ff, 0xddb055ff, 0xd7a645ff, 0xd6a542ff, 0xd9a747ff, 0xd8a543ff, 0xd4a138ff, 0xd9a84aff, 0xd7a53bff, 0xd6a339ff, 0xd29f3bff, 0xc49435ff, 0xa8802eff, 0x8c6022ff, 0xc95a38ff, 0xd95c3dff, 0x6b3b3cff, 0x051c3cff, 0x091c3cff, 0x091d3dff, 0x091d3dff, 0x091d3dff, 0x091d3dff, 0x091d3dff, 0x091d3dff, 0x081d3cff, 0x081d3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x111e3cff, 0x9f3733ff, 0xd54130ff, 0x7b261aff, 0x41140bff, 0x782519ff, 0xab3426ff, 0xc93e2eff, 0xc63e31ff, 0xa23833ff, 0x6a2d36ff, 0x342439ff, 0x121e3bff, 0x061c3cff, 0x061c3cff, 0x071c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x081c3cff, 0x051b3cff, 0x57343cff, 0xd65b3cff, 0xcf5d3eff, 0xa48569ff, 0xc3ba9aff, 0xe5d8b5ff, 0xefdaaeff, 0xe4c383ff, 0xca9945ff, 0xb77919ff, 0xb77a12ff, 0xb7780eff, 0xb67811ff, 0xb67817ff, 0xb6781eff, 0xb37519ff, 0xb6781dff, 0xb27017ff, 0xb6771fff, 0xb17018ff, 0xa7610aff, 0xa65f04ff, 0xaf6a04ff, 0xaf6a04ff, 0xae6704ff, 0xad6405ff, 0xaa6004ff, 0xa75f03ff, 0xa86409ff, 0xcb9a3aff, 0xe6bd58ff, 0xe4ba56ff, 0xdeb24aff, 0xd8aa3cff, 0xd2a02cff, 0xd09b25ff, 0xcc8b1bff, 0xd0942aff, 0xd49d3cff, 0xd29a3aff, 0xd59d44ff, 0xd19a3aff, 0xd29b3cff, 0xd49d43ff, 0xd3a047ff, 0xd29e42ff, 0xcf9b41ff, 0xcd973cff, 0xcc9842ff, 0xce983bff, 0xdeb058ff, 0xe2b967ff, 0xe2b968ff, 0xdfb663ff, 0xdcb158ff, 0xd8a94aff, 0xdaac51ff, 0xdcb15dff, 0xd5a240ff, 0xd8a74aff, 0xd8a747ff, 0xd4a33eff, 0xd7a544ff, 0xd6a33eff, 0xd3a03aff, 0xd19f3bff, 0xc59535ff, 0xa9812eff, 0x8f652bff, 0xc85d3aff, 0xdb5f3eff, 0x713f3fff, 0x081f40ff, 0x0a2040ff, 0x0b203fff, 0x0b203fff, 0x0b1f3fff, 0x0b1f3fff, 0x0b1f3fff, 0x0b1f3fff, 0x0b1f3fff, 0x0b1f3fff, 0x0b1f3fff, 0x0b1f40ff, 0x0b1f40ff, 0x0b2040ff, 0x0b2040ff, 0x0a1f40ff, 0x12213fff, 0x9e3834ff, 0xd64130ff, 0x732418ff, 0x110500ff, 0x150700ff, 0x230b03ff, 0x45150cff, 0x792519ff, 0xac3526ff, 0xc93e2eff, 0xc53e31ff, 0xa13834ff, 0x692f38ff, 0x34263bff, 0x13213eff, 0x081f3fff, 0x081f3fff, 0x091f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x0a1f3fff, 0x071e3fff, 0x56363eff, 0xd65e3dff, 0xd06040ff, 0xa5876eff, 0xc1b89bff, 0xdcc9a1ff, 0xd5ae69ff, 0xc0882aff, 0xbc8222ff, 0xbd8321ff, 0xbc851fff, 0xbf8924ff, 0xc2902fff, 0xc6953bff, 0xc99c45ff, 0xc99d45ff, 0xca9d47ff, 0xd1a659ff, 0xd7b171ff, 0xd3ac65ff, 0xcd9d46ff, 0xc28924ff, 0xb3720aff, 0xae6603ff, 0xad6604ff, 0xab6305ff, 0xa86005ff, 0xa65d05ff, 0xa15804ff, 0xa4610aff, 0xc38f2eff, 0xdcaf44ff, 0xddaf44ff, 0xd8aa3dff, 0xd0a02bff, 0xce9d2dff, 0xca8612ff, 0xcc8c1cff, 0xce932bff, 0xce9431ff, 0xcf932dff, 0xcf932dff, 0xd2983bff, 0xd09731ff, 0xcf9837ff, 0xd29d44ff, 0xcf993aff, 0xcc9533ff, 0xca9230ff, 0xcc9734ff, 0xdaad55ff, 0xdfb462ff, 0xdfb568ff, 0xddb261ff, 0xd7a94dff, 0xd39f3bff, 0xd4a040ff, 0xd7a74eff, 0xd5a241ff, 0xd6a545ff, 0xd8a94cff, 0xd9ab51ff, 0xd9a847ff, 0xdaaa4dff, 0xd5a243ff, 0xd19c3aff, 0xc5973bff, 0xab8434ff, 0x8e6528ff, 0xc75e3aff, 0xdb623fff, 0x774341ff, 0x0a2243ff, 0x0c2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2343ff, 0x0d2342ff, 0x0d2342ff, 0x0d2342ff, 0x0d2342ff, 0x0c2343ff, 0x132442ff, 0x9c3835ff, 0xd64130ff, 0x762519ff, 0x140700ff, 0x160700ff, 0x150700ff, 0x140600ff, 0x150700ff, 0x240b04ff, 0x47160dff, 0x7b261aff, 0xad3526ff, 0xca3e2eff, 0xc53f32ff, 0xa03935ff, 0x693039ff, 0x35283eff, 0x152441ff, 0x0a2243ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x0c2242ff, 0x092142ff, 0x543841ff, 0xd6603eff, 0xd16140ff, 0xa07a59ff, 0xaf9867ff, 0xb98d43ff, 0xb77e1bff, 0xba811aff, 0xbd8624ff, 0xc18c2cff, 0xc18e32ff, 0xc08c2bff, 0xc49438ff, 0xc89b43ff, 0xd0a75aff, 0xcda351ff, 0xcca14fff, 0xd1aa62ff, 0xd9b87cff, 0xdbb97eff, 0xdab877ff, 0xd4aa5bff, 0xc99737ff, 0xc08520ff, 0xb47410ff, 0xaa6305ff, 0xa55d04ff, 0xa05905ff, 0x9e5804ff, 0x9b5503ff, 0xa05e09ff, 0xc38e21ff, 0xd7a32cff, 0xd5a434ff, 0xd19f2eff, 0xcd9821ff, 0xcb8a20ff, 0xcb891cff, 0xd19534ff, 0xd1993eff, 0xcf9431ff, 0xd29838ff, 0xd19939ff, 0xd19836ff, 0xd49f43ff, 0xd29e46ff, 0xcd9635ff, 0xca922eff, 0xc78e29ff, 0xc68d2aff, 0xd09d3fff, 0xdbae5bff, 0xdbaf5dff, 0xd9ac58ff, 0xd6a64bff, 0xd29f3cff, 0xcf9837ff, 0xd09a38ff, 0xd5a448ff, 0xd5a445ff, 0xd9a84cff, 0xd7a746ff, 0xdaac52ff, 0xdbad58ff, 0xdaad5cff, 0xd7a857ff, 0xcca254ff, 0xaf883eff, 0x8f682cff, 0xc6613bff, 0xdc6440ff, 0x7d4742ff, 0x0d2646ff, 0x0e2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0f2646ff, 0x0e2646ff, 0x142745ff, 0x9b3a36ff, 0xd64230ff, 0x7a271aff, 0x170901ff, 0x190901ff, 0x190901ff, 0x190901ff, 0x190901ff, 0x180901ff, 0x160900ff, 0x180901ff, 0x270e05ff, 0x4b190eff, 0x7e291bff, 0xaf3726ff, 0xca402dff, 0xc34031ff, 0x9d3b35ff, 0x60323cff, 0x162745ff, 0x0e2645ff, 0x0e2645ff, 0x0e2645ff, 0x0e2645ff, 0x0e2645ff, 0x0e2645ff, 0x0e2645ff, 0x0e2645ff, 0x0e2645ff, 0x0e2545ff, 0x0e2545ff, 0x0e2545ff, 0x0e2545ff, 0x0e2545ff, 0x0e2545ff, 0x0e2545ff, 0x0e2545ff, 0x0e2545ff, 0x0b2445ff, 0x533b43ff, 0xd66340ff, 0xd2633fff, 0x92602eff, 0x906417ff, 0xaa761aff, 0xba8221ff, 0xbb841aff, 0xbd861fff, 0xc59239ff, 0xc79949ff, 0xc18f37ff, 0xc19035ff, 0xc4963bff, 0xc99e4aff, 0xcca352ff, 0xcda351ff, 0xc89943ff, 0xcca453ff, 0xd3b067ff, 0xd5b26cff, 0xd6b26dff, 0xd4ac5cff, 0xd4aa57ff, 0xce9f45ff, 0xc18b2bff, 0xab6809ff, 0xa45e04ff, 0xa55f04ff, 0xa25e04ff, 0x9f5a04ff, 0xa66407ff, 0xcb9314ff, 0xd19c22ff, 0xd2a235ff, 0xd09e2dff, 0xc68213ff, 0xc9881eff, 0xcf9231ff, 0xd09638ff, 0xd09634ff, 0xd39f45ff, 0xd09732ff, 0xd19a35ff, 0xd6a34aff, 0xd09b3eff, 0xcd9739ff, 0xcc9432ff, 0xc8902aff, 0xc1871dff, 0xcb9230ff, 0xd7a74dff, 0xd7a84fff, 0xd8ab55ff, 0xd9ab55ff, 0xcf9c38ff, 0xd19e43ff, 0xd29d40ff, 0xd7a64fff, 0xd7a649ff, 0xd9a950ff, 0xd9a84fff, 0xdaad59ff, 0xd9ac57ff, 0xdaac5aff, 0xd7a954ff, 0xcb9f4cff, 0xb28c48ff, 0x906c36ff, 0xc4633dff, 0xdd6741ff, 0x824c44ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x102949ff, 0x142a48ff, 0x993c36ff, 0xd5442fff, 0x7d2a1bff, 0x1b0c02ff, 0x1c0d02ff, 0x1d0d03ff, 0x1d0d03ff, 0x1d0d03ff, 0x1d0d03ff, 0x1d0d03ff, 0x1c0d02ff, 0x1c0c02ff, 0x1a0c02ff, 0x1c0c02ff, 0x2b1106ff, 0x4d1c0fff, 0x92311fff, 0xd3442eff, 0xba4132ff, 0x282c45ff, 0x0f2949ff, 0x112949ff, 0x112949ff, 0x112949ff, 0x112949ff, 0x112949ff, 0x112949ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x102948ff, 0x0d2748ff, 0x523c46ff, 0xd66541ff, 0xd2643eff, 0x8a541dff, 0x8c5f0fff, 0xae7f2fff, 0xc19241ff, 0xc08c2fff, 0xc38e2eff, 0xc89942ff, 0xca9d4dff, 0xc19037ff, 0xc08f34ff, 0xc19237ff, 0xc19236ff, 0xc5973dff, 0xc89b44ff, 0xc89b42ff, 0xcca451ff, 0xd0ab5dff, 0xd4b16bff, 0xd2ac61ff, 0xd8b56fff, 0xd8b46dff, 0xd5ae61ff, 0xd3aa56ff, 0xc18b28ff, 0xac6a06ff, 0xa76304ff, 0xa05d04ff, 0xa05c04ff, 0x9c5604ff, 0xb57a0cff, 0xcc9313ff, 0xd09c29ff, 0xddb65cff, 0xcb8e2bff, 0xc78922ff, 0xc8891fff, 0xcf9535ff, 0xd19938ff, 0xd7a551ff, 0xd29e41ff, 0xd29e3dff, 0xd4a145ff, 0xd19c41ff, 0xce963cff, 0xcd9536ff, 0xc8912eff, 0xc0851bff, 0xc28720ff, 0xcb9530ff, 0xcf9933ff, 0xd6a54bff, 0xd7a854ff, 0xd19d3bff, 0xd5a54eff, 0xd4a44aff, 0xd4a142ff, 0xd9a94fff, 0xdeb366ff, 0xdcb062ff, 0xdcaf5eff, 0xdbae5bff, 0xdbad5bff, 0xd8ac59ff, 0xcea55bff, 0xb18b46ff, 0x916e39ff, 0xc3653eff, 0xdd6942ff, 0x885046ff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x122c4cff, 0x122c4cff, 0x122c4cff, 0x122c4cff, 0x122c4cff, 0x162d4bff, 0x973e37ff, 0xd5462eff, 0x812d1bff, 0x1f0f04ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x201004ff, 0x1f0f04ff, 0x1b0e03ff, 0x511f10ff, 0xcd452dff, 0xbd4331ff, 0x2f3047ff, 0x112c4cff, 0x132c4bff, 0x132c4bff, 0x132c4bff, 0x132c4bff, 0x132c4bff, 0x132c4bff, 0x132c4bff, 0x132c4bff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x132c4cff, 0x122c4bff, 0x122c4bff, 0x0f2b4cff, 0x513f48ff, 0xd66842ff, 0xd36740ff, 0x8c5825ff, 0x8e631aff, 0xab7a26ff, 0xc0923fff, 0xc59338ff, 0xc7973eff, 0xcb9d48ff, 0xcba150ff, 0xc89e49ff, 0xc99d49ff, 0xc79a45ff, 0xc3953cff, 0xcba251ff, 0xcda557ff, 0xcba14bff, 0xcfa857ff, 0xd1aa5dff, 0xd2ad64ff, 0xd4ae63ff, 0xd7b36cff, 0xd8b46cff, 0xd8b46cff, 0xd8b46bff, 0xd0a246ff, 0xc5902dff, 0xb57a15ff, 0xa15e04ff, 0xa05d04ff, 0x9b5704ff, 0xa26207ff, 0xbe800aff, 0xc78d15ff, 0xdcb65bff, 0xcc942fff, 0xcf9737ff, 0xce9739ff, 0xcf973cff, 0xd49f4aff, 0xd7a54eff, 0xd6a54fff, 0xd39f43ff, 0xd3a148ff, 0xd4a34dff, 0xcf993cff, 0xcd973aff, 0xcb9336ff, 0xc68c2cff, 0xbe801aff, 0xc68e27ff, 0xcf9a38ff, 0xd6a54fff, 0xd29c3aff, 0xd3a246ff, 0xd9ab5dff, 0xd9ab5bff, 0xd8a74fff, 0xdaab55ff, 0xdcb164ff, 0xdcb164ff, 0xdaae5eff, 0xdaae62ff, 0xdaad5eff, 0xd7aa57ff, 0xca9f51ff, 0xae863aff, 0x8e6a30ff, 0xc2663dff, 0xde6c43ff, 0x8e5448ff, 0x16304fff, 0x152f4fff, 0x15304fff, 0x15304fff, 0x15304fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x142f4fff, 0x18304eff, 0x964038ff, 0xd4482eff, 0x85301cff, 0x231205ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x231305ff, 0x211205ff, 0x502110ff, 0xc9462cff, 0xbf4630ff, 0x363449ff, 0x122f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4fff, 0x152f4eff, 0x152f4eff, 0x152f4eff, 0x152f4eff, 0x152f4eff, 0x152f4eff, 0x112e4fff, 0x50414bff, 0xd66a43ff, 0xd76942ff, 0x955625ff, 0x8c6418ff, 0xa9771eff, 0xbb882cff, 0xc6953cff, 0xc99b45ff, 0xd0a558ff, 0xcea456ff, 0xcfa658ff, 0xcda558ff, 0xc89a47ff, 0xc5963eff, 0xcea75cff, 0xd1ab62ff, 0xcda451ff, 0xd7b36dff, 0xd5b26cff, 0xd4af64ff, 0xdebd7dff, 0xe1c287ff, 0xddbc7aff, 0xd8b36cff, 0xd8b570ff, 0xd0a955ff, 0xcfa44aff, 0xc5922eff, 0xa76706ff, 0x9f5c04ff, 0x9a5704ff, 0x995604ff, 0xae6a04ff, 0xc08214ff, 0xddb65eff, 0xcc9430ff, 0xd29d3cff, 0xd3a04aff, 0xd39e4aff, 0xd5a452ff, 0xd6a651ff, 0xd8a956ff, 0xd29e44ff, 0xd09c41ff, 0xcf9c3eff, 0xcd9a39ff, 0xcf9f4eff, 0xcd9946ff, 0xc58d2fff, 0xbf8321ff, 0xc18622ff, 0xca922bff, 0xd4a44bff, 0xd3a346ff, 0xd5a449ff, 0xd6a54aff, 0xd8aa55ff, 0xdbaf5bff, 0xdaad5aff, 0xd7a64cff, 0xd8aa56ff, 0xd5a246ff, 0xd7a754ff, 0xdbae61ff, 0xd8aa5cff, 0xcea461ff, 0xb18c4bff, 0x8c682dff, 0xc1693fff, 0xdf6e44ff, 0x945849ff, 0x183352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173352ff, 0x173252ff, 0x193352ff, 0x934239ff, 0xd44a2dff, 0x88331dff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x271606ff, 0x251506ff, 0x4e220fff, 0xc6472bff, 0xc3492fff, 0x3d374aff, 0x143252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x173252ff, 0x133152ff, 0x4f434eff, 0xd66c44ff, 0xd96b43ff, 0x995624ff, 0x8c641aff, 0xa97820ff, 0xb98623ff, 0xc79940ff, 0xcba04fff, 0xcca151ff, 0xd2ab60ff, 0xd2ab61ff, 0xcea557ff, 0xc79c47ff, 0xca9f4cff, 0xc9a04eff, 0xcba251ff, 0xcea555ff, 0xdab879ff, 0xd8b573ff, 0xd8b572ff, 0xdebe81ff, 0xe4c998ff, 0xe1c28cff, 0xd8b46bff, 0xd8b56dff, 0xd3ae65ff, 0xd2a85aff, 0xca9c3fff, 0xbb831eff, 0xa36106ff, 0x9e5a04ff, 0x9b5804ff, 0xa75f02ff, 0xbf8427ff, 0xe0c071ff, 0xd29d41ff, 0xd5a34cff, 0xd3a249ff, 0xd19e42ff, 0xd5a54dff, 0xd3a246ff, 0xcd9836ff, 0xcd9735ff, 0xcc993fff, 0xcf9b41ff, 0xd4a34eff, 0xd2a551ff, 0xcb973aff, 0xc68f2eff, 0xc28824ff, 0xc18725ff, 0xc58b29ff, 0xd19f45ff, 0xd2a142ff, 0xd2a142ff, 0xd3a142ff, 0xd6a546ff, 0xd8a94fff, 0xd8a84eff, 0xd5a649ff, 0xd5a549ff, 0xcf9832ff, 0xcf9a3dff, 0xd7a856ff, 0xd8ab5bff, 0xcda158ff, 0xae853fff, 0x8a652aff, 0xbf693eff, 0xdf7145ff, 0x995c4bff, 0x1b3655ff, 0x193656ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x193655ff, 0x1b3655ff, 0x92443aff, 0xd44c2cff, 0x8b371eff, 0x2b1908ff, 0x2a1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x2b1908ff, 0x291808ff, 0x4c230fff, 0xc34829ff, 0xc64b2eff, 0x433b4bff, 0x163556ff, 0x183555ff, 0x183555ff, 0x183555ff, 0x183555ff, 0x183555ff, 0x183555ff, 0x183555ff, 0x183555ff, 0x193555ff, 0x193555ff, 0x193555ff, 0x193555ff, 0x193555ff, 0x193555ff, 0x193555ff, 0x193555ff, 0x193555ff, 0x153455ff, 0x4f4651ff, 0xd66e46ff, 0xda6e45ff, 0x9a5623ff, 0x8c651bff, 0xae802dff, 0xbe8d32ff, 0xc99a43ff, 0xcda456ff, 0xcb9f4dff, 0xcea451ff, 0xd1a858ff, 0xcda552ff, 0xc89c46ff, 0xcb9e48ff, 0xc99d45ff, 0xcaa14aff, 0xd4ae63ff, 0xd7b16bff, 0xd8b36cff, 0xdab977ff, 0xdfc085ff, 0xddbe84ff, 0xddbc7eff, 0xe1c285ff, 0xddbc7eff, 0xd7b473ff, 0xd7b472ff, 0xcca04aff, 0xc38f2aff, 0xb27511ff, 0xa46307ff, 0x9c5503ff, 0xa7600dff, 0xd2a758ff, 0xe3c880ff, 0xcf9937ff, 0xd3a145ff, 0xd3a248ff, 0xd19f42ff, 0xd7a64eff, 0xd4a44aff, 0xd09f43ff, 0xcd9b3dff, 0xce9c42ff, 0xcf9f4fff, 0xd09f4dff, 0xcfa14cff, 0xce9b44ff, 0xc99336ff, 0xc68e30ff, 0xc38929ff, 0xc38b2cff, 0xcc9b42ff, 0xd1a045ff, 0xd09c39ff, 0xd19f3fff, 0xd3a245ff, 0xd3a242ff, 0xd4a140ff, 0xd2a03dff, 0xd29e3aff, 0xd2a046ff, 0xc78f27ff, 0xcd9736ff, 0xd19e45ff, 0xc89a4aff, 0xae843eff, 0x88642dff, 0xbb673aff, 0xe07447ff, 0x9f604cff, 0x1e3a58ff, 0x1b3959ff, 0x1b3959ff, 0x1b3959ff, 0x1b3959ff, 0x1b3959ff, 0x1b3958ff, 0x1b3959ff, 0x1b3959ff, 0x1b3959ff, 0x1b3959ff, 0x1b3958ff, 0x1b3958ff, 0x1b3958ff, 0x1b3958ff, 0x1b3958ff, 0x1d3958ff, 0x90463bff, 0xd34e2cff, 0x8e3a1eff, 0x2e1c0aff, 0x2e1c09ff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2e1c0aff, 0x2c1b09ff, 0x4a240fff, 0xc04929ff, 0xc84d2dff, 0x4a3e4cff, 0x183859ff, 0x1b3858ff, 0x1b3858ff, 0x1b3958ff, 0x1b3858ff, 0x1b3858ff, 0x1b3858ff, 0x1b3858ff, 0x1b3858ff, 0x1a3858ff, 0x1a3858ff, 0x1a3858ff, 0x1a3858ff, 0x1a3858ff, 0x1a3858ff, 0x1a3858ff, 0x1a3858ff, 0x1a3858ff, 0x173758ff, 0x4e4853ff, 0xd67147ff, 0xdb7146ff, 0xa16034ff, 0x947334ff, 0xb48c41ff, 0xc59a47ff, 0xc99a40ff, 0xcfa75aff, 0xd0a95dff, 0xcea14dff, 0xcfa248ff, 0xcfa44dff, 0xc7983aff, 0xca9a3aff, 0xca9a39ff, 0xcb9c3dff, 0xd6ae5bff, 0xdcb873ff, 0xdcba79ff, 0xddbc7fff, 0xdfc089ff, 0xdbba7cff, 0xdebf83ff, 0xddbd80ff, 0xd9b674ff, 0xd3ac63ff, 0xca9f49ff, 0xc49331ff, 0xc49230ff, 0xc38d2aff, 0xbf8720ff, 0xa9660bff, 0xbc8a43ff, 0xe7cf93ff, 0xe5ca8cff, 0xd4a043ff, 0xdaad5fff, 0xd9ac5dff, 0xd6a852ff, 0xd8ab54ff, 0xd4a64bff, 0xd4a852ff, 0xd3a54fff, 0xd3a551ff, 0xcfa14eff, 0xcfa24fff, 0xcd9e4aff, 0xcb9b49ff, 0xc79339ff, 0xc38b2cff, 0xc28a2cff, 0xc1892aff, 0xc9963dff, 0xd1a34fff, 0xcd9835ff, 0xd1a045ff, 0xd3a552ff, 0xd3a54eff, 0xd29f41ff, 0xd2a040ff, 0xd2a042ff, 0xd09e3fff, 0xc9932dff, 0xc18313ff, 0xc58a24ff, 0xc18f38ff, 0xad8137ff, 0x896632ff, 0xbb6a3dff, 0xe17648ff, 0xa4644dff, 0x213d5bff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1d3c5cff, 0x1e3c5bff, 0x8e483cff, 0xd2502bff, 0x923c1fff, 0x321f0bff, 0x311f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x321f0bff, 0x301e0aff, 0x48260fff, 0xbd4b28ff, 0xcb4f2cff, 0x51424dff, 0x1a3b5cff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3c5bff, 0x1d3b5bff, 0x1d3b5bff, 0x1d3b5bff, 0x1a3b5bff, 0x4e4a56ff, 0xd57348ff, 0xdc7447ff, 0xa3633bff, 0x987d49ff, 0xbc995dff, 0xcaa458ff, 0xcea551ff, 0xd2a95aff, 0xd1ab60ff, 0xd0a654ff, 0xcea041ff, 0xd2a243ff, 0xce9e3cff, 0xcea041ff, 0xd4a851ff, 0xd5aa54ff, 0xd9af57ff, 0xd8af59ff, 0xd7af5dff, 0xd9b363ff, 0xd7b164ff, 0xd5af62ff, 0xd6b36dff, 0xd9b876ff, 0xd9b571ff, 0xd0a856ff, 0xca9d45ff, 0xbf8f2dff, 0xc18e29ff, 0xc28e26ff, 0xc39030ff, 0xc18c29ff, 0xdbbb75ff, 0xe6d097ff, 0xe4c98cff, 0xd7a64cff, 0xdeb56eff, 0xddb36bff, 0xd9af5fff, 0xd2a348ff, 0xd3a54cff, 0xd6a855ff, 0xd3a751ff, 0xd0a249ff, 0xd0a24eff, 0xca993cff, 0xc9973aff, 0xcc9a45ff, 0xc9943aff, 0xc58f33ff, 0xc38c30ff, 0xbe8523ff, 0xc08726ff, 0xd0a152ff, 0xd3a85aff, 0xd3a857ff, 0xd3a757ff, 0xd1a551ff, 0xd2a44eff, 0xd1a34bff, 0xd1a34cff, 0xcfa045ff, 0xca9738ff, 0xc48a1fff, 0xc38920ff, 0xb47a19ff, 0xa16f1cff, 0x845c22ff, 0xb8693bff, 0xe17949ff, 0xa9684fff, 0x24415eff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x1f3f5fff, 0x203f5eff, 0x8d4a3eff, 0xd2522aff, 0x953f1fff, 0x36220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x35220cff, 0x34210cff, 0x472710ff, 0xba4c26ff, 0xcc522bff, 0x59454dff, 0x1c3e5fff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1f3f5eff, 0x1c3e5fff, 0x4d4d59ff, 0xd47549ff, 0xdd7648ff, 0xa7673dff, 0x9b8151ff, 0xbea06aff, 0xd3b175ff, 0xd6b06cff, 0xd3ab60ff, 0xd5ad65ff, 0xd7af65ff, 0xd2a54bff, 0xd7a845ff, 0xdaab52ff, 0xdaaf5fff, 0xdcb365ff, 0xddb468ff, 0xdbb260ff, 0xdcb467ff, 0xd5aa55ff, 0xd6ae5eff, 0xd7b164ff, 0xd4ac5cff, 0xcda34dff, 0xd1a855ff, 0xd3ac5aff, 0xd2af63ff, 0xcea553ff, 0xc39436ff, 0xc39230ff, 0xc39233ff, 0xc3933bff, 0xca9d48ff, 0xe3c989ff, 0xe4cd94ff, 0xe5cb8fff, 0xd8aa50ff, 0xd9ae5cff, 0xdbb164ff, 0xd9b063ff, 0xd2a34bff, 0xd09f45ff, 0xd4a54fff, 0xd4a855ff, 0xd1a34cff, 0xcc9c40ff, 0xca983dff, 0xcc9d46ff, 0xca9843ff, 0xc8943bff, 0xc68f32ff, 0xc38c30ff, 0xbf8627ff, 0xbb801dff, 0xcb9844ff, 0xd4a960ff, 0xd3a858ff, 0xd4a959ff, 0xd5ab60ff, 0xd3a95cff, 0xd1a655ff, 0xd3a95bff, 0xd0a554ff, 0xd0a24eff, 0xca983aff, 0xc6912dff, 0xb9821eff, 0x9e6910ff, 0x784a08ff, 0xb16232ff, 0xe27c4bff, 0xad6c50ff, 0x294561ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x214262ff, 0x224262ff, 0x8b4c3fff, 0xd1532aff, 0x974220ff, 0x3a250eff, 0x39250dff, 0x39250eff, 0x39250eff, 0x39250eff, 0x39250eff, 0x39250eff, 0x39250eff, 0x39250eff, 0x39250eff, 0x39250eff, 0x39250eff, 0x38250dff, 0x472a10ff, 0xb64d25ff, 0xcc542aff, 0x60484dff, 0x1e4262ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x214261ff, 0x1e4162ff, 0x4d4f5cff, 0xd4774bff, 0xde7949ff, 0xa76536ff, 0x95793eff, 0xbc9d5fff, 0xd0ae6dff, 0xd3ae69ff, 0xd0a75bff, 0xd3ac60ff, 0xd6af66ff, 0xd2a54cff, 0xd6a33cff, 0xdeb360ff, 0xdfb76fff, 0xe0b86eff, 0xdfb974ff, 0xdcb66cff, 0xd7af60ff, 0xd2a955ff, 0xd4ae61ff, 0xd7b572ff, 0xd5b065ff, 0xca9f3fff, 0xc99b38ff, 0xcfa54bff, 0xcea44cff, 0xca9f45ff, 0xcca14aff, 0xc99c3eff, 0xc2902cff, 0xbe8924ff, 0xd3ac57ff, 0xe4cb8bff, 0xe2ca8aff, 0xe5cc8eff, 0xd8a851ff, 0xd9ab58ff, 0xdcb367ff, 0xdeb973ff, 0xd4a855ff, 0xcfa044ff, 0xd3a853ff, 0xd5aa5aff, 0xd1a54eff, 0xcea14bff, 0xd0a454ff, 0xd0a455ff, 0xc99946ff, 0xc48f34ff, 0xc38c2cff, 0xc59138ff, 0xc48f36ff, 0xc08628ff, 0xc38c31ff, 0xcfa255ff, 0xd2a554ff, 0xd4a95dff, 0xd5ab60ff, 0xd4aa5eff, 0xd4aa5fff, 0xd7af67ff, 0xd5ad64ff, 0xd5aa60ff, 0xd3a653ff, 0xcd9f43ff, 0xbf8f30ff, 0xaa7d29ff, 0x865f1eff, 0xb36937ff, 0xe27e4bff, 0xb26f51ff, 0x2d4864ff, 0x234565ff, 0x244665ff, 0x244665ff, 0x244665ff, 0x244665ff, 0x244665ff, 0x234665ff, 0x234665ff, 0x234665ff, 0x234665ff, 0x234565ff, 0x234565ff, 0x234565ff, 0x234565ff, 0x234565ff, 0x234665ff, 0x894f41ff, 0xd15529ff, 0x9a4520ff, 0x3e280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3c280fff, 0x3b280fff, 0x482c11ff, 0xb24d24ff, 0xcd5629ff, 0x684c4cff, 0x204566ff, 0x234565ff, 0x234565ff, 0x234565ff, 0x234565ff, 0x234564ff, 0x234565ff, 0x234565ff, 0x234565ff, 0x234565ff, 0x234564ff, 0x234564ff, 0x234564ff, 0x234564ff, 0x234564ff, 0x234564ff, 0x234564ff, 0x234564ff, 0x204465ff, 0x4d515fff, 0xd3794cff, 0xdf7c4bff, 0xab6b41ff, 0x95773aff, 0xbd9b57ff, 0xd3b272ff, 0xd6b26cff, 0xd5af65ff, 0xd7b369ff, 0xd6b065ff, 0xd6ad5dff, 0xd9ac56ff, 0xdeb669ff, 0xdcb364ff, 0xdab56bff, 0xdab678ff, 0xd9b671ff, 0xd5ad5cff, 0xd2a852ff, 0xd2a954ff, 0xd2a953ff, 0xcda347ff, 0xcfa546ff, 0xd0a64dff, 0xcb9e41ff, 0xcc9f3dff, 0xcda141ff, 0xca9f41ff, 0xc6983bff, 0xbd8b24ff, 0xc3912eff, 0xdfc073ff, 0xe4cc8bff, 0xe3cd8dff, 0xe5ce95ff, 0xdaad5cff, 0xd8aa55ff, 0xdbb160ff, 0xddb56cff, 0xd3a855ff, 0xce9e3fff, 0xd4a95aff, 0xd5ac64ff, 0xd6ab5eff, 0xcfa355ff, 0xcea252ff, 0xcea250ff, 0xc89844ff, 0xc6923bff, 0xc38c2dff, 0xc38e36ff, 0xc38e37ff, 0xc28a2eff, 0xbe8324ff, 0xc89846ff, 0xd1a656ff, 0xd3a85bff, 0xd4aa5eff, 0xd6ac61ff, 0xd8b069ff, 0xd9b16bff, 0xd9b16dff, 0xd8af66ff, 0xd5ab5aff, 0xd0a44dff, 0xc3953cff, 0xaf8538ff, 0x8e6f35ff, 0xb4703fff, 0xe2804cff, 0xb67352ff, 0x304c66ff, 0x254968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x264968ff, 0x254968ff, 0x254968ff, 0x885143ff, 0xd05728ff, 0x9d4821ff, 0x412b10ff, 0x3f2b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x402b10ff, 0x3f2b10ff, 0x492e12ff, 0xae4e23ff, 0xce5828ff, 0x6f4f4bff, 0x224869ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x254868ff, 0x234768ff, 0x4d5462ff, 0xd37c4eff, 0xe07e4bff, 0xae7048ff, 0x9b8354ff, 0xc3a467ff, 0xdaba7dff, 0xe1bf83ff, 0xdfbb7aff, 0xdcbb78ff, 0xddba73ff, 0xdeb972ff, 0xdfb76fff, 0xdfb86eff, 0xdaaf5cff, 0xd5aa56ff, 0xd9b46fff, 0xd9b46eff, 0xd7ae5bff, 0xd7b05dff, 0xd6af5eff, 0xd3a954ff, 0xcea349ff, 0xd8b26cff, 0xdbb87aff, 0xd0a34cff, 0xc5921eff, 0xc59429ff, 0xc3912eff, 0xc1902dff, 0xc18f2cff, 0xd1a751ff, 0xe3c67fff, 0xe3c889ff, 0xe4cb8fff, 0xe5cb93ff, 0xdaae5bff, 0xd6a953ff, 0xd8ab52ff, 0xd7ac5aff, 0xd2a54eff, 0xd2a54dff, 0xd4a757ff, 0xd3a758ff, 0xd6ac61ff, 0xcfa554ff, 0xcb9f48ff, 0xcfa556ff, 0xcfa55dff, 0xcda054ff, 0xc89740ff, 0xc39139ff, 0xbd8427ff, 0xbc8224ff, 0xb77b16ff, 0xc59340ff, 0xd2a75fff, 0xd4aa5fff, 0xd7ae64ff, 0xd9af68ff, 0xd9b16aff, 0xd6ad63ff, 0xd7ad65ff, 0xd4a95cff, 0xd5a957ff, 0xd1a44eff, 0xc69c49ff, 0xb48e4aff, 0x92743fff, 0xb37342ff, 0xe2824dff, 0xba7753ff, 0x345069ff, 0x274c6cff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x284c6bff, 0x274c6bff, 0x274c6bff, 0x865344ff, 0xd05927ff, 0xa04b21ff, 0x452f12ff, 0x432e11ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e12ff, 0x432e11ff, 0x4a3013ff, 0xab4f22ff, 0xcf5a27ff, 0x76524aff, 0x254b6cff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x274b6bff, 0x254b6bff, 0x4d5665ff, 0xd27e4fff, 0xe0814cff, 0xb1734cff, 0x9b8355ff, 0xbfa168ff, 0xdec188ff, 0xeacd96ff, 0xe7c88fff, 0xe5c58aff, 0xe1bf7fff, 0xdfbb74ff, 0xdebb73ff, 0xdeb96eff, 0xd7ab54ff, 0xd5a954ff, 0xd7af5fff, 0xdab466ff, 0xdbb567ff, 0xe1c07cff, 0xdebb77ff, 0xd5ac5eff, 0xd0a652ff, 0xd6b06bff, 0xd5b06cff, 0xcea145ff, 0xc7952bff, 0xc79733ff, 0xc08e23ff, 0xbc891fff, 0xc49536ff, 0xd8b15aff, 0xdfbe6eff, 0xe1c57cff, 0xe3c988ff, 0xe2c685ff, 0xdbad5aff, 0xd9aa54ff, 0xd8ab57ff, 0xd9ad5eff, 0xd5a955ff, 0xd3a551ff, 0xd4a755ff, 0xd2a655ff, 0xd0a454ff, 0xd1a653ff, 0xd4aa5fff, 0xd4ae6cff, 0xd1aa6aff, 0xd0a863ff, 0xcca154ff, 0xc79546ff, 0xbd8428ff, 0xb67610ff, 0xb87a1aff, 0xbc8122ff, 0xc89744ff, 0xd1a65aff, 0xd6ab62ff, 0xd5aa5dff, 0xd5ab5eff, 0xd4ab60ff, 0xd4a95bff, 0xd2a757ff, 0xcfa24cff, 0xd1a550ff, 0xcda761ff, 0xbb9960ff, 0x967a4aff, 0xb17443ff, 0xe2854eff, 0xbe7a54ff, 0x38536cff, 0x294f6fff, 0x2a4f6fff, 0x2a4f6fff, 0x2a4f6fff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x2a4f6eff, 0x294f6eff, 0x294f6eff, 0x855546ff, 0xd05b27ff, 0xa24e21ff, 0x493213ff, 0x463113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x473113ff, 0x463113ff, 0x4b3213ff, 0xa85022ff, 0xcf5c26ff, 0x7d5549ff, 0x284f6fff, 0x294f6eff, 0x294e6eff, 0x294f6eff, 0x294f6eff, 0x294f6eff, 0x294f6eff, 0x294f6eff, 0x294f6eff, 0x294f6eff, 0x294e6eff, 0x294e6eff, 0x294e6eff, 0x294e6eff, 0x294e6eff, 0x294e6eff, 0x294e6eff, 0x294e6eff, 0x274e6eff, 0x4c5968ff, 0xd28050ff, 0xe1834dff, 0xb4774fff, 0xa18b61ff, 0xbfa167ff, 0xd8ba7bff, 0xe5c687ff, 0xe6c88aff, 0xe8ca8dff, 0xe5c483ff, 0xe4c27dff, 0xe0bb6fff, 0xdcb361ff, 0xd6aa53ff, 0xd6ad58ff, 0xd7b061ff, 0xd9b36cff, 0xdfbf7cff, 0xe5c68aff, 0xddb977ff, 0xcfa451ff, 0xcea24cff, 0xd2a758ff, 0xd0a44eff, 0xcea143ff, 0xca9b39ff, 0xc89832ff, 0xc18f26ff, 0xc1902dff, 0xcca144ff, 0xdcb55cff, 0xdfbc6eff, 0xe0c37dff, 0xe1c37aff, 0xe0be6eff, 0xe1b86dff, 0xdfb873ff, 0xddb469ff, 0xdaaf62ff, 0xdbb370ff, 0xd6ab60ff, 0xd1a24bff, 0xd5aa5eff, 0xd2a657ff, 0xd5aa5eff, 0xd6ae67ff, 0xd7b171ff, 0xd5ac6bff, 0xd1a862ff, 0xcb9f55ff, 0xc79344ff, 0xbe852bff, 0xb3720aff, 0xb4710aff, 0xb6710eff, 0xba7f21ff, 0xcb9b4dff, 0xd3a760ff, 0xd3a85dff, 0xd5a95cff, 0xd3a85bff, 0xd2a654ff, 0xd0a555ff, 0xcea04bff, 0xd3a95cff, 0xd3ad6eff, 0xbf9f69ff, 0x998053ff, 0xb17748ff, 0xe3874fff, 0xc27e55ff, 0x3c576eff, 0x2a5272ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5272ff, 0x2b5272ff, 0x2c5272ff, 0x2c5272ff, 0x2c5272ff, 0x2c5272ff, 0x2c5271ff, 0x2c5271ff, 0x2b5271ff, 0x2a5272ff, 0x835848ff, 0xcf5d26ff, 0xa55121ff, 0x4d3515ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4a3414ff, 0x4d3515ff, 0xa45121ff, 0xcf5e25ff, 0x845848ff, 0x2b5272ff, 0x2b5271ff, 0x2c5271ff, 0x2c5271ff, 0x2c5271ff, 0x2c5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x2b5271ff, 0x295171ff, 0x4c5b6bff, 0xd18252ff, 0xe2864fff, 0xb57748ff, 0xa18b59ff, 0xc7ad73ff, 0xddbe7fff, 0xe3c37fff, 0xe2c17cff, 0xe4c581ff, 0xe6c884ff, 0xe6c885ff, 0xe6c582ff, 0xe0b96fff, 0xdbb05fff, 0xdbb362ff, 0xdbb76bff, 0xd9b36aff, 0xdab872ff, 0xdab771ff, 0xd8b168ff, 0xcfa551ff, 0xcfa34aff, 0xd0a34aff, 0xcea043ff, 0xd1a651ff, 0xd1a958ff, 0xcfa659ff, 0xcb9e4bff, 0xc5963cff, 0xcea44bff, 0xdcb45eff, 0xdebb6fff, 0xe0c279ff, 0xe0be6cff, 0xddb863ff, 0xdeb872ff, 0xd5a756ff, 0xdaaf66ff, 0xdbb169ff, 0xdab170ff, 0xd5aa5fff, 0xcf9f45ff, 0xd8af66ff, 0xd5ab60ff, 0xd4a95aff, 0xd9b170ff, 0xd9b176ff, 0xd6ac6bff, 0xd2a355ff, 0xce9b44ff, 0xcb9537ff, 0xc48722ff, 0xbf7f13ff, 0xbf7e13ff, 0xbb740aff, 0xb77312ff, 0xc58f3fff, 0xd3aa69ff, 0xd6af72ff, 0xd5ad6aff, 0xd3aa5fff, 0xd2a85dff, 0xd4aa65ff, 0xd5ab62ff, 0xd3a85dff, 0xd2ac6dff, 0xbf9d64ff, 0x9b804eff, 0xb07949ff, 0xe38a50ff, 0xc68256ff, 0x405b71ff, 0x2d5575ff, 0x2e5675ff, 0x2e5675ff, 0x2e5575ff, 0x2e5575ff, 0x2e5575ff, 0x2e5575ff, 0x2e5575ff, 0x2d5574ff, 0x2d5574ff, 0x2d5574ff, 0x2d5574ff, 0x2d5574ff, 0x2d5574ff, 0x2d5575ff, 0x2c5575ff, 0x825a4aff, 0xce5f25ff, 0xa75321ff, 0x503816ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4e3716ff, 0x4f3816ff, 0xa15220ff, 0xce6025ff, 0x8a5b46ff, 0x2d5574ff, 0x2d5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2e5574ff, 0x2d5574ff, 0x2d5574ff, 0x2d5574ff, 0x2c5475ff, 0x4d5e6eff, 0xd18554ff, 0xe28950ff, 0xb87c50ff, 0xa58f61ff, 0xc9ae6fff, 0xe2c688ff, 0xe9cb8dff, 0xe3c37eff, 0xe4c582ff, 0xe5c57fff, 0xe5c681ff, 0xe4c37eff, 0xdeb96fff, 0xddb465ff, 0xdfb76eff, 0xdcb568ff, 0xd8b062ff, 0xd8b368ff, 0xd9b369ff, 0xd9b56cff, 0xd2aa58ff, 0xd1a651ff, 0xcca149ff, 0xcea34fff, 0xd1a757ff, 0xcda350ff, 0xca9e49ff, 0xcba04eff, 0xc7983eff, 0xd2a445ff, 0xdbaf56ff, 0xe1c27cff, 0xe2c37bff, 0xddb965ff, 0xd8b25aff, 0xd9af5bff, 0xd8ab54ff, 0xdaaf63ff, 0xd7ab5dff, 0xd5a756ff, 0xcd9b40ff, 0xd0a14aff, 0xd1a350ff, 0xd5a95bff, 0xd9ad65ff, 0xdbb172ff, 0xd8ab63ff, 0xd7a752ff, 0xdaa74eff, 0xd9a344ff, 0xd5a03bff, 0xd1992bff, 0xce952aff, 0xca9028ff, 0xc68820ff, 0xbb7b15ff, 0xc2862eff, 0xcc9f56ff, 0xd6af6dff, 0xdcb781ff, 0xdab47aff, 0xd8b172ff, 0xdcb578ff, 0xdeb97eff, 0xdab272ff, 0xd5af70ff, 0xbf9c62ff, 0x9c804eff, 0xb07a49ff, 0xe48c51ff, 0xca8556ff, 0x445e74ff, 0x2f5878ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305978ff, 0x305878ff, 0x305878ff, 0x305878ff, 0x2e5878ff, 0x815c4cff, 0xce6124ff, 0xa95621ff, 0x553b17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x513a17ff, 0x523a17ff, 0x9d5320ff, 0xce6224ff, 0x905e43ff, 0x305877ff, 0x2f5877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x305877ff, 0x2e5778ff, 0x4d6071ff, 0xd18755ff, 0xe38b51ff, 0xba7f55ff, 0xa79672ff, 0xceb885ff, 0xe6cf98ff, 0xeacc8fff, 0xe4c37dff, 0xe1bf7aff, 0xe5c580ff, 0xe6c882ff, 0xe3bd6eff, 0xe0b96bff, 0xdcb363ff, 0xd9af5dff, 0xd9ae5bff, 0xd9b061ff, 0xd9b266ff, 0xdbb772ff, 0xdab770ff, 0xd6af5dff, 0xcfa44aff, 0xcba044ff, 0xd1a955ff, 0xcb9f42ff, 0xc5932eff, 0xc59430ff, 0xc39330ff, 0xc99936ff, 0xdcb254ff, 0xe0bd6dff, 0xe3c785ff, 0xdfc17aff, 0xd8b460ff, 0xd5af58ff, 0xd6ab52ff, 0xd4a74cff, 0xd5a956ff, 0xd0a047ff, 0xce9a3aff, 0xc8932dff, 0xce9e42ff, 0xd2a24aff, 0xdca951ff, 0xe1ac52ff, 0xe0aa47ff, 0xe2ad50ff, 0xe3b152ff, 0xdaa342ff, 0xd49937ff, 0xddaa50ff, 0xdaa74bff, 0xd29d39ff, 0xcf9a39ff, 0xcd9637ff, 0xc99036ff, 0xc3892dff, 0xc99545ff, 0xd8af6dff, 0xe2c08dff, 0xdebb87ff, 0xdfb981ff, 0xe4bf89ff, 0xe4bf8cff, 0xe4c08bff, 0xddba85ff, 0xc3a36fff, 0x9c814fff, 0xb07b4aff, 0xe48f53ff, 0xce8857ff, 0x486276ff, 0x315b7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x325c7bff, 0x305b7cff, 0x805f4eff, 0xcd6324ff, 0xaa5822ff, 0x593e19ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x553d18ff, 0x9a541fff, 0xcd6423ff, 0x976041ff, 0x335b79ff, 0x315b7bff, 0x325b7bff, 0x325b7bff, 0x325b7bff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x325b7aff, 0x305b7bff, 0x4c6374ff, 0xd08957ff, 0xe48e52ff, 0xbb8157ff, 0xa6977aff, 0xcdba8fff, 0xe7d3a0ff, 0xedd494ff, 0xe6c16eff, 0xe5c075ff, 0xe5c682ff, 0xe4c27dff, 0xe2bc71ff, 0xe0b96aff, 0xdab059ff, 0xd6a74eff, 0xd8ac57ff, 0xd8af5fff, 0xd8b065ff, 0xd7b16cff, 0xd8b166ff, 0xd8b265ff, 0xd5ad5aff, 0xd5ab57ff, 0xcfa44dff, 0xc89739ff, 0xc18f2bff, 0xc08e25ff, 0xbe8c1eff, 0xd5aa4cff, 0xe1bc69ff, 0xe3c480ff, 0xe3c78cff, 0xddc281ff, 0xd6b66cff, 0xd7b66eff, 0xdcb36fff, 0xdaaf65ff, 0xd8ab5fff, 0xd09f46ff, 0xcf9a3eff, 0xca942cff, 0xd29d39ff, 0xe1ac4bff, 0xeab75dff, 0xecbc68ff, 0xeab75aff, 0xeab964ff, 0xe8b964ff, 0xe3b053ff, 0xe0ac54ff, 0xe4b663ff, 0xe0b25dff, 0xd9a951ff, 0xd3a144ff, 0xd29f48ff, 0xce9b49ff, 0xc48a2cff, 0xc9923bff, 0xdbb175ff, 0xe3c092ff, 0xe0bb85ff, 0xe0ba7bff, 0xe5c28bff, 0xe9c796ff, 0xe9c898ff, 0xe2c191ff, 0xcaaa79ff, 0xa08451ff, 0xb17d4cff, 0xe49153ff, 0xd08b58ff, 0x4d6678ff, 0x325e7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x345f7eff, 0x325f7fff, 0x7f6151ff, 0xcd6523ff, 0xac5b21ff, 0x5d421aff, 0x58401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x59401aff, 0x58401aff, 0x97551fff, 0xcd6622ff, 0x9d633eff, 0x365f7cff, 0x335e7eff, 0x335e7dff, 0x335e7dff, 0x335e7dff, 0x335e7dff, 0x335e7eff, 0x335e7eff, 0x345e7eff, 0x345e7eff, 0x345e7eff, 0x345e7eff, 0x345e7eff, 0x345e7eff, 0x345e7dff, 0x345e7dff, 0x345e7dff, 0x345e7dff, 0x325e7eff, 0x4c6677ff, 0xd08b58ff, 0xe49153ff, 0xbd8457ff, 0xa6997bff, 0xcebe95ff, 0xe6d3a3ff, 0xf0ddabff, 0xedd291ff, 0xe5be67ff, 0xe4c177ff, 0xe3c27dff, 0xe2bd78ff, 0xe0b86eff, 0xdeb567ff, 0xd8ac58ff, 0xdab263ff, 0xd7ae5eff, 0xd6ac58ff, 0xd4ad5bff, 0xd6af61ff, 0xd8b062ff, 0xd4ac5cff, 0xd3ac5dff, 0xd3aa5aff, 0xcb9f47ff, 0xc49433ff, 0xc29027ff, 0xc4932bff, 0xdbb356ff, 0xe1bd68ff, 0xe4c785ff, 0xe3c98fff, 0xdabe7eff, 0xd2b367ff, 0xd5b56dff, 0xdbae64ff, 0xd9ab5fff, 0xd7ab5cff, 0xd1a146ff, 0xc9952eff, 0xd1992dff, 0xe5b04dff, 0xeec273ff, 0xefc477ff, 0xefc472ff, 0xedbd64ff, 0xe9b85fff, 0xe9b861ff, 0xe7b65eff, 0xe8bc70ff, 0xe8c07bff, 0xe4b969ff, 0xddaf58ff, 0xd7a54aff, 0xd9ab60ff, 0xd1a151ff, 0xc78f30ff, 0xc68d2cff, 0xdbaf6fff, 0xe2bc87ff, 0xe3bd86ff, 0xe6c18aff, 0xe9c692ff, 0xebcc9cff, 0xeacc9bff, 0xe4c392ff, 0xcdaf7fff, 0xa58c5eff, 0xb38051ff, 0xe49354ff, 0xd38f59ff, 0x526a7aff, 0x346282ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x346282ff, 0x7e6453ff, 0xcc6722ff, 0xae5e21ff, 0x61451cff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5c431bff, 0x5b431bff, 0x94561fff, 0xcc6822ff, 0xa1663cff, 0x3a627eff, 0x356281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x366281ff, 0x356280ff, 0x356280ff, 0x356280ff, 0x356280ff, 0x356280ff, 0x356280ff, 0x356280ff, 0x356281ff, 0x366281ff, 0x366281ff, 0x346181ff, 0x4c687bff, 0xd08e5aff, 0xe59354ff, 0xc08656ff, 0xa69873ff, 0xcdbd96ff, 0xe5d2a4ff, 0xefddabff, 0xeed7a4ff, 0xe8c779ff, 0xe5bd6aff, 0xe4c281ff, 0xe0bc78ff, 0xddb468ff, 0xd9ae5dff, 0xd7ab56ff, 0xd2a650ff, 0xd3a753ff, 0xd2a751ff, 0xd5ac59ff, 0xd5af5fff, 0xd9b368ff, 0xd2a956ff, 0xcea552ff, 0xd0a758ff, 0xca9f46ff, 0xc79a3cff, 0xc2922cff, 0xcda13cff, 0xe0ba64ff, 0xe0bf6fff, 0xdec077ff, 0xddc180ff, 0xd9bb77ff, 0xd8ba78ff, 0xd6b877ff, 0xd29c3bff, 0xd19b3cff, 0xd4a247ff, 0xd39f40ff, 0xd49f3bff, 0xe3af4cff, 0xf0c87bff, 0xf0cd89ff, 0xf1ce88ff, 0xf0cc88ff, 0xedc880ff, 0xedc476ff, 0xedc57aff, 0xe9bc6bff, 0xe6b767ff, 0xe5b867ff, 0xe4b764ff, 0xdeaf57ff, 0xd4a03bff, 0xd09b37ff, 0xcf9b37ff, 0xc68d22ff, 0xbf8215ff, 0xd19f4dff, 0xdaae6bff, 0xdfb674ff, 0xeac892ff, 0xebc996ff, 0xecca96ff, 0xedcc98ff, 0xe6c590ff, 0xceb07cff, 0xa89166ff, 0xb38253ff, 0xe49555ff, 0xd5925aff, 0x576e7cff, 0x366585ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x366586ff, 0x7d6656ff, 0xcb6922ff, 0xb06021ff, 0x65481dff, 0x5f461cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5f471cff, 0x5e461cff, 0x92571fff, 0xcb6921ff, 0xa66839ff, 0x3f657fff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386584ff, 0x386484ff, 0x386484ff, 0x376483ff, 0x366484ff, 0x4c6a7eff, 0xcf905bff, 0xe69656ff, 0xc18755ff, 0xa5936bff, 0xcdbc91ff, 0xe7d6a7ff, 0xf0deaeff, 0xefdbaaff, 0xefd9a1ff, 0xe6c170ff, 0xd9ad5bff, 0xdab15fff, 0xdcb461ff, 0xdbb262ff, 0xd1a64eff, 0xcea146ff, 0xd0a44cff, 0xd5ac5cff, 0xd4aa57ff, 0xd0a64fff, 0xd0a650ff, 0xd1a850ff, 0xd1a652ff, 0xd2a958ff, 0xcca044ff, 0xc59636ff, 0xc2932fff, 0xd9b35fff, 0xe0be71ff, 0xdebe70ff, 0xdaba6eff, 0xd9ba72ff, 0xdbbf7fff, 0xdbc085ff, 0xd5b673ff, 0xcf9528ff, 0xd39b39ff, 0xd9a54bff, 0xe2ae4dff, 0xeab85bff, 0xf0c577ff, 0xefcb86ff, 0xefcd8bff, 0xf1cf93ff, 0xf0cd91ff, 0xefcb88ff, 0xeecb86ff, 0xecc67fff, 0xe4b55eff, 0xe5b355ff, 0xebc174ff, 0xe4b75eff, 0xdca83eff, 0xd69f32ff, 0xd39e3bff, 0xd29c3aff, 0xce983aff, 0xc18317ff, 0xc48620ff, 0xd29e46ff, 0xddae62ff, 0xe9c38aff, 0xecca92ff, 0xedcb91ff, 0xe8c07bff, 0xe0b76dff, 0xcdab6dff, 0xa58855ff, 0xad7942ff, 0xe59857ff, 0xd8955bff, 0x5c727eff, 0x386888ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x386889ff, 0x7c6958ff, 0xca6a21ff, 0xb16321ff, 0x694b1eff, 0x63491eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x634a1eff, 0x61491eff, 0x905820ff, 0xca6b21ff, 0xaa6a37ff, 0x436880ff, 0x396887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x3a6887ff, 0x386787ff, 0x4d6d81ff, 0xcf925dff, 0xe69957ff, 0xc28854ff, 0xa28f64ff, 0xcebd8eff, 0xeadcb4ff, 0xf2e0b4ff, 0xf0dcacff, 0xefdcacff, 0xe8c980ff, 0xdcae4bff, 0xe0b96bff, 0xe0bd75ff, 0xdab56aff, 0xd2a852ff, 0xd0a44aff, 0xcea048ff, 0xd1a753ff, 0xd2a752ff, 0xd1a653ff, 0xd2a858ff, 0xcfa44eff, 0xcba045ff, 0xca9f43ff, 0xc89a38ff, 0xc69834ff, 0xcea248ff, 0xdebe74ff, 0xdcba6dff, 0xdbb969ff, 0xd9b96cff, 0xd8ba6eff, 0xdbc389ff, 0xd8c187ff, 0xd1b166ff, 0xc7850eff, 0xd49b30ff, 0xeabc69ff, 0xf0c675ff, 0xf2c980ff, 0xf1cc89ff, 0xedc578ff, 0xedc780ff, 0xefcb86ff, 0xf2d396ff, 0xefcb80ff, 0xebc26cff, 0xe7b755ff, 0xe4b14cff, 0xebbd69ff, 0xeabf74ff, 0xe4b764ff, 0xe1b05bff, 0xdca953ff, 0xd4a046ff, 0xd39e42ff, 0xd59f47ff, 0xd09530ff, 0xce9129ff, 0xd29a35ff, 0xd59c34ff, 0xd9a64aff, 0xe1b563ff, 0xe3b460ff, 0xdfaf52ff, 0xd9ac55ff, 0xc4994bff, 0x976d21ff, 0xa56c2aff, 0xe59a58ff, 0xd9985cff, 0x617680ff, 0x3a6b8bff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8aff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3a6b8cff, 0x7b6c5bff, 0xc96c21ff, 0xb26521ff, 0x6c4e1fff, 0x664c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x674c1fff, 0x654c1fff, 0x8e5a20ff, 0xc96d20ff, 0xae6d34ff, 0x486c82ff, 0x3b6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6b8bff, 0x3c6c8bff, 0x3c6c8bff, 0x3c6c8bff, 0x3b6b8bff, 0x4e7186ff, 0xcd9460ff, 0xe79b58ff, 0xc58b55ff, 0xa38f63ff, 0xc9b484ff, 0xe2cea0ff, 0xecd8a7ff, 0xefdbabff, 0xefdcadff, 0xead090ff, 0xe5be68ff, 0xe4c37eff, 0xdcb76eff, 0xd7af60ff, 0xd1a652ff, 0xd0a54eff, 0xcfa24bff, 0xcca049ff, 0xd2a856ff, 0xd2aa5cff, 0xd0a85aff, 0xcba047ff, 0xcda248ff, 0xcda246ff, 0xca9c3aff, 0xc3942aff, 0xcfa74fff, 0xddbe77ff, 0xdbb96bff, 0xdaba68ff, 0xd9bc70ff, 0xd9bc77ff, 0xd8be7dff, 0xd8be82ff, 0xd2b470ff, 0xcb881fff, 0xe7b559ff, 0xf3d091ff, 0xf3d492ff, 0xf1cc84ff, 0xeec67aff, 0xeec471ff, 0xeec679ff, 0xf3d491ff, 0xf3d694ff, 0xf0cd82ff, 0xeabd5fff, 0xe8b854ff, 0xe9b762ff, 0xe8b969ff, 0xe4b35cff, 0xdeac51ff, 0xdeac5bff, 0xddaa58ff, 0xdba84fff, 0xdda642ff, 0xdfa948ff, 0xe5b869ff, 0xe6b969ff, 0xe2b35dff, 0xe0ad4fff, 0xdca746ff, 0xd69f33ff, 0xd79e2eff, 0xd79d2cff, 0xd0972cff, 0xb98017ff, 0x936104ff, 0xa46925ff, 0xe18d53ff, 0xce8551ff, 0x54443aff, 0x333336ff, 0x353436ff, 0x353436ff, 0x353537ff, 0x353537ff, 0x353537ff, 0x353537ff, 0x353538ff, 0x353538ff, 0x353538ff, 0x353538ff, 0x36373aff, 0x36373bff, 0x36373bff, 0x36373bff, 0x33363bff, 0x764f2fff, 0xc96f21ff, 0xb46821ff, 0x705221ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x6a4f21ff, 0x694f21ff, 0x8c5b21ff, 0xc86f20ff, 0xb16725ff, 0x443e3aff, 0x35383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36383dff, 0x36393eff, 0x363a3fff, 0x363a3fff, 0x35393fff, 0x403d3eff, 0xb77b4dff, 0xe49355ff, 0xc58750ff, 0xa39060ff, 0xcab57eff, 0xe1c889ff, 0xecd497ff, 0xf1deabff, 0xf3e1b1ff, 0xeed7a0ff, 0xe5c274ff, 0xe1be75ff, 0xdcb76dff, 0xddb974ff, 0xdab36bff, 0xd2a754ff, 0xd3a754ff, 0xcea14bff, 0xd2a859ff, 0xcea452ff, 0xcba14dff, 0xc99d44ff, 0xc59636ff, 0xc99d3fff, 0xc69734ff, 0xc08d21ff, 0xd4ae5dff, 0xddbf7bff, 0xd9b86dff, 0xd8b460ff, 0xdabc74ff, 0xddc286ff, 0xdbc087ff, 0xdac08aff, 0xd4b679ff, 0xebb95fff, 0xf4cf8bff, 0xf3d293ff, 0xf3d28fff, 0xf5d899ff, 0xf3d291ff, 0xf0cb84ff, 0xf2d294ff, 0xf1d18bff, 0xf0ce82ff, 0xf0cc82ff, 0xefc877ff, 0xefc97eff, 0xe8bb69ff, 0xdeaa48ff, 0xdca745ff, 0xdfa94dff, 0xdfaf5bff, 0xe1b056ff, 0xe9bb68ff, 0xebbe6dff, 0xeec376ff, 0xeeca87ff, 0xefca89ff, 0xefca87ff, 0xedc57aff, 0xe8bb65ff, 0xe4b250ff, 0xdea739ff, 0xd89d27ff, 0xcf9114ff, 0xba7f0bff, 0x98690bff, 0xa46928ff, 0xe0854fff, 0xcf7a47ff, 0x4c220dff, 0x2e0d00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x2d0d00ff, 0x70380eff, 0xc87120ff, 0xb56a21ff, 0x745522ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6d5322ff, 0x6c5222ff, 0x8b5d21ff, 0xc67120ff, 0xb4651cff, 0x411a04ff, 0x2f0e00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300f00ff, 0x300e00ff, 0x331101ff, 0xab6539ff, 0xe58d53ff, 0xc5834cff, 0xa38f5cff, 0xcdbb84ff, 0xe8d599ff, 0xf4e2acff, 0xf5e4b1ff, 0xf2e2b2ff, 0xedd79fff, 0xe4c06fff, 0xe2be71ff, 0xdeba71ff, 0xdbb56dff, 0xd8b166ff, 0xd2a956ff, 0xcda14bff, 0xcc9f43ff, 0xcda24dff, 0xca9d48ff, 0xc69739ff, 0xc59537ff, 0xc49230ff, 0xc29029ff, 0xc08f28ff, 0xc19025ff, 0xd7b568ff, 0xdec285ff, 0xd8b872ff, 0xd4b05fff, 0xd5b364ff, 0xd8bb77ff, 0xdbc28aff, 0xd9c290ff, 0xd9bf8aff, 0xf4cf8dff, 0xf7d8a0ff, 0xf4d697ff, 0xf1d191ff, 0xf6dba7ff, 0xf6dca9ff, 0xf3d69aff, 0xf2d392ff, 0xeec670ff, 0xf1cd82ff, 0xf1cf8aff, 0xf3d492ff, 0xf1d191ff, 0xedc67aff, 0xe3b257ff, 0xe1ae52ff, 0xe3b057ff, 0xe8b864ff, 0xefc778ff, 0xf0c97aff, 0xf1cc82ff, 0xf1cd82ff, 0xf2d28fff, 0xf3d296ff, 0xf0cc8aff, 0xefc87cff, 0xedc26dff, 0xecbe63ff, 0xebbb60ff, 0xe3ac40ff, 0xdaa433ff, 0xc5932dff, 0xa27b2bff, 0xa77235ff, 0xe18a51ff, 0xd17f4aff, 0x4e250eff, 0x2e0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x6e390dff, 0xc77220ff, 0xb66d20ff, 0x785823ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x715623ff, 0x705523ff, 0x8a5e22ff, 0xc57320ff, 0xb7691cff, 0x461e05ff, 0x2f0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x331201ff, 0xa96739ff, 0xe69255ff, 0xc58348ff, 0xa08750ff, 0xccba80ff, 0xe7d396ff, 0xf3dea1ff, 0xf5e4afff, 0xf2e0acff, 0xedd79bff, 0xeacc80ff, 0xe8ca85ff, 0xe6c88aff, 0xdebe7cff, 0xd8b164ff, 0xd3aa58ff, 0xcca24bff, 0xc89a3cff, 0xca9f49ff, 0xc99c47ff, 0xc59438ff, 0xc49232ff, 0xc39130ff, 0xbd891eff, 0xb98517ff, 0xc1902aff, 0xd7b465ff, 0xdcc081ff, 0xd9ba7bff, 0xd5b46dff, 0xd3af5fff, 0xd3b266ff, 0xd9bc7cff, 0xdbc089ff, 0xd9be86ff, 0xf3d499ff, 0xf4d59fff, 0xf3d79aff, 0xf1d393ff, 0xf1d193ff, 0xf3d599ff, 0xf0cd82ff, 0xebbd5eff, 0xedc163ff, 0xf1ce88ff, 0xf0cd89ff, 0xf6d9a3ff, 0xf1d190ff, 0xecc376ff, 0xe7b962ff, 0xe6b55cff, 0xecc06aff, 0xf3d187ff, 0xf4d690ff, 0xf2d183ff, 0xf3cf7eff, 0xf6d78fff, 0xf6d891ff, 0xf3d289ff, 0xf2cd7cff, 0xf4cf7cff, 0xf5d27fff, 0xf5d17fff, 0xf5cf7cff, 0xf3c970ff, 0xecc56fff, 0xd6b568ff, 0xaf924eff, 0xae7d45ff, 0xe18d53ff, 0xd2834cff, 0x4f260fff, 0x2e0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x6d380dff, 0xc7741fff, 0xb86f20ff, 0x7b5b24ff, 0x745925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x755925ff, 0x735825ff, 0x8a6023ff, 0xc4741fff, 0xb96c1cff, 0x4a2105ff, 0x2e0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x321201ff, 0xa76739ff, 0xe79657ff, 0xc6864aff, 0x9d8044ff, 0xc8b276ff, 0xe6d293ff, 0xefd993ff, 0xf4e1a4ff, 0xf7e7b7ff, 0xf1dda7ff, 0xecd08bff, 0xe8c882ff, 0xe5c481ff, 0xddbb74ff, 0xd7b163ff, 0xd6b060ff, 0xd3aa5aff, 0xc99940ff, 0xc49335ff, 0xc39133ff, 0xc18d29ff, 0xc29232ff, 0xc08e2cff, 0xba8518ff, 0xb77e0aff, 0xbc8a1fff, 0xd3ae5eff, 0xddbe83ff, 0xdcbe84ff, 0xd7b879ff, 0xd4b26bff, 0xd1ae60ff, 0xd4b268ff, 0xd9bc7dff, 0xd8ba7eff, 0xf3d49aff, 0xf4d9a4ff, 0xf2d69aff, 0xf4d69dff, 0xf1d092ff, 0xefcc83ff, 0xecc16bff, 0xefc777ff, 0xf1cc86ff, 0xf0ce8dff, 0xefce8dff, 0xf4d799ff, 0xf4d593ff, 0xf1cf8dff, 0xebc06aff, 0xeec062ff, 0xf4d282ff, 0xf6d78dff, 0xf8db97ff, 0xf7da92ff, 0xf5d580ff, 0xf8da8aff, 0xf9da89ff, 0xf9d784ff, 0xf9d780ff, 0xfad883ff, 0xfad886ff, 0xfad988ff, 0xfada8bff, 0xf8d888ff, 0xf1d07cff, 0xdcbf74ff, 0xb49c5fff, 0xaf8249ff, 0xe29154ff, 0xd4874dff, 0x50280fff, 0x2e0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x6a380cff, 0xc6761eff, 0xb87120ff, 0x7f5e25ff, 0x785b26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x785c26ff, 0x775b26ff, 0x8a6224ff, 0xc2761fff, 0xbb701cff, 0x4e2506ff, 0x2e0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x321101ff, 0xa56838ff, 0xe89a59ff, 0xc98a4eff, 0x997c40ff, 0xc3a65fff, 0xe6d093ff, 0xf3e0a2ff, 0xf4e1a5ff, 0xf7e8bbff, 0xf3e2b3ff, 0xedd498ff, 0xeace8cff, 0xe6c784ff, 0xdcb86eff, 0xd6b05eff, 0xd4ac58ff, 0xd0a650ff, 0xca9e47ff, 0xc5963bff, 0xc18e2dff, 0xbf8b25ff, 0xc1902eff, 0xbe8c27ff, 0xb98313ff, 0xb77d08ff, 0xb98312ff, 0xcfa74fff, 0xdbbd7eff, 0xdbbe82ff, 0xd7b877ff, 0xd4b575ff, 0xd4b375ff, 0xceac64ff, 0xcca859ff, 0xcda453ff, 0xf3d79fff, 0xf3d8a3ff, 0xf6daa6ff, 0xf3d59aff, 0xefc97eff, 0xebba61ff, 0xebbd71ff, 0xefcc8eff, 0xf3d398ff, 0xf4d59aff, 0xf5d89aff, 0xf7dc9eff, 0xf5d99cff, 0xf1d08dff, 0xf2cb79ff, 0xf6d685ff, 0xf8db92ff, 0xf9de97ff, 0xf9dd93ff, 0xfadc8cff, 0xfada81ff, 0xfadd89ff, 0xfbdd88ff, 0xfbdb7cff, 0xfbdb83ff, 0xfbdd86ff, 0xfbdd8aff, 0xfbdd88ff, 0xfbde8cff, 0xfbe08eff, 0xf4d98aff, 0xdfc37bff, 0xb7a065ff, 0xb18751ff, 0xe39556ff, 0xd58b4fff, 0x522910ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x68370bff, 0xc5771dff, 0xba741fff, 0x836127ff, 0x7b5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7c5f28ff, 0x7b5e28ff, 0x8b6425ff, 0xc1771eff, 0xbe731cff, 0x522807ff, 0x2e0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x311101ff, 0xa26838ff, 0xe99e5aff, 0xcb8e50ff, 0x997c44ff, 0xc2a25bff, 0xddb974ff, 0xf4dfa9ff, 0xf2dd9eff, 0xf4e2b0ff, 0xf2deb1ff, 0xecd497ff, 0xedd295ff, 0xe7c785ff, 0xddbb72ff, 0xd7b165ff, 0xd4ad5eff, 0xd2aa59ff, 0xcb9e47ff, 0xc08f2dff, 0xc08c29ff, 0xc18e2cff, 0xbe8d29ff, 0xbb8920ff, 0xbc891fff, 0xba8515ff, 0xb88310ff, 0xcda44bff, 0xdbbc7eff, 0xdcbf86ff, 0xd6b878ff, 0xd3b574ff, 0xd4b57aff, 0xcead69ff, 0xc9a14dff, 0xc6983aff, 0xf5ddaeff, 0xf6dbabff, 0xf6d9a1ff, 0xf1cf84ff, 0xebbb5dff, 0xeebf6eff, 0xefc880ff, 0xf0cf91ff, 0xf5d69fff, 0xf9e0adff, 0xf9dfa6ff, 0xf8dea6ff, 0xf5d698ff, 0xf6d48bff, 0xf9d67bff, 0xfbda85ff, 0xfbe39aff, 0xfce7a4ff, 0xfce49eff, 0xfbe191ff, 0xfce08aff, 0xfce08bff, 0xfcdf83ff, 0xfbde7dff, 0xfcdf88ff, 0xfce188ff, 0xfce18aff, 0xfce493ff, 0xfce596ff, 0xfce38dff, 0xf5dc89ff, 0xdfc679ff, 0xb8a25fff, 0xb18a50ff, 0xe39957ff, 0xd78f51ff, 0x532b11ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x66370bff, 0xc4791dff, 0xbb761fff, 0x866428ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x7f6229ff, 0x8c6627ff, 0xbf791eff, 0xbf771cff, 0x562c07ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x311100ff, 0xa06838ff, 0xe9a25cff, 0xcc9050ff, 0x94763aff, 0xbb9953ff, 0xdfbe75ff, 0xefd595ff, 0xf0d894ff, 0xf2e0a8ff, 0xf0dda9ff, 0xeed89aff, 0xeed69bff, 0xe6ca8aff, 0xdcba71ff, 0xd7b266ff, 0xd6af64ff, 0xcea450ff, 0xc8993fff, 0xbf8e2bff, 0xc08e30ff, 0xc39438ff, 0xc29233ff, 0xbd8b25ff, 0xbd8922ff, 0xbb871bff, 0xb8830fff, 0xcda54eff, 0xdbbd7eff, 0xdcc087ff, 0xd7b97aff, 0xd4b36dff, 0xd0ae67ff, 0xcca858ff, 0xcba54eff, 0xcfa852ff, 0xf5dca9ff, 0xf6dba6ff, 0xf4d693ff, 0xf1cc7eff, 0xf0c573ff, 0xf1c877ff, 0xf7d99bff, 0xf7dba5ff, 0xf4d49dff, 0xf4d49bff, 0xf6d99eff, 0xf5d59aff, 0xf4d18aff, 0xf8d476ff, 0xfad672ff, 0xfbdc82ff, 0xfce597ff, 0xfceaa1ff, 0xfceba1ff, 0xfceda0ff, 0xfcea9aff, 0xfce896ff, 0xfce58cff, 0xfce587ff, 0xfce689ff, 0xfce791ff, 0xfce895ff, 0xfce998ff, 0xfcea9dff, 0xfce895ff, 0xf5df8fff, 0xe0ca7eff, 0xb8a561ff, 0xb18d52ff, 0xe49d59ff, 0xd89453ff, 0x542c12ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x64360aff, 0xc37b1cff, 0xbb791eff, 0x8a6729ff, 0x82652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x83652aff, 0x82652aff, 0x8d6928ff, 0xbe7a1dff, 0xc17a1bff, 0x5b2f08ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x9e6937ff, 0xeaa75eff, 0xcd9451ff, 0x917234ff, 0xb28e47ff, 0xd6b15cff, 0xedd28aff, 0xf2dc99ff, 0xf2dfa0ff, 0xf2dea5ff, 0xf0dba0ff, 0xefd9a0ff, 0xe6ca88ff, 0xddba6fff, 0xdab76bff, 0xd6b368ff, 0xcea550ff, 0xc79a40ff, 0xc39435ff, 0xbe8b2bff, 0xbe8b29ff, 0xbf8d2bff, 0xba871dff, 0xb88318ff, 0xb88518ff, 0xbb8718ff, 0xcca751ff, 0xdcbf82ff, 0xdbc28bff, 0xd8be85ff, 0xd4b570ff, 0xd1b06aff, 0xceac5dff, 0xceaa55ff, 0xd1ae5eff, 0xf5dba7ff, 0xf1cf83ff, 0xf3cf83ff, 0xf1cd89ff, 0xf3cf87ff, 0xf1ca7dff, 0xf3ce8bff, 0xf7dca7ff, 0xf4d59cff, 0xf5d699ff, 0xf4d798ff, 0xf2cf8aff, 0xf6d17cff, 0xf9d36dff, 0xf9d672ff, 0xfada7bff, 0xfce391ff, 0xfce89cff, 0xfdf0b0ff, 0xfdf1afff, 0xfcf2acff, 0xfcf0a6ff, 0xfcea9bff, 0xfce892ff, 0xfce78dff, 0xfce893ff, 0xfce891ff, 0xfce793ff, 0xfce997ff, 0xfce895ff, 0xf5df8fff, 0xe0ca7fff, 0xb9a666ff, 0xb19057ff, 0xe5a15bff, 0xda9855ff, 0x562e13ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x62350aff, 0xc37d1bff, 0xbc7b1eff, 0x8d6a2aff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x86682cff, 0x8e6b2aff, 0xbd7c1dff, 0xc27d1bff, 0x603409ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x9b6937ff, 0xebab60ff, 0xcf9854ff, 0x8f7134ff, 0xad8b42ff, 0xc9a351ff, 0xdeb963ff, 0xf0d78bff, 0xf4e2a4ff, 0xf4e3adff, 0xf0dca5ff, 0xedd69bff, 0xe5c883ff, 0xdcb86bff, 0xd8b466ff, 0xd2ad5bff, 0xd1aa59ff, 0xca9e47ff, 0xc5963aff, 0xc29131ff, 0xbe8a23ff, 0xbc8722ff, 0xb68318ff, 0xb57f12ff, 0xb78418ff, 0xbb881cff, 0xc89d3fff, 0xd9bb77ff, 0xdcbf87ff, 0xdbc089ff, 0xd9bd86ff, 0xd4b675ff, 0xd0af63ff, 0xd0ae5fff, 0xd1b168ff, 0xefce91ff, 0xefc87eff, 0xf4d18eff, 0xf1ce8aff, 0xf3d392ff, 0xf0cc87ff, 0xedbf71ff, 0xf6d69dff, 0xf7d8a0ff, 0xf5d595ff, 0xf2cf88ff, 0xf3cb79ff, 0xf5ce6bff, 0xf7d16cff, 0xf9d574ff, 0xf8d473ff, 0xf8d77eff, 0xfbe18aff, 0xfceeb1ff, 0xfcedaaff, 0xfceea6ff, 0xfce998ff, 0xfce992ff, 0xfcea99ff, 0xfce791ff, 0xfceb9dff, 0xfce893ff, 0xfce790ff, 0xfce795ff, 0xfce895ff, 0xf5df8eff, 0xe0cc84ff, 0xb9a96aff, 0xb19156ff, 0xe6a55dff, 0xdb9c57ff, 0x573014ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0e00ff, 0x603509ff, 0xc27e1bff, 0xbd7d1dff, 0x916d2bff, 0x896b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x8a6b2dff, 0x896b2dff, 0x906d2bff, 0xbb7d1dff, 0xc2801aff, 0x65390aff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2f0f00ff, 0x996836ff, 0xecaf62ff, 0xd09b55ff, 0x8e7136ff, 0xaa8639ff, 0xc59d48ff, 0xd0a243ff, 0xe4c26eff, 0xf2dc9aff, 0xf4e2a8ff, 0xf1dda6ff, 0xead291ff, 0xe8cb88ff, 0xe0be72ff, 0xdab76bff, 0xd3ac5aff, 0xcfa652ff, 0xc99e45ff, 0xc79d46ff, 0xc89c45ff, 0xc39234ff, 0xb9851eff, 0xb68117ff, 0xb47f13ff, 0xb68216ff, 0xb68318ff, 0xc2922eff, 0xd5b46dff, 0xdabd7fff, 0xdbbd82ff, 0xd9bc83ff, 0xd5b877ff, 0xd1af61ff, 0xd2b26cff, 0xd1b36fff, 0xf1d298ff, 0xf1cf90ff, 0xf6d9a5ff, 0xf3d393ff, 0xf2cf89ff, 0xf0ca80ff, 0xedc170ff, 0xf0c87eff, 0xefc77eff, 0xf1cb84ff, 0xf4d294ff, 0xf0c87bff, 0xf1c662ff, 0xf0c762ff, 0xe9bc54ff, 0xe4b03fff, 0xe1ab37ff, 0xf6d372ff, 0xfce79cff, 0xfceaa1ff, 0xfceb9eff, 0xfce894ff, 0xfce997ff, 0xfce792ff, 0xfce895ff, 0xfce99aff, 0xfce794ff, 0xfce795ff, 0xfce693ff, 0xfce489ff, 0xf5e08aff, 0xe1ce88ff, 0xbaaa71ff, 0xb19359ff, 0xe6a95eff, 0xdda159ff, 0x593215ff, 0x2e0e00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2e0f00ff, 0x5f3408ff, 0xc1801aff, 0xbd7f1dff, 0x94702cff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x8d6e2eff, 0x92702dff, 0xba7f1eff, 0xc3831aff, 0x6b3e0aff, 0x2e0f00ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x301000ff, 0x2f0f00ff, 0x976836ff, 0xedb364ff, 0xd3a057ff, 0x8e6f32ff, 0xa98436ff, 0xc1963bff, 0xcb9c3dff, 0xcc9e3fff, 0xd8b15bff, 0xeacd82ff, 0xf2dea5ff, 0xedd69bff, 0xedd497ff, 0xe3c47eff, 0xd8b466ff, 0xd5b064ff, 0xcea755ff, 0xcaa04bff, 0xc89f4cff, 0xc89e4cff, 0xc5983fff, 0xb68218ff, 0xb47e0dff, 0xb37d0dff, 0xb17d0eff, 0xb27d0fff, 0xb7851dff, 0xcfac5bff, 0xd9bc7cff, 0xd9bd83ff, 0xd9bc86ff, 0xd7ba7eff, 0xd1b169ff, 0xd5b679ff, 0xd2b372ff, 0xf4d7a1ff, 0xf0cc8eff, 0xf3d193ff, 0xf5d691ff, 0xf9da9aff, 0xf7d795ff, 0xf3d085ff, 0xf1c977ff, 0xf0c77bff, 0xf2ce8dff, 0xf2ce8aff, 0xefc57aff, 0xecbc67ff, 0xe8b554ff, 0xe1aa3bff, 0xdda128ff, 0xdb9c1bff, 0xe4af3aff, 0xf7da7fff, 0xfae697ff, 0xfce79aff, 0xfcea9fff, 0xfceba2ff, 0xfce693ff, 0xfce48cff, 0xfbe389ff, 0xfce590ff, 0xfce490ff, 0xfce48fff, 0xfce590ff, 0xf6e191ff, 0xe1cd83ff, 0xbaab72ff, 0xb19256ff, 0xe7ad60ff, 0xdea55bff, 0x5d3717ff, 0x321201ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x321201ff, 0x5f3609ff, 0xc18219ff, 0xbd821cff, 0x98732dff, 0x907130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x917130ff, 0x94722eff, 0xb8811eff, 0xc38519ff, 0x72450cff, 0x321202ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x341402ff, 0x331301ff, 0x966a36ff, 0xeeb765ff, 0xd5a358ff, 0x8e6d2cff, 0xa78131ff, 0xbf9338ff, 0xca9c41ff, 0xc89736ff, 0xc7932cff, 0xd3a546ff, 0xeacc80ff, 0xeed595ff, 0xedd59bff, 0xe2c57fff, 0xd6b364ff, 0xd5b165ff, 0xcea553ff, 0xc99f4aff, 0xc59b45ff, 0xc59942ff, 0xc19234ff, 0xb7831cff, 0xb47e11ff, 0xb47e0fff, 0xb58118ff, 0xb0790bff, 0xb47e14ff, 0xc9a046ff, 0xd8ba77ff, 0xdbc08cff, 0xdac192ff, 0xd8be8bff, 0xd3b576ff, 0xd4b678ff, 0xcfad63ff, 0xf5dba6ff, 0xf0cf8eff, 0xf2cf8aff, 0xf8db9eff, 0xf8dba2ff, 0xf8da9cff, 0xf4d389ff, 0xf2cc7dff, 0xf4d18eff, 0xf3d090ff, 0xf2c97eff, 0xedc274ff, 0xedbc69ff, 0xe8b558ff, 0xe2ad42ff, 0xdda32dff, 0xda9a19ff, 0xda9918ff, 0xf0ca6aff, 0xfce89fff, 0xfce8a2ff, 0xfce89cff, 0xfbe391ff, 0xfbe28cff, 0xfbe18cff, 0xfbe38fff, 0xfce799ff, 0xfbe18dff, 0xfbe28eff, 0xfbe28cff, 0xf5df8fff, 0xe1cd87ff, 0xbaa869ff, 0xb09256ff, 0xe8b062ff, 0xe0aa5dff, 0x633c1aff, 0x381703ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x381704ff, 0x62380aff, 0xc08319ff, 0xbe841cff, 0x9b772eff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x947431ff, 0x977530ff, 0xb8821eff, 0xc48819ff, 0x7b4d0eff, 0x381704ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x3a1904ff, 0x381703ff, 0x966c37ff, 0xeebb67ff, 0xd5a658ff, 0x845d1cff, 0x96661bff, 0xa46d1bff, 0xac7c23ff, 0xb57f1dff, 0xaf781bff, 0xad771aff, 0xc99941ff, 0xeacc80ff, 0xebd293ff, 0xe3c782ff, 0xd9b76bff, 0xd3ad5eff, 0xcea652ff, 0xc9a24dff, 0xc49843ff, 0xc2953cff, 0xbd8b2eff, 0xb78423ff, 0xb4801dff, 0xb57f18ff, 0xb57f16ff, 0xb37d10ff, 0xb58013ff, 0xc59a40ff, 0xd5b267ff, 0xdbbe82ff, 0xdbbf8aff, 0xd8bb82ff, 0xd3b778ff, 0xd2b36fff, 0xcfac5fff, 0xf7dda9ff, 0xf5d99dff, 0xf5d99eff, 0xf5d89dff, 0xf4d697ff, 0xf4d389ff, 0xf1ca7bff, 0xf1ca7fff, 0xf2cd8bff, 0xf2ce8cff, 0xf1ca81ff, 0xeec370ff, 0xedbe67ff, 0xe7b353ff, 0xe2aa3cff, 0xdda32fff, 0xda9c1eff, 0xd99b20ff, 0xe9bc55ff, 0xfbe499ff, 0xfae49aff, 0xfae190ff, 0xf9dd84ff, 0xf9db81ff, 0xf9dd89ff, 0xfade88ff, 0xfadd87ff, 0xfad97bff, 0xfadb82ff, 0xfbdf8eff, 0xf5db89ff, 0xe0c87eff, 0xbaa76dff, 0xb0955cff, 0xe8b464ff, 0xe2ae60ff, 0x68421dff, 0x3d1c05ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3d1c06ff, 0x643b0cff, 0xbf8518ff, 0xbe861bff, 0x9f7a2eff, 0x977733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x987733ff, 0x997832ff, 0xb7841fff, 0xc48a18ff, 0x82540fff, 0x3e1c06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3f1e06ff, 0x3d1c05ff, 0x966e37ff, 0xefbf69ff, 0xd5a758ff, 0x6d380dff, 0x621603ff, 0x490b03ff, 0x271204ff, 0x351d02ff, 0x291302ff, 0x461403ff, 0x803308ff, 0xbd9249ff, 0xe8cc84ff, 0xe8ce8dff, 0xddbc72ff, 0xd5b165ff, 0xcea95bff, 0xcba558ff, 0xc69c49ff, 0xc0923bff, 0xb8882dff, 0xb5852cff, 0xb6872fff, 0xb58223ff, 0xb17b12ff, 0xb37d12ff, 0xb6821cff, 0xba8727ff, 0xcba44eff, 0xd5b772ff, 0xd7ba7eff, 0xd5b779ff, 0xd3b578ff, 0xcfb16dff, 0xcfae65ff, 0xf8dfa2ff, 0xf9e1a7ff, 0xf6db9dff, 0xf7dc9dff, 0xf6da9aff, 0xf5d38fff, 0xf2ca7fff, 0xf3ce81ff, 0xf3ce88ff, 0xf2cb84ff, 0xf0c77eff, 0xeec171ff, 0xeab95eff, 0xeab95fff, 0xe3af48ff, 0xe0a736ff, 0xdea531ff, 0xda9f26ff, 0xebc15fff, 0xf8df91ff, 0xf9de8dff, 0xf9df8fff, 0xf8dc85ff, 0xf7d87eff, 0xf8d97fff, 0xf8dc87ff, 0xf6d475ff, 0xf6d169ff, 0xf8d87cff, 0xfadb86ff, 0xf3d47fff, 0xdfc375ff, 0xb9a46aff, 0xaf9358ff, 0xe9b865ff, 0xe4b362ff, 0x6d481fff, 0x422107ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x432108ff, 0x663f0dff, 0xbe8617ff, 0xbe881aff, 0xa27d2fff, 0x9b7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9c7a34ff, 0x9b7a34ff, 0x9c7a33ff, 0xb68520ff, 0xc48c17ff, 0x895b11ff, 0x442208ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x452309ff, 0x422107ff, 0x977038ff, 0xf0c36bff, 0xd8ac5bff, 0x6e3b0eff, 0x5f1602ff, 0x591103ff, 0x3d0902ff, 0x240001ff, 0x430b02ff, 0x732b04ff, 0x833402ff, 0x995c24ff, 0xe3c57cff, 0xead293ff, 0xe1c280ff, 0xd6b56cff, 0xcda85aff, 0xc79e4cff, 0xc0933cff, 0xbb8f38ff, 0xb4852bff, 0xb6852dff, 0xb7872dff, 0xb27e1bff, 0xb07810ff, 0xb07810ff, 0xb07813ff, 0xac7511ff, 0xc19436ff, 0xd1b065ff, 0xd3b677ff, 0xd1b36fff, 0xcfb26dff, 0xcfb06aff, 0xcfaf68ff, 0xf8de9fff, 0xf9e0a7ff, 0xf8dfa5ff, 0xf8dda5ff, 0xf6d999ff, 0xf2d18eff, 0xf1cb82ff, 0xf1cc83ff, 0xf0c883ff, 0xf0c57dff, 0xefc477ff, 0xedc06dff, 0xecbd65ff, 0xe8b75eff, 0xe4b24fff, 0xe4b251ff, 0xdda635ff, 0xdda42dff, 0xefc86fff, 0xf6da89ff, 0xf6da85ff, 0xf6d987ff, 0xf6d985ff, 0xf5d67dff, 0xf4d373ff, 0xf4d16eff, 0xf2cb60ff, 0xf3cc62ff, 0xf5d173ff, 0xf5d171ff, 0xefce75ff, 0xdbbe72ff, 0xb79f5fff, 0xad9256ff, 0xeabc67ff, 0xe5b764ff, 0x734d22ff, 0x48260aff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x48260bff, 0x69420eff, 0xbd8817ff, 0xbe8a1aff, 0xa57f30ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0x9f7d35ff, 0xb68721ff, 0xc38e17ff, 0x906312ff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x4a280bff, 0x482609ff, 0x987238ff, 0xf0c66cff, 0xdcb35eff, 0x7a4e0fff, 0x7e4402ff, 0x843d04ff, 0x7c2b03ff, 0x7e2503ff, 0x8b3904ff, 0x904804ff, 0x8b4203ff, 0x874408ff, 0xc8a14eff, 0xe8ca82ff, 0xe6cb8fff, 0xd8b973ff, 0xcfad63ff, 0xc6a053ff, 0xbf933eff, 0xba8e38ff, 0xb5862cff, 0xb48127ff, 0xb27c1bff, 0xac730cff, 0xa66605ff, 0xa66905ff, 0xa86f08ff, 0xa8700aff, 0xb48323ff, 0xc6a050ff, 0xc8a760ff, 0xc8a458ff, 0xc8a65aff, 0xccac65ff, 0xccab66ff, 0xf6db9cff, 0xf9dda1ff, 0xf7dc9eff, 0xf3d495ff, 0xf2d08aff, 0xf0c97dff, 0xf0cb88ff, 0xefca8aff, 0xefc682ff, 0xeec275ff, 0xebbb67ff, 0xebbb65ff, 0xebbc69ff, 0xe8b55aff, 0xe3ae49ff, 0xe1ab40ff, 0xe0aa3bff, 0xe0ab3bff, 0xefc869ff, 0xf5d783ff, 0xf5d883ff, 0xf5d985ff, 0xf3d47aff, 0xf3d06fff, 0xf3d070ff, 0xf2ce72ff, 0xf0c860ff, 0xf0c85fff, 0xf1c968ff, 0xf0c65fff, 0xecc66aff, 0xd8b767ff, 0xb49855ff, 0xac9152ff, 0xebc069ff, 0xe7bc66ff, 0x785325ff, 0x4d2b0cff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4e2c0dff, 0x6c450fff, 0xbc8916ff, 0xbf8c19ff, 0xa88331ff, 0xa28037ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28036ff, 0xa28037ff, 0xa38036ff, 0xb68922ff, 0xc39016ff, 0x966913ff, 0x502d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4f2d0dff, 0x4d2b0cff, 0x987439ff, 0xf0ca6eff, 0xdeb861ff, 0x805815ff, 0x865405ff, 0x965904ff, 0x9b5904ff, 0x985104ff, 0x954e04ff, 0x954f04ff, 0x965104ff, 0x934e03ff, 0xa26716ff, 0xdbb769ff, 0xe5c989ff, 0xdabb77ff, 0xd2b16cff, 0xcba964ff, 0xc39c4fff, 0xbb8e39ff, 0xb38023ff, 0xb07c1dff, 0xae761cff, 0xa76a0cff, 0xa26606ff, 0xa76e10ff, 0xa8700eff, 0xa97210ff, 0xaf7c20ff, 0xb5852aff, 0xba8c33ff, 0xb98930ff, 0xc29848ff, 0xcdac6bff, 0xceac6cff, 0xf7dda7ff, 0xf9dfa9ff, 0xf9e1aaff, 0xf8dba1ff, 0xf3d18dff, 0xf0c87cff, 0xeec780ff, 0xecc279ff, 0xedc47eff, 0xeabb6bff, 0xe9b660ff, 0xe9b964ff, 0xe8ba68ff, 0xe6b259ff, 0xe3ae47ff, 0xe0aa3eff, 0xdfaa3dff, 0xe1ab3fff, 0xf0ca6dff, 0xf4d989ff, 0xf5d98aff, 0xf4d681ff, 0xf2d177ff, 0xf2d076ff, 0xf2ce6fff, 0xf0c968ff, 0xeec560ff, 0xedc45bff, 0xeec562ff, 0xefc564ff, 0xe9c164ff, 0xd6b05bff, 0xb2924aff, 0xaa8c48ff, 0xebc36aff, 0xe9c168ff, 0x7d5928ff, 0x53300eff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x54310fff, 0x6f4811ff, 0xbb8b16ff, 0xbe8e18ff, 0xab8631ff, 0xa58338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xa68338ff, 0xb68b23ff, 0xc29215ff, 0x9c7014ff, 0x563310ff, 0x55320fff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x553210ff, 0x53300eff, 0x997639ff, 0xf1cd6fff, 0xe0bd64ff, 0x846221ff, 0x8d5e12ff, 0xa0680fff, 0xa96e10ff, 0xa56607ff, 0xa36306ff, 0xa26205ff, 0xa05d04ff, 0x9d5904ff, 0xa05e06ff, 0xcca14aff, 0xe2c177ff, 0xdcbd7cff, 0xd3b26fff, 0xc7a256ff, 0xc6a15aff, 0xbe9345ff, 0xb48127ff, 0xad7617ff, 0x9c5d0aff, 0x945004ff, 0x9a5b06ff, 0xa66e14ff, 0xa97315ff, 0xac781bff, 0xae7c22ff, 0xaf7e23ff, 0xb18028ff, 0xb07d23ff, 0xb6842aff, 0xc29646ff, 0xc6a056ff, 0xf8e1adff, 0xf8dfacff, 0xf6d9a1ff, 0xf7d99eff, 0xf2ce88ff, 0xf2cd89ff, 0xf1cf8eff, 0xeabc6cff, 0xebbd6fff, 0xe8b660ff, 0xe8b865ff, 0xe8b966ff, 0xe6b55fff, 0xe6b45eff, 0xe4af52ff, 0xe0aa42ff, 0xdea93dff, 0xe0ab3dff, 0xf0ca6dff, 0xf4d686ff, 0xf4d783ff, 0xf3d57eff, 0xf2d176ff, 0xf2cf74ff, 0xf0cc6aff, 0xf0c966ff, 0xf0ca71ff, 0xefc667ff, 0xeec463ff, 0xeec567ff, 0xe4b953ff, 0xd1a849ff, 0xb08e43ff, 0xa88a44ff, 0xecc76cff, 0xeac56aff, 0x825e2aff, 0x583510ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x593612ff, 0x724c13ff, 0xbb8c15ff, 0xbf9118ff, 0xae8932ff, 0xa9863aff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa98639ff, 0xa9863aff, 0xa9863aff, 0xb68d25ff, 0xc29414ff, 0xa17714ff, 0x5c3912ff, 0x5a3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x5b3712ff, 0x583510ff, 0x9a783aff, 0xf1d070ff, 0xe2c267ff, 0x8b6b30ff, 0x946b26ff, 0xa57113ff, 0xb07817ff, 0xab6f09ff, 0xaa6d08ff, 0xa96e09ff, 0xa96a07ff, 0xa76806ff, 0xa96a05ff, 0xc6973cff, 0xe1c178ff, 0xdabb79ff, 0xd0ae66ff, 0xc8a35bff, 0xc5a159ff, 0xbe9245ff, 0xb37e28ff, 0xa3640aff, 0x975203ff, 0x874003ff, 0x925005ff, 0xa7690dff, 0xab7415ff, 0xac791bff, 0xac7c22ff, 0xb0812dff, 0xaf7c26ff, 0xb1802aff, 0xb07b1eff, 0xaf7615ff, 0xba852aff, 0xf7e1afff, 0xf8dfabff, 0xf8dba4ff, 0xf8dda3ff, 0xf6d79bff, 0xf6d69dff, 0xf2ce8fff, 0xeec47cff, 0xecc075ff, 0xe9b961ff, 0xe8b865ff, 0xe8b967ff, 0xe4b25cff, 0xe4b058ff, 0xe4b055ff, 0xe1ac4bff, 0xdea839ff, 0xe5b348ff, 0xf2ce70ff, 0xf3d685ff, 0xf4da8fff, 0xf3d789ff, 0xf1d382ff, 0xf1d07cff, 0xefcb74ff, 0xefca76ff, 0xefc76bff, 0xecc15cff, 0xebc05bff, 0xe8bd56ff, 0xe1b64eff, 0xd0a749ff, 0xaf9042ff, 0xa78b46ff, 0xecca6dff, 0xecca6cff, 0x87642dff, 0x5e3a13ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x5f3b14ff, 0x754f14ff, 0xba8e15ff, 0xbf9317ff, 0xb18b32ff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xad893bff, 0xaf8a36ff, 0xb48d2bff, 0xbd931aff, 0xc19614ff, 0xa27a14ff, 0x623e14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x603c14ff, 0x5e3a13ff, 0x9b793aff, 0xf1d171ff, 0xe3c469ff, 0x8d6c2bff, 0x976f28ff, 0xab7a23ff, 0xb17b16ff, 0xb17914ff, 0xaf7410ff, 0xaf7514ff, 0xad730bff, 0xac7107ff, 0xac6f07ff, 0xc59635ff, 0xe3c175ff, 0xdabb77ff, 0xd0ae68ff, 0xcfad6eff, 0xc8a25fff, 0xb88530ff, 0xa86809ff, 0xa8680bff, 0xae7217ff, 0xa5670aff, 0xac7012ff, 0xb47e27ff, 0xb88634ff, 0xb3822dff, 0xac7b25ff, 0xae7e2aff, 0xaf802cff, 0xb2832fff, 0xb0802cff, 0xb07f23ff, 0xb48126ff, 0xf7dfaaff, 0xf8dda4ff, 0xf7dda3ff, 0xf6d89dff, 0xf3d295ff, 0xf2cf93ff, 0xeec785ff, 0xecc17aff, 0xebbe73ff, 0xe9bb69ff, 0xe8b662ff, 0xe8b864ff, 0xe6b35cff, 0xe3ad52ff, 0xe2ac4eff, 0xe1ab4cff, 0xdea941ff, 0xe6b54aff, 0xf1cb6fff, 0xf4da90ff, 0xf4db92ff, 0xf3da90ff, 0xf2d58dff, 0xf0d28bff, 0xefcf85ff, 0xeecb79ff, 0xedc466ff, 0xeabf5eff, 0xe8bc59ff, 0xe7ba58ff, 0xe1b653ff, 0xd1a94dff, 0xb1924cff, 0xa88d4aff, 0xeccc6eff, 0xedcd6eff, 0x8c692fff, 0x633f15ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x644016ff, 0x785316ff, 0xb98f14ff, 0xbf9516ff, 0xb48e33ff, 0xb08c3dff, 0xb08c3cff, 0xb08c3cff, 0xb08c3cff, 0xb08c3cff, 0xb08c3cff, 0xb08c3cff, 0xb08c3cff, 0xb08c3dff, 0xb18c3bff, 0xb38e34ff, 0xb89127ff, 0xbd941aff, 0xbf9514ff, 0xb38a14ff, 0x997115ff, 0x785216ff, 0x664116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x654116ff, 0x633f15ff, 0x9c793aff, 0xf0d070ff, 0xe5c66aff, 0x8f7031ff, 0x98712aff, 0xb28536ff, 0xba852aff, 0xb9852aff, 0xb57f20ff, 0xb27d1aff, 0xb07912ff, 0xae7409ff, 0xac6d04ff, 0xbe8b26ff, 0xdfbc6cff, 0xd6b46dff, 0xd3b26fff, 0xd2b072ff, 0xc3994dff, 0xb2791bff, 0xad6f09ff, 0xb37918ff, 0xb67e20ff, 0xb88125ff, 0xbc882fff, 0xbd892fff, 0xc39648ff, 0xc39a50ff, 0xbd9346ff, 0xb38633ff, 0xb0802bff, 0xaf7c25ff, 0xb2822eff, 0xb28530ff, 0xb0822eff, 0xf4dda7ff, 0xf4da9dff, 0xf2d695ff, 0xf3d597ff, 0xf0cc89ff, 0xe9bc6dff, 0xe8ba6aff, 0xeabb6aff, 0xe9bb6aff, 0xeabd6cff, 0xe6b55dff, 0xe6b65cff, 0xe6b55aff, 0xe3ae52ff, 0xe0aa49ff, 0xe0a947ff, 0xe0a945ff, 0xe5b448ff, 0xefcc72ff, 0xf3d891ff, 0xf2d98fff, 0xf1d487ff, 0xf1d387ff, 0xf1d184ff, 0xefce7bff, 0xefcc7bff, 0xedca7cff, 0xebc97eff, 0xebc67bff, 0xeac274ff, 0xe5bc60ff, 0xd2aa4fff, 0xaf8d44ff, 0xa78c49ff, 0xeccc6eff, 0xeece6eff, 0x916d32ff, 0x684417ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6a4519ff, 0x7c5618ff, 0xb99014ff, 0xbf9516ff, 0xb79134ff, 0xb48f3eff, 0xb48f3eff, 0xb48f3eff, 0xb48f3eff, 0xb48f3eff, 0xb48f3eff, 0xb48f3eff, 0xb5903bff, 0xb79131ff, 0xbb9322ff, 0xbf9517ff, 0xbd9314ff, 0xac8315ff, 0x906917ff, 0x765118ff, 0x6b4618ff, 0x6a4519ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x6b4619ff, 0x684417ff, 0x9d7a3aff, 0xf0d070ff, 0xe6c76bff, 0x947940ff, 0x9b7838ff, 0xb48a3aff, 0xc19341ff, 0xbf8e39ff, 0xba872cff, 0xb57f1cff, 0xb7801dff, 0xb67f1aff, 0xb27a10ff, 0xbe8922ff, 0xd4a745ff, 0xd8b46aff, 0xd7b776ff, 0xd0ab5fff, 0xc2923aff, 0xb88227ff, 0xb47c17ff, 0xb8821fff, 0xbd882aff, 0xc18e35ff, 0xc2923cff, 0xc3943fff, 0xc79c4fff, 0xc8a25cff, 0xc8a35fff, 0xc2984eff, 0xb4822dff, 0xb38532ff, 0xb88f41ff, 0xb98f44ff, 0xb88e48ff, 0xf4deadff, 0xf5dca6ff, 0xf2d593ff, 0xf1d38eff, 0xebc373ff, 0xe5b459ff, 0xe4af50ff, 0xe4b255ff, 0xebc176ff, 0xe8bc71ff, 0xe5b159ff, 0xe5b153ff, 0xe3af51ff, 0xe2ac53ff, 0xe1ac4fff, 0xe1ac4eff, 0xdea741ff, 0xe5b653ff, 0xf0ce79ff, 0xf2d791ff, 0xf3da98ff, 0xf2d794ff, 0xf0d288ff, 0xf0d083ff, 0xefd188ff, 0xf0d491ff, 0xeed08aff, 0xedce86ff, 0xedc97cff, 0xecc570ff, 0xe7c06aff, 0xd3ac57ff, 0xaf8d46ff, 0xa58b49ff, 0xebcc6eff, 0xeece6fff, 0x967234ff, 0x6e4919ff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x6f4a1bff, 0x7f5919ff, 0xb98f14ff, 0xbf9516ff, 0xb99334ff, 0xb8923fff, 0xb8933fff, 0xb8933fff, 0xb8933fff, 0xb8933fff, 0xb89339ff, 0xba932dff, 0xbe951eff, 0xbf9515ff, 0xb98f14ff, 0xa57c16ff, 0x896219ff, 0x75501aff, 0x6f4a1bff, 0x6f4a1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x704b1bff, 0x6e4919ff, 0x9f7b3aff, 0xf0d070ff, 0xe7c86cff, 0x967c42ff, 0x9d7b3bff, 0xb99146ff, 0xc59949ff, 0xc1913bff, 0xbf8e36ff, 0xba8829ff, 0xba8628ff, 0xb98320ff, 0xb8821fff, 0xba8523ff, 0xc28d24ff, 0xcfa54eff, 0xcea65dff, 0xcaa155ff, 0xc79948ff, 0xc79748ff, 0xbe872bff, 0xbe8728ff, 0xc4943cff, 0xc79946ff, 0xc99a4aff, 0xc99c4cff, 0xc79744ff, 0xc79b4dff, 0xc49648ff, 0xc2964fff, 0xb9893dff, 0xb7893bff, 0xbe9753ff, 0xbd9956ff, 0xbc9955ff, 0xf3d9a2ff, 0xf5d99dff, 0xf3d798ff, 0xecc67aff, 0xe9bd65ff, 0xebc06aff, 0xecc06dff, 0xebbe68ff, 0xeec273ff, 0xebc074ff, 0xe7b763ff, 0xe7b763ff, 0xe4b35fff, 0xe4b262ff, 0xe4b363ff, 0xe2af5aff, 0xdea946ff, 0xe0ab41ff, 0xeec76bff, 0xf2d795ff, 0xf4d89aff, 0xf4d896ff, 0xefd388ff, 0xefd186ff, 0xf0d38dff, 0xf0d493ff, 0xefd392ff, 0xedcf89ff, 0xedce88ff, 0xecca7fff, 0xe7c478ff, 0xd6b670ff, 0xb29558ff, 0xa68d50ff, 0xebcb6eff, 0xeece6fff, 0x9b7736ff, 0x744e1cff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x754f1dff, 0x835c1bff, 0xb98f15ff, 0xbf9516ff, 0xbc9535ff, 0xbb9641ff, 0xbb9641ff, 0xbb963fff, 0xbc9536ff, 0xbd9528ff, 0xbf961aff, 0xbf9514ff, 0xb48b15ff, 0x9e7618ff, 0x855f1bff, 0x78511dff, 0x754f1dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x76501dff, 0x744e1cff, 0xa07c3aff, 0xefcf6fff, 0xe8c96cff, 0x9a8048ff, 0x9f8046ff, 0xb99247ff, 0xc39542ff, 0xc2913aff, 0xc2943eff, 0xc09238ff, 0xc1943fff, 0xbf8f39ff, 0xbd892aff, 0xb98520ff, 0xba8623ff, 0xbc8823ff, 0xc08d32ff, 0xc49744ff, 0xcb9e4eff, 0xd0a65aff, 0xcda255ff, 0xc99845ff, 0xcda252ff, 0xcd9f4cff, 0xc79335ff, 0xc8963cff, 0xc7953dff, 0xc79945ff, 0xc18f3eff, 0xbe8f46ff, 0xc29857ff, 0xc29a59ff, 0xbd9857ff, 0xbb9755ff, 0xbb9855ff, 0xf4dca8ff, 0xf4dba0ff, 0xf3d798ff, 0xecc77dff, 0xedc673ff, 0xefc878ff, 0xf1cc81ff, 0xf1cd85ff, 0xf0cb83ff, 0xedc475ff, 0xeab962ff, 0xe8b762ff, 0xe7b767ff, 0xe6b667ff, 0xe3b05aff, 0xdfa94aff, 0xdca53eff, 0xdca337ff, 0xeabd5eff, 0xf1d28bff, 0xf1d38eff, 0xf0d28aff, 0xeecf87ff, 0xefd18eff, 0xf1d294ff, 0xf0d394ff, 0xf0d395ff, 0xeed092ff, 0xedcd8aff, 0xecca81ff, 0xe6c479ff, 0xd7b97bff, 0xb59e6dff, 0xa7915cff, 0xeacb6eff, 0xefce6fff, 0xa07b38ff, 0x7a531eff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b541fff, 0x87601dff, 0xb98f15ff, 0xbf9515ff, 0xbf9836ff, 0xbe983fff, 0xbe9733ff, 0xbf9623ff, 0xc09617ff, 0xbc9314ff, 0xaf8517ff, 0x98701bff, 0x845d1eff, 0x7b551fff, 0x7b541fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7b551fff, 0x7a531eff, 0xa27e3aff, 0xefcf6fff, 0xe9ca6dff, 0x9c834bff, 0xa28551ff, 0xbc954dff, 0xc89d4cff, 0xc99f4dff, 0xc59a44ff, 0xc49743ff, 0xc69a47ff, 0xc69b49ff, 0xc3943fff, 0xc19138ff, 0xc19037ff, 0xc09035ff, 0xc69a47ff, 0xcb9f4fff, 0xcfa453ff, 0xd3ac61ff, 0xd8b476ff, 0xdab77aff, 0xdbb97cff, 0xdcbb79ff, 0xd7b36cff, 0xd2ab60ff, 0xcda352ff, 0xc99b49ff, 0xcba158ff, 0xc2944eff, 0xbf934cff, 0xc9a568ff, 0xbe9955ff, 0xbe9c5bff, 0xbe9e5cff, 0xf0d7a6ff, 0xf3dca7ff, 0xf3d79cff, 0xf1d08eff, 0xf1d290ff, 0xf0cc82ff, 0xf0ce85ff, 0xefcb82ff, 0xeec779ff, 0xebc06aff, 0xe8b758ff, 0xe6b254ff, 0xe3ae52ff, 0xe1ac50ff, 0xe2ac4dff, 0xdfa944ff, 0xdca439ff, 0xdea53bff, 0xe8b95cff, 0xefcc7dff, 0xefd18aff, 0xefd388ff, 0xefd68aff, 0xf1d691ff, 0xf2d699ff, 0xf2d49aff, 0xf0d294ff, 0xedcd8aff, 0xeac77dff, 0xebc87dff, 0xe6c47eff, 0xd4b574ff, 0xb49c69ff, 0xa89361ff, 0xeacb6eff, 0xefcf6fff, 0xa4803bff, 0x7f5820ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x805922ff, 0x8b641fff, 0xb99015ff, 0xbf9515ff, 0xc09724ff, 0xc0971eff, 0xbf9515ff, 0xb99015ff, 0xa98019ff, 0x936c1eff, 0x845d21ff, 0x805922ff, 0x805922ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x815a22ff, 0x7f5820ff, 0xa47f3aff, 0xefcf6fff, 0xeacb6dff, 0x9d854eff, 0xa28450ff, 0xbd974cff, 0xcba14fff, 0xcfaa61ff, 0xcca458ff, 0xcaa157ff, 0xc99f4eff, 0xca9f4eff, 0xc69944ff, 0xc69d4bff, 0xcca55cff, 0xcca459ff, 0xcca65cff, 0xc0944fff, 0xd2ad67ff, 0xd9b775ff, 0xdebd82ff, 0xe1c287ff, 0xe0c083ff, 0xe1c386ff, 0xe4c792ff, 0xe1c48fff, 0xd8b575ff, 0xd0a85eff, 0xcdaa66ff, 0xc2974bff, 0xbd9141ff, 0xbf994fff, 0xbc964cff, 0xc0a15fff, 0xbf9c5bff, 0xf0daacff, 0xf4e0afff, 0xf4ddaaff, 0xf4d89eff, 0xf4d79bff, 0xf2d594ff, 0xefce86ff, 0xeeca7dff, 0xecc574ff, 0xe9be5fff, 0xe8ba59ff, 0xe7b558ff, 0xe4af4fff, 0xe6b055ff, 0xe3af4fff, 0xe3af4fff, 0xe5b257ff, 0xe5b155ff, 0xe4b055ff, 0xeabd65ff, 0xeecc7fff, 0xefd084ff, 0xefd185ff, 0xf0d38eff, 0xf0d391ff, 0xefcf8aff, 0xedca82ff, 0xecca86ff, 0xecc883ff, 0xecc87eff, 0xe8c887ff, 0xd5b777ff, 0xb59c67ff, 0xa7925eff, 0xeaca6eff, 0xefcf6fff, 0xa9843dff, 0x845d23ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865e24ff, 0x8f6721ff, 0xba9015ff, 0xc09614ff, 0xbe9414ff, 0xb68c16ff, 0xa47b1cff, 0x916921ff, 0x876024ff, 0x855e24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x865f24ff, 0x845d23ff, 0xa6813bff, 0xeece6fff, 0xebcb6eff, 0x9f8651ff, 0xa38651ff, 0xc29f5aff, 0xcca250ff, 0xcda555ff, 0xcfa65aff, 0xcea961ff, 0xcaa052ff, 0xcba04eff, 0xcca354ff, 0xcda761ff, 0xd6b377ff, 0xd4af6dff, 0xd3ad66ff, 0xd2ae6eff, 0xdab97dff, 0xdfc287ff, 0xdebf7eff, 0xe2c487ff, 0xe6c78eff, 0xe5c88fff, 0xe5cc99ff, 0xe1c591ff, 0xdfc28eff, 0xddc18cff, 0xd7b77eff, 0xd1b173ff, 0xc7a35eff, 0xbd9549ff, 0xbc964bff, 0xc3a263ff, 0xc09f5dff, 0xf2ddb1ff, 0xf2ddadff, 0xf2d89eff, 0xf2d699ff, 0xf0d493ff, 0xf0d495ff, 0xefcd88ff, 0xeec674ff, 0xedc26dff, 0xecc26aff, 0xecc167ff, 0xe9bb62ff, 0xe9b75dff, 0xebbc6bff, 0xe8ba66ff, 0xe9b865ff, 0xe7b665ff, 0xe5b25bff, 0xe3ac4fff, 0xe2ab4bff, 0xebc371ff, 0xeecb7eff, 0xefcf88ff, 0xf0d391ff, 0xefd28eff, 0xedce87ff, 0xedcd88ff, 0xebc882ff, 0xeac170ff, 0xecc982ff, 0xebcd91ff, 0xd7b878ff, 0xb59a64ff, 0xa69059ff, 0xe9ca6eff, 0xefcf6fff, 0xad883fff, 0x8a6225ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x936b24ff, 0xb78e17ff, 0xb28819ff, 0xa0771fff, 0x916924ff, 0x8b6426ff, 0x8b6326ff, 0x8b6426ff, 0x8c6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8b6426ff, 0x8c6426ff, 0x8a6225ff, 0xa7823bff, 0xeece6fff, 0xeccc6eff, 0xa38a52ff, 0xa58853ff, 0xc3a160ff, 0xd2ac66ff, 0xd3ab5fff, 0xd1a95cff, 0xd2ac65ff, 0xd1ac67ff, 0xd0a960ff, 0xd1aa65ff, 0xd6b275ff, 0xdbba81ff, 0xd5b06eff, 0xd5ae66ff, 0xdebb80ff, 0xe2c58fff, 0xe4c78fff, 0xe0c182ff, 0xe5c78eff, 0xe5c890ff, 0xe4c790ff, 0xe0c189ff, 0xe2c792ff, 0xe1ca9aff, 0xdfc799ff, 0xdcc293ff, 0xdac091ff, 0xd4b784ff, 0xc4a25eff, 0xbe9c57ff, 0xc1a05cff, 0xc3a461ff, 0xf1dcadff, 0xf2dcaaff, 0xf1d998ff, 0xf1d693ff, 0xf0d495ff, 0xf0d192ff, 0xeece89ff, 0xefce86ff, 0xeec87cff, 0xefcd86ff, 0xedc77aff, 0xecc373ff, 0xedc16eff, 0xeec37cff, 0xebbf74ff, 0xe8b764ff, 0xe7b461ff, 0xe6b463ff, 0xe5b15eff, 0xe1a949ff, 0xe2b154ff, 0xedca80ff, 0xf0d293ff, 0xeecf8cff, 0xeece8bff, 0xeed08eff, 0xeccc87ff, 0xeac577ff, 0xedc982ff, 0xebca87ff, 0xe7c482ff, 0xd6b572ff, 0xb59c67ff, 0xa5905bff, 0xe9ca6eff, 0xefcf6fff, 0xb18c41ff, 0x8f6727ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916929ff, 0x946b27ff, 0x9b7324ff, 0x936b28ff, 0x906829ff, 0x916829ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x916928ff, 0x906828ff, 0x926a2aff, 0xb79346ff, 0xefcf6fff, 0xeecf6fff, 0xaa9159ff, 0xab915eff, 0xc7a666ff, 0xd5b16bff, 0xd8b370ff, 0xd4af64ff, 0xd9b778ff, 0xd8b370ff, 0xd6af67ff, 0xd4af67ff, 0xd9b576ff, 0xddbc81ff, 0xd9b674ff, 0xd9b66fff, 0xdfbd7fff, 0xe1c184ff, 0xe6c892ff, 0xe4c589ff, 0xe2c283ff, 0xe3c48bff, 0xe1c48dff, 0xe1c895ff, 0xe6d1a3ff, 0xe4cea0ff, 0xddc390ff, 0xddc494ff, 0xddc498ff, 0xd9c090ff, 0xcaaa6aff, 0xc4a466ff, 0xc7a96dff, 0xc7a968ff, 0xefd8a6ff, 0xf1d9a1ff, 0xefd48fff, 0xf0d591ff, 0xefd290ff, 0xedcc89ff, 0xedcd89ff, 0xf0d191ff, 0xeecc89ff, 0xe5be73ff, 0xe1b460ff, 0xe4b662ff, 0xedc57cff, 0xebc582ff, 0xe8bd72ff, 0xe7b45dff, 0xe8b663ff, 0xe9bb70ff, 0xe5b15fff, 0xe2ad54ff, 0xdda743ff, 0xe8bf73ff, 0xf0d398ff, 0xf1d39aff, 0xf0d296ff, 0xedcb8bff, 0xeac377ff, 0xeac275ff, 0xedc885ff, 0xe9c57eff, 0xe6c27aff, 0xd8b879ff, 0xb9a275ff, 0xa79261ff, 0xe8c96eff, 0xf0d070ff, 0xb69043ff, 0x956c2aff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966d2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x966e2bff, 0x956d2aff, 0x99712dff, 0xae883dff, 0xd1ae58ff, 0xeccb6cff, 0xeece6fff, 0xd3b765ff, 0xa58e5aff, 0xb59b66ff, 0xccaa69ff, 0xd8b46fff, 0xdbb871ff, 0xdab468ff, 0xdbb56cff, 0xdbb66fff, 0xdab46eff, 0xdbb97bff, 0xdcbc7dff, 0xe1c286ff, 0xe2c28bff, 0xe0c081ff, 0xe2c386ff, 0xe3c48bff, 0xe4c58dff, 0xe5c792ff, 0xe1c186ff, 0xddbd7fff, 0xe2c692ff, 0xe2cb9bff, 0xe3cc9fff, 0xe3cfa5ff, 0xdfc89aff, 0xddc292ff, 0xdec59aff, 0xdcc497ff, 0xd3b57bff, 0xc9a668ff, 0xccac74ff, 0xccac6fff, 0xf0daa9ff, 0xf1dba4ff, 0xefd395ff, 0xf0d497ff, 0xf0d293ff, 0xeecf8cff, 0xefd292ff, 0xe5c37eff, 0xd3a457ff, 0xd3a253ff, 0xdbb062ff, 0xe0b66bff, 0xe6be77ff, 0xebc680ff, 0xeac074ff, 0xe5b663ff, 0xd89f4aff, 0xdfa855ff, 0xe9ba6aff, 0xe5b45fff, 0xdfa843ff, 0xdda747ff, 0xe6bd73ff, 0xecca8bff, 0xeecc93ff, 0xeac584ff, 0xe7bd6fff, 0xe3b664ff, 0xe4b763ff, 0xe4b864ff, 0xe3bb70ff, 0xd6b579ff, 0xb89e71ff, 0xa58e5aff, 0xe8c96dff, 0xf0d070ff, 0xba9445ff, 0x9a712cff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9c732dff, 0x9b732dff, 0x9b722cff, 0x9f762fff, 0xb38d40ff, 0xd4b15aff, 0xedcd6dff, 0xedce6fff, 0xcaaf60ff, 0x9b8651ff, 0x89764cff, 0xa38a57ff, 0xc1a56bff, 0xd3b270ff, 0xdcb975ff, 0xe1bd7aff, 0xe4c284ff, 0xe0bf7dff, 0xddb870ff, 0xddba72ff, 0xe1c080ff, 0xe2c284ff, 0xe4c587ff, 0xe3c484ff, 0xe1c17dff, 0xe4c588ff, 0xe0bf7dff, 0xe0c07fff, 0xe1c181ff, 0xe0c082ff, 0xe1c58bff, 0xe5cc9dff, 0xe5d0a4ff, 0xe2cb9eff, 0xe2cca4ff, 0xdfc797ff, 0xdec390ff, 0xdcc390ff, 0xdcc291ff, 0xdabe8aff, 0xccad70ff, 0xc9a96cff, 0xcfb27aff, 0xf3e0b3ff, 0xf3deadff, 0xf1daa9ff, 0xeccf96ff, 0xdfb672ff, 0xd09f5aff, 0xcf9f62ff, 0xb57330ff, 0x9f4f0fff, 0xbe833eff, 0xddb272ff, 0xe8c28aff, 0xe9c689ff, 0xe7c283ff, 0xe2b870ff, 0xd7a85eff, 0xaf6411ff, 0xbc7622ff, 0xcb8d3cff, 0xd29438ff, 0xd0922dff, 0xcf9024ff, 0xd19633ff, 0xd8a348ff, 0xe0b167ff, 0xe2b462ff, 0xe5ba71ff, 0xe1b261ff, 0xe2b45fff, 0xe1b35dff, 0xdaab4eff, 0xd0a85fff, 0xaf8b4bff, 0xa08346ff, 0xe7c86cff, 0xf0d070ff, 0xbe9847ff, 0xa0772eff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa1782fff, 0xa0772eff, 0xa57c32ff, 0xb99243ff, 0xd8b55cff, 0xeece6eff, 0xeccd6fff, 0xc7ad60ff, 0x907f51ff, 0x726344ff, 0x81704eff, 0xa08b62ff, 0xbea472ff, 0xcfb275ff, 0xddbf7fff, 0xe2c281ff, 0xe4c281ff, 0xe6c686ff, 0xe6c583ff, 0xe4c27dff, 0xe4c27cff, 0xe7c789ff, 0xe7c78aff, 0xe8c78aff, 0xe9cb8fff, 0xe7c885ff, 0xe6c786ff, 0xe3c481ff, 0xe0c17dff, 0xdfbf78ff, 0xe0c07dff, 0xe4ca91ff, 0xe6cd9eff, 0xe3cc9dff, 0xe1c691ff, 0xdec58fff, 0xdec38bff, 0xdabd80ff, 0xd7ba77ff, 0xdcc089ff, 0xdbbe8bff, 0xcdac6fff, 0xc9a969ff, 0xccad71ff, 0xe0bd7fff, 0xeacc96ff, 0xe3c084ff, 0xc8933bff, 0xb16a0eff, 0xaa5c05ff, 0xa95b08ff, 0xa45303ff, 0x9a4003ff, 0xaa5f1cff, 0xd6a66aff, 0xebca9aff, 0xe6c185ff, 0xe0b670ff, 0xd2a560ff, 0xb36f25ff, 0xa65704ff, 0xa95b04ff, 0xae6003ff, 0xb16404ff, 0xb26604ff, 0xb66c05ff, 0xb96f07ff, 0xbc7209ff, 0xc0770fff, 0xcb891fff, 0xcf942bff, 0xd39b34ff, 0xdaa544ff, 0xddac50ff, 0xdaaa51ff, 0xcca151ff, 0xab8339ff, 0x9d7d3cff, 0xe7c76bff, 0xf0d070ff, 0xc29c49ff, 0xa67b30ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa77d31ff, 0xa67c31ff, 0xa67c31ff, 0xaa8135ff, 0xbe9745ff, 0xdbb85eff, 0xefcf6fff, 0xebcc6eff, 0xc4ab5fff, 0x8c7a48ff, 0x706141ff, 0x7d6e54ff, 0x968463ff, 0xaf996fff, 0xc4ac7dff, 0xd3b681ff, 0xdbbc7bff, 0xe3c583ff, 0xe5c581ff, 0xe7c880ff, 0xeacc86ff, 0xeacc8cff, 0xe9ca88ff, 0xe7c780ff, 0xe9cc8aff, 0xe9cc8cff, 0xe9cb8cff, 0xeace92ff, 0xebce90ff, 0xe9cc8dff, 0xe5c482ff, 0xe1bf77ff, 0xdfbc71ff, 0xe0c17cff, 0xe2c382ff, 0xe6cd9bff, 0xe5cf9dff, 0xe0c488ff, 0xd9bd7bff, 0xe1c996ff, 0xdec590ff, 0xdcc489ff, 0xdcc38dff, 0xdabe8cff, 0xcaa762ff, 0xc4a156ff, 0xc49e50ff, 0xcb973cff, 0xce9b47ff, 0xce9c46ff, 0xca9338ff, 0xb67110ff, 0xae6104ff, 0xad6204ff, 0xad6405ff, 0xa45303ff, 0xb56d21ff, 0xe4be88ff, 0xe5c18fff, 0xe0b87cff, 0xd3a159ff, 0xb36f26ff, 0xa35203ff, 0xa85904ff, 0xac5d04ff, 0xb06404ff, 0xb36a05ff, 0xb46b05ff, 0xb66d05ff, 0xba6f05ff, 0xbe7505ff, 0xc07805ff, 0xc27d05ff, 0xc7850eff, 0xcf9325ff, 0xd6a033ff, 0xd9a53aff, 0xd8a741ff, 0xc99936ff, 0xab822cff, 0x9b7b33ff, 0xe6c66aff, 0xf0d070ff, 0xc6a04bff, 0xab8133ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8234ff, 0xac8233ff, 0xab8133ff, 0xb08737ff, 0xc39c48ff, 0xdebb60ff, 0xf0cf6fff, 0xeacb6dff, 0xc1a65aff, 0x8a7847ff, 0x736649ff, 0x7f7153ff, 0x96835aff, 0xaa9365ff, 0xbca26fff, 0xcfb584ff, 0xdcc293ff, 0xe2c489ff, 0xe4c37cff, 0xe8c885ff, 0xe7c579ff, 0xe9c87cff, 0xedd08dff, 0xedd08cff, 0xebce88ff, 0xe8c779ff, 0xe9c87bff, 0xebcc83ff, 0xedd090ff, 0xedd297ff, 0xeed195ff, 0xeacc8dff, 0xe5c682ff, 0xe2bf76ff, 0xe2c27bff, 0xe2c17fff, 0xdfbe79ff, 0xe3c68eff, 0xe8d3a5ff, 0xe3cc98ff, 0xe3cb98ff, 0xe2ca9aff, 0xe1cc9fff, 0xdfc898ff, 0xddc38eff, 0xd8bc86ff, 0xc09644ff, 0xb17f1fff, 0xb18023ff, 0xcc993fff, 0xcc983dff, 0xca973bff, 0xcb963dff, 0xc08524ff, 0xaf6a04ff, 0xac6304ff, 0xab6205ff, 0xa95c04ff, 0xb06615ff, 0xc28741ff, 0xc99352ff, 0xb37132ff, 0xa3530eff, 0x9e4a05ff, 0xa35104ff, 0xa85a05ff, 0xac5e04ff, 0xb16805ff, 0xb26805ff, 0xb36a05ff, 0xb66a05ff, 0xba6e04ff, 0xc27d0fff, 0xc68310ff, 0xc8870eff, 0xce931cff, 0xd7a132ff, 0xd9a434ff, 0xdcaa45ff, 0xd9aa42ff, 0xc99e3fff, 0xab8430ff, 0x9a7a32ff, 0xe5c66aff, 0xf0d070ff, 0xcaa34dff, 0xb18635ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18736ff, 0xb18636ff, 0xb18635ff, 0xb68c3aff, 0xc7a14bff, 0xe0be62ff, 0xf1d070ff, 0xe8ca6dff, 0xbfa55cff, 0x85713cff, 0x6d5b32ff, 0x7f6f4bff, 0x9a8862ff, 0xb09c72ff, 0xc2ac7dff, 0xd0b884ff, 0xdbc089ff, 0xe0c285ff, 0xe4c484ff, 0xeacc8cff, 0xebcd87ff, 0xebcc83ff, 0xeccd84ff, 0xeacc82ff, 0xeccf88ff, 0xedcf87ff, 0xebcc81ff, 0xecca7aff, 0xebc56dff, 0xebc776ff, 0xeccd83ff, 0xedcf89ff, 0xeacb83ff, 0xe8c77bff, 0xe6c57bff, 0xe5c377ff, 0xe7c984ff, 0xe7cb8dff, 0xe6c88bff, 0xe2c484ff, 0xe3c78eff, 0xe3c994ff, 0xe4cc9bff, 0xe4cc9bff, 0xe1ca9aff, 0xe1c797ff, 0xe0c591ff, 0xdbc28eff, 0xb98b36ff, 0xa77004ff, 0xa87207ff, 0xcb993cff, 0xd0a251ff, 0xcd9c49ff, 0xc9963dff, 0xc48e2dff, 0xb2730bff, 0xa95f05ff, 0xa85a05ff, 0xab6005ff, 0xad6304ff, 0xac6103ff, 0xa95e04ff, 0x9b4903ff, 0x9d4803ff, 0xa04d04ff, 0xa65505ff, 0xa85a05ff, 0xac6004ff, 0xb16704ff, 0xb06604ff, 0xb46805ff, 0xb76a04ff, 0xbe7405ff, 0xc48212ff, 0xcb8c19ff, 0xcf9522ff, 0xd8a33bff, 0xddaa47ff, 0xe0ae4bff, 0xe1b352ff, 0xe0b35aff, 0xcfa651ff, 0xa07531ff, 0x855c23ff, 0xe3c368ff, 0xf1d170ff, 0xcea74eff, 0xb68b37ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78c38ff, 0xb78b38ff, 0xb78b38ff, 0xbb913dff, 0xcca54dff, 0xe3c063ff, 0xf1d170ff, 0xe7c86cff, 0xbba157ff, 0x84703dff, 0x6f5e39ff, 0x7e6c43ff, 0x988453ff, 0xb09b6bff, 0xc5b082ff, 0xd2ba89ff, 0xddc58fff, 0xe7d09bff, 0xead094ff, 0xeacb87ff, 0xeac982ff, 0xeed28eff, 0xeccd86ff, 0xedd088ff, 0xeed288ff, 0xf1d691ff, 0xf1d794ff, 0xf1d593ff, 0xeecb7eff, 0xedc978ff, 0xedc771ff, 0xebc469ff, 0xecc872ff, 0xedc875ff, 0xebc879ff, 0xeac878ff, 0xedcf84ff, 0xeed493ff, 0xecd091ff, 0xe8cb86ff, 0xe7c985ff, 0xe6c88aff, 0xe4c787ff, 0xe5ca8fff, 0xe5cd98ff, 0xe4cc97ff, 0xe3cb9aff, 0xe4d0a2ff, 0xe2cb98ff, 0xdbbe86ff, 0xbc9040ff, 0xae7b1aff, 0xb17f1eff, 0xcb973dff, 0xce9f4fff, 0xcc9c4aff, 0xcb963cff, 0xc89337ff, 0xb87b1aff, 0xa75b04ff, 0xa65605ff, 0xaf6806ff, 0xb57409ff, 0xb5730dff, 0xa76109ff, 0x933e04ff, 0x9a4204ff, 0x9f4a04ff, 0xa65404ff, 0xa95804ff, 0xad6005ff, 0xb06505ff, 0xb36805ff, 0xb76b04ff, 0xba6e06ff, 0xc17b0aff, 0xc7850dff, 0xd2982bff, 0xd9a53fff, 0xdeac4cff, 0xe3b55aff, 0xe3b859ff, 0xe7bc62ff, 0xdeb35eff, 0xb78842ff, 0x793e10ff, 0x754414ff, 0xe2c166ff, 0xf1d171ff, 0xd1aa50ff, 0xbc903aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbd913aff, 0xbc913aff, 0xbc903aff, 0xbc903aff, 0xc1963fff, 0xd1aa50ff, 0xe5c264ff, 0xf1d170ff, 0xe5c66aff, 0xb4984cff, 0x7b6329ff, 0x6d592dff, 0x7f6c42ff, 0x97804dff, 0xaf965fff, 0xbea365ff, 0xd0b677ff, 0xdec68bff, 0xe7d097ff, 0xebd298ff, 0xeed397ff, 0xefd393ff, 0xefd492ff, 0xf2d897ff, 0xf2d998ff, 0xf1d797ff, 0xf0d58fff, 0xf2d792ff, 0xf2d790ff, 0xf2d88fff, 0xf2d78eff, 0xf0d286ff, 0xeec873ff, 0xeec770ff, 0xeec569ff, 0xefca71ff, 0xefcb72ff, 0xefcd79ff, 0xefd286ff, 0xf2d892ff, 0xf2da9fff, 0xeed495ff, 0xebd08aff, 0xecd193ff, 0xeace91ff, 0xead19aff, 0xead4a5ff, 0xe9d4a5ff, 0xead5a4ff, 0xe6cf9dff, 0xe3cd9fff, 0xe3cca1ff, 0xdbbe84ff, 0xc2994cff, 0xb98d38ff, 0xbc9240ff, 0xc89332ff, 0xc79330ff, 0xcb9b44ff, 0xca9a45ff, 0xca9a44ff, 0xc0892aff, 0xac6206ff, 0xaa5e04ff, 0xb27005ff, 0xba7d11ff, 0xba7d15ff, 0xaf6a07ff, 0x9e4a04ff, 0x983d04ff, 0x9a4004ff, 0xa04904ff, 0xa65305ff, 0xae6005ff, 0xb16705ff, 0xb36805ff, 0xb76a04ff, 0xbc7306ff, 0xc48311ff, 0xd0972dff, 0xe1b361ff, 0xe3b762ff, 0xe6bc64ff, 0xe8bf68ff, 0xe7c06dff, 0xcf9f50ff, 0x9e571eff, 0x833407ff, 0x702a01ff, 0x764514ff, 0xe1c166ff, 0xf1d171ff, 0xd5ae52ff, 0xc1953cff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2963dff, 0xc2953dff, 0xc2953cff, 0xc69b42ff, 0xd5ae52ff, 0xe7c566ff, 0xf1d170ff, 0xe4c66cff, 0xb79f5aff, 0x816e40ff, 0x6b562aff, 0x75581dff, 0x967b42ff, 0xb39c6cff, 0xc5ad79ff, 0xd2b87aff, 0xd9bb74ff, 0xe3c782ff, 0xebd18bff, 0xf1da9cff, 0xf0d594ff, 0xebcc86ff, 0xf0d391ff, 0xf2d899ff, 0xf2d895ff, 0xf2d795ff, 0xf3d899ff, 0xf2d68fff, 0xf3d890ff, 0xf5dc97ff, 0xf2d88eff, 0xf0d281ff, 0xf0cf7eff, 0xefcb75ff, 0xeec86bff, 0xefc86cff, 0xf1cc73ff, 0xf2d17dff, 0xf2d483ff, 0xf2d78cff, 0xf4db97ff, 0xf5dda1ff, 0xf1d898ff, 0xeed491ff, 0xeed395ff, 0xecd090ff, 0xecd39cff, 0xecd6a7ff, 0xead5a8ff, 0xebd7abff, 0xe9d6a8ff, 0xe5cd9bff, 0xe0c590ff, 0xdabc80ff, 0xc9a359ff, 0xc29b4bff, 0xc49f50ff, 0xc89733ff, 0xc49028ff, 0xc89639ff, 0xca9840ff, 0xcea150ff, 0xca9a46ff, 0xb57312ff, 0xae6304ff, 0xb37107ff, 0xb97a12ff, 0xbf8625ff, 0xb67813ff, 0xaf6b07ff, 0x9c4a05ff, 0x973c04ff, 0x9e4504ff, 0xa55004ff, 0xac5e04ff, 0xb06604ff, 0xb46804ff, 0xbc730aff, 0xc27f0dff, 0xc88b19ff, 0xdba94fff, 0xe7bf72ff, 0xe8be66ff, 0xecc676ff, 0xe8c37aff, 0xcb974aff, 0x8f3e0fff, 0x873103ff, 0x924a03ff, 0x8a5002ff, 0x885e15ff, 0xe3c366ff, 0xf1d171ff, 0xd9b154ff, 0xc79a3eff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc89b3fff, 0xc79b3fff, 0xc79a3fff, 0xc79a3fff, 0xcca144ff, 0xd9b254ff, 0xe9c767ff, 0xf1d170ff, 0xe2c56bff, 0xb39c5aff, 0x81714aff, 0x73664aff, 0x857453ff, 0x9b8355ff, 0xab8a4aff, 0xc0a05eff, 0xd4bb80ff, 0xdec384ff, 0xe5c784ff, 0xecd190ff, 0xf0d793ff, 0xf6e0a6ff, 0xf6e1abff, 0xf5dea3ff, 0xf3dca0ff, 0xf6dfa8ff, 0xf6dda1ff, 0xf4db9aff, 0xf3da94ff, 0xf6e0a4ff, 0xf5de9cff, 0xf7e09dff, 0xf6df99ff, 0xf5dc95ff, 0xf4d788ff, 0xf4d88cff, 0xf1d07dff, 0xf0cb6cff, 0xf3d27aff, 0xf5d887ff, 0xf6dd96ff, 0xf6df99ff, 0xf6e09eff, 0xf5df9bff, 0xf5dd99ff, 0xf5de9fff, 0xf3dea3ff, 0xf3dda7ff, 0xf1d89fff, 0xeed6a3ff, 0xebd3a4ff, 0xead4a6ff, 0xe9d5a6ff, 0xe8d5a9ff, 0xe7d1a0ff, 0xe0c184ff, 0xd5b26aff, 0xcca85eff, 0xcba85cff, 0xcca95eff, 0xcb9a3dff, 0xca993fff, 0xcc9d47ff, 0xc99741ff, 0xcd9f4bff, 0xcfa355ff, 0xba8021ff, 0xae6504ff, 0xb26f07ff, 0xb77812ff, 0xc18b32ff, 0xbf882cff, 0xb87b18ff, 0xa35908ff, 0x984204ff, 0x9c4404ff, 0xa44e05ff, 0xa85805ff, 0xaf6305ff, 0xba750fff, 0xc3831fff, 0xc88c21ff, 0xd19a31ff, 0xdead4bff, 0xe8bd62ff, 0xebc36bff, 0xe5be72ff, 0xc78b30ff, 0xb66d06ff, 0x9e4e03ff, 0xa95e05ff, 0xae730dff, 0x96660cff, 0x8c6720ff, 0xe2c367ff, 0xf1d171ff, 0xdcb556ff, 0xcc9f41ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcda041ff, 0xcd9f41ff, 0xcda041ff, 0xd1a547ff, 0xddb557ff, 0xebc868ff, 0xf1d170ff, 0xe1c36aff, 0xb09856ff, 0x7e6e47ff, 0x73654aff, 0x867758ff, 0x9f8d67ff, 0xb39b6fff, 0xc0a269ff, 0xcba55fff, 0xd6b062ff, 0xe4c67dff, 0xe9cc85ff, 0xeed38eff, 0xf0d58dff, 0xf1d68fff, 0xf7e2a8ff, 0xfae6b0ff, 0xf8e4aaff, 0xf7e2a7ff, 0xf7e1a2ff, 0xf7e2a0ff, 0xfae4a6ff, 0xf9e3a3ff, 0xfbe9b6ff, 0xf9e4a8ff, 0xfae5a9ff, 0xf9e5a5ff, 0xf8e298ff, 0xf8e094ff, 0xf9e29bff, 0xf6da8aff, 0xf4d279ff, 0xf6d986ff, 0xfae399ff, 0xfae59fff, 0xfae6aaff, 0xfbe7b5ff, 0xf9e5a9ff, 0xf8e4aaff, 0xf6e1a7ff, 0xf4dfa6ff, 0xf4e1aeff, 0xf2deabff, 0xeed79fff, 0xebd5a2ff, 0xead3a4ff, 0xe9d4a6ff, 0xe9d5a8ff, 0xe9d3a7ff, 0xe3c68eff, 0xd3ad60ff, 0xcda959ff, 0xcfab5bff, 0xd1ae66ff, 0xcb9b45ff, 0xcda251ff, 0xcea250ff, 0xca9b47ff, 0xcfa454ff, 0xd2a85dff, 0xc79540ff, 0xb26e0aff, 0xb16c04ff, 0xb3730aff, 0xc0872cff, 0xc38d33ff, 0xb87c19ff, 0xa6600bff, 0x923804ff, 0x993f04ff, 0x9f4a04ff, 0xa65605ff, 0xb06506ff, 0xbe7c18ff, 0xc78a23ff, 0xd09935ff, 0xdeb057ff, 0xe8c06bff, 0xebc56dff, 0xdbad5aff, 0xb77327ff, 0xb56a05ff, 0xbb7706ff, 0xb46c05ff, 0xb7750cff, 0xb47d21ff, 0x996b1fff, 0x8c6928ff, 0xe2c267ff, 0xf1d171ff, 0xdfb857ff, 0xd2a443ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a544ff, 0xd2a443ff, 0xd2a443ff, 0xd6aa4aff, 0xe0b959ff, 0xecca69ff, 0xf1d171ff, 0xdfc169ff, 0xad9656ff, 0x7b6b43ff, 0x716242ff, 0x85734eff, 0x9e8960ff, 0xb39b6fff, 0xc5aa7aff, 0xd0b27cff, 0xd2aa65ff, 0xd8ac5aff, 0xe0b65eff, 0xefd28fff, 0xf2d996ff, 0xf5de9aff, 0xf1d58bff, 0xefce7fff, 0xf5de9aff, 0xfce9b3ff, 0xfbe7aeff, 0xf7e19fff, 0xf8e3a5ff, 0xfae5a7ff, 0xfae7aaff, 0xfce9aeff, 0xfce8afff, 0xfce8b1ff, 0xfce9aeff, 0xfce9aaff, 0xfbe6a2ff, 0xfae49aff, 0xfae39aff, 0xfae294ff, 0xf9df91ff, 0xf9e192ff, 0xfbe49dff, 0xfbe7a9ff, 0xfce9adff, 0xfbe9b0ff, 0xfae5a7ff, 0xf8e4a6ff, 0xf6e3abff, 0xf6e1a9ff, 0xf6e3aeff, 0xf4e1afff, 0xf1dca4ff, 0xedd69eff, 0xead49fff, 0xe9d3a5ff, 0xe9d4a8ff, 0xe8d2a4ff, 0xe6ce9eff, 0xd8b670ff, 0xd0aa57ff, 0xd2af63ff, 0xd5b268ff, 0xd0a357ff, 0xd1a659ff, 0xd2a85cff, 0xd1a65bff, 0xcfa456ff, 0xd1a659ff, 0xc99b49ff, 0xb3720fff, 0xb16c04ff, 0xb3740bff, 0xbc821fff, 0xc7943dff, 0xc28b30ff, 0xaf6e14ff, 0x913704ff, 0x933604ff, 0x993f04ff, 0xa45004ff, 0xaf6305ff, 0xbf7e16ff, 0xcd9333ff, 0xdbab57ff, 0xe5ba63ff, 0xeac677ff, 0xeac87fff, 0xbf833eff, 0xa04502ff, 0xb15f04ff, 0xba7606ff, 0xb67007ff, 0xb16d07ff, 0xab7014ff, 0x946317ff, 0x876220ff, 0xe1c166ff, 0xf1d171ff, 0xe3bb59ff, 0xd7a945ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8aa46ff, 0xd8a946ff, 0xd8aa46ff, 0xdbaf4cff, 0xe3bc5bff, 0xeecb6aff, 0xf1d170ff, 0xddbf68ff, 0xa89150ff, 0x78663cff, 0x70603dff, 0x85724aff, 0x9c8759ff, 0xb09763ff, 0xc1a56cff, 0xceaf74ff, 0xd8b676ff, 0xdcb873ff, 0xdeb567ff, 0xddb35dff, 0xe0b356ff, 0xf0ce84ff, 0xf6e09fff, 0xf7e19eff, 0xf5dd97ff, 0xf3d88dff, 0xf8e3a0ff, 0xfbe7acff, 0xfbe7adff, 0xf8e3a1ff, 0xf8e3a1ff, 0xfbe6a7ff, 0xfceaaeff, 0xfdeab4ff, 0xfceab4ff, 0xfdeab9ff, 0xfdeab3ff, 0xfce9aaff, 0xfce9a8ff, 0xfce8a5ff, 0xfce6a0ff, 0xfbe69cff, 0xfce8a5ff, 0xfceaaeff, 0xfce9b0ff, 0xfceab5ff, 0xfcebb8ff, 0xfbe9b3ff, 0xfbe9b1ff, 0xf9e6a9ff, 0xf7e5abff, 0xf6e3abff, 0xf6e2acff, 0xf6e6b7ff, 0xf4e2b3ff, 0xf0d9a4ff, 0xebd094ff, 0xe8ce93ff, 0xe7cc97ff, 0xe6cc94ff, 0xe5cb95ff, 0xdcbc7bff, 0xd4ae5dff, 0xd4b162ff, 0xd9b76dff, 0xd3aa64ff, 0xd2a961ff, 0xd3a962ff, 0xd4aa64ff, 0xd3aa63ff, 0xd1a85aff, 0xcc9f50ff, 0xb57413ff, 0xb26d05ff, 0xbd8321ff, 0xc5923aff, 0xc99842ff, 0xc59138ff, 0xb47620ff, 0x8c3205ff, 0x8e2f03ff, 0x953a04ff, 0xa14a05ff, 0xb06405ff, 0xc0821bff, 0xd19a3bff, 0xe2b869ff, 0xe8be69ff, 0xebc87eff, 0xdcb168ff, 0xa65915ff, 0x9e4003ff, 0xac5605ff, 0xb97406ff, 0xb7760aff, 0xab6405ff, 0xa15d05ff, 0x8c5404ff, 0x815912ff, 0xe0c064ff, 0xf1d171ff, 0xe6be5aff, 0xddae47ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddaf48ff, 0xddae48ff, 0xddaf48ff, 0xe0b44eff, 0xe6c05dff, 0xefcd6bff, 0xf1d171ff, 0xdabe67ff, 0xa69052ff, 0x77663cff, 0x6f5d36ff, 0x836d3eff, 0x9b7f47ff, 0xae9256ff, 0xc0a46aff, 0xcdaf73ff, 0xd6b470ff, 0xdbb66bff, 0xdeb86cff, 0xe1b96bff, 0xe1bb6cff, 0xe1ba6dff, 0xe2b866ff, 0xf1d187ff, 0xf8e3a3ff, 0xf7e09dff, 0xf6df9bff, 0xf7e19fff, 0xf9e4a4ff, 0xf7df97ff, 0xfae6abff, 0xfae5a8ff, 0xfae5a6ff, 0xfceab1ff, 0xfdebb4ff, 0xfceaadff, 0xfceab2ff, 0xfcecbbff, 0xfcebb4ff, 0xfce9aaff, 0xfdeaadff, 0xfdeaafff, 0xfdeab0ff, 0xfce9abff, 0xfbe8aaff, 0xfbe7b0ff, 0xfcecbeff, 0xfcedc1ff, 0xfbecbcff, 0xfbeabaff, 0xfbe9b6ff, 0xfae9b4ff, 0xf9e8b2ff, 0xf7e3adff, 0xf5e0a7ff, 0xf4e1aaff, 0xf2e1b0ff, 0xf1dda6ff, 0xefd89cff, 0xe8cc84ff, 0xe5ca88ff, 0xe2c581ff, 0xe1c285ff, 0xd9b670ff, 0xd7b365ff, 0xd9b76dff, 0xdcb970ff, 0xd3aa62ff, 0xd1a65dff, 0xd3a75eff, 0xd4aa60ff, 0xd6ae69ff, 0xd4ad6aff, 0xd0a65dff, 0xb7781cff, 0xb46f08ff, 0xc1872dff, 0xcd9c50ff, 0xcd9e4eff, 0xca9946ff, 0xb77e2dff, 0x893006ff, 0x8c2c03ff, 0x933404ff, 0x9f4904ff, 0xb06505ff, 0xc28520ff, 0xd39f3cff, 0xe3b760ff, 0xe4b756ff, 0xdcad51ff, 0xc1831bff, 0xb46c06ff, 0xa55004ff, 0xa74f04ff, 0xb76f06ff, 0xb9790eff, 0xa75f05ff, 0x984b04ff, 0x864702ff, 0x7d5211ff, 0xdfbf64ff, 0xf1d171ff, 0xe9c15cff, 0xe2b34aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe3b44aff, 0xe2b44aff, 0xe2b44aff, 0xe3b44bff, 0xe4b851ff, 0xe9c35fff, 0xf0ce6cff, 0xf0d070ff, 0xd8bb65ff, 0xa38c4dff, 0x76653fff, 0x71603eff, 0x856e40ff, 0x9a8047ff, 0xad8c4aff, 0xbc974bff, 0xc8a151ff, 0xd5b16bff, 0xdcba77ff, 0xdfbc73ff, 0xe0bb70ff, 0xdfb86dff, 0xdfb668ff, 0xdeb665ff, 0xdfba6fff, 0xe5c077ff, 0xf4d792ff, 0xfae5aaff, 0xfbe8b2ff, 0xfbe8adff, 0xfae4a1ff, 0xf5db93ff, 0xf6db90ff, 0xfae5a1ff, 0xfce9aaff, 0xfde9acff, 0xfceaaaff, 0xfcebabff, 0xfdeaabff, 0xfceab1ff, 0xfcecb5ff, 0xfdedb3ff, 0xfdebb2ff, 0xfdeab2ff, 0xfcebb7ff, 0xfdeab7ff, 0xfdeab4ff, 0xfbe9b0ff, 0xf9e5a9ff, 0xf9e6b0ff, 0xf6e1a8ff, 0xf4dd9dff, 0xf5dfa0ff, 0xf6e3aaff, 0xf8e8b4ff, 0xf8e9b8ff, 0xf7e7b4ff, 0xf6e4b3ff, 0xf6e6b6ff, 0xf2e0acff, 0xf1dca2ff, 0xf0db9eff, 0xeed798ff, 0xecd59dff, 0xe5c787ff, 0xdcb97aff, 0xd9b56dff, 0xdcb971ff, 0xe0c079ff, 0xdfbc71ff, 0xcfa45bff, 0xcfa257ff, 0xcc9d4cff, 0xcd9f4dff, 0xd4ab65ff, 0xd6b174ff, 0xd1a860ff, 0xba7d20ff, 0xb26a05ff, 0xb77715ff, 0xbd7f26ff, 0xc48d36ff, 0xcc9d4cff, 0xbc863eff, 0x883007ff, 0x882a04ff, 0x8f3204ff, 0xa24e04ff, 0xb06304ff, 0xc4861eff, 0xdaaa4cff, 0xe4b85bff, 0xe3b95aff, 0xc38726ff, 0xb87304ff, 0xbb7909ff, 0xba780bff, 0xb26507ff, 0xb46c04ff, 0xb87609ff, 0x9f5406ff, 0x873604ff, 0x763102ff, 0x754510ff, 0xdebd63ff, 0xf1d171ff, 0xecc45dff, 0xe8b84cff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94dff, 0xe8b94cff, 0xe8b84cff, 0xe8b94dff, 0xe9bd53ff, 0xecc561ff, 0xf1cf6dff, 0xf0d070ff, 0xd6b964ff, 0x9f8849ff, 0x736033ff, 0x715d33ff, 0x876f42ff, 0x9d824bff, 0xad8c48ff, 0xb99242ff, 0xc49943ff, 0xcb9e41ff, 0xd3a547ff, 0xdbb15cff, 0xe0b96fff, 0xe1ba70ff, 0xe0b96cff, 0xdfb669ff, 0xddb262ff, 0xdbb361ff, 0xddb567ff, 0xe0b76dff, 0xeecc83ff, 0xfae3a5ff, 0xfbe5a8ff, 0xfceaaeff, 0xfbe8a2ff, 0xf8df94ff, 0xf8e197ff, 0xfceaadff, 0xfdecb3ff, 0xfceaaeff, 0xfceaa8ff, 0xfcecb1ff, 0xfdefbaff, 0xfdefb8ff, 0xfdedb6ff, 0xfdecb7ff, 0xfdebb6ff, 0xfce9b1ff, 0xf9e4a7ff, 0xf9e5a9ff, 0xf9e5b0ff, 0xefd396ff, 0xe3c071ff, 0xe1bc67ff, 0xdab257ff, 0xd7ad52ff, 0xd7ae50ff, 0xd7ad52ff, 0xe0bb69ff, 0xe9ce8cff, 0xefda9eff, 0xf1db9fff, 0xf3e1a8ff, 0xf4e2b0ff, 0xf2dfaaff, 0xf0da9cff, 0xeed898ff, 0xefdca8ff, 0xecd7a2ff, 0xeacf93ff, 0xe2c17bff, 0xdfbe75ff, 0xe3c380ff, 0xe2c27bff, 0xcea157ff, 0xcfa154ff, 0xcb9c4aff, 0xc58f33ff, 0xd2a458ff, 0xd9b47bff, 0xd2a964ff, 0xbb8023ff, 0xb26f08ff, 0xb87816ff, 0xbc7d1eff, 0xc18628ff, 0xcb9b4bff, 0xb17225ff, 0x852903ff, 0x852704ff, 0x8c2d04ff, 0x9c4404ff, 0xb16408ff, 0xc88c28ff, 0xe1b560ff, 0xe9c271ff, 0xdaad5cff, 0xb46c0eff, 0xba7507ff, 0xbe7f18ff, 0xc18620ff, 0xbd7e13ff, 0xb77107ff, 0xb57005ff, 0x974a05ff, 0x771f04ff, 0x6d2402ff, 0x6b370fff, 0xdcbb63ff, 0xf1d271ff, 0xefc65fff, 0xedbd4eff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbe4fff, 0xedbd4eff, 0xedbe4fff, 0xedc156ff, 0xeec862ff, 0xf1d06dff, 0xefd070ff, 0xd4b762ff, 0x9b8445ff, 0x715d30ff, 0x6f592cff, 0x856b34ff, 0x9b7d40ff, 0xad8b48ff, 0xbb9448ff, 0xc2963cff, 0xc6962eff, 0xca9b31ff, 0xd2a23dff, 0xd6a744ff, 0xd9ab4cff, 0xdcb057ff, 0xdbb058ff, 0xddb361ff, 0xdbb05bff, 0xd9ae56ff, 0xdaaf5aff, 0xdaae58ff, 0xdaae59ff, 0xe3b964ff, 0xf6db91ff, 0xfbe7a7ff, 0xfdeaafff, 0xfce9a8ff, 0xfbe49fff, 0xfae296ff, 0xfceaabff, 0xfdeeb8ff, 0xfcecb0ff, 0xfdecb2ff, 0xfdeeb9ff, 0xfceeb3ff, 0xfcebabff, 0xfdeab7ff, 0xfceabaff, 0xfce8b0ff, 0xfbe6a8ff, 0xf6dc97ff, 0xebcd86ff, 0xe0bc73ff, 0xd7ad55ff, 0xd3a743ff, 0xd2a53bff, 0xd3a63fff, 0xcfa33cff, 0xcca034ff, 0xc99827ff, 0xc89420ff, 0xcc9d30ff, 0xd3a947ff, 0xe0bc66ff, 0xebd38bff, 0xf0dba1ff, 0xefdba1ff, 0xf0db9fff, 0xefd99aff, 0xefdba3ff, 0xeddba6ff, 0xedd79eff, 0xe8cb88ff, 0xdfbd74ff, 0xe1c076ff, 0xe3c276ff, 0xd0a457ff, 0xd0a355ff, 0xc99740ff, 0xc58d30ff, 0xd3a55dff, 0xd6ae71ff, 0xcfa35dff, 0xba7d1fff, 0xb3720fff, 0xb67613ff, 0xb97b1aff, 0xbf8626ff, 0xc28a35ff, 0xae6b14ff, 0x8a3203ff, 0x832304ff, 0x872704ff, 0x984005ff, 0xb1690dff, 0xce9534ff, 0xe3ba66ff, 0xe6bf6cff, 0xc08638ff, 0xae6104ff, 0xbb7609ff, 0xbe7f19ff, 0xbe811bff, 0xc08116ff, 0xc08215ff, 0xb77509ff, 0x934204ff, 0x751d04ff, 0x6b2402ff, 0x69340fff, 0xdbb962ff, 0xf1d271ff, 0xf0c860ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c051ff, 0xf0c457ff, 0xf0ca64ff, 0xf2d06eff, 0xefcf70ff, 0xd2b664ff, 0x9d8952ff, 0x76663eff, 0x766236ff, 0x886d35ff, 0x9a7937ff, 0xa98233ff, 0xb48830ff, 0xbf8f31ff, 0xc49125ff, 0xc89521ff, 0xcd9b2aff, 0xd09f36ff, 0xd3a339ff, 0xd4a53dff, 0xd9ad53ff, 0xd5a643ff, 0xd6a544ff, 0xd7a74fff, 0xd3a44bff, 0xd1a245ff, 0xd1a349ff, 0xd0a247ff, 0xd2a54dff, 0xd6aa52ff, 0xeecd7fff, 0xf9e09aff, 0xfce7a7ff, 0xfce9a5ff, 0xfdeba8ff, 0xfbe499ff, 0xfce8a1ff, 0xfdecabff, 0xfdedb3ff, 0xfdecafff, 0xfceaa8ff, 0xfbe79fff, 0xf8df97ff, 0xf9e3a9ff, 0xf8e2a2ff, 0xf6de9aff, 0xf2d78fff, 0xe6c37aff, 0xdab467ff, 0xd8af5aff, 0xd4aa47ff, 0xd5ac49ff, 0xd6ab46ff, 0xd3a740ff, 0xcfa53eff, 0xcea237ff, 0xcc9c2aff, 0xca9922ff, 0xcb9c28ff, 0xcb9c2eff, 0xcd9e36ff, 0xd3ab4bff, 0xe4c575ff, 0xead087ff, 0xecd594ff, 0xefdca4ff, 0xefdba6ff, 0xeedaa2ff, 0xefdba4ff, 0xedd59eff, 0xe0bc71ff, 0xe1bd6bff, 0xe4c172ff, 0xd2a354ff, 0xd2a65bff, 0xca9843ff, 0xc28825ff, 0xcb9542ff, 0xd0a45bff, 0xca994cff, 0xb57516ff, 0xb06d07ff, 0xb67713ff, 0xbc812aff, 0xc0872eff, 0xbb7f1aff, 0xb3710bff, 0x9d4e05ff, 0x842904ff, 0x852404ff, 0xa04b05ff, 0xb16808ff, 0xce9438ff, 0xe2b762ff, 0xdbaf54ff, 0xa55f17ff, 0xa55503ff, 0xba7408ff, 0xbd7c16ff, 0xc08221ff, 0xc18520ff, 0xc28921ff, 0xc48a1fff, 0xa05811ff, 0x772605ff, 0x692202ff, 0x67320eff, 0xdab861ff, 0xf1d271ff, 0xf0c860ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c051ff, 0xf0c458ff, 0xf0ca64ff, 0xf2d16fff, 0xeece6fff, 0xcfb361ff, 0x988347ff, 0x74643cff, 0x796943ff, 0x927c47ff, 0xa99053ff, 0xbb9d59ff, 0xc6a559ff, 0xc3993fff, 0xbf8c22ff, 0xc8952cff, 0xcb962aff, 0xce9927ff, 0xd3a238ff, 0xd4a440ff, 0xd3a23aff, 0xd5a340ff, 0xd6a649ff, 0xd3a445ff, 0xd2a347ff, 0xd1a24cff, 0xd1a44fff, 0xd1a755ff, 0xcfa250ff, 0xcb9c48ff, 0xcea04dff, 0xcfa251ff, 0xdfb664ff, 0xf2d381ff, 0xfae4a1ff, 0xfbe7a3ff, 0xfce9a2ff, 0xfbe79fff, 0xfbe69bff, 0xfceaa5ff, 0xfdebafff, 0xfdeab1ff, 0xfce9afff, 0xf9e4a6ff, 0xf5d98eff, 0xf4d78aff, 0xf2d58aff, 0xe8c67cff, 0xdbb361ff, 0xd5ab52ff, 0xd8ae5aff, 0xd7ae56ff, 0xd1a742ff, 0xd4aa46ff, 0xd9b054ff, 0xd4aa49ff, 0xd0a43fff, 0xcfa33cff, 0xcfa336ff, 0xcea132ff, 0xcd9f2fff, 0xc99923ff, 0xc89722ff, 0xca9c2eff, 0xd2a946ff, 0xe3c371ff, 0xe9ce85ff, 0xeed99fff, 0xefdca8ff, 0xf0dba4ff, 0xf1dea9ff, 0xecd39cff, 0xe2bf75ff, 0xe3be6fff, 0xe3bf6eff, 0xd19d45ff, 0xd1a04eff, 0xca9538ff, 0xbf821cff, 0xc28325ff, 0xce9c4fff, 0xca984cff, 0xb37110ff, 0xb16f07ff, 0xb97b1fff, 0xbd8329ff, 0xc28b32ff, 0xc28a27ff, 0xb97b15ff, 0xab6408ff, 0x913f04ff, 0x852704ff, 0xa34e05ff, 0xb26408ff, 0xd1993fff, 0xe2b760ff, 0xd3a34dff, 0x9c4e0bff, 0xa04c04ff, 0xb66e07ff, 0xbd7b12ff, 0xc18323ff, 0xc3872aff, 0xc58b2eff, 0xc89235ff, 0xc69135ff, 0x9a5c1bff, 0x682202ff, 0x67320eff, 0xd9b761ff, 0xf1d271ff, 0xf0c860ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c051ff, 0xf0c458ff, 0xf0cb65ff, 0xf2d16fff, 0xedce6fff, 0xccb05dff, 0x947e40ff, 0x736239ff, 0x78663cff, 0x907943ff, 0xa78c4fff, 0xba9b53ff, 0xc9a757ff, 0xd8b86cff, 0xe5c780ff, 0xe7c77eff, 0xd4a345ff, 0xd4a043ff, 0xd6a54aff, 0xd4a345ff, 0xd29f3dff, 0xd4a13eff, 0xd2a13aff, 0xd1a13fff, 0xd09f42ff, 0xd0a048ff, 0xd0a24fff, 0xcfa252ff, 0xcda050ff, 0xcc9d4dff, 0xc99846ff, 0xc89644ff, 0xc89744ff, 0xcb9d4eff, 0xd3a658ff, 0xe9c471ff, 0xf5d98fff, 0xfbe4a0ff, 0xfce49cff, 0xfbe399ff, 0xfce79dff, 0xfce9a4ff, 0xfbe6a6ff, 0xfae5acff, 0xfbe4acff, 0xf4d898ff, 0xeaca7dff, 0xe4bf6eff, 0xdfb665ff, 0xd7af5dff, 0xd4aa51ff, 0xd6ad53ff, 0xd5ab4fff, 0xd3a94aff, 0xd2a741ff, 0xd4aa44ff, 0xd5ac4eff, 0xd5ab4bff, 0xd3a944ff, 0xd3a844ff, 0xd1a43cff, 0xcda138ff, 0xcd9f36ff, 0xcd9f31ff, 0xca9d2eff, 0xcc9e32ff, 0xcc9f34ff, 0xdab55bff, 0xe8cd84ff, 0xeacf8dff, 0xedd89eff, 0xeedba2ff, 0xf0dca5ff, 0xe8c988ff, 0xe1b969ff, 0xe3bc6aff, 0xe4bf6eff, 0xce983bff, 0xcf9d45ff, 0xcd9a43ff, 0xc18425ff, 0xc3872bff, 0xcb994eff, 0xc48e3dff, 0xb16d0bff, 0xb3710aff, 0xb87a1bff, 0xbe8429ff, 0xc48e38ff, 0xc89643ff, 0xbc8329ff, 0xaa6007ff, 0x924204ff, 0x852904ff, 0xa34c05ff, 0xb06006ff, 0xd19a40ff, 0xe3ba64ff, 0xbc8234ff, 0x964304ff, 0x9f4e04ff, 0xa95b05ff, 0xb87208ff, 0xbf801cff, 0xc38729ff, 0xc78e3aff, 0xca9646ff, 0xca9745ff, 0xb7832dff, 0x783c0dff, 0x65300dff, 0xd8b660ff, 0xf2d271ff, 0xf0c860ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c151ff, 0xf0c559ff, 0xf0cb66ff, 0xf2d16fff, 0xeccd6fff, 0xcaae5eff, 0x927c41ff, 0x715d2dff, 0x7a6535ff, 0x948155ff, 0xa9935eff, 0xb99c59ff, 0xc8a658ff, 0xd7b66aff, 0xe0bd6fff, 0xe7c880ff, 0xefd18dff, 0xf2d695ff, 0xe8be67ff, 0xdead4fff, 0xdaa74aff, 0xd9aa4fff, 0xd6a447ff, 0xd19f3fff, 0xcf9f40ff, 0xd0a146ff, 0xcfa04bff, 0xcd9e4cff, 0xca9a48ff, 0xc99846ff, 0xc89743ff, 0xc28d35ff, 0xc5903aff, 0xc7933eff, 0xc7933cff, 0xca9b46ff, 0xcfa250ff, 0xd7ab56ff, 0xe9c26aff, 0xf3d688ff, 0xf7dc8eff, 0xf9e091ff, 0xfae297ff, 0xf7df97ff, 0xf6dc99ff, 0xf1d590ff, 0xeed292ff, 0xd8ae6dff, 0xb07826ff, 0xb07a1eff, 0xcea145ff, 0xd6ae5aff, 0xd5ab53ff, 0xd7ae56ff, 0xd4ab4dff, 0xd4aa4aff, 0xd5ab49ff, 0xd5aa4aff, 0xd4aa4fff, 0xd4aa4eff, 0xd4a948ff, 0xd3a847ff, 0xd0a540ff, 0xcea23aff, 0xcda139ff, 0xcda137ff, 0xc89929ff, 0xc99b2fff, 0xc99b31ff, 0xcfa443ff, 0xe3c67bff, 0xe5c87eff, 0xe9d193ff, 0xeddaa3ff, 0xf1dda8ff, 0xe6c580ff, 0xdbb25dff, 0xdeb65eff, 0xe4be6bff, 0xc78d25ff, 0xc88d2fff, 0xcd9743ff, 0xc1852aff, 0xbd7e1dff, 0xbf8127ff, 0xbb7b1cff, 0xaf6806ff, 0xb36d07ff, 0xb97a18ff, 0xbf8325ff, 0xc79142ff, 0xc69443ff, 0xb6771aff, 0x9c4e04ff, 0x802a04ff, 0x802504ff, 0x9a4305ff, 0xb3650cff, 0xd5a247ff, 0xdfb55aff, 0xa65f1fff, 0x913c02ff, 0x984504ff, 0xa15005ff, 0xae6305ff, 0xba7911ff, 0xc18422ff, 0xc68b33ff, 0xcb9748ff, 0xcb9a4dff, 0xc19546ff, 0x9c702bff, 0x724616ff, 0xd6b45fff, 0xf2d272ff, 0xf0c860ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c151ff, 0xf0c55aff, 0xf0cc66ff, 0xf2d170ff, 0xebcc6eff, 0xc6aa59ff, 0x8f7841ff, 0x6f5c30ff, 0x7a6538ff, 0x947b43ff, 0xac965fff, 0xc2ac7cff, 0xd2bc88ff, 0xdcc388ff, 0xe1c37cff, 0xe8ca86ff, 0xe8c87fff, 0xebca83ff, 0xeece8cff, 0xefcf8bff, 0xecc774ff, 0xe9bf66ff, 0xeabe5fff, 0xeec979ff, 0xeabe63ff, 0xd8aa4fff, 0xcd9d44ff, 0xce9d47ff, 0xce9c49ff, 0xca9743ff, 0xc79139ff, 0xc58f35ff, 0xc58d34ff, 0xc28b32ff, 0xc28c36ff, 0xc18a30ff, 0xc58f37ff, 0xc99742ff, 0xce9f4eff, 0xcb9c4aff, 0xcc9a40ff, 0xd9af5fff, 0xe1bb70ff, 0xe3bc6cff, 0xddb566ff, 0xd3a34fff, 0xd3a454ff, 0xc7913eff, 0xbc822bff, 0xb06b18ff, 0x9d5d05ff, 0xa46d0bff, 0xc99b3cff, 0xd5ac56ff, 0xd5ac56ff, 0xd7b05dff, 0xd8af59ff, 0xd4ab4eff, 0xd5ad52ff, 0xd7ae57ff, 0xd4ab4fff, 0xd4ab51ff, 0xd4aa4cff, 0xd4a94aff, 0xd2a848ff, 0xd0a542ff, 0xd0a543ff, 0xcea33cff, 0xca9d2fff, 0xc99d32ff, 0xc79a2fff, 0xc99d35ff, 0xd4ac52ff, 0xe2c57bff, 0xe9d197ff, 0xebd39eff, 0xeed79fff, 0xe5c580ff, 0xd8af56ff, 0xdeb760ff, 0xe4be6fff, 0xc98d29ff, 0xc88b2aff, 0xc78c2eff, 0xbb7b19ff, 0xb6710aff, 0xb46e09ff, 0xaf6603ff, 0xad6303ff, 0xb06804ff, 0xb87513ff, 0xc08328ff, 0xc89345ff, 0xc58e40ff, 0xb6741bff, 0x8b3a02ff, 0x741e03ff, 0x791e03ff, 0x832903ff, 0xa8540fff, 0xd9a751ff, 0xdfb457ff, 0xa25b22ff, 0x892c02ff, 0x964104ff, 0x9d4b04ff, 0xa55604ff, 0xae6507ff, 0xba7914ff, 0xc68a2eff, 0xcb9443ff, 0xca9544ff, 0xc49854ff, 0xa88348ff, 0x886628ff, 0xd9b961ff, 0xf1d271ff, 0xf0c861ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c152ff, 0xf0c55aff, 0xf1cc67ff, 0xf2d270ff, 0xeaca6dff, 0xc2a657ff, 0x886d31ff, 0x684d17ff, 0x775d2eff, 0x90743eff, 0xa88c50ff, 0xbda162ff, 0xd0b67cff, 0xdfc791ff, 0xe9d39fff, 0xedd59eff, 0xefd498ff, 0xefd497ff, 0xeccc8aff, 0xeccd89ff, 0xedce8dff, 0xebca7eff, 0xeac573ff, 0xecc777ff, 0xedc770ff, 0xefcd81ff, 0xefcc7bff, 0xebc776ff, 0xd5a54dff, 0xc9943dff, 0xc8933bff, 0xc69136ff, 0xc58d2fff, 0xc68e33ff, 0xbf8529ff, 0xbd8326ff, 0xbe852aff, 0xbe852bff, 0xc18c35ff, 0xc69341ff, 0xc5913bff, 0xc18a2bff, 0xbb8227ff, 0xbc8740ff, 0xac681eff, 0xa7610bff, 0xa76309ff, 0xac6808ff, 0xad6a0bff, 0xaf6d0dff, 0xac6b0fff, 0x9d5803ff, 0x985c04ff, 0xa46c0aff, 0xc59437ff, 0xd2aa57ff, 0xd4ac59ff, 0xd4ad59ff, 0xd4ac55ff, 0xd4ac54ff, 0xd3ab51ff, 0xd5ae54ff, 0xd5ad52ff, 0xd5ac50ff, 0xd4ab4bff, 0xd2a846ff, 0xd1a745ff, 0xcfa541ff, 0xd1a644ff, 0xd0a642ff, 0xcea33dff, 0xcda23bff, 0xcca13cff, 0xcda23fff, 0xcb9f3aff, 0xd1a94cff, 0xdebd72ff, 0xe2c27fff, 0xe6c785ff, 0xdeb86aff, 0xdab45fff, 0xdeb863ff, 0xdfb761ff, 0xc98d2aff, 0xc48622ff, 0xbe7b18ff, 0xb26908ff, 0xad5e05ff, 0xb77119ff, 0xc18431ff, 0xc89343ff, 0xc79445ff, 0xcfa157ff, 0xd4a75eff, 0xd9af66ff, 0xd1a560ff, 0xc08839ff, 0xa46328ff, 0x80320dff, 0x762406ff, 0x751c02ff, 0x8e3b14ff, 0xd6a64fff, 0xdbac50ff, 0xab661aff, 0x7f2003ff, 0x892e04ff, 0x964004ff, 0xa35304ff, 0xa85b04ff, 0xad6306ff, 0xb77312ff, 0xc18326ff, 0xc48a2dff, 0xbe8b38ff, 0xa98145ff, 0x8d6d33ff, 0xdabb62ff, 0xf1d271ff, 0xf0c861ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c152ff, 0xf0c55bff, 0xf1cc68ff, 0xf3d270ff, 0xe8c96cff, 0xba9c50ff, 0x785a25ff, 0x64491bff, 0x715218ff, 0x87621aff, 0x9e7529ff, 0xb48c3fff, 0xc9a457ff, 0xd8b972ff, 0xe8d09aff, 0xefd7a1ff, 0xf2dba6ff, 0xf3daa1ff, 0xf6e0abff, 0xf1d697ff, 0xf0d395ff, 0xedce8fff, 0xebcc89ff, 0xebca84ff, 0xe9c77eff, 0xeac87cff, 0xebc97fff, 0xecca7cff, 0xecca7bff, 0xf1d490ff, 0xebc880ff, 0xd9ac54ff, 0xc99235ff, 0xbf8523ff, 0xc18726ff, 0xc0862aff, 0xb77b19ff, 0xb67a17ff, 0xb87d1cff, 0xb97e1eff, 0xbd8225ff, 0xc0882dff, 0xbd8424ff, 0xb87f1dff, 0x9b580fff, 0x92480bff, 0x934503ff, 0x964c03ff, 0x954d04ff, 0x985304ff, 0x9b5504ff, 0x985504ff, 0x955805ff, 0x955b04ff, 0x955e05ff, 0x9f6606ff, 0xc18f30ff, 0xd4ac5cff, 0xd5ae5dff, 0xd3ac58ff, 0xd4ad59ff, 0xd4ae59ff, 0xd4ad53ff, 0xd5ae56ff, 0xd6ae59ff, 0xd5ae58ff, 0xd3ab4cff, 0xd3a947ff, 0xd3aa49ff, 0xd3aa49ff, 0xd1a845ff, 0xd1a847ff, 0xcfa441ff, 0xd0a443ff, 0xd1a84bff, 0xcfa749ff, 0xcea443ff, 0xcfa548ff, 0xd2ab50ff, 0xdbb766ff, 0xdcb668ff, 0xdab563ff, 0xdcb96bff, 0xdeb966ff, 0xdeb861ff, 0xc2821cff, 0xbd7712ff, 0xb46806ff, 0xb36913ff, 0xc08134ff, 0xe3bb7eff, 0xe9c995ff, 0xebd0a0ff, 0xe9cd9dff, 0xecd1a6ff, 0xeacfa1ff, 0xe5c692ff, 0xe2c18cff, 0xe0bd85ff, 0xe0bc81ff, 0xd0a668ff, 0xbe8e4cff, 0x833a0eff, 0x873711ff, 0xd3a14eff, 0xd7a749ff, 0xb97916ff, 0x852d03ff, 0x802104ff, 0x8d3104ff, 0x974104ff, 0xa04e04ff, 0xa65804ff, 0xac6104ff, 0xb26805ff, 0xbc7a15ff, 0xb78025ff, 0xa57835ff, 0x90703bff, 0xdabb64ff, 0xf1d271ff, 0xf0c961ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c050ff, 0xf0c152ff, 0xf0c65bff, 0xf1cd68ff, 0xf3d271ff, 0xe7c86bff, 0xb7994eff, 0x755723ff, 0x512f07ff, 0x552c00ff, 0x7e5615ff, 0x986d1bff, 0xae8028ff, 0xc1933dff, 0xd2a858ff, 0xe0be79ff, 0xe7c683ff, 0xedcf8fff, 0xf2d9a4ff, 0xf3ddaeff, 0xf2dba7ff, 0xf5e0b0ff, 0xf2d99dff, 0xf3dba5ff, 0xf1d79fff, 0xeed091ff, 0xeaca88ff, 0xe8c77fff, 0xebcb84ff, 0xebcc87ff, 0xebc880ff, 0xe9c577ff, 0xefd192ff, 0xf0d299ff, 0xeeca7eff, 0xe5ba63ff, 0xc68e2cff, 0xb87a16ff, 0xb67917ff, 0xb2750fff, 0xb3740fff, 0xb47512ff, 0xb2720fff, 0xad6b0aff, 0xac690cff, 0xaf6e0eff, 0xa46209ff, 0x874503ff, 0x914d04ff, 0x9e5404ff, 0x985004ff, 0x914b04ff, 0x904a04ff, 0x8d4d04ff, 0x8c5204ff, 0x8d5604ff, 0x905904ff, 0x925c04ff, 0x986005ff, 0xba8626ff, 0xd3aa55ff, 0xd4b05fff, 0xd4b062ff, 0xd5b064ff, 0xd5af60ff, 0xd5b05dff, 0xd6b160ff, 0xd6b15eff, 0xd7af5dff, 0xd5ad53ff, 0xd5ad52ff, 0xd5ad57ff, 0xd6b05dff, 0xd5ae57ff, 0xd1a84bff, 0xd0a645ff, 0xd0a74aff, 0xcfa64bff, 0xcfa74eff, 0xd0a750ff, 0xcfa84eff, 0xd1aa52ff, 0xd4ac54ff, 0xd4ad53ff, 0xd8b360ff, 0xdbb86dff, 0xddbc74ff, 0xdfbb6eff, 0xb86f09ff, 0xb86e0bff, 0xcc903aff, 0xe4bd79ff, 0xebcc96ff, 0xedd1a2ff, 0xedd3a8ff, 0xeed6b0ff, 0xeed3a7ff, 0xedd3a6ff, 0xebcf9fff, 0xeacb99ff, 0xecd4abff, 0xe9cd9dff, 0xe5c48fff, 0xe6c692ff, 0xdebb7dff, 0xb88541ff, 0x955821ff, 0xc79342ff, 0xd3a046ff, 0xc2851fff, 0x9c5008ff, 0x7c1e03ff, 0x862804ff, 0x8d3404ff, 0x994204ff, 0xa55505ff, 0xa95d04ff, 0xac6104ff, 0xb06906ff, 0xb07318ff, 0x9e6e23ff, 0x8c6a30ff, 0xd9ba63ff, 0xf1d271ff, 0xf0c962ff, 0xf0c050ff, 0xf0c050ff, 0xf0c152ff, 0xf0c65cff, 0xf1cd69ff, 0xf2d271ff, 0xe6c66aff, 0xb4954cff, 0x705221ff, 0x4d2b06ff, 0x562c01ff, 0x663602ff, 0x7b4608ff, 0xa27326ff, 0xb98a30ff, 0xca9937ff, 0xd7aa50ff, 0xe3bd72ff, 0xeac88dff, 0xeecd98ff, 0xedcd8fff, 0xefd49bff, 0xf2daaaff, 0xf3dca9ff, 0xf4deaeff, 0xf0d496ff, 0xf4ddabff, 0xf4dbaaff, 0xf0d39aff, 0xe9c886ff, 0xe7c47aff, 0xe8c577ff, 0xeac985ff, 0xeccc8bff, 0xe8c57cff, 0xeac67eff, 0xefd198ff, 0xecca85ff, 0xedc671ff, 0xe5bb64ff, 0xc99237ff, 0xb2720eff, 0xad6a06ff, 0xad6a08ff, 0xaf6e09ff, 0xad6804ff, 0xab6504ff, 0xa45c04ff, 0xa15a04ff, 0x8d4b04ff, 0x955305ff, 0xab6705ff, 0xaa6405ff, 0xa25904ff, 0x9c5204ff, 0x9a5004ff, 0x964e04ff, 0x8e4e04ff, 0x8b5304ff, 0x8d5604ff, 0x905904ff, 0x925b03ff, 0xaa7110ff, 0xcb9e47ff, 0xd6b56eff, 0xd6b36cff, 0xd6b36dff, 0xd5b168ff, 0xd5b167ff, 0xd5af67ff, 0xd4ad5fff, 0xd7b260ff, 0xd7b260ff, 0xd6b360ff, 0xd6b262ff, 0xd6b05fff, 0xd5af5cff, 0xd3ad55ff, 0xd1a94eff, 0xd0a749ff, 0xd1a954ff, 0xcfa853ff, 0xcea64bff, 0xcfa74bff, 0xd1ab56ff, 0xd3ad56ff, 0xd5af58ff, 0xd9b667ff, 0xd9b870ff, 0xd8b66fff, 0xdcb972ff, 0xce9339ff, 0xdaa859ff, 0xe9c382ff, 0xefd09aff, 0xeccb99ff, 0xeac796ff, 0xe9c897ff, 0xeccca0ff, 0xebcd9fff, 0xeacf9fff, 0xebd0a1ff, 0xedd3abff, 0xecd8b5ff, 0xead3aaff, 0xe8ca98ff, 0xe6c691ff, 0xe5c58eff, 0xdeba7eff, 0xd3a961ff, 0xc08c3fff, 0xc28729ff, 0xc1821bff, 0xb47312ff, 0x883204ff, 0x812403ff, 0x8a3004ff, 0x953c04ff, 0xa04f04ff, 0xa55805ff, 0xa85a04ff, 0xaa5e04ff, 0xab6a10ff, 0x9c691dff, 0x865f1eff, 0xd7b75fff, 0xf1d272ff, 0xf0c962ff, 0xf0c153ff, 0xf0c65dff, 0xf1cd69ff, 0xf2d271ff, 0xe4c469ff, 0xb0924aff, 0x6e501fff, 0x4e2b05ff, 0x552b01ff, 0x663402ff, 0x753d03ff, 0x89510eff, 0xb68a48ff, 0xcfa964ff, 0xd2a550ff, 0xd8a84bff, 0xe2b762ff, 0xe8c279ff, 0xe9c389ff, 0xeecc9aff, 0xf1d39fff, 0xedce94ff, 0xeed196ff, 0xf1d6a0ff, 0xf2d8a2ff, 0xf0d396ff, 0xf2d9a7ff, 0xf2dbaaff, 0xf2d9a5ff, 0xeccc8dff, 0xe7c47aff, 0xe9c67aff, 0xeac884ff, 0xebc987ff, 0xe9c47bff, 0xe7be69ff, 0xe8c06cff, 0xe8be66ff, 0xe7bc57ff, 0xedc875ff, 0xe8c47cff, 0xb97d1fff, 0xab6403ff, 0xaa6205ff, 0xab6305ff, 0xaa6104ff, 0xab6304ff, 0xa45c04ff, 0x9c5204ff, 0x965304ff, 0xb0710cff, 0xb57811ff, 0xac6805ff, 0xa75e05ff, 0xa15805ff, 0xa05704ff, 0x9d5404ff, 0x984e04ff, 0x8f5104ff, 0x8c5404ff, 0x8f5704ff, 0x905903ff, 0x9d6207ff, 0xc28e36ff, 0xd3ae67ff, 0xd6b573ff, 0xd7b571ff, 0xd5b26aff, 0xd5b36eff, 0xd4af6aff, 0xd4ae64ff, 0xd7b367ff, 0xd7b264ff, 0xd5af5eff, 0xd5b05dff, 0xd4af5eff, 0xd5ae5dff, 0xd3ae5dff, 0xd1a954ff, 0xcea54aff, 0xcfa954ff, 0xcea753ff, 0xcea74eff, 0xd1aa55ff, 0xd0aa57ff, 0xd1aa55ff, 0xd3ad59ff, 0xd8b56cff, 0xd8b671ff, 0xd7b46dff, 0xd9b56cff, 0xeecc91ff, 0xefd096ff, 0xeece95ff, 0xeccc95ff, 0xedcc99ff, 0xf0d1a5ff, 0xeecf9fff, 0xeed2a6ff, 0xeed3a7ff, 0xedd0a2ff, 0xebcd9dff, 0xecd0a2ff, 0xecd2a9ff, 0xeacfa0ff, 0xebcf9fff, 0xe9cb99ff, 0xe9ca98ff, 0xe1bf87ff, 0xd8b26fff, 0xb98645ff, 0xb97a24ff, 0xbb7710ff, 0xba7813ff, 0xa25707ff, 0x7f2603ff, 0x862b04ff, 0x913804ff, 0x9b4904ff, 0xa15204ff, 0xa45405ff, 0xa85c05ff, 0xa8670bff, 0x976113ff, 0x845a19ff, 0xd6b55dff, 0xf2d271ff, 0xf0cd6aff, 0xf1ce6aff, 0xf2d271ff, 0xe3c469ff, 0xb1954cff, 0x6e5120ff, 0x4f2d06ff, 0x542901ff, 0x683603ff, 0x7a4103ff, 0x854703ff, 0x965912ff, 0xc69c5aff, 0xdfbe88ff, 0xe0bb78ff, 0xd9aa4dff, 0xe0b563ff, 0xe8c386ff, 0xe9c684ff, 0xe9c78dff, 0xefcf9eff, 0xf0d3a3ff, 0xeecf98ff, 0xeccb90ff, 0xf0d398ff, 0xf1d39aff, 0xf2d8a4ff, 0xf2ddb0ff, 0xf1dcadff, 0xf1d9a8ff, 0xf1d6a4ff, 0xedcf95ff, 0xebc988ff, 0xedcd90ff, 0xeccd8fff, 0xeed195ff, 0xedcb89ff, 0xecc881ff, 0xeac577ff, 0xe7be65ff, 0xebc471ff, 0xeece88ff, 0xd8ac58ff, 0xab630eff, 0xa55603ff, 0xa75b05ff, 0xa65905ff, 0xa65a04ff, 0xa55c05ff, 0xa66107ff, 0xb16f08ff, 0xba7c13ff, 0xb57610ff, 0xaa6305ff, 0xa85e04ff, 0xa45b04ff, 0x9f5504ff, 0x9d5304ff, 0x9f5404ff, 0x975004ff, 0x8b5204ff, 0x8e5604ff, 0x905804ff, 0x935a03ff, 0xad751cff, 0xc69b48ff, 0xd3b06cff, 0xd8b87bff, 0xd6b474ff, 0xd7b473ff, 0xd5b271ff, 0xd4b16cff, 0xd6b165ff, 0xd6b060ff, 0xd4ae5bff, 0xd3af5aff, 0xd4af5fff, 0xd3af60ff, 0xd2ac5bff, 0xd0a851ff, 0xcea64dff, 0xcfa751ff, 0xcca446ff, 0xcfa64cff, 0xd2ad5bff, 0xd1aa58ff, 0xcba249ff, 0xcda54bff, 0xd2ad5aff, 0xd5b36dff, 0xd5b46eff, 0xd8b56dff, 0xeac788ff, 0xefd097ff, 0xefd29fff, 0xf1d5a4ff, 0xf1d8adff, 0xf1d8b0ff, 0xf0d6a9ff, 0xf0d4abff, 0xf0d6acff, 0xefd3a9ff, 0xedd1a6ff, 0xecce9dff, 0xefd3a5ff, 0xecd0a0ff, 0xedd3a4ff, 0xedd4a9ff, 0xeacf9eff, 0xe6c68dff, 0xe6c892ff, 0xe6c796ff, 0xe1bf8dff, 0xcb9a50ff, 0xb57514ff, 0xab6107ff, 0x8c3904ff, 0x802504ff, 0x8c3304ff, 0x974004ff, 0x9d4c04ff, 0xa15205ff, 0xa85b04ff, 0xa7650bff, 0x976013ff, 0x845813ff, 0xd6b55dff, 0xf3d372ff, 0xf2d272ff, 0xe1c167ff, 0xaf924aff, 0x765e28ff, 0x63491cff, 0x6f4e1bff, 0x7d5621ff, 0x824d08ff, 0x8b4e05ff, 0x925004ff, 0x995605ff, 0xbf8d43ff, 0xe2bf88ff, 0xebca9aff, 0xe7c389ff, 0xdcab50ff, 0xe3ba6eff, 0xeac791ff, 0xecca96ff, 0xecc995ff, 0xeecd9cff, 0xedcb99ff, 0xeac792ff, 0xebc994ff, 0xefd098ff, 0xeece96ff, 0xefd29eff, 0xf0d7a8ff, 0xf2d9abff, 0xf0d19bff, 0xf1d4a3ff, 0xeed19aff, 0xeaca8aff, 0xebcb8fff, 0xebcb8dff, 0xeecf95ff, 0xefd19cff, 0xeecf97ff, 0xedcd94ff, 0xeac780ff, 0xe8c068ff, 0xeac46eff, 0xebc879ff, 0xd6a95cff, 0xae6a1fff, 0x9e4e03ff, 0x9e4e04ff, 0xa25506ff, 0xb37112ff, 0xbf841cff, 0xbe821bff, 0xb97b16ff, 0xae6806ff, 0xa96204ff, 0xa65d05ff, 0xa35804ff, 0x9e5004ff, 0x9a4d04ff, 0x9d5104ff, 0x9e5204ff, 0x914b04ff, 0x8c5304ff, 0x8f5704ff, 0x915903ff, 0xa16711ff, 0xc19545ff, 0xceab61ff, 0xd6b478ff, 0xd6b377ff, 0xd4b270ff, 0xd4b16fff, 0xd5b069ff, 0xd3ae5fff, 0xd4af60ff, 0xd5b063ff, 0xd4b061ff, 0xd4b167ff, 0xd3af62ff, 0xd1a959ff, 0xd0a753ff, 0xd0a956ff, 0xd1ac5bff, 0xd2ab58ff, 0xd3ac58ff, 0xd2ac5aff, 0xd1ab59ff, 0xcda44cff, 0xcea651ff, 0xd0aa59ff, 0xd3af62ff, 0xd5b167ff, 0xd8b468ff, 0xebc98bff, 0xf0d19eff, 0xefd4a8ff, 0xf1d4a7ff, 0xf2d8afff, 0xf1d9b0ff, 0xf0d4a6ff, 0xf1d6a6ff, 0xf2daafff, 0xefd3a3ff, 0xf0d3a5ff, 0xf0d2a0ff, 0xefd19fff, 0xefd5a8ff, 0xefd8b1ff, 0xeed4adff, 0xeed3a6ff, 0xedd3a3ff, 0xedd4a6ff, 0xead1a5ff, 0xead2a9ff, 0xe5c99bff, 0xd2a863ff, 0xac6e2bff, 0x802c03ff, 0x802903ff, 0x8d3604ff, 0x923c04ff, 0x984504ff, 0x9f4c04ff, 0xa45305ff, 0xa25b05ff, 0x955909ff, 0x88570eff, 0xd7b55cff, 0xe3c367ff, 0xae8e43ff, 0x6e4c1aff, 0x5a330aff, 0x704b14ff, 0x886220ff, 0x986c23ff, 0xa16c1dff, 0xa66a0eff, 0xa9690aff, 0xa15a03ff, 0xb67726ff, 0xe1ba7bff, 0xeccd9fff, 0xeecea0ff, 0xe9c791ff, 0xdcac55ff, 0xdeae59ff, 0xe7c082ff, 0xe9c58eff, 0xeac892ff, 0xeccc99ff, 0xeccc9dff, 0xedcd9dff, 0xeccc9bff, 0xeecf9eff, 0xeccd9cff, 0xebce9bff, 0xedd09eff, 0xf0d4a5ff, 0xefd098ff, 0xf0d29eff, 0xefd4a2ff, 0xeccb91ff, 0xeccc91ff, 0xeac98aff, 0xeac98aff, 0xedcd96ff, 0xeccc90ff, 0xe9c88dff, 0xeac88eff, 0xe8c784ff, 0xeac887ff, 0xedce8eff, 0xf1d497ff, 0xe6c685ff, 0xb2722eff, 0xa25609ff, 0xbf8224ff, 0xcb9633ff, 0xc89031ff, 0xbb7c16ff, 0xb06906ff, 0xac6404ff, 0xa86204ff, 0xa35904ff, 0xa05204ff, 0xa05504ff, 0x9c4f04ff, 0x9c4e04ff, 0x9d4f04ff, 0x994d04ff, 0x904f04ff, 0x8e5504ff, 0x915803ff, 0xac7420ff, 0xcca55eff, 0xcea95aff, 0xcea959ff, 0xd1ad65ff, 0xd1ad65ff, 0xd0a958ff, 0xd1aa5bff, 0xd3af63ff, 0xd4b26aff, 0xd4b064ff, 0xd2ad5bff, 0xd3af63ff, 0xd3af62ff, 0xd1ab56ff, 0xd3ab56ff, 0xd0a955ff, 0xd1a955ff, 0xd3ac5bff, 0xd2ad5dff, 0xd4af61ff, 0xd3af64ff, 0xd1ab58ff, 0xd1ab5bff, 0xd3ad62ff, 0xd4b065ff, 0xd5b167ff, 0xd6b168ff, 0xefd097ff, 0xeed0a1ff, 0xecc896ff, 0xeac48bff, 0xefcfa0ff, 0xf2d8abff, 0xf2d5a3ff, 0xf4ddb5ff, 0xf3dab2ff, 0xf2daaeff, 0xf4ddb0ff, 0xf3daa9ff, 0xf1d8acff, 0xf0d5a5ff, 0xf0d6a8ff, 0xefd4a6ff, 0xefd5a7ff, 0xf1dbb2ff, 0xefd6abff, 0xeed5abff, 0xeed8b0ff, 0xecd5aaff, 0xe6c793ff, 0xd5ad6cff, 0xb78242ff, 0x8f4514ff, 0x873103ff, 0x8e3804ff, 0x964504ff, 0x9b4604ff, 0x9f4d05ff, 0xa05504ff, 0x985703ff, 0x8e5809ff, 0xab8131ff, 0x865f1bff, 0x673f05ff, 0x6b3e03ff, 0x783c03ff, 0x803904ff, 0x9d5b0eff, 0xb07419ff, 0xb57714ff, 0xb77710ff, 0xb36f05ff, 0xaa6208ff, 0xd09d50ff, 0xebca9bff, 0xefd0a8ff, 0xedcd9bff, 0xe3bd7aff, 0xd8a74cff, 0xdbad5aff, 0xe0b66bff, 0xe6c180ff, 0xeac791ff, 0xeac897ff, 0xeac997ff, 0xeac997ff, 0xebcb9aff, 0xedce9eff, 0xeccc9aff, 0xeccb97ff, 0xebcb96ff, 0xedcc99ff, 0xedce97ff, 0xefd099ff, 0xeecf9bff, 0xebc98eff, 0xe8c582ff, 0xe4c07fff, 0xe3c080ff, 0xe7c684ff, 0xe8c688ff, 0xebcb93ff, 0xebca94ff, 0xe6c381ff, 0xe9c789ff, 0xedcd93ff, 0xedcc8eff, 0xf2d69eff, 0xe5c185ff, 0xcd9741ff, 0xd39c3eff, 0xce993fff, 0xbe7f21ff, 0xb16906ff, 0xad6204ff, 0xac6204ff, 0xa85e05ff, 0xa25504ff, 0x9e5204ff, 0xa15604ff, 0xa15705ff, 0x9d5004ff, 0x9b4d04ff, 0x9e5504ff, 0xa36005ff, 0xa26204ff, 0xa56504ff, 0xba8428ff, 0xcfa95fff, 0xcda65bff, 0xd3af68ff, 0xd1ad5dff, 0xd2ac60ff, 0xd3ae65ff, 0xd3ac5eff, 0xd5b064ff, 0xd5b26bff, 0xd4b065ff, 0xd2ac58ff, 0xd1a952ff, 0xd2ab53ff, 0xd3ab50ff, 0xd5ad56ff, 0xd4ad5aff, 0xd3ac58ff, 0xd5ae5eff, 0xd3ae5fff, 0xd4af62ff, 0xd3ad61ff, 0xd1ad60ff, 0xd2ac5cff, 0xd4ae64ff, 0xd5b16bff, 0xd5b26cff, 0xd6b067ff, 0xf0d29eff, 0xf1d4a7ff, 0xedcd9bff, 0xeecb97ff, 0xf2d8aeff, 0xf1d7a9ff, 0xf2d8a7ff, 0xf3dcb3ff, 0xf1d59fff, 0xf0d28eff, 0xf3d89aff, 0xf3d9a2ff, 0xf0d49cff, 0xf3dbaaff, 0xefd299ff, 0xefd196ff, 0xf1daaeff, 0xf1dab4ff, 0xf0dab0ff, 0xf0d9adff, 0xf2deb9ff, 0xeed9afff, 0xebcf9dff, 0xe4c388ff, 0xe5c283ff, 0xbe8e52ff, 0x853406ff, 0x8b3302ff, 0x923b04ff, 0x984404ff, 0xa04f04ff, 0xa55b05ff, 0x9e5805ff, 0x965704ff, 0x8c5405ff, 0x865307ff, 0x875204ff, 0x915805ff, 0x9c5a05ff, 0xa35904ff, 0xae6504ff, 0xb87007ff, 0xbe7c10ff, 0xbf8117ff, 0xb97608ff, 0xc3852bff, 0xe0b677ff, 0xeccca2ff, 0xeccea2ff, 0xe8c68dff, 0xe0b86dff, 0xd19e42ff, 0xd3a049ff, 0xd7a751ff, 0xdcae5aff, 0xe5c07fff, 0xe8c48bff, 0xe7c58dff, 0xe7c38eff, 0xe8c48fff, 0xe9c592ff, 0xe9c690ff, 0xeac892ff, 0xe8c58fff, 0xe9c790ff, 0xeac792ff, 0xe9c78aff, 0xeac88eff, 0xe6c17cff, 0xe5bb6dff, 0xe4bd76ff, 0xe5c181ff, 0xe6c27fff, 0xe7c485ff, 0xe9c68dff, 0xe7c489ff, 0xe6c481ff, 0xe9c684ff, 0xe7c380ff, 0xe7c178ff, 0xe9c377ff, 0xe7c282ff, 0xd6a24fff, 0xc98a2bff, 0xbe7a1cff, 0xb16707ff, 0xae6104ff, 0xab5c05ff, 0xa95905ff, 0xa55404ff, 0xa25504ff, 0xa05104ff, 0xa75804ff, 0xab6105ff, 0xad6805ff, 0xae6907ff, 0xb26f0aff, 0xb67a10ff, 0xb87a0fff, 0xba7c0fff, 0xbe8722ff, 0xd1a95fff, 0xd3af6fff, 0xd6b377ff, 0xd6b472ff, 0xd3ae63ff, 0xd5b26cff, 0xd7b470ff, 0xd7b56eff, 0xd7b46eff, 0xd7b46eff, 0xd6b164ff, 0xd4ad55ff, 0xd6ae57ff, 0xd5af5aff, 0xd6b05cff, 0xd9b56bff, 0xd8b36bff, 0xd9b369ff, 0xd6b265ff, 0xd6b268ff, 0xd6b370ff, 0xd4b16aff, 0xd2ad60ff, 0xd3ac5eff, 0xd3ad63ff, 0xd4b36dff, 0xd5b167ff, 0xf0d39fff, 0xf0d3a6ff, 0xf1d3a7ff, 0xf2d4a6ff, 0xf2d6a9ff, 0xf2d8a8ff, 0xf2d5a0ff, 0xf2d6a3ff, 0xf2d59aff, 0xf0d38fff, 0xf2d394ff, 0xf3d69eff, 0xf2d497ff, 0xf3daa2ff, 0xebcc8aff, 0xeed093ff, 0xf0d7a7ff, 0xf0d5a3ff, 0xf0d7a2ff, 0xf0daabff, 0xeed8b0ff, 0xefdab2ff, 0xedd4a3ff, 0xe9ca91ff, 0xeac991ff, 0xe7c78fff, 0xba884bff, 0x94480dff, 0x913c03ff, 0x9a4604ff, 0x9f4e05ff, 0xa25705ff, 0xa35805ff, 0xa25c05ff, 0xa16409ff, 0xa1680fff, 0xa26409ff, 0xa86304ff, 0xb26e06ff, 0xb87006ff, 0xbd7a0dff, 0xbe7c0cff, 0xc07e10ff, 0xc2831aff, 0xbe7c10ff, 0xce973fff, 0xe3bd82ff, 0xe6c38eff, 0xe5c085ff, 0xe7c389ff, 0xe5c083ff, 0xd5a651ff, 0xd19f44ff, 0xd8a85aff, 0xdeb26cff, 0xe0b66aff, 0xe8c388ff, 0xeac794ff, 0xe8c593ff, 0xe5c088ff, 0xe5c189ff, 0xe8c48fff, 0xe5c287ff, 0xe6c387ff, 0xe7c48eff, 0xe8c38dff, 0xe6c180ff, 0xe7c07bff, 0xe3b767ff, 0xdeb059ff, 0xdfb25dff, 0xe4bc74ff, 0xe4be7aff, 0xe6c383ff, 0xe7c589ff, 0xe5c07eff, 0xe9c789ff, 0xe8c786ff, 0xe5c17fff, 0xe4bb6dff, 0xe4bb69ff, 0xe4bc78ff, 0xd9aa5bff, 0xbc771aff, 0xb06103ff, 0xae5b04ff, 0xac5a04ff, 0xaa5604ff, 0xa65004ff, 0xaa5904ff, 0xa65404ff, 0xa34f06ff, 0xba7717ff, 0xbe8220ff, 0xbf841cff, 0xc38927ff, 0xc48a28ff, 0xc1851fff, 0xbe8014ff, 0xbc7e12ff, 0xbd8218ff, 0xcea355ff, 0xd5b276ff, 0xd7b47bff, 0xdab882ff, 0xd6b26eff, 0xd4af62ff, 0xd8b775ff, 0xd8b774ff, 0xd9b66fff, 0xd9b772ff, 0xd9b76fff, 0xd9b569ff, 0xd7b364ff, 0xd8b369ff, 0xd8b66fff, 0xd9b774ff, 0xd9b773ff, 0xd9b773ff, 0xdbba78ff, 0xdab874ff, 0xd6b672ff, 0xd6b470ff, 0xd5b065ff, 0xd3ae60ff, 0xd3ae62ff, 0xd4b067ff, 0xd7b26cff, 0xefd09dff, 0xf1d4a9ff, 0xf1d5a7ff, 0xf2d6a9ff, 0xf3dbb3ff, 0xf1d7a8ff, 0xebc782ff, 0xeecb8dff, 0xf1d199ff, 0xf2d69eff, 0xf3d9a5ff, 0xf4dcafff, 0xf2d5a0ff, 0xf2d49dff, 0xebca89ff, 0xeccb8bff, 0xedcc89ff, 0xebca81ff, 0xebca82ff, 0xf0d49cff, 0xf0daadff, 0xf0daaeff, 0xf1dcaeff, 0xefd7a3ff, 0xefd6a3ff, 0xeed6a5ff, 0xeacd92ff, 0xcca05bff, 0x9e570fff, 0x994702ff, 0x9f4d04ff, 0xa35604ff, 0xa75a04ff, 0xaa6304ff, 0xad6c0bff, 0xaf6d0cff, 0xad5f06ff, 0xb56e06ff, 0xba770bff, 0xbe7a0dff, 0xc18015ff, 0xc4841cff, 0xc58920ff, 0xc3881bff, 0xbf7f0fff, 0xcf9a43ff, 0xe3bd81ff, 0xe6c38aff, 0xe8c690ff, 0xe6c48fff, 0xe1bb80ff, 0xd5a651ff, 0xd5a54dff, 0xe1b97aff, 0xe3bd83ff, 0xe0b469ff, 0xe6c083ff, 0xe2ba7eff, 0xdcae6bff, 0xe1b77cff, 0xe7c38fff, 0xe7c48dff, 0xe5be84ff, 0xe4c086ff, 0xe6c28bff, 0xe7c389ff, 0xe6c383ff, 0xe5bd75ff, 0xe1b25eff, 0xe2b664ff, 0xe5bd73ff, 0xe7c17eff, 0xe4c17dff, 0xe9c68aff, 0xe9c68cff, 0xe6c282ff, 0xe6c381ff, 0xe9c68cff, 0xe9c68eff, 0xe3ba71ff, 0xe6c07fff, 0xe7c38bff, 0xe4bd78ff, 0xd49e49ff, 0xb76d0eff, 0xb16104ff, 0xb56807ff, 0xb87012ff, 0xb1620dff, 0xb86b08ff, 0xb86905ff, 0xb86c0eff, 0xca8c36ff, 0xcb9240ff, 0xc68d2fff, 0xc88e2fff, 0xc38725ff, 0xc4872cff, 0xba7a12ff, 0xb16d04ff, 0xb26f06ff, 0xc28d32ff, 0xd6b071ff, 0xd9b780ff, 0xdab884ff, 0xd6b170ff, 0xd3ab58ff, 0xd7b268ff, 0xdbb978ff, 0xdbb874ff, 0xdab874ff, 0xdbb875ff, 0xdaba77ff, 0xdab977ff, 0xd9b977ff, 0xdcbb80ff, 0xdcbb80ff, 0xd8b574ff, 0xd9b777ff, 0xdebe84ff, 0xddbd7fff, 0xd9b878ff, 0xd8b570ff, 0xd6b16aff, 0xd5b16cff, 0xd4af64ff, 0xd4ae63ff, 0xd6b16bff, 0xebc889ff, 0xf0d09cff, 0xf1d3a5ff, 0xf1d8adff, 0xf2d7acff, 0xefd298ff, 0xedc982ff, 0xf1d39aff, 0xf3d9a9ff, 0xf2d6a2ff, 0xf4dcb0ff, 0xf3deb6ff, 0xf2d9aaff, 0xf4daa8ff, 0xefd296ff, 0xedcc89ff, 0xedce89ff, 0xeed18dff, 0xedd395ff, 0xeccf8fff, 0xefd59fff, 0xf2dcb0ff, 0xf2deb4ff, 0xefd9abff, 0xf0d8a6ff, 0xefd9abff, 0xefd6a7ff, 0xebce9aff, 0xd1a868ff, 0xa8651fff, 0xa35503ff, 0xa55704ff, 0xa85805ff, 0xaf6205ff, 0xb36d09ff, 0xb16206ff, 0xb46405ff, 0xba760eff, 0xc07e1fff, 0xc38529ff, 0xc48827ff, 0xc78c29ff, 0xc78e27ff, 0xc78c23ff, 0xc68a22ff, 0xddb06dff, 0xe9c591ff, 0xeecd9fff, 0xe9c89aff, 0xdfb97eff, 0xd6a859ff, 0xd4a651ff, 0xd9ae64ff, 0xe0b97aff, 0xe1b977ff, 0xdfb468ff, 0xe5bf86ff, 0xddb474ff, 0xcc9433ff, 0xd4a452ff, 0xe6c18cff, 0xe7c38dff, 0xe7c18cff, 0xe7c28aff, 0xe7c488ff, 0xe7c48bff, 0xe9c68dff, 0xe3b970ff, 0xdead53ff, 0xdfb05aff, 0xe4ba6dff, 0xe6be75ff, 0xe6c07cff, 0xe6be77ff, 0xe5bc75ff, 0xe5be7cff, 0xe5c17fff, 0xe6c17fff, 0xe7c588ff, 0xe3bd79ff, 0xe5be7dff, 0xeac691ff, 0xe8c386ff, 0xe4ba6cff, 0xd0983bff, 0xbc7106ff, 0xc37c11ff, 0xca8a2bff, 0xc88620ff, 0xc88421ff, 0xc68322ff, 0xc58223ff, 0xcd9040ff, 0xc88a35ff, 0xc6862dff, 0xc28225ff, 0xbd7b17ff, 0xba7813ff, 0xb36e07ff, 0xb06904ff, 0xb26d03ff, 0xbc801aff, 0xd1a75aff, 0xd8b579ff, 0xd8b47aff, 0xd6b26dff, 0xd9b66fff, 0xd8b466ff, 0xddbc7bff, 0xe0bf87ff, 0xdbbc7cff, 0xdebf82ff, 0xdebe83ff, 0xdebe85ff, 0xddbd84ff, 0xdcbb82ff, 0xdbba7eff, 0xddbb81ff, 0xdbbb80ff, 0xe0c18cff, 0xdfc085ff, 0xdbba7cff, 0xd8b56fff, 0xd6b167ff, 0xd6b26cff, 0xd5b16aff, 0xd4ae66ff, 0xd6b068ff, 0xeac57cff, 0xecc989ff, 0xf0d5a4ff, 0xf0d9acff, 0xf0d29dff, 0xf2d396ff, 0xf3d699ff, 0xf5dba7ff, 0xf4ddafff, 0xf3daaaff, 0xf2d8a8ff, 0xf3dcb2ff, 0xf4ddb8ff, 0xf4dfb8ff, 0xf3dfb1ff, 0xf0d59aff, 0xf0d396ff, 0xf2daa6ff, 0xf1dbadff, 0xe8c782ff, 0xe8c778ff, 0xeed49dff, 0xefd5a3ff, 0xeed8a6ff, 0xeed6a4ff, 0xefd6a6ff, 0xedd4a8ff, 0xedd4a6ff, 0xeed5aaff, 0xdbb883ff, 0xb27322ff, 0xaa5f03ff, 0xac6305ff, 0xae6105ff, 0xb16305ff, 0xb15d04ff, 0xb9730bff, 0xc18428ff, 0xc78f40ff, 0xc89345ff, 0xc99342ff, 0xca933cff, 0xc78d26ff, 0xc68d20ff, 0xca9030ff, 0xe0b87eff, 0xeac997ff, 0xebcb9cff, 0xe8c697ff, 0xdab16dff, 0xd6aa5bff, 0xe0ba7eff, 0xe1bb7fff, 0xdeb979ff, 0xdeb874ff, 0xdfb66cff, 0xdeb46bff, 0xe1b97aff, 0xdaaf65ff, 0xddb36aff, 0xe4bf83ff, 0xe3bd80ff, 0xe3bc7cff, 0xe1b871ff, 0xdeb162ff, 0xe2b872ff, 0xe5bd7cff, 0xe1b362ff, 0xddad55ff, 0xdeb05bff, 0xe2b565ff, 0xe2b563ff, 0xe1b562ff, 0xdfb35bff, 0xe0b562ff, 0xe4ba70ff, 0xe6c080ff, 0xe5bf7eff, 0xe5c382ff, 0xe6c383ff, 0xe5c07cff, 0xe5c07eff, 0xe6bc73ff, 0xe6ba6bff, 0xddac55ff, 0xca8b26ff, 0xc5831cff, 0xc88523ff, 0xc6821bff, 0xc78627ff, 0xcb8f39ff, 0xc8882eff, 0xc7832eff, 0xca8a3bff, 0xc4832eff, 0xc07b20ff, 0xbb7715ff, 0xb7740cff, 0xb5740bff, 0xb56e05ff, 0xb67105ff, 0xb77711ff, 0xcda04eff, 0xd5ae67ff, 0xd5b06aff, 0xdbbb80ff, 0xe0c08bff, 0xdfbe85ff, 0xddbd7fff, 0xdebf86ff, 0xdfc087ff, 0xe0c290ff, 0xdfc28eff, 0xdebe86ff, 0xdcbc83ff, 0xdbba80ff, 0xdbba80ff, 0xdcba81ff, 0xdab880ff, 0xdcbc84ff, 0xdcbc7cff, 0xdbba77ff, 0xd7b46aff, 0xd7b366ff, 0xd5b26aff, 0xd5b16aff, 0xd6b270ff, 0xd6b26dff, 0xefd08aff, 0xeed093ff, 0xf0d6a3ff, 0xefd39bff, 0xf0d394ff, 0xf4d9a0ff, 0xf5dba5ff, 0xf7e2b3ff, 0xf4dba7ff, 0xf2d9a4ff, 0xf0d8a5ff, 0xf0d8a8ff, 0xf0d5abff, 0xf0d9abff, 0xf0daa9ff, 0xf0d9a2ff, 0xeed295ff, 0xf0d7a2ff, 0xf0daa9ff, 0xeacd8dff, 0xe6c675ff, 0xe9cd86ff, 0xf0daa7ff, 0xedd6a5ff, 0xebce98ff, 0xedd39fff, 0xefd6a5ff, 0xf0daafff, 0xeed7aeff, 0xeed3a2ff, 0xdfbb7bff, 0xba7e28ff, 0xae6907ff, 0xae6505ff, 0xaf6204ff, 0xb36604ff, 0xba7912ff, 0xc58b37ff, 0xca974fff, 0xca994fff, 0xcb9a4dff, 0xcb9644ff, 0xc78e2eff, 0xc58b20ff, 0xc88f2dff, 0xe1ba7dff, 0xebcc9cff, 0xe5c48fff, 0xdfb97eff, 0xd7ab5fff, 0xe0ba82ff, 0xe5c291ff, 0xe5c38dff, 0xe0bd7eff, 0xe2c083ff, 0xe3c186ff, 0xdcb56eff, 0xd8ac5cff, 0xddb369ff, 0xdbae5fff, 0xdfb56cff, 0xdfb56bff, 0xdbad57ff, 0xd9aa54ff, 0xdbad5cff, 0xdeb467ff, 0xe1b971ff, 0xdfb05dff, 0xdcac53ff, 0xdfb25cff, 0xe3b96bff, 0xe5bc73ff, 0xe4bb70ff, 0xe0b45fff, 0xe0b15aff, 0xdfaf54ff, 0xe4b967ff, 0xe6c07dff, 0xe8c68cff, 0xe7c484ff, 0xe6c17eff, 0xe5bf7aff, 0xe4b96cff, 0xe5bb70ff, 0xe5b96dff, 0xddaf5aff, 0xc98b2bff, 0xc68019ff, 0xc78524ff, 0xc8882fff, 0xc88929ff, 0xc5821fff, 0xc07813ff, 0xc47e25ff, 0xbb7215ff, 0xb76d09ff, 0xb9750fff, 0xbd8021ff, 0xbb7e1aff, 0xb67407ff, 0xb47005ff, 0xb07009ff, 0xc89b45ff, 0xd4af69ff, 0xd6b26cff, 0xddbc83ff, 0xe1c08eff, 0xe1c290ff, 0xe0c18dff, 0xdebe84ff, 0xe0c18bff, 0xe0c291ff, 0xdec18dff, 0xddbe85ff, 0xdebe86ff, 0xdcbf82ff, 0xdcbd82ff, 0xdbbb83ff, 0xdec08eff, 0xdec38eff, 0xdcbc7eff, 0xdbb978ff, 0xdab875ff, 0xd7b36aff, 0xd5b26bff, 0xd4b36fff, 0xd5b26dff, 0xd5b16dff, 0xf2d79eff, 0xefd396ff, 0xeecf8fff, 0xefd290ff, 0xf3d99fff, 0xf3dca7ff, 0xf4dda8ff, 0xf5dfaeff, 0xf2d9a3ff, 0xf2d59cff, 0xf0d7a2ff, 0xefd7a2ff, 0xf0d6a3ff, 0xeed6a0ff, 0xefd8a6ff, 0xeed7a0ff, 0xebce8cff, 0xeed49aff, 0xf0dbabff, 0xefd79dff, 0xebce82ff, 0xedd38cff, 0xf0dcabff, 0xecd6a6ff, 0xedd5a0ff, 0xecd29aff, 0xf0d9a8ff, 0xeedaadff, 0xebd2a1ff, 0xefd7a3ff, 0xefd8a4ff, 0xdebd7aff, 0xb57a1dff, 0xb26f0bff, 0xb5700aff, 0xb77309ff, 0xbb7d18ff, 0xc1862cff, 0xc79446ff, 0xca994eff, 0xcc9d51ff, 0xcb9847ff, 0xc89134ff, 0xc3891fff, 0xc58b21ff, 0xddb573ff, 0xe9ca9aff, 0xe3c188ff, 0xd9b16bff, 0xd7ad62ff, 0xdeba81ff, 0xe1bd86ff, 0xdeb975ff, 0xe1bf7fff, 0xe4c48cff, 0xe1bf82ff, 0xd8b165ff, 0xd0a346ff, 0xcfa042ff, 0xd0a044ff, 0xd8ab58ff, 0xddb366ff, 0xdaae5cff, 0xd8ab54ff, 0xdfb46aff, 0xe1b975ff, 0xe2bb77ff, 0xe1b76fff, 0xddae56ff, 0xe1b665ff, 0xe4bb71ff, 0xe3bb70ff, 0xe5bb6eff, 0xe1b561ff, 0xdfb25cff, 0xdeb156ff, 0xe5bd70ff, 0xe6c280ff, 0xe9c68eff, 0xe7c384ff, 0xe6c17cff, 0xe6be77ff, 0xe5bb6bff, 0xe5b96cff, 0xe3b666ff, 0xe1b462ff, 0xcc8e30ff, 0xc57f18ff, 0xc88520ff, 0xcd8f36ff, 0xc98b2bff, 0xcc943cff, 0xc98e34ff, 0xc58629ff, 0xbb740eff, 0xb46c04ff, 0xbc7b19ff, 0xc18528ff, 0xb97c15ff, 0xb37206ff, 0xb17208ff, 0xaf6f06ff, 0xc49135ff, 0xd5ad67ff, 0xd9b677ff, 0xdebe86ff, 0xe0c18fff, 0xe0c493ff, 0xe0c696ff, 0xe0c289ff, 0xe2c58fff, 0xe2c798ff, 0xdfc391ff, 0xddc086ff, 0xddbd85ff, 0xdbbd7eff, 0xdcbe80ff, 0xdebe87ff, 0xddbf89ff, 0xdfc18bff, 0xdcbc7eff, 0xdab876ff, 0xd9b876ff, 0xd7b56eff, 0xd7b571ff, 0xd7b675ff, 0xd3b06aff, 0xd3ac63ff, }; crispy-doom-crispy-doom-5.6.4/src/m_argv.c000066400000000000000000000222061360717211000204440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // #include #include #include #include #include "SDL_stdinc.h" #include "doomtype.h" #include "d_iwad.h" #include "i_system.h" #include "m_misc.h" #include "m_argv.h" // haleyjd 20110212: warning fix 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_CheckParmWithArgs(const char *check, int num_args) { int i; for (i = 1; i < myargc - num_args; i++) { if (!strcasecmp(check, myargv[i])) return i; } return 0; } // // M_ParmExists // // Returns true if the given parameter exists in the program's command // line arguments, false if not. // boolean M_ParmExists(const char *check) { return M_CheckParm(check) != 0; } int M_CheckParm(const char *check) { return M_CheckParmWithArgs(check, 0); } #define MAXARGVS 100 static void LoadResponseFile(int argv_index, const char *filename) { FILE *handle; int size; char *infile; char *file; char **newargv; int newargc; int i, k; // Read the response file into memory handle = fopen(filename, "rb"); if (handle == NULL) { printf ("\nNo such response file!"); exit(1); } printf("Found response file %s!\n", filename); size = M_FileLength(handle); // Read in the entire file // Allocate one byte extra - this is in case there is an argument // at the end of the response file, in which case a '\0' will be // needed. file = malloc(size + 1); i = 0; while (i < size) { k = fread(file + i, 1, size - i, handle); if (k < 0) { I_Error("Failed to read full contents of '%s'", filename); } i += k; } fclose(handle); // Create new arguments list array newargv = malloc(sizeof(char *) * MAXARGVS); newargc = 0; memset(newargv, 0, sizeof(char *) * MAXARGVS); // Copy all the arguments in the list up to the response file for (i=0; i= size) { break; } // If the next argument is enclosed in quote marks, treat // the contents as a single argument. This allows long filenames // to be specified. if (infile[k] == '\"') { // Skip the first character(") ++k; newargv[newargc++] = &infile[k]; // Read all characters between quotes while (k < size && infile[k] != '\"' && infile[k] != '\n') { ++k; } if (k >= size || infile[k] == '\n') { I_Error("Quotes unclosed in response file '%s'", filename); } // Cut off the string at the closing quote infile[k] = '\0'; ++k; } else { // Read in the next argument until a space is reached newargv[newargc++] = &infile[k]; while(k < size && !isspace(infile[k])) { ++k; } // Cut off the end of the argument at the first space infile[k] = '\0'; ++k; } } // Add arguments following the response file argument for (i=argv_index + 1; i // // Load extra command line arguments from the given response file. // Arguments read from the file will be inserted into the command // line replacing this argument. A response file can also be loaded // using the abbreviated syntax '@filename.rsp'. // i = M_CheckParmWithArgs("-response", 1); if (i <= 0) { break; } // Replace the -response argument so that the next time through // the loop we'll ignore it. Since some parameters stop reading when // an argument beginning with a '-' is encountered, we keep something // that starts with a '-'. myargv[i] = "-_"; LoadResponseFile(i + 1, myargv[i + 1]); } } #if defined(_WIN32) enum { FILETYPE_UNKNOWN = 0x0, FILETYPE_IWAD = 0x2, FILETYPE_PWAD = 0x4, FILETYPE_DEH = 0x8, }; static int GuessFileType(const char *name) { int ret = FILETYPE_UNKNOWN; const char *base; char *lower; static boolean iwad_found = false; base = M_BaseName(name); lower = M_StringDuplicate(base); M_ForceLowercase(lower); // only ever add one argument to the -iwad parameter if (iwad_found == false && D_IsIWADName(lower)) { ret = FILETYPE_IWAD; iwad_found = true; } else if (M_StringEndsWith(lower, ".wad") || M_StringEndsWith(lower, ".lmp")) { ret = FILETYPE_PWAD; } else if (M_StringEndsWith(lower, ".deh") || M_StringEndsWith(lower, ".bex") || // [crispy] *.bex M_StringEndsWith(lower, ".hhe") || M_StringEndsWith(lower, ".seh")) { ret = FILETYPE_DEH; } free(lower); return ret; } typedef struct { char *str; int type, stable; } argument_t; static int CompareByFileType(const void *a, const void *b) { const argument_t *arg_a = (const argument_t *) a; const argument_t *arg_b = (const argument_t *) b; const int ret = arg_a->type - arg_b->type; return ret ? ret : (arg_a->stable - arg_b->stable); } void M_AddLooseFiles(void) { int i, types = 0; char **newargv; argument_t *arguments; if (myargc < 2) { return; } // allocate space for up to three additional regular parameters arguments = malloc((myargc + 3) * sizeof(*arguments)); memset(arguments, 0, (myargc + 3) * sizeof(*arguments)); // check the command line and make sure it does not already // contain any regular parameters or response files // but only fully-qualified LFS or UNC file paths for (i = 1; i < myargc; i++) { char *arg = myargv[i]; int type; if (strlen(arg) < 3 || arg[0] == '-' || arg[0] == '@' || ((!isalpha(arg[0]) || arg[1] != ':' || arg[2] != '\\') && (arg[0] != '\\' || arg[1] != '\\'))) { free(arguments); return; } type = GuessFileType(arg); arguments[i].str = arg; arguments[i].type = type; arguments[i].stable = i; types |= type; } // add space for one additional regular parameter // for each discovered file type in the new argument list // and sort parameters right before their corresponding file paths if (types & FILETYPE_IWAD) { arguments[myargc].str = M_StringDuplicate("-iwad"); arguments[myargc].type = FILETYPE_IWAD - 1; myargc++; } if (types & FILETYPE_PWAD) { arguments[myargc].str = M_StringDuplicate("-merge"); arguments[myargc].type = FILETYPE_PWAD - 1; myargc++; } if (types & FILETYPE_DEH) { arguments[myargc].str = M_StringDuplicate("-deh"); arguments[myargc].type = FILETYPE_DEH - 1; myargc++; } newargv = malloc(myargc * sizeof(*newargv)); // sort the argument list by file type, except for the zeroth argument // which is the executable invocation itself SDL_qsort(arguments + 1, myargc - 1, sizeof(*arguments), CompareByFileType); newargv[0] = myargv[0]; for (i = 1; i < myargc; i++) { newargv[i] = arguments[i].str; } free(arguments); myargv = newargv; } #endif // Return the name of the executable used to start the program: const char *M_GetExecutableName(void) { return M_BaseName(myargv[0]); } crispy-doom-crispy-doom-5.6.4/src/m_argv.h000066400000000000000000000023721360717211000204530ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Nil. // #ifndef __M_ARGV__ #define __M_ARGV__ #include "doomtype.h" // // 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); // Same as M_CheckParm, but checks that num_args arguments are available // following the specified argument. int M_CheckParmWithArgs(const char *check, int num_args); void M_FindResponseFile(void); void M_AddLooseFiles(void); // Parameter has been specified? boolean M_ParmExists(const char *check); // Get name of executable used to run this program: const char *M_GetExecutableName(void); #endif crispy-doom-crispy-doom-5.6.4/src/m_bbox.c000066400000000000000000000021241360717211000204340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Main loop menu stuff. // Random number LUT. // Default Config File. // PCX Screenshots. // #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; } crispy-doom-crispy-doom-5.6.4/src/m_bbox.h000066400000000000000000000017011360717211000204410ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Nil. // #ifndef __M_BBOX__ #define __M_BBOX__ #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 crispy-doom-crispy-doom-5.6.4/src/m_cheat.c000066400000000000000000000041731360717211000205740ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Cheat sequence checking. // #include #include "doomtype.h" #include "m_cheat.h" // // CHEAT SEQUENCE PACKAGE // // // Called in st_stuff module, which handles the input. // Returns a 1 if the cheat was successful, 0 if failed. // int cht_CheckCheat ( cheatseq_t* cht, char key ) { // if we make a short sequence on a cheat with parameters, this // will not work in vanilla doom. behave the same. if (cht->parameter_chars > 0 && strlen(cht->sequence) < cht->sequence_len) return false; if (cht->chars_read < strlen(cht->sequence)) { // still reading characters from the cheat code // and verifying. reset back to the beginning // if a key is wrong if (key == cht->sequence[cht->chars_read]) ++cht->chars_read; else cht->chars_read = 0; cht->param_chars_read = 0; } else if (cht->param_chars_read < cht->parameter_chars) { // we have passed the end of the cheat sequence and are // entering parameters now cht->parameter_buf[cht->param_chars_read] = key; ++cht->param_chars_read; } if (cht->chars_read >= strlen(cht->sequence) && cht->param_chars_read >= cht->parameter_chars) { cht->chars_read = cht->param_chars_read = 0; return true; } // cheat not matched yet return false; } void cht_GetParam ( cheatseq_t* cht, char* buffer ) { memcpy(buffer, cht->parameter_buf, cht->parameter_chars); } crispy-doom-crispy-doom-5.6.4/src/m_cheat.h000066400000000000000000000023751360717211000206030ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Cheat code checking. // #ifndef __M_CHEAT__ #define __M_CHEAT__ // // CHEAT SEQUENCE PACKAGE // // declaring a cheat #define CHEAT(value, parameters) \ { value, sizeof(value) - 1, parameters, 0, 0, "" } #define MAX_CHEAT_LEN 25 #define MAX_CHEAT_PARAMS 5 typedef struct { // settings for this cheat char sequence[MAX_CHEAT_LEN]; size_t sequence_len; int parameter_chars; // state used during the game size_t chars_read; int param_chars_read; char parameter_buf[MAX_CHEAT_PARAMS]; } cheatseq_t; int cht_CheckCheat ( cheatseq_t* cht, char key ); void cht_GetParam ( cheatseq_t* cht, char* buffer ); #endif crispy-doom-crispy-doom-5.6.4/src/m_config.c000066400000000000000000001671271360717211000207660ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Configuration file interface. // #include #include #include #include #include #include #include "SDL_filesystem.h" #include "config.h" #include "doomtype.h" #include "doomkeys.h" #include "i_system.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "z_zone.h" // // DEFAULTS // // Location where all configuration data is stored - // default.cfg, savegames, etc. const char *configdir; static char *autoload_path = ""; // Default filenames for configuration files. static const char *default_main_config; static const char *default_extra_config; typedef enum { DEFAULT_INT, DEFAULT_INT_HEX, DEFAULT_STRING, DEFAULT_FLOAT, DEFAULT_KEY, } default_type_t; typedef struct { // Name of the variable const char *name; // Pointer to the location in memory of the variable union { int *i; char **s; float *f; } location; // Type of the variable default_type_t type; // If this is a key value, the original integer scancode we read from // the config file before translating it to the internal key value. // If zero, we didn't read this value from a config file. int untranslated; // The value we translated the scancode into when we read the // config file on startup. If the variable value is different from // this, it has been changed and needs to be converted; otherwise, // use the 'untranslated' value. int original_translated; // If true, this config variable has been bound to a variable // and is being used. boolean bound; } default_t; typedef struct { default_t *defaults; int numdefaults; const char *filename; } default_collection_t; #define CONFIG_VARIABLE_GENERIC(name, type) \ { #name, {NULL}, type, 0, 0, false } #define CONFIG_VARIABLE_KEY(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_KEY) #define CONFIG_VARIABLE_INT(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT) #define CONFIG_VARIABLE_INT_HEX(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT_HEX) #define CONFIG_VARIABLE_FLOAT(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_FLOAT) #define CONFIG_VARIABLE_STRING(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_STRING) //! @begin_config_file default static default_t doom_defaults_list[] = { //! // Mouse sensitivity. This value is used to multiply input mouse // movement to control the effect of moving the mouse. // // The "normal" maximum value available for this through the // in-game options menu is 9. A value of 31 or greater will cause // the game to crash when entering the options menu. // CONFIG_VARIABLE_INT(mouse_sensitivity), //! // Horizontal mouse sensitivity (strafe) // CONFIG_VARIABLE_INT(mouse_sensitivity_x2), //! // Vertical mouse sensitivity // CONFIG_VARIABLE_INT(mouse_sensitivity_y), //! // Volume of sound effects, range 0-15. // CONFIG_VARIABLE_INT(sfx_volume), //! // Volume of in-game music, range 0-15. // CONFIG_VARIABLE_INT(music_volume), //! // @game strife // // If non-zero, dialogue text is displayed over characters' pictures // when engaging actors who have voices. // CONFIG_VARIABLE_INT(show_talk), //! // @game strife // // Volume of voice sound effects, range 0-15. // CONFIG_VARIABLE_INT(voice_volume), //! // @game doom // // If non-zero, messages are displayed on the heads-up display // in the game ("picked up a clip", etc). If zero, these messages // are not displayed. // CONFIG_VARIABLE_INT(show_messages), //! // Keyboard key to turn right. // CONFIG_VARIABLE_KEY(key_right), //! // Keyboard key to turn left. // CONFIG_VARIABLE_KEY(key_left), //! // Keyboard key to move forward. // CONFIG_VARIABLE_KEY(key_up), //! // Keyboard key to move backward. // CONFIG_VARIABLE_KEY(key_down), //! // Keyboard key to move forward (alternative). // CONFIG_VARIABLE_KEY(key_alt_up), //! // Keyboard key to move backward (alternative). // CONFIG_VARIABLE_KEY(key_alt_down), //! // Keyboard key to strafe left. // CONFIG_VARIABLE_KEY(key_strafeleft), //! // Keyboard key to strafe left (alternative). // CONFIG_VARIABLE_KEY(key_alt_strafeleft), //! // Keyboard key to strafe right. // CONFIG_VARIABLE_KEY(key_straferight), //! // Keyboard key to strafe right (alternative). // CONFIG_VARIABLE_KEY(key_alt_straferight), //! // @game strife // // Keyboard key to use health. // CONFIG_VARIABLE_KEY(key_useHealth), //! // @game hexen // // Keyboard key to jump. // CONFIG_VARIABLE_KEY(key_jump), //! // @game heretic hexen // // Keyboard key to fly upward. // CONFIG_VARIABLE_KEY(key_flyup), //! // @game heretic hexen // // Keyboard key to fly downwards. // CONFIG_VARIABLE_KEY(key_flydown), //! // @game heretic hexen // // Keyboard key to center flying. // CONFIG_VARIABLE_KEY(key_flycenter), //! // @game heretic hexen // // Keyboard key to look up. // CONFIG_VARIABLE_KEY(key_lookup), //! // @game heretic hexen // // Keyboard key to look down. // CONFIG_VARIABLE_KEY(key_lookdown), //! // @game heretic hexen // // Keyboard key to center the view. // CONFIG_VARIABLE_KEY(key_lookcenter), //! // @game strife // // Keyboard key to query inventory. // CONFIG_VARIABLE_KEY(key_invquery), //! // @game strife // // Keyboard key to display mission objective. // CONFIG_VARIABLE_KEY(key_mission), //! // @game strife // // Keyboard key to display inventory popup. // CONFIG_VARIABLE_KEY(key_invPop), //! // @game strife // // Keyboard key to display keys popup. // CONFIG_VARIABLE_KEY(key_invKey), //! // @game strife // // Keyboard key to jump to start of inventory. // CONFIG_VARIABLE_KEY(key_invHome), //! // @game strife // // Keyboard key to jump to end of inventory. // CONFIG_VARIABLE_KEY(key_invEnd), //! // @game heretic hexen // // Keyboard key to scroll left in the inventory. // CONFIG_VARIABLE_KEY(key_invleft), //! // @game heretic hexen // // Keyboard key to scroll right in the inventory. // CONFIG_VARIABLE_KEY(key_invright), //! // @game strife // // Keyboard key to scroll left in the inventory. // CONFIG_VARIABLE_KEY(key_invLeft), //! // @game strife // // Keyboard key to scroll right in the inventory. // CONFIG_VARIABLE_KEY(key_invRight), //! // @game heretic hexen // // Keyboard key to use the current item in the inventory. // CONFIG_VARIABLE_KEY(key_useartifact), //! // @game strife // // Keyboard key to use inventory item. // CONFIG_VARIABLE_KEY(key_invUse), //! // @game strife // // Keyboard key to drop an inventory item. // CONFIG_VARIABLE_KEY(key_invDrop), //! // @game strife // // Keyboard key to look up. // CONFIG_VARIABLE_KEY(key_lookUp), //! // @game strife // // Keyboard key to look down. // CONFIG_VARIABLE_KEY(key_lookDown), //! // Keyboard key to fire the currently selected weapon. // CONFIG_VARIABLE_KEY(key_fire), //! // Keyboard key to "use" an object, eg. a door or switch. // CONFIG_VARIABLE_KEY(key_use), //! // Keyboard key to turn on strafing. When held down, pressing the // key to turn left or right causes the player to strafe left or // right instead. // CONFIG_VARIABLE_KEY(key_strafe), //! // Keyboard key to make the player run. // CONFIG_VARIABLE_KEY(key_speed), //! // If non-zero, mouse input is enabled. If zero, mouse input is // disabled. // CONFIG_VARIABLE_INT(use_mouse), //! // Mouse button to fire the currently selected weapon. // CONFIG_VARIABLE_INT(mouseb_fire), //! // Mouse button to turn on strafing. When held down, the player // will strafe left and right instead of turning left and right. // CONFIG_VARIABLE_INT(mouseb_strafe), //! // Mouse button to move forward. // CONFIG_VARIABLE_INT(mouseb_forward), //! // @game hexen strife // // Mouse button to jump. // CONFIG_VARIABLE_INT(mouseb_jump), //! // @game doom // // Mouse button to enable free looking. // CONFIG_VARIABLE_INT(mouseb_mouselook), //! // @game doom // // Quick 180° reverse. // CONFIG_VARIABLE_INT(mouseb_reverse), //! // If non-zero, joystick input is enabled. // CONFIG_VARIABLE_INT(use_joystick), //! // Joystick virtual button that fires the current weapon. // CONFIG_VARIABLE_INT(joyb_fire), //! // Joystick virtual button that makes the player strafe while // held down. // CONFIG_VARIABLE_INT(joyb_strafe), //! // Joystick virtual button to "use" an object, eg. a door or switch. // CONFIG_VARIABLE_INT(joyb_use), //! // Joystick virtual button that makes the player run while held // down. // // If this has a value of 20 or greater, the player will always run, // even if use_joystick is 0. // CONFIG_VARIABLE_INT(joyb_speed), //! // @game hexen strife // // Joystick virtual button that makes the player jump. // CONFIG_VARIABLE_INT(joyb_jump), //! // @game doom heretic hexen // // Screen size, range 3-11. // // A value of 11 gives a full-screen view with the status bar not // displayed. A value of 10 gives a full-screen view with the // status bar displayed. // CONFIG_VARIABLE_INT(screenblocks), //! // @game strife // // Screen size, range 3-11. // // A value of 11 gives a full-screen view with the status bar not // displayed. A value of 10 gives a full-screen view with the // status bar displayed. // CONFIG_VARIABLE_INT(screensize), //! // @game doom // // Screen detail. Zero gives normal "high detail" mode, while // a non-zero value gives "low detail" mode. // CONFIG_VARIABLE_INT(detaillevel), //! // Number of sounds that will be played simultaneously. // CONFIG_VARIABLE_INT(snd_channels), //! // Music output device. A non-zero value gives MIDI sound output, // while a value of zero disables music. // CONFIG_VARIABLE_INT(snd_musicdevice), //! // Sound effects device. A value of zero disables in-game sound // effects, a value of 1 enables PC speaker sound effects, while // a value in the range 2-9 enables the "normal" digital sound // effects. // CONFIG_VARIABLE_INT(snd_sfxdevice), //! // SoundBlaster I/O port. Unused. // CONFIG_VARIABLE_INT(snd_sbport), //! // SoundBlaster IRQ. Unused. // CONFIG_VARIABLE_INT(snd_sbirq), //! // SoundBlaster DMA channel. Unused. // CONFIG_VARIABLE_INT(snd_sbdma), //! // Output port to use for OPL MIDI playback. Unused. // CONFIG_VARIABLE_INT(snd_mport), //! // Gamma correction level. A value of zero disables gamma // correction, while a value in the range 1-4 gives increasing // levels of gamma correction. // CONFIG_VARIABLE_INT(usegamma), //! // @game hexen // // Directory in which to store savegames. // CONFIG_VARIABLE_STRING(savedir), //! // @game hexen // // Controls whether messages are displayed in the heads-up display. // If this has a non-zero value, messages are displayed. // CONFIG_VARIABLE_INT(messageson), //! // @game strife // // Name of background flat used by view border. // CONFIG_VARIABLE_STRING(back_flat), //! // @game strife // // Multiplayer nickname (?). // CONFIG_VARIABLE_STRING(nickname), //! // Multiplayer chat macro: message to send when alt+0 is pressed. // CONFIG_VARIABLE_STRING(chatmacro0), //! // Multiplayer chat macro: message to send when alt+1 is pressed. // CONFIG_VARIABLE_STRING(chatmacro1), //! // Multiplayer chat macro: message to send when alt+2 is pressed. // CONFIG_VARIABLE_STRING(chatmacro2), //! // Multiplayer chat macro: message to send when alt+3 is pressed. // CONFIG_VARIABLE_STRING(chatmacro3), //! // Multiplayer chat macro: message to send when alt+4 is pressed. // CONFIG_VARIABLE_STRING(chatmacro4), //! // Multiplayer chat macro: message to send when alt+5 is pressed. // CONFIG_VARIABLE_STRING(chatmacro5), //! // Multiplayer chat macro: message to send when alt+6 is pressed. // CONFIG_VARIABLE_STRING(chatmacro6), //! // Multiplayer chat macro: message to send when alt+7 is pressed. // CONFIG_VARIABLE_STRING(chatmacro7), //! // Multiplayer chat macro: message to send when alt+8 is pressed. // CONFIG_VARIABLE_STRING(chatmacro8), //! // Multiplayer chat macro: message to send when alt+9 is pressed. // CONFIG_VARIABLE_STRING(chatmacro9), //! // @game strife // // Serial port number to use for SERSETUP.EXE (unused). // CONFIG_VARIABLE_INT(comport), }; static default_collection_t doom_defaults = { doom_defaults_list, arrlen(doom_defaults_list), NULL, }; //! @begin_config_file extended static default_t extra_defaults_list[] = { //! // Name of the SDL video driver to use. If this is an empty string, // the default video driver is used. // CONFIG_VARIABLE_STRING(video_driver), //! // Position of the window on the screen when running in windowed // mode. Accepted values are: "" (empty string) - don't care, // "center" - place window at center of screen, "x,y" - place // window at the specified coordinates. // CONFIG_VARIABLE_STRING(window_position), //! // If non-zero, the game will run in full screen mode. If zero, // the game will run in a window. // CONFIG_VARIABLE_INT(fullscreen), //! // Index of the display on which the game should run. This has no // effect if running in windowed mode (fullscreen=0) and // window_position is not set to "center". // CONFIG_VARIABLE_INT(video_display), //! // If non-zero, the screen will be stretched vertically to display // correctly on a square pixel video mode. // CONFIG_VARIABLE_INT(aspect_ratio_correct), //! // If non-zero, forces integer scales for resolution-independent rendering. // CONFIG_VARIABLE_INT(integer_scaling), // If non-zero, any pillar/letter boxes drawn around the game area // will "flash" when the game palette changes, simulating the VGA // "porch" CONFIG_VARIABLE_INT(vga_porch_flash), //! // Window width when running in windowed mode. // CONFIG_VARIABLE_INT(window_width), //! // Window height when running in windowed mode. // CONFIG_VARIABLE_INT(window_height), //! // Width for screen mode when running fullscreen. // If this and fullscreen_height are both set to zero, we run // fullscreen as a desktop window that covers the entire screen, // rather than ever switching screen modes. It should usually // be unnecessary to set this value. // CONFIG_VARIABLE_INT(fullscreen_width), //! // Height for screen mode when running fullscreen. // See documentation for fullscreen_width. // CONFIG_VARIABLE_INT(fullscreen_height), //! // If non-zero, force the use of a software renderer. For use on // systems lacking hardware acceleration. // CONFIG_VARIABLE_INT(force_software_renderer), //! // Maximum number of pixels to use for intermediate scaling buffer. // More pixels mean that the screen can be rendered more precisely, // but there are diminishing returns on quality. The default limits to // 16,000,000 pixels, which is enough to cover 4K monitor standards. CONFIG_VARIABLE_INT(max_scaling_buffer_pixels), //! // Number of milliseconds to wait on startup after the video mode // has been set, before the game will start. This allows the // screen to settle on some monitors that do not display an image // for a brief interval after changing video modes. // CONFIG_VARIABLE_INT(startup_delay), //! // @game heretic hexen strife // // If non-zero, display the graphical startup screen. // CONFIG_VARIABLE_INT(graphical_startup), //! // @game doom heretic strife // // If non-zero, the ENDOOM text screen is displayed when exiting the // game. If zero, the ENDOOM screen is not displayed. // CONFIG_VARIABLE_INT(show_endoom), //! // @game doom strife // // If non-zero, a disk activity indicator is displayed when data is read // from disk. If zero, the disk activity indicator is not displayed. // CONFIG_VARIABLE_INT(show_diskicon), //! // If non-zero, save screenshots in PNG format. If zero, screenshots are // saved in PCX format, as Vanilla Doom does. // CONFIG_VARIABLE_INT(png_screenshots), //! // Vertical mouse acceleration factor. When the speed of mouse movement // exceeds the threshold value (mouse_threshold), the speed is // multiplied by this value. // CONFIG_VARIABLE_FLOAT(mouse_acceleration_y), //! // Vertical mouse acceleration threshold. When the speed of mouse movement // exceeds this threshold value, the speed is multiplied by an // acceleration factor (mouse_acceleration). // CONFIG_VARIABLE_INT(mouse_threshold_y), //! // Sound output sample rate, in Hz. Typical values to use are // 11025, 22050, 44100 and 48000. // CONFIG_VARIABLE_INT(snd_samplerate), //! // Maximum number of bytes to allocate for caching converted sound // effects in memory. If set to zero, there is no limit applied. // CONFIG_VARIABLE_INT(snd_cachesize), //! // Maximum size of the output sound buffer size in milliseconds. // Sound output is generated periodically in slices. Higher values // might be more efficient but will introduce latency to the // sound output. The default is 28ms (one slice per tic with the // 35fps timer). // CONFIG_VARIABLE_INT(snd_maxslicetime_ms), //! // If non-zero, sound effects will have their pitch varied up or // down by a random amount during play. If zero, sound effects // play back at their default pitch. // CONFIG_VARIABLE_INT(snd_pitchshift), //! // External command to invoke to perform MIDI playback. If set to // the empty string, SDL_mixer's internal MIDI playback is used. // This only has any effect when snd_musicdevice is set to General // MIDI output. // CONFIG_VARIABLE_STRING(snd_musiccmd), //! // Value to set for the DMXOPTION environment variable. If this contains // "-opl3", output for an OPL3 chip is generated when in OPL MIDI // playback mode. // CONFIG_VARIABLE_STRING(snd_dmxoption), //! // The I/O port to use to access the OPL chip. Only relevant when // using native OPL music playback. // CONFIG_VARIABLE_INT_HEX(opl_io_port), //! // Controls whether libsamplerate support is used for performing // sample rate conversions of sound effects. Support for this // must be compiled into the program. // // If zero, libsamplerate support is disabled. If non-zero, // libsamplerate is enabled. Increasing values roughly correspond // to higher quality conversion; the higher the quality, the // slower the conversion process. Linear conversion = 1; // Zero order hold = 2; Fast Sinc filter = 3; Medium quality // Sinc filter = 4; High quality Sinc filter = 5. // CONFIG_VARIABLE_INT(use_libsamplerate), //! // Scaling factor used by libsamplerate. This is used when converting // sounds internally back into integer form; normally it should not // be necessary to change it from the default value. The only time // it might be needed is if a PWAD file is loaded that contains very // loud sounds, in which case the conversion may cause sound clipping // and the scale factor should be reduced. The lower the value, the // quieter the sound effects become, so it should be set as high as is // possible without clipping occurring. CONFIG_VARIABLE_FLOAT(libsamplerate_scale), //! // Full path to a directory in which WAD files and dehacked patches // can be placed to be automatically loaded on startup. A subdirectory // of this directory matching the IWAD name is checked to find the // files to load. CONFIG_VARIABLE_STRING(autoload_path), //! // Full path to a directory containing configuration files for // substitute music packs. These packs contain high quality renderings // of game music to be played instead of using the system's built-in // MIDI playback. // CONFIG_VARIABLE_STRING(music_pack_path), //! // Full path to a Timidity configuration file to use for MIDI // playback. The file will be evaluated from the directory where // it is evaluated, so there is no need to add "dir" commands // into it. // CONFIG_VARIABLE_STRING(timidity_cfg_path), //! // Path to GUS patch files to use when operating in GUS emulation // mode. // CONFIG_VARIABLE_STRING(gus_patch_path), //! // Number of kilobytes of RAM to use in GUS emulation mode. Valid // values are 256, 512, 768 or 1024. // CONFIG_VARIABLE_INT(gus_ram_kb), //! // @game doom strife // // If non-zero, the Vanilla savegame limit is enforced; if the // savegame exceeds 180224 bytes in size, the game will exit with // an error. If this has a value of zero, there is no limit to // the size of savegames. // CONFIG_VARIABLE_INT(vanilla_savegame_limit), //! // @game doom strife // // If non-zero, the Vanilla demo size limit is enforced; the game // exits with an error when a demo exceeds the demo size limit // (128KiB by default). If this has a value of zero, there is no // limit to the size of demos. // CONFIG_VARIABLE_INT(vanilla_demo_limit), //! // If non-zero, the game behaves like Vanilla Doom, always assuming // an American keyboard mapping. If this has a value of zero, the // native keyboard mapping of the keyboard is used. // CONFIG_VARIABLE_INT(vanilla_keyboard_mapping), //! // Name to use in network games for identification. This is only // used on the "waiting" screen while waiting for the game to start. // CONFIG_VARIABLE_STRING(player_name), //! // If this is non-zero, the mouse will be "grabbed" when running // in windowed mode so that it can be used as an input device. // When running full screen, this has no effect. // CONFIG_VARIABLE_INT(grabmouse), //! // If non-zero, all vertical mouse movement is ignored. This // emulates the behavior of the "novert" tool available under DOS // that performs the same function. // CONFIG_VARIABLE_INT(novert), //! // Mouse acceleration factor. When the speed of mouse movement // exceeds the threshold value (mouse_threshold), the speed is // multiplied by this value. // CONFIG_VARIABLE_FLOAT(mouse_acceleration), //! // Mouse acceleration threshold. When the speed of mouse movement // exceeds this threshold value, the speed is multiplied by an // acceleration factor (mouse_acceleration). // CONFIG_VARIABLE_INT(mouse_threshold), //! // Mouse button to strafe left. // CONFIG_VARIABLE_INT(mouseb_strafeleft), //! // Mouse button to strafe right. // CONFIG_VARIABLE_INT(mouseb_straferight), //! // Mouse button to "use" an object, eg. a door or switch. // CONFIG_VARIABLE_INT(mouseb_use), //! // Mouse button to move backwards. // CONFIG_VARIABLE_INT(mouseb_backward), //! // Mouse button to cycle to the previous weapon. // CONFIG_VARIABLE_INT(mouseb_prevweapon), //! // Mouse button to cycle to the next weapon. // CONFIG_VARIABLE_INT(mouseb_nextweapon), //! // If non-zero, double-clicking a mouse button acts like pressing // the "use" key to use an object in-game, eg. a door or switch. // CONFIG_VARIABLE_INT(dclick_use), //! // SDL GUID string indicating the joystick to use. An empty string // indicates that no joystick is configured. // CONFIG_VARIABLE_STRING(joystick_guid), //! // Index of SDL joystick to use; this is only used in the case where // multiple identical joystick devices are connected which have the // same GUID, to distinguish between devices. // CONFIG_VARIABLE_INT(joystick_index), //! // Joystick axis to use to for horizontal (X) movement. // CONFIG_VARIABLE_INT(joystick_x_axis), //! // If non-zero, movement on the horizontal joystick axis is inverted. // CONFIG_VARIABLE_INT(joystick_x_invert), //! // Joystick axis to use to for vertical (Y) movement. // CONFIG_VARIABLE_INT(joystick_y_axis), //! // If non-zero, movement on the vertical joystick axis is inverted. // CONFIG_VARIABLE_INT(joystick_y_invert), //! // Joystick axis to use to for strafing movement. // CONFIG_VARIABLE_INT(joystick_strafe_axis), //! // If non-zero, movement on the joystick axis used for strafing // is inverted. // CONFIG_VARIABLE_INT(joystick_strafe_invert), //! // Joystick axis to use to for looking up and down. // CONFIG_VARIABLE_INT(joystick_look_axis), //! // If non-zero, movement on the joystick axis used for looking // is inverted. // CONFIG_VARIABLE_INT(joystick_look_invert), //! // The physical joystick button that corresponds to joystick // virtual button #0. // CONFIG_VARIABLE_INT(joystick_physical_button0), //! // The physical joystick button that corresponds to joystick // virtual button #1. // CONFIG_VARIABLE_INT(joystick_physical_button1), //! // The physical joystick button that corresponds to joystick // virtual button #2. // CONFIG_VARIABLE_INT(joystick_physical_button2), //! // The physical joystick button that corresponds to joystick // virtual button #3. // CONFIG_VARIABLE_INT(joystick_physical_button3), //! // The physical joystick button that corresponds to joystick // virtual button #4. // CONFIG_VARIABLE_INT(joystick_physical_button4), //! // The physical joystick button that corresponds to joystick // virtual button #5. // CONFIG_VARIABLE_INT(joystick_physical_button5), //! // The physical joystick button that corresponds to joystick // virtual button #6. // CONFIG_VARIABLE_INT(joystick_physical_button6), //! // The physical joystick button that corresponds to joystick // virtual button #7. // CONFIG_VARIABLE_INT(joystick_physical_button7), //! // The physical joystick button that corresponds to joystick // virtual button #8. // CONFIG_VARIABLE_INT(joystick_physical_button8), //! // The physical joystick button that corresponds to joystick // virtual button #9. // CONFIG_VARIABLE_INT(joystick_physical_button9), //! // The physical joystick button that corresponds to joystick // virtual button #10. // CONFIG_VARIABLE_INT(joystick_physical_button10), //! // Joystick virtual button to make the player strafe left. // CONFIG_VARIABLE_INT(joyb_strafeleft), //! // Joystick virtual button to make the player strafe right. // CONFIG_VARIABLE_INT(joyb_straferight), //! // Joystick virtual button to activate the menu. // CONFIG_VARIABLE_INT(joyb_menu_activate), //! // Joystick virtual button to toggle the automap. // CONFIG_VARIABLE_INT(joyb_toggle_automap), //! // Joystick virtual button that cycles to the previous weapon. // CONFIG_VARIABLE_INT(joyb_prevweapon), //! // Joystick virtual button that cycles to the next weapon. // CONFIG_VARIABLE_INT(joyb_nextweapon), //! // Key to pause or unpause the game. // CONFIG_VARIABLE_KEY(key_pause), //! // Key that activates the menu when pressed. // CONFIG_VARIABLE_KEY(key_menu_activate), //! // Key that moves the cursor up on the menu. // CONFIG_VARIABLE_KEY(key_menu_up), //! // Key that moves the cursor down on the menu. // CONFIG_VARIABLE_KEY(key_menu_down), //! // Key that moves the currently selected slider on the menu left. // CONFIG_VARIABLE_KEY(key_menu_left), //! // Key that moves the currently selected slider on the menu right. // CONFIG_VARIABLE_KEY(key_menu_right), //! // Key to go back to the previous menu. // CONFIG_VARIABLE_KEY(key_menu_back), //! // Key to activate the currently selected menu item. // CONFIG_VARIABLE_KEY(key_menu_forward), //! // Key to answer 'yes' to a question in the menu. // CONFIG_VARIABLE_KEY(key_menu_confirm), //! // Key to answer 'no' to a question in the menu. // CONFIG_VARIABLE_KEY(key_menu_abort), //! // Keyboard shortcut to bring up the help screen. // CONFIG_VARIABLE_KEY(key_menu_help), //! // Keyboard shortcut to bring up the save game menu. // CONFIG_VARIABLE_KEY(key_menu_save), //! // Keyboard shortcut to bring up the load game menu. // CONFIG_VARIABLE_KEY(key_menu_load), //! // Keyboard shortcut to bring up the sound volume menu. // CONFIG_VARIABLE_KEY(key_menu_volume), //! // Keyboard shortcut to toggle the detail level. // CONFIG_VARIABLE_KEY(key_menu_detail), //! // Keyboard shortcut to quicksave the current game. // CONFIG_VARIABLE_KEY(key_menu_qsave), //! // Keyboard shortcut to end the game. // CONFIG_VARIABLE_KEY(key_menu_endgame), //! // Keyboard shortcut to toggle heads-up messages. // CONFIG_VARIABLE_KEY(key_menu_messages), //! // Keyboard shortcut to load the last quicksave. // CONFIG_VARIABLE_KEY(key_menu_qload), //! // Keyboard shortcut to quit the game. // CONFIG_VARIABLE_KEY(key_menu_quit), //! // Keyboard shortcut to toggle the gamma correction level. // CONFIG_VARIABLE_KEY(key_menu_gamma), //! // Keyboard shortcut to switch view in multiplayer. // CONFIG_VARIABLE_KEY(key_spy), //! // Keyboard shortcut to go to next level. // CONFIG_VARIABLE_KEY(key_menu_nextlevel), //! // Keyboard shortcut to restart current level or demo. // CONFIG_VARIABLE_KEY(key_menu_reloadlevel), //! // Keyboard shortcut to increase the screen size. // CONFIG_VARIABLE_KEY(key_menu_incscreen), //! // Keyboard shortcut to decrease the screen size. // CONFIG_VARIABLE_KEY(key_menu_decscreen), //! // Keyboard shortcut to save a screenshot. // CONFIG_VARIABLE_KEY(key_menu_screenshot), //! // Keyboard shortcut to save a clean screenshot. // CONFIG_VARIABLE_KEY(key_menu_cleanscreenshot), //! // Key to delete a savegame. // CONFIG_VARIABLE_KEY(key_menu_del), //! // Key to toggle the map view. // CONFIG_VARIABLE_KEY(key_map_toggle), //! // Key to pan north when in the map view. // CONFIG_VARIABLE_KEY(key_map_north), //! // Key to pan south when in the map view. // CONFIG_VARIABLE_KEY(key_map_south), //! // Key to pan east when in the map view. // CONFIG_VARIABLE_KEY(key_map_east), //! // Key to pan west when in the map view. // CONFIG_VARIABLE_KEY(key_map_west), //! // Key to zoom in when in the map view. // CONFIG_VARIABLE_KEY(key_map_zoomin), //! // Key to zoom out when in the map view. // CONFIG_VARIABLE_KEY(key_map_zoomout), //! // Key to zoom out the maximum amount when in the map view. // CONFIG_VARIABLE_KEY(key_map_maxzoom), //! // Key to toggle follow mode when in the map view. // CONFIG_VARIABLE_KEY(key_map_follow), //! // Key to toggle the grid display when in the map view. // CONFIG_VARIABLE_KEY(key_map_grid), //! // Key to set a mark when in the map view. // CONFIG_VARIABLE_KEY(key_map_mark), //! // Key to clear all marks when in the map view. // CONFIG_VARIABLE_KEY(key_map_clearmark), //! // Key to toggle the overlay mode when in the map view. // CONFIG_VARIABLE_KEY(key_map_overlay), //! // Key to toggle the rotate mode when in the map view. // CONFIG_VARIABLE_KEY(key_map_rotate), //! // Key to select weapon 1. // CONFIG_VARIABLE_KEY(key_weapon1), //! // Key to select weapon 2. // CONFIG_VARIABLE_KEY(key_weapon2), //! // Key to select weapon 3. // CONFIG_VARIABLE_KEY(key_weapon3), //! // Key to select weapon 4. // CONFIG_VARIABLE_KEY(key_weapon4), //! // Key to select weapon 5. // CONFIG_VARIABLE_KEY(key_weapon5), //! // Key to select weapon 6. // CONFIG_VARIABLE_KEY(key_weapon6), //! // Key to select weapon 7. // CONFIG_VARIABLE_KEY(key_weapon7), //! // Key to select weapon 8. // CONFIG_VARIABLE_KEY(key_weapon8), //! // Key to cycle to the previous weapon. // CONFIG_VARIABLE_KEY(key_prevweapon), //! // Key to cycle to the next weapon. // CONFIG_VARIABLE_KEY(key_nextweapon), //! // @game hexen // // Key to use one of each artifact. // CONFIG_VARIABLE_KEY(key_arti_all), //! // @game hexen // // Key to use "quartz flask" artifact. // CONFIG_VARIABLE_KEY(key_arti_health), //! // @game hexen // // Key to use "flechette" artifact. // CONFIG_VARIABLE_KEY(key_arti_poisonbag), //! // @game hexen // // Key to use "disc of repulsion" artifact. // CONFIG_VARIABLE_KEY(key_arti_blastradius), //! // @game hexen // // Key to use "chaos device" artifact. // CONFIG_VARIABLE_KEY(key_arti_teleport), //! // @game hexen // // Key to use "banishment device" artifact. // CONFIG_VARIABLE_KEY(key_arti_teleportother), //! // @game hexen // // Key to use "porkalator" artifact. // CONFIG_VARIABLE_KEY(key_arti_egg), //! // @game hexen // // Key to use "icon of the defender" artifact. // CONFIG_VARIABLE_KEY(key_arti_invulnerability), //! // Key to re-display last message. // CONFIG_VARIABLE_KEY(key_message_refresh), //! // Key to quit the game when recording a demo. // CONFIG_VARIABLE_KEY(key_demo_quit), //! // Key to send a message during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msg), //! // Key to send a message to player 1 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer1), //! // Key to send a message to player 2 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer2), //! // Key to send a message to player 3 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer3), //! // Key to send a message to player 4 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer4), //! // @game hexen strife // // Key to send a message to player 5 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer5), //! // @game hexen strife // // Key to send a message to player 6 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer6), //! // @game hexen strife // // Key to send a message to player 7 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer7), //! // @game hexen strife // // Key to send a message to player 8 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer8), //! // @game doom // Quick 180° reverse. // CONFIG_VARIABLE_KEY(key_reverse), //! // @game doom // Toggle always run. // CONFIG_VARIABLE_KEY(key_toggleautorun), //! // @game doom // Toggle vertical mouse movement. // CONFIG_VARIABLE_KEY(key_togglenovert), //! // @game doom // Invert vertical mouse movement. // CONFIG_VARIABLE_KEY(mouse_y_invert), //! // @game doom // // Automap overlay mode. // CONFIG_VARIABLE_INT(crispy_automapoverlay), //! // @game doom // // Automap rotate mode. // CONFIG_VARIABLE_INT(crispy_automaprotate), //! // @game doom // // Show additional level statistics. // CONFIG_VARIABLE_INT(crispy_automapstats), //! // @game doom // // Variable player view bob. // CONFIG_VARIABLE_INT(crispy_bobfactor), //! // @game doom // // Apply brightmaps to select textures and sprites. // CONFIG_VARIABLE_INT(crispy_brightmaps), //! // @game doom // // Center Weapon when Firing. // CONFIG_VARIABLE_INT(crispy_centerweapon), //! // @game doom // // Enable Colored Blood. // CONFIG_VARIABLE_INT(crispy_coloredblood), //! // @game doom // // Show colored numbers in the status bar. // CONFIG_VARIABLE_INT(crispy_coloredhud), //! // @game doom // // Draw a crosshair. // CONFIG_VARIABLE_INT(crispy_crosshair), //! // @game doom // // Crosshair Color indicates Health. // CONFIG_VARIABLE_INT(crispy_crosshairhealth), //! // @game doom // // Highlight Crosshair on target. // CONFIG_VARIABLE_INT(crispy_crosshairtarget), //! // @game doom // // Crosshair type. // CONFIG_VARIABLE_INT(crispy_crosshairtype), //! // @game doom // // Show a progress bar when playing back a demo. // CONFIG_VARIABLE_INT(crispy_demobar), //! // @game doom // // Show a timer when recording or playing pack demos. // CONFIG_VARIABLE_INT(crispy_demotimer), //! // @game doom // // Timer direction whan playing back a demo. // CONFIG_VARIABLE_INT(crispy_demotimerdir), //! // @game doom // // Extended Automap. // CONFIG_VARIABLE_INT(crispy_extautomap), //! // @game doom // // Extended Savegames. // CONFIG_VARIABLE_INT(crispy_extsaveg), //! // @game doom // // Enable Mirrored Corpses. // CONFIG_VARIABLE_INT(crispy_flipcorpses), //! // @game doom // // Enable vertical aiming. // CONFIG_VARIABLE_INT(crispy_freeaim), //! // @game doom // // Enable looking up and down. // CONFIG_VARIABLE_INT(crispy_freelook), //! // @game doom // // High Resolution Rendering. // CONFIG_VARIABLE_INT(crispy_hires), //! // @game doom // // Enable jumping. // CONFIG_VARIABLE_INT(crispy_jump), //! // @game doom // // Show level time. // CONFIG_VARIABLE_INT(crispy_leveltime), //! // @game doom // // Use the mouse to look up and down. // CONFIG_VARIABLE_INT(crispy_mouselook), //! // @game doom // // Negative player health. // CONFIG_VARIABLE_INT(crispy_neghealth), //! // @game doom // // Players may walk over and under shootable things. // CONFIG_VARIABLE_INT(crispy_overunder), //! // @game doom // // Enable weapon recoil pitch. // CONFIG_VARIABLE_INT(crispy_pitch), //! // @game doom // // Show player coordinates. // CONFIG_VARIABLE_INT(crispy_playercoords), //! // @game doom // // Enable weapon recoil thrust. // CONFIG_VARIABLE_INT(crispy_recoil), //! // @game doom // // Show a centered message and play a sound when a secret is found. // CONFIG_VARIABLE_INT(crispy_secretmessage), //! // @game doom // // Smooth Diminishing Lighting. // CONFIG_VARIABLE_INT(crispy_smoothlight), //! // @game doom // // Smooth Scaling. // CONFIG_VARIABLE_INT(crispy_smoothscaling), //! // @game doom // // Misc. sound fixes. // CONFIG_VARIABLE_INT(crispy_soundfix), //! // @game doom // // Play sounds in full length. // CONFIG_VARIABLE_INT(crispy_soundfull), //! // @game doom // // Mono sound effects. // CONFIG_VARIABLE_INT(crispy_soundmono), //! // @game doom // // Enable translucency. // CONFIG_VARIABLE_INT(crispy_translucency), #ifdef CRISPY_TRUECOLOR //! // @game doom // // Enable true-color rendering (experimental). // CONFIG_VARIABLE_INT(crispy_truecolor), #endif //! // @game doom // // Uncapped Framerate. // CONFIG_VARIABLE_INT(crispy_uncapped), //! // @game doom // // Enable VSync. // CONFIG_VARIABLE_INT(crispy_vsync), //! // @game doom // // Squat down weapon on impact. // CONFIG_VARIABLE_INT(crispy_weaponsquat), }; static default_collection_t extra_defaults = { extra_defaults_list, arrlen(extra_defaults_list), NULL, }; // Search a collection for a variable static default_t *SearchCollection(default_collection_t *collection, const char *name) { int i; for (i=0; inumdefaults; ++i) { if (!strcmp(name, collection->defaults[i].name)) { return &collection->defaults[i]; } } return NULL; } // Mapping from DOS keyboard scan code to internal key code (as defined // in doomkey.h). I think I (fraggle) reused this from somewhere else // but I can't find where. Anyway, notes: // * KEY_PAUSE is wrong - it's in the KEY_NUMLOCK spot. This shouldn't // matter in terms of Vanilla compatibility because neither of // those were valid for key bindings. // * There is no proper scan code for PrintScreen (on DOS machines it // sends an interrupt). So I added a fake scan code of 126 for it. // The presence of this is important so we can bind PrintScreen as // a screenshot key. static const int scantokey[128] = { 0 , 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', KEY_BACKSPACE, 9, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13, KEY_RCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', KEY_RSHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT,KEYP_MULTIPLY, KEY_RALT, ' ', KEY_CAPSLOCK,KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /*KEY_NUMLOCK?*/KEY_PAUSE,KEY_SCRLCK,KEY_HOME, KEY_UPARROW,KEY_PGUP,KEY_MINUS,KEY_LEFTARROW,KEYP_5,KEY_RIGHTARROW,KEYP_PLUS,KEY_END, KEY_DOWNARROW,KEY_PGDN,KEY_INS,KEY_DEL,0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PRTSCR, 0 }; static void SaveDefaultCollection(default_collection_t *collection) { default_t *defaults; int i, v; FILE *f; f = fopen (collection->filename, "w"); if (!f) return; // can't write the file, but don't complain defaults = collection->defaults; for (i=0 ; inumdefaults ; i++) { int chars_written; // Ignore unbound variables if (!defaults[i].bound) { continue; } // Print the name and line up all values at 30 characters chars_written = fprintf(f, "%s ", defaults[i].name); for (; chars_written < 30; ++chars_written) fprintf(f, " "); // Print the value switch (defaults[i].type) { case DEFAULT_KEY: // use the untranslated version if we can, to reduce // the possibility of screwing up the user's config // file v = *defaults[i].location.i; if (v == KEY_RSHIFT) { // Special case: for shift, force scan code for // right shift, as this is what Vanilla uses. // This overrides the change check below, to fix // configuration files made by old versions that // mistakenly used the scan code for left shift. v = 54; } else if (defaults[i].untranslated && v == defaults[i].original_translated) { // Has not been changed since the last time we // read the config file. v = defaults[i].untranslated; } else { // search for a reverse mapping back to a scancode // in the scantokey table int s; for (s=0; s<128; ++s) { if (scantokey[s] == v) { v = s; break; } } } fprintf(f, "%i", v); break; case DEFAULT_INT: fprintf(f, "%i", *defaults[i].location.i); break; case DEFAULT_INT_HEX: fprintf(f, "0x%x", *defaults[i].location.i); break; case DEFAULT_FLOAT: fprintf(f, "%f", *defaults[i].location.f); break; case DEFAULT_STRING: fprintf(f,"\"%s\"", *defaults[i].location.s); break; } fprintf(f, "\n"); } fclose (f); } // Parses integer values in the configuration file static int ParseIntParameter(const char *strparm) { int parm; if (strparm[0] == '0' && strparm[1] == 'x') sscanf(strparm+2, "%x", &parm); else sscanf(strparm, "%i", &parm); return parm; } static void SetVariable(default_t *def, const char *value) { int intparm; // parameter found switch (def->type) { case DEFAULT_STRING: *def->location.s = M_StringDuplicate(value); break; case DEFAULT_INT: case DEFAULT_INT_HEX: *def->location.i = ParseIntParameter(value); break; case DEFAULT_KEY: // translate scancodes read from config // file (save the old value in untranslated) intparm = ParseIntParameter(value); def->untranslated = intparm; if (intparm >= 0 && intparm < 128) { intparm = scantokey[intparm]; } else { intparm = 0; } def->original_translated = intparm; *def->location.i = intparm; break; case DEFAULT_FLOAT: *def->location.f = (float) atof(value); break; } } static void LoadDefaultCollection(default_collection_t *collection) { FILE *f; default_t *def; char defname[80]; char strparm[100]; // read the file in, overriding any set defaults f = fopen(collection->filename, "r"); if (f == NULL) { // File not opened, but don't complain. // It's probably just the first time they ran the game. return; } while (!feof(f)) { if (fscanf(f, "%79s %99[^\n]\n", defname, strparm) != 2) { // This line doesn't match continue; } // Find the setting in the list def = SearchCollection(collection, defname); if (def == NULL || !def->bound) { // Unknown variable? Unbound variables are also treated // as unknown. continue; } // Strip off trailing non-printable characters (\r characters // from DOS text files) while (strlen(strparm) > 0 && !isprint(strparm[strlen(strparm)-1])) { strparm[strlen(strparm)-1] = '\0'; } // Surrounded by quotes? If so, remove them. if (strlen(strparm) >= 2 && strparm[0] == '"' && strparm[strlen(strparm) - 1] == '"') { strparm[strlen(strparm) - 1] = '\0'; memmove(strparm, strparm + 1, sizeof(strparm) - 1); } SetVariable(def, strparm); } fclose (f); } // Set the default filenames to use for configuration files. void M_SetConfigFilenames(const char *main_config, const char *extra_config) { default_main_config = main_config; default_extra_config = extra_config; } // // M_SaveDefaults // void M_SaveDefaults (void) { SaveDefaultCollection(&doom_defaults); SaveDefaultCollection(&extra_defaults); } // // Save defaults to alternate filenames // void M_SaveDefaultsAlternate(const char *main, const char *extra) { const char *orig_main; const char *orig_extra; // Temporarily change the filenames orig_main = doom_defaults.filename; orig_extra = extra_defaults.filename; doom_defaults.filename = main; extra_defaults.filename = extra; M_SaveDefaults(); // Restore normal filenames doom_defaults.filename = orig_main; extra_defaults.filename = orig_extra; } // // M_LoadDefaults // void M_LoadDefaults (void) { int i; // This variable is a special snowflake for no good reason. M_BindStringVariable("autoload_path", &autoload_path); // check for a custom default file //! // @arg // @vanilla // // Load main configuration from the specified file, instead of the // default. // i = M_CheckParmWithArgs("-config", 1); if (i) { doom_defaults.filename = myargv[i+1]; printf (" default file: %s\n",doom_defaults.filename); } else { doom_defaults.filename = M_StringJoin(configdir, default_main_config, NULL); } printf("saving config in %s\n", doom_defaults.filename); //! // @arg // // Load additional configuration from the specified file, instead of // the default. // i = M_CheckParmWithArgs("-extraconfig", 1); if (i) { extra_defaults.filename = myargv[i+1]; printf(" extra configuration file: %s\n", extra_defaults.filename); } else { extra_defaults.filename = M_StringJoin(configdir, default_extra_config, NULL); } LoadDefaultCollection(&doom_defaults); LoadDefaultCollection(&extra_defaults); } // Get a configuration file variable by its name static default_t *GetDefaultForName(const char *name) { default_t *result; // Try the main list and the extras result = SearchCollection(&doom_defaults, name); if (result == NULL) { result = SearchCollection(&extra_defaults, name); } // Not found? Internal error. if (result == NULL) { I_Error("Unknown configuration variable: '%s'", name); } return result; } // // Bind a variable to a given configuration file variable, by name. // void M_BindIntVariable(const char *name, int *location) { default_t *variable; variable = GetDefaultForName(name); assert(variable->type == DEFAULT_INT || variable->type == DEFAULT_INT_HEX || variable->type == DEFAULT_KEY); variable->location.i = location; variable->bound = true; } void M_BindFloatVariable(const char *name, float *location) { default_t *variable; variable = GetDefaultForName(name); assert(variable->type == DEFAULT_FLOAT); variable->location.f = location; variable->bound = true; } void M_BindStringVariable(const char *name, char **location) { default_t *variable; variable = GetDefaultForName(name); assert(variable->type == DEFAULT_STRING); variable->location.s = location; variable->bound = true; } // Set the value of a particular variable; an API function for other // parts of the program to assign values to config variables by name. boolean M_SetVariable(const char *name, const char *value) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound) { return false; } SetVariable(variable, value); return true; } // Get the value of a variable. int M_GetIntVariable(const char *name) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound || (variable->type != DEFAULT_INT && variable->type != DEFAULT_INT_HEX)) { return 0; } return *variable->location.i; } const char *M_GetStringVariable(const char *name) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound || variable->type != DEFAULT_STRING) { return NULL; } return *variable->location.s; } float M_GetFloatVariable(const char *name) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound || variable->type != DEFAULT_FLOAT) { return 0; } return *variable->location.f; } // Get the path to the default configuration dir to use, if NULL // is passed to M_SetConfigDir. static char *GetDefaultConfigDir(void) { #if !defined(_WIN32) || defined(_WIN32_WCE) // Configuration settings are stored in an OS-appropriate path // determined by SDL. On typical Unix systems, this might be // ~/.local/share/chocolate-doom. On Windows, we behave like // Vanilla Doom and save in the current directory. char *result; char *copy; result = SDL_GetPrefPath("", PACKAGE_TARNAME); if (result != NULL) { copy = M_StringDuplicate(result); SDL_free(result); return copy; } #endif /* #ifndef _WIN32 */ return M_StringDuplicate(""); } // // SetConfigDir: // // Sets the location of the configuration directory, where configuration // files are stored - default.cfg, chocolate-doom.cfg, savegames, etc. // void M_SetConfigDir(const char *dir) { // Use the directory that was passed, or find the default. if (dir != NULL) { configdir = dir; } else { configdir = GetDefaultConfigDir(); } if (strcmp(configdir, "") != 0) { printf("Using %s for configuration and saves\n", configdir); } // Make the directory if it doesn't already exist: M_MakeDirectory(configdir); } #define MUSIC_PACK_README \ "Extract music packs into this directory in .flac or .ogg format;\n" \ "they will be automatically loaded based on filename to replace the\n" \ "in-game music with high quality versions.\n\n" \ "For more information check here:\n\n" \ " \n\n" // Set the value of music_pack_path if it is currently empty, and create // the directory if necessary. void M_SetMusicPackDir(void) { const char *current_path; char *prefdir, *music_pack_path, *readme_path; current_path = M_GetStringVariable("music_pack_path"); if (current_path != NULL && strlen(current_path) > 0) { return; } prefdir = SDL_GetPrefPath("", PACKAGE_TARNAME); music_pack_path = M_StringJoin(prefdir, "music-packs", NULL); M_MakeDirectory(prefdir); M_MakeDirectory(music_pack_path); M_SetVariable("music_pack_path", music_pack_path); // We write a README file with some basic instructions on how to use // the directory. readme_path = M_StringJoin(music_pack_path, DIR_SEPARATOR_S, "README.txt", NULL); M_WriteFile(readme_path, MUSIC_PACK_README, strlen(MUSIC_PACK_README)); free(readme_path); free(music_pack_path); SDL_free(prefdir); } // // Calculate the path to the directory to use to store save games. // Creates the directory as necessary. // char *M_GetSaveGameDir(const char *iwadname) { char *savegamedir; char *topdir; int p; //! // @arg // // Specify a path from which to load and save games. If the directory // does not exist then it will automatically be created. // p = M_CheckParmWithArgs("-savedir", 1); if (p) { savegamedir = myargv[p + 1]; if (!M_FileExists(savegamedir)) { M_MakeDirectory(savegamedir); } // add separator at end just in case savegamedir = M_StringJoin(savegamedir, DIR_SEPARATOR_S, NULL); printf("Save directory changed to %s.\n", savegamedir); } #ifdef _WIN32 // In -cdrom mode, we write savegames to a specific directory // in addition to configs. else if (M_ParmExists("-cdrom")) { savegamedir = M_StringDuplicate(configdir); } #endif // If not "doing" a configuration directory (Windows), don't "do" // a savegame directory, either. else if (!strcmp(configdir, "")) { savegamedir = M_StringDuplicate(""); } else { // ~/.local/share/chocolate-doom/savegames topdir = M_StringJoin(configdir, "savegames", NULL); M_MakeDirectory(topdir); // eg. ~/.local/share/chocolate-doom/savegames/doom2.wad/ savegamedir = M_StringJoin(topdir, DIR_SEPARATOR_S, iwadname, DIR_SEPARATOR_S, NULL); M_MakeDirectory(savegamedir); free(topdir); } return savegamedir; } // // Calculate the path to the directory for autoloaded WADs/DEHs. // Creates the directory as necessary. // char *M_GetAutoloadDir(const char *iwadname) { char *result; if (autoload_path == NULL || strlen(autoload_path) == 0) { char *prefdir; prefdir = SDL_GetPrefPath("", PACKAGE_TARNAME); autoload_path = M_StringJoin(prefdir, "autoload", NULL); SDL_free(prefdir); } M_MakeDirectory(autoload_path); result = M_StringJoin(autoload_path, DIR_SEPARATOR_S, iwadname, NULL); M_MakeDirectory(result); // TODO: Add README file return result; } crispy-doom-crispy-doom-5.6.4/src/m_config.h000066400000000000000000000027271360717211000207650ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Configuration file interface. // #ifndef __M_CONFIG__ #define __M_CONFIG__ #include "doomtype.h" void M_LoadDefaults(void); void M_SaveDefaults(void); void M_SaveDefaultsAlternate(const char *main, const char *extra); void M_SetConfigDir(const char *dir); void M_SetMusicPackDir(void); void M_BindIntVariable(const char *name, int *variable); void M_BindFloatVariable(const char *name, float *variable); void M_BindStringVariable(const char *name, char **variable); boolean M_SetVariable(const char *name, const char *value); int M_GetIntVariable(const char *name); const char *M_GetStringVariable(const char *name); float M_GetFloatVariable(const char *name); void M_SetConfigFilenames(const char *main_config, const char *extra_config); char *M_GetSaveGameDir(const char *iwadname); char *M_GetAutoloadDir(const char *iwadname); extern const char *configdir; #endif crispy-doom-crispy-doom-5.6.4/src/m_controls.c000066400000000000000000000350761360717211000213610ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "doomtype.h" #include "doomkeys.h" #include "m_config.h" #include "m_misc.h" // // Keyboard controls // int key_right = KEY_RIGHTARROW; int key_left = KEY_LEFTARROW; int key_reverse = 0; // [crispy] int key_up = KEY_UPARROW; int key_alt_up = 'w'; // [crispy] int key_down = KEY_DOWNARROW; int key_alt_down = 's'; // [crispy] int key_strafeleft = ','; int key_alt_strafeleft = 'a'; // [crispy] int key_straferight = '.'; int key_alt_straferight = 'd'; // [crispy] int key_fire = KEY_RCTRL; int key_use = ' '; int key_strafe = KEY_RALT; int key_speed = KEY_RSHIFT; int key_toggleautorun = KEY_CAPSLOCK; // [crispy] int key_togglenovert = 0; // [crispy] // // Heretic keyboard controls // int key_flyup = KEY_PGUP; int key_flydown = KEY_INS; int key_flycenter = KEY_HOME; int key_lookup = KEY_PGDN; int key_lookdown = KEY_DEL; int key_lookcenter = KEY_END; int key_invleft = '['; int key_invright = ']'; int key_useartifact = KEY_ENTER; // // Hexen key controls // int key_jump = '/'; int key_arti_all = KEY_BACKSPACE; int key_arti_health = '\\'; int key_arti_poisonbag = '0'; int key_arti_blastradius = '9'; int key_arti_teleport = '8'; int key_arti_teleportother = '7'; int key_arti_egg = '6'; int key_arti_invulnerability = '5'; // // Strife key controls // // haleyjd 09/01/10 // // Note: Strife also uses key_invleft, key_invright, key_jump, key_lookup, and // key_lookdown, but with different default values. int key_usehealth = 'h'; int key_invquery = 'q'; int key_mission = 'w'; int key_invpop = 'z'; int key_invkey = 'k'; int key_invhome = KEY_HOME; int key_invend = KEY_END; int key_invuse = KEY_ENTER; int key_invdrop = KEY_BACKSPACE; // // Mouse controls // int mousebfire = 0; int mousebstrafe = 1; int mousebforward = 2; int mousebjump = -1; int mousebstrafeleft = -1; int mousebstraferight = -1; int mousebbackward = -1; int mousebuse = -1; int mousebmouselook = -1; // [crispy] int mousebreverse = -1; // [crispy] int mousebprevweapon = 4; // [crispy] int mousebnextweapon = 3; // [crispy] int key_message_refresh = KEY_ENTER; int key_pause = KEY_PAUSE; int key_demo_quit = 'q'; int key_spy = KEY_F12; // Multiplayer chat keys: int key_multi_msg = 't'; int key_multi_msgplayer[8]; // Weapon selection keys: int key_weapon1 = '1'; int key_weapon2 = '2'; int key_weapon3 = '3'; int key_weapon4 = '4'; int key_weapon5 = '5'; int key_weapon6 = '6'; int key_weapon7 = '7'; int key_weapon8 = '8'; int key_prevweapon = 0; int key_nextweapon = 0; // Map control keys: int key_map_north = KEY_UPARROW; int key_map_south = KEY_DOWNARROW; int key_map_east = KEY_RIGHTARROW; int key_map_west = KEY_LEFTARROW; int key_map_zoomin = '='; int key_map_zoomout = '-'; int key_map_toggle = KEY_TAB; int key_map_maxzoom = '0'; int key_map_follow = 'f'; int key_map_grid = 'g'; int key_map_mark = 'm'; int key_map_clearmark = 'c'; int key_map_overlay = 'o'; // [crispy] int key_map_rotate = 'r'; // [crispy] // menu keys: int key_menu_activate = KEY_ESCAPE; int key_menu_up = KEY_UPARROW; int key_menu_down = KEY_DOWNARROW; int key_menu_left = KEY_LEFTARROW; int key_menu_right = KEY_RIGHTARROW; int key_menu_back = KEY_BACKSPACE; int key_menu_forward = KEY_ENTER; int key_menu_confirm = 'y'; int key_menu_abort = 'n'; int key_menu_help = KEY_F1; int key_menu_save = KEY_F2; int key_menu_load = KEY_F3; int key_menu_volume = KEY_F4; int key_menu_detail = KEY_F5; int key_menu_qsave = KEY_F6; int key_menu_endgame = KEY_F7; int key_menu_messages = KEY_F8; int key_menu_qload = KEY_F9; int key_menu_quit = KEY_F10; int key_menu_gamma = KEY_F11; int key_menu_incscreen = KEY_EQUALS; int key_menu_decscreen = KEY_MINUS; int key_menu_screenshot = 0; int key_menu_cleanscreenshot = 0; // [crispy] int key_menu_del = KEY_DEL; // [crispy] int key_menu_nextlevel = 0; // [crispy] int key_menu_reloadlevel = 0; // [crispy] // // Joystick controls // int joybfire = 0; int joybstrafe = 1; int joybuse = 3; int joybspeed = 2; int joybstrafeleft = -1; int joybstraferight = -1; int joybjump = -1; int joybprevweapon = -1; int joybnextweapon = -1; int joybmenu = -1; int joybautomap = -1; // Control whether if a mouse button is double clicked, it acts like // "use" has been pressed int dclick_use = 1; // // Bind all of the common controls used by Doom and all other games. // void M_BindBaseControls(void) { M_BindIntVariable("key_right", &key_right); M_BindIntVariable("key_left", &key_left); M_BindIntVariable("key_up", &key_up); M_BindIntVariable("key_alt_up", &key_alt_up); // [crispy] M_BindIntVariable("key_down", &key_down); M_BindIntVariable("key_alt_down", &key_alt_down); // [crispy] M_BindIntVariable("key_strafeleft", &key_strafeleft); M_BindIntVariable("key_alt_strafeleft", &key_alt_strafeleft); // [crispy] M_BindIntVariable("key_straferight", &key_straferight); M_BindIntVariable("key_alt_straferight", &key_alt_straferight); // [crispy] M_BindIntVariable("key_fire", &key_fire); M_BindIntVariable("key_use", &key_use); M_BindIntVariable("key_strafe", &key_strafe); M_BindIntVariable("key_speed", &key_speed); M_BindIntVariable("mouseb_fire", &mousebfire); M_BindIntVariable("mouseb_strafe", &mousebstrafe); M_BindIntVariable("mouseb_forward", &mousebforward); M_BindIntVariable("joyb_fire", &joybfire); M_BindIntVariable("joyb_strafe", &joybstrafe); M_BindIntVariable("joyb_use", &joybuse); M_BindIntVariable("joyb_speed", &joybspeed); M_BindIntVariable("joyb_menu_activate", &joybmenu); M_BindIntVariable("joyb_toggle_automap", &joybautomap); // Extra controls that are not in the Vanilla versions: M_BindIntVariable("joyb_strafeleft", &joybstrafeleft); M_BindIntVariable("joyb_straferight", &joybstraferight); M_BindIntVariable("mouseb_strafeleft", &mousebstrafeleft); M_BindIntVariable("mouseb_straferight", &mousebstraferight); M_BindIntVariable("mouseb_use", &mousebuse); M_BindIntVariable("mouseb_backward", &mousebbackward); M_BindIntVariable("dclick_use", &dclick_use); M_BindIntVariable("key_pause", &key_pause); M_BindIntVariable("key_message_refresh", &key_message_refresh); M_BindIntVariable("key_lookup", &key_lookup); // [crispy] M_BindIntVariable("key_lookdown", &key_lookdown); // [crispy] M_BindIntVariable("key_lookcenter", &key_lookcenter); // [crispy] M_BindIntVariable("key_jump", &key_jump); // [crispy] M_BindIntVariable("mouseb_jump", &mousebjump); // [crispy] M_BindIntVariable("joyb_jump", &joybjump); // [crispy] M_BindIntVariable("mouseb_mouselook", &mousebmouselook); // [crispy] M_BindIntVariable("mouseb_reverse", &mousebreverse); // [crispy] M_BindIntVariable("key_reverse", &key_reverse); // [crispy] M_BindIntVariable("key_toggleautorun", &key_toggleautorun); // [crispy] M_BindIntVariable("key_togglenovert", &key_togglenovert); // [crispy] } void M_BindHereticControls(void) { M_BindIntVariable("key_flyup", &key_flyup); M_BindIntVariable("key_flydown", &key_flydown); M_BindIntVariable("key_flycenter", &key_flycenter); // [crispy] key_look* moved to M_BindBaseControls() M_BindIntVariable("key_invleft", &key_invleft); M_BindIntVariable("key_invright", &key_invright); M_BindIntVariable("key_useartifact", &key_useartifact); } void M_BindHexenControls(void) { // [crispy] *_jump moved to M_BindBaseControls() M_BindIntVariable("key_arti_all", &key_arti_all); M_BindIntVariable("key_arti_health", &key_arti_health); M_BindIntVariable("key_arti_poisonbag", &key_arti_poisonbag); M_BindIntVariable("key_arti_blastradius", &key_arti_blastradius); M_BindIntVariable("key_arti_teleport", &key_arti_teleport); M_BindIntVariable("key_arti_teleportother", &key_arti_teleportother); M_BindIntVariable("key_arti_egg", &key_arti_egg); M_BindIntVariable("key_arti_invulnerability", &key_arti_invulnerability); } void M_BindStrifeControls(void) { // These are shared with all games, but have different defaults: key_message_refresh = '/'; // These keys are shared with Heretic/Hexen but have different defaults: key_jump = 'a'; key_lookup = KEY_PGUP; key_lookdown = KEY_PGDN; key_invleft = KEY_INS; key_invright = KEY_DEL; M_BindIntVariable("key_jump", &key_jump); M_BindIntVariable("key_lookUp", &key_lookup); M_BindIntVariable("key_lookDown", &key_lookdown); M_BindIntVariable("key_invLeft", &key_invleft); M_BindIntVariable("key_invRight", &key_invright); // Custom Strife-only Keys: M_BindIntVariable("key_useHealth", &key_usehealth); M_BindIntVariable("key_invquery", &key_invquery); M_BindIntVariable("key_mission", &key_mission); M_BindIntVariable("key_invPop", &key_invpop); M_BindIntVariable("key_invKey", &key_invkey); M_BindIntVariable("key_invHome", &key_invhome); M_BindIntVariable("key_invEnd", &key_invend); M_BindIntVariable("key_invUse", &key_invuse); M_BindIntVariable("key_invDrop", &key_invdrop); // Strife also supports jump on mouse and joystick, and in the exact same // manner as Hexen! M_BindIntVariable("mouseb_jump", &mousebjump); M_BindIntVariable("joyb_jump", &joybjump); } void M_BindWeaponControls(void) { M_BindIntVariable("key_weapon1", &key_weapon1); M_BindIntVariable("key_weapon2", &key_weapon2); M_BindIntVariable("key_weapon3", &key_weapon3); M_BindIntVariable("key_weapon4", &key_weapon4); M_BindIntVariable("key_weapon5", &key_weapon5); M_BindIntVariable("key_weapon6", &key_weapon6); M_BindIntVariable("key_weapon7", &key_weapon7); M_BindIntVariable("key_weapon8", &key_weapon8); M_BindIntVariable("key_prevweapon", &key_prevweapon); M_BindIntVariable("key_nextweapon", &key_nextweapon); M_BindIntVariable("joyb_prevweapon", &joybprevweapon); M_BindIntVariable("joyb_nextweapon", &joybnextweapon); M_BindIntVariable("mouseb_prevweapon", &mousebprevweapon); M_BindIntVariable("mouseb_nextweapon", &mousebnextweapon); } void M_BindMapControls(void) { M_BindIntVariable("key_map_north", &key_map_north); M_BindIntVariable("key_map_south", &key_map_south); M_BindIntVariable("key_map_east", &key_map_east); M_BindIntVariable("key_map_west", &key_map_west); M_BindIntVariable("key_map_zoomin", &key_map_zoomin); M_BindIntVariable("key_map_zoomout", &key_map_zoomout); M_BindIntVariable("key_map_toggle", &key_map_toggle); M_BindIntVariable("key_map_maxzoom", &key_map_maxzoom); M_BindIntVariable("key_map_follow", &key_map_follow); M_BindIntVariable("key_map_grid", &key_map_grid); M_BindIntVariable("key_map_mark", &key_map_mark); M_BindIntVariable("key_map_clearmark", &key_map_clearmark); M_BindIntVariable("key_map_overlay", &key_map_overlay); // [crispy] M_BindIntVariable("key_map_rotate", &key_map_rotate); // [crispy] } void M_BindMenuControls(void) { M_BindIntVariable("key_menu_activate", &key_menu_activate); M_BindIntVariable("key_menu_up", &key_menu_up); M_BindIntVariable("key_menu_down", &key_menu_down); M_BindIntVariable("key_menu_left", &key_menu_left); M_BindIntVariable("key_menu_right", &key_menu_right); M_BindIntVariable("key_menu_back", &key_menu_back); M_BindIntVariable("key_menu_forward", &key_menu_forward); M_BindIntVariable("key_menu_confirm", &key_menu_confirm); M_BindIntVariable("key_menu_abort", &key_menu_abort); M_BindIntVariable("key_menu_help", &key_menu_help); M_BindIntVariable("key_menu_save", &key_menu_save); M_BindIntVariable("key_menu_load", &key_menu_load); M_BindIntVariable("key_menu_volume", &key_menu_volume); M_BindIntVariable("key_menu_detail", &key_menu_detail); M_BindIntVariable("key_menu_qsave", &key_menu_qsave); M_BindIntVariable("key_menu_endgame", &key_menu_endgame); M_BindIntVariable("key_menu_messages", &key_menu_messages); M_BindIntVariable("key_menu_qload", &key_menu_qload); M_BindIntVariable("key_menu_quit", &key_menu_quit); M_BindIntVariable("key_menu_gamma", &key_menu_gamma); M_BindIntVariable("key_menu_incscreen", &key_menu_incscreen); M_BindIntVariable("key_menu_decscreen", &key_menu_decscreen); M_BindIntVariable("key_menu_screenshot",&key_menu_screenshot); M_BindIntVariable("key_menu_cleanscreenshot",&key_menu_cleanscreenshot); // [crispy] M_BindIntVariable("key_menu_del", &key_menu_del); // [crispy] M_BindIntVariable("key_demo_quit", &key_demo_quit); M_BindIntVariable("key_spy", &key_spy); M_BindIntVariable("key_menu_nextlevel", &key_menu_nextlevel); // [crispy] M_BindIntVariable("key_menu_reloadlevel", &key_menu_reloadlevel); // [crispy] } void M_BindChatControls(unsigned int num_players) { char name[32]; // haleyjd: 20 not large enough - Thank you, come again! unsigned int i; // haleyjd: signedness conflict M_BindIntVariable("key_multi_msg", &key_multi_msg); for (i=0; i> FRACBITS; } // // FixedDiv, C version. // fixed_t FixedDiv(fixed_t a, fixed_t b) { if ((abs(a) >> 14) >= abs(b)) { return (a^b) < 0 ? INT_MIN : INT_MAX; } else { int64_t result; result = ((int64_t) a << FRACBITS) / b; return (fixed_t) result; } } crispy-doom-crispy-doom-5.6.4/src/m_fixed.h000066400000000000000000000016741360717211000206170ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Fixed point arithemtics, implementation. // #ifndef __M_FIXED__ #define __M_FIXED__ // // Fixed point, 32bit as 16.16. // #define FRACBITS 16 #define FRACUNIT (1< #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #ifdef _MSC_VER #include #endif #else #include #include #endif #include "doomtype.h" #include "deh_str.h" #include "i_swap.h" #include "i_system.h" #include "i_video.h" #include "m_misc.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" // // Create a directory // void M_MakeDirectory(const char *path) { #ifdef _WIN32 mkdir(path); #else mkdir(path, 0755); #endif } // Check if a file exists boolean M_FileExists(const char *filename) { FILE *fstream; fstream = fopen(filename, "r"); if (fstream != NULL) { fclose(fstream); return true; } else { // If we can't open because the file is a directory, the // "file" exists at least! return errno == EISDIR; } } // Check if a file exists by probing for common case variation of its filename. // Returns a newly allocated string that the caller is responsible for freeing. char *M_FileCaseExists(const char *path) { char *path_dup, *filename, *ext; path_dup = M_StringDuplicate(path); // 0: actual path if (M_FileExists(path_dup)) { return path_dup; } filename = strrchr(path_dup, DIR_SEPARATOR); if (filename != NULL) { filename++; } else { filename = path_dup; } // 1: lowercase filename, e.g. doom2.wad M_ForceLowercase(filename); if (M_FileExists(path_dup)) { return path_dup; } // 2: uppercase filename, e.g. DOOM2.WAD M_ForceUppercase(filename); if (M_FileExists(path_dup)) { return path_dup; } // 3. uppercase basename with lowercase extension, e.g. DOOM2.wad ext = strrchr(path_dup, '.'); if (ext != NULL && ext > filename) { M_ForceLowercase(ext + 1); if (M_FileExists(path_dup)) { return path_dup; } } // 4. lowercase filename with uppercase first letter, e.g. Doom2.wad if (strlen(filename) > 1) { M_ForceLowercase(filename + 1); if (M_FileExists(path_dup)) { return path_dup; } } // 5. no luck free(path_dup); return NULL; } // // Determine the length of an open file. // long M_FileLength(FILE *handle) { long savedpos; long length; // save the current position in the file savedpos = ftell(handle); // jump to the end and find the length fseek(handle, 0, SEEK_END); length = ftell(handle); // go back to the old location fseek(handle, savedpos, SEEK_SET); return length; } // // M_WriteFile // boolean M_WriteFile(const char *name, const void *source, int length) { FILE *handle; int count; handle = fopen(name, "wb"); if (handle == NULL) return false; count = fwrite(source, 1, length, handle); fclose(handle); if (count < length) return false; return true; } // // M_ReadFile // int M_ReadFile(const char *name, byte **buffer) { FILE *handle; int count, length; byte *buf; handle = fopen(name, "rb"); if (handle == NULL) I_Error ("Couldn't read file %s", name); // find the size of the file by seeking to the end and // reading the current position length = M_FileLength(handle); buf = Z_Malloc (length + 1, PU_STATIC, NULL); count = fread(buf, 1, length, handle); fclose (handle); if (count < length) I_Error ("Couldn't read file %s", name); buf[length] = '\0'; *buffer = buf; return length; } // Returns the path to a temporary file of the given name, stored // inside the system temporary directory. // // The returned value must be freed with Z_Free after use. char *M_TempFile(const char *s) { const char *tempdir; #ifdef _WIN32 // Check the TEMP environment variable to find the location. tempdir = getenv("TEMP"); if (tempdir == NULL) { tempdir = "."; } #else // In Unix, just use /tmp. tempdir = "/tmp"; #endif return M_StringJoin(tempdir, DIR_SEPARATOR_S, s, NULL); } boolean M_StrToInt(const char *str, int *result) { return sscanf(str, " 0x%x", result) == 1 || sscanf(str, " 0X%x", result) == 1 || sscanf(str, " 0%o", result) == 1 || sscanf(str, " %d", result) == 1; } // Returns the directory portion of the given path, without the trailing // slash separator character. If no directory is described in the path, // the string "." is returned. In either case, the result is newly allocated // and must be freed by the caller after use. char *M_DirName(const char *path) { char *p, *result; p = strrchr(path, DIR_SEPARATOR); if (p == NULL) { return M_StringDuplicate("."); } else { result = M_StringDuplicate(path); result[p - path] = '\0'; return result; } } // Returns the base filename described by the given path (without the // directory name). The result points inside path and nothing new is // allocated. const char *M_BaseName(const char *path) { const char *p; p = strrchr(path, DIR_SEPARATOR); if (p == NULL) { return path; } else { return p + 1; } } void M_ExtractFileBase(const char *path, char *dest) { const char *src; const char *filename; int length; src = path + strlen(path) - 1; // back up until a \ or the start while (src != path && *(src - 1) != DIR_SEPARATOR) { src--; } filename = src; // Copy up to eight characters // Note: Vanilla Doom exits with an error if a filename is specified // with a base of more than eight characters. To remove the 8.3 // filename limit, instead we simply truncate the name. length = 0; memset(dest, 0, 8); while (*src != '\0' && *src != '.') { if (length >= 8) { printf("Warning: Truncated '%s' lump name to '%.8s'.\n", filename, dest); break; } dest[length++] = toupper((int)*src++); } } //--------------------------------------------------------------------------- // // PROC M_ForceUppercase // // Change string to uppercase. // //--------------------------------------------------------------------------- void M_ForceUppercase(char *text) { char *p; for (p = text; *p != '\0'; ++p) { *p = toupper(*p); } } //--------------------------------------------------------------------------- // // PROC M_ForceLowercase // // Change string to lowercase. // //--------------------------------------------------------------------------- void M_ForceLowercase(char *text) { char *p; for (p = text; *p != '\0'; ++p) { *p = tolower(*p); } } // // M_StrCaseStr // // Case-insensitive version of strstr() // const char *M_StrCaseStr(const char *haystack, const char *needle) { unsigned int haystack_len; unsigned int needle_len; unsigned int len; unsigned int i; haystack_len = strlen(haystack); needle_len = strlen(needle); if (haystack_len < needle_len) { return NULL; } len = haystack_len - needle_len; for (i = 0; i <= len; ++i) { if (!strncasecmp(haystack + i, needle, needle_len)) { return haystack + i; } } return NULL; } // // Safe version of strdup() that checks the string was successfully // allocated. // char *M_StringDuplicate(const char *orig) { char *result; result = strdup(orig); if (result == NULL) { I_Error("Failed to duplicate string (length %" PRIuPTR ")\n", strlen(orig)); } return result; } // // String replace function. // char *M_StringReplace(const char *haystack, const char *needle, const char *replacement) { char *result, *dst; const char *p; size_t needle_len = strlen(needle); size_t result_len, dst_len; // Iterate through occurrences of 'needle' and calculate the size of // the new string. result_len = strlen(haystack) + 1; p = haystack; for (;;) { p = strstr(p, needle); if (p == NULL) { break; } p += needle_len; result_len += strlen(replacement) - needle_len; } // Construct new string. result = malloc(result_len); if (result == NULL) { I_Error("M_StringReplace: Failed to allocate new string"); return NULL; } dst = result; dst_len = result_len; p = haystack; while (*p != '\0') { if (!strncmp(p, needle, needle_len)) { M_StringCopy(dst, replacement, dst_len); p += needle_len; dst += strlen(replacement); dst_len -= strlen(replacement); } else { *dst = *p; ++dst; --dst_len; ++p; } } *dst = '\0'; return result; } // Safe string copy function that works like OpenBSD's strlcpy(). // Returns true if the string was not truncated. boolean M_StringCopy(char *dest, const char *src, size_t dest_size) { size_t len; if (dest_size >= 1) { dest[dest_size - 1] = '\0'; strncpy(dest, src, dest_size - 1); } else { return false; } len = strlen(dest); return src[len] == '\0'; } // Safe string concat function that works like OpenBSD's strlcat(). // Returns true if string not truncated. boolean M_StringConcat(char *dest, const char *src, size_t dest_size) { size_t offset; offset = strlen(dest); if (offset > dest_size) { offset = dest_size; } return M_StringCopy(dest + offset, src, dest_size - offset); } // Returns true if 's' begins with the specified prefix. boolean M_StringStartsWith(const char *s, const char *prefix) { return strlen(s) >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0; } // Returns true if 's' ends with the specified suffix. boolean M_StringEndsWith(const char *s, const char *suffix) { return strlen(s) >= strlen(suffix) && strcmp(s + strlen(s) - strlen(suffix), suffix) == 0; } // Return a newly-malloced string with all the strings given as arguments // concatenated together. char *M_StringJoin(const char *s, ...) { char *result; const char *v; va_list args; size_t result_len; result_len = strlen(s) + 1; va_start(args, s); for (;;) { v = va_arg(args, const char *); if (v == NULL) { break; } result_len += strlen(v); } va_end(args); result = malloc(result_len); if (result == NULL) { I_Error("M_StringJoin: Failed to allocate new string."); return NULL; } M_StringCopy(result, s, result_len); va_start(args, s); for (;;) { v = va_arg(args, const char *); if (v == NULL) { break; } M_StringConcat(result, v, result_len); } va_end(args); return result; } // 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 M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args) { int result; if (buf_len < 1) { return 0; } // Windows (and other OSes?) has a vsnprintf() that doesn't always // append a trailing \0. So we must do it, and write into a buffer // that is one byte shorter; otherwise this function is unsafe. result = vsnprintf(buf, buf_len, s, args); // If truncated, change the final char in the buffer to a \0. // A negative result indicates a truncated buffer on Windows. if (result < 0 || result >= buf_len) { buf[buf_len - 1] = '\0'; result = buf_len - 1; } return result; } // Safe, portable snprintf(). int M_snprintf(char *buf, size_t buf_len, const char *s, ...) { va_list args; int result; va_start(args, s); result = M_vsnprintf(buf, buf_len, s, args); va_end(args); return result; } #ifdef _WIN32 char *M_OEMToUTF8(const char *oem) { unsigned int len = strlen(oem) + 1; wchar_t *tmp; char *result; tmp = malloc(len * sizeof(wchar_t)); MultiByteToWideChar(CP_OEMCP, 0, oem, len, tmp, len); result = malloc(len * 4); WideCharToMultiByte(CP_UTF8, 0, tmp, len, result, len * 4, NULL, NULL); free(tmp); return result; } #endif crispy-doom-crispy-doom-5.6.4/src/m_misc.h000066400000000000000000000037541360717211000204540ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Miscellaneous. // #ifndef __M_MISC__ #define __M_MISC__ #include #include #include "doomtype.h" boolean M_WriteFile(const char *name, const void *source, int length); int M_ReadFile(const char *name, byte **buffer); void M_MakeDirectory(const char *dir); char *M_TempFile(const char *s); boolean M_FileExists(const char *file); char *M_FileCaseExists(const char *file); long M_FileLength(FILE *handle); boolean M_StrToInt(const char *str, int *result); char *M_DirName(const char *path); const char *M_BaseName(const char *path); void M_ExtractFileBase(const char *path, char *dest); void M_ForceUppercase(char *text); void M_ForceLowercase(char *text); const char *M_StrCaseStr(const char *haystack, const char *needle); char *M_StringDuplicate(const char *orig); boolean M_StringCopy(char *dest, const char *src, size_t dest_size); boolean M_StringConcat(char *dest, const char *src, size_t dest_size); char *M_StringReplace(const char *haystack, const char *needle, const char *replacement); char *M_StringJoin(const char *s, ...); boolean M_StringStartsWith(const char *s, const char *prefix); boolean M_StringEndsWith(const char *s, const char *suffix); int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args); int M_snprintf(char *buf, size_t buf_len, const char *s, ...) PRINTF_ATTR(3, 4); char *M_OEMToUTF8(const char *ansi); #endif crispy-doom-crispy-doom-5.6.4/src/manifest.xml000066400000000000000000000006741360717211000213620ustar00rootroot00000000000000 true crispy-doom-crispy-doom-5.6.4/src/memio.c000066400000000000000000000070411360717211000202770ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Emulates the IO functions in C stdio.h reading and writing to // memory. // #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(void *buf, size_t buflen) { MEMFILE *file; file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0); file->buf = (unsigned char *) buf; file->buflen = buflen; file->position = 0; file->mode = MODE_READ; return file; } // Read bytes size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream) { size_t items; if (stream->mode != MODE_READ) { printf("not a read stream\n"); return -1; } // Trying to read more bytes than we have left? items = nmemb; if (items * size > stream->buflen - stream->position) { items = (stream->buflen - stream->position) / size; } // Copy bytes to buffer memcpy(buf, stream->buf + stream->position, items * size); // Update position stream->position += items * size; return items; } // Open a memory area for writing MEMFILE *mem_fopen_write(void) { MEMFILE *file; file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0); file->alloced = 1024; file->buf = Z_Malloc(file->alloced, PU_STATIC, 0); file->buflen = 0; file->position = 0; file->mode = MODE_WRITE; return file; } // Write bytes to stream size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream) { size_t bytes; if (stream->mode != MODE_WRITE) { return -1; } // More bytes than can fit in the buffer? // If so, reallocate bigger. bytes = size * nmemb; while (bytes > stream->alloced - stream->position) { unsigned char *newbuf; newbuf = Z_Malloc(stream->alloced * 2, PU_STATIC, 0); memcpy(newbuf, stream->buf, stream->alloced); Z_Free(stream->buf); stream->buf = newbuf; stream->alloced *= 2; } // Copy into buffer memcpy(stream->buf + stream->position, ptr, bytes); stream->position += bytes; if (stream->position > stream->buflen) stream->buflen = stream->position; return nmemb; } void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen) { *buf = stream->buf; *buflen = stream->buflen; } void mem_fclose(MEMFILE *stream) { if (stream->mode == MODE_WRITE) { Z_Free(stream->buf); } Z_Free(stream); } long mem_ftell(MEMFILE *stream) { return stream->position; } int mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence) { unsigned int newpos; switch (whence) { case MEM_SEEK_SET: newpos = (int) position; break; case MEM_SEEK_CUR: newpos = (int) (stream->position + position); break; case MEM_SEEK_END: newpos = (int) (stream->buflen + position); break; default: return -1; } if (newpos < stream->buflen) { stream->position = newpos; return 0; } else { printf("Error seeking to %u\n", newpos); return -1; } } crispy-doom-crispy-doom-5.6.4/src/memio.h000066400000000000000000000022661360717211000203100ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef MEMIO_H #define MEMIO_H typedef struct _MEMFILE MEMFILE; typedef enum { MEM_SEEK_SET, MEM_SEEK_CUR, MEM_SEEK_END, } mem_rel_t; MEMFILE *mem_fopen_read(void *buf, size_t buflen); size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream); MEMFILE *mem_fopen_write(void); size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream); void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen); void mem_fclose(MEMFILE *stream); long mem_ftell(MEMFILE *stream); int mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence); #endif /* #ifndef MEMIO_H */ crispy-doom-crispy-doom-5.6.4/src/midifile.c000066400000000000000000000433621360717211000207610ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Reading of MIDI files. // #include #include #include #include #include "doomtype.h" #include "i_swap.h" #include "i_system.h" #include "midifile.h" #define HEADER_CHUNK_ID "MThd" #define TRACK_CHUNK_ID "MTrk" #define MAX_BUFFER_SIZE 0x10000 // haleyjd 09/09/10: packing required #ifdef _MSC_VER #pragma pack(push, 1) #endif typedef PACKED_STRUCT ( { byte chunk_id[4]; unsigned int chunk_size; }) chunk_header_t; typedef PACKED_STRUCT ( { chunk_header_t chunk_header; unsigned short format_type; unsigned short num_tracks; unsigned short time_division; }) midi_header_t; // haleyjd 09/09/10: packing off. #ifdef _MSC_VER #pragma pack(pop) #endif typedef struct { // Length in bytes: unsigned int data_len; // Events in this track: midi_event_t *events; int num_events; } midi_track_t; struct midi_track_iter_s { midi_track_t *track; unsigned int position; }; 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 boolean CheckChunkHeader(chunk_header_t *chunk, const char *expected_id) { boolean result; result = (memcmp((char *) chunk->chunk_id, expected_id, 4) == 0); if (!result) { fprintf(stderr, "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 boolean ReadByte(byte *result, FILE *stream) { int c; c = fgetc(stream); if (c == EOF) { fprintf(stderr, "ReadByte: Unexpected end of file\n"); return false; } else { *result = (byte) c; return true; } } // Read a variable-length value. static boolean ReadVariableLength(unsigned int *result, FILE *stream) { int i; byte b = 0; *result = 0; for (i=0; i<4; ++i) { if (!ReadByte(&b, stream)) { fprintf(stderr, "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; } } fprintf(stderr, "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, FILE *stream) { unsigned int i; byte *result; // Allocate a buffer. Allocate one extra byte, as malloc(0) is // non-portable. result = malloc(num_bytes + 1); if (result == NULL) { fprintf(stderr, "ReadByteSequence: Failed to allocate buffer\n"); return NULL; } // Read the data: for (i=0; ievent_type = event_type & 0xf0; event->data.channel.channel = event_type & 0x0f; // Read parameters: if (!ReadByte(&b, stream)) { fprintf(stderr, "ReadChannelEvent: Error while reading channel " "event parameters\n"); return false; } event->data.channel.param1 = b; // Second parameter: if (two_param) { if (!ReadByte(&b, stream)) { fprintf(stderr, "ReadChannelEvent: Error while reading channel " "event parameters\n"); return false; } event->data.channel.param2 = b; } return true; } // Read sysex event: static boolean ReadSysExEvent(midi_event_t *event, int event_type, FILE *stream) { event->event_type = event_type; if (!ReadVariableLength(&event->data.sysex.length, stream)) { fprintf(stderr, "ReadSysExEvent: Failed to read length of " "SysEx block\n"); return false; } // Read the byte sequence: event->data.sysex.data = ReadByteSequence(event->data.sysex.length, stream); if (event->data.sysex.data == NULL) { fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n"); return false; } return true; } // Read meta event: static boolean ReadMetaEvent(midi_event_t *event, FILE *stream) { byte b = 0; event->event_type = MIDI_EVENT_META; // Read meta event type: if (!ReadByte(&b, stream)) { fprintf(stderr, "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, stream)) { fprintf(stderr, "ReadSysExEvent: Failed to read length of " "SysEx block\n"); return false; } // Read the byte sequence: event->data.meta.data = ReadByteSequence(event->data.meta.length, stream); if (event->data.meta.data == NULL) { fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n"); return false; } return true; } static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type, FILE *stream) { byte event_type = 0; if (!ReadVariableLength(&event->delta_time, stream)) { fprintf(stderr, "ReadEvent: Failed to read event timestamp\n"); return false; } if (!ReadByte(&event_type, stream)) { fprintf(stderr, "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; if (fseek(stream, -1, SEEK_CUR) < 0) { fprintf(stderr, "ReadEvent: Unable to seek in stream\n"); return false; } } else { *last_event_type = event_type; } // Check event type: switch (event_type & 0xf0) { // Two parameter channel events: case MIDI_EVENT_NOTE_OFF: case MIDI_EVENT_NOTE_ON: case MIDI_EVENT_AFTERTOUCH: case MIDI_EVENT_CONTROLLER: case MIDI_EVENT_PITCH_BEND: return ReadChannelEvent(event, event_type, true, stream); // Single parameter channel events: case MIDI_EVENT_PROGRAM_CHANGE: case MIDI_EVENT_CHAN_AFTERTOUCH: return ReadChannelEvent(event, event_type, false, stream); default: break; } // Specific value? switch (event_type) { case MIDI_EVENT_SYSEX: case MIDI_EVENT_SYSEX_SPLIT: return ReadSysExEvent(event, event_type, stream); case MIDI_EVENT_META: return ReadMetaEvent(event, stream); default: break; } fprintf(stderr, "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 boolean ReadTrackHeader(midi_track_t *track, FILE *stream) { size_t records_read; chunk_header_t chunk_header; records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, stream); if (records_read < 1) { return false; } if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID)) { return false; } track->data_len = SDL_SwapBE32(chunk_header.chunk_size); return true; } static boolean ReadTrack(midi_track_t *track, FILE *stream) { midi_event_t *new_events; midi_event_t *event; unsigned int last_event_type; track->num_events = 0; track->events = NULL; // Read the header: if (!ReadTrackHeader(track, stream)) { return false; } // Then the events: last_event_type = 0; for (;;) { // Resize the track slightly larger to hold another event: new_events = I_Realloc(track->events, sizeof(midi_event_t) * (track->num_events + 1)); track->events = new_events; // Read the next event: event = &track->events[track->num_events]; if (!ReadEvent(event, &last_event_type, stream)) { return false; } ++track->num_events; // End of track? if (event->event_type == MIDI_EVENT_META && event->data.meta.type == MIDI_META_END_OF_TRACK) { break; } } return true; } // Free a track: static void FreeTrack(midi_track_t *track) { unsigned int i; for (i=0; inum_events; ++i) { FreeEvent(&track->events[i]); } free(track->events); } static boolean ReadAllTracks(midi_file_t *file, FILE *stream) { unsigned int i; // Allocate list of tracks and read each track: file->tracks = malloc(sizeof(midi_track_t) * file->num_tracks); if (file->tracks == NULL) { return false; } memset(file->tracks, 0, sizeof(midi_track_t) * file->num_tracks); // Read each track: for (i=0; inum_tracks; ++i) { if (!ReadTrack(&file->tracks[i], stream)) { return false; } } return true; } // Read and check the header chunk. static boolean ReadFileHeader(midi_file_t *file, FILE *stream) { size_t records_read; unsigned int format_type; records_read = fread(&file->header, sizeof(midi_header_t), 1, stream); if (records_read < 1) { return false; } if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID) || SDL_SwapBE32(file->header.chunk_header.chunk_size) != 6) { fprintf(stderr, "ReadFileHeader: Invalid MIDI chunk header! " "chunk_size=%i\n", SDL_SwapBE32(file->header.chunk_header.chunk_size)); return false; } format_type = SDL_SwapBE16(file->header.format_type); file->num_tracks = SDL_SwapBE16(file->header.num_tracks); if ((format_type != 0 && format_type != 1) || file->num_tracks < 1) { fprintf(stderr, "ReadFileHeader: Only type 0/1 " "MIDI files supported!\n"); return false; } return true; } void MIDI_FreeFile(midi_file_t *file) { int 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(char *filename) { midi_file_t *file; FILE *stream; file = malloc(sizeof(midi_file_t)); if (file == NULL) { return NULL; } file->tracks = NULL; file->num_tracks = 0; file->buffer = NULL; file->buffer_size = 0; // Open file stream = fopen(filename, "rb"); if (stream == NULL) { fprintf(stderr, "MIDI_LoadFile: Failed to open '%s'\n", filename); MIDI_FreeFile(file); return NULL; } // Read MIDI file header if (!ReadFileHeader(file, stream)) { fclose(stream); MIDI_FreeFile(file); return NULL; } // Read all tracks: if (!ReadAllTracks(file, stream)) { fclose(stream); MIDI_FreeFile(file); return NULL; } fclose(stream); return file; } // Get the number of tracks in a MIDI file. unsigned int MIDI_NumTracks(midi_file_t *file) { return file->num_tracks; } // Start iterating over the events in a track. midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track) { midi_track_iter_t *iter; assert(track < file->num_tracks); iter = malloc(sizeof(*iter)); iter->track = &file->tracks[track]; iter->position = 0; 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(midi_file_t *file) { short result = SDL_SwapBE16(file->header.time_division); // Negative time division indicates SMPTE time and must be handled // differently. if (result < 0) { return (signed int)(-(result/256)) * (signed int)(result & 0xFF); } else { return result; } } void MIDI_RestartIterator(midi_track_iter_t *iter) { iter->position = 0; } #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: %u 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: %u\n", event->data.channel.channel); printf("\tParameter 1: %u\n", event->data.channel.param1); printf("\tParameter 2: %u\n", event->data.channel.param2); break; case MIDI_EVENT_SYSEX: case MIDI_EVENT_SYSEX_SPLIT: printf("\tLength: %u\n", event->data.sysex.length); break; case MIDI_EVENT_META: printf("\tMeta type: %u\n", event->data.meta.type); printf("\tLength: %u\n", event->data.meta.length); break; } } } int main(int argc, char *argv[]) { midi_file_t *file; unsigned int i; if (argc < 2) { printf("Usage: %s \n", argv[0]); exit(1); } file = MIDI_LoadFile(argv[1]); if (file == NULL) { fprintf(stderr, "Failed to open %s\n", argv[1]); exit(1); } for (i=0; inum_tracks; ++i) { printf("\n== Track %u ==\n\n", i); PrintTrack(&file->tracks[i]); } return 0; } #endif crispy-doom-crispy-doom-5.6.4/src/midifile.h000066400000000000000000000077061360717211000207700ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // MIDI file parsing. // #ifndef MIDIFILE_H #define MIDIFILE_H 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 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_ALL_NOTES_OFF = 0x7b, } 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(char *filename); // Free a MIDI file. void MIDI_FreeFile(midi_file_t *file); // Get the time division value from the MIDI header. unsigned int MIDI_GetFileTimeDivision(midi_file_t *file); // Get the number of tracks in a MIDI file. unsigned int MIDI_NumTracks(midi_file_t *file); // Start iterating over the events in a track. midi_track_iter_t *MIDI_IterateTrack(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); #endif /* #ifndef MIDIFILE_H */ crispy-doom-crispy-doom-5.6.4/src/mus2mid.c000066400000000000000000000423611360717211000205550ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2006 Ben Ryves 2006 // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // mus2mid.c - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com // Use to convert a MUS file into a single track, type 0 MIDI file. #include #include "doomtype.h" #include "i_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; // Structure to hold MUS file header typedef PACKED_STRUCT ( { byte id[4]; unsigned short scorelength; unsigned short scorestart; unsigned short primarychannels; unsigned short secondarychannels; unsigned short instrumentcount; }) musheader; // Standard MIDI type 0 header + track header static const byte midiheader[] = { 'M', 'T', 'h', 'd', // Main header 0x00, 0x00, 0x00, 0x06, // Header size 0x00, 0x00, // MIDI type (0) 0x00, 0x01, // Number of tracks 0x00, 0x46, // Resolution 'M', 'T', 'r', 'k', // Start of track 0x00, 0x00, 0x00, 0x00 // Placeholder for track length }; // Cached channel velocities static byte channelvelocities[] = { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }; // Timestamps between sequences of MUS events static unsigned int queuedtime = 0; // Counter for the length of the track static unsigned int tracksize; static const byte controller_map[] = { 0x00, 0x20, 0x01, 0x07, 0x0A, 0x0B, 0x5B, 0x5D, 0x40, 0x43, 0x78, 0x7B, 0x7E, 0x7F, 0x79 }; static int channel_map[NUM_CHANNELS]; // Write timestamp to a MIDI file. static boolean 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 boolean 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 boolean 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 boolean 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 boolean 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 boolean 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 boolean 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 boolean 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, MEMFILE *midioutput) { // Find the MIDI channel to use for this MUS channel. // MUS channel 15 is the percusssion channel. if (mus_channel == MUS_PERCUSSION_CHAN) { return MIDI_PERCUSSION_CHAN; } else { // If a MIDI channel hasn't been allocated for this MUS channel // yet, allocate the next free MIDI channel. if (channel_map[mus_channel] == -1) { channel_map[mus_channel] = AllocateMIDIChannel(); // First time using the channel, send an "all notes off" // event. This fixes "The D_DDTBLU disease" described here: // https://www.doomworld.com/vb/source-ports/66802-the WriteChangeController_Valueless(channel_map[mus_channel], 0x7b, midioutput); } return channel_map[mus_channel]; } } static boolean ReadMusHeader(MEMFILE *file, musheader *header) { boolean result; result = mem_fread(&header->id, sizeof(byte), 4, file) == 4 && mem_fread(&header->scorelength, sizeof(short), 1, file) == 1 && mem_fread(&header->scorestart, sizeof(short), 1, file) == 1 && mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1 && mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1 && mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1; if (result) { header->scorelength = SHORT(header->scorelength); header->scorestart = SHORT(header->scorestart); header->primarychannels = SHORT(header->primarychannels); header->secondarychannels = SHORT(header->secondarychannels); header->instrumentcount = SHORT(header->instrumentcount); } return result; } // Read a MUS file from a stream (musinput) and output a MIDI file to // a stream (midioutput). // // Returns 0 on success or 1 on failure. boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput) { // Header for the MUS file musheader musfileheader; // Descriptor for the current MUS event byte eventdescriptor; int channel; // Channel number musevent event; // Bunch of vars read from MUS lump byte key; byte controllernumber; byte controllervalue; // Buffer used for MIDI track size record byte tracksizebuffer[4]; // Flag for when the score end marker is hit. int hitscoreend = 0; // Temp working byte byte working; // Used in building up time delays unsigned int timedelay; // Initialise channel map to mark all channels as unused. for (channel=0; channel 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; } #ifdef STANDALONE #include "m_misc.h" #include "z_zone.h" int main(int argc, char *argv[]) { MEMFILE *src, *dst; byte *infile; long infile_len; void *outfile; size_t outfile_len; if (argc != 3) { printf("Usage: %s \n", argv[0]); exit(-1); } Z_Init(); infile_len = M_ReadFile(argv[1], &infile); src = mem_fopen_read(infile, infile_len); dst = mem_fopen_write(); if (mus2mid(src, dst)) { fprintf(stderr, "mus2mid() failed\n"); exit(-1); } // Write result to output file: mem_get_buf(dst, &outfile, &outfile_len); M_WriteFile(argv[2], outfile, outfile_len); return 0; } #endif crispy-doom-crispy-doom-5.6.4/src/mus2mid.h000066400000000000000000000016621360717211000205610ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2006 Ben Ryves 2006 // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // mus2mid.h - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com // Use to convert a MUS file into a single track, type 0 MIDI file. #ifndef MUS2MID_H #define MUS2MID_H #include "doomtype.h" #include "memio.h" boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput); #endif /* #ifndef MUS2MID_H */ crispy-doom-crispy-doom-5.6.4/src/net_client.c000066400000000000000000000740501360717211000213210ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Network client code // #include #include #include #include #include "config.h" #include "doomtype.h" #include "deh_main.h" #include "deh_str.h" #include "i_system.h" #include "i_timer.h" #include "m_argv.h" #include "m_fixed.h" #include "m_config.h" #include "m_misc.h" #include "net_client.h" #include "net_common.h" #include "net_defs.h" #include "net_gui.h" #include "net_io.h" #include "net_packet.h" #include "net_query.h" #include "net_server.h" #include "net_structrw.h" #include "net_petname.h" #include "w_checksum.h" #include "w_wad.h" extern void D_ReceiveTic(ticcmd_t *ticcmds, boolean *playeringame); typedef enum { // waiting for the game to launch CLIENT_STATE_WAITING_LAUNCH, // waiting for the game to start CLIENT_STATE_WAITING_START, // in game CLIENT_STATE_IN_GAME, } net_clientstate_t; // Type of structure used in the receive window typedef struct { // Whether this tic has been received yet boolean active; // Last time we sent a resend request for this tic unsigned int resend_time; // Tic data from server net_full_ticcmd_t cmd; } net_server_recv_t; // Type of structure used in the send window typedef struct { // Whether this slot is active yet boolean active; // The tic number unsigned int seq; // Time the command was generated unsigned int time; // Ticcmd diff net_ticdiff_t cmd; } net_server_send_t; extern fixed_t offsetms; static net_connection_t client_connection; static net_clientstate_t client_state; static net_addr_t *server_addr; static net_context_t *client_context; // game settings, as received from the server when the game started static net_gamesettings_t settings; // Why did the server reject us? char *net_client_reject_reason = NULL; // true if the client code is in use boolean net_client_connected; // true if we have received waiting data from the server, // and the wait data that was received. boolean net_client_received_wait_data; net_waitdata_t net_client_wait_data; // Waiting at the initial wait screen for the game to be launched? boolean net_waiting_for_launch = false; // Name that we send to the server char *net_player_name = NULL; // Connected but not participating in the game (observer) boolean drone = false; // The last ticcmd constructed static ticcmd_t last_ticcmd; // Buffer of ticcmd diffs being sent to the server static net_server_send_t send_queue[BACKUPTICS]; // Receive window static ticcmd_t recvwindow_cmd_base[NET_MAXPLAYERS]; static int recvwindow_start; static net_server_recv_t recvwindow[BACKUPTICS]; // Whether we need to send an acknowledgement and // when gamedata was last received. static boolean need_to_acknowledge; static unsigned int gamedata_recv_time; // The latency (time between when we sent our command and we got all // the other players' commands from the server) for the last tic we // received. We include this latency in tics we send to the server so // that they can adjust to us. static int last_latency; // Hash checksums of our wad directory and dehacked data. sha1_digest_t net_local_wad_sha1sum; sha1_digest_t net_local_deh_sha1sum; // Are we playing with the freedoom IWAD? unsigned int net_local_is_freedoom; #define NET_CL_ExpandTicNum(b) NET_ExpandTicNum(recvwindow_start, (b)) // Called when we become disconnected from the server static void NET_CL_Disconnected(void) { D_ReceiveTic(NULL, NULL); } // Called when a packet is received from the server containing game // data. This updates the clock synchronization variable (offsetms) // using a PID filter that keeps client clocks in sync. static void UpdateClockSync(unsigned int seq, unsigned int remote_latency) { static int last_error, cumul_error; int latency, error; if (seq == send_queue[seq % BACKUPTICS].seq) { latency = I_GetTimeMS() - send_queue[seq % BACKUPTICS].time; } else if (seq > send_queue[seq % BACKUPTICS].seq) { // We have received the ticcmd from the server before we have // even sent ours latency = 0; } else { return; } // PID filter. These are manually trained parameters. #define KP 0.1 #define KI 0.01 #define KD 0.02 // How does our latency compare to the worst other player? error = latency - remote_latency; cumul_error += error; offsetms = KP * (FRACUNIT * error) - KI * (FRACUNIT * cumul_error) + (KD * FRACUNIT) * (last_error - error); last_error = error; last_latency = latency; NET_Log("client: latency %d, remote %d -> offset=%dms, cumul_error=%d", latency, remote_latency, offsetms / FRACUNIT, cumul_error); } // Expand a net_full_ticcmd_t, applying the diffs in cmd->cmds as // patches against recvwindow_cmd_base. Place the results into // the d_net.c structures (netcmds/nettics) and save the new ticcmd // back into recvwindow_cmd_base. static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq, ticcmd_t *ticcmds) { int i; // Expand tic diffs for all players for (i=0; iplayeringame[i]) { net_ticdiff_t *diff; diff = &cmd->cmds[i]; // Use the ticcmd diff to patch the previous ticcmd to // the new ticcmd NET_TiccmdPatch(&recvwindow_cmd_base[i], diff, &ticcmds[i]); // Store a copy for next time recvwindow_cmd_base[i] = ticcmds[i]; } } } // Advance the receive window static void NET_CL_AdvanceWindow(void) { ticcmd_t ticcmds[NET_MAXPLAYERS]; while (recvwindow[0].active) { // Expand tic diff data into d_net.c structures NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd, recvwindow_start, ticcmds); D_ReceiveTic(ticcmds, recvwindow[0].cmd.playeringame); // Advance the window memmove(recvwindow, recvwindow + 1, sizeof(net_server_recv_t) * (BACKUPTICS - 1)); memset(&recvwindow[BACKUPTICS-1], 0, sizeof(net_server_recv_t)); ++recvwindow_start; NET_Log("client: advanced receive window to %d", recvwindow_start); } } // Shut down the client code, etc. Invoked after a disconnect. static void NET_CL_Shutdown(void) { if (net_client_connected) { net_client_connected = false; NET_ReleaseAddress(server_addr); // Shut down network module, etc. To do. } } void NET_CL_LaunchGame(void) { NET_Conn_NewReliable(&client_connection, NET_PACKET_TYPE_LAUNCH); } void NET_CL_StartGame(net_gamesettings_t *settings) { net_packet_t *packet; // Start from a ticcmd of all zeros memset(&last_ticcmd, 0, sizeof(ticcmd_t)); // Send packet packet = NET_Conn_NewReliable(&client_connection, NET_PACKET_TYPE_GAMESTART); NET_WriteSettings(packet, settings); } static void NET_CL_SendGameDataACK(void) { net_packet_t *packet; packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA_ACK); NET_WriteInt8(packet, recvwindow_start & 0xff); NET_Conn_SendPacket(&client_connection, packet); NET_FreePacket(packet); need_to_acknowledge = false; } static void NET_CL_SendTics(int start, int end) { net_packet_t *packet; int i; if (!net_client_connected) { // Disconnected from server return; } if (start < 0) start = 0; // Build a new packet to send to the server packet = NET_NewPacket(512); NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA); // Write the start tic and number of tics. Send only the low byte // of start - it can be inferred by the server. NET_WriteInt8(packet, recvwindow_start & 0xff); NET_WriteInt8(packet, start & 0xff); NET_WriteInt8(packet, end - start + 1); // Add the tics. for (i=start; i<=end; ++i) { net_server_send_t *sendobj; sendobj = &send_queue[i % BACKUPTICS]; NET_WriteInt16(packet, last_latency); NET_WriteTiccmdDiff(packet, &sendobj->cmd, settings.lowres_turn); } // Send the packet NET_Conn_SendPacket(&client_connection, packet); // All done! NET_FreePacket(packet); // Acknowledgement has been sent as part of the packet need_to_acknowledge = false; } // Add a new ticcmd to the send queue void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic) { net_ticdiff_t diff; net_server_send_t *sendobj; int starttic, endtic; // Calculate the difference to the last ticcmd NET_TiccmdDiff(&last_ticcmd, ticcmd, &diff); // Store in the send queue sendobj = &send_queue[maketic % BACKUPTICS]; sendobj->active = true; sendobj->seq = maketic; sendobj->time = I_GetTimeMS(); sendobj->cmd = diff; last_ticcmd = *ticcmd; // Send to server. starttic = maketic - settings.extratics; endtic = maketic; if (starttic < 0) starttic = 0; NET_Log("client: generated tic %d, sending %d-%d", maketic, starttic, endtic); NET_CL_SendTics(starttic, endtic); } // Parse a SYN packet received back from the server indicating a successful // connection attempt. static void NET_CL_ParseSYN(net_packet_t *packet) { net_protocol_t protocol; char *server_version; NET_Log("client: processing SYN response"); server_version = NET_ReadSafeString(packet); if (server_version == NULL) { NET_Log("client: error: failed to read server version"); return; } protocol = NET_ReadProtocol(packet); if (protocol == NET_PROTOCOL_UNKNOWN) { NET_Log("client: error: can't find a common protocol"); return; } // We are now successfully connected. NET_Log("client: connected to server"); client_connection.state = NET_CONN_STATE_CONNECTED; client_connection.protocol = protocol; // Even though we have negotiated a compatible protocol, the game may still // desync. Chocolate Doom's philosophy makes this unlikely, but if we're // playing with a forked version, or even against a different version that // fixes a compatibility issue, we may still have problems. if (strcmp(server_version, PACKAGE_STRING) != 0) { fprintf(stderr, "NET_CL_ParseSYN: This is '%s', but the server is " "'%s'. It is possible that this mismatch may cause the game " "to desync.\n", PACKAGE_STRING, server_version); } } static void SetRejectReason(const char *s) { free(net_client_reject_reason); if (s != NULL) { net_client_reject_reason = strdup(s); } else { net_client_reject_reason = NULL; } } static void NET_CL_ParseReject(net_packet_t *packet) { char *msg; msg = NET_ReadSafeString(packet); if (msg == NULL) { return; } if (client_connection.state == NET_CONN_STATE_CONNECTING) { client_connection.state = NET_CONN_STATE_DISCONNECTED; client_connection.disconnect_reason = NET_DISCONNECT_REMOTE; SetRejectReason(msg); } } // data received while we are waiting for the game to start static void NET_CL_ParseWaitingData(net_packet_t *packet) { net_waitdata_t wait_data; if (!NET_ReadWaitData(packet, &wait_data)) { // Invalid packet? return; } if (wait_data.num_players > wait_data.max_players || wait_data.ready_players > wait_data.num_players || wait_data.max_players > NET_MAXPLAYERS) { // insane data return; } if ((wait_data.consoleplayer >= 0 && drone) || (wait_data.consoleplayer < 0 && !drone) || (wait_data.consoleplayer >= wait_data.num_players)) { // Invalid player number return; } memcpy(&net_client_wait_data, &wait_data, sizeof(net_waitdata_t)); net_client_received_wait_data = true; } static void NET_CL_ParseLaunch(net_packet_t *packet) { unsigned int num_players; NET_Log("client: processing launch packet"); if (client_state != CLIENT_STATE_WAITING_LAUNCH) { NET_Log("client: error: not in waiting launch state, client_state=%d", client_state); return; } // The launch packet contains the number of players that will be // in the game when it starts, so that we can do the startup // progress indicator (the wait data is unreliable). if (!NET_ReadInt8(packet, &num_players)) { NET_Log("client: error: failed to read number of players"); return; } net_client_wait_data.num_players = num_players; client_state = CLIENT_STATE_WAITING_START; NET_Log("client: now waiting for game start"); } static void NET_CL_ParseGameStart(net_packet_t *packet) { NET_Log("client: processing game start packet"); if (!NET_ReadSettings(packet, &settings)) { NET_Log("client: error: failed to read settings"); return; } if (client_state != CLIENT_STATE_WAITING_START) { NET_Log("client: error: not in waiting start state, client_state=%d", client_state); return; } if (settings.num_players > NET_MAXPLAYERS || settings.consoleplayer >= (signed int) settings.num_players) { // insane values NET_Log("client: error: bad settings, num_players=%d, consoleplayer=%d", settings.num_players, settings.consoleplayer); return; } if ((drone && settings.consoleplayer >= 0) || (!drone && settings.consoleplayer < 0)) { // Invalid player number: must be positive for real players, // negative for drones NET_Log("client: error: mismatch: drone=%d, consoleplayer=%d", drone, settings.consoleplayer); return; } NET_Log("client: beginning game state"); client_state = CLIENT_STATE_IN_GAME; // Clear the receive window memset(recvwindow, 0, sizeof(recvwindow)); recvwindow_start = 0; memset(&recvwindow_cmd_base, 0, sizeof(recvwindow_cmd_base)); // Clear the send queue memset(&send_queue, 0x00, sizeof(send_queue)); } static void NET_CL_SendResendRequest(int start, int end) { net_packet_t *packet; unsigned int nowtime; int i; //printf("CL: Send resend %i-%i\n", start, end); packet = NET_NewPacket(64); NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA_RESEND); NET_WriteInt32(packet, start); NET_WriteInt8(packet, end - start + 1); NET_Conn_SendPacket(&client_connection, packet); NET_FreePacket(packet); nowtime = I_GetTimeMS(); // Save the time we sent the resend request for (i=start; i<=end; ++i) { int index; index = i - recvwindow_start; if (index < 0 || index >= BACKUPTICS) continue; recvwindow[index].resend_time = nowtime; } } // Check for expired resend requests static void NET_CL_CheckResends(void) { int i; int resend_start, resend_end; unsigned int nowtime; boolean maybe_deadlocked; nowtime = I_GetTimeMS(); maybe_deadlocked = nowtime - gamedata_recv_time > 1000; resend_start = -1; resend_end = -1; for (i=0; iactive && recvobj->resend_time != 0 && nowtime > recvobj->resend_time + 300; // if no game data has been received in a long time, we may be in // a deadlock scenario where tics from the server have been lost, so // we've stopped generating any more, so the server isn't sending us // any, so we don't get any to trigger a resend request. So force the // first few tics in the receive window to be requested. if (i == 0 && !recvobj->active && recvobj->resend_time == 0 && maybe_deadlocked) { need_resend = true; } if (need_resend) { // Start a new run of resend tics? if (resend_start < 0) { resend_start = i; } resend_end = i; } else if (resend_start >= 0) { // End of a run of resend tics NET_Log("client: resend request timed out for %d-%d (%d)", recvwindow_start + resend_start, recvwindow_start + resend_end, recvwindow[resend_start].resend_time); NET_CL_SendResendRequest(recvwindow_start + resend_start, recvwindow_start + resend_end); resend_start = -1; } } if (resend_start >= 0) { NET_Log("client: resend request timed out for %d-%d (%d)", recvwindow_start + resend_start, recvwindow_start + resend_end, recvwindow[resend_start].resend_time); NET_CL_SendResendRequest(recvwindow_start + resend_start, recvwindow_start + resend_end); } // We have received some data from the server and not acknowledged // it yet. Normally this gets acknowledged when we send our game // data, but if the client is a drone we need to do this. if (need_to_acknowledge && nowtime - gamedata_recv_time > 200) { NET_Log("client: no game data received since %d: triggering ack", gamedata_recv_time); NET_CL_SendGameDataACK(); } } // Parsing of NET_PACKET_TYPE_GAMEDATA packets // (packets containing the actual ticcmd data) static void NET_CL_ParseGameData(net_packet_t *packet) { net_server_recv_t *recvobj; unsigned int seq, num_tics; unsigned int nowtime; int resend_start, resend_end; size_t i; int index; NET_Log("client: processing game data packet"); // Read header if (!NET_ReadInt8(packet, &seq) || !NET_ReadInt8(packet, &num_tics)) { NET_Log("client: error: failed to read header"); return; } nowtime = I_GetTimeMS(); // Whatever happens, we now need to send an acknowledgement of our // current receive point. if (!need_to_acknowledge) { need_to_acknowledge = true; gamedata_recv_time = nowtime; } // Expand byte value into the full tic number seq = NET_CL_ExpandTicNum(seq); NET_Log("client: got game data, seq=%d, num_tics=%d", seq, num_tics); for (i=0; i= BACKUPTICS) { // Out of range of the recv window continue; } // Store in the receive window recvobj = &recvwindow[index]; recvobj->active = true; recvobj->cmd = cmd; NET_Log("client: stored tic %d in receive window", seq + i); // If a packet is lost or arrives out of order, we might get // the tic in the next packet instead (because of extratic). // If that's the case then the latency for receiving that tic // now will be bogus. So we only use the last tic in the packet // to trigger a clock sync update. if (i == num_tics - 1) { UpdateClockSync(seq + i, cmd.latency); } } // Has this been received out of sequence, ie. have we not received // all tics before the first tic in this packet? If so, send a // resend request. //printf("CL: %p: %i\n", client, seq); resend_end = seq - recvwindow_start; if (resend_end <= 0) return; if (resend_end >= BACKUPTICS) resend_end = BACKUPTICS - 1; index = resend_end - 1; resend_start = resend_end; while (index >= 0) { recvobj = &recvwindow[index]; if (recvobj->active) { // ended our run of unreceived tics break; } if (recvobj->resend_time != 0) { // Already sent a resend request for this tic break; } resend_start = index; --index; } // Possibly send a resend request if (resend_start < resend_end) { NET_Log("client: request resend for %d-%d before %d", recvwindow_start + resend_start, recvwindow_start + resend_end - 1, seq); NET_CL_SendResendRequest(recvwindow_start + resend_start, recvwindow_start + resend_end - 1); } } // Parse a resend request from the server due to a dropped packet static void NET_CL_ParseResendRequest(net_packet_t *packet) { static unsigned int start; static unsigned int end; static unsigned int num_tics; NET_Log("client: processing resend request"); if (drone) { // Drones don't send gamedata. NET_Log("client: error: resend request but we're a drone?"); return; } if (!NET_ReadInt32(packet, &start) || !NET_ReadInt8(packet, &num_tics)) { NET_Log("client: error: couldn't read start and num_tics"); return; } end = start + num_tics - 1; //printf("requested resend %i-%i .. ", start, end); NET_Log("client: resend request: start=%d, num_tics=%d", start, num_tics); // Check we have the tics being requested. If not, reduce the // window of tics to only what we have. while (start <= end && (!send_queue[start % BACKUPTICS].active || send_queue[start % BACKUPTICS].seq != start)) { ++start; } while (start <= end && (!send_queue[end % BACKUPTICS].active || send_queue[end % BACKUPTICS].seq != end)) { --end; } // Resend those tics if (start <= end) { NET_Log("client: resending %d-%d", start, end); NET_CL_SendTics(start, end); } else { NET_Log("client: don't have the tics to resend"); } } // Console message that the server wants the client to print static void NET_CL_ParseConsoleMessage(net_packet_t *packet) { char *msg; msg = NET_ReadSafeString(packet); if (msg == NULL) { return; } printf("Message from server:\n%s\n", msg); } // parse a received packet static void NET_CL_ParsePacket(net_packet_t *packet) { unsigned int packet_type; if (!NET_ReadInt16(packet, &packet_type)) { return; } NET_Log("client: packet from server, type %d", packet_type & ~NET_RELIABLE_PACKET); NET_LogPacket(packet); if (NET_Conn_Packet(&client_connection, packet, &packet_type)) { // Packet eaten by the common connection code } else { switch (packet_type) { case NET_PACKET_TYPE_SYN: NET_CL_ParseSYN(packet); break; case NET_PACKET_TYPE_REJECTED: NET_CL_ParseReject(packet); break; case NET_PACKET_TYPE_WAITING_DATA: NET_CL_ParseWaitingData(packet); break; case NET_PACKET_TYPE_LAUNCH: NET_CL_ParseLaunch(packet); break; case NET_PACKET_TYPE_GAMESTART: NET_CL_ParseGameStart(packet); break; case NET_PACKET_TYPE_GAMEDATA: NET_CL_ParseGameData(packet); break; case NET_PACKET_TYPE_GAMEDATA_RESEND: NET_CL_ParseResendRequest(packet); break; case NET_PACKET_TYPE_CONSOLE_MESSAGE: NET_CL_ParseConsoleMessage(packet); break; default: break; } } } // "Run" the client code: check for new packets, send packets as // needed void NET_CL_Run(void) { net_addr_t *addr; net_packet_t *packet; if (!net_client_connected) { return; } while (NET_RecvPacket(client_context, &addr, &packet)) { // only accept packets from the server if (addr == server_addr) { NET_CL_ParsePacket(packet); } NET_FreePacket(packet); NET_ReleaseAddress(addr); } // Run the common connection code to send any packets as needed NET_Conn_Run(&client_connection); if (client_connection.state == NET_CONN_STATE_DISCONNECTED || client_connection.state == NET_CONN_STATE_DISCONNECTED_SLEEP) { NET_CL_Disconnected(); NET_CL_Shutdown(); } net_waiting_for_launch = client_connection.state == NET_CONN_STATE_CONNECTED && client_state == CLIENT_STATE_WAITING_LAUNCH; if (client_state == CLIENT_STATE_IN_GAME) { // Possibly advance the receive window NET_CL_AdvanceWindow(); // Check if our resend requests have timed out NET_CL_CheckResends(); } } static void NET_CL_SendSYN(net_connect_data_t *data) { net_packet_t *packet; NET_Log("client: sending SYN"); packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_SYN); NET_WriteInt32(packet, NET_MAGIC_NUMBER); NET_WriteString(packet, PACKAGE_STRING); NET_WriteProtocolList(packet); NET_WriteConnectData(packet, data); NET_WriteString(packet, net_player_name); NET_Conn_SendPacket(&client_connection, packet); NET_FreePacket(packet); } // Connect to a server boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data) { int start_time; int last_send_time; boolean sent_hole_punch; server_addr = addr; NET_ReferenceAddress(addr); memcpy(net_local_wad_sha1sum, data->wad_sha1sum, sizeof(sha1_digest_t)); memcpy(net_local_deh_sha1sum, data->deh_sha1sum, sizeof(sha1_digest_t)); net_local_is_freedoom = data->is_freedoom; // create a new network I/O context and add just the necessary module client_context = NET_NewContext(); // initialize module for client mode if (!addr->module->InitClient()) { SetRejectReason("Failed to initialize client module"); return false; } NET_AddModule(client_context, addr->module); net_client_connected = true; net_client_received_wait_data = false; sent_hole_punch = false; NET_Conn_InitClient(&client_connection, addr, NET_PROTOCOL_UNKNOWN); // try to connect start_time = I_GetTimeMS(); last_send_time = -1; SetRejectReason("Unknown reason"); while (client_connection.state == NET_CONN_STATE_CONNECTING) { int nowtime = I_GetTimeMS(); // Send a SYN packet every second. if (nowtime - last_send_time > 1000 || last_send_time < 0) { NET_CL_SendSYN(data); last_send_time = nowtime; } // time out after 5 seconds if (nowtime - start_time > 5000) { SetRejectReason("No response from server"); break; } if (!sent_hole_punch && nowtime - start_time > 2000) { NET_Log("client: no response to SYN, requesting hole punch"); NET_RequestHolePunch(client_context, addr); sent_hole_punch = true; } // run client code NET_CL_Run(); // run the server, just in case we are doing a loopback connect NET_SV_Run(); // Don't hog the CPU I_Sleep(1); } if (client_connection.state == NET_CONN_STATE_CONNECTED) { // connected ok! NET_Log("client: connected successfully"); SetRejectReason(NULL); client_state = CLIENT_STATE_WAITING_LAUNCH; drone = data->drone; return true; } else { // failed to connect NET_Log("client: failed to connect"); NET_CL_Shutdown(); return false; } } // read game settings received from server boolean NET_CL_GetSettings(net_gamesettings_t *_settings) { if (client_state != CLIENT_STATE_IN_GAME) { return false; } memcpy(_settings, &settings, sizeof(net_gamesettings_t)); return true; } // disconnect from the server void NET_CL_Disconnect(void) { int start_time; if (!net_client_connected) { return; } NET_Log("client: beginning disconnect"); NET_Conn_Disconnect(&client_connection); start_time = I_GetTimeMS(); while (client_connection.state != NET_CONN_STATE_DISCONNECTED && client_connection.state != NET_CONN_STATE_DISCONNECTED_SLEEP) { if (I_GetTimeMS() - start_time > 5000) { // time out after 5 seconds NET_Log("client: no acknowledgement of disconnect received"); client_state = CLIENT_STATE_WAITING_START; fprintf(stderr, "NET_CL_Disconnect: Timeout while disconnecting " "from server\n"); break; } NET_CL_Run(); NET_SV_Run(); I_Sleep(1); } // Finished sending disconnect packets, etc. NET_Log("client: disconnect complete"); NET_CL_Shutdown(); } void NET_CL_Init(void) { // Try to set from the USER and USERNAME environment variables // Otherwise, fallback to "Player" if (net_player_name == NULL) { net_player_name = NET_GetRandomPetName(); } } void NET_Init(void) { NET_OpenLog(); NET_CL_Init(); } void NET_BindVariables(void) { M_BindStringVariable("player_name", &net_player_name); } crispy-doom-crispy-doom-5.6.4/src/net_client.h000066400000000000000000000031721360717211000213230ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Network client code // #ifndef NET_CLIENT_H #define NET_CLIENT_H #include "doomtype.h" #include "d_ticcmd.h" #include "sha1.h" #include "net_defs.h" boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data); void NET_CL_Disconnect(void); void NET_CL_Run(void); void NET_CL_Init(void); void NET_CL_LaunchGame(void); void NET_CL_StartGame(net_gamesettings_t *settings); void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic); boolean NET_CL_GetSettings(net_gamesettings_t *_settings); void NET_Init(void); void NET_BindVariables(void); extern boolean net_client_connected; extern boolean net_client_received_wait_data; extern net_waitdata_t net_client_wait_data; extern char *net_client_reject_reason; extern boolean net_waiting_for_launch; extern char *net_player_name; extern sha1_digest_t net_server_wad_sha1sum; extern sha1_digest_t net_server_deh_sha1sum; extern unsigned int net_server_is_freedoom; extern sha1_digest_t net_local_wad_sha1sum; extern sha1_digest_t net_local_deh_sha1sum; extern unsigned int net_local_is_freedoom; extern boolean drone; #endif /* #ifndef NET_CLIENT_H */ crispy-doom-crispy-doom-5.6.4/src/net_common.c000066400000000000000000000320751360717211000213340ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Common code shared between the client and server // #include #include #include #include #include "doomtype.h" #include "d_mode.h" #include "i_system.h" #include "i_timer.h" #include "m_argv.h" #include "net_common.h" #include "net_io.h" #include "net_packet.h" #include "net_structrw.h" // connections time out after 30 seconds #define CONNECTION_TIMEOUT_LEN 30 // maximum time between sending packets #define KEEPALIVE_PERIOD 1 // reliable packet that is guaranteed to reach its destination struct net_reliable_packet_s { net_packet_t *packet; int last_send_time; int seq; net_reliable_packet_t *next; }; static FILE *net_debug = NULL; static void NET_Conn_Init(net_connection_t *conn, net_addr_t *addr, net_protocol_t protocol) { conn->last_send_time = -1; conn->num_retries = 0; conn->addr = addr; conn->protocol = protocol; conn->reliable_packets = NULL; conn->reliable_send_seq = 0; conn->reliable_recv_seq = 0; conn->keepalive_recv_time = I_GetTimeMS(); } // Initialize as a client connection void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr, net_protocol_t protocol) { NET_Conn_Init(conn, addr, protocol); conn->state = NET_CONN_STATE_CONNECTING; } // Initialize as a server connection void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr, net_protocol_t protocol) { NET_Conn_Init(conn, addr, protocol); conn->state = NET_CONN_STATE_CONNECTED; } // Send a packet to a connection // All packets should be sent through this interface, as it maintains the // keepalive_send_time counter. void NET_Conn_SendPacket(net_connection_t *conn, net_packet_t *packet) { conn->keepalive_send_time = I_GetTimeMS(); NET_SendPacket(conn->addr, packet); } static void NET_Conn_ParseDisconnect(net_connection_t *conn, net_packet_t *packet) { net_packet_t *reply; // Other end wants to disconnect // Send a DISCONNECT_ACK reply. reply = NET_NewPacket(10); NET_WriteInt16(reply, NET_PACKET_TYPE_DISCONNECT_ACK); NET_Conn_SendPacket(conn, reply); NET_FreePacket(reply); conn->last_send_time = I_GetTimeMS(); conn->state = NET_CONN_STATE_DISCONNECTED_SLEEP; conn->disconnect_reason = NET_DISCONNECT_REMOTE; } // Parse a DISCONNECT_ACK packet static void NET_Conn_ParseDisconnectACK(net_connection_t *conn, net_packet_t *packet) { if (conn->state == NET_CONN_STATE_DISCONNECTING) { // We have received an acknowledgement to our disconnect // request. We have been disconnected successfully. conn->state = NET_CONN_STATE_DISCONNECTED; conn->disconnect_reason = NET_DISCONNECT_LOCAL; conn->last_send_time = -1; } } static void NET_Conn_ParseReliableACK(net_connection_t *conn, net_packet_t *packet) { unsigned int seq; if (!NET_ReadInt8(packet, &seq)) { return; } if (conn->reliable_packets == NULL) { return; } // Is this an acknowledgement for the first packet in the list? if (seq == (unsigned int)((conn->reliable_packets->seq + 1) & 0xff)) { net_reliable_packet_t *rp; // Discard it, then. // Unlink from the list. rp = conn->reliable_packets; conn->reliable_packets = rp->next; NET_FreePacket(rp->packet); free(rp); } } // Process the header of a reliable packet // // Returns true if the packet should be discarded (incorrect sequence) static boolean NET_Conn_ReliablePacket(net_connection_t *conn, net_packet_t *packet) { unsigned int seq; net_packet_t *reply; boolean result; // Read the sequence number if (!NET_ReadInt8(packet, &seq)) { return true; } if (seq != (unsigned int)(conn->reliable_recv_seq & 0xff)) { // This is not the next expected packet in the sequence! // // Discard the packet. If we were smart, we would use a proper // sliding window protocol to do this, but I'm lazy. result = true; } else { // Now we can receive the next packet in the sequence. conn->reliable_recv_seq = (conn->reliable_recv_seq + 1) & 0xff; result = false; } // Send an acknowledgement // Note: this is braindead. It would be much more sensible to // include this in the next packet, rather than the overhead of // sending a complete packet just for one byte of information. reply = NET_NewPacket(10); NET_WriteInt16(reply, NET_PACKET_TYPE_RELIABLE_ACK); NET_WriteInt8(reply, conn->reliable_recv_seq & 0xff); NET_Conn_SendPacket(conn, reply); NET_FreePacket(reply); return result; } // Process a packet received by the server // // Returns true if eaten by common code boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet, unsigned int *packet_type) { conn->keepalive_recv_time = I_GetTimeMS(); // Is this a reliable packet? if (*packet_type & NET_RELIABLE_PACKET) { if (NET_Conn_ReliablePacket(conn, packet)) { // Invalid packet: eat it. return true; } // Remove the reliable bit *packet_type &= ~NET_RELIABLE_PACKET; } switch (*packet_type) { case NET_PACKET_TYPE_DISCONNECT: NET_Conn_ParseDisconnect(conn, packet); break; case NET_PACKET_TYPE_DISCONNECT_ACK: NET_Conn_ParseDisconnectACK(conn, packet); break; case NET_PACKET_TYPE_KEEPALIVE: // No special action needed. break; case NET_PACKET_TYPE_RELIABLE_ACK: NET_Conn_ParseReliableACK(conn, packet); break; default: // Not a common packet return false; } // We found a packet that we found interesting, and ate it. return true; } void NET_Conn_Disconnect(net_connection_t *conn) { if (conn->state != NET_CONN_STATE_DISCONNECTED && conn->state != NET_CONN_STATE_DISCONNECTING && conn->state != NET_CONN_STATE_DISCONNECTED_SLEEP) { conn->state = NET_CONN_STATE_DISCONNECTING; conn->disconnect_reason = NET_DISCONNECT_LOCAL; conn->last_send_time = -1; conn->num_retries = 0; } } void NET_Conn_Run(net_connection_t *conn) { net_packet_t *packet; unsigned int nowtime; nowtime = I_GetTimeMS(); if (conn->state == NET_CONN_STATE_CONNECTED) { // Check the keepalive counters if (nowtime - conn->keepalive_recv_time > CONNECTION_TIMEOUT_LEN * 1000) { // Haven't received any packets from the other end in a long // time. Assume disconnected. conn->state = NET_CONN_STATE_DISCONNECTED; conn->disconnect_reason = NET_DISCONNECT_TIMEOUT; } if (nowtime - conn->keepalive_send_time > KEEPALIVE_PERIOD * 1000) { // We have not sent anything in a long time. // Send a keepalive. packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_KEEPALIVE); NET_Conn_SendPacket(conn, packet); NET_FreePacket(packet); } // Check the reliable packet list. Has the first packet in the // list timed out? // // NB. This is braindead, we have a fixed time of one second. if (conn->reliable_packets != NULL && (conn->reliable_packets->last_send_time < 0 || nowtime - conn->reliable_packets->last_send_time > 1000)) { // Packet timed out, time to resend NET_Conn_SendPacket(conn, conn->reliable_packets->packet); conn->reliable_packets->last_send_time = nowtime; } } else if (conn->state == NET_CONN_STATE_DISCONNECTING) { // Waiting for a reply to our DISCONNECT request. if (conn->last_send_time < 0 || nowtime - conn->last_send_time > 1000) { // it has been a second since the last disconnect packet // was sent, and still no reply. if (conn->num_retries < MAX_RETRIES) { // send another disconnect packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_DISCONNECT); NET_Conn_SendPacket(conn, packet); NET_FreePacket(packet); conn->last_send_time = nowtime; ++conn->num_retries; } else { // No more retries allowed. // Force disconnect. conn->state = NET_CONN_STATE_DISCONNECTED; conn->disconnect_reason = NET_DISCONNECT_LOCAL; } } } else if (conn->state == NET_CONN_STATE_DISCONNECTED_SLEEP) { // We are disconnected, waiting in case we need to send // a DISCONNECT_ACK to the server again. if (nowtime - conn->last_send_time > 5000) { // Idle for 5 seconds, switch state conn->state = NET_CONN_STATE_DISCONNECTED; conn->disconnect_reason = NET_DISCONNECT_REMOTE; } } } net_packet_t *NET_Conn_NewReliable(net_connection_t *conn, int packet_type) { net_packet_t *packet; net_reliable_packet_t *rp; net_reliable_packet_t **listend; // Generate a packet with the right header packet = NET_NewPacket(100); NET_WriteInt16(packet, packet_type | NET_RELIABLE_PACKET); // write the low byte of the send sequence number NET_WriteInt8(packet, conn->reliable_send_seq & 0xff); // Add to the list of reliable packets rp = malloc(sizeof(net_reliable_packet_t)); rp->packet = packet; rp->next = NULL; rp->seq = conn->reliable_send_seq; rp->last_send_time = -1; for (listend = &conn->reliable_packets; *listend != NULL; listend = &((*listend)->next)); *listend = rp; // Count along the sequence conn->reliable_send_seq = (conn->reliable_send_seq + 1) & 0xff; // Finished return packet; } // Used to expand the least significant byte of a tic number into // the full tic number, from the current tic number unsigned int NET_ExpandTicNum(unsigned int relative, unsigned int b) { unsigned int l, h; unsigned int result; h = relative & ~0xff; l = relative & 0xff; result = h | b; if (l < 0x40 && b > 0xb0) result -= 0x100; if (l > 0xb0 && b < 0x40) result += 0x100; return result; } // Check that game settings are valid boolean NET_ValidGameSettings(GameMode_t mode, GameMission_t mission, net_gamesettings_t *settings) { if (settings->ticdup <= 0) return false; if (settings->extratics < 0) return false; if (settings->deathmatch < 0 || settings->deathmatch > 3) return false; if (settings->skill < sk_noitems || settings->skill > sk_nightmare) return false; if (!D_ValidGameVersion(mission, settings->gameversion)) return false; if (!D_ValidEpisodeMap(mission, mode, settings->episode, settings->map)) return false; return true; } static void CloseLog(void) { if (net_debug != NULL) { fclose(net_debug); net_debug = NULL; } } void NET_OpenLog(void) { int p; p = M_CheckParmWithArgs("-netlog", 1); if (p > 0) { net_debug = fopen(myargv[p + 1], "w"); if (net_debug == NULL) { I_Error("Failed to open %s to write debug log.", myargv[p + 1]); } I_AtExit(CloseLog, true); } } void NET_Log(const char *fmt, ...) { va_list args; if (net_debug == NULL) { return; } fprintf(net_debug, "%8d: ", I_GetTimeMS()); va_start(args, fmt); vfprintf(net_debug, fmt, args); va_end(args); fprintf(net_debug, "\n"); } void NET_LogPacket(net_packet_t *packet) { int i, bytes; if (net_debug == NULL) { return; } bytes = packet->len - packet->pos; if (bytes == 0) { return; } fprintf(net_debug, "\t%02x", packet->data[packet->pos]); for (i = 1; i < bytes; ++i) { if ((i % 16) == 0) { fprintf(net_debug, "\n\t"); } else { fprintf(net_debug, " "); } fprintf(net_debug, "%02x", packet->data[packet->pos + i]); } fprintf(net_debug, "\n"); } crispy-doom-crispy-doom-5.6.4/src/net_common.h000066400000000000000000000062121360717211000213330ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Common code shared between the client and server // #ifndef NET_COMMON_H #define NET_COMMON_H #include "d_mode.h" #include "net_defs.h" #include "net_packet.h" typedef enum { // Client has sent a SYN, is waiting for a SYN in response. NET_CONN_STATE_CONNECTING, // Successfully connected. NET_CONN_STATE_CONNECTED, // Sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply NET_CONN_STATE_DISCONNECTING, // Client successfully disconnected NET_CONN_STATE_DISCONNECTED, // We are disconnected, but in a sleep state, waiting for several // seconds. This is in case the DISCONNECT_ACK we sent failed // to arrive, and we need to send another one. We keep this as // a valid connection for a few seconds until we are sure that // the other end has successfully disconnected as well. NET_CONN_STATE_DISCONNECTED_SLEEP, } net_connstate_t; // Reason a connection was terminated typedef enum { // As the result of a local disconnect request NET_DISCONNECT_LOCAL, // As the result of a remote disconnect request NET_DISCONNECT_REMOTE, // Timeout (no data received in a long time) NET_DISCONNECT_TIMEOUT, } net_disconnect_reason_t; #define MAX_RETRIES 5 typedef struct net_reliable_packet_s net_reliable_packet_t; typedef struct { net_connstate_t state; net_disconnect_reason_t disconnect_reason; net_addr_t *addr; net_protocol_t protocol; int last_send_time; int num_retries; int keepalive_send_time; int keepalive_recv_time; net_reliable_packet_t *reliable_packets; int reliable_send_seq; int reliable_recv_seq; } net_connection_t; void NET_Conn_SendPacket(net_connection_t *conn, net_packet_t *packet); void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr, net_protocol_t protocol); void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr, net_protocol_t protocol); boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet, unsigned int *packet_type); void NET_Conn_Disconnect(net_connection_t *conn); void NET_Conn_Run(net_connection_t *conn); net_packet_t *NET_Conn_NewReliable(net_connection_t *conn, int packet_type); // Other miscellaneous common functions unsigned int NET_ExpandTicNum(unsigned int relative, unsigned int b); boolean NET_ValidGameSettings(GameMode_t mode, GameMission_t mission, net_gamesettings_t *settings); void NET_OpenLog(void); void NET_Log(const char *fmt, ...); void NET_LogPacket(net_packet_t *packet); #endif /* #ifndef NET_COMMON_H */ crispy-doom-crispy-doom-5.6.4/src/net_dedicated.c000066400000000000000000000042221360717211000217430ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Dedicated server code. // #include #include #include "doomtype.h" #include "i_system.h" #include "i_timer.h" #include "m_argv.h" #include "net_common.h" #include "net_sdl.h" #include "net_server.h" // // People can become confused about how dedicated servers work. Game // options are specified to the controlling player who is the first to // join a game. Bomb out with an error message if game options are // specified to a dedicated server. // static const char *not_dedicated_options[] = { "-deh", "-iwad", "-cdrom", "-gameversion", "-nomonsters", "-respawn", "-fast", "-altdeath", "-deathmatch", "-turbo", "-merge", "-af", "-as", "-aa", "-file", "-wart", "-skill", "-episode", "-timer", "-avg", "-warp", "-loadgame", "-longtics", "-extratics", "-dup", "-shorttics", NULL, }; static void CheckForClientOptions(void) { int i; for (i=0; not_dedicated_options[i] != NULL; ++i) { if (M_CheckParm(not_dedicated_options[i]) > 0) { I_Error("The command line parameter '%s' was specified to a " "dedicated server.\nGame parameters should be specified " "to the first player to join a server, \nnot to the " "server itself. ", not_dedicated_options[i]); } } } void NET_DedicatedServer(void) { CheckForClientOptions(); NET_OpenLog(); NET_SV_Init(); NET_SV_AddModule(&net_sdl_module); NET_SV_RegisterWithMaster(); while (true) { NET_SV_Run(); // TODO: Block on socket instead of polling. I_Sleep(1); } } crispy-doom-crispy-doom-5.6.4/src/net_dedicated.h000066400000000000000000000013001360717211000217420ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Dedicated server code. // #ifndef NET_DEDICATED_H #define NET_DEDICATED_H void NET_DedicatedServer(void); #endif /* #ifndef NET_DEDICATED_H */ crispy-doom-crispy-doom-5.6.4/src/net_defs.h000066400000000000000000000156721360717211000207760ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Definitions for use in networking code. // #ifndef NET_DEFS_H #define NET_DEFS_H #include #include "doomtype.h" #include "d_ticcmd.h" #include "sha1.h" // Absolute maximum number of "nodes" in the game. This is different to // NET_MAXPLAYERS, as there may be observers that are not participating // (eg. left/right monitors) #define MAXNETNODES 16 // The maximum number of players, multiplayer/networking. // This is the maximum supported by the networking code; individual games // have their own values for MAXPLAYERS that can be smaller. #define NET_MAXPLAYERS 8 // Maximum length of a player's name. #define MAXPLAYERNAME 30 // Networking and tick handling related. #define BACKUPTICS 128 typedef struct _net_module_s net_module_t; typedef struct _net_packet_s net_packet_t; typedef struct _net_addr_s net_addr_t; typedef struct _net_context_s net_context_t; struct _net_packet_s { byte *data; size_t len; size_t alloced; unsigned int pos; }; struct _net_module_s { // Initialize this module for use as a client boolean (*InitClient)(void); // Initialize this module for use as a server boolean (*InitServer)(void); // Send a packet void (*SendPacket)(net_addr_t *addr, net_packet_t *packet); // Check for new packets to receive // // Returns true if packet received boolean (*RecvPacket)(net_addr_t **addr, net_packet_t **packet); // Converts an address to a string void (*AddrToString)(net_addr_t *addr, char *buffer, int buffer_len); // Free back an address when no longer in use void (*FreeAddress)(net_addr_t *addr); // Try to resolve a name to an address net_addr_t *(*ResolveAddress)(const char *addr); }; // net_addr_t struct _net_addr_s { net_module_t *module; int refcount; void *handle; }; // Magic number sent when connecting to check this is a valid client #define NET_MAGIC_NUMBER 1454104972U // Old magic number used by Chocolate Doom versions before v3.0: #define NET_OLD_MAGIC_NUMBER 3436803284U // header field value indicating that the packet is a reliable packet #define NET_RELIABLE_PACKET (1 << 15) // Supported protocols. If you're developing a fork of Chocolate // Doom, you can add your own entry to this list while maintaining // compatibility with Chocolate Doom servers. Higher-numbered enum values // will be preferred when negotiating a protocol for the client and server // to use, so the order matters. // NOTE: The values in this enum do not have any special value outside of // the program they're compiled in. What matters is the string representation. typedef enum { // Protocol introduced with Chocolate Doom v3.0. Each compatibility- // breaking change to the network protocol will produce a new protocol // number in this enum. NET_PROTOCOL_CHOCOLATE_DOOM_0, // Add your own protocol here; be sure to add a name for it to the list // in net_common.c too. NET_NUM_PROTOCOLS, NET_PROTOCOL_UNKNOWN, } net_protocol_t; // packet types typedef enum { NET_PACKET_TYPE_SYN, NET_PACKET_TYPE_ACK, // deprecated NET_PACKET_TYPE_REJECTED, NET_PACKET_TYPE_KEEPALIVE, NET_PACKET_TYPE_WAITING_DATA, NET_PACKET_TYPE_GAMESTART, NET_PACKET_TYPE_GAMEDATA, NET_PACKET_TYPE_GAMEDATA_ACK, NET_PACKET_TYPE_DISCONNECT, NET_PACKET_TYPE_DISCONNECT_ACK, NET_PACKET_TYPE_RELIABLE_ACK, NET_PACKET_TYPE_GAMEDATA_RESEND, NET_PACKET_TYPE_CONSOLE_MESSAGE, NET_PACKET_TYPE_QUERY, NET_PACKET_TYPE_QUERY_RESPONSE, NET_PACKET_TYPE_LAUNCH, NET_PACKET_TYPE_NAT_HOLE_PUNCH, } net_packet_type_t; typedef enum { NET_MASTER_PACKET_TYPE_ADD, NET_MASTER_PACKET_TYPE_ADD_RESPONSE, NET_MASTER_PACKET_TYPE_QUERY, NET_MASTER_PACKET_TYPE_QUERY_RESPONSE, NET_MASTER_PACKET_TYPE_GET_METADATA, NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE, NET_MASTER_PACKET_TYPE_SIGN_START, NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE, NET_MASTER_PACKET_TYPE_SIGN_END, NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH_ALL, } net_master_packet_type_t; // Settings specified when the client connects to the server. typedef struct { int gamemode; int gamemission; int lowres_turn; int drone; int max_players; int is_freedoom; sha1_digest_t wad_sha1sum; sha1_digest_t deh_sha1sum; int player_class; } net_connect_data_t; // Game settings sent by client to server when initiating game start, // and received from the server by clients when the game starts. typedef struct { int ticdup; int extratics; int deathmatch; int episode; int nomonsters; int fast_monsters; int respawn_monsters; int map; int skill; int gameversion; int lowres_turn; int new_sync; int timelimit; int loadgame; int random; // [Strife only] // These fields are only used by the server when sending a game // start message: int num_players; int consoleplayer; // Hexen player classes: int player_classes[NET_MAXPLAYERS]; } net_gamesettings_t; #define NET_TICDIFF_FORWARD (1 << 0) #define NET_TICDIFF_SIDE (1 << 1) #define NET_TICDIFF_TURN (1 << 2) #define NET_TICDIFF_BUTTONS (1 << 3) #define NET_TICDIFF_CONSISTANCY (1 << 4) #define NET_TICDIFF_CHATCHAR (1 << 5) #define NET_TICDIFF_RAVEN (1 << 6) #define NET_TICDIFF_STRIFE (1 << 7) typedef struct { unsigned int diff; ticcmd_t cmd; } net_ticdiff_t; // Complete set of ticcmds from all players typedef struct { signed int latency; unsigned int seq; boolean playeringame[NET_MAXPLAYERS]; net_ticdiff_t cmds[NET_MAXPLAYERS]; } net_full_ticcmd_t; // Data sent in response to server queries typedef struct { const char *version; int server_state; int num_players; int max_players; int gamemode; int gamemission; const char *description; net_protocol_t protocol; } net_querydata_t; // Data sent by the server while waiting for the game to start. typedef struct { int num_players; int num_drones; int ready_players; int max_players; int is_controller; int consoleplayer; char player_names[NET_MAXPLAYERS][MAXPLAYERNAME]; char player_addrs[NET_MAXPLAYERS][MAXPLAYERNAME]; sha1_digest_t wad_sha1sum; sha1_digest_t deh_sha1sum; int is_freedoom; } net_waitdata_t; #endif /* #ifndef NET_DEFS_H */ crispy-doom-crispy-doom-5.6.4/src/net_gui.c000066400000000000000000000265531360717211000206340ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Graphical stuff related to the networking code: // // * The client waiting screen when we are waiting for the server to // start the game. // #include #include #include #include "config.h" #include "doomkeys.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "m_argv.h" #include "m_misc.h" #include "net_client.h" #include "net_gui.h" #include "net_query.h" #include "net_server.h" #include "textscreen.h" static txt_window_t *window; static int old_max_players; static txt_label_t *player_labels[NET_MAXPLAYERS]; static txt_label_t *ip_labels[NET_MAXPLAYERS]; static txt_label_t *drone_label; static txt_label_t *master_msg_label; static boolean had_warning; // Number of players we expect to be in the game. When the number is // reached, we auto-start the game (if we're the controller). If // zero, do not autostart. static int expected_nodes; static void EscapePressed(TXT_UNCAST_ARG(widget), void *unused) { TXT_Shutdown(); I_Quit(); } static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { NET_CL_LaunchGame(); } static void OpenWaitDialog(void) { txt_window_action_t *cancel; TXT_SetDesktopTitle(PACKAGE_STRING); window = TXT_NewWindow("Waiting for game start..."); TXT_AddWidget(window, TXT_NewLabel("\nPlease wait...\n\n")); cancel = TXT_NewWindowAction(KEY_ESCAPE, "Cancel"); TXT_SignalConnect(cancel, "pressed", EscapePressed, NULL); TXT_SetWindowAction(window, TXT_HORIZ_LEFT, cancel); TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_BOTTOM, TXT_SCREEN_W / 2, TXT_SCREEN_H - 9); old_max_players = 0; } static void BuildWindow(void) { char buf[50]; txt_table_t *table; int i; TXT_ClearTable(window); table = TXT_NewTable(3); TXT_AddWidget(window, table); // Add spacers TXT_AddWidget(table, NULL); TXT_AddWidget(table, TXT_NewStrut(25, 1)); TXT_AddWidget(table, TXT_NewStrut(17, 1)); // Player labels for (i = 0; i < net_client_wait_data.max_players; ++i) { M_snprintf(buf, sizeof(buf), " %i. ", i + 1); TXT_AddWidget(table, TXT_NewLabel(buf)); player_labels[i] = TXT_NewLabel(""); ip_labels[i] = TXT_NewLabel(""); TXT_AddWidget(table, player_labels[i]); TXT_AddWidget(table, ip_labels[i]); } drone_label = TXT_NewLabel(""); TXT_AddWidget(window, drone_label); } static void UpdateGUI(void) { txt_window_action_t *startgame; char buf[50]; unsigned int i; // If the value of max_players changes, we must rebuild the // contents of the window. This includes when the first // waiting data packet is received. if (net_client_received_wait_data) { if (net_client_wait_data.max_players != old_max_players) { BuildWindow(); } } else { return; } for (i = 0; i < net_client_wait_data.max_players; ++i) { txt_color_t color = TXT_COLOR_BRIGHT_WHITE; if ((signed) i == net_client_wait_data.consoleplayer) { color = TXT_COLOR_YELLOW; } TXT_SetFGColor(player_labels[i], color); TXT_SetFGColor(ip_labels[i], color); if (i < net_client_wait_data.num_players) { TXT_SetLabel(player_labels[i], net_client_wait_data.player_names[i]); TXT_SetLabel(ip_labels[i], net_client_wait_data.player_addrs[i]); } else { TXT_SetLabel(player_labels[i], ""); TXT_SetLabel(ip_labels[i], ""); } } if (net_client_wait_data.num_drones > 0) { M_snprintf(buf, sizeof(buf), " (+%i observer clients)", net_client_wait_data.num_drones); TXT_SetLabel(drone_label, buf); } else { TXT_SetLabel(drone_label, ""); } if (net_client_wait_data.is_controller) { startgame = TXT_NewWindowAction(' ', "Start game"); TXT_SignalConnect(startgame, "pressed", StartGame, NULL); } else { startgame = NULL; } TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, startgame); } static void BuildMasterStatusWindow(void) { txt_window_t *master_window; master_window = TXT_NewWindow(NULL); master_msg_label = TXT_NewLabel(""); TXT_AddWidget(master_window, master_msg_label); // This window is here purely for information, so it should be // in the background. TXT_LowerWindow(master_window); TXT_SetWindowPosition(master_window, TXT_HORIZ_CENTER, TXT_VERT_CENTER, TXT_SCREEN_W / 2, TXT_SCREEN_H - 4); TXT_SetWindowAction(master_window, TXT_HORIZ_LEFT, NULL); TXT_SetWindowAction(master_window, TXT_HORIZ_CENTER, NULL); TXT_SetWindowAction(master_window, TXT_HORIZ_RIGHT, NULL); } static void CheckMasterStatus(void) { boolean added; if (!NET_Query_CheckAddedToMaster(&added)) { return; } if (master_msg_label == NULL) { BuildMasterStatusWindow(); } if (added) { TXT_SetLabel(master_msg_label, "Your server is now registered with the global master server.\n" "Other players can find your server online."); } else { TXT_SetLabel(master_msg_label, "Failed to register with the master server. Your server is not\n" "publicly accessible. You may need to reconfigure your Internet\n" "router to add a port forward for UDP port 2342. Look up\n" "information on port forwarding online."); } } static void PrintSHA1Digest(const char *s, const byte *digest) { unsigned int i; printf("%s: ", s); for (i=0; i // @category net // // Autostart the netgame when n nodes (clients) have joined the server. // i = M_CheckParmWithArgs("-nodes", 1); if (i > 0) { expected_nodes = atoi(myargv[i + 1]); } } static void CheckAutoLaunch(void) { int nodes; if (net_client_received_wait_data && net_client_wait_data.is_controller && expected_nodes > 0) { nodes = net_client_wait_data.num_players + net_client_wait_data.num_drones; if (nodes >= expected_nodes) { StartGame(NULL, NULL); expected_nodes = 0; } } } void NET_WaitForLaunch(void) { if (!TXT_Init()) { fprintf(stderr, "Failed to initialize GUI\n"); exit(-1); } TXT_SetColor(TXT_COLOR_BLUE, 0x04, 0x14, 0x40); // Romero's "funky blue" color // [crispy] Crispy colors for Crispy Network GUI TXT_SetColor(TXT_COLOR_BRIGHT_GREEN, 249, 227, 0); // 0xF9, 0xE3, 0x00 TXT_SetColor(TXT_COLOR_CYAN, 220, 153, 0); // 0xDC, 0x99, 0x00 TXT_SetColor(TXT_COLOR_BRIGHT_CYAN, 76, 160, 223); // 0x4C, 0xA0, 0xDF I_InitWindowIcon(); ParseCommandLineArgs(); OpenWaitDialog(); had_warning = false; while (net_waiting_for_launch) { UpdateGUI(); CheckAutoLaunch(); CheckSHA1Sums(); CheckMasterStatus(); TXT_DispatchEvents(); TXT_DrawDesktop(); NET_CL_Run(); NET_SV_Run(); if (!net_client_connected) { I_Error("Lost connection to server"); } TXT_Sleep(100); } TXT_Shutdown(); } crispy-doom-crispy-doom-5.6.4/src/net_gui.h000066400000000000000000000014761360717211000206360ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Graphical stuff related to the networking code: // // * The client waiting screen when we are waiting for the server to // start the game. // #ifndef NET_GUI_H #define NET_GUI_H #include "doomtype.h" extern void NET_WaitForLaunch(void); #endif /* #ifndef NET_GUI_H */ crispy-doom-crispy-doom-5.6.4/src/net_io.c000066400000000000000000000062121360717211000204450ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Network packet I/O. Base layer for sending/receiving packets, // through the network module system // #include #include "i_system.h" #include "net_defs.h" #include "net_io.h" #include "z_zone.h" #define MAX_MODULES 16 struct _net_context_s { net_module_t *modules[MAX_MODULES]; int num_modules; }; net_addr_t net_broadcast_addr; net_context_t *NET_NewContext(void) { net_context_t *context; context = Z_Malloc(sizeof(net_context_t), PU_STATIC, 0); context->num_modules = 0; return context; } void NET_AddModule(net_context_t *context, net_module_t *module) { if (context->num_modules >= MAX_MODULES) { I_Error("NET_AddModule: No more modules for context"); } context->modules[context->num_modules] = module; ++context->num_modules; } net_addr_t *NET_ResolveAddress(net_context_t *context, const char *addr) { int i; net_addr_t *result; for (i=0; inum_modules; ++i) { result = context->modules[i]->ResolveAddress(addr); if (result != NULL) { NET_ReferenceAddress(result); return result; } } return NULL; } void NET_SendPacket(net_addr_t *addr, net_packet_t *packet) { addr->module->SendPacket(addr, packet); } void NET_SendBroadcast(net_context_t *context, net_packet_t *packet) { int i; for (i=0; inum_modules; ++i) { context->modules[i]->SendPacket(&net_broadcast_addr, packet); } } boolean NET_RecvPacket(net_context_t *context, net_addr_t **addr, net_packet_t **packet) { int i; // check all modules for new packets for (i=0; inum_modules; ++i) { if (context->modules[i]->RecvPacket(addr, packet)) { NET_ReferenceAddress(*addr); return true; } } return false; } // Note: this prints into a static buffer, calling again overwrites // the first result char *NET_AddrToString(net_addr_t *addr) { static char buf[128]; addr->module->AddrToString(addr, buf, sizeof(buf) - 1); return buf; } void NET_ReferenceAddress(net_addr_t *addr) { if (addr == NULL) { return; } ++addr->refcount; //printf("%s: +refcount=%d\n", NET_AddrToString(addr), addr->refcount); } void NET_ReleaseAddress(net_addr_t *addr) { if (addr == NULL) { return; } --addr->refcount; //printf("%s: -refcount=%d\n", NET_AddrToString(addr), addr->refcount); if (addr->refcount <= 0) { addr->module->FreeAddress(addr); } } crispy-doom-crispy-doom-5.6.4/src/net_io.h000066400000000000000000000043071360717211000204550ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Network packet manipulation (net_packet_t) // #ifndef NET_IO_H #define NET_IO_H #include "net_defs.h" extern net_addr_t net_broadcast_addr; // Create a new network context. net_context_t *NET_NewContext(void); // Add a network module to a context. void NET_AddModule(net_context_t *context, net_module_t *module); // Send a packet to the given address. void NET_SendPacket(net_addr_t *addr, net_packet_t *packet); // Send a broadcast using all modules in the given context. void NET_SendBroadcast(net_context_t *context, net_packet_t *packet); // Check all modules in the given context and receive a packet, returning true // if a packet was received. The result is stored in *packet and the source is // stored in *addr, with an implicit reference added. The packet must be freed // by the caller and the reference releasd. boolean NET_RecvPacket(net_context_t *context, net_addr_t **addr, net_packet_t **packet); // Return a string representation of the given address. The result points to a // static buffer and will become invalid with the next call. char *NET_AddrToString(net_addr_t *addr); // Add a reference to the given address. void NET_ReferenceAddress(net_addr_t *addr); // Release a reference to the given address. When there are no more references, // the address will be freed. void NET_ReleaseAddress(net_addr_t *addr); // Resolve a string representation of an address. If successful, a net_addr_t // pointer is received with an implicit reference that must be freed by the // caller when it is no longer needed. net_addr_t *NET_ResolveAddress(net_context_t *context, const char *address); #endif /* #ifndef NET_IO_H */ crispy-doom-crispy-doom-5.6.4/src/net_loop.c000066400000000000000000000113301360717211000210040ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Loopback network module for server compiled into the client // #include #include #include "doomtype.h" #include "i_system.h" #include "m_misc.h" #include "net_defs.h" #include "net_loop.h" #include "net_packet.h" #define MAX_QUEUE_SIZE 16 typedef struct { net_packet_t *packets[MAX_QUEUE_SIZE]; int head, tail; } packet_queue_t; static packet_queue_t client_queue; static packet_queue_t server_queue; static net_addr_t client_addr; static net_addr_t server_addr; static void QueueInit(packet_queue_t *queue) { queue->head = queue->tail = 0; } static void QueuePush(packet_queue_t *queue, net_packet_t *packet) { int new_tail; new_tail = (queue->tail + 1) % MAX_QUEUE_SIZE; if (new_tail == queue->head) { // queue is full return; } queue->packets[queue->tail] = packet; queue->tail = new_tail; } static net_packet_t *QueuePop(packet_queue_t *queue) { net_packet_t *packet; if (queue->tail == queue->head) { // queue empty return NULL; } packet = queue->packets[queue->head]; queue->head = (queue->head + 1) % MAX_QUEUE_SIZE; return packet; } //----------------------------------------------------------------------------- // // Client end code // //----------------------------------------------------------------------------- static boolean NET_CL_InitClient(void) { QueueInit(&client_queue); return true; } static boolean NET_CL_InitServer(void) { I_Error("NET_CL_InitServer: attempted to initialize client pipe end as a server!"); return false; } static void NET_CL_SendPacket(net_addr_t *addr, net_packet_t *packet) { QueuePush(&server_queue, NET_PacketDup(packet)); } static boolean NET_CL_RecvPacket(net_addr_t **addr, net_packet_t **packet) { net_packet_t *popped; popped = QueuePop(&client_queue); if (popped != NULL) { *packet = popped; *addr = &client_addr; client_addr.module = &net_loop_client_module; return true; } return false; } static void NET_CL_AddrToString(net_addr_t *addr, char *buffer, int buffer_len) { M_snprintf(buffer, buffer_len, "local server"); } static void NET_CL_FreeAddress(net_addr_t *addr) { } static net_addr_t *NET_CL_ResolveAddress(const char *address) { if (address == NULL) { client_addr.module = &net_loop_client_module; return &client_addr; } else { return NULL; } } net_module_t net_loop_client_module = { NET_CL_InitClient, NET_CL_InitServer, NET_CL_SendPacket, NET_CL_RecvPacket, NET_CL_AddrToString, NET_CL_FreeAddress, NET_CL_ResolveAddress, }; //----------------------------------------------------------------------------- // // Server end code // //----------------------------------------------------------------------------- static boolean NET_SV_InitClient(void) { I_Error("NET_SV_InitClient: attempted to initialize server pipe end as a client!"); return false; } static boolean NET_SV_InitServer(void) { QueueInit(&server_queue); return true; } static void NET_SV_SendPacket(net_addr_t *addr, net_packet_t *packet) { QueuePush(&client_queue, NET_PacketDup(packet)); } static boolean NET_SV_RecvPacket(net_addr_t **addr, net_packet_t **packet) { net_packet_t *popped; popped = QueuePop(&server_queue); if (popped != NULL) { *packet = popped; *addr = &server_addr; server_addr.module = &net_loop_server_module; return true; } return false; } static void NET_SV_AddrToString(net_addr_t *addr, char *buffer, int buffer_len) { M_snprintf(buffer, buffer_len, "local client"); } static void NET_SV_FreeAddress(net_addr_t *addr) { } static net_addr_t *NET_SV_ResolveAddress(const char *address) { if (address == NULL) { server_addr.module = &net_loop_server_module; return &server_addr; } else { return NULL; } } net_module_t net_loop_server_module = { NET_SV_InitClient, NET_SV_InitServer, NET_SV_SendPacket, NET_SV_RecvPacket, NET_SV_AddrToString, NET_SV_FreeAddress, NET_SV_ResolveAddress, }; crispy-doom-crispy-doom-5.6.4/src/net_loop.h000066400000000000000000000014651360717211000210210ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Loopback network module for server compiled into the client // #ifndef NET_LOOP_H #define NET_LOOP_H #include "net_defs.h" extern net_module_t net_loop_client_module; extern net_module_t net_loop_server_module; #endif /* #ifndef NET_LOOP_H */ crispy-doom-crispy-doom-5.6.4/src/net_packet.c000066400000000000000000000156221360717211000213120ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Network packet manipulation (net_packet_t) // #include #include #include "m_misc.h" #include "net_packet.h" #include "z_zone.h" static int total_packet_memory = 0; net_packet_t *NET_NewPacket(int initial_size) { net_packet_t *packet; packet = (net_packet_t *) Z_Malloc(sizeof(net_packet_t), PU_STATIC, 0); if (initial_size == 0) initial_size = 256; packet->alloced = initial_size; packet->data = Z_Malloc(initial_size, PU_STATIC, 0); packet->len = 0; packet->pos = 0; total_packet_memory += sizeof(net_packet_t) + initial_size; //printf("total packet memory: %i bytes\n", total_packet_memory); //printf("%p: allocated\n", packet); return packet; } // duplicates an existing packet net_packet_t *NET_PacketDup(net_packet_t *packet) { net_packet_t *newpacket; newpacket = NET_NewPacket(packet->len); memcpy(newpacket->data, packet->data, packet->len); newpacket->len = packet->len; return newpacket; } void NET_FreePacket(net_packet_t *packet) { //printf("%p: destroyed\n", packet); total_packet_memory -= sizeof(net_packet_t) + packet->alloced; Z_Free(packet->data); Z_Free(packet); } // Read a byte from the packet, returning true if read // successfully boolean NET_ReadInt8(net_packet_t *packet, unsigned int *data) { if (packet->pos + 1 > packet->len) return false; *data = packet->data[packet->pos]; packet->pos += 1; return true; } // Read a 16-bit integer from the packet, returning true if read // successfully boolean NET_ReadInt16(net_packet_t *packet, unsigned int *data) { byte *p; if (packet->pos + 2 > packet->len) return false; p = packet->data + packet->pos; *data = (p[0] << 8) | p[1]; packet->pos += 2; return true; } // Read a 32-bit integer from the packet, returning true if read // successfully boolean NET_ReadInt32(net_packet_t *packet, unsigned int *data) { byte *p; if (packet->pos + 4 > packet->len) return false; p = packet->data + packet->pos; *data = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; packet->pos += 4; return true; } // Signed read functions boolean NET_ReadSInt8(net_packet_t *packet, signed int *data) { if (NET_ReadInt8(packet,(unsigned int *) data)) { if (*data & (1 << 7)) { *data &= ~(1 << 7); *data -= (1 << 7); } return true; } else { return false; } } boolean NET_ReadSInt16(net_packet_t *packet, signed int *data) { if (NET_ReadInt16(packet, (unsigned int *) data)) { if (*data & (1 << 15)) { *data &= ~(1 << 15); *data -= (1 << 15); } return true; } else { return false; } } boolean NET_ReadSInt32(net_packet_t *packet, signed int *data) { if (NET_ReadInt32(packet, (unsigned int *) data)) { if (*data & (1U << 31)) { *data &= ~(1U << 31); *data -= (1U << 31); } return true; } else { return false; } } // Read a string from the packet. Returns NULL if a terminating // NUL character was not found before the end of the packet. char *NET_ReadString(net_packet_t *packet) { char *start; start = (char *) packet->data + packet->pos; // Search forward for a NUL character while (packet->pos < packet->len && packet->data[packet->pos] != '\0') { ++packet->pos; } if (packet->pos >= packet->len) { // Reached the end of the packet return NULL; } // packet->data[packet->pos] == '\0': We have reached a terminating // NULL. Skip past this NULL and continue reading immediately // after it. ++packet->pos; return start; } // Read a string from the packet, but (potentially) modify it to strip // out any unprintable characters which could be malicious control codes. // Note that this may modify the original packet contents. char *NET_ReadSafeString(net_packet_t *packet) { char *r, *w, *result; result = NET_ReadString(packet); if (result == NULL) { return NULL; } // w is always <= r, so we never produce a longer string than the original. w = result; for (r = result; *r != '\0'; ++r) { // TODO: This is a very naive way of producing a safe string; only // ASCII characters are allowed. Probably this should really support // UTF-8 characters as well. if (isprint(*r) || *r == '\n') { *w = *r; ++w; } } *w = '\0'; return result; } // Dynamically increases the size of a packet static void NET_IncreasePacket(net_packet_t *packet) { byte *newdata; total_packet_memory -= packet->alloced; packet->alloced *= 2; newdata = Z_Malloc(packet->alloced, PU_STATIC, 0); memcpy(newdata, packet->data, packet->len); Z_Free(packet->data); packet->data = newdata; total_packet_memory += packet->alloced; } // Write a single byte to the packet void NET_WriteInt8(net_packet_t *packet, unsigned int i) { if (packet->len + 1 > packet->alloced) NET_IncreasePacket(packet); packet->data[packet->len] = i; packet->len += 1; } // Write a 16-bit integer to the packet void NET_WriteInt16(net_packet_t *packet, unsigned int i) { byte *p; if (packet->len + 2 > packet->alloced) NET_IncreasePacket(packet); p = packet->data + packet->len; p[0] = (i >> 8) & 0xff; p[1] = i & 0xff; packet->len += 2; } // Write a single byte to the packet void NET_WriteInt32(net_packet_t *packet, unsigned int i) { byte *p; if (packet->len + 4 > packet->alloced) NET_IncreasePacket(packet); p = packet->data + packet->len; p[0] = (i >> 24) & 0xff; p[1] = (i >> 16) & 0xff; p[2] = (i >> 8) & 0xff; p[3] = i & 0xff; packet->len += 4; } void NET_WriteString(net_packet_t *packet, const char *string) { byte *p; size_t string_size; string_size = strlen(string) + 1; // Increase the packet size until large enough to hold the string while (packet->len + string_size > packet->alloced) { NET_IncreasePacket(packet); } p = packet->data + packet->len; M_StringCopy((char *) p, string, string_size); packet->len += string_size; } crispy-doom-crispy-doom-5.6.4/src/net_packet.h000066400000000000000000000030551360717211000213140ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Definitions for use in networking code. // #ifndef NET_PACKET_H #define NET_PACKET_H #include "net_defs.h" net_packet_t *NET_NewPacket(int initial_size); net_packet_t *NET_PacketDup(net_packet_t *packet); void NET_FreePacket(net_packet_t *packet); boolean NET_ReadInt8(net_packet_t *packet, unsigned int *data); boolean NET_ReadInt16(net_packet_t *packet, unsigned int *data); boolean NET_ReadInt32(net_packet_t *packet, unsigned int *data); boolean NET_ReadSInt8(net_packet_t *packet, signed int *data); boolean NET_ReadSInt16(net_packet_t *packet, signed int *data); boolean NET_ReadSInt32(net_packet_t *packet, signed int *data); char *NET_ReadString(net_packet_t *packet); char *NET_ReadSafeString(net_packet_t *packet); void NET_WriteInt8(net_packet_t *packet, unsigned int i); void NET_WriteInt16(net_packet_t *packet, unsigned int i); void NET_WriteInt32(net_packet_t *packet, unsigned int i); void NET_WriteString(net_packet_t *packet, const char *string); #endif /* #ifndef NET_PACKET_H */ crispy-doom-crispy-doom-5.6.4/src/net_petname.c000066400000000000000000000044501360717211000214710ustar00rootroot00000000000000// // Copyright(C) 2019 Jonathan Dowland // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Generate a randomized, private, memorable name for a Player // #include #include #include "doomtype.h" #include "m_misc.h" static const char * const adjectives [] = { "Grumpy", "Ecstatic", "Surly", "Prepared", "Crafty", "Alert", "Sluggish", "Testy", "Reluctant", "Languid", "Passive", "Pacifist", "Aggressive", "Hostile", "Bubbly", "Giggly", "Laughing", "Crying", "Frowning", "Torpid", "Lethargic", "Manic", "Patient", "Protective", "Philosophical", "Enquiring", "Debating", "Furious", "Laid-Back", "Easy-Going", "Cromulent", "Excitable", "Tired", "Exhausted", "Ruminating", "Redundant", "Sporty", "Ginger", "Scary", "Posh", "Baby", }; static const char * const nouns[] = { "Frad", // Doom "Cacodemon", "Arch-Vile", "Cyberdemon", "Imp", "Demon", "Mancubus", "Arachnotron", "Baron", "Knight", "Revenant", // Hexen "Ettin", "Maulotaur", "Centaur", "Afrit", "Serpent", // Heretic "Disciple", "Gargoyle", "Golem", "Lich", // Strife "Sentinel", "Acolyte", "Templar", "Reaver", "Spectre", }; /* * ideally we would export this and the caller would invoke it during * their setup routine. But, the two callers only invoke getRandomPetName * once, so the initialization might as well occur then. */ static void InitPetName() { srand((unsigned int)time(NULL)); } char *NET_GetRandomPetName() { const char *a, *n; InitPetName(); a = adjectives[rand() % arrlen(adjectives)]; n = nouns[rand() % arrlen(nouns)]; return M_StringJoin(a, " ", n, NULL); } crispy-doom-crispy-doom-5.6.4/src/net_petname.h000066400000000000000000000012311360717211000214700ustar00rootroot00000000000000// // Copyright(C) 2019 Jonathan Dowland // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Generate a randomized, private, memorable name for a Player // char *NET_GetRandomPetName(); crispy-doom-crispy-doom-5.6.4/src/net_query.c000066400000000000000000000560471360717211000212160ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Querying servers to find their current status. // #include #include #include #include #include "i_system.h" #include "i_timer.h" #include "m_misc.h" #include "net_common.h" #include "net_defs.h" #include "net_io.h" #include "net_packet.h" #include "net_query.h" #include "net_structrw.h" #include "net_sdl.h" // DNS address of the Internet master server. #define MASTER_SERVER_ADDRESS "master.chocolate-doom.org:2342" // Time to wait for a response before declaring a timeout. #define QUERY_TIMEOUT_SECS 2 // Time to wait for secure demo signatures before declaring a timeout. #define SIGNATURE_TIMEOUT_SECS 5 // Number of query attempts to make before giving up on a server. #define QUERY_MAX_ATTEMPTS 3 typedef enum { QUERY_TARGET_SERVER, // Normal server target. QUERY_TARGET_MASTER, // The master server. QUERY_TARGET_BROADCAST // Send a broadcast query } query_target_type_t; typedef enum { QUERY_TARGET_QUEUED, // Query not yet sent QUERY_TARGET_QUERIED, // Query sent, waiting response QUERY_TARGET_RESPONDED, // Response received QUERY_TARGET_NO_RESPONSE } query_target_state_t; typedef struct { query_target_type_t type; query_target_state_t state; net_addr_t *addr; net_querydata_t data; unsigned int ping_time; unsigned int query_time; unsigned int query_attempts; boolean printed; } query_target_t; static boolean registered_with_master = false; static boolean got_master_response = false; static net_context_t *query_context; static query_target_t *targets; static int num_targets; static boolean query_loop_running = false; static boolean printed_header = false; static int last_query_time = 0; static char *securedemo_start_message = NULL; // Resolve the master server address. net_addr_t *NET_Query_ResolveMaster(net_context_t *context) { net_addr_t *addr; addr = NET_ResolveAddress(context, MASTER_SERVER_ADDRESS); if (addr == NULL) { fprintf(stderr, "Warning: Failed to resolve address " "for master server: %s\n", MASTER_SERVER_ADDRESS); } return addr; } // Send a registration packet to the master server to register // ourselves with the global list. void NET_Query_AddToMaster(net_addr_t *master_addr) { net_packet_t *packet; packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_ADD); NET_SendPacket(master_addr, packet); NET_FreePacket(packet); } // Process a packet received from the master server. void NET_Query_AddResponse(net_packet_t *packet) { unsigned int result; if (!NET_ReadInt16(packet, &result)) { return; } if (result != 0) { // Only show the message once. if (!registered_with_master) { printf("Registered with master server at %s\n", MASTER_SERVER_ADDRESS); registered_with_master = true; } } else { // Always show rejections. printf("Failed to register with master server at %s\n", MASTER_SERVER_ADDRESS); } got_master_response = true; } boolean NET_Query_CheckAddedToMaster(boolean *result) { // Got response from master yet? if (!got_master_response) { return false; } *result = registered_with_master; return true; } // Send a query to the master server. static void NET_Query_SendMasterQuery(net_addr_t *addr) { net_packet_t *packet; packet = NET_NewPacket(4); NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_QUERY); NET_SendPacket(addr, packet); NET_FreePacket(packet); // We also send a NAT_HOLE_PUNCH_ALL packet so that servers behind // NAT gateways will open themselves up to us. packet = NET_NewPacket(4); NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH_ALL); NET_SendPacket(addr, packet); NET_FreePacket(packet); } // Send a hole punch request to the master server for the server at the // given address. void NET_RequestHolePunch(net_context_t *context, net_addr_t *addr) { net_addr_t *master_addr; net_packet_t *packet; master_addr = NET_Query_ResolveMaster(context); if (master_addr == NULL) { return; } packet = NET_NewPacket(32); NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH); NET_WriteString(packet, NET_AddrToString(addr)); NET_SendPacket(master_addr, packet); NET_FreePacket(packet); NET_ReleaseAddress(master_addr); } // Given the specified address, find the target associated. If no // target is found, and 'create' is true, a new target is created. static query_target_t *GetTargetForAddr(net_addr_t *addr, boolean create) { query_target_t *target; int i; for (i=0; itype = QUERY_TARGET_SERVER; target->state = QUERY_TARGET_QUEUED; target->printed = false; target->query_attempts = 0; target->addr = addr; NET_ReferenceAddress(addr); ++num_targets; return target; } static void FreeTargets(void) { int i; for (i = 0; i < num_targets; ++i) { NET_ReleaseAddress(targets[i].addr); } free(targets); targets = NULL; num_targets = 0; } // Transmit a query packet static void NET_Query_SendQuery(net_addr_t *addr) { net_packet_t *request; request = NET_NewPacket(10); NET_WriteInt16(request, NET_PACKET_TYPE_QUERY); if (addr == NULL) { NET_SendBroadcast(query_context, request); } else { NET_SendPacket(addr, request); } NET_FreePacket(request); } static void NET_Query_ParseResponse(net_addr_t *addr, net_packet_t *packet, net_query_callback_t callback, void *user_data) { unsigned int packet_type; net_querydata_t querydata; query_target_t *target; // Read the header if (!NET_ReadInt16(packet, &packet_type) || packet_type != NET_PACKET_TYPE_QUERY_RESPONSE) { return; } // Read query data if (!NET_ReadQueryData(packet, &querydata)) { return; } // Find the target that responded. target = GetTargetForAddr(addr, false); // If the target is not found, it may be because we are doing // a LAN broadcast search, in which case we need to create a // target for the new responder. if (target == NULL) { query_target_t *broadcast_target; broadcast_target = GetTargetForAddr(NULL, false); // Not in broadcast mode, unexpected response that came out // of nowhere. Ignore. if (broadcast_target == NULL || broadcast_target->state != QUERY_TARGET_QUERIED) { return; } // Create new target. target = GetTargetForAddr(addr, true); target->state = QUERY_TARGET_QUERIED; target->query_time = broadcast_target->query_time; } if (target->state != QUERY_TARGET_RESPONDED) { target->state = QUERY_TARGET_RESPONDED; memcpy(&target->data, &querydata, sizeof(net_querydata_t)); // Calculate RTT. target->ping_time = I_GetTimeMS() - target->query_time; // Invoke callback to signal that we have a new address. callback(addr, &target->data, target->ping_time, user_data); } } // Parse a response packet from the master server. static void NET_Query_ParseMasterResponse(net_addr_t *master_addr, net_packet_t *packet) { unsigned int packet_type; query_target_t *target; char *addr_str; net_addr_t *addr; // Read the header. We are only interested in query responses. if (!NET_ReadInt16(packet, &packet_type) || packet_type != NET_MASTER_PACKET_TYPE_QUERY_RESPONSE) { return; } // Read a list of strings containing the addresses of servers // that the master knows about. for (;;) { addr_str = NET_ReadString(packet); if (addr_str == NULL) { break; } // Resolve address and add to targets list if it is not already // there. addr = NET_ResolveAddress(query_context, addr_str); if (addr != NULL) { GetTargetForAddr(addr, true); NET_ReleaseAddress(addr); } } // Mark the master as having responded. target = GetTargetForAddr(master_addr, true); target->state = QUERY_TARGET_RESPONDED; } static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet, net_query_callback_t callback, void *user_data) { query_target_t *target; // This might be the master server responding. target = GetTargetForAddr(addr, false); if (target != NULL && target->type == QUERY_TARGET_MASTER) { NET_Query_ParseMasterResponse(addr, packet); } else { NET_Query_ParseResponse(addr, packet, callback, user_data); } } static void NET_Query_GetResponse(net_query_callback_t callback, void *user_data) { net_addr_t *addr; net_packet_t *packet; if (NET_RecvPacket(query_context, &addr, &packet)) { NET_Query_ParsePacket(addr, packet, callback, user_data); NET_ReleaseAddress(addr); NET_FreePacket(packet); } } // Find a target we have not yet queried and send a query. static void SendOneQuery(void) { unsigned int now; unsigned int i; now = I_GetTimeMS(); // Rate limit - only send one query every 50ms. if (now - last_query_time < 50) { return; } for (i = 0; i < num_targets; ++i) { // Not queried yet? // Or last query timed out without a response? if (targets[i].state == QUERY_TARGET_QUEUED || (targets[i].state == QUERY_TARGET_QUERIED && now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000)) { break; } } if (i >= num_targets) { return; } // Found a target to query. Send a query; how to do this depends on // the target type. switch (targets[i].type) { case QUERY_TARGET_SERVER: NET_Query_SendQuery(targets[i].addr); break; case QUERY_TARGET_BROADCAST: NET_Query_SendQuery(NULL); break; case QUERY_TARGET_MASTER: NET_Query_SendMasterQuery(targets[i].addr); break; } //printf("Queried %s\n", NET_AddrToString(targets[i].addr)); targets[i].state = QUERY_TARGET_QUERIED; targets[i].query_time = now; ++targets[i].query_attempts; last_query_time = now; } // Time out servers that have been queried and not responded. static void CheckTargetTimeouts(void) { unsigned int i; unsigned int now; now = I_GetTimeMS(); for (i = 0; i < num_targets; ++i) { /* printf("target %i: state %i, queries %i, query time %i\n", i, targets[i].state, targets[i].query_attempts, now - targets[i].query_time); */ // We declare a target to be "no response" when we've sent // multiple query packets to it (QUERY_MAX_ATTEMPTS) and // received no response to any of them. if (targets[i].state == QUERY_TARGET_QUERIED && targets[i].query_attempts >= QUERY_MAX_ATTEMPTS && now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000) { targets[i].state = QUERY_TARGET_NO_RESPONSE; if (targets[i].type == QUERY_TARGET_MASTER) { fprintf(stderr, "NET_MasterQuery: no response " "from master server.\n"); } } } } // If all targets have responded or timed out, returns true. static boolean AllTargetsDone(void) { unsigned int i; for (i = 0; i < num_targets; ++i) { if (targets[i].state != QUERY_TARGET_RESPONDED && targets[i].state != QUERY_TARGET_NO_RESPONSE) { return false; } } return true; } // Polling function, invoked periodically to send queries and // interpret new responses received from remote servers. // Returns zero when the query sequence has completed and all targets // have returned responses or timed out. int NET_Query_Poll(net_query_callback_t callback, void *user_data) { CheckTargetTimeouts(); // Send a query. This will only send a single query at once. SendOneQuery(); // Check for a response NET_Query_GetResponse(callback, user_data); return !AllTargetsDone(); } // Stop the query loop static void NET_Query_ExitLoop(void) { query_loop_running = false; } // Loop waiting for responses. // The specified callback is invoked when a new server responds. static void NET_Query_QueryLoop(net_query_callback_t callback, void *user_data) { query_loop_running = true; while (query_loop_running && NET_Query_Poll(callback, user_data)) { // Don't thrash the CPU I_Sleep(1); } } void NET_Query_Init(void) { if (query_context == NULL) { query_context = NET_NewContext(); NET_AddModule(query_context, &net_sdl_module); net_sdl_module.InitClient(); } free(targets); targets = NULL; num_targets = 0; printed_header = false; } // Callback that exits the query loop when the first server is found. static void NET_Query_ExitCallback(net_addr_t *addr, net_querydata_t *data, unsigned int ping_time, void *user_data) { NET_Query_ExitLoop(); } // Search the targets list and find a target that has responded. // If none have responded, returns NULL. static query_target_t *FindFirstResponder(void) { unsigned int i; for (i = 0; i < num_targets; ++i) { if (targets[i].type == QUERY_TARGET_SERVER && targets[i].state == QUERY_TARGET_RESPONDED) { return &targets[i]; } } return NULL; } // Return a count of the number of responses. static int GetNumResponses(void) { unsigned int i; int result; result = 0; for (i = 0; i < num_targets; ++i) { if (targets[i].type == QUERY_TARGET_SERVER && targets[i].state == QUERY_TARGET_RESPONDED) { ++result; } } return result; } int NET_StartLANQuery(void) { query_target_t *target; NET_Query_Init(); // Add a broadcast target to the list. target = GetTargetForAddr(NULL, true); target->type = QUERY_TARGET_BROADCAST; return 1; } int NET_StartMasterQuery(void) { net_addr_t *master; query_target_t *target; NET_Query_Init(); // Resolve master address and add to targets list. master = NET_Query_ResolveMaster(query_context); if (master == NULL) { return 0; } target = GetTargetForAddr(master, true); target->type = QUERY_TARGET_MASTER; NET_ReleaseAddress(master); return 1; } // ----------------------------------------------------------------------- static void formatted_printf(int wide, const char *s, ...) PRINTF_ATTR(2, 3); static void formatted_printf(int wide, const char *s, ...) { va_list args; int i; va_start(args, s); i = vprintf(s, args); va_end(args); while (i < wide) { putchar(' '); ++i; } } static const char *GameDescription(GameMode_t mode, GameMission_t mission) { switch (mission) { case doom: if (mode == shareware) return "swdoom"; else if (mode == registered) return "regdoom"; else if (mode == retail) return "ultdoom"; else return "doom"; case doom2: return "doom2"; case pack_tnt: return "tnt"; case pack_plut: return "plutonia"; case pack_chex: return "chex"; case pack_hacx: return "hacx"; case heretic: return "heretic"; case hexen: return "hexen"; case strife: return "strife"; default: return "?"; } } static void PrintHeader(void) { int i; putchar('\n'); formatted_printf(5, "Ping"); formatted_printf(18, "Address"); formatted_printf(8, "Players"); puts("Description"); for (i=0; i<70; ++i) putchar('='); putchar('\n'); } // Callback function that just prints information in a table. static void NET_QueryPrintCallback(net_addr_t *addr, net_querydata_t *data, unsigned int ping_time, void *user_data) { // If this is the first server, print the header. if (!printed_header) { PrintHeader(); printed_header = true; } formatted_printf(5, "%4i", ping_time); formatted_printf(22, "%s", NET_AddrToString(addr)); formatted_printf(4, "%i/%i ", data->num_players, data->max_players); if (data->gamemode != indetermined) { printf("(%s) ", GameDescription(data->gamemode, data->gamemission)); } if (data->server_state) { printf("(game running) "); } printf("%s\n", data->description); } void NET_LANQuery(void) { if (NET_StartLANQuery()) { printf("\nSearching for servers on local LAN ...\n"); NET_Query_QueryLoop(NET_QueryPrintCallback, NULL); printf("\n%i server(s) found.\n", GetNumResponses()); FreeTargets(); } } void NET_MasterQuery(void) { if (NET_StartMasterQuery()) { printf("\nSearching for servers on Internet ...\n"); NET_Query_QueryLoop(NET_QueryPrintCallback, NULL); printf("\n%i server(s) found.\n", GetNumResponses()); FreeTargets(); } } void NET_QueryAddress(char *addr_str) { net_addr_t *addr; query_target_t *target; NET_Query_Init(); addr = NET_ResolveAddress(query_context, addr_str); if (addr == NULL) { I_Error("NET_QueryAddress: Host '%s' not found!", addr_str); } // Add the address to the list of targets. target = GetTargetForAddr(addr, true); printf("\nQuerying '%s'...\n", addr_str); // Run query loop. NET_Query_QueryLoop(NET_Query_ExitCallback, NULL); // Check if the target responded. if (target->state == QUERY_TARGET_RESPONDED) { NET_QueryPrintCallback(addr, &target->data, target->ping_time, NULL); NET_ReleaseAddress(addr); FreeTargets(); } else { I_Error("No response from '%s'", addr_str); } } net_addr_t *NET_FindLANServer(void) { query_target_t *target; query_target_t *responder; net_addr_t *result; NET_Query_Init(); // Add a broadcast target to the list. target = GetTargetForAddr(NULL, true); target->type = QUERY_TARGET_BROADCAST; // Run the query loop, and stop at the first target found. NET_Query_QueryLoop(NET_Query_ExitCallback, NULL); responder = FindFirstResponder(); if (responder != NULL) { result = responder->addr; NET_ReferenceAddress(result); } else { result = NULL; } FreeTargets(); return result; } // Block until a packet of the given type is received from the given // address. static net_packet_t *BlockForPacket(net_addr_t *addr, unsigned int packet_type, unsigned int timeout_ms) { net_packet_t *packet; net_addr_t *packet_src; unsigned int read_packet_type; unsigned int start_time; start_time = I_GetTimeMS(); while (I_GetTimeMS() < start_time + timeout_ms) { if (!NET_RecvPacket(query_context, &packet_src, &packet)) { I_Sleep(20); continue; } // Caller doesn't need additional reference. NET_ReleaseAddress(packet_src); if (packet_src == addr && NET_ReadInt16(packet, &read_packet_type) && packet_type == read_packet_type) { return packet; } NET_FreePacket(packet); } // Timeout - no response. return NULL; } // Query master server for secure demo start seed value. boolean NET_StartSecureDemo(prng_seed_t seed) { net_packet_t *request, *response; net_addr_t *master_addr; char *signature; boolean result; NET_Query_Init(); master_addr = NET_Query_ResolveMaster(query_context); // Send request packet to master server. request = NET_NewPacket(10); NET_WriteInt16(request, NET_MASTER_PACKET_TYPE_SIGN_START); NET_SendPacket(master_addr, request); NET_FreePacket(request); // Block for response and read contents. // The signed start message will be saved for later. response = BlockForPacket(master_addr, NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE, SIGNATURE_TIMEOUT_SECS * 1000); result = false; if (response != NULL) { if (NET_ReadPRNGSeed(response, seed)) { signature = NET_ReadString(response); if (signature != NULL) { securedemo_start_message = M_StringDuplicate(signature); result = true; } } NET_FreePacket(response); } return result; } // Query master server for secure demo end signature. char *NET_EndSecureDemo(sha1_digest_t demo_hash) { net_packet_t *request, *response; net_addr_t *master_addr; char *signature; master_addr = NET_Query_ResolveMaster(query_context); // Construct end request and send to master server. request = NET_NewPacket(10); NET_WriteInt16(request, NET_MASTER_PACKET_TYPE_SIGN_END); NET_WriteSHA1Sum(request, demo_hash); NET_WriteString(request, securedemo_start_message); NET_SendPacket(master_addr, request); NET_FreePacket(request); // Block for response. The response packet simply contains a string // with the ASCII signature. response = BlockForPacket(master_addr, NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, SIGNATURE_TIMEOUT_SECS * 1000); if (response == NULL) { return NULL; } signature = NET_ReadString(response); NET_FreePacket(response); return signature; } crispy-doom-crispy-doom-5.6.4/src/net_query.h000066400000000000000000000030641360717211000212120ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Querying servers to find their current status. // #ifndef NET_QUERY_H #define NET_QUERY_H #include "net_defs.h" typedef void (*net_query_callback_t)(net_addr_t *addr, net_querydata_t *querydata, unsigned int ping_time, void *user_data); extern int NET_StartLANQuery(void); extern int NET_StartMasterQuery(void); extern void NET_LANQuery(void); extern void NET_MasterQuery(void); extern void NET_QueryAddress(char *addr); extern net_addr_t *NET_FindLANServer(void); extern int NET_Query_Poll(net_query_callback_t callback, void *user_data); extern net_addr_t *NET_Query_ResolveMaster(net_context_t *context); extern void NET_Query_AddToMaster(net_addr_t *master_addr); extern boolean NET_Query_CheckAddedToMaster(boolean *result); extern void NET_Query_AddResponse(net_packet_t *packet); extern void NET_RequestHolePunch(net_context_t *context, net_addr_t *addr); #endif /* #ifndef NET_QUERY_H */ crispy-doom-crispy-doom-5.6.4/src/net_sdl.c000066400000000000000000000203231360717211000206170ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Networking module which uses SDL_net // #include #include #include #include "doomtype.h" #include "i_system.h" #include "m_argv.h" #include "m_misc.h" #include "net_defs.h" #include "net_io.h" #include "net_packet.h" #include "net_sdl.h" #include "z_zone.h" // // NETWORKING // #include #define DEFAULT_PORT 2342 static boolean initted = false; static int port = DEFAULT_PORT; static UDPsocket udpsocket; static UDPpacket *recvpacket; typedef struct { net_addr_t net_addr; IPaddress sdl_addr; } addrpair_t; static addrpair_t **addr_table; static int addr_table_size = -1; // Initializes the address table static void NET_SDL_InitAddrTable(void) { addr_table_size = 16; addr_table = Z_Malloc(sizeof(addrpair_t *) * addr_table_size, PU_STATIC, 0); memset(addr_table, 0, sizeof(addrpair_t *) * addr_table_size); } static boolean AddressesEqual(IPaddress *a, IPaddress *b) { return a->host == b->host && a->port == b->port; } // Finds an address by searching the table. If the address is not found, // it is added to the table. static net_addr_t *NET_SDL_FindAddress(IPaddress *addr) { addrpair_t *new_entry; int empty_entry = -1; int i; if (addr_table_size < 0) { NET_SDL_InitAddrTable(); } for (i=0; isdl_addr)) { return &addr_table[i]->net_addr; } if (empty_entry < 0 && addr_table[i] == NULL) empty_entry = i; } // Was not found in list. We need to add it. // Is there any space in the table? If not, increase the table size if (empty_entry < 0) { addrpair_t **new_addr_table; int new_addr_table_size; // after reallocing, we will add this in as the first entry // in the new block of memory empty_entry = addr_table_size; // allocate a new array twice the size, init to 0 and copy // the existing table in. replace the old table. new_addr_table_size = addr_table_size * 2; new_addr_table = Z_Malloc(sizeof(addrpair_t *) * new_addr_table_size, PU_STATIC, 0); memset(new_addr_table, 0, sizeof(addrpair_t *) * new_addr_table_size); memcpy(new_addr_table, addr_table, sizeof(addrpair_t *) * addr_table_size); Z_Free(addr_table); addr_table = new_addr_table; addr_table_size = new_addr_table_size; } // Add a new entry new_entry = Z_Malloc(sizeof(addrpair_t), PU_STATIC, 0); new_entry->sdl_addr = *addr; new_entry->net_addr.refcount = 0; new_entry->net_addr.handle = &new_entry->sdl_addr; new_entry->net_addr.module = &net_sdl_module; addr_table[empty_entry] = new_entry; return &new_entry->net_addr; } static void NET_SDL_FreeAddress(net_addr_t *addr) { int i; for (i=0; inet_addr) { Z_Free(addr_table[i]); addr_table[i] = NULL; return; } } I_Error("NET_SDL_FreeAddress: Attempted to remove an unused address!"); } static boolean NET_SDL_InitClient(void) { int p; if (initted) return true; //! // @category net // @arg // // Use the specified UDP port for communications, instead of // the default (2342). // p = M_CheckParmWithArgs("-port", 1); if (p > 0) port = atoi(myargv[p+1]); SDLNet_Init(); udpsocket = SDLNet_UDP_Open(0); if (udpsocket == NULL) { I_Error("NET_SDL_InitClient: Unable to open a socket!"); } recvpacket = SDLNet_AllocPacket(1500); #ifdef DROP_PACKETS srand(time(NULL)); #endif initted = true; return true; } static boolean NET_SDL_InitServer(void) { int p; if (initted) return true; p = M_CheckParmWithArgs("-port", 1); if (p > 0) port = atoi(myargv[p+1]); SDLNet_Init(); udpsocket = SDLNet_UDP_Open(port); if (udpsocket == NULL) { I_Error("NET_SDL_InitServer: Unable to bind to port %i", port); } recvpacket = SDLNet_AllocPacket(1500); #ifdef DROP_PACKETS srand(time(NULL)); #endif initted = true; return true; } static void NET_SDL_SendPacket(net_addr_t *addr, net_packet_t *packet) { UDPpacket sdl_packet; IPaddress ip; if (addr == &net_broadcast_addr) { SDLNet_ResolveHost(&ip, NULL, port); ip.host = INADDR_BROADCAST; } else { ip = *((IPaddress *) addr->handle); } #if 0 { static int this_second_sent = 0; static int lasttime; this_second_sent += packet->len + 64; if (I_GetTime() - lasttime > TICRATE) { printf("%i bytes sent in the last second\n", this_second_sent); lasttime = I_GetTime(); this_second_sent = 0; } } #endif #ifdef DROP_PACKETS if ((rand() % 4) == 0) return; #endif sdl_packet.channel = 0; sdl_packet.data = packet->data; sdl_packet.len = packet->len; sdl_packet.address = ip; if (!SDLNet_UDP_Send(udpsocket, -1, &sdl_packet)) { I_Error("NET_SDL_SendPacket: Error transmitting packet: %s", SDLNet_GetError()); } } static boolean NET_SDL_RecvPacket(net_addr_t **addr, net_packet_t **packet) { int result; result = SDLNet_UDP_Recv(udpsocket, recvpacket); if (result < 0) { I_Error("NET_SDL_RecvPacket: Error receiving packet: %s", SDLNet_GetError()); } // no packets received if (result == 0) return false; // Put the data into a new packet structure *packet = NET_NewPacket(recvpacket->len); memcpy((*packet)->data, recvpacket->data, recvpacket->len); (*packet)->len = recvpacket->len; // Address *addr = NET_SDL_FindAddress(&recvpacket->address); return true; } void NET_SDL_AddrToString(net_addr_t *addr, char *buffer, int buffer_len) { IPaddress *ip; uint32_t host; uint16_t port; ip = (IPaddress *) addr->handle; host = SDLNet_Read32(&ip->host); port = SDLNet_Read16(&ip->port); M_snprintf(buffer, buffer_len, "%i.%i.%i.%i", (host >> 24) & 0xff, (host >> 16) & 0xff, (host >> 8) & 0xff, host & 0xff); // If we are using the default port we just need to show the IP address, // but otherwise we need to include the port. This is important because // we use the string representation in the setup tool to provided an // address to connect to. if (port != DEFAULT_PORT) { char portbuf[10]; M_snprintf(portbuf, sizeof(portbuf), ":%i", port); M_StringConcat(buffer, portbuf, buffer_len); } } net_addr_t *NET_SDL_ResolveAddress(const char *address) { IPaddress ip; char *addr_hostname; int addr_port; int result; char *colon; colon = strchr(address, ':'); addr_hostname = M_StringDuplicate(address); if (colon != NULL) { addr_hostname[colon - address] = '\0'; addr_port = atoi(colon + 1); } else { addr_port = port; } result = SDLNet_ResolveHost(&ip, addr_hostname, addr_port); free(addr_hostname); if (result) { // unable to resolve return NULL; } else { return NET_SDL_FindAddress(&ip); } } // Complete module net_module_t net_sdl_module = { NET_SDL_InitClient, NET_SDL_InitServer, NET_SDL_SendPacket, NET_SDL_RecvPacket, NET_SDL_AddrToString, NET_SDL_FreeAddress, NET_SDL_ResolveAddress, }; crispy-doom-crispy-doom-5.6.4/src/net_sdl.h000066400000000000000000000013461360717211000206300ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Networking module which uses SDL_net // #ifndef NET_SDL_H #define NET_SDL_H #include "net_defs.h" extern net_module_t net_sdl_module; #endif /* #ifndef NET_SDL_H */ crispy-doom-crispy-doom-5.6.4/src/net_server.c000066400000000000000000001427311360717211000213530ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Network server code // #include #include #include #include #include "config.h" #include "doomtype.h" #include "d_mode.h" #include "i_system.h" #include "i_timer.h" #include "m_argv.h" #include "m_misc.h" #include "net_client.h" #include "net_common.h" #include "net_defs.h" #include "net_io.h" #include "net_loop.h" #include "net_packet.h" #include "net_query.h" #include "net_server.h" #include "net_sdl.h" #include "net_structrw.h" // How often to refresh our registration with the master server. #define MASTER_REFRESH_PERIOD 30 /* twice per minute */ // How often to re-resolve the address of the master server? #define MASTER_RESOLVE_PERIOD 8 * 60 * 60 /* 8 hours */ typedef enum { // waiting for the game to be "launched" (key player to press the start // button) SERVER_WAITING_LAUNCH, // game has been launched, we are waiting for all players to be ready // so the game can start. SERVER_WAITING_START, // in a game SERVER_IN_GAME, } net_server_state_t; typedef struct { boolean active; int player_number; net_addr_t *addr; net_connection_t connection; int last_send_time; char *name; // If true, the client has sent the NET_PACKET_TYPE_GAMESTART // message indicating that it is ready for the game to start. boolean ready; // Time that this client connected to the server. // This is used to determine the controller (oldest client). unsigned int connect_time; // Last time new gamedata was received from this client int last_gamedata_time; // recording a demo without -longtics boolean recording_lowres; // send queue: items to send to the client // this is a circular buffer int sendseq; net_full_ticcmd_t sendqueue[BACKUPTICS]; // Latest acknowledged by the client unsigned int acknowledged; // Value of max_players specified by the client on connect. int max_players; // Observer: receives data but does not participate in the game. boolean drone; // SHA1 hash sums of the client's WAD directory and dehacked data sha1_digest_t wad_sha1sum; sha1_digest_t deh_sha1sum; // Is this client is playing with the Freedoom IWAD? unsigned int is_freedoom; // Player class (for Hexen) int player_class; } net_client_t; // structure used for the recv window typedef struct { // Whether this tic has been received yet boolean active; // Latency value received from the client signed int latency; // Last time we sent a resend request for this tic unsigned int resend_time; // Tic data itself net_ticdiff_t diff; } net_client_recv_t; static net_server_state_t server_state; static boolean server_initialized = false; static net_client_t clients[MAXNETNODES]; static net_client_t *sv_players[NET_MAXPLAYERS]; static net_context_t *server_context; static unsigned int sv_gamemode; static unsigned int sv_gamemission; static net_gamesettings_t sv_settings; // For registration with master server: static net_addr_t *master_server = NULL; static unsigned int master_refresh_time; static unsigned int master_resolve_time; // receive window static unsigned int recvwindow_start; static net_client_recv_t recvwindow[BACKUPTICS][NET_MAXPLAYERS]; #define NET_SV_ExpandTicNum(b) NET_ExpandTicNum(recvwindow_start, (b)) static void NET_SV_DisconnectClient(net_client_t *client) { if (client->active) { NET_Conn_Disconnect(&client->connection); } } static boolean ClientConnected(net_client_t *client) { // Check that the client is properly connected: ie. not in the // process of connecting or disconnecting return client->active && client->connection.state == NET_CONN_STATE_CONNECTED; } // Send a message to be displayed on a client's console static void NET_SV_SendConsoleMessage(net_client_t *client, const char *s, ...) PRINTF_ATTR(2, 3); static void NET_SV_SendConsoleMessage(net_client_t *client, const char *s, ...) { char buf[1024]; va_list args; net_packet_t *packet; va_start(args, s); M_vsnprintf(buf, sizeof(buf), s, args); va_end(args); packet = NET_Conn_NewReliable(&client->connection, NET_PACKET_TYPE_CONSOLE_MESSAGE); NET_WriteString(packet, buf); } // Send a message to all clients static void NET_SV_BroadcastMessage(const char *s, ...) PRINTF_ATTR(1, 2); static void NET_SV_BroadcastMessage(const char *s, ...) { char buf[1024]; va_list args; int i; va_start(args, s); M_vsnprintf(buf, sizeof(buf), s, args); va_end(args); for (i=0; iplayer_number = pl; ++pl; } else { clients[i].player_number = -1; } } } for (; plconnect_time) { best = &clients[i]; } } return best; } static void NET_SV_SendWaitingData(net_client_t *client) { net_waitdata_t wait_data; net_packet_t *packet; net_client_t *controller; int i; NET_SV_AssignPlayers(); controller = NET_SV_Controller(); wait_data.num_players = NET_SV_NumPlayers(); wait_data.num_drones = NET_SV_NumDrones(); wait_data.ready_players = NET_SV_NumReadyPlayers(); wait_data.max_players = NET_SV_MaxPlayers(); wait_data.is_controller = (client == controller); wait_data.consoleplayer = client->player_number; // Send the WAD and dehacked checksums of the controlling client. // If no controller found (?), send the details that the client // is expecting anyway. if (controller == NULL) { controller = client; } memcpy(&wait_data.wad_sha1sum, &controller->wad_sha1sum, sizeof(sha1_digest_t)); memcpy(&wait_data.deh_sha1sum, &controller->deh_sha1sum, sizeof(sha1_digest_t)); wait_data.is_freedoom = controller->is_freedoom; // set name and address of each player: for (i = 0; i < wait_data.num_players; ++i) { M_StringCopy(wait_data.player_names[i], sv_players[i]->name, MAXPLAYERNAME); M_StringCopy(wait_data.player_addrs[i], NET_AddrToString(sv_players[i]->addr), MAXPLAYERNAME); } // Construct packet: packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA); NET_WriteWaitData(packet, &wait_data); // Send packet to client and free NET_Conn_SendPacket(&client->connection, packet); NET_FreePacket(packet); } // Find the latest tic which has been acknowledged as received by // all clients. static unsigned int NET_SV_LatestAcknowledged(void) { unsigned int lowtic = UINT_MAX; int i; for (i=0; iactive = true; client->connect_time = I_GetTimeMS(); NET_Conn_InitServer(&client->connection, addr, protocol); client->addr = addr; NET_ReferenceAddress(addr); client->last_send_time = -1; // init the ticcmd send queue client->sendseq = 0; client->acknowledged = 0; client->drone = false; client->ready = false; client->last_gamedata_time = 0; memset(client->sendqueue, 0xff, sizeof(client->sendqueue)); NET_Log("server: initialized new client from %s", NET_AddrToString(addr)); } // parse a SYN from a client(initiating a connection) static void NET_SV_ParseSYN(net_packet_t *packet, net_client_t *client, net_addr_t *addr) { unsigned int magic; net_connect_data_t data; net_packet_t *reply; net_protocol_t protocol; char *player_name; char *client_version; int num_players; int i; NET_Log("server: processing SYN packet"); // Read the magic number and check it is the expected one. if (!NET_ReadInt32(packet, &magic)) { NET_Log("server: error: no magic number for SYN"); return; } switch (magic) { case NET_MAGIC_NUMBER: break; case NET_OLD_MAGIC_NUMBER: NET_Log("server: error: client using old magic number: %d", magic); NET_SV_SendReject(addr, "You are using an old client version that is not supported by " "this server. This server is running " PACKAGE_STRING "."); return; default: NET_Log("server: error: wrong magic number: %d", magic); return; } // Read the client version string. We actually now only use this when // sending a reject message, as we only reject if we can't negotiate a // common protocol (below). client_version = NET_ReadString(packet); if (client_version == NULL) { NET_Log("server: error: no version from client"); return; } // Read the client's list of accepted protocols. Net play between forks // of Chocolate Doom is accepted provided that they can negotiate a // common accepted protocol. protocol = NET_ReadProtocolList(packet); if (protocol == NET_PROTOCOL_UNKNOWN) { char reject_msg[256]; M_snprintf(reject_msg, sizeof(reject_msg), "Version mismatch: server version is: " PACKAGE_STRING "; " "client is: %s. No common compatible protocol could be " "negotiated.", client_version); NET_SV_SendReject(addr, reject_msg); NET_Log("server: error: no common protocol"); return; } // Read connect data, and check that the game mode/mission are valid // and the max_players value is in a sensible range. if (!NET_ReadConnectData(packet, &data)) { NET_Log("server: error: failed to read connect data"); return; } if (!D_ValidGameMode(data.gamemission, data.gamemode) || data.max_players > NET_MAXPLAYERS) { NET_Log("server: error: invalid connect data, max_players=%d, " "gamemission=%d, gamemode=%d", data.max_players, data.gamemission, data.gamemode); return; } // Read the player's name player_name = NET_ReadString(packet); if (player_name == NULL) { NET_Log("server: error: failed to read player name"); return; } // At this point we have received a valid SYN. // Not accepting new connections? if (server_state != SERVER_WAITING_LAUNCH) { NET_Log("server: error: not in waiting launch state, server_state=%d", server_state); NET_SV_SendReject(addr, "Server is not currently accepting connections"); return; } // Before accepting a new client, check that there is a slot free. NET_SV_AssignPlayers(); num_players = NET_SV_NumPlayers(); if ((!data.drone && num_players >= NET_SV_MaxPlayers()) || NET_SV_NumClients() >= MAXNETNODES) { NET_Log("server: no more players, num_players=%d, max=%d", num_players, NET_SV_MaxPlayers()); NET_SV_SendReject(addr, "Server is full!"); return; } // TODO: Add server option to allow rejecting clients which set // lowres_turn. This is potentially desirable as the presence of such // clients affects turning resolution. // Adopt the game mode and mission of the first connecting client: if (num_players == 0 && !data.drone) { sv_gamemode = data.gamemode; sv_gamemission = data.gamemission; NET_Log("server: new game, mode=%d, mission=%d", sv_gamemode, sv_gamemission); } // Check the connecting client is playing the same game as all // the other clients if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission) { char msg[128]; NET_Log("server: wrong mode/mission, %d != %d || %d != %d", data.gamemode, sv_gamemode, data.gamemission, sv_gamemission); M_snprintf(msg, sizeof(msg), "Game mismatch: server is %s (%s), client is %s (%s)", D_GameMissionString(sv_gamemission), D_GameModeString(sv_gamemode), D_GameMissionString(data.gamemission), D_GameModeString(data.gamemode)); NET_SV_SendReject(addr, msg); return; } // Allocate a client slot if there isn't one already if (client == NULL) { // find a slot, or return if none found for (i=0; iconnection.state == NET_CONN_STATE_DISCONNECTED) { client->active = false; } } // Client already connected? if (client->active) { NET_Log("server: client is already initialized (duplicate SYN?)"); return; } // Activate, initialize connection NET_SV_InitNewClient(client, addr, protocol); // Save the SHA1 checksums and other details. memcpy(client->wad_sha1sum, data.wad_sha1sum, sizeof(sha1_digest_t)); memcpy(client->deh_sha1sum, data.deh_sha1sum, sizeof(sha1_digest_t)); client->is_freedoom = data.is_freedoom; client->max_players = data.max_players; client->name = M_StringDuplicate(player_name); client->recording_lowres = data.lowres_turn; client->drone = data.drone; client->player_class = data.player_class; // Send a reply back to the client, indicating a successful connection // and specifying the protocol that will be used for communications. reply = NET_Conn_NewReliable(&client->connection, NET_PACKET_TYPE_SYN); NET_WriteString(reply, PACKAGE_STRING); NET_WriteProtocol(reply, protocol); } // Parse a launch packet. This is sent by the key player when the "start" // button is pressed, and causes the startup process to continue. static void NET_SV_ParseLaunch(net_packet_t *packet, net_client_t *client) { net_packet_t *launchpacket; int num_players; unsigned int i; NET_Log("server: processing launch packet"); // Only the controller can launch the game. if (client != NET_SV_Controller()) { NET_Log("server: error: this client isn't the controller, %d != %d", client, NET_SV_Controller()); return; } // Can only launch when we are in the waiting state. if (server_state != SERVER_WAITING_LAUNCH) { NET_Log("server: error: not in waiting launch state, state=%d", server_state); return; } // Forward launch on to all clients. NET_Log("server: sending launch to all clients"); NET_SV_AssignPlayers(); num_players = NET_SV_NumPlayers(); for (i=0; irecording_lowres) { sv_settings.lowres_turn = true; } } sv_settings.num_players = NET_SV_NumPlayers(); // Copy player classes: for (i = 0; i < NET_MAXPLAYERS; ++i) { if (sv_players[i] != NULL) { sv_settings.player_classes[i] = sv_players[i]->player_class; } else { sv_settings.player_classes[i] = 0; } } nowtime = I_GetTimeMS(); // Send start packets to each connected node for (i = 0; i < MAXNETNODES; ++i) { if (!ClientConnected(&clients[i])) continue; clients[i].last_gamedata_time = nowtime; startpacket = NET_Conn_NewReliable(&clients[i].connection, NET_PACKET_TYPE_GAMESTART); sv_settings.consoleplayer = clients[i].player_number; NET_WriteSettings(startpacket, &sv_settings); } // Change server state NET_Log("server: beginning game state"); server_state = SERVER_IN_GAME; memset(recvwindow, 0, sizeof(recvwindow)); recvwindow_start = 0; } // Returns true when all nodes have indicated readiness to start the game. static boolean AllNodesReady(void) { unsigned int i; for (i = 0; i < MAXNETNODES; ++i) { if (ClientConnected(&clients[i]) && !clients[i].ready) { return false; } } return true; } // Check if the game should start, and if so, start it. static void CheckStartGame(void) { if (!AllNodesReady()) { NET_Log("server: not all clients ready to start yet"); return; } NET_Log("server: all clients ready, starting game"); StartGame(); } // Send waiting data with current status to all nodes that are ready to // start the game. static void SendAllWaitingData(void) { unsigned int i; for (i = 0; i < MAXNETNODES; ++i) { if (ClientConnected(&clients[i]) && clients[i].ready) { NET_SV_SendWaitingData(&clients[i]); } } } // Parse a game start packet static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client) { net_gamesettings_t settings; NET_Log("server: processing game start packet"); // Can only start a game if we are in the waiting start state. if (server_state != SERVER_WAITING_START) { NET_Log("server: error: not in waiting start state, server_state=%d", server_state); return; } if (client == NET_SV_Controller()) { if (!NET_ReadSettings(packet, &settings)) { // Malformed packet NET_Log("server: error: no settings from controller"); return; } // Check the game settings are valid if (!NET_ValidGameSettings(sv_gamemode, sv_gamemission, &settings)) { NET_Log("server: error: invalid game settings"); return; } sv_settings = settings; } client->ready = true; CheckStartGame(); // Update all ready clients with the current state (number of players // ready, etc.). This is used by games that show startup progress // (eg. Hexen's spinal loading) SendAllWaitingData(); } // Send a resend request to a client static void NET_SV_SendResendRequest(net_client_t *client, int start, int end) { net_packet_t *packet; net_client_recv_t *recvobj; int i; unsigned int nowtime; int index; NET_Log("server: send resend to %s for tics %d-%d", NET_AddrToString(client->addr), start, end); packet = NET_NewPacket(20); NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA_RESEND); NET_WriteInt32(packet, start); NET_WriteInt8(packet, end - start + 1); NET_Conn_SendPacket(&client->connection, packet); NET_FreePacket(packet); // Store the time we send the resend request nowtime = I_GetTimeMS(); for (i=start; i<=end; ++i) { index = i - recvwindow_start; if (index >= BACKUPTICS) { // Outside the range continue; } recvobj = &recvwindow[index][client->player_number]; recvobj->resend_time = nowtime; } } // Check for expired resend requests static void NET_SV_CheckResends(net_client_t *client) { int i; int player; int resend_start, resend_end; unsigned int nowtime; nowtime = I_GetTimeMS(); player = client->player_number; resend_start = -1; resend_end = -1; for (i=0; iactive && recvobj->resend_time != 0 && nowtime > recvobj->resend_time + 300; if (need_resend) { // Start a new run of resend tics? if (resend_start < 0) { resend_start = i; } resend_end = i; } else if (resend_start >= 0) { // End of a run of resend tics NET_Log("server: resend request to %s timed out for %d-%d (%d)", NET_AddrToString(client->addr), recvwindow_start + resend_start, recvwindow_start + resend_end, &recvwindow[resend_start][player].resend_time); NET_SV_SendResendRequest(client, recvwindow_start + resend_start, recvwindow_start + resend_end); resend_start = -1; } } if (resend_start >= 0) { NET_Log("server: resend request to %s timed out for %d-%d (%d)", NET_AddrToString(client->addr), recvwindow_start + resend_start, recvwindow_start + resend_end, &recvwindow[resend_start][player].resend_time); NET_SV_SendResendRequest(client, recvwindow_start + resend_start, recvwindow_start + resend_end); } } // Process game data from a client static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client) { net_client_recv_t *recvobj; unsigned int seq; unsigned int ackseq; unsigned int num_tics; unsigned int nowtime; size_t i; int player; int resend_start, resend_end; int index; if (server_state != SERVER_IN_GAME) { NET_Log("server: error: not in game state: server_state=%d", server_state); return; } if (client->drone) { // Drones do not contribute any game data. NET_Log("server: error: game data from a drone?"); return; } player = client->player_number; // Read header if (!NET_ReadInt8(packet, &ackseq) || !NET_ReadInt8(packet, &seq) || !NET_ReadInt8(packet, &num_tics)) { NET_Log("server: error: failed to read header"); return; } NET_Log("server: got game data, seq=%d, num_tics=%d, ackseq=%d", seq, num_tics, ackseq); // Get the current time nowtime = I_GetTimeMS(); // Expand 8-bit values to the full sequence number ackseq = NET_SV_ExpandTicNum(ackseq); seq = NET_SV_ExpandTicNum(seq); // Sanity checks for (i=0; i= BACKUPTICS) { // Not in range of the recv window continue; } recvobj = &recvwindow[index][player]; recvobj->active = true; recvobj->diff = diff; recvobj->latency = latency; client->last_gamedata_time = nowtime; NET_Log("server: stored tic %d for player %d", seq + i, player); } // Higher acknowledgement point? if (ackseq > client->acknowledged) { NET_Log("server: acknowledged up to %d", ackseq); client->acknowledged = ackseq; } // Has this been received out of sequence, ie. have we not received // all tics before the first tic in this packet? If so, send a // resend request. //printf("SV: %p: %i\n", client, seq); resend_end = seq - recvwindow_start; if (resend_end <= 0) return; if (resend_end >= BACKUPTICS) resend_end = BACKUPTICS - 1; index = resend_end - 1; resend_start = resend_end; while (index >= 0) { recvobj = &recvwindow[index][player]; if (recvobj->active) { // ended our run of unreceived tics break; } if (recvobj->resend_time != 0) { // Already sent a resend request for this tic break; } resend_start = index; --index; } // Possibly send a resend request if (resend_start < resend_end) { NET_Log("server: request resend for %d-%d before %d", recvwindow_start + resend_start, recvwindow_start + resend_end - 1, seq); NET_SV_SendResendRequest(client, recvwindow_start + resend_start, recvwindow_start + resend_end - 1); } } static void NET_SV_ParseGameDataACK(net_packet_t *packet, net_client_t *client) { unsigned int ackseq; NET_Log("server: processing game data ack packet"); if (server_state != SERVER_IN_GAME) { NET_Log("server: error: not in game state, server_state=%d", server_state); return; } // Read header if (!NET_ReadInt8(packet, &ackseq)) { NET_Log("server: error: missing acknowledgement field"); return; } // Expand 8-bit values to the full sequence number ackseq = NET_SV_ExpandTicNum(ackseq); // Higher acknowledgement point than we already have? if (ackseq > client->acknowledged) { NET_Log("server: acknowledged up to %d", ackseq); client->acknowledged = ackseq; } } static void NET_SV_SendTics(net_client_t *client, unsigned int start, unsigned int end) { net_packet_t *packet; unsigned int i; packet = NET_NewPacket(500); NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA); // Send the start tic and number of tics NET_WriteInt8(packet, start & 0xff); NET_WriteInt8(packet, end-start + 1); // Write the tics for (i=start; i<=end; ++i) { net_full_ticcmd_t *cmd; cmd = &client->sendqueue[i % BACKUPTICS]; if (i != cmd->seq) { I_Error("Wanted to send %i, but %i is in its place", i, cmd->seq); } // Add command NET_WriteFullTiccmd(packet, cmd, sv_settings.lowres_turn); } // Send packet NET_Conn_SendPacket(&client->connection, packet); NET_FreePacket(packet); } // Parse a retransmission request from a client static void NET_SV_ParseResendRequest(net_packet_t *packet, net_client_t *client) { unsigned int start, last; unsigned int num_tics; unsigned int i; NET_Log("server: processing resend request"); // Read the starting tic and number of tics if (!NET_ReadInt32(packet, &start) || !NET_ReadInt8(packet, &num_tics)) { NET_Log("server: error: missing fields for resend"); return; } //printf("SV: %p: resend %i-%i\n", client, start, start+num_tics-1); // Check we have all the requested tics last = start + num_tics - 1; for (i=start; i<=last; ++i) { net_full_ticcmd_t *cmd; cmd = &client->sendqueue[i % BACKUPTICS]; if (i != cmd->seq) { // We do not have the requested tic (any more) // This is pretty fatal. We could disconnect the client, // but then again this could be a spoofed packet. Just // ignore it. NET_Log("server: error: don't have tic %d any more, " "can't resend", i); return; } } // Resend those tics NET_Log("server: resending tics %d-%d", start, last); NET_SV_SendTics(client, start, last); } // Send a response back to the client void NET_SV_SendQueryResponse(net_addr_t *addr) { net_packet_t *reply; net_querydata_t querydata; int p; // Version querydata.version = PACKAGE_STRING; // Server state querydata.server_state = server_state; // Number of players/maximum players querydata.num_players = NET_SV_NumPlayers(); querydata.max_players = NET_SV_MaxPlayers(); // Game mode/mission querydata.gamemode = sv_gamemode; querydata.gamemission = sv_gamemission; //! // @category net // @arg // // When starting a network server, specify a name for the server. // p = M_CheckParmWithArgs("-servername", 1); if (p > 0) { querydata.description = myargv[p + 1]; } else { querydata.description = "Unnamed server"; } // Send it and we're done. NET_Log("server: sending query response to %s", NET_AddrToString(addr)); reply = NET_NewPacket(64); NET_WriteInt16(reply, NET_PACKET_TYPE_QUERY_RESPONSE); NET_WriteQueryData(reply, &querydata); NET_SendPacket(addr, reply); NET_FreePacket(reply); } static void NET_SV_ParseHolePunch(net_packet_t *packet) { const char *addr_string; net_packet_t *sendpacket; net_addr_t *addr; addr_string = NET_ReadString(packet); if (addr_string == NULL) { NET_Log("server: error: hole punch request but no address provided"); return; } addr = NET_ResolveAddress(server_context, addr_string); if (addr == NULL) { NET_Log("server: error: failed to resolve address: %s", addr_string); return; } sendpacket = NET_NewPacket(16); NET_WriteInt16(sendpacket, NET_PACKET_TYPE_NAT_HOLE_PUNCH); NET_SendPacket(addr, sendpacket); NET_FreePacket(sendpacket); NET_ReleaseAddress(addr); NET_Log("server: sent hole punch to %s", addr_string); } static void NET_SV_MasterPacket(net_packet_t *packet) { unsigned int packet_type; // Read the packet type if (!NET_ReadInt16(packet, &packet_type)) { NET_Log("server: error: no packet type in master server message"); return; } NET_Log("server: packet from master server; type %d", packet_type); NET_LogPacket(packet); switch (packet_type) { case NET_MASTER_PACKET_TYPE_ADD_RESPONSE: NET_Query_AddResponse(packet); break; case NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH: NET_SV_ParseHolePunch(packet); break; } } // Process a packet received by the server static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr) { net_client_t *client; unsigned int packet_type; // Response from master server? if (addr != NULL && addr == master_server) { NET_SV_MasterPacket(packet); return; } // Find which client this packet came from client = NET_SV_FindClient(addr); // Read the packet type if (!NET_ReadInt16(packet, &packet_type)) { // no packet type return; } NET_Log("server: packet from %s; type %d", NET_AddrToString(addr), packet_type & ~NET_RELIABLE_PACKET); NET_LogPacket(packet); if (packet_type == NET_PACKET_TYPE_SYN) { NET_SV_ParseSYN(packet, client, addr); } else if (packet_type == NET_PACKET_TYPE_QUERY) { NET_SV_SendQueryResponse(addr); } else if (client == NULL) { // Must come from a valid client; ignore otherwise } else if (NET_Conn_Packet(&client->connection, packet, &packet_type)) { // Packet was eaten by the common connection code } else { //printf("SV: %s: %i\n", NET_AddrToString(addr), packet_type); switch (packet_type) { case NET_PACKET_TYPE_GAMESTART: NET_SV_ParseGameStart(packet, client); break; case NET_PACKET_TYPE_LAUNCH: NET_SV_ParseLaunch(packet, client); break; case NET_PACKET_TYPE_GAMEDATA: NET_SV_ParseGameData(packet, client); break; case NET_PACKET_TYPE_GAMEDATA_ACK: NET_SV_ParseGameDataACK(packet, client); break; case NET_PACKET_TYPE_GAMEDATA_RESEND: NET_SV_ParseResendRequest(packet, client); break; default: // unknown packet type break; } } } static void NET_SV_PumpSendQueue(net_client_t *client) { net_full_ticcmd_t cmd; int recv_index; int num_players; int i; int starttic, endtic; // If a client has not sent any acknowledgments for a while, // wait until they catch up. if (client->sendseq - NET_SV_LatestAcknowledged() > 40) { return; } // Work out the index into the receive window recv_index = client->sendseq - recvwindow_start; if (recv_index < 0 || recv_index >= BACKUPTICS) { return; } // Check if we can generate a new entry for the send queue // using the data in recvwindow. num_players = 0; for (i=0; isendseq > recvwindow_start + 10) { return; } // We have all data we need to generate a command for this tic. cmd.seq = client->sendseq; // Add ticcmds from all players cmd.latency = 0; for (i=0; idiff; if (recvobj->latency > cmd.latency) cmd.latency = recvobj->latency; } //printf("SV: %i: latency %i\n", client->player_number, cmd.latency); // Add into the queue client->sendqueue[client->sendseq % BACKUPTICS] = cmd; // Transmit the new tic to the client starttic = client->sendseq - sv_settings.extratics; endtic = client->sendseq; if (starttic < 0) starttic = 0; NET_Log("server: send tics %d-%d to %s", starttic, endtic, NET_AddrToString(client->addr)); NET_SV_SendTics(client, starttic, endtic); ++client->sendseq; } // Prevent against deadlock: resend requests are usually only // triggered if we miss a packet and receive the next one. // If we miss a whole load of packets, we can end up in a // deadlock situation where the client will not send any more. // If we don't receive any game data in a while, trigger a resend // request for the next tic we're expecting. void NET_SV_CheckDeadlock(net_client_t *client) { int nowtime; int i; // Don't expect game data from clients. if (client->drone) { return; } nowtime = I_GetTimeMS(); // If we haven't received anything for a long time, it may be a deadlock. if (nowtime - client->last_gamedata_time > 1000) { NET_Log("server: no gamedata from %s since %d - deadlock?", NET_AddrToString(client->addr), client->last_gamedata_time); // Search the receive window for the first tic we are expecting // from this player. for (i=0; iplayer_number][i].active) { NET_Log("server: deadlock: sending resend request for %d-%d", recvwindow_start + i, recvwindow_start + i + 5); // Found a tic we haven't received. Send a resend request. NET_SV_SendResendRequest(client, recvwindow_start + i, recvwindow_start + i + 5); client->last_gamedata_time = nowtime; break; } } // If we sent a resend request to break the deadlock, also trigger a // resend of any tics we have sitting in the send queue, in case the // client is blocked waiting on tics from us that have been lost. // This fixes deadlock with some older clients which do not send // resends to break deadlock. if (i < BACKUPTICS && client->sendseq > client->acknowledged) { NET_Log("server: also resending tics %d-%d to break deadlock", client->acknowledged, client->sendseq - 1); NET_SV_SendTics(client, client->acknowledged, client->sendseq - 1); } } } // Called when all players have disconnected. Return to listening for // players to start a new game, and disconnect any drones still connected. static void NET_SV_GameEnded(void) { int i; server_state = SERVER_WAITING_LAUNCH; sv_gamemode = indetermined; for (i=0; iconnection); if (client->connection.state == NET_CONN_STATE_DISCONNECTED && client->connection.disconnect_reason == NET_DISCONNECT_TIMEOUT) { NET_Log("server: client at %s timed out", NET_AddrToString(client->addr)); NET_SV_BroadcastMessage("Client '%s' timed out and disconnected", client->name); } // Is this client disconnected? if (client->connection.state == NET_CONN_STATE_DISCONNECTED) { client->active = false; // If we were about to start a game, any player disconnecting // should cause an abort. if (server_state == SERVER_WAITING_START && !client->drone) { NET_SV_BroadcastMessage("Game startup aborted because " "player '%s' disconnected.", client->name); NET_SV_GameEnded(); } free(client->name); NET_ReleaseAddress(client->addr); // Are there any clients left connected? If not, return the // server to the waiting-for-players state. // // Disconnect any drones still connected. if (NET_SV_NumPlayers() <= 0) { NET_Log("server: no player clients left, game ended"); NET_SV_GameEnded(); } } if (!ClientConnected(client)) { // client has not yet finished connecting return; } if (server_state == SERVER_WAITING_LAUNCH) { // Waiting for the game to start // Send information once every second if (client->last_send_time < 0 || I_GetTimeMS() - client->last_send_time > 1000) { NET_SV_SendWaitingData(client); client->last_send_time = I_GetTimeMS(); } } if (server_state == SERVER_IN_GAME) { NET_SV_PumpSendQueue(client); NET_SV_CheckDeadlock(client); } } // Add a network module to the server context void NET_SV_AddModule(net_module_t *module) { module->InitServer(); NET_AddModule(server_context, module); } // Initialize server and wait for connections void NET_SV_Init(void) { int i; // initialize send/receive context server_context = NET_NewContext(); // no clients yet for (i=0; i MASTER_RESOLVE_PERIOD * 1000) { net_addr_t *new_addr; new_addr = NET_Query_ResolveMaster(server_context); NET_ReleaseAddress(master_server); master_server = new_addr; master_resolve_time = now; } // Possibly refresh our registration with the master server. if (now - master_refresh_time > MASTER_REFRESH_PERIOD * 1000) { NET_Query_AddToMaster(master_server); master_refresh_time = now; } } void NET_SV_RegisterWithMaster(void) { //! // @category net // // When running a server, don't register with the global master server. // Implies -server. // if (!M_CheckParm("-privateserver")) { master_server = NET_Query_ResolveMaster(server_context); } else { master_server = NULL; } // Send request. if (master_server != NULL) { NET_Query_AddToMaster(master_server); master_refresh_time = I_GetTimeMS(); master_resolve_time = master_refresh_time; } } // Run server code to check for new packets/send packets as the server // requires void NET_SV_Run(void) { net_addr_t *addr; net_packet_t *packet; int i; if (!server_initialized) { return; } while (NET_RecvPacket(server_context, &addr, &packet)) { NET_SV_Packet(packet, addr); NET_FreePacket(packet); NET_ReleaseAddress(addr); } if (master_server != NULL) { UpdateMasterServer(); } // "Run" any clients that may have things to do, independent of responses // to received packets for (i=0; i 5000) { running = false; fprintf(stderr, "SV: Timed out waiting for clients to disconnect.\n"); } // Run the client code in case this is a loopback client. NET_CL_Run(); NET_SV_Run(); // Don't hog the CPU I_Sleep(1); } } crispy-doom-crispy-doom-5.6.4/src/net_server.h000066400000000000000000000021241360717211000213470ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Network server code // #ifndef NET_SERVER_H #define NET_SERVER_H // initialize server and wait for connections void NET_SV_Init(void); // run server: check for new packets received etc. void NET_SV_Run(void); // Shut down the server // Blocks until all clients disconnect, or until a 5 second timeout void NET_SV_Shutdown(void); // Add a network module to the context used by the server void NET_SV_AddModule(net_module_t *module); // Register server with master server. void NET_SV_RegisterWithMaster(void); #endif /* #ifndef NET_SERVER_H */ crispy-doom-crispy-doom-5.6.4/src/net_structrw.c000066400000000000000000000445671360717211000217520ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Reading and writing various structures into packets // #include #include #include #include "doomtype.h" #include "i_system.h" #include "m_misc.h" #include "net_packet.h" #include "net_structrw.h" // String names for the enum values in net_protocol_t, which are what is // sent over the wire. Every enum value must have an entry in this list. static struct { net_protocol_t protocol; const char *name; } protocol_names[] = { {NET_PROTOCOL_CHOCOLATE_DOOM_0, "CHOCOLATE_DOOM_0"}, }; void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data) { NET_WriteInt8(packet, data->gamemode); NET_WriteInt8(packet, data->gamemission); NET_WriteInt8(packet, data->lowres_turn); NET_WriteInt8(packet, data->drone); NET_WriteInt8(packet, data->max_players); NET_WriteInt8(packet, data->is_freedoom); NET_WriteSHA1Sum(packet, data->wad_sha1sum); NET_WriteSHA1Sum(packet, data->deh_sha1sum); NET_WriteInt8(packet, data->player_class); } boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data) { return NET_ReadInt8(packet, (unsigned int *) &data->gamemode) && NET_ReadInt8(packet, (unsigned int *) &data->gamemission) && NET_ReadInt8(packet, (unsigned int *) &data->lowres_turn) && NET_ReadInt8(packet, (unsigned int *) &data->drone) && NET_ReadInt8(packet, (unsigned int *) &data->max_players) && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom) && NET_ReadSHA1Sum(packet, data->wad_sha1sum) && NET_ReadSHA1Sum(packet, data->deh_sha1sum) && NET_ReadInt8(packet, (unsigned int *) &data->player_class); } void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings) { int i; NET_WriteInt8(packet, settings->ticdup); NET_WriteInt8(packet, settings->extratics); NET_WriteInt8(packet, settings->deathmatch); NET_WriteInt8(packet, settings->nomonsters); NET_WriteInt8(packet, settings->fast_monsters); NET_WriteInt8(packet, settings->respawn_monsters); NET_WriteInt8(packet, settings->episode); NET_WriteInt8(packet, settings->map); NET_WriteInt8(packet, settings->skill); NET_WriteInt8(packet, settings->gameversion); NET_WriteInt8(packet, settings->lowres_turn); NET_WriteInt8(packet, settings->new_sync); NET_WriteInt32(packet, settings->timelimit); NET_WriteInt8(packet, settings->loadgame); NET_WriteInt8(packet, settings->random); NET_WriteInt8(packet, settings->num_players); NET_WriteInt8(packet, settings->consoleplayer); for (i = 0; i < settings->num_players; ++i) { NET_WriteInt8(packet, settings->player_classes[i]); } } boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings) { boolean success; int i; success = NET_ReadInt8(packet, (unsigned int *) &settings->ticdup) && NET_ReadInt8(packet, (unsigned int *) &settings->extratics) && NET_ReadInt8(packet, (unsigned int *) &settings->deathmatch) && NET_ReadInt8(packet, (unsigned int *) &settings->nomonsters) && NET_ReadInt8(packet, (unsigned int *) &settings->fast_monsters) && NET_ReadInt8(packet, (unsigned int *) &settings->respawn_monsters) && NET_ReadInt8(packet, (unsigned int *) &settings->episode) && NET_ReadInt8(packet, (unsigned int *) &settings->map) && NET_ReadSInt8(packet, &settings->skill) && NET_ReadInt8(packet, (unsigned int *) &settings->gameversion) && NET_ReadInt8(packet, (unsigned int *) &settings->lowres_turn) && NET_ReadInt8(packet, (unsigned int *) &settings->new_sync) && NET_ReadInt32(packet, (unsigned int *) &settings->timelimit) && NET_ReadSInt8(packet, (signed int *) &settings->loadgame) && NET_ReadInt8(packet, (unsigned int *) &settings->random) && NET_ReadInt8(packet, (unsigned int *) &settings->num_players) && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer); if (!success) { return false; } for (i = 0; i < settings->num_players; ++i) { if (!NET_ReadInt8(packet, (unsigned int *) &settings->player_classes[i])) { return false; } } return true; } boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query) { boolean success; query->version = NET_ReadSafeString(packet); success = query->version != NULL && NET_ReadInt8(packet, (unsigned int *) &query->server_state) && NET_ReadInt8(packet, (unsigned int *) &query->num_players) && NET_ReadInt8(packet, (unsigned int *) &query->max_players) && NET_ReadInt8(packet, (unsigned int *) &query->gamemode) && NET_ReadInt8(packet, (unsigned int *) &query->gamemission); if (!success) { return false; } query->description = NET_ReadSafeString(packet); // We read the list of protocols supported by the server. However, // old versions of Chocolate Doom do not support this field; it is // okay if it cannot be successfully read. query->protocol = NET_ReadProtocolList(packet); return query->description != NULL; } void NET_WriteQueryData(net_packet_t *packet, net_querydata_t *query) { NET_WriteString(packet, query->version); NET_WriteInt8(packet, query->server_state); NET_WriteInt8(packet, query->num_players); NET_WriteInt8(packet, query->max_players); NET_WriteInt8(packet, query->gamemode); NET_WriteInt8(packet, query->gamemission); NET_WriteString(packet, query->description); // Write a list of all supported protocols. Note that the query->protocol // field is ignored here; it is only used when receiving. NET_WriteProtocolList(packet); } void NET_WriteTiccmdDiff(net_packet_t *packet, net_ticdiff_t *diff, boolean lowres_turn) { // Header NET_WriteInt8(packet, diff->diff); // Write the fields which are enabled: if (diff->diff & NET_TICDIFF_FORWARD) NET_WriteInt8(packet, diff->cmd.forwardmove); if (diff->diff & NET_TICDIFF_SIDE) NET_WriteInt8(packet, diff->cmd.sidemove); if (diff->diff & NET_TICDIFF_TURN) { if (lowres_turn) { NET_WriteInt8(packet, diff->cmd.angleturn / 256); } else { NET_WriteInt16(packet, diff->cmd.angleturn); } } if (diff->diff & NET_TICDIFF_BUTTONS) NET_WriteInt8(packet, diff->cmd.buttons); if (diff->diff & NET_TICDIFF_CONSISTANCY) NET_WriteInt8(packet, diff->cmd.consistancy); if (diff->diff & NET_TICDIFF_CHATCHAR) NET_WriteInt8(packet, diff->cmd.chatchar); if (diff->diff & NET_TICDIFF_RAVEN) { NET_WriteInt8(packet, diff->cmd.lookfly); NET_WriteInt8(packet, diff->cmd.arti); } if (diff->diff & NET_TICDIFF_STRIFE) { NET_WriteInt8(packet, diff->cmd.buttons2); NET_WriteInt16(packet, diff->cmd.inventory); } } boolean NET_ReadTiccmdDiff(net_packet_t *packet, net_ticdiff_t *diff, boolean lowres_turn) { unsigned int val; signed int sval; // Read header if (!NET_ReadInt8(packet, &diff->diff)) return false; // Read fields if (diff->diff & NET_TICDIFF_FORWARD) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.forwardmove = sval; } if (diff->diff & NET_TICDIFF_SIDE) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.sidemove = sval; } if (diff->diff & NET_TICDIFF_TURN) { if (lowres_turn) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.angleturn = sval * 256; } else { if (!NET_ReadSInt16(packet, &sval)) return false; diff->cmd.angleturn = sval; } } if (diff->diff & NET_TICDIFF_BUTTONS) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.buttons = val; } if (diff->diff & NET_TICDIFF_CONSISTANCY) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.consistancy = val; } if (diff->diff & NET_TICDIFF_CHATCHAR) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.chatchar = val; } if (diff->diff & NET_TICDIFF_RAVEN) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.lookfly = val; if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.arti = val; } if (diff->diff & NET_TICDIFF_STRIFE) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.buttons2 = val; if (!NET_ReadInt16(packet, &val)) return false; diff->cmd.inventory = val; } return true; } void NET_TiccmdDiff(ticcmd_t *tic1, ticcmd_t *tic2, net_ticdiff_t *diff) { diff->diff = 0; diff->cmd = *tic2; if (tic1->forwardmove != tic2->forwardmove) diff->diff |= NET_TICDIFF_FORWARD; if (tic1->sidemove != tic2->sidemove) diff->diff |= NET_TICDIFF_SIDE; if (tic1->angleturn != tic2->angleturn) diff->diff |= NET_TICDIFF_TURN; if (tic1->buttons != tic2->buttons) diff->diff |= NET_TICDIFF_BUTTONS; if (tic1->consistancy != tic2->consistancy) diff->diff |= NET_TICDIFF_CONSISTANCY; if (tic2->chatchar != 0) diff->diff |= NET_TICDIFF_CHATCHAR; // Heretic/Hexen-specific if (tic1->lookfly != tic2->lookfly || tic2->arti != 0) diff->diff |= NET_TICDIFF_RAVEN; // Strife-specific if (tic1->buttons2 != tic2->buttons2 || tic2->inventory != 0) diff->diff |= NET_TICDIFF_STRIFE; } void NET_TiccmdPatch(ticcmd_t *src, net_ticdiff_t *diff, ticcmd_t *dest) { memmove(dest, src, sizeof(ticcmd_t)); // Apply the diff if (diff->diff & NET_TICDIFF_FORWARD) dest->forwardmove = diff->cmd.forwardmove; if (diff->diff & NET_TICDIFF_SIDE) dest->sidemove = diff->cmd.sidemove; if (diff->diff & NET_TICDIFF_TURN) dest->angleturn = diff->cmd.angleturn; if (diff->diff & NET_TICDIFF_BUTTONS) dest->buttons = diff->cmd.buttons; if (diff->diff & NET_TICDIFF_CONSISTANCY) dest->consistancy = diff->cmd.consistancy; if (diff->diff & NET_TICDIFF_CHATCHAR) dest->chatchar = diff->cmd.chatchar; else dest->chatchar = 0; // Heretic/Hexen specific: if (diff->diff & NET_TICDIFF_RAVEN) { dest->lookfly = diff->cmd.lookfly; dest->arti = diff->cmd.arti; } else { dest->arti = 0; } // Strife-specific: if (diff->diff & NET_TICDIFF_STRIFE) { dest->buttons2 = diff->cmd.buttons2; dest->inventory = diff->cmd.inventory; } else { dest->inventory = 0; } } // // net_full_ticcmd_t // boolean NET_ReadFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd, boolean lowres_turn) { unsigned int bitfield; int i; // Latency if (!NET_ReadSInt16(packet, &cmd->latency)) { return false; } // Regenerate playeringame from the "header" bitfield if (!NET_ReadInt8(packet, &bitfield)) { return false; } for (i=0; iplayeringame[i] = (bitfield & (1 << i)) != 0; } // Read cmds for (i=0; iplayeringame[i]) { if (!NET_ReadTiccmdDiff(packet, &cmd->cmds[i], lowres_turn)) { return false; } } } return true; } void NET_WriteFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd, boolean lowres_turn) { unsigned int bitfield; int i; // Write the latency NET_WriteInt16(packet, cmd->latency); // Write "header" byte indicating which players are active // in this ticcmd bitfield = 0; for (i=0; iplayeringame[i]) { bitfield |= 1 << i; } } NET_WriteInt8(packet, bitfield); // Write player ticcmds for (i=0; iplayeringame[i]) { NET_WriteTiccmdDiff(packet, &cmd->cmds[i], lowres_turn); } } } void NET_WriteWaitData(net_packet_t *packet, net_waitdata_t *data) { int i; NET_WriteInt8(packet, data->num_players); NET_WriteInt8(packet, data->num_drones); NET_WriteInt8(packet, data->ready_players); NET_WriteInt8(packet, data->max_players); NET_WriteInt8(packet, data->is_controller); NET_WriteInt8(packet, data->consoleplayer); for (i = 0; i < data->num_players && i < NET_MAXPLAYERS; ++i) { NET_WriteString(packet, data->player_names[i]); NET_WriteString(packet, data->player_addrs[i]); } NET_WriteSHA1Sum(packet, data->wad_sha1sum); NET_WriteSHA1Sum(packet, data->deh_sha1sum); NET_WriteInt8(packet, data->is_freedoom); } boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data) { int i; char *s; if (!NET_ReadInt8(packet, (unsigned int *) &data->num_players) || !NET_ReadInt8(packet, (unsigned int *) &data->num_drones) || !NET_ReadInt8(packet, (unsigned int *) &data->ready_players) || !NET_ReadInt8(packet, (unsigned int *) &data->max_players) || !NET_ReadInt8(packet, (unsigned int *) &data->is_controller) || !NET_ReadSInt8(packet, &data->consoleplayer)) { return false; } for (i = 0; i < data->num_players && i < NET_MAXPLAYERS; ++i) { s = NET_ReadString(packet); if (s == NULL || strlen(s) >= MAXPLAYERNAME) { return false; } M_StringCopy(data->player_names[i], s, MAXPLAYERNAME); s = NET_ReadString(packet); if (s == NULL || strlen(s) >= MAXPLAYERNAME) { return false; } M_StringCopy(data->player_addrs[i], s, MAXPLAYERNAME); } return NET_ReadSHA1Sum(packet, data->wad_sha1sum) && NET_ReadSHA1Sum(packet, data->deh_sha1sum) && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom); } static boolean NET_ReadBlob(net_packet_t *packet, uint8_t *buf, size_t len) { unsigned int b; int i; for (i=0; i CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@top_srcdir@/src/manifest.xml" 1 VERSIONINFO PRODUCTVERSION @WINDOWS_RC_VERSION@ FILEVERSION @WINDOWS_RC_VERSION@ FILETYPE 1 { BLOCK "StringFileInfo" { BLOCK "040904E4" { VALUE "FileVersion", "@PACKAGE_VERSION@.0" VALUE "FileDescription", "@PACKAGE_STRING@" VALUE "InternalName", "@PACKAGE_TARNAME@" VALUE "CompanyName", "@PACKAGE_BUGREPORT@" VALUE "LegalCopyright", "@PACKAGE_COPYRIGHT@. Licensed under @PACKAGE_LICENSE@" VALUE "ProductName", "@PACKAGE_NAME@" VALUE "ProductVersion", "@PACKAGE_VERSION@" } } BLOCK "VarFileInfo" { VALUE "Translation", 0x409, 1252 } } crispy-doom-crispy-doom-5.6.4/src/setup-res.rc.in000066400000000000000000000012261360717211000217060ustar00rootroot000000000000001 ICON "@top_srcdir@/data/setup.ico" 1 24 MOVEABLE PURE "@top_builddir@/src/setup/setup-manifest.xml" 1 VERSIONINFO PRODUCTVERSION @WINDOWS_RC_VERSION@ FILEVERSION @WINDOWS_RC_VERSION@ FILETYPE 1 { BLOCK "StringFileInfo" { BLOCK "040904E4" { VALUE "FileVersion", "@PACKAGE_VERSION@.0" VALUE "FileDescription", "@PACKAGE_STRING@ Setup" VALUE "InternalName", "@PACKAGE_TARNAME@" VALUE "CompanyName", "@PACKAGE_BUGREPORT@" VALUE "LegalCopyright", "GNU General Public License" VALUE "ProductName", "@PACKAGE_NAME@ Setup" VALUE "ProductVersion", "@PACKAGE_VERSION@" } } BLOCK "VarFileInfo" { VALUE "Translation", 0x409, 1252 } } crispy-doom-crispy-doom-5.6.4/src/setup/000077500000000000000000000000001360717211000201635ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/src/setup/.gitignore000066400000000000000000000000751360717211000221550ustar00rootroot00000000000000Makefile.in Makefile .deps setup-manifest.xml *.rc tags TAGS crispy-doom-crispy-doom-5.6.4/src/setup/CMakeLists.txt000066400000000000000000000014031360717211000227210ustar00rootroot00000000000000add_library(setup STATIC compatibility.c compatibility.h display.c display.h joystick.c joystick.h keyboard.c keyboard.h mainmenu.c mode.c mode.h mouse.c mouse.h multiplayer.c multiplayer.h sound.c sound.h execute.c execute.h txt_joyaxis.c txt_joyaxis.h txt_joybinput.c txt_joybinput.h txt_keyinput.c txt_keyinput.h txt_mouseinput.c txt_mouseinput.h) target_include_directories(setup PRIVATE "../" "${CMAKE_CURRENT_BINARY_DIR}/../../") target_link_libraries(setup textscreen SDL2::SDL2 SDL2::mixer) crispy-doom-crispy-doom-5.6.4/src/setup/Makefile.am000066400000000000000000000025671360717211000222310ustar00rootroot00000000000000 AM_CFLAGS = @SDL_CFLAGS@ \ @SDLMIXER_CFLAGS@ \ -I$(top_srcdir)/textscreen -I$(top_srcdir)/src noinst_LIBRARIES = libsetup.a libsetup_a_SOURCES = \ compatibility.c compatibility.h \ display.c display.h \ joystick.c joystick.h \ keyboard.c keyboard.h \ mainmenu.c \ mode.c mode.h \ mouse.c mouse.h \ multiplayer.c multiplayer.h \ sound.c sound.h \ execute.c execute.h \ txt_joyaxis.c txt_joyaxis.h \ txt_joybinput.c txt_joybinput.h \ txt_keyinput.c txt_keyinput.h \ txt_mouseinput.c txt_mouseinput.h EXTRA_DIST= \ CMakeLists.txt \ setup_icon.c appdir = $(prefix)/share/applications app_DATA = @PACKAGE_RDNS@.Setup.desktop CLEANFILES = $(app_DATA) @PACKAGE_RDNS@.Setup.desktop : Setup.desktop cp Setup.desktop $@ if HAVE_PYTHON setup_icon.c : $(top_builddir)/data/setup.png $(top_builddir)/data/convert-icon $(top_builddir)/data/setup.png $@ endif crispy-doom-crispy-doom-5.6.4/src/setup/Setup.desktop.in000066400000000000000000000003671360717211000232710ustar00rootroot00000000000000[Desktop Entry] Name=@PACKAGE_SHORTNAME@ Setup Exec=@PROGRAM_PREFIX@setup Icon=@PROGRAM_PREFIX@setup Type=Application Comment=Setup tool for @PACKAGE_SHORTNAME@ Categories=Settings; Keywords=first;person;shooter;doom;heretic;hexen;strife;vanilla; crispy-doom-crispy-doom-5.6.4/src/setup/compatibility.c000066400000000000000000000111711360717211000232010ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Sound control menu #include #include "crispy.h" #include "m_config.h" #include "textscreen.h" #include "mode.h" #include "compatibility.h" #define WINDOW_HELP_URL "https://www.chocolate-doom.org/setup-compat" int vanilla_savegame_limit = 0; int vanilla_demo_limit = 0; void CompatibilitySettings(TXT_UNCAST_ARG(widget), void *user_data) { txt_window_t *window; // [crispy] if (gamemission == doom) { TXT_MessageBox(NULL, "Please refer to the in-game Crispness menu."); } else { window = TXT_NewWindow("Compatibility"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); TXT_AddWidgets(window, TXT_NewCheckBox("Vanilla savegame limit", &vanilla_savegame_limit), TXT_NewCheckBox("Vanilla demo limit", &vanilla_demo_limit), NULL); } } void BindCompatibilityVariables(void) { // [crispy] if (gamemission == doom) { M_BindIntVariable("crispy_automapoverlay", &crispy->automapoverlay); M_BindIntVariable("crispy_automaprotate", &crispy->automaprotate); M_BindIntVariable("crispy_automapstats", &crispy->automapstats); M_BindIntVariable("crispy_bobfactor", &crispy->bobfactor); M_BindIntVariable("crispy_brightmaps", &crispy->brightmaps); M_BindIntVariable("crispy_centerweapon", &crispy->centerweapon); M_BindIntVariable("crispy_coloredblood", &crispy->coloredblood); M_BindIntVariable("crispy_coloredhud", &crispy->coloredhud); M_BindIntVariable("crispy_crosshair", &crispy->crosshair); M_BindIntVariable("crispy_crosshairhealth", &crispy->crosshairhealth); M_BindIntVariable("crispy_crosshairtarget", &crispy->crosshairtarget); M_BindIntVariable("crispy_crosshairtype", &crispy->crosshairtype); M_BindIntVariable("crispy_demobar", &crispy->demobar); M_BindIntVariable("crispy_demotimer", &crispy->demotimer); M_BindIntVariable("crispy_demotimerdir", &crispy->demotimerdir); M_BindIntVariable("crispy_extautomap", &crispy->extautomap); M_BindIntVariable("crispy_extsaveg", &crispy->extsaveg); M_BindIntVariable("crispy_flipcorpses", &crispy->flipcorpses); M_BindIntVariable("crispy_freeaim", &crispy->freeaim); M_BindIntVariable("crispy_freelook", &crispy->freelook); M_BindIntVariable("crispy_hires", &crispy->hires); M_BindIntVariable("crispy_jump", &crispy->jump); M_BindIntVariable("crispy_leveltime", &crispy->leveltime); M_BindIntVariable("crispy_mouselook", &crispy->mouselook); M_BindIntVariable("crispy_neghealth", &crispy->neghealth); M_BindIntVariable("crispy_overunder", &crispy->overunder); M_BindIntVariable("crispy_pitch", &crispy->pitch); M_BindIntVariable("crispy_playercoords", &crispy->playercoords); M_BindIntVariable("crispy_recoil", &crispy->recoil); M_BindIntVariable("crispy_secretmessage", &crispy->secretmessage); M_BindIntVariable("crispy_smoothlight", &crispy->smoothlight); M_BindIntVariable("crispy_smoothscaling", &crispy->smoothscaling); M_BindIntVariable("crispy_soundfix", &crispy->soundfix); M_BindIntVariable("crispy_soundfull", &crispy->soundfull); M_BindIntVariable("crispy_soundmono", &crispy->soundmono); M_BindIntVariable("crispy_translucency", &crispy->translucency); #ifdef CRISPY_TRUECOLOR M_BindIntVariable("crispy_truecolor", &crispy->truecolor); #endif M_BindIntVariable("crispy_uncapped", &crispy->uncapped); M_BindIntVariable("crispy_vsync", &crispy->vsync); M_BindIntVariable("crispy_weaponsquat", &crispy->weaponsquat); } else { M_BindIntVariable("vanilla_savegame_limit", &vanilla_savegame_limit); M_BindIntVariable("vanilla_demo_limit", &vanilla_demo_limit); } } crispy-doom-crispy-doom-5.6.4/src/setup/compatibility.h000066400000000000000000000014641360717211000232120ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_COMPATIBILITY_H #define SETUP_COMPATIBILITY_H void CompatibilitySettings(void *widget, void *user_data); void BindCompatibilityVariables(void); extern int vanilla_savegame_limit; extern int vanilla_demo_limit; #endif /* #ifndef SETUP_COMPATIBILITY_H */ crispy-doom-crispy-doom-5.6.4/src/setup/display.c000066400000000000000000000207521360717211000220020ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include "textscreen.h" #include "m_config.h" #include "m_misc.h" #include "mode.h" #include "display.h" #include "config.h" #define WINDOW_HELP_URL "https://www.chocolate-doom.org/setup-display" extern void RestartTextscreen(void); typedef struct { int w, h; } window_size_t; // List of aspect ratio-uncorrected window sizes: static window_size_t window_sizes_unscaled[] = { // { 320, 200 }, // hires { 640, 400 }, { 960, 600 }, { 1280, 800 }, { 1600, 1000 }, { 1920, 1200 }, // hires * 3 { 2560, 1600 }, // hires * 4 { 3200, 2000 }, // hires * 5 { 0, 0}, }; // List of aspect ratio-corrected window sizes: static window_size_t window_sizes_scaled[] = { // { 320, 240 }, // hires // { 512, 400 }, // hires { 640, 480 }, { 800, 600 }, { 960, 720 }, { 1024, 800 }, { 1280, 960 }, { 1600, 1200 }, { 1920, 1440 }, { 2560, 1920 }, // hires * 4 { 3200, 2400 }, // hires * 5 { 0, 0}, }; static char *video_driver = ""; static char *window_position = ""; static int aspect_ratio_correct = 1; static int integer_scaling = 0; static int vga_porch_flash = 0; static int force_software_renderer = 0; static int fullscreen = 1; static int fullscreen_width = 0, fullscreen_height = 0; static int window_width = 800, window_height = 600; static int startup_delay = 1000; static int max_scaling_buffer_pixels = 16000000; static int usegamma = 0; int graphical_startup = 0; // [crispy] int show_endoom = 0; // [crispy] int show_diskicon = 1; int png_screenshots = 1; // [crispy] static int system_video_env_set; // Set the SDL_VIDEODRIVER environment variable void SetDisplayDriver(void) { static int first_time = 1; if (first_time) { system_video_env_set = getenv("SDL_VIDEODRIVER") != NULL; first_time = 0; } // Don't override the command line environment, if it has been set. if (system_video_env_set) { return; } // Use the value from the configuration file, if it has been set. if (strcmp(video_driver, "") != 0) { char *env_string; env_string = M_StringJoin("SDL_VIDEODRIVER=", video_driver, NULL); putenv(env_string); free(env_string); } } static void WindowSizeSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(size)) { TXT_CAST_ARG(window_size_t, size); window_width = size->w; window_height = size->h; } static txt_radiobutton_t *SizeSelectButton(window_size_t *size) { char buf[15]; txt_radiobutton_t *result; M_snprintf(buf, sizeof(buf), "%ix%i", size->w, size->h); result = TXT_NewRadioButton(buf, &window_width, size->w); TXT_SignalConnect(result, "selected", WindowSizeSelected, size); return result; } static void GenerateSizesTable(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(sizes_table)) { TXT_CAST_ARG(txt_table_t, sizes_table); window_size_t *sizes; boolean have_size; int i; // Pick which window sizes list to use if (aspect_ratio_correct == 1) { sizes = window_sizes_scaled; } else { sizes = window_sizes_unscaled; } // Build the table TXT_ClearTable(sizes_table); TXT_SetColumnWidths(sizes_table, 14, 14, 14); TXT_AddWidget(sizes_table, TXT_NewSeparator("Window size")); have_size = false; for (i = 0; sizes[i].w != 0; ++i) { TXT_AddWidget(sizes_table, SizeSelectButton(&sizes[i])); have_size = have_size || window_width == sizes[i].w; } // Windows can be any arbitrary size. We key off the width of the // window in pixels. If the current size is not in the list of // standard (integer multiply) sizes, create a special button to // mean "the current window size". if (!have_size) { static window_size_t current_size; current_size.w = window_width; current_size.h = window_height; TXT_AddWidget(sizes_table, SizeSelectButton(¤t_size)); } } static void AdvancedDisplayConfig(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(sizes_table)) { TXT_CAST_ARG(txt_table_t, sizes_table); txt_window_t *window; txt_checkbox_t *ar_checkbox; window = TXT_NewWindow("Advanced display options"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); TXT_SetColumnWidths(window, 40); TXT_AddWidgets(window, ar_checkbox = TXT_NewCheckBox("Force correct aspect ratio", &aspect_ratio_correct), TXT_If(gamemission == heretic || gamemission == hexen || gamemission == strife, TXT_NewCheckBox("Graphical startup", &graphical_startup)), TXT_If(gamemission == doom || gamemission == heretic || gamemission == strife, TXT_NewCheckBox("Show ENDOOM screen on exit", &show_endoom)), #ifdef HAVE_LIBPNG TXT_NewCheckBox("Save screenshots in PNG format", &png_screenshots), #endif NULL); TXT_SignalConnect(ar_checkbox, "changed", GenerateSizesTable, sizes_table); } void ConfigDisplay(TXT_UNCAST_ARG(widget), void *user_data) { txt_window_t *window; txt_table_t *sizes_table; txt_window_action_t *advanced_button; // Open the window window = TXT_NewWindow("Display Configuration"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); // Build window: TXT_AddWidgets(window, TXT_NewCheckBox("Full screen", &fullscreen), TXT_NewConditional(&fullscreen, 0, sizes_table = TXT_NewTable(3)), NULL); TXT_SetColumnWidths(window, 42); // The window is set at a fixed vertical position. This keeps // the top of the window stationary when switching between // fullscreen and windowed mode (which causes the window's // height to change). TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, TXT_SCREEN_W / 2, 6); GenerateSizesTable(NULL, sizes_table); // Button to open "advanced" window. // Need to pass a pointer to the window sizes table, as some of the options // in there trigger a rebuild of it. advanced_button = TXT_NewWindowAction('a', "Advanced"); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, advanced_button); TXT_SignalConnect(advanced_button, "pressed", AdvancedDisplayConfig, sizes_table); } void BindDisplayVariables(void) { M_BindIntVariable("aspect_ratio_correct", &aspect_ratio_correct); M_BindIntVariable("integer_scaling", &integer_scaling); M_BindIntVariable("fullscreen", &fullscreen); M_BindIntVariable("fullscreen_width", &fullscreen_width); M_BindIntVariable("fullscreen_height", &fullscreen_height); M_BindIntVariable("window_width", &window_width); M_BindIntVariable("window_height", &window_height); M_BindIntVariable("startup_delay", &startup_delay); M_BindStringVariable("video_driver", &video_driver); M_BindStringVariable("window_position", &window_position); M_BindIntVariable("usegamma", &usegamma); M_BindIntVariable("png_screenshots", &png_screenshots); M_BindIntVariable("vga_porch_flash", &vga_porch_flash); M_BindIntVariable("force_software_renderer", &force_software_renderer); M_BindIntVariable("max_scaling_buffer_pixels", &max_scaling_buffer_pixels); if (gamemission == doom || gamemission == heretic || gamemission == strife) { M_BindIntVariable("show_endoom", &show_endoom); } if (gamemission == doom || gamemission == strife) { M_BindIntVariable("show_diskicon", &show_diskicon); } if (gamemission == heretic || gamemission == hexen || gamemission == strife) { M_BindIntVariable("graphical_startup", &graphical_startup); } } crispy-doom-crispy-doom-5.6.4/src/setup/display.h000066400000000000000000000015021360717211000217770ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_DISPLAY_H #define SETUP_DISPLAY_H void ConfigDisplay(void *widget, void *user_data); void SetDisplayDriver(void); void BindDisplayVariables(void); extern int show_endoom; extern int graphical_startup; extern int png_screenshots; #endif /* #ifndef SETUP_DISPLAY_H */ crispy-doom-crispy-doom-5.6.4/src/setup/execute.c000066400000000000000000000215771360717211000220050ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Code for invoking Doom #include #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #include #else #include #include #endif #include "textscreen.h" #include "config.h" #include "execute.h" #include "mode.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" struct execute_context_s { char *response_file; FILE *stream; }; // Returns the path to a temporary file of the given name, stored // inside the system temporary directory. static char *TempFile(const char *s) { const char *tempdir; #ifdef _WIN32 // Check the TEMP environment variable to find the location. tempdir = getenv("TEMP"); if (tempdir == NULL) { tempdir = "."; } #else // In Unix, just use /tmp. tempdir = "/tmp"; #endif return M_StringJoin(tempdir, DIR_SEPARATOR_S, s, NULL); } static int ArgumentNeedsEscape(char *arg) { char *p; for (p = arg; *p != '\0'; ++p) { if (isspace(*p)) { return 1; } } return 0; } // Arguments passed to the setup tool should be passed through to the // game when launching a game. Calling this adds all arguments from // myargv to the output context. void PassThroughArguments(execute_context_t *context) { int i; for (i = 1; i < myargc; ++i) { if (ArgumentNeedsEscape(myargv[i])) { AddCmdLineParameter(context, "\"%s\"", myargv[i]); } else { AddCmdLineParameter(context, "%s", myargv[i]); } } } execute_context_t *NewExecuteContext(void) { execute_context_t *result; result = malloc(sizeof(execute_context_t)); result->response_file = TempFile("chocolat.rsp"); result->stream = fopen(result->response_file, "w"); if (result->stream == NULL) { fprintf(stderr, "Error opening response file\n"); exit(-1); } return result; } void AddCmdLineParameter(execute_context_t *context, const char *s, ...) { va_list args; va_start(args, s); vfprintf(context->stream, s, args); fprintf(context->stream, "\n"); va_end(args); } #if defined(_WIN32) boolean OpenFolder(const char *path) { // "If the function succeeds, it returns a value greater than 32." return (int)ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWDEFAULT) > 32; } // Wait for the specified process to exit. Returns the exit code. static unsigned int WaitForProcessExit(HANDLE subprocess) { DWORD exit_code; for (;;) { WaitForSingleObject(subprocess, INFINITE); if (!GetExitCodeProcess(subprocess, &exit_code)) { return -1; } if (exit_code != STILL_ACTIVE) { return exit_code; } } } static void ConcatWCString(wchar_t *buf, const char *value) { MultiByteToWideChar(CP_OEMCP, 0, value, strlen(value) + 1, buf + wcslen(buf), strlen(value) + 1); } // Build the command line string, a wide character string of the form: // // "program" "arg" static wchar_t *BuildCommandLine(const char *program, const char *arg) { wchar_t exe_path[MAX_PATH]; wchar_t *result; wchar_t *sep; // Get the path to this .exe file. GetModuleFileNameW(NULL, exe_path, MAX_PATH); // Allocate buffer to contain result string. result = calloc(wcslen(exe_path) + strlen(program) + strlen(arg) + 6, sizeof(wchar_t)); wcscpy(result, L"\""); // Copy the path part of the filename (including ending \) // into the result buffer: sep = wcsrchr(exe_path, DIR_SEPARATOR); if (sep != NULL) { wcsncpy(result + 1, exe_path, sep - exe_path + 1); result[sep - exe_path + 2] = '\0'; } // Concatenate the name of the program: ConcatWCString(result, program); // End of program name, start of argument: wcscat(result, L"\" \""); ConcatWCString(result, arg); wcscat(result, L"\""); return result; } static int ExecuteCommand(const char *program, const char *arg) { STARTUPINFOW startup_info; PROCESS_INFORMATION proc_info; wchar_t *command; int result = 0; command = BuildCommandLine(program, arg); // Invoke the program: memset(&proc_info, 0, sizeof(proc_info)); memset(&startup_info, 0, sizeof(startup_info)); startup_info.cb = sizeof(startup_info); if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &startup_info, &proc_info)) { result = -1; } else { // Wait for the process to finish, and save the exit code. result = WaitForProcessExit(proc_info.hProcess); CloseHandle(proc_info.hProcess); CloseHandle(proc_info.hThread); } free(command); return result; } #else boolean OpenFolder(const char *path) { char *cmd; int result; #if defined(__MACOSX__) cmd = M_StringJoin("open \"", path, "\"", NULL); #else cmd = M_StringJoin("xdg-open \"", path, "\"", NULL); #endif result = system(cmd); free(cmd); return result == 0; } // Given the specified program name, get the full path to the program, // assuming that it is in the same directory as this program is. static char *GetFullExePath(const char *program) { char *result; char *sep; size_t result_len; unsigned int path_len; sep = strrchr(myargv[0], DIR_SEPARATOR); if (sep == NULL) { result = M_StringDuplicate(program); } else { path_len = sep - myargv[0] + 1; result_len = strlen(program) + path_len + 1; result = malloc(result_len); M_StringCopy(result, myargv[0], result_len); result[path_len] = '\0'; M_StringConcat(result, program, result_len); } return result; } static int ExecuteCommand(const char *program, const char *arg) { pid_t childpid; int result; const char *argv[3]; childpid = fork(); if (childpid == 0) { // This is the child. Execute the command. argv[0] = GetFullExePath(program); argv[1] = arg; argv[2] = NULL; execvp(argv[0], (char **) argv); exit(0x80); } else { // This is the parent. Wait for the child to finish, and return // the status code. waitpid(childpid, &result, 0); if (WIFEXITED(result) && WEXITSTATUS(result) != 0x80) { return WEXITSTATUS(result); } else { return -1; } } } #endif int ExecuteDoom(execute_context_t *context) { char *response_file_arg; int result; fclose(context->stream); // Build the command line response_file_arg = M_StringJoin("@", context->response_file, NULL); // Run Doom result = ExecuteCommand(GetExecutableName(), response_file_arg); free(response_file_arg); // Destroy context remove(context->response_file); free(context->response_file); free(context); return result; } static void TestCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data)) { execute_context_t *exec; char *main_cfg; char *extra_cfg; txt_window_t *testwindow; testwindow = TXT_MessageBox("Starting Doom", "Starting Doom to test the\n" "settings. Please wait."); TXT_DrawDesktop(); // Save temporary configuration files with the current configuration main_cfg = TempFile("tmp.cfg"); extra_cfg = TempFile("extratmp.cfg"); M_SaveDefaultsAlternate(main_cfg, extra_cfg); // Run with the -testcontrols parameter exec = NewExecuteContext(); AddCmdLineParameter(exec, "-testcontrols"); AddCmdLineParameter(exec, "-config \"%s\"", main_cfg); AddCmdLineParameter(exec, "-extraconfig \"%s\"", extra_cfg); ExecuteDoom(exec); TXT_CloseWindow(testwindow); // Delete the temporary config files remove(main_cfg); remove(extra_cfg); free(main_cfg); free(extra_cfg); } txt_window_action_t *TestConfigAction(void) { txt_window_action_t *test_action; test_action = TXT_NewWindowAction('t', "Test"); TXT_SignalConnect(test_action, "pressed", TestCallback, NULL); return test_action; } crispy-doom-crispy-doom-5.6.4/src/setup/execute.h000066400000000000000000000025651360717211000220060ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 TESTCONFIG_H #define TESTCONFIG_H #include "doomtype.h" #include "textscreen.h" typedef struct execute_context_s execute_context_t; #define IWAD_DOOM2 (1 << 0) /* doom2.wad */ #define IWAD_PLUTONIA (1 << 1) /* plutonia.wad */ #define IWAD_TNT (1 << 2) /* tnt.wad */ #define IWAD_DOOM (1 << 3) /* doom.wad */ #define IWAD_DOOM1 (1 << 4) /* doom1.wad */ #define IWAD_CHEX (1 << 5) /* chex.wad */ execute_context_t *NewExecuteContext(void); void AddCmdLineParameter(execute_context_t *context, const char *s, ...) PRINTF_ATTR(2, 3); void PassThroughArguments(execute_context_t *context); int ExecuteDoom(execute_context_t *context); int FindInstalledIWADs(void); boolean OpenFolder(const char *path); txt_window_action_t *TestConfigAction(void); #endif /* #ifndef TESTCONFIG_H */ crispy-doom-crispy-doom-5.6.4/src/setup/joystick.c000066400000000000000000001006241360717211000221710ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include "doomtype.h" #include "i_joystick.h" #include "m_config.h" #include "m_controls.h" #include "m_misc.h" #include "textscreen.h" #include "execute.h" #include "joystick.h" #include "mode.h" #include "txt_joyaxis.h" #include "txt_joybinput.h" #define WINDOW_HELP_URL "https://www.chocolate-doom.org/setup-gamepad" typedef struct { const char *name; // Config file name int value; } joystick_config_t; typedef struct { const char *name; int axes, buttons, hats; const joystick_config_t *configs; } known_joystick_t; // SDL joystick successfully initialized? static int joystick_initted = 0; // Joystick enable/disable static int usejoystick = 0; // GUID and index of joystick to use. char *joystick_guid = ""; int joystick_index = -1; // Calibration button. This is the button the user pressed at the // start of the calibration sequence. They *must* press this button // for each subsequent sequence. static int calibrate_button = -1; // Which joystick axis to use for horizontal movement, and whether to // invert the direction: static int joystick_x_axis = 0; static int joystick_x_invert = 0; // Which joystick axis to use for vertical movement, and whether to // invert the direction: static int joystick_y_axis = 1; static int joystick_y_invert = 0; // Strafe axis. static int joystick_strafe_axis = -1; static int joystick_strafe_invert = 0; // Look axis. static int joystick_look_axis = -1; static int joystick_look_invert = 0; // Virtual to physical mapping. int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; static txt_button_t *joystick_button; static txt_joystick_axis_t *x_axis_widget; static txt_joystick_axis_t *y_axis_widget; // // Calibration // static txt_window_t *calibration_window; static SDL_Joystick **all_joysticks = NULL; static int all_joysticks_len = 0; // Known controllers. // There are lots of game controllers on the market. Try to configure // them in a consistent way: // // * Use the D-pad rather than an analog stick. left/right turns the // player, up/down moves forward/backward - ie. a "traditional" // layout like Vanilla Doom rather than something more elaborate. // * No strafe axis. // * Fire and run keys together, on the main right-side buttons, // ideally arranged so both can be controlled/covered simultaneously // with the thumb. // * Jump/use keys in the same cluster if possible. // * Strafe left/right configured to map to shoulder buttons if they // are present. No "strafe on" key unless shoulder buttons not present. // * If a second set of shoulder buttons are also present, these map // to prev weapon/next weapon. // * Menu button mapped to start button. // // With the common right-side button arrangement that looks like this, // which is similar to the Vanilla default configuration when using // a Gravis Gamepad: // // B A = Fire // A D B = Jump // C C = Speed // D = Use // Always loaded before others, to get a known starting configuration. static const joystick_config_t empty_defaults[] = { {"joystick_x_axis", -1}, {"joystick_x_invert", 0}, {"joystick_y_axis", -1}, {"joystick_y_invert", 0}, {"joystick_strafe_axis", -1}, {"joystick_strafe_invert", 0}, {"joystick_look_axis", -1}, {"joystick_look_invert", 0}, {"joyb_fire", -1}, {"joyb_use", -1}, {"joyb_strafe", -1}, {"joyb_speed", -1}, {"joyb_strafeleft", -1}, {"joyb_straferight", -1}, {"joyb_prevweapon", -1}, {"joyb_nextweapon", -1}, {"joyb_jump", -1}, {"joyb_menu_activate", -1}, {"joyb_toggle_automap", -1}, {"joystick_physical_button0", 0}, {"joystick_physical_button1", 1}, {"joystick_physical_button2", 2}, {"joystick_physical_button3", 3}, {"joystick_physical_button4", 4}, {"joystick_physical_button5", 5}, {"joystick_physical_button6", 6}, {"joystick_physical_button7", 7}, {"joystick_physical_button8", 8}, {"joystick_physical_button9", 9}, {NULL, 0}, }; static const joystick_config_t ps3_controller[] = { {"joystick_x_axis", CREATE_BUTTON_AXIS(7, 5)}, {"joystick_y_axis", CREATE_BUTTON_AXIS(4, 6)}, {"joyb_fire", 15}, // Square {"joyb_speed", 14}, // X {"joyb_use", 13}, // Circle {"joyb_jump", 12}, // Triangle {"joyb_strafeleft", 8}, // Bottom shoulder buttons {"joyb_straferight", 9}, {"joyb_prevweapon", 10}, // Top shoulder buttons {"joyb_nextweapon", 11}, {"joyb_menu_activate", 3}, // Start {NULL, 0}, }; // Playstation 4 Dual Shock 4 (DS4) static const joystick_config_t ps4_ds4_controller[] = { {"joystick_x_axis", CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)}, {"joystick_y_axis", CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)}, {"joyb_fire", 0}, // Square {"joyb_speed", 1}, // X {"joyb_use", 2}, // Circle {"joyb_jump", 3}, // Triangle {"joyb_strafeleft", 6}, // Bottom shoulder buttons {"joyb_straferight", 7}, {"joyb_prevweapon", 4}, // Top shoulder buttons {"joyb_nextweapon", 5}, {"joyb_menu_activate", 12}, // Playstation logo button }; static const joystick_config_t airflo_controller[] = { {"joystick_x_axis", CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)}, {"joystick_y_axis", CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)}, {"joyb_fire", 2}, // "3" {"joyb_speed", 0}, // "1" {"joyb_jump", 3}, // "4" {"joyb_use", 1}, // "2" {"joyb_strafeleft", 6}, // Bottom shoulder buttons {"joyb_straferight", 7}, {"joyb_prevweapon", 4}, // Top shoulder buttons {"joyb_nextweapon", 5}, {"joyb_menu_activate", 9}, // "10", where "Start" usually is. {NULL, 0}, }; // Wii controller is weird, so we have to take some liberties. // Also it's not a HID device, so it won't appear the same everywhere. // Assume there is no nunchuk or classic controller attached. // When using WJoy on OS X. static const joystick_config_t wii_controller_wjoy[] = { {"joystick_x_axis", CREATE_BUTTON_AXIS(2, 3)}, {"joystick_y_axis", CREATE_BUTTON_AXIS(1, 0)}, {"joyb_fire", 9}, // Button 1 {"joyb_speed", 10}, // Button 2 {"joyb_use", 5}, // Button B (trigger) {"joyb_prevweapon", 7}, // - {"joyb_nextweapon", 6}, // + {"joyb_menu_activate", 9}, // Button A {NULL, 0}, }; // Xbox 360 controller. Thanks to Brad Harding for the details. static const joystick_config_t xbox360_controller[] = { {"joystick_x_axis", CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)}, {"joystick_y_axis", CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)}, {"joystick_strafe_axis", 2}, // Trigger buttons form an axis(???) {"joyb_fire", 2}, // X {"joyb_speed", 0}, // A {"joyb_jump", 3}, // Y {"joyb_use", 1}, // B {"joyb_prevweapon", 4}, // LB {"joyb_nextweapon", 5}, // RB {"joyb_menu_activate", 9}, // Start {NULL, 0}, }; // Xbox 360 controller under Linux. static const joystick_config_t xbox360_controller_linux[] = { {"joystick_x_axis", CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)}, {"joystick_y_axis", CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)}, // Ideally we'd like the trigger buttons to be strafe left/right // But Linux presents each trigger button as its own axis, which // we can't really work with. So we have to settle for a // suboptimal setup. {"joyb_fire", 2}, // X {"joyb_speed", 0}, // A {"joyb_jump", 3}, // Y {"joyb_use", 1}, // B {"joyb_strafeleft", 4}, // LB {"joyb_straferight", 5}, // RB {"joyb_menu_activate", 7}, // Start {"joyb_prevweapon", 6}, // Back {NULL, 0}, }; // Logitech Dual Action (F310, F710). Thanks to Brad Harding for details. static const joystick_config_t logitech_f310_controller[] = { {"joystick_x_axis", CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)}, {"joystick_y_axis", CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)}, {"joyb_fire", 0}, // X {"joyb_speed", 1}, // A {"joyb_jump", 3}, // Y {"joyb_use", 2}, // B {"joyb_strafeleft", 6}, // LT {"joyb_straferight", 7}, // RT {"joyb_prevweapon", 4}, // LB {"joyb_nextweapon", 5}, // RB {"joyb_menu_activate", 11}, // Start {NULL, 0}, }; // Multilaser JS030 gamepad, similar to a PS2 controller. static const joystick_config_t multilaser_js030_controller[] = { {"joystick_x_axis", 0}, // Left stick / D-pad {"joystick_y_axis", 1}, {"joyb_fire", 3}, // Square {"joyb_speed", 2}, // X {"joyb_use", 1}, // Circle {"joyb_jump", 0}, // Triangle {"joyb_strafeleft", 6}, // Bottom shoulder buttons {"joyb_straferight", 7}, {"joyb_prevweapon", 4}, // Top shoulder buttons {"joyb_nextweapon", 5}, {"joyb_menu_activate", 9}, // Start {NULL, 0}, }; // Buffalo Classic USB Gamepad (thanks Fabian Greffrath). static const joystick_config_t buffalo_classic_controller[] = { {"joystick_x_axis", 0}, {"joystick_y_axis", 1}, {"joyb_use", 0}, // A {"joyb_speed", 1}, // B {"joyb_jump", 2}, // X {"joyb_fire", 3}, // Y {"joyb_strafeleft", 4}, // Left shoulder {"joyb_straferight", 5}, // Right shoulder {"joyb_prevweapon", 6}, // Select {"joyb_menu_activate", 7}, // Start {NULL, 0}, }; // Config for if the user is actually using an old PC joystick or gamepad, // probably via a USB-Gameport adapter. static const joystick_config_t pc_gameport_controller[] = { {"joystick_x_axis", 0}, {"joystick_y_axis", 1}, // Button configuration is the default as used for Vanilla Doom, // Heretic and Hexen. When playing with a Gravis Gamepad, this // layout should also be vaguely similar to the standard layout // described above. {"joyb_fire", 0}, {"joyb_strafe", 1}, {"joyb_use", 3}, {"joyb_speed", 2}, {NULL, 0}, }; // http://www.8bitdo.com/nes30pro/ and http://www.8bitdo.com/fc30pro/ static const joystick_config_t nes30_pro_controller[] = { {"joystick_x_axis", CREATE_HAT_AXIS(0, HAT_AXIS_HORIZONTAL)}, {"joystick_y_axis", CREATE_HAT_AXIS(0, HAT_AXIS_VERTICAL)}, {"joyb_fire", 4}, // Y {"joyb_speed", 1}, // B {"joyb_jump", 3}, // X {"joyb_use", 0}, // A {"joyb_strafeleft", 6}, // L1 {"joyb_straferight", 7}, // R1 {"joyb_prevweapon", 8}, // L2 {"joyb_nextweapon", 9}, // R2 {"joyb_menu_activate", 11}, // Start {"joyb_toggle_automap", 10}, // Select {NULL, 0}, }; // http://www.8bitdo.com/sfc30/ or http://www.8bitdo.com/snes30/ // and http://www.nes30.com/ and http://www.fc30.com/ static const joystick_config_t sfc30_controller[] = { {"joystick_x_axis", 0}, {"joystick_y_axis", 1}, {"joyb_fire", 4}, // Y {"joyb_speed", 1}, // B {"joyb_jump", 3}, // X {"joyb_use", 0}, // A {"joyb_strafeleft", 6}, // L {"joyb_straferight", 7}, // R {"joyb_menu_activate", 11}, // Start {"joyb_toggle_automap", 10}, // Select {NULL, 0}, }; static const known_joystick_t known_joysticks[] = { { "PLAYSTATION(R)3 Controller", 4, 19, 0, ps3_controller, }, { "AIRFLO ", 4, 13, 1, airflo_controller, }, { "Wiimote (*", // WJoy includes the Wiimote MAC address. 6, 26, 0, wii_controller_wjoy, }, { "Controller (XBOX 360 For Windows)", 5, 10, 1, xbox360_controller, }, { "Controller (XBOX One For Windows)", 5, 10, 1, xbox360_controller, }, // Xbox 360 controller as it appears on Linux. { "Microsoft X-Box 360 pad", 6, 11, 1, xbox360_controller_linux, }, // Xbox One controller as it appears on Linux. { "Microsoft X-Box One pad", 6, 11, 1, xbox360_controller_linux, }, { "Logitech Dual Action", 4, 12, 1, logitech_f310_controller, }, { "USB Vibration Joystick", 4, 12, 1, multilaser_js030_controller, }, { "USB,2-axis 8-button gamepad ", 2, 8, 0, buffalo_classic_controller, }, // PS4 controller just appears as the generic-sounding "Wireless // Controller". Hopefully the number of buttons/axes/hats should be // enough to distinguish it from other gamepads. { "Wireless Controller", 6, 14, 1, ps4_ds4_controller, }, // This is the configuration for the USB-Gameport adapter listed on // this page as the "Mayflash USB to Gameport Adapter" (though the // device is labeled as "Super Joy Box 7"): // https://sites.google.com/site/joystickrehab/itemcatal // TODO: Add extra configurations here for other USB-Gameport adapters, // which should just be the same configuration. { "WiseGroup.,Ltd Gameport to USB Controller", 4, 8, 1, pc_gameport_controller, }, // How the Super Joy Box 7 appears on Mac OS X. { "Gameport to USB Controller", 2, 8, 1, pc_gameport_controller, }, // 8Bitdo NES30 Pro, http://www.8bitdo.com/nes30pro/ { "8Bitdo NES30 Pro", 4, 16, 1, nes30_pro_controller, }, // the above, NES variant, via USB on Linux/Raspbian (odd values) { "8Bitdo NES30 Pro*", 6, 15, 1, nes30_pro_controller, }, // the above, NES variant, connected over bluetooth { "8Bitdo NES30 Pro", 6, 16, 1, nes30_pro_controller, }, // 8bitdo NES30 Pro, in joystick mode (R1+Power), swaps the D-Pad // and analog stick inputs. Only applicable over Bluetooth. On USB, // this mode registers the device as an Xbox 360 pad. { "8Bitdo NES30 Pro Joystick", 6, 16, 1, nes30_pro_controller, }, // variant of the above, via USB on Mac // Note: untested, but theorized to exist based on us comparing // a NES30 Pro tested on Linux with a FC30 Pro tested with Mac & Linux { "8Bitdo NES30 Pro", 4, 15, 1, nes30_pro_controller, }, // 8Bitdo FC30 Pro, http://8bitdo.cn/fc30pro/ // connected over bluetooth { "8Bitdo FC30 Pro", 4, 16, 1, nes30_pro_controller, }, // variant of the above, via USB on Linux/Raspbian { "8Bitdo FC30 Pro*", 6, 15, 1, nes30_pro_controller, }, // variant of the above, Linux/bluetooth { "8Bitdo FC30 Pro", 6, 16, 1, nes30_pro_controller, }, // variant of the above, via USB on Mac { "8Bitdo FC30 Pro", 4, 15, 1, nes30_pro_controller, }, // 8Bitdo SFC30 SNES replica controller // in default mode and in controller mode (Start+R) // the latter suffixes "Joystick" to the name // http://www.8bitdo.com/sfc30/ { "8Bitdo SFC30 GamePad*", 4, 16, 1, sfc30_controller, }, // As above, but as detected on RHEL Linux (odd extra axes) { "8Bitdo SFC30 GamePad*", 6, 16, 1, sfc30_controller, }, // SNES30 colour variation of the above // http://www.8bitdo.com/snes30/ { "8Bitdo SNES30 GamePad*", 4, 16, 1, sfc30_controller, }, // 8Bitdo SFC30 SNES replica controller in USB controller mode // tested with firmware V2.68 (Beta); latest stable V2.65 doesn't work on // OS X in USB controller mode // Names seen so far: // 'SFC30 Joystick' (OS X) // 'SFC30 SFC30 Joystick' (Fedora 24; RHEL7) // XXX: there is probably a SNES30 variant of this too { "SFC30 *", 4, 12, 1, sfc30_controller, }, // NES30 (not pro), tested in default and "hold R whilst turning on" // mode, with whatever firmware it came with out of the box. Latter // mode puts " Joystick" suffix on the name string { "8Bitdo NES30 GamePad*", 4, 16, 1, sfc30_controller, // identical to SFC30 }, // FC30 variant of the above { "8Bitdo FC30 GamePad*", 4, 16, 1, sfc30_controller, // identical to SFC30 }, // NES30 in USB mode { "NES30*", 4, 12, 1, sfc30_controller, // identical to SFC30 }, // FC30 variant of the above { "FC30*", 4, 12, 1, sfc30_controller, // identical to SFC30 }, }; static const known_joystick_t *GetJoystickType(int index) { SDL_Joystick *joystick; const char *name; int axes, buttons, hats; int i; joystick = all_joysticks[index]; name = SDL_JoystickName(joystick); axes = SDL_JoystickNumAxes(joystick); buttons = SDL_JoystickNumButtons(joystick); hats = SDL_JoystickNumHats(joystick); for (i = 0; i < arrlen(known_joysticks); ++i) { // Check for a name match. If the name ends in '*', this means // ignore the rest. if (M_StringEndsWith(known_joysticks[i].name, "*")) { if (strncmp(known_joysticks[i].name, name, strlen(known_joysticks[i].name) - 1) != 0) { continue; } } else { if (strcmp(known_joysticks[i].name, name) != 0) { continue; } } if (known_joysticks[i].axes == axes && known_joysticks[i].buttons == buttons && known_joysticks[i].hats == hats) { return &known_joysticks[i]; } } printf("Unknown joystick '%s' with %i axes, %i buttons, %i hats\n", name, axes, buttons, hats); printf("Please consider sending in details about your gamepad!\n"); return NULL; } // Query if the joystick at the given index is a known joystick type. static boolean IsKnownJoystick(int index) { return GetJoystickType(index) != NULL; } // Load a configuration set. static void LoadConfigurationSet(const joystick_config_t *configs) { const joystick_config_t *config; char buf[10]; int button; int i; button = 0; for (i = 0; configs[i].name != NULL; ++i) { config = &configs[i]; // Don't overwrite autorun if it is set. if (!strcmp(config->name, "joyb_speed") && joybspeed >= 20) { continue; } // For buttons, set the virtual button mapping as well. if (M_StringStartsWith(config->name, "joyb_") && config->value >= 0) { joystick_physical_buttons[button] = config->value; M_snprintf(buf, sizeof(buf), "%i", button); M_SetVariable(config->name, buf); ++button; } else { M_snprintf(buf, sizeof(buf), "%i", config->value); M_SetVariable(config->name, buf); } } } // Load configuration for joystick_index based on known types. static void LoadKnownConfiguration(void) { const known_joystick_t *jstype; jstype = GetJoystickType(joystick_index); if (jstype == NULL) { return; } LoadConfigurationSet(empty_defaults); LoadConfigurationSet(jstype->configs); } static void InitJoystick(void) { if (!joystick_initted) { joystick_initted = SDL_Init(SDL_INIT_JOYSTICK) >= 0; } } static void UnInitJoystick(void) { if (joystick_initted) { SDL_QuitSubSystem(SDL_INIT_JOYSTICK); joystick_initted = 0; } } // We identify joysticks using GUID where possible, but joystick_index // is used to distinguish between different devices. As the index can // change, UpdateJoystickIndex() checks to see if it is still valid and // updates it as appropriate. static void UpdateJoystickIndex(void) { SDL_JoystickGUID guid, dev_guid; int i; guid = SDL_JoystickGetGUIDFromString(joystick_guid); // Is joystick_index already correct? if (joystick_index >= 0 && joystick_index < SDL_NumJoysticks()) { dev_guid = SDL_JoystickGetDeviceGUID(joystick_index); if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID))) { return; } } // If index is not correct, look for the first device with the // expected GUID. It may have moved to a different index. for (i = 0; i < SDL_NumJoysticks(); ++i) { dev_guid = SDL_JoystickGetDeviceGUID(i); if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID))) { joystick_index = i; return; } } // Not found; it's possible the device is disconnected. Do not // reset joystick_guid or joystick_index in case they are // reconnected later. } // Set the label showing the name of the currently selected joystick static void SetJoystickButtonLabel(void) { SDL_JoystickGUID guid, dev_guid; const char *name; if (!usejoystick || !strcmp(joystick_guid, "")) { name = "None set"; } else { name = "Not found (device disconnected?)"; // Use the device name if the GUID and index match. if (joystick_index >= 0 && joystick_index < SDL_NumJoysticks()) { guid = SDL_JoystickGetGUIDFromString(joystick_guid); dev_guid = SDL_JoystickGetDeviceGUID(joystick_index); if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID))) { name = SDL_JoystickNameForIndex(joystick_index); } } } TXT_SetButtonLabel(joystick_button, (char *) name); } // Try to open all joysticks visible to SDL. static int OpenAllJoysticks(void) { int i; int result; InitJoystick(); // SDL_JoystickOpen() all joysticks. all_joysticks_len = SDL_NumJoysticks(); all_joysticks = calloc(all_joysticks_len, sizeof(SDL_Joystick *)); result = 0; for (i = 0; i < all_joysticks_len; ++i) { all_joysticks[i] = SDL_JoystickOpen(i); // If any joystick is successfully opened, return true. if (all_joysticks[i] != NULL) { result = 1; } } // Success? Turn on joystick events. if (result) { SDL_JoystickEventState(SDL_ENABLE); } else { free(all_joysticks); all_joysticks = NULL; } return result; } // Close all the joysticks opened with OpenAllJoysticks() static void CloseAllJoysticks(void) { int i; for (i = 0; i < all_joysticks_len; ++i) { if (all_joysticks[i] != NULL) { SDL_JoystickClose(all_joysticks[i]); } } SDL_JoystickEventState(SDL_DISABLE); free(all_joysticks); all_joysticks = NULL; UnInitJoystick(); } static void CalibrateXAxis(void) { TXT_ConfigureJoystickAxis(x_axis_widget, calibrate_button, NULL); } // Given the SDL_JoystickID instance ID from a button event, set the // joystick_guid and joystick_index config variables. static boolean SetJoystickGUID(SDL_JoystickID joy_id) { SDL_JoystickGUID guid; int i; for (i = 0; i < all_joysticks_len; ++i) { if (SDL_JoystickInstanceID(all_joysticks[i]) == joy_id) { guid = SDL_JoystickGetGUID(all_joysticks[i]); joystick_guid = malloc(33); SDL_JoystickGetGUIDString(guid, joystick_guid, 33); joystick_index = i; return true; } } return false; } static int CalibrationEventCallback(SDL_Event *event, void *user_data) { if (event->type != SDL_JOYBUTTONDOWN) { return 0; } if (!SetJoystickGUID(event->jbutton.which)) { return 0; } // At this point, we have a button press. // In the first "center" stage, we're just trying to work out which // joystick is being configured and which button the user is pressing. usejoystick = 1; calibrate_button = event->jbutton.button; // If the joystick is a known one, auto-load default // config for it. Otherwise, proceed with calibration. if (IsKnownJoystick(joystick_index)) { LoadKnownConfiguration(); TXT_CloseWindow(calibration_window); } else { TXT_CloseWindow(calibration_window); // Calibrate joystick axes: Y axis first, then X axis once // completed. TXT_ConfigureJoystickAxis(y_axis_widget, calibrate_button, CalibrateXAxis); } return 1; } static void NoJoystick(void) { TXT_MessageBox(NULL, "No gamepads or joysticks could be found.\n\n" "Try configuring your controller from within\n" "your OS first. Maybe you need to install\n" "some drivers or otherwise configure it."); usejoystick = 0; joystick_index = -1; SetJoystickButtonLabel(); } static void CalibrateWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { TXT_SDL_SetEventCallback(NULL, NULL); SetJoystickButtonLabel(); CloseAllJoysticks(); } static void CalibrateJoystick(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { // Try to open all available joysticks. If none are opened successfully, // bomb out with an error. if (!OpenAllJoysticks()) { NoJoystick(); return; } calibration_window = TXT_NewWindow("Gamepad/Joystick calibration"); TXT_AddWidgets(calibration_window, TXT_NewStrut(0, 1), TXT_NewLabel("Center the D-pad or joystick,\n" "and press a button."), TXT_NewStrut(0, 1), NULL); TXT_SetWindowAction(calibration_window, TXT_HORIZ_LEFT, NULL); TXT_SetWindowAction(calibration_window, TXT_HORIZ_CENTER, TXT_NewWindowAbortAction(calibration_window)); TXT_SetWindowAction(calibration_window, TXT_HORIZ_RIGHT, NULL); TXT_SDL_SetEventCallback(CalibrationEventCallback, NULL); TXT_SignalConnect(calibration_window, "closed", CalibrateWindowClosed, NULL); // Start calibration usejoystick = 0; joystick_index = -1; } // // GUI // static void AddJoystickControl(TXT_UNCAST_ARG(table), const char *label, int *var) { TXT_CAST_ARG(txt_table_t, table); txt_joystick_input_t *joy_input; joy_input = TXT_NewJoystickInput(var); TXT_AddWidgets(table, TXT_NewLabel(label), joy_input, TXT_TABLE_EMPTY, NULL); } void ConfigJoystick(TXT_UNCAST_ARG(widget), void *user_data) { txt_window_t *window; window = TXT_NewWindow("Gamepad/Joystick configuration"); TXT_SetTableColumns(window, 6); TXT_SetColumnWidths(window, 18, 10, 1, 15, 10, 0); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); TXT_AddWidgets(window, TXT_NewLabel("Controller"), joystick_button = TXT_NewButton("zzzz"), TXT_TABLE_EOL, TXT_NewSeparator("Axes"), TXT_NewLabel("Forward/backward"), y_axis_widget = TXT_NewJoystickAxis(&joystick_y_axis, &joystick_y_invert, JOYSTICK_AXIS_VERTICAL), TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_EMPTY, TXT_TABLE_EMPTY, TXT_NewLabel("Turn left/right"), x_axis_widget = TXT_NewJoystickAxis(&joystick_x_axis, &joystick_x_invert, JOYSTICK_AXIS_HORIZONTAL), TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_EMPTY, TXT_TABLE_EMPTY, TXT_NewLabel("Strafe left/right"), TXT_NewJoystickAxis(&joystick_strafe_axis, &joystick_strafe_invert, JOYSTICK_AXIS_HORIZONTAL), TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_EMPTY, TXT_TABLE_EMPTY, NULL); if (gamemission == doom || gamemission == heretic || gamemission == hexen || gamemission == strife) // [crispy] { TXT_AddWidgets(window, TXT_NewLabel("Look up/down"), TXT_NewJoystickAxis(&joystick_look_axis, &joystick_look_invert, JOYSTICK_AXIS_VERTICAL), TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_EMPTY, TXT_TABLE_EMPTY, NULL); } TXT_AddWidget(window, TXT_NewSeparator("Buttons")); AddJoystickControl(window, "Fire/Attack", &joybfire); AddJoystickControl(window, "Strafe Left", &joybstrafeleft); AddJoystickControl(window, "Use", &joybuse); AddJoystickControl(window, "Strafe Right", &joybstraferight); AddJoystickControl(window, "Previous weapon", &joybprevweapon); AddJoystickControl(window, "Strafe", &joybstrafe); AddJoystickControl(window, "Next weapon", &joybnextweapon); // High values of joybspeed are used to activate the "always run mode" // trick in Vanilla Doom. If this has been enabled, not only is the // joybspeed value meaningless, but the control itself is useless. if (joybspeed < 20) { AddJoystickControl(window, "Speed", &joybspeed); } if (gamemission == doom || gamemission == hexen || gamemission == strife) // [crispy] { AddJoystickControl(window, "Jump", &joybjump); } AddJoystickControl(window, "Activate menu", &joybmenu); AddJoystickControl(window, "Toggle Automap", &joybautomap); TXT_SignalConnect(joystick_button, "pressed", CalibrateJoystick, NULL); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction()); InitJoystick(); UpdateJoystickIndex(); SetJoystickButtonLabel(); UnInitJoystick(); } void BindJoystickVariables(void) { int i; M_BindIntVariable("use_joystick", &usejoystick); M_BindStringVariable("joystick_guid", &joystick_guid); M_BindIntVariable("joystick_index", &joystick_index); M_BindIntVariable("joystick_x_axis", &joystick_x_axis); M_BindIntVariable("joystick_y_axis", &joystick_y_axis); M_BindIntVariable("joystick_strafe_axis", &joystick_strafe_axis); M_BindIntVariable("joystick_x_invert", &joystick_x_invert); M_BindIntVariable("joystick_y_invert", &joystick_y_invert); M_BindIntVariable("joystick_strafe_invert", &joystick_strafe_invert); M_BindIntVariable("joystick_look_axis", &joystick_look_axis); M_BindIntVariable("joystick_look_invert", &joystick_look_invert); for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i) { char name[32]; M_snprintf(name, sizeof(name), "joystick_physical_button%i", i); M_BindIntVariable(name, &joystick_physical_buttons[i]); } } crispy-doom-crispy-doom-5.6.4/src/setup/joystick.h000066400000000000000000000013631360717211000221760ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_JOYSTICK_H #define SETUP_JOYSTICK_H extern int joystick_index; void ConfigJoystick(void *widget, void *user_data); void BindJoystickVariables(void); #endif /* #ifndef SETUP_JOYSTICK_H */ crispy-doom-crispy-doom-5.6.4/src/setup/keyboard.c000066400000000000000000000425561360717211000221430ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include "textscreen.h" #include "doomtype.h" #include "m_config.h" #include "m_controls.h" #include "m_misc.h" #include "execute.h" #include "txt_keyinput.h" #include "mode.h" #include "joystick.h" #include "keyboard.h" #define WINDOW_HELP_URL "https://www.chocolate-doom.org/setup-keyboard" int vanilla_keyboard_mapping = 1; static int always_run = 0; // Keys within these groups cannot have the same value. static int *controls[] = { &key_left, &key_right, &key_up, &key_down, &key_alt_up, &key_alt_down, &key_reverse, &key_toggleautorun, &key_togglenovert, &key_strafeleft, &key_straferight, &key_fire, &key_alt_strafeleft, &key_alt_straferight, &key_use, &key_strafe, &key_speed, &key_jump, &key_flyup, &key_flydown, &key_flycenter, &key_lookup, &key_lookdown, &key_lookcenter, &key_invleft, &key_invright, &key_invquery, &key_invuse, &key_invpop, &key_mission, &key_invkey, &key_invhome, &key_invend, &key_invdrop, &key_useartifact, &key_pause, &key_usehealth, &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, &key_weapon5, &key_weapon6, &key_weapon7, &key_weapon8, &key_arti_all, &key_arti_health, &key_arti_poisonbag, &key_arti_blastradius, &key_arti_teleport, &key_arti_teleportother, &key_arti_egg, &key_arti_invulnerability, &key_prevweapon, &key_nextweapon, NULL }; static int *menu_nav[] = { &key_menu_activate, &key_menu_up, &key_menu_down, &key_menu_left, &key_menu_right, &key_menu_back, &key_menu_forward, &key_menu_del, NULL }; static int *shortcuts[] = { &key_menu_help, &key_menu_save, &key_menu_load, &key_menu_volume, &key_menu_detail, &key_menu_qsave, &key_menu_endgame, &key_menu_messages, &key_spy, &key_menu_qload, &key_menu_quit, &key_menu_gamma, &key_menu_nextlevel, &key_menu_reloadlevel, &key_menu_incscreen, &key_menu_decscreen, &key_menu_screenshot, &key_menu_cleanscreenshot, &key_message_refresh, &key_multi_msg, &key_multi_msgplayer[0], &key_multi_msgplayer[1], &key_multi_msgplayer[2], &key_multi_msgplayer[3] }; static int *map_keys[] = { &key_map_north, &key_map_south, &key_map_east, &key_map_west, &key_map_zoomin, &key_map_zoomout, &key_map_toggle, &key_map_maxzoom, &key_map_follow, &key_map_grid, &key_map_mark, &key_map_clearmark, &key_map_overlay, &key_map_rotate, NULL }; static void UpdateJoybSpeed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(var)) { if (always_run) { /* if you want to pick one for chocolate doom to use, pick 29, since that is the most universal one that also works with heretic, hexen and strife =P NB. This choice also works with original, ultimate and final exes. */ joybspeed = 29; } else { joybspeed = 2; } } static int VarInGroup(int *variable, int **group) { unsigned int i; for (i=0; group[i] != NULL; ++i) { if (group[i] == variable) { return 1; } } return 0; } static void CheckKeyGroup(int *variable, int **group) { unsigned int i; // Don't check unless the variable is in this group. if (!VarInGroup(variable, group)) { return; } // If another variable has the same value as the new value, reset it. for (i=0; group[i] != NULL; ++i) { if (*variable == *group[i] && group[i] != variable) { // A different key has the same value. Clear the existing // value. This ensures that no two keys can have the same // value. *group[i] = 0; } } } // Callback invoked when a key control is set static void KeySetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable)) { TXT_CAST_ARG(int, variable); CheckKeyGroup(variable, controls); CheckKeyGroup(variable, menu_nav); CheckKeyGroup(variable, shortcuts); CheckKeyGroup(variable, map_keys); } // Add a label and keyboard input to the specified table. static void AddKeyControl(TXT_UNCAST_ARG(table), const char *name, int *var) { TXT_CAST_ARG(txt_table_t, table); txt_key_input_t *key_input; TXT_AddWidget(table, TXT_NewLabel(name)); key_input = TXT_NewKeyInput(var); TXT_AddWidget(table, key_input); TXT_SignalConnect(key_input, "set", KeySetCallback, var); } static void AddSectionLabel(TXT_UNCAST_ARG(table), const char *title, boolean add_space) { TXT_CAST_ARG(txt_table_t, table); char buf[64]; if (add_space) { TXT_AddWidgets(table, TXT_NewStrut(0, 1), TXT_TABLE_EOL, NULL); } M_snprintf(buf, sizeof(buf), " - %s - ", title); TXT_AddWidgets(table, TXT_NewLabel(buf), TXT_TABLE_EOL, NULL); } static void ConfigExtraKeys(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { txt_window_t *window; txt_scrollpane_t *scrollpane; txt_table_t *table; boolean extra_keys = gamemission == heretic || gamemission == hexen || gamemission == strife; window = TXT_NewWindow("Extra keyboard controls"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); table = TXT_NewTable(2); TXT_SetColumnWidths(table, 21, 9); if (extra_keys || 1) // Crispy { // When we have extra controls, a scrollable pane must be used. scrollpane = TXT_NewScrollPane(0, 13, table); TXT_AddWidget(window, scrollpane); if (gamemission == doom) { AddSectionLabel(table, "View", false); AddKeyControl(table, "Look up [*]", &key_lookup); AddKeyControl(table, "Look down [*]", &key_lookdown); AddKeyControl(table, "Center view [*]", &key_lookcenter); AddSectionLabel(table, "Movement", false); AddKeyControl(table, "Move Forward (alt.)", &key_alt_up); AddKeyControl(table, "Move Backward (alt.)", &key_alt_down); AddKeyControl(table, "Strafe Left (alt.)", &key_alt_strafeleft); AddKeyControl(table, "Strafe Right (alt.)", &key_alt_straferight); AddKeyControl(table, "Toggle always run", &key_toggleautorun); AddKeyControl(table, "Toggle vert. mouse", &key_togglenovert); AddKeyControl(table, "Quick Reverse", &key_reverse); } else { AddSectionLabel(table, "View", false); AddKeyControl(table, "Look up", &key_lookup); AddKeyControl(table, "Look down", &key_lookdown); AddKeyControl(table, "Center view", &key_lookcenter); } if (gamemission == heretic || gamemission == hexen) { AddSectionLabel(table, "Flying", true); AddKeyControl(table, "Fly up", &key_flyup); AddKeyControl(table, "Fly down", &key_flydown); AddKeyControl(table, "Fly center", &key_flycenter); } if (gamemission != doom) { AddSectionLabel(table, "Inventory", true); AddKeyControl(table, "Inventory left", &key_invleft); AddKeyControl(table, "Inventory right", &key_invright); } if (gamemission == strife) { AddKeyControl(table, "Home", &key_invhome); AddKeyControl(table, "End", &key_invend); AddKeyControl(table, "Query", &key_invquery); AddKeyControl(table, "Drop", &key_invdrop); AddKeyControl(table, "Show weapons", &key_invpop); AddKeyControl(table, "Show mission", &key_mission); AddKeyControl(table, "Show keys", &key_invkey); AddKeyControl(table, "Use", &key_invuse); AddKeyControl(table, "Use health", &key_usehealth); } else if (gamemission == heretic || gamemission == hexen) { AddKeyControl(table, "Use artifact", &key_useartifact); } if (gamemission == hexen) { AddSectionLabel(table, "Artifacts", true); AddKeyControl(table, "One of each", &key_arti_all); AddKeyControl(table, "Quartz Flask", &key_arti_health); AddKeyControl(table, "Flechette", &key_arti_poisonbag); AddKeyControl(table, "Disc of Repulsion", &key_arti_blastradius); AddKeyControl(table, "Chaos Device", &key_arti_teleport); AddKeyControl(table, "Banishment Device", &key_arti_teleportother); AddKeyControl(table, "Porkalator", &key_arti_egg); AddKeyControl(table, "Icon of the Defender", &key_arti_invulnerability); } } else { TXT_AddWidget(window, table); } AddSectionLabel(table, "Weapons", extra_keys); AddKeyControl(table, "Weapon 1", &key_weapon1); AddKeyControl(table, "Weapon 2", &key_weapon2); AddKeyControl(table, "Weapon 3", &key_weapon3); AddKeyControl(table, "Weapon 4", &key_weapon4); AddKeyControl(table, "Weapon 5", &key_weapon5); AddKeyControl(table, "Weapon 6", &key_weapon6); AddKeyControl(table, "Weapon 7", &key_weapon7); AddKeyControl(table, "Weapon 8", &key_weapon8); AddKeyControl(table, "Previous weapon", &key_prevweapon); AddKeyControl(table, "Next weapon", &key_nextweapon); } static void OtherKeysDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { txt_window_t *window; txt_table_t *table; txt_scrollpane_t *scrollpane; window = TXT_NewWindow("Other keys"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); table = TXT_NewTable(2); TXT_SetColumnWidths(table, 25, 9); AddSectionLabel(table, "Menu navigation", false); AddKeyControl(table, "Activate menu", &key_menu_activate); AddKeyControl(table, "Move cursor up", &key_menu_up); AddKeyControl(table, "Move cursor down", &key_menu_down); AddKeyControl(table, "Move slider left", &key_menu_left); AddKeyControl(table, "Move slider right", &key_menu_right); AddKeyControl(table, "Go to previous menu", &key_menu_back); AddKeyControl(table, "Activate menu item", &key_menu_forward); AddKeyControl(table, "Confirm action", &key_menu_confirm); AddKeyControl(table, "Cancel action", &key_menu_abort); AddKeyControl(table, "Delete savegame", &key_menu_del); AddSectionLabel(table, "Shortcut keys", true); AddKeyControl(table, "Pause game", &key_pause); AddKeyControl(table, "Help screen", &key_menu_help); AddKeyControl(table, "Save game", &key_menu_save); AddKeyControl(table, "Load game", &key_menu_load); AddKeyControl(table, "Sound volume", &key_menu_volume); AddKeyControl(table, "Toggle detail", &key_menu_detail); AddKeyControl(table, "Quick save", &key_menu_qsave); AddKeyControl(table, "End game", &key_menu_endgame); AddKeyControl(table, "Toggle messages", &key_menu_messages); AddKeyControl(table, "Quick load", &key_menu_qload); AddKeyControl(table, "Quit game", &key_menu_quit); AddKeyControl(table, "Toggle gamma", &key_menu_gamma); AddKeyControl(table, "Multiplayer spy", &key_spy); AddKeyControl(table, "Go to next level", &key_menu_nextlevel); AddKeyControl(table, "Restart level/demo", &key_menu_reloadlevel); AddKeyControl(table, "Increase screen size", &key_menu_incscreen); AddKeyControl(table, "Decrease screen size", &key_menu_decscreen); AddKeyControl(table, "Save a screenshot", &key_menu_screenshot); AddKeyControl(table, "Save a clean screenshot",&key_menu_cleanscreenshot); AddKeyControl(table, "Display last message", &key_message_refresh); AddKeyControl(table, "Finish recording demo", &key_demo_quit); AddSectionLabel(table, "Map", true); AddKeyControl(table, "Toggle map", &key_map_toggle); AddKeyControl(table, "Zoom in", &key_map_zoomin); AddKeyControl(table, "Zoom out", &key_map_zoomout); AddKeyControl(table, "Maximum zoom out", &key_map_maxzoom); AddKeyControl(table, "Follow mode", &key_map_follow); AddKeyControl(table, "Pan north", &key_map_north); AddKeyControl(table, "Pan south", &key_map_south); AddKeyControl(table, "Pan east", &key_map_east); AddKeyControl(table, "Pan west", &key_map_west); AddKeyControl(table, "Toggle grid", &key_map_grid); AddKeyControl(table, "Mark location", &key_map_mark); AddKeyControl(table, "Clear all marks", &key_map_clearmark); AddKeyControl(table, "Overlay mode", &key_map_overlay); AddKeyControl(table, "Rotate mode", &key_map_rotate); AddSectionLabel(table, "Multiplayer", true); AddKeyControl(table, "Send message", &key_multi_msg); AddKeyControl(table, "- to player 1", &key_multi_msgplayer[0]); AddKeyControl(table, "- to player 2", &key_multi_msgplayer[1]); AddKeyControl(table, "- to player 3", &key_multi_msgplayer[2]); AddKeyControl(table, "- to player 4", &key_multi_msgplayer[3]); if (gamemission == hexen || gamemission == strife) { AddKeyControl(table, "- to player 5", &key_multi_msgplayer[4]); AddKeyControl(table, "- to player 6", &key_multi_msgplayer[5]); AddKeyControl(table, "- to player 7", &key_multi_msgplayer[6]); AddKeyControl(table, "- to player 8", &key_multi_msgplayer[7]); } scrollpane = TXT_NewScrollPane(0, 13, table); TXT_AddWidget(window, scrollpane); } void ConfigKeyboard(TXT_UNCAST_ARG(widget), void *user_data) { txt_window_t *window; txt_checkbox_t *run_control; always_run = joybspeed >= 20; window = TXT_NewWindow("Keyboard configuration"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); // The window is on a 5-column grid layout that looks like: // Label | Control | | Label | Control // There is a small gap between the two conceptual "columns" of // controls, just for spacing. TXT_SetTableColumns(window, 5); TXT_SetColumnWidths(window, 15, 8, 2, 15, 8); TXT_AddWidget(window, TXT_NewSeparator("Movement")); AddKeyControl(window, "Move Forward", &key_up); TXT_AddWidget(window, TXT_TABLE_EMPTY); AddKeyControl(window, "Strafe Left", &key_strafeleft); AddKeyControl(window, "Move Backward", &key_down); TXT_AddWidget(window, TXT_TABLE_EMPTY); AddKeyControl(window, "Strafe Right", &key_straferight); AddKeyControl(window, "Turn Left", &key_left); TXT_AddWidget(window, TXT_TABLE_EMPTY); AddKeyControl(window, "Speed On", &key_speed); AddKeyControl(window, "Turn Right", &key_right); TXT_AddWidget(window, TXT_TABLE_EMPTY); AddKeyControl(window, "Strafe On", &key_strafe); if (gamemission == hexen || gamemission == strife) { AddKeyControl(window, "Jump", &key_jump); } else if (gamemission == doom) // Crispy { AddKeyControl(window, "Jump [*]", &key_jump); } TXT_AddWidget(window, TXT_NewSeparator("Action")); AddKeyControl(window, "Fire/Attack", &key_fire); TXT_AddWidget(window, TXT_TABLE_EMPTY); AddKeyControl(window, "Use", &key_use); TXT_AddWidgets(window, TXT_NewButton2("More controls...", ConfigExtraKeys, NULL), TXT_TABLE_OVERFLOW_RIGHT, TXT_TABLE_EMPTY, TXT_NewButton2("Other keys...", OtherKeysDialog, NULL), TXT_TABLE_OVERFLOW_RIGHT, TXT_NewSeparator("Misc."), run_control = TXT_NewCheckBox("Always run", &always_run), TXT_TABLE_EOL, TXT_NewInvertedCheckBox("Use native keyboard mapping", &vanilla_keyboard_mapping), TXT_TABLE_EOL, NULL); TXT_SignalConnect(run_control, "changed", UpdateJoybSpeed, NULL); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TestConfigAction()); } void BindKeyboardVariables(void) { M_BindIntVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping); } crispy-doom-crispy-doom-5.6.4/src/setup/keyboard.h000066400000000000000000000013761360717211000221430ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_KEYBOARD_H #define SETUP_KEYBOARD_H void ConfigKeyboard(void *widget, void *user_data); void BindKeyboardVariables(void); extern int vanilla_keyboard_mapping; #endif /* #ifndef SETUP_KEYBOARD_H */ crispy-doom-crispy-doom-5.6.4/src/setup/mainmenu.c000066400000000000000000000216731360717211000221510ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include "config.h" #include "textscreen.h" #include "execute.h" #include "m_argv.h" #include "m_config.h" #include "m_controls.h" #include "m_misc.h" #include "z_zone.h" #include "setup_icon.c" #include "mode.h" #include "compatibility.h" #include "display.h" #include "joystick.h" #include "keyboard.h" #include "mouse.h" #include "multiplayer.h" #include "sound.h" #define WINDOW_HELP_URL "https://www.chocolate-doom.org/setup" static const int cheat_sequence[] = { KEY_UPARROW, KEY_UPARROW, KEY_DOWNARROW, KEY_DOWNARROW, KEY_LEFTARROW, KEY_RIGHTARROW, KEY_LEFTARROW, KEY_RIGHTARROW, 'b', 'a', KEY_ENTER, 0 }; static unsigned int cheat_sequence_index = 0; // I think these are good "sensible" defaults: static void SensibleDefaults(void) { key_up = 'w'; key_down = 's'; key_strafeleft = 'a'; key_straferight = 'd'; key_jump = '/'; key_lookup = KEY_PGUP; key_lookdown = KEY_PGDN; key_lookcenter = KEY_HOME; key_flyup = KEY_INS; key_flydown = KEY_DEL; key_flycenter = KEY_END; key_prevweapon = ','; key_nextweapon = '.'; key_invleft = '['; key_invright = ']'; key_message_refresh = '\''; key_mission = 'i'; // Strife keys key_invpop = 'o'; key_invkey = 'p'; key_multi_msgplayer[0] = 'g'; key_multi_msgplayer[1] = 'h'; key_multi_msgplayer[2] = 'j'; key_multi_msgplayer[3] = 'k'; key_multi_msgplayer[4] = 'v'; key_multi_msgplayer[5] = 'b'; key_multi_msgplayer[6] = 'n'; key_multi_msgplayer[7] = 'm'; mousebprevweapon = 4; // Scroll wheel = weapon cycle mousebnextweapon = 3; snd_musicdevice = 3; joybspeed = 29; // Always run vanilla_savegame_limit = 0; vanilla_keyboard_mapping = 0; vanilla_demo_limit = 0; graphical_startup = 0; show_endoom = 0; dclick_use = 0; novert = 1; snd_dmxoption = "-opl3 -reverse"; png_screenshots = 1; } static int MainMenuKeyPress(txt_window_t *window, int key, void *user_data) { if (key == cheat_sequence[cheat_sequence_index]) { ++cheat_sequence_index; if (cheat_sequence[cheat_sequence_index] == 0) { SensibleDefaults(); cheat_sequence_index = 0; window = TXT_MessageBox(NULL, " \x01 "); return 1; } } else { cheat_sequence_index = 0; } return 0; } static void DoQuit(void *widget, void *dosave) { if (dosave != NULL) { M_SaveDefaults(); } TXT_Shutdown(); exit(0); } static void QuitConfirm(void *unused1, void *unused2) { txt_window_t *window; txt_label_t *label; txt_button_t *yes_button; txt_button_t *no_button; window = TXT_NewWindow(NULL); TXT_AddWidgets(window, label = TXT_NewLabel("Exiting setup.\nSave settings?"), TXT_NewStrut(24, 0), yes_button = TXT_NewButton2(" Yes ", DoQuit, DoQuit), no_button = TXT_NewButton2(" No ", DoQuit, NULL), NULL); TXT_SetWidgetAlign(label, TXT_HORIZ_CENTER); TXT_SetWidgetAlign(yes_button, TXT_HORIZ_CENTER); TXT_SetWidgetAlign(no_button, TXT_HORIZ_CENTER); // Only an "abort" button in the middle. TXT_SetWindowAction(window, TXT_HORIZ_LEFT, NULL); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, TXT_NewWindowAbortAction(window)); TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, NULL); } static void LaunchDoom(void *unused1, void *unused2) { execute_context_t *exec; // Save configuration first M_SaveDefaults(); // Shut down textscreen GUI TXT_Shutdown(); // Launch Doom exec = NewExecuteContext(); PassThroughArguments(exec); ExecuteDoom(exec); exit(0); } static txt_button_t *GetLaunchButton(void) { const char *label; switch (gamemission) { case doom: label = "Save parameters and launch DOOM"; break; case heretic: label = "Save parameters and launch Heretic"; break; case hexen: label = "Save parameters and launch Hexen"; break; case strife: label = "Save parameters and launch STRIFE!"; break; default: label = "Save parameters and launch game"; break; } return TXT_NewButton2(label, LaunchDoom, NULL); } void MainMenu(void) { txt_window_t *window; txt_window_action_t *quit_action; txt_window_action_t *warp_action; window = TXT_NewWindow("Main Menu"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); TXT_AddWidgets(window, TXT_NewButton2("Configure Display", (TxtWidgetSignalFunc) ConfigDisplay, NULL), TXT_NewButton2("Configure Sound", (TxtWidgetSignalFunc) ConfigSound, NULL), TXT_NewButton2("Configure Keyboard", (TxtWidgetSignalFunc) ConfigKeyboard, NULL), TXT_NewButton2("Configure Mouse", (TxtWidgetSignalFunc) ConfigMouse, NULL), TXT_NewButton2("Configure Gamepad/Joystick", (TxtWidgetSignalFunc) ConfigJoystick, NULL), TXT_NewButton2(gamemission == doom ? "Crispness" : "Compatibility", (TxtWidgetSignalFunc) CompatibilitySettings, NULL), GetLaunchButton(), TXT_NewStrut(0, 1), TXT_NewButton2("Start a Network Game", (TxtWidgetSignalFunc) StartMultiGame, NULL), TXT_NewButton2("Join a Network Game", (TxtWidgetSignalFunc) JoinMultiGame, NULL), TXT_NewButton2("Multiplayer Configuration", (TxtWidgetSignalFunc) MultiplayerConfig, NULL), NULL); quit_action = TXT_NewWindowAction(KEY_ESCAPE, "Quit"); warp_action = TXT_NewWindowAction(KEY_F2, "Warp"); TXT_SignalConnect(quit_action, "pressed", QuitConfirm, NULL); TXT_SignalConnect(warp_action, "pressed", (TxtWidgetSignalFunc) WarpMenu, NULL); TXT_SetWindowAction(window, TXT_HORIZ_LEFT, quit_action); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, warp_action); TXT_SetKeyListener(window, MainMenuKeyPress, NULL); } // // Initialize all configuration variables, load config file, etc // static void InitConfig(void) { M_SetConfigDir(NULL); InitBindings(); SetChatMacroDefaults(); SetPlayerNameDefault(); M_LoadDefaults(); // Create and configure the music pack directory if it does not // already exist. M_SetMusicPackDir(); } // // Application icon // static void SetIcon(void) { extern SDL_Window *TXT_SDLWindow; SDL_Surface *surface; surface = SDL_CreateRGBSurfaceFrom((void *) setup_icon_data, setup_icon_w, setup_icon_h, 32, setup_icon_w * 4, 0xff << 24, 0xff << 16, 0xff << 8, 0xff << 0); SDL_SetWindowIcon(TXT_SDLWindow, surface); SDL_FreeSurface(surface); } static void SetWindowTitle(void) { char *title; title = M_StringReplace(PACKAGE_NAME " Setup ver " PACKAGE_VERSION, "Doom", GetGameTitle()); TXT_SetDesktopTitle(title); free(title); } // Initialize the textscreen library. static void InitTextscreen(void) { SetDisplayDriver(); if (!TXT_Init()) { fprintf(stderr, "Failed to initialize GUI\n"); exit(-1); } // Set Romero's "funky blue" color: // TXT_SetColor(TXT_COLOR_BLUE, 0x04, 0x14, 0x40); // [crispy] Crispy colors for Crispy Setup TXT_SetColor(TXT_COLOR_BRIGHT_GREEN, 249, 227, 0); // 0xF9, 0xE3, 0x00 TXT_SetColor(TXT_COLOR_CYAN, 220, 153, 0); // 0xDC, 0x99, 0x00 TXT_SetColor(TXT_COLOR_BRIGHT_CYAN, 76, 160, 223); // 0x4C, 0xA0, 0xDF SetIcon(); SetWindowTitle(); } // Restart the textscreen library. Used when the video_driver variable // is changed. void RestartTextscreen(void) { TXT_Shutdown(); InitTextscreen(); } // // Initialize and run the textscreen GUI. // static void RunGUI(void) { InitTextscreen(); TXT_GUIMainLoop(); } static void MissionSet(void) { SetWindowTitle(); InitConfig(); MainMenu(); } void D_DoomMain(void) { SetupMission(MissionSet); RunGUI(); } crispy-doom-crispy-doom-5.6.4/src/setup/mode.c000066400000000000000000000201461360717211000212560ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include "doomtype.h" #include "config.h" #include "textscreen.h" #include "doomtype.h" #include "d_mode.h" #include "d_iwad.h" #include "i_system.h" #include "m_argv.h" #include "m_config.h" #include "m_controls.h" #include "m_misc.h" #include "compatibility.h" #include "display.h" #include "joystick.h" #include "keyboard.h" #include "mouse.h" #include "multiplayer.h" #include "sound.h" #include "mode.h" GameMission_t gamemission; static const iwad_t **iwads; typedef struct { const char *label; GameMission_t mission; int mask; const char *name; const char *config_file; const char *extra_config_file; const char *executable; } mission_config_t; // Default mission to fall back on, if no IWADs are found at all: #define DEFAULT_MISSION (&mission_configs[0]) static mission_config_t mission_configs[] = { { "Doom", doom, IWAD_MASK_DOOM, "doom", "default.cfg", PROGRAM_PREFIX "doom.cfg", PROGRAM_PREFIX "doom" }, { "Heretic", heretic, IWAD_MASK_HERETIC, "heretic", "heretic.cfg", PROGRAM_PREFIX "heretic.cfg", PROGRAM_PREFIX "heretic" }, { "Hexen", hexen, IWAD_MASK_HEXEN, "hexen", "hexen.cfg", PROGRAM_PREFIX "hexen.cfg", PROGRAM_PREFIX "hexen" }, { "Strife", strife, IWAD_MASK_STRIFE, "strife", "strife.cfg", PROGRAM_PREFIX "strife.cfg", PROGRAM_PREFIX "strife" } }; static GameSelectCallback game_selected_callback; // Miscellaneous variables that aren't used in setup. static int showMessages = 1; static int screenblocks = 10; static int detailLevel = 0; static char *savedir = NULL; static char *executable = NULL; static const char *game_title = "Doom"; static char *back_flat = "F_PAVE01"; static int comport = 0; static char *nickname = NULL; static void BindMiscVariables(void) { if (gamemission == doom) { M_BindIntVariable("detaillevel", &detailLevel); M_BindIntVariable("show_messages", &showMessages); } if (gamemission == hexen) { M_BindStringVariable("savedir", &savedir); M_BindIntVariable("messageson", &showMessages); // Hexen has a variable to control the savegame directory // that is used. savedir = M_GetSaveGameDir("hexen.wad"); // On Windows, hexndata\ is the default. if (!strcmp(savedir, "")) { free(savedir); savedir = "hexndata" DIR_SEPARATOR_S; } } if (gamemission == strife) { // Strife has a different default value than the other games screenblocks = 10; M_BindStringVariable("back_flat", &back_flat); M_BindStringVariable("nickname", &nickname); M_BindIntVariable("screensize", &screenblocks); M_BindIntVariable("comport", &comport); } else { M_BindIntVariable("screenblocks", &screenblocks); } } // // Initialise all configuration file bindings. // void InitBindings(void) { M_ApplyPlatformDefaults(); // Keyboard, mouse, joystick controls M_BindBaseControls(); M_BindWeaponControls(); M_BindMapControls(); M_BindMenuControls(); if (gamemission == heretic || gamemission == hexen) { M_BindHereticControls(); } if (gamemission == hexen) { M_BindHexenControls(); } if (gamemission == strife) { M_BindStrifeControls(); } // All other variables BindCompatibilityVariables(); BindDisplayVariables(); BindJoystickVariables(); BindKeyboardVariables(); BindMouseVariables(); BindSoundVariables(); BindMiscVariables(); BindMultiplayerVariables(); } // Set the name of the executable program to run the game: static void SetExecutable(mission_config_t *config) { char *extension; free(executable); #ifdef _WIN32 extension = ".exe"; #else extension = ""; #endif executable = M_StringJoin(config->executable, extension, NULL); } static void SetMission(mission_config_t *config) { iwads = D_FindAllIWADs(config->mask); gamemission = config->mission; SetExecutable(config); game_title = config->label; M_SetConfigFilenames(config->config_file, config->extra_config_file); } static mission_config_t *GetMissionForName(char *name) { int i; for (i=0; iname) != NULL) { SetMission(config); callback(); return true; } } return false; } static void GameSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(config)) { TXT_CAST_ARG(mission_config_t, config); SetMission(config); game_selected_callback(); } static void OpenGameSelectDialog(GameSelectCallback callback) { mission_config_t *mission = NULL; txt_window_t *window; const iwad_t **iwads; int num_games; int i; window = TXT_NewWindow("Select game"); TXT_AddWidget(window, TXT_NewLabel("Select a game to configure:\n")); num_games = 0; // Add a button for each game. for (i=0; i // // Specify the game to configure the settings for. Valid // values are 'doom', 'heretic', 'hexen' and 'strife'. // p = M_CheckParm("-game"); if (p > 0) { mission_name = myargv[p + 1]; config = GetMissionForName(mission_name); if (config == NULL) { I_Error("Invalid parameter - '%s'", mission_name); } SetMission(config); callback(); } else if (!CheckExecutableName(callback)) { OpenGameSelectDialog(callback); } } const char *GetExecutableName(void) { return executable; } const char *GetGameTitle(void) { return game_title; } const iwad_t **GetIwads(void) { return iwads; } crispy-doom-crispy-doom-5.6.4/src/setup/mode.h000066400000000000000000000016301360717211000212600ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_MODE_H #define SETUP_MODE_H #include "d_mode.h" #include "d_iwad.h" typedef void (*GameSelectCallback)(void); extern GameMission_t gamemission; void SetupMission(GameSelectCallback callback); void InitBindings(void); const char *GetExecutableName(void); const char *GetGameTitle(void); const iwad_t **GetIwads(void); #endif /* #ifndef SETUP_MODE_H */ crispy-doom-crispy-doom-5.6.4/src/setup/mouse.c000066400000000000000000000173241360717211000214660ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include "crispy.h" #include "textscreen.h" #include "doomtype.h" #include "m_config.h" #include "m_controls.h" #include "execute.h" #include "txt_mouseinput.h" #include "mode.h" #include "mouse.h" #define WINDOW_HELP_URL "https://www.chocolate-doom.org/setup-mouse" static int usemouse = 1; static int mouseSensitivity = 5; static int mouseSensitivity_x2 = 5; // [crispy] static float mouse_acceleration = 2.0; static int mouse_threshold = 10; static int mouseSensitivity_y = 5; // [crispy] static float mouse_acceleration_y = 1.0; // [crispy] static int mouse_threshold_y = 0; // [crispy] static int grabmouse = 1; int novert = 1; static int *all_mouse_buttons[] = { &mousebfire, &mousebstrafe, &mousebforward, &mousebstrafeleft, &mousebstraferight, &mousebbackward, &mousebuse, &mousebjump, &mousebprevweapon, &mousebnextweapon, &mousebmouselook, // [crispy] &mousebreverse // [crispy] }; static void MouseSetCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(variable)) { TXT_CAST_ARG(int, variable); unsigned int i; // Check if the same mouse button is used for a different action // If so, set the other action(s) to -1 (unset) for (i=0; imouselook); } } crispy-doom-crispy-doom-5.6.4/src/setup/mouse.h000066400000000000000000000013331360717211000214640ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_MOUSE_H #define SETUP_MOUSE_H void ConfigMouse(void *widget, void *user_data); void BindMouseVariables(void); extern int novert; #endif /* #ifndef SETUP_MOUSE_H */ crispy-doom-crispy-doom-5.6.4/src/setup/multiplayer.c000066400000000000000000000741471360717211000227130ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include "doomtype.h" #include "textscreen.h" #include "d_iwad.h" #include "m_config.h" #include "m_misc.h" #include "doom/d_englsh.h" #include "m_controls.h" #include "multiplayer.h" #include "mode.h" #include "execute.h" #include "net_io.h" #include "net_query.h" #include "net_petname.h" #define MULTI_START_HELP_URL "https://www.chocolate-doom.org/setup-multi-start" #define MULTI_JOIN_HELP_URL "https://www.chocolate-doom.org/setup-multi-join" #define MULTI_CONFIG_HELP_URL "https://www.chocolate-doom.org/setup-multi-config" #define LEVEL_WARP_HELP_URL "https://www.chocolate-doom.org/setup-level-warp" #define NUM_WADS 10 #define NUM_EXTRA_PARAMS 10 typedef enum { WARP_ExMy, WARP_MAPxy, } warptype_t; // Fallback IWADs to use if no IWADs are detected. static const iwad_t fallback_iwads[] = { { "doom.wad", doom, registered, "Doom" }, { "heretic.wad", heretic, retail, "Heretic" }, { "hexen.wad", hexen, commercial, "Hexen" }, { "strife1.wad", strife, commercial, "Strife" }, }; // Array of IWADs found to be installed static const iwad_t **found_iwads; static const char **iwad_labels; // Index of the currently selected IWAD static int found_iwad_selected = -1; // Filename to pass to '-iwad'. static const char *iwadfile; static const char *wad_extensions[] = { "wad", "lmp", "deh", NULL }; static const char *doom_skills[] = { "I'm too young to die.", "Hey, not too rough.", "Hurt me plenty.", "Ultra-Violence.", "NIGHTMARE!", }; static const char *chex_skills[] = { "Easy does it", "Not so sticky", "Gobs of goo", "Extreme ooze", "SUPER SLIMEY!" }; static const char *heretic_skills[] = { "Thou needeth a wet-nurse", "Yellowbellies-R-us", "Bringest them oneth", "Thou art a smite-meister", "Black plague possesses thee" }; static const char *hexen_fighter_skills[] = { "Squire", "Knight", "Warrior", "Berserker", "Titan" }; static const char *hexen_cleric_skills[] = { "Altar boy", "Acolyte", "Priest", "Cardinal", "Pope" }; static const char *hexen_mage_skills[] = { "Apprentice", "Enchanter", "Sorceror", "Warlock", "Archimage" }; static const char *strife_skills[] = { "Training", "Rookie", "Veteran", "Elite", "Bloodbath" }; static const char *character_classes[] = { "Fighter", "Cleric", "Mage" }; static const char *gamemodes[] = { "Co-operative", "Deathmatch", "Deathmatch 2.0", "Deathmatch 3.0" }; static const char *strife_gamemodes[] = { "Normal deathmatch", "Items respawn", // (altdeath) }; static char *net_player_name; static char *chat_macros[10]; static char *wads[NUM_WADS]; static char *extra_params[NUM_EXTRA_PARAMS]; static int character_class = 0; static int skill = 2; static int nomonsters = 0; static int deathmatch = 0; static int strife_altdeath = 0; static int fast = 0; static int respawn = 0; static int udpport = 2342; static int timer = 0; static int privateserver = 0; static txt_dropdown_list_t *skillbutton; static txt_button_t *warpbutton; static warptype_t warptype = WARP_MAPxy; static int warpepisode = 1; static int warpmap = 1; // Address to connect to when joining a game static char *connect_address = NULL; static txt_window_t *query_window; static int query_servers_found; // Find an IWAD from its description static const iwad_t *GetCurrentIWAD(void) { return found_iwads[found_iwad_selected]; } // Is the currently selected IWAD the Chex Quest chex.wad? static boolean IsChexQuest(const iwad_t *iwad) { return !strcmp(iwad->name, "chex.wad"); } static void AddWADs(execute_context_t *exec) { int have_wads = 0; int i; for (i=0; i 0) { if (!have_wads) { AddCmdLineParameter(exec, "-file"); } AddCmdLineParameter(exec, "\"%s\"", wads[i]); } } } static void AddExtraParameters(execute_context_t *exec) { int i; for (i=0; i 0) { AddCmdLineParameter(exec, "%s", extra_params[i]); } } } static void AddIWADParameter(execute_context_t *exec) { if (iwadfile != NULL) { AddCmdLineParameter(exec, "-iwad %s", iwadfile); } } // Callback function invoked to launch the game. // This is used when starting a server and also when starting a // single player game via the "warp" menu. static void StartGame(int multiplayer) { execute_context_t *exec; exec = NewExecuteContext(); // Extra parameters come first, before all others; this way, // they can override any of the options set in the dialog. AddExtraParameters(exec); AddIWADParameter(exec); AddCmdLineParameter(exec, "-skill %i", skill + 1); if (gamemission == hexen) { AddCmdLineParameter(exec, "-class %i", character_class); } if (nomonsters) { AddCmdLineParameter(exec, "-nomonsters"); } if (fast) { AddCmdLineParameter(exec, "-fast"); } if (respawn) { AddCmdLineParameter(exec, "-respawn"); } if (warptype == WARP_ExMy) { // TODO: select IWAD based on warp type AddCmdLineParameter(exec, "-warp %i %i", warpepisode, warpmap); } else if (warptype == WARP_MAPxy) { AddCmdLineParameter(exec, "-warp %i", warpmap); } // Multiplayer-specific options: if (multiplayer) { AddCmdLineParameter(exec, "-server"); AddCmdLineParameter(exec, "-port %i", udpport); if (deathmatch == 1) { AddCmdLineParameter(exec, "-deathmatch"); } else if (deathmatch == 2 || strife_altdeath != 0) { AddCmdLineParameter(exec, "-altdeath"); } else if (deathmatch == 3) // AX: this is a Crispy-specific change { AddCmdLineParameter(exec, "-dm3"); } if (timer > 0) { AddCmdLineParameter(exec, "-timer %i", timer); } if (privateserver) { AddCmdLineParameter(exec, "-privateserver"); } } AddWADs(exec); TXT_Shutdown(); M_SaveDefaults(); PassThroughArguments(exec); ExecuteDoom(exec); exit(0); } static void StartServerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { StartGame(1); } static void StartSinglePlayerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { StartGame(0); } static void UpdateWarpButton(void) { char buf[10]; if (warptype == WARP_ExMy) { M_snprintf(buf, sizeof(buf), "E%iM%i", warpepisode, warpmap); } else if (warptype == WARP_MAPxy) { M_snprintf(buf, sizeof(buf), "MAP%02i", warpmap); } TXT_SetButtonLabel(warpbutton, buf); } static void UpdateSkillButton(void) { const iwad_t *iwad = GetCurrentIWAD(); if (IsChexQuest(iwad)) { skillbutton->values = chex_skills; } else switch(gamemission) { default: case doom: skillbutton->values = doom_skills; break; case heretic: skillbutton->values = heretic_skills; break; case hexen: if (character_class == 0) { skillbutton->values = hexen_fighter_skills; } else if (character_class == 1) { skillbutton->values = hexen_cleric_skills; } else { skillbutton->values = hexen_mage_skills; } break; case strife: skillbutton->values = strife_skills; break; } } static void SetExMyWarp(TXT_UNCAST_ARG(widget), void *val) { int l; l = (intptr_t) val; warpepisode = l / 10; warpmap = l % 10; UpdateWarpButton(); } static void SetMAPxyWarp(TXT_UNCAST_ARG(widget), void *val) { int l; l = (intptr_t) val; warpmap = l; UpdateWarpButton(); } static void CloseLevelSelectDialog(TXT_UNCAST_ARG(button), TXT_UNCAST_ARG(window)) { TXT_CAST_ARG(txt_window_t, window); TXT_CloseWindow(window); } static void LevelSelectDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data)) { txt_window_t *window; txt_button_t *button; const iwad_t *iwad; char buf[10]; int episodes; intptr_t x, y; intptr_t l; int i; window = TXT_NewWindow("Select level"); iwad = GetCurrentIWAD(); if (warptype == WARP_ExMy) { episodes = D_GetNumEpisodes(iwad->mission, iwad->mode); TXT_SetTableColumns(window, episodes); // ExMy levels for (y=1; y<10; ++y) { for (x=1; x<=episodes; ++x) { if (IsChexQuest(iwad) && (x > 1 || y > 5)) { continue; } if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, x, y)) { TXT_AddWidget(window, NULL); continue; } M_snprintf(buf, sizeof(buf), " E%" PRIiPTR "M%" PRIiPTR " ", x, y); button = TXT_NewButton(buf); TXT_SignalConnect(button, "pressed", SetExMyWarp, (void *) (x * 10 + y)); TXT_SignalConnect(button, "pressed", CloseLevelSelectDialog, window); TXT_AddWidget(window, button); if (warpepisode == x && warpmap == y) { TXT_SelectWidget(window, button); } } } } else { TXT_SetTableColumns(window, 6); for (i=0; i<60; ++i) { x = i % 6; y = i / 6; l = x * 10 + y + 1; if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, 1, l)) { TXT_AddWidget(window, NULL); continue; } M_snprintf(buf, sizeof(buf), " MAP%02" PRIiPTR " ", l); button = TXT_NewButton(buf); TXT_SignalConnect(button, "pressed", SetMAPxyWarp, (void *) l); TXT_SignalConnect(button, "pressed", CloseLevelSelectDialog, window); TXT_AddWidget(window, button); if (warpmap == l) { TXT_SelectWidget(window, button); } } } } static void IWADSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { const iwad_t *iwad; // Find the iwad_t selected iwad = GetCurrentIWAD(); // Update iwadfile iwadfile = iwad->name; } // Called when the IWAD button is changed, to update warptype. static void UpdateWarpType(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { warptype_t new_warptype; const iwad_t *iwad; // Get the selected IWAD iwad = GetCurrentIWAD(); // Find the new warp type if (D_IsEpisodeMap(iwad->mission)) { new_warptype = WARP_ExMy; } else { new_warptype = WARP_MAPxy; } // Reset to E1M1 / MAP01 when the warp type is changed. if (new_warptype != warptype) { warpepisode = 1; warpmap = 1; } warptype = new_warptype; UpdateWarpButton(); UpdateSkillButton(); } // Get an IWAD list with a default fallback IWAD that is appropriate // for the game we are configuring (matches gamemission global variable). static const iwad_t **GetFallbackIwadList(void) { static const iwad_t *fallback_iwad_list[2]; unsigned int i; // Default to use if we don't find something better. fallback_iwad_list[0] = &fallback_iwads[0]; fallback_iwad_list[1] = NULL; for (i = 0; i < arrlen(fallback_iwads); ++i) { if (gamemission == fallback_iwads[i].mission) { fallback_iwad_list[0] = &fallback_iwads[i]; break; } } return fallback_iwad_list; } static txt_widget_t *IWADSelector(void) { txt_dropdown_list_t *dropdown; txt_widget_t *result; int num_iwads; unsigned int i; // Find out what WADs are installed found_iwads = GetIwads(); // Build a list of the descriptions for all installed IWADs num_iwads = 0; for (i=0; found_iwads[i] != NULL; ++i) { ++num_iwads; } iwad_labels = malloc(sizeof(*iwad_labels) * num_iwads); for (i=0; i < num_iwads; ++i) { iwad_labels[i] = found_iwads[i]->description; } // If no IWADs are found, provide Doom 2 as an option, but // we're probably screwed. if (num_iwads == 0) { found_iwads = GetFallbackIwadList(); num_iwads = 1; } // Build a dropdown list of IWADs if (num_iwads < 2) { // We have only one IWAD. Show as a label. result = (txt_widget_t *) TXT_NewLabel(found_iwads[0]->description); } else { // Dropdown list allowing IWAD to be selected. dropdown = TXT_NewDropdownList(&found_iwad_selected, iwad_labels, num_iwads); TXT_SignalConnect(dropdown, "changed", IWADSelected, NULL); result = (txt_widget_t *) dropdown; } // The first time the dialog is opened, found_iwad_selected=-1, // so select the first IWAD in the list. Don't lose the setting // if we close and reopen the dialog. if (found_iwad_selected < 0 || found_iwad_selected >= num_iwads) { found_iwad_selected = 0; } IWADSelected(NULL, NULL); return result; } // Create the window action button to start the game. This invokes // a different callback depending on whether to start a multiplayer // or single player game. static txt_window_action_t *StartGameAction(int multiplayer) { txt_window_action_t *action; TxtWidgetSignalFunc callback; action = TXT_NewWindowAction(KEY_F10, "Start"); if (multiplayer) { callback = StartServerGame; } else { callback = StartSinglePlayerGame; } TXT_SignalConnect(action, "pressed", callback, NULL); return action; } static void OpenWadsWindow(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data)) { txt_window_t *window; int i; window = TXT_NewWindow("Add WADs"); for (i=0; iserver_state != 0) { TXT_MessageBox("Cannot connect to server", "Gameplay is already in progress\n" "on this server."); return; } // Set address to connect to: free(connect_address); connect_address = M_StringDuplicate(button->label); // Auto-choose IWAD if there is already a player connected. if (querydata->num_players > 0) { for (i = 0; found_iwads[i] != NULL; ++i) { if (found_iwads[i]->mode == querydata->gamemode && found_iwads[i]->mission == querydata->gamemission) { found_iwad_selected = i; iwadfile = found_iwads[i]->name; break; } } if (found_iwads[i] == NULL) { TXT_MessageBox(NULL, "The game on this server seems to be:\n" "\n" " %s\n" "\n" "but the IWAD file %s is not found!\n" "Without the required IWAD file, it may not be\n" "possible to join this game.", D_SuggestGameName(querydata->gamemission, querydata->gamemode), D_SuggestIWADName(querydata->gamemission, querydata->gamemode)); } } // Finished with search. TXT_CloseWindow(query_window); } static void QueryResponseCallback(net_addr_t *addr, net_querydata_t *querydata, unsigned int ping_time, TXT_UNCAST_ARG(results_table)) { TXT_CAST_ARG(txt_table_t, results_table); char ping_time_str[16]; char description[47]; // When we connect we'll have to negotiate a common protocol that we // can agree upon between the client and server. If we can't then we // won't be able to connect, so it's pointless to include it in the // results list. If protocol==NET_PROTOCOL_UNKNOWN then this may be // an old, pre-3.0 Chocolate Doom server that doesn't support the new // protocol negotiation mechanism, or it may be an incompatible fork. if (querydata->protocol == NET_PROTOCOL_UNKNOWN) { return; } M_snprintf(ping_time_str, sizeof(ping_time_str), "%ims", ping_time); // Build description from server name field. Because there is limited // space, we only include the player count if there are already players // connected to the server. if (querydata->num_players > 0) { M_snprintf(description, sizeof(description), "(%d/%d) ", querydata->num_players, querydata->max_players); } else { M_StringCopy(description, "", sizeof(description)); } M_StringConcat(description, querydata->description, sizeof(description)); TXT_AddWidgets(results_table, TXT_NewLabel(ping_time_str), TXT_NewButton2(NET_AddrToString(addr), SelectQueryAddress, querydata), TXT_NewLabel(description), NULL); ++query_servers_found; } static void QueryPeriodicCallback(TXT_UNCAST_ARG(results_table)) { TXT_CAST_ARG(txt_table_t, results_table); if (!NET_Query_Poll(QueryResponseCallback, results_table)) { TXT_SetPeriodicCallback(NULL, NULL, 0); if (query_servers_found == 0) { TXT_AddWidgets(results_table, TXT_TABLE_EMPTY, TXT_NewLabel("No compatible servers found."), NULL ); } } } static void QueryWindowClosed(TXT_UNCAST_ARG(window), void *unused) { TXT_SetPeriodicCallback(NULL, NULL, 0); } static void ServerQueryWindow(const char *title) { txt_table_t *results_table; query_servers_found = 0; query_window = TXT_NewWindow(title); TXT_AddWidget(query_window, TXT_NewScrollPane(70, 10, results_table = TXT_NewTable(3))); TXT_SetColumnWidths(results_table, 7, 22, 40); TXT_SetPeriodicCallback(QueryPeriodicCallback, results_table, 1); TXT_SignalConnect(query_window, "closed", QueryWindowClosed, NULL); } static void FindInternetServer(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data)) { NET_StartMasterQuery(); ServerQueryWindow("Find Internet server"); } static void FindLANServer(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data)) { NET_StartLANQuery(); ServerQueryWindow("Find LAN server"); } void JoinMultiGame(TXT_UNCAST_ARG(widget), void *user_data) { txt_window_t *window; txt_inputbox_t *address_box; window = TXT_NewWindow("Join multiplayer game"); TXT_SetTableColumns(window, 2); TXT_SetColumnWidths(window, 12, 12); TXT_SetWindowHelpURL(window, MULTI_JOIN_HELP_URL); TXT_AddWidgets(window, TXT_NewLabel("Game"), IWADSelector(), NULL); if (gamemission == hexen) { TXT_AddWidgets(window, TXT_NewLabel("Character class "), TXT_NewDropdownList(&character_class, character_classes, 3), NULL); } TXT_AddWidgets(window, TXT_NewSeparator("Server"), TXT_NewLabel("Connect to address: "), address_box = TXT_NewInputBox(&connect_address, 30), TXT_NewButton2("Find server on Internet...", FindInternetServer, NULL), TXT_TABLE_OVERFLOW_RIGHT, TXT_NewButton2("Find server on local network...", FindLANServer, NULL), TXT_TABLE_OVERFLOW_RIGHT, TXT_NewStrut(0, 1), TXT_TABLE_OVERFLOW_RIGHT, TXT_NewButton2("Add extra parameters...", OpenExtraParamsWindow, NULL), NULL); TXT_SelectWidget(window, address_box); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction()); TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, JoinGameAction()); } void SetChatMacroDefaults(void) { int i; const char *const defaults[] = { HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8, HUSTR_CHATMACRO9, }; // If the chat macros have not been set, initialize with defaults. for (i=0; i<10; ++i) { if (chat_macros[i] == NULL) { chat_macros[i] = M_StringDuplicate(defaults[i]); } } } void SetPlayerNameDefault(void) { if (net_player_name == NULL) { net_player_name = NET_GetRandomPetName(); } } void MultiplayerConfig(TXT_UNCAST_ARG(widget), void *user_data) { txt_window_t *window; txt_label_t *label; txt_table_t *table; char buf[10]; int i; window = TXT_NewWindow("Multiplayer Configuration"); TXT_SetWindowHelpURL(window, MULTI_CONFIG_HELP_URL); TXT_AddWidgets(window, TXT_NewStrut(0, 1), TXT_NewHorizBox(TXT_NewLabel("Player name: "), TXT_NewInputBox(&net_player_name, 25), NULL), TXT_NewStrut(0, 1), TXT_NewSeparator("Chat macros"), NULL); table = TXT_NewTable(2); for (i=0; i<10; ++i) { M_snprintf(buf, sizeof(buf), "#%i ", i + 1); label = TXT_NewLabel(buf); TXT_SetFGColor(label, TXT_COLOR_BRIGHT_CYAN); TXT_AddWidgets(table, label, TXT_NewInputBox(&chat_macros[(i + 1) % 10], 40), NULL); } TXT_AddWidget(window, table); } void BindMultiplayerVariables(void) { char buf[15]; int i; M_BindStringVariable("player_name", &net_player_name); for (i=0; i<10; ++i) { M_snprintf(buf, sizeof(buf), "chatmacro%i", i); M_BindStringVariable(buf, &chat_macros[i]); } switch (gamemission) { case doom: M_BindChatControls(4); key_multi_msgplayer[0] = 'g'; key_multi_msgplayer[1] = 'i'; key_multi_msgplayer[2] = 'b'; key_multi_msgplayer[3] = 'r'; break; case heretic: M_BindChatControls(4); key_multi_msgplayer[0] = 'g'; key_multi_msgplayer[1] = 'y'; key_multi_msgplayer[2] = 'r'; key_multi_msgplayer[3] = 'b'; break; case hexen: M_BindChatControls(8); key_multi_msgplayer[0] = 'b'; key_multi_msgplayer[1] = 'r'; key_multi_msgplayer[2] = 'y'; key_multi_msgplayer[3] = 'g'; key_multi_msgplayer[4] = 'j'; key_multi_msgplayer[5] = 'w'; key_multi_msgplayer[6] = 'h'; key_multi_msgplayer[7] = 'p'; break; default: break; } } crispy-doom-crispy-doom-5.6.4/src/setup/multiplayer.h000066400000000000000000000016771360717211000227160ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_MULTIPLAYER_H #define SETUP_MULTIPLAYER_H void StartMultiGame(void *widget, void *user_data); void WarpMenu(void *widget, void *user_data); void JoinMultiGame(void *widget, void *user_data); void MultiplayerConfig(void *widget, void *user_data); void SetChatMacroDefaults(void); void SetPlayerNameDefault(void); void BindMultiplayerVariables(void); #endif /* #ifndef SETUP_MULTIPLAYER_H */ crispy-doom-crispy-doom-5.6.4/src/setup/setup-manifest.xml.in000066400000000000000000000033511360717211000242600ustar00rootroot00000000000000 true crispy-doom-crispy-doom-5.6.4/src/setup/setup_icon.c000066400000000000000000006327111360717211000225110ustar00rootroot00000000000000static int setup_icon_w = 128; static int setup_icon_h = 128; static const unsigned int setup_icon_data[] = { 0xce9b35ff, 0xca982eff, 0xcb972bff, 0xd4a648ff, 0xd7ab5bff, 0xd7ac5cff, 0xd7ac5eff, 0xd5a448ff, 0xc18a20ff, 0xb37a0cff, 0xb37a0cff, 0xb37606ff, 0xb97609ff, 0xdca946ff, 0xe9c16dff, 0xedc97bff, 0xf3d78fff, 0xf7db92ff, 0xeac068ff, 0xe0ae4eff, 0xd99a27ff, 0xd99617ff, 0xdea12bff, 0xe1a735ff, 0xe6b142ff, 0xe8b444ff, 0xeab647ff, 0xebb746ff, 0xebb849ff, 0xecb84aff, 0xecb94aff, 0xebb948ff, 0xeab843ff, 0xeab946ff, 0xecb63eff, 0xecb63cff, 0xecbe5dff, 0xecc571ff, 0xedc776ff, 0xeac060ff, 0xebc268ff, 0xebc679ff, 0xecc882ff, 0xe9c579ff, 0xe7bf68ff, 0xebc26eff, 0xebc374ff, 0xebc579ff, 0xeac376ff, 0xe9bd66ff, 0xe9be64ff, 0xebc47aff, 0xecc479ff, 0xeac06dff, 0xeabe66ff, 0xecc26cff, 0xebc26dff, 0xedc675ff, 0xeec67bff, 0xeec77cff, 0xeec97eff, 0xf1d391ff, 0xf0cf89ff, 0xefcb7fff, 0xeec36aff, 0xedc36fff, 0xecc36dff, 0xf3d495ff, 0xf3d69dff, 0xf1d194ff, 0xedc880ff, 0xf3d295ff, 0xf6d69dff, 0xe8b96aff, 0xe1ab48ff, 0xe3b157ff, 0xe6bd75ff, 0xe7c485ff, 0xe8c585ff, 0xe9c684ff, 0xeac98bff, 0xecc990ff, 0xeac98eff, 0xe6c688ff, 0xe8c78bff, 0xe9c98dff, 0xe6c37eff, 0xe8c686ff, 0xebc995ff, 0xedcc95ff, 0xedcc93ff, 0xefd2a2ff, 0xebcd9dff, 0xe9c98fff, 0xecd1a5ff, 0xebd2abff, 0xe9cea2ff, 0xeacfa0ff, 0xe8cea0ff, 0xe4c28aff, 0xe1ba77ff, 0xdcb268ff, 0xd3a556ff, 0xc48f44ff, 0xc68f43ff, 0xe3ae5bff, 0xeabd7bff, 0xeecd99ff, 0xe8c892ff, 0xe2c080ff, 0xe0bd78ff, 0xe3c27fff, 0xe6c792ff, 0xddba7cff, 0xd7b169ff, 0xd3ac61ff, 0xd1b070ff, 0xcead6eff, 0xc5a35eff, 0xb98b33ff, 0xa9710eff, 0xac7417ff, 0xb38124ff, 0xb58424ff, 0xb88930ff, 0xbb8e36ff, 0xbc8d2fff, 0xbd8c2bff, 0xcb972dff, 0xc9982eff, 0xcd9a2eff, 0xd4a649ff, 0xd7ab57ff, 0xd8ab5aff, 0xd8aa59ff, 0xd8a851ff, 0xca952eff, 0xb3790bff, 0xb27607ff, 0xb17305ff, 0xb46d07ff, 0xd7a13dff, 0xe3b967ff, 0xe7c075ff, 0xeac578ff, 0xf2d388ff, 0xfae1a4ff, 0xfbe7b1ff, 0xf1d28bff, 0xdfa73fff, 0xda9c26ff, 0xdc9f29ff, 0xdfa32cff, 0xe1a72fff, 0xe3ab37ff, 0xe6b13dff, 0xe8b344ff, 0xe7b44aff, 0xe9b547ff, 0xe7b544ff, 0xe7b23eff, 0xe7b23fff, 0xe8b23aff, 0xe7b034ff, 0xe8b74dff, 0xecc571ff, 0xecc87dff, 0xebc269ff, 0xeac065ff, 0xeac372ff, 0xeac372ff, 0xe7bc62ff, 0xe8bc61ff, 0xeac06dff, 0xe8bc65ff, 0xe9bd64ff, 0xe8bd65ff, 0xe7b95bff, 0xe9c06bff, 0xe9c173ff, 0xe9be69ff, 0xe8bb65ff, 0xe7b958ff, 0xeabf67ff, 0xedc473ff, 0xeec676ff, 0xefc87bff, 0xefcc84ff, 0xeec77eff, 0xefcc8aff, 0xefce85ff, 0xf0cb81ff, 0xefc978ff, 0xf1ce85ff, 0xefc97aff, 0xf1d18aff, 0xf2d28eff, 0xf2d18bff, 0xf4d694ff, 0xf9e0aaff, 0xfbe3b2ff, 0xf2cd8fff, 0xe4b053ff, 0xe1af4fff, 0xe4b864ff, 0xe7c17dff, 0xebc98fff, 0xebc991ff, 0xe9c788ff, 0xeaca8bff, 0xeaca8fff, 0xe7c589ff, 0xe6c486ff, 0xe6c481ff, 0xe5c27cff, 0xe9c785ff, 0xeac98dff, 0xeacc8fff, 0xefd099ff, 0xeccd98ff, 0xe8c78bff, 0xeaca8fff, 0xebd1a3ff, 0xead0a7ff, 0xe9cc9eff, 0xe9cea1ff, 0xe8cfa5ff, 0xe7c896ff, 0xe3c189ff, 0xdfb97fff, 0xd5a75fff, 0xc1893dff, 0xa0652bff, 0xc79044ff, 0xe8b667ff, 0xe9c080ff, 0xe7c385ff, 0xe2bf7eff, 0xdeba72ff, 0xdebb75ff, 0xe0c28bff, 0xdcbb7eff, 0xd9b777ff, 0xd3b16cff, 0xd0af6fff, 0xcead70ff, 0xc5a35eff, 0xba913dff, 0xab7413ff, 0xa5680aff, 0xac761bff, 0xaf7d1eff, 0xb27f21ff, 0xb17f1fff, 0xb8882cff, 0xbb8c30ff, 0xce9d36ff, 0xcb9a31ff, 0xce992cff, 0xd8a951ff, 0xd9ad5bff, 0xd8ac5dff, 0xd5a855ff, 0xd6aa53ff, 0xd5a447ff, 0xbf861dff, 0xae7107ff, 0xad6c05ff, 0xb36905ff, 0xce922bff, 0xdba949ff, 0xdeb155ff, 0xe4b862ff, 0xe9c277ff, 0xf1d493ff, 0xf7e1aaff, 0xfceab4ff, 0xfae2a4ff, 0xedc772ff, 0xdea537ff, 0xda9b21ff, 0xdc9e21ff, 0xdea227ff, 0xe0a529ff, 0xe3aa32ff, 0xe3ad38ff, 0xe3ab37ff, 0xe4ab34ff, 0xe3a92fff, 0xe3a931ff, 0xe2ab35ff, 0xe2a92fff, 0xe7b750ff, 0xebc470ff, 0xeac673ff, 0xebc472ff, 0xeac06cff, 0xe8bb5eff, 0xe6b857ff, 0xe6b854ff, 0xe7b857ff, 0xe9bf69ff, 0xe7bb63ff, 0xe7ba5eff, 0xe7ba5dff, 0xe7ba5bff, 0xe8bd65ff, 0xeac379ff, 0xe7bc66ff, 0xe5b759ff, 0xe7ba5cff, 0xecc473ff, 0xecc26dff, 0xecc36fff, 0xeec97dff, 0xefcd88ff, 0xefca81ff, 0xeec87bff, 0xefc875ff, 0xefc977ff, 0xf1ca7aff, 0xf6db9fff, 0xf6d89aff, 0xf5d795ff, 0xf9dfa2ff, 0xf9dfa0ff, 0xf8dd9bff, 0xfce6b2ff, 0xfce7b7ff, 0xf7d9a1ff, 0xe9bd6dff, 0xe4b358ff, 0xe7bd6fff, 0xeac483ff, 0xe9c486ff, 0xe9c486ff, 0xe7c481ff, 0xe8c681ff, 0xe8c98aff, 0xe7c786ff, 0xe4c079ff, 0xe6c380ff, 0xe9c98aff, 0xedcc92ff, 0xe8c88aff, 0xe9c989ff, 0xebcb91ff, 0xeacb91ff, 0xe9ca8fff, 0xe9c890ff, 0xe9cb96ff, 0xe8c994ff, 0xe9ca9aff, 0xe9cda1ff, 0xe7ca9eff, 0xe3c088ff, 0xdeb671ff, 0xddb475ff, 0xd6ab68ff, 0xc8954cff, 0xa76b30ff, 0xac7533ff, 0xe4b15eff, 0xeabf7aff, 0xebc58dff, 0xe8c692ff, 0xe3c186ff, 0xdeb977ff, 0xdab877ff, 0xdebc80ff, 0xdcba7fff, 0xd2b06cff, 0xcead6cff, 0xcbaa6eff, 0xc09c53ff, 0xba9140ff, 0xae7f28ff, 0xa36d14ff, 0xa36a11ff, 0xa97415ff, 0xac7719ff, 0xaa7614ff, 0xb07f20ff, 0xb38220ff, 0xcd9c37ff, 0xce9d39ff, 0xd3a242ff, 0xd7aa57ff, 0xd7ab5bff, 0xd5aa57ff, 0xd7ab58ff, 0xd9ad59ff, 0xd7a751ff, 0xcd9a35ff, 0xb07414ff, 0xa56004ff, 0xb26904ff, 0xc17e11ff, 0xd5a13dff, 0xdaa748ff, 0xddad51ff, 0xe1b65fff, 0xe9c173ff, 0xeecc83ff, 0xf4da96ff, 0xfae4a5ff, 0xfce7a5ff, 0xf8df97ff, 0xe6ba58ff, 0xd8981cff, 0xd99a19ff, 0xdc9d20ff, 0xdea025ff, 0xdfa223ff, 0xe0a328ff, 0xe1a52dff, 0xe0a42bff, 0xdea123ff, 0xdea122ff, 0xdfa62bff, 0xe8bb58ff, 0xecc57aff, 0xeac372ff, 0xeac274ff, 0xeac275ff, 0xe9bd66ff, 0xe8bc62ff, 0xe7bb60ff, 0xe6b95cff, 0xe6bb60ff, 0xe7ba60ff, 0xe7ba5dff, 0xe7b95bff, 0xe7ba63ff, 0xe7ba62ff, 0xe8bb66ff, 0xe6b656ff, 0xe5b654ff, 0xe8be66ff, 0xecc475ff, 0xebc36dff, 0xedc97eff, 0xeeca81ff, 0xedc87bff, 0xf0cf89ff, 0xefcc7eff, 0xeec876ff, 0xefc97bff, 0xf4d48fff, 0xf6db9fff, 0xf6d893ff, 0xfbe2a8ff, 0xfce7b1ff, 0xfceab3ff, 0xfce9b2ff, 0xfce8b9ff, 0xfce5aeff, 0xfbe3abff, 0xefc981ff, 0xe3b258ff, 0xe6ba69ff, 0xe8bf75ff, 0xe6bb6cff, 0xe7c079ff, 0xe8c37eff, 0xe6be72ff, 0xe4bd73ff, 0xe5bf76ff, 0xe8c37cff, 0xeecf93ff, 0xf0d4a0ff, 0xeed19cff, 0xebcd95ff, 0xebcc92ff, 0xeed39fff, 0xedd4a4ff, 0xe7c78cff, 0xe9cb95ff, 0xe9ce9cff, 0xe8cb97ff, 0xe9ca99ff, 0xe9cca0ff, 0xe5c492ff, 0xdfba7aff, 0xddb46dff, 0xdbaf67ff, 0xd6aa60ff, 0xcd9b4eff, 0xb87e39ff, 0xa46727ff, 0xd99d42ff, 0xe7b769ff, 0xe8be7dff, 0xe6bd7fff, 0xe4bf86ff, 0xe3c189ff, 0xdcb879ff, 0xdcbb7fff, 0xd8b77cff, 0xd4b273ff, 0xcaa866ff, 0xc5a561ff, 0xbe9848ff, 0xbb9142ff, 0xab7f31ff, 0x9b722aff, 0x9b691aff, 0x9d640aff, 0x9f6a0dff, 0xa67416ff, 0xaa7617ff, 0xaf7c1eff, 0xd3a54fff, 0xd4a74eff, 0xd6ab57ff, 0xd8ac5bff, 0xd5aa57ff, 0xd5aa56ff, 0xd8ad5cff, 0xd8ae5aff, 0xd6a851ff, 0xd3a142ff, 0xba8420ff, 0x9d5b04ff, 0xac6404ff, 0xbd7d12ff, 0xd09d3fff, 0xd5a347ff, 0xd6a541ff, 0xddad51ff, 0xe3b967ff, 0xe8bf6dff, 0xecc776ff, 0xf1d58eff, 0xf6de9cff, 0xfae6acff, 0xfae4a3ff, 0xe8bf63ff, 0xd99c26ff, 0xd79718ff, 0xd99818ff, 0xdb9c1bff, 0xdc9e1dff, 0xdc9e1eff, 0xdc9e1dff, 0xdb9c1aff, 0xdb9b16ff, 0xdea327ff, 0xe6b750ff, 0xebc274ff, 0xeac376ff, 0xe7bf6cff, 0xe8bd65ff, 0xe9be69ff, 0xe7ba61ff, 0xe5b552ff, 0xe6b85bff, 0xe7ba5eff, 0xe7ba5fff, 0xe7bb60ff, 0xe8bc63ff, 0xe7bc67ff, 0xe5b85eff, 0xe4b351ff, 0xe5b658ff, 0xe4b24eff, 0xe7b95eff, 0xeac06aff, 0xebc270ff, 0xecc77aff, 0xecc778ff, 0xedc776ff, 0xf0d18bff, 0xeec97cff, 0xeec775ff, 0xf0ce82ff, 0xf7dda0ff, 0xf5d894ff, 0xf7dd98ff, 0xfce6acff, 0xfdeab6ff, 0xfdecbbff, 0xfdebbbff, 0xfdefc4ff, 0xfdecbaff, 0xfdeab3ff, 0xf7dda0ff, 0xe7b860ff, 0xe3b459ff, 0xe4b55dff, 0xe4b660ff, 0xe5b869ff, 0xe5bc6fff, 0xe2b561ff, 0xe4b967ff, 0xe8bf71ff, 0xefcf8dff, 0xf3daa4ff, 0xf2dcabff, 0xf1d8a9ff, 0xeed5a2ff, 0xefd29cff, 0xefd8acff, 0xecd3a4ff, 0xe6c485ff, 0xe9cb93ff, 0xebd0a0ff, 0xe9cf9dff, 0xeacfa2ff, 0xe9cea2ff, 0xe6c492ff, 0xe3c08aff, 0xe2bc85ff, 0xddb677ff, 0xd8aa63ff, 0xd19b49ff, 0xc58939ff, 0xbb7a32ff, 0xcf8920ff, 0xe1a94bff, 0xe7ba6dff, 0xeac17fff, 0xe6bf80ff, 0xdfbc81ff, 0xd9b573ff, 0xd9b776ff, 0xdab980ff, 0xd0ac68ff, 0xc69e51ff, 0xc29d4fff, 0xbd9645ff, 0xb58b3cff, 0xa57930ff, 0x9d7840ff, 0x9c763eff, 0x96661cff, 0x98630fff, 0xa06b12ff, 0xa6751bff, 0xa8781bff, 0xd8ab59ff, 0xd7aa55ff, 0xd5a952ff, 0xd7ac55ff, 0xd9b062ff, 0xd6ad5cff, 0xd7ac5bff, 0xd8ac5aff, 0xd6a952ff, 0xd2a245ff, 0xca9533ff, 0xaf7112ff, 0xa96004ff, 0xba7a11ff, 0xcd9835ff, 0xd3a246ff, 0xd6a345ff, 0xdbaa51ff, 0xddb057ff, 0xe0b459ff, 0xe5bb63ff, 0xeac476ff, 0xefce83ff, 0xf2d892ff, 0xf8e09fff, 0xfae39dff, 0xf6da91ff, 0xebc56fff, 0xdfa63aff, 0xd7971aff, 0xd79513ff, 0xd89511ff, 0xd79610ff, 0xd79611ff, 0xd7960fff, 0xdea326ff, 0xe4b247ff, 0xe5b85cff, 0xe8bc65ff, 0xe7bb61ff, 0xe5b758ff, 0xe7bc63ff, 0xe5b759ff, 0xe3b34bff, 0xe4b554ff, 0xe5b657ff, 0xe6b95fff, 0xe7bd68ff, 0xe6b85bff, 0xe5b658ff, 0xe4b65aff, 0xe1b14aff, 0xe3b252ff, 0xe2b04cff, 0xe4b451ff, 0xe9bc60ff, 0xebc06bff, 0xecc26dff, 0xebc370ff, 0xecc36dff, 0xefcc80ff, 0xf1d38dff, 0xf3d48dff, 0xf5d894ff, 0xf6da96ff, 0xf8dd9aff, 0xfbe5a8ff, 0xfce8aeff, 0xfcebb9ff, 0xfdeec0ff, 0xfdf0bfff, 0xfdf4c8ff, 0xfdf2c8ff, 0xfdefc0ff, 0xfbe6b0ff, 0xefc87cff, 0xe1b151ff, 0xe3b458ff, 0xe4b75fff, 0xe4b760ff, 0xe3b55dff, 0xe2b55fff, 0xe7be70ff, 0xe9c274ff, 0xedcd8bff, 0xf1d497ff, 0xf1d9a2ff, 0xf0d7a3ff, 0xeed199ff, 0xeecf96ff, 0xeccf9aff, 0xe8ca91ff, 0xebcd94ff, 0xecd29eff, 0xeacd98ff, 0xe8ca93ff, 0xe8cc98ff, 0xe8ca97ff, 0xe5c08aff, 0xe6c290ff, 0xe4bf8dff, 0xe0b87fff, 0xdbaf6cff, 0xd4a04fff, 0xd0963eff, 0xc27e2eff, 0xca8420ff, 0xdc9d33ff, 0xe4b055ff, 0xe7ba6dff, 0xe7be74ff, 0xe3bd7bff, 0xdab574ff, 0xdbb87aff, 0xd9b77cff, 0xcea559ff, 0xc29742ff, 0xbe9542ff, 0xb48831ff, 0xaa7720ff, 0xa06e1dff, 0x9e7e4fff, 0x9f835bff, 0x9d7e4cff, 0x976a24ff, 0x955e09ff, 0xa16f19ff, 0xaa7c26ff, 0xd6aa54ff, 0xd4a74dff, 0xd4a646ff, 0xd6a94cff, 0xd7ad58ff, 0xd6ab5aff, 0xd8ad61ff, 0xd4a954ff, 0xd4a64dff, 0xd1a24aff, 0xd0a049ff, 0xc6902bff, 0xac6406ff, 0xb36c08ff, 0xc89029ff, 0xcd9937ff, 0xd1a041ff, 0xd3a13eff, 0xd9a948ff, 0xdcae49ff, 0xe2b65dff, 0xe4ba5eff, 0xe9c064ff, 0xedcc7fff, 0xf1d695ff, 0xf6df9cff, 0xfae6a4ff, 0xfdebb1ff, 0xfae4aaff, 0xf0ce82ff, 0xdca438ff, 0xd4900dff, 0xd38c05ff, 0xd48e08ff, 0xd5920eff, 0xdca227ff, 0xe2af3fff, 0xe3b34eff, 0xe6b95fff, 0xe7bc68ff, 0xe4b65aff, 0xe5b75bff, 0xe5b75cff, 0xe2b248ff, 0xe4b654ff, 0xe5b657ff, 0xe4b453ff, 0xe5b960ff, 0xe4b557ff, 0xe2b351ff, 0xe4b65cff, 0xe4b659ff, 0xe3b251ff, 0xe2b04aff, 0xe2b046ff, 0xe7b959ff, 0xebc171ff, 0xedc779ff, 0xecc571ff, 0xecc369ff, 0xf0cd81ff, 0xf1d38eff, 0xf4d691ff, 0xf6d993ff, 0xf7d992ff, 0xfae2a2ff, 0xfceab5ff, 0xfdeab0ff, 0xfce9acff, 0xfdebb1ff, 0xfdf2bfff, 0xfdf5c8ff, 0xfdf7d1ff, 0xfdf9d6ff, 0xfdf4cfff, 0xf6d99bff, 0xe3b254ff, 0xe4b35aff, 0xe5b75eff, 0xe2b354ff, 0xe2b355ff, 0xe2b357ff, 0xe2b459ff, 0xe4b85eff, 0xeac578ff, 0xeccd8bff, 0xeed197ff, 0xf0d39cff, 0xeccd90ff, 0xe8c683ff, 0xe6c583ff, 0xe8c68cff, 0xebcc94ff, 0xeacb92ff, 0xeacd94ff, 0xe9ca93ff, 0xe8c890ff, 0xe8c790ff, 0xe5c089ff, 0xeac79aff, 0xe7c497ff, 0xe3bb86ff, 0xddb06cff, 0xd9a558ff, 0xd5a051ff, 0xcf9852ff, 0xcf9548ff, 0xd99b35ff, 0xdfa642ff, 0xe5b25bff, 0xe6b964ff, 0xe8c37aff, 0xdfb879ff, 0xd6b06cff, 0xcfa658ff, 0xcb9e4aff, 0xc1963eff, 0xbd9241ff, 0xb2822cff, 0xa56e12ff, 0x9f6b17ff, 0x9e8057ff, 0xa18d6fff, 0xa18a68ff, 0x9f7f52ff, 0x986a22ff, 0x9d6d19ff, 0xac8237ff, 0xd6a84fff, 0xcfa041ff, 0xd4a443ff, 0xd7a94fff, 0xd6a952ff, 0xd6ab59ff, 0xd6ab5bff, 0xd1a449ff, 0xd1a144ff, 0xd5a752ff, 0xd2a553ff, 0xcb9633ff, 0xb4710cff, 0xb56f08ff, 0xc2881cff, 0xc79129ff, 0xc99228ff, 0xcf9b35ff, 0xd8a84bff, 0xdcad51ff, 0xe0b45cff, 0xe4ba63ff, 0xe7c06cff, 0xeac575ff, 0xeecd88ff, 0xf3d9a0ff, 0xf5dea5ff, 0xf7e1a5ff, 0xfae6b0ff, 0xfdebb7ff, 0xf6dc95ff, 0xdda93eff, 0xd49318ff, 0xd28e0aff, 0xd48f0aff, 0xdaa027ff, 0xdfac42ff, 0xe1af48ff, 0xe4b356ff, 0xe5b762ff, 0xe4b762ff, 0xe2b256ff, 0xe0af48ff, 0xe0ad3eff, 0xe3b24fff, 0xe3b251ff, 0xe2b14eff, 0xe3b556ff, 0xe2b350ff, 0xe2b452ff, 0xe4b65dff, 0xe3b457ff, 0xe3b252ff, 0xe3b24eff, 0xe5b655ff, 0xe7b95bff, 0xeabf6cff, 0xebc36fff, 0xebc370ff, 0xeec876ff, 0xf1d086ff, 0xf3d58fff, 0xf6db98ff, 0xf9e09fff, 0xf9df96ff, 0xfbe6a5ff, 0xfceab1ff, 0xfceaadff, 0xfdecb1ff, 0xfdf1bdff, 0xfdf4c7ff, 0xfdf7cbff, 0xfdf9d2ff, 0xfdf7cdff, 0xfdf0c1ff, 0xf6d993ff, 0xe5b658ff, 0xe1b154ff, 0xe2b050ff, 0xe0b14aff, 0xe2b556ff, 0xe3b659ff, 0xe1b257ff, 0xe1b355ff, 0xe7bf6bff, 0xe9c77fff, 0xebcb8bff, 0xedcf94ff, 0xeaca8aff, 0xe8c783ff, 0xe9c888ff, 0xe8c68bff, 0xe9c88eff, 0xeccf9bff, 0xecd29fff, 0xe9c991ff, 0xe9cb94ff, 0xe9c995ff, 0xe7c48fff, 0xe9c89aff, 0xe9c89fff, 0xe5be8fff, 0xe0b373ff, 0xdfb170ff, 0xdcad6aff, 0xd4a15cff, 0xd09c56ff, 0xd89c3fff, 0xe1a74fff, 0xe3b05eff, 0xe6b869ff, 0xe8c480ff, 0xe6c17dff, 0xd7ac5fff, 0xcc9c44ff, 0xca9d46ff, 0xc79c48ff, 0xc49d52ff, 0xba8e41ff, 0xa46b10ff, 0x9d6710ff, 0x9d783fff, 0xa58759ff, 0xa38c65ff, 0xa28b68ff, 0x9f8354ff, 0x9f7936ff, 0xa77f35ff, 0xe4bd6aff, 0xd8ab4fff, 0xd2a243ff, 0xd3a341ff, 0xd3a443ff, 0xd2a548ff, 0xd3a64cff, 0xd1a349ff, 0xd0a148ff, 0xcf9e41ff, 0xd1a047ff, 0xcb9733ff, 0xb97811ff, 0xae6704ff, 0xbe8114ff, 0xc58c23ff, 0xc79026ff, 0xcd9a3aff, 0xd4a750ff, 0xd9ac54ff, 0xdbae51ff, 0xe1b966ff, 0xe6c178ff, 0xe8c57bff, 0xeac77eff, 0xeecd8eff, 0xf2d49aff, 0xf3d89aff, 0xf4db9aff, 0xf7dfa0ff, 0xf9e29cff, 0xf9de90ff, 0xf2d07dff, 0xe7b651ff, 0xd89b24ff, 0xd99a1cff, 0xdca634ff, 0xdeac44ff, 0xe1ae4fff, 0xe3b358ff, 0xe4b561ff, 0xe1b056ff, 0xe0ad4aff, 0xddaa39ff, 0xdeab3eff, 0xe0af47ff, 0xe2b04bff, 0xe3b250ff, 0xe4b45aff, 0xe3b456ff, 0xe3b65aff, 0xe4b355ff, 0xe2b250ff, 0xe2b450ff, 0xe4b54fff, 0xe4b44dff, 0xe8bb5eff, 0xeabf62ff, 0xecc267ff, 0xf0ca79ff, 0xf3d389ff, 0xf4d489ff, 0xf6d88fff, 0xfae29dff, 0xfbe6a2ff, 0xfce7a4ff, 0xfce8a6ff, 0xfce8a4ff, 0xfceaa9ff, 0xfdefbaff, 0xfdf0bfff, 0xfdf5c9ff, 0xfdf9d2ff, 0xfdf9d2ff, 0xfdf0c0ff, 0xf1d082ff, 0xdfb04cff, 0xdfaf4cff, 0xe0af4dff, 0xe1b14dff, 0xe4b453ff, 0xe4b455ff, 0xe3b354ff, 0xe6bb67ff, 0xe8bf6eff, 0xe6be6aff, 0xe9c57bff, 0xe9c885ff, 0xe8c884ff, 0xe9c987ff, 0xebca8fff, 0xe9c88eff, 0xeacb90ff, 0xedd19dff, 0xeacd99ff, 0xe7c68cff, 0xe9c790ff, 0xe9c791ff, 0xe8c691ff, 0xe9c798ff, 0xe8c79cff, 0xe7c295ff, 0xe4bc89ff, 0xe7c395ff, 0xe2be8bff, 0xd8aa68ff, 0xcf994eff, 0xd89a40ff, 0xe5af63ff, 0xe6b771ff, 0xe5b86eff, 0xe6be75ff, 0xe7c683ff, 0xdeb66eff, 0xc99439ff, 0xc59236ff, 0xc69643ff, 0xc09140ff, 0xb28028ff, 0xa26f15ff, 0xa37424ff, 0xa77e38ff, 0xaa833fff, 0xa8833dff, 0xa9884dff, 0xa68857ff, 0xa18457ff, 0xa07d44ff, 0xe7c377ff, 0xe2b762ff, 0xd9ab52ff, 0xd7a74cff, 0xd7ab50ff, 0xd4a749ff, 0xd2a447ff, 0xd1a34aff, 0xd1a148ff, 0xd09e40ff, 0xcf9d3fff, 0xce9a38ff, 0xc08620ff, 0xaf6704ff, 0xb77609ff, 0xbd8116ff, 0xc08719ff, 0xc58e25ff, 0xcf9e45ff, 0xd3a54cff, 0xd7a745ff, 0xddaf56ff, 0xe3bc71ff, 0xe6c27dff, 0xe8c378ff, 0xeac578ff, 0xecc572ff, 0xedca7bff, 0xefd18bff, 0xf1d792ff, 0xf4da95ff, 0xf5dc95ff, 0xf5d88eff, 0xf6d27eff, 0xecbc59ff, 0xcf9234ff, 0xd3992eff, 0xd8a432ff, 0xdeac45ff, 0xdfad4bff, 0xe1af53ff, 0xdfae50ff, 0xddaa45ff, 0xdca93eff, 0xdca734ff, 0xdcaa3dff, 0xdfaf4dff, 0xe2b252ff, 0xe5b862ff, 0xe4b55aff, 0xe3b353ff, 0xe4b456ff, 0xe3b352ff, 0xe6b95eff, 0xe4b54fff, 0xe4b347ff, 0xe5b54bff, 0xe6ba55ff, 0xecc265ff, 0xefc874ff, 0xf1d184ff, 0xf4d88dff, 0xf7de98ff, 0xfae3a0ff, 0xf8df96ff, 0xfae49eff, 0xfce9abff, 0xfcecb5ff, 0xfcecb2ff, 0xfcedb4ff, 0xfdf1baff, 0xfdf5caff, 0xfdf5cbff, 0xfdefbfff, 0xf8e1a3ff, 0xe7bb5dff, 0xddaa3fff, 0xe2b454ff, 0xe5b75cff, 0xe5b456ff, 0xe6b759ff, 0xe3b151ff, 0xe4b354ff, 0xe9be6aff, 0xe8bd6cff, 0xe5ba64ff, 0xe9c579ff, 0xe8c47eff, 0xe6c27cff, 0xebca8eff, 0xedce96ff, 0xeac98eff, 0xeac989ff, 0xebcc92ff, 0xeccd95ff, 0xeccd98ff, 0xecd1a2ff, 0xe9cb9dff, 0xe4bf88ff, 0xe6c390ff, 0xe5c191ff, 0xe5bf8eff, 0xe4bc89ff, 0xe5be8dff, 0xe7c598ff, 0xe6c495ff, 0xdfb67fff, 0xdda75cff, 0xe4af62ff, 0xe6bb7bff, 0xe5bb75ff, 0xe5bb70ff, 0xe9ca8fff, 0xe7c88dff, 0xca953dff, 0xb97e1dff, 0xbd872eff, 0xaf7714ff, 0xad7d25ff, 0xb0883eff, 0xb79557ff, 0xb69554ff, 0xb3904dff, 0xb08c41ff, 0xb09148ff, 0xae8b44ff, 0xa88443ff, 0xa78344ff, 0xe7c27cff, 0xe4bb6bff, 0xe5bf76ff, 0xe5c17fff, 0xe2bc72ff, 0xe2ba6eff, 0xdeb363ff, 0xd9ac5bff, 0xd09e41ff, 0xcc9834ff, 0xd1a248ff, 0xcf9e46ff, 0xc48f2bff, 0xad6706ff, 0xb16b04ff, 0xb77609ff, 0xb97b0aff, 0xbd7e0bff, 0xc58c21ff, 0xce9b3dff, 0xd3a241ff, 0xd8a94aff, 0xddb15cff, 0xe4bd72ff, 0xe7be6eff, 0xe8c06dff, 0xe8c06cff, 0xebc269ff, 0xedc977ff, 0xeecf85ff, 0xf1d590ff, 0xf3d894ff, 0xf3d790ff, 0xf2d388ff, 0xedc46cff, 0xc88a3bff, 0xcc882bff, 0xce9432ff, 0xd6a237ff, 0xdaa73bff, 0xdaa63aff, 0xd9a639ff, 0xdaa636ff, 0xdba636ff, 0xdca737ff, 0xddab43ff, 0xdfad48ff, 0xe1b14fff, 0xe4b65dff, 0xe3b555ff, 0xe3b555ff, 0xe4b75bff, 0xe4b659ff, 0xe3b350ff, 0xe3b245ff, 0xe4b448ff, 0xe6b750ff, 0xe6b84eff, 0xe9bd57ff, 0xeec66aff, 0xf1d07eff, 0xf5d990ff, 0xf9e09dff, 0xf9df98ff, 0xf9e197ff, 0xfbe5a1ff, 0xfce8a7ff, 0xfdecb4ff, 0xfdedb3ff, 0xfdeeb0ff, 0xfdedb3ff, 0xfdeeb8ff, 0xfce9adff, 0xf9e19dff, 0xeecc78ff, 0xdcab3eff, 0xdfac42ff, 0xe3b459ff, 0xe3b457ff, 0xe6b757ff, 0xe8ba5eff, 0xe4b353ff, 0xe7b95eff, 0xe8bb66ff, 0xe7bb67ff, 0xe5ba63ff, 0xe6bd69ff, 0xe7be6fff, 0xe6be73ff, 0xeac98cff, 0xebcc93ff, 0xeaca8eff, 0xeccc8fff, 0xedd09aff, 0xecd2a1ff, 0xe9d4a9ff, 0xe8d3b0ff, 0xe1c9a3ff, 0xe2c393ff, 0xe3c292ff, 0xe5c597ff, 0xe8c89dff, 0xeacaa1ff, 0xe9c699ff, 0xe3bd85ff, 0xe2bb82ff, 0xe1bb86ff, 0xd9a047ff, 0xdea748ff, 0xe4b871ff, 0xe5bb77ff, 0xe4bd73ff, 0xe5c587ff, 0xe8cb93ff, 0xd9b370ff, 0xb27b1dff, 0xac7b1dff, 0xad812cff, 0xb28b3eff, 0xb7944fff, 0xc1a26cff, 0xc1a369ff, 0xc2a468ff, 0xbf9f59ff, 0xc1a058ff, 0xc6a65fff, 0xc8a45aff, 0xc8a156ff, 0xe3bd6bff, 0xe2b963ff, 0xe4bd72ff, 0xe3be7aff, 0xe5c27fff, 0xe8c688ff, 0xe5c17cff, 0xe6c487ff, 0xddb366ff, 0xcb952cff, 0xcd9936ff, 0xcc9739ff, 0xc38920ff, 0xae6605ff, 0xa95c04ff, 0xb16904ff, 0xb77305ff, 0xba7a07ff, 0xbf8411ff, 0xc79126ff, 0xd09e3dff, 0xd8a850ff, 0xdaa94dff, 0xdfb564ff, 0xe3b868ff, 0xe5bc6bff, 0xe6be6fff, 0xe9c072ff, 0xebc370ff, 0xecc36cff, 0xeec775ff, 0xefcc7eff, 0xf0cd7fff, 0xf1ce7fff, 0xf0ce7eff, 0xecbf5eff, 0xda9b2bff, 0xc28223ff, 0xb17a22ff, 0xd0972aff, 0xd69d29ff, 0xd79d28ff, 0xd6a12fff, 0xd8a230ff, 0xdaa636ff, 0xdcaa43ff, 0xe0ad4dff, 0xe0b052ff, 0xe3b35cff, 0xe5b65eff, 0xe3b559ff, 0xe4b557ff, 0xe4b85bff, 0xe3b455ff, 0xe3b146ff, 0xe4b347ff, 0xe4b345ff, 0xe5b546ff, 0xe8bc53ff, 0xeec76fff, 0xefcc72ff, 0xf5da8fff, 0xf7dc93ff, 0xfae19aff, 0xfce6a3ff, 0xfce8a4ff, 0xfce7a2ff, 0xfceaadff, 0xfcebaeff, 0xfdedb3ff, 0xfcefbaff, 0xfce7a7ff, 0xfae093ff, 0xf2cf7aff, 0xddaa3cff, 0xdaa42cff, 0xdeab3dff, 0xe1b14dff, 0xe5b658ff, 0xe5b653ff, 0xe7b959ff, 0xe6b654ff, 0xe8bb61ff, 0xebc172ff, 0xe9be6eff, 0xe8bd68ff, 0xe4b75bff, 0xe4b75dff, 0xe8c278ff, 0xe9c382ff, 0xe6c27dff, 0xe6c380ff, 0xe9ca8bff, 0xe8ca94ff, 0xe2cb9eff, 0xd5bd97ff, 0xa29177ff, 0x7d7463ff, 0xb49c75ff, 0xdaba8dff, 0xe0c39aff, 0xe7c9a4ff, 0xe9caa3ff, 0xe9c99cff, 0xe3bc83ff, 0xe4bf87ff, 0xe5c28cff, 0xdaa249ff, 0xdca343ff, 0xe0b05cff, 0xe4bb72ff, 0xe3bb70ff, 0xe5c384ff, 0xe6c88fff, 0xe4c792ff, 0xbc8c3bff, 0xad7f25ff, 0xb18a39ff, 0xb6934aff, 0xba9b55ff, 0xc0a466ff, 0xc5a664ff, 0xcba657ff, 0xd4ae61ff, 0xdcbd77ff, 0xdfc385ff, 0xdfc387ff, 0xe1c48dff, 0xe1b760ff, 0xdfb359ff, 0xe3b969ff, 0xe3bd75ff, 0xe6c58aff, 0xe8c792ff, 0xe3bf7eff, 0xe6c68dff, 0xe4c181ff, 0xdeb25eff, 0xcf9c36ff, 0xc79022ff, 0xc48a18ff, 0xb77309ff, 0xa75b04ff, 0xa35804ff, 0xb16a04ff, 0xb77507ff, 0xbb7b09ff, 0xc0830eff, 0xcf9b3cff, 0xd8a958ff, 0xd8a84cff, 0xdcad57ff, 0xdfb25dff, 0xe1b562ff, 0xe5ba65ff, 0xe7bb64ff, 0xe8be64ff, 0xeac16bff, 0xecc470ff, 0xefc66fff, 0xefc66fff, 0xefc871ff, 0xf1c96eff, 0xf0c561ff, 0xe6b145ff, 0xe2a836ff, 0xd39020ff, 0xca852aff, 0xd09431ff, 0xd29722ff, 0xd59e2cff, 0xd7a236ff, 0xd69e28ff, 0xd9a330ff, 0xdca942ff, 0xdca941ff, 0xdfad4eff, 0xe1b153ff, 0xe2b253ff, 0xe2b253ff, 0xe3b352ff, 0xe2b04cff, 0xe1af44ff, 0xe2b246ff, 0xe4b241ff, 0xe4b440ff, 0xe7ba50ff, 0xedc261ff, 0xefc96cff, 0xf1d07cff, 0xf4d78aff, 0xf7dc90ff, 0xf8e29cff, 0xfbe7a3ff, 0xfce7a1ff, 0xfceaa7ff, 0xfcebaaff, 0xfdf0bbff, 0xfdefbaff, 0xf9e098ff, 0xf1ce76ff, 0xe1b24aff, 0xd8a22bff, 0xdba42dff, 0xdfab39ff, 0xe8b857ff, 0xefc677ff, 0xeec471ff, 0xecc16aff, 0xe9ba59ff, 0xe9ba5aff, 0xe9bd68ff, 0xebc273ff, 0xebc476ff, 0xe9bf6eff, 0xe6b964ff, 0xe9c177ff, 0xe7bf74ff, 0xe8c27dff, 0xe6c688ff, 0xe0c385ff, 0xdbc08dff, 0xbaa47dff, 0x7c715fff, 0x8d8c89ff, 0xafb0b0ff, 0x6f695eff, 0xa8916eff, 0xd4b991ff, 0xdfc39dff, 0xe5c79eff, 0xe8cda0ff, 0xe7c490ff, 0xe7c38eff, 0xe6c48fff, 0xdeae64ff, 0xdda64dff, 0xe0b05aff, 0xe4bd78ff, 0xe5c07eff, 0xead09eff, 0xecd6aaff, 0xe9d0a1ff, 0xcba35eff, 0xb28631ff, 0xb68f3fff, 0xb9984cff, 0xbd9d55ff, 0xc49e4cff, 0xcfa34dff, 0xd9b161ff, 0xe0c282ff, 0xe1ca93ff, 0xe3ca96ff, 0xe2cb99ff, 0xe2cd9eff, 0xe3b862ff, 0xe2b75cff, 0xe1b762ff, 0xdfb55fff, 0xe3bc73ff, 0xe5c180ff, 0xdfb768ff, 0xe1bc77ff, 0xe0b970ff, 0xdfb76dff, 0xdcb264ff, 0xd2a042ff, 0xc89125ff, 0xbe8111ff, 0xa75f05ff, 0x9a4c04ff, 0xae6504ff, 0xb46f04ff, 0xb67305ff, 0xbe800eff, 0xcd9b3dff, 0xd6a959ff, 0xdaac5cff, 0xd9a94fff, 0xdaa847ff, 0xddad51ff, 0xe3b761ff, 0xe6bd68ff, 0xe7bc63ff, 0xe9be69ff, 0xe9bf67ff, 0xebc067ff, 0xecc36fff, 0xefc671ff, 0xefc66dff, 0xf0c76eff, 0xf1c566ff, 0xefbe4cff, 0xeeb63bff, 0xd3912cff, 0xb16824ff, 0xc17d22ff, 0xd1972aff, 0xd29a27ff, 0xd3991fff, 0xd59e26ff, 0xd9a336ff, 0xdaa537ff, 0xdba73aff, 0xdbaa3eff, 0xdeac43ff, 0xdfac43ff, 0xdfaf48ff, 0xdfae44ff, 0xdfac3bff, 0xe0ad3dff, 0xe3b141ff, 0xe4b342ff, 0xe7b84bff, 0xedc364ff, 0xefc667ff, 0xf1ce77ff, 0xf4d88aff, 0xf8df96ff, 0xf7dd90ff, 0xf9e3a0ff, 0xfcedb8ff, 0xfcefbaff, 0xfdf2c2ff, 0xfdf2c5ff, 0xfceaaeff, 0xf8df98ff, 0xdfb14cff, 0xd8a22bff, 0xdaa52dff, 0xdea933ff, 0xe8b953ff, 0xf0c771ff, 0xf1cc7eff, 0xf1ce83ff, 0xf1ce85ff, 0xedc269ff, 0xe8b855ff, 0xe9bb5eff, 0xebc16cff, 0xecc473ff, 0xecc680ff, 0xeac277ff, 0xe8bd6dff, 0xe7be70ff, 0xe4be79ff, 0xdbb975ff, 0xcdaf75ff, 0x9b8764ff, 0x716b5fff, 0xabababff, 0xe0e1e0ff, 0xe5e6e4ff, 0xbcbdbcff, 0x6a645aff, 0xa08764ff, 0xd0b085ff, 0xdebf90ff, 0xe7ca9aff, 0xeacc99ff, 0xeac995ff, 0xe6c690ff, 0xddac5eff, 0xdfab57ff, 0xe4bd7cff, 0xe6c58dff, 0xe1bd7fff, 0xe2c285ff, 0xead6acff, 0xead6acff, 0xd2ad6cff, 0xb88e3eff, 0xba9445ff, 0xbd9a4cff, 0xc8a049ff, 0xd3a240ff, 0xd8ad5aff, 0xddbb75ff, 0xe0c48cff, 0xe0c897ff, 0xe0cb9aff, 0xe1cda0ff, 0xe0ca9eff, 0xe6bb68ff, 0xe3b964ff, 0xe0b459ff, 0xdfb050ff, 0xe4bb6dff, 0xe4bd73ff, 0xe4ba6bff, 0xe0b665ff, 0xddb25dff, 0xdeb667ff, 0xe1bc7aff, 0xdcb264ff, 0xd29f41ff, 0xc89029ff, 0xa8630eff, 0x954504ff, 0xab5f04ff, 0xaf6905ff, 0xb57104ff, 0xbb7a07ff, 0xc6902aff, 0xd1a24dff, 0xd7ab5cff, 0xd8ab55ff, 0xdbaa4bff, 0xddad51ff, 0xe0b564ff, 0xe2b766ff, 0xe5ba64ff, 0xe7bc68ff, 0xe8bd62ff, 0xe7bb5cff, 0xe9bd5eff, 0xecbf5fff, 0xeec160ff, 0xeec362ff, 0xefc463ff, 0xf0c45eff, 0xf0c25dff, 0xedbc54ff, 0xd1912eff, 0xb67a2cff, 0xd59f48ff, 0xd59b2fff, 0xd39822ff, 0xd19821ff, 0xd49d27ff, 0xd79f2fff, 0xd7a12bff, 0xdaa637ff, 0xdcab40ff, 0xddab3fff, 0xdca839ff, 0xdda834ff, 0xdeaa38ff, 0xdfac3dff, 0xe1af3fff, 0xe4b344ff, 0xe6b74dff, 0xedc366ff, 0xf0ca72ff, 0xf0cb72ff, 0xf2d280ff, 0xf7e19bff, 0xfae49fff, 0xfbe7a8ff, 0xfdedb9ff, 0xfcedb8ff, 0xfcedb8ff, 0xfbe6aaff, 0xf8dd93ff, 0xe8c46cff, 0xd7a129ff, 0xdaa329ff, 0xdea832ff, 0xe6b64bff, 0xeec366ff, 0xf0cc7dff, 0xf2d087ff, 0xf3d38cff, 0xf1cf8aff, 0xedc26dff, 0xe9b959ff, 0xedc16bff, 0xecc16eff, 0xeabe66ff, 0xecc475ff, 0xecc780ff, 0xe7bd72ff, 0xe1bc77ff, 0xd5b16bff, 0xb99a5cff, 0x796a4bff, 0x7e7b75ff, 0xcacbcaff, 0xe4e5e3ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe4e4e3ff, 0xbbbcbbff, 0x696258ff, 0xa18a68ff, 0xd0b281ff, 0xdfc08fff, 0xe6ca97ff, 0xeacf9eff, 0xe8ca98ff, 0xddac61ff, 0xe0b369ff, 0xe5c187ff, 0xe0bc81ff, 0xdbb56fff, 0xddb972ff, 0xe7cfa0ff, 0xe7cf9fff, 0xd6b16fff, 0xb88e3fff, 0xb48d3bff, 0xc39b44ff, 0xd6ab4eff, 0xdcb35cff, 0xe1bf80ff, 0xe2c68fff, 0xdfc28aff, 0xe0c694ff, 0xdec792ff, 0xdfc895ff, 0xe1c896ff, 0xe5b960ff, 0xe5bc69ff, 0xe4b960ff, 0xe4b85eff, 0xe6bd71ff, 0xe5bf76ff, 0xe7c582ff, 0xe5c17aff, 0xdfb466ff, 0xd9aa55ff, 0xdbae60ff, 0xddb165ff, 0xdaad5aff, 0xd8aa55ff, 0xc48e32ff, 0x8f4308ff, 0xa45704ff, 0xae6605ff, 0xb26c04ff, 0xb97907ff, 0xc08517ff, 0xca973bff, 0xd3a451ff, 0xd5a74dff, 0xdbaf59ff, 0xdcae57ff, 0xdbae54ff, 0xddb15aff, 0xe2b65eff, 0xe3b761ff, 0xe3b456ff, 0xe4b554ff, 0xe6b856ff, 0xe8b955ff, 0xebbd5dff, 0xecbf5eff, 0xecbe57ff, 0xedbd57ff, 0xeebf5aff, 0xf0c362ff, 0xf0c055ff, 0xeaba50ff, 0xebbe5fff, 0xe5b255ff, 0xe4b453ff, 0xcf901bff, 0xcc8c0fff, 0xcf8e10ff, 0xd49a20ff, 0xd8a030ff, 0xdaa538ff, 0xdca83bff, 0xdba632ff, 0xdca632ff, 0xdda62fff, 0xdfaa39ff, 0xe1ae3eff, 0xe2b143ff, 0xe5b74eff, 0xe9bd5aff, 0xedc463ff, 0xf0cb70ff, 0xf3d485ff, 0xf4da8eff, 0xf8e29aff, 0xfbe7a7ff, 0xfae7a8ff, 0xf9e4a0ff, 0xf8de95ff, 0xf6d582ff, 0xebc467ff, 0xdaa537ff, 0xd9a127ff, 0xdda52bff, 0xe4b445ff, 0xeabc57ff, 0xefc66cff, 0xf0cb7bff, 0xf2d086ff, 0xf3d48dff, 0xf2d494ff, 0xf0ca80ff, 0xecbe62ff, 0xecbf65ff, 0xecbf6aff, 0xeabc65ff, 0xeac16fff, 0xe6bf72ff, 0xdfba70ff, 0xd1b171ff, 0xa1885aff, 0x6a614dff, 0x9c9c9bff, 0xdddddcff, 0xe3e4e2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e3ff, 0xbbbcbbff, 0x696359ff, 0xa18964ff, 0xd1b484ff, 0xdebf8dff, 0xe6cb9aff, 0xe9cd9fff, 0xdfb36dff, 0xe0b36bff, 0xe5c188ff, 0xe5c28cff, 0xe3c189ff, 0xe4c68dff, 0xe7cb96ff, 0xe6cb91ff, 0xdab775ff, 0xbd9242ff, 0xbc903aff, 0xd7ae5aff, 0xe3bf79ff, 0xe3c48aff, 0xe3c99aff, 0xe2c89aff, 0xe0c388ff, 0xe1c895ff, 0xdcc28bff, 0xdec187ff, 0xdcbe7cff, 0xe8bf70ff, 0xe7c278ff, 0xe4bb67ff, 0xe5bd6fff, 0xe7c278ff, 0xe9c785ff, 0xe8c586ff, 0xe6c383ff, 0xe3bd78ff, 0xe0b873ff, 0xd39f49ff, 0xd3a14eff, 0xdbb167ff, 0xdcb46cff, 0xd8ad5aff, 0xac7027ff, 0x9f5104ff, 0xad6304ff, 0xb06c04ff, 0xb67305ff, 0xbc7c0aff, 0xc58d27ff, 0xce9a3eff, 0xd1a242ff, 0xd6a951ff, 0xdaac56ff, 0xd9a849ff, 0xdbad53ff, 0xddb058ff, 0xdeaf50ff, 0xdfad46ff, 0xdfae46ff, 0xe2b14aff, 0xe3b348ff, 0xe4b44bff, 0xe6b54cff, 0xe8b549ff, 0xe8b648ff, 0xeab950ff, 0xebbb53ff, 0xedbc54ff, 0xeebe57ff, 0xf2c769ff, 0xf4d17bff, 0xf4d07bff, 0xeabd63ff, 0xddac4bff, 0xcd8d15ff, 0xcf9219ff, 0xd39b2aff, 0xd69f2eff, 0xd7a32fff, 0xd8a22cff, 0xd9a129ff, 0xdaa52eff, 0xdda83aff, 0xe1ae4aff, 0xe2b147ff, 0xe4b64eff, 0xe7bb55ff, 0xebc05aff, 0xefc86aff, 0xf5d992ff, 0xf5db91ff, 0xf5dc90ff, 0xf7df9aff, 0xfbe7abff, 0xf8e19dff, 0xf4d480ff, 0xeac160ff, 0xdeb358ff, 0xdaa73cff, 0xdda730ff, 0xe2ad35ff, 0xe6b74aff, 0xebbd5aff, 0xefc770ff, 0xf0ce83ff, 0xf3d592ff, 0xf4d794ff, 0xf2d393ff, 0xefc87cff, 0xedc166ff, 0xebbc61ff, 0xecc16cff, 0xebc371ff, 0xe5c074ff, 0xdbb567ff, 0xc4a462ff, 0x82714dff, 0x737069ff, 0xbfc0bfff, 0xe3e4e3ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0x9e9f9dff, 0x615b4fff, 0xa68d65ff, 0xd5b481ff, 0xe0c391ff, 0xe4c593ff, 0xdcaf64ff, 0xdeb064ff, 0xe3be83ff, 0xe4c28aff, 0xe6c892ff, 0xe5c890ff, 0xe6c88cff, 0xe8cc90ff, 0xdfbd79ff, 0xc59745ff, 0xd0a756ff, 0xe5c485ff, 0xe7c999ff, 0xe7cfa5ff, 0xe5cea1ff, 0xe0c48aff, 0xe2c68aff, 0xe0c793ff, 0xddc392ff, 0xdcc18cff, 0xdec283ff, 0xe7c37cff, 0xe6bf70ff, 0xe6bd68ff, 0xe6bb68ff, 0xe8c277ff, 0xe9c785ff, 0xe7c587ff, 0xe4c180ff, 0xe1b96fff, 0xe2bb78ff, 0xdaae62ff, 0xd29b40ff, 0xdaae62ff, 0xdab26bff, 0xdab164ff, 0xd2a450ff, 0xa96410ff, 0xa95f04ff, 0xad6604ff, 0xb26c04ff, 0xb87706ff, 0xc48b24ff, 0xce993aff, 0xd1a243ff, 0xd3a54bff, 0xd6a547ff, 0xd7a642ff, 0xd7a53eff, 0xd8a843ff, 0xdca947ff, 0xddaa44ff, 0xdca93fff, 0xdbaa3fff, 0xdfac40ff, 0xdfab3aff, 0xe1ad40ff, 0xe2ac39ff, 0xe3ad3bff, 0xe5af40ff, 0xe7b03fff, 0xe7b03cff, 0xe9b549ff, 0xecbb5aff, 0xf0c86fff, 0xf4d486ff, 0xf6d485ff, 0xf9db8cff, 0xeeca74ff, 0xd49c34ff, 0xcb8e17ff, 0xd09521ff, 0xd29720ff, 0xd49a20ff, 0xd69d24ff, 0xd9a435ff, 0xdaa434ff, 0xdba635ff, 0xe0ac42ff, 0xe5b451ff, 0xe8bc5cff, 0xebc160ff, 0xedc465ff, 0xf0cf7bff, 0xf5da92ff, 0xf5dc92ff, 0xf8e3a7ff, 0xf9e6b2ff, 0xf3d589ff, 0xf0cb70ff, 0xdcac43ff, 0xd59d28ff, 0xdba431ff, 0xe0ab35ff, 0xe3b242ff, 0xe6b74dff, 0xeabe5bff, 0xeec56cff, 0xf0d18bff, 0xf1d38eff, 0xf4d798ff, 0xf4d898ff, 0xf1ce87ff, 0xf1c977ff, 0xf0c977ff, 0xecca7fff, 0xe4c079ff, 0xd7b56eff, 0xab9056ff, 0x6d6249ff, 0x8e8e8cff, 0xd7d8d7ff, 0xe4e4e3ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdfe0deff, 0xb6b7b4ff, 0x92948fff, 0x737573ff, 0x6d604eff, 0xcdb082ff, 0xdbbb83ff, 0xdeb97aff, 0xd9a650ff, 0xdcaa50ff, 0xe0b871ff, 0xe0bd7cff, 0xe3c388ff, 0xdfbf7eff, 0xe3c684ff, 0xe4c684ff, 0xd9b061ff, 0xcd9b3fff, 0xdfb771ff, 0xe5c48bff, 0xe5c896ff, 0xe7cfa3ff, 0xe5ca94ff, 0xe1c383ff, 0xe4ca95ff, 0xe0c68fff, 0xd9b97dff, 0xdbbc81ff, 0xdebf86ff, 0xe4b964ff, 0xe4b861ff, 0xe7c06fff, 0xe5be6bff, 0xe6bf71ff, 0xe6c17bff, 0xe5c383ff, 0xe2bc74ff, 0xe1b86dff, 0xe0b870ff, 0xdeb66cff, 0xdbaf5aff, 0xdaaf60ff, 0xd9b167ff, 0xd9ad63ff, 0xd6ab5bff, 0xbe872dff, 0xa85f05ff, 0xab6304ff, 0xaf6904ff, 0xb46f04ff, 0xc0851eff, 0xcc9a3bff, 0xd2a64eff, 0xd3a652ff, 0xd5a751ff, 0xd5a543ff, 0xd7a43aff, 0xdaa743ff, 0xdaa845ff, 0xdaa847ff, 0xdaa73fff, 0xd9a637ff, 0xdaa83dff, 0xdca83dff, 0xdca839ff, 0xdba42bff, 0xdda328ff, 0xdea229ff, 0xdea022ff, 0xdda126ff, 0xe1a838ff, 0xe5af41ff, 0xe9b852ff, 0xeec46bff, 0xf3cd73ff, 0xf7d67eff, 0xfae195ff, 0xf5dc97ff, 0xe6bb5fff, 0xe1b04eff, 0xd59b2eff, 0xcd9018ff, 0xcf9317ff, 0xd39721ff, 0xd59c23ff, 0xd89f26ff, 0xdca533ff, 0xdfac3dff, 0xe4b54eff, 0xe9be5bff, 0xedc465ff, 0xeec76aff, 0xf4d68dff, 0xf5da93ff, 0xf5dd9bff, 0xf5dc9aff, 0xf1d07aff, 0xe5b857ff, 0xd59d2bff, 0xd7a02bff, 0xdca52fff, 0xe0ac34ff, 0xe2b344ff, 0xe6b953ff, 0xe9bc59ff, 0xecc369ff, 0xeecc80ff, 0xf0d18eff, 0xefcf8dff, 0xf2d393ff, 0xefce86ff, 0xefcc7bff, 0xebca82ff, 0xe0c07aff, 0xcba862ff, 0x8b7548ff, 0x6b655aff, 0xb2b3b2ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdfe0deff, 0xb5b6b3ff, 0x8d8f8aff, 0x828480ff, 0x535047ff, 0x8e7a5bff, 0xcaaf82ff, 0xd9b87eff, 0xdab269ff, 0xd8a343ff, 0xdeaf5cff, 0xe0b976ff, 0xdeb566ff, 0xdfbc70ff, 0xe0bf77ff, 0xe2c27eff, 0xddba70ff, 0xd2a146ff, 0xd39c35ff, 0xdaa854ff, 0xe7c489ff, 0xe1c288ff, 0xe4c893ff, 0xe5ca91ff, 0xe5c98cff, 0xe2c487ff, 0xddbf83ff, 0xdfc288ff, 0xe1c590ff, 0xdfbe80ff, 0xe4b75eff, 0xe5ba65ff, 0xe5be6dff, 0xe6be6cff, 0xe8c788ff, 0xe7c487ff, 0xe5c282ff, 0xe1ba70ff, 0xdfb565ff, 0xddb260ff, 0xddb464ff, 0xddb466ff, 0xdbb368ff, 0xdab26aff, 0xd9af66ff, 0xd5aa5cff, 0xca9a3cff, 0xac680aff, 0xa96104ff, 0xae6804ff, 0xb16d04ff, 0xbd831cff, 0xc89432ff, 0xd0a24aff, 0xd6ab5fff, 0xd2a557ff, 0xd1a248ff, 0xd7a64aff, 0xd8a94aff, 0xd7a743ff, 0xd8a541ff, 0xd9a640ff, 0xd7a333ff, 0xd7a12fff, 0xd7a029ff, 0xd79f29ff, 0xd69b1dff, 0xd89b21ff, 0xd9981dff, 0xd89615ff, 0xd69310ff, 0xd89719ff, 0xdba12aff, 0xe0a42fff, 0xe4ae3eff, 0xe9b546ff, 0xefc35bff, 0xf6d47eff, 0xf9e39cff, 0xfce6a1ff, 0xfdebaeff, 0xf7e0a4ff, 0xe9c06fff, 0xcf9224ff, 0xca860aff, 0xce8f13ff, 0xd19316ff, 0xd5981aff, 0xdba42dff, 0xe1ae3fff, 0xe3b74bff, 0xe9c15bff, 0xedc562ff, 0xf0ce7dff, 0xf0cf80ff, 0xefce7dff, 0xeecb77ff, 0xe6bc5eff, 0xdaa742ff, 0xd49c27ff, 0xdaa332ff, 0xdba329ff, 0xe1ae3bff, 0xe5b54dff, 0xe6b753ff, 0xe9bb5aff, 0xedc66fff, 0xedcb7fff, 0xefce87ff, 0xf1d18bff, 0xeecc84ff, 0xecc87bff, 0xe7c270ff, 0xdbb86eff, 0xb69a5eff, 0x716344ff, 0x807f7cff, 0xcfd0cfff, 0xe4e5e3ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdfe0ddff, 0xb4b5b2ff, 0x8d8f8aff, 0x828481ff, 0x4d4b44ff, 0x6f5d3aff, 0xaf9569ff, 0xccaf7cff, 0xd8b370ff, 0xd5a751ff, 0xd7a242ff, 0xdcac58ff, 0xdeb266ff, 0xd7a744ff, 0xd9b04dff, 0xe1be70ff, 0xe4c687ff, 0xdab36bff, 0xd49c3eff, 0xdaa23bff, 0xdfb056ff, 0xe7c485ff, 0xe9c993ff, 0xe6c78cff, 0xe4c68bff, 0xe5c88eff, 0xe4c68dff, 0xd8b879ff, 0xd8b776ff, 0xd9b879ff, 0xdbbb7aff, 0xe6c072ff, 0xe6c279ff, 0xe4ba67ff, 0xe5bf72ff, 0xe5c383ff, 0xe2bc73ff, 0xe1b969ff, 0xe2bc72ff, 0xe0b769ff, 0xdbaf56ff, 0xdaad55ff, 0xdeb466ff, 0xdeb670ff, 0xdab066ff, 0xd6ac5eff, 0xd4a959ff, 0xc79435ff, 0xa7610cff, 0xa55a04ff, 0xac6304ff, 0xb16d04ff, 0xb7790dff, 0xc38c23ff, 0xcd9c3eff, 0xd5aa5aff, 0xd3a857ff, 0xcf9f47ff, 0xd3a54eff, 0xd8a74aff, 0xd7a641ff, 0xd8a544ff, 0xd8a542ff, 0xd5a134ff, 0xd59c25ff, 0xd3981bff, 0xd39518ff, 0xd39416ff, 0xd39112ff, 0xd39115ff, 0xd38e10ff, 0xd38e0cff, 0xd18d0dff, 0xd49110ff, 0xd79413ff, 0xda9611ff, 0xdb9915ff, 0xde9f21ff, 0xebb94cff, 0xf3cf79ff, 0xf9de94ff, 0xfce6a5ff, 0xfdeeb7ff, 0xfdedb7ff, 0xf1d188ff, 0xd1962aff, 0xc58105ff, 0xcb8b14ff, 0xd0941dff, 0xd49922ff, 0xdaa533ff, 0xe1af41ff, 0xe7bc54ff, 0xeac161ff, 0xedc66bff, 0xefca75ff, 0xecc364ff, 0xe6b954ff, 0xd9a642ff, 0xd9a43bff, 0xdaa538ff, 0xdaa530ff, 0xdca72cff, 0xe0b03eff, 0xe2b349ff, 0xe6b856ff, 0xe9bf64ff, 0xebc267ff, 0xedc978ff, 0xefce86ff, 0xeed08cff, 0xe9ca84ff, 0xe2c47fff, 0xd2b36dff, 0x978250ff, 0x676152ff, 0xa4a5a4ff, 0xdfe0deff, 0xe3e4e2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdedfddff, 0xb3b5b1ff, 0x8d8f8aff, 0x828481ff, 0x4b4a45ff, 0x655943ff, 0x9c865dff, 0xbca070ff, 0xd1b179ff, 0xd9b672ff, 0xd5a64eff, 0xd5a244ff, 0xd8a850ff, 0xdbac56ff, 0xdaac4cff, 0xdfb865ff, 0xe5c784ff, 0xe9cf97ff, 0xe0bb7dff, 0xdba751ff, 0xe2b15dff, 0xe5bd6dff, 0xecca8dff, 0xefd19cff, 0xeacc93ff, 0xe1c283ff, 0xdfc080ff, 0xdfc081ff, 0xdebf7eff, 0xdab976ff, 0xdab875ff, 0xdcbb77ff, 0xe5be72ff, 0xe5be71ff, 0xe3ba65ff, 0xe4be72ff, 0xe2bb70ff, 0xe0b664ff, 0xdfb561ff, 0xdfb564ff, 0xdcaf58ff, 0xdcaf57ff, 0xe0ba74ff, 0xe1bc7dff, 0xdeb670ff, 0xdab064ff, 0xd8ad62ff, 0xd3a855ff, 0xce9d40ff, 0xc58d2eff, 0xb17012ff, 0xa96205ff, 0xaf6c05ff, 0xb27104ff, 0xbe8315ff, 0xcc9a3aff, 0xd1a34bff, 0xd3a653ff, 0xd0a34fff, 0xd1a452ff, 0xd3a348ff, 0xd7a545ff, 0xd5a23dff, 0xd39e2dff, 0xd29c29ff, 0xd3971eff, 0xd59b24ff, 0xda9d25ff, 0xdb9d24ff, 0xd69518ff, 0xd49116ff, 0xd28b0eff, 0xd08a0fff, 0xcf8a0dff, 0xd0880bff, 0xd08806ff, 0xd38a06ff, 0xd38906ff, 0xd18805ff, 0xd9961cff, 0xe9b750ff, 0xf2c969ff, 0xf6d37bff, 0xf8df96ff, 0xfae8abff, 0xfdecb2ff, 0xf1d791ff, 0xd8a84eff, 0xd59f3cff, 0xcc8c1eff, 0xcb8a11ff, 0xd29620ff, 0xdaa22aff, 0xe2b245ff, 0xeac265ff, 0xedc972ff, 0xeec86dff, 0xeac25cff, 0xe4b445ff, 0xdba942ff, 0xd9a83fff, 0xdba83bff, 0xdaa52fff, 0xdba62cff, 0xe0ae39ff, 0xe2b246ff, 0xe6b958ff, 0xe9be64ff, 0xecc672ff, 0xedcc82ff, 0xeacb85ff, 0xe5c57fff, 0xdbb96eff, 0xbc9e5cff, 0x766848ff, 0x75736fff, 0xc5c6c6ff, 0xe4e4e3ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdedfddff, 0xb3b4b1ff, 0x8d8f8aff, 0x828481ff, 0x4c4b45ff, 0x645d49ff, 0x9a8e74ff, 0xbbab89ff, 0xd1b88dff, 0xdfc293ff, 0xdfbc7cff, 0xd7a852ff, 0xd19d37ff, 0xd5a341ff, 0xdaa84bff, 0xdbad50ff, 0xe2bf71ff, 0xe7c889ff, 0xebd39dff, 0xe3c07cff, 0xdda74dff, 0xe2b25dff, 0xe7c27eff, 0xeacb8fff, 0xefd29fff, 0xedd09bff, 0xe1bf79ff, 0xddbc76ff, 0xe3c584ff, 0xe5c88bff, 0xe4c78dff, 0xdfbe7bff, 0xdcb86eff, 0xe5bd70ff, 0xe2b966ff, 0xe2b660ff, 0xe2b869ff, 0xe1b566ff, 0xdfb462ff, 0xe1b769ff, 0xddae56ff, 0xdaab50ff, 0xdeb568ff, 0xdfbc79ff, 0xddb872ff, 0xdab265ff, 0xd8af5fff, 0xdab46dff, 0xd6ad5fff, 0xcd9e43ff, 0xcc9839ff, 0xc38d21ff, 0xb2700cff, 0xaa6304ff, 0xb06c04ff, 0xb87a0cff, 0xc79530ff, 0xcea042ff, 0xd2a755ff, 0xd1a558ff, 0xd3a85dff, 0xd0a24dff, 0xd2a142ff, 0xd29e31ff, 0xd29c2dff, 0xd19924ff, 0xd4981fff, 0xe1a631ff, 0xe6ad3cff, 0xe6a831ff, 0xe8ad3dff, 0xe7aa38ff, 0xebb449ff, 0xe8b54fff, 0xe5ad45ff, 0xe1a231ff, 0xdd9c21ff, 0xde9e25ff, 0xd89315ff, 0xd69017ff, 0xd58f18ff, 0xdfa234ff, 0xe4b249ff, 0xe8b74bff, 0xeec05bff, 0xf2d178ff, 0xf7db93ff, 0xfae39dff, 0xfbe79fff, 0xf9e09cff, 0xeeca7dff, 0xd9a343ff, 0xcd8f22ff, 0xd0921eff, 0xdaa42fff, 0xe6b854ff, 0xedc66cff, 0xeec974ff, 0xecc873ff, 0xe9c062ff, 0xddac47ff, 0xddaa43ff, 0xdeab41ff, 0xdaa42eff, 0xdaa429ff, 0xe0ad3aff, 0xe3b54bff, 0xe7bb5aff, 0xeac168ff, 0xeac472ff, 0xe8c881ff, 0xe2c481ff, 0xd6ba7aff, 0x9e8650ff, 0x645b46ff, 0x969795ff, 0xdadbdaff, 0xe3e4e2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdedfddff, 0xb2b3b0ff, 0x8d8f8aff, 0x828481ff, 0x4c4b47ff, 0x645d4bff, 0x9b9179ff, 0xbbb097ff, 0xd5c6a8ff, 0xe1cca4ff, 0xe9d3acff, 0xe1bd81ff, 0xd39e42ff, 0xd19b39ff, 0xd2a042ff, 0xdbae54ff, 0xe0b868ff, 0xe4c47fff, 0xe4c47fff, 0xe5c784ff, 0xe3bc6fff, 0xe1b056ff, 0xe4ba6cff, 0xeccc90ff, 0xedcd93ff, 0xeacb93ff, 0xe8c98fff, 0xe6c684ff, 0xe6c684ff, 0xe7c88bff, 0xe4c887ff, 0xe1c381ff, 0xdeba6fff, 0xdeb96dff, 0xe5bc6fff, 0xe2b96aff, 0xe3b868ff, 0xe2b868ff, 0xe1b562ff, 0xe0b665ff, 0xe0b665ff, 0xe0b669ff, 0xdfb975ff, 0xddb871ff, 0xddbb75ff, 0xdcb770ff, 0xdab366ff, 0xd8af60ff, 0xdab269ff, 0xd9b068ff, 0xd2a653ff, 0xcb9838ff, 0xc48e21ff, 0xb97e10ff, 0xa86005ff, 0xab6304ff, 0xb57508ff, 0xc28c23ff, 0xcd9d3fff, 0xd2a856ff, 0xd5ac63ff, 0xd4aa61ff, 0xd0a454ff, 0xcd9e42ff, 0xd19e35ff, 0xd19b2bff, 0xcf951dff, 0xcf941aff, 0xd5971cff, 0xd7971aff, 0xd68d09ff, 0xdb951aff, 0xdf9d2aff, 0xe3a740ff, 0xe7b246ff, 0xedbd63ff, 0xe7b14eff, 0xe1a334ff, 0xd99018ff, 0xdc9211ff, 0xe3a328ff, 0xe7af3dff, 0xeec265ff, 0xefc86dff, 0xedc461ff, 0xefc55eff, 0xefc55dff, 0xeabd53ff, 0xe6b74fff, 0xedc563ff, 0xf3d383ff, 0xf6d88fff, 0xf2ce7aff, 0xe6b454ff, 0xcf8f29ff, 0xc98712ff, 0xdda834ff, 0xe5b74fff, 0xebc46bff, 0xedc873ff, 0xe9c162ff, 0xdeac46ff, 0xdfae4bff, 0xdead45ff, 0xdba93bff, 0xdca939ff, 0xdfaf3eff, 0xe4b651ff, 0xe6ba5aff, 0xe7c16cff, 0xe3c379ff, 0xddbf7aff, 0xc4ab6eff, 0x7d6f4cff, 0x6b6961ff, 0xbabbbbff, 0xe3e3e2ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdedfddff, 0xb1b3afff, 0x8d8e8aff, 0x828481ff, 0x4c4b46ff, 0x656051ff, 0x9b9178ff, 0xbcb093ff, 0xd5c8acff, 0xe3d3b1ff, 0xead6acff, 0xecd7afff, 0xe2c085ff, 0xd6a451ff, 0xd5a249ff, 0xd8a74dff, 0xdfb569ff, 0xe6c58aff, 0xe4c17dff, 0xe1b969ff, 0xe0ba6aff, 0xe5ba67ff, 0xe3b864ff, 0xe6c07bff, 0xe7c589ff, 0xe7c889ff, 0xe4c280ff, 0xe0be75ff, 0xe1be76ff, 0xe4c37dff, 0xe8c885ff, 0xe7c782ff, 0xe5c681ff, 0xdeba70ff, 0xe1bc72ff, 0xe5bf78ff, 0xe1b561ff, 0xdfb25aff, 0xe2b869ff, 0xe0b668ff, 0xdeb25bff, 0xdcaf55ff, 0xe2bd7dff, 0xe0bc7fff, 0xdcb366ff, 0xdcb266ff, 0xdcb46bff, 0xdab36dff, 0xd8b067ff, 0xd7af68ff, 0xd4ac5fff, 0xd2a750ff, 0xca9835ff, 0xc58f26ff, 0xbe8718ff, 0xab6506ff, 0xa86004ff, 0xb16f05ff, 0xbd831aff, 0xcb993cff, 0xcea047ff, 0xd3a858ff, 0xd2a654ff, 0xcfa24eff, 0xcc9c42ff, 0xd09c37ff, 0xd19b2dff, 0xce9623ff, 0xcd931cff, 0xcf9626ff, 0xcf9528ff, 0xcb8a0fff, 0xce8809ff, 0xce8206ff, 0xce7e04ff, 0xd1830cff, 0xd89423ff, 0xe1a32fff, 0xe5ab3dff, 0xe4ab45ff, 0xe0a23dff, 0xe7b557ff, 0xe9bb60ff, 0xe7b557ff, 0xe6b043ff, 0xe7b342ff, 0xebc164ff, 0xeec974ff, 0xeec96cff, 0xebc263ff, 0xe7b64cff, 0xe7b953ff, 0xecc66aff, 0xedc464ff, 0xe9bd5bff, 0xe4b44eff, 0xd99f30ff, 0xd89e29ff, 0xdca630ff, 0xe3b54dff, 0xe5b452ff, 0xe5b852ff, 0xdead48ff, 0xdfaf4dff, 0xe2b450ff, 0xe3b456ff, 0xe3b350ff, 0xe1b347ff, 0xe5b753ff, 0xe7be5dff, 0xe7c578ff, 0xdcbf7aff, 0xac965fff, 0x665e48ff, 0x888887ff, 0xd4d5d4ff, 0xe3e4e2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdddedcff, 0xb0b2aeff, 0x8c8e89ff, 0x828481ff, 0x4c4b46ff, 0x655e4cff, 0x9c937aff, 0xbfb397ff, 0xd8ccb0ff, 0xe7dabdff, 0xeddebaff, 0xeddcb3ff, 0xe6cb9bff, 0xd8ad68ff, 0xd8aa63ff, 0xdcb068ff, 0xddb365ff, 0xdeb86cff, 0xe0bb76ff, 0xe2c181ff, 0xe1bd75ff, 0xdfb462ff, 0xe4b869ff, 0xe6bf75ff, 0xe5c179ff, 0xe1bb73ff, 0xdeb96dff, 0xe0b968ff, 0xe4bf71ff, 0xe4bf72ff, 0xe5c27cff, 0xe3c075ff, 0xe6c278ff, 0xe7c680ff, 0xe1bc6fff, 0xdeb663ff, 0xe4c07cff, 0xe1b768ff, 0xe0b15cff, 0xe2b870ff, 0xdfb464ff, 0xdeb364ff, 0xdeb56fff, 0xe0bc83ff, 0xe1bb80ff, 0xdbb060ff, 0xd9af5eff, 0xdab065ff, 0xd7ad60ff, 0xd5ab5fff, 0xd3a85cff, 0xd4ab5eff, 0xd0a34bff, 0xc99839ff, 0xc5912aff, 0xbe8618ff, 0xae6b05ff, 0xa75e05ff, 0xad6903ff, 0xb87c12ff, 0xc89534ff, 0xd0a249ff, 0xd3a853ff, 0xd2a752ff, 0xd2a756ff, 0xcea04dff, 0xce9c40ff, 0xd09b32ff, 0xcd9626ff, 0xcf9727ff, 0xce9727ff, 0xcd9426ff, 0xc88810ff, 0xc8860bff, 0xc78308ff, 0xc78307ff, 0xc67f05ff, 0xc57803ff, 0xc67804ff, 0xca7c07ff, 0xd18912ff, 0xd59424ff, 0xd9992bff, 0xd79521ff, 0xd99c2fff, 0xd7961eff, 0xdb9d21ff, 0xdfa733ff, 0xe4b44dff, 0xe9bf65ff, 0xecc572ff, 0xecc774ff, 0xecc66cff, 0xecc971ff, 0xeecf7fff, 0xedca7aff, 0xe7b95cff, 0xdfab41ff, 0xdeaa45ff, 0xd79f36ff, 0xc88e21ff, 0xd59e32ff, 0xdba73eff, 0xdca944ff, 0xe1b452ff, 0xe6bc62ff, 0xe7be68ff, 0xe7bc5eff, 0xe9be5eff, 0xecc366ff, 0xedce83ff, 0xe5ce91ff, 0xb2a175ff, 0x5d5a4eff, 0x8f908fff, 0xdedfddff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdddedcff, 0xafb1adff, 0x8c8e89ff, 0x828481ff, 0x4c4c48ff, 0x655f4fff, 0x9d957fff, 0xbfb497ff, 0xdaceb2ff, 0xe9dbc0ff, 0xf3e7cfff, 0xf4e5c7ff, 0xefdbb1ff, 0xdfbb7fff, 0xd2a151ff, 0xdab16fff, 0xdfba80ff, 0xdfbb7dff, 0xdebb77ff, 0xdaaf5bff, 0xdbb05bff, 0xdcb461ff, 0xe0bb6fff, 0xe7c280ff, 0xe7c17eff, 0xe5c27dff, 0xe3bb6cff, 0xe1b965ff, 0xe3bc6aff, 0xe7c57eff, 0xe8c67cff, 0xeac881ff, 0xebcd87ff, 0xe7c679ff, 0xe6bf6eff, 0xdfb35bff, 0xd8a948ff, 0xdeaf55ff, 0xdfb057ff, 0xe2b86bff, 0xe1bb79ff, 0xe0b66aff, 0xdeb369ff, 0xdbb16bff, 0xdcb473ff, 0xdab16bff, 0xd8ac5aff, 0xd8ac5aff, 0xd8ad60ff, 0xd5aa5aff, 0xd4a856ff, 0xd2a552ff, 0xd1a653ff, 0xce9f45ff, 0xc7932eff, 0xc5912aff, 0xbf881dff, 0xb87b0bff, 0xad6805ff, 0xa75d04ff, 0xb3720bff, 0xc38e29ff, 0xcc9e44ff, 0xd4a959ff, 0xd4aa57ff, 0xd5aa5aff, 0xcfa251ff, 0xcc9a40ff, 0xd09c3dff, 0xcf9b37ff, 0xd0982cff, 0xce9424ff, 0xcb8f19ff, 0xc98a11ff, 0xc9860bff, 0xc58005ff, 0xc88510ff, 0xca8a19ff, 0xc8820fff, 0xc88217ff, 0xce8d2bff, 0xdca54cff, 0xd49a3fff, 0xd29531ff, 0xcf8e25ff, 0xd9a344ff, 0xc67b18ff, 0xc17208ff, 0xc88012ff, 0xc98317ff, 0xcb8721ff, 0xd29229ff, 0xd99e34ff, 0xdfab3fff, 0xe4b95aff, 0xe8c16dff, 0xe9c677ff, 0xedcd8aff, 0xebc985ff, 0xefcf8dff, 0xe7bf73ff, 0xcc902dff, 0xd09225ff, 0xdca641ff, 0xdfae4eff, 0xe5ba62ff, 0xeac171ff, 0xebc475ff, 0xedc776ff, 0xeec872ff, 0xf0d288ff, 0xedd69bff, 0xddc990ff, 0x8e8261ff, 0x696a67ff, 0x9d9f9aff, 0xd9dad7ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdddedcff, 0xafb0adff, 0x8c8e89ff, 0x828481ff, 0x4c4c48ff, 0x676255ff, 0x9f9784ff, 0xbeb39aff, 0xd7caadff, 0xe8dcc1ff, 0xf2e5cdff, 0xf6ebd3ff, 0xf5e8caff, 0xf2e0baff, 0xe3c38dff, 0xd2a152ff, 0xdbb16aff, 0xd9b066ff, 0xdbb468ff, 0xdcb56cff, 0xd9af5dff, 0xd5a94fff, 0xd6ae5bff, 0xdfbf7aff, 0xe4c384ff, 0xe6c788ff, 0xe9c989ff, 0xe6c37cff, 0xe4be74ff, 0xe7c27bff, 0xe8c377ff, 0xebca80ff, 0xedcd87ff, 0xedcb83ff, 0xe5bd66ff, 0xe3b85eff, 0xdfb250ff, 0xd7a236ff, 0xdfb154ff, 0xe0b35bff, 0xe0b566ff, 0xe0b66eff, 0xddb165ff, 0xdbae5dff, 0xdcb165ff, 0xdbb164ff, 0xdaaf63ff, 0xd6a852ff, 0xd2a449ff, 0xd0a249ff, 0xd0a44eff, 0xd1a34eff, 0xce9f43ff, 0xc9983cff, 0xc89637ff, 0xc89637ff, 0xc5912eff, 0xc28c26ff, 0xba7f10ff, 0xb27309ff, 0xa55c04ff, 0xac6705ff, 0xbd871dff, 0xc89739ff, 0xd1a54fff, 0xd5ab59ff, 0xd6ab5aff, 0xce9f45ff, 0xcc973aff, 0xd09d3eff, 0xd09d39ff, 0xd09930ff, 0xcc9322ff, 0xc98b14ff, 0xcb901eff, 0xcf9428ff, 0xd59d37ff, 0xe7bc67ff, 0xecc97dff, 0xe8bd6dff, 0xeac376ff, 0xebc577ff, 0xebc77eff, 0xe5ba6aff, 0xdfb058ff, 0xd9a644ff, 0xdba84eff, 0xce922dff, 0xb76609ff, 0xb15904ff, 0xb15b03ff, 0xb05703ff, 0xb15703ff, 0xb75f03ff, 0xbe6c09ff, 0xcc8c29ff, 0xd49c39ff, 0xd49c36ff, 0xdbab4cff, 0xe5bd69ff, 0xebc87dff, 0xebc981ff, 0xcc9033ff, 0xce8f24ff, 0xdfab4eff, 0xe2b55dff, 0xe8be6dff, 0xeac375ff, 0xecc87fff, 0xedca7bff, 0xecc773ff, 0xeed18aff, 0xecd499ff, 0xd7c38cff, 0x968967ff, 0x5f5f5bff, 0x92938fff, 0xcdceccff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdddedbff, 0xaeb0acff, 0x8c8e89ff, 0x838481ff, 0x4c4c47ff, 0x676254ff, 0x9f9785ff, 0xc0b7a0ff, 0xd8cbaeff, 0xe7d9bbff, 0xf0e2c7ff, 0xf1e2c6ff, 0xf1e1bfff, 0xf1e0bcff, 0xedd8afff, 0xdab36eff, 0xd3a452ff, 0xdbaf65ff, 0xd9ae5fff, 0xd6a952ff, 0xd7ae5eff, 0xd4b069ff, 0xcea75aff, 0xb3914fff, 0x82704dff, 0xb29860ff, 0xd8b871ff, 0xe5c98bff, 0xeacd8dff, 0xe6c076ff, 0xe6be71ff, 0xe8c378ff, 0xebca80ff, 0xedce87ff, 0xebc679ff, 0xe8bf68ff, 0xe0b358ff, 0xd9a941ff, 0xd7a232ff, 0xe3b767ff, 0xe1b562ff, 0xdeb15eff, 0xdcae5bff, 0xdcaf64ff, 0xd9aa5bff, 0xd8ab57ff, 0xd7a958ff, 0xd4a550ff, 0xd3a24bff, 0xd0a149ff, 0xd0a24dff, 0xce9f4aff, 0xcf9f49ff, 0xcd9d47ff, 0xca9a42ff, 0xc99a41ff, 0xc8973eff, 0xc48f30ff, 0xc08a23ff, 0xb5780aff, 0xb06c05ff, 0xa86204ff, 0xaa6605ff, 0xba8016ff, 0xc79333ff, 0xcea148ff, 0xd1a64fff, 0xd2a54fff, 0xd9ab51ff, 0xe7bd6cff, 0xe1b45bff, 0xe0b25aff, 0xe3b45aff, 0xe0b158ff, 0xe0b35aff, 0xe7c06eff, 0xf0d189ff, 0xf4d995ff, 0xf3d892ff, 0xf1d796ff, 0xefd390ff, 0xebc87eff, 0xeac577ff, 0xe8c278ff, 0xe4ba6fff, 0xdeb15eff, 0xdaaa52ff, 0xd7a44dff, 0xd09630ff, 0xbe750eff, 0xb25a04ff, 0xb45c04ff, 0xba6204ff, 0xb55c04ff, 0xb25605ff, 0xae5504ff, 0xb15605ff, 0xb66009ff, 0xbd6b0cff, 0xc6831dff, 0xd29833ff, 0xddac50ff, 0xe6ba65ff, 0xcb8929ff, 0xd29633ff, 0xe4b663ff, 0xe6bb67ff, 0xe9c171ff, 0xebc578ff, 0xedc97fff, 0xeecc80ff, 0xeecb7bff, 0xf3d795ff, 0xefdbaaff, 0xd6c79cff, 0x9f9475ff, 0x575650ff, 0x8b8d89ff, 0xbebfbcff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdcdddbff, 0xaeafacff, 0x8b8d88ff, 0x838480ff, 0x4b4b48ff, 0x656050ff, 0x9d967dff, 0xc0b598ff, 0xdaceb2ff, 0xeadfc7ff, 0xf2e6cfff, 0xf2e4c8ff, 0xf1e1beff, 0xf1deb7ff, 0xefd9abff, 0xe3c188ff, 0xd09f4bff, 0xd0a14cff, 0xd2a352ff, 0xd7ac60ff, 0xd0a14aff, 0xcaa049ff, 0xc09845ff, 0x9f7c36ff, 0x61553dff, 0x6c6d6cff, 0x635c4aff, 0xa68b51ff, 0xd3b56fff, 0xe1c684ff, 0xe7c881ff, 0xe8c274ff, 0xe7c071ff, 0xe7c070ff, 0xeac77dff, 0xe8bf6cff, 0xe6ba61ff, 0xe0b353ff, 0xd8a535ff, 0xd59f29ff, 0xe1b86bff, 0xe0b460ff, 0xddb15fff, 0xdaaa53ff, 0xdaac56ff, 0xdaab5aff, 0xd8aa5bff, 0xd6a859ff, 0xcf9f47ff, 0xcf993dff, 0xce9b42ff, 0xcb993cff, 0xcb9738ff, 0xc9973dff, 0xc8953aff, 0xc6933aff, 0xc59239ff, 0xc49139ff, 0xc0892aff, 0xbc841bff, 0xb37306ff, 0xaf6a04ff, 0xa96404ff, 0xa96704ff, 0xb17307ff, 0xc28b26ff, 0xd0a148ff, 0xd7ac57ff, 0xe2b966ff, 0xedc97aff, 0xf2d38aff, 0xf2d48bff, 0xf3d68fff, 0xf3d58fff, 0xf4d68fff, 0xf4d78fff, 0xf3d690ff, 0xf3d898ff, 0xf3d79aff, 0xf0d596ff, 0xefd295ff, 0xebcb8aff, 0xe6bf6fff, 0xe5bc67ff, 0xe4ba6fff, 0xdfb464ff, 0xd7a54aff, 0xd29d3bff, 0xcc9229ff, 0xc58313ff, 0xbe7105ff, 0xbe6d05ff, 0xc3780aff, 0xc57f0fff, 0xc0780cff, 0xb96806ff, 0xac5405ff, 0xa14104ff, 0xab5204ff, 0xb45e04ff, 0xb66104ff, 0xc07410ff, 0xc9841dff, 0xcb821aff, 0xc3780fff, 0xd49834ff, 0xe4b864ff, 0xe8c06fff, 0xecc87eff, 0xeecd86ff, 0xf2d48eff, 0xf4d893ff, 0xf5d78eff, 0xf8e0a7ff, 0xf1e3bdff, 0xd8ceadff, 0xaaa180ff, 0x55544aff, 0x848582ff, 0xaeafacff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd2d3d1ff, 0x91928eff, 0x898b87ff, 0x4c4c49ff, 0x5d584cff, 0x99917aff, 0xbfb597ff, 0xdacdabff, 0xeadcbcff, 0xf1e5caff, 0xf2e3c6ff, 0xf0debbff, 0xf2e2beff, 0xf2e0b4ff, 0xecd5a2ff, 0xd3a75bff, 0xcc973fff, 0xd2a355ff, 0xd5a862ff, 0xd0a452ff, 0xc5953aff, 0xb6872cff, 0x8b6212ff, 0x594e34ff, 0x7a7c79ff, 0x8f918cff, 0x80827fff, 0x635d4fff, 0xa38c58ff, 0xd3b570ff, 0xe2c57fff, 0xe7c67aff, 0xe8c372ff, 0xe7c06dff, 0xe9c273ff, 0xe5b85cff, 0xe1b44fff, 0xddaf45ff, 0xdaa93dff, 0xd6a12cff, 0xe0b563ff, 0xdfb15dff, 0xddb061ff, 0xdcaf60ff, 0xdbad5bff, 0xd9ab5cff, 0xd6a657ff, 0xd5a859ff, 0xd2a34eff, 0xd09e44ff, 0xcc983bff, 0xca953aff, 0xcb973bff, 0xca9840ff, 0xc59032ff, 0xc28d2fff, 0xc38e36ff, 0xc0892fff, 0xbc831fff, 0xb87d11ff, 0xb27207ff, 0xba7c19ff, 0xb5761aff, 0xae6e0eff, 0xbe8625ff, 0xdaad56ff, 0xeccc82ff, 0xf1d692ff, 0xf4da9aff, 0xf1d593ff, 0xefd087ff, 0xefd087ff, 0xefcf86ff, 0xefcd7fff, 0xeecd7fff, 0xeecc7fff, 0xecc978ff, 0xebc674ff, 0xebc87eff, 0xeac377ff, 0xe8c278ff, 0xe4ba67ff, 0xdfb051ff, 0xdcac4cff, 0xdaaa50ff, 0xd39d36ff, 0xcd9221ff, 0xc98c1bff, 0xc6840eff, 0xc88512ff, 0xc98514ff, 0xcd8e1fff, 0xcf952cff, 0xcb9025ff, 0xc58417ff, 0xbd7309ff, 0xb25f04ff, 0xa74a04ff, 0xa94f04ff, 0xaf5a05ff, 0xb96b05ff, 0xc88621ff, 0xcc9034ff, 0xc07408ff, 0xc68312ff, 0xdaa646ff, 0xe7be73ff, 0xecc880ff, 0xefd08bff, 0xf2d792ff, 0xf3d996ff, 0xf6dda0ff, 0xf8e0a1ff, 0xfae6b0ff, 0xf4eac7ff, 0xddd5afff, 0xb6b08dff, 0x5d5c4fff, 0x7a7b78ff, 0x9fa19dff, 0xdddedcff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdddedcff, 0x9fa09cff, 0x878985ff, 0x4a4946ff, 0x807a6cff, 0xb5ad98ff, 0xd8cdb1ff, 0xeee3c5ff, 0xf6ead1ff, 0xf6e8caff, 0xf0dfb8ff, 0xf0dcb4ff, 0xf4e7c6ff, 0xf3e5c1ff, 0xeacf9cff, 0xd3a553ff, 0xce9c43ff, 0xd2a352ff, 0xcfa253ff, 0xc79844ff, 0xb98e3bff, 0x8e6721ff, 0x53452cff, 0x7a7c79ff, 0x8c8e89ff, 0x878984ff, 0xa6a8a4ff, 0xb5b6b5ff, 0x6c675aff, 0xa4915fff, 0xd5b873ff, 0xe1c37bff, 0xe9cb7eff, 0xe9c677ff, 0xe8c16eff, 0xe5b95dff, 0xe3b756ff, 0xdcad45ff, 0xd8a739ff, 0xd6a130ff, 0xe0b262ff, 0xddb05bff, 0xdbaa4fff, 0xdcae5eff, 0xdcaf65ff, 0xdbad65ff, 0xd7a859ff, 0xd2a04aff, 0xd3a34eff, 0xd29f48ff, 0xcf9d45ff, 0xcb9535ff, 0xc99636ff, 0xca963eff, 0xc58f31ff, 0xc28d2eff, 0xc08a2eff, 0xc28b2eff, 0xc79231ff, 0xd6a74dff, 0xdaae5dff, 0xe5bb68ff, 0xe5bb67ff, 0xe4c075ff, 0xefd38fff, 0xf2d690ff, 0xf2d58fff, 0xf0d189ff, 0xeece85ff, 0xedca7dff, 0xedc770ff, 0xeccb7dff, 0xecca7cff, 0xecc671ff, 0xebc571ff, 0xe9c372ff, 0xe9bf68ff, 0xe9bf69ff, 0xe8c06fff, 0xe5bb68ff, 0xe2b55fff, 0xdfb259ff, 0xdaab4eff, 0xd8a546ff, 0xd29d35ff, 0xcb8f1bff, 0xc8870fff, 0xc8850eff, 0xce9125ff, 0xd7a23fff, 0xd8a648ff, 0xd7a345ff, 0xd5a34bff, 0xd29b40ff, 0xca8d25ff, 0xbe770bff, 0xbc6f05ff, 0xbf7506ff, 0xbc7107ff, 0xb46104ff, 0xb25e04ff, 0xb86a0fff, 0xc07b17ff, 0xc0780aff, 0xc47f0eff, 0xdaa84aff, 0xecc885ff, 0xf4d9a1ff, 0xf4da99ff, 0xf5dc99ff, 0xf5dea1ff, 0xf9e4b2ff, 0xfbe7aeff, 0xfce8adff, 0xf5e5b1ff, 0xddc890ff, 0xb8a373ff, 0x685e49ff, 0x6d6e6bff, 0x959792ff, 0xd4d5d2ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xb4b5b2ff, 0x8c8e89ff, 0x5c5d5aff, 0x7f7a6dff, 0xc3b9a2ff, 0xe2d8b9ff, 0xf5ebcfff, 0xf9eed3ff, 0xfaefd5ff, 0xf7e9c8ff, 0xf5e4c1ff, 0xf2e2c0ff, 0xf0dfb7ff, 0xe5c48dff, 0xd0a14cff, 0xcc9c45ff, 0xcca053ff, 0xc6994bff, 0xb48430ff, 0x865a17ff, 0x534631ff, 0x7a7c7aff, 0x8c8e89ff, 0x878984ff, 0xa3a5a1ff, 0xd7d8d6ff, 0xe4e5e3ff, 0xbcbdbcff, 0x6b6659ff, 0xa69360ff, 0xd5b972ff, 0xe0c277ff, 0xe8c778ff, 0xeac574ff, 0xe9c36dff, 0xe5bd64ff, 0xdcad47ff, 0xd6a534ff, 0xd29d25ff, 0xdda948ff, 0xdca94dff, 0xdbaa55ff, 0xd9ab59ff, 0xdbae64ff, 0xdaae64ff, 0xd6a857ff, 0xd4a452ff, 0xd4a453ff, 0xd5a658ff, 0xd0a149ff, 0xcd9838ff, 0xcc9b44ff, 0xca953dff, 0xc38b28ff, 0xbb8118ff, 0xc08724ff, 0xd8ab5bff, 0xe7c174ff, 0xefd18cff, 0xf0d594ff, 0xeed28aff, 0xefd087ff, 0xf2d692ff, 0xf1d597ff, 0xedcd82ff, 0xe9c369ff, 0xeac36dff, 0xeac46fff, 0xeac16aff, 0xe9c26cff, 0xe8c373ff, 0xe9c271ff, 0xeac16fff, 0xe9c06cff, 0xe5b95fff, 0xe4b95fff, 0xe4b961ff, 0xe2b65dff, 0xdfb359ff, 0xdcad50ff, 0xdaa745ff, 0xd49f34ff, 0xd0992cff, 0xcb8f18ff, 0xca890dff, 0xd0921eff, 0xdaa441ff, 0xe2b566ff, 0xe2b767ff, 0xe1b569ff, 0xdeb163ff, 0xd9a954ff, 0xd5a14bff, 0xd19b45ff, 0xc5841bff, 0xca8a16ff, 0xce9023ff, 0xcb8e24ff, 0xc48214ff, 0xbc750dff, 0xb36204ff, 0xb05d03ff, 0xb66904ff, 0xbe7809ff, 0xd5a043ff, 0xefcc8fff, 0xf4db9cff, 0xf6dc99ff, 0xf9e4a9ff, 0xfbe8b3ff, 0xfbe9b5ff, 0xf8de9cff, 0xf4d184ff, 0xebbd66ff, 0xce993aff, 0xa77121ff, 0x694b18ff, 0x5f5e59ff, 0x8f918cff, 0xc6c8c5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xcacbc8ff, 0x8f918cff, 0x70716fff, 0x6e6a5fff, 0xc5bca5ff, 0xe2d6b4ff, 0xf5ebcdff, 0xfcf2d7ff, 0xf6e9c4ff, 0xf6e9c5ff, 0xf6e8c4ff, 0xf1deb6ff, 0xe9cd99ff, 0xd5a95dff, 0xcb973bff, 0xc7953eff, 0xc49543ff, 0xb78a39ff, 0x83530fff, 0x4f3e29ff, 0x7a7b79ff, 0x8c8e89ff, 0x878984ff, 0xa3a4a0ff, 0xd7d8d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe4e5e3ff, 0xbcbdbcff, 0x6b6657ff, 0xa79056ff, 0xd7ba6fff, 0xe2c172ff, 0xe8c676ff, 0xe9c673ff, 0xe2b95bff, 0xdcae4bff, 0xd7a739ff, 0xcf981aff, 0xd8a33fff, 0xdaa74eff, 0xd9a853ff, 0xdbad5eff, 0xd8ab5fff, 0xd7a95bff, 0xd8a85cff, 0xd5a754ff, 0xd09d47ff, 0xd3a45aff, 0xd4a755ff, 0xd5a554ff, 0xd09e4cff, 0xc99237ff, 0xc69035ff, 0xc0851fff, 0xd39e3cff, 0xe6bd71ff, 0xeac982ff, 0xe9c87eff, 0xe9c77dff, 0xe7c373ff, 0xe6c06dff, 0xe4bb5fff, 0xe4bb66ff, 0xe5bc64ff, 0xe4ba5dff, 0xe5bb64ff, 0xe5bc65ff, 0xe6bd69ff, 0xe2b65aff, 0xe2b658ff, 0xe3b75fff, 0xe3b75fff, 0xe2b55bff, 0xe1b55bff, 0xdfb156ff, 0xdbab4bff, 0xdaa947ff, 0xd7a43dff, 0xd4a036ff, 0xd19a29ff, 0xcd921aff, 0xcb8e16ff, 0xce911aff, 0xd9a239ff, 0xe1b155ff, 0xe3b762ff, 0xe6bf79ff, 0xe5bf7aff, 0xe2bc77ff, 0xdeb36aff, 0xd9aa55ff, 0xd6a44bff, 0xd3a14eff, 0xcb9132ff, 0xc98c1cff, 0xd59e3eff, 0xd8a64bff, 0xd69e38ff, 0xcf942cff, 0xc38014ff, 0xba7007ff, 0xb36504ff, 0xb36603ff, 0xc98d2fff, 0xedc67aff, 0xf2d287ff, 0xf6da95ff, 0xf5d58fff, 0xedc87eff, 0xe5b566ff, 0xe7b45bff, 0xe7b558ff, 0xdaa649ff, 0xb5761bff, 0x9f6916ff, 0x734f0cff, 0x524e44ff, 0x8b8d89ff, 0xb6b7b4ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdadbd8ff, 0x9a9b97ff, 0x81837fff, 0x5c584eff, 0xb7ab8eff, 0xdccca6ff, 0xeedfb6ff, 0xf7eac5ff, 0xf8eac3ff, 0xf5e5bdff, 0xf1e0b5ff, 0xefd9abff, 0xddb571ff, 0xcb9943ff, 0xc7973eff, 0xc18f38ff, 0xb4832eff, 0x8b621cff, 0x52432cff, 0x7a7c79ff, 0x8c8e89ff, 0x878984ff, 0xa2a4a0ff, 0xd7d8d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe4e5e3ff, 0xbdbebdff, 0x6e6652ff, 0xb89e5eff, 0xddbc6aff, 0xe6c472ff, 0xe9c673ff, 0xe1b859ff, 0xdbad46ff, 0xd5a534ff, 0xd09b1eff, 0xd69f3fff, 0xd39c3bff, 0xd6a048ff, 0xdcad62ff, 0xd9ab5eff, 0xd4a14aff, 0xd5a24cff, 0xd3a24bff, 0xd29f4aff, 0xd2a152ff, 0xd5a75aff, 0xd3a556ff, 0xcc983bff, 0xc99130ff, 0xc99136ff, 0xd6a243ff, 0xe5ba65ff, 0xe7c276ff, 0xe5bf6fff, 0xe4bc69ff, 0xe3b861ff, 0xddb151ff, 0xdbae4eff, 0xd9aa47ff, 0xd8a744ff, 0xdaab4cff, 0xdeb156ff, 0xddb04eff, 0xddb04cff, 0xdbad50ff, 0xd8a847ff, 0xd7a742ff, 0xd6a43cff, 0xd5a032ff, 0xd39e34ff, 0xd29f36ff, 0xd19b2dff, 0xcf9927ff, 0xcf9623ff, 0xce941fff, 0xce901aff, 0xcb8d16ff, 0xc9880cff, 0xd2982dff, 0xe1b45bff, 0xe7be6aff, 0xe8c57fff, 0xe9c886ff, 0xe7c587ff, 0xe6c286ff, 0xe4c080ff, 0xdfb670ff, 0xd8a64eff, 0xd49e3bff, 0xd3a146ff, 0xcd973aff, 0xc08114ff, 0xc28325ff, 0xbc7a26ff, 0xc18331ff, 0xd5a146ff, 0xdaa746ff, 0xce942bff, 0xc58115ff, 0xc07a15ff, 0xbf7912ff, 0xd6a13cff, 0xe8bc5eff, 0xebc16aff, 0xe4b351ff, 0xd79a32ff, 0xcd8b25ff, 0xca8c28ff, 0xbf7a1cff, 0xb3670dff, 0xa65704ff, 0x944f03ff, 0x784803ff, 0x4b412fff, 0x868885ff, 0xa6a7a3ff, 0xe0e1dfff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xadaeaaff, 0x8a8c87ff, 0x595853ff, 0xa0957bff, 0xd5c69fff, 0xebdbaeff, 0xf3e1b3ff, 0xf8e9c0ff, 0xf6e7beff, 0xf1ddafff, 0xe8ca94ff, 0xcc9b4dff, 0xbf8b34ff, 0xb48024ff, 0xa6721aff, 0x80510cff, 0x50412bff, 0x7a7c79ff, 0x8c8e89ff, 0x878984ff, 0xa2a4a0ff, 0xd7d8d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e3ff, 0x817b70ff, 0x927129ff, 0xd5b057ff, 0xe4be66ff, 0xe5be64ff, 0xdfb553ff, 0xdbae47ff, 0xd5a432ff, 0xd09a1cff, 0xd29835ff, 0xd39a36ff, 0xd7a34fff, 0xd6a048ff, 0xd8a450ff, 0xd5a14cff, 0xd29c3fff, 0xd49f45ff, 0xd29d42ff, 0xd09d41ff, 0xce993cff, 0xcb9535ff, 0xcd9738ff, 0xc99233ff, 0xd29c3cff, 0xe4ba63ff, 0xe6bf71ff, 0xe4bd6fff, 0xe0b761ff, 0xddb256ff, 0xddb055ff, 0xd7a645ff, 0xd6a542ff, 0xd9a747ff, 0xd8a543ff, 0xd4a138ff, 0xd9a84aff, 0xd7a53bff, 0xd5a339ff, 0xd4a13cff, 0xd4a039ff, 0xd3a03aff, 0xd09a30ff, 0xd09a2eff, 0xcc9529ff, 0xc88e1cff, 0xc48410ff, 0xc38614ff, 0xc78811ff, 0xc9890cff, 0xc7850aff, 0xc8860aff, 0xd19624ff, 0xe3b866ff, 0xe8c275ff, 0xe8c67eff, 0xeaca8eff, 0xe8c783ff, 0xe7c584ff, 0xe8c68cff, 0xe8c58dff, 0xe1b873ff, 0xdaaa53ff, 0xd7a445ff, 0xd6a349ff, 0xcf993dff, 0xc08015ff, 0xb66d03ff, 0xa75303ff, 0x953904ff, 0x9e4d13ff, 0xbe8335ff, 0xd39f49ff, 0xdcab4eff, 0xdbaa4eff, 0xd1962fff, 0xcd9128ff, 0xdaa742ff, 0xdaa740ff, 0xcf962cff, 0xc8891fff, 0xbc7409ff, 0xb66804ff, 0xb56703ff, 0xb26404ff, 0xaa5d05ff, 0x974f04ff, 0x7a4102ff, 0x47331cff, 0x7d807dff, 0x999b96ff, 0xd9dad8ff, 0xe2e2e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc3c4c1ff, 0x8e908bff, 0x676865ff, 0x837d6eff, 0xcdbf9bff, 0xe6d7acff, 0xf5e8c1ff, 0xf9ebc5ff, 0xf2deb1ff, 0xe4c383ff, 0xc99945ff, 0xb47819ff, 0xaf7411ff, 0xa56c0cff, 0x835509ff, 0x51412aff, 0x7a7c79ff, 0x8c8e89ff, 0x878984ff, 0xa2a39fff, 0xd7d8d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe4e5e3ff, 0xb3b3b1ff, 0x5a401eff, 0x834d06ff, 0xb98d35ff, 0xe0b856ff, 0xe4b956ff, 0xdeb24aff, 0xd8aa3cff, 0xd2a02cff, 0xd09b25ff, 0xcc8b1bff, 0xd0942aff, 0xd49d3cff, 0xd29a3aff, 0xd59d44ff, 0xd19a3aff, 0xd29b3cff, 0xd49d43ff, 0xd3a047ff, 0xd29e42ff, 0xcf9b41ff, 0xcd973cff, 0xcc9842ff, 0xce983bff, 0xdeb058ff, 0xe2b967ff, 0xe2b968ff, 0xdfb663ff, 0xdcb158ff, 0xd8a94aff, 0xdaac51ff, 0xdcb15dff, 0xd5a240ff, 0xd8a74aff, 0xd8a747ff, 0xd4a33eff, 0xd7a544ff, 0xd6a33eff, 0xd3a03aff, 0xd4a13cff, 0xd4a139ff, 0xd4a13aff, 0xd5a141ff, 0xd39f3fff, 0xcf9832ff, 0xcc922aff, 0xca8e29ff, 0xcb912dff, 0xc78a20ff, 0xc5840dff, 0xd49b2fff, 0xdda840ff, 0xe2b34eff, 0xe8be69ff, 0xe8c375ff, 0xe9c988ff, 0xedcf99ff, 0xebcb91ff, 0xeccc8dff, 0xe9c88cff, 0xe7c486ff, 0xe3bb74ff, 0xddad59ff, 0xd4a142ff, 0xd6a54eff, 0xd09b3eff, 0xc88e28ff, 0xbc7a10ff, 0xaf6205ff, 0x994004ff, 0x8c2f03ff, 0x903a03ff, 0x98510dff, 0xad7420ff, 0xc59030ff, 0xd49e31ff, 0xd6a136ff, 0xd5a138ff, 0xd4a035ff, 0xd29b2fff, 0xc48212ff, 0xbd7404ff, 0xbc7104ff, 0xba6f05ff, 0xb56b04ff, 0xaf6504ff, 0x9f5a04ff, 0x834802ff, 0x4b2e0fff, 0x717270ff, 0x91938eff, 0xcecfcdff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd5d6d4ff, 0x969793ff, 0x797b77ff, 0x625f56ff, 0xbab095ff, 0xd9ceabff, 0xe9ddb9ff, 0xe8d4aaff, 0xd5ae6aff, 0xbd862aff, 0xb87f21ff, 0xb47d20ff, 0xa9771cff, 0x8a631aff, 0x564a32ff, 0x7a7c79ff, 0x8c8e89ff, 0x878984ff, 0xa1a39fff, 0xd6d7d5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xd1d2d2ff, 0x5e5344ff, 0x623601ff, 0x834703ff, 0x975909ff, 0xbf8d2eff, 0xdcaf44ff, 0xddaf44ff, 0xd8aa3dff, 0xd0a02bff, 0xce9d2dff, 0xca8612ff, 0xcc8c1cff, 0xce932bff, 0xce9431ff, 0xcf932dff, 0xcf932dff, 0xd2983bff, 0xd09731ff, 0xcf9837ff, 0xd29d44ff, 0xcf993aff, 0xcc9533ff, 0xca9230ff, 0xcc9734ff, 0xdaad55ff, 0xdfb462ff, 0xdfb568ff, 0xddb261ff, 0xd7a94dff, 0xd39f3bff, 0xd4a040ff, 0xd7a74eff, 0xd5a241ff, 0xd6a545ff, 0xd8a94cff, 0xd9ab51ff, 0xd9a847ff, 0xdaaa4dff, 0xd5a243ff, 0xd39e3bff, 0xd4a340ff, 0xd5a340ff, 0xd59f3cff, 0xd49f3bff, 0xd09c38ff, 0xd29d42ff, 0xd29d49ff, 0xce963eff, 0xcd9334ff, 0xdaa73dff, 0xeac368ff, 0xeec870ff, 0xecc874ff, 0xeac674ff, 0xe9c67aff, 0xeac989ff, 0xeac992ff, 0xe7c686ff, 0xe9c988ff, 0xe9c889ff, 0xe3bb6dff, 0xe1b55eff, 0xdcae52ff, 0xd7a342ff, 0xd7a54bff, 0xd4a145ff, 0xcf983bff, 0xc48820ff, 0xba770dff, 0xa95b04ff, 0x964004ff, 0x8a3504ff, 0x894803ff, 0x874602ff, 0x904706ff, 0xab6410ff, 0xc88f23ff, 0xd09b2cff, 0xd29e2cff, 0xd49f30ff, 0xd39e30ff, 0xc88916ff, 0xc17b05ff, 0xbe7604ff, 0xbb7004ff, 0xb46a04ff, 0xa45f04ff, 0x8b5003ff, 0x563307ff, 0x62615dff, 0x8d8f8aff, 0xbfc0bdff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdfe0deff, 0xa6a7a3ff, 0x888985ff, 0x545450ff, 0x686252ff, 0x938568ff, 0xb19a6cff, 0xb28942ff, 0xab7619ff, 0xad7818ff, 0xaf7c22ff, 0xaa7c26ff, 0x8a6623ff, 0x554a32ff, 0x7a7c79ff, 0x8c8e89ff, 0x878984ff, 0xa1a39fff, 0xd6d7d5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e1ff, 0x807d78ff, 0x4a2b06ff, 0x713f03ff, 0x874b04ff, 0x925103ff, 0x9e5d09ff, 0xc38d21ff, 0xd7a32cff, 0xd5a434ff, 0xd19f2eff, 0xcd9821ff, 0xcb8a20ff, 0xcb891cff, 0xd19534ff, 0xd1993eff, 0xcf9431ff, 0xd29838ff, 0xd19939ff, 0xd19836ff, 0xd49f43ff, 0xd29e46ff, 0xcd9635ff, 0xca922eff, 0xc78e29ff, 0xc68d2aff, 0xd09d3fff, 0xdbae5bff, 0xdbaf5dff, 0xd9ac58ff, 0xd6a64bff, 0xd29f3cff, 0xcf9837ff, 0xd09a38ff, 0xd5a448ff, 0xd5a445ff, 0xd9a84cff, 0xd7a746ff, 0xdaac52ff, 0xdbad58ff, 0xdaad5cff, 0xd9aa58ff, 0xdbae5aff, 0xd8a84cff, 0xd6a443ff, 0xd5a346ff, 0xd3a248ff, 0xd6a552ff, 0xd4a14fff, 0xd39c44ff, 0xd59e32ff, 0xe5b850ff, 0xf0cc78ff, 0xefd080ff, 0xeccb75ff, 0xeac773ff, 0xe8c575ff, 0xeac885ff, 0xe7c583ff, 0xe5c075ff, 0xe9c781ff, 0xe7c682ff, 0xe6bf74ff, 0xe2b763ff, 0xdaaa4cff, 0xd9a847ff, 0xd9a950ff, 0xd7a654ff, 0xd09c40ff, 0xc9902aff, 0xbe8013ff, 0xb16605ff, 0xa14d05ff, 0x913404ff, 0x8c3f04ff, 0x884204ff, 0x914104ff, 0x974503ff, 0x9e5206ff, 0xaf680eff, 0xc58716ff, 0xcd9219ff, 0xd49d29ff, 0xd29d2eff, 0xcb8d19ff, 0xc37d05ff, 0xc27905ff, 0xbc7705ff, 0xac6805ff, 0x945903ff, 0x643b03ff, 0x535048ff, 0x8b8d89ff, 0xaeb0acff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xbcbdbaff, 0x8a8b87ff, 0x868884ff, 0x757674ff, 0x63625eff, 0x575041ff, 0x594622ff, 0x6d4e16ff, 0x835d12ff, 0x916716ff, 0x846124ff, 0x564c39ff, 0x7a7c79ff, 0x8c8e89ff, 0x878984ff, 0xa1a39fff, 0xd6d7d5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe4e5e3ff, 0xaaabaaff, 0x4b3d24ff, 0x6a4004ff, 0x834b03ff, 0x965604ff, 0x9e5c04ff, 0x9e5904ff, 0xa66407ff, 0xcb9314ff, 0xd19c22ff, 0xd2a235ff, 0xd09e2dff, 0xc68213ff, 0xc9881eff, 0xcf9231ff, 0xd09638ff, 0xd09634ff, 0xd39f45ff, 0xd09732ff, 0xd19a35ff, 0xd6a34aff, 0xd09b3eff, 0xcd9739ff, 0xcc9432ff, 0xc8902aff, 0xc1871dff, 0xcb9230ff, 0xd7a74dff, 0xd7a84fff, 0xd8ab55ff, 0xd9ab55ff, 0xcf9c38ff, 0xd19e43ff, 0xd29d40ff, 0xd7a64fff, 0xd7a649ff, 0xd9a950ff, 0xd9a84fff, 0xdaad59ff, 0xd9ac57ff, 0xdaac5aff, 0xd9aa55ff, 0xdaaa52ff, 0xdbac58ff, 0xd8a952ff, 0xd8a953ff, 0xd6a755ff, 0xd6a450ff, 0xd8a656ff, 0xd39c3fff, 0xdaa031ff, 0xecc268ff, 0xf0d083ff, 0xeece78ff, 0xeecf7bff, 0xedcd7aff, 0xeac774ff, 0xe9c679ff, 0xe6c272ff, 0xe7c273ff, 0xe9c67dff, 0xe9c780ff, 0xe7c278ff, 0xdfb35bff, 0xdeae52ff, 0xdfb35eff, 0xdbad57ff, 0xd9ab5bff, 0xd3a045ff, 0xcc942fff, 0xc3881eff, 0xb77209ff, 0xaf6304ff, 0x9d4404ff, 0x943804ff, 0x8f3904ff, 0x974304ff, 0x9c4e04ff, 0xa05304ff, 0xbe7f20ff, 0xc78c24ff, 0xb16605ff, 0xbc7b10ff, 0xd09922ff, 0xd19a28ff, 0xcb8a0eff, 0xc78207ff, 0xc17e05ff, 0xb57405ff, 0x9d6204ff, 0x734601ff, 0x4a4132ff, 0x888a87ff, 0x9fa09cff, 0xdddedcff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd7d8d6ff, 0xb1b2afff, 0x9c9e9aff, 0x91938eff, 0x8c8e8aff, 0x888b87ff, 0x7e807eff, 0x6c6c69ff, 0x5a564dff, 0x524731ff, 0x4f4738ff, 0x7b7c79ff, 0x8c8e89ff, 0x878984ff, 0xa1a29eff, 0xd6d7d5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xcdcdcdff, 0x58544bff, 0x6e582cff, 0x8f671dff, 0x965d05ff, 0x9f5e04ff, 0x9e5c04ff, 0x9f5c04ff, 0x9c5604ff, 0xb57a0cff, 0xcc9313ff, 0xd09c29ff, 0xddb65cff, 0xcb8e2bff, 0xc78922ff, 0xc8891fff, 0xcf9535ff, 0xd19938ff, 0xd7a551ff, 0xd29e41ff, 0xd29e3dff, 0xd4a145ff, 0xd19c41ff, 0xce963cff, 0xcd9536ff, 0xc8912eff, 0xc0851bff, 0xc28720ff, 0xcb9530ff, 0xcf9933ff, 0xd6a54bff, 0xd7a854ff, 0xd19d3bff, 0xd5a54eff, 0xd4a44aff, 0xd4a142ff, 0xd9a94fff, 0xdeb366ff, 0xdcb062ff, 0xdcaf5eff, 0xdbae5bff, 0xdbad5bff, 0xdaae5aff, 0xdcb061ff, 0xd9aa55ff, 0xd9aa56ff, 0xd7a852ff, 0xd5a659ff, 0xd3a14aff, 0xd4a046ff, 0xd7a041ff, 0xe2ac43ff, 0xf0cc7cff, 0xf2d488ff, 0xf1d481ff, 0xf3d992ff, 0xf0d386ff, 0xeed082ff, 0xeccc80ff, 0xe8c271ff, 0xe8c67eff, 0xeaca86ff, 0xebcc8dff, 0xe4bd6dff, 0xe0b153ff, 0xe4ba65ff, 0xe2b967ff, 0xddb059ff, 0xddb265ff, 0xd3a246ff, 0xcd9936ff, 0xc78e28ff, 0xba760bff, 0xb36d05ff, 0xab6005ff, 0x9d4304ff, 0x974004ff, 0x9b4804ff, 0xa35604ff, 0xb16b0cff, 0xd09c35ff, 0xd09b33ff, 0xb26708ff, 0x994b03ff, 0xab660cff, 0xcb921fff, 0xcf941cff, 0xcb8b0eff, 0xc68407ff, 0xbd7e07ff, 0xa76e06ff, 0x825302ff, 0x483920ff, 0x808380ff, 0x939591ff, 0xd5d6d3ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdcdddaff, 0xcecfcdff, 0xbabcb8ff, 0xa5a7a3ff, 0x959792ff, 0x8e908bff, 0x8b8d89ff, 0x848683ff, 0x858784ff, 0x8c8d89ff, 0x878984ff, 0xa1a29eff, 0xd6d7d4ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdfe0deff, 0x797875ff, 0x5a4b30ff, 0x947a49ff, 0xad863aff, 0xb7872aff, 0xb27815ff, 0xa15e04ff, 0xa05d04ff, 0x9b5704ff, 0xa26207ff, 0xbe800aff, 0xc78d15ff, 0xdcb65bff, 0xcc942fff, 0xcf9737ff, 0xce9739ff, 0xcf973cff, 0xd49f4aff, 0xd7a54eff, 0xd6a54fff, 0xd39f43ff, 0xd3a148ff, 0xd4a34dff, 0xcf993cff, 0xcd973aff, 0xcb9336ff, 0xc68c2cff, 0xbe801aff, 0xc68e27ff, 0xcf9a38ff, 0xd6a54fff, 0xd29c3aff, 0xd3a246ff, 0xd9ab5dff, 0xd9ab5bff, 0xd8a74fff, 0xdaab55ff, 0xdcb164ff, 0xdcb164ff, 0xdaae5eff, 0xdaae62ff, 0xdaad5eff, 0xd8ac58ff, 0xd8aa56ff, 0xd4a346ff, 0xd5a449ff, 0xd7a756ff, 0xd7a85dff, 0xd4a24dff, 0xd19d3dff, 0xd39b35ff, 0xe4b046ff, 0xf1cb77ff, 0xf3d68bff, 0xf4da8cff, 0xf4da90ff, 0xf3d68cff, 0xf3d993ff, 0xf0d58dff, 0xefd088ff, 0xedd08bff, 0xedcf8cff, 0xeac881ff, 0xe8c271ff, 0xe8c173ff, 0xe9c37cff, 0xe5bd70ff, 0xe0b562ff, 0xdbaf5eff, 0xd4a248ff, 0xcf9c39ff, 0xca9332ff, 0xbc7a10ff, 0xb46b05ff, 0xb06805ff, 0xa35005ff, 0x9c4704ff, 0xa35504ff, 0xab6405ff, 0xc38926ff, 0xddb156ff, 0xdbac4bff, 0xc28116ff, 0x9d4f03ff, 0x964804ff, 0xa25b09ff, 0xc68c1fff, 0xcf9619ff, 0xcb8e10ff, 0xc1850dff, 0xac750aff, 0x895a04ff, 0x4b3611ff, 0x747673ff, 0x8d8f8aff, 0xc7c9c6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xdfe0deff, 0xd6d7d5ff, 0xc5c6c3ff, 0xafb1adff, 0x9c9e99ff, 0x8f918cff, 0x888a85ff, 0xa0a29eff, 0xd6d7d4ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe4e5e3ff, 0xa5a5a5ff, 0x4c4434ff, 0x836c41ff, 0xa98e57ff, 0xbc984cff, 0xc89f48ff, 0xc3912eff, 0xa76706ff, 0x9f5c04ff, 0x9a5704ff, 0x995604ff, 0xae6a04ff, 0xc08214ff, 0xddb65eff, 0xcc9430ff, 0xd29d3cff, 0xd3a04aff, 0xd39e4aff, 0xd5a452ff, 0xd6a651ff, 0xd8a956ff, 0xd29e44ff, 0xd09c41ff, 0xcf9c3eff, 0xcd9a39ff, 0xcf9f4eff, 0xcd9946ff, 0xc58d2fff, 0xbf8321ff, 0xc18622ff, 0xca922bff, 0xd4a44bff, 0xd3a346ff, 0xd5a449ff, 0xd6a54aff, 0xd8aa55ff, 0xdbaf5bff, 0xdaad5aff, 0xd7a64cff, 0xd8aa56ff, 0xd5a246ff, 0xd7a754ff, 0xdbae61ff, 0xd9ac5cff, 0xdbaf67ff, 0xd6a859ff, 0xd2a045ff, 0xd5a450ff, 0xd19f47ff, 0xcd973bff, 0xca9435ff, 0xcf952bff, 0xe2ac3aff, 0xeec76cff, 0xf7df98ff, 0xfae7a7ff, 0xf7e093ff, 0xf7dd91ff, 0xf7de9aff, 0xf4d991ff, 0xf5dfa0ff, 0xf3d999ff, 0xf1d591ff, 0xedcd82ff, 0xedcc84ff, 0xebca87ff, 0xebc985ff, 0xe6c37cff, 0xe3bc73ff, 0xdeb163ff, 0xd7a751ff, 0xd2a144ff, 0xca9331ff, 0xbf8012ff, 0xb76f05ff, 0xb26b05ff, 0xab5f04ff, 0xa35004ff, 0xa95f04ff, 0xae6a07ff, 0xcc983dff, 0xe6c177ff, 0xe5bd6cff, 0xd29e39ff, 0xa45b0aff, 0x974903ff, 0x974904ff, 0xa05506ff, 0xbc7d13ff, 0xcb921aff, 0xc18a17ff, 0xac7b15ff, 0x7f580cff, 0x45371dff, 0x787977ff, 0x8a8c87ff, 0xb9bab7ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xdbdcdaff, 0xcecfccff, 0xbebfbcff, 0xd5d6d4ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xc9cac9ff, 0x55534dff, 0x716144ff, 0x9e844fff, 0xbb9d5fff, 0xc9a660ff, 0xcfa659ff, 0xca9c3fff, 0xbb831eff, 0xa36106ff, 0x9e5a04ff, 0x9b5804ff, 0xa75f02ff, 0xbf8427ff, 0xe0c071ff, 0xd29d41ff, 0xd5a34cff, 0xd3a249ff, 0xd19e42ff, 0xd5a54dff, 0xd3a246ff, 0xcd9836ff, 0xcd9735ff, 0xcc993fff, 0xcf9b41ff, 0xd4a34eff, 0xd2a551ff, 0xcb973aff, 0xc68f2eff, 0xc28824ff, 0xc18725ff, 0xc58b29ff, 0xd19f45ff, 0xd2a142ff, 0xd2a142ff, 0xd3a142ff, 0xd6a546ff, 0xd8a94fff, 0xd8a84eff, 0xd5a649ff, 0xd5a549ff, 0xcf9832ff, 0xcf9a3dff, 0xd7a856ff, 0xd9ac5bff, 0xd9ab5eff, 0xd19f4bff, 0xcf9a40ff, 0xd09d48ff, 0xc99136ff, 0xc28724ff, 0xc28422ff, 0xcb8f24ff, 0xe2a832ff, 0xefc668ff, 0xf9e2a2ff, 0xfbe8aeff, 0xfce9a7ff, 0xfbe8a6ff, 0xf9e29eff, 0xf8e1a0ff, 0xf6e0a6ff, 0xf4dc9eff, 0xf3d998ff, 0xf2d596ff, 0xeed18cff, 0xeecf8cff, 0xebca85ff, 0xe8c680ff, 0xe2bb72ff, 0xdeb15dff, 0xdcae5cff, 0xd6a753ff, 0xc9912bff, 0xc18314ff, 0xba7507ff, 0xb46d04ff, 0xae6404ff, 0xa65405ff, 0xaa5e04ff, 0xad6705ff, 0xc68e31ff, 0xe5be6fff, 0xe7c278ff, 0xdeb156ff, 0xb97b22ff, 0x984a03ff, 0x994b04ff, 0x994b04ff, 0x994d04ff, 0xaa690cff, 0xb98519ff, 0x926a13ff, 0x4e4123ff, 0x6d6e6cff, 0x8b8d89ff, 0x999b96ff, 0xcfd0ceff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdddeddff, 0x767674ff, 0x554a34ff, 0x947e54ff, 0xbaa06eff, 0xccae74ff, 0xd2b070ff, 0xd6b372ff, 0xcca04aff, 0xc38f2aff, 0xb27511ff, 0xa46307ff, 0x9c5503ff, 0xa7600dff, 0xd2a758ff, 0xe3c880ff, 0xcf9937ff, 0xd3a145ff, 0xd3a248ff, 0xd19f42ff, 0xd7a64eff, 0xd4a44aff, 0xd09f43ff, 0xcd9b3dff, 0xce9c42ff, 0xcf9f4fff, 0xd09f4dff, 0xcfa14cff, 0xce9b44ff, 0xc99336ff, 0xc68e30ff, 0xc38929ff, 0xc38b2cff, 0xcc9b42ff, 0xd1a045ff, 0xd09c39ff, 0xd19f3fff, 0xd3a245ff, 0xd3a242ff, 0xd4a140ff, 0xd2a03dff, 0xd29e3aff, 0xd2a046ff, 0xc78f27ff, 0xcd9736ff, 0xd29f45ff, 0xd3a34eff, 0xd19e4bff, 0xcb9541ff, 0xc88f3aff, 0xca9540ff, 0xc18320ff, 0xbb7812ff, 0xc7871fff, 0xe6ad45ff, 0xf2c970ff, 0xfbe7a6ff, 0xfbe9aaff, 0xfcedb4ff, 0xfceeb9ff, 0xfbe9b1ff, 0xf8e19fff, 0xf6df9aff, 0xf6dfa2ff, 0xf4da97ff, 0xf3d893ff, 0xf3d995ff, 0xf3d89bff, 0xedcf91ff, 0xeac888ff, 0xe7c27eff, 0xe0b565ff, 0xdeb362ff, 0xd9ac5aff, 0xcc9434ff, 0xc08111ff, 0xbe7c0dff, 0xb9740aff, 0xb16604ff, 0xab5c05ff, 0xab6004ff, 0xad6704ff, 0xb5750fff, 0xdaad57ff, 0xe6c173ff, 0xe6bf6eff, 0xd6a648ff, 0xa45b0bff, 0x954903ff, 0x944804ff, 0x8e4404ff, 0x8d4704ff, 0x8b5c0eff, 0x554728ff, 0x717270ff, 0x8c8e89ff, 0x9c9d99ff, 0xd1d2cfff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xa2a3a2ff, 0x484236ff, 0x816d48ff, 0xac9466ff, 0xc6a973ff, 0xd1af70ff, 0xd1ab62ff, 0xca9f49ff, 0xc49331ff, 0xc49230ff, 0xc38d2aff, 0xbf8720ff, 0xa9660bff, 0xbc8a43ff, 0xe7cf93ff, 0xe5ca8cff, 0xd4a043ff, 0xdaad5fff, 0xd9ac5dff, 0xd6a852ff, 0xd8ab54ff, 0xd4a64bff, 0xd4a852ff, 0xd3a54fff, 0xd3a551ff, 0xcfa14eff, 0xcfa24fff, 0xcd9e4aff, 0xcb9b49ff, 0xc79339ff, 0xc38b2cff, 0xc28a2cff, 0xc1892aff, 0xc9963dff, 0xd1a34fff, 0xcd9835ff, 0xd1a045ff, 0xd3a552ff, 0xd3a54eff, 0xd29f41ff, 0xd2a040ff, 0xd2a042ff, 0xd09e3fff, 0xc9932dff, 0xc18313ff, 0xc68b24ff, 0xcc973bff, 0xce9b42ff, 0xcc994aff, 0xca9343ff, 0xc99243ff, 0xbd7d21ff, 0xad6305ff, 0xc47e18ff, 0xecb54cff, 0xf7d482ff, 0xfce8abff, 0xfbe9b0ff, 0xfbeab4ff, 0xfcebbaff, 0xfbeab6ff, 0xfae4a4ff, 0xf9e39fff, 0xf8e1a2ff, 0xf5de9cff, 0xf4dc97ff, 0xf4d99aff, 0xf3d79aff, 0xf1d49bff, 0xeecd92ff, 0xe9c689ff, 0xdfb86cff, 0xe2b86cff, 0xdbaf62ff, 0xcb9532ff, 0xc38715ff, 0xc68b27ff, 0xbf811eff, 0xb56e07ff, 0xaf6105ff, 0xaf6704ff, 0xb16e06ff, 0xb27108ff, 0xc18722ff, 0xdbae52ff, 0xe4ba5fff, 0xe5bb64ff, 0xba7d2aff, 0x954903ff, 0x914703ff, 0x8a4303ff, 0x723a03ff, 0x4e3d27ff, 0x757774ff, 0x8c8e89ff, 0x9ea09cff, 0xd3d4d2ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xc7c8c7ff, 0x535049ff, 0x67552eff, 0x9a7e46ff, 0xb8995dff, 0xcdad70ff, 0xd5b26fff, 0xcfa856ff, 0xca9d45ff, 0xbf8f2dff, 0xc18e29ff, 0xc28e26ff, 0xc39030ff, 0xc18c29ff, 0xdbbb75ff, 0xe6d097ff, 0xe4c98cff, 0xd7a64cff, 0xdeb56eff, 0xddb36bff, 0xd9af5fff, 0xd2a348ff, 0xd3a54cff, 0xd6a855ff, 0xd3a751ff, 0xd0a249ff, 0xd0a24eff, 0xca993cff, 0xc9973aff, 0xcc9a45ff, 0xc9943aff, 0xc58f33ff, 0xc38c30ff, 0xbe8523ff, 0xc08726ff, 0xd0a152ff, 0xd3a85aff, 0xd3a857ff, 0xd3a757ff, 0xd1a551ff, 0xd2a44eff, 0xd1a34bff, 0xd1a34cff, 0xcfa045ff, 0xca9738ff, 0xc48a1fff, 0xc48a21ff, 0xbe801aff, 0xc08421ff, 0xc38933ff, 0xbf842dff, 0xbd8028ff, 0xb67517ff, 0xaf6404ff, 0xc27c0eff, 0xebb852ff, 0xf9db91ff, 0xfce9b0ff, 0xfbe8b3ff, 0xfae7b2ff, 0xfbe9b4ff, 0xfae7adff, 0xfae5aaff, 0xf8e4a7ff, 0xf6de98ff, 0xf5dd99ff, 0xf6dd98ff, 0xf7dea1ff, 0xf5da9fff, 0xefd191ff, 0xeaca88ff, 0xe6c383ff, 0xe2bc74ff, 0xdfb567ff, 0xd7a752ff, 0xcd9731ff, 0xcb942dff, 0xcc9539ff, 0xc3892aff, 0xba760bff, 0xb06404ff, 0xae6205ff, 0xb26f08ff, 0xb3720aff, 0xb6760cff, 0xbe8016ff, 0xd5a33dff, 0xe2b75dff, 0xdcb05dff, 0xbc853aff, 0x9d611cff, 0x6d3908ff, 0x4b3b2aff, 0x797b78ff, 0x8c8e89ff, 0xa1a39fff, 0xd5d6d4ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdbdcdbff, 0x757573ff, 0x504329ff, 0x8e7442ff, 0xad8c4bff, 0xbd9647ff, 0xcba453ff, 0xd2ab5aff, 0xd2af63ff, 0xcea553ff, 0xc39436ff, 0xc39230ff, 0xc39233ff, 0xc3933bff, 0xca9d48ff, 0xe3c989ff, 0xe4cd94ff, 0xe5cb8fff, 0xd8aa50ff, 0xd9ae5cff, 0xdbb164ff, 0xd9b063ff, 0xd2a34bff, 0xd09f45ff, 0xd4a54fff, 0xd4a855ff, 0xd1a34cff, 0xcc9c40ff, 0xca983dff, 0xcc9d46ff, 0xca9843ff, 0xc8943bff, 0xc68f32ff, 0xc38c30ff, 0xbf8627ff, 0xbb801dff, 0xcb9844ff, 0xd4a960ff, 0xd3a858ff, 0xd4a959ff, 0xd5ab60ff, 0xd3a95cff, 0xd1a655ff, 0xd3a95bff, 0xd0a554ff, 0xd0a24eff, 0xca983aff, 0xc7922dff, 0xc2881fff, 0xba7d13ff, 0xb06c0aff, 0xae670bff, 0xbb7c14ff, 0xbe8317ff, 0xc2851aff, 0xc4881eff, 0xe2ad49ff, 0xf6d381ff, 0xfce8abff, 0xfdeab8ff, 0xfbeab8ff, 0xfbeab6ff, 0xfae7afff, 0xf8e3a6ff, 0xf7e3a5ff, 0xf7df9cff, 0xf7de98ff, 0xf5d890ff, 0xf5db9eff, 0xf2d598ff, 0xf0d291ff, 0xeccb89ff, 0xe7c480ff, 0xe5be77ff, 0xe1b86eff, 0xdcae5cff, 0xd7a54fff, 0xd2a048ff, 0xcd983bff, 0xc68c2dff, 0xbc7a0eff, 0xb26804ff, 0xad6205ff, 0xb26f08ff, 0xb26f07ff, 0xb37006ff, 0xb47206ff, 0xbf8216ff, 0xd3a442ff, 0xd6ac54ff, 0xcba453ff, 0x97783fff, 0x51493cff, 0x7b7d7bff, 0x8c8e89ff, 0xa5a6a2ff, 0xd7d9d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xa1a3a1ff, 0x443d2dff, 0x7a6438ff, 0xa58b57ff, 0xbd9d59ff, 0xc2993cff, 0xc79a37ff, 0xcfa54bff, 0xcea44cff, 0xca9f45ff, 0xcca14aff, 0xc99c3eff, 0xc2902cff, 0xbe8924ff, 0xd3ac57ff, 0xe4cb8bff, 0xe2ca8aff, 0xe5cc8eff, 0xd8a851ff, 0xd9ab58ff, 0xdcb367ff, 0xdeb973ff, 0xd4a855ff, 0xcfa044ff, 0xd3a853ff, 0xd5aa5aff, 0xd1a54eff, 0xcea14bff, 0xd0a454ff, 0xd0a455ff, 0xc99946ff, 0xc48f34ff, 0xc38c2cff, 0xc59138ff, 0xc48f36ff, 0xc08628ff, 0xc38c31ff, 0xcfa255ff, 0xd2a554ff, 0xd4a95dff, 0xd5ab60ff, 0xd4aa5eff, 0xd4aa5fff, 0xd7af67ff, 0xd5ad64ff, 0xd5aa60ff, 0xd3a653ff, 0xcea044ff, 0xc99633ff, 0xc99331ff, 0xc38b2bff, 0xc58b2bff, 0xc99234ff, 0xcc9a42ff, 0xcc9a42ff, 0xca9336ff, 0xd39d38ff, 0xefc56bff, 0xf9e3a0ff, 0xfae6aaff, 0xfbe8b7ff, 0xfae8b9ff, 0xfbeabaff, 0xfbe9b3ff, 0xf9e4a7ff, 0xf7e0a3ff, 0xf7dea0ff, 0xf5db96ff, 0xf2d692ff, 0xefd28eff, 0xf1d392ff, 0xf0d297ff, 0xe9c680ff, 0xe4be74ff, 0xdcb261ff, 0xddb05cff, 0xdcb066ff, 0xd7a857ff, 0xd09a3dff, 0xc78f2aff, 0xbc7a0fff, 0xb36804ff, 0xaf6404ff, 0xb37209ff, 0xb4740aff, 0xb5760aff, 0xb7780fff, 0xb5760cff, 0xba8318ff, 0xbd9233ff, 0x90722fff, 0x554e3fff, 0x7e807eff, 0x8c8e89ff, 0xa8aaa6ff, 0xd9dad8ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc5c6c4ff, 0x53514aff, 0x614e26ff, 0x95783bff, 0xb38f46ff, 0xc19942ff, 0xcca245ff, 0xcfa54dff, 0xcb9e41ff, 0xcc9f3dff, 0xcda141ff, 0xca9f41ff, 0xc6983bff, 0xbd8b24ff, 0xc3912eff, 0xdfc073ff, 0xe4cc8bff, 0xe3cd8dff, 0xe5ce95ff, 0xdaad5cff, 0xd8aa55ff, 0xdbb160ff, 0xddb56cff, 0xd3a855ff, 0xce9e3fff, 0xd4a95aff, 0xd5ac64ff, 0xd6ab5eff, 0xcfa355ff, 0xcea252ff, 0xcea250ff, 0xc89844ff, 0xc6923bff, 0xc38c2dff, 0xc38e36ff, 0xc38e37ff, 0xc28a2eff, 0xbe8324ff, 0xc89846ff, 0xd1a656ff, 0xd3a85bff, 0xd4aa5eff, 0xd6ac61ff, 0xd8b069ff, 0xd9b16bff, 0xd9b16dff, 0xd8af66ff, 0xd5ab5aff, 0xd0a44dff, 0xcc9b3eff, 0xce9d42ff, 0xd0a24dff, 0xcfa04cff, 0xd2a452ff, 0xd2a554ff, 0xd2a34fff, 0xd1a049ff, 0xd29c3dff, 0xe7b655ff, 0xf1cf7eff, 0xf7e1a0ff, 0xf7e3a9ff, 0xf8e4afff, 0xf7e3b3ff, 0xf9e4b0ff, 0xf8e3a8ff, 0xf8e2a5ff, 0xf5dd9fff, 0xf3d791ff, 0xf4d899ff, 0xf2d694ff, 0xf1d390ff, 0xebc982ff, 0xe8c170ff, 0xe4bb69ff, 0xdaad53ff, 0xddb05cff, 0xdfb56bff, 0xdaad5dff, 0xd09c3eff, 0xc58b1eff, 0xbf8011ff, 0xb77007ff, 0xaf6504ff, 0xb26f05ff, 0xb5750aff, 0xb87b12ff, 0xba801fff, 0xb07714ff, 0xa36a07ff, 0x815d17ff, 0x575140ff, 0x818380ff, 0x8c8e89ff, 0xabada9ff, 0xdbdcdaff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdadbd9ff, 0xb7b8b5ff, 0xbbbcb9ff, 0xcbcdcaff, 0xd8d9d6ff, 0xdfe0deff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd8d9d7ff, 0x767775ff, 0x4d4026ff, 0x8b713cff, 0xad8d4cff, 0xc19a4dff, 0xc89e47ff, 0xd6b16cff, 0xdbb87bff, 0xd0a34cff, 0xc5921eff, 0xc59429ff, 0xc3912eff, 0xc1902dff, 0xc18f2cff, 0xd1a751ff, 0xe3c67fff, 0xe3c889ff, 0xe4cb8fff, 0xe5cb93ff, 0xdaae5bff, 0xd6a953ff, 0xd8ab52ff, 0xd7ac5aff, 0xd2a54eff, 0xd2a54dff, 0xd4a757ff, 0xd3a758ff, 0xd6ac61ff, 0xcfa554ff, 0xcb9f48ff, 0xcfa556ff, 0xcfa55dff, 0xcda054ff, 0xc89740ff, 0xc39139ff, 0xbd8427ff, 0xbc8224ff, 0xb77b16ff, 0xc59340ff, 0xd2a75fff, 0xd4aa5fff, 0xd7ae64ff, 0xd9af68ff, 0xd9b16aff, 0xd6ad63ff, 0xd7ad65ff, 0xd4a95cff, 0xd5a957ff, 0xd1a54eff, 0xcfa34cff, 0xd3a656ff, 0xd4a95bff, 0xd5aa5eff, 0xd7ab5fff, 0xd5a857ff, 0xd5a856ff, 0xd8ac5fff, 0xd4a24fff, 0xd4992dff, 0xe5b553ff, 0xf7e0a0ff, 0xfae7afff, 0xf9e7b5ff, 0xf5e0aaff, 0xf8e5afff, 0xf7e2a5ff, 0xf7e2a2ff, 0xf6df9cff, 0xf1d386ff, 0xefcd7fff, 0xf3d99bff, 0xf2d79eff, 0xe9c67bff, 0xe9c478ff, 0xe4bb6aff, 0xdfb45eff, 0xdcaf5bff, 0xdeb469ff, 0xd7a74fff, 0xd09f40ff, 0xc9922bff, 0xc88f28ff, 0xbb790cff, 0xaf6604ff, 0xae6904ff, 0xb6780eff, 0xb67a13ff, 0xb17817ff, 0xa26e12ff, 0x6d4809ff, 0x544d40ff, 0x848683ff, 0x8c8e89ff, 0xafb0adff, 0xdcdddbff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdedfddff, 0xb0b1aeff, 0x8c8e8aff, 0x8c8e89ff, 0x8f918cff, 0x969894ff, 0xa3a5a1ff, 0xb4b5b2ff, 0xc5c6c3ff, 0xd3d4d2ff, 0xdcdddbff, 0xe1e2e0ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe2e2e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe0e1dfff, 0xa0a19fff, 0x453f33ff, 0x79653aff, 0xaa915fff, 0xc4a569ff, 0xcca55aff, 0xcea451ff, 0xd6b06bff, 0xd5b06cff, 0xcea145ff, 0xc7952bff, 0xc79733ff, 0xc08e23ff, 0xbc891fff, 0xc49536ff, 0xd8b15aff, 0xdfbe6eff, 0xe1c57cff, 0xe3c988ff, 0xe2c685ff, 0xdbad5aff, 0xd9aa54ff, 0xd8ab57ff, 0xd9ad5eff, 0xd5a955ff, 0xd3a551ff, 0xd4a755ff, 0xd2a655ff, 0xd0a454ff, 0xd1a653ff, 0xd4aa5fff, 0xd4ae6cff, 0xd1aa6aff, 0xd0a863ff, 0xcca154ff, 0xc79546ff, 0xbd8428ff, 0xb67610ff, 0xb87a1aff, 0xbc8122ff, 0xc89744ff, 0xd1a65aff, 0xd6ab62ff, 0xd5aa5dff, 0xd5ab5eff, 0xd4ab60ff, 0xd4a95bff, 0xd2a757ff, 0xcfa24cff, 0xd1a551ff, 0xd6ae66ff, 0xdab270ff, 0xd8b16bff, 0xd7ac61ff, 0xdab068ff, 0xdab067ff, 0xdbb064ff, 0xdbaf64ff, 0xd6a757ff, 0xcc8d25ff, 0xe4b34cff, 0xf2d58cff, 0xf7e2a8ff, 0xf8e5b1ff, 0xf6e3aaff, 0xf6e4b1ff, 0xf7e4b0ff, 0xf7e2a9ff, 0xf7e1a2ff, 0xf4d994ff, 0xefcb71ff, 0xefd189ff, 0xf2d699ff, 0xedcc85ff, 0xebc575ff, 0xe8c06eff, 0xe6bf71ff, 0xddb25dff, 0xdeb264ff, 0xd8a850ff, 0xd3a241ff, 0xcb952dff, 0xc8902cff, 0xbe7d11ff, 0xb26b06ff, 0xae6706ff, 0xb57a16ff, 0xb0791aff, 0xa36f17ff, 0x6c4b14ff, 0x534f45ff, 0x858885ff, 0x8d8f8aff, 0xb3b4b1ff, 0xdedfddff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe0e1dfff, 0xa8a9a7ff, 0x4c4b47ff, 0x4e4d48ff, 0x646562ff, 0x767875ff, 0x838582ff, 0x8a8c88ff, 0x8c8e89ff, 0x8d8f8aff, 0x929490ff, 0x9d9f9bff, 0xadaeabff, 0xbebfbcff, 0xcecfccff, 0xd9dad8ff, 0xe0e1dfff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xc1c2c0ff, 0x55544fff, 0x5f4f31ff, 0x9c8557ff, 0xc1a775ff, 0xcead6fff, 0xcba14fff, 0xcea24cff, 0xd2a759ff, 0xd0a44eff, 0xcea143ff, 0xca9b39ff, 0xc89832ff, 0xc18f26ff, 0xc1902dff, 0xcca144ff, 0xdcb55cff, 0xdfbc6eff, 0xe0c37dff, 0xe1c37aff, 0xe0be6eff, 0xe1b86dff, 0xdfb873ff, 0xddb469ff, 0xdaaf62ff, 0xdbb370ff, 0xd6ab60ff, 0xd1a24bff, 0xd5aa5eff, 0xd2a657ff, 0xd5aa5eff, 0xd6ae67ff, 0xd7b171ff, 0xd5ac6bff, 0xd1a862ff, 0xcb9f55ff, 0xc79344ff, 0xbe852bff, 0xb3720aff, 0xb4710aff, 0xb6710eff, 0xba7f21ff, 0xcb9b4dff, 0xd3a760ff, 0xd3a85dff, 0xd5a95cff, 0xd3a85bff, 0xd2a654ff, 0xd0a555ff, 0xcea04bff, 0xd4a95cff, 0xdbb473ff, 0xdeb97aff, 0xdcb876ff, 0xddb571ff, 0xddb671ff, 0xddb46cff, 0xddb366ff, 0xdaae60ff, 0xd7a852ff, 0xdaa74aff, 0xe7b95bff, 0xf1cf7cff, 0xf3da94ff, 0xf3db96ff, 0xf5dfa0ff, 0xf6e0a9ff, 0xf6e3b3ff, 0xf6e0acff, 0xf6dfa4ff, 0xf5dca0ff, 0xf1d185ff, 0xebc56bff, 0xedcb7fff, 0xebc87aff, 0xebc46fff, 0xebc87eff, 0xe7c070ff, 0xe3ba6cff, 0xe5bb70ff, 0xdbad58ff, 0xd7a64dff, 0xcc9630ff, 0xc18415ff, 0xb87406ff, 0xb06a04ff, 0xae6b06ff, 0xae7512ff, 0xa06c12ff, 0x6b4d18ff, 0x58554dff, 0x878986ff, 0x8d8f8aff, 0xb6b8b4ff, 0xdfe0deff, 0xe1e2e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xaeb0aeff, 0x46423cff, 0x2e1c03ff, 0x412f0cff, 0x483d26ff, 0x443e30ff, 0x444139ff, 0x4c4b46ff, 0x5c5d5aff, 0x6f706eff, 0x7f807dff, 0x888a86ff, 0x8b8d89ff, 0x8c8e8aff, 0x90928dff, 0x989a95ff, 0xa6a7a3ff, 0xb7b8b5ff, 0xc8c9c7ff, 0xcbccc9ff, 0x777876ff, 0x4c422dff, 0x887043ff, 0xad935bff, 0xc6a666ff, 0xd2ac65ff, 0xcea450ff, 0xcfa34aff, 0xd0a34aff, 0xcea043ff, 0xd1a651ff, 0xd1a958ff, 0xcfa659ff, 0xcb9e4bff, 0xc5963cff, 0xcea44bff, 0xdcb45eff, 0xdebb6fff, 0xe0c279ff, 0xe0be6cff, 0xddb863ff, 0xdeb872ff, 0xd5a756ff, 0xdaaf66ff, 0xdbb169ff, 0xdab170ff, 0xd5aa5fff, 0xcf9f45ff, 0xd8af66ff, 0xd5ab60ff, 0xd4a95aff, 0xd9b170ff, 0xd9b176ff, 0xd6ac6bff, 0xd2a355ff, 0xce9b44ff, 0xcb9537ff, 0xc48722ff, 0xbf7f13ff, 0xbf7e13ff, 0xbb740aff, 0xb77312ff, 0xc58f3fff, 0xd3aa69ff, 0xd6af72ff, 0xd5ad6aff, 0xd3aa5fff, 0xd2a85dff, 0xd4aa65ff, 0xd4ab62ff, 0xd3a95dff, 0xdbb371ff, 0xddb573ff, 0xdeb66eff, 0xdeba75ff, 0xdebb79ff, 0xdeb670ff, 0xdbad5bff, 0xdaab55ff, 0xdbac55ff, 0xddaf59ff, 0xe9c06bff, 0xf1d180ff, 0xf5da92ff, 0xf3da92ff, 0xf4de9fff, 0xf5dfa3ff, 0xf6e0aaff, 0xf4dca6ff, 0xf1d697ff, 0xf3d99cff, 0xf2d692ff, 0xf0d17fff, 0xecca76ff, 0xeac573ff, 0xeac677ff, 0xeac77aff, 0xe8c474ff, 0xebc985ff, 0xe7c27cff, 0xdeb25dff, 0xdaaa55ff, 0xcb9430ff, 0xb9780aff, 0xb06b05ff, 0xaa6704ff, 0xa56605ff, 0x976207ff, 0x674811ff, 0x5c5951ff, 0x898b87ff, 0x8e908bff, 0xbabbb8ff, 0xe0e1dfff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xb3b4b2ff, 0x4f4e49ff, 0x4b3c22ff, 0x573308ff, 0x633d08ff, 0x826a3bff, 0x857452ff, 0x786a4fff, 0x675b46ff, 0x564d3aff, 0x494132ff, 0x423e32ff, 0x47453fff, 0x555551ff, 0x676866ff, 0x797a78ff, 0x858783ff, 0x8b8c88ff, 0x8c8e89ff, 0x90928dff, 0x888986ff, 0x49443aff, 0x766136ff, 0xa18349ff, 0xbd9d5bff, 0xcfab65ff, 0xd6b36bff, 0xd2a958ff, 0xd1a651ff, 0xcca149ff, 0xcea34fff, 0xd1a757ff, 0xcda350ff, 0xca9e49ff, 0xcba04eff, 0xc7983eff, 0xd2a445ff, 0xdbaf56ff, 0xe1c27cff, 0xe2c37bff, 0xddb965ff, 0xd8b25aff, 0xd9af5bff, 0xd8ab54ff, 0xdaaf63ff, 0xd7ab5dff, 0xd5a756ff, 0xcd9b40ff, 0xd0a14aff, 0xd1a350ff, 0xd5a95bff, 0xd9ad65ff, 0xdbb172ff, 0xd8ab63ff, 0xd7a752ff, 0xdaa74eff, 0xd9a344ff, 0xd5a03bff, 0xd1992bff, 0xce952aff, 0xca9028ff, 0xc68820ff, 0xbb7b15ff, 0xc2862eff, 0xcc9f56ff, 0xd6af6dff, 0xdcb781ff, 0xdab47aff, 0xd8b172ff, 0xdcb578ff, 0xdeb97eff, 0xdab272ff, 0xddb574ff, 0xdcb370ff, 0xdeb66eff, 0xe0ba76ff, 0xe1bc7aff, 0xdfb46aff, 0xdcad5aff, 0xdcae59ff, 0xdeb15fff, 0xdeae5aff, 0xe6bb63ff, 0xf1d180ff, 0xf3d589ff, 0xf0cf7dff, 0xf1d183ff, 0xf1d58dff, 0xf3dc9eff, 0xf3da9eff, 0xf2d799ff, 0xf1d594ff, 0xf2d792ff, 0xf2d88eff, 0xf3d891ff, 0xeccb7dff, 0xeccb81ff, 0xf3d89aff, 0xf1d69aff, 0xedcf8bff, 0xebc988ff, 0xe2ba6bff, 0xdeb266ff, 0xd09d44ff, 0xb6740cff, 0xa86504ff, 0xa36405ff, 0x945e06ff, 0x5d4114ff, 0x5f5d56ff, 0x8a8c88ff, 0x8f918cff, 0xbdbfbcff, 0xe0e1dfff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e2e0ff, 0xb8b9b7ff, 0x51514cff, 0x544930ff, 0x89764fff, 0x917038ff, 0x794409ff, 0x956c28ff, 0xb19764ff, 0xad9c77ff, 0xa29275ff, 0x98896cff, 0x8c7f60ff, 0x7e7152ff, 0x6f6449ff, 0x5c5038ff, 0x4a402bff, 0x413a2cff, 0x434037ff, 0x4e4d48ff, 0x5f605dff, 0x727471ff, 0x54534dff, 0x614f2bff, 0x98793fff, 0xb69451ff, 0xcaa660ff, 0xd6b370ff, 0xd9b770ff, 0xd6af5dff, 0xcfa44aff, 0xcba044ff, 0xd1a955ff, 0xcb9f42ff, 0xc5932eff, 0xc59430ff, 0xc39330ff, 0xc99936ff, 0xdcb254ff, 0xe0bd6dff, 0xe3c785ff, 0xdfc17aff, 0xd8b460ff, 0xd5af58ff, 0xd6ab52ff, 0xd4a74cff, 0xd5a956ff, 0xd0a047ff, 0xce9a3aff, 0xc8932dff, 0xce9e42ff, 0xd2a24aff, 0xdca951ff, 0xe1ac52ff, 0xe0aa47ff, 0xe2ad50ff, 0xe3b152ff, 0xdaa342ff, 0xd49937ff, 0xddaa50ff, 0xdaa74bff, 0xd29d39ff, 0xcf9a39ff, 0xcd9637ff, 0xc99036ff, 0xc3892dff, 0xc99545ff, 0xd8af6dff, 0xe2c08dff, 0xdebb87ff, 0xdfb981ff, 0xe4bf89ff, 0xe4bf8cff, 0xe4c08bff, 0xe5c18aff, 0xe0bb7eff, 0xdeb66fff, 0xe0b975ff, 0xe1bb7aff, 0xe4bd7aff, 0xe2b66dff, 0xe2b972ff, 0xe1b56cff, 0xd9a550ff, 0xddaa4aff, 0xefc772ff, 0xedc971ff, 0xe9c15dff, 0xf0cc74ff, 0xf1d387ff, 0xf3d997ff, 0xf3d99bff, 0xf0d68eff, 0xf3d993ff, 0xf5dd9cff, 0xf3d890ff, 0xf2d890ff, 0xf0d288ff, 0xf1d48fff, 0xf5deabff, 0xf2d7a0ff, 0xebca82ff, 0xe8c377ff, 0xe2b96aff, 0xdcae5bff, 0xcb9532ff, 0xaf6e0aff, 0xa26404ff, 0x905a03ff, 0x573c12ff, 0x605f5bff, 0x8b8d89ff, 0x91938eff, 0xc1c2bfff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xbdbebcff, 0x56554fff, 0x504224ff, 0x8a7445ff, 0xac935cff, 0xc1a466ff, 0xae803dff, 0x8f510bff, 0xba9042ff, 0xd0b781ff, 0xc8b287ff, 0xc0ae8aff, 0xbaaa89ff, 0xb1a07bff, 0xa89a75ff, 0x9c8c63ff, 0x8c7543ff, 0x7f6a40ff, 0x71613fff, 0x615234ff, 0x57492dff, 0x52472fff, 0x63522dff, 0x917135ff, 0xae8b47ff, 0xc49e56ff, 0xd1aa62ff, 0xd6b06bff, 0xd8b166ff, 0xd8b265ff, 0xd5ad5aff, 0xd5ab57ff, 0xcfa44dff, 0xc89739ff, 0xc18f2bff, 0xc08e25ff, 0xbe8c1eff, 0xd5aa4cff, 0xe1bc69ff, 0xe3c480ff, 0xe3c78cff, 0xddc281ff, 0xd6b66cff, 0xd7b66eff, 0xdcb36fff, 0xdaaf65ff, 0xd8ab5fff, 0xd09f46ff, 0xcf9a3eff, 0xca942cff, 0xd29d39ff, 0xe1ac4bff, 0xeab75dff, 0xecbc68ff, 0xeab75aff, 0xeab964ff, 0xe8b964ff, 0xe3b053ff, 0xe0ac54ff, 0xe4b663ff, 0xe0b25dff, 0xd9a951ff, 0xd3a144ff, 0xd29f48ff, 0xce9b49ff, 0xc48a2cff, 0xc9923bff, 0xdbb175ff, 0xe3c092ff, 0xe0bb85ff, 0xe0ba7bff, 0xe5c28bff, 0xe9c796ff, 0xe9c898ff, 0xebc897ff, 0xe7c38aff, 0xe2ba73ff, 0xe5bf7eff, 0xe8c287ff, 0xecc891ff, 0xe6c182ff, 0xdfb370ff, 0xd39e4cff, 0xc88c2cff, 0xc68b23ff, 0xdeae4cff, 0xe8ba55ff, 0xeabc59ff, 0xf0cb75ff, 0xf3d382ff, 0xf2d487ff, 0xf3d992ff, 0xf5e0a3ff, 0xf8e2acff, 0xf8e4afff, 0xf8e0a8ff, 0xf6dc9bff, 0xf1d080ff, 0xf4d48cff, 0xf3d99fff, 0xf1d597ff, 0xeece90ff, 0xe8c479ff, 0xe5bd72ff, 0xd9aa57ff, 0xc08a27ff, 0xa16506ff, 0x8d5702ff, 0x563d15ff, 0x646460ff, 0x8b8d89ff, 0x92948fff, 0xc4c5c3ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc1c3c1ff, 0x5c5c58ff, 0x4e4022ff, 0x816528ff, 0xa28036ff, 0xc19d50ff, 0xd4b26aff, 0xd8b266ff, 0xbd8931ff, 0xbf8b2dff, 0xddb96aff, 0xe2c88dff, 0xdfca9bff, 0xdbcaa2ff, 0xd5c49aff, 0xcebd92ff, 0xc7b78eff, 0xbca874ff, 0xae904eff, 0xa38a54ff, 0x9b8456ff, 0x977f50ff, 0x9a7e4bff, 0xa4864cff, 0xb08d48ff, 0xc39f59ff, 0xcda65aff, 0xd3aa57ff, 0xd4ad5bff, 0xd6af61ff, 0xd8b062ff, 0xd4ac5cff, 0xd3ac5dff, 0xd3aa5aff, 0xcb9f47ff, 0xc49433ff, 0xc29027ff, 0xc4932bff, 0xdbb356ff, 0xe1bd68ff, 0xe4c785ff, 0xe3c98fff, 0xdabe7eff, 0xd2b367ff, 0xd5b56dff, 0xdbae64ff, 0xd9ab5fff, 0xd7ab5cff, 0xd1a146ff, 0xc9952eff, 0xd1992dff, 0xe5b04dff, 0xeec273ff, 0xefc477ff, 0xefc472ff, 0xedbd64ff, 0xe9b85fff, 0xe9b861ff, 0xe7b65eff, 0xe8bc70ff, 0xe8c07bff, 0xe4b969ff, 0xddaf58ff, 0xd7a54aff, 0xd9ab60ff, 0xd1a151ff, 0xc78f30ff, 0xc68d2cff, 0xdbaf6fff, 0xe2bc87ff, 0xe3bd86ff, 0xe6c18aff, 0xe9c692ff, 0xebcc9cff, 0xebcc9cff, 0xedca97ff, 0xebc791ff, 0xe9c585ff, 0xecc98eff, 0xeecd95ff, 0xe9c387ff, 0xdbac60ff, 0xcc943bff, 0xbe811bff, 0xb4760bff, 0xb17205ff, 0xbb7e10ff, 0xd59f32ff, 0xe3b144ff, 0xecbe58ff, 0xf1cb71ff, 0xf4d98fff, 0xf6df9fff, 0xf7e3adff, 0xf6dfa8ff, 0xf7e2b0ff, 0xf6e2b1ff, 0xf3da9bff, 0xf2d78cff, 0xf5dca0ff, 0xf0d594ff, 0xefd18aff, 0xefd290ff, 0xebca82ff, 0xe0b767ff, 0xc69432ff, 0xa76f16ff, 0x895507ff, 0x58411aff, 0x696965ff, 0x8c8e89ff, 0x949691ff, 0xc7c9c6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc6c7c5ff, 0x62625fff, 0x544c3bff, 0x8d7c57ff, 0xac9053ff, 0xbb9441ff, 0xc99d40ff, 0xd1a441ff, 0xd6a747ff, 0xd0a03dff, 0xcc9a30ff, 0xdaad4dff, 0xecd18fff, 0xf0dda8ff, 0xecd8a5ff, 0xe8d6aaff, 0xe5d2a4ff, 0xe2d0a1ff, 0xdbc697ff, 0xd1b26dff, 0xc7a55cff, 0xc1a46dff, 0xbb9d64ff, 0xba9857ff, 0xbd9851ff, 0xc49c4fff, 0xc89e4dff, 0xcfa451ff, 0xd1a651ff, 0xd5ac59ff, 0xd5af5fff, 0xd9b368ff, 0xd2a956ff, 0xcea552ff, 0xd0a758ff, 0xca9f46ff, 0xc79a3cff, 0xc2922cff, 0xcda13cff, 0xe0ba64ff, 0xe0bf6fff, 0xdec077ff, 0xddc180ff, 0xd9bb77ff, 0xd8ba78ff, 0xd6b877ff, 0xd29c3bff, 0xd19b3cff, 0xd4a247ff, 0xd39f40ff, 0xd49f3bff, 0xe3af4cff, 0xf0c87bff, 0xf0cd89ff, 0xf1ce88ff, 0xf0cc88ff, 0xedc880ff, 0xedc476ff, 0xedc57aff, 0xe9bc6bff, 0xe6b767ff, 0xe5b867ff, 0xe4b764ff, 0xdeaf57ff, 0xd4a03bff, 0xd09b37ff, 0xcf9b37ff, 0xc68d22ff, 0xbf8215ff, 0xd19f4dff, 0xdaae6bff, 0xdfb674ff, 0xeac892ff, 0xebc996ff, 0xecca96ff, 0xedcc98ff, 0xeecc95ff, 0xebc98dff, 0xedcc8eff, 0xedca8dff, 0xe8c383ff, 0xd8a858ff, 0xc28527ff, 0xb47311ff, 0xaf7004ff, 0xaf7004ff, 0xb17205ff, 0xb27405ff, 0xb37606ff, 0xcc9629ff, 0xebc05fff, 0xf7da94ff, 0xf7de9dff, 0xf5dd9aff, 0xf7e1a6ff, 0xf4dd9fff, 0xf4dda2ff, 0xf4dba5ff, 0xf3da9cff, 0xf2d58eff, 0xf4da99ff, 0xf2d58fff, 0xf0d182ff, 0xf2d894ff, 0xeac980ff, 0xd2a449ff, 0xb07a1cff, 0x936823ff, 0x57482dff, 0x6d6e6aff, 0x8c8e89ff, 0x969894ff, 0xcbccc9ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc9cbc9ff, 0x656563ff, 0x4d4532ff, 0x897854ff, 0xb19d70ff, 0xcbb06fff, 0xddc17aff, 0xe7ca84ff, 0xe5c271ff, 0xd8ab45ff, 0xd3a339ff, 0xce9a30ff, 0xcc9623ff, 0xe6c577ff, 0xf1d9a0ff, 0xf0d69cff, 0xf0dcaaff, 0xf0deaeff, 0xefddadff, 0xebd7a7ff, 0xe9d39dff, 0xddba6cff, 0xcea455ff, 0xcda75aff, 0xd0aa5cff, 0xd1aa5dff, 0xcba14bff, 0xcb9e44ff, 0xcfa34cff, 0xd5ac5cff, 0xd4aa57ff, 0xd0a64fff, 0xd0a650ff, 0xd1a850ff, 0xd1a652ff, 0xd2a958ff, 0xcca044ff, 0xc59636ff, 0xc2932fff, 0xd9b35fff, 0xe0be71ff, 0xdebe70ff, 0xdaba6eff, 0xd9ba72ff, 0xdbbf7fff, 0xdbc085ff, 0xd5b673ff, 0xcf9528ff, 0xd39b39ff, 0xd9a54bff, 0xe2ae4dff, 0xeab85bff, 0xf0c577ff, 0xefcb86ff, 0xefcd8bff, 0xf1cf93ff, 0xf0cd91ff, 0xefcb88ff, 0xeecb86ff, 0xecc67fff, 0xe4b55eff, 0xe5b355ff, 0xebc174ff, 0xe4b75eff, 0xdca83eff, 0xd69f32ff, 0xd39e3bff, 0xd29c3aff, 0xce983aff, 0xc18317ff, 0xc48620ff, 0xd29e46ff, 0xddae62ff, 0xe9c38aff, 0xecca92ff, 0xedcb91ff, 0xe8c07bff, 0xe7bd71ff, 0xeac47cff, 0xe7bd76ff, 0xdca850ff, 0xce9330ff, 0xc17c12ff, 0xb87005ff, 0xb76f04ff, 0xb56e04ff, 0xb36f04ff, 0xb06f05ff, 0xb17205ff, 0xb07004ff, 0xb77c11ff, 0xd39f33ff, 0xefca75ff, 0xf9e3a5ff, 0xf8e3a2ff, 0xf9e4a2ff, 0xf7e3a0ff, 0xf8e4a7ff, 0xf6dca3ff, 0xf3d999ff, 0xf0d387ff, 0xf6de9cff, 0xf4d993ff, 0xf2d891ff, 0xf0d696ff, 0xe1bf77ff, 0xc09035ff, 0x9b7430ff, 0x594e37ff, 0x70716fff, 0x8c8e89ff, 0x999a96ff, 0xcdcfccff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xcdceccff, 0x6a6b69ff, 0x463e2dff, 0x826f4cff, 0xa89164ff, 0xc4a96eff, 0xdabc7aff, 0xe7cb87ff, 0xf0d598ff, 0xf4dda3ff, 0xf4da9cff, 0xeac97bff, 0xe2bc67ff, 0xcf9b2dff, 0xdab052ff, 0xedd190ff, 0xedd393ff, 0xf3dea7ff, 0xf6e6bdff, 0xf3e1b5ff, 0xefdcacff, 0xeedcacff, 0xe7c980ff, 0xdbad4bff, 0xdeb86bff, 0xdebb74ff, 0xd9b369ff, 0xd1a752ff, 0xd0a34aff, 0xcea048ff, 0xd1a753ff, 0xd2a752ff, 0xd1a653ff, 0xd2a858ff, 0xcfa44eff, 0xcba045ff, 0xca9f43ff, 0xc89a38ff, 0xc69834ff, 0xcea248ff, 0xdebe74ff, 0xdcba6dff, 0xdbb969ff, 0xd9b96cff, 0xd8ba6eff, 0xdbc389ff, 0xd8c187ff, 0xd1b166ff, 0xc7850eff, 0xd49b30ff, 0xeabc69ff, 0xf0c675ff, 0xf2c980ff, 0xf1cc89ff, 0xedc578ff, 0xedc780ff, 0xefcb86ff, 0xf2d396ff, 0xefcb80ff, 0xebc26cff, 0xe7b755ff, 0xe4b14cff, 0xebbd69ff, 0xeabf74ff, 0xe4b764ff, 0xe1b05bff, 0xdca953ff, 0xd4a046ff, 0xd39e42ff, 0xd59f47ff, 0xd09530ff, 0xce9129ff, 0xd29a35ff, 0xd59c34ff, 0xd9a64aff, 0xe1b563ff, 0xe3b460ff, 0xe0af52ff, 0xe0b158ff, 0xdfae55ff, 0xd3982eff, 0xc67e0aff, 0xc37a04ff, 0xc78409ff, 0xc8850aff, 0xc68208ff, 0xc37b06ff, 0xbd7204ff, 0xb77204ff, 0xb57205ff, 0xb17205ff, 0xb47505ff, 0xbf8412ff, 0xe4b552ff, 0xf6da91ff, 0xfbe8a9ff, 0xfbe9aaff, 0xfce8adff, 0xfae7b6ff, 0xf6dfa8ff, 0xf2d68eff, 0xf3d98cff, 0xf6de9aff, 0xf3d996ff, 0xeed28dff, 0xe2bf76ff, 0xcca550ff, 0x99742aff, 0x584d36ff, 0x737572ff, 0x8c8e89ff, 0x9b9d99ff, 0xd0d1cfff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd0d1cfff, 0x717270ff, 0x453e2eff, 0x7e6b45ff, 0xa79065ff, 0xc3a878ff, 0xd1b172ff, 0xdcb76aff, 0xe3c27bff, 0xeaca88ff, 0xeccd8bff, 0xedcd87ff, 0xecca78ff, 0xf2d796ff, 0xebcc8aff, 0xdaad50ff, 0xedcd83ff, 0xefd593ff, 0xeed59bff, 0xedd8a8ff, 0xedd8a8ff, 0xefdbabff, 0xefdcadff, 0xead090ff, 0xe5be68ff, 0xe4c37eff, 0xdcb76eff, 0xd7af60ff, 0xd1a652ff, 0xd0a54eff, 0xcfa24bff, 0xcca049ff, 0xd2a856ff, 0xd2aa5cff, 0xd0a85aff, 0xcba047ff, 0xcda248ff, 0xcda246ff, 0xca9c3aff, 0xc3942aff, 0xcfa74fff, 0xddbe77ff, 0xdbb96bff, 0xdaba68ff, 0xd9bc70ff, 0xd9bc77ff, 0xd8be7dff, 0xd8be82ff, 0xd2b470ff, 0xcb881fff, 0xe7b559ff, 0xf3d091ff, 0xf3d492ff, 0xf1cc84ff, 0xeec67aff, 0xeec471ff, 0xeec679ff, 0xf3d491ff, 0xf3d694ff, 0xf0cd82ff, 0xeabd5fff, 0xe8b854ff, 0xe9b762ff, 0xe8b969ff, 0xe4b35cff, 0xdeac51ff, 0xdeac5bff, 0xddaa58ff, 0xdba84fff, 0xdda642ff, 0xdfa948ff, 0xe5b869ff, 0xe6b969ff, 0xe2b35dff, 0xe0ad4fff, 0xdca746ff, 0xd69f33ff, 0xd79e2eff, 0xd79e2dff, 0xd79c2eff, 0xd29119ff, 0xce8807ff, 0xcd8807ff, 0xcc8607ff, 0xcc8d12ff, 0xcc8c16ff, 0xc98910ff, 0xcb870aff, 0xc7840aff, 0xc57f06ff, 0xc47c05ff, 0xc17f09ff, 0xc0820cff, 0xb97c07ff, 0xce9c38ff, 0xebc671ff, 0xf5db91ff, 0xf8e19dff, 0xf7df9dff, 0xf7e1a5ff, 0xf5dd9fff, 0xf9e1a0ff, 0xf7df9cff, 0xf4da94ff, 0xedd18cff, 0xe2bf72ff, 0xcba248ff, 0x9b7934ff, 0x5a4f38ff, 0x777976ff, 0x8c8e89ff, 0x9ea09cff, 0xd3d4d1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd3d4d2ff, 0x787876ff, 0x453e30ff, 0x796642ff, 0x9f8653ff, 0xbea269ff, 0xd2b378ff, 0xdcbc7bff, 0xe0bf7aff, 0xe2c07aff, 0xe5c480ff, 0xe7c57dff, 0xe8c374ff, 0xe8c576ff, 0xebc982ff, 0xeccd8aff, 0xe5be6fff, 0xebc570ff, 0xf0d68eff, 0xefd795ff, 0xecd391ff, 0xedd497ff, 0xf1deabff, 0xf3e1b1ff, 0xeed7a0ff, 0xe5c274ff, 0xe1be75ff, 0xdcb76dff, 0xddb974ff, 0xdab36bff, 0xd2a754ff, 0xd3a754ff, 0xcea14bff, 0xd2a859ff, 0xcea452ff, 0xcba14dff, 0xc99d44ff, 0xc59636ff, 0xc99d3fff, 0xc69734ff, 0xc08d21ff, 0xd4ae5dff, 0xddbf7bff, 0xd9b86dff, 0xd8b460ff, 0xdabc74ff, 0xddc286ff, 0xdbc087ff, 0xdac08aff, 0xd4b679ff, 0xebb95fff, 0xf4cf8bff, 0xf3d293ff, 0xf3d28fff, 0xf5d899ff, 0xf3d291ff, 0xf0cb84ff, 0xf2d294ff, 0xf1d18bff, 0xf0ce82ff, 0xf0cc82ff, 0xefc877ff, 0xefc97eff, 0xe8bb69ff, 0xdeaa48ff, 0xdca745ff, 0xdfa94dff, 0xdfaf5bff, 0xe1b056ff, 0xe9bb68ff, 0xebbe6dff, 0xeec376ff, 0xeeca87ff, 0xefca89ff, 0xefca87ff, 0xedc57aff, 0xe8bb65ff, 0xe4b250ff, 0xdea739ff, 0xd99d27ff, 0xd59515ff, 0xd3900cff, 0xd49212ff, 0xd29214ff, 0xd29112ff, 0xd2951cff, 0xcf9017ff, 0xcf9016ff, 0xd09113ff, 0xce9013ff, 0xcc8b0cff, 0xcb8808ff, 0xcb880bff, 0xcb8c13ff, 0xc7880dff, 0xc98c17ff, 0xd29c31ff, 0xe7bf69ff, 0xf2d287ff, 0xf3d585ff, 0xf6de97ff, 0xf8e0a0ff, 0xf8df9fff, 0xf4d992ff, 0xeccc81ff, 0xdfb86aff, 0xcea655ff, 0x927130ff, 0x514938ff, 0x7a7c7aff, 0x8c8e89ff, 0xa1a39fff, 0xd5d6d4ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd6d7d5ff, 0x7d7e7cff, 0x453e31ff, 0x776544ff, 0x9f885cff, 0xba9c63ff, 0xd0b379ff, 0xdab878ff, 0xddb970ff, 0xdebb71ff, 0xe2c181ff, 0xe3c280ff, 0xe3c177ff, 0xe3bc69ff, 0xe4bd6bff, 0xe6c06eff, 0xe7c274ff, 0xe8c274ff, 0xe7c068ff, 0xf0d387ff, 0xf3de9dff, 0xf4e1a1ff, 0xf5e3acff, 0xf5e4b1ff, 0xf2e2b2ff, 0xedd79fff, 0xe4c06fff, 0xe2be71ff, 0xdeba71ff, 0xdbb56dff, 0xd8b166ff, 0xd2a956ff, 0xcda14bff, 0xcc9f43ff, 0xcda24dff, 0xca9d48ff, 0xc69739ff, 0xc59537ff, 0xc49230ff, 0xc29029ff, 0xc08f28ff, 0xc19025ff, 0xd7b568ff, 0xdec285ff, 0xd8b872ff, 0xd4b05fff, 0xd5b364ff, 0xd8bb77ff, 0xdbc28aff, 0xd9c290ff, 0xd9bf8aff, 0xf4cf8dff, 0xf7d8a0ff, 0xf4d697ff, 0xf1d191ff, 0xf6dba7ff, 0xf6dca9ff, 0xf3d69aff, 0xf2d392ff, 0xeec670ff, 0xf1cd82ff, 0xf1cf8aff, 0xf3d492ff, 0xf1d191ff, 0xedc67aff, 0xe3b257ff, 0xe1ae52ff, 0xe3b057ff, 0xe8b864ff, 0xefc778ff, 0xf0c97aff, 0xf1cc82ff, 0xf1cd82ff, 0xf2d28fff, 0xf3d296ff, 0xf0cc8aff, 0xefc87cff, 0xedc26dff, 0xecbe63ff, 0xebbb60ff, 0xe3ac40ff, 0xe0a935ff, 0xdfa733ff, 0xe0a93dff, 0xdda739ff, 0xdca431ff, 0xd9a12dff, 0xd69c24ff, 0xd9a232ff, 0xd89f2fff, 0xd49a24ff, 0xd29923ff, 0xd39824ff, 0xd19721ff, 0xd19821ff, 0xd49b2cff, 0xd49b29ff, 0xce9117ff, 0xd39b2cff, 0xe6ba5cff, 0xecc368ff, 0xefc76cff, 0xefca78ff, 0xebc572ff, 0xe3ba63ff, 0xcc9e46ff, 0xb98d39ff, 0x8a6827ff, 0x524a3aff, 0x7d7f7cff, 0x8c8d89ff, 0xa4a6a2ff, 0xd7d8d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd8d9d7ff, 0x838482ff, 0x403a2dff, 0x72603dff, 0x9c8358ff, 0xbca172ff, 0xcbab6dff, 0xd9bb7eff, 0xdfc082ff, 0xdfbe7fff, 0xdebc78ff, 0xdeba73ff, 0xddb567ff, 0xdeb667ff, 0xe1bc71ff, 0xe2bc6fff, 0xe2bb6aff, 0xe2b865ff, 0xe3bc6aff, 0xe1b65bff, 0xebc774ff, 0xf4dd99ff, 0xf4de9fff, 0xf4dfa1ff, 0xf5e4aeff, 0xf2e0acff, 0xedd79bff, 0xeacc80ff, 0xe8ca85ff, 0xe6c88aff, 0xdebe7cff, 0xd8b164ff, 0xd3aa58ff, 0xcca24bff, 0xc89a3cff, 0xca9f49ff, 0xc99c47ff, 0xc59438ff, 0xc49232ff, 0xc39130ff, 0xbd891eff, 0xb98517ff, 0xc1902aff, 0xd7b465ff, 0xdcc081ff, 0xd9ba7bff, 0xd5b46dff, 0xd3af5fff, 0xd3b266ff, 0xd9bc7cff, 0xdbc089ff, 0xd9be86ff, 0xf3d499ff, 0xf4d59fff, 0xf3d79aff, 0xf1d393ff, 0xf1d193ff, 0xf3d599ff, 0xf0cd82ff, 0xebbd5eff, 0xedc163ff, 0xf1ce88ff, 0xf0cd89ff, 0xf6d9a3ff, 0xf1d190ff, 0xecc376ff, 0xe7b962ff, 0xe6b55cff, 0xecc06aff, 0xf3d187ff, 0xf4d690ff, 0xf2d183ff, 0xf3cf7eff, 0xf6d78fff, 0xf6d891ff, 0xf3d289ff, 0xf2cd7cff, 0xf4cf7cff, 0xf5d27fff, 0xf5d17fff, 0xf5cf7cff, 0xf3c970ff, 0xf3cb72ff, 0xf2cc75ff, 0xf1c86bff, 0xeec365ff, 0xeabb55ff, 0xe8b953ff, 0xe6b64fff, 0xe7b653ff, 0xe4b04cff, 0xe2b04eff, 0xe1b04dff, 0xe1ae4aff, 0xdeab3eff, 0xdba739ff, 0xdca942ff, 0xdea942ff, 0xdda740ff, 0xd79f2fff, 0xd3971fff, 0xdaa333ff, 0xd9a438ff, 0xd5a23dff, 0xcc9931ff, 0xbd8822ff, 0xa67112ff, 0x7d5811ff, 0x534c3cff, 0x808280ff, 0x8c8d89ff, 0xa7a9a5ff, 0xd9dad8ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdadbd9ff, 0x8a8b89ff, 0x403a2eff, 0x6d5a33ff, 0x997f4eff, 0xb69962ff, 0xccac71ff, 0xd5b26eff, 0xdbb770ff, 0xdcb86eff, 0xdeb970ff, 0xddb870ff, 0xdbb76eff, 0xd7ae5bff, 0xd8af58ff, 0xdfb96cff, 0xdfbb73ff, 0xe0bb73ff, 0xdeb96bff, 0xe0bb6eff, 0xdfb65cff, 0xe6be62ff, 0xefd48cff, 0xf2de9cff, 0xf0da94ff, 0xf4e1a4ff, 0xf7e7b7ff, 0xf1dda7ff, 0xecd08bff, 0xe8c882ff, 0xe5c481ff, 0xddbb74ff, 0xd7b163ff, 0xd6b060ff, 0xd3aa5aff, 0xc99940ff, 0xc49335ff, 0xc39133ff, 0xc18d29ff, 0xc29232ff, 0xc08e2cff, 0xba8518ff, 0xb77e0aff, 0xbc8a1fff, 0xd3ae5eff, 0xddbe83ff, 0xdcbe84ff, 0xd7b879ff, 0xd4b26bff, 0xd1ae60ff, 0xd4b268ff, 0xd9bc7dff, 0xd8ba7eff, 0xf3d49aff, 0xf4d9a4ff, 0xf2d69aff, 0xf4d69dff, 0xf1d092ff, 0xefcc83ff, 0xecc16bff, 0xefc777ff, 0xf1cc86ff, 0xf0ce8dff, 0xefce8dff, 0xf4d799ff, 0xf4d593ff, 0xf1cf8dff, 0xebc06aff, 0xeec062ff, 0xf4d282ff, 0xf6d78dff, 0xf8db97ff, 0xf7da92ff, 0xf5d580ff, 0xf8da8aff, 0xf9da89ff, 0xf9d784ff, 0xf9d780ff, 0xfad883ff, 0xfad886ff, 0xfad988ff, 0xfada8bff, 0xf9d888ff, 0xf9d67fff, 0xf9d883ff, 0xf7d581ff, 0xf4ce70ff, 0xf3cb6cff, 0xf1c868ff, 0xf1c667ff, 0xefc361ff, 0xeec060ff, 0xeec269ff, 0xedc267ff, 0xeabd5fff, 0xe4b44fff, 0xe2b14bff, 0xe4b14eff, 0xe2af48ff, 0xe1ad46ff, 0xdeaa42ff, 0xdda842ff, 0xdaa63cff, 0xc28615ff, 0xb27506ff, 0xaa6e06ff, 0x9d6402ff, 0x714d0aff, 0x575040ff, 0x838582ff, 0x8c8e89ff, 0xabada9ff, 0xdbdcdaff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdcdddbff, 0x90918fff, 0x423c30ff, 0x6b5834ff, 0x967b46ff, 0xb49557ff, 0xc8a560ff, 0xd2ad64ff, 0xd9b36bff, 0xdbb66dff, 0xd9b469ff, 0xdab566ff, 0xdcb76dff, 0xddba73ff, 0xdab261ff, 0xd9b15bff, 0xd9ad54ff, 0xdcb465ff, 0xdeb974ff, 0xddb873ff, 0xdfbb73ff, 0xe0b966ff, 0xe2b95fff, 0xeac771ff, 0xf3dc9cff, 0xf4e1a3ff, 0xf3e1a5ff, 0xf7e8bbff, 0xf3e2b3ff, 0xedd498ff, 0xeace8cff, 0xe6c784ff, 0xdcb86eff, 0xd6b05eff, 0xd4ac58ff, 0xd0a650ff, 0xca9e47ff, 0xc5963bff, 0xc18e2dff, 0xbf8b25ff, 0xc1902eff, 0xbe8c27ff, 0xb98313ff, 0xb77d08ff, 0xb98312ff, 0xcfa74fff, 0xdbbd7eff, 0xdbbe82ff, 0xd7b877ff, 0xd4b575ff, 0xd4b375ff, 0xceac64ff, 0xcca859ff, 0xcda453ff, 0xf3d79fff, 0xf3d8a3ff, 0xf6daa6ff, 0xf3d59aff, 0xefc97eff, 0xebba61ff, 0xebbd71ff, 0xefcc8eff, 0xf3d398ff, 0xf4d59aff, 0xf5d89aff, 0xf7dc9eff, 0xf5d99cff, 0xf1d08dff, 0xf2cb79ff, 0xf6d685ff, 0xf8db92ff, 0xf9de97ff, 0xf9dd93ff, 0xfadc8cff, 0xfada81ff, 0xfadd89ff, 0xfbdd88ff, 0xfbdb7cff, 0xfbdb83ff, 0xfbdd86ff, 0xfbdd8aff, 0xfbdd88ff, 0xfbde8cff, 0xfbe08fff, 0xfbdf8dff, 0xfbdc8aff, 0xfbda8aff, 0xf9d67eff, 0xf7d071ff, 0xf4cd68ff, 0xf6cf72ff, 0xf5cf77ff, 0xf3cc6fff, 0xf4cf78ff, 0xf3ce7cff, 0xf1ca77ff, 0xedc570ff, 0xedc46aff, 0xecbf62ff, 0xe8b858ff, 0xe2b14cff, 0xe0ae49ff, 0xe4b45aff, 0xe3b963ff, 0xc7922eff, 0xaf7406ff, 0xa16904ff, 0x6a4609ff, 0x524d43ff, 0x858784ff, 0x8c8e89ff, 0xafb0acff, 0xdcdddbff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdddedcff, 0x979896ff, 0x443e32ff, 0x665025ff, 0x92763dff, 0xb08f50ff, 0xc49f55ff, 0xcfa652ff, 0xd7af5eff, 0xdbb56eff, 0xddb871ff, 0xdcb770ff, 0xddba74ff, 0xddb971ff, 0xdfbe7cff, 0xdebc75ff, 0xdeba71ff, 0xd8ad53ff, 0xd7ad53ff, 0xddb76cff, 0xdbb467ff, 0xdab15cff, 0xdeb865ff, 0xe1b966ff, 0xe9c36dff, 0xeac47aff, 0xf5e0aaff, 0xf2dd9eff, 0xf4e2b0ff, 0xf2deb1ff, 0xecd497ff, 0xedd295ff, 0xe7c785ff, 0xddbb72ff, 0xd7b165ff, 0xd4ad5eff, 0xd2aa59ff, 0xcb9e47ff, 0xc08f2dff, 0xc08c29ff, 0xc18e2cff, 0xbe8d29ff, 0xbb8920ff, 0xbc891fff, 0xba8515ff, 0xb88310ff, 0xcda44bff, 0xdbbc7eff, 0xdcbf86ff, 0xd6b878ff, 0xd3b574ff, 0xd4b57aff, 0xcead69ff, 0xc9a14dff, 0xc6983aff, 0xf5ddaeff, 0xf6dbabff, 0xf6d9a1ff, 0xf1cf84ff, 0xebbb5dff, 0xeebf6eff, 0xefc880ff, 0xf0cf91ff, 0xf5d69fff, 0xf9e0adff, 0xf9dfa6ff, 0xf8dea6ff, 0xf5d698ff, 0xf6d48bff, 0xf9d67bff, 0xfbda85ff, 0xfbe39aff, 0xfce7a4ff, 0xfce49eff, 0xfbe191ff, 0xfce08aff, 0xfce08bff, 0xfcdf83ff, 0xfbde7dff, 0xfcdf88ff, 0xfce188ff, 0xfce18aff, 0xfce493ff, 0xfce596ff, 0xfce38dff, 0xfce28dff, 0xfcdf88ff, 0xfbdc81ff, 0xfbdc82ff, 0xfad87dff, 0xfad77aff, 0xfadb88ff, 0xf9da8aff, 0xf9d988ff, 0xf7d582ff, 0xf8d88dff, 0xf8d992ff, 0xf6d68aff, 0xf4d283ff, 0xf1cb74ff, 0xf0c76dff, 0xedc46eff, 0xebbf66ff, 0xeac16cff, 0xe7c479ff, 0xd3ad61ff, 0xa4700eff, 0x6a480aff, 0x555148ff, 0x878986ff, 0x8c8e89ff, 0xb2b4b0ff, 0xdededcff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e1ff, 0xdedfddff, 0x9c9d9cff, 0x433e34ff, 0x644f25ff, 0x8f7033ff, 0xaf8c4aff, 0xc59f58ff, 0xd0a95dff, 0xd3aa5bff, 0xd7ab5aff, 0xdab368ff, 0xddba75ff, 0xdcb66cff, 0xdbb56bff, 0xddb76fff, 0xdeba73ff, 0xdebc78ff, 0xdebb7aff, 0xd9b46eff, 0xd7af5eff, 0xd6ae5dff, 0xd2a74fff, 0xd8af57ff, 0xdbb159ff, 0xd9af55ff, 0xe1b963ff, 0xecca7cff, 0xf0d696ff, 0xf0d894ff, 0xf2e0a8ff, 0xf0dda9ff, 0xeed89aff, 0xeed69bff, 0xe6ca8aff, 0xdcba71ff, 0xd7b266ff, 0xd6af64ff, 0xcea450ff, 0xc8993fff, 0xbf8e2bff, 0xc08e30ff, 0xc39438ff, 0xc29233ff, 0xbd8b25ff, 0xbd8922ff, 0xbb871bff, 0xb8830fff, 0xcda54eff, 0xdbbd7eff, 0xdcc087ff, 0xd7b97aff, 0xd4b36dff, 0xd0ae67ff, 0xcca858ff, 0xcba54eff, 0xcfa852ff, 0xf5dca9ff, 0xf6dba6ff, 0xf4d693ff, 0xf1cc7eff, 0xf0c573ff, 0xf1c877ff, 0xf7d99bff, 0xf7dba5ff, 0xf4d49dff, 0xf4d49bff, 0xf6d99eff, 0xf5d59aff, 0xf4d18aff, 0xf8d476ff, 0xfad672ff, 0xfbdc82ff, 0xfce597ff, 0xfceaa1ff, 0xfceba1ff, 0xfceda0ff, 0xfcea9aff, 0xfce896ff, 0xfce58cff, 0xfce587ff, 0xfce689ff, 0xfce791ff, 0xfce895ff, 0xfce998ff, 0xfcea9dff, 0xfce895ff, 0xfce592ff, 0xfce38dff, 0xfbdf83ff, 0xfce289ff, 0xfce390ff, 0xfce18dff, 0xfce18fff, 0xfbe092ff, 0xfbdf90ff, 0xfbe192ff, 0xfbe297ff, 0xfbe39aff, 0xfae299ff, 0xfae096ff, 0xfae098ff, 0xf8dd94ff, 0xf4d487ff, 0xf3d48dff, 0xf0d696ff, 0xe6ce93ff, 0xd0b781ff, 0x735a29ff, 0x59564cff, 0x888a87ff, 0x8d8f8aff, 0xb6b7b4ff, 0xdfe0deff, 0xe2e2e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe0e0deff, 0xa2a4a2ff, 0x444037ff, 0x5f4b25ff, 0x8e6e31ff, 0xac863cff, 0xc19a4cff, 0xcca14bff, 0xd5ac5bff, 0xdab266ff, 0xdab269ff, 0xd7ae5eff, 0xd8af5eff, 0xd9b15dff, 0xdab160ff, 0xdbb569ff, 0xdcb465ff, 0xdab466ff, 0xdbb873ff, 0xdbb977ff, 0xdab872ff, 0xdab873ff, 0xd5ae61ff, 0xd8b05cff, 0xd7ad55ff, 0xd5a94cff, 0xd8ac56ff, 0xe3bc62ff, 0xefd48bff, 0xf2dc98ff, 0xf2dfa0ff, 0xf2dea5ff, 0xf0dba0ff, 0xefd9a0ff, 0xe6ca88ff, 0xddba6fff, 0xdab76bff, 0xd6b368ff, 0xcea550ff, 0xc79a40ff, 0xc39435ff, 0xbe8b2bff, 0xbe8b29ff, 0xbf8d2bff, 0xba871dff, 0xb88318ff, 0xb88518ff, 0xbb8718ff, 0xcca751ff, 0xdcbf82ff, 0xdbc28bff, 0xd8be85ff, 0xd4b570ff, 0xd1b06aff, 0xceac5dff, 0xceaa55ff, 0xd1ae5eff, 0xf5dba7ff, 0xf1cf83ff, 0xf3cf83ff, 0xf1cd89ff, 0xf3cf87ff, 0xf1ca7dff, 0xf3ce8bff, 0xf7dca7ff, 0xf4d59cff, 0xf5d699ff, 0xf4d798ff, 0xf2cf8aff, 0xf6d17cff, 0xf9d36dff, 0xf9d672ff, 0xfada7bff, 0xfce391ff, 0xfce89cff, 0xfdf0b0ff, 0xfdf1afff, 0xfcf2acff, 0xfcf0a6ff, 0xfcea9bff, 0xfce892ff, 0xfce78dff, 0xfce893ff, 0xfce891ff, 0xfce793ff, 0xfce997ff, 0xfce895ff, 0xfce693ff, 0xfce38eff, 0xfce28aff, 0xfce48eff, 0xfce595ff, 0xfce593ff, 0xfce190ff, 0xfce69cff, 0xfce8a2ff, 0xfce69bff, 0xfce69bff, 0xfce397ff, 0xfce69aff, 0xfce8a0ff, 0xfbe49cff, 0xfae29dff, 0xf8e19eff, 0xf5e5abff, 0xebdfafff, 0xd2c496ff, 0x82795dff, 0x62625cff, 0x898b87ff, 0x8e908bff, 0xb9bbb7ff, 0xdfe0deff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe0e1dfff, 0xa9aaa8ff, 0x47443cff, 0x5d4b28ff, 0x8f723bff, 0xaa863dff, 0xbe9440ff, 0xcda04bff, 0xd2a44bff, 0xd7ab56ff, 0xdab061ff, 0xdfbb78ff, 0xdeb974ff, 0xd9b167ff, 0xd7ac5bff, 0xd4a94fff, 0xd8ae58ff, 0xd7af58ff, 0xd6ac56ff, 0xd9b367ff, 0xdbb771ff, 0xdcb975ff, 0xdbb772ff, 0xd8b368ff, 0xd4ab56ff, 0xd3aa57ff, 0xd2a74dff, 0xd3a950ff, 0xd6ad56ff, 0xdfba64ff, 0xf0d78bff, 0xf4e2a4ff, 0xf4e3adff, 0xf0dca5ff, 0xedd69bff, 0xe5c883ff, 0xdcb86bff, 0xd8b466ff, 0xd2ad5bff, 0xd1aa59ff, 0xca9e47ff, 0xc5963aff, 0xc29131ff, 0xbe8a23ff, 0xbc8722ff, 0xb68318ff, 0xb57f12ff, 0xb78418ff, 0xbb881cff, 0xc89d3fff, 0xd9bb77ff, 0xdcbf87ff, 0xdbc089ff, 0xd9bd86ff, 0xd4b675ff, 0xd0af63ff, 0xd0ae5fff, 0xd1b168ff, 0xefce91ff, 0xefc87eff, 0xf4d18eff, 0xf1ce8aff, 0xf3d392ff, 0xf0cc87ff, 0xedbf71ff, 0xf6d69dff, 0xf7d8a0ff, 0xf5d595ff, 0xf2cf88ff, 0xf3cb79ff, 0xf5ce6bff, 0xf7d16cff, 0xf9d574ff, 0xf8d473ff, 0xf8d77eff, 0xfbe18aff, 0xfceeb1ff, 0xfcedaaff, 0xfceea6ff, 0xfce998ff, 0xfce992ff, 0xfcea99ff, 0xfce791ff, 0xfceb9dff, 0xfce893ff, 0xfce790ff, 0xfce795ff, 0xfce895ff, 0xfce592ff, 0xfce594ff, 0xfbe48eff, 0xfce48dff, 0xfce696ff, 0xfce89bff, 0xfce79dff, 0xfce69eff, 0xfce499ff, 0xfce69dff, 0xfce9a3ff, 0xfceaa0ff, 0xfceba5ff, 0xfceca9ff, 0xfce8a5ff, 0xf9e6a3ff, 0xf4e1a4ff, 0xe9daa2ff, 0xcec191ff, 0x766f54ff, 0x60615dff, 0x8a8c88ff, 0x8f918cff, 0xbdbebbff, 0xe0e1dfff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xaeb0aeff, 0x4b473fff, 0x594621ff, 0x8a6b2fff, 0xa9843dff, 0xbe9443ff, 0xca9c41ff, 0xd5a853ff, 0xdaaf60ff, 0xdaad5bff, 0xd7ab59ff, 0xd3a752ff, 0xd6aa57ff, 0xd9ad60ff, 0xd3a44eff, 0xd1a54bff, 0xd2a342ff, 0xd3a649ff, 0xd4a950ff, 0xd6ac53ff, 0xdbb361ff, 0xd9af60ff, 0xd5ab57ff, 0xd5ab55ff, 0xd3a958ff, 0xcea451ff, 0xcfa64fff, 0xcfa446ff, 0xd2a74dff, 0xd2a343ff, 0xe4c26eff, 0xf2dc9aff, 0xf4e2a8ff, 0xf1dda6ff, 0xead291ff, 0xe8cb88ff, 0xe0be72ff, 0xdab76bff, 0xd3ac5aff, 0xcfa652ff, 0xc99e45ff, 0xc79d46ff, 0xc89c45ff, 0xc39234ff, 0xb9851eff, 0xb68117ff, 0xb47f13ff, 0xb68216ff, 0xb68318ff, 0xc2922eff, 0xd5b46dff, 0xdabd7fff, 0xdbbd82ff, 0xd9bc83ff, 0xd5b877ff, 0xd1af61ff, 0xd2b26cff, 0xd1b36fff, 0xf1d298ff, 0xf1cf90ff, 0xf6d9a5ff, 0xf3d393ff, 0xf2cf89ff, 0xf0ca80ff, 0xedc170ff, 0xf0c87eff, 0xefc77eff, 0xf1cb84ff, 0xf4d294ff, 0xf0c87bff, 0xf1c662ff, 0xf0c762ff, 0xe9bc54ff, 0xe4b03fff, 0xe1ab37ff, 0xf6d372ff, 0xfce79cff, 0xfceaa1ff, 0xfceb9eff, 0xfce894ff, 0xfce997ff, 0xfce792ff, 0xfce895ff, 0xfce99aff, 0xfce794ff, 0xfce795ff, 0xfce693ff, 0xfce58aff, 0xfce68dff, 0xfce798ff, 0xfbe697ff, 0xfce48fff, 0xfce695ff, 0xfce595ff, 0xfce79cff, 0xfce69bff, 0xfce8a0ff, 0xfceba7ff, 0xfcedabff, 0xfceda9ff, 0xfceba9ff, 0xfbe9a3ff, 0xfae9abff, 0xf4e4a9ff, 0xe6d397ff, 0xcab781ff, 0x736c53ff, 0x646461ff, 0x8b8d88ff, 0x90928dff, 0xc0c2bfff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xb3b5b3ff, 0x4f4c43ff, 0x57421cff, 0x87672cff, 0xa47d32ff, 0xb98e38ff, 0xc89940ff, 0xcfa043ff, 0xd2a247ff, 0xd6a850ff, 0xd7ab57ff, 0xd7aa56ff, 0xd3a347ff, 0xd3a44bff, 0xd3a550ff, 0xd0a248ff, 0xd1a249ff, 0xcf9f43ff, 0xce9c3fff, 0xcf9e3dff, 0xd3a343ff, 0xd4a747ff, 0xd4a644ff, 0xd4a849ff, 0xd3a749ff, 0xd2a853ff, 0xd1a750ff, 0xcfa348ff, 0xcfa243ff, 0xcfa03fff, 0xcd9e3dff, 0xcc9e3fff, 0xd8b15bff, 0xeacd82ff, 0xf2dea5ff, 0xedd69bff, 0xedd497ff, 0xe3c47eff, 0xd8b466ff, 0xd5b064ff, 0xcea755ff, 0xcaa04bff, 0xc89f4cff, 0xc89e4cff, 0xc5983fff, 0xb68218ff, 0xb47e0dff, 0xb37d0dff, 0xb17d0eff, 0xb27d0fff, 0xb7851dff, 0xcfac5bff, 0xd9bc7cff, 0xd9bd83ff, 0xd9bc86ff, 0xd7ba7eff, 0xd1b169ff, 0xd5b679ff, 0xd2b372ff, 0xf4d7a1ff, 0xf0cc8eff, 0xf3d193ff, 0xf5d691ff, 0xf9da9aff, 0xf7d795ff, 0xf3d085ff, 0xf1c977ff, 0xf0c77bff, 0xf2ce8dff, 0xf2ce8aff, 0xefc57aff, 0xecbc67ff, 0xe8b554ff, 0xe1aa3bff, 0xdda128ff, 0xdb9c1bff, 0xe4af3aff, 0xf7da7fff, 0xfae697ff, 0xfce79aff, 0xfcea9fff, 0xfceba2ff, 0xfce693ff, 0xfce48cff, 0xfbe389ff, 0xfce590ff, 0xfce490ff, 0xfce48fff, 0xfce590ff, 0xfce695ff, 0xfce593ff, 0xfce698ff, 0xfbdf87ff, 0xfbe38fff, 0xfbe393ff, 0xfce597ff, 0xfce79aff, 0xfce9a4ff, 0xfceaa6ff, 0xfceaa8ff, 0xfbe79fff, 0xfae39bff, 0xf8e5a0ff, 0xf1db93ff, 0xe5cf8dff, 0xc8b77fff, 0x736b51ff, 0x686966ff, 0x8b8d89ff, 0x92948fff, 0xc4c5c2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xb9bab8ff, 0x504d47ff, 0x503c14ff, 0x825f20ff, 0x9e7526ff, 0xb5862dff, 0xc8983eff, 0xd1a34dff, 0xd4a44fff, 0xd19f41ff, 0xd2a145ff, 0xd2a144ff, 0xd1a142ff, 0xd09f41ff, 0xcf9d3fff, 0xcc9a3aff, 0xca9838ff, 0xcb9a3dff, 0xc89532ff, 0xcb9737ff, 0xcb9938ff, 0xcc9936ff, 0xcf9e3fff, 0xcd9c3cff, 0xca9932ff, 0xc9972eff, 0xcd9c39ff, 0xcd9f3fff, 0xce9e3dff, 0xcd9f3cff, 0xcc9e3cff, 0xcc9e42ff, 0xc89736ff, 0xc7932cff, 0xd3a546ff, 0xeacc80ff, 0xeed595ff, 0xedd59bff, 0xe2c57fff, 0xd6b364ff, 0xd5b165ff, 0xcea553ff, 0xc99f4aff, 0xc59b45ff, 0xc59942ff, 0xc19234ff, 0xb7831cff, 0xb47e11ff, 0xb47e0fff, 0xb58118ff, 0xb0790bff, 0xb47e14ff, 0xc9a046ff, 0xd8ba77ff, 0xdbc08cff, 0xdac192ff, 0xd8be8bff, 0xd3b576ff, 0xd4b678ff, 0xcfad63ff, 0xf5dba6ff, 0xf0cf8eff, 0xf2cf8aff, 0xf8db9eff, 0xf8dba2ff, 0xf8da9cff, 0xf4d389ff, 0xf2cc7dff, 0xf4d18eff, 0xf3d090ff, 0xf2c97eff, 0xedc274ff, 0xedbc69ff, 0xe8b558ff, 0xe2ad42ff, 0xdda32dff, 0xda9a19ff, 0xda9918ff, 0xf0ca6aff, 0xfce89fff, 0xfce8a2ff, 0xfce89cff, 0xfbe391ff, 0xfbe28cff, 0xfbe18cff, 0xfbe38fff, 0xfce799ff, 0xfbe18dff, 0xfbe28eff, 0xfbe28cff, 0xfbe493ff, 0xfbe697ff, 0xfbe18cff, 0xfbde87ff, 0xfbe294ff, 0xfbe49aff, 0xfbe59bff, 0xfae298ff, 0xfae295ff, 0xf9e297ff, 0xfae6a2ff, 0xf8e296ff, 0xf5de95ff, 0xefd996ff, 0xe3ce8aff, 0xbdaa6fff, 0x696149ff, 0x6d6e6aff, 0x8b8d89ff, 0x949691ff, 0xc7c8c5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xbebfbdff, 0x555552ff, 0x53452bff, 0x8a6f3dff, 0xa78546ff, 0xb88b38ff, 0xbc8722ff, 0xc68e2aff, 0xc99432ff, 0xcc9837ff, 0xd09c41ff, 0xcf9a3bff, 0xd0a042ff, 0xd3a54bff, 0xd1a347ff, 0xce9c3eff, 0xc99536ff, 0xc69130ff, 0xc89436ff, 0xc89536ff, 0xc79435ff, 0xc89637ff, 0xc89534ff, 0xca993aff, 0xc79531ff, 0xc48e23ff, 0xc28c1fff, 0xc18a20ff, 0xbd841cff, 0xbb821eff, 0xba7f22ff, 0xb0741dff, 0xad7d24ff, 0xb57f1dff, 0xaf781bff, 0xad771aff, 0xc99941ff, 0xeacc80ff, 0xebd293ff, 0xe3c782ff, 0xd9b76bff, 0xd3ad5eff, 0xcea652ff, 0xc9a24dff, 0xc49843ff, 0xc2953cff, 0xbd8b2eff, 0xb78423ff, 0xb4801dff, 0xb57f18ff, 0xb57f16ff, 0xb37d10ff, 0xb58013ff, 0xc59a40ff, 0xd5b267ff, 0xdbbe82ff, 0xdbbf8aff, 0xd8bb82ff, 0xd3b778ff, 0xd2b36fff, 0xcfac5fff, 0xf7dda9ff, 0xf5d99dff, 0xf5d99eff, 0xf5d89dff, 0xf4d697ff, 0xf4d389ff, 0xf1ca7bff, 0xf1ca7fff, 0xf2cd8bff, 0xf2ce8cff, 0xf1ca81ff, 0xeec370ff, 0xedbe67ff, 0xe7b353ff, 0xe2aa3cff, 0xdda32fff, 0xda9c1eff, 0xd99b20ff, 0xe9bc55ff, 0xfbe499ff, 0xfae49aff, 0xfae190ff, 0xf9dd84ff, 0xf9db81ff, 0xf9dd89ff, 0xfade88ff, 0xfadd87ff, 0xfad97bff, 0xfadb82ff, 0xfbdf8eff, 0xfbe08cff, 0xfbe08cff, 0xfbe192ff, 0xfbe297ff, 0xfadf91ff, 0xf9df91ff, 0xfadf8fff, 0xf9de91ff, 0xfae39bff, 0xf8e197ff, 0xf5da8aff, 0xf1d789ff, 0xead087ff, 0xdfc57dff, 0xb8a46cff, 0x615a45ff, 0x6d6e6cff, 0x8c8e89ff, 0x969893ff, 0xcacbc9ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc2c3c1ff, 0x5b5b58ff, 0x554d3dff, 0x908160ff, 0xb39e73ff, 0xc9ad79ff, 0xd6b378ff, 0xce9f4bff, 0xc58b27ff, 0xc1841aff, 0xc2881cff, 0xc58d29ff, 0xc88f2eff, 0xce9a3dff, 0xd09f45ff, 0xcf9d40ff, 0xd1a046ff, 0xd0a14eff, 0xc9943aff, 0xc48b29ff, 0xc89234ff, 0xc79233ff, 0xc58f2dff, 0xc58d2cff, 0xc0871fff, 0xba8014ff, 0xba7f14ff, 0xb4720cff, 0xad6907ff, 0xa45d07ff, 0x944304ff, 0x7b1d05ff, 0x4e0b03ff, 0x281204ff, 0x351d02ff, 0x291302ff, 0x461403ff, 0x803308ff, 0xbd9249ff, 0xe8cc84ff, 0xe8ce8dff, 0xddbc72ff, 0xd5b165ff, 0xcea95bff, 0xcba558ff, 0xc69c49ff, 0xc0923bff, 0xb8882dff, 0xb5852cff, 0xb6872fff, 0xb58223ff, 0xb17b12ff, 0xb37d12ff, 0xb6821cff, 0xba8727ff, 0xcba44eff, 0xd5b772ff, 0xd7ba7eff, 0xd5b779ff, 0xd3b578ff, 0xcfb16dff, 0xcfae65ff, 0xf8dfa2ff, 0xf9e1a7ff, 0xf6db9dff, 0xf7dc9dff, 0xf6da9aff, 0xf5d38fff, 0xf2ca7fff, 0xf3ce81ff, 0xf3ce88ff, 0xf2cb84ff, 0xf0c77eff, 0xeec171ff, 0xeab95eff, 0xeab95fff, 0xe3af48ff, 0xe0a736ff, 0xdea531ff, 0xda9f26ff, 0xebc15fff, 0xf8df91ff, 0xf9de8dff, 0xf9df8fff, 0xf8dc85ff, 0xf7d87eff, 0xf8d97fff, 0xf8dc87ff, 0xf6d475ff, 0xf6d169ff, 0xf8d87cff, 0xfadb86ff, 0xf9da81ff, 0xf8d983ff, 0xf8dc8dff, 0xf8db8bff, 0xf8db8aff, 0xf5d783ff, 0xf8dd91ff, 0xf8de94ff, 0xf7dd93ff, 0xf5d78dff, 0xeecf7cff, 0xe6c87bff, 0xdbbd73ff, 0xb39d5fff, 0x5f5944ff, 0x717370ff, 0x8c8e89ff, 0x989a95ff, 0xcdceccff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc6c7c5ff, 0x61615eff, 0x544d3eff, 0x8f8266ff, 0xb4a583ff, 0xcfbb91ff, 0xdbbd82ff, 0xddba76ff, 0xddb469ff, 0xd2a250ff, 0xc48726ff, 0xc28624ff, 0xc18622ff, 0xc28721ff, 0xc99333ff, 0xcc9a3dff, 0xcc9533ff, 0xcd9839ff, 0xcf9d47ff, 0xcb963eff, 0xc58b23ff, 0xc58d27ff, 0xc38d27ff, 0xc0881fff, 0xbd8218ff, 0xb6760bff, 0xb16b05ff, 0xae6606ff, 0xac6205ff, 0xaa6204ff, 0xa35904ff, 0x964504ff, 0x791e04ff, 0x5f1203ff, 0x3e0902ff, 0x240001ff, 0x430b02ff, 0x732b04ff, 0x833402ff, 0x995c24ff, 0xe3c57cff, 0xead293ff, 0xe1c280ff, 0xd6b56cff, 0xcda85aff, 0xc79e4cff, 0xc0933cff, 0xbb8f38ff, 0xb4852bff, 0xb6852dff, 0xb7872dff, 0xb27e1bff, 0xb07810ff, 0xb07810ff, 0xb07813ff, 0xac7511ff, 0xc19436ff, 0xd1b065ff, 0xd3b677ff, 0xd1b36fff, 0xcfb26dff, 0xcfb06aff, 0xcfaf68ff, 0xf8de9fff, 0xf9e0a7ff, 0xf8dfa5ff, 0xf8dda5ff, 0xf6d999ff, 0xf2d18eff, 0xf1cb82ff, 0xf1cc83ff, 0xf0c883ff, 0xf0c57dff, 0xefc477ff, 0xedc06dff, 0xecbd65ff, 0xe8b75eff, 0xe4b24fff, 0xe4b251ff, 0xdda635ff, 0xdda42dff, 0xefc86fff, 0xf6da89ff, 0xf6da85ff, 0xf6d987ff, 0xf6d985ff, 0xf5d67dff, 0xf4d373ff, 0xf4d16eff, 0xf2cb60ff, 0xf3cc62ff, 0xf5d173ff, 0xf5d171ff, 0xf5d377ff, 0xf5d47fff, 0xf6d47eff, 0xf5d684ff, 0xf5d786ff, 0xf4d583ff, 0xf4d78aff, 0xf6db90ff, 0xf3d586ff, 0xeed183ff, 0xe7cc85ff, 0xd9bf7cff, 0xb09b61ff, 0x615a46ff, 0x757774ff, 0x8c8e89ff, 0x9b9c98ff, 0xd0d1ceff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xcacbc9ff, 0x656563ff, 0x524b3dff, 0x8d7f62ff, 0xb2a27fff, 0xcdba91ff, 0xdfca9eff, 0xe7cd9cff, 0xe1bf7eff, 0xdeb567ff, 0xdeb569ff, 0xd2a152ff, 0xc68d38ff, 0xbf852bff, 0xbd801cff, 0xc38a26ff, 0xc89132ff, 0xc78f2cff, 0xc8902bff, 0xc99330ff, 0xc68d28ff, 0xbf8318ff, 0xbc8016ff, 0xbe851eff, 0xbc8014ff, 0xb77508ff, 0xb37004ff, 0xb16c05ff, 0xaf6905ff, 0xae6c05ff, 0xaf6e05ff, 0xac6a05ff, 0xa96205ff, 0x9e5704ff, 0x8f4204ff, 0x7d2b03ff, 0x7e2503ff, 0x8b3904ff, 0x904804ff, 0x8b4203ff, 0x874408ff, 0xc8a14eff, 0xe8ca82ff, 0xe6cb8fff, 0xd8b973ff, 0xcfad63ff, 0xc6a053ff, 0xbf933eff, 0xba8e38ff, 0xb5862cff, 0xb48127ff, 0xb27c1bff, 0xac730cff, 0xa66605ff, 0xa66905ff, 0xa86f08ff, 0xa8700aff, 0xb48323ff, 0xc6a050ff, 0xc8a760ff, 0xc8a458ff, 0xc8a65aff, 0xccac65ff, 0xccab66ff, 0xf6db9cff, 0xf9dda1ff, 0xf7dc9eff, 0xf3d495ff, 0xf2d08aff, 0xf0c97dff, 0xf0cb88ff, 0xefca8aff, 0xefc682ff, 0xeec275ff, 0xebbb67ff, 0xebbb65ff, 0xebbc69ff, 0xe8b55aff, 0xe3ae49ff, 0xe1ab40ff, 0xe0aa3bff, 0xe0ab3bff, 0xefc869ff, 0xf5d783ff, 0xf5d883ff, 0xf5d985ff, 0xf3d47aff, 0xf3d06fff, 0xf3d070ff, 0xf2ce72ff, 0xf0c860ff, 0xf0c85fff, 0xf1c968ff, 0xf0c65fff, 0xf1cb6cff, 0xf1cb72ff, 0xf2cc71ff, 0xf3d17bff, 0xf3d381ff, 0xf2d380ff, 0xf2d383ff, 0xf2d68cff, 0xefd38eff, 0xe4c67bff, 0xd5b771ff, 0xa28c57ff, 0x5a5444ff, 0x797b78ff, 0x8b8d89ff, 0x9e9f9bff, 0xd2d4d1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xcecfcdff, 0x6a6a68ff, 0x4b4538ff, 0x897c5fff, 0xaf9d77ff, 0xcbb689ff, 0xddc594ff, 0xe5cc9aff, 0xeace9bff, 0xe6c68cff, 0xdeb971ff, 0xdfbc78ff, 0xd9b06aff, 0xcd9b47ff, 0xcc9a4cff, 0xc18528ff, 0xbe8119ff, 0xc78f35ff, 0xc79033ff, 0xc48b23ff, 0xc28a22ff, 0xc1871dff, 0xbe821bff, 0xbe801bff, 0xb8790dff, 0xb37107ff, 0xb16c06ff, 0xb97b15ff, 0xc18a26ff, 0xba7e13ff, 0xb77c12ff, 0xb67b16ff, 0xb1730cff, 0xb0710cff, 0xa96a08ff, 0xa26004ff, 0x9d5a04ff, 0x985104ff, 0x954e04ff, 0x954f04ff, 0x965104ff, 0x934e03ff, 0xa26716ff, 0xdbb769ff, 0xe5c989ff, 0xdabb77ff, 0xd2b16cff, 0xcba964ff, 0xc39c4fff, 0xbb8e39ff, 0xb38023ff, 0xb07c1dff, 0xae761cff, 0xa76a0cff, 0xa26606ff, 0xa76e10ff, 0xa8700eff, 0xa97210ff, 0xaf7c20ff, 0xb5852aff, 0xba8c33ff, 0xb98930ff, 0xc29848ff, 0xcdac6bff, 0xceac6cff, 0xf7dda7ff, 0xf9dfa9ff, 0xf9e1aaff, 0xf8dba1ff, 0xf3d18dff, 0xf0c87cff, 0xeec780ff, 0xecc279ff, 0xedc47eff, 0xeabb6bff, 0xe9b660ff, 0xe9b964ff, 0xe8ba68ff, 0xe6b259ff, 0xe3ae47ff, 0xe0aa3eff, 0xdfaa3dff, 0xe1ab3fff, 0xf0ca6dff, 0xf4d989ff, 0xf5d98aff, 0xf4d681ff, 0xf2d177ff, 0xf2d076ff, 0xf2ce6fff, 0xf0c968ff, 0xeec560ff, 0xedc45bff, 0xeec562ff, 0xefc564ff, 0xeec566ff, 0xeec365ff, 0xedc363ff, 0xefc869ff, 0xf0cf7bff, 0xf1d07dff, 0xefcd7aff, 0xeac670ff, 0xe2bf6eff, 0xd3b162ff, 0x9d834aff, 0x565042ff, 0x7b7d7aff, 0x8b8d88ff, 0xa1a29eff, 0xd5d6d4ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd1d2d0ff, 0x70706eff, 0x484131ff, 0x847553ff, 0xad996fff, 0xc9b081ff, 0xdabd88ff, 0xe4c994ff, 0xe8cc94ff, 0xe7c88fff, 0xe2bf80ff, 0xdeb870ff, 0xdfbb78ff, 0xdcb470ff, 0xd4a856ff, 0xd7ad65ff, 0xd0a357ff, 0xc48d33ff, 0xc28829ff, 0xc48b2bff, 0xc58d2dff, 0xbf8520ff, 0xbc7e14ff, 0xb97b0fff, 0xb97b0fff, 0xb47106ff, 0xb4710cff, 0xc0882bff, 0xcea14dff, 0xd1a453ff, 0xc69237ff, 0xbf8a28ff, 0xbd8625ff, 0xba8123ff, 0xb67f20ff, 0xb27818ff, 0xad7110ff, 0xab6f10ff, 0xa56607ff, 0xa36306ff, 0xa26205ff, 0xa05d04ff, 0x9d5904ff, 0xa05e06ff, 0xcca14aff, 0xe2c177ff, 0xdcbd7cff, 0xd3b26fff, 0xc7a256ff, 0xc6a15aff, 0xbe9345ff, 0xb48127ff, 0xad7617ff, 0x9c5d0aff, 0x945004ff, 0x9a5b06ff, 0xa66e14ff, 0xa97315ff, 0xac781bff, 0xae7c22ff, 0xaf7e23ff, 0xb18028ff, 0xb07d23ff, 0xb6842aff, 0xc29646ff, 0xc6a056ff, 0xf8e1adff, 0xf8dfacff, 0xf6d9a1ff, 0xf7d99eff, 0xf2ce88ff, 0xf2cd89ff, 0xf1cf8eff, 0xeabc6cff, 0xebbd6fff, 0xe8b660ff, 0xe8b865ff, 0xe8b966ff, 0xe6b55fff, 0xe6b45eff, 0xe4af52ff, 0xe0aa42ff, 0xdea93dff, 0xe0ab3dff, 0xf0ca6dff, 0xf4d686ff, 0xf4d783ff, 0xf3d57eff, 0xf2d176ff, 0xf2cf74ff, 0xf0cc6aff, 0xf0c966ff, 0xf0ca71ff, 0xefc667ff, 0xeec463ff, 0xeec567ff, 0xe9bd55ff, 0xe8bb51ff, 0xeabd59ff, 0xecc15fff, 0xefc972ff, 0xedc872ff, 0xe9c473ff, 0xe1bd6cff, 0xd1b063ff, 0x99824fff, 0x585346ff, 0x7e807eff, 0x8b8d88ff, 0xa4a5a1ff, 0xd7d8d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd4d5d3ff, 0x777775ff, 0x484335ff, 0x827554ff, 0xa9966aff, 0xc5ad79ff, 0xd5b878ff, 0xdfbe7bff, 0xe4c589ff, 0xe5c78bff, 0xe3c483ff, 0xdfbb77ff, 0xe1bb77ff, 0xdebb77ff, 0xdcb76fff, 0xdcb56fff, 0xdfbb80ff, 0xddb87bff, 0xd4a964ff, 0xc1872bff, 0xbe8019ff, 0xbf841bff, 0xbd8018ff, 0xb8780aff, 0xb7780aff, 0xb87b0fff, 0xb47206ff, 0xb06c08ff, 0xcc9b43ff, 0xd8b060ff, 0xd5ad5fff, 0xd3a961ff, 0xca9c48ff, 0xc38e36ff, 0xbf8b31ff, 0xbf8c36ff, 0xbc8932ff, 0xb37a15ff, 0xb27917ff, 0xab6f09ff, 0xaa6d08ff, 0xa96e09ff, 0xa96a07ff, 0xa76806ff, 0xa96a05ff, 0xc6973cff, 0xe1c178ff, 0xdabb79ff, 0xd0ae66ff, 0xc8a35bff, 0xc5a159ff, 0xbe9245ff, 0xb37e28ff, 0xa3640aff, 0x975203ff, 0x874003ff, 0x925005ff, 0xa7690dff, 0xab7415ff, 0xac791bff, 0xac7c22ff, 0xb0812dff, 0xaf7c26ff, 0xb1802aff, 0xb07b1eff, 0xaf7615ff, 0xba852aff, 0xf7e1afff, 0xf8dfabff, 0xf8dba4ff, 0xf8dda3ff, 0xf6d79bff, 0xf6d69dff, 0xf2ce8fff, 0xeec47cff, 0xecc075ff, 0xe9b961ff, 0xe8b865ff, 0xe8b967ff, 0xe4b25cff, 0xe4b058ff, 0xe4b055ff, 0xe1ac4bff, 0xdea839ff, 0xe5b348ff, 0xf2ce70ff, 0xf3d685ff, 0xf4da8fff, 0xf3d789ff, 0xf1d382ff, 0xf1d07cff, 0xefcb74ff, 0xefca76ff, 0xefc76bff, 0xecc15cff, 0xebc05bff, 0xe8bd56ff, 0xe6ba4fff, 0xe7ba51ff, 0xe9bf58ff, 0xeac061ff, 0xeabf61ff, 0xe5bb5eff, 0xdeba70ff, 0xcfb067ff, 0x977f4bff, 0x5b574cff, 0x818380ff, 0x8b8d88ff, 0xa7a9a5ff, 0xd9dad8ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd6d7d5ff, 0x7d7e7bff, 0x484235ff, 0x7c6b4bff, 0xa8956bff, 0xc7b182ff, 0xd8bd86ff, 0xdec17fff, 0xdfc07bff, 0xe0bf7bff, 0xe1c07eff, 0xe2c281ff, 0xe1be7aff, 0xe1be7bff, 0xe1c17eff, 0xddba72ff, 0xddb976ff, 0xe0bd83ff, 0xdeb97cff, 0xd9b067ff, 0xcb9945ff, 0xbd7f16ff, 0xb9780dff, 0xb8770cff, 0xb77609ff, 0xb57206ff, 0xb67509ff, 0xb37005ff, 0xb16e07ff, 0xce9c41ff, 0xd8b05fff, 0xd7b060ff, 0xd5ad5eff, 0xd2a95aff, 0xcb9e4cff, 0xc99d4cff, 0xc29033ff, 0xc08e33ff, 0xba8526ff, 0xb47d17ff, 0xb17914ff, 0xaf7410ff, 0xaf7514ff, 0xad730bff, 0xac7107ff, 0xac6f07ff, 0xc59635ff, 0xe3c175ff, 0xdabb77ff, 0xd0ae68ff, 0xcfad6eff, 0xc8a25fff, 0xb88530ff, 0xa86809ff, 0xa8680bff, 0xae7217ff, 0xa5670aff, 0xac7012ff, 0xb47e27ff, 0xb88634ff, 0xb3822dff, 0xac7b25ff, 0xae7e2aff, 0xaf802cff, 0xb2832fff, 0xb0802cff, 0xb07f23ff, 0xb48126ff, 0xf7dfaaff, 0xf8dda4ff, 0xf7dda3ff, 0xf6d89dff, 0xf3d295ff, 0xf2cf93ff, 0xeec785ff, 0xecc17aff, 0xebbe73ff, 0xe9bb69ff, 0xe8b662ff, 0xe8b864ff, 0xe6b35cff, 0xe3ad52ff, 0xe2ac4eff, 0xe1ab4cff, 0xdea941ff, 0xe6b54aff, 0xf1cb6fff, 0xf4da90ff, 0xf4db92ff, 0xf3da90ff, 0xf2d58dff, 0xf0d28bff, 0xefcf85ff, 0xeecb79ff, 0xedc466ff, 0xeabf5eff, 0xe7bc59ff, 0xe7ba58ff, 0xe6ba54ff, 0xe8bc56ff, 0xebc166ff, 0xe9bf62ff, 0xe3bb5cff, 0xdbb661ff, 0xcdad69ff, 0x887548ff, 0x59564cff, 0x848682ff, 0x8b8d88ff, 0xaaaca8ff, 0xdbdcd9ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd9dad8ff, 0x828382ff, 0x453f31ff, 0x7b6c4dff, 0x9e8551ff, 0xbca165ff, 0xd6bd83ff, 0xddbf7eff, 0xdebd75ff, 0xe0c179ff, 0xe1c27dff, 0xe0c07aff, 0xe4c687ff, 0xe3c282ff, 0xdcb66cff, 0xdeb971ff, 0xdab264ff, 0xdbb46aff, 0xddbb7cff, 0xdbb675ff, 0xd8ae66ff, 0xce9d48ff, 0xc89030ff, 0xc0851fff, 0xb8770bff, 0xb67204ff, 0xb36f04ff, 0xb57307ff, 0xb26e04ff, 0xbd811dff, 0xd9b063ff, 0xdebb7cff, 0xdab671ff, 0xdab46eff, 0xd6af66ff, 0xd4ad69ff, 0xcda251ff, 0xc5963dff, 0xc39035ff, 0xc2913bff, 0xbc872bff, 0xb9852aff, 0xb57f20ff, 0xb27d1aff, 0xb07912ff, 0xae7409ff, 0xac6d04ff, 0xbe8b26ff, 0xdfbc6cff, 0xd6b46dff, 0xd3b26fff, 0xd2b072ff, 0xc3994dff, 0xb2791bff, 0xad6f09ff, 0xb37918ff, 0xb67e20ff, 0xb88125ff, 0xbc882fff, 0xbd892fff, 0xc39648ff, 0xc39a50ff, 0xbd9346ff, 0xb38633ff, 0xb0802bff, 0xaf7c25ff, 0xb2822eff, 0xb28530ff, 0xb0822eff, 0xf4dda7ff, 0xf4da9dff, 0xf2d695ff, 0xf3d597ff, 0xf0cc89ff, 0xe9bc6dff, 0xe8ba6aff, 0xeabb6aff, 0xe9bb6aff, 0xeabd6cff, 0xe6b55dff, 0xe6b65cff, 0xe6b55aff, 0xe3ae52ff, 0xe0aa49ff, 0xe0a947ff, 0xe0a945ff, 0xe5b448ff, 0xefcc72ff, 0xf3d891ff, 0xf2d98fff, 0xf1d487ff, 0xf1d387ff, 0xf1d184ff, 0xefce7bff, 0xefcc7bff, 0xedca7cff, 0xebc97eff, 0xebc67bff, 0xeac274ff, 0xeac062ff, 0xe8bc57ff, 0xe6ba5aff, 0xe3ba60ff, 0xdab35bff, 0xc9a85cff, 0x836e3eff, 0x57544bff, 0x858784ff, 0x8c8e89ff, 0xaeb0acff, 0xdcdddbff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdbdcdaff, 0x898a88ff, 0x423e33ff, 0x786a4cff, 0xa49067ff, 0xbfa56fff, 0xd2b477ff, 0xdcc082ff, 0xdebd76ff, 0xdcb766ff, 0xe0c075ff, 0xe3c787ff, 0xe2c481ff, 0xe1bf7aff, 0xdcb66aff, 0xdab161ff, 0xdcb971ff, 0xddba76ff, 0xdcb56cff, 0xdcb771ff, 0xdcb877ff, 0xd9b16cff, 0xcf9e4aff, 0xcc973eff, 0xc89231ff, 0xc48c2bff, 0xbe8020ff, 0xbd8124ff, 0xb47007ff, 0xb16c03ff, 0xc8943aff, 0xe0bc77ff, 0xddbb7fff, 0xdebd7fff, 0xe3c28dff, 0xdab676ff, 0xd5ad65ff, 0xd1a85cff, 0xcda254ff, 0xc79b49ff, 0xc5963fff, 0xc49642ff, 0xbf8e39ff, 0xba872cff, 0xb57f1cff, 0xb7801dff, 0xb67f1aff, 0xb27a10ff, 0xbe8922ff, 0xd4a745ff, 0xd8b46aff, 0xd7b776ff, 0xd0ab5fff, 0xc2923aff, 0xb88227ff, 0xb47c17ff, 0xb8821fff, 0xbd882aff, 0xc18e35ff, 0xc2923cff, 0xc3943fff, 0xc79c4fff, 0xc8a25cff, 0xc8a35fff, 0xc2984eff, 0xb4822dff, 0xb38532ff, 0xb88f41ff, 0xb98f44ff, 0xb88e48ff, 0xf4deadff, 0xf5dca6ff, 0xf2d593ff, 0xf1d38eff, 0xebc373ff, 0xe5b459ff, 0xe4af50ff, 0xe4b255ff, 0xebc176ff, 0xe8bc71ff, 0xe5b159ff, 0xe5b153ff, 0xe3af51ff, 0xe2ac53ff, 0xe1ac4fff, 0xe1ac4eff, 0xdea741ff, 0xe5b653ff, 0xf0ce79ff, 0xf2d791ff, 0xf3da98ff, 0xf2d794ff, 0xf0d288ff, 0xf0d083ff, 0xefd188ff, 0xf0d491ff, 0xeed08aff, 0xedce86ff, 0xedc97cff, 0xecc570ff, 0xecc46cff, 0xe7bc5fff, 0xe1b65aff, 0xd8b25dff, 0xc7a559ff, 0x816d40ff, 0x5b5952ff, 0x878986ff, 0x8c8e89ff, 0xb2b3b0ff, 0xdddedcff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdcdddbff, 0x90918fff, 0x433e31ff, 0x71613bff, 0xa08b5dff, 0xbaa066ff, 0xd0b473ff, 0xdec080ff, 0xe1c17dff, 0xe4c583ff, 0xe3c47cff, 0xe2c27bff, 0xdebb71ff, 0xe0bd75ff, 0xdfbc73ff, 0xdbb460ff, 0xdcb362ff, 0xdebb73ff, 0xdfbc79ff, 0xe0bd77ff, 0xdcb56dff, 0xdcb672ff, 0xd9b06aff, 0xd1a251ff, 0xcf9f48ff, 0xce9d43ff, 0xce9d48ff, 0xc99440ff, 0xbd8229ff, 0xb16a07ff, 0xac6703ff, 0xc59034ff, 0xe0bd73ff, 0xe1c184ff, 0xe2c48aff, 0xe3c28bff, 0xddba7cff, 0xd7af64ff, 0xd4ae68ff, 0xcfa75cff, 0xcb9f4cff, 0xca9e4cff, 0xc89c4aff, 0xc1913bff, 0xbf8e36ff, 0xba8829ff, 0xba8628ff, 0xb98320ff, 0xb8821fff, 0xba8523ff, 0xc28d24ff, 0xcfa54eff, 0xcea65dff, 0xcaa155ff, 0xc79948ff, 0xc79748ff, 0xbe872bff, 0xbe8728ff, 0xc4943cff, 0xc79946ff, 0xc99a4aff, 0xc99c4cff, 0xc79744ff, 0xc79b4dff, 0xc49648ff, 0xc2964fff, 0xb9893dff, 0xb7893bff, 0xbe9753ff, 0xbd9956ff, 0xbc9955ff, 0xf3d9a2ff, 0xf5d99dff, 0xf3d798ff, 0xecc67aff, 0xe9bd65ff, 0xebc06aff, 0xecc06dff, 0xebbe68ff, 0xeec273ff, 0xebc074ff, 0xe7b763ff, 0xe7b763ff, 0xe4b35fff, 0xe4b262ff, 0xe4b363ff, 0xe2af5aff, 0xdea946ff, 0xe0ab41ff, 0xeec76bff, 0xf2d795ff, 0xf4d89aff, 0xf4d896ff, 0xefd388ff, 0xefd186ff, 0xf0d38dff, 0xf0d493ff, 0xefd392ff, 0xedcf89ff, 0xedce88ff, 0xebca7fff, 0xeac679ff, 0xe5c378ff, 0xdab76cff, 0xc5a45fff, 0x7e6b41ff, 0x5f5e56ff, 0x898b87ff, 0x8d8f8aff, 0xb5b7b3ff, 0xdedfddff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdedfddff, 0x969796ff, 0x454034ff, 0x6b5a34ff, 0x987f4aff, 0xb39553ff, 0xc6a356ff, 0xd6b567ff, 0xdbb86aff, 0xdcb765ff, 0xddba6bff, 0xe0c077ff, 0xe2c178ff, 0xdeb968ff, 0xdcb869ff, 0xdebb6fff, 0xddb967ff, 0xdbb35eff, 0xd9b15eff, 0xdeb86fff, 0xddb66dff, 0xddb56bff, 0xdab166ff, 0xd5a957ff, 0xd1a14bff, 0xd3a551ff, 0xd4a756ff, 0xd2a552ff, 0xc99339ff, 0xb37008ff, 0xad6404ff, 0xac6403ff, 0xc89539ff, 0xe3c176ff, 0xe7c889ff, 0xe6c88bff, 0xe5c68dff, 0xdebd80ff, 0xd9b873ff, 0xd4ae66ff, 0xd3ac64ff, 0xcfa65bff, 0xcb9f4eff, 0xc69843ff, 0xc2913aff, 0xc2943eff, 0xc09238ff, 0xc1943fff, 0xbf8f39ff, 0xbd892aff, 0xb98520ff, 0xba8623ff, 0xbc8823ff, 0xc08d32ff, 0xc49744ff, 0xcb9e4eff, 0xd0a65aff, 0xcda255ff, 0xc99845ff, 0xcda252ff, 0xcd9f4cff, 0xc79335ff, 0xc8963cff, 0xc7953dff, 0xc79945ff, 0xc18f3eff, 0xbe8f46ff, 0xc29857ff, 0xc29a59ff, 0xbd9857ff, 0xbb9755ff, 0xbb9855ff, 0xf4dca8ff, 0xf4dba0ff, 0xf3d798ff, 0xecc77dff, 0xedc673ff, 0xefc878ff, 0xf1cc81ff, 0xf1cd85ff, 0xf0cb83ff, 0xedc475ff, 0xeab962ff, 0xe8b762ff, 0xe7b767ff, 0xe6b667ff, 0xe3b05aff, 0xdfa94aff, 0xdca53eff, 0xdca337ff, 0xeabd5eff, 0xf1d28bff, 0xf1d38eff, 0xf0d28aff, 0xeecf87ff, 0xefd18eff, 0xf1d294ff, 0xf0d394ff, 0xf0d395ff, 0xeed092ff, 0xeccd8aff, 0xeac980ff, 0xe4c277ff, 0xdcbe7eff, 0xc4ab75ff, 0x716346ff, 0x5f5f5aff, 0x8a8c88ff, 0x8d8f8bff, 0xb9bab7ff, 0xdfe0deff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xdfe0deff, 0x9c9d9bff, 0x474237ff, 0x6a5a35ff, 0x967d48ff, 0xb39454ff, 0xc7a355ff, 0xd3af5cff, 0xdbb765ff, 0xdfbb6cff, 0xe1bf74ff, 0xdeb96dff, 0xe5c683ff, 0xe3c27aff, 0xdfbb6eff, 0xe1c077ff, 0xe1be73ff, 0xddb767ff, 0xdbb25cff, 0xd9af59ff, 0xd7ac58ff, 0xd6aa56ff, 0xd8ac59ff, 0xdbb263ff, 0xd5a850ff, 0xd0a044ff, 0xd6a754ff, 0xd9af66ff, 0xd2a553ff, 0xc99234ff, 0xbb7c13ff, 0xad6503ff, 0xb77817ff, 0xdcb666ff, 0xe2c077ff, 0xe9cb8aff, 0xebcf99ff, 0xe6c991ff, 0xe1c283ff, 0xdcbc7aff, 0xd6af64ff, 0xd5ae65ff, 0xd4ad6aff, 0xcea455ff, 0xcca04eff, 0xc99f4dff, 0xc59a44ff, 0xc49743ff, 0xc69a47ff, 0xc69b49ff, 0xc3943fff, 0xc19138ff, 0xc19037ff, 0xc09035ff, 0xc69a47ff, 0xcb9f4fff, 0xcfa453ff, 0xd3ac61ff, 0xd8b476ff, 0xdab77aff, 0xdbb97cff, 0xdcbb79ff, 0xd7b36cff, 0xd2ab60ff, 0xcda352ff, 0xc99b49ff, 0xcba158ff, 0xc2944eff, 0xbf934cff, 0xc9a568ff, 0xbe9955ff, 0xbe9c5bff, 0xbe9e5cff, 0xf0d7a6ff, 0xf3dca7ff, 0xf3d79cff, 0xf1d08eff, 0xf1d290ff, 0xf0cc82ff, 0xf0ce85ff, 0xefcb82ff, 0xeec779ff, 0xebc06aff, 0xe8b758ff, 0xe6b254ff, 0xe3ae52ff, 0xe1ac50ff, 0xe2ac4dff, 0xdfa944ff, 0xdca439ff, 0xdea53bff, 0xe8b95cff, 0xefcc7dff, 0xefd18aff, 0xefd388ff, 0xefd68aff, 0xf1d691ff, 0xf2d699ff, 0xf2d49aff, 0xf0d294ff, 0xedcd8aff, 0xe8c57cff, 0xe3c279ff, 0xd8b976ff, 0xbfa268ff, 0x6e6246ff, 0x62625fff, 0x8b8d88ff, 0x8f908cff, 0xbcbebbff, 0xe0e1dfff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe0e1dfff, 0xa2a3a2ff, 0x44423aff, 0x655532ff, 0x937a43ff, 0xb49757ff, 0xc9a761ff, 0xd7b467ff, 0xdbb866ff, 0xdfba68ff, 0xdfbb6eff, 0xe0be74ff, 0xe0bf78ff, 0xdebb6fff, 0xdcb769ff, 0xe3c380ff, 0xe4c582ff, 0xe0bd72ff, 0xe0bd72ff, 0xdcb464ff, 0xdbb15eff, 0xdaad59ff, 0xd6aa53ff, 0xd9ad58ff, 0xdbb164ff, 0xd9ad5cff, 0xd6a752ff, 0xd7aa59ff, 0xd6aa5aff, 0xd1a14fff, 0xcb9538ff, 0xc0851cff, 0xb6750cff, 0xcc9b41ff, 0xe4c47eff, 0xe2bf71ff, 0xe5c377ff, 0xe8cc8fff, 0xe6c78cff, 0xe3c587ff, 0xdfbe7eff, 0xdab772ff, 0xd7b26eff, 0xd4ad69ff, 0xd0a654ff, 0xcea450ff, 0xcfaa61ff, 0xcca458ff, 0xcaa157ff, 0xc99f4eff, 0xca9f4eff, 0xc69944ff, 0xc69d4bff, 0xcca55cff, 0xcca459ff, 0xcca65cff, 0xc0944fff, 0xd2ad67ff, 0xd9b775ff, 0xdebd82ff, 0xe1c287ff, 0xe0c083ff, 0xe1c386ff, 0xe4c792ff, 0xe1c48fff, 0xd8b575ff, 0xd0a85eff, 0xcdaa66ff, 0xc2974bff, 0xbd9141ff, 0xbf994fff, 0xbc964cff, 0xc0a15fff, 0xbf9c5bff, 0xf0daacff, 0xf4e0afff, 0xf4ddaaff, 0xf4d89eff, 0xf4d79bff, 0xf2d594ff, 0xefce86ff, 0xeeca7dff, 0xecc574ff, 0xe9be5fff, 0xe8ba59ff, 0xe7b558ff, 0xe4af4fff, 0xe6b055ff, 0xe3af4fff, 0xe3af4fff, 0xe5b257ff, 0xe5b155ff, 0xe4b055ff, 0xeabd65ff, 0xeecc7fff, 0xefd084ff, 0xefd185ff, 0xf0d38eff, 0xf0d391ff, 0xefcf8aff, 0xecca82ff, 0xe9c885ff, 0xe3c27eff, 0xd9b874ff, 0xbea46fff, 0x6d6045ff, 0x666663ff, 0x8b8d89ff, 0x90928dff, 0xc0c1beff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xa8aaa8ff, 0x47453fff, 0x625436ff, 0x957e4cff, 0xb0914dff, 0xc9a95fff, 0xd7b56aff, 0xddbb6dff, 0xddbb6bff, 0xdeb966ff, 0xdeba6aff, 0xe0bd73ff, 0xe1c17aff, 0xe0c079ff, 0xdcb86bff, 0xddb96dff, 0xdfbb6dff, 0xddb765ff, 0xdeb766ff, 0xdbb05dff, 0xdbb05bff, 0xdcb25eff, 0xdcb360ff, 0xd7a950ff, 0xd6a74eff, 0xd6a750ff, 0xd4a44aff, 0xd2a044ff, 0xd1a047ff, 0xcf9c42ff, 0xc9932fff, 0xc18617ff, 0xc48c24ff, 0xddb460ff, 0xe5c279ff, 0xe5c275ff, 0xe3c072ff, 0xe5c680ff, 0xe5c787ff, 0xe4c688ff, 0xdebd78ff, 0xdcb974ff, 0xd9b371ff, 0xd6b06bff, 0xd5af64ff, 0xd0a551ff, 0xcda554ff, 0xcfa65aff, 0xcea961ff, 0xcaa052ff, 0xcba04eff, 0xcca354ff, 0xcda761ff, 0xd6b377ff, 0xd4af6dff, 0xd3ad66ff, 0xd2ae6eff, 0xdab97dff, 0xdfc287ff, 0xdebf7eff, 0xe2c487ff, 0xe6c78eff, 0xe5c88fff, 0xe5cc99ff, 0xe1c591ff, 0xdfc28eff, 0xddc18cff, 0xd7b77eff, 0xd1b173ff, 0xc7a35eff, 0xbd9549ff, 0xbc964bff, 0xc3a263ff, 0xc09f5dff, 0xf2ddb1ff, 0xf2ddadff, 0xf2d89eff, 0xf2d699ff, 0xf0d493ff, 0xf0d495ff, 0xefcd88ff, 0xeec674ff, 0xedc26dff, 0xecc26aff, 0xecc167ff, 0xe9bb62ff, 0xe9b75dff, 0xebbc6bff, 0xe8ba66ff, 0xe9b865ff, 0xe7b665ff, 0xe5b25bff, 0xe3ac4fff, 0xe2ab4bff, 0xebc371ff, 0xeecb7eff, 0xefcf88ff, 0xf0d391ff, 0xefd28eff, 0xedcd86ff, 0xe9cb86ff, 0xe2c07dff, 0xd6b167ff, 0xb99d64ff, 0x6d634eff, 0x6b6b68ff, 0x8c8d89ff, 0x91938fff, 0xc3c5c2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xaeafaeff, 0x4b4943ff, 0x615437ff, 0x927c4cff, 0xb19659ff, 0xc7a75fff, 0xd8b568ff, 0xdcb967ff, 0xe0be6eff, 0xdfbc6cff, 0xddb765ff, 0xdfba69ff, 0xe1be74ff, 0xe0c177ff, 0xe2c37cff, 0xe0be72ff, 0xdbb763ff, 0xdeb768ff, 0xddb768ff, 0xdfba6fff, 0xdeb669ff, 0xdeb668ff, 0xdcb15eff, 0xdaad57ff, 0xd4a445ff, 0xd4a445ff, 0xd5a448ff, 0xd1a041ff, 0xcd9a36ff, 0xcb952fff, 0xc8932bff, 0xc78f26ff, 0xc2881cff, 0xd2a241ff, 0xe2ba66ff, 0xe6c47aff, 0xe5c57fff, 0xe6c57cff, 0xe6c783ff, 0xe4c483ff, 0xe1c17cff, 0xe3c482ff, 0xe1c184ff, 0xdcb974ff, 0xd9b36dff, 0xd7b16aff, 0xd6af68ff, 0xd3ab5fff, 0xd1a95cff, 0xd2ac65ff, 0xd1ac67ff, 0xd0a960ff, 0xd1aa65ff, 0xd6b275ff, 0xdbba81ff, 0xd5b06eff, 0xd5ae66ff, 0xdebb80ff, 0xe2c58fff, 0xe4c78fff, 0xe0c182ff, 0xe5c78eff, 0xe5c890ff, 0xe4c790ff, 0xe0c189ff, 0xe2c792ff, 0xe1ca9aff, 0xdfc799ff, 0xdcc293ff, 0xdac091ff, 0xd4b784ff, 0xc4a25eff, 0xbe9c57ff, 0xc1a05cff, 0xc3a461ff, 0xf1dcadff, 0xf2dcaaff, 0xf1d998ff, 0xf1d693ff, 0xf0d495ff, 0xf0d192ff, 0xeece89ff, 0xefce86ff, 0xeec87cff, 0xefcd86ff, 0xedc77aff, 0xecc373ff, 0xedc16eff, 0xeec37cff, 0xebbf74ff, 0xe8b764ff, 0xe7b461ff, 0xe6b463ff, 0xe5b15eff, 0xe1a949ff, 0xe2b154ff, 0xedca80ff, 0xf0d293ff, 0xeecf8cff, 0xedce8aff, 0xebcd8cff, 0xe3c481ff, 0xd5b46cff, 0xb39860ff, 0x5f5642ff, 0x6d6e6cff, 0x8c8e89ff, 0x939591ff, 0xc7c8c5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xb4b5b3ff, 0x4f4e47ff, 0x5d4f32ff, 0x917b4aff, 0xae9253ff, 0xc5a55cff, 0xd2b060ff, 0xddb96cff, 0xddbb6eff, 0xdcb765ff, 0xdeb967ff, 0xddb866ff, 0xd8af57ff, 0xd9b059ff, 0xdbb45fff, 0xdab35cff, 0xddb765ff, 0xdeb868ff, 0xdfb96aff, 0xdeb666ff, 0xe1be76ff, 0xdfb86bff, 0xdbb25eff, 0xd9ad54ff, 0xd5a647ff, 0xd4a242ff, 0xd19f3eff, 0xd09d38ff, 0xce9a35ff, 0xcc9832ff, 0xc58c1cff, 0xc48b18ff, 0xc28618ff, 0xcb9530ff, 0xe2ba65ff, 0xe4c179ff, 0xe4c378ff, 0xe6c781ff, 0xe6c784ff, 0xe8ca8aff, 0xe7c98bff, 0xe5c585ff, 0xe7c88dff, 0xe4c488ff, 0xdfbe7dff, 0xddbb79ff, 0xd9b670ff, 0xd9b46cff, 0xd8b370ff, 0xd4af64ff, 0xd9b778ff, 0xd8b370ff, 0xd6af67ff, 0xd4af67ff, 0xd9b576ff, 0xddbc81ff, 0xd9b674ff, 0xd9b66fff, 0xdfbd7fff, 0xe1c184ff, 0xe6c892ff, 0xe4c589ff, 0xe2c283ff, 0xe3c48bff, 0xe1c48dff, 0xe1c895ff, 0xe6d1a3ff, 0xe4cea0ff, 0xddc390ff, 0xddc494ff, 0xddc498ff, 0xd9c090ff, 0xcaaa6aff, 0xc4a466ff, 0xc7a96dff, 0xc7a968ff, 0xefd8a6ff, 0xf1d9a1ff, 0xefd48fff, 0xf0d591ff, 0xefd290ff, 0xedcc89ff, 0xedcd89ff, 0xf0d191ff, 0xeecc89ff, 0xe5be73ff, 0xe1b460ff, 0xe4b662ff, 0xedc57cff, 0xebc582ff, 0xe8bd72ff, 0xe7b45dff, 0xe8b663ff, 0xe9bb70ff, 0xe5b15fff, 0xe2ad54ff, 0xdda743ff, 0xe8bf73ff, 0xf0d398ff, 0xf0d29aff, 0xedcf94ff, 0xe3c386ff, 0xd5b16cff, 0xae9157ff, 0x5e5644ff, 0x70716fff, 0x8c8e89ff, 0x959793ff, 0xcacbc8ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xb9bab8ff, 0x52504bff, 0x5b5034ff, 0x907c4eff, 0xae9559ff, 0xc6aa69ff, 0xd4b36aff, 0xd7b160ff, 0xdab665ff, 0xddb96dff, 0xdab360ff, 0xddb86aff, 0xd9b15dff, 0xcea043ff, 0xcfa245ff, 0xd6ac59ff, 0xd9b160ff, 0xdcb665ff, 0xddb665ff, 0xddb565ff, 0xdfba6bff, 0xe4c37dff, 0xddb767ff, 0xd8ac51ff, 0xd8a94bff, 0xd7a74aff, 0xd9a950ff, 0xd4a448ff, 0xd19f3aff, 0xd09e37ff, 0xcc972aff, 0xc58c1bff, 0xc48b1dff, 0xc48b1fff, 0xd8aa4aff, 0xe5bf6bff, 0xe3c074ff, 0xe3bf70ff, 0xe8c986ff, 0xeace90ff, 0xeacc92ff, 0xe9ca90ff, 0xe5c786ff, 0xe4c585ff, 0xe4c281ff, 0xe1c07fff, 0xe0c07eff, 0xdcb871ff, 0xdcb771ff, 0xdbb871ff, 0xdab468ff, 0xdbb56cff, 0xdbb66fff, 0xdab46eff, 0xdbb97bff, 0xdcbc7dff, 0xe1c286ff, 0xe2c28bff, 0xe0c081ff, 0xe2c386ff, 0xe3c48bff, 0xe4c58dff, 0xe5c792ff, 0xe1c186ff, 0xddbd7fff, 0xe2c692ff, 0xe2cb9bff, 0xe3cc9fff, 0xe3cfa5ff, 0xdfc89aff, 0xddc292ff, 0xdec59aff, 0xdcc497ff, 0xd3b57bff, 0xc9a668ff, 0xccac74ff, 0xccac6fff, 0xf0daa9ff, 0xf1dba4ff, 0xefd395ff, 0xf0d497ff, 0xf0d293ff, 0xeecf8cff, 0xefd292ff, 0xe5c37eff, 0xd3a457ff, 0xd3a253ff, 0xdbb062ff, 0xe0b66bff, 0xe6be77ff, 0xebc680ff, 0xeac074ff, 0xe5b663ff, 0xd89f4aff, 0xdfa855ff, 0xe9ba6aff, 0xe5b45fff, 0xdfa843ff, 0xdda747ff, 0xe5bc72ff, 0xe8c689ff, 0xe4c38dff, 0xd4b277ff, 0xaa8b51ff, 0x5d533eff, 0x747573ff, 0x8c8e89ff, 0x989a95ff, 0xcdcecbff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xbebfbdff, 0x54534fff, 0x54492fff, 0x8d7a4dff, 0xad945aff, 0xc5a765ff, 0xd5b672ff, 0xdbb86dff, 0xdbb669ff, 0xdab567ff, 0xdcb565ff, 0xd4aa4eff, 0xd4a951ff, 0xd0a34aff, 0xcd9f46ff, 0xd2a751ff, 0xd9b05fff, 0xdbb466ff, 0xdab466ff, 0xdcb666ff, 0xdbb25fff, 0xdeb767ff, 0xe0b96cff, 0xddb361ff, 0xdcb15fff, 0xdaad56ff, 0xdbad5aff, 0xdcae59ff, 0xd9a94cff, 0xd8a547ff, 0xd2a13bff, 0xce9a2dff, 0xc99223ff, 0xcc9833ff, 0xe1b864ff, 0xe5be6aff, 0xe3bb64ff, 0xe1b75aff, 0xe4c16eff, 0xeacd8bff, 0xeacc8dff, 0xecce96ff, 0xe9cc91ff, 0xe5c585ff, 0xe8ca8bff, 0xe4c383ff, 0xe1bf78ff, 0xe1c17dff, 0xe0bd77ff, 0xdebb77ff, 0xe1bd7aff, 0xe4c284ff, 0xe0bf7dff, 0xddb870ff, 0xddba72ff, 0xe1c080ff, 0xe2c284ff, 0xe4c587ff, 0xe3c484ff, 0xe1c17dff, 0xe4c588ff, 0xe0bf7dff, 0xe0c07fff, 0xe1c181ff, 0xe0c082ff, 0xe1c58bff, 0xe5cc9dff, 0xe5d0a4ff, 0xe2cb9eff, 0xe2cca4ff, 0xdfc797ff, 0xdec390ff, 0xdcc390ff, 0xdcc291ff, 0xdabe8aff, 0xccad70ff, 0xc9a96cff, 0xcfb27aff, 0xf3e0b3ff, 0xf3deadff, 0xf1daa9ff, 0xeccf96ff, 0xdfb672ff, 0xd09f5aff, 0xcf9f62ff, 0xb57330ff, 0x9f4f0fff, 0xbe833eff, 0xddb272ff, 0xe8c28aff, 0xe9c689ff, 0xe7c283ff, 0xe2b870ff, 0xd7a85eff, 0xaf6411ff, 0xbc7622ff, 0xcb8d3cff, 0xd29438ff, 0xd0922dff, 0xce8f24ff, 0xcd9332ff, 0xce9b45ff, 0xc9a05cff, 0x9f8047ff, 0x5d5442ff, 0x787977ff, 0x8b8d89ff, 0x9a9c98ff, 0xcfd1ceff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc3c4c2ff, 0x5a5956ff, 0x534932ff, 0x8d7b4fff, 0xb09b68ff, 0xc5a968ff, 0xd1af62ff, 0xd9b565ff, 0xd9b365ff, 0xdcb86cff, 0xd7ae5aff, 0xd8b05bff, 0xd6ac56ff, 0xd5ab56ff, 0xd7ae60ff, 0xd4aa5bff, 0xd6ad5dff, 0xd7ae5cff, 0xd7ad5aff, 0xdbb260ff, 0xdbb15dff, 0xd8ad55ff, 0xdbb05cff, 0xdbad5aff, 0xdbb05cff, 0xddb05eff, 0xdfb260ff, 0xdfb462ff, 0xddb05cff, 0xdcac56ff, 0xd8a64aff, 0xd7a644ff, 0xd19f35ff, 0xcc9528ff, 0xd6a748ff, 0xe9c375ff, 0xe8bf6aff, 0xe3bd66ff, 0xe5bf68ff, 0xeac97dff, 0xeed395ff, 0xebcf91ff, 0xeccf91ff, 0xecd39fff, 0xe7c88cff, 0xe8c98bff, 0xe7c98dff, 0xe9c98bff, 0xe4c381ff, 0xe5c684ff, 0xe4c382ff, 0xe4c281ff, 0xe6c686ff, 0xe6c583ff, 0xe4c27dff, 0xe4c27cff, 0xe7c789ff, 0xe7c78aff, 0xe8c78aff, 0xe9cb8fff, 0xe7c885ff, 0xe6c786ff, 0xe3c481ff, 0xe0c17dff, 0xdfbf78ff, 0xe0c07dff, 0xe4ca91ff, 0xe6cd9eff, 0xe3cc9dff, 0xe1c691ff, 0xdec58fff, 0xdec38bff, 0xdabd80ff, 0xd7ba77ff, 0xdcc089ff, 0xdbbe8bff, 0xcdac6fff, 0xc9a969ff, 0xccad71ff, 0xe0bd7fff, 0xeacc96ff, 0xe3c084ff, 0xc8933bff, 0xb16a0eff, 0xaa5c05ff, 0xa95b08ff, 0xa45303ff, 0x9a4003ff, 0xaa5f1cff, 0xd6a66aff, 0xebca9aff, 0xe6c185ff, 0xe0b670ff, 0xd2a560ff, 0xb36f25ff, 0xa65704ff, 0xa95b04ff, 0xae6003ff, 0xb16404ff, 0xb16604ff, 0xb36a05ff, 0xb06907ff, 0xa96607ff, 0x81510dff, 0x514837ff, 0x7a7c79ff, 0x8b8d88ff, 0x9d9f9bff, 0xd2d3d1ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc7c8c6ff, 0x605f5cff, 0x514731ff, 0x8a784dff, 0xaf9964ff, 0xc8af74ff, 0xd4b56eff, 0xd7b363ff, 0xdab462ff, 0xdab463ff, 0xd9b361ff, 0xd7ac58ff, 0xd6ab59ff, 0xd6ad5dff, 0xd5ab53ff, 0xdab468ff, 0xe0be7bff, 0xddb86fff, 0xd8af5fff, 0xd8af5eff, 0xdbb361ff, 0xd8ac56ff, 0xd5a648ff, 0xd7a84eff, 0xd6a84fff, 0xd5a649ff, 0xd8aa52ff, 0xd9aa51ff, 0xd6a74cff, 0xd6a74fff, 0xd7a94fff, 0xdcac57ff, 0xdbac50ff, 0xd4a139ff, 0xcf9727ff, 0xdcac48ff, 0xe8be65ff, 0xe8c16bff, 0xe8c477ff, 0xeacb84ff, 0xeacb81ff, 0xeacc85ff, 0xebce8dff, 0xeacc8cff, 0xeed29fff, 0xedd19cff, 0xedd096ff, 0xedd097ff, 0xebca8fff, 0xe6c581ff, 0xe7c885ff, 0xe6c681ff, 0xe7c880ff, 0xeacc86ff, 0xeacc8cff, 0xe9ca88ff, 0xe7c780ff, 0xe9cc8aff, 0xe9cc8cff, 0xe9cb8cff, 0xeace92ff, 0xebce90ff, 0xe9cc8dff, 0xe5c482ff, 0xe1bf77ff, 0xdfbc71ff, 0xe0c17cff, 0xe2c382ff, 0xe6cd9bff, 0xe5cf9dff, 0xe0c488ff, 0xd9bd7bff, 0xe1c996ff, 0xdec590ff, 0xdcc489ff, 0xdcc38dff, 0xdabe8cff, 0xcaa762ff, 0xc4a156ff, 0xc49e50ff, 0xcb973cff, 0xce9b47ff, 0xce9c46ff, 0xca9338ff, 0xb67110ff, 0xae6104ff, 0xad6204ff, 0xad6405ff, 0xa45303ff, 0xb56d21ff, 0xe4be88ff, 0xe5c18fff, 0xe0b87cff, 0xd3a159ff, 0xb36f26ff, 0xa35203ff, 0xa85904ff, 0xac5d04ff, 0xb06404ff, 0xb36a05ff, 0xb06905ff, 0xad6704ff, 0xa56203ff, 0x7c4d05ff, 0x4f4533ff, 0x7d7f7dff, 0x8b8d88ff, 0xa0a29eff, 0xd4d6d3ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xcbcccaff, 0x656663ff, 0x534b39ff, 0x8c7953ff, 0xae9866ff, 0xc7ae73ff, 0xd5b772ff, 0xdab96cff, 0xdab765ff, 0xd9b360ff, 0xdab566ff, 0xdbb564ff, 0xd7ad56ff, 0xd3a850ff, 0xd4a951ff, 0xd4aa54ff, 0xd9b165ff, 0xdbb366ff, 0xd6aa52ff, 0xd3a64bff, 0xd6ac52ff, 0xd8ad56ff, 0xd7ac55ff, 0xd3a445ff, 0xd4a64aff, 0xd2a346ff, 0xd2a242ff, 0xd3a343ff, 0xd19f3dff, 0xd1a03eff, 0xd2a240ff, 0xd5a446ff, 0xd7a648ff, 0xd6a544ff, 0xd39f35ff, 0xd5a136ff, 0xe3b85eff, 0xebc778ff, 0xebc87cff, 0xe7c274ff, 0xe9c982ff, 0xeccd87ff, 0xf0d79bff, 0xefd49aff, 0xeccd8dff, 0xeaca8bff, 0xe9c989ff, 0xeed097ff, 0xefd39fff, 0xeccd8fff, 0xe8c77eff, 0xeac986ff, 0xe7c579ff, 0xe9c87cff, 0xedd08dff, 0xedd08cff, 0xebce88ff, 0xe8c779ff, 0xe9c87bff, 0xebcc83ff, 0xedd090ff, 0xedd297ff, 0xeed195ff, 0xeacc8dff, 0xe5c682ff, 0xe2bf76ff, 0xe2c27bff, 0xe2c17fff, 0xdfbe79ff, 0xe3c68eff, 0xe8d3a5ff, 0xe3cc98ff, 0xe3cb98ff, 0xe2ca9aff, 0xe1cc9fff, 0xdfc898ff, 0xddc38eff, 0xd8bc86ff, 0xc09644ff, 0xb17f1fff, 0xb18023ff, 0xcc993fff, 0xcc983dff, 0xca973bff, 0xcb963dff, 0xc08524ff, 0xaf6a04ff, 0xac6304ff, 0xab6205ff, 0xa95c04ff, 0xb06615ff, 0xc28741ff, 0xc99352ff, 0xb37132ff, 0xa3530eff, 0x9e4a05ff, 0xa35104ff, 0xa85a05ff, 0xac5e04ff, 0xb06805ff, 0xae6605ff, 0xa96505ff, 0xa15e04ff, 0x794806ff, 0x534939ff, 0x818380ff, 0x8b8d88ff, 0xa3a5a1ff, 0xd7d8d5ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xcecfceff, 0x686967ff, 0x4d4533ff, 0x88774dff, 0xac965fff, 0xc8b274ff, 0xd8be7fff, 0xddbd72ff, 0xe0be6fff, 0xdfbe72ff, 0xdbb767ff, 0xdbb565ff, 0xddb869ff, 0xdbb462ff, 0xd6ac53ff, 0xd4a84cff, 0xd4a950ff, 0xd4aa53ff, 0xd3a74bff, 0xd6ac56ff, 0xd6ab57ff, 0xd5aa55ff, 0xd6aa56ff, 0xd7ab58ff, 0xd2a449ff, 0xcf9d39ff, 0xd2a13eff, 0xd7a64bff, 0xd1a03aff, 0xcd992cff, 0xcc992bff, 0xce9b32ff, 0xce9a34ff, 0xd19d35ff, 0xd4a03cff, 0xd29f37ff, 0xd7a53eff, 0xe3b95dff, 0xe6bd63ff, 0xe9c77aff, 0xe4bd66ff, 0xe4bc68ff, 0xecce89ff, 0xf0d498ff, 0xf0d59bff, 0xf0d59bff, 0xefd397ff, 0xeed195ff, 0xeacb8bff, 0xe9c987ff, 0xedce8eff, 0xeccd87ff, 0xebcc83ff, 0xeccd84ff, 0xeacc82ff, 0xeccf88ff, 0xedcf87ff, 0xebcc81ff, 0xecca7aff, 0xebc56dff, 0xebc776ff, 0xeccd83ff, 0xedcf89ff, 0xeacb83ff, 0xe8c77bff, 0xe6c57bff, 0xe5c377ff, 0xe7c984ff, 0xe7cb8dff, 0xe6c88bff, 0xe2c484ff, 0xe3c78eff, 0xe3c994ff, 0xe4cc9bff, 0xe4cc9bff, 0xe1ca9aff, 0xe1c797ff, 0xe0c591ff, 0xdbc28eff, 0xb98b36ff, 0xa77004ff, 0xa87207ff, 0xcb993cff, 0xd0a251ff, 0xcd9c49ff, 0xc9963dff, 0xc48e2dff, 0xb2730bff, 0xa95f05ff, 0xa85a05ff, 0xab6005ff, 0xad6304ff, 0xac6103ff, 0xa95e04ff, 0x9b4903ff, 0x9d4803ff, 0xa04d04ff, 0xa65505ff, 0xa85a05ff, 0xab6004ff, 0xad6504ff, 0xa66004ff, 0x9d5b03ff, 0x704307ff, 0x564c3eff, 0x838582ff, 0x8b8d88ff, 0xa7a8a4ff, 0xd9dad7ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd2d3d1ff, 0x6f6f6cff, 0x463d2aff, 0x84724aff, 0xab935bff, 0xc8b072ff, 0xd9be79ff, 0xe2c47dff, 0xe6c984ff, 0xe4c279ff, 0xe1be74ff, 0xddb768ff, 0xdcb665ff, 0xdcb766ff, 0xd9b058ff, 0xd5ab4dff, 0xd6ac52ff, 0xd5aa51ff, 0xd4a94fff, 0xd5aa53ff, 0xd6aa57ff, 0xd7ac57ff, 0xd5a951ff, 0xd3a64cff, 0xd1a345ff, 0xd09f3fff, 0xcc9831ff, 0xca972aff, 0xce9c34ff, 0xcb972cff, 0xc89221ff, 0xca9523ff, 0xca9527ff, 0xc99528ff, 0xcb9628ff, 0xcd9829ff, 0xd29e31ff, 0xdaa945ff, 0xe6bc65ff, 0xe6bf69ff, 0xe9c579ff, 0xeac87bff, 0xebcb7fff, 0xefd291ff, 0xf2d99fff, 0xf0d49cff, 0xf0d59bff, 0xf1daa2ff, 0xefd497ff, 0xeccd88ff, 0xeaca82ff, 0xeed28eff, 0xeccd85ff, 0xedd088ff, 0xeed288ff, 0xf1d691ff, 0xf1d794ff, 0xf1d593ff, 0xeecb7eff, 0xedc978ff, 0xedc771ff, 0xebc469ff, 0xecc872ff, 0xedc875ff, 0xebc879ff, 0xeac878ff, 0xedcf84ff, 0xeed493ff, 0xecd091ff, 0xe8cb86ff, 0xe7c985ff, 0xe6c88aff, 0xe4c787ff, 0xe5ca8fff, 0xe5cd98ff, 0xe4cc97ff, 0xe3cb9aff, 0xe4d0a2ff, 0xe2cb98ff, 0xdbbe86ff, 0xbc9040ff, 0xae7b1aff, 0xb17f1eff, 0xcb973dff, 0xce9f4fff, 0xcc9c4aff, 0xcb963cff, 0xc89337ff, 0xb87b1aff, 0xa75b04ff, 0xa65605ff, 0xaf6806ff, 0xb57409ff, 0xb5730dff, 0xa76109ff, 0x933e04ff, 0x9a4204ff, 0x9f4a04ff, 0xa65404ff, 0xa75704ff, 0xa95e05ff, 0xa55f05ff, 0x9a5903ff, 0x694008ff, 0x524d43ff, 0x858784ff, 0x8b8d88ff, 0xaaaca8ff, 0xdadbd9ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd5d6d4ff, 0x757573ff, 0x423720ff, 0x7c6637ff, 0xa8925eff, 0xc5aa69ff, 0xd8bc74ff, 0xe2c172ff, 0xe7ca81ff, 0xe4c275ff, 0xdeb766ff, 0xe2c077ff, 0xdfbb6fff, 0xdbb361ff, 0xdbb25eff, 0xd9af59ff, 0xd7ae56ff, 0xd5aa50ff, 0xd4aa4fff, 0xd7ac55ff, 0xd7ab54ff, 0xd5a850ff, 0xd3a546ff, 0xcf9e3cff, 0xcb9a34ff, 0xcb982eff, 0xcd992dff, 0xce9c38ff, 0xcf9d39ff, 0xd4a440ff, 0xd3a342ff, 0xd0a13dff, 0xcb982dff, 0xcb9729ff, 0xd09d34ff, 0xcf9a2eff, 0xc9911eff, 0xcb9420ff, 0xd29c2cff, 0xe4b95dff, 0xe9c677ff, 0xe8c475ff, 0xecca7fff, 0xe9c87cff, 0xeccf88ff, 0xf0d696ff, 0xf1d99dff, 0xf0d79bff, 0xf0d599ff, 0xefd493ff, 0xefd492ff, 0xf2d897ff, 0xf2d998ff, 0xf1d797ff, 0xf0d58fff, 0xf2d792ff, 0xf2d790ff, 0xf2d88fff, 0xf2d78eff, 0xf0d286ff, 0xeec873ff, 0xeec770ff, 0xeec569ff, 0xefca71ff, 0xefcb72ff, 0xefcd79ff, 0xefd286ff, 0xf2d892ff, 0xf2da9fff, 0xeed495ff, 0xebd08aff, 0xecd193ff, 0xeace91ff, 0xead19aff, 0xead4a5ff, 0xe9d4a5ff, 0xead5a4ff, 0xe6cf9dff, 0xe3cd9fff, 0xe3cca1ff, 0xdbbe84ff, 0xc2994cff, 0xb98d38ff, 0xbc9240ff, 0xc89332ff, 0xc79330ff, 0xcb9b44ff, 0xca9a45ff, 0xca9a44ff, 0xc0892aff, 0xac6206ff, 0xaa5e04ff, 0xb27005ff, 0xba7d11ff, 0xba7d15ff, 0xaf6a07ff, 0x9e4a04ff, 0x983d04ff, 0x9a4004ff, 0x9f4804ff, 0xa25105ff, 0xa35a05ff, 0x985803ff, 0x653f0aff, 0x555149ff, 0x878986ff, 0x8b8d88ff, 0xaeafabff, 0xdcdddbff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd7d8d6ff, 0x7c7d7aff, 0x42361fff, 0x745925ff, 0xa18345ff, 0xc4a86aff, 0xd9bc79ff, 0xe3c67fff, 0xe7c97fff, 0xe5c77bff, 0xe2c16fff, 0xddb660ff, 0xe1bf76ff, 0xdfb96cff, 0xd9b056ff, 0xd7ab51ff, 0xd7ac51ff, 0xd6aa50ff, 0xd2a648ff, 0xd4a648ff, 0xd6aa51ff, 0xd3a449ff, 0xd1a241ff, 0xd6a646ff, 0xcf9c37ff, 0xc9922aff, 0xc89024ff, 0xc88e24ff, 0xd7a03bff, 0xe5b856ff, 0xedcb7dff, 0xf0d290ff, 0xf1d499ff, 0xeecf8eff, 0xeccc84ff, 0xebc87eff, 0xe9c578ff, 0xe2b965ff, 0xdcae50ff, 0xd49f33ff, 0xe4bb63ff, 0xf0d190ff, 0xf0d394ff, 0xeed18bff, 0xeaca7dff, 0xedd087ff, 0xf0d58eff, 0xf3dc9dff, 0xf0d694ff, 0xebcc86ff, 0xf0d391ff, 0xf2d899ff, 0xf2d895ff, 0xf2d795ff, 0xf3d899ff, 0xf2d68fff, 0xf3d890ff, 0xf5dc97ff, 0xf2d88eff, 0xf0d281ff, 0xf0cf7eff, 0xefcb75ff, 0xeec86bff, 0xefc86cff, 0xf1cc73ff, 0xf2d17dff, 0xf2d483ff, 0xf2d78cff, 0xf4db97ff, 0xf5dda1ff, 0xf1d898ff, 0xeed491ff, 0xeed395ff, 0xecd090ff, 0xecd39cff, 0xecd6a7ff, 0xead5a8ff, 0xebd7abff, 0xe9d6a8ff, 0xe5cd9bff, 0xe0c590ff, 0xdabc80ff, 0xc9a359ff, 0xc29b4bff, 0xc49f50ff, 0xc89733ff, 0xc49028ff, 0xc89639ff, 0xca9840ff, 0xcea150ff, 0xca9a46ff, 0xb57312ff, 0xae6304ff, 0xb37107ff, 0xb97a12ff, 0xbf8625ff, 0xb67813ff, 0xaf6b07ff, 0x9c4a05ff, 0x963b04ff, 0x9a4304ff, 0x9b4b04ff, 0x935103ff, 0x643e0cff, 0x5a564eff, 0x898b87ff, 0x8c8e89ff, 0xb1b3afff, 0xdddedcff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xd9dad8ff, 0x828381ff, 0x423723ff, 0x6d501bff, 0x96722cff, 0xbb974cff, 0xd7b974ff, 0xe3c781ff, 0xe8cc87ff, 0xe9ce87ff, 0xe8c87dff, 0xe4c375ff, 0xe1be6cff, 0xe1bc6dff, 0xdeb661ff, 0xd9ae55ff, 0xd7ab4fff, 0xd3a542ff, 0xd3a446ff, 0xd0a142ff, 0xd3a341ff, 0xdbb159ff, 0xe2bc6aff, 0xe9c77aff, 0xf1d291ff, 0xe8c075ff, 0xd9a13dff, 0xd8a03fff, 0xd8a043ff, 0xe3b551ff, 0xebc66dff, 0xeccd83ff, 0xeed08cff, 0xefd291ff, 0xf1d497ff, 0xf2d79eff, 0xf2d79fff, 0xf1d79eff, 0xf0d197ff, 0xeac782ff, 0xe3b763ff, 0xe8c171ff, 0xf0d492ff, 0xefd28eff, 0xeecf89ff, 0xf1d592ff, 0xf2d995ff, 0xf7e1a6ff, 0xf6e1abff, 0xf5dea3ff, 0xf3dca0ff, 0xf6dfa8ff, 0xf6dda1ff, 0xf4db9aff, 0xf3da94ff, 0xf6e0a4ff, 0xf5de9cff, 0xf7e09dff, 0xf6df99ff, 0xf5dc95ff, 0xf4d788ff, 0xf4d88cff, 0xf1d07dff, 0xf0cb6cff, 0xf3d27aff, 0xf5d887ff, 0xf6dd96ff, 0xf6df99ff, 0xf6e09eff, 0xf5df9bff, 0xf5dd99ff, 0xf5de9fff, 0xf3dea3ff, 0xf3dda7ff, 0xf1d89fff, 0xeed6a3ff, 0xebd3a4ff, 0xead4a6ff, 0xe9d5a6ff, 0xe8d5a9ff, 0xe7d1a0ff, 0xe0c184ff, 0xd5b26aff, 0xcca85eff, 0xcba85cff, 0xcca95eff, 0xcb9a3dff, 0xca993fff, 0xcc9d47ff, 0xc99741ff, 0xcd9f4bff, 0xcfa355ff, 0xba8021ff, 0xae6504ff, 0xb26f07ff, 0xb77812ff, 0xc18b32ff, 0xbf882cff, 0xb87b18ff, 0xa35908ff, 0x964104ff, 0x934004ff, 0x8e4303ff, 0x61360bff, 0x5b5750ff, 0x8a8c88ff, 0x8c8e89ff, 0xb5b6b3ff, 0xdedfddff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdbdcdaff, 0x888988ff, 0x3d3422ff, 0x684c1aff, 0x8e681fff, 0xaf8533ff, 0xcfa858ff, 0xe3c580ff, 0xe8ca83ff, 0xeace89ff, 0xeacc85ff, 0xe7c879ff, 0xe5c273ff, 0xe2be6dff, 0xdbaf54ff, 0xdaae51ff, 0xd8ab4eff, 0xd9a94aff, 0xcf9d32ff, 0xd09d36ff, 0xd8a94bff, 0xe8c475ff, 0xeed08bff, 0xf2d899ff, 0xf4daa0ff, 0xf2d69bff, 0xf0d398ff, 0xeccd88ff, 0xe9c67aff, 0xeac678ff, 0xe9c777ff, 0xeaca80ff, 0xebc981ff, 0xeaca80ff, 0xecce8aff, 0xeed195ff, 0xf0d39bff, 0xefd39cff, 0xefd39bff, 0xeece93ff, 0xe8c47fff, 0xe5bb6cff, 0xe6bd69ff, 0xeecd82ff, 0xeecf87ff, 0xf0d58fff, 0xf0d58eff, 0xf1d68fff, 0xf7e2a8ff, 0xfae6b0ff, 0xf8e4aaff, 0xf7e2a7ff, 0xf7e1a2ff, 0xf7e2a0ff, 0xfae4a6ff, 0xf9e3a3ff, 0xfbe9b6ff, 0xf9e4a8ff, 0xfae5a9ff, 0xf9e5a5ff, 0xf8e298ff, 0xf8e094ff, 0xf9e29bff, 0xf6da8aff, 0xf4d279ff, 0xf6d986ff, 0xfae399ff, 0xfae59fff, 0xfae6aaff, 0xfbe7b5ff, 0xf9e5a9ff, 0xf8e4aaff, 0xf6e1a7ff, 0xf4dfa6ff, 0xf4e1aeff, 0xf2deabff, 0xeed79fff, 0xebd5a2ff, 0xead3a4ff, 0xe9d4a6ff, 0xe9d5a8ff, 0xe9d3a7ff, 0xe3c68eff, 0xd3ad60ff, 0xcda959ff, 0xcfab5bff, 0xd1ae66ff, 0xcb9b45ff, 0xcda251ff, 0xcea250ff, 0xca9b47ff, 0xcfa454ff, 0xd2a85dff, 0xc79540ff, 0xb26e0aff, 0xb16c04ff, 0xb3730aff, 0xc0872cff, 0xc38d33ff, 0xb87c19ff, 0xa55f0bff, 0x8c3604ff, 0x893804ff, 0x6b3205ff, 0x524a40ff, 0x888b87ff, 0x8d8f8aff, 0xb8bab6ff, 0xdfe0deff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdddedcff, 0x8f908fff, 0x3e3526ff, 0x5f420dff, 0x865e10ff, 0xa3751fff, 0xc29440ff, 0xd8af5cff, 0xe9cb88ff, 0xedd18eff, 0xeacc81ff, 0xe7c478ff, 0xe9ca84ff, 0xe7c67eff, 0xe3bd69ff, 0xd9ae4eff, 0xd4a640ff, 0xd6a743ff, 0xd3a137ff, 0xcf9a2bff, 0xd19d31ff, 0xe8c678ff, 0xf3daa0ff, 0xf3d99eff, 0xf2daa2ff, 0xf1d7a0ff, 0xefd397ff, 0xeed094ff, 0xecce8dff, 0xeacc88ff, 0xeacb85ff, 0xeacd88ff, 0xebcc88ff, 0xeccb89ff, 0xeacb88ff, 0xeacb89ff, 0xeacb8aff, 0xeccd8fff, 0xedcd93ff, 0xedcd94ff, 0xeac98cff, 0xe1b76cff, 0xe1b35eff, 0xe4b960ff, 0xf1d490ff, 0xf3d997ff, 0xf5de9aff, 0xf1d58bff, 0xefce7fff, 0xf5de9aff, 0xfce9b3ff, 0xfbe7aeff, 0xf7e19fff, 0xf8e3a5ff, 0xfae5a7ff, 0xfae7aaff, 0xfce9aeff, 0xfce8afff, 0xfce8b1ff, 0xfce9aeff, 0xfce9aaff, 0xfbe6a2ff, 0xfae49aff, 0xfae39aff, 0xfae294ff, 0xf9df91ff, 0xf9e192ff, 0xfbe49dff, 0xfbe7a9ff, 0xfce9adff, 0xfbe9b0ff, 0xfae5a7ff, 0xf8e4a6ff, 0xf6e3abff, 0xf6e1a9ff, 0xf6e3aeff, 0xf4e1afff, 0xf1dca4ff, 0xedd69eff, 0xead49fff, 0xe9d3a5ff, 0xe9d4a8ff, 0xe8d2a4ff, 0xe6ce9eff, 0xd8b670ff, 0xd0aa57ff, 0xd2af63ff, 0xd5b268ff, 0xd0a357ff, 0xd1a659ff, 0xd2a85cff, 0xd1a65bff, 0xcfa456ff, 0xd1a659ff, 0xc99b49ff, 0xb3720fff, 0xb16c04ff, 0xb3740bff, 0xbc821fff, 0xc7943dff, 0xc18b30ff, 0xac6c14ff, 0x863304ff, 0x762b02ff, 0x492f20ff, 0x7c7f7cff, 0x8d8f8aff, 0xb7b8b5ff, 0xe0e1dfff, 0xe2e3e1ff, 0xe4e5e4ff, 0xe4e5e3ff, 0xe3e4e3ff, 0xe4e5e4ff, 0xe3e4e2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xdedfddff, 0x969795ff, 0x3f3829ff, 0x604415ff, 0x855d14ff, 0x9a6b0fff, 0xb17d1eff, 0xcd9e47ff, 0xdfba6fff, 0xe8c57eff, 0xf0d494ff, 0xe7c574ff, 0xe3bb66ff, 0xe5c173ff, 0xe5c06fff, 0xe4bc69ff, 0xddb354ff, 0xd6a941ff, 0xd6a63fff, 0xd19e30ff, 0xce992aff, 0xe2b65cff, 0xf0d390ff, 0xf0d499ff, 0xf0d396ff, 0xf0d59eff, 0xf0d39bff, 0xeed294ff, 0xedd094ff, 0xebcc8bff, 0xeacb89ff, 0xe8c883ff, 0xe7c57dff, 0xe7c47cff, 0xe8c67fff, 0xe7c781ff, 0xe7c884ff, 0xe7c781ff, 0xe7c682ff, 0xe7c583ff, 0xe7c27fff, 0xe4bf77ff, 0xe1b869ff, 0xdfb55eff, 0xe0b356ff, 0xf0ce84ff, 0xf6e09fff, 0xf7e19eff, 0xf5dd97ff, 0xf3d88dff, 0xf8e3a0ff, 0xfbe7acff, 0xfbe7adff, 0xf8e3a1ff, 0xf8e3a1ff, 0xfbe6a7ff, 0xfceaaeff, 0xfdeab4ff, 0xfceab4ff, 0xfdeab9ff, 0xfdeab3ff, 0xfce9aaff, 0xfce9a8ff, 0xfce8a5ff, 0xfce6a0ff, 0xfbe69cff, 0xfce8a5ff, 0xfceaaeff, 0xfce9b0ff, 0xfceab5ff, 0xfcebb8ff, 0xfbe9b3ff, 0xfbe9b1ff, 0xf9e6a9ff, 0xf7e5abff, 0xf6e3abff, 0xf6e2acff, 0xf6e6b7ff, 0xf4e2b3ff, 0xf0d9a4ff, 0xebd094ff, 0xe8ce93ff, 0xe7cc97ff, 0xe6cc94ff, 0xe5cb95ff, 0xdcbc7bff, 0xd4ae5dff, 0xd4b162ff, 0xd9b76dff, 0xd3aa64ff, 0xd2a961ff, 0xd3a962ff, 0xd4aa64ff, 0xd3aa63ff, 0xd1a85aff, 0xcc9f50ff, 0xb57413ff, 0xb26d05ff, 0xbd8321ff, 0xc5923aff, 0xc99842ff, 0xc49038ff, 0xad721fff, 0x7c2d05ff, 0x5a1f05ff, 0x5a5551ff, 0x8c8e8aff, 0xa5a7a3ff, 0xdddedcff, 0xe3e4e2ff, 0xdadbdaff, 0xaeaeadff, 0x8e8c88ff, 0x8a8680ff, 0x9e9c98ff, 0xc9c9c9ff, 0xe3e4e3ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe0e0dfff, 0x9c9e9cff, 0x423a2aff, 0x5d4112ff, 0x855d18ff, 0x9e6f1cff, 0xad7715ff, 0xb9811aff, 0xd1a04aff, 0xdcb163ff, 0xe3ba6bff, 0xe8c477ff, 0xe3be69ff, 0xdfb656ff, 0xdeb24fff, 0xe0b755ff, 0xe1b85cff, 0xdaae4bff, 0xd5a53bff, 0xd29e2eff, 0xcf9a26ff, 0xd7a53dff, 0xe9c375ff, 0xedcc8bff, 0xeccb8bff, 0xebcb89ff, 0xeccd8fff, 0xeccd90ff, 0xeccc8fff, 0xebcc8fff, 0xe9ca87ff, 0xe8c886ff, 0xe7c57bff, 0xe4bf6eff, 0xe3bd6cff, 0xe3bb69ff, 0xe4be70ff, 0xe5c47fff, 0xe5c480ff, 0xe4c178ff, 0xe3bd6fff, 0xe2bb6eff, 0xe2ba6bff, 0xe1bb6cff, 0xe1ba6dff, 0xe2b866ff, 0xf1d187ff, 0xf8e3a3ff, 0xf7e09dff, 0xf6df9bff, 0xf7e19fff, 0xf9e4a4ff, 0xf7df97ff, 0xfae6abff, 0xfae5a8ff, 0xfae5a6ff, 0xfceab1ff, 0xfdebb4ff, 0xfceaadff, 0xfceab2ff, 0xfcecbbff, 0xfcebb4ff, 0xfce9aaff, 0xfdeaadff, 0xfdeaafff, 0xfdeab0ff, 0xfce9abff, 0xfbe8aaff, 0xfbe7b0ff, 0xfcecbeff, 0xfcedc1ff, 0xfbecbcff, 0xfbeabaff, 0xfbe9b6ff, 0xfae9b4ff, 0xf9e8b2ff, 0xf7e3adff, 0xf5e0a7ff, 0xf4e1aaff, 0xf2e1b0ff, 0xf1dda6ff, 0xefd89cff, 0xe8cc84ff, 0xe5ca88ff, 0xe2c581ff, 0xe1c285ff, 0xd9b670ff, 0xd7b365ff, 0xd9b76dff, 0xdcb970ff, 0xd3aa62ff, 0xd1a65dff, 0xd3a75eff, 0xd4aa60ff, 0xd6ae69ff, 0xd4ad6aff, 0xd0a65dff, 0xb7781cff, 0xb46f08ff, 0xc1872dff, 0xcd9c50ff, 0xcc9e4eff, 0xc79745ff, 0xad772bff, 0x732804ff, 0x472112ff, 0x757774ff, 0x90928dff, 0xcaccc9ff, 0xe3e4e2ff, 0xd3d4d2ff, 0x706f6bff, 0x423822ff, 0x4e3c1aff, 0x493108ff, 0x442d08ff, 0x4c3e2eff, 0xa1a09fff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe0e1dfff, 0xa2a4a2ff, 0x413a2dff, 0x563806ff, 0x7d520aff, 0x95630fff, 0xab7319ff, 0xb57b17ff, 0xb87b12ff, 0xc58b29ff, 0xd4a243ff, 0xddb158ff, 0xe6c075ff, 0xe2bb66ff, 0xdcaf4bff, 0xdbb04cff, 0xddb251ff, 0xdcaf4fff, 0xd4a63bff, 0xcf9c27ff, 0xcc9318ff, 0xce961fff, 0xdfb24fff, 0xe6bb5eff, 0xe8bf6cff, 0xe8c172ff, 0xe7c274ff, 0xe8c47dff, 0xe7c47cff, 0xe6c175ff, 0xe7c378ff, 0xe7c682ff, 0xe6c47fff, 0xe3be6eff, 0xe1ba68ff, 0xe0b561ff, 0xdfb45aff, 0xdfb45bff, 0xe3bd72ff, 0xe3c07bff, 0xe2be75ff, 0xe2bc71ff, 0xe0b96dff, 0xdfb668ff, 0xdeb665ff, 0xdfba6fff, 0xe5c077ff, 0xf4d792ff, 0xfae5aaff, 0xfbe8b2ff, 0xfbe8adff, 0xfae4a1ff, 0xf5db93ff, 0xf6db90ff, 0xfae5a1ff, 0xfce9aaff, 0xfde9acff, 0xfceaaaff, 0xfcebabff, 0xfdeaabff, 0xfceab1ff, 0xfcecb5ff, 0xfdedb3ff, 0xfdebb2ff, 0xfdeab2ff, 0xfcebb7ff, 0xfdeab7ff, 0xfdeab4ff, 0xfbe9b0ff, 0xf9e5a9ff, 0xf9e6b0ff, 0xf6e1a8ff, 0xf4dd9dff, 0xf5dfa0ff, 0xf6e3aaff, 0xf8e8b4ff, 0xf8e9b8ff, 0xf7e7b4ff, 0xf6e4b3ff, 0xf6e6b6ff, 0xf2e0acff, 0xf1dca2ff, 0xf0db9eff, 0xeed798ff, 0xecd59dff, 0xe5c787ff, 0xdcb97aff, 0xd9b56dff, 0xdcb971ff, 0xe0c079ff, 0xdfbc71ff, 0xcfa45bff, 0xcfa257ff, 0xcc9d4cff, 0xcd9f4dff, 0xd4ab65ff, 0xd6b174ff, 0xd1a860ff, 0xba7d20ff, 0xb26a05ff, 0xb77715ff, 0xbd7f26ff, 0xc48d36ff, 0xc89a4aff, 0xad7c39ff, 0x692504ff, 0x432c23ff, 0x828582ff, 0x9ea09bff, 0xdddedcff, 0xdddedcff, 0x8e8f8dff, 0x3f3724ff, 0x6b572aff, 0x714d15ff, 0x714703ff, 0x6a4405ff, 0x513402ff, 0x423827ff, 0xbbbcbbff, 0xe3e4e2ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xa9aaa9ff, 0x413a31ff, 0x4c2b03ff, 0x734403ff, 0x8e5705ff, 0xa16507ff, 0xae700dff, 0xb4740dff, 0xb37208ff, 0xb8780dff, 0xd09c37ff, 0xddb059ff, 0xe2bb6bff, 0xe0b761ff, 0xdbae4cff, 0xdbae49ff, 0xd8ab43ff, 0xd3a337ff, 0xd19e2dff, 0xcb9216ff, 0xcb9016ff, 0xdcad48ff, 0xe9c269ff, 0xe7bd63ff, 0xe3b95cff, 0xe2b85cff, 0xe3bb63ff, 0xe5bf70ff, 0xe5bc69ff, 0xe4bb64ff, 0xe5bc67ff, 0xe5bd70ff, 0xe4bc6dff, 0xe0b55dff, 0xdcae4fff, 0xdaaa4bff, 0xd8a945ff, 0xdaab4aff, 0xdfb35eff, 0xe2ba70ff, 0xe1ba70ff, 0xe0b96cff, 0xdfb669ff, 0xddb262ff, 0xdbb361ff, 0xddb567ff, 0xe0b76dff, 0xeecc83ff, 0xfae3a5ff, 0xfbe5a8ff, 0xfceaaeff, 0xfbe8a2ff, 0xf8df94ff, 0xf8e197ff, 0xfceaadff, 0xfdecb3ff, 0xfceaaeff, 0xfceaa8ff, 0xfcecb1ff, 0xfdefbaff, 0xfdefb8ff, 0xfdedb6ff, 0xfdecb7ff, 0xfdebb6ff, 0xfce9b1ff, 0xf9e4a7ff, 0xf9e5a9ff, 0xf9e5b0ff, 0xefd396ff, 0xe3c071ff, 0xe1bc67ff, 0xdab257ff, 0xd7ad52ff, 0xd7ae50ff, 0xd7ad52ff, 0xe0bb69ff, 0xe9ce8cff, 0xefda9eff, 0xf1db9fff, 0xf3e1a8ff, 0xf4e2b0ff, 0xf2dfaaff, 0xf0da9cff, 0xeed898ff, 0xefdca8ff, 0xecd7a2ff, 0xeacf93ff, 0xe2c17bff, 0xdfbe75ff, 0xe3c380ff, 0xe2c27bff, 0xcea157ff, 0xcfa154ff, 0xcb9c4aff, 0xc58f33ff, 0xd2a458ff, 0xd9b47bff, 0xd2a964ff, 0xbb8023ff, 0xb26f08ff, 0xb87816ff, 0xbc7d1fff, 0xc18628ff, 0xc69749ff, 0xa06823ff, 0x611d01ff, 0x43322cff, 0x868985ff, 0xacada9ff, 0xe2e3e1ff, 0xc4c6c3ff, 0x626360ff, 0x594b2cff, 0x886b38ff, 0x85500aff, 0x925c06ff, 0x8b5c11ff, 0x755013ff, 0x42300fff, 0x868785ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xafb0aeff, 0x443e37ff, 0x462603ff, 0x6c3a03ff, 0x854903ff, 0x9b5b04ff, 0xa86404ff, 0xad6704ff, 0xb06c05ff, 0xaf6b05ff, 0xb06b05ff, 0xc88d2aff, 0xdbac56ff, 0xe0b764ff, 0xdfb65fff, 0xdaac48ff, 0xdaaa43ff, 0xd6a439ff, 0xd29f2cff, 0xcf9922ff, 0xcd941bff, 0xe0b351ff, 0xeac165ff, 0xeeca74ff, 0xf1d081ff, 0xebc976ff, 0xe4bc60ff, 0xe1b75aff, 0xe2b860ff, 0xe1b45aff, 0xdfb458ff, 0xe0b45cff, 0xdfb35cff, 0xddb056ff, 0xd8a743ff, 0xd29f31ff, 0xd1a032ff, 0xd5a53eff, 0xd7a844ff, 0xd9ab4cff, 0xdcb057ff, 0xdbb058ff, 0xddb361ff, 0xdbb05bff, 0xd9ae56ff, 0xdaaf5aff, 0xdaae58ff, 0xdaae59ff, 0xe3b964ff, 0xf6db91ff, 0xfbe7a7ff, 0xfdeaafff, 0xfce9a8ff, 0xfbe49fff, 0xfae296ff, 0xfceaabff, 0xfdeeb8ff, 0xfcecb0ff, 0xfdecb2ff, 0xfdeeb9ff, 0xfceeb3ff, 0xfcebabff, 0xfdeab7ff, 0xfceabaff, 0xfce8b0ff, 0xfbe6a8ff, 0xf6dc97ff, 0xebcd86ff, 0xe0bc73ff, 0xd7ad55ff, 0xd3a743ff, 0xd2a53bff, 0xd3a63fff, 0xcfa33cff, 0xcca034ff, 0xc99827ff, 0xc89420ff, 0xcc9d30ff, 0xd3a947ff, 0xe0bc66ff, 0xebd38bff, 0xf0dba1ff, 0xefdba1ff, 0xf0db9fff, 0xefd99aff, 0xefdba3ff, 0xeddba6ff, 0xedd79eff, 0xe8cb88ff, 0xdfbd74ff, 0xe1c076ff, 0xe3c276ff, 0xd0a457ff, 0xd0a355ff, 0xc99740ff, 0xc58d30ff, 0xd3a55dff, 0xd6ae71ff, 0xcfa35dff, 0xba7d1fff, 0xb3720fff, 0xb67613ff, 0xb97b1aff, 0xbe8526ff, 0xbd8633ff, 0x9b6013ff, 0x642401ff, 0x412f28ff, 0x858784ff, 0xb0b2aeff, 0xe1e2e0ff, 0xb0b1aeff, 0x61615eff, 0x645430ff, 0x885d26ff, 0x935203ff, 0xa66908ff, 0x9d6914ff, 0x845913ff, 0x4b3511ff, 0x797a77ff, 0xdbdcd9ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xb4b6b4ff, 0x49433dff, 0x422204ff, 0x663502ff, 0x804404ff, 0x945004ff, 0xa05804ff, 0xa75d04ff, 0xa96104ff, 0xab6204ff, 0xab6004ff, 0xab6103ff, 0xb77614ff, 0xd4a145ff, 0xddb15dff, 0xe2bc6fff, 0xe0b561ff, 0xd9ac4dff, 0xd7a646ff, 0xd39f33ff, 0xcc9319ff, 0xd9a435ff, 0xecc268ff, 0xedc365ff, 0xf0cd74ff, 0xf3d78eff, 0xf6da97ff, 0xf0cf7fff, 0xe9c168ff, 0xe1b557ff, 0xddad4fff, 0xd9a742ff, 0xd5a13aff, 0xd39f38ff, 0xd09927ff, 0xce9922ff, 0xd09d2aff, 0xd1a036ff, 0xd3a339ff, 0xd4a53dff, 0xd9ac53ff, 0xd5a643ff, 0xd6a544ff, 0xd7a74fff, 0xd3a44bff, 0xd1a245ff, 0xd1a349ff, 0xd0a247ff, 0xd2a54dff, 0xd6aa52ff, 0xeecd7fff, 0xf9e09aff, 0xfce7a7ff, 0xfce9a5ff, 0xfdeba8ff, 0xfbe499ff, 0xfce8a1ff, 0xfdecabff, 0xfdedb3ff, 0xfdecafff, 0xfceaa8ff, 0xfbe79fff, 0xf8df97ff, 0xf9e3a9ff, 0xf8e2a2ff, 0xf6de9aff, 0xf2d78fff, 0xe6c37aff, 0xdab467ff, 0xd8af5aff, 0xd4aa47ff, 0xd5ac49ff, 0xd6ab46ff, 0xd3a740ff, 0xcfa53eff, 0xcea237ff, 0xcc9c2aff, 0xca9922ff, 0xcb9c28ff, 0xcb9c2eff, 0xcd9e36ff, 0xd3ab4bff, 0xe4c575ff, 0xead087ff, 0xecd594ff, 0xefdca4ff, 0xefdba6ff, 0xeedaa2ff, 0xefdba4ff, 0xedd59eff, 0xe0bc71ff, 0xe1bd6bff, 0xe4c172ff, 0xd2a354ff, 0xd2a65bff, 0xca9843ff, 0xc28825ff, 0xcb9542ff, 0xd0a45bff, 0xca994cff, 0xb57516ff, 0xb06d07ff, 0xb67713ff, 0xbc812aff, 0xbf872eff, 0xb67c1aff, 0xa0650aff, 0x753b02ff, 0x3e271bff, 0x7c7f7cff, 0xabada9ff, 0xe0e1dfff, 0xabaca8ff, 0x767775ff, 0x50442aff, 0x723f0dff, 0x8c4903ff, 0xa56707ff, 0x9d6712ff, 0x7e5514ff, 0x483c27ff, 0x8b8c8aff, 0xd7d8d6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xbabbb9ff, 0x4c4742ff, 0x3f1f05ff, 0x623002ff, 0x7b3f03ff, 0x904b04ff, 0x9a5005ff, 0xa15404ff, 0xa35404ff, 0xa25305ff, 0xa35504ff, 0xa45604ff, 0xa55904ff, 0xa85d06ff, 0xc1862bff, 0xdbaf5aff, 0xe2bb70ff, 0xe2ba6eff, 0xdcb05eff, 0xd5a143ff, 0xd6a138ff, 0xdba73dff, 0xe8bc5aff, 0xebc25fff, 0xeec86eff, 0xedc769ff, 0xefcd7dff, 0xf0d087ff, 0xf0cd78ff, 0xf1cc78ff, 0xedc772ff, 0xe8c068ff, 0xd7a945ff, 0xca9424ff, 0xce992cff, 0xce982bff, 0xcf9a27ff, 0xd4a238ff, 0xd5a440ff, 0xd3a23aff, 0xd5a340ff, 0xd6a649ff, 0xd3a445ff, 0xd2a347ff, 0xd1a24cff, 0xd1a44fff, 0xd1a755ff, 0xcfa250ff, 0xcb9c48ff, 0xcea04dff, 0xcfa251ff, 0xdfb664ff, 0xf2d381ff, 0xfae4a1ff, 0xfbe7a3ff, 0xfce9a2ff, 0xfbe79fff, 0xfbe69bff, 0xfceaa5ff, 0xfdebafff, 0xfdeab1ff, 0xfce9afff, 0xf9e4a6ff, 0xf5d98eff, 0xf4d78aff, 0xf2d58aff, 0xe8c67cff, 0xdbb361ff, 0xd5ab52ff, 0xd8ae5aff, 0xd7ae56ff, 0xd1a742ff, 0xd4aa46ff, 0xd9b054ff, 0xd4aa49ff, 0xd0a43fff, 0xcfa33cff, 0xcfa336ff, 0xcea132ff, 0xcd9f2fff, 0xc99923ff, 0xc89722ff, 0xca9c2eff, 0xd2a946ff, 0xe3c371ff, 0xe9ce85ff, 0xeed99fff, 0xefdca8ff, 0xf0dba4ff, 0xf1dea9ff, 0xecd39cff, 0xe2bf75ff, 0xe3be6fff, 0xe3bf6eff, 0xd19d45ff, 0xd1a04eff, 0xca9538ff, 0xbf821cff, 0xc28325ff, 0xce9c4fff, 0xca984cff, 0xb37110ff, 0xb16f07ff, 0xb97b1fff, 0xbd8329ff, 0xc28b32ff, 0xbe8726ff, 0xa87013ff, 0x854e06ff, 0x49280aff, 0x666664ff, 0x9fa19dff, 0xdcdddbff, 0xb7b8b5ff, 0x8b8d89ff, 0x5c5b55ff, 0x47270eff, 0x6e3402ff, 0x8b5404ff, 0x7e520bff, 0x503c1dff, 0x646561ff, 0xa5a7a3ff, 0xdddedcff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xbfc0beff, 0x4f4c49ff, 0x381905ff, 0x5c2702ff, 0x773803ff, 0x8b4304ff, 0x984a04ff, 0x9e4d05ff, 0x9f5004ff, 0xa05004ff, 0xa15105ff, 0xa15105ff, 0xa25504ff, 0xa15405ff, 0xa25404ff, 0xa75d0aff, 0xd3a14fff, 0xe1b76dff, 0xddae5dff, 0xd6a549ff, 0xd6a23eff, 0xe9bf66ff, 0xeec569ff, 0xeac05aff, 0xe7bc51ff, 0xedc975ff, 0xecc877ff, 0xecc56eff, 0xebc670ff, 0xebc469ff, 0xebc364ff, 0xeecb77ff, 0xf2d287ff, 0xeecd81ff, 0xd6a545ff, 0xd5a143ff, 0xd6a54aff, 0xd4a345ff, 0xd19f3dff, 0xd4a13eff, 0xd2a13aff, 0xd1a13fff, 0xd09f42ff, 0xd0a048ff, 0xd0a24fff, 0xcfa252ff, 0xcda050ff, 0xcc9d4dff, 0xc99846ff, 0xc89644ff, 0xc89744ff, 0xcb9d4eff, 0xd3a658ff, 0xe9c471ff, 0xf5d98fff, 0xfbe4a0ff, 0xfce49cff, 0xfbe399ff, 0xfce79dff, 0xfce9a4ff, 0xfbe6a6ff, 0xfae5acff, 0xfbe4acff, 0xf4d898ff, 0xeaca7dff, 0xe4bf6eff, 0xdfb665ff, 0xd7af5dff, 0xd4aa51ff, 0xd6ad53ff, 0xd5ab4fff, 0xd3a94aff, 0xd2a741ff, 0xd4aa44ff, 0xd5ac4eff, 0xd5ab4bff, 0xd3a944ff, 0xd3a844ff, 0xd1a43cff, 0xcda138ff, 0xcd9f36ff, 0xcd9f31ff, 0xca9d2eff, 0xcc9e32ff, 0xcc9f34ff, 0xdab55bff, 0xe8cd84ff, 0xeacf8dff, 0xedd89eff, 0xeedba2ff, 0xf0dca5ff, 0xe8c988ff, 0xe1b969ff, 0xe3bc6aff, 0xe4bf6eff, 0xce983bff, 0xcf9d45ff, 0xcd9a43ff, 0xc18425ff, 0xc3872bff, 0xcb994eff, 0xc48e3dff, 0xb16d0bff, 0xb3710aff, 0xb87a1bff, 0xbe8429ff, 0xc48e38ff, 0xc59342ff, 0xaf7a27ff, 0x8a4e05ff, 0x582902ff, 0x443b36ff, 0x8d8f8cff, 0xc8cac7ff, 0xd4d5d3ff, 0x969793ff, 0x898b87ff, 0x686864ff, 0x4e4439ff, 0x4e402cff, 0x534c3eff, 0x6e6f6cff, 0x949692ff, 0xc8c9c6ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xc3c4c3ff, 0x54514fff, 0x351807ff, 0x582502ff, 0x6e2e03ff, 0x813804ff, 0x8f4104ff, 0x954604ff, 0x9f5104ff, 0xa15204ff, 0xa05104ff, 0x9e5004ff, 0x9e5005ff, 0xa05304ff, 0xa25604ff, 0xa35505ff, 0xa15304ff, 0xc1842cff, 0xd8a954ff, 0xd6a245ff, 0xd39d35ff, 0xe3b456ff, 0xeec774ff, 0xebc262ff, 0xeabf59ff, 0xecc464ff, 0xefd089ff, 0xedcd83ff, 0xe9c470ff, 0xe8c166ff, 0xebc874ff, 0xecc774ff, 0xeecd84ff, 0xf2d38fff, 0xf3d795ff, 0xe8bf67ff, 0xdead4fff, 0xdaa74aff, 0xd9aa4fff, 0xd6a447ff, 0xd19f3fff, 0xcf9f40ff, 0xd0a146ff, 0xcfa04bff, 0xcd9e4cff, 0xca9a48ff, 0xc99846ff, 0xc89743ff, 0xc28d35ff, 0xc5903aff, 0xc7933eff, 0xc7933cff, 0xca9b46ff, 0xcfa250ff, 0xd7ab56ff, 0xe9c26aff, 0xf3d688ff, 0xf7dc8eff, 0xf9e091ff, 0xfae297ff, 0xf7df97ff, 0xf6dc99ff, 0xf1d590ff, 0xeed292ff, 0xd8ae6dff, 0xb07826ff, 0xb07a1eff, 0xcea145ff, 0xd6ae5aff, 0xd5ab53ff, 0xd7ae56ff, 0xd4ab4dff, 0xd4aa4aff, 0xd5ab49ff, 0xd5aa4aff, 0xd4aa4fff, 0xd4aa4eff, 0xd4a948ff, 0xd3a847ff, 0xd0a540ff, 0xcea23aff, 0xcda139ff, 0xcda137ff, 0xc89929ff, 0xc99b2fff, 0xc99b31ff, 0xcfa443ff, 0xe3c67bff, 0xe5c87eff, 0xe9d193ff, 0xeddaa3ff, 0xf1dda8ff, 0xe6c580ff, 0xdbb25dff, 0xdeb65eff, 0xe4be6bff, 0xc78d25ff, 0xc88d2fff, 0xcd9743ff, 0xc1852aff, 0xbd7e1dff, 0xbf8127ff, 0xbb7b1cff, 0xaf6806ff, 0xb36d07ff, 0xb97a18ff, 0xbf8325ff, 0xc79142ff, 0xc59343ff, 0xad7119ff, 0x864404ff, 0x5b1d02ff, 0x37170cff, 0x636360ff, 0xa7a8a5ff, 0xdbdcdaff, 0xc7c9c6ff, 0x999b97ff, 0x8d8f8aff, 0x898b87ff, 0x868885ff, 0x8c8e8aff, 0x9ea09bff, 0xc7c8c5ff, 0xe1e2e0ff, 0xe1e2e0ff, 0xe3e4e2ff, 0xc7c8c7ff, 0x5d5c58ff, 0x39210cff, 0x552101ff, 0x6b2d03ff, 0x7c3503ff, 0x8b3e04ff, 0x944504ff, 0x964304ff, 0x9c4a04ff, 0x9f5004ff, 0x9f5204ff, 0x9d5004ff, 0x9d5104ff, 0xa05404ff, 0xa15504ff, 0xa05304ff, 0xa15404ff, 0xac650dff, 0xcf9739ff, 0xdca84aff, 0xdba338ff, 0xe6b85eff, 0xe8bd63ff, 0xeac26bff, 0xedc46aff, 0xf0d083ff, 0xf3d89cff, 0xf3d99dff, 0xf0d595ff, 0xeccd82ff, 0xeecf89ff, 0xebca81ff, 0xeccb84ff, 0xeece8dff, 0xefcf8bff, 0xecc774ff, 0xe9bf66ff, 0xeabe5fff, 0xeec979ff, 0xeabe63ff, 0xd8aa4fff, 0xcd9d44ff, 0xce9d47ff, 0xce9c49ff, 0xca9743ff, 0xc79139ff, 0xc58f35ff, 0xc58d34ff, 0xc28b32ff, 0xc28c36ff, 0xc18a30ff, 0xc58f37ff, 0xc99742ff, 0xce9f4eff, 0xcb9c4aff, 0xcc9a40ff, 0xd9af5fff, 0xe1bb70ff, 0xe3bc6cff, 0xddb566ff, 0xd3a34fff, 0xd3a454ff, 0xc7913eff, 0xbc822bff, 0xb06b18ff, 0x9d5d05ff, 0xa46d0bff, 0xc99b3cff, 0xd5ac56ff, 0xd5ac56ff, 0xd7b05dff, 0xd8af59ff, 0xd4ab4eff, 0xd5ad52ff, 0xd7ae57ff, 0xd4ab4fff, 0xd4ab51ff, 0xd4aa4cff, 0xd4a94aff, 0xd2a848ff, 0xd0a542ff, 0xd0a543ff, 0xcea33cff, 0xca9d2fff, 0xc99d32ff, 0xc79a2fff, 0xc99d35ff, 0xd4ac52ff, 0xe2c57bff, 0xe9d197ff, 0xebd39eff, 0xeed79fff, 0xe5c580ff, 0xd8af56ff, 0xdeb760ff, 0xe4be6fff, 0xc98d29ff, 0xc88b2aff, 0xc78c2eff, 0xbb7b19ff, 0xb6710aff, 0xb46e09ff, 0xaf6603ff, 0xad6303ff, 0xb06804ff, 0xb87513ff, 0xc08328ff, 0xc89345ff, 0xc48d40ff, 0xb1711bff, 0x7f3502ff, 0x5c1702ff, 0x481101ff, 0x36211bff, 0x737471ff, 0xb3b5b2ff, 0xdedfddff, 0xd8d9d7ff, 0xbfc0bdff, 0xafb1adff, 0xb0b1aeff, 0xc0c1beff, 0xd8d9d7ff, 0xe2e3e1ff, 0xe1e2e0ff, 0xe2e3e1ff, 0xc8c9c7ff, 0x63635fff, 0x4a3b23ff, 0x73501bff, 0x713709ff, 0x793203ff, 0x873b04ff, 0x8d3e04ff, 0x944504ff, 0x984a04ff, 0x994a04ff, 0x9b4e04ff, 0xa15405ff, 0xa05304ff, 0x9b5004ff, 0x9f5404ff, 0x9f5404ff, 0xa25504ff, 0xa05504ff, 0xa65c05ff, 0xce9130ff, 0xd99f3aff, 0xd79b28ff, 0xe3b359ff, 0xe5b963ff, 0xe9c26fff, 0xecc97aff, 0xf0d28eff, 0xf3d99eff, 0xf5dea7ff, 0xf3dba2ff, 0xf2d799ff, 0xf0d598ff, 0xeccc8aff, 0xeccd89ff, 0xedce8dff, 0xebca7eff, 0xeac573ff, 0xecc777ff, 0xedc770ff, 0xefcd81ff, 0xefcc7bff, 0xebc776ff, 0xd5a54dff, 0xc9943dff, 0xc8933bff, 0xc69136ff, 0xc58d2fff, 0xc68e33ff, 0xbf8529ff, 0xbd8326ff, 0xbe852aff, 0xbe852bff, 0xc18c35ff, 0xc69341ff, 0xc5913bff, 0xc18a2bff, 0xbb8227ff, 0xbc8740ff, 0xac681eff, 0xa7610bff, 0xa76309ff, 0xac6808ff, 0xad6a0bff, 0xaf6d0dff, 0xac6b0fff, 0x9d5803ff, 0x985c04ff, 0xa46c0aff, 0xc59437ff, 0xd2aa57ff, 0xd4ac59ff, 0xd4ad59ff, 0xd4ac55ff, 0xd4ac54ff, 0xd3ab51ff, 0xd5ae54ff, 0xd5ad52ff, 0xd5ac50ff, 0xd4ab4bff, 0xd2a846ff, 0xd1a745ff, 0xcfa541ff, 0xd1a644ff, 0xd0a642ff, 0xcea33dff, 0xcda23bff, 0xcca13cff, 0xcda23fff, 0xcb9f3aff, 0xd1a94cff, 0xdebd72ff, 0xe2c27fff, 0xe6c785ff, 0xdeb86aff, 0xdab45fff, 0xdeb863ff, 0xdfb761ff, 0xc98d2aff, 0xc48622ff, 0xbe7b18ff, 0xb26908ff, 0xad5e05ff, 0xb77119ff, 0xc18431ff, 0xc89343ff, 0xc79445ff, 0xcfa157ff, 0xd4a75eff, 0xd9af66ff, 0xd1a560ff, 0xbd8639ff, 0x9c5f26ff, 0x702c0bff, 0x571c05ff, 0x3f0f01ff, 0x35231dff, 0x6e6f6cff, 0xb1b2b0ff, 0xdbdcdaff, 0xe3e4e2ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xe3e4e2ff, 0xe2e3e1ff, 0xe2e3e1ff, 0xdedfddff, 0xbabbb9ff, 0x61605bff, 0x49391eff, 0x7c5e32ff, 0x997331ff, 0xa16e25ff, 0x8a4109ff, 0x8b3c02ff, 0x8f4104ff, 0x924404ff, 0x984b04ff, 0x984b04ff, 0x984c04ff, 0x9c5204ff, 0x9a5004ff, 0x9d5204ff, 0x9d5404ff, 0x9f5604ff, 0xa15704ff, 0xa15604ff, 0xa45905ff, 0xcd8f2dff, 0xd79c30ff, 0xd59a28ff, 0xd9a138ff, 0xdfae4fff, 0xe7bc63ff, 0xebca7dff, 0xf4daa1ff, 0xf5dca5ff, 0xf4dda8ff, 0xf3dba2ff, 0xf6e0abff, 0xf1d697ff, 0xf0d395ff, 0xedce8fff, 0xebcc89ff, 0xebca84ff, 0xe9c77eff, 0xeac87cff, 0xebc97fff, 0xecca7cff, 0xecca7bff, 0xf1d490ff, 0xebc880ff, 0xd9ac54ff, 0xc99235ff, 0xbf8523ff, 0xc18726ff, 0xc0862aff, 0xb77b19ff, 0xb67a17ff, 0xb87d1cff, 0xb97e1eff, 0xbd8225ff, 0xc0882dff, 0xbd8424ff, 0xb87f1dff, 0x9b580fff, 0x92480bff, 0x934503ff, 0x964c03ff, 0x954d04ff, 0x985304ff, 0x9b5504ff, 0x985504ff, 0x955805ff, 0x955b04ff, 0x955e05ff, 0x9f6606ff, 0xc18f30ff, 0xd4ac5cff, 0xd5ae5dff, 0xd3ac58ff, 0xd4ad59ff, 0xd4ae59ff, 0xd4ad53ff, 0xd5ae56ff, 0xd6ae59ff, 0xd5ae58ff, 0xd3ab4cff, 0xd3a947ff, 0xd3aa49ff, 0xd3aa49ff, 0xd1a845ff, 0xd1a847ff, 0xcfa441ff, 0xd0a443ff, 0xd1a84bff, 0xcfa749ff, 0xcea443ff, 0xcfa548ff, 0xd2ab50ff, 0xdbb766ff, 0xdcb668ff, 0xdab563ff, 0xdcb96bff, 0xdeb966ff, 0xdeb861ff, 0xc2821cff, 0xbd7712ff, 0xb46806ff, 0xb36913ff, 0xc08134ff, 0xe3bb7eff, 0xe9c995ff, 0xebd0a0ff, 0xe9cd9dff, 0xecd1a6ff, 0xeacfa1ff, 0xe5c692ff, 0xe2c18cff, 0xdfbc84ff, 0xdab87eff, 0xc19a62ff, 0xa17942ff, 0x602c0bff, 0x451a06ff, 0x453825ff, 0x565651ff, 0x8f918fff, 0xbebfbdff, 0xd4d5d3ff, 0xdbdcdaff, 0xdbdcdaff, 0xd5d6d3ff, 0xc0c1bfff, 0x8d8f8dff, 0x4a463fff, 0x47310fff, 0x745016ff, 0x986f32ff, 0xb48946ff, 0xc69949ff, 0xbf8c42ff, 0x9d5815ff, 0x934502ff, 0x954804ff, 0x974a04ff, 0x974b04ff, 0x974d04ff, 0x994f04ff, 0x9a4f04ff, 0x9d5404ff, 0x9f5604ff, 0x9e5405ff, 0xa35805ff, 0xa35805ff, 0xa15504ff, 0xc58722ff, 0xd19625ff, 0xd79f32ff, 0xdda846ff, 0xe4b65fff, 0xebc77eff, 0xeccb86ff, 0xefd190ff, 0xf3daa5ff, 0xf3ddafff, 0xf2dba7ff, 0xf5e0b0ff, 0xf2d99dff, 0xf3dba5ff, 0xf1d79fff, 0xeed091ff, 0xeaca88ff, 0xe8c77fff, 0xebcb84ff, 0xebcc87ff, 0xebc880ff, 0xe9c577ff, 0xefd192ff, 0xf0d299ff, 0xeeca7eff, 0xe5ba63ff, 0xc68e2cff, 0xb87a16ff, 0xb67917ff, 0xb2750fff, 0xb3740fff, 0xb47512ff, 0xb2720fff, 0xad6b0aff, 0xac690cff, 0xaf6e0eff, 0xa46209ff, 0x874503ff, 0x914d04ff, 0x9e5404ff, 0x985004ff, 0x914b04ff, 0x904a04ff, 0x8d4d04ff, 0x8c5204ff, 0x8d5604ff, 0x905904ff, 0x925c04ff, 0x986005ff, 0xba8626ff, 0xd3aa55ff, 0xd4b05fff, 0xd4b062ff, 0xd5b064ff, 0xd5af60ff, 0xd5b05dff, 0xd6b160ff, 0xd6b15eff, 0xd7af5dff, 0xd5ad53ff, 0xd5ad52ff, 0xd5ad57ff, 0xd6b05dff, 0xd5ae57ff, 0xd1a84bff, 0xd0a645ff, 0xd0a74aff, 0xcfa64bff, 0xcfa74eff, 0xd0a750ff, 0xcfa84eff, 0xd1aa52ff, 0xd4ac54ff, 0xd4ad53ff, 0xd8b360ff, 0xdbb86dff, 0xddbc74ff, 0xdfbb6eff, 0xb86f09ff, 0xb86e0bff, 0xcc903aff, 0xe4bd79ff, 0xebcc96ff, 0xedd1a2ff, 0xedd3a8ff, 0xeed6b0ff, 0xeed3a7ff, 0xedd3a6ff, 0xebcf9fff, 0xeacb99ff, 0xecd4abff, 0xe8cd9dff, 0xe3c38eff, 0xdec08dff, 0xccab73ff, 0x9b7238ff, 0x6c4118ff, 0x735425ff, 0x564220ff, 0x3f3523ff, 0x4c4941ff, 0x656563ff, 0x767775ff, 0x767775ff, 0x656562ff, 0x49443eff, 0x392a16ff, 0x4c2b03ff, 0x6f4204ff, 0x8e5d14ff, 0xa87525ff, 0xc08d3eff, 0xce9c4bff, 0xd8ab5fff, 0xcb9a48ff, 0xaa6a1cff, 0x954a03ff, 0x994d03ff, 0x974d04ff, 0x974d04ff, 0x9b5104ff, 0x9d5304ff, 0x9f5304ff, 0x9e5304ff, 0x9c5104ff, 0xa15505ff, 0xa05503ff, 0xa65e0bff, 0xc68c2dff, 0xd39e37ff, 0xdaa53bff, 0xe1b154ff, 0xe8c174ff, 0xecca8eff, 0xefce98ff, 0xedcd90ff, 0xefd49bff, 0xf2daaaff, 0xf3dca9ff, 0xf4deaeff, 0xf0d496ff, 0xf4ddabff, 0xf4dbaaff, 0xf0d39aff, 0xe9c886ff, 0xe7c47aff, 0xe8c577ff, 0xeac985ff, 0xeccc8bff, 0xe8c57cff, 0xeac67eff, 0xefd198ff, 0xecca85ff, 0xedc671ff, 0xe5bb64ff, 0xc99237ff, 0xb2720eff, 0xad6a06ff, 0xad6a08ff, 0xaf6e09ff, 0xad6804ff, 0xab6504ff, 0xa45c04ff, 0xa15a04ff, 0x8d4b04ff, 0x955305ff, 0xab6705ff, 0xaa6405ff, 0xa25904ff, 0x9c5204ff, 0x9a5004ff, 0x964e04ff, 0x8e4e04ff, 0x8b5304ff, 0x8d5604ff, 0x905904ff, 0x925b03ff, 0xaa7110ff, 0xcb9e47ff, 0xd6b56eff, 0xd6b36cff, 0xd6b36dff, 0xd5b168ff, 0xd5b167ff, 0xd5af67ff, 0xd4ad5fff, 0xd7b260ff, 0xd7b260ff, 0xd6b360ff, 0xd6b262ff, 0xd6b05fff, 0xd5af5cff, 0xd3ad55ff, 0xd1a94eff, 0xd0a749ff, 0xd1a954ff, 0xcfa853ff, 0xcea64bff, 0xcfa74bff, 0xd1ab56ff, 0xd3ad56ff, 0xd5af58ff, 0xd9b667ff, 0xd9b870ff, 0xd8b66fff, 0xdcb972ff, 0xce9339ff, 0xdaa859ff, 0xe9c382ff, 0xefd09aff, 0xeccb99ff, 0xeac796ff, 0xe9c897ff, 0xeccca0ff, 0xebcd9fff, 0xeacf9fff, 0xebd0a1ff, 0xedd3abff, 0xecd8b5ff, 0xead3aaff, 0xe7ca98ff, 0xe4c48fff, 0xddbf8aff, 0xcdac74ff, 0xb49053ff, 0x926a30ff, 0x80581aff, 0x6b480dff, 0x523508ff, 0x361905ff, 0x2f1309ff, 0x321709ff, 0x391905ff, 0x4a2401ff, 0x5f3202ff, 0x733d03ff, 0x874b03ff, 0xa0630fff, 0xb47922ff, 0xbc8223ff, 0xc58a2aff, 0xd3a251ff, 0xd6a759ff, 0xd4a557ff, 0xaf7129ff, 0x9d540aff, 0x994e03ff, 0x9a4f04ff, 0x9d5304ff, 0x9e5404ff, 0x9f5304ff, 0x9d5104ff, 0x9d5104ff, 0x9e5304ff, 0xa76210ff, 0xcf9c50ff, 0xe0b66cff, 0xdbad54ff, 0xdcac4cff, 0xe4b963ff, 0xe9c379ff, 0xe9c389ff, 0xedcc9aff, 0xf1d39fff, 0xedce94ff, 0xeed196ff, 0xf1d6a0ff, 0xf2d8a2ff, 0xf0d396ff, 0xf2d9a7ff, 0xf2dbaaff, 0xf2d9a5ff, 0xeccc8dff, 0xe7c47aff, 0xe9c67aff, 0xeac884ff, 0xebc987ff, 0xe9c47bff, 0xe7be69ff, 0xe8c06cff, 0xe8be66ff, 0xe7bc57ff, 0xedc875ff, 0xe8c47cff, 0xb97d1fff, 0xab6403ff, 0xaa6205ff, 0xab6305ff, 0xaa6104ff, 0xab6304ff, 0xa45c04ff, 0x9c5204ff, 0x965304ff, 0xb0710cff, 0xb57811ff, 0xac6805ff, 0xa75e05ff, 0xa15805ff, 0xa05704ff, 0x9d5404ff, 0x984e04ff, 0x8f5104ff, 0x8c5404ff, 0x8f5704ff, 0x905903ff, 0x9d6207ff, 0xc28e36ff, 0xd3ae67ff, 0xd6b573ff, 0xd7b571ff, 0xd5b26aff, 0xd5b36eff, 0xd4af6aff, 0xd4ae64ff, 0xd7b367ff, 0xd7b264ff, 0xd5af5eff, 0xd5b05dff, 0xd4af5eff, 0xd5ae5dff, 0xd3ae5dff, 0xd1a954ff, 0xcea54aff, 0xcfa954ff, 0xcea753ff, 0xcea74eff, 0xd1aa55ff, 0xd0aa57ff, 0xd1aa55ff, 0xd3ad59ff, 0xd8b56cff, 0xd8b671ff, 0xd7b46dff, 0xd9b56cff, 0xeecc91ff, 0xefd096ff, 0xeece95ff, 0xeccc95ff, 0xedcc99ff, 0xf0d1a5ff, 0xeecf9fff, 0xeed2a6ff, 0xeed3a7ff, 0xedd0a2ff, 0xebcd9dff, 0xecd0a2ff, 0xecd2a9ff, 0xeacfa0ff, 0xebcf9fff, 0xe9cb99ff, 0xe6c896ff, 0xdaba83ff, 0xcaa668ff, 0xa5783fff, 0x986520ff, 0x8e5a0cff, 0x82540dff, 0x6b3a04ff, 0x511802ff, 0x551a02ff, 0x602502ff, 0x6e3303ff, 0x7c3f03ff, 0x894604ff, 0x985304ff, 0xaa690cff, 0xb57517ff, 0xbc7b1aff, 0xbd7e17ff, 0xc99035ff, 0xdbae63ff, 0xe1b874ff, 0xd2a252ff, 0xbb7f28ff, 0xaa6813ff, 0xa45b0aff, 0xa15707ff, 0x9a4f04ff, 0x9e5304ff, 0xa35805ff, 0xa25704ff, 0xa96414ff, 0xd5a75fff, 0xe8c68eff, 0xe5be7aff, 0xdbab4eff, 0xe1b663ff, 0xe8c486ff, 0xe9c684ff, 0xe9c78dff, 0xefcf9eff, 0xf0d3a3ff, 0xeecf98ff, 0xeccb90ff, 0xf0d398ff, 0xf1d39aff, 0xf2d8a4ff, 0xf2ddb0ff, 0xf1dcadff, 0xf1d9a8ff, 0xf1d6a4ff, 0xedcf95ff, 0xebc988ff, 0xedcd90ff, 0xeccd8fff, 0xeed195ff, 0xedcb89ff, 0xecc881ff, 0xeac577ff, 0xe7be65ff, 0xebc471ff, 0xeece88ff, 0xd8ac58ff, 0xab630eff, 0xa55603ff, 0xa75b05ff, 0xa65905ff, 0xa65a04ff, 0xa55c05ff, 0xa66107ff, 0xb16f08ff, 0xba7c13ff, 0xb57610ff, 0xaa6305ff, 0xa85e04ff, 0xa45b04ff, 0x9f5504ff, 0x9d5304ff, 0x9f5404ff, 0x975004ff, 0x8b5204ff, 0x8e5604ff, 0x905804ff, 0x935a03ff, 0xad751cff, 0xc69b48ff, 0xd3b06cff, 0xd8b87bff, 0xd6b474ff, 0xd7b473ff, 0xd5b271ff, 0xd4b16cff, 0xd6b165ff, 0xd6b060ff, 0xd4ae5bff, 0xd3af5aff, 0xd4af5fff, 0xd3af60ff, 0xd2ac5bff, 0xd0a851ff, 0xcea64dff, 0xcfa751ff, 0xcca446ff, 0xcfa64cff, 0xd2ad5bff, 0xd1aa58ff, 0xcba249ff, 0xcda54bff, 0xd2ad5aff, 0xd5b36dff, 0xd5b46eff, 0xd8b56dff, 0xeac788ff, 0xefd097ff, 0xefd29fff, 0xf1d5a4ff, 0xf1d8adff, 0xf1d8b0ff, 0xf0d6a9ff, 0xf0d4abff, 0xf0d6acff, 0xefd3a9ff, 0xedd1a6ff, 0xecce9dff, 0xefd3a5ff, 0xecd0a0ff, 0xedd3a4ff, 0xedd4a9ff, 0xeacf9dff, 0xe4c58cff, 0xe2c48fff, 0xdcbf90ff, 0xd2b284ff, 0xb68b49ff, 0x9b6411ff, 0x8e5106ff, 0x732f03ff, 0x691e03ff, 0x752a03ff, 0x823704ff, 0x8d4504ff, 0x974c05ff, 0xa45904ff, 0xaf6b0bff, 0xb77517ff, 0xb97712ff, 0xbc7b16ff, 0xc07e1dff, 0xca8e33ff, 0xd29946ff, 0xcf9433ff, 0xcc922bff, 0xcd9637ff, 0xc78c30ff, 0xbd8231ff, 0xac660aff, 0xa85e06ff, 0xa55a04ff, 0xa55d06ff, 0xc79345ff, 0xe7c38aff, 0xedcc9bff, 0xe8c489ff, 0xdcac50ff, 0xe3ba6eff, 0xeac791ff, 0xecca96ff, 0xecc995ff, 0xeecd9cff, 0xedcb99ff, 0xeac792ff, 0xebc994ff, 0xefd098ff, 0xeece96ff, 0xefd29eff, 0xf0d7a8ff, 0xf2d9abff, 0xf0d19bff, 0xf1d4a3ff, 0xeed19aff, 0xeaca8aff, 0xebcb8fff, 0xebcb8dff, 0xeecf95ff, 0xefd19cff, 0xeecf97ff, 0xedcd94ff, 0xeac780ff, 0xe8c068ff, 0xeac46eff, 0xebc879ff, 0xd6a95cff, 0xae6a1fff, 0x9e4e03ff, 0x9e4e04ff, 0xa25506ff, 0xb37112ff, 0xbf841cff, 0xbe821bff, 0xb97b16ff, 0xae6806ff, 0xa96204ff, 0xa65d05ff, 0xa35804ff, 0x9e5004ff, 0x9a4d04ff, 0x9d5104ff, 0x9e5204ff, 0x914b04ff, 0x8c5304ff, 0x8f5704ff, 0x915903ff, 0xa16711ff, 0xc19545ff, 0xceab61ff, 0xd6b478ff, 0xd6b377ff, 0xd4b270ff, 0xd4b16fff, 0xd5b069ff, 0xd3ae5fff, 0xd4af60ff, 0xd5b063ff, 0xd4b061ff, 0xd4b167ff, 0xd3af62ff, 0xd1a959ff, 0xd0a753ff, 0xd0a956ff, 0xd1ac5bff, 0xd2ab58ff, 0xd3ac58ff, 0xd2ac5aff, 0xd1ab59ff, 0xcda44cff, 0xcea651ff, 0xd0aa59ff, 0xd3af62ff, 0xd5b167ff, 0xd8b468ff, 0xebc98bff, 0xf0d19eff, 0xefd4a8ff, 0xf1d4a7ff, 0xf2d8afff, 0xf1d9b0ff, 0xf0d4a6ff, 0xf1d6a6ff, 0xf2daafff, 0xefd3a3ff, 0xf0d3a5ff, 0xf0d2a0ff, 0xefd19fff, 0xefd5a8ff, 0xefd8b1ff, 0xeed4adff, 0xeed3a6ff, 0xedd3a3ff, 0xecd3a5ff, 0xe8d0a4ff, 0xe6cfa6ff, 0xdec396ff, 0xc9a15fff, 0xa26829ff, 0x772903ff, 0x772603ff, 0x853204ff, 0x8b3a04ff, 0x944304ff, 0x9c4b04ff, 0xa45305ff, 0xab6105ff, 0xb16b0bff, 0xb6700aff, 0xbc7a16ff, 0xbb750cff, 0xbc740aff, 0xb76509ff, 0xbb6a13ff, 0xc98a27ff, 0xcd9431ff, 0xca902fff, 0xc38424ff, 0xbc7810ff, 0xb6710bff, 0xa75e03ff, 0xb97926ff, 0xe2bc7cff, 0xedce9fff, 0xeecfa0ff, 0xe9c791ff, 0xdcac55ff, 0xdeae59ff, 0xe7c082ff, 0xe9c58eff, 0xeac892ff, 0xeccc99ff, 0xeccc9dff, 0xedcd9dff, 0xeccc9bff, 0xeecf9eff, 0xeccd9cff, 0xebce9bff, 0xedd09eff, 0xf0d4a5ff, 0xefd098ff, 0xf0d29eff, 0xefd4a2ff, 0xeccb91ff, 0xeccc91ff, 0xeac98aff, 0xeac98aff, 0xedcd96ff, 0xeccc90ff, 0xe9c88dff, 0xeac88eff, 0xe8c784ff, 0xeac887ff, 0xedce8eff, 0xf1d497ff, 0xe6c685ff, 0xb2722eff, 0xa25609ff, 0xbf8224ff, 0xcb9633ff, 0xc89031ff, 0xbb7c16ff, 0xb06906ff, 0xac6404ff, 0xa86204ff, 0xa35904ff, 0xa05204ff, 0xa05504ff, 0x9c4f04ff, 0x9c4e04ff, 0x9d4f04ff, 0x994d04ff, 0x904f04ff, 0x8e5504ff, 0x915803ff, 0xac7420ff, 0xcca55eff, 0xcea95aff, 0xcea959ff, 0xd1ad65ff, 0xd1ad65ff, 0xd0a958ff, 0xd1aa5bff, 0xd3af63ff, 0xd4b26aff, 0xd4b064ff, 0xd2ad5bff, 0xd3af63ff, 0xd3af62ff, 0xd1ab56ff, 0xd3ab56ff, 0xd0a955ff, 0xd1a955ff, 0xd3ac5bff, 0xd2ad5dff, 0xd4af61ff, 0xd3af64ff, 0xd1ab58ff, 0xd1ab5bff, 0xd3ad62ff, 0xd4b065ff, 0xd5b167ff, 0xd6b168ff, 0xefd097ff, 0xeed0a1ff, 0xecc896ff, 0xeac48bff, 0xefcfa0ff, 0xf2d8abff, 0xf2d5a3ff, 0xf4ddb5ff, 0xf3dab2ff, 0xf2daaeff, 0xf4ddb0ff, 0xf3daa9ff, 0xf1d8acff, 0xf0d5a5ff, 0xf0d6a8ff, 0xefd4a6ff, 0xefd5a7ff, 0xf1dbb2ff, 0xefd6abff, 0xeed5abff, 0xedd8b0ff, 0xebd4a9ff, 0xe5c692ff, 0xd3ac6bff, 0xb68242ff, 0x8e4514ff, 0x863103ff, 0x8d3804ff, 0x964504ff, 0x9b4604ff, 0xa04e05ff, 0xa85a04ff, 0xae6304ff, 0xb36a05ff, 0xb9730eff, 0xba720bff, 0xba7006ff, 0xba6c06ff, 0xb35a04ff, 0xaa4c06ff, 0xbe6f12ff, 0xc6831dff, 0xc27f16ff, 0xbd7b11ff, 0xb67105ff, 0xab6308ff, 0xd19d50ff, 0xebca9bff, 0xefd0a8ff, 0xedcd9bff, 0xe3bd7aff, 0xd8a74cff, 0xdbad5aff, 0xe0b66bff, 0xe6c180ff, 0xeac791ff, 0xeac897ff, 0xeac997ff, 0xeac997ff, 0xebcb9aff, 0xedce9eff, 0xeccc9aff, 0xeccb97ff, 0xebcb96ff, 0xedcc99ff, 0xedce97ff, 0xefd099ff, 0xeecf9bff, 0xebc98eff, 0xe8c582ff, 0xe4c07fff, 0xe3c080ff, 0xe7c684ff, 0xe8c688ff, 0xebcb93ff, 0xebca94ff, 0xe6c381ff, 0xe9c789ff, 0xedcd93ff, 0xedcc8eff, 0xf2d69eff, 0xe5c185ff, 0xcd9741ff, 0xd39c3eff, 0xce993fff, 0xbe7f21ff, 0xb16906ff, 0xad6204ff, 0xac6204ff, 0xa85e05ff, 0xa25504ff, 0x9e5204ff, 0xa15604ff, 0xa15705ff, 0x9d5004ff, 0x9b4d04ff, 0x9e5504ff, 0xa36005ff, 0xa26204ff, 0xa56504ff, 0xba8428ff, 0xcfa95fff, 0xcda65bff, 0xd3af68ff, 0xd1ad5dff, 0xd2ac60ff, 0xd3ae65ff, 0xd3ac5eff, 0xd5b064ff, 0xd5b26bff, 0xd4b065ff, 0xd2ac58ff, 0xd1a952ff, 0xd2ab53ff, 0xd3ab50ff, 0xd5ad56ff, 0xd4ad5aff, 0xd3ac58ff, 0xd5ae5eff, 0xd3ae5fff, 0xd4af62ff, 0xd3ad61ff, 0xd1ad60ff, 0xd2ac5cff, 0xd4ae64ff, 0xd5b16bff, 0xd5b26cff, 0xd6b067ff, 0xf0d29eff, 0xf1d4a7ff, 0xedcd9bff, 0xeecb97ff, 0xf2d8aeff, 0xf1d7a9ff, 0xf2d8a7ff, 0xf3dcb3ff, 0xf1d59fff, 0xf0d28eff, 0xf3d89aff, 0xf3d9a2ff, 0xf0d49cff, 0xf3dbaaff, 0xefd299ff, 0xefd196ff, 0xf1daaeff, 0xf1dab4ff, 0xf0dab0ff, 0xf0d9adff, 0xf2deb9ff, 0xeed9afff, 0xebcf9dff, 0xe4c388ff, 0xe5c283ff, 0xbe8e52ff, 0x853406ff, 0x8b3402ff, 0x923b04ff, 0x984404ff, 0xa14f04ff, 0xaa5e05ff, 0xad6105ff, 0xb26705ff, 0xb86f07ff, 0xbb740bff, 0xba7006ff, 0xbc7207ff, 0xbb6b06ff, 0xb76304ff, 0xba6b04ff, 0xbf7507ff, 0xc17e11ff, 0xc08217ff, 0xba7608ff, 0xc3852bff, 0xe0b677ff, 0xeccca2ff, 0xeccea2ff, 0xe8c68dff, 0xe0b86dff, 0xd19e42ff, 0xd3a049ff, 0xd7a751ff, 0xdcae5aff, 0xe5c07fff, 0xe8c48bff, 0xe7c58dff, 0xe7c38eff, 0xe8c48fff, 0xe9c592ff, 0xe9c690ff, 0xeac892ff, 0xe8c58fff, 0xe9c790ff, 0xeac792ff, 0xe9c78aff, 0xeac88eff, 0xe6c17cff, 0xe5bb6dff, 0xe4bd76ff, 0xe5c181ff, 0xe6c27fff, 0xe7c485ff, 0xe9c68dff, 0xe7c489ff, 0xe6c481ff, 0xe9c684ff, 0xe7c380ff, 0xe7c178ff, 0xe9c377ff, 0xe7c282ff, 0xd6a24fff, 0xc98a2bff, 0xbe7a1cff, 0xb16707ff, 0xae6104ff, 0xab5c05ff, 0xa95905ff, 0xa55404ff, 0xa25504ff, 0xa05104ff, 0xa75804ff, 0xab6105ff, 0xad6805ff, 0xae6907ff, 0xb26f0aff, 0xb67a10ff, 0xb87a0fff, 0xba7c0fff, 0xbe8722ff, 0xd1a95fff, 0xd3af6fff, 0xd6b377ff, 0xd6b472ff, 0xd3ae63ff, 0xd5b26cff, 0xd7b470ff, 0xd7b56eff, 0xd7b46eff, 0xd7b46eff, 0xd6b164ff, 0xd4ad55ff, 0xd6ae57ff, 0xd5af5aff, 0xd6b05cff, 0xd9b56bff, 0xd8b36bff, 0xd9b369ff, 0xd6b265ff, 0xd6b268ff, 0xd6b370ff, 0xd4b16aff, 0xd2ad60ff, 0xd3ac5eff, 0xd3ad63ff, 0xd4b36dff, 0xd5b167ff, 0xf0d39fff, 0xf0d3a6ff, 0xf1d3a7ff, 0xf2d4a6ff, 0xf2d6a9ff, 0xf2d8a8ff, 0xf2d5a0ff, 0xf2d6a3ff, 0xf2d59aff, 0xf0d38fff, 0xf2d394ff, 0xf3d69eff, 0xf2d497ff, 0xf3daa2ff, 0xebcc8aff, 0xeed093ff, 0xf0d7a7ff, 0xf0d5a3ff, 0xf0d7a2ff, 0xf0daabff, 0xeed8b0ff, 0xefdab2ff, 0xedd4a3ff, 0xe9ca91ff, 0xeac991ff, 0xe7c78fff, 0xba884bff, 0x94480dff, 0x913c03ff, 0x9a4604ff, 0x9f4f05ff, 0xa55805ff, 0xab5c05ff, 0xb16505ff, 0xb8720aff, 0xbb7911ff, 0xba730aff, 0xba6e05ff, 0xbe7507ff, 0xbf7406ff, 0xc07c0dff, 0xbf7c0cff, 0xc07e10ff, 0xc2831aff, 0xbe7c10ff, 0xce973fff, 0xe3bd82ff, 0xe6c38eff, 0xe5c085ff, 0xe7c389ff, 0xe5c083ff, 0xd5a651ff, 0xd19f44ff, 0xd8a85aff, 0xdeb26cff, 0xe0b66aff, 0xe8c388ff, 0xeac794ff, 0xe8c593ff, 0xe5c088ff, 0xe5c189ff, 0xe8c48fff, 0xe5c287ff, 0xe6c387ff, 0xe7c48eff, 0xe8c38dff, 0xe6c180ff, 0xe7c07bff, 0xe3b767ff, 0xdeb059ff, 0xdfb25dff, 0xe4bc74ff, 0xe4be7aff, 0xe6c383ff, 0xe7c589ff, 0xe5c07eff, 0xe9c789ff, 0xe8c786ff, 0xe5c17fff, 0xe4bb6dff, 0xe4bb69ff, 0xe4bc78ff, 0xd9aa5bff, 0xbc771aff, 0xb06103ff, 0xae5b04ff, 0xac5a04ff, 0xaa5604ff, 0xa65004ff, 0xaa5904ff, 0xa65404ff, 0xa34f06ff, 0xba7717ff, 0xbe8220ff, 0xbf841cff, 0xc38927ff, 0xc48a28ff, 0xc1851fff, 0xbe8014ff, 0xbc7e12ff, 0xbd8218ff, 0xcea355ff, 0xd5b276ff, 0xd7b47bff, 0xdab882ff, 0xd6b26eff, 0xd4af62ff, 0xd8b775ff, 0xd8b774ff, 0xd9b66fff, 0xd9b772ff, 0xd9b76fff, 0xd9b569ff, 0xd7b364ff, 0xd8b369ff, 0xd8b66fff, 0xd9b774ff, 0xd9b773ff, 0xd9b773ff, 0xdbba78ff, 0xdab874ff, 0xd6b672ff, 0xd6b470ff, 0xd5b065ff, 0xd3ae60ff, 0xd3ae62ff, 0xd4b067ff, 0xd7b26cff, 0xefd09dff, 0xf1d4a9ff, 0xf1d5a7ff, 0xf2d6a9ff, 0xf3dbb3ff, 0xf1d7a8ff, 0xebc782ff, 0xeecb8dff, 0xf1d199ff, 0xf2d69eff, 0xf3d9a5ff, 0xf4dcafff, 0xf2d5a0ff, 0xf2d49dff, 0xebca89ff, 0xeccb8bff, 0xedcc89ff, 0xebca81ff, 0xebca82ff, 0xf0d49cff, 0xf0daadff, 0xf0daaeff, 0xf1dcaeff, 0xefd7a3ff, 0xefd6a3ff, 0xeed6a5ff, 0xeacd92ff, 0xcca05bff, 0x9e570fff, 0x994702ff, 0x9f4d04ff, 0xa45704ff, 0xaa5c04ff, 0xb06604ff, 0xb7720bff, 0xb8730dff, 0xb56406ff, 0xba7106ff, 0xbd790bff, 0xbf7a0dff, 0xc28015ff, 0xc4851cff, 0xc58920ff, 0xc3881bff, 0xbf7f0fff, 0xcf9a43ff, 0xe3bd81ff, 0xe6c38aff, 0xe8c690ff, 0xe6c48fff, 0xe1bb80ff, 0xd5a651ff, 0xd5a54dff, 0xe1b97aff, 0xe3bd83ff, 0xe0b469ff, 0xe6c083ff, 0xe2ba7eff, 0xdcae6bff, 0xe1b77cff, 0xe7c38fff, 0xe7c48dff, 0xe5be84ff, 0xe4c086ff, 0xe6c28bff, 0xe7c389ff, 0xe6c383ff, 0xe5bd75ff, 0xe1b25eff, 0xe2b664ff, 0xe5bd73ff, 0xe7c17eff, 0xe4c17dff, 0xe9c68aff, 0xe9c68cff, 0xe6c282ff, 0xe6c381ff, 0xe9c68cff, 0xe9c68eff, 0xe3ba71ff, 0xe6c07fff, 0xe7c38bff, 0xe4bd78ff, 0xd49e49ff, 0xb76d0eff, 0xb16104ff, 0xb56807ff, 0xb87012ff, 0xb1620dff, 0xb86b08ff, 0xb86905ff, 0xb86c0eff, 0xca8c36ff, 0xcb9240ff, 0xc68d2fff, 0xc88e2fff, 0xc38725ff, 0xc4872cff, 0xba7a12ff, 0xb16d04ff, 0xb26f06ff, 0xc28d32ff, 0xd6b071ff, 0xd9b780ff, 0xdab884ff, 0xd6b170ff, 0xd3ab58ff, 0xd7b268ff, 0xdbb978ff, 0xdbb874ff, 0xdab874ff, 0xdbb875ff, 0xdaba77ff, 0xdab977ff, 0xd9b977ff, 0xdcbb80ff, 0xdcbb80ff, 0xd8b574ff, 0xd9b777ff, 0xdebe84ff, 0xddbd7fff, 0xd9b878ff, 0xd8b570ff, 0xd6b16aff, 0xd5b16cff, 0xd4af64ff, 0xd4ae63ff, 0xd6b16bff, 0xebc889ff, 0xf0d09cff, 0xf1d3a5ff, 0xf1d8adff, 0xf2d7acff, 0xefd298ff, 0xedc982ff, 0xf1d39aff, 0xf3d9a9ff, 0xf2d6a2ff, 0xf4dcb0ff, 0xf3deb6ff, 0xf2d9aaff, 0xf4daa8ff, 0xefd296ff, 0xedcc89ff, 0xedce89ff, 0xeed18dff, 0xedd395ff, 0xeccf8fff, 0xefd59fff, 0xf2dcb0ff, 0xf2deb4ff, 0xefd9abff, 0xf0d8a6ff, 0xefd9abff, 0xefd6a7ff, 0xebce9aff, 0xd1a868ff, 0xa8651fff, 0xa35503ff, 0xa55704ff, 0xa95805ff, 0xb06305ff, 0xb56e09ff, 0xb36306ff, 0xb66505ff, 0xbb770eff, 0xc07e1fff, 0xc38529ff, 0xc48827ff, 0xc78c29ff, 0xc78e27ff, 0xc78c23ff, 0xc68a22ff, 0xddb06dff, 0xe9c591ff, 0xeecd9fff, 0xe9c89aff, 0xdfb97eff, 0xd6a859ff, 0xd4a651ff, 0xd9ae64ff, 0xe0b97aff, 0xe1b977ff, 0xdfb468ff, 0xe5bf86ff, 0xddb474ff, 0xcc9433ff, 0xd4a452ff, 0xe6c18cff, 0xe7c38dff, 0xe7c18cff, 0xe7c28aff, 0xe7c488ff, 0xe7c48bff, 0xe9c68dff, 0xe3b970ff, 0xdead53ff, 0xdfb05aff, 0xe4ba6dff, 0xe6be75ff, 0xe6c07cff, 0xe6be77ff, 0xe5bc75ff, 0xe5be7cff, 0xe5c17fff, 0xe6c17fff, 0xe7c588ff, 0xe3bd79ff, 0xe5be7dff, 0xeac691ff, 0xe8c386ff, 0xe4ba6cff, 0xd0983bff, 0xbc7106ff, 0xc37c11ff, 0xca8a2bff, 0xc88620ff, 0xc88421ff, 0xc68322ff, 0xc58223ff, 0xcd9040ff, 0xc88a35ff, 0xc6862dff, 0xc28225ff, 0xbd7b17ff, 0xba7813ff, 0xb36e07ff, 0xb06904ff, 0xb26d03ff, 0xbc801aff, 0xd1a75aff, 0xd8b579ff, 0xd8b47aff, 0xd6b26dff, 0xd9b66fff, 0xd8b466ff, 0xddbc7bff, 0xe0bf87ff, 0xdbbc7cff, 0xdebf82ff, 0xdebe83ff, 0xdebe85ff, 0xddbd84ff, 0xdcbb82ff, 0xdbba7eff, 0xddbb81ff, 0xdbbb80ff, 0xe0c18cff, 0xdfc085ff, 0xdbba7cff, 0xd8b56fff, 0xd6b167ff, 0xd6b26cff, 0xd5b16aff, 0xd4ae66ff, 0xd6b068ff, 0xeac57cff, 0xecc989ff, 0xf0d5a4ff, 0xf0d9acff, 0xf0d29dff, 0xf2d396ff, 0xf3d699ff, 0xf5dba7ff, 0xf4ddafff, 0xf3daaaff, 0xf2d8a8ff, 0xf3dcb2ff, 0xf4ddb8ff, 0xf4dfb8ff, 0xf3dfb1ff, 0xf0d59aff, 0xf0d396ff, 0xf2daa6ff, 0xf1dbadff, 0xe8c782ff, 0xe8c778ff, 0xeed49dff, 0xefd5a3ff, 0xeed8a6ff, 0xeed6a4ff, 0xefd6a6ff, 0xedd4a8ff, 0xedd4a6ff, 0xeed5aaff, 0xdbb883ff, 0xb27322ff, 0xaa5f03ff, 0xac6305ff, 0xae6105ff, 0xb16305ff, 0xb15d04ff, 0xb9730bff, 0xc18428ff, 0xc78f40ff, 0xc89345ff, 0xc99342ff, 0xca933cff, 0xc78d26ff, 0xc68d20ff, 0xca9030ff, 0xe0b87eff, 0xeac997ff, 0xebcb9cff, 0xe8c697ff, 0xdab16dff, 0xd6aa5bff, 0xe0ba7eff, 0xe1bb7fff, 0xdeb979ff, 0xdeb874ff, 0xdfb66cff, 0xdeb46bff, 0xe1b97aff, 0xdaaf65ff, 0xddb36aff, 0xe4bf83ff, 0xe3bd80ff, 0xe3bc7cff, 0xe1b871ff, 0xdeb162ff, 0xe2b872ff, 0xe5bd7cff, 0xe1b362ff, 0xddad55ff, 0xdeb05bff, 0xe2b565ff, 0xe2b563ff, 0xe1b562ff, 0xdfb35bff, 0xe0b562ff, 0xe4ba70ff, 0xe6c080ff, 0xe5bf7eff, 0xe5c382ff, 0xe6c383ff, 0xe5c07cff, 0xe5c07eff, 0xe6bc73ff, 0xe6ba6bff, 0xddac55ff, 0xca8b26ff, 0xc5831cff, 0xc88523ff, 0xc6821bff, 0xc78627ff, 0xcb8f39ff, 0xc8882eff, 0xc7832eff, 0xca8a3bff, 0xc4832eff, 0xc07b20ff, 0xbb7715ff, 0xb7740cff, 0xb5740bff, 0xb56e05ff, 0xb67105ff, 0xb77711ff, 0xcda04eff, 0xd5ae67ff, 0xd5b06aff, 0xdbbb80ff, 0xe0c08bff, 0xdfbe85ff, 0xddbd7fff, 0xdebf86ff, 0xdfc087ff, 0xe0c290ff, 0xdfc28eff, 0xdebe86ff, 0xdcbc83ff, 0xdbba80ff, 0xdbba80ff, 0xdcba81ff, 0xdab880ff, 0xdcbc84ff, 0xdcbc7cff, 0xdbba77ff, 0xd7b46aff, 0xd7b366ff, 0xd5b26aff, 0xd5b16aff, 0xd6b270ff, 0xd6b26dff, 0xefd08aff, 0xeed093ff, 0xf0d6a3ff, 0xefd39bff, 0xf0d394ff, 0xf4d9a0ff, 0xf5dba5ff, 0xf7e2b3ff, 0xf4dba7ff, 0xf2d9a4ff, 0xf0d8a5ff, 0xf0d8a8ff, 0xf0d5abff, 0xf0d9abff, 0xf0daa9ff, 0xf0d9a2ff, 0xeed295ff, 0xf0d7a2ff, 0xf0daa9ff, 0xeacd8dff, 0xe6c675ff, 0xe9cd86ff, 0xf0daa7ff, 0xedd6a5ff, 0xebce98ff, 0xedd39fff, 0xefd6a5ff, 0xf0daafff, 0xeed7aeff, 0xeed3a2ff, 0xdfbb7bff, 0xba7e28ff, 0xae6907ff, 0xae6505ff, 0xaf6204ff, 0xb26604ff, 0xba7912ff, 0xc58b37ff, 0xca974fff, 0xca994fff, 0xcb9a4dff, 0xcb9644ff, 0xc78e2eff, 0xc58b20ff, 0xc88f2dff, 0xe1ba7dff, 0xebcc9cff, 0xe5c48fff, 0xdfb97eff, 0xd7ab5fff, 0xe0ba82ff, 0xe5c291ff, 0xe5c38dff, 0xe0bd7eff, 0xe2c083ff, 0xe3c186ff, 0xdcb56eff, 0xd8ac5cff, 0xddb369ff, 0xdbae5fff, 0xdfb56cff, 0xdfb56bff, 0xdbad57ff, 0xd9aa54ff, 0xdbad5cff, 0xdeb467ff, 0xe1b971ff, 0xdfb05dff, 0xdcac53ff, 0xdfb25cff, 0xe3b96bff, 0xe5bc73ff, 0xe4bb70ff, 0xe0b45fff, 0xe0b15aff, 0xdfaf54ff, 0xe4b967ff, 0xe6c07dff, 0xe8c68cff, 0xe7c484ff, 0xe6c17eff, 0xe5bf7aff, 0xe4b96cff, 0xe5bb70ff, 0xe5b96dff, 0xddaf5aff, 0xc98b2bff, 0xc68019ff, 0xc78524ff, 0xc8882fff, 0xc88929ff, 0xc5821fff, 0xc07813ff, 0xc47e25ff, 0xbb7215ff, 0xb76d09ff, 0xb9750fff, 0xbd8021ff, 0xbb7e1aff, 0xb67407ff, 0xb47005ff, 0xb07009ff, 0xc89b45ff, 0xd4af69ff, 0xd6b26cff, 0xddbc83ff, 0xe1c08eff, 0xe1c290ff, 0xe0c18dff, 0xdebe84ff, 0xe0c18bff, 0xe0c291ff, 0xdec18dff, 0xddbe85ff, 0xdebe86ff, 0xdcbf82ff, 0xdcbd82ff, 0xdbbb83ff, 0xdec08eff, 0xdec38eff, 0xdcbc7eff, 0xdbb978ff, 0xdab875ff, 0xd7b36aff, 0xd5b26bff, 0xd4b36fff, 0xd5b26dff, 0xd5b16dff, 0xf2d79eff, 0xefd396ff, 0xeecf8fff, 0xefd290ff, 0xf3d99fff, 0xf3dca7ff, 0xf4dda8ff, 0xf5dfaeff, 0xf2d9a3ff, 0xf2d59cff, 0xf0d7a2ff, 0xefd7a2ff, 0xf0d6a3ff, 0xeed6a0ff, 0xefd8a6ff, 0xeed7a0ff, 0xebce8cff, 0xeed49aff, 0xf0dbabff, 0xefd79dff, 0xebce82ff, 0xedd38cff, 0xf0dcabff, 0xecd6a6ff, 0xedd5a0ff, 0xecd29aff, 0xf0d9a8ff, 0xeedaadff, 0xebd2a1ff, 0xefd7a3ff, 0xefd8a4ff, 0xdebd7aff, 0xb57a1dff, 0xb26f0bff, 0xb5700aff, 0xb77309ff, 0xbb7d18ff, 0xc1862cff, 0xc79446ff, 0xca994eff, 0xcc9d51ff, 0xcb9847ff, 0xc89134ff, 0xc3891fff, 0xc58b21ff, 0xddb573ff, 0xe9ca9aff, 0xe3c188ff, 0xd9b16bff, 0xd7ad62ff, 0xdeba81ff, 0xe1bd86ff, 0xdeb975ff, 0xe1bf7fff, 0xe4c48cff, 0xe1bf82ff, 0xd8b165ff, 0xd0a346ff, 0xcfa042ff, 0xd0a044ff, 0xd8ab58ff, 0xddb366ff, 0xdaae5cff, 0xd8ab54ff, 0xdfb46aff, 0xe1b975ff, 0xe2bb77ff, 0xe1b76fff, 0xddae56ff, 0xe1b665ff, 0xe4bb71ff, 0xe3bb70ff, 0xe5bb6eff, 0xe1b561ff, 0xdfb25cff, 0xdeb156ff, 0xe5bd70ff, 0xe6c280ff, 0xe9c68eff, 0xe7c384ff, 0xe6c17cff, 0xe6be77ff, 0xe5bb6bff, 0xe5b96cff, 0xe3b666ff, 0xe1b462ff, 0xcc8e30ff, 0xc57f18ff, 0xc88520ff, 0xcd8f36ff, 0xc98b2bff, 0xcc943cff, 0xc98e34ff, 0xc58629ff, 0xbb740eff, 0xb46c04ff, 0xbc7b19ff, 0xc18528ff, 0xb97c15ff, 0xb37206ff, 0xb17208ff, 0xaf6f06ff, 0xc49135ff, 0xd5ad67ff, 0xd9b677ff, 0xdebe86ff, 0xe0c18fff, 0xe0c493ff, 0xe0c696ff, 0xe0c289ff, 0xe2c58fff, 0xe2c798ff, 0xdfc391ff, 0xddc086ff, 0xddbd85ff, 0xdbbd7eff, 0xdcbe80ff, 0xdebe87ff, 0xddbf89ff, 0xdfc18bff, 0xdcbc7eff, 0xdab876ff, 0xd9b876ff, 0xd7b56eff, 0xd7b571ff, 0xd7b675ff, 0xd3b06aff, 0xd3ac63ff, }; crispy-doom-crispy-doom-5.6.4/src/setup/sound.c000066400000000000000000000210351360717211000214600ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Sound control menu #include #include "SDL_mixer.h" #include "textscreen.h" #include "m_config.h" #include "m_misc.h" #include "execute.h" #include "mode.h" #include "sound.h" #define WINDOW_HELP_URL "https://www.chocolate-doom.org/setup-sound" typedef enum { OPLMODE_OPL2, OPLMODE_OPL3, NUM_OPLMODES, } oplmode_t; static const char *opltype_strings[] = { "OPL2", "OPL3" }; static const char *cfg_extension[] = { "cfg", NULL }; // Config file variables: int snd_sfxdevice = SNDDEVICE_SB; int snd_musicdevice = SNDDEVICE_SB; int snd_samplerate = 44100; int opl_io_port = 0x388; int snd_cachesize = 64 * 1024 * 1024; int snd_maxslicetime_ms = 28; char *snd_musiccmd = ""; int snd_pitchshift = 0; char *snd_dmxoption = "-opl3"; // [crispy] default to OPL3 emulation static int numChannels = 8; static int sfxVolume = 8; static int musicVolume = 8; static int voiceVolume = 15; static int show_talk = 0; // [crispy] values 3 and higher might reproduce DOOM.EXE more accurately, // but 1 is closer to "use_libsamplerate = 0" which is the default in Choco // and causes only a short delay at startup static int use_libsamplerate = 1; static float libsamplerate_scale = 0.65; static char *music_pack_path = NULL; static char *timidity_cfg_path = NULL; static char *gus_patch_path = NULL; static int gus_ram_kb = 1024; // DOS specific variables: these are unused but should be maintained // so that the config file can be shared between chocolate // doom and doom.exe static int snd_sbport = 0; static int snd_sbirq = 0; static int snd_sbdma = 0; static int snd_mport = 0; static int snd_oplmode; static void UpdateSndDevices(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data)) { switch (snd_oplmode) { default: case OPLMODE_OPL2: snd_dmxoption = ""; break; case OPLMODE_OPL3: snd_dmxoption = "-opl3"; break; } } static txt_dropdown_list_t *OPLTypeSelector(void) { txt_dropdown_list_t *result; if (snd_dmxoption != NULL && strstr(snd_dmxoption, "-opl3") != NULL) { snd_oplmode = OPLMODE_OPL3; } else { snd_oplmode = OPLMODE_OPL2; } result = TXT_NewDropdownList(&snd_oplmode, opltype_strings, 2); TXT_SignalConnect(result, "changed", UpdateSndDevices, NULL); return result; } static void OpenMusicPackDir(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) { if (!OpenFolder(music_pack_path)) { TXT_MessageBox("Error", "Failed to open music pack directory."); } } void ConfigSound(TXT_UNCAST_ARG(widget), void *user_data) { txt_window_t *window; txt_window_action_t *music_action; // Build the window window = TXT_NewWindow("Sound configuration"); TXT_SetWindowHelpURL(window, WINDOW_HELP_URL); TXT_SetColumnWidths(window, 40); TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, TXT_SCREEN_W / 2, 3); music_action = TXT_NewWindowAction('m', "Music Packs"); TXT_SetWindowAction(window, TXT_HORIZ_CENTER, music_action); TXT_SignalConnect(music_action, "pressed", OpenMusicPackDir, NULL); TXT_AddWidgets(window, TXT_NewSeparator("Sound effects"), TXT_NewRadioButton("Disabled", &snd_sfxdevice, SNDDEVICE_NONE), TXT_If(gamemission == doom, TXT_NewRadioButton("PC speaker effects", &snd_sfxdevice, SNDDEVICE_PCSPEAKER)), TXT_NewRadioButton("Digital sound effects", &snd_sfxdevice, SNDDEVICE_SB), TXT_If(gamemission == doom || gamemission == heretic || gamemission == hexen, TXT_NewConditional(&snd_sfxdevice, SNDDEVICE_SB, TXT_NewHorizBox( TXT_NewStrut(4, 0), TXT_NewCheckBox("Pitch-shifted sounds", &snd_pitchshift), NULL))), TXT_If(gamemission == strife, TXT_NewConditional(&snd_sfxdevice, SNDDEVICE_SB, TXT_NewHorizBox( TXT_NewStrut(4, 0), TXT_NewCheckBox("Show text with voices", &show_talk), NULL))), TXT_NewSeparator("Music"), TXT_NewRadioButton("Disabled", &snd_musicdevice, SNDDEVICE_NONE), TXT_NewRadioButton("OPL (Adlib/Soundblaster)", &snd_musicdevice, SNDDEVICE_SB), TXT_NewConditional(&snd_musicdevice, SNDDEVICE_SB, TXT_NewHorizBox( TXT_NewStrut(4, 0), TXT_NewLabel("Chip type: "), OPLTypeSelector(), NULL)), TXT_NewRadioButton("GUS (emulated)", &snd_musicdevice, SNDDEVICE_GUS), TXT_NewConditional(&snd_musicdevice, SNDDEVICE_GUS, TXT_MakeTable(2, TXT_NewStrut(4, 0), TXT_NewLabel("Path to patch files: "), TXT_NewStrut(4, 0), TXT_NewFileSelector(&gus_patch_path, 34, "Select directory containing GUS patches", TXT_DIRECTORY), NULL)), TXT_NewRadioButton("MIDI/MP3/OGG/FLAC", &snd_musicdevice, SNDDEVICE_GENMIDI), // [crispy] improve ambigious music backend name TXT_NewConditional(&snd_musicdevice, SNDDEVICE_GENMIDI, TXT_MakeTable(2, TXT_NewStrut(4, 0), TXT_NewLabel("Timidity configuration file: "), TXT_NewStrut(4, 0), TXT_NewFileSelector(&timidity_cfg_path, 34, "Select Timidity config file", cfg_extension), NULL)), NULL); } void BindSoundVariables(void) { M_BindIntVariable("snd_sfxdevice", &snd_sfxdevice); M_BindIntVariable("snd_musicdevice", &snd_musicdevice); M_BindIntVariable("snd_channels", &numChannels); M_BindIntVariable("snd_samplerate", &snd_samplerate); M_BindIntVariable("sfx_volume", &sfxVolume); M_BindIntVariable("music_volume", &musicVolume); M_BindIntVariable("use_libsamplerate", &use_libsamplerate); M_BindFloatVariable("libsamplerate_scale", &libsamplerate_scale); M_BindIntVariable("gus_ram_kb", &gus_ram_kb); M_BindStringVariable("gus_patch_path", &gus_patch_path); M_BindStringVariable("music_pack_path", &music_pack_path); M_BindStringVariable("timidity_cfg_path", &timidity_cfg_path); M_BindIntVariable("snd_sbport", &snd_sbport); M_BindIntVariable("snd_sbirq", &snd_sbirq); M_BindIntVariable("snd_sbdma", &snd_sbdma); M_BindIntVariable("snd_mport", &snd_mport); M_BindIntVariable("snd_maxslicetime_ms", &snd_maxslicetime_ms); M_BindStringVariable("snd_musiccmd", &snd_musiccmd); M_BindStringVariable("snd_dmxoption", &snd_dmxoption); M_BindIntVariable("snd_cachesize", &snd_cachesize); M_BindIntVariable("opl_io_port", &opl_io_port); M_BindIntVariable("snd_pitchshift", &snd_pitchshift); if (gamemission == strife) { M_BindIntVariable("voice_volume", &voiceVolume); M_BindIntVariable("show_talk", &show_talk); } music_pack_path = M_StringDuplicate(""); timidity_cfg_path = M_StringDuplicate(""); gus_patch_path = M_StringDuplicate(""); // All versions of Heretic and Hexen did pitch-shifting. // Most versions of Doom did not and Strife never did. snd_pitchshift = gamemission == heretic || gamemission == hexen; // Default sound volumes - different games use different values. switch (gamemission) { case doom: default: sfxVolume = 8; musicVolume = 8; break; case heretic: case hexen: sfxVolume = 10; musicVolume = 10; break; case strife: sfxVolume = 8; musicVolume = 13; break; } } crispy-doom-crispy-doom-5.6.4/src/setup/sound.h000066400000000000000000000013721360717211000214670ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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 SETUP_SOUND_H #define SETUP_SOUND_H #include "i_sound.h" void ConfigSound(void *widget, void *user_data); void BindSoundVariables(void); extern char *snd_dmxoption; #endif /* #ifndef SETUP_SOUND_H */ crispy-doom-crispy-doom-5.6.4/src/setup/txt_joyaxis.c000066400000000000000000000340741360717211000227240ustar00rootroot00000000000000// // Copyright(C) 2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include "SDL.h" #include "joystick.h" #include "i_joystick.h" #include "i_system.h" #include "m_controls.h" #include "m_misc.h" #include "textscreen.h" #include "txt_gui.h" #include "txt_io.h" #include "txt_joyaxis.h" #include "txt_utf8.h" #define JOYSTICK_AXIS_WIDTH 20 static const char *CalibrationLabel(txt_joystick_axis_t *joystick_axis) { switch (joystick_axis->config_stage) { case CONFIG_CENTER: return "Center the D-pad or joystick,\n" "and press a button."; case CONFIG_STAGE1: if (joystick_axis->dir == JOYSTICK_AXIS_VERTICAL) { return "Push the D-pad or joystick up,\n" "and press the button."; } else { return "Push the D-pad or joystick to the\n" "left, and press the button."; } case CONFIG_STAGE2: if (joystick_axis->dir == JOYSTICK_AXIS_VERTICAL) { return "Push the D-pad or joystick down,\n" "and press the button."; } else { return "Push the D-pad or joystick to the\n" "right, and press the button."; } } return NULL; } static void SetCalibrationLabel(txt_joystick_axis_t *joystick_axis) { TXT_SetLabel(joystick_axis->config_label, CalibrationLabel(joystick_axis)); } // Search all axes on joystick being configured; find a button that is // pressed (other than the calibrate button). Returns the button number. static int FindPressedAxisButton(txt_joystick_axis_t *joystick_axis) { int i; for (i = 0; i < SDL_JoystickNumButtons(joystick_axis->joystick); ++i) { if (i == joystick_axis->config_button) { continue; } if (SDL_JoystickGetButton(joystick_axis->joystick, i)) { return i; } } return -1; } // Look for a hat that isn't centered. Returns the encoded hat axis. static int FindUncenteredHat(SDL_Joystick *joystick, int *axis_invert) { int i, hatval; for (i = 0; i < SDL_JoystickNumHats(joystick); ++i) { hatval = SDL_JoystickGetHat(joystick, i); switch (hatval) { case SDL_HAT_LEFT: case SDL_HAT_RIGHT: *axis_invert = hatval != SDL_HAT_LEFT; return CREATE_HAT_AXIS(i, HAT_AXIS_HORIZONTAL); case SDL_HAT_UP: case SDL_HAT_DOWN: *axis_invert = hatval != SDL_HAT_UP; return CREATE_HAT_AXIS(i, HAT_AXIS_VERTICAL); // If the hat is centered, or is not pointing in a // definite direction, then ignore it. We don't accept // the hat being pointed to the upper-left for example, // because it's ambiguous. case SDL_HAT_CENTERED: default: break; } } // None found. return -1; } static boolean CalibrateAxis(txt_joystick_axis_t *joystick_axis) { int best_axis; int best_value; int best_invert; Sint16 axis_value; int i; // Check all axes to find which axis has the largest value. We test // for one axis at a time, so eg. when we prompt to push the joystick // left, whichever axis has the largest value is the left axis. best_axis = 0; best_value = 0; best_invert = 0; for (i = 0; i < SDL_JoystickNumAxes(joystick_axis->joystick); ++i) { axis_value = SDL_JoystickGetAxis(joystick_axis->joystick, i); if (joystick_axis->bad_axis[i]) { continue; } if (abs(axis_value) > best_value) { best_value = abs(axis_value); best_invert = axis_value > 0; best_axis = i; } } // Did we find one axis that had a significant value? if (best_value > 32768 / 4) { // Save the best values we have found *joystick_axis->axis = best_axis; *joystick_axis->invert = best_invert; return true; } // Otherwise, maybe this is a "button axis", like the PS3 SIXAXIS // controller that exposes the D-pad as four individual buttons. // Search for a button. i = FindPressedAxisButton(joystick_axis); if (i >= 0) { *joystick_axis->axis = CREATE_BUTTON_AXIS(i, 0); *joystick_axis->invert = 0; return true; } // Maybe it's a D-pad that is presented as a hat. This sounds weird // but gamepads like this really do exist; an example is the // Nyko AIRFLO Ex. i = FindUncenteredHat(joystick_axis->joystick, joystick_axis->invert); if (i >= 0) { *joystick_axis->axis = i; return true; } // User pressed the button without pushing the joystick anywhere. return false; } static boolean SetButtonAxisPositive(txt_joystick_axis_t *joystick_axis) { int button; button = FindPressedAxisButton(joystick_axis); if (button >= 0) { *joystick_axis->axis |= CREATE_BUTTON_AXIS(0, button); return true; } return false; } static void IdentifyBadAxes(txt_joystick_axis_t *joystick_axis) { int i, val; free(joystick_axis->bad_axis); joystick_axis->bad_axis = calloc(SDL_JoystickNumAxes(joystick_axis->joystick), sizeof(boolean)); // Look for uncentered axes. for (i = 0; i < SDL_JoystickNumAxes(joystick_axis->joystick); ++i) { val = SDL_JoystickGetAxis(joystick_axis->joystick, i); joystick_axis->bad_axis[i] = abs(val) > (32768 / 5); if (joystick_axis->bad_axis[i]) { printf("Ignoring uncentered joystick axis #%i: %i\n", i, val); } } } static int NextCalibrateStage(txt_joystick_axis_t *joystick_axis) { switch (joystick_axis->config_stage) { case CONFIG_CENTER: return CONFIG_STAGE1; // After pushing to the left, there are two possibilities: // either it is a button axis, in which case we need to find // the other button, or we can just move on to the next axis. case CONFIG_STAGE1: if (IS_BUTTON_AXIS(*joystick_axis->axis)) { return CONFIG_STAGE2; } else { return CONFIG_CENTER; } case CONFIG_STAGE2: return CONFIG_CENTER; } return -1; } static int EventCallback(SDL_Event *event, TXT_UNCAST_ARG(joystick_axis)) { TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); boolean advance; if (event->type != SDL_JOYBUTTONDOWN) { return 0; } // At this point, we have a button press. // In the first "center" stage, we're just trying to work out which // joystick is being configured and which button the user is pressing. if (joystick_axis->config_stage == CONFIG_CENTER) { joystick_axis->config_button = event->jbutton.button; IdentifyBadAxes(joystick_axis); // Advance to next stage. joystick_axis->config_stage = CONFIG_STAGE1; SetCalibrationLabel(joystick_axis); return 1; } // In subsequent stages, the user is asked to push in a specific // direction and press the button. They must push the same button // as they did before; this is necessary to support button axes. if (event->jbutton.which == SDL_JoystickInstanceID(joystick_axis->joystick) && event->jbutton.button == joystick_axis->config_button) { switch (joystick_axis->config_stage) { default: case CONFIG_STAGE1: advance = CalibrateAxis(joystick_axis); break; case CONFIG_STAGE2: advance = SetButtonAxisPositive(joystick_axis); break; } // Advance to the next calibration stage? if (advance) { joystick_axis->config_stage = NextCalibrateStage(joystick_axis); SetCalibrationLabel(joystick_axis); // Finished? if (joystick_axis->config_stage == CONFIG_CENTER) { TXT_CloseWindow(joystick_axis->config_window); if (joystick_axis->callback != NULL) { joystick_axis->callback(); } } return 1; } } return 0; } static void CalibrateWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(joystick_axis)) { TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); free(joystick_axis->bad_axis); joystick_axis->bad_axis = NULL; SDL_JoystickClose(joystick_axis->joystick); SDL_JoystickEventState(SDL_DISABLE); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); TXT_SDL_SetEventCallback(NULL, NULL); } void TXT_ConfigureJoystickAxis(txt_joystick_axis_t *joystick_axis, int using_button, txt_joystick_axis_callback_t callback) { // Open the joystick first. if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { return; } joystick_axis->joystick = SDL_JoystickOpen(joystick_index); if (joystick_axis->joystick == NULL) { TXT_MessageBox(NULL, "Please configure a controller first!"); return; } SDL_JoystickEventState(SDL_ENABLE); // Build the prompt window. joystick_axis->config_window = TXT_NewWindow("Gamepad/Joystick calibration"); TXT_AddWidgets(joystick_axis->config_window, TXT_NewStrut(0, 1), joystick_axis->config_label = TXT_NewLabel(""), TXT_NewStrut(0, 1), NULL); TXT_SetWindowAction(joystick_axis->config_window, TXT_HORIZ_LEFT, NULL); TXT_SetWindowAction(joystick_axis->config_window, TXT_HORIZ_CENTER, TXT_NewWindowAbortAction(joystick_axis->config_window)); TXT_SetWindowAction(joystick_axis->config_window, TXT_HORIZ_RIGHT, NULL); TXT_SetWidgetAlign(joystick_axis->config_window, TXT_HORIZ_CENTER); if (using_button >= 0) { joystick_axis->config_stage = CONFIG_STAGE1; joystick_axis->config_button = using_button; IdentifyBadAxes(joystick_axis); } else { joystick_axis->config_stage = CONFIG_CENTER; } SetCalibrationLabel(joystick_axis); // Close the joystick and shut down joystick subsystem when the window // is closed. TXT_SignalConnect(joystick_axis->config_window, "closed", CalibrateWindowClosed, joystick_axis); TXT_SDL_SetEventCallback(EventCallback, joystick_axis); // When successfully calibrated, invoke this callback: joystick_axis->callback = callback; } static void TXT_JoystickAxisSizeCalc(TXT_UNCAST_ARG(joystick_axis)) { TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); // All joystickinputs are the same size. joystick_axis->widget.w = JOYSTICK_AXIS_WIDTH; joystick_axis->widget.h = 1; } static void TXT_JoystickAxisDrawer(TXT_UNCAST_ARG(joystick_axis)) { TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); char buf[JOYSTICK_AXIS_WIDTH + 1]; int i; if (*joystick_axis->axis < 0) { M_StringCopy(buf, "(none)", sizeof(buf)); } else if (IS_BUTTON_AXIS(*joystick_axis->axis)) { int neg, pos; neg = BUTTON_AXIS_NEG(*joystick_axis->axis); pos = BUTTON_AXIS_POS(*joystick_axis->axis); M_snprintf(buf, sizeof(buf), "BUTTONS #%i+#%i", neg, pos); } else if (IS_HAT_AXIS(*joystick_axis->axis)) { int hat, dir; hat = HAT_AXIS_HAT(*joystick_axis->axis); dir = HAT_AXIS_DIRECTION(*joystick_axis->axis); M_snprintf(buf, sizeof(buf), "HAT #%i (%s)", hat, dir == HAT_AXIS_HORIZONTAL ? "horizontal" : "vertical"); } else { M_snprintf(buf, sizeof(buf), "AXIS #%i", *joystick_axis->axis); } TXT_SetWidgetBG(joystick_axis); TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); TXT_DrawString(buf); for (i = TXT_UTF8_Strlen(buf); i < joystick_axis->widget.w; ++i) { TXT_DrawString(" "); } } static void TXT_JoystickAxisDestructor(TXT_UNCAST_ARG(joystick_axis)) { } static int TXT_JoystickAxisKeyPress(TXT_UNCAST_ARG(joystick_axis), int key) { TXT_CAST_ARG(txt_joystick_axis_t, joystick_axis); if (key == KEY_ENTER) { TXT_ConfigureJoystickAxis(joystick_axis, -1, NULL); return 1; } if (key == KEY_BACKSPACE || key == KEY_DEL) { *joystick_axis->axis = -1; } return 0; } static void TXT_JoystickAxisMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b) { TXT_CAST_ARG(txt_joystick_axis_t, widget); // Clicking is like pressing enter if (b == TXT_MOUSE_LEFT) { TXT_JoystickAxisKeyPress(widget, KEY_ENTER); } } txt_widget_class_t txt_joystick_axis_class = { TXT_AlwaysSelectable, TXT_JoystickAxisSizeCalc, TXT_JoystickAxisDrawer, TXT_JoystickAxisKeyPress, TXT_JoystickAxisDestructor, TXT_JoystickAxisMousePress, NULL, }; txt_joystick_axis_t *TXT_NewJoystickAxis(int *axis, int *invert, txt_joystick_axis_direction_t dir) { txt_joystick_axis_t *joystick_axis; joystick_axis = malloc(sizeof(txt_joystick_axis_t)); TXT_InitWidget(joystick_axis, &txt_joystick_axis_class); joystick_axis->axis = axis; joystick_axis->invert = invert; joystick_axis->dir = dir; joystick_axis->bad_axis = NULL; return joystick_axis; } crispy-doom-crispy-doom-5.6.4/src/setup/txt_joyaxis.h000066400000000000000000000053321360717211000227240ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef TXT_JOY_AXIS_H #define TXT_JOY_AXIS_H typedef struct txt_joystick_axis_s txt_joystick_axis_t; typedef enum { JOYSTICK_AXIS_HORIZONTAL, JOYSTICK_AXIS_VERTICAL, } txt_joystick_axis_direction_t; typedef enum { CONFIG_CENTER, // "Center the joystick and press a button..." CONFIG_STAGE1, // "Top or left and press a button..." CONFIG_STAGE2, // [Optional] "Bottom or right and press a button..." } txt_joystick_axis_stage_t; // Callback invoked when calibration is completed. typedef void (*txt_joystick_axis_callback_t)(void); #include "txt_widget.h" #include "txt_window.h" #include "SDL.h" // // A joystick axis. // struct txt_joystick_axis_s { txt_widget_t widget; int *axis, *invert; txt_joystick_axis_direction_t dir; // Only used when configuring: // Configuration prompt window and label. txt_window_t *config_window; txt_label_t *config_label; // SDL joystick handle for reading joystick state. SDL_Joystick *joystick; // "Bad" joystick axes. Sometimes an axis can be stuck or "bad". An // example I found is that if you unplug the nunchuck extension from // a Wii remote, the axes from the nunchuck can be stuck at one of // the maximum values. These have to be ignored, so when we ask the // user to center the joystick, we look for bad axes that are not // close to zero. boolean *bad_axis; // Stage we have reached in configuring joystick axis. txt_joystick_axis_stage_t config_stage; // Button to press to advance to next stage. int config_button; // Callback invoked when the axis is calibrated. txt_joystick_axis_callback_t callback; }; txt_joystick_axis_t *TXT_NewJoystickAxis(int *axis, int *invert, txt_joystick_axis_direction_t dir); // Configure a joystick axis widget. // axis: The axis widget to configure. // using_button: If non-negative, use this joystick button as the button // to expect from the user. Otherwise, ask. void TXT_ConfigureJoystickAxis(txt_joystick_axis_t *axis, int using_button, txt_joystick_axis_callback_t callback); #endif /* #ifndef TXT_JOY_AXIS_H */ crispy-doom-crispy-doom-5.6.4/src/setup/txt_joybinput.c000066400000000000000000000204711360717211000232550ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include "SDL_joystick.h" #include "doomkeys.h" #include "joystick.h" #include "i_joystick.h" #include "i_system.h" #include "m_controls.h" #include "m_misc.h" #include "txt_joybinput.h" #include "txt_gui.h" #include "txt_io.h" #include "txt_label.h" #include "txt_sdl.h" #include "txt_utf8.h" #include "txt_window.h" #define JOYSTICK_INPUT_WIDTH 10 extern int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS]; // Joystick button variables. // The ordering of this array is important. We will always try to map // each variable to the virtual button with the same array index. For // example: joybfire should always be 0, and then we change // joystick_physical_buttons[0] to point to the physical joystick // button that the user wants to use for firing. We do this so that // the menus work (the game code is hard coded to interpret // button #0 = select menu item, button #1 = go back to previous menu). static int *all_joystick_buttons[NUM_VIRTUAL_BUTTONS] = { &joybfire, &joybuse, &joybstrafe, &joybspeed, &joybstrafeleft, &joybstraferight, &joybprevweapon, &joybnextweapon, &joybjump, &joybmenu, &joybautomap, }; static int PhysicalForVirtualButton(int vbutton) { if (vbutton < NUM_VIRTUAL_BUTTONS) { return joystick_physical_buttons[vbutton]; } else { return vbutton; } } // Get the virtual button number for the given variable, ie. the // variable's index in all_joystick_buttons[NUM_VIRTUAL_BUTTONS]. static int VirtualButtonForVariable(int *variable) { int i; for (i = 0; i < arrlen(all_joystick_buttons); ++i) { if (variable == all_joystick_buttons[i]) { return i; } } I_Error("Couldn't find virtual button"); return -1; } // Rearrange joystick button configuration to be in "canonical" form: // each joyb* variable should have a value equal to its index in // all_joystick_buttons[NUM_VIRTUAL_BUTTONS] above. static void CanonicalizeButtons(void) { int new_mapping[NUM_VIRTUAL_BUTTONS]; int vbutton; int i; for (i = 0; i < arrlen(all_joystick_buttons); ++i) { vbutton = *all_joystick_buttons[i]; // Don't remap the speed key if it's bound to "always run". // Also preserve "unbound" variables. if ((all_joystick_buttons[i] == &joybspeed && vbutton >= 20) || vbutton < 0) { new_mapping[i] = i; } else { new_mapping[i] = PhysicalForVirtualButton(vbutton); *all_joystick_buttons[i] = i; } } for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i) { joystick_physical_buttons[i] = new_mapping[i]; } } // Check all existing buttons and clear any using the specified physical // button. static void ClearVariablesUsingButton(int physbutton) { int vbutton; int i; for (i = 0; i < arrlen(all_joystick_buttons); ++i) { vbutton = *all_joystick_buttons[i]; if (vbutton >= 0 && physbutton == PhysicalForVirtualButton(vbutton)) { *all_joystick_buttons[i] = -1; } } } // Called in response to SDL events when the prompt window is open: static int EventCallback(SDL_Event *event, TXT_UNCAST_ARG(joystick_input)) { TXT_CAST_ARG(txt_joystick_input_t, joystick_input); // Got the joystick button press? if (event->type == SDL_JOYBUTTONDOWN) { int vbutton, physbutton; // Before changing anything, remap button configuration into // canonical form, to avoid conflicts. CanonicalizeButtons(); vbutton = VirtualButtonForVariable(joystick_input->variable); physbutton = event->jbutton.button; if (joystick_input->check_conflicts) { ClearVariablesUsingButton(physbutton); } // Set mapping. *joystick_input->variable = vbutton; joystick_physical_buttons[vbutton] = physbutton; TXT_CloseWindow(joystick_input->prompt_window); return 1; } return 0; } // When the prompt window is closed, disable the event callback function; // we are no longer interested in receiving notification of events. static void PromptWindowClosed(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(joystick)) { TXT_CAST_ARG(SDL_Joystick, joystick); SDL_JoystickClose(joystick); TXT_SDL_SetEventCallback(NULL, NULL); SDL_JoystickEventState(SDL_DISABLE); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } static void OpenErrorWindow(void) { TXT_MessageBox(NULL, "Please configure a controller first!"); } static void OpenPromptWindow(txt_joystick_input_t *joystick_input) { txt_window_t *window; SDL_Joystick *joystick; // Silently update when the shift button is held down. joystick_input->check_conflicts = !TXT_GetModifierState(TXT_MOD_SHIFT); if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { return; } // Check the current joystick is valid joystick = SDL_JoystickOpen(joystick_index); if (joystick == NULL) { OpenErrorWindow(); return; } // Open the prompt window window = TXT_MessageBox(NULL, "Press the new button on the controller..."); TXT_SDL_SetEventCallback(EventCallback, joystick_input); TXT_SignalConnect(window, "closed", PromptWindowClosed, joystick); joystick_input->prompt_window = window; SDL_JoystickEventState(SDL_ENABLE); } static void TXT_JoystickInputSizeCalc(TXT_UNCAST_ARG(joystick_input)) { TXT_CAST_ARG(txt_joystick_input_t, joystick_input); // All joystickinputs are the same size. joystick_input->widget.w = JOYSTICK_INPUT_WIDTH; joystick_input->widget.h = 1; } static void GetJoystickButtonDescription(int vbutton, char *buf, size_t buf_len) { M_snprintf(buf, buf_len, "BUTTON #%i", PhysicalForVirtualButton(vbutton) + 1); } static void TXT_JoystickInputDrawer(TXT_UNCAST_ARG(joystick_input)) { TXT_CAST_ARG(txt_joystick_input_t, joystick_input); char buf[20]; int i; if (*joystick_input->variable < 0) { M_StringCopy(buf, "(none)", sizeof(buf)); } else { GetJoystickButtonDescription(*joystick_input->variable, buf, sizeof(buf)); } TXT_SetWidgetBG(joystick_input); TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); TXT_DrawString(buf); for (i = TXT_UTF8_Strlen(buf); i < JOYSTICK_INPUT_WIDTH; ++i) { TXT_DrawString(" "); } } static void TXT_JoystickInputDestructor(TXT_UNCAST_ARG(joystick_input)) { } static int TXT_JoystickInputKeyPress(TXT_UNCAST_ARG(joystick_input), int key) { TXT_CAST_ARG(txt_joystick_input_t, joystick_input); if (key == KEY_ENTER) { // Open a window to prompt for the new joystick press OpenPromptWindow(joystick_input); return 1; } if (key == KEY_BACKSPACE || key == KEY_DEL) { *joystick_input->variable = -1; } return 0; } static void TXT_JoystickInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b) { TXT_CAST_ARG(txt_joystick_input_t, widget); // Clicking is like pressing enter if (b == TXT_MOUSE_LEFT) { TXT_JoystickInputKeyPress(widget, KEY_ENTER); } } txt_widget_class_t txt_joystick_input_class = { TXT_AlwaysSelectable, TXT_JoystickInputSizeCalc, TXT_JoystickInputDrawer, TXT_JoystickInputKeyPress, TXT_JoystickInputDestructor, TXT_JoystickInputMousePress, NULL, }; txt_joystick_input_t *TXT_NewJoystickInput(int *variable) { txt_joystick_input_t *joystick_input; joystick_input = malloc(sizeof(txt_joystick_input_t)); TXT_InitWidget(joystick_input, &txt_joystick_input_class); joystick_input->variable = variable; return joystick_input; } crispy-doom-crispy-doom-5.6.4/src/setup/txt_joybinput.h000066400000000000000000000020721360717211000232570ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef TXT_JOYB_INPUT_H #define TXT_JOYB_INPUT_H typedef struct txt_joystick_input_s txt_joystick_input_t; #include "txt_widget.h" #include "txt_window.h" // // A joystick input is like an input box. When selected, a box pops up // allowing a joystick button to be pressed to select it. // struct txt_joystick_input_s { txt_widget_t widget; int *variable; txt_window_t *prompt_window; int check_conflicts; }; txt_joystick_input_t *TXT_NewJoystickInput(int *variable); #endif /* #ifndef TXT_JOYB_INPUT_H */ crispy-doom-crispy-doom-5.6.4/src/setup/txt_keyinput.c000066400000000000000000000102051360717211000230740ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include "doomkeys.h" #include "m_misc.h" #include "txt_keyinput.h" #include "txt_gui.h" #include "txt_io.h" #include "txt_label.h" #include "txt_utf8.h" #include "txt_window.h" #define KEY_INPUT_WIDTH 8 static int KeyPressCallback(txt_window_t *window, int key, TXT_UNCAST_ARG(key_input)) { TXT_CAST_ARG(txt_key_input_t, key_input); if (key != KEY_ESCAPE) { // Got the key press. Save to the variable and close the window. *key_input->variable = key; if (key_input->check_conflicts) { TXT_EmitSignal(key_input, "set"); } TXT_CloseWindow(window); // Return to normal input mode now that we have the key. TXT_SetInputMode(TXT_INPUT_NORMAL); return 1; } else { return 0; } } static void ReleaseGrab(TXT_UNCAST_ARG(window), TXT_UNCAST_ARG(unused)) { // SDL2-TODO: Needed? // SDL_WM_GrabInput(SDL_GRAB_OFF); } static void OpenPromptWindow(txt_key_input_t *key_input) { txt_window_t *window; // Silently update when the shift button is held down. key_input->check_conflicts = !TXT_GetModifierState(TXT_MOD_SHIFT); window = TXT_MessageBox(NULL, "Press the new key..."); TXT_SetKeyListener(window, KeyPressCallback, key_input); // Switch to raw input mode while we're grabbing the key. TXT_SetInputMode(TXT_INPUT_RAW); // Grab input while reading the key. On Windows Mobile // handheld devices, the hardware keypresses are only // detected when input is grabbed. // SDL2-TODO: Needed? //SDL_WM_GrabInput(SDL_GRAB_ON); TXT_SignalConnect(window, "closed", ReleaseGrab, NULL); } static void TXT_KeyInputSizeCalc(TXT_UNCAST_ARG(key_input)) { TXT_CAST_ARG(txt_key_input_t, key_input); // All keyinputs are the same size. key_input->widget.w = KEY_INPUT_WIDTH; key_input->widget.h = 1; } static void TXT_KeyInputDrawer(TXT_UNCAST_ARG(key_input)) { TXT_CAST_ARG(txt_key_input_t, key_input); char buf[20]; int i; if (*key_input->variable == 0) { M_StringCopy(buf, "(none)", sizeof(buf)); } else { TXT_GetKeyDescription(*key_input->variable, buf, sizeof(buf)); } TXT_SetWidgetBG(key_input); TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); TXT_DrawString(buf); for (i = TXT_UTF8_Strlen(buf); i < KEY_INPUT_WIDTH; ++i) { TXT_DrawString(" "); } } static void TXT_KeyInputDestructor(TXT_UNCAST_ARG(key_input)) { } static int TXT_KeyInputKeyPress(TXT_UNCAST_ARG(key_input), int key) { TXT_CAST_ARG(txt_key_input_t, key_input); if (key == KEY_ENTER) { // Open a window to prompt for the new key press OpenPromptWindow(key_input); return 1; } if (key == KEY_BACKSPACE || key == KEY_DEL) { *key_input->variable = 0; } return 0; } static void TXT_KeyInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b) { TXT_CAST_ARG(txt_key_input_t, widget); // Clicking is like pressing enter if (b == TXT_MOUSE_LEFT) { TXT_KeyInputKeyPress(widget, KEY_ENTER); } } txt_widget_class_t txt_key_input_class = { TXT_AlwaysSelectable, TXT_KeyInputSizeCalc, TXT_KeyInputDrawer, TXT_KeyInputKeyPress, TXT_KeyInputDestructor, TXT_KeyInputMousePress, NULL, }; txt_key_input_t *TXT_NewKeyInput(int *variable) { txt_key_input_t *key_input; key_input = malloc(sizeof(txt_key_input_t)); TXT_InitWidget(key_input, &txt_key_input_class); key_input->variable = variable; return key_input; } crispy-doom-crispy-doom-5.6.4/src/setup/txt_keyinput.h000066400000000000000000000017101360717211000231020ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef TXT_KEY_INPUT_H #define TXT_KEY_INPUT_H typedef struct txt_key_input_s txt_key_input_t; #include "txt_widget.h" // // A key input is like an input box. When selected, a box pops up // allowing a key to be selected. // struct txt_key_input_s { txt_widget_t widget; int *variable; int check_conflicts; }; txt_key_input_t *TXT_NewKeyInput(int *variable); #endif /* #ifndef TXT_KEY_INPUT_H */ crispy-doom-crispy-doom-5.6.4/src/setup/txt_mouseinput.c000066400000000000000000000101141360717211000234330ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #include #include #include #include "doomkeys.h" #include "m_misc.h" #include "txt_mouseinput.h" #include "txt_gui.h" #include "txt_io.h" #include "txt_label.h" #include "txt_utf8.h" #include "txt_window.h" // eg. "BUTTON #10" #define MOUSE_INPUT_WIDTH 10 static int MousePressCallback(txt_window_t *window, int x, int y, int b, TXT_UNCAST_ARG(mouse_input)) { TXT_CAST_ARG(txt_mouse_input_t, mouse_input); // Got the mouse press. Save to the variable and close the window. *mouse_input->variable = b - TXT_MOUSE_BASE; if (mouse_input->check_conflicts) { TXT_EmitSignal(mouse_input, "set"); } TXT_CloseWindow(window); return 1; } static void OpenPromptWindow(txt_mouse_input_t *mouse_input) { txt_window_t *window; // Silently update when the shift key is held down. mouse_input->check_conflicts = !TXT_GetModifierState(TXT_MOD_SHIFT); window = TXT_MessageBox(NULL, "Press the new mouse button..."); TXT_SetMouseListener(window, MousePressCallback, mouse_input); } static void TXT_MouseInputSizeCalc(TXT_UNCAST_ARG(mouse_input)) { TXT_CAST_ARG(txt_mouse_input_t, mouse_input); // All mouseinputs are the same size. mouse_input->widget.w = MOUSE_INPUT_WIDTH; mouse_input->widget.h = 1; } static void GetMouseButtonDescription(int button, char *buf, size_t buf_len) { switch (button) { case 0: M_StringCopy(buf, "LEFT", buf_len); break; case 1: M_StringCopy(buf, "RIGHT", buf_len); break; case 2: M_StringCopy(buf, "MID", buf_len); break; default: M_snprintf(buf, buf_len, "BUTTON #%i", button + 1); break; } } static void TXT_MouseInputDrawer(TXT_UNCAST_ARG(mouse_input)) { TXT_CAST_ARG(txt_mouse_input_t, mouse_input); char buf[20]; int i; if (*mouse_input->variable < 0) { M_StringCopy(buf, "(none)", sizeof(buf)); } else { GetMouseButtonDescription(*mouse_input->variable, buf, sizeof(buf)); } TXT_SetWidgetBG(mouse_input); TXT_FGColor(TXT_COLOR_BRIGHT_WHITE); TXT_DrawString(buf); for (i = TXT_UTF8_Strlen(buf); i < MOUSE_INPUT_WIDTH; ++i) { TXT_DrawString(" "); } } static void TXT_MouseInputDestructor(TXT_UNCAST_ARG(mouse_input)) { } static int TXT_MouseInputKeyPress(TXT_UNCAST_ARG(mouse_input), int key) { TXT_CAST_ARG(txt_mouse_input_t, mouse_input); if (key == KEY_ENTER) { // Open a window to prompt for the new mouse press OpenPromptWindow(mouse_input); return 1; } if (key == KEY_BACKSPACE || key == KEY_DEL) { *mouse_input->variable = -1; } return 0; } static void TXT_MouseInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b) { TXT_CAST_ARG(txt_mouse_input_t, widget); // Clicking is like pressing enter if (b == TXT_MOUSE_LEFT) { TXT_MouseInputKeyPress(widget, KEY_ENTER); } } txt_widget_class_t txt_mouse_input_class = { TXT_AlwaysSelectable, TXT_MouseInputSizeCalc, TXT_MouseInputDrawer, TXT_MouseInputKeyPress, TXT_MouseInputDestructor, TXT_MouseInputMousePress, NULL, }; txt_mouse_input_t *TXT_NewMouseInput(int *variable) { txt_mouse_input_t *mouse_input; mouse_input = malloc(sizeof(txt_mouse_input_t)); TXT_InitWidget(mouse_input, &txt_mouse_input_class); mouse_input->variable = variable; return mouse_input; } crispy-doom-crispy-doom-5.6.4/src/setup/txt_mouseinput.h000066400000000000000000000017341360717211000234500ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // #ifndef TXT_MOUSE_INPUT_H #define TXT_MOUSE_INPUT_H typedef struct txt_mouse_input_s txt_mouse_input_t; #include "txt_widget.h" // // A mouse input is like an input box. When selected, a box pops up // allowing a mouse to be selected. // struct txt_mouse_input_s { txt_widget_t widget; int *variable; int check_conflicts; }; txt_mouse_input_t *TXT_NewMouseInput(int *variable); #endif /* #ifndef TXT_MOUSE_INPUT_H */ crispy-doom-crispy-doom-5.6.4/src/sha1.c000066400000000000000000000210151360717211000200220ustar00rootroot00000000000000/* sha1.c - SHA1 hash function * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * Please see below for more legal information! * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ /* Test vectors: * * "abc" * A999 3E36 4706 816A BA3E 2571 7850 C26C 9CD0 D89D * * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 8498 3E44 1C3B D26E BAAE 4AA1 F951 29E5 E546 70F1 */ #include #include #include #include #include "i_swap.h" #include "sha1.h" void SHA1_Init(sha1_context_t *hd) { hd->h0 = 0x67452301; hd->h1 = 0xefcdab89; hd->h2 = 0x98badcfe; hd->h3 = 0x10325476; hd->h4 = 0xc3d2e1f0; hd->nblocks = 0; hd->count = 0; } /**************** * Transform the message X which consists of 16 32-bit-words */ static void Transform(sha1_context_t *hd, byte *data) { uint32_t a,b,c,d,e,tm; uint32_t x[16]; /* get values from the chaining vars */ a = hd->h0; b = hd->h1; c = hd->h2; d = hd->h3; e = hd->h4; #ifdef SYS_BIG_ENDIAN memcpy(x, data, 64); #else { int i; byte *p2; for(i=0, p2=(byte*)x; i < 16; i++, p2 += 4 ) { p2[3] = *data++; p2[2] = *data++; p2[1] = *data++; p2[0] = *data++; } } #endif #define K1 0x5A827999L #define K2 0x6ED9EBA1L #define K3 0x8F1BBCDCL #define K4 0xCA62C1D6L #define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) #define F2(x,y,z) ( x ^ y ^ z ) #define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) #define F4(x,y,z) ( x ^ y ^ z ) #define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) #define M(i) ( tm = x[i&0x0f] ^ x[(i-14)&0x0f] \ ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f] \ , (x[i&0x0f] = rol(tm,1)) ) #define R(a,b,c,d,e,f,k,m) do { e += rol( a, 5 ) \ + f( b, c, d ) \ + k \ + m; \ b = rol( b, 30 ); \ } while(0) R( a, b, c, d, e, F1, K1, x[ 0] ); R( e, a, b, c, d, F1, K1, x[ 1] ); R( d, e, a, b, c, F1, K1, x[ 2] ); R( c, d, e, a, b, F1, K1, x[ 3] ); R( b, c, d, e, a, F1, K1, x[ 4] ); R( a, b, c, d, e, F1, K1, x[ 5] ); R( e, a, b, c, d, F1, K1, x[ 6] ); R( d, e, a, b, c, F1, K1, x[ 7] ); R( c, d, e, a, b, F1, K1, x[ 8] ); R( b, c, d, e, a, F1, K1, x[ 9] ); R( a, b, c, d, e, F1, K1, x[10] ); R( e, a, b, c, d, F1, K1, x[11] ); R( d, e, a, b, c, F1, K1, x[12] ); R( c, d, e, a, b, F1, K1, x[13] ); R( b, c, d, e, a, F1, K1, x[14] ); R( a, b, c, d, e, F1, K1, x[15] ); R( e, a, b, c, d, F1, K1, M(16) ); R( d, e, a, b, c, F1, K1, M(17) ); R( c, d, e, a, b, F1, K1, M(18) ); R( b, c, d, e, a, F1, K1, M(19) ); R( a, b, c, d, e, F2, K2, M(20) ); R( e, a, b, c, d, F2, K2, M(21) ); R( d, e, a, b, c, F2, K2, M(22) ); R( c, d, e, a, b, F2, K2, M(23) ); R( b, c, d, e, a, F2, K2, M(24) ); R( a, b, c, d, e, F2, K2, M(25) ); R( e, a, b, c, d, F2, K2, M(26) ); R( d, e, a, b, c, F2, K2, M(27) ); R( c, d, e, a, b, F2, K2, M(28) ); R( b, c, d, e, a, F2, K2, M(29) ); R( a, b, c, d, e, F2, K2, M(30) ); R( e, a, b, c, d, F2, K2, M(31) ); R( d, e, a, b, c, F2, K2, M(32) ); R( c, d, e, a, b, F2, K2, M(33) ); R( b, c, d, e, a, F2, K2, M(34) ); R( a, b, c, d, e, F2, K2, M(35) ); R( e, a, b, c, d, F2, K2, M(36) ); R( d, e, a, b, c, F2, K2, M(37) ); R( c, d, e, a, b, F2, K2, M(38) ); R( b, c, d, e, a, F2, K2, M(39) ); R( a, b, c, d, e, F3, K3, M(40) ); R( e, a, b, c, d, F3, K3, M(41) ); R( d, e, a, b, c, F3, K3, M(42) ); R( c, d, e, a, b, F3, K3, M(43) ); R( b, c, d, e, a, F3, K3, M(44) ); R( a, b, c, d, e, F3, K3, M(45) ); R( e, a, b, c, d, F3, K3, M(46) ); R( d, e, a, b, c, F3, K3, M(47) ); R( c, d, e, a, b, F3, K3, M(48) ); R( b, c, d, e, a, F3, K3, M(49) ); R( a, b, c, d, e, F3, K3, M(50) ); R( e, a, b, c, d, F3, K3, M(51) ); R( d, e, a, b, c, F3, K3, M(52) ); R( c, d, e, a, b, F3, K3, M(53) ); R( b, c, d, e, a, F3, K3, M(54) ); R( a, b, c, d, e, F3, K3, M(55) ); R( e, a, b, c, d, F3, K3, M(56) ); R( d, e, a, b, c, F3, K3, M(57) ); R( c, d, e, a, b, F3, K3, M(58) ); R( b, c, d, e, a, F3, K3, M(59) ); R( a, b, c, d, e, F4, K4, M(60) ); R( e, a, b, c, d, F4, K4, M(61) ); R( d, e, a, b, c, F4, K4, M(62) ); R( c, d, e, a, b, F4, K4, M(63) ); R( b, c, d, e, a, F4, K4, M(64) ); R( a, b, c, d, e, F4, K4, M(65) ); R( e, a, b, c, d, F4, K4, M(66) ); R( d, e, a, b, c, F4, K4, M(67) ); R( c, d, e, a, b, F4, K4, M(68) ); R( b, c, d, e, a, F4, K4, M(69) ); R( a, b, c, d, e, F4, K4, M(70) ); R( e, a, b, c, d, F4, K4, M(71) ); R( d, e, a, b, c, F4, K4, M(72) ); R( c, d, e, a, b, F4, K4, M(73) ); R( b, c, d, e, a, F4, K4, M(74) ); R( a, b, c, d, e, F4, K4, M(75) ); R( e, a, b, c, d, F4, K4, M(76) ); R( d, e, a, b, c, F4, K4, M(77) ); R( c, d, e, a, b, F4, K4, M(78) ); R( b, c, d, e, a, F4, K4, M(79) ); /* update chainig vars */ hd->h0 += a; hd->h1 += b; hd->h2 += c; hd->h3 += d; hd->h4 += e; } /* Update the message digest with the contents * of INBUF with length INLEN. */ void SHA1_Update(sha1_context_t *hd, byte *inbuf, size_t inlen) { if (hd->count == 64) { /* flush the buffer */ Transform(hd, hd->buf); hd->count = 0; hd->nblocks++; } if (!inbuf) return; if (hd->count) { for (; inlen && hd->count < 64; inlen--) hd->buf[hd->count++] = *inbuf++; SHA1_Update(hd, NULL, 0); if (!inlen) return; } while (inlen >= 64) { Transform(hd, inbuf); hd->count = 0; hd->nblocks++; inlen -= 64; inbuf += 64; } for (; inlen && hd->count < 64; inlen--) hd->buf[hd->count++] = *inbuf++; } /* The routine final terminates the computation and * returns the digest. * The handle is prepared for a new cycle, but adding bytes to the * handle will the destroy the returned buffer. * Returns: 20 bytes representing the digest. */ void SHA1_Final(sha1_digest_t digest, sha1_context_t *hd) { uint32_t t, msb, lsb; byte *p; SHA1_Update(hd, NULL, 0); /* flush */; t = hd->nblocks; /* multiply by 64 to make a byte count */ lsb = t << 6; msb = t >> 26; /* add the count */ t = lsb; if ((lsb += hd->count) < t) msb++; /* multiply by 8 to make a bit count */ t = lsb; lsb <<= 3; msb <<= 3; msb |= t >> 29; if (hd->count < 56) { /* enough room */ hd->buf[hd->count++] = 0x80; /* pad */ while (hd->count < 56) hd->buf[hd->count++] = 0; /* pad */ } else { /* need one extra block */ hd->buf[hd->count++] = 0x80; /* pad character */ while (hd->count < 64) hd->buf[hd->count++] = 0; SHA1_Update(hd, NULL, 0); /* flush */; memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ } /* append the 64 bit count */ hd->buf[56] = msb >> 24; hd->buf[57] = msb >> 16; hd->buf[58] = msb >> 8; hd->buf[59] = msb ; hd->buf[60] = lsb >> 24; hd->buf[61] = lsb >> 16; hd->buf[62] = lsb >> 8; hd->buf[63] = lsb ; Transform(hd, hd->buf); p = hd->buf; #ifdef SYS_BIG_ENDIAN #define X(a) do { *(uint32_t*)p = hd->h##a ; p += 4; } while(0) #else /* little endian */ #define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16; \ *p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0) #endif X(0); X(1); X(2); X(3); X(4); #undef X memcpy(digest, hd->buf, sizeof(sha1_digest_t)); } void SHA1_UpdateInt32(sha1_context_t *context, unsigned int val) { byte buf[4]; buf[0] = (val >> 24) & 0xff; buf[1] = (val >> 16) & 0xff; buf[2] = (val >> 8) & 0xff; buf[3] = val & 0xff; SHA1_Update(context, buf, 4); } void SHA1_UpdateString(sha1_context_t *context, char *str) { SHA1_Update(context, (byte *) str, strlen(str) + 1); } crispy-doom-crispy-doom-5.6.4/src/sha1.h000066400000000000000000000022261360717211000200320ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // SHA-1 digest. // #ifndef __SHA1_H__ #define __SHA1_H__ #include "doomtype.h" typedef struct sha1_context_s sha1_context_t; typedef byte sha1_digest_t[20]; struct sha1_context_s { uint32_t h0,h1,h2,h3,h4; uint32_t nblocks; byte buf[64]; int count; }; void SHA1_Init(sha1_context_t *context); void SHA1_Update(sha1_context_t *context, byte *buf, size_t len); void SHA1_Final(sha1_digest_t digest, sha1_context_t *context); void SHA1_UpdateInt32(sha1_context_t *context, unsigned int val); void SHA1_UpdateString(sha1_context_t *context, char *str); #endif /* #ifndef __SHA1_H__ */ crispy-doom-crispy-doom-5.6.4/src/strife/000077500000000000000000000000001360717211000203175ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/src/strife/.gitignore000066400000000000000000000000451360717211000223060ustar00rootroot00000000000000Makefile Makefile.in .deps tags TAGS crispy-doom-crispy-doom-5.6.4/src/strife/CMakeLists.txt000066400000000000000000000036011360717211000230570ustar00rootroot00000000000000set(STRIFE_SOURCES am_map.c am_map.h deh_ammo.c deh_cheat.c deh_strife.c deh_frame.c deh_misc.c deh_misc.h deh_ptr.c deh_sound.c deh_thing.c deh_weapon.c d_englsh.h d_items.c d_items.h d_main.c d_main.h d_net.c doomdata.h doomdef.c doomdef.h doomstat.c doomstat.h d_player.h dstrings.c dstrings.h d_textur.h d_think.h f_finale.c f_finale.h f_wipe.c f_wipe.h g_game.c g_game.h hu_lib.c hu_lib.h hu_stuff.c hu_stuff.h info.c info.h m_menu.c m_menu.h m_random.c m_random.h m_saves.c m_saves.h p_ceilng.c p_dialog.c p_dialog.h p_doors.c p_enemy.c p_floor.c p_inter.c p_inter.h p_lights.c p_local.h p_map.c p_maputl.c 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 r_bsp.c r_bsp.h r_data.c r_data.h r_defs.h r_draw.c r_draw.h r_local.h r_main.c r_main.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 s_sound.c s_sound.h sounds.c sounds.h st_lib.c st_lib.h st_stuff.c st_stuff.h wi_stuff.c wi_stuff.h) add_library(strife STATIC ${STRIFE_SOURCES}) target_include_directories(strife PRIVATE "../" "../../win32/" "${CMAKE_CURRENT_BINARY_DIR}/../../") target_link_libraries(strife textscreen SDL2::SDL2 SDL2::mixer SDL2::net) crispy-doom-crispy-doom-5.6.4/src/strife/Makefile.am000066400000000000000000000047561360717211000223670ustar00rootroot00000000000000AM_CFLAGS=-I$(top_srcdir)/src \ -I$(top_srcdir)/textscreen \ @SDL_CFLAGS@ @SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@ EXTRA_DIST = CMakeLists.txt noinst_LIBRARIES=libstrife.a libstrife_a_SOURCES = \ am_map.c am_map.h \ deh_ammo.c \ deh_cheat.c \ deh_strife.c \ deh_frame.c \ deh_misc.c deh_misc.h \ deh_ptr.c \ deh_sound.c \ deh_thing.c \ deh_weapon.c \ d_englsh.h \ d_items.c d_items.h \ d_main.c d_main.h \ d_net.c \ doomdata.h \ doomdef.c doomdef.h \ doomstat.c doomstat.h \ d_player.h \ dstrings.c dstrings.h \ d_textur.h \ d_think.h \ f_finale.c f_finale.h \ f_wipe.c f_wipe.h \ g_game.c g_game.h \ hu_lib.c hu_lib.h \ hu_stuff.c hu_stuff.h \ info.c info.h \ m_menu.c m_menu.h \ m_random.c m_random.h \ m_saves.c m_saves.h \ p_ceilng.c \ p_dialog.c p_dialog.h \ p_doors.c \ p_enemy.c \ p_floor.c \ p_inter.c p_inter.h \ p_lights.c \ p_local.h \ p_map.c \ p_maputl.c \ 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 \ r_bsp.c r_bsp.h \ r_data.c r_data.h \ r_defs.h \ r_draw.c r_draw.h \ r_local.h \ r_main.c r_main.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 \ s_sound.c s_sound.h \ sounds.c sounds.h \ st_lib.c st_lib.h \ st_stuff.c st_stuff.h \ wi_stuff.c wi_stuff.h crispy-doom-crispy-doom-5.6.4/src/strife/am_map.c000066400000000000000000000740131360717211000217220ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // DESCRIPTION: the automap code // #include #include "deh_main.h" #include "z_zone.h" #include "doomkeys.h" #include "doomdef.h" #include "m_misc.h" #include "st_stuff.h" #include "p_local.h" #include "w_wad.h" #include "m_cheat.h" #include "m_controls.h" #include "i_system.h" #include "i_timer.h" // Needs access to LFB. #include "v_video.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "dstrings.h" #include "am_map.h" // Automap colors #define BACKGROUND 240 // haleyjd [STRIFE] #define WALLCOLORS 5 // villsa [STRIFE] #define WALLRANGE 16 #define TSWALLCOLORS 16 #define FDWALLCOLORS 122 // villsa [STRIFE] #define CDWALLCOLORS 116 #define CTWALLCOLORS 19 // villsa [STRIFE] #define SPWALLCOLORS 243 // villsa [STRIFE] #define THINGCOLORS 233 // villsa [STRIFE] #define MISSILECOLORS 227 // villsa [STRIFE] #define SHOOTABLECOLORS 235 // villsa [STRIFE] // drawing stuff #define AM_NUMMARKPOINTS 10 // scale on entry #define INITSCALEMTOF (.2*FRACUNIT) // how much the automap moves window per tic in frame-buffer coordinates // moves 140 pixels in 1 second #define F_PANINC 4 // how much zoom-in per tic // goes to 2x in 1 second #define M_ZOOMIN ((int) (1.02*FRACUNIT)) // how much zoom-out per tic // pulls out to 0.5x in 1 second #define M_ZOOMOUT ((int) (FRACUNIT/1.02)) // translates between frame-buffer and map distances #define FTOM(x) FixedMul(((x)<<16),scale_ftom) #define MTOF(x) (FixedMul((x),scale_mtof)>>16) // translates between frame-buffer and map coordinates #define CXMTOF(x) (f_x + MTOF((x)-m_x)) #define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) // the following is crap #define LINE_NEVERSEE ML_DONTDRAW typedef struct { int x, y; } fpoint_t; typedef struct { fpoint_t a, b; } fline_t; typedef struct { fixed_t x,y; } mpoint_t; typedef struct { mpoint_t a, b; } mline_t; typedef struct { fixed_t slp, islp; } islope_t; // // The vector graphics for the automap. // A line drawing of the player pointing right, // starting from the middle. // #define R ((8*PLAYERRADIUS)/7) mline_t player_arrow[] = { { { -R+R/8, 0 }, { R, 0 } }, // ----- { { R, 0 }, { R-R/2, R/4 } }, // -----> { { R, 0 }, { R-R/2, -R/4 } }, { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } } }; #undef R #define R ((8*PLAYERRADIUS)/7) mline_t cheat_player_arrow[] = { { { -R+R/8, 0 }, { R, 0 } }, // ----- { { R, 0 }, { R-R/2, R/6 } }, // -----> { { R, 0 }, { R-R/2, -R/6 } }, { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >-----> { { -R+R/8, 0 }, { -R-R/8, -R/6 } }, { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>-----> { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } }, { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d---> { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } }, { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } }, { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd--> { { -R/6, -R/6 }, { 0, -R/6 } }, { { 0, -R/6 }, { 0, R/4 } }, { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt-> { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } }, { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } } }; #undef R #define R (FRACUNIT) mline_t triangle_guy[] = { { { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } }, { { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)(R ) } }, { { (fixed_t)(0 ), (fixed_t)(R ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } } }; #undef R #define R (FRACUNIT) mline_t thintriangle_guy[] = { { { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)(R ), (fixed_t)(0 ) } }, { { (fixed_t)(R ), (fixed_t)(0 ) }, { (fixed_t)(-.5*R), (fixed_t)(.7*R ) } }, { { (fixed_t)(-.5*R), (fixed_t)(.7*R ) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } } }; #undef R static int cheating = 0; //static int grid = 0; [STRIFE]: no such variable static int leveljuststarted = 1; // kluge until AM_LevelInit() is called boolean automapactive = false; //static int finit_width = SCREENWIDTH; //static int finit_height = SCREENHEIGHT - (32 << crispy->hires); // location of window on screen static int f_x; static int f_y; // size of window on screen static int f_w; static int f_h; static int lightlev; // used for funky strobing effect static byte* fb; // pseudo-frame buffer static int amclock; static mpoint_t m_paninc; // how far the window pans each tic (map coords) static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords) static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords) // // width/height of window on map (map coords) // static fixed_t m_w; static fixed_t m_h; // based on level size static fixed_t min_x; static fixed_t min_y; static fixed_t max_x; static fixed_t max_y; static fixed_t max_w; // max_x-min_x, static fixed_t max_h; // max_y-min_y // based on player size static fixed_t min_w; static fixed_t min_h; static fixed_t min_scale_mtof; // used to tell when to stop zooming out static fixed_t max_scale_mtof; // used to tell when to stop zooming in // old stuff for recovery later static fixed_t old_m_w, old_m_h; static fixed_t old_m_x, old_m_y; // old location used by the Follower routine static mpoint_t f_oldloc; // used by MTOF to scale from map-to-frame-buffer coords static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; // used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) static fixed_t scale_ftom; static player_t *plr; // the player represented by an arrow static patch_t *marknums[10]; // numbers used for marking by the automap static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are static int markpointnum = 0; // next point to be assigned static int mapmarknum = 0; // villsa [STRIFE] unused but this was meant to be used for objective based markers static int followplayer = 1; // specifies whether to follow the player around cheatseq_t cheat_amap = CHEAT("topo", 0); // villsa [STRIFE] static boolean stopped = true; // Calculates the slope and slope according to the x-axis of a line // segment in map coordinates (with the upright y-axis n' all) so // that it can be used with the brain-dead drawing stuff. void AM_getIslope ( mline_t* ml, islope_t* is ) { int dx, dy; dy = ml->a.y - ml->b.y; dx = ml->b.x - ml->a.x; if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX); else is->islp = FixedDiv(dx, dy); if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX); else is->slp = FixedDiv(dy, dx); } // // // void AM_activateNewScale(void) { m_x += m_w/2; m_y += m_h/2; m_w = FTOM(f_w); m_h = FTOM(f_h); m_x -= m_w/2; m_y -= m_h/2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; } // // // void AM_saveScaleAndLoc(void) { old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; } // // // void AM_restoreScaleAndLoc(void) { m_w = old_m_w; m_h = old_m_h; if (!followplayer) { m_x = old_m_x; m_y = old_m_y; } else { m_x = plr->mo->x - m_w/2; m_y = plr->mo->y - m_h/2; } m_x2 = m_x + m_w; m_y2 = m_y + m_h; // Change the scaling multipliers scale_mtof = FixedDiv(f_w<mo->x; // 20160306 [STRIFE]: use player position markpoints[markpointnum].y = plr->mo->y; //markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; ++markpointnum; // haleyjd 20141101: [STRIFE] does not wrap around } // // Determines bounding box of all vertices, // sets global variables controlling zoom range. // void AM_findMinMaxBoundaries(void) { int i; fixed_t a; fixed_t b; min_x = min_y = 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 - min_x; max_h = max_y - min_y; min_w = 2*PLAYERRADIUS; // const? never changed? min_h = 2*PLAYERRADIUS; 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; } // // // void AM_initVariables(void) { int pnum; static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 }; automapactive = true; fb = I_VideoBuffer; f_oldloc.x = INT_MAX; amclock = 0; lightlev = 0; m_paninc.x = m_paninc.y = 0; ftom_zoommul = FRACUNIT; mtof_zoommul = FRACUNIT; m_w = FTOM(f_w); m_h = FTOM(f_h); // find player to center on initially if (playeringame[consoleplayer]) { plr = &players[consoleplayer]; } else { plr = &players[0]; for (pnum=0;pnummo->x - m_w/2; m_y = plr->mo->y - m_h/2; AM_changeWindowLoc(); // for saving & restoring old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; // inform the status bar of the change ST_Responder(&st_notify); } // // AM_loadPics // // haleyjd 08/27/10: [STRIFE] Changed marknums to PLMNUM%d // void AM_loadPics(void) { int i; char namebuf[9]; for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "PLMNUM%d", i); marknums[i] = W_CacheLumpName(namebuf, PU_STATIC); } } void AM_unloadPics(void) { int i; char namebuf[9]; for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "PLMNUM%d", i); W_ReleaseLumpName(namebuf); } } void AM_clearMarks(void) { int i; for (i=0;ihires);//finit_height; AM_clearMarks(); AM_findMinMaxBoundaries(); scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7*FRACUNIT)); if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } // // // void AM_Stop (void) { static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 }; AM_unloadPics(); automapactive = false; ST_Responder(&st_notify); stopped = true; } // // // void AM_Start (void) { static int lastlevel = -1; //static int lastepisode = -1; if (!stopped) AM_Stop(); stopped = false; if (lastlevel != gamemap /*|| lastepisode != gameepisode*/) { AM_LevelInit(); lastlevel = gamemap; //lastepisode = gameepisode; } AM_initVariables(); AM_loadPics(); } // // set the window scale to the maximum size // void AM_minOutWindowScale(void) { scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // // set the window scale to the minimum size // void AM_maxOutWindowScale(void) { scale_mtof = max_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // // Handle events (user inputs) in automap mode // boolean AM_Responder ( event_t* ev ) { int rc; static int bigstate=0; static int joywait = 0; static char buffer[20]; int key; rc = false; if (ev->type == ev_joystick && joybautomap >= 0 && (ev->data1 & (1 << joybautomap)) != 0 && joywait < I_GetTime()) { joywait = I_GetTime() + 5; if (!automapactive) { AM_Start (); viewactive = false; } else { bigstate = 0; viewactive = true; AM_Stop (); } return true; } if (!automapactive) { if (ev->type == ev_keydown && ev->data1 == key_map_toggle) { AM_Start (); viewactive = false; rc = true; } } else if (ev->type == ev_keydown) { rc = true; key = ev->data1; if (key == key_map_east) // pan right { if (!followplayer) m_paninc.x = FTOM(F_PANINC); else rc = false; } else if (key == key_map_west) // pan left { if (!followplayer) m_paninc.x = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_north) // pan up { if (!followplayer) m_paninc.y = FTOM(F_PANINC); else rc = false; } else if (key == key_map_south) // pan down { if (!followplayer) m_paninc.y = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_zoomout) // zoom out { mtof_zoommul = M_ZOOMOUT; ftom_zoommul = M_ZOOMIN; } else if (key == key_map_zoomin) // zoom in { mtof_zoommul = M_ZOOMIN; ftom_zoommul = M_ZOOMOUT; } else if (key == key_map_toggle) { bigstate = 0; viewactive = true; AM_Stop (); } else if (key == key_map_maxzoom) { bigstate = !bigstate; if (bigstate) { AM_saveScaleAndLoc(); AM_minOutWindowScale(); } else AM_restoreScaleAndLoc(); } else if (key == key_map_follow) { followplayer = !followplayer; f_oldloc.x = INT_MAX; if (followplayer) plr->message = DEH_String(AMSTR_FOLLOWON); else plr->message = DEH_String(AMSTR_FOLLOWOFF); } // haleyjd 20141101: [STRIFE] grid is not supported /* else if (key == key_map_grid) { grid = !grid; if (grid) plr->message = DEH_String(AMSTR_GRIDON); else plr->message = DEH_String(AMSTR_GRIDOFF); } */ else if (key == key_map_mark) { // haleyjd 20141101: [STRIFE] if full, mark 9 is replaced if(markpointnum == AM_NUMMARKPOINTS) --markpointnum; M_snprintf(buffer, sizeof(buffer), "%s %d", DEH_String(AMSTR_MARKEDSPOT), markpointnum + 1); // [STRIFE] plr->message = buffer; AM_addMark(); } else if (key == key_map_clearmark) { // haleyjd 20141101: [STRIFE] clears last mark only if(markpointnum > 0) { markpoints[markpointnum - 1].x = -1; --markpointnum; plr->message = DEH_String(AMSTR_MARKSCLEARED); } } else { rc = false; } if (!deathmatch && cht_CheckCheat(&cheat_amap, ev->data2)) { rc = false; cheating = (cheating+1) % 3; } } else if (ev->type == ev_keyup) { rc = false; key = ev->data1; if (key == key_map_east) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_west) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_north) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_south) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_zoomout || key == key_map_zoomin) { mtof_zoommul = FRACUNIT; ftom_zoommul = FRACUNIT; } } return rc; } // // Zooming // void AM_changeWindowScale(void) { // Change the scaling multipliers scale_mtof = FixedMul(scale_mtof, mtof_zoommul); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); if (scale_mtof < min_scale_mtof) AM_minOutWindowScale(); else if (scale_mtof > max_scale_mtof) AM_maxOutWindowScale(); else AM_activateNewScale(); } // // // void AM_doFollowPlayer(void) { if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) { m_x = FTOM(MTOF(plr->mo->x)) - m_w/2; m_y = FTOM(MTOF(plr->mo->y)) - m_h/2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; f_oldloc.x = plr->mo->x; f_oldloc.y = plr->mo->y; // m_x = FTOM(MTOF(plr->mo->x - m_w/2)); // m_y = FTOM(MTOF(plr->mo->y - m_h/2)); // m_x = plr->mo->x - m_w/2; // m_y = plr->mo->y - m_h/2; } } // // // void AM_updateLightLev(void) { static int nexttic = 0; //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; static int litelevelscnt = 0; // Change light level if (amclock>nexttic) { lightlev = litelevels[litelevelscnt++]; if (litelevelscnt == arrlen(litelevels)) litelevelscnt = 0; nexttic = amclock + 6 - (amclock % 6); } } // // Updates on Game Tick // void AM_Ticker (void) { if (!automapactive) return; amclock++; if (followplayer) AM_doFollowPlayer(); // Change the zoom if necessary if (ftom_zoommul != FRACUNIT) AM_changeWindowScale(); // Change x,y location if (m_paninc.x || m_paninc.y) AM_changeWindowLoc(); // Update light level // AM_updateLightLev(); } // // Clear automap frame buffer. // void AM_clearFB(int color) { memset(fb, color, f_w*f_h); } // // Automap clipping of lines. // // Based on Cohen-Sutherland clipping algorithm but with a slightly // faster reject and precalculated slopes. If the speed is needed, // use a hash algorithm to handle the common cases. // boolean AM_clipMline ( mline_t* ml, fline_t* fl ) { enum { LEFT =1, RIGHT =2, BOTTOM =4, TOP =8 }; register int outcode1 = 0; register int outcode2 = 0; register int outside; fpoint_t tmp; int dx; int dy; #define DOOUTCODE(oc, mx, my) \ (oc) = 0; \ if ((my) < 0) (oc) |= TOP; \ else if ((my) >= f_h) (oc) |= BOTTOM; \ if ((mx) < 0) (oc) |= LEFT; \ else if ((mx) >= f_w) (oc) |= RIGHT; // do trivial rejects and outcodes if (ml->a.y > m_y2) outcode1 = TOP; else if (ml->a.y < m_y) outcode1 = BOTTOM; if (ml->b.y > m_y2) outcode2 = TOP; else if (ml->b.y < m_y) outcode2 = BOTTOM; if (outcode1 & outcode2) return false; // trivially outside if (ml->a.x < m_x) outcode1 |= LEFT; else if (ml->a.x > m_x2) outcode1 |= RIGHT; if (ml->b.x < m_x) outcode2 |= LEFT; else if (ml->b.x > m_x2) outcode2 |= RIGHT; if (outcode1 & outcode2) return false; // trivially outside // transform to frame-buffer coordinates. fl->a.x = CXMTOF(ml->a.x); fl->a.y = CYMTOF(ml->a.y); fl->b.x = CXMTOF(ml->b.x); fl->b.y = CYMTOF(ml->b.y); DOOUTCODE(outcode1, fl->a.x, fl->a.y); DOOUTCODE(outcode2, fl->b.x, fl->b.y); if (outcode1 & outcode2) return false; while (outcode1 | outcode2) { // may be partially inside box // find an outside point if (outcode1) outside = outcode1; else outside = outcode2; // clip to each side if (outside & TOP) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx*(fl->a.y))/dy; tmp.y = 0; } else if (outside & BOTTOM) { dy = fl->a.y - fl->b.y; dx = fl->b.x - fl->a.x; tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy; tmp.y = f_h-1; } else if (outside & RIGHT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx; tmp.x = f_w-1; } else if (outside & LEFT) { dy = fl->b.y - fl->a.y; dx = fl->b.x - fl->a.x; tmp.y = fl->a.y + (dy*(-fl->a.x))/dx; tmp.x = 0; } else { tmp.x = 0; tmp.y = 0; } if (outside == outcode1) { fl->a = tmp; DOOUTCODE(outcode1, fl->a.x, fl->a.y); } else { fl->b = tmp; DOOUTCODE(outcode2, fl->b.x, fl->b.y); } if (outcode1 & outcode2) return false; // trivially outside } return true; } #undef DOOUTCODE // // Classic Bresenham w/ whatever optimizations needed for speed // void AM_drawFline ( fline_t* fl, int color ) { register int x; register int y; register int dx; register int dy; register int sx; register int sy; register int ax; register int ay; register int d; static int fuck = 0; // For debugging only if ( fl->a.x < 0 || fl->a.x >= f_w || fl->a.y < 0 || fl->a.y >= f_h || fl->b.x < 0 || fl->b.x >= f_w || fl->b.y < 0 || fl->b.y >= f_h) { DEH_fprintf(stderr, "fuck %d \r", fuck++); return; } #define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) dx = fl->b.x - fl->a.x; ax = 2 * (dx<0 ? -dx : dx); sx = dx<0 ? -1 : 1; dy = fl->b.y - fl->a.y; ay = 2 * (dy<0 ? -dy : dy); sy = dy<0 ? -1 : 1; x = fl->a.x; y = fl->a.y; if (ax > ay) { d = ay - ax/2; while (1) { PUTDOT(x,y,color); if (x == fl->b.x) return; if (d>=0) { y += sy; d -= ax; } x += sx; d += ay; } } else { d = ax - ay/2; while (1) { PUTDOT(x, y, color); if (y == fl->b.y) return; if (d >= 0) { x += sx; d -= ay; } y += sy; d += ax; } } } // // Clip lines, draw visible part sof lines. // void AM_drawMline ( mline_t* ml, int color ) { static fline_t fl; if (AM_clipMline(ml, &fl)) AM_drawFline(&fl, color); // draws it on frame buffer using fb coords } // // Draws flat (floor/ceiling tile) aligned grid lines. // /*void AM_drawGrid(int color) { fixed_t x, y; fixed_t start, end; mline_t ml; // Figure out start of vertical gridlines start = m_x; if ((start-bmaporgx)%(MAPBLOCKUNITS<v1->x; l.a.y = line->v1->y; l.b.x = line->v2->x; l.b.y = line->v2->y; if(cheating || (line->flags & ML_MAPPED)) { if((line->flags & LINE_NEVERSEE) && !cheating) continue; // villsa [STRIFE] if(line->special == 145 || line->special == 186) { AM_drawMline(&l, SPWALLCOLORS); } // villsa [STRIFE] lightlev is unused here else if(!line->backsector) { AM_drawMline(&l, WALLCOLORS); } else { if(line->special == 39) { // teleporters AM_drawMline(&l, WALLCOLORS+WALLRANGE/2); } else if (line->flags & ML_SECRET) // secret door { // villsa [STRIFE] just draw the wall as is! AM_drawMline(&l, WALLCOLORS); } else if(line->backsector->floorheight != line->frontsector->floorheight) { AM_drawMline(&l, FDWALLCOLORS); // floor level change } else if(line->backsector->ceilingheight != line->frontsector->ceilingheight) { AM_drawMline(&l, CDWALLCOLORS); // ceiling level change } else if (cheating) { AM_drawMline(&l, TSWALLCOLORS); } } } // villsa [STRIFE] show all of the map on map 15 else if(plr->powers[pw_allmap] || gamemap == 15) { if(!(line->flags & LINE_NEVERSEE)) AM_drawMline(&l, CTWALLCOLORS); } } } // // Rotation in 2D. // Used to rotate player arrow line character. // void AM_rotate ( fixed_t* x, fixed_t* y, angle_t a ) { fixed_t tmpx; tmpx = FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT]) - FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]); *y = FixedMul(*x,finesine[a>>ANGLETOFINESHIFT]) + FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]); *x = tmpx; } void AM_drawLineCharacter ( mline_t* lineguy, int lineguylines, fixed_t scale, angle_t angle, int color, fixed_t x, fixed_t y ) { int i; mline_t l; for (i=0;imo->angle, 224, plr->mo->x, plr->mo->y); return; } for(i = 0; i < MAXPLAYERS; i++) { their_color++; p = &players[i]; // villsa [STRIFE] check for gameskill?? if((gameskill && deathmatch && !singledemo) && p != plr) continue; if(!playeringame[i]) continue; // villsa [STRIFE] change to 27 if(p->powers[pw_invisibility]) color = 27; // *close* to black else color = their_colors[their_color]; AM_drawLineCharacter (player_arrow, arrlen(player_arrow), 0, p->mo->angle, color, p->mo->x, p->mo->y); } } // // AM_drawThings // // villsa [STRIFE] no arguments // void AM_drawThings(void) { int i; mobj_t* t; int colors; fixed_t radius; // villsa [STRIFE] almost re-written // specific things can have different radius, color and appearence for(i = 0; i < numsectors; i++) { t = sectors[i].thinglist; while(t) { radius = t->radius; if(t->flags & (MF_MISSILE|MF_SPECIAL)) { colors = MISSILECOLORS; } else if(t->flags & MF_COUNTKILL) { colors = SHOOTABLECOLORS; } else { colors = THINGCOLORS; radius = (16<angle, colors, t->x, t->y); t = t->snext; } } } // // AM_drawMarks // void AM_drawMarks(void) { int i, fx, fy, w, h; for(i = 0; i < AM_NUMMARKPOINTS; i++) { if(markpoints[i].x != -1) { // w = SHORT(marknums[i]->width); // h = SHORT(marknums[i]->height); w = 5; // because something's wrong with the wad, i guess h = 6; // because something's wrong with the wad, i guess fx = CXMTOF(markpoints[i].x); fy = CYMTOF(markpoints[i].y); if(fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) { // villsa [STRIFE] if(i >= mapmarknum) V_DrawPatch(fx >> crispy->hires, fy >> crispy->hires, marknums[i]); } } } } // villsa [STRIFE] unused /*void AM_drawCrosshair(int color) { fb[(f_w*(f_h+1))/2] = color; // single point for now }*/ void AM_Drawer (void) { if (!automapactive) return; AM_clearFB(BACKGROUND); // villsa [STRIFE] not used /*if(grid) AM_drawGrid(GRIDCOLORS);*/ AM_drawWalls(); AM_drawPlayers(); // villsa [STRIFE] draw things when map powerup is enabled if(cheating == 2 || plr->powers[pw_allmap] > 1) AM_drawThings(); // villsa [STRIFE] not used //AM_drawCrosshair(XHAIRCOLORS); AM_drawMarks(); V_MarkRect(f_x, f_y, f_w, f_h); } crispy-doom-crispy-doom-5.6.4/src/strife/am_map.h000066400000000000000000000023331360717211000217230ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // AutoMap module. // #ifndef __AMMAP_H__ #define __AMMAP_H__ #include "d_event.h" #include "m_cheat.h" // Used by ST StatusBar stuff. #define AM_MSGHEADER (('a'<<24)+('m'<<16)) #define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) #define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) // Called by main loop. boolean AM_Responder (event_t* ev); // Called by main loop. void AM_Ticker (void); // Called by main loop, // called instead of view drawer if automap active. void AM_Drawer (void); // Called to force the automap to quit // if the level is completed while it is up. void AM_Stop (void); extern cheatseq_t cheat_amap; #endif crispy-doom-crispy-doom-5.6.4/src/strife/d_englsh.h000066400000000000000000000446301360717211000222620ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Printed strings for translation. // English language support (default). // #ifndef __D_ENGLSH__ #define __D_ENGLSH__ // // Printed strings for translation // // // D_Main.C // #define D_DEVSTR "Development mode ON.\n" #define D_CDROM "CD-ROM Version: Accessing strife.cd\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?" // [STRIFE] modified: #define LOADNET "you can't load while in a net game!\n\n"PRESSKEY #define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY // [STRIFE] modified: #define QSAVESPOT "you haven't picked a\nquicksave slot yet!\n\n"PRESSKEY // [STRIFE] modified: #define SAVEDEAD "you're not playing a game\n\n"PRESSKEY #define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN // [STRIFE] modified: #define QLPROMPT "do you want to quickload\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\nto end the game?\n\n"PRESSYN // haleyjd 09/11/10: [STRIFE] No "to dos." on this #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" // [crispy] intermediate gamma levels #define GAMMALVL05 "Gamma correction level 0.5" #define GAMMALVL15 "Gamma correction level 1.5" #define GAMMALVL25 "Gamma correction level 2.5" #define GAMMALVL35 "Gamma correction level 3.5" #define EMPTYSTRING "empty slot" // // P_inter.C // #define GOTARMOR "Picked up the armor." #define GOTMEGA "Picked up the MegaArmor!" #define GOTHTHBONUS "Picked up a health bonus." #define GOTARMBONUS "Picked up an armor bonus." #define GOTSTIM "Picked up a stimpack." #define GOTMEDINEED "Picked up a medikit that you REALLY need!" #define GOTMEDIKIT "Picked up a medikit." #define GOTSUPER "Supercharge!" #define GOTBLUECARD "Picked up a blue keycard." #define GOTYELWCARD "Picked up a yellow keycard." #define GOTREDCARD "Picked up a red keycard." #define GOTBLUESKUL "Picked up a blue skull key." #define GOTYELWSKUL "Picked up a yellow skull key." #define GOTREDSKULL "Picked up a red skull key." #define GOTINVUL "Invulnerability!" #define GOTBERSERK "Berserk!" #define GOTINVIS "Partial Invisibility" #define GOTSUIT "Radiation Shielding Suit" #define GOTMAP "Computer Area Map" #define GOTVISOR "Light Amplification Visor" #define GOTMSPHERE "MegaSphere!" #define GOTCLIP "Picked up a clip." #define GOTCLIPBOX "Picked up a box of bullets." #define GOTROCKET "Picked up a rocket." #define GOTROCKBOX "Picked up a box of rockets." #define GOTCELL "Picked up an energy cell." #define GOTCELLBOX "Picked up an energy cell pack." #define GOTSHELLS "Picked up 4 shotgun shells." #define GOTSHELLBOX "Picked up a box of shotgun shells." #define GOTBACKPACK "Picked up a backpack full of ammo!" #define GOTBFG9000 "You got the BFG9000! Oh, yes." #define GOTCHAINGUN "You got the chaingun!" #define GOTCHAINSAW "A chainsaw! Find some meat!" #define GOTLAUNCHER "You got the rocket launcher!" #define GOTPLASMA "You got the plasma gun!" #define GOTSHOTGUN "You got the shotgun!" #define GOTSHOTGUN2 "You got the super shotgun!" // // P_Doors.C // #define PD_BLUEO "You need a blue key to activate this object" #define PD_REDO "You need a red key to activate this object" #define PD_YELLOWO "You need a yellow key to activate this object" #define PD_BLUEK "You need a blue key to open this door" #define PD_REDK "You need a red key to open this door" #define PD_YELLOWK "You need a yellow key to open this door" // // G_game.C // #define GGSAVED "game saved." // // HU_stuff.C // #define HUSTR_MSGU "[Message unsent]" // haleyjd 08/31/10: [STRIFE] Strife map names #define HUSTR_1 "AREA 1: sanctuary" #define HUSTR_2 "AREA 2: town" #define HUSTR_3 "AREA 3: front base" #define HUSTR_4 "AREA 4: power station" #define HUSTR_5 "AREA 5: prison" #define HUSTR_6 "AREA 6: sewers" #define HUSTR_7 "AREA 7: castle" #define HUSTR_8 "AREA 8: Audience Chamber" #define HUSTR_9 "AREA 9: Castle: Programmer's Keep" #define HUSTR_10 "AREA 10: New Front Base" #define HUSTR_11 "AREA 11: Borderlands" #define HUSTR_12 "AREA 12: the temple of the oracle" #define HUSTR_13 "AREA 13: Catacombs" #define HUSTR_14 "AREA 14: mines" #define HUSTR_15 "AREA 15: Fortress: Administration" #define HUSTR_16 "AREA 16: Fortress: Bishop's Tower" #define HUSTR_17 "AREA 17: Fortress: The Bailey" #define HUSTR_18 "AREA 18: Fortress: Stores" #define HUSTR_19 "AREA 19: Fortress: Security Complex" #define HUSTR_20 "AREA 20: Factory: Receiving" #define HUSTR_21 "AREA 21: Factory: Manufacturing" #define HUSTR_22 "AREA 22: Factory: Forge" #define HUSTR_23 "AREA 23: Order Commons" #define HUSTR_24 "AREA 24: Factory: Conversion Chapel" #define HUSTR_25 "AREA 25: Catacombs: Ruined Temple" #define HUSTR_26 "AREA 26: proving grounds" #define HUSTR_27 "AREA 27: The Lab" #define HUSTR_28 "AREA 28: Alien Ship" #define HUSTR_29 "AREA 29: Entity's Lair" #define HUSTR_30 "AREA 30: Abandoned Front Base" #define HUSTR_31 "AREA 31: Training Facility" #define HUSTR_32 "AREA 1: Sanctuary" #define HUSTR_33 "AREA 2: Town" #define HUSTR_34 "AREA 3: Movement Base" // haleyjd 20110219: [STRIFE] replaced all with Strife chat macros: #define HUSTR_CHATMACRO1 "Fucker!" #define HUSTR_CHATMACRO2 "--SPLAT-- Instant wall art." #define HUSTR_CHATMACRO3 "That had to hurt!" #define HUSTR_CHATMACRO4 "Smackings!" #define HUSTR_CHATMACRO5 "Gib-O-Matic baby." #define HUSTR_CHATMACRO6 "Burn! Yah! Yah!" #define HUSTR_CHATMACRO7 "Buh-Bye!" #define HUSTR_CHATMACRO8 "Sizzle chest!" #define HUSTR_CHATMACRO9 "That sucked!" #define HUSTR_CHATMACRO0 "Mommy?" #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 // [STRIFE]: Not used, as strings are local to hu_stuff.c //#define HUSTR_PLRGREEN "Green: " //#define HUSTR_PLRINDIGO "Indigo: " //#define HUSTR_PLRBROWN "Brown: " //#define HUSTR_PLRRED "Red: " #define HUSTR_KEYGREEN 'g' #define HUSTR_KEYINDIGO 'i' #define HUSTR_KEYBROWN 'b' #define HUSTR_KEYRED 'r' // // AM_map.C // #define AMSTR_FOLLOWON "Follow Mode ON" #define AMSTR_FOLLOWOFF "Follow Mode OFF" #define AMSTR_GRIDON "Grid ON" #define AMSTR_GRIDOFF "Grid OFF" #define AMSTR_MARKEDSPOT "Marked Spot" #define AMSTR_MARKSCLEARED "Last Mark Cleared" // [STRIFE] // // ST_stuff.C // #define STSTR_MUS "Music Change" #define STSTR_NOMUS "IMPOSSIBLE SELECTION" #define STSTR_DQDON "You're Invincible!" // [STRIFE] #define STSTR_DQDOFF "You're a looney!" // [STRIFE] #define STSTR_KFAADDED "Very Happy Ammo Added" #define STSTR_FAADDED "Ammo Added" // [STRIFE] #define STSTR_NCON "No Clipping Mode ON" #define STSTR_NCOFF "No Clipping Mode OFF" #define STSTR_BEHOLD "Bzrk, Inviso, Mask, Health, Pack, Stats" // [STRIFE] #define STSTR_BEHOLDX "Power-up Toggled" #define STSTR_CHOPPERS "... doesn't suck - GM" #define STSTR_CLEV "Changing Level..." // // F_Finale.C // #define E1TEXT \ "Once you beat the big badasses and\n"\ "clean out the moon base you're supposed\n"\ "to win, aren't you? Aren't you? Where's\n"\ "your fat reward and ticket home? What\n"\ "the hell is this? It's not supposed to\n"\ "end this way!\n"\ "\n" \ "It stinks like rotten meat, but looks\n"\ "like the lost Deimos base. Looks like\n"\ "you're stuck on The Shores of Hell.\n"\ "The only way out is through.\n"\ "\n"\ "To continue the DOOM experience, play\n"\ "The Shores of Hell and its amazing\n"\ "sequel, Inferno!\n" #define E2TEXT \ "You've done it! The hideous cyber-\n"\ "demon lord that ruled the lost Deimos\n"\ "moon base has been slain and you\n"\ "are triumphant! But ... where are\n"\ "you? You clamber to the edge of the\n"\ "moon and look down to see the awful\n"\ "truth.\n" \ "\n"\ "Deimos floats above Hell itself!\n"\ "You've never heard of anyone escaping\n"\ "from Hell, but you'll make the bastards\n"\ "sorry they ever heard of you! Quickly,\n"\ "you rappel down to the surface of\n"\ "Hell.\n"\ "\n" \ "Now, it's on to the final chapter of\n"\ "DOOM! -- Inferno." #define E3TEXT \ "The loathsome spiderdemon that\n"\ "masterminded the invasion of the moon\n"\ "bases and caused so much death has had\n"\ "its ass kicked for all time.\n"\ "\n"\ "A hidden doorway opens and you enter.\n"\ "You've proven too tough for Hell to\n"\ "contain, and now Hell at last plays\n"\ "fair -- for you emerge from the door\n"\ "to see the green fields of Earth!\n"\ "Home at last.\n" \ "\n"\ "You wonder what's been happening on\n"\ "Earth while you were battling evil\n"\ "unleashed. It's good that no Hell-\n"\ "spawn could have come through that\n"\ "door with you ..." #define E4TEXT \ "the spider mastermind must have sent forth\n"\ "its legions of hellspawn before your\n"\ "final confrontation with that terrible\n"\ "beast from hell. but you stepped forward\n"\ "and brought forth eternal damnation and\n"\ "suffering upon the horde as a true hero\n"\ "would in the face of something so evil.\n"\ "\n"\ "besides, someone was gonna pay for what\n"\ "happened to daisy, your pet rabbit.\n"\ "\n"\ "but now, you see spread before you more\n"\ "potential pain and gibbitude as a nation\n"\ "of demons run amok among our cities.\n"\ "\n"\ "next stop, hell on earth!" // after level 6, put this: #define C1TEXT \ "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ "STARPORT. BUT SOMETHING IS WRONG. THE\n" \ "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ "IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ "\n"\ "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ "OF THE STARBASE AND FIND THE CONTROLLING\n" \ "SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ "HOSTAGE." // After level 11, put this: #define C2TEXT \ "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"\ "THE NIGHTMARE. NOW YOU ARE THE ONLY\n"\ "HUMAN LEFT ON THE FACE OF THE PLANET.\n"\ "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"\ "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"\ "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"\ "THAT YOU HAVE SAVED YOUR SPECIES.\n"\ "\n"\ "BUT THEN, EARTH CONTROL BEAMS DOWN A\n"\ "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"\ "THE SOURCE OF THE ALIEN INVASION. IF YOU\n"\ "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"\ "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"\ "YOUR OWN HOME CITY, NOT FAR FROM THE\n"\ "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"\ "UP AND RETURN TO THE FRAY." // After level 20, put this: #define C3TEXT \ "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"\ "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"\ "YOU SEE NO WAY TO DESTROY THE CREATURES'\n"\ "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"\ "TEETH AND PLUNGE THROUGH IT.\n"\ "\n"\ "THERE MUST BE A WAY TO CLOSE IT ON THE\n"\ "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"\ "GOT TO GO THROUGH HELL TO GET TO IT?" // After level 29, put this: #define C4TEXT \ "THE HORRENDOUS VISAGE OF THE BIGGEST\n"\ "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"\ "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"\ "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"\ "UP AND DIES, ITS THRASHING LIMBS\n"\ "DEVASTATING UNTOLD MILES OF HELL'S\n"\ "SURFACE.\n"\ "\n"\ "YOU'VE DONE IT. THE INVASION IS OVER.\n"\ "EARTH IS SAVED. HELL IS A WRECK. YOU\n"\ "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"\ "DIE, NOW. WIPING THE SWEAT FROM YOUR\n"\ "FOREHEAD YOU BEGIN THE LONG TREK BACK\n"\ "HOME. REBUILDING EARTH OUGHT TO BE A\n"\ "LOT MORE FUN THAN RUINING IT WAS.\n" // Before level 31, put this: #define C5TEXT \ "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"\ "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"\ "HUMANS, RATHER THAN DEMONS. YOU WONDER\n"\ "WHO THE INMATES OF THIS CORNER OF HELL\n"\ "WILL BE." // Before level 32, put this: #define C6TEXT \ "CONGRATULATIONS, YOU'VE FOUND THE\n"\ "SUPER SECRET LEVEL! YOU'D BETTER\n"\ "BLAZE THROUGH THIS ONE!\n" // after map 06 #define P1TEXT \ "You gloat over the steaming carcass of the\n"\ "Guardian. With its death, you've wrested\n"\ "the Accelerator from the stinking claws\n"\ "of Hell. You relax and glance around the\n"\ "room. Damn! There was supposed to be at\n"\ "least one working prototype, but you can't\n"\ "see it. The demons must have taken it.\n"\ "\n"\ "You must find the prototype, or all your\n"\ "struggles will have been wasted. Keep\n"\ "moving, keep fighting, keep killing.\n"\ "Oh yes, keep living, too." // after map 11 #define P2TEXT \ "Even the deadly Arch-Vile labyrinth could\n"\ "not stop you, and you've gotten to the\n"\ "prototype Accelerator which is soon\n"\ "efficiently and permanently deactivated.\n"\ "\n"\ "You're good at that kind of thing." // after map 20 #define P3TEXT \ "You've bashed and battered your way into\n"\ "the heart of the devil-hive. Time for a\n"\ "Search-and-Destroy mission, aimed at the\n"\ "Gatekeeper, whose foul offspring is\n"\ "cascading to Earth. Yeah, he's bad. But\n"\ "you know who's worse!\n"\ "\n"\ "Grinning evilly, you check your gear, and\n"\ "get ready to give the bastard a little Hell\n"\ "of your own making!" // after map 30 #define P4TEXT \ "The Gatekeeper's evil face is splattered\n"\ "all over the place. As its tattered corpse\n"\ "collapses, an inverted Gate forms and\n"\ "sucks down the shards of the last\n"\ "prototype Accelerator, not to mention the\n"\ "few remaining demons. You're done. Hell\n"\ "has gone back to pounding bad dead folks \n"\ "instead of good live ones. Remember to\n"\ "tell your grandkids to put a rocket\n"\ "launcher in your coffin. If you go to Hell\n"\ "when you die, you'll need it for some\n"\ "final cleaning-up ..." // before map 31 #define P5TEXT \ "You've found the second-hardest level we\n"\ "got. Hope you have a saved game a level or\n"\ "two previous. If not, be prepared to die\n"\ "aplenty. For master marines only." // before map 32 #define P6TEXT \ "Betcha wondered just what WAS the hardest\n"\ "level we had ready for ya? Now you know.\n"\ "No one gets out alive." #define T1TEXT \ "You've fought your way out of the infested\n"\ "experimental labs. It seems that UAC has\n"\ "once again gulped it down. With their\n"\ "high turnover, it must be hard for poor\n"\ "old UAC to buy corporate health insurance\n"\ "nowadays..\n"\ "\n"\ "Ahead lies the military complex, now\n"\ "swarming with diseased horrors hot to get\n"\ "their teeth into you. With luck, the\n"\ "complex still has some warlike ordnance\n"\ "laying around." #define T2TEXT \ "You hear the grinding of heavy machinery\n"\ "ahead. You sure hope they're not stamping\n"\ "out new hellspawn, but you're ready to\n"\ "ream out a whole herd if you have to.\n"\ "They might be planning a blood feast, but\n"\ "you feel about as mean as two thousand\n"\ "maniacs packed into one mad killer.\n"\ "\n"\ "You don't plan to go down easy." #define T3TEXT \ "The vista opening ahead looks real damn\n"\ "familiar. Smells familiar, too -- like\n"\ "fried excrement. You didn't like this\n"\ "place before, and you sure as hell ain't\n"\ "planning to like it now. The more you\n"\ "brood on it, the madder you get.\n"\ "Hefting your gun, an evil grin trickles\n"\ "onto your face. Time to take some names." #define T4TEXT \ "Suddenly, all is silent, from one horizon\n"\ "to the other. The agonizing echo of Hell\n"\ "fades away, the nightmare sky turns to\n"\ "blue, the heaps of monster corpses start \n"\ "to evaporate along with the evil stench \n"\ "that filled the air. Jeeze, maybe you've\n"\ "done it. Have you really won?\n"\ "\n"\ "Something rumbles in the distance.\n"\ "A blue light begins to glow inside the\n"\ "ruined skull of the demon-spitter." #define T5TEXT \ "What now? Looks totally different. Kind\n"\ "of like King Tut's condo. Well,\n"\ "whatever's here can't be any worse\n"\ "than usual. Can it? Or maybe it's best\n"\ "to let sleeping gods lie.." #define T6TEXT \ "Time for a vacation. You've burst the\n"\ "bowels of hell and by golly you're ready\n"\ "for a break. You mutter to yourself,\n"\ "Maybe someone else can kick Hell's ass\n"\ "next time around. Ahead lies a quiet town,\n"\ "with peaceful flowing water, quaint\n"\ "buildings, and presumably no Hellspawn.\n"\ "\n"\ "As you step off the transport, you hear\n"\ "the stomp of a cyberdemon's iron shoe." // // Character cast strings F_FINALE.C // #define CC_ZOMBIE "ZOMBIEMAN" #define CC_SHOTGUN "SHOTGUN GUY" #define CC_HEAVY "HEAVY WEAPON DUDE" #define CC_IMP "IMP" #define CC_DEMON "DEMON" #define CC_LOST "LOST SOUL" #define CC_CACO "CACODEMON" #define CC_HELL "HELL KNIGHT" #define CC_BARON "BARON OF HELL" #define CC_ARACH "ARACHNOTRON" #define CC_PAIN "PAIN ELEMENTAL" #define CC_REVEN "REVENANT" #define CC_MANCU "MANCUBUS" #define CC_ARCH "ARCH-VILE" #define CC_SPIDER "THE SPIDER MASTERMIND" #define CC_CYBER "THE CYBERDEMON" #define CC_HERO "OUR HERO" #endif crispy-doom-crispy-doom-5.6.4/src/strife/d_items.c000066400000000000000000000052751360717211000221200ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // We are referring to sprite numbers. #include "info.h" #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 // // villsa [STRIFE] weaponinfo_t weaponinfo[NUMWEAPONS] = { { // fist am_noammo, S_PNCH_03, S_PNCH_02, S_PNCH_01, S_PNCH_04, S_NULL, 1 }, { // electric bow am_elecbolts, S_XBOW_02, S_XBOW_01, S_XBOW_00, S_XBOW_03, S_NULL, 1 }, { // rifle am_bullets, S_RIFG_02, S_RIFG_01, S_RIFG_00, S_RIFF_00, S_NULL, 1 }, { // missile launcher am_missiles, S_MMIS_02, S_MMIS_01, S_MMIS_00, S_MMIS_03, S_NULL, 0 }, { // grenade launcher am_hegrenades, S_GREN_02, S_GREN_01, S_GREN_00, S_GREN_03, S_GREF_00, 0 }, { // flame thrower am_cell, S_FLMT_03, S_FLMT_02, S_FLMT_00, S_FLMF_00, S_NULL, 1 }, { // mauler am_cell, S_BLST_05, S_BLST_04, S_BLST_00, S_BLSF_00, S_NULL, 0 }, { // sigil am_noammo, S_SIGH_06, S_SIGH_05, S_SIGH_00, S_SIGH_07, S_SIGF_00, 0 }, { // poison bow am_poisonbolts, S_XBOW_15, S_XBOW_14, S_XBOW_13, S_XBOW_16, S_NULL, 1 }, { // wp grenade launcher am_wpgrenades, S_GREN_10, S_GREN_09, S_GREN_08, S_GREN_11, S_GREF_03, 0 }, { // torpedo am_cell, S_BLST_18, S_BLST_17, S_BLST_13, S_BLST_19, S_NULL, 0 }, }; crispy-doom-crispy-doom-5.6.4/src/strife/d_items.h000066400000000000000000000020221360717211000221100ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Items: key cards, artifacts, weapon, ammunition. // #ifndef __D_ITEMS__ #define __D_ITEMS__ #include "doomdef.h" // Weapon info: sprite frames, ammunition use. typedef struct { ammotype_t ammo; int upstate; int downstate; int readystate; int atkstate; int flashstate; boolean availabledemo; // villsa [STRIFE] } weaponinfo_t; extern weaponinfo_t weaponinfo[NUMWEAPONS]; #endif crispy-doom-crispy-doom-5.6.4/src/strife/d_main.c000066400000000000000000001551041360717211000217200ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM main program (D_DoomMain) and game loop (D_DoomLoop), // plus functions to determine game mode (shareware, registered), // parse command line parameters, configure game parameters (turbo), // and call the startup functions. // #include #include #include #include #include "config.h" #include "deh_main.h" #include "doomdef.h" #include "doomstat.h" #include "dstrings.h" #include "sounds.h" #include "txt_main.h" #include "txt_io.h" #include "d_iwad.h" #include "z_zone.h" #include "w_main.h" #include "w_wad.h" #include "s_sound.h" #include "v_diskicon.h" #include "v_video.h" #include "f_finale.h" #include "f_wipe.h" #include "m_argv.h" #include "m_config.h" #include "m_controls.h" #include "m_misc.h" #include "m_menu.h" #include "m_saves.h" // haleyjd [STRIFE] #include "p_saveg.h" #include "p_dialog.h" // haleyjd [STRIFE] #include "i_endoom.h" #include "i_input.h" #include "i_joystick.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "i_swap.h" #include "g_game.h" #include "hu_stuff.h" #include "wi_stuff.h" #include "st_stuff.h" #include "am_map.h" #include "net_client.h" #include "net_dedicated.h" #include "net_query.h" #include "p_setup.h" #include "r_local.h" #include "d_main.h" // // 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 // void D_DoomLoop (void); static boolean D_AddFile(char *filename); // Location where savegames are stored char * savegamedir; // location of IWAD and WAD files char * iwadfile; boolean devparm; // started game with -devparm boolean nomonsters; // checkparm of -nomonsters boolean respawnparm; // checkparm of -respawn boolean fastparm; // checkparm of -fast boolean flipparm; // [STRIFE] haleyjd 20110629: checkparm of -flip boolean randomparm; // [STRIFE] haleyjd 20130915: checkparm of -random boolean showintro = true; // [STRIFE] checkparm of -nograph, disables intro //extern int soundVolume; //extern int sfxVolume; //extern int musicVolume; extern boolean inhelpscreens; skill_t startskill; int startepisode; int startmap; boolean autostart; int startloadgame; boolean advancedemo; // villsa [STRIFE] workparm variable (similar to devparm?) boolean workparm = false; // villsa [STRIFE] stonecold cheat variable boolean stonecold = false; // haleyjd 09/11/10: [STRIFE] Game type variables boolean isregistered; boolean isdemoversion; // Store demo, do not accept any inputs // haleyjd [STRIFE] Unused. //boolean storedemo; char wadfile[1024]; // primary wad file char mapdir[1024]; // directory of development maps int show_endoom = 0; int show_diskicon = 1; int graphical_startup = 0; static boolean using_text_startup; // If true, startup has completed and the main game loop has started. static boolean main_loop_started = false; // fraggle 06/03/11 [STRIFE]: Unused config variable, preserved // for compatibility: static int comport = 0; // fraggle 06/03/11 [STRIFE]: Multiplayer nickname? char *nickname = NULL; void D_ConnectNetGame(void); void D_CheckNetGame(void); // // D_ProcessEvents // Send all the events of the given timestamp down the responder chain // void D_ProcessEvents (void) { event_t* ev; // haleyjd 08/22/2010: [STRIFE] there is no such thing as a "store demo" // version of Strife // IF STORE DEMO, DO NOT ACCEPT INPUT //if (storedemo) // return; while ((ev = D_PopEvent()) != NULL) { if (M_Responder (ev)) continue; // menu ate the event G_Responder (ev); } } // // 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 // // haleyjd 08/23/10: [STRIFE]: // * Changes to eliminate intermission and change timing of screenwipe // * 20100901: Added ST_DrawExternal and popupactivestate static variable // * 20110206: Start wipegamestate at GS_UNKNOWN (STRIFE-TODO: rename?) // gamestate_t wipegamestate = GS_UNKNOWN; extern boolean setsizeneeded; //extern int showMessages; [STRIFE] no such variable void R_ExecuteSetViewSize (void); void D_Display (void) { static boolean viewactivestate = false; static boolean menuactivestate = false; static boolean inhelpscreensstate = false; static boolean popupactivestate = false; // [STRIFE] static boolean fullscreen = false; static gamestate_t oldgamestate = -1; static int borderdrawcount; int nowtime; int tics; int wipestart; int y; boolean done; boolean wipe; boolean redrawsbar; if (nodrawers) return; // for comparative timing / profiling redrawsbar = false; // change the view size if needed if (setsizeneeded) { R_ExecuteSetViewSize (); oldgamestate = -1; // force background redraw borderdrawcount = 3; } // save the current screen if about to wipe if (gamestate != wipegamestate) { wipe = true; wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); } else wipe = false; if (gamestate == GS_LEVEL && gametic) HU_Erase(); // do buffered drawing switch (gamestate) { case GS_LEVEL: if (!gametic) break; if (automapactive) AM_Drawer (); if (wipe || (viewheight != (200 <hires) && fullscreen) ) redrawsbar = true; // haleyjd 08/29/10: [STRIFE] Always redraw sbar if menu is/was active if (menuactivestate || (inhelpscreensstate && !inhelpscreens)) redrawsbar = true; // just put away the help screen ST_Drawer (viewheight == (200 << crispy->hires), redrawsbar ); fullscreen = viewheight == (200 << crispy->hires); break; // haleyjd 08/23/2010: [STRIFE] No intermission /* case GS_INTERMISSION: WI_Drawer (); break; */ case GS_FINALE: F_Drawer (); break; case GS_DEMOSCREEN: D_PageDrawer (); break; default: break; } // draw buffered stuff to screen I_UpdateNoBlit (); // draw the view directly if (gamestate == GS_LEVEL && !automapactive && gametic) R_RenderPlayerView (&players[displayplayer]); // clean up border stuff if (gamestate != oldgamestate && gamestate != GS_LEVEL) I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE)); // see if the border needs to be initially drawn if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) { viewactivestate = false; // view was not active R_FillBackScreen (); // draw the pattern into the back screen } // see if the border needs to be updated to the screen if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != (320 << crispy->hires)) { if (menuactive || menuactivestate || !viewactivestate) { borderdrawcount = 3; popupactivestate = false; } if (borderdrawcount) { R_DrawViewBorder (); // erase old menu stuff borderdrawcount--; } } if (testcontrols) { // Box showing current mouse speed V_DrawMouseSpeedBox(testcontrols_mousespeed); } menuactivestate = menuactive; viewactivestate = viewactive; inhelpscreensstate = inhelpscreens; oldgamestate = wipegamestate = gamestate; // haleyjd 20120208: [STRIFE] Rogue moved this down to below border drawing if (gamestate == GS_LEVEL && gametic) { HU_Drawer (); if(ST_DrawExternal()) popupactivestate = true; else if(popupactivestate) { popupactivestate = false; menuactivestate = 1; } } // draw pause pic if (paused) { if (automapactive) y = 4; else y = (viewwindowy >> crispy->hires)+4; V_DrawPatchDirect((viewwindowx >> crispy->hires) + ((scaledviewwidth >> crispy->hires) - 68) / 2, y, W_CacheLumpName (DEH_String("M_PAUSE"), PU_CACHE)); } // menus go directly to the screen M_Drawer (); // menu is drawn even on top of everything NetUpdate (); // send out any new accumulation // normal update if (!wipe) { I_FinishUpdate (); // page flip or blit buffer return; } // wipe update wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); wipestart = I_GetTime () - 1; do { do { nowtime = I_GetTime (); tics = nowtime - wipestart; I_Sleep(1); } while (tics < 3); // haleyjd 08/23/2010: [STRIFE] Changed from == 0 to < 3 // haleyjd 08/26/10: [STRIFE] Changed to use ColorXForm wipe. wipestart = nowtime; done = wipe_ScreenWipe(wipe_ColorXForm , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); I_UpdateNoBlit (); M_Drawer (); // menu is drawn even on top of wipes I_FinishUpdate (); // page flip or blit buffer } while (!done); } // // Add configuration file variable bindings. // void D_BindVariables(void) { int i; M_ApplyPlatformDefaults(); I_BindInputVariables(); I_BindVideoVariables(); I_BindJoystickVariables(); I_BindSoundVariables(); M_BindBaseControls(); M_BindWeaponControls(); M_BindMapControls(); M_BindMenuControls(); M_BindStrifeControls(); // haleyjd 09/01/10: [STRIFE] M_BindChatControls(MAXPLAYERS); // haleyjd 20130915: Strife chat keys key_multi_msgplayer[0] = '1'; key_multi_msgplayer[1] = '2'; key_multi_msgplayer[2] = '3'; key_multi_msgplayer[3] = '4'; key_multi_msgplayer[4] = '5'; key_multi_msgplayer[5] = '6'; key_multi_msgplayer[6] = '7'; key_multi_msgplayer[7] = '8'; NET_BindVariables(); // haleyjd 08/29/10: [STRIFE] // * Added voice volume // * Added back flat // * Removed show_messages // * Added show_talk // fraggle 03/06/10: [STRIFE] // * Removed detailLevel // * screenblocks -> screensize // * Added nickname, comport M_BindIntVariable("mouse_sensitivity", &mouseSensitivity); M_BindIntVariable("sfx_volume", &sfxVolume); M_BindIntVariable("music_volume", &musicVolume); M_BindIntVariable("voice_volume", &voiceVolume); M_BindIntVariable("show_talk", &dialogshowtext); M_BindIntVariable("screensize", &screenblocks); M_BindIntVariable("snd_channels", &snd_channels); M_BindIntVariable("vanilla_savegame_limit", &vanilla_savegame_limit); M_BindIntVariable("vanilla_demo_limit", &vanilla_demo_limit); M_BindIntVariable("show_endoom", &show_endoom); M_BindIntVariable("show_diskicon", &show_diskicon); M_BindIntVariable("graphical_startup", &graphical_startup); M_BindStringVariable("back_flat", &back_flat); M_BindStringVariable("nickname", &nickname); M_BindIntVariable("comport", &comport); // Multiplayer chat macros for (i=0; i<10; ++i) { char buf[12]; M_snprintf(buf, sizeof(buf), "chatmacro%i", i); M_BindStringVariable(buf, &chat_macros[i]); } } // // D_GrabMouseCallback // // Called to determine whether to grab the mouse pointer // boolean D_GrabMouseCallback(void) { // Drone players don't need mouse focus if (drone) return false; // 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; } // During startup, never grab the mouse. static boolean D_StartupGrabCallback(void) { return false; } // // D_DoomLoop // // haleyjd 08/23/10: [STRIFE] Verified unmodified. // void D_DoomLoop (void) { if (demorecording) G_BeginRecording (); main_loop_started = true; TryRunTics(); if (!showintro) { I_InitGraphics(); } if (show_diskicon) { V_EnableLoadingDisk("STDISK", SCREENWIDTH - LOADING_DISK_W, 3); } I_SetGrabMouseCallback(D_GrabMouseCallback); V_RestoreBuffer(); R_ExecuteSetViewSize(); D_StartGameLoop(); if (testcontrols) { wipegamestate = gamestate; } while (1) { // frame syncronous IO operations I_StartFrame (); // process one or more tics TryRunTics (); // will run at least one tic S_UpdateSounds (players[consoleplayer].mo);// move positional sounds // Update display, next frame, with current state. if (screenvisible) D_Display (); } } // // DEMO LOOP // int demosequence; int pagetic; const char *pagename; // // D_PageTicker // Handles timing for warped projection // // haleyjd 08/22/2010: [STRIFE] verified unmodified // void D_PageTicker (void) { if (--pagetic < 0) D_AdvanceDemo (); } // // D_PageDrawer // // haleyjd 08/22/2010: [STRIFE] verified unmodified // void D_PageDrawer (void) { V_DrawPatch (0, 0, W_CacheLumpName(pagename, PU_CACHE)); } // // D_AdvanceDemo // Called after each demo or intro demosequence finishes // // haleyjd 08/22/2010: [STRIFE] verified unmodified // void D_AdvanceDemo (void) { advancedemo = true; } // // This cycles through the demo sequences. // FIXME - version dependend demo numbers? // // [STRIFE] Modified for the opening slideshow and the exit screen // void D_DoAdvanceDemo (void) { players[consoleplayer].playerstate = PST_LIVE; // not reborn advancedemo = false; usergame = false; // no save / end game here paused = false; gameaction = ga_nothing; // villsa 09/12/10: [STRIFE] converted pagetics to ticrate switch (demosequence) { case -5: // exit the game I_Quit(); return; case -4: // show exit screen menuactive = false; pagetic = 3*TICRATE; gamestate = GS_DEMOSCREEN; pagename = DEH_String("PANEL7"); S_StartMusic(mus_fast); if(isdemoversion) demosequence = -3; // show Velocity logo else demosequence = -5; // exit return; case -3: // show Velocity logo for demo version pagetic = 6*TICRATE; gamestate = GS_DEMOSCREEN; pagename = DEH_String("vellogo"); demosequence = -5; // exit return; case -2: // title screen pagetic = 6*TICRATE; gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLEPIC"); S_StartMusic(mus_logo); demosequence = -1; // start intro cinematic return; case -1: // start of intro cinematic pagetic = 10; gamestate = GS_DEMOSCREEN; pagename = DEH_String("PANEL0"); S_StartSound(NULL, sfx_rb2act); wipegamestate = -1; break; case 0: // Rogue logo pagetic = 4*TICRATE; gamestate = GS_DEMOSCREEN; pagename = DEH_String("RGELOGO"); wipegamestate = -1; break; case 1: pagetic = 7*TICRATE; // The comet struck our planet without gamestate = GS_DEMOSCREEN; // warning.We lost our paradise in a pagename = DEH_String("PANEL1"); // single, violent stroke. I_StartVoice(DEH_String("pro1")); S_StartMusic(mus_intro); break; case 2: pagetic = 9*TICRATE; // The impact released a virus which gamestate = GS_DEMOSCREEN; // swept through the land and killed pagename = DEH_String("PANEL2"); // millions. They turned out to be I_StartVoice(DEH_String("pro2")); // the lucky ones... break; case 3: pagetic = 12*TICRATE; // For those that did not die became gamestate = GS_DEMOSCREEN; // mutations of humanity. Some became pagename = DEH_String("PANEL3"); // fanatics who heard the voice of a I_StartVoice(DEH_String("pro3")); // malignant God in their heads, and break; // called themselves the Order. case 4: pagetic = 11*TICRATE; // Those of us who were deaf to this pagename = DEH_String("PANEL4"); // voice suffer horribly and are gamestate = GS_DEMOSCREEN; // forced to serve these ruthless I_StartVoice(DEH_String("pro4")); // psychotics, who wield weapons more break; // powerful than anything we can muster. case 5: pagetic = 10*TICRATE; // They destroy our women and children, gamestate = GS_DEMOSCREEN; // so that we must hide them underground, pagename = DEH_String("PANEL5"); // and live like animals in constant I_StartVoice(DEH_String("pro5")); // fear for our lives. break; case 6: // But there are whispers of discontent. pagetic = 16*TICRATE; // If we organize, can we defeat our gamestate = GS_DEMOSCREEN; // masters? Weapons are being stolen, pagename = DEH_String("PANEL6"); // soldiers are being trained. A I_StartVoice(DEH_String("pro6")); // Movement is born! Born of lifelong break; // STRIFE! case 7: // titlepic again - unused... pagetic = 9*TICRATE; gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLEPIC"); wipegamestate = -1; break; case 8: // demo ClearTmp(); pagetic = 9*TICRATE; G_DeferedPlayDemo(DEH_String("demo1")); break; case 9: // velocity logo? - unused... pagetic = 6*TICRATE; gamestate = GS_DEMOSCREEN; pagename = DEH_String("vellogo"); wipegamestate = -1; break; case 10: // credits gamestate = GS_DEMOSCREEN; pagetic = 12*TICRATE; pagename = DEH_String("CREDIT"); wipegamestate = -1; break; default: break; } ++demosequence; if(demosequence > 11) demosequence = -2; if(demosequence == 7 || demosequence == 9) ++demosequence; } // // D_StartTitle // // [STRIFE] // haleyjd 09/11/10: Small modifications for new demo sequence. // void D_StartTitle (void) { gamestate = GS_DEMOSCREEN; gameaction = ga_nothing; demosequence = -2; D_AdvanceDemo (); } // // D_QuitGame // // [STRIFE] New function // haleyjd 09/11/10: Sets up the quit game snippet powered by the // demo sequence. // void D_QuitGame(void) { gameaction = ga_nothing; demosequence = -4; D_AdvanceDemo(); } // Strings for dehacked replacements of the startup banner // // These are from the original source: some of them are perhaps // not used in any dehacked patches static const char *banners[] = { // strife1.wad: " " "STRIFE: Quest for the Sigil v1.2" " " }; // // Get game name: if the startup banner has been replaced, use that. // Otherwise, use the name given // static char *GetGameName(char *gamename) { size_t i; const char *deh_sub; for (i=0; i 1996 || date.day > 15 && date.month > 4) I_Error("Data error! Corrupted WAD File!"); serial_year = serialnum / 10000; serial_month = serialnum / 100 - 100 * serial_year; if(date.year < serial_year || date.day < serialnum - 100 * serial_month - 10000 * serial_year && date.month < serial_month) I_Error("Bad wadfile"); } #endif // Set the gamedescription string void D_SetGameDescription(void) { gamedescription = GetGameName("Strife: Quest for the Sigil"); } // print title for every printed line static char title[128] = ""; static void InitTitleString(void) { switch (gameversion) { case exe_strife_1_2: DEH_snprintf(title, sizeof(title), " " "STRIFE: Quest for the Sigil v1.2" " " ); break; case exe_strife_1_31: default: DEH_snprintf(title, sizeof(title), " " "STRIFE: Quest for the Sigil v1.31" " " ); break; } } static boolean D_AddFile(char *filename) { wad_file_t *handle; printf(" adding %s\n", filename); handle = W_AddFile(filename); return handle != NULL; } // Copyright message banners // Some dehacked mods replace these. These are only displayed if they are // replaced by dehacked. // haleyjd 08/22/2010: [STRIFE] altered to match strings from binary static const char *copyright_banners[] = { "===========================================================================\n" "ATTENTION: This version of STRIFE has extra files added to it.\n" " You will not receive technical support for modified games.\n" "===========================================================================\n", "===========================================================================\n" " This version is NOT SHAREWARE, do not distribute!\n" " Please report software piracy to the SPA: 1-800-388-PIR8\n" "===========================================================================\n", "===========================================================================\n" " Shareware!\n" "===========================================================================\n" }; // Prints a message only if it has been modified by dehacked. void PrintDehackedBanners(void) { size_t i; for (i=0; i // @category compat // // Emulate a specific version of Strife. Valid values are "1.2" and "1.31". // p = M_CheckParmWithArgs("-gameversion", 1); if (p) { for (i=0; gameversions[i].description != NULL; ++i) { if (!strcmp(myargv[p+1], gameversions[i].cmdline)) { gameversion = gameversions[i].version; break; } } if (gameversions[i].description == NULL) { printf("Supported game versions:\n"); for (i=0; gameversions[i].description != NULL; ++i) { printf("\t%s (%s)\n", gameversions[i].cmdline, gameversions[i].description); } I_Error("Unknown game version '%s'", myargv[p+1]); } } else { gameversion = exe_strife_1_31; } } void PrintGameVersion(void) { int i; for (i=0; gameversions[i].description != NULL; ++i) { if (gameversions[i].version == gameversion) { printf("Emulating the behavior of the " "'%s' executable.\n", gameversions[i].description); break; } } } // Function called at exit to display the ENDOOM screen static void D_Endoom(void) { byte *endoom; // Don't show ENDOOM if we have it disabled, or we're running // in screensaver or control test mode. Only show it once the // game has actually started. if (!show_endoom || !main_loop_started || screensaver_mode || testcontrols) { return; } // haleyjd 08/27/10: [STRIFE] ENDOOM -> ENDSTRF endoom = W_CacheLumpName(DEH_String("ENDSTRF"), PU_STATIC); I_Endoom(endoom); } // // D_GetCursorColumn // static int D_GetCursorColumn(void) { int x, y; TXT_GetXY(&x, &y); return x; } // // D_GetCursorRow // static int D_GetCursorRow(void) { int x, y; TXT_GetXY(&x, &y); return y; } // // D_SetCursorPosition // static void D_SetCursorPosition(int column, int row) { TXT_GotoXY(column, row); } // // D_SetChar // static void D_SetChar(char c) { int x, y; // Backup position TXT_GetXY(&x, &y); TXT_PutChar(c); // Restore position TXT_GotoXY(x, y); } // // D_DrawText // static void D_DrawText(const char *string, int bc, int fc) { int column; int row; int i; if (!using_text_startup) { return; } // Set text color TXT_BGColor(bc, 0); TXT_FGColor(fc); // Get column position column = D_GetCursorColumn(); // Get row position row = D_GetCursorRow(); for (i = 0; i < strlen(string); i++) { // Set character D_SetChar(string[i]); // Check cursor position if (++column >= 80) column = 0; // Set postition D_SetCursorPosition(column, row); } } //============================================================================= // // haleyjd: Chocolate Strife Specifics // // None of the code in here is from the original executable, but is needed for // other reasons. // // D_PatchClipCallback // // haleyjd 08/28/10: Clip patches to the framebuffer without errors. // Returns false if V_DrawPatch should return without drawing. // boolean D_PatchClipCallback(patch_t *patch, int x, int y) { // note that offsets were already accounted for in V_DrawPatch return (x >= 0 && y >= 0 && x + SHORT(patch->width) <= ORIGWIDTH && y + SHORT(patch->height) <= ORIGHEIGHT); } // // D_InitChocoStrife // // haleyjd 08/28/10: Take care of some Strife-specific initialization // that is necessitated by Chocolate Doom issues, such as setting global // callbacks. // static void D_InitChocoStrife(void) { // set the V_DrawPatch clipping callback V_SetPatchClipCallback(D_PatchClipCallback); } // // STRIFE Graphical Intro Sequence // #define MAXINTROPROGRESS 69 static int introprogress; // track the progress of the intro static byte *rawgfx_startup0; // raw linear gfx for intro static byte *rawgfx_startp[4]; static byte *rawgfx_startlz[2]; static byte *rawgfx_startbot; // // D_IntroBackground // // [STRIFE] New function // haleyjd 20110206: Strife only drew this once, but for supporting double- // buffered or page-flipped surfaces it is best to redraw the entire screen // every frame. // static void D_IntroBackground(void) { if(!showintro) return; // Fill the background entirely (wasn't needed in vanilla) V_DrawFilledBox(0, 0, SCREENWIDTH, SCREENHEIGHT, 0); // Strife cleared the screen somewhere in the low-level code between the // intro and the titlescreen, so this is to take care of that and get // proper fade-in behavior on the titlescreen if(introprogress >= MAXINTROPROGRESS) { I_FinishUpdate(); return; } // Draw a 95-pixel rect from STARTUP0 starting at y=57 to (0,41) on the // screen (this was a memcpy directly to 0xA3340 in low DOS memory) V_DrawScaledBlock(0, 41, 320, 95, rawgfx_startup0 + (320*57)); } // // D_InitIntroSequence // // [STRIFE] New function // haleyjd 20110206: Initialize the graphical introduction sequence // static void D_InitIntroSequence(void) { byte *textScreen; char string[80]; if (devparm || !graphical_startup || testcontrols) { using_text_startup = false; showintro = false; return; } if(showintro) { // In vanilla Strife, Mode 13h was initialized directly in D_DoomMain. // We have to be a little more courteous of the low-level code here. I_SetGrabMouseCallback(D_StartupGrabCallback); I_InitGraphics(); V_RestoreBuffer(); // make the V_ routines work // Load all graphics rawgfx_startup0 = W_CacheLumpName("STARTUP0", PU_STATIC); rawgfx_startp[0] = W_CacheLumpName("STRTPA1", PU_STATIC); rawgfx_startp[1] = W_CacheLumpName("STRTPB1", PU_STATIC); rawgfx_startp[2] = W_CacheLumpName("STRTPC1", PU_STATIC); rawgfx_startp[3] = W_CacheLumpName("STRTPD1", PU_STATIC); rawgfx_startlz[0] = W_CacheLumpName("STRTLZ1", PU_STATIC); rawgfx_startlz[1] = W_CacheLumpName("STRTLZ2", PU_STATIC); rawgfx_startbot = W_CacheLumpName("STRTBOT", PU_STATIC); // Draw the background D_IntroBackground(); using_text_startup = false; } else { if (!TXT_Init()) { using_text_startup = false; return; } I_InitWindowTitle(); I_InitWindowIcon(); // Clear screen textScreen = TXT_GetScreenData(); memset(textScreen, 0, 4000); using_text_startup = true; // Print title D_SetCursorPosition(0, 0); D_DrawText(title, TXT_COLOR_GREEN, TXT_COLOR_BLACK); DEH_snprintf(string, sizeof(string), "Rogue Entertainment"); D_SetCursorPosition(40 - strlen(string) / 2, 5); D_DrawText(string, TXT_COLOR_BLUE, TXT_COLOR_GREEN); DEH_snprintf(string, sizeof(string), "and"); D_SetCursorPosition(40 - strlen(string) / 2, 7); D_DrawText(string, TXT_COLOR_BLUE, TXT_COLOR_GREEN); DEH_snprintf(string, sizeof(string), "Velocity Games"); D_SetCursorPosition(40 - strlen(string) / 2, 9); D_DrawText(string, TXT_COLOR_BLUE, TXT_COLOR_GREEN); DEH_snprintf(string, sizeof(string), "present"); D_SetCursorPosition(40 - strlen(string) / 2, 11); D_DrawText(string, TXT_COLOR_BLUE, TXT_COLOR_GREEN); DEH_snprintf(string, sizeof(string), "S T R I F E"); D_SetCursorPosition(40 - strlen(string) / 2, 14); D_DrawText(string, TXT_COLOR_BLUE, TXT_COLOR_GREEN); DEH_snprintf(string, sizeof(string), "Loading..."); D_SetCursorPosition(40 - strlen(string) / 2, 17); D_DrawText(string, TXT_COLOR_BLUE, TXT_COLOR_GREEN); DEH_snprintf(string, sizeof(string), "[ ]"); D_SetCursorPosition(14, 18); D_DrawText(string, TXT_COLOR_BLUE, TXT_COLOR_GREEN); TXT_UpdateScreen(); } } // // D_DrawIntroSequence // // [STRIFE] New function // haleyjd 20110206: Refresh the intro sequence // static void D_DrawIntroSequence(void) { int laserpos; int robotpos; int i; if (showintro) { D_IntroBackground(); // haleyjd: refresh the background // Laser position laserpos = (200 * introprogress / MAXINTROPROGRESS) + 60; // BUG: (?) Due to this clip, the laser never even comes close to // touching the peasant; confirmed with vanilla. This MAY have been // intentional, for effect, however, since no death frames are shown // either... kind of a black-out death. if (laserpos > 200) laserpos = 200; // Draw the laser // Blitted 16 bytes for 16 rows starting at 705280 + laserpos // (705280 - 0xA0000) / 320 == 156 V_DrawBlock(laserpos, 156, 16, 16, rawgfx_startlz[laserpos % 2]); // Robot position robotpos = laserpos % 5 - 2; // Draw the robot // Blitted 48 bytes for 48 rows starting at 699534 + (320*robotpos) // 699534 - 0xA0000 == 44174, which % 320 == 14, / 320 == 138 V_DrawScaledBlock(14, 138 + robotpos, 48, 48, rawgfx_startbot); // Draw the peasant // Blitted 32 bytes for 64 rows starting at 699142 // 699142 - 0xA0000 == 43782, which % 320 == 262, / 320 == 136 V_DrawBlock(262, 136, 32, 64, rawgfx_startp[laserpos % 4]); I_FinishUpdate(); } else if (using_text_startup) { // Laser position laserpos = 50 * introprogress / MAXINTROPROGRESS; if (laserpos > 50) { laserpos = 50; } for (i = 0; i < laserpos; i++) { D_SetCursorPosition(15 + i, 18); D_DrawText("#", TXT_COLOR_GREEN, TXT_COLOR_BLUE); } I_Sleep(10); TXT_UpdateScreen(); } } // // D_IntroTick // // Advance the intro sequence // void D_IntroTick(void) { static boolean didsound = false; // haleyjd 20120209 if(devparm) return; ++introprogress; if(introprogress >= MAXINTROPROGRESS) { D_IntroBackground(); // haleyjd: clear the bg anyway // haleyjd 20120209: This isn't 100% true to vanilla because vanilla // would play this sound a half-dozen times. BUT, in vanilla, for // whatever reason, under DMX, playing the same sound multiple times // doesn't add up violently like it does under SDL_mixer. This means // that without this one-time limitation, the sound is far too loud. if(!didsound) { S_StartSound(NULL, sfx_psdtha); didsound = true; } } else D_DrawIntroSequence(); } // // End Chocolate Strife Specifics // //============================================================================= static void G_CheckDemoStatusAtExit (void) { G_CheckDemoStatus(); } // // D_DoomMain // void D_DoomMain (void) { int p; char file[256]; char demolumpname[9]; I_AtExit(D_Endoom, false); // haleyjd 20110206 [STRIFE]: -nograph parameter //! // @vanilla // // Disable graphical introduction sequence // if (M_ParmExists("-nograph")) showintro = false; // Undocumented: // Invoked by setup to test the controls. if (M_ParmExists("-testcontrols")) { testcontrols = true; showintro = false; } // haleyjd 20110206: Moved up -devparm for max visibility //! // @vanilla // // Developer mode. Implies -nograph. // devparm = M_CheckParm ("-devparm"); // print banner I_PrintBanner(PACKAGE_STRING); //DEH_printf("Z_Init: Init zone memory allocation daemon. \n"); [STRIFE] removed Z_Init (); //! // @category net // // Start a dedicated server, routing packets but not participating // in the game itself. // if (M_CheckParm("-dedicated") > 0) { printf("Dedicated server mode.\n"); NET_DedicatedServer(); // Never returns } //! // @category net // // Query the Internet master server for a global list of active // servers. // if (M_CheckParm("-search")) { NET_MasterQuery(); exit(0); } //! // @arg
// @category net // // Query the status of the server running on the given IP // address. // p = M_CheckParmWithArgs("-query", 1); if (p) { NET_QueryAddress(myargv[p+1]); exit(0); } //! // @category net // // Search the local LAN for running servers. // if (M_CheckParm("-localsearch")) { NET_LANQuery(); exit(0); } //! // @category game // @vanilla // // Disable monsters. // nomonsters = M_CheckParm ("-nomonsters"); //! // @category obscure // @vanilla // // Set Rogue playtesting mode (godmode, noclip toggled by backspace) // workparm = M_CheckParm ("-work"); //! // @category obscure // @vanilla // // Flip player gun sprites (broken). // flipparm = M_CheckParm ("-flip"); //! // @category game // @vanilla // // Respawn monsters after they are killed. // respawnparm = M_CheckParm ("-respawn"); //! // @category game // @vanilla // // Items respawn at random locations // randomparm = M_CheckParm ("-random"); //! // @category game // @vanilla // // Monsters move faster. // fastparm = M_CheckParm ("-fast"); I_DisplayFPSDots(devparm); // haleyjd 20110206 [STRIFE]: -devparm implies -nograph if(devparm) showintro = false; // Note: Vanilla Strife does not understand the -deathmatch command // line parameter. deathmatch=1 is the default behavior when // playing a netgame. //! // @category net // @vanilla // // Start a deathmatch game. Weapons do not stay in place and // all items respawn after 30 seconds. // if (M_CheckParm ("-altdeath")) deathmatch = 2; if (devparm) DEH_printf(D_DEVSTR); // find which dir to use for config files #ifdef _WIN32 //! // @category obscure // @platform windows // @vanilla // // Save configuration data and savegames in c:\strife.cd, // allowing play from CD. // if (M_CheckParm("-cdrom") > 0) { printf(D_CDROM); // haleyjd 08/22/2010: [STRIFE] Use strife.cd folder for -cdrom M_SetConfigDir("c:\\strife.cd\\"); } else #endif { // Auto-detect the configuration dir. M_SetConfigDir(NULL); } //! // @category game // @arg // @vanilla // // Turbo mode. The player's speed is multiplied by x%. If unspecified, // x defaults to 200. Values are rounded up to 10 and down to 400. // if ( (p=M_CheckParm ("-turbo")) ) { int scale = 200; extern int forwardmove[2]; extern int sidemove[2]; if (p 400) scale = 400; DEH_printf("turbo scale: %i%%\n", scale); forwardmove[0] = forwardmove[0]*scale/100; forwardmove[1] = forwardmove[1]*scale/100; sidemove[0] = sidemove[0]*scale/100; sidemove[1] = sidemove[1]*scale/100; } // init subsystems // DEH_printf("V_Init: allocate screens.\n"); [STRIFE] removed V_Init (); // Load configuration files before initialising other subsystems. // haleyjd 08/22/2010: [STRIFE] - use strife.cfg // DEH_printf("M_LoadDefaults: Load system defaults.\n"); [STRIFE] removed M_SetConfigFilenames("strife.cfg", PROGRAM_PREFIX "strife.cfg"); D_BindVariables(); M_LoadDefaults(); // Save configuration at exit. I_AtExit(M_SaveDefaults, false); // Find the main IWAD file and load it. iwadfile = D_FindIWAD(IWAD_MASK_STRIFE, &gamemission); // None found? if (iwadfile == NULL) { I_Error("Game mode indeterminate. No IWAD file was found. Try\n" "specifying one with the '-iwad' command line parameter.\n"); } modifiedgame = false; if(devparm) // [STRIFE] Devparm only DEH_printf("W_Init: Init WADfiles.\n"); D_AddFile(iwadfile); W_CheckCorrectIWAD(strife); D_IdentifyVersion(); //! // @category mod // // Disable auto-loading of .wad files. // if (!M_ParmExists("-noautoload")) { char *autoload_dir; autoload_dir = M_GetAutoloadDir("strife1.wad"); DEH_AutoLoadPatches(autoload_dir); W_AutoLoadWADs(autoload_dir); free(autoload_dir); } // Load dehacked patches specified on the command line. DEH_ParseCommandLine(); // Load PWAD files. modifiedgame = W_ParseCommandLine(); // [STRIFE] serial number output if(devparm) { char msgbuf[80]; char *serial = W_CacheLumpName("SERIAL", PU_CACHE); int serialnum = atoi(serial); DEH_snprintf(msgbuf, sizeof(msgbuf), "Wad Serial Number: %d:", serialnum); printf("%s\n", msgbuf); } // add any files specified on the command line with -file wadfile // to the wad list // // Debug: // W_PrintDirectory(); //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp. // p = M_CheckParmWithArgs ("-playdemo", 1); if (!p) { //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp, determining the framerate // of the screen. // p = M_CheckParmWithArgs("-timedemo", 1); } if (p) { char *uc_filename = strdup(myargv[p + 1]); M_ForceUppercase(uc_filename); // With Vanilla you have to specify the file without extension, // but make that optional. if (M_StringEndsWith(uc_filename, ".LMP")) { M_StringCopy(file, myargv[p + 1], sizeof(file)); } else { DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p+1]); } free(uc_filename); if (D_AddFile (file)) { M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name, sizeof(demolumpname)); } else { // If file failed to load, still continue trying to play // the demo in the same way as Vanilla Doom. This makes // tricks like "-playdemo demo1" possible. M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname)); } printf("Playing demo %s.\n", file); } I_AtExit(G_CheckDemoStatusAtExit, true); // Generate the WAD hash table. Speed things up a bit. W_GenerateHashTable(); InitGameVersion(); InitTitleString(); D_SetGameDescription(); I_SetWindowTitle(gamedescription); savegamedir = M_GetSaveGameDir("strife1.wad"); // fraggle 20130405: I_InitTimer is needed here for the netgame // startup. Start low-level sound init here too. I_InitTimer(); I_InitSound(true); I_InitMusic(); if(devparm) // [STRIFE] printf ("NET_Init: Init network subsystem.\n"); NET_Init(); D_ConnectNetGame(); // haleyjd 20110210: Create Strife hub save folders M_CreateSaveDirs(savegamedir); I_GraphicsCheckCommandLine(); // haleyjd 20110206 [STRIFE] Startup the introduction sequence D_InitIntroSequence(); // haleyjd 20110924: moved S_Init up to here if(devparm) // [STRIFE] DEH_printf("S_Init: Setting up sound.\n"); S_Init (sfxVolume * 8, musicVolume * 8, voiceVolume * 8); // [STRIFE]: voice D_IntroTick(); // [STRIFE] // Check for -file in shareware if (modifiedgame) { // These are the lumps that will be checked in IWAD, // if any one is not present, execution will be aborted. // haleyjd 08/22/2010: [STRIFE] Check for Strife lumps. char name[3][8]= { "map23", "map30", "ROB3E1" }; int i; // haleyjd 08/22/2010: [STRIFE] Changed string to match binary // STRIFE-FIXME: Needs to test isdemoversion variable if ( gamemode == shareware) I_Error(DEH_String("\nYou cannot -file with the demo " "version. You must buy the real game!")); // Check for fake IWAD with right name, // but w/o all the lumps of the registered version. // STRIFE-FIXME: Needs to test isregistered variable if (gamemode == registered) for (i = 0; i < 3; i++) if (W_CheckNumForName(name[i])<0) I_Error(DEH_String("\nThis is not the registered version.")); } D_IntroTick(); // [STRIFE] // get skill / episode / map from parms startskill = sk_easy; // [STRIFE]: inits to sk_easy startepisode = 1; startmap = 1; autostart = false; //! // @category game // @arg // @vanilla // // Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of // 0 disables all monsters. // p = M_CheckParmWithArgs("-skill", 1); if (p) { startskill = myargv[p+1][0]-'1'; autostart = true; } // [STRIFE] no such thing in Strife // // // @category game // // @arg // // @vanilla // // // // Start playing on episode n (1-4) // // // p = M_CheckParmWithArgs("-episode", 1); // if (p) // { // startepisode = myargv[p+1][0]-'0'; // startmap = 1; // autostart = true; // } timelimit = 0; //! // @arg // @category net // @vanilla // // For multiplayer games: exit each level after n minutes. // p = M_CheckParmWithArgs("-timer", 1); if (p) { timelimit = atoi(myargv[p+1]); printf("timer: %i\n", timelimit); } //! // @category net // @vanilla // // Austin Virtual Gaming: end levels after 20 minutes. // p = M_CheckParm ("-avg"); if (p) { timelimit = 20; } //! // @category game // @arg x // @vanilla // // Start a game immediately, warping to level x. // p = M_CheckParmWithArgs("-warp", 1); if (p) { if (gamemode == commercial) startmap = atoi (myargv[p+1]); else { startepisode = myargv[p+1][0]-'0'; if (p + 2 < myargc) { startmap = myargv[p+2][0]-'0'; } else { startmap = 1; } } autostart = true; } if (testcontrols) { startepisode = 1; startmap = 3; autostart = true; } // Check for load game parameter // We do this here and save the slot number, so that the network code // can override it or send the load slot to other players. //! // @category game // @arg // @vanilla // // Load the game in slot s. // p = M_CheckParmWithArgs("-loadgame", 1); if (p) { startloadgame = atoi(myargv[p+1]); } else { // Not loading a game startloadgame = -1; } if (W_CheckNumForName("SS_START") >= 0 || W_CheckNumForName("FF_END") >= 0) { I_PrintDivider(); printf(" WARNING: The loaded WAD file contains modified sprites or\n" " floor textures. You may want to use the '-merge' command\n" " line option instead of '-file'.\n"); } I_PrintStartupBanner(gamedescription); PrintDehackedBanners(); // haleyjd 08/28/10: Init Choco Strife stuff. D_InitChocoStrife(); // haleyjd 08/22/2010: [STRIFE] Modified string to match binary if(devparm) // [STRIFE] DEH_printf("R_Init: Loading Graphics - "); R_Init (); D_IntroTick(); // [STRIFE] if(devparm) // [STRIFE] DEH_printf("\nP_Init: Init Playloop state.\n"); P_Init (); D_IntroTick(); // [STRIFE] if(devparm) // [STRIFE] DEH_printf("I_Init: Setting up machine state.\n"); I_CheckIsScreensaver(); I_InitJoystick(); D_IntroTick(); // [STRIFE] D_IntroTick(); // [STRIFE] if(devparm) // [STRIFE] DEH_printf("M_Init: Init Menu.\n"); M_Init (); D_IntroTick(); // [STRIFE] // haleyjd 20110924: Moved S_Init up. D_IntroTick(); // haleyjd 20110220: This stuff was done in I_StartupSound in vanilla, but // we'll do it here instead so we don't have to modify the low-level shared // code with Strife-specific stuff. //! // @vanilla // // Disable voice dialog and show dialog as text instead, // even if voices.wad can be found. // if(disable_voices || M_CheckParm("-novoice")) { dialogshowtext = disable_voices = 1; } if(devparm) DEH_printf(" Play voices = %d\n", disable_voices == 0); if(devparm) // [STRIFE] DEH_printf("D_CheckNetGame: Checking network game status.\n"); D_CheckNetGame (); PrintGameVersion(); if(devparm) DEH_printf("HU_Init: Setting up heads up display.\n"); HU_Init (); D_IntroTick(); // [STRIFE] if(devparm) DEH_printf("ST_Init: Init status bar.\n"); ST_Init (); D_IntroTick(); // [STRIFE] // haleyjd [STRIFE] -statcopy used to be here... D_IntroTick(); // [STRIFE] // If Doom II without a MAP01 lump, this is a store demo. // Moved this here so that MAP01 isn't constantly looked up // in the main loop. // haleyjd 08/23/2010: [STRIFE] There is no storedemo version of Strife /* if (gamemode == commercial && W_CheckNumForName("map01") < 0) storedemo = true; */ //! // @arg // @category demo // @vanilla // // Record a demo named x.lmp. // p = M_CheckParmWithArgs("-record", 1); if (p) { G_RecordDemo (myargv[p+1]); autostart = true; } D_IntroTick(); // [STRIFE] p = M_CheckParmWithArgs("-playdemo", 1); if (p) { singledemo = true; // quit after one demo G_DeferedPlayDemo (demolumpname); D_DoomLoop (); // never returns } D_IntroTick(); // [STRIFE] p = M_CheckParmWithArgs("-timedemo", 1); if (p) { G_TimeDemo (demolumpname); D_DoomLoop (); // never returns } D_IntroTick(); // [STRIFE] if (startloadgame >= 0) { // [STRIFE]: different, for hubs M_LoadSelect(startloadgame); } D_IntroTick(); // [STRIFE] if (gameaction != ga_loadgame ) { if (autostart || netgame) G_InitNew (startskill, startmap); else D_StartTitle (); // start up intro loop } if (using_text_startup) { TXT_Shutdown(); } D_DoomLoop (); // never returns } crispy-doom-crispy-doom-5.6.4/src/strife/d_main.h000066400000000000000000000024531360717211000217230ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System specific interface stuff. // #ifndef __D_MAIN__ #define __D_MAIN__ #include "doomdef.h" // Read events from all input devices void D_ProcessEvents (void); // // BASE LEVEL // void D_PageTicker (void); void D_PageDrawer (void); void D_AdvanceDemo (void); void D_DoAdvanceDemo (void); void D_StartTitle (void); void D_QuitGame (void); // [STRIFE] void D_IntroTick(void); // [STRIFE] // // GLOBAL VARIABLES // extern gameaction_t gameaction; extern boolean isregistered; // villsa [STRIFE] extern boolean isdemoversion; // haleyjd [STRIFE] extern boolean stonecold; // villsa [STRIFE] extern boolean workparm; // villsa [STRIFE] #endif crispy-doom-crispy-doom-5.6.4/src/strife/d_net.c000066400000000000000000000153501360717211000215600ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM Network game communication and protocol, // all OS independend parts. // #include #include "d_main.h" #include "m_argv.h" #include "m_menu.h" #include "m_misc.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "g_game.h" #include "doomdef.h" #include "doomstat.h" #include "w_checksum.h" #include "deh_main.h" #include "d_loop.h" ticcmd_t *netcmds; // Called when a player leaves the game static void PlayerQuitGame(player_t *player) { static char exitmsg[80]; unsigned int player_num; player_num = player - players; // Do this the same way as Vanilla Doom does, to allow dehacked // replacements of this message M_StringCopy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg)); exitmsg[7] += player_num; playeringame[player_num] = false; players[consoleplayer].message = exitmsg; // TODO: check if it is sensible to do this: if (demorecording) { G_CheckDemoStatus (); } } static void RunTic(ticcmd_t *cmds, boolean *ingame) { extern boolean advancedemo; unsigned int i; // Check for player quits. for (i = 0; i < MAXPLAYERS; ++i) { if (!demoplayback && playeringame[i] && !ingame[i]) { PlayerQuitGame(&players[i]); } } netcmds = cmds; // check that there are players in the game. if not, we cannot // run a tic. if (advancedemo) D_DoAdvanceDemo (); M_Ticker(); G_Ticker(); } static void NullMenuTicker() { // no-op. } static loop_interface_t strife_loop_interface = { D_ProcessEvents, G_BuildTiccmd, RunTic, NullMenuTicker }; // Load game settings from the specified structure and // set global variables. static void LoadGameSettings(net_gamesettings_t *settings) { unsigned int i; deathmatch = settings->deathmatch; ticdup = settings->ticdup; startepisode = settings->episode; startmap = settings->map; startskill = settings->skill; startloadgame = settings->loadgame; lowres_turn = settings->lowres_turn; nomonsters = settings->nomonsters; fastparm = settings->fast_monsters; respawnparm = settings->respawn_monsters; timelimit = settings->timelimit; consoleplayer = settings->consoleplayer; randomparm = settings->random; if (lowres_turn) { printf("NOTE: Turning resolution is reduced; this is probably " "because there is a client recording a Vanilla demo.\n"); } for (i = 0; i < MAXPLAYERS; ++i) { playeringame[i] = i < settings->num_players; } } // Save the game settings from global variables to the specified // game settings structure. static void SaveGameSettings(net_gamesettings_t *settings) { // Fill in game settings structure with appropriate parameters // for the new game settings->deathmatch = deathmatch; settings->episode = startepisode; settings->map = startmap; settings->skill = startskill; settings->loadgame = startloadgame; settings->gameversion = gameversion; settings->nomonsters = nomonsters; settings->fast_monsters = fastparm; settings->respawn_monsters = respawnparm; settings->timelimit = timelimit; settings->random = randomparm; settings->lowres_turn = M_ParmExists("-record") && !M_ParmExists("-longtics"); } static void InitConnectData(net_connect_data_t *connect_data) { connect_data->drone = false; connect_data->max_players = MAXPLAYERS; //! // @category net // // Run as the left screen in three screen mode. // if (M_CheckParm("-left") > 0) { viewangleoffset = ANG90; connect_data->drone = true; } //! // @category net // // Run as the right screen in three screen mode. // if (M_CheckParm("-right") > 0) { viewangleoffset = ANG270; connect_data->drone = true; } // // Connect data // // Game type fields: connect_data->gamemode = gamemode; connect_data->gamemission = gamemission; // Are we recording a demo? Possibly set lowres turn mode connect_data->lowres_turn = M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0; // Read checksums of our WAD directory and dehacked information W_Checksum(connect_data->wad_sha1sum); DEH_Checksum(connect_data->deh_sha1sum); connect_data->is_freedoom = 0; } void D_ConnectNetGame(void) { net_connect_data_t connect_data; InitConnectData(&connect_data); netgame = D_InitNetGame(&connect_data); //! // @category net // // Start the game playing as though in a netgame with a single // player. This can also be used to play back single player netgame // demos. // if (M_CheckParm("-solo-net") > 0) { netgame = true; } } // // D_CheckNetGame // Works out player numbers among the net participants // void D_CheckNetGame(void) { net_gamesettings_t settings; D_RegisterLoopCallbacks(&strife_loop_interface); if (netgame) { autostart = true; } SaveGameSettings(&settings); D_StartNetGame(&settings, NULL); LoadGameSettings(&settings); // Strife games are always deathmatch, though -altdeath is // supported for respawning items. if (netgame && deathmatch == 0) { deathmatch = 1; } DEH_printf("startmap: %i, skill: %i, enemies: %i, random: %i\n", startmap, startskill, !nomonsters, randomparm); DEH_printf("player %i of %i (%i nodes)\n", consoleplayer+1, settings.num_players, settings.num_players); // Show players here; the server might have specified a time limit if (timelimit > 0 && deathmatch) { // Gross hack to work like Vanilla: if (timelimit == 20 && M_CheckParm("-avg")) { DEH_printf("Austin Virtual Gaming: Levels will end " "after 20 minutes\n"); } else { DEH_printf("Levels will end after %d minute", timelimit); if (timelimit > 1) printf("s"); printf(".\n"); } } } crispy-doom-crispy-doom-5.6.4/src/strife/d_player.h000066400000000000000000000137461360717211000223020ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __D_PLAYER__ #define __D_PLAYER__ // The player data structure depends on a number // of other structs: items (internal inventory), // animation states (closely tied to the sprites // used to represent them, unfortunately). #include "d_items.h" #include "p_pspr.h" // In addition, the player is just a special // case of the generic moving object/actor. #include "p_mobj.h" // Finally, for odd reasons, the player input // is buffered within the player data struct, // as commands per game tick. #include "d_ticcmd.h" #include "net_defs.h" // // 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, // villsa [STRIFE] new cheat // set when on fire and disable inventory CF_ONFIRE = 8, // villsa [STRIFE] new cheat // auto-use medkits CF_AUTOHEALTH = 16 } cheat_t; // haleyjd 08/30/10: [STRIFE] // Player Inventory Item Structure typedef struct inventory_s { int sprite; // a sprite number int type; // a thing type int amount; // amount being carried } inventory_t; #define NUMINVENTORY 32 // // Extended player object info: player_t // // haleyjd 08/30/10: [STRIFE] // * Transformed to match binary structure layout. // 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; short armorpoints; // [STRIFE] Changed to short // Armor type is 0-2. short armortype; // [STRIFE] Changed to short // Power ups. invinc and invis are tic counters. int powers[NUMPOWERS]; // [STRIFE] Additions: int sigiltype; // Type of Sigil carried int nukagecount; // Nukage exposure counter int questflags; // Quest bit flags int pitch; // Up/down look angle boolean centerview; // True if view should be centered inventory_t inventory[NUMINVENTORY]; // Player inventory items boolean st_update; // If true, update status bar short numinventory; // Num. active inventory items short inventorycursor; // Selected inventory item short accuracy; // Accuracy stat short stamina; // Stamina stat boolean cards[NUMCARDS]; boolean backpack; // True if button down last tic. int attackdown; int usedown; int inventorydown; // [STRIFE] Use inventory item // Frags, kills of other players. int frags[MAXPLAYERS]; weapontype_t readyweapon; // Is wp_nochange if not changing. weapontype_t pendingweapon; boolean weaponowned[NUMWEAPONS]; int ammo[NUMAMMO]; int maxammo[NUMAMMO]; // Bit flags, for cheats and debug. // See cheat_t, above. int cheats; // Refired shots are less accurate. int refire; // For intermission stats. short killcount; // [STRIFE] Changed to short //int itemcount; // [STRIFE] Eliminated these. //int secretcount; // Hint messages. const char *message; // For screen flashing (red or bright). int damagecount; int bonuscount; // Who did damage (NULL for floors/ceilings). mobj_t* attacker; // So gun flashes light up areas. int extralight; // Current PLAYPAL, ??? // can be set to REDCOLORMAP for pain, etc. int fixedcolormap; // Player skin colorshift, // 0-3 for which color to draw player. //int colormap; [STRIFE] no such? or did it become the below? // [STRIFE] For use of teleport beacons short allegiance; // Overlay view sprites (gun, etc). pspdef_t psprites[NUMPSPRITES]; // [STRIFE] Inefficient means of tracking automap state on all maps boolean mapstate[40]; // True if secret level has been done. //boolean didsecret; [STRIFE] Removed this. } player_t; // // INTERMISSION // Structure passed e.g. to WI_Start(wb) // typedef struct { boolean in; // whether the player is in game // Player stats, kills, collected items etc. int skills; int sitems; int ssecret; int stime; int frags[4]; int score; // current score on entry, modified on return } wbplayerstruct_t; typedef struct { int epsd; // episode # (0-2) // if true, splash the secret level boolean didsecret; // previous and next levels, origin 0 int last; int next; int maxkills; int maxitems; int maxsecret; int maxfrags; // the par time int partime; // index of this player in game int pnum; wbplayerstruct_t plyr[MAXPLAYERS]; } wbstartstruct_t; #endif crispy-doom-crispy-doom-5.6.4/src/strife/d_textur.h000066400000000000000000000016401360717211000223270ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Typedefs related to to textures etc., // isolated here to make it easier separating modules. // #ifndef __D_TEXTUR__ #define __D_TEXTUR__ #include "doomtype.h" // // Flats? // // a pic is an unmasked block of pixels typedef struct { byte width; byte height; byte data; } pic_t; #endif crispy-doom-crispy-doom-5.6.4/src/strife/d_think.h000066400000000000000000000027111360717211000221110ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // MapObj data. Map Objects or mobjs are actors, entities, // thinker, take-your-pick... anything that moves, acts, or // suffers state changes of more or less violent nature. // #ifndef __D_THINK__ #define __D_THINK__ // // Experimental stuff. // To compile this as "ANSI C with classes" // we will need to handle the various // action functions cleanly. // typedef void (*actionf_v)(); typedef void (*actionf_p1)( void* ); typedef void (*actionf_p2)( void*, void* ); typedef union { actionf_v acv; actionf_p1 acp1; actionf_p2 acp2; } actionf_t; // Historically, "think_t" is yet another // function pointer to a routine to handle // an actor. typedef actionf_t think_t; // Doubly linked list of actors. typedef struct thinker_s { struct thinker_s* prev; struct thinker_s* next; think_t function; } thinker_t; #endif crispy-doom-crispy-doom-5.6.4/src/strife/deh_ammo.c000066400000000000000000000044271360717211000222430ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Ammo" sections in dehacked files // #include #include #include #include "doomdef.h" #include "doomtype.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "p_local.h" static void *DEH_AmmoStart(deh_context_t *context, char *line) { int ammo_number = 0; if (sscanf(line, "Ammo %i", &ammo_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (ammo_number < 0 || ammo_number >= NUMAMMO) { DEH_Warning(context, "Invalid ammo number: %i", ammo_number); return NULL; } return &maxammo[ammo_number]; } static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; int ivalue; int ammo_number; if (tag == NULL) return; ammo_number = ((int *) tag) - maxammo; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); // maxammo if (!strcasecmp(variable_name, "Per ammo")) clipammo[ammo_number] = ivalue; else if (!strcasecmp(variable_name, "Max ammo")) maxammo[ammo_number] = ivalue; else { DEH_Warning(context, "Field named '%s' not found", variable_name); } } static void DEH_AmmoSHA1Hash(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "am_map.h" #include "st_stuff.h" typedef struct { const char *name; cheatseq_t *seq; } deh_cheat_t; static deh_cheat_t allcheats[] = { // haleyjd 20110224: filled in all cheats {"Change music", &cheat_mus }, {"Level Warp", &cheat_clev }, {"Stealth Boots", &cheat_stealth }, {"Sigil piece", &cheat_lego }, {"FPS", &cheat_mypos }, {"TeleportMapSpot", &cheat_scoot }, {"Gold&StatTokens", &cheat_midas }, {"God mode", &cheat_god }, {"Keys", &cheat_keys }, {"Weapons & Ammo", &cheat_ammo }, {"Massacre", &cheat_nuke }, {"No Clipping", &cheat_noclip }, {"Berserk", &cheat_powerup[0] }, {"Invisibility", &cheat_powerup[1] }, {"Enviro Suit", &cheat_powerup[2] }, {"Health", &cheat_powerup[3] }, {"Backpack", &cheat_powerup[4] }, // STRIFE-FIXME/TODO: Does SeHackEd not support PUMPUP{S,T,nil}, or "DOTS" ? }; static deh_cheat_t *FindCheatByName(char *name) { size_t i; for (i=0; i= cheat->seq->sequence_len) { DEH_Warning(context, "Cheat sequence longer than supported by " "Vanilla dehacked"); break; } if (deh_apply_cheats) { cheat->seq->sequence[i] = unsvalue[i]; } ++i; // Absolute limit - don't exceed if (i >= MAX_CHEAT_LEN - cheat->seq->parameter_chars) { DEH_Error(context, "Cheat sequence too long!"); return; } } if (deh_apply_cheats) { cheat->seq->sequence[i] = '\0'; } } deh_section_t deh_section_cheat = { "Cheat", NULL, DEH_CheatStart, DEH_CheatParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/strife/deh_frame.c000066400000000000000000000075371360717211000224110ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Frame" sections in dehacked files // #include #include #include "doomtype.h" #include "d_items.h" #include "info.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "deh_mapping.h" DEH_BEGIN_MAPPING(state_mapping, state_t) DEH_MAPPING("Sprite number", sprite) DEH_MAPPING("Sprite subnumber", frame) DEH_MAPPING("Duration", tics) DEH_MAPPING("Next frame", nextstate) DEH_UNSUPPORTED_MAPPING("Action pointer") DEH_END_MAPPING static void *DEH_FrameStart(deh_context_t *context, char *line) { int frame_number = 0; state_t *state; if (sscanf(line, "Frame %i", &frame_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (frame_number < 0 || frame_number >= NUMSTATES) { DEH_Warning(context, "Invalid frame number: %i", frame_number); return NULL; } if (frame_number >= DEH_VANILLA_NUMSTATES) { DEH_Warning(context, "Attempt to modify frame %i: this will cause " "problems in Vanilla dehacked.", frame_number); } state = &states[frame_number]; return state; } // Simulate a frame overflow: Doom has 967 frames in the states[] array, but // DOS dehacked internally only allocates memory for 966. As a result, // attempts to set frame 966 (the last frame) will overflow the dehacked // array and overwrite the weaponinfo[] array instead. // // This is noticable in Batman Doom where it is impossible to switch weapons // away from the fist once selected. static void DEH_FrameOverflow(deh_context_t *context, char *varname, int value) { if (!strcasecmp(varname, "Duration")) { weaponinfo[0].ammo = value; } else if (!strcasecmp(varname, "Codep frame")) { weaponinfo[0].upstate = value; } else if (!strcasecmp(varname, "Next frame")) { weaponinfo[0].downstate = value; } else if (!strcasecmp(varname, "Unknown 1")) { weaponinfo[0].readystate = value; } else if (!strcasecmp(varname, "Unknown 2")) { weaponinfo[0].atkstate = value; } else { DEH_Error(context, "Unable to simulate frame overflow: field '%s'", varname); } } static void DEH_FrameParseLine(deh_context_t *context, char *line, void *tag) { state_t *state; char *variable_name, *value; int ivalue; if (tag == NULL) return; state = (state_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // all values are integers ivalue = atoi(value); if (state == &states[NUMSTATES - 1]) { DEH_FrameOverflow(context, variable_name, ivalue); } else { // set the appropriate field DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue); } } static void DEH_FrameSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" #include "deh_misc.h" // Dehacked: "Initial Health" // This is the initial health a player has when starting anew. // See G_PlayerReborn in g_game.c int deh_initial_health = DEH_DEFAULT_INITIAL_HEALTH; // Dehacked: "Initial bullets" // This is the number of bullets the player has when starting anew. // See G_PlayerReborn in g_game.c int deh_initial_bullets = DEH_DEFAULT_INITIAL_BULLETS; // Dehacked: "Max Health" // This is the maximum health that can be reached using medikits // alone. See P_TouchSpecialThing in p_inter.c int deh_max_health = DEH_DEFAULT_MAX_HEALTH; // Dehacked: "Max Armor" // This is the maximum armor which can be reached by picking up // armor helmets. See P_TouchSpecialThing in p_inter.c int deh_max_armor = DEH_DEFAULT_MAX_ARMOR; // Dehacked: "Green Armor Class" // This is the armor class that is given when picking up the green // armor or an armor helmet. See P_TouchSpecialThing in p_inter.c // // DOS dehacked only modifies the behavior of the green armor shirt, // the armor class set by armor helmets is not affected. int deh_green_armor_class = DEH_DEFAULT_GREEN_ARMOR_CLASS; // Dehacked: "Blue Armor Class" // This is the armor class that is given when picking up the blue // armor or a megasphere. See P_TouchSpecialThing in p_inter.c // // DOS dehacked only modifies the MegaArmor behavior and not // the MegaSphere, which always gives armor type 2. int deh_blue_armor_class = DEH_DEFAULT_BLUE_ARMOR_CLASS; // Dehacked: "Max soulsphere" // The maximum health which can be reached by picking up the // soulsphere. See P_TouchSpecialThing in p_inter.c int deh_max_soulsphere = DEH_DEFAULT_MAX_SOULSPHERE; // Dehacked: "Soulsphere health" // The amount of health bonus that picking up a soulsphere // gives. See P_TouchSpecialThing in p_inter.c int deh_soulsphere_health = DEH_DEFAULT_SOULSPHERE_HEALTH; // Dehacked: "Megasphere health" // This is what the health is set to after picking up a // megasphere. See P_TouchSpecialThing in p_inter.c int deh_megasphere_health = DEH_DEFAULT_MEGASPHERE_HEALTH; // Dehacked: "God mode health" // This is what the health value is set to when cheating using // the IDDQD god mode cheat. See ST_Responder in st_stuff.c int deh_god_mode_health = DEH_DEFAULT_GOD_MODE_HEALTH; // Dehacked: "IDFA Armor" // This is what the armor is set to when using the IDFA cheat. // See ST_Responder in st_stuff.c int deh_idfa_armor = DEH_DEFAULT_IDFA_ARMOR; // Dehacked: "IDFA Armor Class" // This is what the armor class is set to when using the IDFA cheat. // See ST_Responder in st_stuff.c int deh_idfa_armor_class = DEH_DEFAULT_IDFA_ARMOR_CLASS; // Dehacked: "IDKFA Armor" // This is what the armor is set to when using the IDKFA cheat. // See ST_Responder in st_stuff.c int deh_idkfa_armor = DEH_DEFAULT_IDKFA_ARMOR; // Dehacked: "IDKFA Armor Class" // This is what the armor class is set to when using the IDKFA cheat. // See ST_Responder in st_stuff.c int deh_idkfa_armor_class = DEH_DEFAULT_IDKFA_ARMOR_CLASS; // Dehacked: "BFG Cells/Shot" // This is the number of CELLs firing the BFG uses up. // See P_CheckAmmo and A_FireBFG in p_pspr.c int deh_bfg_cells_per_shot = DEH_DEFAULT_BFG_CELLS_PER_SHOT; // Dehacked: "Monsters infight" // This controls whether monsters can harm other monsters of the same // species. For example, whether an imp fireball will damage other // imps. The value of this in dehacked patches is weird - '202' means // off, while '221' means on. // // See PIT_CheckThing in p_map.c int deh_species_infighting = DEH_DEFAULT_SPECIES_INFIGHTING; static struct { const char *deh_name; int *value; } misc_settings[] = { {"Initial Health", &deh_initial_health}, {"Initial Bullets", &deh_initial_bullets}, {"Max Health", &deh_max_health}, {"Max Armor", &deh_max_armor}, {"LeatherArmorClass", &deh_green_armor_class}, {"Metal Armor Class", &deh_blue_armor_class}, {"Max Soulsphere", &deh_max_soulsphere}, {"Soulsphere Health", &deh_soulsphere_health}, {"Megasphere Health", &deh_megasphere_health}, {"God Mode Health", &deh_god_mode_health}, {"IDFA Armor", &deh_idfa_armor}, {"IDFA Armor Class", &deh_idfa_armor_class}, {"IDKFA Armor", &deh_idkfa_armor}, {"IDKFA Armor Class", &deh_idkfa_armor_class}, {"Mauler Cells/Shot", &deh_bfg_cells_per_shot}, }; static void *DEH_MiscStart(deh_context_t *context, char *line) { return NULL; } static void DEH_MiscParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; int ivalue; size_t i; if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); if (!strcasecmp(variable_name, "Monsters Infight")) { // See notes above. if (ivalue == 202) { deh_species_infighting = 0; } else if (ivalue == 221) { deh_species_infighting = 1; } else { DEH_Warning(context, "Invalid value for 'Monsters Infight': %i", ivalue); } return; } for (i=0; i #include #include #include "doomtype.h" #include "info.h" #include "deh_defs.h" #include "deh_io.h" #include "deh_main.h" static actionf_t codeptrs[NUMSTATES]; static int CodePointerIndex(actionf_t *ptr) { int i; for (i=0; i= NUMSTATES) { DEH_Warning(context, "Invalid frame number: %i", frame_number); return NULL; } return &states[frame_number]; } static void DEH_PointerParseLine(deh_context_t *context, char *line, void *tag) { state_t *state; char *variable_name, *value; int ivalue; if (tag == NULL) return; state = (state_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // printf("Set %s to %s for state\n", variable_name, value); // all values are integers ivalue = atoi(value); // set the appropriate field if (!strcasecmp(variable_name, "Codep frame")) { if (ivalue < 0 || ivalue >= NUMSTATES) { DEH_Warning(context, "Invalid state '%i'", ivalue); } else { state->action = codeptrs[ivalue]; } } else { DEH_Warning(context, "Unknown variable name '%s'", variable_name); } } static void DEH_PointerSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" #include "sounds.h" DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t) DEH_UNSUPPORTED_MAPPING("Offset") DEH_UNSUPPORTED_MAPPING("Zero/One") DEH_MAPPING("Value", priority) DEH_MAPPING("Zero 1", link) DEH_MAPPING("Zero 2", pitch) DEH_MAPPING("Zero 3", volume) DEH_UNSUPPORTED_MAPPING("Zero 4") DEH_MAPPING("Neg. One 1", usefulness) DEH_MAPPING("Neg. One 2", lumpnum) DEH_END_MAPPING static void *DEH_SoundStart(deh_context_t *context, char *line) { int sound_number = 0; if (sscanf(line, "Sound %i", &sound_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (sound_number < 0 || sound_number >= NUMSFX) { DEH_Warning(context, "Invalid sound number: %i", sound_number); return NULL; } if (sound_number >= DEH_VANILLA_NUMSFX) { DEH_Warning(context, "Attempt to modify SFX %i. This will cause " "problems in Vanilla dehacked.", sound_number); } return &S_sfx[sound_number]; } static void DEH_SoundParseLine(deh_context_t *context, char *line, void *tag) { sfxinfo_t *sfx; char *variable_name, *value; int ivalue; if (tag == NULL) return; sfx = (sfxinfo_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // all values are integers ivalue = atoi(value); // Set the field value DEH_SetMapping(context, &sound_mapping, sfx, variable_name, ivalue); } deh_section_t deh_section_sound = { "Sound", NULL, DEH_SoundStart, DEH_SoundParseLine, NULL, NULL, }; crispy-doom-crispy-doom-5.6.4/src/strife/deh_strife.c000066400000000000000000000031051360717211000225760ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Top-level dehacked definitions for Strife dehacked (sehacked) // #include #include "deh_defs.h" #include "deh_main.h" const char *deh_signatures[] = { "Patch File for SeHackEd v0.4", "Patch File for SeHackEd v0.3", NULL }; // deh_ammo.c: extern deh_section_t deh_section_ammo; // deh_cheat.c: extern deh_section_t deh_section_cheat; // deh_frame.c: extern deh_section_t deh_section_frame; // deh_misc.c: extern deh_section_t deh_section_misc; // deh_ptr.c: extern deh_section_t deh_section_pointer; // deh_sound.c extern deh_section_t deh_section_sound; // deh_text.c: extern deh_section_t deh_section_text; // deh_thing.c: extern deh_section_t deh_section_thing; // deh_weapon.c: extern deh_section_t deh_section_weapon; // // List of section types: // deh_section_t *deh_section_types[] = { &deh_section_ammo, &deh_section_cheat, &deh_section_frame, &deh_section_misc, &deh_section_pointer, &deh_section_sound, &deh_section_text, &deh_section_thing, &deh_section_weapon, NULL }; crispy-doom-crispy-doom-5.6.4/src/strife/deh_thing.c000066400000000000000000000066631360717211000224270ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Parses "Thing" sections in dehacked files // #include #include #include "doomtype.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" #include "info.h" DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t) DEH_MAPPING("ID #", doomednum) DEH_MAPPING("Initial frame", spawnstate) DEH_MAPPING("Hit points", spawnhealth) DEH_MAPPING("First moving frame", seestate) DEH_MAPPING("Alert sound", seesound) DEH_MAPPING("Reaction time", reactiontime) DEH_MAPPING("Attack sound", attacksound) DEH_MAPPING("Injury frame", painstate) DEH_MAPPING("Pain chance", painchance) DEH_MAPPING("Pain sound", painsound) DEH_MAPPING("Close attack frame", meleestate) DEH_MAPPING("Far attack frame", missilestate) DEH_MAPPING("Crash frame", crashstate) DEH_MAPPING("Death frame", deathstate) DEH_MAPPING("Exploding frame", xdeathstate) DEH_MAPPING("Death sound", deathsound) DEH_MAPPING("Speed", speed) DEH_MAPPING("Width", radius) DEH_MAPPING("Height", height) DEH_MAPPING("Mass", mass) DEH_MAPPING("Missile damage", damage) DEH_MAPPING("Action sound", activesound) DEH_MAPPING("Bits", flags) DEH_UNSUPPORTED_MAPPING("Name pointer") DEH_END_MAPPING static void *DEH_ThingStart(deh_context_t *context, char *line) { int thing_number = 0; mobjinfo_t *mobj; if (sscanf(line, "Thing %i", &thing_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } // dehacked files are indexed from 1 --thing_number; if (thing_number < 0 || thing_number >= NUMMOBJTYPES) { DEH_Warning(context, "Invalid thing number: %i", thing_number); return NULL; } mobj = &mobjinfo[thing_number]; return mobj; } static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag) { mobjinfo_t *mobj; char *variable_name, *value; int ivalue; if (tag == NULL) return; mobj = (mobjinfo_t *) tag; // Parse the assignment if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } // printf("Set %s to %s for mobj\n", variable_name, value); // all values are integers ivalue = atoi(value); // Set the field value DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue); } static void DEH_ThingSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include #include "doomtype.h" #include "d_items.h" #include "deh_defs.h" #include "deh_main.h" #include "deh_mapping.h" DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t) DEH_MAPPING("Ammo type", ammo) DEH_MAPPING("Deselect frame", upstate) DEH_MAPPING("Select frame", downstate) DEH_MAPPING("Bobbing frame", readystate) DEH_MAPPING("Shooting frame", atkstate) DEH_MAPPING("Firing frame", flashstate) DEH_UNSUPPORTED_MAPPING("? Unknown") DEH_END_MAPPING static void *DEH_WeaponStart(deh_context_t *context, char *line) { int weapon_number = 0; if (sscanf(line, "Weapon %i", &weapon_number) != 1) { DEH_Warning(context, "Parse error on section start"); return NULL; } if (weapon_number < 0 || weapon_number >= NUMWEAPONS) { DEH_Warning(context, "Invalid weapon number: %i", weapon_number); return NULL; } return &weaponinfo[weapon_number]; } static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag) { char *variable_name, *value; weaponinfo_t *weapon; int ivalue; if (tag == NULL) return; weapon = (weaponinfo_t *) tag; if (!DEH_ParseAssignment(line, &variable_name, &value)) { // Failed to parse DEH_Warning(context, "Failed to parse assignment"); return; } ivalue = atoi(value); DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue); } static void DEH_WeaponSHA1Sum(sha1_context_t *context) { int i; for (i=0; i #include #include "doomtype.h" #include "i_timer.h" #include "d_mode.h" // // Global parameters/defines. // // DOOM version // // haleyjd 09/28/10: Replaced with Strife version #define STRIFE_VERSION 101 // Version code for cph's longtics hack ("v1.91") #define DOOM_191_VERSION 111 // Maximum players for Strife: #define MAXPLAYERS 8 // If rangecheck is undefined, // most parameter validation debugging code will not be compiled #define RANGECHECK // 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_UNKNOWN, GS_FINALE, GS_DEMOSCREEN, } gamestate_t; typedef enum { ga_nothing, ga_loadlevel, ga_newgame, ga_loadgame, ga_savegame, ga_playdemo, ga_completed, ga_victory, ga_worlddone, ga_screenshot } gameaction_t; // // Difficulty/skill settings/filters. // // Skill flags. #define MTF_EASY 1 #define MTF_NORMAL 2 #define MTF_HARD 4 // villsa [STRIFE] standing monsters #define MTF_STAND 8 // villsa [STRIFE] don't spawn in single player #define MTF_NOTSINGLE 16 // Deaf monsters/do not react to sound. #define MTF_AMBUSH 32 // villsa [STRIFE] friendly to players #define MTF_FRIEND 64 // villsa [STRIFE] TODO - identify #define MTF_UNKNOWN1 128 // villsa [STRIFE] thing is translucent - STRIFE-TODO: But how much? #define MTF_TRANSLUCENT 256 // villsa [STRIFE] thing is more - or less? - translucent - STRIFE-TODO #define MTF_MVIS 512 // villsa [STRIFE] TODO - identify #define MTF_UNKNOWN2 1024 // // Key cards. // // villsa [STRIFE] typedef enum { key_BaseKey, // 0 key_GovsKey, // 1 key_Passcard, // 2 key_IDCard, // 3 key_PrisonKey, // 4 key_SeveredHand, // 5 key_Power1Key, // 6 key_Power2Key, // 7 key_Power3Key, // 8 key_GoldKey, // 9 key_IDBadge, // 10 key_SilverKey, // 11 key_OracleKey, // 12 key_MilitaryID, // 13 key_OrderKey, // 14 key_WarehouseKey, // 15 key_BrassKey, // 16 key_RedCrystalKey, // 17 key_BlueCrystalKey, // 18 key_ChapelKey, // 19 key_CatacombKey, // 20 key_SecurityKey, // 21 key_CoreKey, // 22 key_MaulerKey, // 23 key_FactoryKey, // 24 key_MineKey, // 25 key_NewKey5, // 26 NUMCARDS // 27 } card_t; // The defined weapons, // including a marker indicating // user has not changed weapon. // villsa [STRIFE] typedef enum { wp_fist, wp_elecbow, wp_rifle, wp_missile, wp_hegrenade, wp_flame, wp_mauler, wp_sigil, wp_poisonbow, wp_wpgrenade, wp_torpedo, NUMWEAPONS, // No pending weapon change. wp_nochange } weapontype_t; // Ammunition types defined. typedef enum { am_bullets, am_elecbolts, am_poisonbolts, am_cell, am_missiles, am_hegrenades, am_wpgrenades, NUMAMMO, am_noammo // unlimited ammo } ammotype_t; // Power up artifacts. // villsa [STRIFE] typedef enum { pw_strength, pw_invisibility, pw_ironfeet, pw_allmap, pw_communicator, pw_targeter, NUMPOWERS } powertype_t; // villsa [STRIFE] // quest numbers typedef enum { // Hex Watcom Name player_t offset tk_quest1, // 0x00000001 questflags & 1 0x4D tk_quest2, // 0x00000002 questflags & 2 tk_quest3, // 0x00000004 questflags & 4 tk_quest4, // 0x00000008 questflags & 8 tk_quest5, // 0x00000010 questflags & 10h tk_quest6, // 0x00000020 questflags & 20h tk_quest7, // 0x00000040 questflags & 40h tk_quest8, // 0x00000080 questflags & 80h tk_quest9, // 0x00000100 BYTE1(questflags) & 1 0x4E tk_quest10, // 0x00000200 BYTE1(questflags) & 2 tk_quest11, // 0x00000400 BYTE1(questflags) & 4 tk_quest12, // 0x00000800 BYTE1(questflags) & 8 tk_quest13, // 0x00001000 BYTE1(questflags) & 10h tk_quest14, // 0x00002000 BYTE1(questflags) & 20h tk_quest15, // 0x00004000 BYTE1(questflags) & 40h tk_quest16, // 0x00008000 BYTE1(questflags) & 80h tk_quest17, // 0x00010000 BYTE2(questflags) & 1 0x4F tk_quest18, // 0x00020000 BYTE2(questflags) & 2 tk_quest19, // 0x00040000 BYTE2(questflags) & 4 tk_quest20, // 0x00080000 BYTE2(questflags) & 8 tk_quest21, // 0x00100000 BYTE2(questflags) & 10h tk_quest22, // 0x00200000 BYTE2(questflags) & 20h tk_quest23, // 0x00400000 BYTE2(questflags) & 40h tk_quest24, // 0x00800000 BYTE2(questflags) & 80h tk_quest25, // 0x01000000 BYTE3(questflags) & 1 0x50 tk_quest26, // 0x02000000 BYTE3(questflags) & 2 tk_quest27, // 0x04000000 BYTE3(questflags) & 4 tk_quest28, // 0x08000000 BYTE3(questflags) & 8 tk_quest29, // 0x10000000 BYTE3(questflags) & 10h tk_quest30, // 0x20000000 BYTE3(questflags) & 20h tk_quest31, // 0x40000000 BYTE3(questflags) & 40h tk_quest32, // most likely unused tk_numquests } questtype_t; // haleyjd 09/12/10: [STRIFE] // flag values for each quest. enum { // Name Flag from bitnum Purpose, if known QF_QUEST1 = (1 << tk_quest1), // Obtained Beldin's ring QF_QUEST2 = (1 << tk_quest2), // Stole the Chalice QF_QUEST3 = (1 << tk_quest3), // Permission to visit Irale (visited Macil) QF_QUEST4 = (1 << tk_quest4), // Accepted Gov. Mourel's "messy" chore QF_QUEST5 = (1 << tk_quest5), // Accepted Gov. Mourel's "bloody" chore QF_QUEST6 = (1 << tk_quest6), // Destroyed the Power Coupling QF_QUEST7 = (1 << tk_quest7), // Killed Blue Acolytes ("Scanning Team") QF_QUEST8 = (1 << tk_quest8), // Unused; formerly, picked up Broken Coupling QF_QUEST9 = (1 << tk_quest9), // Obtained Derwin's ear QF_QUEST10 = (1 << tk_quest10), // Obtained Prison Pass QF_QUEST11 = (1 << tk_quest11), // Obtained Prison Key QF_QUEST12 = (1 << tk_quest12), // Obtained Judge Wolenick's hand QF_QUEST13 = (1 << tk_quest13), // Freed the Prisoners QF_QUEST14 = (1 << tk_quest14), // Destroyed the Power Crystal QF_QUEST15 = (1 << tk_quest15), // Obtained Guard Uniform QF_QUEST16 = (1 << tk_quest16), // Destroyed the Gate Mechanism QF_QUEST17 = (1 << tk_quest17), // Heard Macil's story about the Sigil (MAP10) QF_QUEST18 = (1 << tk_quest18), // Obtained Oracle Pass QF_QUEST19 = (1 << tk_quest19), QF_QUEST20 = (1 << tk_quest20), QF_QUEST21 = (1 << tk_quest21), // Killed Bishop QF_QUEST22 = (1 << tk_quest22), // Killed Oracle with QUEST21 set QF_QUEST23 = (1 << tk_quest23), // Killed Oracle (always given) QF_QUEST24 = (1 << tk_quest24), // Killed Macil QF_QUEST25 = (1 << tk_quest25), // Destroyed the Converter QF_QUEST26 = (1 << tk_quest26), // Killed Loremaster QF_QUEST27 = (1 << tk_quest27), // Destroyed the Computer (checked for good ending) QF_QUEST28 = (1 << tk_quest28), // Obtained Catacomb Key (checked by line type 228) QF_QUEST29 = (1 << tk_quest29), // Destroyed the Mines Transmitter QF_QUEST30 = (1 << tk_quest30), QF_QUEST31 = (1 << tk_quest31), QF_QUEST32 = (1U << tk_quest32), // Unused; BUG: Broken Coupling accidentally sets it. QF_ALLQUESTS = (QF_QUEST31 + (QF_QUEST31 - 1)) // does not include bit 32! }; // // Power up durations, // how many seconds till expiration, // assuming TICRATE is 35 ticks/second. // typedef enum { INVISTICS = (55*TICRATE), // villsa [STRIFE] changed from 60 to 55 IRONTICS = (80*TICRATE), // villsa [STRIFE] changed from 60 to 80 PMUPTICS = (80*TICRATE), // villsa [STRIFE] TARGTICS = (160*TICRATE),// villsa [STRIFE] } powerduration_t; #endif // __DOOMDEF__ crispy-doom-crispy-doom-5.6.4/src/strife/doomstat.c000066400000000000000000000017121360717211000223160ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Put all global tate variables here. // #include #include "doomstat.h" // Game Mode - identify IWAD as shareware, retail etc. GameMode_t gamemode = indetermined; GameMission_t gamemission = doom; GameVersion_t gameversion = exe_strife_1_31; const char *gamedescription; // Set if homebrew PWAD stuff has been added. boolean modifiedgame; crispy-doom-crispy-doom-5.6.4/src/strife/doomstat.h000066400000000000000000000153251360717211000223300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // All the global variables that store the internal state. // Theoretically speaking, the internal state of the engine // should be found by looking at the variables collected // here, and every relevant module will have to include // this header file. // In practice, things are a bit messy. // #ifndef __D_STATE__ #define __D_STATE__ // We need globally shared data structures, // for defining the global state variables. #include "doomdata.h" #include "d_loop.h" // We need the playr data structure as well. #include "d_player.h" // Game mode/mission #include "d_mode.h" #include "net_defs.h" // ------------------------ // Command line parameters. // extern boolean nomonsters; // checkparm of -nomonsters extern boolean respawnparm; // checkparm of -respawn extern boolean fastparm; // checkparm of -fast extern boolean randomparm; // [STRIFE] checkparm of -random extern boolean flipparm; // [STRIFE] checkparm of -flip extern boolean devparm; // DEBUG: launched with -devparm // ----------------------------------------------------- // Game Mode - identify IWAD as shareware, retail etc. // extern GameMode_t gamemode; extern GameMission_t gamemission; extern GameVersion_t gameversion; extern const char *gamedescription; // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; // ------------------------------------------- // Selected skill type, map etc. // // Defaults for menu, methinks. extern skill_t startskill; extern int startepisode; extern int startmap; // Savegame slot to load on startup. This is the value provided to // the -loadgame option. If this has not been provided, this is -1. extern int startloadgame; extern boolean autostart; // Selected by user. extern skill_t gameskill; extern int gameepisode; extern int gamemap; // If non-zero, exit the level after this number of minutes extern int timelimit; // Nightmare mode flag, single player. extern boolean respawnmonsters; // Netgame? Only true if >1 player. extern boolean netgame; // 0=Co-op; 1=Deathmatch; 2=Altdeath extern int deathmatch; // ------------------------- // Internal parameters for sound rendering. // These have been taken from the DOS version, // but are not (yet) supported with Linux // (e.g. no sound volume adjustment with menu. // From m_menu.c: // Sound FX volume has default, 0 - 15 // Music volume has default, 0 - 15 // These are multiplied by 8. extern int sfxVolume; extern int musicVolume; extern int voiceVolume; // haleyjd 08/29/10: [STRIFE] // Current music/sfx card - index useless // w/o a reference LUT in a sound module. // Ideally, this would use indices found // in: /usr/include/linux/soundcard.h extern int snd_MusicDevice; extern int snd_SfxDevice; // Config file? Same disclaimer as above. extern int snd_DesiredMusicDevice; extern int snd_DesiredSfxDevice; // ------------------------- // Status flags for refresh. // // Depending on view size - no status bar? // Note that there is no way to disable the // status bar explicitely. extern boolean statusbaractive; extern boolean automapactive; // In AutoMap mode? extern boolean menuactive; // Menu overlayed? extern boolean menupause; // haleyjd 08/29/10: [STRIFE] extern int menupausetime; // haleyjd 09/04/10: [STRIFE] extern boolean menuindialog; // haleyjd: ditto extern boolean paused; // Game Pause? extern boolean viewactive; extern boolean nodrawers; extern boolean testcontrols; extern int testcontrols_mousespeed; // This one is related to the 3-screen display mode. // ANG90 = left side, ANG270 = right extern int viewangleoffset; // Player taking events, and displaying. extern int consoleplayer; extern int displayplayer; // ------------------------------------- // Scores, rating. // Statistics on a given map, for intermission. // extern int totalkills; //extern int totalitems; [STRIFE] unused extern int totalsecret; // Timer, for scores. extern int levelstarttic; // gametic at level start extern int leveltime; // tics in game play for par // -------------------------------------- // DEMO playback/recording related stuff. // No demo, there is a human player in charge? // Disable save/end game? extern boolean usergame; //? extern boolean demoplayback; extern boolean demorecording; extern int mouse_fire_countdown; // villsa [STRIFE] // Round angleturn in ticcmds to the nearest 256. This is used when // recording Vanilla demos in netgames. extern boolean lowres_turn; // Quit after playing a demo from cmdline. extern boolean singledemo; //? extern gamestate_t gamestate; //----------------------------- // Internal parameters, fixed. // These are set by the engine, and not changed // according to user inputs. Partly load from // WAD, partly set at startup time. // Bookkeeping on players - state. extern player_t players[MAXPLAYERS]; // Alive? Disconnected? extern boolean playeringame[MAXPLAYERS]; // Player spawn spots for deathmatch. #define MAX_DM_STARTS 10 extern mapthing_t deathmatchstarts[MAX_DM_STARTS]; extern mapthing_t* deathmatch_p; // Player spawn spots. extern mapthing_t playerstarts[MAXPLAYERS]; // haleyjd 08/24/10: [STRIFE] rift spots #define MAXRIFTSPOTS 16 extern mapthing_t riftSpots[MAXRIFTSPOTS]; // Intermission stats. // Parameters for world map / intermission. extern wbstartstruct_t wminfo; //----------------------------------------- // Internal parameters, used for engine. // // File handling stuff. extern char * savegamedir; // if true, load all graphics at level load extern boolean precache; // wipegamestate can be set to -1 // to force a wipe on the next draw extern gamestate_t wipegamestate; extern int mouseSensitivity; //extern int bodyqueslot; [STRIFE] unused // Needed to store the number of the dummy sky flat. // Used for rendering, // as well as tracking projectiles etc. extern int skyflatnum; // Netgame stuff (buffers and pointers, i.e. indices). extern int rndindex; extern ticcmd_t *netcmds; #endif crispy-doom-crispy-doom-5.6.4/src/strife/dstrings.c000066400000000000000000000043771360717211000223330ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Globally defined strings. // #include "dstrings.h" char *doom1_endmsg[] = { "are you sure you want to\nquit this great game?", "please don't leave, there's more\ndemons to toast!", "let's beat it -- this is turning\ninto a bloodbath!", "i wouldn't leave if i were you.\ndos is much worse.", "you're trying to say you like dos\nbetter than me, right?", "don't leave yet -- there's a\ndemon around that corner!", "ya know, next time you come in here\ni'm gonna toast ya.", "go ahead and leave. see if i care.", }; char *doom2_endmsg[] = { // QuitDOOM II messages "are you sure you want to\nquit this great game?", "you want to quit?\nthen, thou hast lost an eighth!", "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", "get outta here and go back\nto your boring programs.", "if i were your boss, i'd \n deathmatch ya in a minute!", "look, bud. you leave now\nand you forfeit your body count!", "just leave. when you come\nback, i'll be waiting with a bat.", "you're lucky i don't smack\nyou for thinking about leaving.", }; #if 0 // UNUSED messages included in the source release char* endmsg[] = { // DOOM1 QUITMSG, // FinalDOOM? "fuck you, pussy!\nget the fuck out!", "you quit and i'll jizz\nin your cystholes!", "if you leave, i'll make\nthe lord drink my jizz.", "hey, ron! can we say\n'fuck' in the game?", "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", "suck it down, asshole!\nyou're a fucking wimp!", "don't quit now! we're \nstill spending your money!", // Internal debug. Different style, too. "THIS IS NO MESSAGE!\nPage intentionally left blank." }; #endif crispy-doom-crispy-doom-5.6.4/src/strife/dstrings.h000066400000000000000000000016641360717211000223340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // DESCRIPTION: // DOOM strings, by language. // #ifndef __DSTRINGS__ #define __DSTRINGS__ // All important printed strings. #include "d_englsh.h" // Misc. other strings. #define SAVEGAMENAME "doomsav" // QuitDOOM messages // 8 per each game type #define NUM_QUITMESSAGES 8 extern char *doom1_endmsg[]; extern char *doom2_endmsg[]; #endif crispy-doom-crispy-doom-5.6.4/src/strife/f_finale.c000066400000000000000000000655751360717211000222500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2010 James Haley, Samuel Villarreal // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Game completion, final screen animation. // // [STRIFE] Module marked finished 2010-09-13 22:56 // #include #include // Functions. #include "deh_main.h" #include "i_system.h" #include "i_swap.h" #include "z_zone.h" #include "v_video.h" #include "w_wad.h" #include "s_sound.h" // Data. #include "d_main.h" #include "dstrings.h" #include "sounds.h" #include "doomstat.h" #include "r_state.h" #include "p_dialog.h" // [STRIFE] typedef enum { F_STAGE_TEXT, F_STAGE_ARTSCREEN, F_STAGE_CAST, } finalestage_t; // ? //#include "doomstat.h" //#include "r_local.h" //#include "f_finale.h" // Stage of animation: finalestage_t finalestage; unsigned int finalecount; // haleyjd 09/12/10: [STRIFE] Slideshow variables const char *slideshow_panel; unsigned int slideshow_tics; int slideshow_state; // haleyjd 09/13/10: [STRIFE] All this is unused. /* #define TEXTSPEED 3 #define TEXTWAIT 250 typedef struct { GameMission_t mission; int episode, level; char *background; char *text; } textscreen_t; static textscreen_t textscreens[] = { { doom, 1, 8, "FLOOR4_8", E1TEXT}, { doom, 2, 8, "SFLR6_1", E2TEXT}, { doom, 3, 8, "MFLR8_4", E3TEXT}, { doom, 4, 8, "MFLR8_3", E4TEXT}, { doom2, 1, 6, "SLIME16", C1TEXT}, { doom2, 1, 11, "RROCK14", C2TEXT}, { doom2, 1, 20, "RROCK07", C3TEXT}, { doom2, 1, 30, "RROCK17", C4TEXT}, { doom2, 1, 15, "RROCK13", C5TEXT}, { doom2, 1, 31, "RROCK19", C6TEXT}, { pack_tnt, 1, 6, "SLIME16", T1TEXT}, { pack_tnt, 1, 11, "RROCK14", T2TEXT}, { pack_tnt, 1, 20, "RROCK07", T3TEXT}, { pack_tnt, 1, 30, "RROCK17", T4TEXT}, { pack_tnt, 1, 15, "RROCK13", T5TEXT}, { pack_tnt, 1, 31, "RROCK19", T6TEXT}, { pack_plut, 1, 6, "SLIME16", P1TEXT}, { pack_plut, 1, 11, "RROCK14", P2TEXT}, { pack_plut, 1, 20, "RROCK07", P3TEXT}, { pack_plut, 1, 30, "RROCK17", P4TEXT}, { pack_plut, 1, 15, "RROCK13", P5TEXT}, { pack_plut, 1, 31, "RROCK19", P6TEXT}, }; char* finaletext; char* finaleflat; */ void F_StartCast (void); void F_CastTicker (void); boolean F_CastResponder (event_t *ev); void F_CastDrawer (void); // [STRIFE] - Slideshow states enumeration enum { // Exit states SLIDE_EXITHACK = -99, // Hacky exit - start a new dialog SLIDE_HACKHACK = -9, // Bizarre unused state SLIDE_EXIT = -1, // Exit to next finale state SLIDE_CHOCO = -2, // haleyjd: This state is Choco-specific... see below. // Unknown SLIDE_UNKNOWN = 0, // Dunno what it's for, possibly unused // MAP03 - Macil's Programmer exposition SLIDE_PROGRAMMER1 = 1, SLIDE_PROGRAMMER2, SLIDE_PROGRAMMER3, SLIDE_PROGRAMMER4, // Next state = -99 // MAP10 - Macil's Sigil exposition SLIDE_SIGIL1 = 5, SLIDE_SIGIL2, SLIDE_SIGIL3, SLIDE_SIGIL4, // Next state = -99 // MAP29 - Endings // Good Ending SLIDE_GOODEND1 = 10, SLIDE_GOODEND2, SLIDE_GOODEND3, SLIDE_GOODEND4, // Next state = -1 // Bad Ending SLIDE_BADEND1 = 14, SLIDE_BADEND2, SLIDE_BADEND3, // Next state = -1 // Blah Ending SLIDE_BLAHEND1 = 17, SLIDE_BLAHEND2, SLIDE_BLAHEND3, // Next state = -1 // Demo Ending - haleyjd 20130301: v1.31 only SLIDE_DEMOEND1 = 25, SLIDE_DEMOEND2 // Next state = -1 }; // // F_StartFinale // // [STRIFE] // haleyjd 09/13/10: Modified to drive slideshow sequences. // void F_StartFinale (void) { #if 0 // haleyjd 20111006: see below... patch_t *panel; #endif gameaction = ga_nothing; gamestate = GS_FINALE; viewactive = false; automapactive = false; wipegamestate = -1; // [STRIFE] // [STRIFE] Setup the slide show slideshow_panel = DEH_String("PANEL0"); // haleyjd 20111006: These two lines of code *are* in vanilla Strife; // however, there, they were completely inconsequential due to the dirty // rects system. No intervening V_MarkRect call means PANEL0 was never // drawn to the framebuffer. In Chocolate Strife, however, with no such // system in place, this only manages to fuck up the fade-out that is // supposed to happen at the beginning of all finales. So, don't do it! #if 0 panel = (patch_t *)W_CacheLumpName(slideshow_panel, PU_CACHE); V_DrawPatch(0, 0, panel); #endif switch(gamemap) { case 3: // Macil's exposition on the Programmer slideshow_state = SLIDE_PROGRAMMER1; break; case 9: // Super hack for death of Programmer slideshow_state = SLIDE_EXITHACK; break; case 10: // Macil's exposition on the Sigil slideshow_state = SLIDE_SIGIL1; break; case 29: // Endings if(!netgame) { if(players[0].health <= 0) // Bad ending slideshow_state = SLIDE_BADEND1; // - Humanity goes extinct else { if((players[0].questflags & QF_QUEST25) && // Converter destroyed (players[0].questflags & QF_QUEST27)) // Computer destroyed (wtf?!) { // Good ending - You get the hot babe. slideshow_state = SLIDE_GOODEND1; } else { // Blah ending - You win the battle, but fail at life. slideshow_state = SLIDE_BLAHEND1; } } } break; case 34: // For the demo version ending slideshow_state = SLIDE_EXIT; // haleyjd 20130301: Somebody noticed the demo levels were missing the // ending they used to have in the demo version EXE, I guess. But the // weird thing is, this will only trigger if you run with strife0.wad, // and no released version thereof actually works with the 1.31 EXE // due to differing dialog formats... was there to be an updated demo // that never got released?! if(gameversion == exe_strife_1_31 && isdemoversion) slideshow_state = SLIDE_DEMOEND1; break; } S_ChangeMusic(mus_dark, 1); slideshow_tics = 7; finalestage = F_STAGE_TEXT; finalecount = 0; } // // F_Responder // // [STRIFE] Verified unmodified // boolean F_Responder (event_t *event) { if (finalestage == F_STAGE_CAST) return F_CastResponder (event); return false; } // // F_WaitTicker // // [STRIFE] New function // haleyjd 09/13/10: This is called from G_Ticker if gamestate is 1, but we // have no idea for what it's supposed to be. It is unused. // void F_WaitTicker(void) { if(++finalecount >= 250) { gamestate = GS_FINALE; finalestage = 0; finalecount = 0; } } // // F_DoSlideShow // // [STRIFE] New function // haleyjd 09/13/10: Handles slideshow states. Begging to be tabulated! // static void F_DoSlideShow(void) { patch_t *patch; switch(slideshow_state) { case SLIDE_UNKNOWN: // state #0, seems to be unused slideshow_tics = 700; slideshow_state = SLIDE_EXIT; // falls through into state 1, so above is pointless? ... case SLIDE_PROGRAMMER1: // state #1 slideshow_panel = DEH_String("SS2F1"); I_StartVoice(DEH_String("MAC10")); slideshow_state = SLIDE_PROGRAMMER2; slideshow_tics = 315; break; case SLIDE_PROGRAMMER2: // state #2 slideshow_panel = DEH_String("SS2F2"); I_StartVoice(DEH_String("MAC11")); slideshow_state = SLIDE_PROGRAMMER3; slideshow_tics = 350; break; case SLIDE_PROGRAMMER3: // state #3 slideshow_panel = DEH_String("SS2F3"); I_StartVoice(DEH_String("MAC12")); slideshow_state = SLIDE_PROGRAMMER4; slideshow_tics = 420; break; case SLIDE_PROGRAMMER4: // state #4 slideshow_panel = DEH_String("SS2F4"); I_StartVoice(DEH_String("MAC13")); slideshow_state = SLIDE_EXITHACK; // End of slides slideshow_tics = 595; break; case SLIDE_SIGIL1: // state #5 slideshow_panel = DEH_String("SS3F1"); I_StartVoice(DEH_String("MAC16")); slideshow_state = SLIDE_SIGIL2; slideshow_tics = 350; break; case SLIDE_SIGIL2: // state #6 slideshow_panel = DEH_String("SS3F2"); I_StartVoice(DEH_String("MAC17")); slideshow_state = SLIDE_SIGIL3; slideshow_tics = 420; break; case SLIDE_SIGIL3: // state #7 slideshow_panel = DEH_String("SS3F3"); I_StartVoice(DEH_String("MAC18")); slideshow_tics = 420; slideshow_state = SLIDE_SIGIL4; break; case SLIDE_SIGIL4: // state #8 slideshow_panel = DEH_String("SS3F4"); I_StartVoice(DEH_String("MAC19")); slideshow_tics = 385; slideshow_state = SLIDE_EXITHACK; // End of slides break; case SLIDE_GOODEND1: // state #10 slideshow_panel = DEH_String("SS4F1"); S_StartMusic(mus_happy); I_StartVoice(DEH_String("RIE01")); slideshow_state = SLIDE_GOODEND2; slideshow_tics = 455; break; case SLIDE_GOODEND2: // state #11 slideshow_panel = DEH_String("SS4F2"); I_StartVoice(DEH_String("BBX01")); slideshow_state = SLIDE_GOODEND3; slideshow_tics = 385; break; case SLIDE_GOODEND3: // state #12 slideshow_panel = DEH_String("SS4F3"); I_StartVoice(DEH_String("BBX02")); slideshow_state = SLIDE_GOODEND4; slideshow_tics = 490; break; case SLIDE_GOODEND4: // state #13 slideshow_panel = DEH_String("SS4F4"); slideshow_state = SLIDE_EXIT; // Go to end credits slideshow_tics = 980; break; case SLIDE_BADEND1: // state #14 S_StartMusic(mus_sad); slideshow_panel = DEH_String("SS5F1"); I_StartVoice(DEH_String("SS501b")); slideshow_state = SLIDE_BADEND2; slideshow_tics = 385; break; case SLIDE_BADEND2: // state #15 slideshow_panel = DEH_String("SS5F2"); I_StartVoice(DEH_String("SS502b")); slideshow_state = SLIDE_BADEND3; slideshow_tics = 350; break; case SLIDE_BADEND3: // state #16 slideshow_panel = DEH_String("SS5F3"); I_StartVoice(DEH_String("SS503b")); slideshow_state = SLIDE_EXIT; // Go to end credits slideshow_tics = 385; break; case SLIDE_BLAHEND1: // state #17 S_StartMusic(mus_end); slideshow_panel = DEH_String("SS6F1"); I_StartVoice(DEH_String("SS601A")); slideshow_state = SLIDE_BLAHEND2; slideshow_tics = 280; break; case SLIDE_BLAHEND2: // state #18 S_StartMusic(mus_end); slideshow_panel = DEH_String("SS6F2"); I_StartVoice(DEH_String("SS602A")); slideshow_state = SLIDE_BLAHEND3; slideshow_tics = 280; break; case SLIDE_BLAHEND3: // state #19 S_StartMusic(mus_end); slideshow_panel = DEH_String("SS6F3"); I_StartVoice(DEH_String("SS603A")); slideshow_state = SLIDE_EXIT; // Go to credits slideshow_tics = 315; break; case SLIDE_DEMOEND1: // state #25 - only exists in 1.31 slideshow_panel = DEH_String("PANEL7"); slideshow_tics = 175; slideshow_state = SLIDE_DEMOEND2; break; case SLIDE_DEMOEND2: // state #26 - ditto slideshow_panel = DEH_String("VELLOGO"); slideshow_tics = 175; slideshow_state = SLIDE_EXIT; // Go to end credits break; case SLIDE_EXITHACK: // state -99: super hack state gamestate = GS_LEVEL; P_DialogStartP1(); break; case SLIDE_HACKHACK: // state -9: unknown bizarre unused state S_StartSound(NULL, sfx_rifle); slideshow_tics = 3150; break; case SLIDE_EXIT: // state -1: proceed to next finale stage finalecount = 0; finalestage = F_STAGE_ARTSCREEN; wipegamestate = -1; S_StartMusic(mus_fast); // haleyjd 20130301: The ONLY glitch fixed in 1.31 of Strife // *would* be something this insignificant, of course! if(gameversion != exe_strife_1_31) slideshow_state = SLIDE_CHOCO; // haleyjd: see below... break; case SLIDE_CHOCO: // haleyjd 09/14/10: This wouldn't be necessary except that Choco // doesn't support the V_MarkRect dirty rectangles system. This // just so happens to have hidden the fact that the ending // does a screenfade every ~19 seconds due to remaining stuck in // SLIDE_EXIT state above, UNLESS the menus were active - the // V_MarkRect calls in the menu system cause it to be visible. // This means that in order to get the same behavior as the vanilla // EXE, I need different code. So, come to this state and only set // wipegamestate if menuactive is true. finalecount = 0; finalestage = F_STAGE_ARTSCREEN; if(menuactive) wipegamestate = -1; S_StartMusic(mus_fast); slideshow_state = SLIDE_CHOCO; // remain here. break; default: break; } finalecount = 0; if(gameversion != exe_strife_1_31) // See above. This was removed in 1.31. { patch = (patch_t *)W_CacheLumpName(DEH_String("PANEL0"), PU_CACHE); V_DrawPatch(0, 0, patch); } } // // F_Ticker // // [STRIFE] Modifications for new finales // haleyjd 09/13/10: Calls F_DoSlideShow // void F_Ticker (void) { size_t i; // check for skipping if (finalecount > 50) // [STRIFE] No commercial check { // go on to the next level for (i=0 ; i slideshow_tics) // [STRIFE] Advance slideshow F_DoSlideShow(); // [STRIFE]: Rest is unused /* if ( gamemode == commercial) return; if (finalestage == F_STAGE_TEXT && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT) { finalecount = 0; finalestage = F_STAGE_ARTSCREEN; wipegamestate = -1; // force a wipe if (gameepisode == 3) S_StartMusic (mus_logo); } */ } // haleyjd 09/13/10: Not present in Strife: Cast drawing functions #include "hu_stuff.h" extern patch_t *hu_font[HU_FONTSIZE]; /* // // F_TextWrite // void F_TextWrite (void) { byte* src; byte* dest; int x,y,w; signed int count; char* ch; int c; int cx; int cy; // erase the entire screen to a tiled background src = W_CacheLumpName ( finaleflat , PU_CACHE); dest = I_VideoBuffer; for (y=0 ; y HU_FONTSIZE) { cx += 4; continue; } w = SHORT (hu_font[c]->width); if (cx+w > SCREENWIDTH) break; V_DrawPatch(cx, cy, hu_font[c]); cx+=w; } } */ // // Final DOOM 2 animation // Casting by id Software. // in order of appearance // typedef struct { int isindemo; // [STRIFE] Changed from name, which is in mobjinfo mobjtype_t type; } castinfo_t; // haleyjd: [STRIFE] A new cast order was defined, however it is unused in any // of the released versions of Strife, even including the demo version :( castinfo_t castorder[] = { { 1, MT_PLAYER }, { 1, MT_BEGGAR1 }, { 1, MT_PEASANT2_A }, { 1, MT_REBEL1 }, { 1, MT_GUARD1 }, { 1, MT_CRUSADER }, { 1, MT_RLEADER2 }, { 0, MT_SENTINEL }, { 0, MT_STALKER }, { 0, MT_PROGRAMMER }, { 0, MT_REAVER }, { 0, MT_PGUARD }, { 0, MT_INQUISITOR }, { 0, MT_PRIEST }, { 0, MT_SPECTRE_A }, { 0, MT_BISHOP }, { 0, MT_ENTITY }, { 1, NUMMOBJTYPES } }; int castnum; int casttics; state_t* caststate; boolean castdeath; int castframes; int castonmelee; boolean castattacking; // // F_StartCast // // haleyjd 09/13/10: [STRIFE] Heavily modified, yet unused. // Evidence suggests this was meant to be started from a menu item. // See m_menu.c for more info. // void F_StartCast (void) { usergame = false; gameaction = ga_nothing; viewactive = false; automapactive = false; castnum = 0; gamestate = GS_FINALE; caststate = &states[mobjinfo[castorder[castnum].type].seestate]; casttics = caststate->tics; if(casttics > 50) casttics = 50; wipegamestate = -1; // force a screen wipe castdeath = false; finalestage = F_STAGE_CAST; castframes = 0; castonmelee = 0; castattacking = false; } // // F_CastTicker // // [STRIFE] Heavily modified, but unused. // haleyjd 09/13/10: Yeah, I bothered translating this even though it isn't // going to be seen, in part because I hope some Strife port or another will // pick it up and finish it, adding it as the optional menu item it was // meant to be, or just adding it as part of the ending sequence. // void F_CastTicker (void) { int st; 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 (isdemoversion) { // [STRIFE] Demo version had a shorter cast if(!castorder[castnum].isindemo) castnum = 0; } // [STRIFE] Break on type == NUMMOBJTYPES rather than name == NULL if (castorder[castnum].type == NUMMOBJTYPES) 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 { int sfx = 0; // just advance to next state in animation if (caststate == &states[S_PLAY_05]) // villsa [STRIFE] - updated goto stopattack; // Oh, gross hack! st = caststate->nextstate; caststate = &states[st]; castframes++; if (st != mobjinfo[castorder[castnum].type].meleestate && st != mobjinfo[castorder[castnum].type].missilestate) { if (st == S_PLAY_05) sfx = sfx_rifle; else sfx = 0; } else sfx = mobjinfo[castorder[castnum].type].attacksound; if (sfx) S_StartSound (NULL, sfx); } if (!castdeath && 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 > 50) // [STRIFE] Cap tics casttics = 50; else if (casttics == -1) casttics = 15; } // // F_CastResponder // // [STRIFE] This still exists in Strife but is never used. // It was used at some point in development, however, as they made // numerous modifications to the cast call system. // boolean F_CastResponder (event_t* ev) { if (ev->type != ev_keydown) return false; if (castdeath) return true; // already in dying frames // go into death frame castdeath = true; caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; casttics = caststate->tics; if(casttics > 50) // [STRIFE] Upper bound on casttics casttics = 50; castframes = 0; castattacking = false; if (mobjinfo[castorder[castnum].type].deathsound) S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound); return true; } // // F_CastPrint // // [STRIFE] Verified unmodified, and unused. // void F_CastPrint (char* text) { char* ch; int c; int cx; int w; int width; // find width ch = text; width = 0; while (ch) { c = *ch++; if (!c) break; c = toupper(c) - HU_FONTSTART; if (c < 0 || c> HU_FONTSIZE) { width += 4; continue; } w = SHORT (hu_font[c]->width); width += w; } // draw it cx = 160-width/2; ch = text; while (ch) { c = *ch++; if (!c) break; c = toupper(c) - HU_FONTSTART; if (c < 0 || c> HU_FONTSIZE) { cx += 4; continue; } w = SHORT (hu_font[c]->width); V_DrawPatch(cx, 180, hu_font[c]); cx+=w; } } // haleyjd 09/13/10: [STRIFE] Unfortunately they removed whatever was // partway finished of this function from the binary, as there is no // trace of it. This means we cannot know for sure what the cast call // would have looked like. :( /* // // F_CastDrawer // void F_CastDrawer (void) { spritedef_t* sprdef; spriteframe_t* sprframe; int lump; boolean flip; patch_t* patch; // erase the entire screen to a background V_DrawPatch (0, 0, W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE)); F_CastPrint (DEH_String(castorder[castnum].name)); // draw the current frame in the middle of the screen sprdef = &sprites[caststate->sprite]; sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK]; lump = sprframe->lump[0]; flip = (boolean)sprframe->flip[0]; patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE); if (flip) V_DrawPatchFlipped(160, 170, patch); else V_DrawPatch(160, 170, patch); } */ #ifdef STRIFE_DEMO_CODE // // F_DrawPatchCol // // [STRIFE] Verified unmodified, but not present in 1.2 // It WAS present in the demo version, however... // void F_DrawPatchCol ( int x, patch_t* patch, int col ) { column_t* column; byte* source; byte* dest; byte* desttop; int count; column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); desttop = I_VideoBuffer + x; // step through the posts in a column while (column->topdelta != 0xff ) { source = (byte *)column + 3; dest = desttop + column->topdelta*SCREENWIDTH; count = column->length; while (count--) { *dest = *source++; dest += SCREENWIDTH; } column = (column_t *)( (byte *)column + column->length + 4 ); } } #endif // // F_DrawMap34End // // [STRIFE] Modified from F_BunnyScroll // * In 1.2 and up this just causes a weird black screen. // * In the demo version, it was an actual scroll between two screens. // I have implemented both code segments, though only the black screen // one will currently be used, as full demo version support isn't looking // likely right now. // void F_DrawMap34End (void) { signed int scrolled; int x; // patch_t* p1; // patch_t* p2; // p1 = W_CacheLumpName (DEH_String("credit"), PU_LEVEL); // p2 = W_CacheLumpName (DEH_String("vellogo"), PU_LEVEL); V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); scrolled = (320 - ((signed int) finalecount-430)/2); if (scrolled > 320) scrolled = 320; if (scrolled < 0) scrolled = 0; #ifdef STRIFE_DEMO_CODE for ( x=0 ; x #include "z_zone.h" #include "i_video.h" #include "v_video.h" #include "m_random.h" #include "doomtype.h" #include "r_defs.h" // haleyjd [STRIFE] #include "r_draw.h" #include "f_wipe.h" // // SCREEN WIPE PACKAGE // // when zero, stop the wipe static boolean go = 0; static byte* wipe_scr_start; static byte* wipe_scr_end; static byte* wipe_scr; void wipe_shittyColMajorXform ( short* array, int width, int height ) { int x; int y; short* dest; dest = (short*) Z_Malloc(width*height*2, PU_STATIC, 0); for(y=0;y 0; i--) { if(*cur_screen != *end_screen) { changed = true; *cur_screen = xlatab[(*cur_screen << 8) + *end_screen]; } ++cur_screen; ++end_screen; } return !changed; } // haleyjd 08/26/10: [STRIFE] Verified unmodified. int wipe_exitColorXForm ( int width, int height, int ticks ) { return 0; } static int* y; int wipe_initMelt ( int width, int height, int ticks ) { int i, r; // copy start screen to main screen memcpy(wipe_scr, wipe_scr_start, width*height); // makes this wipe faster (in theory) // to have stuff in column-major format wipe_shittyColMajorXform((short*)wipe_scr_start, width/2, height); wipe_shittyColMajorXform((short*)wipe_scr_end, width/2, height); // setup initial column positions // (y<0 => not ready to scroll yet) y = (int *) Z_Malloc(width*sizeof(int), PU_STATIC, 0); y[0] = -(M_Random()%16); for (i=1;i 0) y[i] = 0; else if (y[i] == -16) y[i] = -15; } return 0; } int wipe_doMelt ( int width, int height, int ticks ) { int i; int j; int dy; int idx; short* s; short* d; boolean done = true; width/=2; while (ticks--) { for (i=0;i= height) dy = height - y[i]; s = &((short *)wipe_scr_end)[i*height+y[i]]; d = &((short *)wipe_scr)[y[i]*width+i]; idx = 0; for (j=dy;j;j--) { d[idx] = *(s++); idx += width; } y[i] += dy; s = &((short *)wipe_scr_start)[i*height]; d = &((short *)wipe_scr)[y[i]*width+i]; idx = 0; for (j=height-y[i];j;j--) { d[idx] = *(s++); idx += width; } done = false; } } } return done; } int wipe_exitMelt ( int width, int height, int ticks ) { Z_Free(y); Z_Free(wipe_scr_start); Z_Free(wipe_scr_end); return 0; } // haleyjd 08/26/10: [STRIFE] Verified unmodified. int wipe_StartScreen ( int x, int y, int width, int height ) { wipe_scr_start = Z_Malloc(SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL); I_ReadScreen(wipe_scr_start); return 0; } // haleyjd 08/26/10: [STRIFE] Verified unmodified. int wipe_EndScreen ( int x, int y, int width, int height ) { wipe_scr_end = Z_Malloc(SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL); I_ReadScreen(wipe_scr_end); V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr. return 0; } // haleyjd 08/26/10: [STRIFE] Verified unmodified. int wipe_ScreenWipe ( int wipeno, int x, int y, int width, int height, int ticks ) { int rc; static int (*wipes[])(int, int, int) = { wipe_initColorXForm, wipe_doColorXForm, wipe_exitColorXForm, wipe_initMelt, wipe_doMelt, wipe_exitMelt }; ticks <<= crispy->hires; // initial stuff if (!go) { go = 1; // haleyjd 20110629 [STRIFE]: We *must* use a temp buffer here. wipe_scr = (byte *) Z_Malloc(width*height, PU_STATIC, 0); // DEBUG //wipe_scr = I_VideoBuffer; (*wipes[wipeno*3])(width, height, ticks); } // do a piece of wipe-in V_MarkRect(0, 0, width, height); rc = (*wipes[wipeno*3+1])(width, height, ticks); // haleyjd 20110629 [STRIFE]: Copy temp buffer to the real screen. V_DrawBlock(x, y, width, height, wipe_scr); // final stuff if (rc) { go = 0; (*wipes[wipeno*3+2])(width, height, ticks); } return !go; } crispy-doom-crispy-doom-5.6.4/src/strife/f_wipe.h000066400000000000000000000022311360717211000217370ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Mission start screen wipe/melt, special effects. // #ifndef __F_WIPE_H__ #define __F_WIPE_H__ // // SCREEN WIPE PACKAGE // enum { // [STRIFE]: ColorXForm reimplemented as a proper crossfade wipe_ColorXForm, // weird screen melt wipe_Melt, wipe_NUMWIPES }; int wipe_StartScreen ( int x, int y, int width, int height ); int wipe_EndScreen ( int x, int y, int width, int height ); int wipe_ScreenWipe ( int wipeno, int x, int y, int width, int height, int ticks ); #endif crispy-doom-crispy-doom-5.6.4/src/strife/g_game.c000066400000000000000000001711161360717211000217110ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // #include #include #include #include "doomdef.h" #include "doomkeys.h" #include "doomstat.h" #include "deh_main.h" #include "deh_misc.h" #include "z_zone.h" #include "f_finale.h" #include "m_argv.h" #include "m_controls.h" #include "m_misc.h" #include "m_menu.h" #include "m_misc.h" #include "m_saves.h" // STRIFE #include "m_random.h" #include "i_input.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "p_setup.h" #include "p_saveg.h" #include "p_tick.h" #include "d_main.h" #include "wi_stuff.h" #include "hu_stuff.h" #include "st_stuff.h" #include "am_map.h" // Needs access to LFB. #include "v_video.h" #include "w_wad.h" #include "p_local.h" #include "s_sound.h" // Data. #include "dstrings.h" #include "sounds.h" // SKY handling - still the wrong place. #include "r_data.h" #include "r_sky.h" #include "p_dialog.h" // villsa [STRIFE] #include "g_game.h" #define SAVEGAMESIZE 0x2c000 void G_ReadDemoTiccmd (ticcmd_t* cmd); void G_WriteDemoTiccmd (ticcmd_t* cmd); void G_PlayerReborn (int player); void G_DoReborn (int playernum); void G_DoLoadLevel (void); void G_DoNewGame (void); void G_DoPlayDemo (void); void G_DoCompleted (void); void G_DoVictory (void); void G_DoWorldDone (void); void G_DoSaveGame (char *path); // Gamestate the last time G_Ticker was called. gamestate_t oldgamestate; gameaction_t gameaction; gamestate_t gamestate; skill_t gameskill = 2; // [STRIFE] Default value set to 2. boolean respawnmonsters; //int gameepisode; int gamemap; // haleyjd 08/24/10: [STRIFE] New variables int destmap; // current destination map when exiting int riftdest; // destination spot for player angle_t riftangle; // player angle saved during exit // If non-zero, exit the level after this number of minutes. int timelimit; boolean paused; boolean sendpause; // send a pause event next tic boolean sendsave; // send a save event next tic boolean usergame; // ok to save / end game boolean timingdemo; // if true, exit with report on completion boolean nodrawers; // for comparative timing purposes int starttime; // for comparative timing purposes boolean viewactive; int deathmatch; // only if started as net death boolean netgame; // only true if packets are broadcast boolean playeringame[MAXPLAYERS]; player_t players[MAXPLAYERS]; boolean turbodetected[MAXPLAYERS]; int consoleplayer; // player taking events and displaying int displayplayer; // view being displayed int levelstarttic; // gametic at level start int totalkills, /*totalitems,*/ totalsecret; // for intermission char *demoname; boolean demorecording; boolean longtics; // cph's doom 1.91 longtics hack boolean lowres_turn; // low resolution turning for longtics boolean demoplayback; boolean netdemo; byte* demobuffer; byte* demo_p; byte* demoend; boolean singledemo; // quit after playing a demo from cmdline boolean precache = true; // if true, load all graphics at start boolean testcontrols = false; // Invoked by setup to test controls wbstartstruct_t wminfo; // parms for world map / intermission byte consistancy[MAXPLAYERS][BACKUPTICS]; #define MAXPLMOVE (forwardmove[1]) #define TURBOTHRESHOLD 0x32 fixed_t forwardmove[2] = {0x19, 0x32}; fixed_t sidemove[2] = {0x18, 0x28}; fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn int mouse_fire_countdown = 0; // villsa [STRIFE] static int *weapon_keys[] = { &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, &key_weapon5, &key_weapon6, &key_weapon7, &key_weapon8 }; // Set to -1 or +1 to switch to the previous or next weapon. static int next_weapon = 0; // Used for prev/next weapon keys. // STRIFE-TODO: Check this table makes sense. static const struct { weapontype_t weapon; weapontype_t weapon_num; } weapon_order_table[] = { { wp_fist, wp_fist }, { wp_poisonbow, wp_elecbow }, { wp_elecbow, wp_elecbow }, { wp_rifle, wp_rifle }, { wp_missile, wp_missile }, { wp_wpgrenade, wp_hegrenade }, { wp_hegrenade, wp_hegrenade }, { wp_flame, wp_flame }, { wp_torpedo, wp_mauler }, { wp_mauler, wp_mauler }, { wp_sigil, wp_sigil }, }; #define SLOWTURNTICS 6 #define NUMKEYS 256 #define MAX_JOY_BUTTONS 20 static boolean gamekeydown[NUMKEYS]; static int turnheld; // for accelerative turning static boolean mousearray[MAX_MOUSE_BUTTONS + 1]; static boolean *mousebuttons = &mousearray[1]; // allow [-1] // mouse values are used once int mousex; int mousey; static int dclicktime; static boolean dclickstate; static int dclicks; static int dclicktime2; static boolean dclickstate2; static int dclicks2; // joystick values are repeated static int joyxmove; static int joyymove; static int joystrafemove; static int joylook; static boolean joyarray[MAX_JOY_BUTTONS + 1]; static boolean *joybuttons = &joyarray[1]; // allow [-1] static int savegameslot = 6; // [STRIFE] initialized to 6 static char savedescription[32]; int testcontrols_mousespeed; #define BODYQUESIZE 32 mobj_t* bodyque[BODYQUESIZE]; //int bodyqueslot; [STRIFE] unused int vanilla_savegame_limit = 1; int vanilla_demo_limit = 1; int G_CmdChecksum (ticcmd_t* cmd) { size_t i; int sum = 0; for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++) sum += ((int *)cmd)[i]; return sum; } static boolean WeaponSelectable(weapontype_t weapon) { player_t *player; player = &players[consoleplayer]; // Can't select a weapon if we don't own it. if (!player->weaponowned[weapon]) { return false; } // Can't use registered-only weapons in demo mode: if (isdemoversion && !weaponinfo[weapon].availabledemo) { return false; } // Special rules for switching to alternate versions of weapons. // These must match the weapon-switching rules in P_PlayerThink() // haleyjd 20141024: same fix here as in P_PlayerThink for torpedo. if (weapon == wp_torpedo && player->ammo[weaponinfo[wp_torpedo].ammo] < 30) { return false; } if (player->ammo[weaponinfo[weapon].ammo] == 0) { return false; } return true; } static int G_NextWeapon(int direction) { weapontype_t weapon; int start_i, i; // Find index in the table. if (players[consoleplayer].pendingweapon == wp_nochange) { weapon = players[consoleplayer].readyweapon; } else { weapon = players[consoleplayer].pendingweapon; } for (i=0; iconsistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; // villsa [STRIFE] look up key if(gamekeydown[key_lookup] || joylook < 0) cmd->buttons2 |= BT2_LOOKUP; // villsa [STRIFE] look down key if(gamekeydown[key_lookdown] || joylook > 0) cmd->buttons2 |= BT2_LOOKDOWN; // villsa [STRIFE] inventory use key if(gamekeydown[key_invuse]) { player_t* player = &players[consoleplayer]; if(player->numinventory > 0) { cmd->buttons2 |= BT2_INVUSE; cmd->inventory = player->inventory[player->inventorycursor].sprite; } } // villsa [STRIFE] inventory drop key if(gamekeydown[key_invdrop]) { player_t* player = &players[consoleplayer]; if(player->numinventory > 0) { cmd->buttons2 |= BT2_INVDROP; cmd->inventory = player->inventory[player->inventorycursor].sprite; } } // villsa [STRIFE] use medkit if(gamekeydown[key_usehealth]) cmd->buttons2 |= BT2_HEALTH; strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; // fraggle: support the old "joyb_speed = 31" hack which // allowed an autorun effect speed = key_speed >= NUMKEYS || joybspeed >= MAX_JOY_BUTTONS || gamekeydown[key_speed] || joybuttons[joybspeed]; forward = side = 0; // villsa [STRIFE] running causes centerview to occur if(speed) cmd->buttons2 |= BT2_CENTERVIEW; // villsa [STRIFE] disable running if low on health if (players[consoleplayer].health <= 15) speed = 0; // use two stage accelerative turning // on the keyboard and joystick if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || gamekeydown[key_left]) turnheld += ticdup; else turnheld = 0; if (turnheld < SLOWTURNTICS) tspeed = 2; // slow turn else tspeed = speed; // let movement keys cancel each other out if (strafe) { if (gamekeydown[key_right]) { // fprintf(stderr, "strafe right\n"); side += sidemove[speed]; } if (gamekeydown[key_left]) { // fprintf(stderr, "strafe left\n"); side -= sidemove[speed]; } if (joyxmove > 0) side += sidemove[speed]; if (joyxmove < 0) side -= sidemove[speed]; } else { if (gamekeydown[key_right]) cmd->angleturn -= angleturn[tspeed]; if (gamekeydown[key_left]) cmd->angleturn += angleturn[tspeed]; if (joyxmove > 0) cmd->angleturn -= angleturn[tspeed]; if (joyxmove < 0) cmd->angleturn += angleturn[tspeed]; } if (gamekeydown[key_up]) { // fprintf(stderr, "up\n"); forward += forwardmove[speed]; } if (gamekeydown[key_down]) { // fprintf(stderr, "down\n"); forward -= forwardmove[speed]; } if (joyymove < 0) forward += forwardmove[speed]; if (joyymove > 0) forward -= forwardmove[speed]; if (gamekeydown[key_strafeleft] || joybuttons[joybstrafeleft] || mousebuttons[mousebstrafeleft] || joystrafemove < 0) { side -= sidemove[speed]; } if (gamekeydown[key_straferight] || joybuttons[joybstraferight] || mousebuttons[mousebstraferight] || joystrafemove > 0) { side += sidemove[speed]; } // buttons cmd->chatchar = HU_dequeueChatChar(); // villsa [STRIFE] - add mouse button support for jump if (gamekeydown[key_jump] || mousebuttons[mousebjump] || joybuttons[joybjump]) cmd->buttons2 |= BT2_JUMP; // villsa [STRIFE]: Moved mousebuttons[mousebfire] to below if (gamekeydown[key_fire] || joybuttons[joybfire]) cmd->buttons |= BT_ATTACK; // villsa [STRIFE] if(mousebuttons[mousebfire]) { if(mouse_fire_countdown <= 0) cmd->buttons |= BT_ATTACK; else --mouse_fire_countdown; } if (gamekeydown[key_use] || joybuttons[joybuse] || mousebuttons[mousebuse]) { cmd->buttons |= BT_USE; // clear double clicks if hit use button dclicks = 0; } // If the previous or next weapon button is pressed, the // next_weapon variable is set to change weapons when // we generate a ticcmd. Choose a new weapon. if (gamestate == GS_LEVEL && next_weapon != 0) { i = G_NextWeapon(next_weapon); cmd->buttons |= BT_CHANGE; cmd->buttons |= i << BT_WEAPONSHIFT; } else { // Check weapon keys. for (i=0; ibuttons |= BT_CHANGE; cmd->buttons |= i< 1 ) { dclickstate = mousebuttons[mousebforward]; if (dclickstate) dclicks++; if (dclicks == 2) { cmd->buttons |= BT_USE; dclicks = 0; } else dclicktime = 0; } else { dclicktime += ticdup; if (dclicktime > 20) { dclicks = 0; dclickstate = 0; } } // strafe double click bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; if (bstrafe != dclickstate2 && dclicktime2 > 1 ) { dclickstate2 = bstrafe; if (dclickstate2) dclicks2++; if (dclicks2 == 2) { cmd->buttons |= BT_USE; dclicks2 = 0; } else dclicktime2 = 0; } else { dclicktime2 += ticdup; if (dclicktime2 > 20) { dclicks2 = 0; dclickstate2 = 0; } } } if (!novert) forward += mousey; if (strafe) side += mousex*2; else cmd->angleturn -= mousex*0x8; if (mousex == 0) { // No movement in the previous frame testcontrols_mousespeed = 0; } mousex = mousey = 0; if (forward > MAXPLMOVE) forward = MAXPLMOVE; else if (forward < -MAXPLMOVE) forward = -MAXPLMOVE; if (side > MAXPLMOVE) side = MAXPLMOVE; else if (side < -MAXPLMOVE) side = -MAXPLMOVE; cmd->forwardmove += forward; cmd->sidemove += side; // special buttons if (sendpause) { sendpause = false; cmd->buttons = BT_SPECIAL | BTS_PAUSE; } if (sendsave) { sendsave = false; cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<angleturn + carry; // round angleturn to the nearest 256 unit boundary // for recording demos with single byte values for turn cmd->angleturn = (desired_angleturn + 128) & 0xff00; // Carry forward the error from the reduced resolution to the // next tic, so that successive small movements can accumulate. carry = desired_angleturn - cmd->angleturn; } } // // G_DoLoadLevel // void G_DoLoadLevel (void) { int i; // haleyjd 10/03/10: [STRIFE] This is not done here. //skyflatnum = R_FlatNumForName(DEH_String(SKYFLATNAME)); levelstarttic = gametic; // for time calculation if (wipegamestate == GS_LEVEL) wipegamestate = -1; // force a wipe gamestate = GS_LEVEL; for (i=0 ; itype == ev_keydown && ev->data1 == key_spy && (singledemo || !gameskill) ) // [STRIFE]: o_O { // spy mode do { displayplayer++; if (displayplayer == MAXPLAYERS) displayplayer = 0; } while (!playeringame[displayplayer] && displayplayer != consoleplayer); return true; } // any other key pops up menu if in demos if (gameaction == ga_nothing && !singledemo && (demoplayback || gamestate == GS_DEMOSCREEN) ) { if (ev->type == ev_keydown || (ev->type == ev_mouse && ev->data1) || (ev->type == ev_joystick && ev->data1) ) { if(devparm && ev->data1 == 'g') D_PageTicker(); // [STRIFE]: wat? o_O else M_StartControlPanel (); return true; } return false; } if (gamestate == GS_LEVEL) { #if 0 if (devparm && ev->type == ev_keydown && ev->data1 == ';') { G_DeathMatchSpawnPlayer (0); return true; } #endif if (HU_Responder (ev)) return true; // chat ate the event if (ST_Responder (ev)) return true; // status window ate it if (AM_Responder (ev)) return true; // automap ate it } if (gamestate == GS_FINALE) { if (F_Responder (ev)) return true; // finale ate the event } if (testcontrols && ev->type == ev_mouse) { // If we are invoked by setup to test the controls, save the // mouse speed so that we can display it on-screen. // Perform a low pass filter on this so that the thermometer // appears to move smoothly. testcontrols_mousespeed = abs(ev->data2); } // If the next/previous weapon keys are pressed, set the next_weapon // variable to change weapons when the next ticcmd is generated. if (ev->type == ev_keydown && ev->data1 == key_prevweapon) { next_weapon = -1; } else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) { next_weapon = 1; } switch (ev->type) { case ev_keydown: if (ev->data1 == key_pause) { sendpause = true; } else if (ev->data1 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); mousex = ev->data2*(mouseSensitivity+5)/10; mousey = ev->data3*(mouseSensitivity+5)/10; return true; // eat events case ev_joystick: SetJoyButtons(ev->data1); joyxmove = ev->data2; joyymove = ev->data3; joystrafemove = ev->data4; joylook = ev->data5; return true; // eat events default: break; } return false; } // // G_Ticker // Make ticcmd_ts for the players. // void G_Ticker (void) { int i; int buf; ticcmd_t* cmd; // do player reborns if needed for (i=0 ; iforwardmove > TURBOTHRESHOLD) { turbodetected[i] = true; } if ((gametic & 31) == 0 && ((gametic >> 5) % MAXPLAYERS) == i && turbodetected[i]) { static char turbomessage[80]; M_snprintf(turbomessage, sizeof(turbomessage), "%s is turbo!", player_names[i]); players[consoleplayer].message = turbomessage; turbodetected[i] = false; } if (netgame && !netdemo && !(gametic%ticdup) ) { if (gametic > BACKUPTICS && consistancy[i][buf] != cmd->consistancy) { I_Error ("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] = rndindex; } } } // check for special buttons for (i=0 ; i>BTS_SAVESHIFT; gameaction = ga_savegame; break; } } } } // Have we just finished displaying an intermission screen? // haleyjd 08/23/10: [STRIFE] No intermission. /* if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION) { WI_End(); } */ oldgamestate = gamestate; // do main actions switch (gamestate) { case GS_LEVEL: P_Ticker (); ST_Ticker (); AM_Ticker (); HU_Ticker (); break; // haleyjd 08/23/10: [STRIFE] No intermission. /* case GS_INTERMISSION: WI_Ticker (); break; */ case GS_UNKNOWN: // STRIFE-TODO: What is this? is it ever used?? F_WaitTicker(); break; case GS_FINALE: F_Ticker (); break; case GS_DEMOSCREEN: D_PageTicker (); break; } } // // PLAYER STRUCTURE FUNCTIONS // also see P_SpawnPlayer in P_Things // // // G_InitPlayer // Called at the start. // Called by the game initialization functions. // // [STRIFE] No such function. /* void G_InitPlayer (int player) { player_t* p; // set up the saved info p = &players[player]; // clear everything else to defaults G_PlayerReborn (player); } */ // // G_PlayerFinishLevel // Can when a player completes a level. // // [STRIFE] No such function. The equivalent to this logic was moved into // G_DoCompleted. /* void G_PlayerFinishLevel (int player) { player_t* p; p = &players[player]; memset (p->powers, 0, sizeof (p->powers)); memset (p->cards, 0, sizeof (p->cards)); p->mo->flags &= ~MF_SHADOW; // cancel invisibility p->extralight = 0; // cancel gun flashes p->fixedcolormap = 0; // cancel ir gogles p->damagecount = 0; // no palette changes p->bonuscount = 0; } */ // // G_PlayerReborn // Called after a player dies // almost everything is cleared and initialized // // [STRIFE] Small changes for allegiance, inventory, health auto-use, and // mission objective. // void G_PlayerReborn (int player) { player_t* p; int i; int frags[MAXPLAYERS]; int killcount; int allegiance; killcount = players[player].killcount; allegiance = players[player].allegiance; // [STRIFE] memcpy(frags,players[player].frags,sizeof(frags)); p = &players[player]; memset (p, 0, sizeof(*p)); memcpy(p->frags, frags, sizeof(p->frags)); p->usedown = true; // don't do anything immediately p->attackdown = true; p->inventorydown = true; // villsa [STRIFE] p->playerstate = PST_LIVE; p->health = deh_initial_health; // Use dehacked value p->readyweapon = wp_fist; // villsa [STRIFE] default to fists p->pendingweapon = wp_fist; // villsa [STRIFE] default to fists p->weaponowned[wp_fist] = true; // villsa [STRIFE] default to fists p->cheats |= CF_AUTOHEALTH; // villsa [STRIFE] p->killcount = killcount; p->allegiance = allegiance; // villsa [STRIFE] p->centerview = true; // villsa [STRIFE] for(i = 0; i < NUMAMMO; i++) p->maxammo[i] = maxammo[i]; // [STRIFE] clear inventory for(i = 0; i < 32; i++) p->inventory[i].type = NUMMOBJTYPES; // villsa [STRIFE]: Default objective M_StringCopy(mission_objective, DEH_String("Find help"), OBJECTIVE_LEN); } // // G_CheckSpot // Returns false if the player cannot be respawned // at the given mapthing_t spot // because something is occupying it // // [STRIFE] Changed to eliminate body queue and an odd error message was added. // void P_SpawnPlayer (mapthing_t* mthing); boolean G_CheckSpot ( int playernum, mapthing_t* mthing ) { fixed_t x; fixed_t y; subsector_t* ss; unsigned an; mobj_t* mo; int i; if (!players[playernum].mo) { // [STRIFE] weird error message added here: if(leveltime > 0) players[playernum].message = DEH_String("you didn't have a body!"); // 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; if (!P_CheckPosition (players[playernum].mo, x, y) ) return false; // flush an old corpse if needed // [STRIFE] player corpses remove themselves after a short time, so // evidently this wasn't needed. /* if (bodyqueslot >= BODYQUESIZE) P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; bodyqueslot++; */ // spawn a teleport fog ss = R_PointInSubsector (x,y); an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) >> ANGLETOFINESHIFT; mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an] , ss->sector->floorheight , MT_TFOG); if (players[consoleplayer].viewz != 1) S_StartSound (mo, sfx_telept); // don't start sound on first frame return true; } // // G_DeathMatchSpawnPlayer // Spawns a player at one of the random death match spots // called at level load and each death // // [STRIFE]: Modified exit message to match binary. // void G_DeathMatchSpawnPlayer (int playernum) { int i,j; int selections; selections = deathmatch_p - deathmatchstarts; if (selections < 4) I_Error ("Only %i deathmatch spots, at least 4 required!", selections); for (j=0 ; j<20 ; j++) { i = P_Random() % selections; if (G_CheckSpot (playernum, &deathmatchstarts[i]) ) { deathmatchstarts[i].type = playernum+1; P_SpawnPlayer (&deathmatchstarts[i]); return; } } // no good spot, so the player will probably get stuck P_SpawnPlayer (&playerstarts[playernum]); } // // G_LoadPath // // haleyjd 20101003: [STRIFE] New function // Sets loadpath based on the map and "savepathtemp" // void G_LoadPath(int map) { char mapbuf[33]; memset(mapbuf, 0, sizeof(mapbuf)); M_snprintf(mapbuf, sizeof(mapbuf), "%d", map); // haleyjd: free if already set, and use M_SafeFilePath if(loadpath) Z_Free(loadpath); loadpath = M_SafeFilePath(savepathtemp, mapbuf); } // // G_DoReborn // void G_DoReborn (int playernum) { int i; if (!netgame) { // reload the level from scratch // [STRIFE] Reborn level load G_LoadPath(gamemap); gameaction = ga_loadgame; } else { // respawn at the start // first dissasociate the corpse // [STRIFE] Checks for NULL first if(players[playernum].mo) 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 (&playerstarts[playernum]); return; } // try to spawn at one of the other players spots for (i=0 ; i Abandoned Front Base map = 30; if(map == 7) // Castle -> New Front Base map = 10; } // no rifting in deathmatch games if(deathmatch) spot = 0; riftangle = angle; riftdest = spot; destmap = map; } // // G_Exit2 // // haleyjd 20101003: [STRIFE] New function. // No xrefs to this, doesn't seem to be used. Could have gotten inlined // somewhere but I haven't seen it. // void G_Exit2(int dest, angle_t angle) { riftdest = dest; gameaction = ga_completed; riftangle = angle; destmap = gamemap; } // // G_ExitLevel // // haleyjd 20100824: [STRIFE]: // * Default to next map in numeric order; init destmap and riftdest. // void G_ExitLevel (int dest) { if(dest == 0) dest = gamemap + 1; destmap = dest; riftdest = 0; gameaction = ga_completed; } /* // haleyjd 20100823: [STRIFE] No secret exits in Strife. // Here's for the german edition. void G_SecretExitLevel (void) { // IF NO WOLF3D LEVELS, NO SECRET EXIT! if ( (gamemode == commercial) && (W_CheckNumForName("map31")<0)) secretexit = false; else secretexit = true; gameaction = ga_completed; } */ // // G_StartFinale // // haleyjd 20100921: [STRIFE] New function. // This replaced G_SecretExitLevel in Strife. I don't know that it's actually // used anywhere in the game, but it *is* usable in mods via linetype 124, // W1 Start Finale. // void G_StartFinale(void) { gameaction = ga_victory; } // // G_DoCompleted // // haleyjd 20100823: [STRIFE]: // * Removed G_PlayerFinishLevel and just sets some powerup states. // * Removed Chex, as not relevant to Strife. // * Removed DOOM level transfer logic // * Removed intermission code. // * Added setting gameaction to ga_worlddone. // void G_DoCompleted (void) { int i; // deal with powerup states for(i = 0; i < MAXPLAYERS; i++) { if(playeringame[i]) { // [STRIFE] restore pw_allmap power from mapstate cache if(destmap < 40) players[i].powers[pw_allmap] = players[i].mapstate[destmap]; // Shadowarmor doesn't persist between maps in netgames if(netgame) players[i].powers[pw_invisibility] = 0; } } stonecold = false; // villsa [STRIFE] if (automapactive) AM_Stop (); // [STRIFE] HUB SAVE if(!deathmatch) G_DoSaveGame(savepathtemp); gameaction = ga_worlddone; } // haleyjd 20100824: [STRIFE] No secret exits. /* // // G_WorldDone // void G_WorldDone (void) { gameaction = ga_worlddone; if (secretexit) players[consoleplayer].didsecret = true; if ( gamemode == commercial ) { switch (gamemap) { case 15: case 31: if (!secretexit) break; case 6: case 11: case 20: case 30: F_StartFinale (); break; } } } */ // // G_RiftPlayer // // haleyjd 20100824: [STRIFE] New function // Teleports the player to the appropriate rift spot. // void G_RiftPlayer(void) { if(riftdest) { P_TeleportMove(players[0].mo, riftSpots[riftdest - 1].x << FRACBITS, riftSpots[riftdest - 1].y << FRACBITS); players[0].mo->angle = riftangle; players[0].mo->health = players[0].health; } } // // G_RiftCheat // // haleyjd 20100824: [STRIFE] New function // Called from the cheat code to jump to a rift spot. // boolean G_RiftCheat(int riftSpotNum) { return P_TeleportMove(players[0].mo, riftSpots[riftSpotNum - 1].x << FRACBITS, riftSpots[riftSpotNum - 1].y << FRACBITS); } // // G_DoWorldDone // // haleyjd 20100824: [STRIFE] Added destmap -> gamemap set. // void G_DoWorldDone (void) { int temp_leveltime = leveltime; boolean temp_shadow = false; boolean temp_mvis = false; gamestate = GS_LEVEL; gamemap = destmap; // [STRIFE] HUB LOAD G_LoadPath(destmap); if (!deathmatch) { // Remember Shadowarmor across hub loads if(players[0].mo->flags & MF_SHADOW) temp_shadow = true; if(players[0].mo->flags & MF_MVIS) temp_mvis = true; } G_DoLoadGame(false); // [STRIFE] leveltime carries over between maps leveltime = temp_leveltime; if(!deathmatch) { // [STRIFE]: transfer saved powerups players[0].mo->flags &= ~(MF_SHADOW|MF_MVIS); if(temp_shadow) players[0].mo->flags |= MF_SHADOW; if(temp_mvis) players[0].mo->flags |= MF_MVIS; // [STRIFE] HUB SAVE G_RiftPlayer(); G_DoSaveGame(savepathtemp); M_SaveMisObj(savepathtemp); } gameaction = ga_nothing; viewactive = true; } // // G_DoWorldDone2 // // haleyjd 20101003: [STRIFE] New function. No xrefs; unused. // void G_DoWorldDone2(void) { gamestate = GS_LEVEL; gameaction = ga_nothing; viewactive = true; } // // G_ReadCurrent // // haleyjd 20101003: [STRIFE] New function. // Reads the "CURRENT" file from the given path and then sets it to // gamemap. // void G_ReadCurrent(const char *path) { char *temppath = NULL; byte *buffer = NULL; temppath = M_SafeFilePath(path, "\\current"); if(M_ReadFile(temppath, &buffer) <= 0) gameaction = ga_newgame; else { // haleyjd 20110211: do endian-correct read gamemap = (((int)buffer[0]) | ((int)buffer[1] << 8) | ((int)buffer[2] << 16) | ((int)buffer[3] << 24)); gameaction = ga_loadgame; Z_Free(buffer); } Z_Free(temppath); G_LoadPath(gamemap); } // // G_InitFromSavegame // Can be called by the startup code or the menu task. // extern boolean setsizeneeded; void R_ExecuteSetViewSize (void); char savename[256]; // [STRIFE]: No such function. /* void G_LoadGame (char* name) { M_StringCopy(savename, name, sizeof(savename)); gameaction = ga_loadgame; } */ // haleyjd 20100928: [STRIFE] VERSIONSIZE == 8 #define VERSIONSIZE 8 void G_DoLoadGame (boolean userload) { int savedleveltime; gameaction = ga_nothing; save_stream = fopen(loadpath, "rb"); // [STRIFE] If the file does not exist, G_DoLoadLevel is called. if (save_stream == NULL) { G_DoLoadLevel(); return; } savegame_error = false; if (!P_ReadSaveGameHeader()) { fclose(save_stream); return; } // haleyjd: A comment would be good here, fraggle... // Evidently this is a Choco-ism, necessitated by reading the savegame // header *before* calling G_DoLoadLevel. savedleveltime = leveltime; // load a base level // STRIFE-TODO: ???? if(userload) G_InitNew(gameskill, gamemap); else G_DoLoadLevel(); leveltime = savedleveltime; // dearchive all the modifications // [STRIFE] some portions of player_t are not overwritten when loading // between hub levels P_UnArchivePlayers (userload); P_UnArchiveWorld (); P_UnArchiveThinkers (); P_UnArchiveSpecials (); if (!P_ReadSaveGameEOF()) I_Error ("Bad savegame"); fclose(save_stream); if (setsizeneeded) R_ExecuteSetViewSize (); // draw the pattern into the back screen R_FillBackScreen (); } // // G_WriteSaveName // // haleyjd 20101003: [STRIFE] New function // // Writes the character name to the NAME file. // boolean G_WriteSaveName(int slot, const char *charname) { //char savedir[16]; char *tmpname; boolean retval; savegameslot = slot; // haleyjd: removed special -cdrom treatment, as I believe it is taken // care of automatically via using Choco's savegamedir setting. // haleyjd: free previous path, if any, and allocate new one using // M_SafeFilePath routine, which isn't limited to 128 characters. if(savepathtemp) Z_Free(savepathtemp); savepathtemp = M_SafeFilePath(savegamedir, "strfsav6.ssg"); // haleyjd: as above. if(savepath) Z_Free(savepath); savepath = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(savegameslot, "")); // haleyjd: memset full character_name for safety memset(character_name, 0, CHARACTER_NAME_LEN); M_StringCopy(character_name, charname, sizeof(character_name)); // haleyjd: use M_SafeFilePath tmpname = M_SafeFilePath(savepathtemp, "name"); // Write the "name" file under the directory retval = M_WriteFile(tmpname, character_name, 32); Z_Free(tmpname); return retval; } // // G_SaveGame // Called by the menu task. // Description is a 24 byte text string // // [STRIFE] No such function, at least in v1.2 // STRIFE-TODO: Does this make a comeback in v1.31? /* void G_SaveGame ( int slot, char* description ) { savegameslot = slot; M_StringCopy(savedescription, description, sizeof(savedescription)); sendsave = true; } */ void G_DoSaveGame (char *path) { char *current_path; char *savegame_file; char *temp_savegame_file; byte gamemapbytes[4]; char gamemapstr[33]; temp_savegame_file = P_TempSaveGameFile(); // [STRIFE] custom save file path logic memset(gamemapstr, 0, sizeof(gamemapstr)); M_snprintf(gamemapstr, sizeof(gamemapstr), "%d", gamemap); savegame_file = M_SafeFilePath(path, gamemapstr); // [STRIFE] write the "current" file, which tells which hub map // the save slot is currently on. current_path = M_SafeFilePath(path, "current"); // haleyjd: endian-agnostic IO gamemapbytes[0] = (byte)( gamemap & 0xff); gamemapbytes[1] = (byte)((gamemap >> 8) & 0xff); gamemapbytes[2] = (byte)((gamemap >> 16) & 0xff); gamemapbytes[3] = (byte)((gamemap >> 24) & 0xff); M_WriteFile(current_path, gamemapbytes, 4); Z_Free(current_path); // Open the savegame file for writing. We write to a temporary file // and then rename it at the end if it was successfully written. // This prevents an existing savegame from being overwritten by // a corrupted one, or if a savegame buffer overrun occurs. save_stream = fopen(temp_savegame_file, "wb"); if (save_stream == NULL) { return; } savegame_error = false; P_WriteSaveGameHeader(savedescription); P_ArchivePlayers (); P_ArchiveWorld (); P_ArchiveThinkers (); P_ArchiveSpecials (); P_WriteSaveGameEOF(); // Enforce the same savegame size limit as in Vanilla Doom, // except if the vanilla_savegame_limit setting is turned off. // [STRIFE]: Verified subject to same limit. if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE) { I_Error ("Savegame buffer overrun"); } // Finish up, close the savegame file. fclose(save_stream); // Now rename the temporary savegame file to the actual savegame // file, overwriting the old savegame if there was one there. remove(savegame_file); rename(temp_savegame_file, savegame_file); // haleyjd: free the savegame_file path Z_Free(savegame_file); gameaction = ga_nothing; //M_StringCopy(savedescription, "", sizeof(savedescription)); // [STRIFE]: custom message logic if(!strcmp(path, savepath)) { M_snprintf(savename, sizeof(savename), "%s saved.", character_name); players[consoleplayer].message = savename; } // draw the pattern into the back screen R_FillBackScreen (); } // skill_t d_skill; //int d_episode; [STRIFE] No such thing as episodes in Strife int d_map; // // G_DeferedInitNew // // Can be called by the startup code or the menu task, // consoleplayer, displayplayer, playeringame[] should be set. // // haleyjd 20100922: [STRIFE] Removed episode parameter // void G_DeferedInitNew(skill_t skill, int map) { d_skill = skill; d_map = map; gameaction = ga_newgame; } // // G_DoNewGame // // [STRIFE] Code added to turn off the stonecold effect. // Someone also removed the nomonsters reset... // void G_DoNewGame (void) { demoplayback = false; netdemo = false; netgame = false; deathmatch = false; playeringame[1] = playeringame[2] = playeringame[3] = 0; respawnparm = false; fastparm = false; stonecold = false; // villsa [STRIFE] //nomonsters = false; [STRIFE] not set here!?! consoleplayer = 0; G_InitNew (d_skill, d_map); gameaction = ga_nothing; } // // G_InitNew // // haleyjd 20100824: [STRIFE]: // * Added riftdest initialization // * Removed episode parameter // void G_InitNew ( skill_t skill, int map ) { const char *skytexturename; int i; if (paused) { paused = false; S_ResumeSound (); } if (skill > sk_nightmare) skill = sk_nightmare; // [STRIFE] Removed episode nonsense and gamemap clipping M_ClearRandom (); if (skill == sk_nightmare || respawnparm ) respawnmonsters = true; else respawnmonsters = false; // [STRIFE] Strife skill level mobjinfo/states tweaking // BUG: None of this code runs properly when loading save games, so // basically it's impossible to play any skill level properly unless // you never quit and reload from the command line. if(!skill && gameskill) { // Setting to Baby skill... make things easier. // Acolytes walk, attack, and feel pain slower for(i = S_AGRD_13; i <= S_AGRD_23; i++) states[i].tics *= 2; // Reavers attack slower for(i = S_ROB1_10; i <= S_ROB1_15; i++) states[i].tics *= 2; // Turrets attack slower for(i = S_TURT_02; i <= S_TURT_03; i++) states[i].tics *= 2; // Crusaders attack and feel pain slower for(i = S_ROB2_09; i <= S_ROB2_19; i++) states[i].tics *= 2; // Stalkers think, walk, and attack slower for(i = S_SPID_03; i <= S_SPID_10; i++) states[i].tics *= 2; // The Bishop's homing missiles are faster (what?? BUG?) mobjinfo[MT_SEEKMISSILE].speed *= 2; } if(skill && !gameskill) { // Setting a higher skill when previously on baby... make things normal // Acolytes for(i = S_AGRD_13; i <= S_AGRD_23; i++) states[i].tics >>= 1; // Reavers for(i = S_ROB1_10; i <= S_ROB1_15; i++) states[i].tics >>= 1; // Turrets for(i = S_TURT_02; i <= S_TURT_03; i++) states[i].tics >>= 1; // Crusaders for(i = S_ROB2_09; i <= S_ROB2_19; i++) states[i].tics >>= 1; // Stalkers for(i = S_SPID_03; i <= S_SPID_10; i++) states[i].tics >>= 1; // The Bishop's homing missiles - again, seemingly backward. mobjinfo[MT_SEEKMISSILE].speed >>= 1; } if(fastparm || (skill == sk_nightmare && skill != gameskill)) { // BLOODBATH! Make some things super-aggressive. // Acolytes walk, attack, and feel pain twice as fast // (This makes just getting out of the first room almost impossible) for(i = S_AGRD_13; i <= S_AGRD_23; i++) states[i].tics >>= 1; // Bishop's homing missiles again get SLOWER and not faster o_O mobjinfo[MT_SEEKMISSILE].speed >>= 1; } else if(skill != sk_nightmare && gameskill == sk_nightmare) { // Setting back to an ordinary skill after being on Bloodbath? // Put stuff back to normal. // Acolytes for(i = S_AGRD_13; i <= S_AGRD_23; i++) states[i].tics *= 2; // Bishop's homing missiles mobjinfo[MT_SEEKMISSILE].speed *= 2; } // force players to be initialized upon first level load for (i=0 ; i= 9 && gamemap < 32) skytexturename = "skymnt01"; else skytexturename = "skymnt02"; skytexturename = DEH_String(skytexturename); skytexture = R_TextureNumForName(skytexturename); // [STRIFE] HUBS G_LoadPath(gamemap); G_DoLoadLevel(); } // // DEMO RECORDING // #define DEMOMARKER 0x80 // // G_ReadDemoTiccmd // // [STRIFE] Modified for Strife ticcmd_t // void G_ReadDemoTiccmd (ticcmd_t* cmd) { if (*demo_p == DEMOMARKER) { // end of demo data stream G_CheckDemoStatus (); return; } cmd->forwardmove = ((signed char)*demo_p++); cmd->sidemove = ((signed char)*demo_p++); cmd->angleturn = ((unsigned char) *demo_p++)<<8; cmd->buttons = (unsigned char)*demo_p++; cmd->buttons2 = (unsigned char)*demo_p++; // [STRIFE] cmd->inventory = (int)*demo_p++; // [STRIFE] } // Increase the size of the demo buffer to allow unlimited demos static void IncreaseDemoBuffer(void) { int current_length; byte *new_demobuffer; byte *new_demop; int new_length; // Find the current size current_length = demoend - demobuffer; // Generate a new buffer twice the size new_length = current_length * 2; new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0); new_demop = new_demobuffer + (demo_p - demobuffer); // Copy over the old data memcpy(new_demobuffer, demobuffer, current_length); // Free the old buffer and point the demo pointers at the new buffer. Z_Free(demobuffer); demobuffer = new_demobuffer; demo_p = new_demop; demoend = demobuffer + new_length; } // // G_WriteDemoTiccmd // // [STRIFE] Modified for Strife ticcmd_t. // void G_WriteDemoTiccmd (ticcmd_t* cmd) { byte *demo_start; if (gamekeydown[key_demo_quit]) // press q to end demo recording G_CheckDemoStatus (); demo_start = demo_p; *demo_p++ = cmd->forwardmove; *demo_p++ = cmd->sidemove; *demo_p++ = cmd->angleturn >> 8; *demo_p++ = cmd->buttons; *demo_p++ = cmd->buttons2; // [STRIFE] *demo_p++ = (byte)(cmd->inventory & 0xff); // [STRIFE] // reset demo pointer back demo_p = demo_start; if (demo_p > demoend - 16) { if (vanilla_demo_limit) { // no more space G_CheckDemoStatus (); return; } else { // Vanilla demo limit disabled: unlimited // demo lengths! IncreaseDemoBuffer(); } } G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same } // // G_RecordDemo // // [STRIFE] Verified unmodified // void G_RecordDemo (char* name) { size_t demoname_size; int i; int maxsize; usergame = false; demoname_size = strlen(name) + 5; demoname = Z_Malloc(demoname_size, PU_STATIC, NULL); M_snprintf(demoname, demoname_size, "%s.lmp", name); maxsize = 0x20000; //! // @arg // @category demo // @vanilla // // Specify the demo buffer size (KiB) // i = M_CheckParmWithArgs("-maxdemo", 1); if (i) maxsize = atoi(myargv[i+1])*1024; demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL); demoend = demobuffer + maxsize; demorecording = true; } void G_BeginRecording (void) { int i; // // @category demo // // Record a high resolution "Doom 1.91" demo. // // STRIFE-TODO: if somebody makes a "Strife Plus", we could add this. /* longtics = M_CheckParm("-longtics") != 0; */ longtics = false; // If not recording a longtics demo, record in low res lowres_turn = !longtics; demo_p = demobuffer; // Save the right version code for this demo *demo_p++ = STRIFE_VERSION; *demo_p++ = gameskill; //*demo_p++ = gameepisode; [STRIFE] Doesn't have episodes. *demo_p++ = gamemap; *demo_p++ = deathmatch; *demo_p++ = respawnparm; *demo_p++ = fastparm; *demo_p++ = nomonsters; *demo_p++ = consoleplayer; for (i=0 ; i #include "doomdef.h" #include "doomkeys.h" #include "v_video.h" #include "i_swap.h" #include "hu_lib.h" #include "r_local.h" #include "r_draw.h" #include "hu_stuff.h" // [STRIFE] // boolean : whether the screen is always erased #define noterased viewwindowx extern boolean automapactive; // in AM_map.c extern boolean D_PatchClipCallback(patch_t *patch, int x, int y); // [STRIFE] // // HUlib_drawYellowText // // haleyjd 20100918: [STRIFE] New function. // void HUlib_drawYellowText(int x, int y, const char *text) { int start_x = x; const char *rover = text; char c; while((c = *rover++)) { if(c == '\n') { x = start_x; y += 12; continue; } // haleyjd 20110213: found MORE code ignored/misinterpreted by Hex-Rays: // Underscores are replaced by spaces. if(c == '_') c = ' '; else if (c == ' ' && x == start_x) // skip spaces at the start of a line continue; c = toupper(c) - HU_FONTSTART; if(c >= 0 && c < HU_FONTSIZE) { patch_t *patch = yfont[(int) c]; int width = SHORT(patch->width); if(x + width <= (SCREENWIDTH - 20)) { // haleyjd: STRIFE-TODO: bit different than the exe... for now if(!D_PatchClipCallback(patch, x + SHORT(patch->leftoffset), y + SHORT(patch->topoffset))) return; V_DrawPatchDirect(x, y, patch); x = x + width; } else { x = start_x; --rover; y += 12; } } else { x += 4; } } } // // HUlib_init // // [STRIFE] Verified unmodified. // void HUlib_init(void) { } // // HUlib_clearTextLine // // [STRIFE] Verified unmodified. // void HUlib_clearTextLine(hu_textline_t* t) { t->len = 0; t->l[0] = 0; t->needsupdate = true; } // // HUlib_initTextLine // // [STRIFE] Verified unmodified // void HUlib_initTextLine ( hu_textline_t* t, int x, int y, patch_t** f, int sc ) { t->x = x; t->y = y; t->f = f; t->sc = sc; HUlib_clearTextLine(t); } // // HUlib_addCharToTextLine // // [STRIFE] Verified unmodified. // boolean HUlib_addCharToTextLine ( hu_textline_t* t, char ch ) { if (t->len == HU_MAXLINELENGTH) return false; else { t->l[t->len++] = ch; t->l[t->len] = 0; t->needsupdate = 4; return true; } } // // HUlib_delCharFromTextLine // // [STRIFE] Verified unmodified. // boolean HUlib_delCharFromTextLine(hu_textline_t* t) { if (!t->len) return false; else { t->l[--t->len] = 0; t->needsupdate = 4; return true; } } // // HUlib_drawTextLine // // haleyjd 09/18/10: [STRIFE] Modified to not draw underscores in text. // void HUlib_drawTextLine ( hu_textline_t* l, boolean drawcursor ) { int i; int w; int x; unsigned char c; // draw the new stuff x = l->x; for(i = 0; i < l->len; i++) { c = toupper(l->l[i]); if (c != ' ' && c >= l->sc && c < '_') // [STRIFE]: Underscores excluded { w = SHORT(l->f[c - l->sc]->width); if (x+w > SCREENWIDTH) break; V_DrawPatchDirect(x, l->y, l->f[c - l->sc]); x += w; } else { x += 4; if (x >= SCREENWIDTH) break; } } // draw the cursor if requested if (drawcursor && x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH) { V_DrawPatchDirect(x, l->y, l->f['_' - l->sc]); } } // // HUlib_eraseTextLine // // sorta called by HU_Erase and just better darn get things straight // // [STRIFE] Verified unmodified. // void HUlib_eraseTextLine(hu_textline_t* l) { int lh; int y; int yoffset; // Only erases when NOT in automap and the screen is reduced, // and the text must either need updating or refreshing // (because of a recent change back from the automap) if (!automapactive && viewwindowx && l->needsupdate) { lh = SHORT(l->f[0]->height) + 1; for (y=l->y,yoffset=y*SCREENWIDTH ; yy+lh ; y++,yoffset+=SCREENWIDTH) { if (y < viewwindowy || y >= viewwindowy + viewheight) R_VideoErase(yoffset, SCREENWIDTH); // erase entire line else { R_VideoErase(yoffset, viewwindowx); // erase left border R_VideoErase(yoffset + viewwindowx + viewwidth, viewwindowx); // erase right border } } } if (l->needsupdate) l->needsupdate--; } // // HUlib_initSText // // [STRIFE] Verified unmodified. // void HUlib_initSText ( hu_stext_t* s, int x, int y, int h, patch_t** font, int startchar, boolean* on ) { int i; s->h = h; s->on = on; s->laston = true; s->cl = 0; for (i=0;il[i], x, y - i*(SHORT(font[0]->height)+1), font, startchar); } } // // HUlib_addLineToSText // // [STRIFE] Verified unmodified. // 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 // // [STRIFE] Verified unmodified. // 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 // // [STRIFE] Verified unmodified. // 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 // // [STRIFE] Verified unmodified. // 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; } // // HUlib_initIText // // [STRIFE] Verified unmodified. // void HUlib_initIText ( hu_itext_t* it, int x, int y, patch_t** font, int startchar, boolean* on ) { it->lm = 0; // default left margin is start of text it->on = on; it->laston = true; HUlib_initTextLine(&it->l, x, y, font, startchar); } // The following deletion routines adhere to the left margin restriction // [STRIFE] Verified unmodified. void HUlib_delCharFromIText(hu_itext_t* it) { if (it->l.len != it->lm) HUlib_delCharFromTextLine(&it->l); } // [STRIFE] Verified unmodified. void HUlib_eraseLineFromIText(hu_itext_t* it) { while (it->lm != it->l.len) HUlib_delCharFromTextLine(&it->l); } // Resets left margin as well // [STRIFE] Verified unmodified. void HUlib_resetIText(hu_itext_t* it) { it->lm = 0; HUlib_clearTextLine(&it->l); } // // HUlib_addPrefixToIText // // [STRIFE] Verified unmodified. // void HUlib_addPrefixToIText ( hu_itext_t* it, char* str ) { while (*str) HUlib_addCharToTextLine(&it->l, *(str++)); it->lm = it->l.len; } // wrapper function for handling general keyed input. // returns true if it ate the key // [STRIFE] Verified unmodified. boolean HUlib_keyInIText ( hu_itext_t* it, unsigned char ch ) { ch = toupper(ch); if (ch >= ' ' && ch <= '_') HUlib_addCharToTextLine(&it->l, (char) ch); else if (ch == KEY_BACKSPACE) HUlib_delCharFromIText(it); else if (ch != KEY_ENTER) return false; // did not eat key return true; // ate the key } // // HUlib_drawIText // // [STRIFE] Verified unmodified. // 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 // // [STRIFE] Verified unmodified. // void HUlib_eraseIText(hu_itext_t* it) { if (it->laston && !*it->on) it->l.needsupdate = 4; HUlib_eraseTextLine(&it->l); it->laston = *it->on; } crispy-doom-crispy-doom-5.6.4/src/strife/hu_lib.h000066400000000000000000000072061360717211000217370ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // #ifndef __HULIB__ #define __HULIB__ // We are referring to patches. #include "r_defs.h" // font stuff #define HU_CHARERASE KEY_BACKSPACE #define HU_MAXLINES 4 #define HU_MAXLINELENGTH 80 // // Typedefs of widgets // // Text Line widget // (parent of Scrolling Text and Input Text widgets) typedef struct { // left-justified position of scrolling text window int x; int y; patch_t** f; // font int sc; // start character char l[HU_MAXLINELENGTH+1]; // line of text int len; // current line length // whether this line needs to be udpated int needsupdate; } hu_textline_t; // Scrolling Text window widget // (child of Text Line widget) typedef struct { hu_textline_t l[HU_MAXLINES]; // text lines to draw int h; // height in lines int cl; // current line number // pointer to boolean stating whether to update window boolean* on; boolean laston; // last value of *->on. } hu_stext_t; // Input Text Line widget // (child of Text Line widget) typedef struct { hu_textline_t l; // text line to input on // left margin past which I am not to delete characters int lm; // pointer to boolean stating whether to update window boolean* on; boolean laston; // last value of *->on; } hu_itext_t; // // Widget creation, access, and update routines // // initializes heads-up widget library void HUlib_init(void); // // textline code // // clear a line of text void HUlib_clearTextLine(hu_textline_t *t); void HUlib_initTextLine(hu_textline_t *t, int x, int y, patch_t **f, int sc); // returns success boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch); // returns success boolean HUlib_delCharFromTextLine(hu_textline_t *t); // draws tline void HUlib_drawTextLine(hu_textline_t *l, boolean drawcursor); // erases text line void HUlib_eraseTextLine(hu_textline_t *l); // villsa [STRIFE] void HUlib_drawYellowText(int x, int y, const char *text); // // Scrolling Text window widget routines // // ? void HUlib_initSText ( hu_stext_t* s, int x, int y, int h, patch_t** font, int startchar, boolean* on ); // add a new line void HUlib_addLineToSText(hu_stext_t* s); // ? 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); // Input Text Line widget routines void HUlib_initIText ( hu_itext_t* it, int x, int y, patch_t** font, int startchar, boolean* on ); // enforces left margin void HUlib_delCharFromIText(hu_itext_t* it); // enforces left margin void HUlib_eraseLineFromIText(hu_itext_t* it); // resets line and left margin void HUlib_resetIText(hu_itext_t* it); // left of left-margin void HUlib_addPrefixToIText ( hu_itext_t* it, char* str ); // whether eaten boolean HUlib_keyInIText ( hu_itext_t* it, unsigned char ch ); void HUlib_drawIText(hu_itext_t* it); // erases all itext lines void HUlib_eraseIText(hu_itext_t* it); #endif crispy-doom-crispy-doom-5.6.4/src/strife/hu_stuff.c000066400000000000000000000442661360717211000223220ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Heads-up displays // #include #include "doomdef.h" #include "doomkeys.h" #include "z_zone.h" #include "deh_main.h" #include "i_input.h" #include "i_swap.h" #include "i_video.h" #include "hu_stuff.h" #include "hu_lib.h" #include "m_controls.h" #include "m_misc.h" #include "w_wad.h" #include "s_sound.h" #include "doomstat.h" // Data. #include "dstrings.h" #include "sounds.h" // // Locally used constants, shortcuts. // #define HU_TITLE (mapnames[gamemap-1]) #define HU_TITLEHEIGHT 1 #define HU_TITLEX 0 // haleyjd 09/01/10: [STRIFE] 167 -> 160 to move up level name #define HU_TITLEY (160 - SHORT(hu_font[0]->height)) #define HU_INPUTTOGGLE 't' #define HU_INPUTX HU_MSGX #define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1)) #define HU_INPUTWIDTH 64 #define HU_INPUTHEIGHT 1 char *chat_macros[10] = { HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8, HUSTR_CHATMACRO9 }; // villsa [STRIFE] char player_names[8][16] = { "1: ", "2: ", "3: ", "4: ", "5: ", "6: ", "7: ", "8: " }; char chat_char; // remove later. static player_t* plr; patch_t* hu_font[HU_FONTSIZE]; patch_t* yfont[HU_FONTSIZE]; // haleyjd 09/18/10: [STRIFE] static hu_textline_t w_title; boolean chat_on; static hu_itext_t w_chat; static boolean always_off = false; static char chat_dest[MAXPLAYERS]; static hu_itext_t w_inputbuffer[MAXPLAYERS]; static boolean message_on; boolean message_dontfuckwithme; static boolean message_nottobefuckedwith; static hu_stext_t w_message; static int message_counter; //extern int showMessages; [STRIFE] no such variable static boolean headsupactive = false; // haleyjd 20130915 [STRIFE]: need nickname extern char *nickname; // haleyjd 20130915 [STRIFE]: true if setting nickname static boolean hu_setting_name = false; // // Builtin map names. // The actual names can be found in DStrings.h. // // haleyjd 08/31/10: [STRIFE] Changed for Strife level names. // List of names for levels. const char *mapnames[] = { // Strife map names // First "episode" - Quest to destroy the Order's Castle HUSTR_1, HUSTR_2, HUSTR_3, HUSTR_4, HUSTR_5, HUSTR_6, HUSTR_7, HUSTR_8, HUSTR_9, // Second "episode" - Kill the Bishop and Make a Choice HUSTR_10, HUSTR_11, HUSTR_12, HUSTR_13, HUSTR_14, HUSTR_15, HUSTR_16, HUSTR_17, HUSTR_18, HUSTR_19, // Third "episode" - Shut down Factory, kill Loremaster and Entity HUSTR_20, HUSTR_21, HUSTR_22, HUSTR_23, HUSTR_24, HUSTR_25, HUSTR_26, HUSTR_27, HUSTR_28, HUSTR_29, // "Secret" levels - Abandoned Base and Training Facility HUSTR_30, HUSTR_31, // Demo version maps HUSTR_32, HUSTR_33, HUSTR_34 }; // // HU_Init // // haleyjd 09/18/10: [STRIFE] // * Modified to load yfont along with hu_font. // void HU_Init(void) { int i; int j; char buffer[9]; // load the heads-up font j = HU_FONTSTART; for (i=0;i This is definitely not the best that Rogue had to offer. Markov. // // Fastcall Registers: edx ebx // Temp Registers: esi edi static void HU_addMessage(const char *prefix, const char *message) { char c; // eax int width = 0; // edx const char *rover1; // ebx (in first loop) const char *rover2; // ecx (in second loop) char *bufptr; // ebx (in second loop) char buffer[HU_MAXLINELENGTH+2]; // esp+52h // Loop 1: Total up width of prefix. rover1 = prefix; if(rover1) { while((c = *rover1)) { c = toupper(c) - HU_FONTSTART; ++rover1; if(c < 0 || c >= HU_FONTSIZE) width += 4; else width += SHORT(hu_font[(int) c]->width); } } // Loop 2: Copy as much of message into buffer as will fit on screen bufptr = buffer; rover2 = message; while((c = *rover2)) { if((c == ' ' || c == '-') && width > 285) break; *bufptr = c; ++bufptr; // BUG: No check for overflow. ++rover2; c = toupper(c); if(c == ' ' || c < '!' || c >= '_') width += 4; else { c -= HU_FONTSTART; width += SHORT(hu_font[(int) c]->width); } } // Too big to fit? // BUG: doesn't consider by how much it's over. if(width > 320) { // backup a char... hell if I know why. --bufptr; --rover2; } // rover2 is not at the end? if((c = *rover2)) { // if not ON a space... if(c != ' ') { // back up both pointers til one is found. // BUG: no check against LHS of buffer. Hurr! while(*bufptr != ' ') { --bufptr; --rover2; } } } *bufptr = '\0'; // Add two message lines. HUlib_addMessageToSText(&w_message, prefix, buffer); HUlib_addMessageToSText(&w_message, NULL, rover2); } // // HU_Ticker // // haleyjd 09/18/10: [STRIFE] Changes to split up message into two lines, // and support for player names (STRIFE-TODO: unfinished!) // void HU_Ticker(void) { int i, rc; char c; //char *prefix; STRIFE-TODO // tick down message counter if message is up if (message_counter && !--message_counter) { message_on = false; message_nottobefuckedwith = false; } // haleyjd 20110219: [STRIFE] this condition was removed //if (showMessages || message_dontfuckwithme) //{ // display message if necessary if ((plr->message && !message_nottobefuckedwith) || (plr->message && message_dontfuckwithme)) { //HUlib_addMessageToSText(&w_message, 0, plr->message); HU_addMessage(NULL, plr->message); // haleyjd [STRIFE] plr->message = 0; message_on = true; message_counter = HU_MSGTIMEOUT; message_nottobefuckedwith = message_dontfuckwithme; message_dontfuckwithme = 0; } //} // else message_on = false; // check for incoming chat characters if (netgame) { for (i=0 ; idata1 == KEY_RSHIFT) { return false; } else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT) { altdown = ev->type == ev_keydown; return false; } if (ev->type != ev_keydown) return false; if (!chat_on) { if (ev->data1 == key_message_refresh) { message_on = true; message_counter = HU_MSGTIMEOUT; eatkey = true; } else if (netgame && ev->data2 == key_multi_msg) { StartChatInput(); eatkey = true; HUlib_resetIText(&w_chat); HU_queueChatChar(HU_BROADCAST); } // [STRIFE]: You cannot go straight to chatting with a particular // player from here... you must press 't' first. See below. } else { c = ev->data3; // send a macro if (altdown) { c = c - '0'; if (c > 9) return false; // fprintf(stderr, "got here\n"); macromessage = chat_macros[c]; // kill last message with a '\n' HU_queueChatChar(KEY_ENTER); // DEBUG!!! // send the macro message while (*macromessage) HU_queueChatChar(*macromessage++); HU_queueChatChar(KEY_ENTER); // leave chat mode and notify that it was sent StopChatInput(); M_StringCopy(lastmessage, chat_macros[c], sizeof(lastmessage)); plr->message = lastmessage; eatkey = true; } else { if(w_chat.l.len) // [STRIFE]: past first char of chat? { eatkey = HUlib_keyInIText(&w_chat, c); if (eatkey) HU_queueChatChar(c); } else { // [STRIFE]: check for player-specific message; // slightly different than vanilla, to allow keys to be customized for(i = 0; i < MAXPLAYERS; i++) { if (ev->data1 == key_multi_msgplayer[i]) break; } if(i < MAXPLAYERS) { // talking to self? if(i == consoleplayer) { num_nobrainers++; if (num_nobrainers < 3) plr->message = DEH_String(HUSTR_TALKTOSELF1); else if (num_nobrainers < 6) plr->message = DEH_String(HUSTR_TALKTOSELF2); else if (num_nobrainers < 9) plr->message = DEH_String(HUSTR_TALKTOSELF3); else if (num_nobrainers < 32) plr->message = DEH_String(HUSTR_TALKTOSELF4); else plr->message = DEH_String(HUSTR_TALKTOSELF5); } else { eatkey = true; HU_queueChatChar(i+1); DEH_snprintf(lastmessage, sizeof(lastmessage), "Talking to: %c", '1' + i); plr->message = lastmessage; } } else if(c == '$') // [STRIFE]: name changing { eatkey = true; HU_queueChatChar(HU_CHANGENAME); M_StringCopy(lastmessage, DEH_String("Changing Name:"), sizeof(lastmessage)); plr->message = lastmessage; hu_setting_name = true; } else { eatkey = HUlib_keyInIText(&w_chat, c); if (eatkey) HU_queueChatChar(c); } } if (c == KEY_ENTER) { StopChatInput(); if (w_chat.l.len) { // [STRIFE]: name setting if(hu_setting_name) { DEH_snprintf(lastmessage, sizeof(lastmessage), "%s now %.13s", player_names[consoleplayer], w_chat.l.l); // haleyjd 20141024: missing name set for local client DEH_snprintf(player_names[consoleplayer], sizeof(player_names[consoleplayer]), "%.13s: ", w_chat.l.l); hu_setting_name = false; } else { M_StringCopy(lastmessage, w_chat.l.l, sizeof(lastmessage)); } plr->message = lastmessage; } } else if (c == KEY_ESCAPE) { StopChatInput(); } } } return eatkey; } crispy-doom-crispy-doom-5.6.4/src/strife/hu_stuff.h000066400000000000000000000034461360717211000223220ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Head up display // #ifndef __HU_STUFF_H__ #define __HU_STUFF_H__ #include "d_event.h" #include "v_patch.h" // // Globally visible constants. // #define HU_FONTSTART '!' // the first font characters #define HU_FONTEND '_' // the last font characters // Calculate # of glyphs in font. #define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) #define HU_BROADCAST 9 // haleyjd [STRIFE] Changed 5 -> 9 #define HU_CHANGENAME 10 // haleyjd [STRIFE] Special command #define HU_MSGX 0 #define HU_MSGY (SHORT(hu_font[0]->height) + 1) // [STRIFE]: DOOM bug fix #define HU_MSGWIDTH 64 // in characters #define HU_MSGHEIGHT 2 // in lines #define HU_MSGTIMEOUT (8*TICRATE) // haleyjd [STRIFE] Doubled message timeout // // HEADS UP TEXT // void HU_Init(void); void HU_Start(void); boolean HU_Responder(event_t* ev); void HU_Ticker(void); void HU_Drawer(void); char HU_dequeueChatChar(void); void HU_Erase(void); extern char *chat_macros[10]; extern char player_names[8][16]; // villsa [STRIFE] // haleyjd [STRIFE] externalized: extern const char *mapnames[]; // [STRIFE] extern patch_t* yfont[HU_FONTSIZE]; // haleyjd 09/18/10: [STRIFE] #endif crispy-doom-crispy-doom-5.6.4/src/strife/info.c000066400000000000000000014615661360717211000214410ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Thing frame/state LUT, // generated by multigen utilitiy. // This one is the original DOOM version, preserved. // #include #include // Data. #include "sounds.h" #include "m_fixed.h" #include "info.h" #include "p_mobj.h" // villsa [STRIFE] const char *sprnames[NUMSPRITES+1] = { "PLAY", "PNCH", "WAVE", "RBPY", "TRGT", "XBOW", "MMIS", "RIFG", "RIFF", "FLMT", "FLMF", "BLST", "BLSF", "GREN", "GREF", "SIGH", "SIGF", "POW1", "POW2", "POW3", "ZAP1", "SPRY", "BLOD", "PUFY", "SHT1", "SHT2", "GRIN", "GRAP", "UBAM", "BNG2", "BNG4", "BNG3", "FLBE", "XPRK", "OCLW", "CCLW", "TEND", "MICR", "MISS", "AROW", "ARWP", "TORP", "THIT", "TWAV", "MISL", "TFOG", "IFOG", "SHRD", "RGIB", "MRYS", "MRNO", "MRST", "MRLK", "MRBD", "MRPN", "MRGT", "BURN", "DISR", "PEAS", "GIBS", "AGRD", "ARMR", "SACR", "TNK1", "TNK2", "TNK3", "TNK4", "TNK5", "TNK6", "NEAL", "BEGR", "HMN1", "LEDR", "LEAD", "ROB1", "PGRD", "ROB2", "MLDR", "ORCL", "PRST", "PDED", "ALN1", "AL1P", "NODE", "MTHD", "MNAM", "MNAL", "MDTH", "NEST", "PODD", "ZAP6", "ZOT3", "ZAP7", "ZOT1", "ZAP5", "ZOT2", "SEWR", "SPID", "ROB3", "RBB3", "PRGR", "BASE", "FRBL", "KLAX", "TURT", "BALL", "PSTN", "SECR", "TARG", "RING", "EARS", "COMM", "BOOM", "RATT", "HOGN", "DEAD", "SBAN", "BOTR", "HATR", "TOPR", "COUP", "BUBB", "BUBF", "BUBC", "ASPR", "SPDL", "TOKN", "OTOK", "HELT", "GUNT", "FULL", "MEAT", "JUNK", "FFOT", "DIE1", "BEAC", "ARM1", "ARM2", "BARW", "BART", "LAMP", "LANT", "BARL", "BOWL", "BRAZ", "TRCH", "LTRH", "LMPC", "LOGS", "TRHO", "WATR", "MUGG", "FUSL", "CRD1", "CRD2", "TPAS", "KY1G", "KY2S", "KY3B", "HAND", "CRYS", "PRIS", "PWR1", "PWR2", "PWR3", "ORAC", "GYID", "FUBR", "WARE", "RCRY", "BCRY", "CHAP", "TUNL", "BLTK", "SECK", "MINE", "REBL", "PROC", "ANKH", "GOID", "STMP", "MDKT", "COIN", "CRED", "SACK", "CHST", "SHD1", "MASK", "UNIF", "OFIC", "PMAP", "PMUP", "BLIT", "BBOX", "MSSL", "ROKT", "BRY1", "CPAC", "PQRL", "XQRL", "GRN1", "GRN2", "BKPK", "RELC", "RIFL", "FLAM", "BFLM", "MMSL", "TRPD", "GRND", "CBOW", "SIGL", "LITE", "CNDL", "CLBR", "LITS", "LITB", "LITG", "ROK1", "ROK2", "ROK3", "ROK4", "LOGG", "RUB1", "RUB2", "RUB3", "RUB4", "RUB5", "RUB6", "RUB7", "RUB8", "CHAN", "STAT", "DSTA", "CRAB", "CAGE", "TREE", "TRE1", "BUSH", "SHRB", "STAK", "BAR1", "VASE", "STOL", "POT1", "TUB1", "ANVL", "TLMP", "TRAY", "APOW", "AFED", "DRIP", "CDRP", "SPLH", "WTFT", "HERT", "TELP", "MONI", "STEL", "STLA", "STLE", "HUGE", "STLG", NULL }; // Doesn't work with g++, needs actionf_p1 // villsa [STRIFE] void A_Look(); void A_RandomWalk(); void A_FriendLook(); void A_Listen(); void A_Chase(); void A_FaceTarget(); void A_PeasantPunch(); void A_ReaverAttack(); void A_BulletAttack(); void A_CheckTargetVisible(); void A_SentinelAttack(); void A_StalkerThink(); void A_StalkerSetLook(); void A_StalkerDrop(); void A_StalkerScratch(); void A_FloatWeave(); void A_RobotMelee(); void A_TemplarMauler(); void A_CrusaderAttack(); void A_CrusaderLeft(); void A_CrusaderRight(); void A_CheckTargetVisible2(); void A_InqFlyCheck(); void A_InqGrenade(); void A_InqTakeOff(); void A_InqFly(); void A_FireSigilWeapon(); void A_ProgrammerAttack(); void A_Sigil_A_Action(); void A_SpectreEAttack(); void A_SpectreCAttack(); void A_AlertSpectreC(); void A_Sigil_E_Action(); void A_SigilTrail(); void A_SpectreDAttack(); void A_FireSigilEOffshoot(); void A_ShadowOff(); void A_ModifyVisibility(); void A_ShadowOn(); void A_SetTLOptions(); void A_BossMeleeAtk(); void A_BishopAttack(); void A_FireHookShot(); void A_FireChainShot(); void A_MissileSmoke(); void A_SpawnSparkPuff(); void A_Tracer(); void A_ProgrammerMelee(); void A_Scream(); void A_XScream(); void A_Pain(); void A_PeasantCrash(); void A_Fall(); void A_HideZombie(); void A_MerchantPain(); void A_ProgrammerDie(); void A_InqTossArm(); void A_SpawnSpectreB(); void A_SpawnSpectreD(); void A_SpawnSpectreE(); void A_SpawnEntity(); void A_EntityDeath(); void A_SpawnZombie(); void A_ZombieInSpecialSector(); void A_CrystalExplode(); void A_QuestMsg(); void A_ExtraLightOff(); void A_CrystalRadiusAtk(); void A_DeathExplode5(); void A_DeathExplode1(); void A_DeathExplode2(); void A_DeathExplode3(); void A_RaiseAlarm(); void A_MissileTick(); void A_SpawnGrenadeFire(); void A_NodeChunk(); void A_HeadChunk(); void A_BurnSpread(); void A_AcolyteSpecial(); void A_BossDeath(); void A_InqChase(); void A_StalkerChase(); void A_PlayerScream(); void A_TeleportBeacon(); void A_BodyParts(); void A_ClaxonBlare(); void A_ActiveSound(); void A_ClearSoundTarget(); void A_DropBurnFlesh(); void A_FlameDeath(); void A_ClearForceField(); void A_WeaponReady(); void A_ReFire(); void A_CheckReload(); void A_Lower(); void A_Raise(); void A_GunFlash(); void A_Punch(); void A_FireFlameThrower(); void A_FireMissile(); void A_FireMauler2(); void A_FireGrenade(); void A_FireElectricBolt(); void A_FirePoisonBolt(); void A_FireRifle(); void A_FireMauler1(); void A_SigilSound(); void A_FireSigil(); void A_GunFlashThinker(); void A_Light0(); void A_Light1(); void A_Light2(); void A_SigilShock(); void A_TorpedoExplode(); void A_MaulerSound(); // villsa [STRIFE] state_t states[NUMSTATES] = { /*S_NULL*/ { SPR_PLAY, 0, -1, { NULL }, S_NULL }, //00 /*S_PNCH_00*/ { SPR_PNCH, 0, 0, { A_Light0 }, S_NULL }, //01 /*S_WAVE_00*/ { SPR_WAVE, 32768, 3, { NULL }, S_WAVE_01 }, //02 /*S_WAVE_01*/ { SPR_WAVE, 32769, 3, { NULL }, S_WAVE_02 }, //03 /*S_WAVE_02*/ { SPR_WAVE, 32770, 3, { NULL }, S_WAVE_03 }, //04 /*S_WAVE_03*/ { SPR_WAVE, 32771, 3, { NULL }, S_WAVE_00 }, //05 /*S_RBPY_00*/ { SPR_RBPY, 32768, 3, { NULL }, S_RBPY_01 }, //06 /*S_RBPY_01*/ { SPR_RBPY, 32769, 3, { NULL }, S_RBPY_02 }, //07 /*S_RBPY_02*/ { SPR_RBPY, 32770, 3, { NULL }, S_RBPY_03 }, //08 /*S_RBPY_03*/ { SPR_RBPY, 32771, 3, { NULL }, S_RBPY_00 }, //09 /*S_TRGT_00*/ { SPR_TRGT, 0, -1, { NULL }, S_NULL }, //10 /*S_TRGT_01*/ { SPR_TRGT, 1, -1, { NULL }, S_NULL }, //11 /*S_TRGT_02*/ { SPR_TRGT, 2, -1, { NULL }, S_NULL }, //12 /*S_PNCH_01*/ { SPR_PNCH, 0, 1, { A_WeaponReady }, S_PNCH_01 }, //13 /*S_PNCH_02*/ { SPR_PNCH, 0, 1, { A_Lower }, S_PNCH_02 }, //14 /*S_PNCH_03*/ { SPR_PNCH, 0, 1, { A_Raise }, S_PNCH_03 }, //15 /*S_PNCH_04*/ { SPR_PNCH, 1, 4, { NULL }, S_PNCH_05 }, //16 /*S_PNCH_05*/ { SPR_PNCH, 2, 4, { A_Punch }, S_PNCH_06 }, //17 /*S_PNCH_06*/ { SPR_PNCH, 3, 5, { NULL }, S_PNCH_07 }, //18 /*S_PNCH_07*/ { SPR_PNCH, 2, 4, { NULL }, S_PNCH_08 }, //19 /*S_PNCH_08*/ { SPR_PNCH, 1, 5, { A_ReFire }, S_PNCH_01 }, //20 /*S_XBOW_00*/ { SPR_XBOW, 0, 1, { A_WeaponReady }, S_XBOW_00 }, //21 /*S_XBOW_01*/ { SPR_XBOW, 0, 1, { A_Lower }, S_XBOW_01 }, //22 /*S_XBOW_02*/ { SPR_XBOW, 0, 1, { A_Raise }, S_XBOW_02 }, //23 /*S_XBOW_03*/ { SPR_XBOW, 0, 3, { A_GunFlashThinker }, S_XBOW_04 }, //24 /*S_XBOW_04*/ { SPR_XBOW, 1, 6, { A_FireElectricBolt }, S_XBOW_05 }, //25 /*S_XBOW_05*/ { SPR_XBOW, 2, 4, { NULL }, S_XBOW_06 }, //26 /*S_XBOW_06*/ { SPR_XBOW, 3, 6, { NULL }, S_XBOW_07 }, //27 /*S_XBOW_07*/ { SPR_XBOW, 4, 3, { NULL }, S_XBOW_08 }, //28 /*S_XBOW_08*/ { SPR_XBOW, 5, 5, { NULL }, S_XBOW_09 }, //29 /*S_XBOW_09*/ { SPR_XBOW, 6, 5, { A_CheckReload }, S_XBOW_00 }, //30 /*S_XBOW_10*/ { SPR_XBOW, 10, 5, { NULL }, S_XBOW_11 }, //31 /*S_XBOW_11*/ { SPR_XBOW, 11, 5, { NULL }, S_XBOW_12 }, //32 /*S_XBOW_12*/ { SPR_XBOW, 12, 5, { NULL }, S_XBOW_10 }, //33 /*S_XBOW_13*/ { SPR_XBOW, 7, 1, { A_WeaponReady }, S_XBOW_13 }, //34 /*S_XBOW_14*/ { SPR_XBOW, 7, 1, { A_Lower }, S_XBOW_14 }, //35 /*S_XBOW_15*/ { SPR_XBOW, 7, 1, { A_Raise }, S_XBOW_15 }, //36 /*S_XBOW_16*/ { SPR_XBOW, 7, 3, { NULL }, S_XBOW_17 }, //37 /*S_XBOW_17*/ { SPR_XBOW, 1, 6, { A_FirePoisonBolt }, S_XBOW_18 }, //38 /*S_XBOW_18*/ { SPR_XBOW, 2, 4, { NULL }, S_XBOW_19 }, //39 /*S_XBOW_19*/ { SPR_XBOW, 3, 6, { NULL }, S_XBOW_20 }, //40 /*S_XBOW_20*/ { SPR_XBOW, 4, 3, { NULL }, S_XBOW_21 }, //41 /*S_XBOW_21*/ { SPR_XBOW, 8, 5, { NULL }, S_XBOW_22 }, //42 /*S_XBOW_22*/ { SPR_XBOW, 9, 5, { A_CheckReload }, S_XBOW_13 }, //43 /*S_MMIS_00*/ { SPR_MMIS, 0, 1, { A_WeaponReady }, S_MMIS_00 }, //44 /*S_MMIS_01*/ { SPR_MMIS, 0, 1, { A_Lower }, S_MMIS_01 }, //45 /*S_MMIS_02*/ { SPR_MMIS, 0, 1, { A_Raise }, S_MMIS_02 }, //46 /*S_MMIS_03*/ { SPR_MMIS, 0, 4, { A_FireMissile }, S_MMIS_04 }, //47 /*S_MMIS_04*/ { SPR_MMIS, 1, 4, { A_Light1 }, S_MMIS_05 }, //48 /*S_MMIS_05*/ { SPR_MMIS, 32770, 5, { NULL }, S_MMIS_06 }, //49 /*S_MMIS_06*/ { SPR_MMIS, 32771, 2, { A_Light2 }, S_MMIS_07 }, //50 /*S_MMIS_07*/ { SPR_MMIS, 32772, 2, { NULL }, S_MMIS_08 }, //51 /*S_MMIS_08*/ { SPR_MMIS, 32773, 2, { A_Light0 }, S_MMIS_09 }, //52 /*S_MMIS_09*/ { SPR_MMIS, 5, 0, { A_ReFire }, S_MMIS_00 }, //53 /*S_RIFG_00*/ { SPR_RIFG, 0, 1, { A_WeaponReady }, S_RIFG_00 }, //54 /*S_RIFG_01*/ { SPR_RIFG, 1, 1, { A_Lower }, S_RIFG_01 }, //55 /*S_RIFG_02*/ { SPR_RIFG, 0, 1, { A_Raise }, S_RIFG_02 }, //56 /*S_RIFF_00*/ { SPR_RIFF, 0, 3, { A_FireRifle }, S_RIFF_01 }, //57 /*S_RIFF_01*/ { SPR_RIFF, 1, 3, { A_FireRifle }, S_RIFG_03 }, //58 /*S_RIFG_03*/ { SPR_RIFG, 3, 3, { A_FireRifle }, S_RIFG_04 }, //59 /*S_RIFG_04*/ { SPR_RIFG, 2, 0, { A_ReFire }, S_RIFG_05 }, //60 /*S_RIFG_05*/ { SPR_RIFG, 1, 2, { NULL }, S_RIFG_00 }, //61 /*S_FLMT_00*/ { SPR_FLMT, 0, 3, { A_WeaponReady }, S_FLMT_01 }, //62 /*S_FLMT_01*/ { SPR_FLMT, 1, 3, { A_WeaponReady }, S_FLMT_00 }, //63 /*S_FLMT_02*/ { SPR_FLMT, 0, 1, { A_Lower }, S_FLMT_02 }, //64 /*S_FLMT_03*/ { SPR_FLMT, 0, 1, { A_Raise }, S_FLMT_03 }, //65 /*S_FLMF_00*/ { SPR_FLMF, 0, 2, { A_FireFlameThrower }, S_FLMF_01 }, //66 /*S_FLMF_01*/ { SPR_FLMF, 1, 3, { A_ReFire }, S_FLMT_00 }, //67 /*S_BLST_00*/ { SPR_BLST, 5, 6, { A_WeaponReady }, S_BLST_01 }, //68 /*S_BLST_01*/ { SPR_BLST, 6, 6, { A_WeaponReady }, S_BLST_02 }, //69 /*S_BLST_02*/ { SPR_BLST, 7, 6, { A_WeaponReady }, S_BLST_03 }, //70 /*S_BLST_03*/ { SPR_BLST, 0, 6, { A_WeaponReady }, S_BLST_00 }, //71 /*S_BLST_04*/ { SPR_BLST, 0, 1, { A_Lower }, S_BLST_04 }, //72 /*S_BLST_05*/ { SPR_BLST, 0, 1, { A_Raise }, S_BLST_05 }, //73 /*S_BLSF_00*/ { SPR_BLSF, 32768, 5, { A_FireMauler1 }, S_BLST_06 }, //74 /*S_BLST_06*/ { SPR_BLST, 32769, 3, { A_Light1 }, S_BLST_07 }, //75 /*S_BLST_07*/ { SPR_BLST, 2, 2, { A_Light2 }, S_BLST_08 }, //76 /*S_BLST_08*/ { SPR_BLST, 3, 2, { NULL }, S_BLST_09 }, //77 /*S_BLST_09*/ { SPR_BLST, 4, 2, { NULL }, S_BLST_10 }, //78 /*S_BLST_10*/ { SPR_BLST, 0, 7, { A_Light0 }, S_BLST_11 }, //79 /*S_BLST_11*/ { SPR_BLST, 7, 7, { NULL }, S_BLST_12 }, //80 /*S_BLST_12*/ { SPR_BLST, 6, 7, { A_CheckReload }, S_BLST_00 }, //81 /*S_BLST_13*/ { SPR_BLST, 8, 7, { A_WeaponReady }, S_BLST_14 }, //82 /*S_BLST_14*/ { SPR_BLST, 9, 7, { A_WeaponReady }, S_BLST_15 }, //83 /*S_BLST_15*/ { SPR_BLST, 10, 7, { A_WeaponReady }, S_BLST_16 }, //84 /*S_BLST_16*/ { SPR_BLST, 11, 7, { A_WeaponReady }, S_BLST_13 }, //85 /*S_BLST_17*/ { SPR_BLST, 8, 1, { A_Lower }, S_BLST_17 }, //86 /*S_BLST_18*/ { SPR_BLST, 8, 1, { A_Raise }, S_BLST_18 }, //87 /*S_BLST_19*/ { SPR_BLST, 8, 20, { A_MaulerSound }, S_BLST_20 }, //88 /*S_BLST_20*/ { SPR_BLST, 9, 10, { A_Light1 }, S_BLSF_01 }, //89 /*S_BLSF_01*/ { SPR_BLSF, 32768, 10, { A_FireMauler2 }, S_BLST_21 }, //90 /*S_BLST_21*/ { SPR_BLST, 32769, 3, { A_Light2 }, S_BLST_22 }, //91 /*S_BLST_22*/ { SPR_BLST, 2, 2, { NULL }, S_BLST_23 }, //92 /*S_BLST_23*/ { SPR_BLST, 3, 2, { A_Light0 }, S_BLST_24 }, //93 /*S_BLST_24*/ { SPR_BLST, 4, 2, { A_ReFire }, S_BLST_13 }, //94 /*S_GREN_00*/ { SPR_GREN, 0, 1, { A_WeaponReady }, S_GREN_00 }, //95 /*S_GREN_01*/ { SPR_GREN, 0, 1, { A_Lower }, S_GREN_01 }, //96 /*S_GREN_02*/ { SPR_GREN, 0, 1, { A_Raise }, S_GREN_02 }, //97 /*S_GREN_03*/ { SPR_GREN, 0, 5, { A_FireGrenade }, S_GREN_04 }, //98 /*S_GREN_04*/ { SPR_GREN, 1, 10, { NULL }, S_GREN_05 }, //99 /*S_GREN_05*/ { SPR_GREN, 0, 5, { A_FireGrenade }, S_GREN_06 }, //100 /*S_GREN_06*/ { SPR_GREN, 2, 10, { NULL }, S_GREN_07 }, //101 /*S_GREN_07*/ { SPR_GREN, 0, 0, { A_ReFire }, S_GREN_00 }, //102 /*S_GREF_00*/ { SPR_GREF, 32768, 5, { A_Light1 }, S_PNCH_00 }, //103 /*S_GREF_01*/ { SPR_GREF, 0, 10, { A_Light0 }, S_PNCH_00 }, //104 /*S_GREF_02*/ { SPR_GREF, 32769, 5, { A_Light2 }, S_PNCH_00 }, //105 /*S_GREN_08*/ { SPR_GREN, 3, 1, { A_WeaponReady }, S_GREN_08 }, //106 /*S_GREN_09*/ { SPR_GREN, 3, 1, { A_Lower }, S_GREN_09 }, //107 /*S_GREN_10*/ { SPR_GREN, 3, 1, { A_Raise }, S_GREN_10 }, //108 /*S_GREN_11*/ { SPR_GREN, 3, 5, { A_FireGrenade }, S_GREN_12 }, //109 /*S_GREN_12*/ { SPR_GREN, 4, 10, { NULL }, S_GREN_13 }, //110 /*S_GREN_13*/ { SPR_GREN, 3, 5, { A_FireGrenade }, S_GREN_14 }, //111 /*S_GREN_14*/ { SPR_GREN, 5, 10, { NULL }, S_GREN_15 }, //112 /*S_GREN_15*/ { SPR_GREN, 0, 0, { A_ReFire }, S_GREN_08 }, //113 /*S_GREF_03*/ { SPR_GREF, 32770, 5, { A_Light1 }, S_PNCH_00 }, //114 /*S_GREF_04*/ { SPR_GREF, 2, 10, { A_Light0 }, S_PNCH_00 }, //115 /*S_GREF_05*/ { SPR_GREF, 32771, 5, { A_Light2 }, S_PNCH_00 }, //116 /*S_SIGH_00*/ { SPR_SIGH, 32768, 1, { A_WeaponReady }, S_SIGH_00 }, //117 /*S_SIGH_01*/ { SPR_SIGH, 32769, -1, { NULL }, S_NULL }, //118 /*S_SIGH_02*/ { SPR_SIGH, 32770, -1, { NULL }, S_NULL }, //119 /*S_SIGH_03*/ { SPR_SIGH, 32771, -1, { NULL }, S_NULL }, //120 /*S_SIGH_04*/ { SPR_SIGH, 32772, -1, { NULL }, S_NULL }, //121 /*S_SIGH_05*/ { SPR_SIGH, 32768, 1, { A_Lower }, S_SIGH_05 }, //122 /*S_SIGH_06*/ { SPR_SIGH, 32768, 1, { A_Raise }, S_SIGH_06 }, //123 /*S_SIGH_07*/ { SPR_SIGH, 32768, 18, { A_SigilSound }, S_SIGH_08 }, //124 /*S_SIGH_08*/ { SPR_SIGH, 32768, 3, { A_GunFlash }, S_SIGH_09 }, //125 /*S_SIGH_09*/ { SPR_SIGH, 0, 10, { A_FireSigil }, S_SIGH_10 }, //126 /*S_SIGH_10*/ { SPR_SIGH, 0, 5, { A_GunFlashThinker }, S_SIGH_00 }, //127 /*S_SIGF_00*/ { SPR_SIGF, 32768, 4, { A_Light2 }, S_SIGF_01 }, //128 /*S_SIGF_01*/ { SPR_SIGF, 32769, 6, { A_SigilShock }, S_SIGF_02 }, //129 /*S_SIGF_02*/ { SPR_SIGF, 32770, 4, { A_Light1 }, S_PNCH_00 }, //130 /*S_POW1_00*/ { SPR_POW1, 0, 4, { NULL }, S_POW1_01 }, //131 /*S_POW1_01*/ { SPR_POW1, 1, 4, { NULL }, S_POW1_02 }, //132 /*S_POW1_02*/ { SPR_POW1, 2, 4, { NULL }, S_POW1_03 }, //133 /*S_POW1_03*/ { SPR_POW1, 3, 4, { NULL }, S_POW1_04 }, //134 /*S_POW1_04*/ { SPR_POW1, 4, 4, { NULL }, S_NULL }, //135 /*S_POW1_05*/ { SPR_POW1, 5, 4, { NULL }, S_POW1_06 }, //136 /*S_POW1_06*/ { SPR_POW1, 6, 4, { NULL }, S_POW1_07 }, //137 /*S_POW1_07*/ { SPR_POW1, 7, 4, { NULL }, S_POW1_08 }, //138 /*S_POW1_08*/ { SPR_POW1, 8, 4, { NULL }, S_POW1_09 }, //139 /*S_POW1_09*/ { SPR_POW1, 9, 4, { NULL }, S_NULL }, //140 /*S_POW2_00*/ { SPR_POW2, 0, 4, { NULL }, S_POW2_01 }, //141 /*S_POW2_01*/ { SPR_POW2, 1, 4, { NULL }, S_POW2_02 }, //142 /*S_POW2_02*/ { SPR_POW2, 2, 4, { NULL }, S_POW2_03 }, //143 /*S_POW2_03*/ { SPR_POW2, 3, 4, { NULL }, S_NULL }, //144 /*S_POW3_00*/ { SPR_POW3, 0, 3, { NULL }, S_POW3_01 }, //145 /*S_POW3_01*/ { SPR_POW3, 1, 3, { NULL }, S_POW3_02 }, //146 /*S_POW3_02*/ { SPR_POW3, 2, 3, { NULL }, S_POW3_03 }, //147 /*S_POW3_03*/ { SPR_POW3, 3, 3, { NULL }, S_POW3_04 }, //148 /*S_POW3_04*/ { SPR_POW3, 4, 3, { NULL }, S_POW3_05 }, //149 /*S_POW3_05*/ { SPR_POW3, 5, 3, { NULL }, S_POW3_06 }, //150 /*S_POW3_06*/ { SPR_POW3, 6, 3, { NULL }, S_POW3_07 }, //151 /*S_POW3_07*/ { SPR_POW3, 7, 3, { NULL }, S_NULL }, //152 /*S_ZAP1_00*/ { SPR_ZAP1, 1, 3, { A_DeathExplode3 }, S_ZAP1_02 }, //153 /*S_ZAP1_01*/ { SPR_ZAP1, 0, 3, { A_RaiseAlarm }, S_ZAP1_02 }, //154 /*S_ZAP1_02*/ { SPR_ZAP1, 1, 3, { NULL }, S_ZAP1_03 }, //155 /*S_ZAP1_03*/ { SPR_ZAP1, 2, 3, { NULL }, S_ZAP1_04 }, //156 /*S_ZAP1_04*/ { SPR_ZAP1, 3, 3, { NULL }, S_ZAP1_05 }, //157 /*S_ZAP1_05*/ { SPR_ZAP1, 4, 3, { NULL }, S_ZAP1_06 }, //158 /*S_ZAP1_06*/ { SPR_ZAP1, 5, 3, { NULL }, S_ZAP1_07 }, //159 /*S_ZAP1_07*/ { SPR_ZAP1, 4, 3, { NULL }, S_ZAP1_08 }, //160 /*S_ZAP1_08*/ { SPR_ZAP1, 3, 2, { NULL }, S_ZAP1_09 }, //161 /*S_ZAP1_09*/ { SPR_ZAP1, 2, 2, { NULL }, S_ZAP1_10 }, //162 /*S_ZAP1_10*/ { SPR_ZAP1, 1, 2, { NULL }, S_ZAP1_11 }, //163 /*S_ZAP1_11*/ { SPR_ZAP1, 0, 1, { NULL }, S_NULL }, //164 /*S_SPRY_00*/ { SPR_SPRY, 0, 3, { NULL }, S_SPRY_01 }, //165 /*S_SPRY_01*/ { SPR_SPRY, 1, 3, { NULL }, S_SPRY_02 }, //166 /*S_SPRY_02*/ { SPR_SPRY, 2, 3, { NULL }, S_SPRY_03 }, //167 /*S_SPRY_03*/ { SPR_SPRY, 3, 3, { NULL }, S_SPRY_04 }, //168 /*S_SPRY_04*/ { SPR_SPRY, 4, 3, { NULL }, S_SPRY_05 }, //169 /*S_SPRY_05*/ { SPR_SPRY, 5, 3, { NULL }, S_SPRY_06 }, //170 /*S_SPRY_06*/ { SPR_SPRY, 6, 2, { NULL }, S_NULL }, //171 /*S_BLOD_00*/ { SPR_BLOD, 2, 8, { NULL }, S_BLOD_01 }, //172 /*S_BLOD_01*/ { SPR_BLOD, 1, 8, { NULL }, S_BLOD_02 }, //173 /*S_BLOD_02*/ { SPR_BLOD, 0, 8, { NULL }, S_NULL }, //174 /*S_PUFY_00*/ { SPR_PUFY, 32768, 4, { NULL }, S_PUFY_01 }, //175 /*S_PUFY_01*/ { SPR_PUFY, 1, 4, { NULL }, S_PUFY_02 }, //176 /*S_PUFY_02*/ { SPR_PUFY, 2, 4, { NULL }, S_PUFY_03 }, //177 /*S_PUFY_03*/ { SPR_PUFY, 3, 4, { NULL }, S_NULL }, //178 /*S_SHT1_00*/ { SPR_SHT1, 0, 4, { NULL }, S_SHT1_01 }, //179 /*S_SHT1_01*/ { SPR_SHT1, 1, 4, { NULL }, S_SHT1_00 }, //180 /*S_SHT2_00*/ { SPR_SHT2, 0, 5, { NULL }, S_SHT2_01 }, //181 /*S_SHT2_01*/ { SPR_SHT2, 1, 5, { NULL }, S_POW1_00 }, //182 /*S_GRIN_00*/ { SPR_GRIN, 0, 3, { A_MissileTick }, S_GRIN_01 }, //183 /*S_GRIN_01*/ { SPR_GRIN, 1, 3, { A_MissileTick }, S_GRIN_00 }, //184 /*S_GRAP_00*/ { SPR_GRAP, 0, 3, { A_MissileTick }, S_GRAP_01 }, //185 /*S_GRAP_01*/ { SPR_GRAP, 1, 3, { A_MissileTick }, S_GRAP_00 }, //186 /*S_UBAM_00*/ { SPR_UBAM, 0, 3, { A_MissileTick }, S_UBAM_01 }, //187 /*S_UBAM_01*/ { SPR_UBAM, 1, 3, { A_MissileTick }, S_UBAM_00 }, //188 /*S_BNG2_00*/ { SPR_BNG2, 32768, 4, { A_DeathExplode5 }, S_BNG2_01 }, //189 /*S_BNG2_01*/ { SPR_BNG2, 32769, 4, { NULL }, S_BNG2_02 }, //190 /*S_BNG2_02*/ { SPR_BNG2, 32770, 4, { NULL }, S_BNG2_03 }, //191 /*S_BNG2_03*/ { SPR_BNG2, 32771, 4, { NULL }, S_BNG2_04 }, //192 /*S_BNG2_04*/ { SPR_BNG2, 32772, 4, { NULL }, S_BNG2_05 }, //193 /*S_BNG2_05*/ { SPR_BNG2, 32773, 4, { NULL }, S_BNG2_06 }, //194 /*S_BNG2_06*/ { SPR_BNG2, 32774, 4, { NULL }, S_BNG2_07 }, //195 /*S_BNG2_07*/ { SPR_BNG2, 32775, 4, { NULL }, S_BNG2_08 }, //196 /*S_BNG2_08*/ { SPR_BNG2, 32776, 4, { NULL }, S_NULL }, //197 /*S_BNG4_00*/ { SPR_BNG4, 32768, 2, { A_DeathExplode5 }, S_BNG4_01 }, //198 /*S_BNG4_01*/ { SPR_BNG4, 32769, 3, { NULL }, S_BNG4_02 }, //199 /*S_BNG4_02*/ { SPR_BNG4, 32770, 3, { NULL }, S_BNG4_03 }, //200 /*S_BNG4_03*/ { SPR_BNG4, 32771, 3, { NULL }, S_BNG4_04 }, //201 /*S_BNG4_04*/ { SPR_BNG4, 32772, 3, { NULL }, S_BNG4_05 }, //202 /*S_BNG4_05*/ { SPR_BNG4, 32773, 3, { NULL }, S_BNG4_06 }, //203 /*S_BNG4_06*/ { SPR_BNG4, 32774, 3, { NULL }, S_BNG4_07 }, //204 /*S_BNG4_07*/ { SPR_BNG4, 32775, 3, { NULL }, S_BNG4_08 }, //205 /*S_BNG4_08*/ { SPR_BNG4, 32776, 3, { NULL }, S_BNG4_09 }, //206 /*S_BNG4_09*/ { SPR_BNG4, 32777, 3, { NULL }, S_BNG4_10 }, //207 /*S_BNG4_10*/ { SPR_BNG4, 32778, 3, { NULL }, S_BNG4_11 }, //208 /*S_BNG4_11*/ { SPR_BNG4, 32779, 3, { NULL }, S_BNG4_12 }, //209 /*S_BNG4_12*/ { SPR_BNG4, 32780, 3, { NULL }, S_BNG4_13 }, //210 /*S_BNG4_13*/ { SPR_BNG4, 32781, 3, { NULL }, S_NULL }, //211 /*S_BNG3_00*/ { SPR_BNG3, 32768, 3, { A_DeathExplode5 }, S_BNG3_01 }, //212 /*S_BNG3_01*/ { SPR_BNG3, 32769, 3, { NULL }, S_BNG3_02 }, //213 /*S_BNG3_02*/ { SPR_BNG3, 32770, 3, { NULL }, S_BNG3_03 }, //214 /*S_BNG3_03*/ { SPR_BNG3, 32771, 3, { NULL }, S_BNG3_04 }, //215 /*S_BNG3_04*/ { SPR_BNG3, 32772, 3, { NULL }, S_BNG3_05 }, //216 /*S_BNG3_05*/ { SPR_BNG3, 32773, 3, { NULL }, S_BNG3_06 }, //217 /*S_BNG3_06*/ { SPR_BNG3, 32774, 3, { NULL }, S_BNG3_07 }, //218 /*S_BNG3_07*/ { SPR_BNG3, 32775, 3, { NULL }, S_NULL }, //219 /*S_BNG3_08*/ { SPR_BNG3, 0, 1, { A_SpawnGrenadeFire }, S_NULL }, //220 /*S_BNG3_09*/ { SPR_BNG3, 32769, 2, { A_DeathExplode1 }, S_BNG3_10 }, //221 /*S_BNG3_10*/ { SPR_BNG3, 32770, 2, { A_MissileTick }, S_FLBE_00 }, //222 /*S_FLBE_00*/ { SPR_FLBE, 32768, 2, { A_BurnSpread }, S_FLBE_01 }, //223 /*S_FLBE_01*/ { SPR_FLBE, 32769, 2, { A_MissileTick }, S_FLBE_02 }, //224 /*S_FLBE_02*/ { SPR_FLBE, 32770, 2, { A_DeathExplode1 }, S_FLBE_03 }, //225 /*S_FLBE_03*/ { SPR_FLBE, 32771, 3, { A_MissileTick }, S_FLBE_04 }, //226 /*S_FLBE_04*/ { SPR_FLBE, 32772, 3, { A_DeathExplode1 }, S_FLBE_05 }, //227 /*S_FLBE_05*/ { SPR_FLBE, 32773, 3, { A_MissileTick }, S_FLBE_06 }, //228 /*S_FLBE_06*/ { SPR_FLBE, 32774, 3, { A_BurnSpread }, S_FLBE_03 }, //229 /*S_FLBE_07*/ { SPR_FLBE, 32775, 2, { NULL }, S_FLBE_08 }, //230 /*S_FLBE_08*/ { SPR_FLBE, 32776, 2, { A_BurnSpread }, S_FLBE_09 }, //231 /*S_FLBE_09*/ { SPR_FLBE, 32777, 2, { NULL }, S_FLBE_10 }, //232 /*S_FLBE_10*/ { SPR_FLBE, 32778, 2, { NULL }, S_NULL }, //233 /*S_XPRK_00*/ { SPR_XPRK, 0, 1, { A_ClearForceField }, S_NULL }, //234 /*S_OCLW_00*/ { SPR_OCLW, 0, 2, { A_FireChainShot }, S_OCLW_00 }, //235 /*S_CCLW_00*/ { SPR_CCLW, 0, 6, { NULL }, S_NULL }, //236 /*S_TEND_00*/ { SPR_TEND, 0, 20, { NULL }, S_NULL }, //237 /*S_MICR_00*/ { SPR_MICR, 32768, 6, { A_MissileSmoke }, S_MICR_00 }, //238 /*S_MISS_00*/ { SPR_MISS, 32768, 4, { A_MissileSmoke }, S_MISS_01 }, //239 /*S_MISS_01*/ { SPR_MISS, 32769, 3, { A_Tracer }, S_MISS_00 }, //240 /*S_AROW_00*/ { SPR_AROW, 0, 10, { A_ActiveSound }, S_AROW_00 }, //241 /*S_ARWP_00*/ { SPR_ARWP, 0, 10, { A_ActiveSound }, S_ARWP_00 }, //242 /*S_AROW_01*/ { SPR_AROW, 0, 1, { NULL }, S_NULL }, //243 /*S_TORP_00*/ { SPR_TORP, 32768, 4, { NULL }, S_TORP_01 }, //244 /*S_TORP_01*/ { SPR_TORP, 32769, 4, { NULL }, S_TORP_02 }, //245 /*S_TORP_02*/ { SPR_TORP, 32770, 4, { NULL }, S_TORP_03 }, //246 /*S_TORP_03*/ { SPR_TORP, 32771, 4, { NULL }, S_TORP_00 }, //247 /*S_THIT_00*/ { SPR_THIT, 32768, 8, { NULL }, S_THIT_01 }, //248 /*S_THIT_01*/ { SPR_THIT, 32769, 8, { NULL }, S_THIT_02 }, //249 /*S_THIT_02*/ { SPR_THIT, 32770, 8, { A_TorpedoExplode }, S_THIT_03 }, //250 /*S_THIT_03*/ { SPR_THIT, 32771, 8, { NULL }, S_THIT_04 }, //251 /*S_THIT_04*/ { SPR_THIT, 32772, 8, { NULL }, S_NULL }, //252 /*S_TWAV_00*/ { SPR_TWAV, 32768, 9, { NULL }, S_TWAV_01 }, //253 /*S_TWAV_01*/ { SPR_TWAV, 32769, 9, { NULL }, S_TWAV_02 }, //254 /*S_TWAV_02*/ { SPR_TWAV, 32770, 9, { NULL }, S_NULL }, //255 /*S_MISL_00*/ { SPR_MISL, 32768, 5, { NULL }, S_MISL_02 }, //256 /*S_MISL_01*/ { SPR_MISL, 32768, 5, { A_DeathExplode2 }, S_MISL_02 }, //257 /*S_MISL_02*/ { SPR_MISL, 32769, 5, { NULL }, S_MISL_03 }, //258 /*S_MISL_03*/ { SPR_MISL, 32770, 4, { NULL }, S_MISL_04 }, //259 /*S_MISL_04*/ { SPR_MISL, 32771, 2, { NULL }, S_MISL_05 }, //260 /*S_MISL_05*/ { SPR_MISL, 32772, 2, { NULL }, S_MISL_06 }, //261 /*S_MISL_06*/ { SPR_MISL, 32773, 2, { NULL }, S_MISL_07 }, //262 /*S_MISL_07*/ { SPR_MISL, 32774, 2, { NULL }, S_NULL }, //263 /*S_TFOG_00*/ { SPR_TFOG, 32768, 6, { NULL }, S_TFOG_01 }, //264 /*S_TFOG_01*/ { SPR_TFOG, 32769, 6, { NULL }, S_TFOG_02 }, //265 /*S_TFOG_02*/ { SPR_TFOG, 32770, 6, { NULL }, S_TFOG_03 }, //266 /*S_TFOG_03*/ { SPR_TFOG, 32771, 6, { NULL }, S_TFOG_04 }, //267 /*S_TFOG_04*/ { SPR_TFOG, 32772, 6, { NULL }, S_TFOG_05 }, //268 /*S_TFOG_05*/ { SPR_TFOG, 32773, 6, { NULL }, S_TFOG_06 }, //269 /*S_TFOG_06*/ { SPR_TFOG, 32772, 6, { NULL }, S_TFOG_07 }, //270 /*S_TFOG_07*/ { SPR_TFOG, 32771, 6, { NULL }, S_TFOG_08 }, //271 /*S_TFOG_08*/ { SPR_TFOG, 32770, 6, { NULL }, S_TFOG_09 }, //272 /*S_TFOG_09*/ { SPR_TFOG, 32769, 6, { NULL }, S_NULL }, //273 /*S_IFOG_00*/ { SPR_IFOG, 32768, 6, { NULL }, S_IFOG_01 }, //274 /*S_IFOG_01*/ { SPR_IFOG, 32769, 6, { NULL }, S_IFOG_02 }, //275 /*S_IFOG_02*/ { SPR_IFOG, 32768, 6, { NULL }, S_IFOG_03 }, //276 /*S_IFOG_03*/ { SPR_IFOG, 32769, 6, { NULL }, S_IFOG_04 }, //277 /*S_IFOG_04*/ { SPR_IFOG, 32770, 6, { NULL }, S_IFOG_05 }, //278 /*S_IFOG_05*/ { SPR_IFOG, 32771, 6, { NULL }, S_IFOG_06 }, //279 /*S_IFOG_06*/ { SPR_IFOG, 32772, 6, { NULL }, S_NULL }, //280 /*S_SHRD_00*/ { SPR_SHRD, 0, 128, { NULL }, S_NULL }, //281 /*S_SHRD_01*/ { SPR_SHRD, 1, 128, { NULL }, S_NULL }, //282 /*S_SHRD_02*/ { SPR_SHRD, 2, 128, { NULL }, S_NULL }, //283 /*S_SHRD_03*/ { SPR_SHRD, 3, 128, { NULL }, S_NULL }, //284 /*S_SHRD_04*/ { SPR_SHRD, 4, 128, { NULL }, S_NULL }, //285 /*S_SHRD_05*/ { SPR_SHRD, 5, 128, { NULL }, S_NULL }, //286 /*S_PLAY_00*/ { SPR_PLAY, 0, -1, { NULL }, S_NULL }, //287 /*S_PLAY_01*/ { SPR_PLAY, 0, 4, { NULL }, S_PLAY_02 }, //288 /*S_PLAY_02*/ { SPR_PLAY, 1, 4, { NULL }, S_PLAY_03 }, //289 /*S_PLAY_03*/ { SPR_PLAY, 2, 4, { NULL }, S_PLAY_04 }, //290 /*S_PLAY_04*/ { SPR_PLAY, 3, 4, { NULL }, S_PLAY_01 }, //291 /*S_PLAY_05*/ { SPR_PLAY, 4, 12, { NULL }, S_PLAY_00 }, //292 /*S_PLAY_06*/ { SPR_PLAY, 5, 6, { NULL }, S_PLAY_05 }, //293 /*S_PLAY_07*/ { SPR_PLAY, 16, 4, { A_Pain }, S_PLAY_08 }, //294 /*S_PLAY_08*/ { SPR_PLAY, 16, 4, { NULL }, S_PLAY_00 }, //295 /*S_PLAY_09*/ { SPR_PLAY, 6, 4, { NULL }, S_PLAY_10 }, //296 /*S_PLAY_10*/ { SPR_PLAY, 7, 3, { A_PlayerScream }, S_PLAY_11 }, //297 /*S_PLAY_11*/ { SPR_PLAY, 8, 3, { A_Fall }, S_PLAY_12 }, //298 /*S_PLAY_12*/ { SPR_PLAY, 9, 4, { NULL }, S_PLAY_13 }, //299 /*S_PLAY_13*/ { SPR_PLAY, 10, 4, { NULL }, S_PLAY_14 }, //300 /*S_PLAY_14*/ { SPR_PLAY, 11, 4, { NULL }, S_PLAY_15 }, //301 /*S_PLAY_15*/ { SPR_PLAY, 12, 4, { NULL }, S_PLAY_16 }, //302 /*S_PLAY_16*/ { SPR_PLAY, 13, 4, { NULL }, S_PLAY_17 }, //303 /*S_PLAY_17*/ { SPR_PLAY, 14, 4, { NULL }, S_PLAY_18 }, //304 /*S_PLAY_18*/ { SPR_PLAY, 15, 700, { NULL }, S_RGIB_07 }, //305 /*S_RGIB_00*/ { SPR_RGIB, 0, 5, { A_BodyParts }, S_RGIB_01 }, //306 /*S_RGIB_01*/ { SPR_RGIB, 1, 5, { A_XScream }, S_RGIB_02 }, //307 /*S_RGIB_02*/ { SPR_RGIB, 2, 5, { A_Fall }, S_RGIB_03 }, //308 /*S_RGIB_03*/ { SPR_RGIB, 3, 5, { A_BodyParts }, S_RGIB_04 }, //309 /*S_RGIB_04*/ { SPR_RGIB, 4, 5, { A_BodyParts }, S_RGIB_05 }, //310 /*S_RGIB_05*/ { SPR_RGIB, 5, 5, { A_BodyParts }, S_RGIB_06 }, //311 /*S_RGIB_06*/ { SPR_RGIB, 6, 5, { A_BodyParts }, S_RGIB_07 }, //312 /*S_RGIB_07*/ { SPR_RGIB, 7, 1400, { NULL }, S_NULL }, //313 /*S_MRYS_00*/ { SPR_MRYS, 0, 30, { NULL }, S_MRST_00 }, //314 /*S_MRNO_00*/ { SPR_MRNO, 0, 6, { NULL }, S_MRNO_01 }, //315 /*S_MRNO_01*/ { SPR_MRNO, 1, 6, { NULL }, S_MRNO_02 }, //316 /*S_MRNO_02*/ { SPR_MRNO, 2, 10, { NULL }, S_MRNO_03 }, //317 /*S_MRNO_03*/ { SPR_MRNO, 1, 6, { NULL }, S_MRNO_04 }, //318 /*S_MRNO_04*/ { SPR_MRNO, 0, 6, { NULL }, S_MRST_00 }, //319 /*S_MRST_00*/ { SPR_MRST, 0, 10, { A_FriendLook }, S_MRST_00 }, //320 /*S_MRLK_00*/ { SPR_MRLK, 0, 30, { A_ActiveSound }, S_MRST_00 }, //321 /*S_MRLK_01*/ { SPR_MRLK, 1, 30, { NULL }, S_MRST_00 }, //322 /*S_MRBD_00*/ { SPR_MRBD, 0, 4, { NULL }, S_MRBD_01 }, //323 /*S_MRBD_01*/ { SPR_MRBD, 1, 4, { NULL }, S_MRBD_02 }, //324 /*S_MRBD_02*/ { SPR_MRBD, 2, 4, { NULL }, S_MRBD_03 }, //325 /*S_MRBD_03*/ { SPR_MRBD, 3, 4, { NULL }, S_MRBD_04 }, //326 /*S_MRBD_04*/ { SPR_MRBD, 4, 4, { NULL }, S_MRBD_05 }, //327 /*S_MRBD_05*/ { SPR_MRBD, 3, 4, { NULL }, S_MRBD_06 }, //328 /*S_MRBD_06*/ { SPR_MRBD, 2, 4, { NULL }, S_MRBD_07 }, //329 /*S_MRBD_07*/ { SPR_MRBD, 1, 4, { NULL }, S_MRBD_08 }, //330 /*S_MRBD_08*/ { SPR_MRBD, 0, 5, { NULL }, S_MRBD_09 }, //331 /*S_MRBD_09*/ { SPR_MRBD, 5, 6, { NULL }, S_MRST_00 }, //332 /*S_MRPN_00*/ { SPR_MRPN, 0, 3, { NULL }, S_MRPN_01 }, //333 /*S_MRPN_01*/ { SPR_MRPN, 1, 3, { A_Pain }, S_MRPN_02 }, //334 /*S_MRPN_02*/ { SPR_MRPN, 2, 3, { NULL }, S_MRPN_03 }, //335 /*S_MRPN_03*/ { SPR_MRPN, 3, 9, { A_MerchantPain }, S_MRPN_04 }, //336 /*S_MRPN_04*/ { SPR_MRPN, 2, 4, { NULL }, S_MRPN_05 }, //337 /*S_MRPN_05*/ { SPR_MRPN, 1, 3, { NULL }, S_MRPN_06 }, //338 /*S_MRPN_06*/ { SPR_MRPN, 0, 3, { A_ClearSoundTarget }, S_MRST_00 }, //339 /*S_MRGT_00*/ { SPR_MRGT, 0, 5, { NULL }, S_MRGT_01 }, //340 /*S_MRGT_01*/ { SPR_MRGT, 1, 5, { NULL }, S_MRGT_02 }, //341 /*S_MRGT_02*/ { SPR_MRGT, 2, 5, { NULL }, S_MRGT_03 }, //342 /*S_MRGT_03*/ { SPR_MRGT, 3, 5, { NULL }, S_MRGT_04 }, //343 /*S_MRGT_04*/ { SPR_MRGT, 4, 5, { NULL }, S_MRGT_05 }, //344 /*S_MRGT_05*/ { SPR_MRGT, 5, 5, { NULL }, S_MRGT_06 }, //345 /*S_MRGT_06*/ { SPR_MRGT, 6, 5, { NULL }, S_MRGT_07 }, //346 /*S_MRGT_07*/ { SPR_MRGT, 7, 5, { NULL }, S_MRGT_08 }, //347 /*S_MRGT_08*/ { SPR_MRGT, 8, 5, { NULL }, S_MRST_00 }, //348 /*S_BURN_00*/ { SPR_BURN, 0, 3, { A_Scream }, S_BURN_01 }, //349 /*S_BURN_01*/ { SPR_BURN, 1, 3, { A_DropBurnFlesh }, S_BURN_02 }, //350 /*S_BURN_02*/ { SPR_BURN, 2, 3, { A_RandomWalk }, S_BURN_03 }, //351 /*S_BURN_03*/ { SPR_BURN, 3, 3, { A_Fall }, S_BURN_04 }, //352 /*S_BURN_04*/ { SPR_BURN, 4, 5, { A_DropBurnFlesh }, S_BURN_05 }, //353 /*S_BURN_05*/ { SPR_BURN, 5, 5, { A_RandomWalk }, S_BURN_06 }, //354 /*S_BURN_06*/ { SPR_BURN, 6, 5, { A_RandomWalk }, S_BURN_07 }, //355 /*S_BURN_07*/ { SPR_BURN, 7, 5, { A_RandomWalk }, S_BURN_08 }, //356 /*S_BURN_08*/ { SPR_BURN, 8, 5, { A_DropBurnFlesh }, S_BURN_09 }, //357 /*S_BURN_09*/ { SPR_BURN, 9, 5, { A_RandomWalk }, S_BURN_10 }, //358 /*S_BURN_10*/ { SPR_BURN, 10, 5, { A_RandomWalk }, S_BURN_11 }, //359 /*S_BURN_11*/ { SPR_BURN, 11, 5, { A_RandomWalk }, S_BURN_12 }, //360 /*S_BURN_12*/ { SPR_BURN, 12, 3, { A_DropBurnFlesh }, S_BURN_13 }, //361 /*S_BURN_13*/ { SPR_BURN, 13, 3, { NULL }, S_BURN_14 }, //362 /*S_BURN_14*/ { SPR_BURN, 14, 5, { NULL }, S_BURN_15 }, //363 /*S_BURN_15*/ { SPR_BURN, 15, 5, { NULL }, S_BURN_16 }, //364 /*S_BURN_16*/ { SPR_BURN, 16, 5, { NULL }, S_BURN_17 }, //365 /*S_BURN_17*/ { SPR_BURN, 15, 5, { NULL }, S_BURN_18 }, //366 /*S_BURN_18*/ { SPR_BURN, 16, 5, { NULL }, S_BURN_19 }, //367 /*S_BURN_19*/ { SPR_BURN, 17, 7, { NULL }, S_BURN_20 }, //368 /*S_BURN_20*/ { SPR_BURN, 18, 7, { NULL }, S_BURN_21 }, //369 /*S_BURN_21*/ { SPR_BURN, 19, 7, { NULL }, S_BURN_22 }, //370 /*S_BURN_22*/ { SPR_BURN, 20, 7, { NULL }, S_BURN_23 }, //371 /*S_BURN_23*/ { SPR_BURN, 21, 700, { A_PeasantCrash }, S_NULL }, //372 /*S_DISR_00*/ { SPR_DISR, 0, 5, { NULL }, S_DISR_01 }, //373 /*S_DISR_01*/ { SPR_DISR, 1, 5, { NULL }, S_DISR_02 }, //374 /*S_DISR_02*/ { SPR_DISR, 2, 5, { NULL }, S_DISR_03 }, //375 /*S_DISR_03*/ { SPR_DISR, 3, 5, { A_Fall }, S_DISR_04 }, //376 /*S_DISR_04*/ { SPR_DISR, 4, 5, { NULL }, S_DISR_05 }, //377 /*S_DISR_05*/ { SPR_DISR, 5, 5, { NULL }, S_DISR_06 }, //378 /*S_DISR_06*/ { SPR_DISR, 6, 4, { NULL }, S_DISR_07 }, //379 /*S_DISR_07*/ { SPR_DISR, 7, 4, { NULL }, S_DISR_08 }, //380 /*S_DISR_08*/ { SPR_DISR, 8, 4, { NULL }, S_DISR_09 }, //381 /*S_DISR_09*/ { SPR_DISR, 9, 4, { NULL }, S_MEAT_03 }, //382 /*S_PEAS_00*/ { SPR_PEAS, 0, 10, { A_FriendLook }, S_PEAS_00 }, //383 /*S_PEAS_01*/ { SPR_PEAS, 0, 5, { A_RandomWalk }, S_PEAS_02 }, //384 /*S_PEAS_02*/ { SPR_PEAS, 0, 5, { A_RandomWalk }, S_PEAS_03 }, //385 /*S_PEAS_03*/ { SPR_PEAS, 1, 5, { A_RandomWalk }, S_PEAS_04 }, //386 /*S_PEAS_04*/ { SPR_PEAS, 1, 5, { A_RandomWalk }, S_PEAS_05 }, //387 /*S_PEAS_05*/ { SPR_PEAS, 2, 5, { A_RandomWalk }, S_PEAS_06 }, //388 /*S_PEAS_06*/ { SPR_PEAS, 2, 5, { A_RandomWalk }, S_PEAS_07 }, //389 /*S_PEAS_07*/ { SPR_PEAS, 3, 5, { A_RandomWalk }, S_PEAS_08 }, //390 /*S_PEAS_08*/ { SPR_PEAS, 3, 5, { A_RandomWalk }, S_PEAS_00 }, //391 /*S_PEAS_09*/ { SPR_PEAS, 4, 10, { A_FaceTarget }, S_PEAS_10 }, //392 /*S_PEAS_10*/ { SPR_PEAS, 5, 8, { A_PeasantPunch }, S_PEAS_11 }, //393 /*S_PEAS_11*/ { SPR_PEAS, 4, 8, { NULL }, S_PEAS_01 }, //394 /*S_PEAS_12*/ { SPR_PEAS, 14, 3, { NULL }, S_PEAS_13 }, //395 /*S_PEAS_13*/ { SPR_PEAS, 14, 3, { A_Pain }, S_PEAS_09 }, //396 /*S_PEAS_14*/ { SPR_PEAS, 6, 5, { NULL }, S_PEAS_15 }, //397 /*S_PEAS_15*/ { SPR_PEAS, 7, 10, { A_PeasantCrash }, S_PEAS_16 }, //398 /*S_PEAS_16*/ { SPR_PEAS, 8, 6, { NULL }, S_PEAS_15 }, //399 /*S_PEAS_17*/ { SPR_PEAS, 6, 5, { NULL }, S_PEAS_18 }, //400 /*S_PEAS_18*/ { SPR_PEAS, 7, 5, { A_Scream }, S_PEAS_19 }, //401 /*S_PEAS_19*/ { SPR_PEAS, 8, 6, { NULL }, S_PEAS_20 }, //402 /*S_PEAS_20*/ { SPR_PEAS, 9, 5, { A_Fall }, S_PEAS_21 }, //403 /*S_PEAS_21*/ { SPR_PEAS, 10, 5, { NULL }, S_PEAS_22 }, //404 /*S_PEAS_22*/ { SPR_PEAS, 11, 6, { NULL }, S_PEAS_23 }, //405 /*S_PEAS_23*/ { SPR_PEAS, 12, 8, { NULL }, S_PEAS_24 }, //406 /*S_PEAS_24*/ { SPR_PEAS, 13, 1400, { NULL }, S_GIBS_08 }, //407 /*S_GIBS_00*/ { SPR_GIBS, 12, 5, { A_BodyParts }, S_GIBS_01 }, //408 /*S_GIBS_01*/ { SPR_GIBS, 13, 5, { A_XScream }, S_GIBS_02 }, //409 /*S_GIBS_02*/ { SPR_GIBS, 14, 5, { A_Fall }, S_GIBS_03 }, //410 /*S_GIBS_03*/ { SPR_GIBS, 15, 4, { A_BodyParts }, S_GIBS_04 }, //411 /*S_GIBS_04*/ { SPR_GIBS, 16, 4, { A_BodyParts }, S_GIBS_05 }, //412 /*S_GIBS_05*/ { SPR_GIBS, 17, 4, { A_BodyParts }, S_GIBS_06 }, //413 /*S_GIBS_06*/ { SPR_GIBS, 18, 4, { A_BodyParts }, S_GIBS_07 }, //414 /*S_GIBS_07*/ { SPR_GIBS, 19, 4, { NULL }, S_GIBS_08 }, //415 /*S_GIBS_08*/ { SPR_GIBS, 20, 5, { NULL }, S_GIBS_09 }, //416 /*S_GIBS_09*/ { SPR_GIBS, 21, 1400, { NULL }, S_NULL }, //417 /*S_PEAS_25*/ { SPR_PEAS, 0, 5, { A_ZombieInSpecialSector }, S_PEAS_25 }, //418 /*S_AGRD_00*/ { SPR_AGRD, 0, 5, { A_ZombieInSpecialSector }, S_AGRD_00 }, //419 /*S_ARMR_00*/ { SPR_ARMR, 0, -1, { NULL }, S_NULL }, //420 /*S_ARMR_01*/ { SPR_ARMR, 0, -1, { A_HideZombie }, S_NULL }, //421 /*S_PLAY_19*/ { SPR_PLAY, 0, 175, { A_SpawnZombie }, S_PLAY_19 }, //422 /*S_SACR_00*/ { SPR_SACR, 0, -1, { NULL }, S_NULL }, //423 /*S_TNK1_00*/ { SPR_TNK1, 0, 15, { NULL }, S_TNK1_01 }, //424 /*S_TNK1_01*/ { SPR_TNK1, 1, 11, { NULL }, S_TNK1_02 }, //425 /*S_TNK1_02*/ { SPR_TNK1, 2, 40, { NULL }, S_TNK1_00 }, //426 /*S_TNK2_00*/ { SPR_TNK2, 0, 15, { NULL }, S_TNK2_01 }, //427 /*S_TNK2_01*/ { SPR_TNK2, 1, 11, { NULL }, S_TNK2_02 }, //428 /*S_TNK2_02*/ { SPR_TNK2, 2, 40, { NULL }, S_TNK2_00 }, //429 /*S_TNK3_00*/ { SPR_TNK3, 0, 15, { NULL }, S_TNK3_01 }, //430 /*S_TNK3_01*/ { SPR_TNK3, 1, 11, { NULL }, S_TNK3_02 }, //431 /*S_TNK3_02*/ { SPR_TNK3, 2, 40, { NULL }, S_TNK3_00 }, //432 /*S_TNK4_00*/ { SPR_TNK4, 0, 15, { NULL }, S_TNK4_01 }, //433 /*S_TNK4_01*/ { SPR_TNK4, 1, 11, { NULL }, S_TNK4_02 }, //434 /*S_TNK4_02*/ { SPR_TNK4, 2, 40, { NULL }, S_TNK4_00 }, //435 /*S_TNK5_00*/ { SPR_TNK5, 0, 15, { NULL }, S_TNK5_01 }, //436 /*S_TNK5_01*/ { SPR_TNK5, 1, 11, { NULL }, S_TNK5_02 }, //437 /*S_TNK5_02*/ { SPR_TNK5, 2, 40, { NULL }, S_TNK5_00 }, //438 /*S_TNK6_00*/ { SPR_TNK6, 0, 15, { NULL }, S_TNK6_01 }, //439 /*S_TNK6_01*/ { SPR_TNK6, 1, 11, { NULL }, S_TNK6_02 }, //440 /*S_TNK6_02*/ { SPR_TNK6, 2, 40, { NULL }, S_TNK6_00 }, //441 /*S_NEAL_00*/ { SPR_NEAL, 0, 15, { A_ActiveSound }, S_NEAL_01 }, //442 /*S_NEAL_01*/ { SPR_NEAL, 1, 40, { A_ActiveSound }, S_NEAL_00 }, //443 /*S_NEAL_02*/ { SPR_NEAL, 2, 5, { A_ShadowOn }, S_NEAL_03 }, //444 /*S_NEAL_03*/ { SPR_NEAL, 1, 4, { A_Pain }, S_NEAL_04 }, //445 /*S_NEAL_04*/ { SPR_NEAL, 2, 5, { A_ShadowOff }, S_NEAL_00 }, //446 /*S_NEAL_05*/ { SPR_NEAL, 1, 6, { NULL }, S_NEAL_06 }, //447 /*S_NEAL_06*/ { SPR_NEAL, 2, 13, { A_PeasantCrash }, S_NEAL_05 }, //448 /*S_NEAL_07*/ { SPR_NEAL, 3, 5, { NULL }, S_NEAL_08 }, //449 /*S_NEAL_08*/ { SPR_NEAL, 4, 5, { A_Scream }, S_NEAL_09 }, //450 /*S_NEAL_09*/ { SPR_NEAL, 5, 6, { NULL }, S_NEAL_10 }, //451 /*S_NEAL_10*/ { SPR_NEAL, 6, 5, { A_Fall }, S_NEAL_11 }, //452 /*S_NEAL_11*/ { SPR_NEAL, 7, 5, { NULL }, S_NEAL_12 }, //453 /*S_NEAL_12*/ { SPR_NEAL, 8, 6, { NULL }, S_NEAL_13 }, //454 /*S_NEAL_13*/ { SPR_NEAL, 9, -1, { NULL }, S_NULL }, //455 /*S_BEGR_00*/ { SPR_BEGR, 0, 10, { A_Look }, S_BEGR_00 }, //456 /*S_BEGR_01*/ { SPR_BEGR, 0, 4, { A_RandomWalk }, S_BEGR_02 }, //457 /*S_BEGR_02*/ { SPR_BEGR, 0, 4, { A_RandomWalk }, S_BEGR_03 }, //458 /*S_BEGR_03*/ { SPR_BEGR, 1, 4, { A_RandomWalk }, S_BEGR_04 }, //459 /*S_BEGR_04*/ { SPR_BEGR, 1, 4, { A_RandomWalk }, S_BEGR_05 }, //460 /*S_BEGR_05*/ { SPR_BEGR, 2, 4, { A_RandomWalk }, S_BEGR_06 }, //461 /*S_BEGR_06*/ { SPR_BEGR, 2, 4, { A_RandomWalk }, S_BEGR_01 }, //462 /*S_BEGR_07*/ { SPR_BEGR, 3, 8, { NULL }, S_BEGR_08 }, //463 /*S_BEGR_08*/ { SPR_BEGR, 4, 8, { A_PeasantPunch }, S_BEGR_09 }, //464 /*S_BEGR_09*/ { SPR_BEGR, 4, 1, { A_Chase }, S_BEGR_10 }, //465 /*S_BEGR_10*/ { SPR_BEGR, 3, 8, { A_CheckTargetVisible }, S_BEGR_07 }, //466 /*S_BEGR_11*/ { SPR_BEGR, 0, 3, { A_Pain }, S_BEGR_12 }, //467 /*S_BEGR_12*/ { SPR_BEGR, 0, 3, { A_Chase }, S_BEGR_07 }, //468 /*S_BEGR_13*/ { SPR_BEGR, 5, 4, { NULL }, S_BEGR_14 }, //469 /*S_BEGR_14*/ { SPR_BEGR, 6, 4, { A_Scream }, S_BEGR_15 }, //470 /*S_BEGR_15*/ { SPR_BEGR, 7, 4, { NULL }, S_BEGR_16 }, //471 /*S_BEGR_16*/ { SPR_BEGR, 8, 4, { A_Fall }, S_BEGR_17 }, //472 /*S_BEGR_17*/ { SPR_BEGR, 9, 4, { NULL }, S_BEGR_18 }, //473 /*S_BEGR_18*/ { SPR_BEGR, 10, 4, { NULL }, S_BEGR_19 }, //474 /*S_BEGR_19*/ { SPR_BEGR, 11, 4, { NULL }, S_BEGR_20 }, //475 /*S_BEGR_20*/ { SPR_BEGR, 12, 4, { NULL }, S_BEGR_21 }, //476 /*S_BEGR_21*/ { SPR_BEGR, 13, -1, { NULL }, S_NULL }, //477 /*S_BEGR_22*/ { SPR_BEGR, 5, 5, { A_BodyParts }, S_GIBS_01 }, //478 /*S_HMN1_00*/ { SPR_HMN1, 15, 5, { A_FriendLook }, S_HMN1_00 }, //479 /*S_HMN1_01*/ { SPR_HMN1, 16, 8, { NULL }, S_HMN1_00 }, //480 /*S_HMN1_02*/ { SPR_HMN1, 17, 8, { NULL }, S_HMN1_00 }, //481 /*S_HMN1_03*/ { SPR_HMN1, 0, 6, { A_RandomWalk }, S_HMN1_04 }, //482 /*S_HMN1_04*/ { SPR_HMN1, 1, 6, { A_RandomWalk }, S_HMN1_05 }, //483 /*S_HMN1_05*/ { SPR_HMN1, 2, 6, { A_RandomWalk }, S_HMN1_06 }, //484 /*S_HMN1_06*/ { SPR_HMN1, 3, 6, { A_RandomWalk }, S_HMN1_07 }, //485 /*S_HMN1_07*/ { SPR_HMN1, 0, 6, { A_RandomWalk }, S_HMN1_08 }, //486 /*S_HMN1_08*/ { SPR_HMN1, 1, 6, { A_RandomWalk }, S_HMN1_09 }, //487 /*S_HMN1_09*/ { SPR_HMN1, 2, 6, { A_RandomWalk }, S_HMN1_10 }, //488 /*S_HMN1_10*/ { SPR_HMN1, 3, 6, { A_RandomWalk }, S_HMN1_00 }, //489 /*S_HMN1_11*/ { SPR_HMN1, 0, 3, { A_Chase }, S_HMN1_12 }, //490 /*S_HMN1_12*/ { SPR_HMN1, 0, 3, { A_Chase }, S_HMN1_13 }, //491 /*S_HMN1_13*/ { SPR_HMN1, 1, 3, { A_Chase }, S_HMN1_14 }, //492 /*S_HMN1_14*/ { SPR_HMN1, 1, 3, { A_Chase }, S_HMN1_15 }, //493 /*S_HMN1_15*/ { SPR_HMN1, 2, 3, { A_Chase }, S_HMN1_16 }, //494 /*S_HMN1_16*/ { SPR_HMN1, 2, 3, { A_Chase }, S_HMN1_17 }, //495 /*S_HMN1_17*/ { SPR_HMN1, 3, 3, { A_Chase }, S_HMN1_18 }, //496 /*S_HMN1_18*/ { SPR_HMN1, 3, 3, { A_Chase }, S_HMN1_11 }, //497 /*S_HMN1_19*/ { SPR_HMN1, 4, 10, { A_FaceTarget }, S_HMN1_20 }, //498 /*S_HMN1_20*/ { SPR_HMN1, 32773, 10, { A_BulletAttack }, S_HMN1_21 }, //499 /*S_HMN1_21*/ { SPR_HMN1, 4, 10, { A_BulletAttack }, S_HMN1_11 }, //500 /*S_HMN1_22*/ { SPR_HMN1, 14, 3, { NULL }, S_HMN1_23 }, //501 /*S_HMN1_23*/ { SPR_HMN1, 14, 3, { A_Pain }, S_HMN1_11 }, //502 /*S_HMN1_24*/ { SPR_HMN1, 6, 5, { NULL }, S_HMN1_25 }, //503 /*S_HMN1_25*/ { SPR_HMN1, 7, 5, { A_Scream }, S_HMN1_26 }, //504 /*S_HMN1_26*/ { SPR_HMN1, 8, 3, { A_Fall }, S_HMN1_27 }, //505 /*S_HMN1_27*/ { SPR_HMN1, 9, 4, { NULL }, S_HMN1_28 }, //506 /*S_HMN1_28*/ { SPR_HMN1, 10, 3, { NULL }, S_HMN1_29 }, //507 /*S_HMN1_29*/ { SPR_HMN1, 11, 3, { NULL }, S_HMN1_30 }, //508 /*S_HMN1_30*/ { SPR_HMN1, 12, 3, { NULL }, S_HMN1_31 }, //509 /*S_HMN1_31*/ { SPR_HMN1, 13, -1, { NULL }, S_NULL }, //510 /*S_RGIB_08*/ { SPR_RGIB, 0, 4, { A_BodyParts }, S_RGIB_09 }, //511 /*S_RGIB_09*/ { SPR_RGIB, 1, 4, { A_XScream }, S_RGIB_10 }, //512 /*S_RGIB_10*/ { SPR_RGIB, 2, 3, { A_Fall }, S_RGIB_11 }, //513 /*S_RGIB_11*/ { SPR_RGIB, 3, 3, { A_BodyParts }, S_RGIB_12 }, //514 /*S_RGIB_12*/ { SPR_RGIB, 4, 3, { A_BodyParts }, S_RGIB_13 }, //515 /*S_RGIB_13*/ { SPR_RGIB, 5, 3, { A_BodyParts }, S_RGIB_14 }, //516 /*S_RGIB_14*/ { SPR_RGIB, 6, 3, { NULL }, S_RGIB_15 }, //517 /*S_RGIB_15*/ { SPR_RGIB, 7, 1400, { NULL }, S_NULL }, //518 /*S_LEDR_00*/ { SPR_LEDR, 2, 5, { A_FriendLook }, S_LEDR_00 }, //519 /*S_LEDR_01*/ { SPR_LEDR, 0, 8, { NULL }, S_LEDR_00 }, //520 /*S_LEDR_02*/ { SPR_LEDR, 1, 8, { NULL }, S_LEDR_00 }, //521 /*S_LEAD_00*/ { SPR_LEAD, 0, 6, { A_RandomWalk }, S_LEAD_01 }, //522 /*S_LEAD_01*/ { SPR_LEAD, 1, 6, { A_RandomWalk }, S_LEAD_02 }, //523 /*S_LEAD_02*/ { SPR_LEAD, 2, 6, { A_RandomWalk }, S_LEAD_03 }, //524 /*S_LEAD_03*/ { SPR_LEAD, 3, 6, { A_RandomWalk }, S_LEDR_00 }, //525 /*S_LEAD_04*/ { SPR_LEAD, 0, 3, { A_Chase }, S_LEAD_05 }, //526 /*S_LEAD_05*/ { SPR_LEAD, 0, 3, { A_Chase }, S_LEAD_06 }, //527 /*S_LEAD_06*/ { SPR_LEAD, 1, 3, { A_Chase }, S_LEAD_07 }, //528 /*S_LEAD_07*/ { SPR_LEAD, 1, 3, { A_Chase }, S_LEAD_08 }, //529 /*S_LEAD_08*/ { SPR_LEAD, 2, 3, { A_Chase }, S_LEAD_09 }, //530 /*S_LEAD_09*/ { SPR_LEAD, 2, 3, { A_Chase }, S_LEAD_10 }, //531 /*S_LEAD_10*/ { SPR_LEAD, 3, 3, { A_Chase }, S_LEAD_11 }, //532 /*S_LEAD_11*/ { SPR_LEAD, 3, 3, { A_Chase }, S_LEAD_04 }, //533 /*S_LEAD_12*/ { SPR_LEAD, 4, 2, { A_FaceTarget }, S_LEAD_13 }, //534 /*S_LEAD_13*/ { SPR_LEAD, 32773, 2, { A_BulletAttack }, S_LEAD_14 }, //535 /*S_LEAD_14*/ { SPR_LEAD, 4, 1, { A_CheckTargetVisible }, S_LEAD_12 }, //536 /*S_LEAD_15*/ { SPR_LEAD, 24, 3, { NULL }, S_LEAD_16 }, //537 /*S_LEAD_16*/ { SPR_LEAD, 24, 3, { A_Pain }, S_LEAD_04 }, //538 /*S_LEAD_17*/ { SPR_LEAD, 4, 4, { A_FaceTarget }, S_LEAD_18 }, //539 /*S_LEAD_18*/ { SPR_LEAD, 32773, 4, { A_BulletAttack }, S_LEAD_19 }, //540 /*S_LEAD_19*/ { SPR_LEAD, 4, 2, { A_CheckTargetVisible }, S_LEAD_17 }, //541 /*S_LEAD_20*/ { SPR_LEAD, 6, 5, { NULL }, S_LEAD_21 }, //542 /*S_LEAD_21*/ { SPR_LEAD, 7, 5, { A_Scream }, S_LEAD_22 }, //543 /*S_LEAD_22*/ { SPR_LEAD, 8, 4, { NULL }, S_LEAD_23 }, //544 /*S_LEAD_23*/ { SPR_LEAD, 9, 4, { NULL }, S_LEAD_24 }, //545 /*S_LEAD_24*/ { SPR_LEAD, 10, 3, { NULL }, S_LEAD_25 }, //546 /*S_LEAD_25*/ { SPR_LEAD, 11, 3, { A_Fall }, S_LEAD_26 }, //547 /*S_LEAD_26*/ { SPR_LEAD, 12, 3, { NULL }, S_LEAD_27 }, //548 /*S_LEAD_27*/ { SPR_LEAD, 13, 3, { NULL }, S_LEAD_28 }, //549 /*S_LEAD_28*/ { SPR_LEAD, 14, 3, { NULL }, S_LEAD_29 }, //550 /*S_LEAD_29*/ { SPR_LEAD, 15, 3, { NULL }, S_LEAD_30 }, //551 /*S_LEAD_30*/ { SPR_LEAD, 16, 3, { NULL }, S_LEAD_31 }, //552 /*S_LEAD_31*/ { SPR_LEAD, 17, 3, { NULL }, S_LEAD_32 }, //553 /*S_LEAD_32*/ { SPR_LEAD, 18, 3, { NULL }, S_LEAD_33 }, //554 /*S_LEAD_33*/ { SPR_LEAD, 19, 3, { NULL }, S_LEAD_34 }, //555 /*S_LEAD_34*/ { SPR_LEAD, 20, 3, { NULL }, S_LEAD_35 }, //556 /*S_LEAD_35*/ { SPR_LEAD, 21, 3, { NULL }, S_LEAD_36 }, //557 /*S_LEAD_36*/ { SPR_LEAD, 22, 3, { A_SpawnSpectreD }, S_LEAD_37 }, //558 /*S_LEAD_37*/ { SPR_LEAD, 23, -1, { NULL }, S_NULL }, //559 /*S_PUFY_04*/ { SPR_PUFY, 1, 4, { NULL }, S_PUFY_05 }, //560 /*S_PUFY_05*/ { SPR_PUFY, 2, 4, { NULL }, S_PUFY_06 }, //561 /*S_PUFY_06*/ { SPR_PUFY, 1, 4, { NULL }, S_PUFY_07 }, //562 /*S_PUFY_07*/ { SPR_PUFY, 2, 4, { NULL }, S_PUFY_08 }, //563 /*S_PUFY_08*/ { SPR_PUFY, 3, 4, { NULL }, S_NULL }, //564 /*S_MICR_01*/ { SPR_MICR, 32768, 2, { A_Tracer }, S_MICR_02 }, //565 /*S_MICR_02*/ { SPR_MICR, 32768, 2, { A_Tracer }, S_MICR_01 }, //566 /*S_ROB1_00*/ { SPR_ROB1, 0, 10, { A_Look }, S_ROB1_01 }, //567 /*S_ROB1_01*/ { SPR_ROB1, 0, 10, { A_Look }, S_ROB1_00 }, //568 /*S_ROB1_02*/ { SPR_ROB1, 1, 3, { A_Chase }, S_ROB1_03 }, //569 /*S_ROB1_03*/ { SPR_ROB1, 1, 3, { A_Chase }, S_ROB1_04 }, //570 /*S_ROB1_04*/ { SPR_ROB1, 2, 3, { A_Chase }, S_ROB1_05 }, //571 /*S_ROB1_05*/ { SPR_ROB1, 2, 3, { A_Chase }, S_ROB1_06 }, //572 /*S_ROB1_06*/ { SPR_ROB1, 3, 3, { A_Chase }, S_ROB1_07 }, //573 /*S_ROB1_07*/ { SPR_ROB1, 3, 3, { A_Chase }, S_ROB1_08 }, //574 /*S_ROB1_08*/ { SPR_ROB1, 4, 3, { A_Chase }, S_ROB1_09 }, //575 /*S_ROB1_09*/ { SPR_ROB1, 4, 3, { A_Chase }, S_ROB1_02 }, //576 /*S_ROB1_10*/ { SPR_ROB1, 7, 6, { A_FaceTarget }, S_ROB1_11 }, //577 /*S_ROB1_11*/ { SPR_ROB1, 8, 8, { A_RobotMelee }, S_ROB1_12 }, //578 /*S_ROB1_12*/ { SPR_ROB1, 7, 6, { NULL }, S_ROB1_02 }, //579 /*S_ROB1_13*/ { SPR_ROB1, 5, 8, { A_FaceTarget }, S_ROB1_14 }, //580 /*S_ROB1_14*/ { SPR_ROB1, 32774, 11, { A_ReaverAttack }, S_ROB1_02 }, //581 /*S_ROB1_15*/ { SPR_ROB1, 0, 2, { NULL }, S_ROB1_16 }, //582 /*S_ROB1_16*/ { SPR_ROB1, 0, 2, { A_Pain }, S_ROB1_02 }, //583 /*S_ROB1_17*/ { SPR_ROB1, 32777, 6, { NULL }, S_ROB1_18 }, //584 /*S_ROB1_18*/ { SPR_ROB1, 32778, 6, { A_Scream }, S_ROB1_19 }, //585 /*S_ROB1_19*/ { SPR_ROB1, 32779, 5, { NULL }, S_ROB1_20 }, //586 /*S_ROB1_20*/ { SPR_ROB1, 32780, 5, { A_Fall }, S_ROB1_21 }, //587 /*S_ROB1_21*/ { SPR_ROB1, 32781, 5, { NULL }, S_ROB1_22 }, //588 /*S_ROB1_22*/ { SPR_ROB1, 32782, 5, { NULL }, S_ROB1_23 }, //589 /*S_ROB1_23*/ { SPR_ROB1, 32783, 5, { NULL }, S_ROB1_24 }, //590 /*S_ROB1_24*/ { SPR_ROB1, 32784, 6, { A_DeathExplode3 }, S_ROB1_25 }, //591 /*S_ROB1_25*/ { SPR_ROB1, 17, -1, { NULL }, S_NULL }, //592 /*S_ROB1_26*/ { SPR_ROB1, 32779, 5, { A_BodyParts }, S_ROB1_27 }, //593 /*S_ROB1_27*/ { SPR_ROB1, 32780, 5, { A_XScream }, S_ROB1_28 }, //594 /*S_ROB1_28*/ { SPR_ROB1, 32781, 5, { A_BodyParts }, S_ROB1_29 }, //595 /*S_ROB1_29*/ { SPR_ROB1, 32782, 5, { A_Fall }, S_ROB1_30 }, //596 /*S_ROB1_30*/ { SPR_ROB1, 32783, 5, { A_BodyParts }, S_ROB1_31 }, //597 /*S_ROB1_31*/ { SPR_ROB1, 32784, 5, { A_DeathExplode3 }, S_ROB1_32 }, //598 /*S_ROB1_32*/ { SPR_ROB1, 17, -1, { NULL }, S_NULL }, //599 /*S_AGRD_01*/ { SPR_AGRD, 0, 5, { A_FriendLook }, S_AGRD_01 }, //600 /*S_AGRD_02*/ { SPR_AGRD, 1, 8, { A_ShadowOff }, S_AGRD_01 }, //601 /*S_AGRD_03*/ { SPR_AGRD, 3, 8, { NULL }, S_AGRD_01 }, //602 /*S_AGRD_04*/ { SPR_AGRD, 0, 5, { A_RandomWalk }, S_AGRD_05 }, //603 /*S_AGRD_05*/ { SPR_AGRD, 1, 5, { A_RandomWalk }, S_AGRD_06 }, //604 /*S_AGRD_06*/ { SPR_AGRD, 2, 5, { A_RandomWalk }, S_AGRD_07 }, //605 /*S_AGRD_07*/ { SPR_AGRD, 3, 5, { A_RandomWalk }, S_AGRD_08 }, //606 /*S_AGRD_08*/ { SPR_AGRD, 0, 5, { A_RandomWalk }, S_AGRD_09 }, //607 /*S_AGRD_09*/ { SPR_AGRD, 1, 5, { A_RandomWalk }, S_AGRD_10 }, //608 /*S_AGRD_10*/ { SPR_AGRD, 2, 5, { A_RandomWalk }, S_AGRD_11 }, //609 /*S_AGRD_11*/ { SPR_AGRD, 3, 5, { A_RandomWalk }, S_AGRD_01 }, //610 /*S_AGRD_12*/ { SPR_AGRD, 0, 6, { A_ModifyVisibility }, S_AGRD_14 }, //611 /*S_AGRD_13*/ { SPR_AGRD, 0, 6, { A_SetTLOptions }, S_AGRD_14 }, //612 /*S_AGRD_14*/ { SPR_AGRD, 1, 6, { A_Chase }, S_AGRD_15 }, //613 /*S_AGRD_15*/ { SPR_AGRD, 2, 6, { A_Chase }, S_AGRD_16 }, //614 /*S_AGRD_16*/ { SPR_AGRD, 3, 6, { A_Chase }, S_AGRD_13 }, //615 /*S_AGRD_17*/ { SPR_AGRD, 4, 8, { A_FaceTarget }, S_AGRD_18 }, //616 /*S_AGRD_18*/ { SPR_AGRD, 5, 4, { A_BulletAttack }, S_AGRD_19 }, //617 /*S_AGRD_19*/ { SPR_AGRD, 4, 4, { A_BulletAttack }, S_AGRD_20 }, //618 /*S_AGRD_20*/ { SPR_AGRD, 5, 6, { A_BulletAttack }, S_AGRD_13 }, //619 /*S_AGRD_21*/ { SPR_AGRD, 14, 0, { A_ShadowOn }, S_AGRD_22 }, //620 /*S_AGRD_22*/ { SPR_AGRD, 14, 8, { A_Pain }, S_AGRD_12 }, //621 /*S_AGRD_23*/ { SPR_AGRD, 14, 8, { A_Pain }, S_AGRD_13 }, //622 /*S_AGRD_24*/ { SPR_AGRD, 6, 4, { NULL }, S_AGRD_25 }, //623 /*S_AGRD_25*/ { SPR_AGRD, 7, 4, { A_Scream }, S_AGRD_26 }, //624 /*S_AGRD_26*/ { SPR_AGRD, 8, 4, { NULL }, S_AGRD_27 }, //625 /*S_AGRD_27*/ { SPR_AGRD, 9, 3, { NULL }, S_AGRD_28 }, //626 /*S_AGRD_28*/ { SPR_AGRD, 10, 3, { A_Fall }, S_AGRD_29 }, //627 /*S_AGRD_29*/ { SPR_AGRD, 11, 3, { NULL }, S_AGRD_30 }, //628 /*S_AGRD_30*/ { SPR_AGRD, 12, 3, { A_AcolyteSpecial }, S_AGRD_31 }, //629 /*S_AGRD_31*/ { SPR_AGRD, 13, 1400, { NULL }, S_GIBS_20 }, //630 /*S_GIBS_10*/ { SPR_GIBS, 0, 5, { A_Fall }, S_GIBS_11 }, //631 /*S_GIBS_11*/ { SPR_GIBS, 1, 5, { A_BodyParts }, S_GIBS_12 }, //632 /*S_GIBS_12*/ { SPR_GIBS, 2, 5, { A_BodyParts }, S_GIBS_13 }, //633 /*S_GIBS_13*/ { SPR_GIBS, 3, 4, { A_BodyParts }, S_GIBS_14 }, //634 /*S_GIBS_14*/ { SPR_GIBS, 4, 4, { A_XScream }, S_GIBS_15 }, //635 /*S_GIBS_15*/ { SPR_GIBS, 5, 4, { A_BodyParts }, S_GIBS_16 }, //636 /*S_GIBS_16*/ { SPR_GIBS, 6, 4, { NULL }, S_GIBS_17 }, //637 /*S_GIBS_17*/ { SPR_GIBS, 7, 4, { NULL }, S_GIBS_18 }, //638 /*S_GIBS_18*/ { SPR_GIBS, 8, 5, { NULL }, S_GIBS_19 }, //639 /*S_GIBS_19*/ { SPR_GIBS, 9, 5, { A_AcolyteSpecial }, S_GIBS_20 }, //640 /*S_GIBS_20*/ { SPR_GIBS, 10, 5, { NULL }, S_GIBS_21 }, //641 /*S_GIBS_21*/ { SPR_GIBS, 11, 1400, { NULL }, S_NULL }, //642 /*S_PGRD_00*/ { SPR_PGRD, 0, 5, { A_FriendLook }, S_PGRD_00 }, //643 /*S_PGRD_01*/ { SPR_PGRD, 1, 10, { NULL }, S_PGRD_00 }, //644 /*S_PGRD_02*/ { SPR_PGRD, 2, 10, { NULL }, S_PGRD_00 }, //645 /*S_PGRD_03*/ { SPR_PGRD, 1, 10, { A_RandomWalk }, S_PGRD_00 }, //646 /*S_PGRD_04*/ { SPR_PGRD, 0, 3, { A_Chase }, S_PGRD_05 }, //647 /*S_PGRD_05*/ { SPR_PGRD, 0, 3, { A_Chase }, S_PGRD_06 }, //648 /*S_PGRD_06*/ { SPR_PGRD, 1, 3, { A_Chase }, S_PGRD_07 }, //649 /*S_PGRD_07*/ { SPR_PGRD, 1, 3, { A_Chase }, S_PGRD_08 }, //650 /*S_PGRD_08*/ { SPR_PGRD, 2, 3, { A_Chase }, S_PGRD_09 }, //651 /*S_PGRD_09*/ { SPR_PGRD, 2, 3, { A_Chase }, S_PGRD_10 }, //652 /*S_PGRD_10*/ { SPR_PGRD, 3, 3, { A_Chase }, S_PGRD_11 }, //653 /*S_PGRD_11*/ { SPR_PGRD, 3, 3, { A_Chase }, S_PGRD_04 }, //654 /*S_PGRD_12*/ { SPR_PGRD, 4, 8, { A_FaceTarget }, S_PGRD_13 }, //655 /*S_PGRD_13*/ { SPR_PGRD, 5, 8, { A_RobotMelee }, S_PGRD_04 }, //656 /*S_PGRD_14*/ { SPR_PGRD, 32774, 8, { A_FaceTarget }, S_PGRD_15 }, //657 /*S_PGRD_15*/ { SPR_PGRD, 32775, 8, { A_TemplarMauler }, S_PGRD_04 }, //658 /*S_PGRD_16*/ { SPR_PGRD, 0, 2, { NULL }, S_PGRD_17 }, //659 /*S_PGRD_17*/ { SPR_PGRD, 0, 2, { A_Pain }, S_PGRD_04 }, //660 /*S_PGRD_18*/ { SPR_PGRD, 32776, 4, { A_BodyParts }, S_PGRD_19 }, //661 /*S_PGRD_19*/ { SPR_PGRD, 32777, 4, { A_Scream }, S_PGRD_20 }, //662 /*S_PGRD_20*/ { SPR_PGRD, 32778, 4, { A_BodyParts }, S_PGRD_21 }, //663 /*S_PGRD_21*/ { SPR_PGRD, 32779, 4, { A_Fall }, S_PGRD_22 }, //664 /*S_PGRD_22*/ { SPR_PGRD, 32780, 4, { NULL }, S_PGRD_23 }, //665 /*S_PGRD_23*/ { SPR_PGRD, 32781, 4, { NULL }, S_PGRD_24 }, //666 /*S_PGRD_24*/ { SPR_PGRD, 14, 4, { A_BodyParts }, S_PGRD_25 }, //667 /*S_PGRD_25*/ { SPR_PGRD, 15, 4, { NULL }, S_PGRD_26 }, //668 /*S_PGRD_26*/ { SPR_PGRD, 16, 4, { NULL }, S_PGRD_27 }, //669 /*S_PGRD_27*/ { SPR_PGRD, 17, 4, { NULL }, S_PGRD_28 }, //670 /*S_PGRD_28*/ { SPR_PGRD, 18, 3, { NULL }, S_PGRD_29 }, //671 /*S_PGRD_29*/ { SPR_PGRD, 19, 3, { NULL }, S_PGRD_30 }, //672 /*S_PGRD_30*/ { SPR_PGRD, 20, 3, { NULL }, S_PGRD_31 }, //673 /*S_PGRD_31*/ { SPR_PGRD, 21, 3, { NULL }, S_PGRD_32 }, //674 /*S_PGRD_32*/ { SPR_PGRD, 22, 3, { NULL }, S_PGRD_33 }, //675 /*S_PGRD_33*/ { SPR_PGRD, 23, 3, { NULL }, S_PGRD_34 }, //676 /*S_PGRD_34*/ { SPR_PGRD, 24, 3, { NULL }, S_PGRD_35 }, //677 /*S_PGRD_35*/ { SPR_PGRD, 25, 3, { NULL }, S_PGRD_36 }, //678 /*S_PGRD_36*/ { SPR_PGRD, 26, 3, { NULL }, S_PGRD_37 }, //679 /*S_PGRD_37*/ { SPR_PGRD, 27, -1, { NULL }, S_NULL }, //680 /*S_ROB2_00*/ { SPR_ROB2, 16, 10, { A_Look }, S_ROB2_00 }, //681 /*S_ROB2_01*/ { SPR_ROB2, 0, 3, { A_Chase }, S_ROB2_02 }, //682 /*S_ROB2_02*/ { SPR_ROB2, 0, 3, { A_Chase }, S_ROB2_03 }, //683 /*S_ROB2_03*/ { SPR_ROB2, 1, 3, { A_Chase }, S_ROB2_04 }, //684 /*S_ROB2_04*/ { SPR_ROB2, 1, 3, { A_Chase }, S_ROB2_05 }, //685 /*S_ROB2_05*/ { SPR_ROB2, 2, 3, { A_Chase }, S_ROB2_06 }, //686 /*S_ROB2_06*/ { SPR_ROB2, 2, 3, { A_Chase }, S_ROB2_07 }, //687 /*S_ROB2_07*/ { SPR_ROB2, 3, 3, { A_Chase }, S_ROB2_08 }, //688 /*S_ROB2_08*/ { SPR_ROB2, 3, 3, { A_Chase }, S_ROB2_01 }, //689 /*S_ROB2_09*/ { SPR_ROB2, 4, 3, { A_FaceTarget }, S_ROB2_10 }, //690 /*S_ROB2_10*/ { SPR_ROB2, 32773, 2, { A_CrusaderAttack }, S_ROB2_11 }, //691 /*S_ROB2_11*/ { SPR_ROB2, 32772, 2, { A_CrusaderLeft }, S_ROB2_12 }, //692 /*S_ROB2_12*/ { SPR_ROB2, 32773, 3, { A_CrusaderLeft }, S_ROB2_13 }, //693 /*S_ROB2_13*/ { SPR_ROB2, 32772, 2, { A_CrusaderLeft }, S_ROB2_14 }, //694 /*S_ROB2_14*/ { SPR_ROB2, 32773, 2, { A_CrusaderLeft }, S_ROB2_15 }, //695 /*S_ROB2_15*/ { SPR_ROB2, 32772, 2, { A_CrusaderRight }, S_ROB2_16 }, //696 /*S_ROB2_16*/ { SPR_ROB2, 32773, 2, { A_CrusaderRight }, S_ROB2_17 }, //697 /*S_ROB2_17*/ { SPR_ROB2, 32772, 2, { A_CrusaderRight }, S_ROB2_18 }, //698 /*S_ROB2_18*/ { SPR_ROB2, 5, 2, { A_CheckTargetVisible2 }, S_ROB2_09 }, //699 /*S_ROB2_19*/ { SPR_ROB2, 3, 1, { A_Pain }, S_ROB2_01 }, //700 /*S_ROB2_20*/ { SPR_ROB2, 6, 3, { A_Scream }, S_ROB2_21 }, //701 /*S_ROB2_21*/ { SPR_ROB2, 7, 5, { A_BodyParts }, S_ROB2_22 }, //702 /*S_ROB2_22*/ { SPR_ROB2, 32776, 4, { A_BodyParts }, S_ROB2_23 }, //703 /*S_ROB2_23*/ { SPR_ROB2, 32777, 4, { A_DeathExplode2 }, S_ROB2_24 }, //704 /*S_ROB2_24*/ { SPR_ROB2, 32778, 4, { A_Fall }, S_ROB2_25 }, //705 /*S_ROB2_25*/ { SPR_ROB2, 11, 4, { A_DeathExplode2 }, S_ROB2_26 }, //706 /*S_ROB2_26*/ { SPR_ROB2, 12, 4, { A_BodyParts }, S_ROB2_27 }, //707 /*S_ROB2_27*/ { SPR_ROB2, 13, 4, { A_BodyParts }, S_ROB2_28 }, //708 /*S_ROB2_28*/ { SPR_ROB2, 14, 4, { A_DeathExplode2 }, S_ROB2_29 }, //709 /*S_ROB2_29*/ { SPR_ROB2, 15, -1, { A_BossDeath }, S_NULL }, //710 /*S_MLDR_00*/ { SPR_MLDR, 0, 10, { A_Look }, S_MLDR_00 }, //711 /*S_MLDR_01*/ { SPR_MLDR, 0, 3, { A_Chase }, S_MLDR_02 }, //712 /*S_MLDR_02*/ { SPR_MLDR, 0, 3, { A_Chase }, S_MLDR_03 }, //713 /*S_MLDR_03*/ { SPR_MLDR, 1, 3, { A_Chase }, S_MLDR_04 }, //714 /*S_MLDR_04*/ { SPR_MLDR, 1, 3, { A_Chase }, S_MLDR_05 }, //715 /*S_MLDR_05*/ { SPR_MLDR, 2, 3, { A_Chase }, S_MLDR_06 }, //716 /*S_MLDR_06*/ { SPR_MLDR, 2, 3, { A_Chase }, S_MLDR_07 }, //717 /*S_MLDR_07*/ { SPR_MLDR, 3, 3, { A_Chase }, S_MLDR_08 }, //718 /*S_MLDR_08*/ { SPR_MLDR, 3, 3, { A_Chase }, S_MLDR_01 }, //719 /*S_MLDR_09*/ { SPR_MLDR, 4, 3, { A_FaceTarget }, S_MLDR_10 }, //720 /*S_MLDR_10*/ { SPR_MLDR, 32773, 2, { A_BishopAttack }, S_MLDR_01 }, //721 /*S_MLDR_11*/ { SPR_MLDR, 3, 1, { A_Pain }, S_MLDR_01 }, //722 /*S_MLDR_12*/ { SPR_MLDR, 32774, 3, { NULL }, S_MLDR_13 }, //723 /*S_MLDR_13*/ { SPR_MLDR, 32775, 5, { A_Scream }, S_MLDR_14 }, //724 /*S_MLDR_14*/ { SPR_MLDR, 32776, 4, { A_BodyParts }, S_MLDR_15 }, //725 /*S_MLDR_15*/ { SPR_MLDR, 32777, 4, { A_DeathExplode2 }, S_MLDR_16 }, //726 /*S_MLDR_16*/ { SPR_MLDR, 32778, 4, { NULL }, S_MLDR_17 }, //727 /*S_MLDR_17*/ { SPR_MLDR, 32779, 4, { NULL }, S_MLDR_18 }, //728 /*S_MLDR_18*/ { SPR_MLDR, 32780, 4, { A_Fall }, S_MLDR_19 }, //729 /*S_MLDR_19*/ { SPR_MLDR, 32781, 4, { NULL }, S_MLDR_20 }, //730 /*S_MLDR_20*/ { SPR_MLDR, 32782, 4, { A_BodyParts }, S_MLDR_21 }, //731 /*S_MLDR_21*/ { SPR_MLDR, 32783, 4, { NULL }, S_MLDR_22 }, //732 /*S_MLDR_22*/ { SPR_MLDR, 32784, 4, { A_BodyParts }, S_MLDR_23 }, //733 /*S_MLDR_23*/ { SPR_MLDR, 32785, 4, { NULL }, S_MLDR_24 }, //734 /*S_MLDR_24*/ { SPR_MLDR, 32786, 4, { A_BodyParts }, S_MLDR_25 }, //735 /*S_MLDR_25*/ { SPR_MLDR, 32787, 4, { NULL }, S_MLDR_26 }, //736 /*S_MLDR_26*/ { SPR_MLDR, 32788, 4, { A_BodyParts }, S_MLDR_27 }, //737 /*S_MLDR_27*/ { SPR_MLDR, 21, 4, { A_SpawnSpectreB }, S_NULL }, //738 /*S_ORCL_00*/ { SPR_ORCL, 0, -1, { NULL }, S_NULL }, //739 /*S_ORCL_01*/ { SPR_ORCL, 1, 5, { NULL }, S_ORCL_02 }, //740 /*S_ORCL_02*/ { SPR_ORCL, 2, 5, { NULL }, S_ORCL_03 }, //741 /*S_ORCL_03*/ { SPR_ORCL, 3, 5, { NULL }, S_ORCL_04 }, //742 /*S_ORCL_04*/ { SPR_ORCL, 4, 5, { NULL }, S_ORCL_05 }, //743 /*S_ORCL_05*/ { SPR_ORCL, 5, 5, { NULL }, S_ORCL_06 }, //744 /*S_ORCL_06*/ { SPR_ORCL, 6, 5, { NULL }, S_ORCL_07 }, //745 /*S_ORCL_07*/ { SPR_ORCL, 7, 5, { NULL }, S_ORCL_08 }, //746 /*S_ORCL_08*/ { SPR_ORCL, 8, 5, { NULL }, S_ORCL_09 }, //747 /*S_ORCL_09*/ { SPR_ORCL, 9, 5, { NULL }, S_ORCL_10 }, //748 /*S_ORCL_10*/ { SPR_ORCL, 10, 5, { NULL }, S_ORCL_11 }, //749 /*S_ORCL_11*/ { SPR_ORCL, 11, 5, { A_Fall }, S_ORCL_12 }, //750 /*S_ORCL_12*/ { SPR_ORCL, 12, 5, { NULL }, S_ORCL_13 }, //751 /*S_ORCL_13*/ { SPR_ORCL, 13, 5, { A_AlertSpectreC }, S_ORCL_14 }, //752 /*S_ORCL_14*/ { SPR_ORCL, 14, 5, { NULL }, S_ORCL_15 }, //753 /*S_ORCL_15*/ { SPR_ORCL, 15, 5, { NULL }, S_ORCL_16 }, //754 /*S_ORCL_16*/ { SPR_ORCL, 16, -1, { NULL }, S_NULL }, //755 /*S_PRST_00*/ { SPR_PRST, 0, 10, { A_Look }, S_PRST_01 }, //756 /*S_PRST_01*/ { SPR_PRST, 1, 10, { A_FloatWeave }, S_PRST_00 }, //757 /*S_PRST_02*/ { SPR_PRST, 0, 4, { A_Chase }, S_PRST_03 }, //758 /*S_PRST_03*/ { SPR_PRST, 0, 4, { A_FloatWeave }, S_PRST_04 }, //759 /*S_PRST_04*/ { SPR_PRST, 1, 4, { A_Chase }, S_PRST_05 }, //760 /*S_PRST_05*/ { SPR_PRST, 1, 4, { A_FloatWeave }, S_PRST_06 }, //761 /*S_PRST_06*/ { SPR_PRST, 2, 4, { A_Chase }, S_PRST_07 }, //762 /*S_PRST_07*/ { SPR_PRST, 2, 4, { A_FloatWeave }, S_PRST_08 }, //763 /*S_PRST_08*/ { SPR_PRST, 3, 4, { A_Chase }, S_PRST_09 }, //764 /*S_PRST_09*/ { SPR_PRST, 3, 4, { A_FloatWeave }, S_PRST_02 }, //765 /*S_PRST_10*/ { SPR_PRST, 4, 4, { A_FaceTarget }, S_PRST_11 }, //766 /*S_PRST_11*/ { SPR_PRST, 5, 4, { A_BossMeleeAtk }, S_PRST_12 }, //767 /*S_PRST_12*/ { SPR_PRST, 4, 4, { A_FloatWeave }, S_PRST_02 }, //768 /*S_PRST_13*/ { SPR_PRST, 4, 4, { A_FaceTarget }, S_PRST_14 }, //769 /*S_PRST_14*/ { SPR_PRST, 5, 4, { A_FireHookShot }, S_PRST_15 }, //770 /*S_PRST_15*/ { SPR_PRST, 4, 4, { A_FloatWeave }, S_PRST_02 }, //771 /*S_PDED_00*/ { SPR_PDED, 0, 6, { NULL }, S_PDED_01 }, //772 /*S_PDED_01*/ { SPR_PDED, 1, 6, { A_Scream }, S_PDED_02 }, //773 /*S_PDED_02*/ { SPR_PDED, 2, 6, { NULL }, S_PDED_03 }, //774 /*S_PDED_03*/ { SPR_PDED, 3, 6, { A_Fall }, S_PDED_04 }, //775 /*S_PDED_04*/ { SPR_PDED, 4, 6, { NULL }, S_PDED_05 }, //776 /*S_PDED_05*/ { SPR_PDED, 5, 5, { NULL }, S_PDED_06 }, //777 /*S_PDED_06*/ { SPR_PDED, 6, 5, { NULL }, S_PDED_07 }, //778 /*S_PDED_07*/ { SPR_PDED, 7, 5, { NULL }, S_PDED_08 }, //779 /*S_PDED_08*/ { SPR_PDED, 8, 5, { NULL }, S_PDED_09 }, //780 /*S_PDED_09*/ { SPR_PDED, 9, 5, { NULL }, S_PDED_10 }, //781 /*S_PDED_10*/ { SPR_PDED, 8, 5, { NULL }, S_PDED_11 }, //782 /*S_PDED_11*/ { SPR_PDED, 9, 5, { NULL }, S_PDED_12 }, //783 /*S_PDED_12*/ { SPR_PDED, 8, 5, { NULL }, S_PDED_14 }, //784 /*S_PDED_13*/ { SPR_PDED, 9, 5, { NULL }, S_PDED_14 }, //785 /*S_PDED_14*/ { SPR_PDED, 10, 5, { NULL }, S_PDED_15 }, //786 /*S_PDED_15*/ { SPR_PDED, 11, 5, { NULL }, S_PDED_16 }, //787 /*S_PDED_16*/ { SPR_PDED, 12, 4, { NULL }, S_PDED_17 }, //788 /*S_PDED_17*/ { SPR_PDED, 13, 4, { NULL }, S_PDED_18 }, //789 /*S_PDED_18*/ { SPR_PDED, 14, 4, { NULL }, S_PDED_19 }, //790 /*S_PDED_19*/ { SPR_PDED, 15, 4, { NULL }, S_PDED_20 }, //791 /*S_PDED_20*/ { SPR_PDED, 16, 4, { A_SpawnSpectreE }, S_PDED_21 }, //792 /*S_PDED_21*/ { SPR_PDED, 17, 4, { NULL }, S_PDED_22 }, //793 /*S_PDED_22*/ { SPR_PDED, 18, 4, { NULL }, S_PDED_23 }, //794 /*S_PDED_23*/ { SPR_PDED, 19, -1, { NULL }, S_NULL }, //795 /*S_ALN1_00*/ { SPR_ALN1, 0, 10, { A_Look }, S_ALN1_01 }, //796 /*S_ALN1_01*/ { SPR_ALN1, 1, 10, { A_FloatWeave }, S_ALN1_00 }, //797 /*S_ALN1_02*/ { SPR_ALN1, 32768, 4, { A_Chase }, S_ALN1_03 }, //798 /*S_ALN1_03*/ { SPR_ALN1, 32769, 4, { A_Chase }, S_ALN1_04 }, //799 /*S_ALN1_04*/ { SPR_ALN1, 32770, 4, { A_FloatWeave }, S_ALN1_05 }, //800 /*S_ALN1_05*/ { SPR_ALN1, 32771, 4, { A_Chase }, S_ALN1_06 }, //801 /*S_ALN1_06*/ { SPR_ALN1, 32772, 4, { A_Chase }, S_ALN1_07 }, //802 /*S_ALN1_07*/ { SPR_ALN1, 32773, 4, { A_Chase }, S_ALN1_08 }, //803 /*S_ALN1_08*/ { SPR_ALN1, 32774, 4, { A_FloatWeave }, S_ALN1_09 }, //804 /*S_ALN1_09*/ { SPR_ALN1, 32775, 4, { A_Chase }, S_ALN1_10 }, //805 /*S_ALN1_10*/ { SPR_ALN1, 32776, 4, { A_Chase }, S_ALN1_11 }, //806 /*S_ALN1_11*/ { SPR_ALN1, 32777, 4, { A_Chase }, S_ALN1_12 }, //807 /*S_ALN1_12*/ { SPR_ALN1, 32778, 4, { A_FloatWeave }, S_ALN1_02 }, //808 /*S_ALN1_13*/ { SPR_ALN1, 32777, 4, { A_FaceTarget }, S_ALN1_14 }, //809 /*S_ALN1_14*/ { SPR_ALN1, 32776, 4, { A_BossMeleeAtk }, S_ALN1_15 }, //810 /*S_ALN1_15*/ { SPR_ALN1, 32775, 4, { NULL }, S_ALN1_04 }, //811 /*S_ALN1_16*/ { SPR_ALN1, 32777, 4, { A_FaceTarget }, S_ALN1_17 }, //812 /*S_ALN1_17*/ { SPR_ALN1, 32776, 4, { A_ProgrammerAttack }, S_ALN1_18 }, //813 /*S_ALN1_18*/ { SPR_ALN1, 32775, 4, { NULL }, S_ALN1_12 }, //814 /*S_ALN1_19*/ { SPR_ALN1, 9, 2, { A_Pain }, S_ALN1_08 }, //815 /*S_AL1P_00*/ { SPR_AL1P, 32768, 6, { A_NodeChunk }, S_AL1P_01 }, //816 /*S_AL1P_01*/ { SPR_AL1P, 32769, 6, { A_Scream }, S_AL1P_02 }, //817 /*S_AL1P_02*/ { SPR_AL1P, 32770, 6, { A_NodeChunk }, S_AL1P_03 }, //818 /*S_AL1P_03*/ { SPR_AL1P, 32771, 6, { NULL }, S_AL1P_04 }, //819 /*S_AL1P_04*/ { SPR_AL1P, 32772, 6, { NULL }, S_AL1P_05 }, //820 /*S_AL1P_05*/ { SPR_AL1P, 32773, 6, { A_NodeChunk }, S_AL1P_06 }, //821 /*S_AL1P_06*/ { SPR_AL1P, 32774, 6, { NULL }, S_AL1P_07 }, //822 /*S_AL1P_07*/ { SPR_AL1P, 32775, 6, { A_NodeChunk }, S_AL1P_08 }, //823 /*S_AL1P_08*/ { SPR_AL1P, 32776, 6, { NULL }, S_AL1P_09 }, //824 /*S_AL1P_09*/ { SPR_AL1P, 32777, 6, { NULL }, S_AL1P_10 }, //825 /*S_AL1P_10*/ { SPR_AL1P, 32778, 6, { NULL }, S_AL1P_11 }, //826 /*S_AL1P_11*/ { SPR_AL1P, 32779, 5, { NULL }, S_AL1P_12 }, //827 /*S_AL1P_12*/ { SPR_AL1P, 32780, 5, { NULL }, S_AL1P_13 }, //828 /*S_AL1P_13*/ { SPR_AL1P, 32781, 5, { A_HeadChunk }, S_AL1P_14 }, //829 /*S_AL1P_14*/ { SPR_AL1P, 32782, 5, { NULL }, S_AL1P_15 }, //830 /*S_AL1P_15*/ { SPR_AL1P, 32783, 5, { NULL }, S_AL1P_16 }, //831 /*S_AL1P_16*/ { SPR_AL1P, 32784, 5, { NULL }, S_AL1P_17 }, //832 /*S_AL1P_17*/ { SPR_AL1P, 32785, 5, { A_BossDeath }, S_NULL }, //833 /*S_NODE_00*/ { SPR_NODE, 32768, 6, { NULL }, S_NODE_01 }, //834 /*S_NODE_01*/ { SPR_NODE, 32769, 6, { NULL }, S_NODE_02 }, //835 /*S_NODE_02*/ { SPR_NODE, 32770, 6, { NULL }, S_NODE_03 }, //836 /*S_NODE_03*/ { SPR_NODE, 32771, 6, { NULL }, S_NODE_04 }, //837 /*S_NODE_04*/ { SPR_NODE, 32772, 6, { NULL }, S_NODE_05 }, //838 /*S_NODE_05*/ { SPR_NODE, 32773, 6, { NULL }, S_NODE_06 }, //839 /*S_NODE_06*/ { SPR_NODE, 32774, 6, { NULL }, S_NULL }, //840 /*S_MTHD_00*/ { SPR_MTHD, 32768, 5, { NULL }, S_MTHD_01 }, //841 /*S_MTHD_01*/ { SPR_MTHD, 32769, 5, { NULL }, S_MTHD_02 }, //842 /*S_MTHD_02*/ { SPR_MTHD, 32770, 5, { NULL }, S_MTHD_03 }, //843 /*S_MTHD_03*/ { SPR_MTHD, 32771, 5, { NULL }, S_MTHD_04 }, //844 /*S_MTHD_04*/ { SPR_MTHD, 32772, 5, { NULL }, S_MTHD_05 }, //845 /*S_MTHD_05*/ { SPR_MTHD, 32773, 5, { NULL }, S_MTHD_06 }, //846 /*S_MTHD_06*/ { SPR_MTHD, 32774, 5, { NULL }, S_MTHD_07 }, //847 /*S_MTHD_07*/ { SPR_MTHD, 32775, 5, { NULL }, S_MTHD_08 }, //848 /*S_MTHD_08*/ { SPR_MTHD, 32776, 5, { NULL }, S_MTHD_09 }, //849 /*S_MTHD_09*/ { SPR_MTHD, 32777, 5, { NULL }, S_MTHD_10 }, //850 /*S_MTHD_10*/ { SPR_MTHD, 32778, 5, { NULL }, S_NULL }, //851 /*S_ALN1_20*/ { SPR_ALN1, 5, 4, { A_FaceTarget }, S_ALN1_21 }, //852 /*S_ALN1_21*/ { SPR_ALN1, 8, 4, { A_FireSigilEOffshoot }, S_ALN1_22 }, //853 /*S_ALN1_22*/ { SPR_ALN1, 4, 4, { NULL }, S_ALN1_12 }, //854 /*S_ALN1_23*/ { SPR_ALN1, 0, 5, { A_FloatWeave }, S_ALN1_24 }, //855 /*S_ALN1_24*/ { SPR_ALN1, 1, 5, { A_FloatWeave }, S_ALN1_25 }, //856 /*S_ALN1_25*/ { SPR_ALN1, 2, 5, { A_FloatWeave }, S_ALN1_26 }, //857 /*S_ALN1_26*/ { SPR_ALN1, 3, 5, { A_FloatWeave }, S_ALN1_27 }, //858 /*S_ALN1_27*/ { SPR_ALN1, 4, 5, { A_FloatWeave }, S_ALN1_28 }, //859 /*S_ALN1_28*/ { SPR_ALN1, 5, 5, { A_FloatWeave }, S_ALN1_29 }, //860 /*S_ALN1_29*/ { SPR_ALN1, 6, 5, { A_FloatWeave }, S_ALN1_30 }, //861 /*S_ALN1_30*/ { SPR_ALN1, 7, 5, { A_FloatWeave }, S_ALN1_31 }, //862 /*S_ALN1_31*/ { SPR_ALN1, 8, 5, { A_FloatWeave }, S_ALN1_32 }, //863 /*S_ALN1_32*/ { SPR_ALN1, 9, 5, { A_FloatWeave }, S_ALN1_33 }, //864 /*S_ALN1_33*/ { SPR_ALN1, 10, 5, { A_FloatWeave }, S_ALN1_23 }, //865 /*S_ALN1_34*/ { SPR_ALN1, 0, 5, { A_Chase }, S_ALN1_35 }, //866 /*S_ALN1_35*/ { SPR_ALN1, 1, 5, { A_Chase }, S_ALN1_36 }, //867 /*S_ALN1_36*/ { SPR_ALN1, 2, 5, { A_FloatWeave }, S_ALN1_37 }, //868 /*S_ALN1_37*/ { SPR_ALN1, 3, 5, { A_Chase }, S_ALN1_38 }, //869 /*S_ALN1_38*/ { SPR_ALN1, 4, 5, { A_Chase }, S_ALN1_39 }, //870 /*S_ALN1_39*/ { SPR_ALN1, 5, 5, { A_Chase }, S_ALN1_40 }, //871 /*S_ALN1_40*/ { SPR_ALN1, 6, 5, { A_FloatWeave }, S_ALN1_41 }, //872 /*S_ALN1_41*/ { SPR_ALN1, 7, 5, { A_Chase }, S_ALN1_42 }, //873 /*S_ALN1_42*/ { SPR_ALN1, 8, 5, { A_Chase }, S_ALN1_43 }, //874 /*S_ALN1_43*/ { SPR_ALN1, 9, 5, { A_Chase }, S_ALN1_44 }, //875 /*S_ALN1_44*/ { SPR_ALN1, 10, 5, { A_FloatWeave }, S_ALN1_34 }, //876 /*S_ALN1_45*/ { SPR_ALN1, 9, 4, { A_FaceTarget }, S_ALN1_46 }, //877 /*S_ALN1_46*/ { SPR_ALN1, 8, 4, { A_BossMeleeAtk }, S_ALN1_47 }, //878 /*S_ALN1_47*/ { SPR_ALN1, 2, 4, { NULL }, S_ALN1_36 }, //879 /*S_ALN1_48*/ { SPR_ALN1, 5, 4, { A_FaceTarget }, S_ALN1_49 }, //880 /*S_ALN1_49*/ { SPR_ALN1, 8, 4, { A_SpectreCAttack }, S_ALN1_50 }, //881 /*S_ALN1_50*/ { SPR_ALN1, 4, 4, { NULL }, S_ALN1_44 }, //882 /*S_ALN1_51*/ { SPR_ALN1, 9, 2, { A_Pain }, S_ALN1_40 }, //883 /*S_ALN1_52*/ { SPR_ALN1, 5, 4, { A_FaceTarget }, S_ALN1_53 }, //884 /*S_ALN1_53*/ { SPR_ALN1, 8, 4, { A_SpectreDAttack }, S_ALN1_54 }, //885 /*S_ALN1_54*/ { SPR_ALN1, 4, 4, { NULL }, S_ALN1_12 }, //886 /*S_ALN1_55*/ { SPR_ALN1, 5, 4, { A_FaceTarget }, S_ALN1_56 }, //887 /*S_ALN1_56*/ { SPR_ALN1, 8, 4, { A_SpectreEAttack }, S_ALN1_57 }, //888 /*S_ALN1_57*/ { SPR_ALN1, 4, 4, { NULL }, S_ALN1_12 }, //889 /*S_MNAM_00*/ { SPR_MNAM, 0, 100, { A_FloatWeave }, S_MNAM_01 }, //890 /*S_MNAM_01*/ { SPR_MNAM, 32769, 60, { A_FloatWeave }, S_MNAM_02 }, //891 /*S_MNAM_02*/ { SPR_MNAM, 32770, 4, { A_FloatWeave }, S_MNAM_03 }, //892 /*S_MNAM_03*/ { SPR_MNAM, 32771, 4, { A_FloatWeave }, S_MNAM_04 }, //893 /*S_MNAM_04*/ { SPR_MNAM, 32772, 4, { A_FloatWeave }, S_MNAM_05 }, //894 /*S_MNAM_05*/ { SPR_MNAM, 32773, 4, { A_FloatWeave }, S_MNAM_06 }, //895 /*S_MNAM_06*/ { SPR_MNAM, 32774, 4, { A_FloatWeave }, S_MNAM_07 }, //896 /*S_MNAM_07*/ { SPR_MNAM, 32775, 4, { A_FloatWeave }, S_MNAM_08 }, //897 /*S_MNAM_08*/ { SPR_MNAM, 32776, 4, { A_FloatWeave }, S_MNAM_09 }, //898 /*S_MNAM_09*/ { SPR_MNAM, 32777, 4, { A_FloatWeave }, S_MNAM_10 }, //899 /*S_MNAM_10*/ { SPR_MNAM, 32778, 4, { A_FloatWeave }, S_MNAM_11 }, //900 /*S_MNAM_11*/ { SPR_MNAM, 32779, 4, { A_FloatWeave }, S_MNAL_00 }, //901 /*S_MNAL_00*/ { SPR_MNAL, 32768, 4, { A_Look }, S_MNAL_01 }, //902 /*S_MNAL_01*/ { SPR_MNAL, 32769, 4, { A_FloatWeave }, S_MNAL_00 }, //903 /*S_MNAL_02*/ { SPR_MNAL, 32768, 4, { A_Chase }, S_MNAL_03 }, //904 /*S_MNAL_03*/ { SPR_MNAL, 32769, 4, { A_Chase }, S_MNAL_04 }, //905 /*S_MNAL_04*/ { SPR_MNAL, 32770, 4, { A_FloatWeave }, S_MNAL_05 }, //906 /*S_MNAL_05*/ { SPR_MNAL, 32771, 4, { A_Chase }, S_MNAL_06 }, //907 /*S_MNAL_06*/ { SPR_MNAL, 32772, 4, { A_Chase }, S_MNAL_07 }, //908 /*S_MNAL_07*/ { SPR_MNAL, 32773, 4, { A_Chase }, S_MNAL_08 }, //909 /*S_MNAL_08*/ { SPR_MNAL, 32774, 4, { A_FloatWeave }, S_MNAL_09 }, //910 /*S_MNAL_09*/ { SPR_MNAL, 32775, 4, { A_Chase }, S_MNAL_10 }, //911 /*S_MNAL_10*/ { SPR_MNAL, 32776, 4, { A_Chase }, S_MNAL_11 }, //912 /*S_MNAL_11*/ { SPR_MNAL, 32777, 4, { A_Chase }, S_MNAL_12 }, //913 /*S_MNAL_12*/ { SPR_MNAL, 32778, 4, { A_FloatWeave }, S_MNAL_02 }, //914 /*S_MNAL_13*/ { SPR_MNAL, 32777, 4, { A_FaceTarget }, S_MNAL_14 }, //915 /*S_MNAL_14*/ { SPR_MNAL, 32776, 4, { A_BossMeleeAtk }, S_MNAL_15 }, //916 /*S_MNAL_15*/ { SPR_MNAL, 32770, 4, { NULL }, S_MNAL_04 }, //917 /*S_MNAL_16*/ { SPR_MNAL, 32773, 4, { A_FaceTarget }, S_MNAL_17 }, //918 /*S_MNAL_17*/ { SPR_MNAL, 32776, 4, { A_FireSigilWeapon }, S_MNAL_18 }, //919 /*S_MNAL_18*/ { SPR_MNAL, 32772, 4, { NULL }, S_MNAL_12 }, //920 /*S_MNAL_19*/ { SPR_MNAL, 32777, 2, { A_Pain }, S_MNAL_08 }, //921 /*S_MNAL_20*/ { SPR_MNAL, 32779, 7, { A_NodeChunk }, S_MNAL_21 }, //922 /*S_MNAL_21*/ { SPR_MNAL, 32780, 7, { A_Scream }, S_MNAL_22 }, //923 /*S_MNAL_22*/ { SPR_MNAL, 32781, 7, { A_NodeChunk }, S_MNAL_23 }, //924 /*S_MNAL_23*/ { SPR_MNAL, 32782, 7, { A_NodeChunk }, S_MNAL_24 }, //925 /*S_MNAL_24*/ { SPR_MNAL, 32783, 7, { A_HeadChunk }, S_MNAL_25 }, //926 /*S_MNAL_25*/ { SPR_MNAL, 32784, 64, { A_NodeChunk }, S_MNAL_26 }, //927 /*S_MNAL_26*/ { SPR_MNAL, 32784, 6, { A_EntityDeath }, S_NULL }, //928 /*S_MNAL_27*/ { SPR_MNAL, 32785, 10, { A_Look }, S_MNAL_27 }, //929 /*S_MNAL_28*/ { SPR_MNAL, 32785, 5, { A_FloatWeave }, S_MNAL_29 }, //930 /*S_MNAL_29*/ { SPR_MNAL, 32786, 5, { A_Chase }, S_MNAL_30 }, //931 /*S_MNAL_30*/ { SPR_MNAL, 32787, 5, { A_Chase }, S_MNAL_31 }, //932 /*S_MNAL_31*/ { SPR_MNAL, 32788, 5, { A_FloatWeave }, S_MNAL_32 }, //933 /*S_MNAL_32*/ { SPR_MNAL, 32789, 5, { A_Chase }, S_MNAL_33 }, //934 /*S_MNAL_33*/ { SPR_MNAL, 32790, 5, { A_FloatWeave }, S_MNAL_28 }, //935 /*S_MNAL_34*/ { SPR_MNAL, 32786, 4, { A_FaceTarget }, S_MNAL_35 }, //936 /*S_MNAL_35*/ { SPR_MNAL, 32785, 4, { A_BossMeleeAtk }, S_MNAL_36 }, //937 /*S_MNAL_36*/ { SPR_MNAL, 32787, 4, { A_FloatWeave }, S_MNAL_29 }, //938 /*S_MNAL_37*/ { SPR_MNAL, 32790, 4, { A_FaceTarget }, S_MNAL_38 }, //939 /*S_MNAL_38*/ { SPR_MNAL, 32788, 4, { A_FireSigilEOffshoot }, S_MNAL_39 }, //940 /*S_MNAL_39*/ { SPR_MNAL, 32789, 4, { A_FloatWeave }, S_MNAL_32 }, //941 /*S_MNAL_40*/ { SPR_MNAL, 32785, 2, { A_Pain }, S_MNAL_28 }, //942 /*S_MDTH_00*/ { SPR_MDTH, 32768, 3, { A_Scream }, S_MDTH_01 }, //943 /*S_MDTH_01*/ { SPR_MDTH, 32769, 3, { A_BodyParts }, S_MDTH_02 }, //944 /*S_MDTH_02*/ { SPR_MDTH, 32770, 3, { A_Fall }, S_MDTH_03 }, //945 /*S_MDTH_03*/ { SPR_MDTH, 32771, 3, { A_BodyParts }, S_MDTH_04 }, //946 /*S_MDTH_04*/ { SPR_MDTH, 32772, 3, { A_BodyParts }, S_MDTH_05 }, //947 /*S_MDTH_05*/ { SPR_MDTH, 32773, 3, { A_BodyParts }, S_MDTH_06 }, //948 /*S_MDTH_06*/ { SPR_MDTH, 32774, 3, { A_BodyParts }, S_MDTH_07 }, //949 /*S_MDTH_07*/ { SPR_MDTH, 32775, 3, { A_BodyParts }, S_MDTH_08 }, //950 /*S_MDTH_08*/ { SPR_MDTH, 32776, 3, { A_BodyParts }, S_MDTH_09 }, //951 /*S_MDTH_09*/ { SPR_MDTH, 32777, 3, { A_BodyParts }, S_MDTH_10 }, //952 /*S_MDTH_10*/ { SPR_MDTH, 32778, 3, { A_BodyParts }, S_MDTH_11 }, //953 /*S_MDTH_11*/ { SPR_MDTH, 32779, 3, { A_BodyParts }, S_MDTH_12 }, //954 /*S_MDTH_12*/ { SPR_MDTH, 32780, 3, { A_BodyParts }, S_MDTH_13 }, //955 /*S_MDTH_13*/ { SPR_MDTH, 32781, 3, { A_BodyParts }, S_MDTH_14 }, //956 /*S_MDTH_14*/ { SPR_MDTH, 32782, 3, { A_BossDeath }, S_NULL }, //957 /*S_NEST_00*/ { SPR_NEST, 0, -1, { NULL }, S_NULL }, //958 /*S_PODD_00*/ { SPR_PODD, 0, 60, { A_Look }, S_PODD_00 }, //959 /*S_PODD_01*/ { SPR_PODD, 0, 360, { NULL }, S_PODD_02 }, //960 /*S_PODD_02*/ { SPR_PODD, 1, 9, { A_Fall }, S_PODD_03 }, //961 /*S_PODD_03*/ { SPR_PODD, 2, 9, { NULL }, S_PODD_04 }, //962 /*S_PODD_04*/ { SPR_PODD, 3, 9, { A_SpawnEntity }, S_PODD_05 }, //963 /*S_PODD_05*/ { SPR_PODD, 4, -1, { NULL }, S_NULL }, //964 /*S_ZAP6_00*/ { SPR_ZAP6, 32768, 4, { NULL }, S_ZAP6_01 }, //965 /*S_ZAP6_01*/ { SPR_ZAP6, 32769, 4, { A_SigilTrail }, S_ZAP6_02 }, //966 /*S_ZAP6_02*/ { SPR_ZAP6, 32770, 4, { A_SigilTrail }, S_ZAP6_00 }, //967 /*S_ZOT3_00*/ { SPR_ZOT3, 32768, 4, { NULL }, S_ZOT3_01 }, //968 /*S_ZOT3_01*/ { SPR_ZOT3, 32769, 4, { NULL }, S_ZOT3_02 }, //969 /*S_ZOT3_02*/ { SPR_ZOT3, 32770, 4, { NULL }, S_ZOT3_03 }, //970 /*S_ZOT3_03*/ { SPR_ZOT3, 32771, 4, { NULL }, S_ZOT3_04 }, //971 /*S_ZOT3_04*/ { SPR_ZOT3, 32772, 4, { NULL }, S_ZOT3_00 }, //972 /*S_ZAP6_03*/ { SPR_ZAP6, 32768, 5, { NULL }, S_ZAP6_04 }, //973 /*S_ZAP6_04*/ { SPR_ZAP6, 32769, 5, { NULL }, S_ZAP6_05 }, //974 /*S_ZAP6_05*/ { SPR_ZAP6, 32770, 5, { NULL }, S_NULL }, //975 /*S_ZAP7_00*/ { SPR_ZAP7, 32768, 4, { A_Sigil_E_Action }, S_ZAP7_01 }, //976 /*S_ZAP7_01*/ { SPR_ZAP7, 32769, 4, { A_Sigil_E_Action }, S_ZAP7_02 }, //977 /*S_ZAP7_02*/ { SPR_ZAP7, 32770, 6, { A_Sigil_E_Action }, S_ZAP7_03 }, //978 /*S_ZAP7_03*/ { SPR_ZAP7, 32771, 6, { A_Sigil_E_Action }, S_ZAP7_04 }, //979 /*S_ZAP7_04*/ { SPR_ZAP7, 32772, 6, { A_Sigil_E_Action }, S_ZAP7_00 }, //980 /*S_ZOT1_00*/ { SPR_ZOT1, 32768, 4, { NULL }, S_ZOT1_01 }, //981 /*S_ZOT1_01*/ { SPR_ZOT1, 32769, 4, { NULL }, S_ZOT1_02 }, //982 /*S_ZOT1_02*/ { SPR_ZOT1, 32770, 6, { NULL }, S_ZOT1_03 }, //983 /*S_ZOT1_03*/ { SPR_ZOT1, 32771, 6, { NULL }, S_ZOT1_04 }, //984 /*S_ZOT1_04*/ { SPR_ZOT1, 32771, 6, { NULL }, S_ZOT1_00 }, //985 /*S_ZAP5_00*/ { SPR_ZAP5, 32768, 4, { A_MissileTick }, S_ZAP5_01 }, //986 /*S_ZAP5_01*/ { SPR_ZAP5, 32769, 4, { A_Sigil_A_Action }, S_ZAP5_02 }, //987 /*S_ZAP5_02*/ { SPR_ZAP5, 32770, 4, { A_MissileTick }, S_ZAP5_03 }, //988 /*S_ZAP5_03*/ { SPR_ZAP5, 32771, 4, { A_MissileTick }, S_ZAP5_00 }, //989 /*S_ZOT2_00*/ { SPR_ZOT2, 32768, 4, { A_Tracer }, S_ZOT2_01 }, //990 /*S_ZOT2_01*/ { SPR_ZOT2, 32769, 4, { A_Tracer }, S_ZOT2_02 }, //991 /*S_ZOT2_02*/ { SPR_ZOT2, 32770, 6, { A_Tracer }, S_ZOT2_03 }, //992 /*S_ZOT2_03*/ { SPR_ZOT2, 32771, 6, { A_Tracer }, S_ZOT2_04 }, //993 /*S_ZOT2_04*/ { SPR_ZOT2, 32772, 5, { A_Tracer }, S_ZOT2_00 }, //994 /*S_SEWR_00*/ { SPR_SEWR, 0, 10, { A_Look }, S_SEWR_00 }, //995 /*S_SEWR_01*/ { SPR_SEWR, 0, 6, { A_FloatWeave }, S_SEWR_02 }, //996 /*S_SEWR_02*/ { SPR_SEWR, 0, 6, { A_Chase }, S_SEWR_01 }, //997 /*S_SEWR_03*/ { SPR_SEWR, 1, 4, { A_FaceTarget }, S_SEWR_04 }, //998 /*S_SEWR_04*/ { SPR_SEWR, 32770, 8, { A_SentinelAttack }, S_SEWR_05 }, //999 /*S_SEWR_05*/ { SPR_SEWR, 32770, 4, { A_CheckTargetVisible }, S_SEWR_04 }, //1000 /*S_SEWR_06*/ { SPR_SEWR, 3, 5, { A_Pain }, S_SEWR_05 }, //1001 /*S_SEWR_07*/ { SPR_SEWR, 3, 7, { A_Fall }, S_SEWR_08 }, //1002 /*S_SEWR_08*/ { SPR_SEWR, 32772, 8, { A_BodyParts }, S_SEWR_09 }, //1003 /*S_SEWR_09*/ { SPR_SEWR, 32773, 5, { A_Scream }, S_SEWR_10 }, //1004 /*S_SEWR_10*/ { SPR_SEWR, 32774, 4, { A_BodyParts }, S_SEWR_11 }, //1005 /*S_SEWR_11*/ { SPR_SEWR, 32775, 4, { A_BodyParts }, S_SEWR_12 }, //1006 /*S_SEWR_12*/ { SPR_SEWR, 8, 4, { NULL }, S_SEWR_13 }, //1007 /*S_SEWR_13*/ { SPR_SEWR, 9, 5, { NULL }, S_NULL }, //1008 /*S_SPID_00*/ { SPR_SPID, 0, 1, { A_StalkerSetLook }, S_SPID_00 }, //1009 /*S_SPID_01*/ { SPR_SPID, 0, 10, { A_Look }, S_SPID_01 }, //1010 /*S_SPID_02*/ { SPR_SPID, 9, 10, { A_Look }, S_SPID_02 }, //1011 /*S_SPID_03*/ { SPR_SPID, 0, 1, { A_StalkerThink }, S_SPID_04 }, //1012 /*S_SPID_04*/ { SPR_SPID, 0, 3, { A_Chase }, S_SPID_05 }, //1013 /*S_SPID_05*/ { SPR_SPID, 1, 3, { A_Chase }, S_SPID_06 }, //1014 /*S_SPID_06*/ { SPR_SPID, 1, 3, { A_Chase }, S_SPID_07 }, //1015 /*S_SPID_07*/ { SPR_SPID, 2, 3, { A_StalkerChase }, S_SPID_08 }, //1016 /*S_SPID_08*/ { SPR_SPID, 2, 3, { A_Chase }, S_SPID_03 }, //1017 /*S_SPID_09*/ { SPR_SPID, 9, 3, { A_FaceTarget }, S_SPID_10 }, //1018 /*S_SPID_10*/ { SPR_SPID, 10, 3, { A_StalkerScratch }, S_SPID_18 }, //1019 /*S_SPID_11*/ { SPR_SPID, 2, 2, { A_StalkerDrop }, S_SPID_12 }, //1020 /*S_SPID_12*/ { SPR_SPID, 8, 3, { NULL }, S_SPID_13 }, //1021 /*S_SPID_13*/ { SPR_SPID, 7, 3, { NULL }, S_SPID_14 }, //1022 /*S_SPID_14*/ { SPR_SPID, 6, 3, { NULL }, S_SPID_15 }, //1023 /*S_SPID_15*/ { SPR_SPID, 5, 3, { NULL }, S_SPID_16 }, //1024 /*S_SPID_16*/ { SPR_SPID, 4, 3, { NULL }, S_SPID_17 }, //1025 /*S_SPID_17*/ { SPR_SPID, 3, 3, { NULL }, S_SPID_09 }, //1026 /*S_SPID_18*/ { SPR_SPID, 9, 3, { A_StalkerChase }, S_SPID_19 }, //1027 /*S_SPID_19*/ { SPR_SPID, 9, 3, { A_Chase }, S_SPID_20 }, //1028 /*S_SPID_20*/ { SPR_SPID, 10, 3, { A_Chase }, S_SPID_21 }, //1029 /*S_SPID_21*/ { SPR_SPID, 10, 3, { A_Chase }, S_SPID_22 }, //1030 /*S_SPID_22*/ { SPR_SPID, 11, 3, { A_StalkerChase }, S_SPID_23 }, //1031 /*S_SPID_23*/ { SPR_SPID, 11, 3, { A_Chase }, S_SPID_18 }, //1032 /*S_SPID_24*/ { SPR_SPID, 11, 1, { A_Pain }, S_SPID_03 }, //1033 /*S_SPID_25*/ { SPR_SPID, 14, 4, { NULL }, S_SPID_26 }, //1034 /*S_SPID_26*/ { SPR_SPID, 15, 4, { A_Scream }, S_SPID_27 }, //1035 /*S_SPID_27*/ { SPR_SPID, 16, 4, { NULL }, S_SPID_28 }, //1036 /*S_SPID_28*/ { SPR_SPID, 17, 4, { NULL }, S_SPID_29 }, //1037 /*S_SPID_29*/ { SPR_SPID, 18, 4, { NULL }, S_SPID_30 }, //1038 /*S_SPID_30*/ { SPR_SPID, 19, 4, { NULL }, S_SPID_31 }, //1039 /*S_SPID_31*/ { SPR_SPID, 20, 4, { A_Fall }, S_SPID_32 }, //1040 /*S_SPID_32*/ { SPR_SPID, 21, 4, { NULL }, S_SPID_33 }, //1041 /*S_SPID_33*/ { SPR_SPID, 22, 4, { NULL }, S_SPID_34 }, //1042 /*S_SPID_34*/ { SPR_SPID, 32791, 4, { NULL }, S_SPID_35 }, //1043 /*S_SPID_35*/ { SPR_SPID, 32792, 4, { NULL }, S_SPID_36 }, //1044 /*S_SPID_36*/ { SPR_SPID, 32793, 4, { NULL }, S_SPID_37 }, //1045 /*S_SPID_37*/ { SPR_SPID, 32794, 4, { NULL }, S_NULL }, //1046 /*S_ROB3_00*/ { SPR_ROB3, 0, 10, { A_Look }, S_ROB3_01 }, //1047 /*S_ROB3_01*/ { SPR_ROB3, 1, 10, { A_Look }, S_ROB3_00 }, //1048 /*S_ROB3_02*/ { SPR_ROB3, 1, 3, { A_InqChase }, S_ROB3_03 }, //1049 /*S_ROB3_03*/ { SPR_ROB3, 1, 3, { A_Chase }, S_ROB3_04 }, //1050 /*S_ROB3_04*/ { SPR_ROB3, 2, 4, { A_Chase }, S_ROB3_05 }, //1051 /*S_ROB3_05*/ { SPR_ROB3, 2, 4, { A_Chase }, S_ROB3_06 }, //1052 /*S_ROB3_06*/ { SPR_ROB3, 3, 4, { A_Chase }, S_ROB3_07 }, //1053 /*S_ROB3_07*/ { SPR_ROB3, 3, 4, { A_Chase }, S_ROB3_08 }, //1054 /*S_ROB3_08*/ { SPR_ROB3, 4, 3, { A_InqChase }, S_ROB3_09 }, //1055 /*S_ROB3_09*/ { SPR_ROB3, 4, 3, { A_InqFlyCheck }, S_ROB3_02 }, //1056 /*S_ROB3_10*/ { SPR_ROB3, 0, 2, { A_InqFlyCheck }, S_ROB3_11 }, //1057 /*S_ROB3_11*/ { SPR_ROB3, 5, 6, { A_FaceTarget }, S_ROB3_12 }, //1058 /*S_ROB3_12*/ { SPR_ROB3, 32774, 8, { A_ReaverAttack }, S_ROB3_13 }, //1059 /*S_ROB3_13*/ { SPR_ROB3, 6, 8, { A_ReaverAttack }, S_ROB3_02 }, //1060 /*S_ROB3_14*/ { SPR_ROB3, 10, 12, { A_FaceTarget }, S_ROB3_15 }, //1061 /*S_ROB3_15*/ { SPR_ROB3, 32777, 6, { A_InqGrenade }, S_ROB3_16 }, //1062 /*S_ROB3_16*/ { SPR_ROB3, 10, 12, { NULL }, S_ROB3_02 }, //1063 /*S_ROB3_17*/ { SPR_ROB3, 32775, 8, { A_InqTakeOff }, S_ROB3_18 }, //1064 /*S_ROB3_18*/ { SPR_ROB3, 32776, 4, { A_InqFly }, S_ROB3_19 }, //1065 /*S_ROB3_19*/ { SPR_ROB3, 32775, 4, { A_InqFly }, S_ROB3_18 }, //1066 /*S_ROB3_20*/ { SPR_ROB3, 11, 4, { A_BodyParts }, S_ROB3_21 }, //1067 /*S_ROB3_21*/ { SPR_ROB3, 12, 4, { A_Scream }, S_ROB3_22 }, //1068 /*S_ROB3_22*/ { SPR_ROB3, 13, 4, { A_BodyParts }, S_ROB3_23 }, //1069 /*S_ROB3_23*/ { SPR_ROB3, 32782, 4, { A_DeathExplode1 }, S_ROB3_24 }, //1070 /*S_ROB3_24*/ { SPR_ROB3, 32783, 4, { A_BodyParts }, S_ROB3_25 }, //1071 /*S_ROB3_25*/ { SPR_ROB3, 32784, 4, { A_Fall }, S_ROB3_26 }, //1072 /*S_ROB3_26*/ { SPR_ROB3, 17, 4, { A_BodyParts }, S_ROB3_27 }, //1073 /*S_ROB3_27*/ { SPR_ROB3, 18, 4, { A_BodyParts }, S_ROB3_28 }, //1074 /*S_ROB3_28*/ { SPR_ROB3, 19, 4, { A_BodyParts }, S_ROB3_29 }, //1075 /*S_ROB3_29*/ { SPR_ROB3, 20, 4, { A_BodyParts }, S_ROB3_30 }, //1076 /*S_ROB3_30*/ { SPR_ROB3, 21, 4, { A_BodyParts }, S_ROB3_31 }, //1077 /*S_ROB3_31*/ { SPR_ROB3, 32790, 4, { A_DeathExplode1 }, S_ROB3_32 }, //1078 /*S_ROB3_32*/ { SPR_ROB3, 32791, 4, { A_BodyParts }, S_ROB3_33 }, //1079 /*S_ROB3_33*/ { SPR_ROB3, 32792, 4, { A_BodyParts }, S_ROB3_34 }, //1080 /*S_ROB3_34*/ { SPR_ROB3, 25, 4, { A_BodyParts }, S_ROB3_35 }, //1081 /*S_ROB3_35*/ { SPR_ROB3, 26, 4, { A_BodyParts }, S_ROB3_36 }, //1082 /*S_ROB3_36*/ { SPR_ROB3, 27, 3, { A_BodyParts }, S_ROB3_37 }, //1083 /*S_ROB3_37*/ { SPR_ROB3, 32796, 3, { A_DeathExplode1 }, S_RBB3_00 }, //1084 /*S_RBB3_00*/ { SPR_RBB3, 32768, 3, { A_InqTossArm }, S_RBB3_01 }, //1085 /*S_RBB3_01*/ { SPR_RBB3, 32769, 3, { A_BodyParts }, S_RBB3_02 }, //1086 /*S_RBB3_02*/ { SPR_RBB3, 2, 3, { A_BodyParts }, S_RBB3_03 }, //1087 /*S_RBB3_03*/ { SPR_RBB3, 3, 3, { A_BodyParts }, S_RBB3_04 }, //1088 /*S_RBB3_04*/ { SPR_RBB3, 4, -1, { A_BossDeath }, S_NULL }, //1089 /*S_RBB3_05*/ { SPR_RBB3, 32773, 5, { NULL }, S_RBB3_06 }, //1090 /*S_RBB3_06*/ { SPR_RBB3, 32774, 5, { NULL }, S_RBB3_07 }, //1091 /*S_RBB3_07*/ { SPR_RBB3, 7, -1, { NULL }, S_NULL }, //1092 /*S_PRGR_00*/ { SPR_PRGR, 0, 5, { A_Look }, S_PRGR_01 }, //1093 /*S_PRGR_01*/ { SPR_PRGR, 0, 1, { A_FloatWeave }, S_PRGR_00 }, //1094 /*S_PRGR_02*/ { SPR_PRGR, 0, 160, { A_FloatWeave }, S_PRGR_03 }, //1095 /*S_PRGR_03*/ { SPR_PRGR, 1, 5, { A_FloatWeave }, S_PRGR_04 }, //1096 /*S_PRGR_04*/ { SPR_PRGR, 2, 5, { A_FloatWeave }, S_PRGR_05 }, //1097 /*S_PRGR_05*/ { SPR_PRGR, 3, 5, { A_FloatWeave }, S_PRGR_06 }, //1098 /*S_PRGR_06*/ { SPR_PRGR, 4, 2, { A_FloatWeave }, S_PRGR_07 }, //1099 /*S_PRGR_07*/ { SPR_PRGR, 5, 2, { A_FloatWeave }, S_PRGR_08 }, //1100 /*S_PRGR_08*/ { SPR_PRGR, 4, 3, { A_Chase }, S_PRGR_09 }, //1101 /*S_PRGR_09*/ { SPR_PRGR, 5, 3, { A_Chase }, S_PRGR_06 }, //1102 /*S_PRGR_10*/ { SPR_PRGR, 4, 2, { A_FloatWeave }, S_PRGR_11 }, //1103 /*S_PRGR_11*/ { SPR_PRGR, 5, 3, { A_FloatWeave }, S_PRGR_12 }, //1104 /*S_PRGR_12*/ { SPR_PRGR, 4, 3, { A_FaceTarget }, S_PRGR_13 }, //1105 /*S_PRGR_13*/ { SPR_PRGR, 5, 4, { A_ProgrammerMelee }, S_PRGR_06 }, //1106 /*S_PRGR_14*/ { SPR_PRGR, 6, 5, { A_FaceTarget }, S_PRGR_15 }, //1107 /*S_PRGR_15*/ { SPR_PRGR, 7, 5, { A_FloatWeave }, S_PRGR_16 }, //1108 /*S_PRGR_16*/ { SPR_PRGR, 32776, 5, { A_FaceTarget }, S_PRGR_17 }, //1109 /*S_PRGR_17*/ { SPR_PRGR, 32777, 5, { A_ProgrammerAttack }, S_PRGR_06 }, //1110 /*S_PRGR_18*/ { SPR_PRGR, 10, 5, { A_Pain }, S_PRGR_19 }, //1111 /*S_PRGR_19*/ { SPR_PRGR, 11, 5, { A_FloatWeave }, S_PRGR_06 }, //1112 /*S_PRGR_20*/ { SPR_PRGR, 32779, 7, { A_BodyParts }, S_PRGR_21 }, //1113 /*S_PRGR_21*/ { SPR_PRGR, 32780, 7, { A_Scream }, S_PRGR_22 }, //1114 /*S_PRGR_22*/ { SPR_PRGR, 32781, 7, { A_BodyParts }, S_PRGR_23 }, //1115 /*S_PRGR_23*/ { SPR_PRGR, 32782, 7, { A_Fall }, S_PRGR_24 }, //1116 /*S_PRGR_24*/ { SPR_PRGR, 32783, 7, { A_BodyParts }, S_PRGR_25 }, //1117 /*S_PRGR_25*/ { SPR_PRGR, 32784, 7, { A_ProgrammerDie }, S_PRGR_26 }, //1118 /*S_PRGR_26*/ { SPR_PRGR, 32785, 7, { NULL }, S_PRGR_27 }, //1119 /*S_PRGR_27*/ { SPR_PRGR, 32786, 6, { NULL }, S_PRGR_28 }, //1120 /*S_PRGR_28*/ { SPR_PRGR, 32787, 5, { NULL }, S_PRGR_29 }, //1121 /*S_PRGR_29*/ { SPR_PRGR, 32788, 5, { NULL }, S_PRGR_30 }, //1122 /*S_PRGR_30*/ { SPR_PRGR, 32789, 5, { NULL }, S_PRGR_31 }, //1123 /*S_PRGR_31*/ { SPR_PRGR, 32790, 5, { NULL }, S_PRGR_32 }, //1124 /*S_PRGR_32*/ { SPR_PRGR, 32791, 32, { NULL }, S_PRGR_33 }, //1125 /*S_PRGR_33*/ { SPR_PRGR, 32791, -1, { A_BossDeath }, S_NULL }, //1126 /*S_BASE_00*/ { SPR_BASE, 32768, 5, { A_DeathExplode3 }, S_BASE_01 }, //1127 /*S_BASE_01*/ { SPR_BASE, 32769, 5, { NULL }, S_BASE_02 }, //1128 /*S_BASE_02*/ { SPR_BASE, 32770, 5, { NULL }, S_BASE_03 }, //1129 /*S_BASE_03*/ { SPR_BASE, 32771, 5, { NULL }, S_BASE_04 }, //1130 /*S_BASE_04*/ { SPR_BASE, 4, 5, { NULL }, S_BASE_05 }, //1131 /*S_BASE_05*/ { SPR_BASE, 5, 5, { NULL }, S_BASE_06 }, //1132 /*S_BASE_06*/ { SPR_BASE, 6, 5, { NULL }, S_BASE_07 }, //1133 /*S_BASE_07*/ { SPR_BASE, 7, -1, { NULL }, S_NULL }, //1134 /*S_FRBL_00*/ { SPR_FRBL, 32768, 3, { NULL }, S_FRBL_01 }, //1135 /*S_FRBL_01*/ { SPR_FRBL, 32769, 3, { NULL }, S_FRBL_02 }, //1136 /*S_FRBL_02*/ { SPR_FRBL, 32770, 3, { A_MissileTick }, S_FRBL_00 }, //1137 /*S_FRBL_03*/ { SPR_FRBL, 32771, 5, { A_FlameDeath }, S_FRBL_04 }, //1138 /*S_FRBL_04*/ { SPR_FRBL, 32772, 5, { NULL }, S_FRBL_05 }, //1139 /*S_FRBL_05*/ { SPR_FRBL, 32773, 5, { NULL }, S_FRBL_06 }, //1140 /*S_FRBL_06*/ { SPR_FRBL, 32774, 5, { NULL }, S_FRBL_07 }, //1141 /*S_FRBL_07*/ { SPR_FRBL, 32775, 5, { NULL }, S_FRBL_08 }, //1142 /*S_FRBL_08*/ { SPR_FRBL, 32776, 5, { NULL }, S_NULL }, //1143 /*S_KLAX_00*/ { SPR_KLAX, 0, 5, { A_Listen }, S_KLAX_00 }, //1144 /*S_KLAX_01*/ { SPR_KLAX, 1, 6, { A_ClaxonBlare }, S_KLAX_02 }, //1145 /*S_KLAX_02*/ { SPR_KLAX, 2, 60, { NULL }, S_KLAX_01 }, //1146 /*S_TURT_00*/ { SPR_TURT, 0, 5, { A_Listen }, S_TURT_00 }, //1147 /*S_TURT_01*/ { SPR_TURT, 0, 2, { A_Chase }, S_TURT_01 }, //1148 /*S_TURT_02*/ { SPR_TURT, 1, 4, { A_BulletAttack }, S_TURT_03 }, //1149 /*S_TURT_03*/ { SPR_TURT, 3, 3, { A_CheckTargetVisible }, S_TURT_04 }, //1150 /*S_TURT_04*/ { SPR_TURT, 0, 4, { A_CheckTargetVisible }, S_TURT_02 }, //1151 /*S_BALL_00*/ { SPR_BALL, 32768, 6, { NULL }, S_BALL_01 }, //1152 /*S_BALL_01*/ { SPR_BALL, 32769, 6, { NULL }, S_BALL_02 }, //1153 /*S_BALL_02*/ { SPR_BALL, 32770, 6, { NULL }, S_BALL_03 }, //1154 /*S_BALL_03*/ { SPR_BALL, 32771, 6, { NULL }, S_BALL_04 }, //1155 /*S_BALL_04*/ { SPR_BALL, 32772, 6, { NULL }, S_TURT_05 }, //1156 /*S_TURT_05*/ { SPR_TURT, 2, -1, { NULL }, S_NULL }, //1157 /*S_PSTN_00*/ { SPR_PSTN, 0, 8, { NULL }, S_PSTN_01 }, //1158 /*S_PSTN_01*/ { SPR_PSTN, 1, 8, { NULL }, S_PSTN_00 }, //1159 /*S_PSTN_02*/ { SPR_PSTN, 32768, 4, { A_Scream }, S_PSTN_03 }, //1160 /*S_PSTN_03*/ { SPR_PSTN, 32769, 4, { A_Fall }, S_PSTN_04 }, //1161 /*S_PSTN_04*/ { SPR_PSTN, 32770, 4, { A_QuestMsg }, S_PSTN_05 }, //1162 /*S_PSTN_05*/ { SPR_PSTN, 32771, 4, { A_SpawnSparkPuff }, S_PSTN_06 }, //1163 /*S_PSTN_06*/ { SPR_PSTN, 32772, 4, { A_BodyParts }, S_PSTN_07 }, //1164 /*S_PSTN_07*/ { SPR_PSTN, 32773, 4, { NULL }, S_PSTN_08 }, //1165 /*S_PSTN_08*/ { SPR_PSTN, 32774, 4, { A_SpawnSparkPuff }, S_PSTN_09 }, //1166 /*S_PSTN_09*/ { SPR_PSTN, 7, 4, { NULL }, S_PSTN_10 }, //1167 /*S_PSTN_10*/ { SPR_PSTN, 8, -1, { NULL }, S_NULL }, //1168 /*S_SECR_00*/ { SPR_SECR, 32768, 4, { NULL }, S_SECR_01 }, //1169 /*S_SECR_01*/ { SPR_SECR, 32769, 4, { NULL }, S_SECR_02 }, //1170 /*S_SECR_02*/ { SPR_SECR, 32770, 4, { NULL }, S_SECR_03 }, //1171 /*S_SECR_03*/ { SPR_SECR, 32771, 4, { NULL }, S_SECR_00 }, //1172 /*S_SECR_04*/ { SPR_SECR, 32772, 5, { A_SpawnSparkPuff }, S_SECR_05 }, //1173 /*S_SECR_05*/ { SPR_SECR, 32773, 5, { A_Fall }, S_SECR_06 }, //1174 /*S_SECR_06*/ { SPR_SECR, 32774, 5, { A_QuestMsg }, S_SECR_07 }, //1175 /*S_SECR_07*/ { SPR_SECR, 32775, 5, { A_BodyParts }, S_SECR_08 }, //1176 /*S_SECR_08*/ { SPR_SECR, 32776, 5, { A_SpawnSparkPuff }, S_SECR_09 }, //1177 /*S_SECR_09*/ { SPR_SECR, 9, 5, { NULL }, S_SECR_10 }, //1178 /*S_SECR_10*/ { SPR_SECR, 10, 5, { A_SpawnSparkPuff }, S_SECR_11 }, //1179 /*S_SECR_11*/ { SPR_SECR, 11, 5, { NULL }, S_SECR_12 }, //1180 /*S_SECR_12*/ { SPR_SECR, 12, 5, { A_SpawnSparkPuff }, S_SECR_13 }, //1181 /*S_SECR_13*/ { SPR_SECR, 13, 5, { NULL }, S_SECR_14 }, //1182 /*S_SECR_14*/ { SPR_SECR, 14, 5, { A_SpawnSparkPuff }, S_SECR_15 }, //1183 /*S_SECR_15*/ { SPR_SECR, 15, -1, { NULL }, S_NULL }, //1184 /*S_XPRK_01*/ { SPR_XPRK, 0, -1, { NULL }, S_NULL }, //1185 /*S_XPRK_02*/ { SPR_XPRK, 0, 1, { A_ClearForceField }, S_BNG3_00 }, //1186 /*S_TARG_00*/ { SPR_TARG, 0, -1, { NULL }, S_NULL }, //1187 /*S_RING_00*/ { SPR_RING, 0, -1, { NULL }, S_NULL }, //1188 /*S_EARS_00*/ { SPR_EARS, 0, -1, { NULL }, S_NULL }, //1189 /*S_COMM_00*/ { SPR_COMM, 0, -1, { NULL }, S_NULL }, //1190 /*S_BOOM_00*/ { SPR_BOOM, 32768, 1, { A_CrystalRadiusAtk }, S_BOOM_01 }, //1191 /*S_BOOM_01*/ { SPR_BOOM, 32769, 3, { A_QuestMsg }, S_BOOM_02 }, //1192 /*S_BOOM_02*/ { SPR_BOOM, 32770, 2, { A_CrystalExplode }, S_BOOM_03 }, //1193 /*S_BOOM_03*/ { SPR_BOOM, 32771, 3, { A_SpawnSparkPuff }, S_BOOM_04 }, //1194 /*S_BOOM_04*/ { SPR_BOOM, 32772, 3, { NULL }, S_BOOM_05 }, //1195 /*S_BOOM_05*/ { SPR_BOOM, 32773, 3, { NULL }, S_BOOM_06 }, //1196 /*S_BOOM_06*/ { SPR_BOOM, 32774, 3, { A_SpawnSparkPuff }, S_BOOM_07 }, //1197 /*S_BOOM_07*/ { SPR_BOOM, 32775, 1, { A_CrystalRadiusAtk }, S_BOOM_08 }, //1198 /*S_BOOM_08*/ { SPR_BOOM, 32776, 3, { NULL }, S_BOOM_09 }, //1199 /*S_BOOM_09*/ { SPR_BOOM, 32777, 3, { A_SpawnSparkPuff }, S_BOOM_10 }, //1200 /*S_BOOM_10*/ { SPR_BOOM, 32778, 3, { A_SpawnSparkPuff }, S_BOOM_11 }, //1201 /*S_BOOM_11*/ { SPR_BOOM, 32779, 3, { A_SpawnSparkPuff }, S_BOOM_12 }, //1202 /*S_BOOM_12*/ { SPR_BOOM, 32780, 3, { NULL }, S_BOOM_13 }, //1203 /*S_BOOM_13*/ { SPR_BOOM, 32781, 3, { NULL }, S_BOOM_14 }, //1204 /*S_BOOM_14*/ { SPR_BOOM, 32782, 3, { A_SpawnSparkPuff }, S_BOOM_15 }, //1205 /*S_BOOM_15*/ { SPR_BOOM, 32783, 3, { NULL }, S_BOOM_16 }, //1206 /*S_BOOM_16*/ { SPR_BOOM, 32784, 3, { NULL }, S_BOOM_17 }, //1207 /*S_BOOM_17*/ { SPR_BOOM, 32785, 3, { NULL }, S_BOOM_18 }, //1208 /*S_BOOM_18*/ { SPR_BOOM, 32786, 3, { NULL }, S_BOOM_19 }, //1209 /*S_BOOM_19*/ { SPR_BOOM, 32787, 3, { NULL }, S_BOOM_20 }, //1210 /*S_BOOM_20*/ { SPR_BOOM, 32788, 3, { A_ExtraLightOff }, S_BOOM_21 }, //1211 /*S_BOOM_21*/ { SPR_BOOM, 32789, 3, { NULL }, S_BOOM_22 }, //1212 /*S_BOOM_22*/ { SPR_BOOM, 32790, 3, { NULL }, S_BOOM_23 }, //1213 /*S_BOOM_23*/ { SPR_BOOM, 32791, 3, { NULL }, S_BOOM_24 }, //1214 /*S_BOOM_24*/ { SPR_BOOM, 32792, 3, { NULL }, S_NULL }, //1215 /*S_RATT_00*/ { SPR_RATT, 0, 10, { A_Look }, S_RATT_00 }, //1216 /*S_RATT_01*/ { SPR_RATT, 0, 4, { A_Chase }, S_RATT_02 }, //1217 /*S_RATT_02*/ { SPR_RATT, 0, 4, { A_Chase }, S_RATT_03 }, //1218 /*S_RATT_03*/ { SPR_RATT, 1, 4, { A_Chase }, S_RATT_04 }, //1219 /*S_RATT_04*/ { SPR_RATT, 1, 4, { A_Chase }, S_RATT_01 }, //1220 /*S_RATT_05*/ { SPR_RATT, 0, 8, { A_RandomWalk }, S_RATT_06 }, //1221 /*S_RATT_06*/ { SPR_RATT, 1, 4, { A_RandomWalk }, S_RATT_01 }, //1222 /*S_HOGN_00*/ { SPR_HOGN, 0, 2, { A_ZombieInSpecialSector }, S_HOGN_00 }, //1223 /*S_HOGN_01*/ { SPR_HOGN, 1, 1, { A_ZombieInSpecialSector }, S_HOGN_02 }, //1224 /*S_HOGN_02*/ { SPR_HOGN, 2, 1, { A_Pain }, S_HOGN_00 }, //1225 /*S_DEAD_00*/ { SPR_DEAD, 0, -1, { NULL }, S_NULL }, //1226 /*S_SBAN_00*/ { SPR_SBAN, 0, -1, { NULL }, S_NULL }, //1227 /*S_BOTR_00*/ { SPR_BOTR, 0, -1, { NULL }, S_NULL }, //1228 /*S_HATR_00*/ { SPR_HATR, 0, -1, { NULL }, S_NULL }, //1229 /*S_TOPR_00*/ { SPR_TOPR, 0, -1, { NULL }, S_NULL }, //1230 /*S_COUP_00*/ { SPR_COUP, 0, 5, { NULL }, S_COUP_01 }, //1231 /*S_COUP_01*/ { SPR_COUP, 1, 5, { NULL }, S_COUP_00 }, //1232 /*S_COUP_02*/ { SPR_COUP, 2, -1, { NULL }, S_COUP_01 }, //1233 /*S_BUBB_00*/ { SPR_BUBB, 0, 4, { A_ActiveSound }, S_BUBB_00 }, //1234 /*S_BUBF_00*/ { SPR_BUBF, 0, 4, { A_ActiveSound }, S_BUBF_00 }, //1235 /*S_BUBC_00*/ { SPR_BUBC, 0, 4, { A_ActiveSound }, S_BUBC_00 }, //1236 /*S_ASPR_00*/ { SPR_ASPR, 0, 4, { A_ActiveSound }, S_ASPR_00 }, //1237 /*S_SPDL_00*/ { SPR_SPDL, 0, 5, { A_ActiveSound }, S_SPDL_01 }, //1238 /*S_SPDL_01*/ { SPR_SPDL, 1, 5, { A_ActiveSound }, S_SPDL_02 }, //1239 /*S_SPDL_02*/ { SPR_SPDL, 2, 5, { A_ActiveSound }, S_SPDL_00 }, //1240 /*S_TOKN_00*/ { SPR_TOKN, 0, -1, { NULL }, S_NULL }, //1241 /*S_OTOK_00*/ { SPR_OTOK, 0, -1, { NULL }, S_NULL }, //1242 /*S_HELT_00*/ { SPR_HELT, 0, -1, { NULL }, S_NULL }, //1243 /*S_GUNT_00*/ { SPR_GUNT, 0, -1, { NULL }, S_NULL }, //1244 /*S_FULL_00*/ { SPR_FULL, 0, 35, { NULL }, S_FULL_01 }, //1245 /*S_FULL_01*/ { SPR_FULL, 1, 35, { NULL }, S_FULL_00 }, //1246 /*S_MEAT_00*/ { SPR_MEAT, 0, 700, { NULL }, S_NULL }, //1247 /*S_MEAT_01*/ { SPR_MEAT, 1, 700, { NULL }, S_NULL }, //1248 /*S_MEAT_02*/ { SPR_MEAT, 2, 700, { NULL }, S_NULL }, //1249 /*S_MEAT_03*/ { SPR_MEAT, 3, 700, { NULL }, S_NULL }, //1250 /*S_MEAT_04*/ { SPR_MEAT, 4, 700, { NULL }, S_NULL }, //1251 /*S_MEAT_05*/ { SPR_MEAT, 5, 700, { NULL }, S_NULL }, //1252 /*S_MEAT_06*/ { SPR_MEAT, 6, 700, { NULL }, S_NULL }, //1253 /*S_MEAT_07*/ { SPR_MEAT, 7, 700, { NULL }, S_NULL }, //1254 /*S_MEAT_08*/ { SPR_MEAT, 8, 700, { NULL }, S_NULL }, //1255 /*S_MEAT_09*/ { SPR_MEAT, 9, 700, { NULL }, S_NULL }, //1256 /*S_MEAT_10*/ { SPR_MEAT, 10, 700, { NULL }, S_NULL }, //1257 /*S_MEAT_11*/ { SPR_MEAT, 11, 700, { NULL }, S_NULL }, //1258 /*S_MEAT_12*/ { SPR_MEAT, 12, 700, { NULL }, S_NULL }, //1259 /*S_MEAT_13*/ { SPR_MEAT, 13, 700, { NULL }, S_NULL }, //1260 /*S_MEAT_14*/ { SPR_MEAT, 14, 700, { NULL }, S_NULL }, //1261 /*S_MEAT_15*/ { SPR_MEAT, 15, 700, { NULL }, S_NULL }, //1262 /*S_MEAT_16*/ { SPR_MEAT, 16, 700, { NULL }, S_NULL }, //1263 /*S_MEAT_17*/ { SPR_MEAT, 17, 700, { NULL }, S_NULL }, //1264 /*S_MEAT_18*/ { SPR_MEAT, 18, 700, { NULL }, S_NULL }, //1265 /*S_MEAT_19*/ { SPR_MEAT, 19, 700, { NULL }, S_NULL }, //1266 /*S_JUNK_00*/ { SPR_JUNK, 0, 700, { NULL }, S_NULL }, //1267 /*S_JUNK_01*/ { SPR_JUNK, 1, 700, { NULL }, S_NULL }, //1268 /*S_JUNK_02*/ { SPR_JUNK, 2, 700, { NULL }, S_NULL }, //1269 /*S_JUNK_03*/ { SPR_JUNK, 3, 700, { NULL }, S_NULL }, //1270 /*S_JUNK_04*/ { SPR_JUNK, 4, 700, { NULL }, S_NULL }, //1271 /*S_JUNK_05*/ { SPR_JUNK, 5, 700, { NULL }, S_NULL }, //1272 /*S_JUNK_06*/ { SPR_JUNK, 6, 700, { NULL }, S_NULL }, //1273 /*S_JUNK_07*/ { SPR_JUNK, 7, 700, { NULL }, S_NULL }, //1274 /*S_JUNK_08*/ { SPR_JUNK, 8, 700, { NULL }, S_NULL }, //1275 /*S_JUNK_09*/ { SPR_JUNK, 9, 700, { NULL }, S_NULL }, //1276 /*S_JUNK_10*/ { SPR_JUNK, 10, 700, { NULL }, S_NULL }, //1277 /*S_JUNK_11*/ { SPR_JUNK, 11, 700, { NULL }, S_NULL }, //1278 /*S_JUNK_12*/ { SPR_JUNK, 12, 700, { NULL }, S_NULL }, //1279 /*S_JUNK_13*/ { SPR_JUNK, 13, 700, { NULL }, S_NULL }, //1280 /*S_JUNK_14*/ { SPR_JUNK, 14, 700, { NULL }, S_NULL }, //1281 /*S_JUNK_15*/ { SPR_JUNK, 15, 700, { NULL }, S_NULL }, //1282 /*S_JUNK_16*/ { SPR_JUNK, 16, 700, { NULL }, S_NULL }, //1283 /*S_JUNK_17*/ { SPR_JUNK, 17, 700, { NULL }, S_NULL }, //1284 /*S_JUNK_18*/ { SPR_JUNK, 18, 700, { NULL }, S_NULL }, //1285 /*S_JUNK_19*/ { SPR_JUNK, 19, 700, { NULL }, S_NULL }, //1286 /*S_FFOT_00*/ { SPR_FFOT, 0, 9, { NULL }, S_FFOT_01 }, //1287 /*S_FFOT_01*/ { SPR_FFOT, 1, 9, { NULL }, S_FFOT_02 }, //1288 /*S_FFOT_02*/ { SPR_FFOT, 2, 9, { NULL }, S_FFOT_03 }, //1289 /*S_FFOT_03*/ { SPR_FFOT, 3, 9, { NULL }, S_NULL }, //1290 /*S_DIE1_00*/ { SPR_DIE1, 0, -1, { NULL }, S_NULL }, //1291 /*S_BEAC_00*/ { SPR_BEAC, 0, -1, { NULL }, S_NULL }, //1292 /*S_BEAC_01*/ { SPR_BEAC, 0, 30, { NULL }, S_BEAC_02 }, //1293 /*S_BEAC_02*/ { SPR_BEAC, 0, 160, { A_TeleportBeacon }, S_BEAC_01 }, //1294 /*S_ARM1_00*/ { SPR_ARM1, 0, -1, { NULL }, S_NULL }, //1295 /*S_ARM2_00*/ { SPR_ARM2, 0, -1, { NULL }, S_NULL }, //1296 /*S_BARW_00*/ { SPR_BARW, 0, -1, { NULL }, S_NULL }, //1297 /*S_BARW_01*/ { SPR_BARW, 1, 2, { A_Scream }, S_BARW_02 }, //1298 /*S_BARW_02*/ { SPR_BARW, 2, 2, { NULL }, S_BARW_03 }, //1299 /*S_BARW_03*/ { SPR_BARW, 3, 2, { A_Fall }, S_BARW_04 }, //1300 /*S_BARW_04*/ { SPR_BARW, 4, 2, { NULL }, S_BARW_05 }, //1301 /*S_BARW_05*/ { SPR_BARW, 5, 2, { NULL }, S_BARW_06 }, //1302 /*S_BARW_06*/ { SPR_BARW, 6, 2, { NULL }, S_BARW_07 }, //1303 /*S_BARW_07*/ { SPR_BARW, 7, -1, { NULL }, S_NULL }, //1304 /*S_BART_00*/ { SPR_BART, 0, -1, { NULL }, S_NULL }, //1305 /*S_BART_01*/ { SPR_BART, 32769, 2, { A_Scream }, S_BART_02 }, //1306 /*S_BART_02*/ { SPR_BART, 32770, 2, { NULL }, S_BART_03 }, //1307 /*S_BART_03*/ { SPR_BART, 32771, 2, { NULL }, S_BART_04 }, //1308 /*S_BART_04*/ { SPR_BART, 32772, 2, { A_Fall }, S_BART_05 }, //1309 /*S_BART_05*/ { SPR_BART, 32773, 2, { A_DeathExplode2 }, S_BART_06 }, //1310 /*S_BART_06*/ { SPR_BART, 32774, 2, { NULL }, S_BART_07 }, //1311 /*S_BART_07*/ { SPR_BART, 32775, 2, { NULL }, S_BART_08 }, //1312 /*S_BART_08*/ { SPR_BART, 32776, 2, { NULL }, S_BART_09 }, //1313 /*S_BART_09*/ { SPR_BART, 32777, 3, { NULL }, S_BART_10 }, //1314 /*S_BART_10*/ { SPR_BART, 32778, 3, { NULL }, S_BART_11 }, //1315 /*S_BART_11*/ { SPR_BART, 11, -1, { NULL }, S_NULL }, //1316 /*S_LAMP_00*/ { SPR_LAMP, 0, -1, { NULL }, S_NULL }, //1317 /*S_LANT_00*/ { SPR_LANT, 0, -1, { NULL }, S_NULL }, //1318 /*S_BARL_00*/ { SPR_BARL, 32768, 4, { NULL }, S_BARL_01 }, //1319 /*S_BARL_01*/ { SPR_BARL, 32769, 4, { NULL }, S_BARL_02 }, //1320 /*S_BARL_02*/ { SPR_BARL, 32770, 4, { NULL }, S_BARL_03 }, //1321 /*S_BARL_03*/ { SPR_BARL, 32771, 4, { NULL }, S_BARL_00 }, //1322 /*S_BOWL_00*/ { SPR_BOWL, 32768, 4, { A_ActiveSound }, S_BOWL_01 }, //1323 /*S_BOWL_01*/ { SPR_BOWL, 32769, 4, { NULL }, S_BOWL_02 }, //1324 /*S_BOWL_02*/ { SPR_BOWL, 32770, 4, { NULL }, S_BOWL_03 }, //1325 /*S_BOWL_03*/ { SPR_BOWL, 32771, 4, { NULL }, S_BOWL_00 }, //1326 /*S_BRAZ_00*/ { SPR_BRAZ, 32768, 4, { A_ActiveSound }, S_BRAZ_01 }, //1327 /*S_BRAZ_01*/ { SPR_BRAZ, 32769, 4, { NULL }, S_BRAZ_02 }, //1328 /*S_BRAZ_02*/ { SPR_BRAZ, 32770, 4, { NULL }, S_BRAZ_03 }, //1329 /*S_BRAZ_03*/ { SPR_BRAZ, 32771, 4, { NULL }, S_BRAZ_00 }, //1330 /*S_TRCH_00*/ { SPR_TRCH, 32768, 4, { A_ActiveSound }, S_TRCH_01 }, //1331 /*S_TRCH_01*/ { SPR_TRCH, 32769, 4, { NULL }, S_TRCH_02 }, //1332 /*S_TRCH_02*/ { SPR_TRCH, 32770, 4, { NULL }, S_TRCH_03 }, //1333 /*S_TRCH_03*/ { SPR_TRCH, 32771, 4, { NULL }, S_TRCH_00 }, //1334 /*S_LTRH_00*/ { SPR_LTRH, 32768, 4, { NULL }, S_LTRH_01 }, //1335 /*S_LTRH_01*/ { SPR_LTRH, 32769, 4, { NULL }, S_LTRH_02 }, //1336 /*S_LTRH_02*/ { SPR_LTRH, 32770, 4, { NULL }, S_LTRH_03 }, //1337 /*S_LTRH_03*/ { SPR_LTRH, 32771, 4, { NULL }, S_LTRH_00 }, //1338 /*S_LMPC_00*/ { SPR_LMPC, 32768, 4, { A_ActiveSound }, S_LMPC_01 }, //1339 /*S_LMPC_01*/ { SPR_LMPC, 32769, 4, { NULL }, S_LMPC_02 }, //1340 /*S_LMPC_02*/ { SPR_LMPC, 32770, 4, { NULL }, S_LMPC_03 }, //1341 /*S_LMPC_03*/ { SPR_LMPC, 32771, 4, { NULL }, S_LMPC_00 }, //1342 /*S_LOGS_00*/ { SPR_LOGS, 32768, 4, { A_ActiveSound }, S_LOGS_01 }, //1343 /*S_LOGS_01*/ { SPR_LOGS, 32769, 4, { NULL }, S_LOGS_02 }, //1344 /*S_LOGS_02*/ { SPR_LOGS, 32770, 4, { NULL }, S_LOGS_03 }, //1345 /*S_LOGS_03*/ { SPR_LOGS, 32771, 4, { NULL }, S_LOGS_00 }, //1346 /*S_TRHO_00*/ { SPR_TRHO, 0, -1, { NULL }, S_NULL }, //1347 /*S_WATR_00*/ { SPR_WATR, 0, -1, { NULL }, S_NULL }, //1348 /*S_MUGG_00*/ { SPR_MUGG, 0, -1, { NULL }, S_NULL }, //1349 /*S_FUSL_00*/ { SPR_FUSL, 0, -1, { NULL }, S_NULL }, //1350 /*S_CRD1_00*/ { SPR_CRD1, 0, -1, { NULL }, S_NULL }, //1351 /*S_CRD2_00*/ { SPR_CRD2, 0, -1, { NULL }, S_NULL }, //1352 /*S_TPAS_00*/ { SPR_TPAS, 0, -1, { NULL }, S_NULL }, //1353 /*S_KY1G_00*/ { SPR_KY1G, 0, -1, { NULL }, S_NULL }, //1354 /*S_KY2S_00*/ { SPR_KY2S, 0, -1, { NULL }, S_NULL }, //1355 /*S_KY3B_00*/ { SPR_KY3B, 0, -1, { NULL }, S_NULL }, //1356 /*S_HAND_00*/ { SPR_HAND, 0, -1, { NULL }, S_NULL }, //1357 /*S_CRYS_00*/ { SPR_CRYS, 0, 16, { A_ActiveSound }, S_CRYS_01 }, //1358 /*S_CRYS_01*/ { SPR_CRYS, 1, 5, { A_ActiveSound }, S_CRYS_02 }, //1359 /*S_CRYS_02*/ { SPR_CRYS, 2, 4, { A_ActiveSound }, S_CRYS_03 }, //1360 /*S_CRYS_03*/ { SPR_CRYS, 3, 4, { A_ActiveSound }, S_CRYS_04 }, //1361 /*S_CRYS_04*/ { SPR_CRYS, 4, 4, { A_ActiveSound }, S_CRYS_05 }, //1362 /*S_CRYS_05*/ { SPR_CRYS, 5, 4, { A_ActiveSound }, S_CRYS_00 }, //1363 /*S_PRIS_00*/ { SPR_PRIS, 0, -1, { NULL }, S_NULL }, //1364 /*S_PWR1_00*/ { SPR_PWR1, 0, -1, { NULL }, S_NULL }, //1365 /*S_PWR2_00*/ { SPR_PWR2, 0, -1, { NULL }, S_NULL }, //1366 /*S_PWR3_00*/ { SPR_PWR3, 0, -1, { NULL }, S_NULL }, //1367 /*S_ORAC_00*/ { SPR_ORAC, 0, -1, { NULL }, S_NULL }, //1368 /*S_GYID_00*/ { SPR_GYID, 0, -1, { NULL }, S_NULL }, //1369 /*S_FUBR_00*/ { SPR_FUBR, 0, -1, { NULL }, S_NULL }, //1370 /*S_WARE_00*/ { SPR_WARE, 0, -1, { NULL }, S_NULL }, //1371 /*S_RCRY_00*/ { SPR_RCRY, 32768, -1, { NULL }, S_NULL }, //1372 /*S_BCRY_00*/ { SPR_BCRY, 32768, -1, { NULL }, S_NULL }, //1373 /*S_CHAP_00*/ { SPR_CHAP, 0, -1, { NULL }, S_NULL }, //1374 /*S_TUNL_00*/ { SPR_TUNL, 0, -1, { NULL }, S_NULL }, //1375 /*S_BLTK_00*/ { SPR_BLTK, 0, -1, { NULL }, S_NULL }, //1376 /*S_SECK_00*/ { SPR_SECK, 0, -1, { NULL }, S_NULL }, //1377 /*S_MINE_00*/ { SPR_MINE, 0, -1, { NULL }, S_NULL }, //1378 /*S_REBL_00*/ { SPR_REBL, 0, -1, { NULL }, S_NULL }, //1379 /*S_PROC_00*/ { SPR_PROC, 0, -1, { NULL }, S_NULL }, //1380 /*S_ANKH_00*/ { SPR_ANKH, 0, -1, { NULL }, S_NULL }, //1381 /*S_GOID_00*/ { SPR_GOID, 0, -1, { NULL }, S_NULL }, //1382 /*S_STMP_00*/ { SPR_STMP, 0, -1, { NULL }, S_NULL }, //1383 /*S_MDKT_00*/ { SPR_MDKT, 0, -1, { NULL }, S_NULL }, //1384 /*S_COIN_00*/ { SPR_COIN, 0, -1, { NULL }, S_NULL }, //1385 /*S_CRED_00*/ { SPR_CRED, 0, -1, { NULL }, S_NULL }, //1386 /*S_SACK_00*/ { SPR_SACK, 0, -1, { NULL }, S_NULL }, //1387 /*S_CHST_00*/ { SPR_CHST, 0, -1, { NULL }, S_NULL }, //1388 /*S_SHD1_00*/ { SPR_SHD1, 0, 17, { A_ShadowOff }, S_SHD1_01 }, //1389 /*S_SHD1_01*/ { SPR_SHD1, 0, 17, { A_ModifyVisibility }, S_SHD1_02 }, //1390 /*S_SHD1_02*/ { SPR_SHD1, 0, 17, { A_ShadowOn }, S_SHD1_03 }, //1391 /*S_SHD1_03*/ { SPR_SHD1, 0, 17, { A_ModifyVisibility }, S_SHD1_00 }, //1392 /*S_MASK_00*/ { SPR_MASK, 0, -1, { NULL }, S_NULL }, //1393 /*S_UNIF_00*/ { SPR_UNIF, 0, -1, { NULL }, S_NULL }, //1394 /*S_OFIC_00*/ { SPR_OFIC, 0, -1, { NULL }, S_NULL }, //1395 /*S_PMAP_00*/ { SPR_PMAP, 32768, 6, { NULL }, S_PMAP_01 }, //1396 /*S_PMAP_01*/ { SPR_PMAP, 32769, 6, { NULL }, S_PMAP_00 }, //1397 /*S_PMUP_00*/ { SPR_PMUP, 32768, 6, { NULL }, S_PMUP_01 }, //1398 /*S_PMUP_01*/ { SPR_PMUP, 32769, 6, { NULL }, S_PMUP_00 }, //1399 /*S_BLIT_00*/ { SPR_BLIT, 0, -1, { NULL }, S_NULL }, //1400 /*S_BBOX_00*/ { SPR_BBOX, 0, -1, { NULL }, S_NULL }, //1401 /*S_MSSL_00*/ { SPR_MSSL, 0, -1, { NULL }, S_NULL }, //1402 /*S_ROKT_00*/ { SPR_ROKT, 0, -1, { NULL }, S_NULL }, //1403 /*S_BRY1_00*/ { SPR_BRY1, 0, 6, { NULL }, S_BRY1_01 }, //1404 /*S_BRY1_01*/ { SPR_BRY1, 1, 6, { NULL }, S_BRY1_00 }, //1405 /*S_CPAC_00*/ { SPR_CPAC, 0, 6, { NULL }, S_CPAC_01 }, //1406 /*S_CPAC_01*/ { SPR_CPAC, 1, 6, { NULL }, S_CPAC_00 }, //1407 /*S_PQRL_00*/ { SPR_PQRL, 0, -1, { NULL }, S_NULL }, //1408 /*S_XQRL_00*/ { SPR_XQRL, 0, -1, { NULL }, S_NULL }, //1409 /*S_GRN1_00*/ { SPR_GRN1, 0, -1, { NULL }, S_NULL }, //1410 /*S_GRN2_00*/ { SPR_GRN2, 0, -1, { NULL }, S_NULL }, //1411 /*S_BKPK_00*/ { SPR_BKPK, 0, -1, { NULL }, S_NULL }, //1412 /*S_RELC_00*/ { SPR_RELC, 32768, -1, { NULL }, S_NULL }, //1413 /*S_RIFL_00*/ { SPR_RIFL, 0, -1, { NULL }, S_NULL }, //1414 /*S_RIFL_01*/ { SPR_RIFL, 1, -1, { NULL }, S_NULL }, //1415 /*S_FLAM_00*/ { SPR_FLAM, 0, -1, { NULL }, S_NULL }, //1416 /*S_BFLM_00*/ { SPR_BFLM, 0, -1, { NULL }, S_NULL }, //1417 /*S_MMSL_00*/ { SPR_MMSL, 0, -1, { NULL }, S_NULL }, //1418 /*S_TRPD_00*/ { SPR_TRPD, 0, -1, { NULL }, S_NULL }, //1419 /*S_GRND_00*/ { SPR_GRND, 0, -1, { NULL }, S_NULL }, //1420 /*S_CBOW_00*/ { SPR_CBOW, 0, -1, { NULL }, S_NULL }, //1421 /*S_SIGL_00*/ { SPR_SIGL, 0, -1, { NULL }, S_NULL }, //1422 /*S_SIGL_01*/ { SPR_SIGL, 1, -1, { NULL }, S_NULL }, //1423 /*S_SIGL_02*/ { SPR_SIGL, 2, -1, { NULL }, S_NULL }, //1424 /*S_SIGL_03*/ { SPR_SIGL, 3, -1, { NULL }, S_NULL }, //1425 /*S_SIGL_04*/ { SPR_SIGL, 4, -1, { NULL }, S_NULL }, //1426 /*S_LITE_00*/ { SPR_LITE, 32768, -1, { NULL }, S_NULL }, //1427 /*S_CNDL_00*/ { SPR_CNDL, 32768, -1, { NULL }, S_NULL }, //1428 /*S_CLBR_00*/ { SPR_CLBR, 32768, -1, { NULL }, S_NULL }, //1429 /*S_LITS_00*/ { SPR_LITS, 32768, -1, { NULL }, S_NULL }, //1430 /*S_LITB_00*/ { SPR_LITB, 32768, -1, { NULL }, S_NULL }, //1431 /*S_LITG_00*/ { SPR_LITG, 32768, -1, { NULL }, S_NULL }, //1432 /*S_ROK1_00*/ { SPR_ROK1, 0, -1, { NULL }, S_NULL }, //1433 /*S_ROK2_00*/ { SPR_ROK2, 0, -1, { NULL }, S_NULL }, //1434 /*S_ROK3_00*/ { SPR_ROK3, 0, -1, { NULL }, S_NULL }, //1435 /*S_ROK4_00*/ { SPR_ROK4, 0, -1, { NULL }, S_NULL }, //1436 /*S_LOGG_00*/ { SPR_LOGG, 0, 5, { A_ActiveSound }, S_LOGG_01 }, //1437 /*S_LOGG_01*/ { SPR_LOGG, 1, 5, { A_ActiveSound }, S_LOGG_02 }, //1438 /*S_LOGG_02*/ { SPR_LOGG, 2, 5, { A_ActiveSound }, S_LOGG_03 }, //1439 /*S_LOGG_03*/ { SPR_LOGG, 3, 5, { A_ActiveSound }, S_LOGG_00 }, //1440 /*S_RUB1_00*/ { SPR_RUB1, 0, -1, { NULL }, S_NULL }, //1441 /*S_RUB2_00*/ { SPR_RUB2, 0, -1, { NULL }, S_NULL }, //1442 /*S_RUB3_00*/ { SPR_RUB3, 0, -1, { NULL }, S_NULL }, //1443 /*S_RUB4_00*/ { SPR_RUB4, 0, -1, { NULL }, S_NULL }, //1444 /*S_RUB5_00*/ { SPR_RUB5, 0, -1, { NULL }, S_NULL }, //1445 /*S_RUB6_00*/ { SPR_RUB6, 0, -1, { NULL }, S_NULL }, //1446 /*S_RUB7_00*/ { SPR_RUB7, 0, -1, { NULL }, S_NULL }, //1447 /*S_RUB8_00*/ { SPR_RUB8, 0, -1, { NULL }, S_NULL }, //1448 /*S_CHAN_00*/ { SPR_CHAN, 0, -1, { NULL }, S_NULL }, //1449 /*S_STAT_00*/ { SPR_STAT, 0, -1, { NULL }, S_NULL }, //1450 /*S_DSTA_00*/ { SPR_DSTA, 0, -1, { NULL }, S_NULL }, //1451 /*S_CRAB_00*/ { SPR_CRAB, 0, -1, { NULL }, S_NULL }, //1452 /*S_CAGE_00*/ { SPR_CAGE, 0, -1, { NULL }, S_NULL }, //1453 /*S_TREE_00*/ { SPR_TREE, 0, -1, { NULL }, S_NULL }, //1454 /*S_TREE_01*/ { SPR_TREE, 1, -1, { NULL }, S_NULL }, //1455 /*S_TREE_02*/ { SPR_TREE, 2, -1, { NULL }, S_NULL }, //1456 /*S_TRE1_00*/ { SPR_TRE1, 0, -1, { NULL }, S_NULL }, //1457 /*S_BUSH_00*/ { SPR_BUSH, 0, -1, { NULL }, S_NULL }, //1458 /*S_SHRB_00*/ { SPR_SHRB, 0, -1, { NULL }, S_NULL }, //1459 /*S_STAK_00*/ { SPR_STAK, 0, -1, { NULL }, S_NULL }, //1460 /*S_BAR1_00*/ { SPR_BAR1, 0, -1, { NULL }, S_NULL }, //1461 /*S_VASE_00*/ { SPR_VASE, 0, -1, { NULL }, S_NULL }, //1462 /*S_VASE_01*/ { SPR_VASE, 1, -1, { NULL }, S_NULL }, //1463 /*S_STOL_00*/ { SPR_STOL, 0, -1, { NULL }, S_NULL }, //1464 /*S_POT1_00*/ { SPR_POT1, 0, -1, { NULL }, S_NULL }, //1465 /*S_TUB1_00*/ { SPR_TUB1, 0, -1, { NULL }, S_NULL }, //1466 /*S_ANVL_00*/ { SPR_ANVL, 0, -1, { NULL }, S_NULL }, //1467 /*S_TLMP_00*/ { SPR_TLMP, 0, -1, { NULL }, S_NULL }, //1468 /*S_TLMP_01*/ { SPR_TLMP, 1, -1, { NULL }, S_NULL }, //1469 /*S_TRAY_00*/ { SPR_TRAY, 0, -1, { NULL }, S_NULL }, //1470 /*S_APOW_00*/ { SPR_APOW, 0, 4, { A_ActiveSound }, S_APOW_00 }, //1471 /*S_AFED_00*/ { SPR_AFED, 0, -1, { NULL }, S_NULL }, //1472 /*S_DRIP_00*/ { SPR_DRIP, 0, 6, { A_ActiveSound }, S_DRIP_01 }, //1473 /*S_DRIP_01*/ { SPR_DRIP, 1, 4, { NULL }, S_DRIP_02 }, //1474 /*S_DRIP_02*/ { SPR_DRIP, 2, 4, { NULL }, S_DRIP_03 }, //1475 /*S_DRIP_03*/ { SPR_DRIP, 3, 4, { A_ActiveSound }, S_DRIP_04 }, //1476 /*S_DRIP_04*/ { SPR_DRIP, 4, 4, { NULL }, S_DRIP_05 }, //1477 /*S_DRIP_05*/ { SPR_DRIP, 5, 4, { NULL }, S_DRIP_06 }, //1478 /*S_DRIP_06*/ { SPR_DRIP, 6, 4, { A_ActiveSound }, S_DRIP_07 }, //1479 /*S_DRIP_07*/ { SPR_DRIP, 7, 4, { NULL }, S_DRIP_00 }, //1480 /*S_CDRP_00*/ { SPR_CDRP, 0, 10, { NULL }, S_CDRP_01 }, //1481 /*S_CDRP_01*/ { SPR_CDRP, 1, 8, { NULL }, S_CDRP_02 }, //1482 /*S_CDRP_02*/ { SPR_CDRP, 2, 8, { NULL }, S_CDRP_03 }, //1483 /*S_CDRP_03*/ { SPR_CDRP, 3, 8, { NULL }, S_CDRP_00 }, //1484 /*S_SPLH_00*/ { SPR_SPLH, 0, 4, { NULL }, S_SPLH_01 }, //1485 /*S_SPLH_01*/ { SPR_SPLH, 1, 4, { NULL }, S_SPLH_02 }, //1486 /*S_SPLH_02*/ { SPR_SPLH, 2, 4, { NULL }, S_SPLH_03 }, //1487 /*S_SPLH_03*/ { SPR_SPLH, 3, 8, { NULL }, S_SPLH_04 }, //1488 /*S_SPLH_04*/ { SPR_SPLH, 4, 4, { NULL }, S_SPLH_05 }, //1489 /*S_SPLH_05*/ { SPR_SPLH, 5, 4, { NULL }, S_SPLH_06 }, //1490 /*S_SPLH_06*/ { SPR_SPLH, 6, 4, { NULL }, S_SPLH_07 }, //1491 /*S_SPLH_07*/ { SPR_SPLH, 7, 4, { A_ActiveSound }, S_SPLH_00 }, //1492 /*S_WTFT_00*/ { SPR_WTFT, 0, 4, { NULL }, S_WTFT_01 }, //1493 /*S_WTFT_01*/ { SPR_WTFT, 1, 4, { NULL }, S_WTFT_02 }, //1494 /*S_WTFT_02*/ { SPR_WTFT, 2, 4, { NULL }, S_WTFT_03 }, //1495 /*S_WTFT_03*/ { SPR_WTFT, 3, 4, { A_ActiveSound }, S_WTFT_00 }, //1496 /*S_HERT_00*/ { SPR_HERT, 32768, 4, { NULL }, S_HERT_01 }, //1497 /*S_HERT_01*/ { SPR_HERT, 32769, 4, { NULL }, S_HERT_02 }, //1498 /*S_HERT_02*/ { SPR_HERT, 32770, 4, { NULL }, S_HERT_00 }, //1499 /*S_TELP_00*/ { SPR_TELP, 32768, 3, { NULL }, S_TELP_01 }, //1500 /*S_TELP_01*/ { SPR_TELP, 32769, 3, { NULL }, S_TELP_02 }, //1501 /*S_TELP_02*/ { SPR_TELP, 32770, 3, { NULL }, S_TELP_03 }, //1502 /*S_TELP_03*/ { SPR_TELP, 32771, 3, { NULL }, S_TELP_00 }, //1503 /*S_MONI_00*/ { SPR_MONI, 0, -1, { NULL }, S_NULL }, //1504 /*S_STEL_00*/ { SPR_STEL, 0, -1, { NULL }, S_NULL }, //1505 /*S_STLA_00*/ { SPR_STLA, 0, -1, { NULL }, S_NULL }, //1506 /*S_STLE_00*/ { SPR_STLE, 0, -1, { NULL }, S_NULL }, //1507 /*S_HUGE_00*/ { SPR_HUGE, 0, 4, { NULL }, S_HUGE_01 }, //1508 /*S_HUGE_01*/ { SPR_HUGE, 1, 5, { NULL }, S_HUGE_02 }, //1509 /*S_HUGE_02*/ { SPR_HUGE, 2, 5, { NULL }, S_HUGE_03 }, //1510 /*S_HUGE_03*/ { SPR_HUGE, 3, 5, { NULL }, S_HUGE_00 }, //1511 /*S_STLG_00*/ { SPR_STLG, 0, -1, { NULL }, S_NULL }, //1512 /*S_STLG_01*/ { SPR_STLG, 1, -1, { NULL }, S_NULL }, //1513 /*S_STLG_02*/ { SPR_STLG, 2, -1, { NULL }, S_NULL }, //1514 /*S_STLG_03*/ { SPR_STLG, 3, -1, { NULL }, S_NULL }, //1515 /*S_STLG_04*/ { SPR_STLG, 4, -1, { NULL }, S_NULL }, //1516 /*S_STLG_05*/ { SPR_STLG, 5, -1, { NULL }, S_NULL }, //1517 }; // villsa [STRIFE] mobjinfo_t mobjinfo[NUMMOBJTYPES] = { { /*MT_FIELDGUARD*/ 25, //doomednum S_TOKN_00, //spawnstate 10, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_XPRK_00, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 2*FRACUNIT, //radius 1*FRACUNIT, //height 10000, //mass 0, //damage sfx_None, //activesound MF_SHOOTABLE|MF_NOSECTOR|MF_NODIALOG, //flags NULL, //namepointer }, { /*MT_PLAYER*/ -1, //doomednum S_PLAY_00, //spawnstate 100, //spawnhealth S_PLAY_01, //seestate sfx_None, //seesound 0, //reactiontime sfx_None, //attacksound S_PLAY_07, //painstate 255, //painchance sfx_plpain, //painsound S_NULL, //meleestate S_PLAY_05, //missilestate S_NULL, //crashstate S_PLAY_09, //deathstate S_RGIB_00, //xdeathstate sfx_pldeth, //deathsound 0, //speed 18*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_NOTDMATCH|MF_ALLY, //flags NULL, //namepointer }, { /*MT_SHOPKEEPER_W*/ 116, //doomednum S_MRST_00, //spawnstate 10000000, //spawnhealth S_MRPN_00, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_MRPN_00, //painstate 150, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 5000, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOTDMATCH, //flags "Weapon_Smith", //namepointer }, { /*MT_SHOPKEEPER_B*/ 72, //doomednum S_MRST_00, //spawnstate 10000000, //spawnhealth S_MRPN_00, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_MRPN_00, //painstate 150, //painchance sfx_ambbar, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 5000, //mass 0, //damage sfx_ambppl, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOTDMATCH|MF_COLORSWAP1|MF_COLORSWAP3, //flags "Bar_Keep", //namepointer }, { /*MT_SHOPKEEPER_A*/ 73, //doomednum S_MRST_00, //spawnstate 10000000, //spawnhealth S_MRPN_00, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_MRPN_00, //painstate 150, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 5000, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOTDMATCH|MF_COLORSWAP2|MF_COLORSWAP3, //flags "Armorer", //namepointer }, { /*MT_SHOPKEEPER_M*/ 74, //doomednum S_MRST_00, //spawnstate 10000000, //spawnhealth S_MRPN_00, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_MRPN_00, //painstate 150, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 50000, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOTDMATCH|MF_COLORSWAP1|MF_COLORSWAP2 |MF_COLORSWAP3, //flags "Medic", //namepointer }, { /*MT_PEASANT2_A*/ 3004, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 4, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_PEASANT2_B*/ 130, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 5, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_PEASANT2_C*/ 131, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 5, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_PEASANT5_A*/ 65, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 7, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1, //flags NULL, //namepointer }, { /*MT_PEASANT5_B*/ 132, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 7, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1, //flags NULL, //namepointer }, { /*MT_PEASANT5_C*/ 133, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 7, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1, //flags NULL, //namepointer }, { /*MT_PEASANT4_A*/ 66, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1 |MF_COLORSWAP2, //flags NULL, //namepointer }, { /*MT_PEASANT4_B*/ 134, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1 |MF_COLORSWAP2, //flags NULL, //namepointer }, { /*MT_PEASANT4_C*/ 135, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1 |MF_COLORSWAP2, //flags NULL, //namepointer }, { /*MT_PEASANT6_A*/ 67, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP2, //flags NULL, //namepointer }, { /*MT_PEASANT6_B*/ 136, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 7, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP2, //flags NULL, //namepointer }, { /*MT_PEASANT6_C*/ 137, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP2, //flags NULL, //namepointer }, { /*MT_PEASANT3_A*/ 172, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT3_B*/ 173, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT3_C*/ 174, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT8_A*/ 175, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP2 |MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT8_B*/ 176, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP2 |MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT8_C*/ 177, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP2 |MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT7_A*/ 178, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1 |MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT7_B*/ 179, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1 |MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT7_C*/ 180, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1 |MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_PEASANT1*/ 181, //doomednum S_PEAS_00, //spawnstate 31, //spawnhealth S_PEAS_01, //seestate sfx_rebact, //seesound 8, //reactiontime sfx_meatht, //attacksound S_PEAS_12, //painstate 200, //painchance sfx_pespna, //painsound S_PEAS_09, //meleestate S_NULL, //missilestate S_PEAS_14, //crashstate S_PEAS_17, //deathstate S_GIBS_00, //xdeathstate sfx_psdtha, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL|MF_COLORSWAP1 |MF_COLORSWAP2|MF_COLORSWAP3, //flags NULL, //namepointer }, { /*MT_ZOMBIE*/ 169, //doomednum S_PEAS_25, //spawnstate 31, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_AGRD_00, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_GIBS_00, //deathstate S_NULL, //xdeathstate sfx_psdtha, //deathsound 0, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP1, //flags NULL, //namepointer }, { /*MT_BECOMING*/ 201, //doomednum S_ARMR_00, //spawnstate 61, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_ARMR_01, //painstate 255, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_GIBS_10, //deathstate S_NULL, //xdeathstate sfx_psdtha, //deathsound 0, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_ZOMBIESPAWNER*/ 170, //doomednum S_PLAY_19, //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_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_telept, //activesound MF_SHOOTABLE|MF_NOSECTOR, //flags NULL, //namepointer }, { /*MT_HUGE_TANK_1*/ 209, //doomednum S_TNK1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 192*FRACUNIT, //height 50000, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_HUGE_TANK_2*/ 210, //doomednum S_TNK2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 192*FRACUNIT, //height 50000, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_HUGE_TANK_3*/ 211, //doomednum S_TNK3_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 192*FRACUNIT, //height 50000, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TANK_4*/ 213, //doomednum S_TNK4_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 56*FRACUNIT, //height 50000, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TANK_5*/ 214, //doomednum S_TNK5_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 56*FRACUNIT, //height 50000, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TANK_6*/ 229, //doomednum S_TNK6_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 56*FRACUNIT, //height 50000, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_KNEELING_GUY*/ 204, //doomednum S_NEAL_00, //spawnstate 51, //spawnhealth S_NEAL_00, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NEAL_02, //painstate 255, //painchance sfx_static, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NEAL_05, //crashstate S_NEAL_07, //deathstate S_NULL, //xdeathstate sfx_static, //deathsound 0, //speed 6*FRACUNIT, //radius 6*FRACUNIT, //height 50000, //mass 0, //damage sfx_chant, //activesound MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_BEGGAR1*/ 141, //doomednum S_BEGR_00, //spawnstate 20, //spawnhealth S_BEGR_01, //seestate sfx_None, //seesound 8, //reactiontime sfx_meatht, //attacksound S_BEGR_11, //painstate 250, //painchance sfx_pespna, //painsound S_BEGR_07, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BEGR_13, //deathstate S_BEGR_22, //xdeathstate sfx_psdtha, //deathsound 3, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags "Beggar", //namepointer }, { /*MT_BEGGAR2*/ 155, //doomednum S_BEGR_00, //spawnstate 20, //spawnhealth S_BEGR_01, //seestate sfx_None, //seesound 8, //reactiontime sfx_meatht, //attacksound S_BEGR_11, //painstate 250, //painchance sfx_pespna, //painsound S_BEGR_07, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BEGR_13, //deathstate S_BEGR_22, //xdeathstate sfx_psdtha, //deathsound 3, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags "Beggar", //namepointer }, { /*MT_BEGGAR3*/ 156, //doomednum S_BEGR_00, //spawnstate 20, //spawnhealth S_BEGR_01, //seestate sfx_None, //seesound 8, //reactiontime sfx_meatht, //attacksound S_BEGR_11, //painstate 250, //painchance sfx_pespna, //painsound S_BEGR_07, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BEGR_13, //deathstate S_BEGR_22, //xdeathstate sfx_psdtha, //deathsound 3, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags "Beggar", //namepointer }, { /*MT_BEGGAR4*/ 157, //doomednum S_BEGR_00, //spawnstate 20, //spawnhealth S_BEGR_01, //seestate sfx_None, //seesound 8, //reactiontime sfx_meatht, //attacksound S_BEGR_11, //painstate 250, //painchance sfx_pespna, //painsound S_BEGR_07, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BEGR_13, //deathstate S_BEGR_22, //xdeathstate sfx_psdtha, //deathsound 3, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags "Beggar", //namepointer }, { /*MT_BEGGAR5*/ 158, //doomednum S_BEGR_00, //spawnstate 20, //spawnhealth S_BEGR_01, //seestate sfx_None, //seesound 8, //reactiontime sfx_meatht, //attacksound S_BEGR_11, //painstate 250, //painchance sfx_pespna, //painsound S_BEGR_07, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BEGR_13, //deathstate S_BEGR_22, //xdeathstate sfx_psdtha, //deathsound 3, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_JUSTHIT|MF_COUNTKILL, //flags "Beggar", //namepointer }, { /*MT_REBEL1*/ 9, //doomednum S_HMN1_00, //spawnstate 60, //spawnhealth S_HMN1_11, //seestate sfx_wpnup, //seesound 8, //reactiontime sfx_None, //attacksound S_HMN1_22, //painstate 250, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_HMN1_19, //missilestate S_NULL, //crashstate S_HMN1_24, //deathstate S_RGIB_08, //xdeathstate sfx_rebdth, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALLY, //flags "Rebel", //namepointer }, { /*MT_REBEL2*/ 144, //doomednum S_HMN1_00, //spawnstate 60, //spawnhealth S_HMN1_11, //seestate sfx_wpnup, //seesound 8, //reactiontime sfx_None, //attacksound S_HMN1_22, //painstate 250, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_HMN1_19, //missilestate S_NULL, //crashstate S_HMN1_24, //deathstate S_RGIB_08, //xdeathstate sfx_rebdth, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALLY, //flags "Rebel", //namepointer }, { /*MT_REBEL3*/ 145, //doomednum S_HMN1_00, //spawnstate 60, //spawnhealth S_HMN1_11, //seestate sfx_wpnup, //seesound 8, //reactiontime sfx_None, //attacksound S_HMN1_22, //painstate 250, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_HMN1_19, //missilestate S_NULL, //crashstate S_HMN1_24, //deathstate S_RGIB_08, //xdeathstate sfx_rebdth, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALLY, //flags "Rebel", //namepointer }, { /*MT_REBEL4*/ 149, //doomednum S_HMN1_00, //spawnstate 60, //spawnhealth S_HMN1_11, //seestate sfx_wpnup, //seesound 8, //reactiontime sfx_None, //attacksound S_HMN1_22, //painstate 250, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_HMN1_19, //missilestate S_NULL, //crashstate S_HMN1_24, //deathstate S_RGIB_08, //xdeathstate sfx_rebdth, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALLY, //flags "Rebel", //namepointer }, { /*MT_REBEL5*/ 150, //doomednum S_HMN1_00, //spawnstate 60, //spawnhealth S_HMN1_11, //seestate sfx_wpnup, //seesound 8, //reactiontime sfx_None, //attacksound S_HMN1_22, //painstate 250, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_HMN1_19, //missilestate S_NULL, //crashstate S_HMN1_24, //deathstate S_RGIB_08, //xdeathstate sfx_rebdth, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALLY, //flags "Rebel", //namepointer }, { /*MT_REBEL6*/ 151, //doomednum S_HMN1_00, //spawnstate 60, //spawnhealth S_HMN1_11, //seestate sfx_wpnup, //seesound 8, //reactiontime sfx_None, //attacksound S_HMN1_22, //painstate 250, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_HMN1_19, //missilestate S_NULL, //crashstate S_HMN1_24, //deathstate S_RGIB_08, //xdeathstate sfx_rebdth, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_ALLY, //flags "Rebel", //namepointer }, { /*MT_RLEADER*/ 64, //doomednum S_LEDR_00, //spawnstate 95, //spawnhealth S_LEAD_04, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_None, //attacksound S_LEAD_15, //painstate 250, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_LEAD_12, //missilestate S_NULL, //crashstate S_LEAD_04, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_NOTDMATCH, //flags "MACIL", //namepointer }, { /*MT_RLEADER2*/ 200, //doomednum S_LEDR_00, //spawnstate 95, //spawnhealth S_LEAD_04, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_None, //attacksound S_LEAD_15, //painstate 200, //painchance sfx_pespna, //painsound S_NULL, //meleestate S_LEAD_17, //missilestate S_NULL, //crashstate S_LEAD_20, //deathstate S_LEAD_20, //xdeathstate sfx_slop, //deathsound 8, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_rebact, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_NOTDMATCH|MF_SPECTRAL, //flags "MACIL", //namepointer }, { /*MT_MISSILESMOKE*/ -1, //doomednum S_PUFY_04, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_rflite, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_SHADOW, //flags NULL, //namepointer }, { /*MT_REAVER*/ 3001, //doomednum S_ROB1_00, //spawnstate 150, //spawnhealth S_ROB1_02, //seestate sfx_revsee, //seesound 8, //reactiontime sfx_None, //attacksound S_ROB1_15, //painstate 128, //painchance sfx_reavpn, //painsound S_ROB1_10, //meleestate S_ROB1_13, //missilestate S_NULL, //crashstate S_ROB1_17, //deathstate S_ROB1_26, //xdeathstate sfx_revdth, //deathsound 12, //speed 20*FRACUNIT, //radius 60*FRACUNIT, //height 500, //mass 0, //damage sfx_revact, //activesound MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_GUARD1*/ 3002, //doomednum S_AGRD_01, //spawnstate 70, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac1, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, //flags "ACOLYTE", //namepointer }, { /*MT_GUARD2*/ 142, //doomednum S_AGRD_01, //spawnstate 70, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac2, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP1, //flags "ACOLYTE", //namepointer }, { /*MT_GUARD3*/ 143, //doomednum S_AGRD_01, //spawnstate 70, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac3, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP2, //flags "ACOLYTE", //namepointer }, { /*MT_GUARD4*/ 146, //doomednum S_AGRD_01, //spawnstate 70, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac1, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP1|MF_COLORSWAP2, //flags "ACOLYTE", //namepointer }, { /*MT_GUARD5*/ 147, //doomednum S_AGRD_01, //spawnstate 70, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac2, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP3, //flags "ACOLYTE", //namepointer }, { /*MT_GUARD6*/ 148, //doomednum S_AGRD_01, //spawnstate 70, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac3, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP1|MF_COLORSWAP3, //flags "ACOLYTE", //namepointer }, { /*MT_GUARD7*/ 232, //doomednum S_AGRD_01, //spawnstate 60, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac3, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP2|MF_COLORSWAP3, //flags "ACOLYTE", //namepointer }, { /*MT_GUARD8*/ 231, //doomednum S_AGRD_01, //spawnstate 60, //spawnhealth S_AGRD_13, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_23, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac3, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL|MF_COLORSWAP1|MF_COLORSWAP2 |MF_COLORSWAP3, //flags "ACOLYTE", //namepointer }, { /*MT_SHADOWGUARD*/ 58, //doomednum S_AGRD_01, //spawnstate 70, //spawnhealth S_AGRD_12, //seestate sfx_agrsee, //seesound 8, //reactiontime sfx_rifle, //attacksound S_AGRD_21, //painstate 150, //painchance sfx_agrdpn, //painsound S_NULL, //meleestate S_AGRD_17, //missilestate S_NULL, //crashstate S_AGRD_24, //deathstate S_GIBS_10, //xdeathstate sfx_agrdth, //deathsound 7, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 400, //mass 0, //damage sfx_agrac2, //activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, //flags "ACOLYTE", //namepointer }, { /*MT_PGUARD*/ 3003, //doomednum S_PGRD_00, //spawnstate 300, //spawnhealth S_PGRD_04, //seestate sfx_pgrsee, //seesound 8, //reactiontime sfx_None, //attacksound S_PGRD_16, //painstate 100, //painchance sfx_pgrdpn, //painsound S_PGRD_12, //meleestate S_PGRD_14, //missilestate S_NULL, //crashstate S_PGRD_18, //deathstate S_NULL, //xdeathstate sfx_pgrdth, //deathsound 8, //speed 20*FRACUNIT, //radius 60*FRACUNIT, //height 500, //mass 0, //damage sfx_pgract, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_COUNTKILL, //flags "TEMPLAR", //namepointer }, { /*MT_CRUSADER*/ 3005, //doomednum S_ROB2_00, //spawnstate 400, //spawnhealth S_ROB2_01, //seestate sfx_rb2see, //seesound 8, //reactiontime sfx_None, //attacksound S_ROB2_19, //painstate 128, //painchance sfx_rb2pn, //painsound S_NULL, //meleestate S_ROB2_09, //missilestate S_NULL, //crashstate S_ROB2_20, //deathstate S_NULL, //xdeathstate sfx_rb2dth, //deathsound 8, //speed 40*FRACUNIT, //radius 56*FRACUNIT, //height 400, //mass 0, //damage sfx_rb2act, //activesound MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_BISHOP*/ 187, //doomednum S_MLDR_00, //spawnstate 500, //spawnhealth S_MLDR_01, //seestate sfx_rb2see, //seesound 8, //reactiontime sfx_None, //attacksound S_MLDR_11, //painstate 128, //painchance sfx_rb2pn, //painsound S_NULL, //meleestate S_MLDR_09, //missilestate S_NULL, //crashstate S_MLDR_12, //deathstate S_NULL, //xdeathstate sfx_pgrdth, //deathsound 8, //speed 40*FRACUNIT, //radius 56*FRACUNIT, //height 500, //mass 0, //damage sfx_rb2act, //activesound MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL |MF_NOTDMATCH, //flags NULL, //namepointer }, { /*MT_ORACLE*/ 199, //doomednum S_ORCL_00, //spawnstate 1, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ORCL_01, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 15*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD|MF_COUNTKILL|MF_NOTDMATCH, //flags "ORACLE", //namepointer }, { /*MT_PRIEST*/ 12, //doomednum S_PRST_00, //spawnstate 800, //spawnhealth S_PRST_02, //seestate sfx_lorsee, //seesound 8, //reactiontime sfx_revbld, //attacksound S_NULL, //painstate 0, //painchance sfx_lorpn, //painsound S_PRST_10, //meleestate S_PRST_13, //missilestate S_NULL, //crashstate S_PDED_00, //deathstate S_NULL, //xdeathstate sfx_slop, //deathsound 10, //speed 15*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_tend, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST|MF_FLOAT |MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL|MF_NOTDMATCH, //flags "PRIEST", //namepointer }, { /*MT_SPECTRE_A*/ 129, //doomednum S_ALN1_00, //spawnstate 1000, //spawnhealth S_ALN1_02, //seestate sfx_alnsee, //seesound 8, //reactiontime sfx_revbld, //attacksound S_ALN1_19, //painstate 250, //painchance sfx_alnpn, //painsound S_ALN1_13, //meleestate S_ALN1_16, //missilestate S_NULL, //crashstate S_AL1P_00, //deathstate S_NULL, //xdeathstate sfx_alndth, //deathsound 12, //speed 64*FRACUNIT, //radius 64*FRACUNIT, //height 1000, //mass 0, //damage sfx_alnact, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH |MF_MVIS|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_NODE*/ -1, //doomednum S_NODE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP|MF_NOTDMATCH, //flags NULL, //namepointer }, { /*MT_SPECTREHEAD*/ -1, //doomednum S_MTHD_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP|MF_NOTDMATCH, //flags NULL, //namepointer }, { /*MT_SPECTRE_B*/ 75, //doomednum S_ALN1_00, //spawnstate 1200, //spawnhealth S_ALN1_02, //seestate sfx_alnsee, //seesound 8, //reactiontime sfx_revbld, //attacksound S_ALN1_19, //painstate 50, //painchance sfx_alnpn, //painsound S_ALN1_13, //meleestate S_ALN1_20, //missilestate S_NULL, //crashstate S_AL1P_00, //deathstate S_NULL, //xdeathstate sfx_alndth, //deathsound 12, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 1000, //mass 0, //damage sfx_alnact, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH |MF_MVIS|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SPECTRE_C*/ 76, //doomednum S_ALN1_23, //spawnstate 1500, //spawnhealth S_ALN1_34, //seestate sfx_alnsee, //seesound 8, //reactiontime sfx_revbld, //attacksound S_ALN1_51, //painstate 50, //painchance sfx_alnpn, //painsound S_ALN1_45, //meleestate S_ALN1_48, //missilestate S_NULL, //crashstate S_AL1P_00, //deathstate S_NULL, //xdeathstate sfx_alndth, //deathsound 12, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 1000, //mass 0, //damage sfx_alnact, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH |MF_MVIS|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SPECTRE_D*/ 167, //doomednum S_ALN1_00, //spawnstate 1700, //spawnhealth S_ALN1_02, //seestate sfx_alnsee, //seesound 8, //reactiontime sfx_revbld, //attacksound S_ALN1_19, //painstate 50, //painchance sfx_alnpn, //painsound S_ALN1_13, //meleestate S_ALN1_52, //missilestate S_NULL, //crashstate S_AL1P_00, //deathstate S_NULL, //xdeathstate sfx_alndth, //deathsound 12, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 1000, //mass 0, //damage sfx_alnact, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH |MF_MVIS|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SPECTRE_E*/ 168, //doomednum S_ALN1_00, //spawnstate 2000, //spawnhealth S_ALN1_02, //seestate sfx_alnsee, //seesound 8, //reactiontime sfx_revbld, //attacksound S_ALN1_19, //painstate 50, //painchance sfx_alnpn, //painsound S_ALN1_13, //meleestate S_ALN1_55, //missilestate S_NULL, //crashstate S_AL1P_00, //deathstate S_NULL, //xdeathstate sfx_alndth, //deathsound 12, //speed 24*FRACUNIT, //radius 64*FRACUNIT, //height 1000, //mass 0, //damage sfx_alnact, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH |MF_MVIS|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_ENTITY*/ 128, //doomednum S_MNAM_00, //spawnstate 2500, //spawnhealth S_MNAL_02, //seestate sfx_mnalse, //seesound 8, //reactiontime sfx_revbld, //attacksound S_MNAL_19, //painstate 255, //painchance sfx_alnpn, //painsound S_MNAL_13, //meleestate S_MNAL_16, //missilestate S_NULL, //crashstate S_MNAL_20, //deathstate S_NULL, //xdeathstate sfx_mnaldt, //deathsound 13, //speed 130*FRACUNIT, //radius 200*FRACUNIT, //height 1000, //mass 0, //damage sfx_alnact, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH |MF_MVIS|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SUBENTITY*/ -1, //doomednum S_MNAL_27, //spawnstate 990, //spawnhealth S_MNAL_28, //seestate sfx_alnsee, //seesound 8, //reactiontime sfx_revbld, //attacksound S_MNAL_40, //painstate 255, //painchance sfx_alnpn, //painsound S_MNAL_34, //meleestate S_MNAL_37, //missilestate S_NULL, //crashstate S_MDTH_00, //deathstate S_NULL, //xdeathstate sfx_alndth, //deathsound 14, //speed 130*FRACUNIT, //radius 200*FRACUNIT, //height 1000, //mass 0, //damage sfx_alnact, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_SHADOW|MF_COUNTKILL|MF_NOTDMATCH |MF_MVIS|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_NEST*/ 26, //doomednum S_NEST_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 84*FRACUNIT, //radius 47*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_NOTDMATCH, //flags NULL, //namepointer }, { /*MT_POD*/ 198, //doomednum S_PODD_00, //spawnstate 1000, //spawnhealth S_PODD_01, //seestate sfx_slop, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 25*FRACUNIT, //radius 91*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_NOTDMATCH, //flags NULL, //namepointer }, { /*MT_SIGIL_B_SHOT*/ -1, //doomednum S_ZAP6_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_00, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 30*FRACUNIT, //speed 8*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 70, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_SB_SHOT*/ -1, //doomednum S_ZAP6_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_00, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 30*FRACUNIT, //speed 8*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 20, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_C_SHOT*/ -1, //doomednum S_ZOT3_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_00, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 30*FRACUNIT, //speed 8*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 70, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_SC_SHOT*/ -1, //doomednum S_ZOT3_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_00, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 30*FRACUNIT, //speed 8*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 20, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_E_OFFSHOOT*/ -1, //doomednum S_ZAP6_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_00, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 30*FRACUNIT, //speed 8*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 10, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_TRAIL*/ -1, //doomednum S_ZAP6_03, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_DROPOFF, //flags NULL, //namepointer }, { /*MT_SIGIL_E_SHOT*/ -1, //doomednum S_ZAP7_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_02, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 18*FRACUNIT, //speed 20*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 130, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_SE_SHOT*/ -1, //doomednum S_ZAP7_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_02, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 18*FRACUNIT, //speed 20*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 30, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_A_ZAP_LEFT*/ -1, //doomednum S_ZOT1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_06, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 22*FRACUNIT, //speed 8*FRACUNIT, //radius 24*FRACUNIT, //height 100, //mass 100, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_A_ZAP_RIGHT*/ -1, //doomednum S_ZOT1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_06, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 22*FRACUNIT, //speed 8*FRACUNIT, //radius 24*FRACUNIT, //height 100, //mass 50, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_A_GROUND*/ -1, //doomednum S_ZAP5_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 70, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_01, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 18*FRACUNIT, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_SHADOW, //flags NULL, //namepointer }, { /*MT_SIGIL_D_SHOT*/ -1, //doomednum S_ZOT2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_01, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 28*FRACUNIT, //speed 8*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 120, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SIGIL_SD_SHOT*/ -1, //doomednum S_ZOT2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_sigil, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_01, //deathstate S_NULL, //xdeathstate sfx_sglhit, //deathsound 28*FRACUNIT, //speed 8*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 60, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE|MF_SPECTRAL, //flags NULL, //namepointer }, { /*MT_SENTINEL*/ 3006, //doomednum S_SEWR_00, //spawnstate 100, //spawnhealth S_SEWR_01, //seestate sfx_sntsee, //seesound 8, //reactiontime sfx_None, //attacksound S_SEWR_06, //painstate 255, //painchance sfx_None, //painsound S_NULL, //meleestate S_SEWR_03, //missilestate S_NULL, //crashstate S_SEWR_07, //deathstate S_NULL, //xdeathstate sfx_sntdth, //deathsound 7, //speed 23*FRACUNIT, //radius 53*FRACUNIT, //height 300, //mass 0, //damage sfx_sntact, //activesound MF_SOLID|MF_SHOOTABLE|MF_SPAWNCEILING|MF_NOGRAVITY|MF_GIVEQUEST |MF_FLOAT|MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_STALKER*/ 186, //doomednum S_SPID_00, //spawnstate 80, //spawnhealth S_SPID_03, //seestate sfx_spisit, //seesound 8, //reactiontime sfx_spdatk, //attacksound S_SPID_24, //painstate 40, //painchance sfx_spdatk, //painsound S_SPID_09, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_SPID_25, //deathstate S_NULL, //xdeathstate sfx_spidth, //deathsound 16, //speed 31*FRACUNIT, //radius 25*FRACUNIT, //height 100, //mass 0, //damage sfx_spisit, //activesound MF_SOLID|MF_SHOOTABLE|MF_SPAWNCEILING|MF_NOGRAVITY|MF_DROPOFF |MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_INQUISITOR*/ 16, //doomednum S_ROB3_00, //spawnstate 1000, //spawnhealth S_ROB3_02, //seestate sfx_inqsee, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_ROB3_10, //missilestate S_NULL, //crashstate S_ROB3_20, //deathstate S_NULL, //xdeathstate sfx_inqdth, //deathsound 12, //speed 40*FRACUNIT, //radius 110*FRACUNIT, //height 1000, //mass 0, //damage sfx_inqact, //activesound MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_NOBLOOD|MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_INQARM*/ -1, //doomednum S_RBB3_05, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 25, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOCLIP|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_PROGRAMMER*/ 71, //doomednum S_PRGR_00, //spawnstate 1100, //spawnhealth S_PRGR_02, //seestate sfx_None, //seesound 8, //reactiontime sfx_revbld, //attacksound S_PRGR_18, //painstate 50, //painchance sfx_prgpn, //painsound S_PRGR_10, //meleestate S_PRGR_14, //missilestate S_NULL, //crashstate S_PRGR_20, //deathstate S_NULL, //xdeathstate sfx_rb2dth, //deathsound 26, //speed 45*FRACUNIT, //radius 60*FRACUNIT, //height 800, //mass 4, //damage sfx_progac, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST|MF_FLOAT |MF_NOBLOOD|MF_COUNTKILL|MF_NOTDMATCH, //flags NULL, //namepointer }, { /*MT_PROGRAMMERBASE*/ -1, //doomednum S_BASE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_HOOKSHOT*/ -1, //doomednum S_OCLW_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_chain, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_CCLW_00, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 20*FRACUNIT, //speed 10*FRACUNIT, //radius 14*FRACUNIT, //height 100, //mass 2, //damage sfx_swish, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_CHAINSHOT*/ -1, //doomednum S_TEND_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_tend, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_MINIMISSLE*/ -1, //doomednum S_MICR_00, //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_NULL, //crashstate S_MISL_01, //deathstate S_NULL, //xdeathstate sfx_mislht, //deathsound 20*FRACUNIT, //speed 10*FRACUNIT, //radius 14*FRACUNIT, //height 100, //mass 10, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_C_MISSILE*/ -1, //doomednum S_MICR_00, //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_NULL, //crashstate S_MISL_00, //deathstate S_NULL, //xdeathstate sfx_mislht, //deathsound 20*FRACUNIT, //speed 10*FRACUNIT, //radius 14*FRACUNIT, //height 100, //mass 7, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_SEEKMISSILE*/ -1, //doomednum S_MISS_00, //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_NULL, //crashstate S_MISL_01, //deathstate S_NULL, //xdeathstate sfx_mislht, //deathsound 20*FRACUNIT, //speed 10*FRACUNIT, //radius 14*FRACUNIT, //height 100, //mass 10, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_ELECARROW*/ -1, //doomednum S_AROW_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_swish, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_ZAP1_01, //deathstate S_NULL, //xdeathstate sfx_firxpl, //deathsound 30*FRACUNIT, //speed 10*FRACUNIT, //radius 10*FRACUNIT, //height 100, //mass 10, //damage sfx_swish, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_POISARROW*/ -1, //doomednum S_ARWP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_swish, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_AROW_01, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 30*FRACUNIT, //speed 10*FRACUNIT, //radius 10*FRACUNIT, //height 100, //mass 500, //damage sfx_swish, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_R_LASER*/ -1, //doomednum S_SHT1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_POW1_09, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 40*FRACUNIT, //speed 10*FRACUNIT, //radius 8*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_L_LASER*/ -1, //doomednum S_SHT1_00, //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_NULL, //crashstate S_POW1_05, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 40*FRACUNIT, //speed 10*FRACUNIT, //radius 8*FRACUNIT, //height 100, //mass 1, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_HEGRENADE*/ -1, //doomednum S_GRAP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_phoot, //seesound 30, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BNG4_00, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 15*FRACUNIT, //speed 13*FRACUNIT, //radius 13*FRACUNIT, //height 20, //mass 1, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_PGRENADE*/ -1, //doomednum S_GRIN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_phoot, //seesound 40, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BNG3_08, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 15*FRACUNIT, //speed 13*FRACUNIT, //radius 13*FRACUNIT, //height 20, //mass 1, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_INQGRENADE*/ -1, //doomednum S_UBAM_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_phoot, //seesound 15, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BNG2_00, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 25*FRACUNIT, //speed 13*FRACUNIT, //radius 13*FRACUNIT, //height 15, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_PFLAME*/ -1, //doomednum S_BNG3_09, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 120, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_FLBE_07, //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 NULL, //namepointer }, { /*MT_TORPEDO*/ -1, //doomednum S_TORP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_protfl, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_THIT_00, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 20*FRACUNIT, //speed 13*FRACUNIT, //radius 8*FRACUNIT, //height 100, //mass 1, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_TORPEDOSPREAD*/ -1, //doomednum S_TWAV_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_TWAV_02, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 35*FRACUNIT, //speed 13*FRACUNIT, //radius 13*FRACUNIT, //height 100, //mass 10, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_NOGRAVITY|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_SFIREBALL*/ -1, //doomednum S_FRBL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_flburn, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_FRBL_03, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 15*FRACUNIT, //speed 8*FRACUNIT, //radius 11*FRACUNIT, //height 10, //mass 4, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_C_FLAME*/ -1, //doomednum S_FRBL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_flburn, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_FRBL_03, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 35*FRACUNIT, //speed 8*FRACUNIT, //radius 11*FRACUNIT, //height 50, //mass 1, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_DROPOFF|MF_MISSILE, //flags NULL, //namepointer }, { /*MT_STRIFEPUFF3*/ -1, //doomednum S_SHT2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_STRIFEPUFF*/ -1, //doomednum S_PUFY_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_SHADOW, //flags NULL, //namepointer }, { /*MT_SPARKPUFF*/ -1, //doomednum S_POW3_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_BLOOD_DEATH*/ -1, //doomednum S_SPRY_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TFOG*/ -1, //doomednum S_TFOG_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_SHADOW, //flags NULL, //namepointer }, { /*MT_IFOG*/ -1, //doomednum S_IFOG_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_SHADOW, //flags NULL, //namepointer }, { /*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, //crashstate 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_NOSECTOR|MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_MISC_01*/ 24, //doomednum S_KLAX_00, //spawnstate 1000, //spawnhealth S_KLAX_01, //seestate sfx_None, //seesound 60, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_STAND|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_TURRET*/ 27, //doomednum S_TURT_00, //spawnstate 125, //spawnhealth S_TURT_01, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_TURT_02, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_TURT_02, //missilestate S_NULL, //crashstate S_BALL_00, //deathstate S_NULL, //xdeathstate sfx_mislht, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 10000000, //mass 0, //damage sfx_None, //activesound MF_SHOOTABLE|MF_STAND|MF_SPAWNCEILING|MF_NOGRAVITY|MF_NOBLOOD |MF_COUNTKILL, //flags NULL, //namepointer }, { /*MT_GATE*/ 45, //doomednum S_PSTN_00, //spawnstate 100, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_PSTN_02, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 16, //speed 20*FRACUNIT, //radius 76*FRACUNIT, //height 10000000, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_GIVEQUEST|MF_NODIALOG|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_COMPUTER*/ 182, //doomednum S_SECR_00, //spawnstate 80, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_SECR_04, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 27, //speed 26*FRACUNIT, //radius 128*FRACUNIT, //height 100000, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_GIVEQUEST|MF_NODIALOG|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_INV_MED1*/ 2011, //doomednum S_STMP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 20, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Med_patch", //namepointer }, { /*MT_INV_MED2*/ 2012, //doomednum S_MDKT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 15, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Medical_kit", //namepointer }, { /*MT_INV_MED3*/ 83, //doomednum S_FULL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 5, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Surgery_Kit", //namepointer }, { /*MT_DEGNINORE*/ 59, //doomednum S_XPRK_01, //spawnstate 10, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_XPRK_02, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 0, //speed 16*FRACUNIT, //radius 16*FRACUNIT, //height 10, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD, //flags "Degnin_Ore", //namepointer }, { /*MT_INV_ARMOR2*/ 2019, //doomednum S_ARM1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 3, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Metal_Armor", //namepointer }, { /*MT_INV_ARMOR1*/ 2018, //doomednum S_ARM2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 5, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Leather_Armor", //namepointer }, { /*MT_MISC_22*/ 2014, //doomednum S_WATR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_MISC_11*/ 164, //doomednum S_MUGG_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_KEY_BASE*/ 230, //doomednum S_FUSL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Base_Key", //namepointer }, { /*MT_GOVSKEY*/ -1, //doomednum S_REBL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Govs_Key", //namepointer }, { /*MT_KEY_TRAVEL*/ 185, //doomednum S_TPAS_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Passcard", //namepointer }, { /*MT_KEY_ID_BLUE*/ 184, //doomednum S_CRD1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "ID_Badge", //namepointer }, { /*MT_PRISONKEY*/ -1, //doomednum S_PRIS_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 11, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST|MF_NOTDMATCH, //flags "Prison_Key", //namepointer }, { /*MT_KEY_HAND*/ 91, //doomednum S_HAND_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 12, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST|MF_NOTDMATCH, //flags "Severed_Hand", //namepointer }, { /*MT_POWER1KEY*/ -1, //doomednum S_PWR1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Power1_Key", //namepointer }, { /*MT_POWER2KEY*/ -1, //doomednum S_PWR2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Power2_Key", //namepointer }, { /*MT_POWER3KEY*/ -1, //doomednum S_PWR3_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Power3_Key", //namepointer }, { /*MT_KEY_GOLD*/ 40, //doomednum S_KY1G_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Gold_Key", //namepointer }, { /*MT_KEY_ID_GOLD*/ 13, //doomednum S_CRD2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "ID_Card", //namepointer }, { /*MT_KEY_SILVER*/ 38, //doomednum S_KY2S_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Silver_Key", //namepointer }, { /*MT_KEY_ORACLE*/ 61, //doomednum S_ORAC_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Oracle_Key", //namepointer }, { /*MT_MILITARYID*/ -1, //doomednum S_GYID_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Military_ID", //namepointer }, { /*MT_KEY_ORDER*/ 86, //doomednum S_FUBR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Order_Key", //namepointer }, { /*MT_KEY_WAREHOUSE*/ 166, //doomednum S_WARE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Warehouse_Key", //namepointer }, { /*MT_KEY_BRASS*/ 39, //doomednum S_KY3B_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Brass_Key", //namepointer }, { /*MT_KEY_RED_CRYSTAL*/ 192, //doomednum S_RCRY_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Red_Crystal_Key", //namepointer }, { /*MT_KEY_BLUE_CRYSTAL*/ 193, //doomednum S_BCRY_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Blue_Crystal_Key", //namepointer }, { /*MT_KEY_CHAPEL*/ 195, //doomednum S_CHAP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Chapel_Key", //namepointer }, { /*MT_CATACOMBKEY*/ -1, //doomednum S_TUNL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 28, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST|MF_NOTDMATCH, //flags "Catacomb_Key", //namepointer }, { /*MT_SECURITYKEY*/ -1, //doomednum S_SECK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Security_Key", //namepointer }, { /*MT_KEY_CORE*/ 236, //doomednum S_GOID_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Core_Key", //namepointer }, { /*MT_KEY_MAULER*/ 233, //doomednum S_BLTK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Mauler_Key", //namepointer }, { /*MT_KEY_FACTORY*/ 234, //doomednum S_PROC_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Factory_Key", //namepointer }, { /*MT_KEY_MINE*/ 235, //doomednum S_MINE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "MINE_KEY", //namepointer }, { /*MT_NEWKEY5*/ -1, //doomednum S_BLTK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "New_Key5", //namepointer }, { /*MT_INV_SHADOWARMOR*/ 2024, //doomednum S_SHD1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 2, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Shadow_armor", //namepointer }, { /*MT_INV_SUIT*/ 2025, //doomednum S_MASK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 5, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Environmental_Suit", //namepointer }, { /*MT_QUEST_UNIFORM*/ 90, //doomednum S_UNIF_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 15, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST, //flags "Guard_Uniform", //namepointer }, { /*MT_QUEST_GUARD_UNIFORM*/ 52, //doomednum S_OFIC_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Officer's_Uniform", //namepointer }, { /*MT_INV_SUPERMAP*/ 2026, //doomednum S_PMAP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "map", //namepointer }, { /*MT_INV_RADAR*/ 2027, //doomednum S_PMUP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 1, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "scanner", //namepointer }, { /*MT_BEACON*/ 10, //doomednum S_BEAC_00, //spawnstate 5, //spawnhealth S_BEAC_01, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 16*FRACUNIT, //height 3, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_DROPPED, //flags "Teleporter_Beacon", //namepointer }, { /*MT_INV_TARGETER*/ 207, //doomednum S_TARG_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 5, //mass 0, //damage sfx_None, //activesound MF_SPECIAL, //flags "Targeter", //namepointer }, { /*MT_MONY_1*/ 93, //doomednum S_COIN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 2147483647, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_DROPPED|MF_NOTDMATCH, //flags "coin", //namepointer }, { /*MT_MONY_10*/ 138, //doomednum S_CRED_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_DROPPED|MF_NOTDMATCH, //flags "10_gold", //namepointer }, { /*MT_MONY_25*/ 139, //doomednum S_SACK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_DROPPED|MF_NOTDMATCH, //flags "25_gold", //namepointer }, { /*MT_MONY_50*/ 140, //doomednum S_CHST_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_DROPPED|MF_NOTDMATCH, //flags "50_gold", //namepointer }, { /*MT_MONY_300*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 3, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST|MF_DROPPED, //flags "300_gold", //namepointer }, { /*MT_TOKEN_RING*/ -1, //doomednum S_RING_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 1, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST|MF_NOTDMATCH, //flags "ring", //namepointer }, { /*MT_INV_CHALICE*/ 205, //doomednum S_RELC_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 2, //speed 10*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST|MF_DROPPED, //flags "Offering_Chalice", //namepointer }, { /*MT_TOKEN_EAR*/ -1, //doomednum S_EARS_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 9, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST, //flags "ear", //namepointer }, { /*MT_INV_COMMUNICATOR*/ 206, //doomednum S_COMM_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Communicator", //namepointer }, { /*MT_AGREN*/ 152, //doomednum S_GRN1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "HE-Grenade_Rounds", //namepointer }, { /*MT_APGREN*/ 153, //doomednum S_GRN2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Phosphorus-Grenade_Rounds", //namepointer }, { /*MT_ACLIP*/ 2007, //doomednum S_BLIT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "clip_of_bullets", //namepointer }, { /*MT_AAMMOBOX*/ 2048, //doomednum S_BBOX_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "ammo", //namepointer }, { /*MT_AMINI*/ 2010, //doomednum S_MSSL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "mini_missiles", //namepointer }, { /*MT_AMINIBOX*/ 2046, //doomednum S_ROKT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "crate_of_missiles", //namepointer }, { /*MT_ACELL*/ 2047, //doomednum S_BRY1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "energy_pod", //namepointer }, { /*MT_APCELL*/ 17, //doomednum S_CPAC_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "energy_pack", //namepointer }, { /*MT_APAROW*/ 115, //doomednum S_PQRL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "poison_bolts", //namepointer }, { /*MT_AAROW*/ 114, //doomednum S_XQRL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "electric_bolts", //namepointer }, { /*MT_INV_SATCHEL*/ 183, //doomednum S_BKPK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "ammo_satchel", //namepointer }, { /*MT_PULSE*/ 2002, //doomednum S_RIFL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "assault_gun", //namepointer }, { /*MT_RIFLESTAND*/ 2006, //doomednum S_RIFL_01, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "assault_gun", //namepointer }, { /*MT_FLAMETHROWER*/ 2005, //doomednum S_FLAM_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "flame_thrower", //namepointer }, { /*MT_TOKEN_FLAME_THROWER_PARTS*/ -1, //doomednum S_BFLM_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "flame_thrower_parts", //namepointer }, { /*MT_MISSILELAUNCHER*/ 2003, //doomednum S_MMSL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "mini_missile_launcher", //namepointer }, { /*MT_BLASTER*/ 2004, //doomednum S_TRPD_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "mauler", //namepointer }, { /*MT_CROSSBOW*/ 2001, //doomednum S_CBOW_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "crossbow", //namepointer }, { /*MT_GRENADELAUNCHER*/ 154, //doomednum S_GRND_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Grenade_launcher", //namepointer }, { /*MT_SIGIL_A*/ 77, //doomednum S_SIGL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "SIGIL", //namepointer }, { /*MT_SIGIL_B*/ 78, //doomednum S_SIGL_01, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "SIGIL", //namepointer }, { /*MT_SIGIL_C*/ 79, //doomednum S_SIGL_02, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "SIGIL", //namepointer }, { /*MT_SIGIL_D*/ 80, //doomednum S_SIGL_03, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "SIGIL", //namepointer }, { /*MT_SIGIL_E*/ 81, //doomednum S_SIGL_04, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "SIGIL", //namepointer }, { /*MT_POWER_CRYSTAL*/ 92, //doomednum S_CRYS_00, //spawnstate 50, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BOOM_00, //deathstate S_NULL, //xdeathstate sfx_explod, //deathsound 14, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 99999999, //mass 0, //damage sfx_reactr, //activesound MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_GIVEQUEST|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_RAT*/ 85, //doomednum S_RATT_00, //spawnstate 5, //spawnhealth S_RATT_01, //seestate sfx_ratact, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_RATT_05, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_MEAT_16, //deathstate S_NULL, //xdeathstate sfx_ratact, //deathsound 13, //speed 10*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_ratact, //activesound MF_NODIALOG|MF_NOBLOOD|MF_COUNTKILL, //flags "rat_buddy", //namepointer }, { /*MT_MISC_05*/ 82, //doomednum S_BARW_00, //spawnstate 10, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BARW_01, //deathstate S_NULL, //xdeathstate sfx_wbrldt, //deathsound 0, //speed 10*FRACUNIT, //radius 32*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_MISC_06*/ 94, //doomednum S_BART_00, //spawnstate 30, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_BART_01, //deathstate S_NULL, //xdeathstate sfx_barexp, //deathsound 0, //speed 10*FRACUNIT, //radius 32*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_MISC_15*/ 208, //doomednum S_HOGN_00, //spawnstate 99999999, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_HOGN_01, //painstate 255, //painchance sfx_mtalht, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 10*FRACUNIT, //radius 72*FRACUNIT, //height 9999999, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_NODIALOG|MF_NOBLOOD, //flags NULL, //namepointer }, { /*MT_LIGHT14*/ 95, //doomednum S_LITS_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 4*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_LIGHT13*/ 96, //doomednum S_LITB_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 4*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_LIGHT12*/ 97, //doomednum S_LITG_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 4*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_LIGHT18*/ 2028, //doomednum S_LITE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_PILLAR2*/ 48, //doomednum S_MONI_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 128*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR3*/ 54, //doomednum S_STEL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 128*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR4*/ 55, //doomednum S_STLA_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 80*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR5*/ 56, //doomednum S_STLE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR6*/ 57, //doomednum S_HUGE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 24*FRACUNIT, //radius 192*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR7*/ 227, //doomednum S_APOW_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 24*FRACUNIT, //radius 192*FRACUNIT, //height 100, //mass 0, //damage sfx_amaln2, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_CAVE2*/ 98, //doomednum S_STLG_02, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 54*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_CAVE3*/ 161, //doomednum S_STLG_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_CAVE4*/ 160, //doomednum S_STLG_01, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_CAVE6*/ 159, //doomednum S_STLG_03, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 128*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_CAVE7*/ 162, //doomednum S_STLG_04, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 128*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_CAVE5*/ 163, //doomednum S_STLG_05, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 25*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT2*/ 34, //doomednum S_CNDL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_LIGHT3*/ 35, //doomednum S_CLBR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_03*/ 103, //doomednum S_DRIP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_wdrip, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_MISC_13*/ 104, //doomednum S_SPLH_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_wfall, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_MISC_02*/ 53, //doomednum S_CDRP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 1*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_MISC_07*/ 112, //doomednum S_WTFT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_wsplsh, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_BIO2*/ 113, //doomednum S_HERT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TELEPORTSTAND*/ 23, //doomednum S_TELP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_SHADOW, //flags NULL, //namepointer }, { /*MT_DEADTHING1*/ 22, //doomednum S_ROB2_28, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_DEADTHING2*/ 15, //doomednum S_PLAY_18, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_DEADTHING3*/ 18, //doomednum S_PEAS_24, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_DEADTHING4*/ 21, //doomednum S_AGRD_31, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_DEADTHING5*/ 20, //doomednum S_ROB1_25, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_DEADTHING6*/ 19, //doomednum S_HMN1_31, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_BIO1*/ 212, //doomednum S_SACR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_GIBS*/ 54, //doomednum S_DEAD_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_MISC_04*/ 70, //doomednum S_BARL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 48*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT11*/ 105, //doomednum S_BOWL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_smfire, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT10*/ 106, //doomednum S_BRAZ_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 10*FRACUNIT, //radius 32*FRACUNIT, //height 100, //mass 0, //damage sfx_smfire, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT9*/ 107, //doomednum S_TRCH_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 0*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_smfire, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_LIGHT8*/ 108, //doomednum S_TRHO_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 0*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_MISC_14*/ 109, //doomednum S_CHAN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 93*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_LIGHT1*/ 28, //doomednum S_CAGE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 3*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_PILLAR8*/ 110, //doomednum S_STAT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 64*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR9*/ 44, //doomednum S_DSTA_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT15*/ 111, //doomednum S_LTRH_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 4*FRACUNIT, //radius 72*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT4*/ 43, //doomednum S_LAMP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 3*FRACUNIT, //radius 80*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT5*/ 46, //doomednum S_LANT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 3*FRACUNIT, //radius 80*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_ROCK1*/ 99, //doomednum S_ROK1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_ROCK2*/ 100, //doomednum S_ROK2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_ROCK3*/ 101, //doomednum S_ROK3_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_ROCK4*/ 102, //doomednum S_ROK4_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TREE7*/ 215, //doomednum S_LOGG_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_wriver, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_RUBBLE1*/ 29, //doomednum S_RUB1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_RUBBLE2*/ 30, //doomednum S_RUB2_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_RUBBLE3*/ 31, //doomednum S_RUB3_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_RUBBLE4*/ 32, //doomednum S_RUB4_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_RUBBLE5*/ 36, //doomednum S_RUB5_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_RUBBLE6*/ 37, //doomednum S_RUB6_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_RUBBLE7*/ 41, //doomednum S_RUB7_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_RUBBLE8*/ 42, //doomednum S_RUB8_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_MISC_08*/ 117, //doomednum S_CRAB_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_LIGHT6*/ 47, //doomednum S_LMPC_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 10*FRACUNIT, //radius 72*FRACUNIT, //height 100, //mass 0, //damage sfx_smfire, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT7*/ 50, //doomednum S_LOGS_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 10*FRACUNIT, //radius 80*FRACUNIT, //height 100, //mass 0, //damage sfx_smfire, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TREE2*/ 51, //doomednum S_TREE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 15*FRACUNIT, //radius 109*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TREE3*/ 202, //doomednum S_TREE_01, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 15*FRACUNIT, //radius 109*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TREE4*/ 203, //doomednum S_TREE_02, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 15*FRACUNIT, //radius 64*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TREE1*/ 33, //doomednum S_TRE1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 15*FRACUNIT, //radius 80*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TREE6*/ 60, //doomednum S_BUSH_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 15*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_TREE5*/ 62, //doomednum S_SHRB_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 64*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_CAVE1*/ 63, //doomednum S_STAK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 20*FRACUNIT, //radius 64*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR1*/ 69, //doomednum S_BAR1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 128*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_10*/ 165, //doomednum S_VASE_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 12*FRACUNIT, //radius 24*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_09*/ 188, //doomednum S_VASE_01, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 12*FRACUNIT, //radius 32*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_17*/ 189, //doomednum S_STOL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 6*FRACUNIT, //radius 24*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_18*/ 190, //doomednum S_POT1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_MISC_19*/ 191, //doomednum S_TUB1_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_MISC_20*/ 194, //doomednum S_ANVL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 32*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT16*/ 196, //doomednum S_TLMP_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 11*FRACUNIT, //radius 64*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT17*/ 197, //doomednum S_TLMP_01, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 8*FRACUNIT, //radius 64*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_21*/ 68, //doomednum S_TRAY_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 24*FRACUNIT, //radius 40*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_12*/ 228, //doomednum S_AFED_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 12*FRACUNIT, //radius 24*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MISC_26*/ 216, //doomednum S_SBAN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 24*FRACUNIT, //radius 96*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_NOBLOCKMAP, //flags NULL, //namepointer }, { /*MT_MISC_23*/ 217, //doomednum S_BOTR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_MISC_24*/ 218, //doomednum S_HATR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_MISC_25*/ 219, //doomednum S_TOPR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_COUPLING*/ 220, //doomednum S_COUP_00, //spawnstate 40, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 6, //speed 17*FRACUNIT, //radius 64*FRACUNIT, //height 999999, //mass 0, //damage sfx_None, //activesound MF_SOLID|MF_SHOOTABLE|MF_GIVEQUEST|MF_NODIALOG|MF_DROPPED |MF_NOBLOOD|MF_NOTDMATCH, //flags NULL, //namepointer }, { /*MT_COUPLING_BROKEN*/ 226, //doomednum S_COUP_02, //spawnstate 40, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 512*FRACUNIT, //speed 16*FRACUNIT, //radius 16*FRACUNIT, //height 1, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST|MF_DROPPED, //flags "BROKEN_POWER_COUPLING", //namepointer }, { /*MT_PILLAR10*/ 221, //doomednum S_BUBB_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 128*FRACUNIT, //height 100, //mass 0, //damage sfx_amaln5, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR11*/ 222, //doomednum S_BUBF_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 72*FRACUNIT, //height 100, //mass 0, //damage sfx_amaln6, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_PILLAR12*/ 223, //doomednum S_BUBF_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 72*FRACUNIT, //height 100, //mass 0, //damage sfx_amaln4, //activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, //flags NULL, //namepointer }, { /*MT_PILLAR13*/ 224, //doomednum S_ASPR_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 16*FRACUNIT, //radius 128*FRACUNIT, //height 100, //mass 0, //damage sfx_amaln3, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_LIGHT19*/ 225, //doomednum S_SPDL_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 0, //speed 32*FRACUNIT, //radius 56*FRACUNIT, //height 100, //mass 0, //damage sfx_amaln1, //activesound MF_SOLID, //flags NULL, //namepointer }, { /*MT_MEAT*/ -1, //doomednum S_MEAT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_JUNK*/ -1, //doomednum S_JUNK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_BURNDROP*/ -1, //doomednum S_FFOT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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_NOCLIP, //flags NULL, //namepointer }, { /*MT_TOKEN_AMMO*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Ammo", //namepointer }, { /*MT_TOKEN_HEALTH*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Health", //namepointer }, { /*MT_TOKEN*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "info", //namepointer }, { /*MT_TOKEN_ALARM*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "alarm", //namepointer }, { /*MT_TOKEN_DOOR1*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_SHOPCLOSE*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_PRISON_PASS*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 10, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST, //flags "Prison_pass", //namepointer }, { /*MT_TOKEN_DOOR3*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_STAMINA*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_NEW_ACCURACY*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_REPORT*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "report", //namepointer }, { /*MT_TOKEN_TOUGHNESS*/ -1, //doomednum S_HELT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Toughness", //namepointer }, { /*MT_TOKEN_ACCURACY*/ -1, //doomednum S_GUNT_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "Accuracy", //namepointer }, { /*MT_TOKEN_ORACLE_PASS*/ -1, //doomednum S_OTOK_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate S_NULL, //deathstate S_NULL, //xdeathstate sfx_None, //deathsound 18, //speed 20*FRACUNIT, //radius 16*FRACUNIT, //height 100, //mass 0, //damage sfx_None, //activesound MF_SPECIAL|MF_GIVEQUEST, //flags "Oracle_Pass", //namepointer }, { /*MT_TOKEN_QUEST1*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST2*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST3*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST4*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "quest4", //namepointer }, { /*MT_TOKEN_QUEST5*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "quest5", //namepointer }, { /*MT_TOKEN_QUEST6*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "quest6", //namepointer }, { /*MT_TOKEN_QUEST7*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST8*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST9*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST10*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST11*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST12*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST13*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_CRYSTAL*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "You've_Blown_Up_the_Crystal", //namepointer }, { /*MT_TOKEN_QUEST15*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_GATEQUEST*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "You've_Blown_Up_the_Gates", //namepointer }, { /*MT_TOKEN_QUEST17*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST18*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST19*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST20*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_BISHOP*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "You_Killed_the_Bishop!", //namepointer }, { /*MT_TOKEN_QUEST22*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_ORACLE*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "You've_Killed_The_Oracle!", //namepointer }, { /*MT_TOKEN_MACIL*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "You_Killed_Macil!", //namepointer }, { /*MT_TOKEN_QUEST25*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_LOREMASTER*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "You've_Killed_The_Loremaster!", //namepointer }, { /*MT_SECRQUEST*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 "You've_Blown_Up_the_Computer", //namepointer }, { /*MT_TOKEN_QUEST28*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST29*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST30*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_TOKEN_QUEST31*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, { /*MT_SLIDESHOW*/ -1, //doomednum S_TOKN_00, //spawnstate 1000, //spawnhealth S_NULL, //seestate sfx_None, //seesound 8, //reactiontime sfx_None, //attacksound S_NULL, //painstate 0, //painchance sfx_None, //painsound S_NULL, //meleestate S_NULL, //missilestate S_NULL, //crashstate 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 NULL, //namepointer }, }; crispy-doom-crispy-doom-5.6.4/src/strife/info.h000066400000000000000000001621451360717211000214340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Thing frame/state LUT, // generated by multigen utilitiy. // This one is the original DOOM version, preserved. // #ifndef __INFO__ #define __INFO__ // Needed for action function pointer handling. #include "d_think.h" // villsa [STRIFE] typedef enum { SPR_PLAY, // 0 SPR_PNCH, // 1 SPR_WAVE, // 2 SPR_RBPY, // 3 SPR_TRGT, // 4 SPR_XBOW, // 5 SPR_MMIS, // 6 SPR_RIFG, // 7 SPR_RIFF, // 8 SPR_FLMT, // 9 SPR_FLMF, // 10 SPR_BLST, // 11 SPR_BLSF, // 12 SPR_GREN, // 13 SPR_GREF, // 14 SPR_SIGH, // 15 SPR_SIGF, // 16 SPR_POW1, // 17 SPR_POW2, // 18 SPR_POW3, // 19 SPR_ZAP1, // 20 SPR_SPRY, // 21 SPR_BLOD, // 22 SPR_PUFY, // 23 SPR_SHT1, // 24 SPR_SHT2, // 25 SPR_GRIN, // 26 SPR_GRAP, // 27 SPR_UBAM, // 28 SPR_BNG2, // 29 SPR_BNG4, // 30 SPR_BNG3, // 31 SPR_FLBE, // 32 SPR_XPRK, // 33 SPR_OCLW, // 34 SPR_CCLW, // 35 SPR_TEND, // 36 SPR_MICR, // 37 SPR_MISS, // 38 SPR_AROW, // 39 SPR_ARWP, // 40 SPR_TORP, // 41 SPR_THIT, // 42 SPR_TWAV, // 43 SPR_MISL, // 44 SPR_TFOG, // 45 SPR_IFOG, // 46 SPR_SHRD, // 47 SPR_RGIB, // 48 SPR_MRYS, // 49 SPR_MRNO, // 50 SPR_MRST, // 51 SPR_MRLK, // 52 SPR_MRBD, // 53 SPR_MRPN, // 54 SPR_MRGT, // 55 SPR_BURN, // 56 SPR_DISR, // 57 SPR_PEAS, // 58 SPR_GIBS, // 59 SPR_AGRD, // 60 SPR_ARMR, // 61 SPR_SACR, // 62 SPR_TNK1, // 63 SPR_TNK2, // 64 SPR_TNK3, // 65 SPR_TNK4, // 66 SPR_TNK5, // 67 SPR_TNK6, // 68 SPR_NEAL, // 69 SPR_BEGR, // 70 SPR_HMN1, // 71 SPR_LEDR, // 72 SPR_LEAD, // 73 SPR_ROB1, // 74 SPR_PGRD, // 75 SPR_ROB2, // 76 SPR_MLDR, // 77 SPR_ORCL, // 78 SPR_PRST, // 79 SPR_PDED, // 80 SPR_ALN1, // 81 SPR_AL1P, // 82 SPR_NODE, // 83 SPR_MTHD, // 84 SPR_MNAM, // 85 SPR_MNAL, // 86 SPR_MDTH, // 87 SPR_NEST, // 88 SPR_PODD, // 89 SPR_ZAP6, // 90 SPR_ZOT3, // 91 SPR_ZAP7, // 92 SPR_ZOT1, // 93 SPR_ZAP5, // 94 SPR_ZOT2, // 95 SPR_SEWR, // 96 SPR_SPID, // 97 SPR_ROB3, // 98 SPR_RBB3, // 99 SPR_PRGR, // 100 SPR_BASE, // SPR_FRBL, // SPR_KLAX, // SPR_TURT, // SPR_BALL, // 105 SPR_PSTN, // SPR_SECR, // SPR_TARG, // SPR_RING, // SPR_EARS, // 110 SPR_COMM, // SPR_BOOM, // SPR_RATT, // SPR_HOGN, // SPR_DEAD, // 115 SPR_SBAN, // SPR_BOTR, // SPR_HATR, // SPR_TOPR, // SPR_COUP, // 120 SPR_BUBB, // SPR_BUBF, // SPR_BUBC, // SPR_ASPR, // SPR_SPDL, // 125 SPR_TOKN, // SPR_OTOK, // SPR_HELT, // SPR_GUNT, // SPR_FULL, // 130 SPR_MEAT, // SPR_JUNK, // SPR_FFOT, // SPR_DIE1, // SPR_BEAC, // 135 SPR_ARM1, // SPR_ARM2, // SPR_BARW, // SPR_BART, // SPR_LAMP, // 140 SPR_LANT, // SPR_BARL, // SPR_BOWL, // SPR_BRAZ, // SPR_TRCH, // 145 SPR_LTRH, // SPR_LMPC, // SPR_LOGS, // SPR_TRHO, // SPR_WATR, // 150 SPR_MUGG, // SPR_FUSL, // SPR_CRD1, // SPR_CRD2, // SPR_TPAS, // 155 SPR_KY1G, // SPR_KY2S, // SPR_KY3B, // SPR_HAND, // SPR_CRYS, // 160 SPR_PRIS, // SPR_PWR1, // SPR_PWR2, // SPR_PWR3, // SPR_ORAC, // 165 SPR_GYID, // SPR_FUBR, // SPR_WARE, // SPR_RCRY, // SPR_BCRY, // 170 SPR_CHAP, // SPR_TUNL, // SPR_BLTK, // SPR_SECK, // SPR_MINE, // 175 SPR_REBL, // SPR_PROC, // SPR_ANKH, // SPR_GOID, // SPR_STMP, // 180 SPR_MDKT, // SPR_COIN, // SPR_CRED, // SPR_SACK, // SPR_CHST, // 185 SPR_SHD1, // SPR_MASK, // SPR_UNIF, // SPR_OFIC, // SPR_PMAP, // 190 SPR_PMUP, // SPR_BLIT, // SPR_BBOX, // SPR_MSSL, // SPR_ROKT, // 195 SPR_BRY1, // SPR_CPAC, // SPR_PQRL, // SPR_XQRL, // SPR_GRN1, // 200 SPR_GRN2, // SPR_BKPK, // SPR_RELC, // SPR_RIFL, // SPR_FLAM, // 205 SPR_BFLM, // SPR_MMSL, // SPR_TRPD, // SPR_GRND, // SPR_CBOW, // 210 SPR_SIGL, // SPR_LITE, // SPR_CNDL, // SPR_CLBR, // SPR_LITS, // 215 SPR_LITB, // SPR_LITG, // SPR_ROK1, // SPR_ROK2, // SPR_ROK3, // 220 SPR_ROK4, // SPR_LOGG, // SPR_RUB1, // SPR_RUB2, // SPR_RUB3, // 225 SPR_RUB4, // SPR_RUB5, // SPR_RUB6, // SPR_RUB7, // SPR_RUB8, // 230 SPR_CHAN, // SPR_STAT, // SPR_DSTA, // SPR_CRAB, // SPR_CAGE, // 235 SPR_TREE, // SPR_TRE1, // SPR_BUSH, // SPR_SHRB, // SPR_STAK, // 240 SPR_BAR1, // SPR_VASE, // SPR_STOL, // SPR_POT1, // SPR_TUB1, // 245 SPR_ANVL, // SPR_TLMP, // SPR_TRAY, // SPR_APOW, // SPR_AFED, // 250 SPR_DRIP, // SPR_CDRP, // SPR_SPLH, // SPR_WTFT, // SPR_HERT, // SPR_TELP, // SPR_MONI, // SPR_STEL, // SPR_STLA, // SPR_STLE, // 260 SPR_HUGE, // 261 SPR_STLG, // 262 NUMSPRITES } spritenum_t; // villsa [STRIFE] typedef enum { S_NULL, // 00 S_PNCH_00, // 01 S_WAVE_00, // 02 S_WAVE_01, // 03 S_WAVE_02, // 04 S_WAVE_03, // 05 S_RBPY_00, // 06 S_RBPY_01, // 07 S_RBPY_02, // 08 S_RBPY_03, // 09 S_TRGT_00, // 10 S_TRGT_01, // 11 S_TRGT_02, // 12 S_PNCH_01, // 13 S_PNCH_02, // 14 S_PNCH_03, // 15 S_PNCH_04, // 16 S_PNCH_05, // 17 S_PNCH_06, // 18 S_PNCH_07, // 19 S_PNCH_08, // 20 S_XBOW_00, // 21 S_XBOW_01, // 22 S_XBOW_02, // 23 S_XBOW_03, // 24 S_XBOW_04, // 25 S_XBOW_05, // 26 S_XBOW_06, // 27 S_XBOW_07, // 28 S_XBOW_08, // 29 S_XBOW_09, // 30 S_XBOW_10, // 31 S_XBOW_11, // 32 S_XBOW_12, // 33 S_XBOW_13, // 34 S_XBOW_14, // 35 S_XBOW_15, // 36 S_XBOW_16, // 37 S_XBOW_17, // 38 S_XBOW_18, // 39 S_XBOW_19, // 40 S_XBOW_20, // 41 S_XBOW_21, // 42 S_XBOW_22, // 43 S_MMIS_00, // 44 S_MMIS_01, // 45 S_MMIS_02, // 46 S_MMIS_03, // 47 S_MMIS_04, // 48 S_MMIS_05, // 49 S_MMIS_06, // 50 S_MMIS_07, // 51 S_MMIS_08, // 52 S_MMIS_09, // 53 S_RIFG_00, // 54 S_RIFG_01, // 55 S_RIFG_02, // 56 S_RIFF_00, // 57 S_RIFF_01, // 58 S_RIFG_03, // 59 S_RIFG_04, // 60 S_RIFG_05, // 61 S_FLMT_00, // 62 S_FLMT_01, // 63 S_FLMT_02, // 64 S_FLMT_03, // 65 S_FLMF_00, // 66 S_FLMF_01, // 67 S_BLST_00, // 68 S_BLST_01, // 69 S_BLST_02, // 70 S_BLST_03, // 71 S_BLST_04, // 72 S_BLST_05, // 73 S_BLSF_00, // 74 S_BLST_06, // 75 S_BLST_07, // 76 S_BLST_08, // 77 S_BLST_09, // 78 S_BLST_10, // 79 S_BLST_11, // 80 S_BLST_12, // 81 S_BLST_13, // 82 S_BLST_14, // 83 S_BLST_15, // 84 S_BLST_16, // 85 S_BLST_17, // 86 S_BLST_18, // 87 S_BLST_19, // 88 S_BLST_20, // 89 S_BLSF_01, // 90 S_BLST_21, // 91 S_BLST_22, // 92 S_BLST_23, // 93 S_BLST_24, // 94 S_GREN_00, // 95 S_GREN_01, // 96 S_GREN_02, // 97 S_GREN_03, // 98 S_GREN_04, // 99 S_GREN_05, // 100 S_GREN_06, // 101 S_GREN_07, // 102 S_GREF_00, // 103 S_GREF_01, // 104 S_GREF_02, // 105 S_GREN_08, // 106 S_GREN_09, // 107 S_GREN_10, // 108 S_GREN_11, // 109 S_GREN_12, // 110 S_GREN_13, // 111 S_GREN_14, // 112 S_GREN_15, // 113 S_GREF_03, // 114 S_GREF_04, // 115 S_GREF_05, // 116 S_SIGH_00, // 117 S_SIGH_01, // 118 S_SIGH_02, // 119 S_SIGH_03, // 120 S_SIGH_04, // 121 S_SIGH_05, // 122 S_SIGH_06, // 123 S_SIGH_07, // 124 S_SIGH_08, // 125 S_SIGH_09, // 126 S_SIGH_10, // 127 S_SIGF_00, // 128 S_SIGF_01, // 129 S_SIGF_02, // 130 S_POW1_00, // 131 S_POW1_01, // 132 S_POW1_02, // 133 S_POW1_03, // 134 S_POW1_04, // 135 S_POW1_05, // 136 S_POW1_06, // 137 S_POW1_07, // 138 S_POW1_08, // 139 S_POW1_09, // 140 S_POW2_00, // 141 S_POW2_01, // 142 S_POW2_02, // 143 S_POW2_03, // 144 S_POW3_00, // 145 S_POW3_01, // 146 S_POW3_02, // 147 S_POW3_03, // 148 S_POW3_04, // 149 S_POW3_05, // 150 S_POW3_06, // 151 S_POW3_07, // 152 S_ZAP1_00, // 153 S_ZAP1_01, // 154 S_ZAP1_02, // 155 S_ZAP1_03, // 156 S_ZAP1_04, // 157 S_ZAP1_05, // 158 S_ZAP1_06, // 159 S_ZAP1_07, // 160 S_ZAP1_08, // 161 S_ZAP1_09, // 162 S_ZAP1_10, // 163 S_ZAP1_11, // 164 S_SPRY_00, // 165 S_SPRY_01, // 166 S_SPRY_02, // 167 S_SPRY_03, // 168 S_SPRY_04, // 169 S_SPRY_05, // 170 S_SPRY_06, // 171 S_BLOD_00, // 172 S_BLOD_01, // 173 S_BLOD_02, // 174 S_PUFY_00, // 175 S_PUFY_01, // 176 S_PUFY_02, // 177 S_PUFY_03, // 178 S_SHT1_00, // 179 S_SHT1_01, // 180 S_SHT2_00, // 181 S_SHT2_01, // 182 S_GRIN_00, // 183 S_GRIN_01, // 184 S_GRAP_00, // 185 S_GRAP_01, // 186 S_UBAM_00, // 187 S_UBAM_01, // 188 S_BNG2_00, // 189 S_BNG2_01, // 190 S_BNG2_02, // 191 S_BNG2_03, // 192 S_BNG2_04, // 193 S_BNG2_05, // 194 S_BNG2_06, // 195 S_BNG2_07, // 196 S_BNG2_08, // 197 S_BNG4_00, // 198 S_BNG4_01, // 199 S_BNG4_02, // 200 S_BNG4_03, // 201 S_BNG4_04, // 202 S_BNG4_05, // 203 S_BNG4_06, // 204 S_BNG4_07, // 205 S_BNG4_08, // 206 S_BNG4_09, // 207 S_BNG4_10, // 208 S_BNG4_11, // 209 S_BNG4_12, // 210 S_BNG4_13, // 211 S_BNG3_00, // 212 S_BNG3_01, // 213 S_BNG3_02, // 214 S_BNG3_03, // 215 S_BNG3_04, // 216 S_BNG3_05, // 217 S_BNG3_06, // 218 S_BNG3_07, // 219 S_BNG3_08, // 220 S_BNG3_09, // 221 S_BNG3_10, // 222 S_FLBE_00, // 223 S_FLBE_01, // 224 S_FLBE_02, // 225 S_FLBE_03, // 226 S_FLBE_04, // 227 S_FLBE_05, // 228 S_FLBE_06, // 229 S_FLBE_07, // 230 S_FLBE_08, // 231 S_FLBE_09, // 232 S_FLBE_10, // 233 S_XPRK_00, // 234 S_OCLW_00, // 235 S_CCLW_00, // 236 S_TEND_00, // 237 S_MICR_00, // 238 S_MISS_00, // 239 S_MISS_01, // 240 S_AROW_00, // 241 S_ARWP_00, // 242 S_AROW_01, // 243 S_TORP_00, // 244 S_TORP_01, // 245 S_TORP_02, // 246 S_TORP_03, // 247 S_THIT_00, // 248 S_THIT_01, // 249 S_THIT_02, // 250 S_THIT_03, // 251 S_THIT_04, // 252 S_TWAV_00, // 253 S_TWAV_01, // 254 S_TWAV_02, // 255 S_MISL_00, // 256 S_MISL_01, // 257 S_MISL_02, // 258 S_MISL_03, // 259 S_MISL_04, // 260 S_MISL_05, // 261 S_MISL_06, // 262 S_MISL_07, // 263 S_TFOG_00, // 264 S_TFOG_01, // 265 S_TFOG_02, // 266 S_TFOG_03, // 267 S_TFOG_04, // 268 S_TFOG_05, // 269 S_TFOG_06, // 270 S_TFOG_07, // 271 S_TFOG_08, // 272 S_TFOG_09, // 273 S_IFOG_00, // 274 S_IFOG_01, // 275 S_IFOG_02, // 276 S_IFOG_03, // 277 S_IFOG_04, // 278 S_IFOG_05, // 279 S_IFOG_06, // 280 S_SHRD_00, // 281 S_SHRD_01, // 282 S_SHRD_02, // 283 S_SHRD_03, // 284 S_SHRD_04, // 285 S_SHRD_05, // 286 S_PLAY_00, // 287 S_PLAY_01, // 288 S_PLAY_02, // 289 S_PLAY_03, // 290 S_PLAY_04, // 291 S_PLAY_05, // 292 S_PLAY_06, // 293 S_PLAY_07, // 294 S_PLAY_08, // 295 S_PLAY_09, // 296 S_PLAY_10, // 297 S_PLAY_11, // 298 S_PLAY_12, // 299 S_PLAY_13, // 300 S_PLAY_14, // 301 S_PLAY_15, // 302 S_PLAY_16, // 303 S_PLAY_17, // 304 S_PLAY_18, // 305 S_RGIB_00, // 306 S_RGIB_01, // 307 S_RGIB_02, // 308 S_RGIB_03, // 309 S_RGIB_04, // 310 S_RGIB_05, // 311 S_RGIB_06, // 312 S_RGIB_07, // 313 S_MRYS_00, // 314 S_MRNO_00, // 315 S_MRNO_01, // 316 S_MRNO_02, // 317 S_MRNO_03, // 318 S_MRNO_04, // 319 S_MRST_00, // 320 S_MRLK_00, // 321 S_MRLK_01, // 322 S_MRBD_00, // 323 S_MRBD_01, // 324 S_MRBD_02, // 325 S_MRBD_03, // 326 S_MRBD_04, // 327 S_MRBD_05, // 328 S_MRBD_06, // 329 S_MRBD_07, // 330 S_MRBD_08, // 331 S_MRBD_09, // 332 S_MRPN_00, // 333 S_MRPN_01, // 334 S_MRPN_02, // 335 S_MRPN_03, // 336 S_MRPN_04, // 337 S_MRPN_05, // 338 S_MRPN_06, // 339 S_MRGT_00, // 340 S_MRGT_01, // 341 S_MRGT_02, // 342 S_MRGT_03, // 343 S_MRGT_04, // 344 S_MRGT_05, // 345 S_MRGT_06, // 346 S_MRGT_07, // 347 S_MRGT_08, // 348 S_BURN_00, // 349 S_BURN_01, // 350 S_BURN_02, // 351 S_BURN_03, // 352 S_BURN_04, // 353 S_BURN_05, // 354 S_BURN_06, // 355 S_BURN_07, // 356 S_BURN_08, // 357 S_BURN_09, // 358 S_BURN_10, // 359 S_BURN_11, // 360 S_BURN_12, // 361 S_BURN_13, // 362 S_BURN_14, // 363 S_BURN_15, // 364 S_BURN_16, // 365 S_BURN_17, // 366 S_BURN_18, // 367 S_BURN_19, // 368 S_BURN_20, // 369 S_BURN_21, // 370 S_BURN_22, // 371 S_BURN_23, // 372 S_DISR_00, // 373 S_DISR_01, // 374 S_DISR_02, // 375 S_DISR_03, // 376 S_DISR_04, // 377 S_DISR_05, // 378 S_DISR_06, // 379 S_DISR_07, // 380 S_DISR_08, // 381 S_DISR_09, // 382 S_PEAS_00, // 383 S_PEAS_01, // 384 S_PEAS_02, // 385 S_PEAS_03, // 386 S_PEAS_04, // 387 S_PEAS_05, // 388 S_PEAS_06, // 389 S_PEAS_07, // 390 S_PEAS_08, // 391 S_PEAS_09, // 392 S_PEAS_10, // 393 S_PEAS_11, // 394 S_PEAS_12, // 395 S_PEAS_13, // 396 S_PEAS_14, // 397 S_PEAS_15, // 398 S_PEAS_16, // 399 S_PEAS_17, // 400 S_PEAS_18, // 401 S_PEAS_19, // 402 S_PEAS_20, // 403 S_PEAS_21, // 404 S_PEAS_22, // 405 S_PEAS_23, // 406 S_PEAS_24, // 407 S_GIBS_00, // 408 S_GIBS_01, // 409 S_GIBS_02, // 410 S_GIBS_03, // 411 S_GIBS_04, // 412 S_GIBS_05, // 413 S_GIBS_06, // 414 S_GIBS_07, // 415 S_GIBS_08, // 416 S_GIBS_09, // 417 S_PEAS_25, // 418 S_AGRD_00, // 419 S_ARMR_00, // 420 S_ARMR_01, // 421 S_PLAY_19, // 422 S_SACR_00, // 423 S_TNK1_00, // 424 S_TNK1_01, // 425 S_TNK1_02, // 426 S_TNK2_00, // 427 S_TNK2_01, // 428 S_TNK2_02, // 429 S_TNK3_00, // 430 S_TNK3_01, // 431 S_TNK3_02, // 432 S_TNK4_00, // 433 S_TNK4_01, // 434 S_TNK4_02, // 435 S_TNK5_00, // 436 S_TNK5_01, // 437 S_TNK5_02, // 438 S_TNK6_00, // 439 S_TNK6_01, // 440 S_TNK6_02, // 441 S_NEAL_00, // 442 S_NEAL_01, // 443 S_NEAL_02, // 444 S_NEAL_03, // 445 S_NEAL_04, // 446 S_NEAL_05, // 447 S_NEAL_06, // 448 S_NEAL_07, // 449 S_NEAL_08, // 450 S_NEAL_09, // 451 S_NEAL_10, // 452 S_NEAL_11, // 453 S_NEAL_12, // 454 S_NEAL_13, // 455 S_BEGR_00, // 456 S_BEGR_01, // 457 S_BEGR_02, // 458 S_BEGR_03, // 459 S_BEGR_04, // 460 S_BEGR_05, // 461 S_BEGR_06, // 462 S_BEGR_07, // 463 S_BEGR_08, // 464 S_BEGR_09, // 465 S_BEGR_10, // 466 S_BEGR_11, // 467 S_BEGR_12, // 468 S_BEGR_13, // 469 S_BEGR_14, // 470 S_BEGR_15, // 471 S_BEGR_16, // 472 S_BEGR_17, // 473 S_BEGR_18, // 474 S_BEGR_19, // 475 S_BEGR_20, // 476 S_BEGR_21, // 477 S_BEGR_22, // 478 S_HMN1_00, // 479 S_HMN1_01, // 480 S_HMN1_02, // 481 S_HMN1_03, // 482 S_HMN1_04, // 483 S_HMN1_05, // 484 S_HMN1_06, // 485 S_HMN1_07, // 486 S_HMN1_08, // 487 S_HMN1_09, // 488 S_HMN1_10, // 489 S_HMN1_11, // 490 S_HMN1_12, // 491 S_HMN1_13, // 492 S_HMN1_14, // 493 S_HMN1_15, // 494 S_HMN1_16, // 495 S_HMN1_17, // 496 S_HMN1_18, // 497 S_HMN1_19, // 498 S_HMN1_20, // 499 S_HMN1_21, // 500 S_HMN1_22, // 501 S_HMN1_23, // 502 S_HMN1_24, // 503 S_HMN1_25, // 504 S_HMN1_26, // 505 S_HMN1_27, // 506 S_HMN1_28, // 507 S_HMN1_29, // 508 S_HMN1_30, // 509 S_HMN1_31, // 510 S_RGIB_08, // 511 S_RGIB_09, // 512 S_RGIB_10, // 513 S_RGIB_11, // 514 S_RGIB_12, // 515 S_RGIB_13, // 516 S_RGIB_14, // 517 S_RGIB_15, // 518 S_LEDR_00, // 519 S_LEDR_01, // 520 S_LEDR_02, // 521 S_LEAD_00, // 522 S_LEAD_01, // 523 S_LEAD_02, // 524 S_LEAD_03, // 525 S_LEAD_04, // 526 S_LEAD_05, // 527 S_LEAD_06, // 528 S_LEAD_07, // 529 S_LEAD_08, // 530 S_LEAD_09, // 531 S_LEAD_10, // 532 S_LEAD_11, // 533 S_LEAD_12, // 534 S_LEAD_13, // 535 S_LEAD_14, // 536 S_LEAD_15, // 537 S_LEAD_16, // 538 S_LEAD_17, // 539 S_LEAD_18, // 540 S_LEAD_19, // 541 S_LEAD_20, // 542 S_LEAD_21, // 543 S_LEAD_22, // 544 S_LEAD_23, // 545 S_LEAD_24, // 546 S_LEAD_25, // 547 S_LEAD_26, // 548 S_LEAD_27, // 549 S_LEAD_28, // 550 S_LEAD_29, // 551 S_LEAD_30, // 552 S_LEAD_31, // 553 S_LEAD_32, // 554 S_LEAD_33, // 555 S_LEAD_34, // 556 S_LEAD_35, // 557 S_LEAD_36, // 558 S_LEAD_37, // 559 S_PUFY_04, // 560 S_PUFY_05, // 561 S_PUFY_06, // 562 S_PUFY_07, // 563 S_PUFY_08, // 564 S_MICR_01, // 565 S_MICR_02, // 566 S_ROB1_00, // 567 S_ROB1_01, // 568 S_ROB1_02, // 569 S_ROB1_03, // 570 S_ROB1_04, // 571 S_ROB1_05, // 572 S_ROB1_06, // 573 S_ROB1_07, // 574 S_ROB1_08, // 575 S_ROB1_09, // 576 S_ROB1_10, // 577 S_ROB1_11, // 578 S_ROB1_12, // 579 S_ROB1_13, // 580 S_ROB1_14, // 581 S_ROB1_15, // 582 S_ROB1_16, // 583 S_ROB1_17, // 584 S_ROB1_18, // 585 S_ROB1_19, // 586 S_ROB1_20, // 587 S_ROB1_21, // 588 S_ROB1_22, // 589 S_ROB1_23, // 590 S_ROB1_24, // 591 S_ROB1_25, // 592 S_ROB1_26, // 593 S_ROB1_27, // 594 S_ROB1_28, // 595 S_ROB1_29, // 596 S_ROB1_30, // 597 S_ROB1_31, // 598 S_ROB1_32, // 599 S_AGRD_01, // 600 S_AGRD_02, // 601 S_AGRD_03, // 602 S_AGRD_04, // 603 S_AGRD_05, // 604 S_AGRD_06, // 605 S_AGRD_07, // 606 S_AGRD_08, // 607 S_AGRD_09, // 608 S_AGRD_10, // 609 S_AGRD_11, // 610 S_AGRD_12, // 611 S_AGRD_13, // 612 S_AGRD_14, // 613 S_AGRD_15, // 614 S_AGRD_16, // 615 S_AGRD_17, // 616 S_AGRD_18, // 617 S_AGRD_19, // 618 S_AGRD_20, // 619 S_AGRD_21, // 620 S_AGRD_22, // 621 S_AGRD_23, // 622 S_AGRD_24, // 623 S_AGRD_25, // 624 S_AGRD_26, // 625 S_AGRD_27, // 626 S_AGRD_28, // 627 S_AGRD_29, // 628 S_AGRD_30, // 629 S_AGRD_31, // 630 S_GIBS_10, // 631 S_GIBS_11, // 632 S_GIBS_12, // 633 S_GIBS_13, // 634 S_GIBS_14, // 635 S_GIBS_15, // 636 S_GIBS_16, // 637 S_GIBS_17, // 638 S_GIBS_18, // 639 S_GIBS_19, // 640 S_GIBS_20, // 641 S_GIBS_21, // 642 S_PGRD_00, // 643 S_PGRD_01, // 644 S_PGRD_02, // 645 S_PGRD_03, // 646 S_PGRD_04, // 647 S_PGRD_05, // 648 S_PGRD_06, // 649 S_PGRD_07, // 650 S_PGRD_08, // 651 S_PGRD_09, // 652 S_PGRD_10, // 653 S_PGRD_11, // 654 S_PGRD_12, // 655 S_PGRD_13, // 656 S_PGRD_14, // 657 S_PGRD_15, // 658 S_PGRD_16, // 659 S_PGRD_17, // 660 S_PGRD_18, // 661 S_PGRD_19, // 662 S_PGRD_20, // 663 S_PGRD_21, // 664 S_PGRD_22, // 665 S_PGRD_23, // 666 S_PGRD_24, // 667 S_PGRD_25, // 668 S_PGRD_26, // 669 S_PGRD_27, // 670 S_PGRD_28, // 671 S_PGRD_29, // 672 S_PGRD_30, // 673 S_PGRD_31, // 674 S_PGRD_32, // 675 S_PGRD_33, // 676 S_PGRD_34, // 677 S_PGRD_35, // 678 S_PGRD_36, // 679 S_PGRD_37, // 680 S_ROB2_00, // 681 S_ROB2_01, // 682 S_ROB2_02, // 683 S_ROB2_03, // 684 S_ROB2_04, // 685 S_ROB2_05, // 686 S_ROB2_06, // 687 S_ROB2_07, // 688 S_ROB2_08, // 689 S_ROB2_09, // 690 S_ROB2_10, // 691 S_ROB2_11, // 692 S_ROB2_12, // 693 S_ROB2_13, // 694 S_ROB2_14, // 695 S_ROB2_15, // 696 S_ROB2_16, // 697 S_ROB2_17, // 698 S_ROB2_18, // 699 S_ROB2_19, // 700 S_ROB2_20, // 701 S_ROB2_21, // 702 S_ROB2_22, // 703 S_ROB2_23, // 704 S_ROB2_24, // 705 S_ROB2_25, // 706 S_ROB2_26, // 707 S_ROB2_27, // 708 S_ROB2_28, // 709 S_ROB2_29, // 710 S_MLDR_00, // 711 S_MLDR_01, // 712 S_MLDR_02, // 713 S_MLDR_03, // 714 S_MLDR_04, // 715 S_MLDR_05, // 716 S_MLDR_06, // 717 S_MLDR_07, // 718 S_MLDR_08, // 719 S_MLDR_09, // 720 S_MLDR_10, // 721 S_MLDR_11, // 722 S_MLDR_12, // 723 S_MLDR_13, // 724 S_MLDR_14, // 725 S_MLDR_15, // 726 S_MLDR_16, // 727 S_MLDR_17, // 728 S_MLDR_18, // 729 S_MLDR_19, // 730 S_MLDR_20, // 731 S_MLDR_21, // 732 S_MLDR_22, // 733 S_MLDR_23, // 734 S_MLDR_24, // 735 S_MLDR_25, // 736 S_MLDR_26, // 737 S_MLDR_27, // 738 S_ORCL_00, // 739 S_ORCL_01, // 740 S_ORCL_02, // 741 S_ORCL_03, // 742 S_ORCL_04, // 743 S_ORCL_05, // 744 S_ORCL_06, // 745 S_ORCL_07, // 746 S_ORCL_08, // 747 S_ORCL_09, // 748 S_ORCL_10, // 749 S_ORCL_11, // 750 S_ORCL_12, // 751 S_ORCL_13, // 752 S_ORCL_14, // 753 S_ORCL_15, // 754 S_ORCL_16, // 755 S_PRST_00, // 756 S_PRST_01, // 757 S_PRST_02, // 758 S_PRST_03, // 759 S_PRST_04, // 760 S_PRST_05, // 761 S_PRST_06, // 762 S_PRST_07, // 763 S_PRST_08, // 764 S_PRST_09, // 765 S_PRST_10, // 766 S_PRST_11, // 767 S_PRST_12, // 768 S_PRST_13, // 769 S_PRST_14, // 770 S_PRST_15, // 771 S_PDED_00, // 772 S_PDED_01, // 773 S_PDED_02, // 774 S_PDED_03, // 775 S_PDED_04, // 776 S_PDED_05, // 777 S_PDED_06, // 778 S_PDED_07, // 779 S_PDED_08, // 780 S_PDED_09, // 781 S_PDED_10, // 782 S_PDED_11, // 783 S_PDED_12, // 784 S_PDED_13, // 785 S_PDED_14, // 786 S_PDED_15, // 787 S_PDED_16, // 788 S_PDED_17, // 789 S_PDED_18, // 790 S_PDED_19, // 791 S_PDED_20, // 792 S_PDED_21, // 793 S_PDED_22, // 794 S_PDED_23, // 795 S_ALN1_00, // 796 S_ALN1_01, // 797 S_ALN1_02, // 798 S_ALN1_03, // 799 S_ALN1_04, // 800 S_ALN1_05, // 801 S_ALN1_06, // 802 S_ALN1_07, // 803 S_ALN1_08, // 804 S_ALN1_09, // 805 S_ALN1_10, // 806 S_ALN1_11, // 807 S_ALN1_12, // 808 S_ALN1_13, // 809 S_ALN1_14, // 810 S_ALN1_15, // 811 S_ALN1_16, // 812 S_ALN1_17, // 813 S_ALN1_18, // 814 S_ALN1_19, // 815 S_AL1P_00, // 816 S_AL1P_01, // 817 S_AL1P_02, // 818 S_AL1P_03, // 819 S_AL1P_04, // 820 S_AL1P_05, // 821 S_AL1P_06, // 822 S_AL1P_07, // 823 S_AL1P_08, // 824 S_AL1P_09, // 825 S_AL1P_10, // 826 S_AL1P_11, // 827 S_AL1P_12, // 828 S_AL1P_13, // 829 S_AL1P_14, // 830 S_AL1P_15, // 831 S_AL1P_16, // 832 S_AL1P_17, // 833 S_NODE_00, // 834 S_NODE_01, // 835 S_NODE_02, // 836 S_NODE_03, // 837 S_NODE_04, // 838 S_NODE_05, // 839 S_NODE_06, // 840 S_MTHD_00, // 841 S_MTHD_01, // 842 S_MTHD_02, // 843 S_MTHD_03, // 844 S_MTHD_04, // 845 S_MTHD_05, // 846 S_MTHD_06, // 847 S_MTHD_07, // 848 S_MTHD_08, // 849 S_MTHD_09, // 850 S_MTHD_10, // 851 S_ALN1_20, // 852 S_ALN1_21, // 853 S_ALN1_22, // 854 S_ALN1_23, // 855 S_ALN1_24, // 856 S_ALN1_25, // 857 S_ALN1_26, // 858 S_ALN1_27, // 859 S_ALN1_28, // 860 S_ALN1_29, // 861 S_ALN1_30, // 862 S_ALN1_31, // 863 S_ALN1_32, // 864 S_ALN1_33, // 865 S_ALN1_34, // 866 S_ALN1_35, // 867 S_ALN1_36, // 868 S_ALN1_37, // 869 S_ALN1_38, // 870 S_ALN1_39, // 871 S_ALN1_40, // 872 S_ALN1_41, // 873 S_ALN1_42, // 874 S_ALN1_43, // 875 S_ALN1_44, // 876 S_ALN1_45, // 877 S_ALN1_46, // 878 S_ALN1_47, // 879 S_ALN1_48, // 880 S_ALN1_49, // 881 S_ALN1_50, // 882 S_ALN1_51, // 883 S_ALN1_52, // 884 S_ALN1_53, // 885 S_ALN1_54, // 886 S_ALN1_55, // 887 S_ALN1_56, // 888 S_ALN1_57, // 889 S_MNAM_00, // 890 S_MNAM_01, // 891 S_MNAM_02, // 892 S_MNAM_03, // 893 S_MNAM_04, // 894 S_MNAM_05, // 895 S_MNAM_06, // 896 S_MNAM_07, // 897 S_MNAM_08, // 898 S_MNAM_09, // 899 S_MNAM_10, // 900 S_MNAM_11, // 901 S_MNAL_00, // 902 S_MNAL_01, // 903 S_MNAL_02, // 904 S_MNAL_03, // 905 S_MNAL_04, // 906 S_MNAL_05, // 907 S_MNAL_06, // 908 S_MNAL_07, // 909 S_MNAL_08, // 910 S_MNAL_09, // 911 S_MNAL_10, // 912 S_MNAL_11, // 913 S_MNAL_12, // 914 S_MNAL_13, // 915 S_MNAL_14, // 916 S_MNAL_15, // 917 S_MNAL_16, // 918 S_MNAL_17, // 919 S_MNAL_18, // 920 S_MNAL_19, // 921 S_MNAL_20, // 922 S_MNAL_21, // 923 S_MNAL_22, // 924 S_MNAL_23, // 925 S_MNAL_24, // 926 S_MNAL_25, // 927 S_MNAL_26, // 928 S_MNAL_27, // 929 S_MNAL_28, // 930 S_MNAL_29, // 931 S_MNAL_30, // 932 S_MNAL_31, // 933 S_MNAL_32, // 934 S_MNAL_33, // 935 S_MNAL_34, // 936 S_MNAL_35, // 937 S_MNAL_36, // 938 S_MNAL_37, // 939 S_MNAL_38, // 940 S_MNAL_39, // 941 S_MNAL_40, // 942 S_MDTH_00, // 943 S_MDTH_01, // 944 S_MDTH_02, // 945 S_MDTH_03, // 946 S_MDTH_04, // 947 S_MDTH_05, // 948 S_MDTH_06, // 949 S_MDTH_07, // 950 S_MDTH_08, // 951 S_MDTH_09, // 952 S_MDTH_10, // 953 S_MDTH_11, // 954 S_MDTH_12, // 955 S_MDTH_13, // 956 S_MDTH_14, // 957 S_NEST_00, // 958 S_PODD_00, // 959 S_PODD_01, // 960 S_PODD_02, // 961 S_PODD_03, // 962 S_PODD_04, // 963 S_PODD_05, // 964 S_ZAP6_00, // 965 S_ZAP6_01, // 966 S_ZAP6_02, // 967 S_ZOT3_00, // 968 S_ZOT3_01, // 969 S_ZOT3_02, // 970 S_ZOT3_03, // 971 S_ZOT3_04, // 972 S_ZAP6_03, // 973 S_ZAP6_04, // 974 S_ZAP6_05, // 975 S_ZAP7_00, // 976 S_ZAP7_01, // 977 S_ZAP7_02, // 978 S_ZAP7_03, // 979 S_ZAP7_04, // 980 S_ZOT1_00, // 981 S_ZOT1_01, // 982 S_ZOT1_02, // 983 S_ZOT1_03, // 984 S_ZOT1_04, // 985 S_ZAP5_00, // 986 S_ZAP5_01, // 987 S_ZAP5_02, // 988 S_ZAP5_03, // 989 S_ZOT2_00, // 990 S_ZOT2_01, // 991 S_ZOT2_02, // 992 S_ZOT2_03, // 993 S_ZOT2_04, // 994 S_SEWR_00, // 995 S_SEWR_01, // 996 S_SEWR_02, // 997 S_SEWR_03, // 998 S_SEWR_04, // 999 S_SEWR_05, // 1000 S_SEWR_06, // 1001 S_SEWR_07, // 1002 S_SEWR_08, // 1003 S_SEWR_09, // 1004 S_SEWR_10, // 1005 S_SEWR_11, // 1006 S_SEWR_12, // 1007 S_SEWR_13, // 1008 S_SPID_00, // 1009 S_SPID_01, // 1010 S_SPID_02, // 1011 S_SPID_03, // 1012 S_SPID_04, // 1013 S_SPID_05, // 1014 S_SPID_06, // 1015 S_SPID_07, // 1016 S_SPID_08, // 1017 S_SPID_09, // 1018 S_SPID_10, // 1019 S_SPID_11, // 1020 S_SPID_12, // 1021 S_SPID_13, // 1022 S_SPID_14, // 1023 S_SPID_15, // 1024 S_SPID_16, // 1025 S_SPID_17, // 1026 S_SPID_18, // 1027 S_SPID_19, // 1028 S_SPID_20, // 1029 S_SPID_21, // 1030 S_SPID_22, // 1031 S_SPID_23, // 1032 S_SPID_24, // 1033 S_SPID_25, // 1034 S_SPID_26, // 1035 S_SPID_27, // 1036 S_SPID_28, // 1037 S_SPID_29, // 1038 S_SPID_30, // 1039 S_SPID_31, // 1040 S_SPID_32, // 1041 S_SPID_33, // 1042 S_SPID_34, // 1043 S_SPID_35, // 1044 S_SPID_36, // 1045 S_SPID_37, // 1046 S_ROB3_00, // 1047 S_ROB3_01, // 1048 S_ROB3_02, // 1049 S_ROB3_03, // 1050 S_ROB3_04, // 1051 S_ROB3_05, // 1052 S_ROB3_06, // 1053 S_ROB3_07, // 1054 S_ROB3_08, // 1055 S_ROB3_09, // 1056 S_ROB3_10, // 1057 S_ROB3_11, // 1058 S_ROB3_12, // 1059 S_ROB3_13, // 1060 S_ROB3_14, // 1061 S_ROB3_15, // 1062 S_ROB3_16, // 1063 S_ROB3_17, // 1064 S_ROB3_18, // 1065 S_ROB3_19, // 1066 S_ROB3_20, // 1067 S_ROB3_21, // 1068 S_ROB3_22, // 1069 S_ROB3_23, // 1070 S_ROB3_24, // 1071 S_ROB3_25, // 1072 S_ROB3_26, // 1073 S_ROB3_27, // 1074 S_ROB3_28, // 1075 S_ROB3_29, // 1076 S_ROB3_30, // 1077 S_ROB3_31, // 1078 S_ROB3_32, // 1079 S_ROB3_33, // 1080 S_ROB3_34, // 1081 S_ROB3_35, // 1082 S_ROB3_36, // 1083 S_ROB3_37, // 1084 S_RBB3_00, // 1085 S_RBB3_01, // 1086 S_RBB3_02, // 1087 S_RBB3_03, // 1088 S_RBB3_04, // 1089 S_RBB3_05, // 1090 S_RBB3_06, // 1091 S_RBB3_07, // 1092 S_PRGR_00, // 1093 S_PRGR_01, // 1094 S_PRGR_02, // 1095 S_PRGR_03, // 1096 S_PRGR_04, // 1097 S_PRGR_05, // 1098 S_PRGR_06, // 1099 S_PRGR_07, // 1100 S_PRGR_08, // 1101 S_PRGR_09, // 1102 S_PRGR_10, // 1103 S_PRGR_11, // 1104 S_PRGR_12, // 1105 S_PRGR_13, // 1106 S_PRGR_14, // 1107 S_PRGR_15, // 1108 S_PRGR_16, // 1109 S_PRGR_17, // 1110 S_PRGR_18, // 1111 S_PRGR_19, // 1112 S_PRGR_20, // 1113 S_PRGR_21, // 1114 S_PRGR_22, // 1115 S_PRGR_23, // 1116 S_PRGR_24, // 1117 S_PRGR_25, // 1118 S_PRGR_26, // 1119 S_PRGR_27, // 1120 S_PRGR_28, // 1121 S_PRGR_29, // 1122 S_PRGR_30, // 1123 S_PRGR_31, // 1124 S_PRGR_32, // 1125 S_PRGR_33, // 1126 S_BASE_00, // 1127 S_BASE_01, // 1128 S_BASE_02, // 1129 S_BASE_03, // 1130 S_BASE_04, // 1131 S_BASE_05, // 1132 S_BASE_06, // 1133 S_BASE_07, // 1134 S_FRBL_00, // 1135 S_FRBL_01, // 1136 S_FRBL_02, // 1137 S_FRBL_03, // 1138 S_FRBL_04, // 1139 S_FRBL_05, // 1140 S_FRBL_06, // 1141 S_FRBL_07, // 1142 S_FRBL_08, // 1143 S_KLAX_00, // 1144 S_KLAX_01, // 1145 S_KLAX_02, // 1146 S_TURT_00, // 1147 S_TURT_01, // 1148 S_TURT_02, // 1149 S_TURT_03, // 1150 S_TURT_04, // 1151 S_BALL_00, // 1152 S_BALL_01, // 1153 S_BALL_02, // 1154 S_BALL_03, // 1155 S_BALL_04, // 1156 S_TURT_05, // 1157 S_PSTN_00, // 1158 S_PSTN_01, // 1159 S_PSTN_02, // 1160 S_PSTN_03, // 1161 S_PSTN_04, // 1162 S_PSTN_05, // 1163 S_PSTN_06, // 1164 S_PSTN_07, // 1165 S_PSTN_08, // 1166 S_PSTN_09, // 1167 S_PSTN_10, // 1168 S_SECR_00, // 1169 S_SECR_01, // 1170 S_SECR_02, // 1171 S_SECR_03, // 1172 S_SECR_04, // 1173 S_SECR_05, // 1174 S_SECR_06, // 1175 S_SECR_07, // 1176 S_SECR_08, // 1177 S_SECR_09, // 1178 S_SECR_10, // 1179 S_SECR_11, // 1180 S_SECR_12, // 1181 S_SECR_13, // 1182 S_SECR_14, // 1183 S_SECR_15, // 1184 S_XPRK_01, // 1185 S_XPRK_02, // 1186 S_TARG_00, // 1187 S_RING_00, // 1188 S_EARS_00, // 1189 S_COMM_00, // 1190 S_BOOM_00, // 1191 S_BOOM_01, // 1192 S_BOOM_02, // 1193 S_BOOM_03, // 1194 S_BOOM_04, // 1195 S_BOOM_05, // 1196 S_BOOM_06, // 1197 S_BOOM_07, // 1198 S_BOOM_08, // 1199 S_BOOM_09, // 1200 S_BOOM_10, // 1201 S_BOOM_11, // 1202 S_BOOM_12, // 1203 S_BOOM_13, // 1204 S_BOOM_14, // 1205 S_BOOM_15, // 1206 S_BOOM_16, // 1207 S_BOOM_17, // 1208 S_BOOM_18, // 1209 S_BOOM_19, // 1210 S_BOOM_20, // 1211 S_BOOM_21, // 1212 S_BOOM_22, // 1213 S_BOOM_23, // 1214 S_BOOM_24, // 1215 S_RATT_00, // 1216 S_RATT_01, // 1217 S_RATT_02, // 1218 S_RATT_03, // 1219 S_RATT_04, // 1220 S_RATT_05, // 1221 S_RATT_06, // 1222 S_HOGN_00, // 1223 S_HOGN_01, // 1224 S_HOGN_02, // 1225 S_DEAD_00, // 1226 S_SBAN_00, // 1227 S_BOTR_00, // 1228 S_HATR_00, // 1229 S_TOPR_00, // 1230 S_COUP_00, // 1231 S_COUP_01, // 1232 S_COUP_02, // 1233 S_BUBB_00, // 1234 S_BUBF_00, // 1235 S_BUBC_00, // 1236 S_ASPR_00, // 1237 S_SPDL_00, // 1238 S_SPDL_01, // 1239 S_SPDL_02, // 1240 S_TOKN_00, // 1241 S_OTOK_00, // 1242 S_HELT_00, // 1243 S_GUNT_00, // 1244 S_FULL_00, // 1245 S_FULL_01, // 1246 S_MEAT_00, // 1247 S_MEAT_01, // 1248 S_MEAT_02, // 1249 S_MEAT_03, // 1250 S_MEAT_04, // 1251 S_MEAT_05, // 1252 S_MEAT_06, // 1253 S_MEAT_07, // 1254 S_MEAT_08, // 1255 S_MEAT_09, // 1256 S_MEAT_10, // 1257 S_MEAT_11, // 1258 S_MEAT_12, // 1259 S_MEAT_13, // 1260 S_MEAT_14, // 1261 S_MEAT_15, // 1262 S_MEAT_16, // 1263 S_MEAT_17, // 1264 S_MEAT_18, // 1265 S_MEAT_19, // 1266 S_JUNK_00, // 1267 S_JUNK_01, // 1268 S_JUNK_02, // 1269 S_JUNK_03, // 1270 S_JUNK_04, // 1271 S_JUNK_05, // 1272 S_JUNK_06, // 1273 S_JUNK_07, // 1274 S_JUNK_08, // 1275 S_JUNK_09, // 1276 S_JUNK_10, // 1277 S_JUNK_11, // 1278 S_JUNK_12, // 1279 S_JUNK_13, // 1280 S_JUNK_14, // 1281 S_JUNK_15, // 1282 S_JUNK_16, // 1283 S_JUNK_17, // 1284 S_JUNK_18, // 1285 S_JUNK_19, // 1286 S_FFOT_00, // 1287 S_FFOT_01, // 1288 S_FFOT_02, // 1289 S_FFOT_03, // 1290 S_DIE1_00, // 1291 S_BEAC_00, // 1292 S_BEAC_01, // 1293 S_BEAC_02, // 1294 S_ARM1_00, // 1295 S_ARM2_00, // 1296 S_BARW_00, // 1297 S_BARW_01, // 1298 S_BARW_02, // 1299 S_BARW_03, // 1300 S_BARW_04, // 1301 S_BARW_05, // 1302 S_BARW_06, // 1303 S_BARW_07, // 1304 S_BART_00, // 1305 S_BART_01, // 1306 S_BART_02, // 1307 S_BART_03, // 1308 S_BART_04, // 1309 S_BART_05, // 1310 S_BART_06, // 1311 S_BART_07, // 1312 S_BART_08, // 1313 S_BART_09, // 1314 S_BART_10, // 1315 S_BART_11, // 1316 S_LAMP_00, // 1317 S_LANT_00, // 1318 S_BARL_00, // 1319 S_BARL_01, // 1320 S_BARL_02, // 1321 S_BARL_03, // 1322 S_BOWL_00, // 1323 S_BOWL_01, // 1324 S_BOWL_02, // 1325 S_BOWL_03, // 1326 S_BRAZ_00, // 1327 S_BRAZ_01, // 1328 S_BRAZ_02, // 1329 S_BRAZ_03, // 1330 S_TRCH_00, // 1331 S_TRCH_01, // 1332 S_TRCH_02, // 1333 S_TRCH_03, // 1334 S_LTRH_00, // 1335 S_LTRH_01, // 1336 S_LTRH_02, // 1337 S_LTRH_03, // 1338 S_LMPC_00, // 1339 S_LMPC_01, // 1340 S_LMPC_02, // 1341 S_LMPC_03, // 1342 S_LOGS_00, // 1343 S_LOGS_01, // 1344 S_LOGS_02, // 1345 S_LOGS_03, // 1346 S_TRHO_00, // 1347 S_WATR_00, // 1348 S_MUGG_00, // 1349 S_FUSL_00, // 1350 S_CRD1_00, // 1351 S_CRD2_00, // 1352 S_TPAS_00, // 1353 S_KY1G_00, // 1354 S_KY2S_00, // 1355 S_KY3B_00, // 1356 S_HAND_00, // 1357 S_CRYS_00, // 1358 S_CRYS_01, // 1359 S_CRYS_02, // 1360 S_CRYS_03, // 1361 S_CRYS_04, // 1362 S_CRYS_05, // 1363 S_PRIS_00, // 1364 S_PWR1_00, // 1365 S_PWR2_00, // 1366 S_PWR3_00, // 1367 S_ORAC_00, // 1368 S_GYID_00, // 1369 S_FUBR_00, // 1370 S_WARE_00, // 1371 S_RCRY_00, // 1372 S_BCRY_00, // 1373 S_CHAP_00, // 1374 S_TUNL_00, // 1375 S_BLTK_00, // 1376 S_SECK_00, // 1377 S_MINE_00, // 1378 S_REBL_00, // 1379 S_PROC_00, // 1380 S_ANKH_00, // 1381 S_GOID_00, // 1382 S_STMP_00, // 1383 S_MDKT_00, // 1384 S_COIN_00, // 1385 S_CRED_00, // 1386 S_SACK_00, // 1387 S_CHST_00, // 1388 S_SHD1_00, // 1389 S_SHD1_01, // 1390 S_SHD1_02, // 1391 S_SHD1_03, // 1392 S_MASK_00, // 1393 S_UNIF_00, // 1394 S_OFIC_00, // 1395 S_PMAP_00, // 1396 S_PMAP_01, // 1397 S_PMUP_00, // 1398 S_PMUP_01, // 1399 S_BLIT_00, // 1400 S_BBOX_00, // 1401 S_MSSL_00, // 1402 S_ROKT_00, // 1403 S_BRY1_00, // 1404 S_BRY1_01, // 1405 S_CPAC_00, // 1406 S_CPAC_01, // 1407 S_PQRL_00, // 1408 S_XQRL_00, // 1409 S_GRN1_00, // 1410 S_GRN2_00, // 1411 S_BKPK_00, // 1412 S_RELC_00, // 1413 S_RIFL_00, // 1414 S_RIFL_01, // 1415 S_FLAM_00, // 1416 S_BFLM_00, // 1417 S_MMSL_00, // 1418 S_TRPD_00, // 1419 S_GRND_00, // 1420 S_CBOW_00, // 1421 S_SIGL_00, // 1422 S_SIGL_01, // 1423 S_SIGL_02, // 1424 S_SIGL_03, // 1425 S_SIGL_04, // 1426 S_LITE_00, // 1427 S_CNDL_00, // 1428 S_CLBR_00, // 1429 S_LITS_00, // 1430 S_LITB_00, // 1431 S_LITG_00, // 1432 S_ROK1_00, // 1433 S_ROK2_00, // 1434 S_ROK3_00, // 1435 S_ROK4_00, // 1436 S_LOGG_00, // 1437 S_LOGG_01, // 1438 S_LOGG_02, // 1439 S_LOGG_03, // 1440 S_RUB1_00, // 1441 S_RUB2_00, // 1442 S_RUB3_00, // 1443 S_RUB4_00, // 1444 S_RUB5_00, // 1445 S_RUB6_00, // 1446 S_RUB7_00, // 1447 S_RUB8_00, // 1448 S_CHAN_00, // 1449 S_STAT_00, // 1450 S_DSTA_00, // 1451 S_CRAB_00, // 1452 S_CAGE_00, // 1453 S_TREE_00, // 1454 S_TREE_01, // 1455 S_TREE_02, // 1456 S_TRE1_00, // 1457 S_BUSH_00, // 1458 S_SHRB_00, // 1459 S_STAK_00, // 1460 S_BAR1_00, // 1461 S_VASE_00, // 1462 S_VASE_01, // 1463 S_STOL_00, // 1464 S_POT1_00, // 1465 S_TUB1_00, // 1466 S_ANVL_00, // 1467 S_TLMP_00, // 1468 S_TLMP_01, // 1469 S_TRAY_00, // 1470 S_APOW_00, // 1471 S_AFED_00, // 1472 S_DRIP_00, // 1473 S_DRIP_01, // 1474 S_DRIP_02, // 1475 S_DRIP_03, // 1476 S_DRIP_04, // 1477 S_DRIP_05, // 1478 S_DRIP_06, // 1479 S_DRIP_07, // 1480 S_CDRP_00, // 1481 S_CDRP_01, // 1482 S_CDRP_02, // 1483 S_CDRP_03, // 1484 S_SPLH_00, // 1485 S_SPLH_01, // 1486 S_SPLH_02, // 1487 S_SPLH_03, // 1488 S_SPLH_04, // 1489 S_SPLH_05, // 1490 S_SPLH_06, // 1491 S_SPLH_07, // 1492 S_WTFT_00, // 1493 S_WTFT_01, // 1494 S_WTFT_02, // 1495 S_WTFT_03, // 1496 S_HERT_00, // 1497 S_HERT_01, // 1498 S_HERT_02, // 1499 S_TELP_00, // 1500 S_TELP_01, // 1501 S_TELP_02, // 1502 S_TELP_03, // 1503 S_MONI_00, // 1504 S_STEL_00, // 1505 S_STLA_00, // 1506 S_STLE_00, // 1507 S_HUGE_00, // 1508 S_HUGE_01, // 1509 S_HUGE_02, // 1510 S_HUGE_03, // 1511 S_STLG_00, // 1512 S_STLG_01, // 1513 S_STLG_02, // 1514 S_STLG_03, // 1515 S_STLG_04, // 1516 S_STLG_05, // 1517 NUMSTATES } statenum_t; typedef struct { spritenum_t sprite; int frame; int tics; // void (*action) (); actionf_t action; statenum_t nextstate; //int misc1; // villsa [STRIFE] unused //int misc2; // villsa [STRIFE] unused } state_t; extern state_t states[NUMSTATES]; extern const char *sprnames[]; typedef enum { MT_FIELDGUARD, //000 MT_PLAYER, //001 MT_SHOPKEEPER_W, //002 MT_SHOPKEEPER_B, //003 MT_SHOPKEEPER_A, //004 MT_SHOPKEEPER_M, //005 MT_PEASANT2_A, //006 MT_PEASANT2_B, //007 MT_PEASANT2_C, //008 MT_PEASANT5_A, //009 MT_PEASANT5_B, //010 MT_PEASANT5_C, //011 MT_PEASANT4_A, //012 MT_PEASANT4_B, //013 MT_PEASANT4_C, //014 MT_PEASANT6_A, //015 MT_PEASANT6_B, //016 MT_PEASANT6_C, //017 MT_PEASANT3_A, //018 MT_PEASANT3_B, //019 MT_PEASANT3_C, //020 MT_PEASANT8_A, //021 MT_PEASANT8_B, //022 MT_PEASANT8_C, //023 MT_PEASANT7_A, //024 MT_PEASANT7_B, //025 MT_PEASANT7_C, //026 MT_PEASANT1, //027 MT_ZOMBIE, //028 MT_BECOMING, //029 MT_ZOMBIESPAWNER, //030 MT_HUGE_TANK_1, //031 MT_HUGE_TANK_2, //032 MT_HUGE_TANK_3, //033 MT_TANK_4, //034 MT_TANK_5, //035 MT_TANK_6, //036 MT_KNEELING_GUY, //037 MT_BEGGAR1, //038 MT_BEGGAR2, //039 MT_BEGGAR3, //040 MT_BEGGAR4, //041 MT_BEGGAR5, //042 MT_REBEL1, //043 MT_REBEL2, //044 MT_REBEL3, //045 MT_REBEL4, //046 MT_REBEL5, //047 MT_REBEL6, //048 MT_RLEADER, //049 MT_RLEADER2, //050 MT_MISSILESMOKE, //051 MT_REAVER, //052 MT_GUARD1, //053 MT_GUARD2, //054 MT_GUARD3, //055 MT_GUARD4, //056 MT_GUARD5, //057 MT_GUARD6, //058 MT_GUARD7, //059 MT_GUARD8, //060 MT_SHADOWGUARD, //061 MT_PGUARD, //062 MT_CRUSADER, //063 MT_BISHOP, //064 MT_ORACLE, //065 MT_PRIEST, //066 MT_SPECTRE_A, //067 MT_NODE, //068 MT_SPECTREHEAD, //069 MT_SPECTRE_B, //070 MT_SPECTRE_C, //071 MT_SPECTRE_D, //072 MT_SPECTRE_E, //073 MT_ENTITY, //074 MT_SUBENTITY, //075 MT_NEST, //076 MT_POD, //077 MT_SIGIL_B_SHOT, //078 MT_SIGIL_SB_SHOT, //079 MT_SIGIL_C_SHOT, //080 MT_SIGIL_SC_SHOT, //081 MT_SIGIL_E_OFFSHOOT, //082 MT_SIGIL_TRAIL, //083 MT_SIGIL_E_SHOT, //084 MT_SIGIL_SE_SHOT, //085 MT_SIGIL_A_ZAP_LEFT, //086 MT_SIGIL_A_ZAP_RIGHT, //087 MT_SIGIL_A_GROUND, //088 MT_SIGIL_D_SHOT, //089 MT_SIGIL_SD_SHOT, //090 MT_SENTINEL, //091 MT_STALKER, //092 MT_INQUISITOR, //093 MT_INQARM, //094 MT_PROGRAMMER, //095 MT_PROGRAMMERBASE, //096 MT_HOOKSHOT, //097 MT_CHAINSHOT, //098 MT_MINIMISSLE, //099 MT_C_MISSILE, //100 MT_SEEKMISSILE, //101 MT_ELECARROW, //102 MT_POISARROW, //103 MT_R_LASER, //104 MT_L_LASER, //105 MT_HEGRENADE, //106 MT_PGRENADE, //107 MT_INQGRENADE, //108 MT_PFLAME, //109 MT_TORPEDO, //110 MT_TORPEDOSPREAD, //111 MT_SFIREBALL, //112 MT_C_FLAME, //113 MT_STRIFEPUFF3, //114 MT_STRIFEPUFF, //115 MT_SPARKPUFF, //116 MT_BLOOD_DEATH, //117 MT_TFOG, //118 MT_IFOG, //119 MT_TELEPORTMAN, //120 MT_MISC_01, //121 MT_TURRET, //122 MT_GATE, //123 MT_COMPUTER, //124 MT_INV_MED1, //125 MT_INV_MED2, //126 MT_INV_MED3, //127 MT_DEGNINORE, //128 MT_INV_ARMOR2, //129 MT_INV_ARMOR1, //130 MT_MISC_22, //131 MT_MISC_11, //132 MT_KEY_BASE, //133 MT_GOVSKEY, //134 MT_KEY_TRAVEL, //135 MT_KEY_ID_BLUE, //136 MT_PRISONKEY, //137 MT_KEY_HAND, //138 MT_POWER1KEY, //139 MT_POWER2KEY, //140 MT_POWER3KEY, //141 MT_KEY_GOLD, //142 MT_KEY_ID_GOLD, //143 MT_KEY_SILVER, //144 MT_KEY_ORACLE, //145 MT_MILITARYID, //146 MT_KEY_ORDER, //147 MT_KEY_WAREHOUSE, //148 MT_KEY_BRASS, //149 MT_KEY_RED_CRYSTAL, //150 MT_KEY_BLUE_CRYSTAL, //151 MT_KEY_CHAPEL, //152 MT_CATACOMBKEY, //153 MT_SECURITYKEY, //154 MT_KEY_CORE, //155 MT_KEY_MAULER, //156 MT_KEY_FACTORY, //157 MT_KEY_MINE, //158 MT_NEWKEY5, //159 MT_INV_SHADOWARMOR, //160 MT_INV_SUIT, //161 MT_QUEST_UNIFORM, //162 MT_QUEST_GUARD_UNIFORM, //163 MT_INV_SUPERMAP, //164 MT_INV_RADAR, //165 MT_BEACON, //166 MT_INV_TARGETER, //167 MT_MONY_1, //168 MT_MONY_10, //169 MT_MONY_25, //170 MT_MONY_50, //171 MT_MONY_300, //172 MT_TOKEN_RING, //173 MT_INV_CHALICE, //174 MT_TOKEN_EAR, //175 MT_INV_COMMUNICATOR, //176 MT_AGREN, //177 MT_APGREN, //178 MT_ACLIP, //179 MT_AAMMOBOX, //180 MT_AMINI, //181 MT_AMINIBOX, //182 MT_ACELL, //183 MT_APCELL, //184 MT_APAROW, //185 MT_AAROW, //186 MT_INV_SATCHEL, //187 MT_PULSE, //188 MT_RIFLESTAND, //189 MT_FLAMETHROWER, //190 MT_TOKEN_FLAME_THROWER_PARTS, //191 MT_MISSILELAUNCHER, //192 MT_BLASTER, //193 MT_CROSSBOW, //194 MT_GRENADELAUNCHER, //195 MT_SIGIL_A, //196 MT_SIGIL_B, //197 MT_SIGIL_C, //198 MT_SIGIL_D, //199 MT_SIGIL_E, //200 MT_POWER_CRYSTAL, //201 MT_RAT, //202 MT_MISC_05, //203 MT_MISC_06, //204 MT_MISC_15, //205 MT_LIGHT14, //206 MT_LIGHT13, //207 MT_LIGHT12, //208 MT_LIGHT18, //209 MT_PILLAR2, //210 MT_PILLAR3, //211 MT_PILLAR4, //212 MT_PILLAR5, //213 MT_PILLAR6, //214 MT_PILLAR7, //215 MT_CAVE2, //216 MT_CAVE3, //217 MT_CAVE4, //218 MT_CAVE6, //219 MT_CAVE7, //220 MT_CAVE5, //221 MT_LIGHT2, //222 MT_LIGHT3, //223 MT_MISC_03, //224 MT_MISC_13, //225 MT_MISC_02, //226 MT_MISC_07, //227 MT_BIO2, //228 MT_TELEPORTSTAND, //229 MT_DEADTHING1, //230 MT_DEADTHING2, //231 MT_DEADTHING3, //232 MT_DEADTHING4, //233 MT_DEADTHING5, //234 MT_DEADTHING6, //235 MT_BIO1, //236 MT_GIBS, //237 MT_MISC_04, //238 MT_LIGHT11, //239 MT_LIGHT10, //240 MT_LIGHT9, //241 MT_LIGHT8, //242 MT_MISC_14, //243 MT_LIGHT1, //244 MT_PILLAR8, //245 MT_PILLAR9, //246 MT_LIGHT15, //247 MT_LIGHT4, //248 MT_LIGHT5, //249 MT_ROCK1, //250 MT_ROCK2, //251 MT_ROCK3, //252 MT_ROCK4, //253 MT_TREE7, //254 MT_RUBBLE1, //255 MT_RUBBLE2, //256 MT_RUBBLE3, //257 MT_RUBBLE4, //258 MT_RUBBLE5, //259 MT_RUBBLE6, //260 MT_RUBBLE7, //261 MT_RUBBLE8, //262 MT_MISC_08, //263 MT_LIGHT6, //264 MT_LIGHT7, //265 MT_TREE2, //266 MT_TREE3, //267 MT_TREE4, //268 MT_TREE1, //269 MT_TREE6, //270 MT_TREE5, //271 MT_CAVE1, //272 MT_PILLAR1, //273 MT_MISC_10, //274 MT_MISC_09, //275 MT_MISC_17, //276 MT_MISC_18, //277 MT_MISC_19, //278 MT_MISC_20, //279 MT_LIGHT16, //280 MT_LIGHT17, //281 MT_MISC_21, //282 MT_MISC_12, //283 MT_MISC_26, //284 MT_MISC_23, //285 MT_MISC_24, //286 MT_MISC_25, //287 MT_COUPLING, //288 MT_COUPLING_BROKEN, //289 MT_PILLAR10, //290 MT_PILLAR11, //291 MT_PILLAR12, //292 MT_PILLAR13, //293 MT_LIGHT19, //294 MT_MEAT, //295 MT_JUNK, //296 MT_BURNDROP, //297 MT_TOKEN_AMMO, //298 MT_TOKEN_HEALTH, //299 MT_TOKEN, //300 MT_TOKEN_ALARM, //301 MT_TOKEN_DOOR1, //302 MT_TOKEN_SHOPCLOSE, //303 MT_TOKEN_PRISON_PASS, //304 MT_TOKEN_DOOR3, //305 MT_TOKEN_STAMINA, //306 MT_TOKEN_NEW_ACCURACY, //307 MT_TOKEN_REPORT, //308 MT_TOKEN_TOUGHNESS, //309 MT_TOKEN_ACCURACY, //310 MT_TOKEN_ORACLE_PASS, //311 MT_TOKEN_QUEST1, //312 MT_TOKEN_QUEST2, //313 MT_TOKEN_QUEST3, //314 MT_TOKEN_QUEST4, //315 MT_TOKEN_QUEST5, //316 MT_TOKEN_QUEST6, //317 MT_TOKEN_QUEST7, //318 MT_TOKEN_QUEST8, //319 MT_TOKEN_QUEST9, //320 MT_TOKEN_QUEST10, //321 MT_TOKEN_QUEST11, //322 MT_TOKEN_QUEST12, //323 MT_TOKEN_QUEST13, //324 MT_TOKEN_CRYSTAL, //325 MT_TOKEN_QUEST15, //326 MT_GATEQUEST, //327 MT_TOKEN_QUEST17, //328 MT_TOKEN_QUEST18, //329 MT_TOKEN_QUEST19, //330 MT_TOKEN_QUEST20, //331 MT_TOKEN_BISHOP, //332 MT_TOKEN_QUEST22, //333 MT_TOKEN_ORACLE, //334 MT_TOKEN_MACIL, //335 MT_TOKEN_QUEST25, //336 MT_TOKEN_LOREMASTER, //337 MT_SECRQUEST, //338 MT_TOKEN_QUEST28, //339 MT_TOKEN_QUEST29, //340 MT_TOKEN_QUEST30, //341 MT_TOKEN_QUEST31, //342 MT_SLIDESHOW, //343 NUMMOBJTYPES } mobjtype_t; // villsa [STRIFE] updated mobjinfo struct typedef struct { int doomednum; int spawnstate; int spawnhealth; int seestate; int seesound; int reactiontime; int attacksound; int painstate; int painchance; int painsound; int meleestate; int missilestate; int crashstate; int deathstate; int xdeathstate; int deathsound; int speed; int radius; int height; int mass; int damage; int activesound; int flags; const char *name; } mobjinfo_t; extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; #endif crispy-doom-crispy-doom-5.6.4/src/strife/m_menu.c000066400000000000000000001515621360717211000217550ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM selection menu, options, episode etc. // Sliders and icons. Kinda widget stuff. // #include #include #include "doomdef.h" #include "doomkeys.h" #include "dstrings.h" #include "d_main.h" #include "deh_main.h" #include "i_input.h" #include "i_swap.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "z_zone.h" #include "v_video.h" #include "w_wad.h" #include "r_local.h" #include "hu_stuff.h" #include "g_game.h" #include "m_argv.h" #include "m_controls.h" #include "m_misc.h" #include "m_saves.h" // [STRIFE] #include "p_saveg.h" #include "s_sound.h" #include "doomstat.h" // Data. #include "sounds.h" #include "m_menu.h" #include "p_dialog.h" extern void M_QuitStrife(int); extern patch_t* hu_font[HU_FONTSIZE]; extern boolean message_dontfuckwithme; extern boolean chat_on; // in heads-up code extern boolean sendsave; // [STRIFE] // // defaulted values // int mouseSensitivity = 5; // [STRIFE]: removed this entirely // Show messages has default, 0 = off, 1 = on //int showMessages = 1; // Blocky mode, has default, 0 = high, 1 = normal int detailLevel = 0; int screenblocks = 10; // [STRIFE] default 10, not 9 // temp for screenblocks (0-9) int screenSize; // -1 = no quicksave slot picked! int quickSaveSlot; // 1 = message to be printed int messageToPrint; // ...and here is the message string! const char *messageString; // message x & y int messx; int messy; int messageLastMenuActive; // timed message = no input from user boolean messageNeedsInput; void (*messageRoutine)(int response); // [crispy] intermediate gamma levels char gammamsg[5+4][26+2] = { GAMMALVL0, GAMMALVL05, GAMMALVL1, GAMMALVL15, GAMMALVL2, GAMMALVL25, GAMMALVL3, GAMMALVL35, GAMMALVL4 }; // we are going to be entering a savegame string int saveStringEnter; int saveSlot; // which slot to save in int saveCharIndex; // which char we're editing // old save description before edit char saveOldString[SAVESTRINGSIZE]; boolean inhelpscreens; boolean menuactive; boolean menupause; // haleyjd 08/29/10: [STRIFE] New global int menupausetime; // haleyjd 09/04/10: [STRIFE] New global boolean menuindialog; // haleyjd 09/04/10: ditto // haleyjd 08/27/10: [STRIFE] SKULLXOFF == -28, LINEHEIGHT == 19 #define CURSORXOFF -28 #define LINEHEIGHT 19 extern boolean sendpause; char savegamestrings[10][SAVESTRINGSIZE]; char endstring[160]; // haleyjd 09/04/10: [STRIFE] Moved menuitem / menu structures into header // because they are needed externally by the dialog engine. // haleyjd 08/27/10: [STRIFE] skull* stuff changed to cursor* stuff short itemOn; // menu item skull is on short cursorAnimCounter; // skull animation counter short whichCursor; // which skull to draw // graphic name of cursors // haleyjd 08/27/10: [STRIFE] M_SKULL* -> M_CURS* const char *cursorName[8] = {"M_CURS1", "M_CURS2", "M_CURS3", "M_CURS4", "M_CURS5", "M_CURS6", "M_CURS7", "M_CURS8" }; // haleyjd 20110210 [STRIFE]: skill level for menus int menuskill; // current menudef menu_t* currentMenu; // haleyjd 03/01/13: [STRIFE] v1.31-only: // Keeps track of whether the save game menu is being used to name a new // character slot, or to just save the current game. In the v1.31 disassembly // this was the new dword_8632C variable. boolean namingCharacter; // // 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_ReadThis3(int choice); // [STRIFE] //void M_ChangeMessages(int choice); [STRIFE] void M_ChangeSensitivity(int choice); void M_SfxVol(int choice); void M_VoiceVol(int choice); // [STRIFE] void M_MusicVol(int choice); void M_SizeDisplay(int choice); void M_StartGame(int choice); void M_Sound(int choice); //void M_FinishReadThis(int choice); - [STRIFE] unused 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_DrawReadThis3(void); // [STRIFE] void M_DrawNewGame(void); void M_DrawEpisode(void); void M_DrawOptions(void); void M_DrawSound(void); void M_DrawLoad(void); void M_DrawSave(void); void M_DrawSaveLoadBorder(int x,int y); void M_SetupNextMenu(menu_t *menudef); void M_DrawThermo(int x,int y,int thermWidth,int thermDot); void M_DrawEmptyCell(menu_t *menu,int item); void M_DrawSelCell(menu_t *menu,int item); int M_StringWidth(const char *string); int M_StringHeight(const char *string); void M_StartMessage(const char *string,void *routine,boolean input); void M_StopMessage(void); // // DOOM MENU // enum { newgame = 0, options, loadgame, savegame, readthis, quitdoom, main_end } main_e; menuitem_t MainMenu[]= { {1,"M_NGAME",M_NewGame,'n'}, {1,"M_OPTION",M_Options,'o'}, {1,"M_LOADG",M_LoadGame,'l'}, {1,"M_SAVEG",M_SaveGame,'s'}, // Another hickup with Special edition. {1,"M_RDTHIS",M_ReadThis,'h'}, // haleyjd 08/28/10: 'r' -> 'h' {1,"M_QUITG",M_QuitStrife,'q'} }; menu_t MainDef = { main_end, NULL, MainMenu, M_DrawMainMenu, 97,45, // haleyjd 08/28/10: [STRIFE] changed y coord 0 }; // // EPISODE SELECT // /* enum { ep1, ep2, ep3, ep4, ep_end } episodes_e; menuitem_t EpisodeMenu[]= { {1,"M_EPI1", M_Episode,'k'}, {1,"M_EPI2", M_Episode,'t'}, {1,"M_EPI3", M_Episode,'i'}, {1,"M_EPI4", M_Episode,'t'} }; menu_t EpiDef = { ep_end, // # of menu items &MainDef, // previous menu EpisodeMenu, // menuitem_t -> M_DrawEpisode, // drawing routine -> 48,63, // x,y ep1 // lastOn }; */ // // NEW GAME // enum { killthings, toorough, hurtme, violence, nightmare, newg_end } newgame_e; menuitem_t NewGameMenu[]= { // haleyjd 08/28/10: [STRIFE] changed all shortcut letters {1,"M_JKILL", M_ChooseSkill, 't'}, {1,"M_ROUGH", M_ChooseSkill, 'r'}, {1,"M_HURT", M_ChooseSkill, 'v'}, {1,"M_ULTRA", M_ChooseSkill, 'e'}, {1,"M_NMARE", M_ChooseSkill, 'b'} }; menu_t NewDef = { newg_end, // # of menu items &MainDef, // previous menu - haleyjd [STRIFE] changed to MainDef NewGameMenu, // menuitem_t -> M_DrawNewGame, // drawing routine -> 48,63, // x,y toorough // lastOn - haleyjd [STRIFE]: default to skill 1 }; // // OPTIONS MENU // enum { // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail. endgame, scrnsize, option_empty1, soundvol, opt_end } options_e; menuitem_t OptionsMenu[]= { // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail. {1,"M_ENDGAM", M_EndGame,'e'}, {2,"M_SCRNSZ", M_SizeDisplay,'s'}, {-1,"",0,'\0'}, {1,"M_SVOL", M_Sound,'s'} }; menu_t OptionsDef = { opt_end, &MainDef, OptionsMenu, M_DrawOptions, 60,37, 0 }; // // Read This! MENU 1 & 2 & [STRIFE] 3 // enum { rdthsempty1, read1_end } read_e; menuitem_t ReadMenu1[] = { {1,"",M_ReadThis2,0} }; menu_t ReadDef1 = { read1_end, &MainDef, ReadMenu1, M_DrawReadThis1, 280,185, 0 }; enum { rdthsempty2, read2_end } read_e2; menuitem_t ReadMenu2[]= { {1,"",M_ReadThis3,0} // haleyjd 08/28/10: [STRIFE] Go to ReadThis3 }; menu_t ReadDef2 = { read2_end, &ReadDef1, ReadMenu2, M_DrawReadThis2, 250,185, // haleyjd 08/28/10: [STRIFE] changed coords 0 }; // haleyjd 08/28/10: Added Read This! menu 3 enum { rdthsempty3, read3_end } read_e3; menuitem_t ReadMenu3[]= { {1,"",M_ClearMenus,0} }; menu_t ReadDef3 = { read3_end, &ReadDef2, ReadMenu3, M_DrawReadThis3, 250, 185, 0 }; // // SOUND VOLUME MENU // enum { sfx_vol, sfx_empty1, music_vol, sfx_empty2, voice_vol, sfx_empty3, sfx_mouse, sfx_empty4, sound_end } sound_e; // haleyjd 08/29/10: // [STRIFE] // * Added voice volume // * Moved mouse sensitivity here (who knows why...) menuitem_t SoundMenu[]= { {2,"M_SFXVOL",M_SfxVol,'s'}, {-1,"",0,'\0'}, {2,"M_MUSVOL",M_MusicVol,'m'}, {-1,"",0,'\0'}, {2,"M_VOIVOL",M_VoiceVol,'v'}, {-1,"",0,'\0'}, {2,"M_MSENS",M_ChangeSensitivity,'m'}, {-1,"",0,'\0'} }; menu_t SoundDef = { sound_end, &OptionsDef, SoundMenu, M_DrawSound, 80,35, // [STRIFE] changed y coord 64 -> 35 0 }; // // LOAD GAME MENU // enum { load1, load2, load3, load4, load5, load6, load_end } load_e; menuitem_t LoadMenu[]= { {1,"", M_LoadSelect,'1'}, {1,"", M_LoadSelect,'2'}, {1,"", M_LoadSelect,'3'}, {1,"", M_LoadSelect,'4'}, {1,"", M_LoadSelect,'5'}, {1,"", M_LoadSelect,'6'} }; menu_t LoadDef = { load_end, &MainDef, LoadMenu, M_DrawLoad, 80,54, 0 }; // // SAVE GAME MENU // menuitem_t SaveMenu[]= { {1,"", M_SaveSelect,'1'}, {1,"", M_SaveSelect,'2'}, {1,"", M_SaveSelect,'3'}, {1,"", M_SaveSelect,'4'}, {1,"", M_SaveSelect,'5'}, {1,"", M_SaveSelect,'6'} }; menu_t SaveDef = { load_end, &MainDef, SaveMenu, M_DrawSave, 80,54, 0 }; void M_DrawNameChar(void); // // NAME CHARACTER MENU // // [STRIFE] // haleyjd 20110210: New "Name Your Character" Menu // menu_t NameCharDef = { load_end, &NewDef, SaveMenu, M_DrawNameChar, 80,54, 0 }; // // M_ReadSaveStrings // read the strings from the savegame files // // [STRIFE] // haleyjd 20110210: Rewritten to read "name" file in each slot directory // void M_ReadSaveStrings(void) { FILE *handle; int i; char *fname = NULL; for(i = 0; i < load_end; i++) { int retval; if(fname) Z_Free(fname); fname = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(i, "\\name")); handle = fopen(fname, "rb"); if(handle == NULL) { M_StringCopy(savegamestrings[i], DEH_String(EMPTYSTRING), sizeof(savegamestrings[i])); LoadMenu[i].status = 0; continue; } retval = fread(savegamestrings[i], 1, SAVESTRINGSIZE, handle); fclose(handle); LoadMenu[i].status = retval == SAVESTRINGSIZE; } if(fname) Z_Free(fname); } // // M_DrawNameChar // // haleyjd 09/22/10: [STRIFE] New function // Handler for drawing the "Name Your Character" menu. // void M_DrawNameChar(void) { int i; M_WriteText(72, 28, DEH_String("Name Your Character")); for (i = 0;i < load_end; i++) { M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]); } if (saveStringEnter) { i = M_StringWidth(savegamestrings[quickSaveSlot]); M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*quickSaveSlot,"_"); } } // // M_DoNameChar // // haleyjd 09/22/10: [STRIFE] New function // Handler for items in the "Name Your Character" menu. // void M_DoNameChar(int choice) { int map; // 20130301: clear naming character flag for 1.31 save logic if(gameversion == exe_strife_1_31) namingCharacter = false; sendsave = 1; ClearTmp(); G_WriteSaveName(choice, savegamestrings[choice]); quickSaveSlot = choice; SaveDef.lastOn = choice; ClearSlot(); FromCurr(); if(isdemoversion) map = 33; else map = 2; G_DeferedInitNew(menuskill, map); M_ClearMenus(0); } // // M_LoadGame & Cie. // void M_DrawLoad(void) { int i; V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String("M_LOADG"), PU_CACHE)); for (i = 0;i < load_end; i++) { M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]); } } // // Draw border for the savegame description // void M_DrawSaveLoadBorder(int x,int y) { int i; V_DrawPatchDirect(x - 8, y + 7, W_CacheLumpName(DEH_String("M_LSLEFT"), PU_CACHE)); for (i = 0;i < 24;i++) { V_DrawPatchDirect(x, y + 7, W_CacheLumpName(DEH_String("M_LSCNTR"), PU_CACHE)); x += 8; } V_DrawPatchDirect(x, y + 7, W_CacheLumpName(DEH_String("M_LSRGHT"), PU_CACHE)); } // // User wants to load this game // void M_LoadSelect(int choice) { // [STRIFE]: completely rewritten char *name = NULL; G_WriteSaveName(choice, savegamestrings[choice]); ToCurr(); // use safe & portable filepath concatenation for Choco name = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(choice, "")); G_ReadCurrent(name); quickSaveSlot = choice; M_ClearMenus(0); Z_Free(name); } // // Selected from DOOM menu // // [STRIFE] Verified unmodified // void M_LoadGame (int choice) { if (netgame) { M_StartMessage(DEH_String(LOADNET), NULL, false); return; } M_SetupNextMenu(&LoadDef); M_ReadSaveStrings(); } // // M_SaveGame & Cie. // void M_DrawSave(void) { int i; V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String("M_SAVEG"), PU_CACHE)); for (i = 0;i < load_end; i++) { M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]); } if (saveStringEnter) { i = M_StringWidth(savegamestrings[quickSaveSlot]); M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*quickSaveSlot,"_"); } } // // M_Responder calls this when user is finished // void M_DoSave(int slot) { // [STRIFE]: completely rewritten if(slot >= 0) { sendsave = 1; G_WriteSaveName(slot, savegamestrings[slot]); M_ClearMenus(0); quickSaveSlot = slot; // haleyjd 20130922: slight divergence. We clear the destination slot // of files here, which vanilla did not do. As a result, 1.31 had // broken save behavior to the point of unusability. fraggle agrees // this is detrimental enough to be fixed - unconditionally, for now. ClearSlot(); FromCurr(); } else M_StartMessage(DEH_String(QSAVESPOT), NULL, false); } // // User wants to save. Start string input for M_Responder // void M_SaveSelect(int choice) { int x, y; // we are going to be intercepting all chars saveStringEnter = 1; // We need to turn on text input: x = LoadDef.x - 11; y = LoadDef.y + choice * LINEHEIGHT - 4; I_StartTextInput(x, y, x + 8 + 24 * 8 + 8, y + LINEHEIGHT - 2); // [STRIFE] quickSaveSlot = choice; //saveSlot = choice; M_StringCopy(saveOldString, savegamestrings[choice], sizeof(saveOldString)); if (!strcmp(savegamestrings[choice], DEH_String(EMPTYSTRING))) savegamestrings[choice][0] = 0; saveCharIndex = strlen(savegamestrings[choice]); } // // Selected from DOOM menu // void M_SaveGame (int choice) { // [STRIFE] if (netgame) { // haleyjd 20110211: Hooray for Rogue's awesome multiplayer support... M_StartMessage(DEH_String("You can't save a netgame"), NULL, false); return; } if (!usergame) { M_StartMessage(DEH_String(SAVEDEAD),NULL,false); return; } if (gamestate != GS_LEVEL) return; // [STRIFE] if(gameversion == exe_strife_1_31) { // haleyjd 20130301: in 1.31, we can choose a slot again. M_SetupNextMenu(&SaveDef); M_ReadSaveStrings(); } else { // In 1.2 and lower, you save over your character slot exclusively M_ReadSaveStrings(); M_DoSave(quickSaveSlot); } } // // M_QuickSave // char tempstring[80]; void M_QuickSaveResponse(int key) { if (key == key_menu_confirm) { M_DoSave(quickSaveSlot); S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound } } void M_QuickSave(void) { if (netgame) { // haleyjd 20110211 [STRIFE]: More fun... M_StartMessage(DEH_String("You can't save a netgame"), NULL, false); return; } if (!usergame) { 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; } DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]); M_StartMessage(tempstring,M_QuickSaveResponse,true); } // // M_QuickLoadResponse // void M_QuickLoadResponse(int key) { if (key == key_menu_confirm) { M_LoadSelect(quickSaveSlot); S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound } } // // M_QuickLoad // // [STRIFE] Verified unmodified // void M_QuickLoad(void) { if (netgame) { M_StartMessage(DEH_String(QLOADNET),NULL,false); return; } if (quickSaveSlot < 0) { M_StartMessage(DEH_String(QSAVESPOT),NULL,false); return; } DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]); M_StartMessage(tempstring,M_QuickLoadResponse,true); } // // Read This Menus // Had a "quick hack to fix romero bug" // haleyjd 08/28/10: [STRIFE] Draw HELP1, unconditionally. // void M_DrawReadThis1(void) { inhelpscreens = true; V_DrawPatchDirect (0, 0, W_CacheLumpName(DEH_String("HELP1"), PU_CACHE)); } // // Read This Menus // haleyjd 08/28/10: [STRIFE] Not optional, draws HELP2 // void M_DrawReadThis2(void) { inhelpscreens = true; V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP2"), PU_CACHE)); } // // Read This Menus // haleyjd 08/28/10: [STRIFE] New function to draw HELP3. // void M_DrawReadThis3(void) { inhelpscreens = true; V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP3"), PU_CACHE)); } // // Change Sfx & Music volumes // // haleyjd 08/29/10: [STRIFE] // * Changed title graphic coordinates // * Added voice volume and sensitivity sliders // void M_DrawSound(void) { V_DrawPatchDirect (100, 10, W_CacheLumpName(DEH_String("M_SVOL"), PU_CACHE)); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1), 16,sfxVolume); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1), 16,musicVolume); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(voice_vol+1), 16,voiceVolume); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_mouse+1), 16,mouseSensitivity); } void M_Sound(int choice) { M_SetupNextMenu(&SoundDef); } void M_SfxVol(int choice) { switch(choice) { case 0: if (sfxVolume) sfxVolume--; break; case 1: if (sfxVolume < 15) sfxVolume++; break; } S_SetSfxVolume(sfxVolume * 8); } // // M_VoiceVol // // haleyjd 08/29/10: [STRIFE] New function // Sets voice volume level. // void M_VoiceVol(int choice) { switch(choice) { case 0: if (voiceVolume) voiceVolume--; break; case 1: if (voiceVolume < 15) voiceVolume++; break; } S_SetVoiceVolume(voiceVolume * 8); } void M_MusicVol(int choice) { switch(choice) { case 0: if (musicVolume) musicVolume--; break; case 1: if (musicVolume < 15) musicVolume++; break; } S_SetMusicVolume(musicVolume * 8); } // // M_DrawMainMenu // // haleyjd 08/27/10: [STRIFE] Changed x coordinate; M_DOOM -> M_STRIFE // void M_DrawMainMenu(void) { V_DrawPatchDirect(84, 2, W_CacheLumpName(DEH_String("M_STRIFE"), PU_CACHE)); } // // M_NewGame // // haleyjd 08/31/10: [STRIFE] Changed M_NEWG -> M_NGAME // void M_DrawNewGame(void) { V_DrawPatchDirect(96, 14, W_CacheLumpName(DEH_String("M_NGAME"), PU_CACHE)); V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_SKILL"), PU_CACHE)); } void M_NewGame(int choice) { if (netgame && !demoplayback) { M_StartMessage(DEH_String(NEWGAME),NULL,false); return; } // haleyjd 09/07/10: [STRIFE] Removed Chex Quest and DOOM gamemodes if(gameversion == exe_strife_1_31) namingCharacter = true; // for 1.31 save logic M_SetupNextMenu(&NewDef); } // // M_Episode // // haleyjd: [STRIFE] Unused /* int epi; void M_DrawEpisode(void) { V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_EPISOD"), PU_CACHE)); } void M_VerifyNightmare(int key) { if (key != key_menu_confirm) return; G_DeferedInitNew(nightmare, 1); M_ClearMenus (0); } */ void M_ChooseSkill(int choice) { // haleyjd 09/07/10: Removed nightmare confirmation // [STRIFE]: start "Name Your Character" menu menuskill = choice; currentMenu = &NameCharDef; itemOn = NameCharDef.lastOn; M_ReadSaveStrings(); } /* // haleyjd [STRIFE] Unused void M_Episode(int choice) { if ( (gamemode == shareware) && choice) { M_StartMessage(DEH_String(SWSTRING),NULL,false); M_SetupNextMenu(&ReadDef1); return; } // Yet another hack... if ( (gamemode == registered) && (choice > 2)) { fprintf( stderr, "M_Episode: 4th episode requires UltimateDOOM\n"); choice = 0; } epi = choice; M_SetupNextMenu(&NewDef); } */ // // M_Options // char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"}; char msgNames[2][9] = {"M_MSGOFF","M_MSGON"}; void M_DrawOptions(void) { // haleyjd 08/27/10: [STRIFE] M_OPTTTL -> M_OPTION V_DrawPatchDirect(108, 15, W_CacheLumpName(DEH_String("M_OPTION"), PU_CACHE)); // haleyjd 08/26/10: [STRIFE] Removed messages, sensitivity, detail. M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1), 9,screenSize); } void M_Options(int choice) { M_SetupNextMenu(&OptionsDef); } // // M_AutoUseHealth // // [STRIFE] New function // haleyjd 20110211: toggle autouse health state // void M_AutoUseHealth(void) { if(!netgame && usergame) { players[consoleplayer].cheats ^= CF_AUTOHEALTH; if(players[consoleplayer].cheats & CF_AUTOHEALTH) players[consoleplayer].message = DEH_String("Auto use health ON"); else players[consoleplayer].message = DEH_String("Auto use health OFF"); } } // // M_ChangeShowText // // [STRIFE] New function // void M_ChangeShowText(void) { dialogshowtext ^= true; if(dialogshowtext) players[consoleplayer].message = DEH_String("Conversation Text On"); else players[consoleplayer].message = DEH_String("Conversation Text Off"); } // // Toggle messages on/off // // [STRIFE] Messages cannot be disabled in Strife /* void M_ChangeMessages(int choice) { // warning: unused parameter `int choice' choice = 0; showMessages = 1 - showMessages; if (!showMessages) players[consoleplayer].message = DEH_String(MSGOFF); else players[consoleplayer].message = DEH_String(MSGON); message_dontfuckwithme = true; } */ // // M_EndGame // void M_EndGameResponse(int key) { if (key != key_menu_confirm) return; currentMenu->lastOn = itemOn; M_ClearMenus (0); D_StartTitle (); } void M_EndGame(int choice) { choice = 0; if (!usergame) { S_StartSound(NULL,sfx_oof); return; } if (netgame) { M_StartMessage(DEH_String(NETEND),NULL,false); return; } M_StartMessage(DEH_String(ENDGAME),M_EndGameResponse,true); } // // M_ReadThis // void M_ReadThis(int choice) { choice = 0; M_SetupNextMenu(&ReadDef1); } // // M_ReadThis2 // // haleyjd 08/28/10: [STRIFE] Eliminated DOOM stuff. // void M_ReadThis2(int choice) { choice = 0; M_SetupNextMenu(&ReadDef2); } // // M_ReadThis3 // // haleyjd 08/28/10: [STRIFE] New function. // void M_ReadThis3(int choice) { choice = 0; M_SetupNextMenu(&ReadDef3); } /* // haleyjd 08/28/10: [STRIFE] Not used. void M_FinishReadThis(int choice) { choice = 0; M_SetupNextMenu(&MainDef); } */ #if 0 extern void F_StartCast(void); // // M_CheckStartCast // // [STRIFE] New but unused function. Was going to start a cast // call from within the menu system... not functional even in // the earliest demo version. // void M_CheckStartCast() { if(usergame) { M_StartMessage(DEH_String("You have to end your game first."), NULL, false); return; } F_StartCast(); M_ClearMenus(0); } #endif // // M_QuitResponse // // haleyjd 09/11/10: [STRIFE] Modifications to start up endgame // demosequence. // void M_QuitResponse(int key) { char buffer[20]; if (key != key_menu_confirm) return; if(netgame) I_Quit(); else { DEH_snprintf(buffer, sizeof(buffer), "qfmrm%i", gametic % 8 + 1); I_StartVoice(buffer); D_QuitGame(); } } /* // haleyjd 09/11/10: [STRIFE] Unused static char *M_SelectEndMessage(void) { } */ // // M_QuitStrife // // [STRIFE] Renamed from M_QuitDOOM // haleyjd 09/11/10: No randomized text message; that's taken care of // by the randomized voice message after confirmation. // void M_QuitStrife(int choice) { DEH_snprintf(endstring, sizeof(endstring), "Do you really want to leave?\n\n" DOSY); M_StartMessage(endstring, M_QuitResponse, true); } void M_ChangeSensitivity(int choice) { switch(choice) { case 0: if (mouseSensitivity) mouseSensitivity--; break; case 1: if (mouseSensitivity < 9) mouseSensitivity++; break; } } /* // haleyjd [STRIFE] Unused void M_ChangeDetail(int choice) { choice = 0; detailLevel = 1 - detailLevel; R_SetViewSize (screenblocks, detailLevel); if (!detailLevel) players[consoleplayer].message = DEH_String(DETAILHI); else players[consoleplayer].message = DEH_String(DETAILLO); } */ // [STRIFE] Verified unmodified void M_SizeDisplay(int choice) { switch(choice) { case 0: if (screenSize > 0) { screenblocks--; screenSize--; } break; case 1: if (screenSize < 8) { screenblocks++; screenSize++; } break; } R_SetViewSize (screenblocks, detailLevel); } // // Menu Functions // // // M_DrawThermo // // haleyjd 08/28/10: [STRIFE] Changes to some patch coordinates. // void M_DrawThermo ( int x, int y, int thermWidth, int thermDot ) { int xx; int yy; // [STRIFE] Needs a temp y coordinate variable int i; xx = x; yy = y + 6; // [STRIFE] +6 to y coordinate V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERML"), PU_CACHE)); xx += 8; for (i=0;ix - 10, menu->y + item * LINEHEIGHT - 1, W_CacheLumpName(DEH_String("M_CELL1"), PU_CACHE)); } void M_DrawSelCell ( menu_t* menu, int item ) { V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1, W_CacheLumpName(DEH_String("M_CELL2"), PU_CACHE)); } void M_StartMessage ( const char *string, void* routine, boolean input ) { messageLastMenuActive = menuactive; messageToPrint = 1; messageString = string; messageRoutine = routine; messageNeedsInput = input; menuactive = true; return; } void M_StopMessage(void) { menuactive = messageLastMenuActive; messageToPrint = 0; } // // Find string width from hu_font chars // int M_StringWidth(const char *string) { size_t i; int w = 0; int c; for (i = 0;i < strlen(string);i++) { c = toupper(string[i]) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE) w += 4; else w += SHORT (hu_font[c]->width); } return w; } // // Find string height from hu_font chars // int M_StringHeight(const char *string) { size_t i; int h; int height = SHORT(hu_font[0]->height); h = height; for (i = 0;i < strlen(string);i++) if (string[i] == '\n') h += height; return h; } // // M_WriteText // // Write a string using the hu_font // haleyjd 09/04/10: [STRIFE] // * Rogue made a lot of changes to this for the dialog system. // int M_WriteText ( int x, int y, const char* string) // haleyjd: made const for safety w/dialog engine { int w; const char* ch; int c; int cx; int cy; ch = string; cx = x; cy = y; while(1) { c = *ch++; if (!c) break; // haleyjd 09/04/10: [STRIFE] Don't draw spaces at the start of lines. if(c == ' ' && cx == x) continue; if (c == '\n') { cx = x; cy += 11; // haleyjd 09/04/10: [STRIFE]: Changed 12 -> 11 continue; } c = toupper(c) - HU_FONTSTART; if (c < 0 || c>= HU_FONTSIZE) { cx += 4; continue; } w = SHORT (hu_font[c]->width); // haleyjd 09/04/10: [STRIFE] Different linebreak handling if (cx + w > ORIGWIDTH - 20) { cx = x; cy += 11; --ch; } else { V_DrawPatchDirect(cx, cy, hu_font[c]); cx += w; } } // [STRIFE] Return final y coordinate. return cy + 12; } // // M_DialogDimMsg // // [STRIFE] New function // haleyjd 09/04/10: Painstakingly transformed from the assembly code, as the // decompiler could not touch it. Redimensions a string to fit on screen, leaving // at least a 20 pixel margin on the right side. The string passed in must be // writable. // void M_DialogDimMsg(int x, int y, char *str, boolean useyfont) { int rightbound = (ORIGWIDTH - 20) - x; patch_t **fontarray; // ebp int linewidth = 0; // esi int i = 0; // edx char *message = str; // edi char bl; // bl if(useyfont) fontarray = yfont; else fontarray = hu_font; bl = toupper(*message); if(!bl) return; // outer loop - run to end of string do { if(bl != '\n') { int charwidth; // eax int tempwidth; // ecx if(bl < HU_FONTSTART || bl > HU_FONTEND) charwidth = 4; else charwidth = SHORT(fontarray[bl - HU_FONTSTART]->width); tempwidth = linewidth + charwidth; // Test if the line still fits within the boundary... if(tempwidth >= rightbound) { // Doesn't fit... char *tempptr = &message[i]; // ebx char al; // al // inner loop - run backward til a space (or the start of the // string) is found, subtracting width off the current line. // BUG: shouldn't we stop at a previous '\n' too? while(*tempptr != ' ' && i > 0) { tempptr--; // BUG: they didn't add the first char to linewidth yet... linewidth -= charwidth; i--; al = toupper(*tempptr); if(al < HU_FONTSTART || al > HU_FONTEND) charwidth = 4; else charwidth = SHORT(fontarray[al - HU_FONTSTART]->width); } // Replace the space with a linebreak. // BUG: what if i is zero? ... infinite loop time! message[i] = '\n'; linewidth = 0; } else { // The line does fit. // Spaces at the start of a line don't count though. if(!(bl == ' ' && linewidth == 0)) linewidth += charwidth; } } else linewidth = 0; // '\n' seen, so reset the line width } while((bl = toupper(message[++i])) != 0); // step to the next character } // These keys evaluate to a "null" key in Vanilla Doom that allows weird // jumping in the menus. Preserve this behavior for accuracy. static boolean IsNullKey(int key) { return key == KEY_PAUSE || key == KEY_CAPSLOCK || key == KEY_SCRLCK || key == KEY_NUMLOCK; } // // CONTROL PANEL // // // M_Responder // boolean M_Responder (event_t* ev) { int ch; int key; int i; static int joywait = 0; static int mousewait = 0; static int mousey = 0; static int lasty = 0; static int mousex = 0; static int lastx = 0; // In testcontrols mode, none of the function keys should do anything // - the only key is escape to quit. if (testcontrols) { if (ev->type == ev_quit || (ev->type == ev_keydown && (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit))) { I_Quit(); return true; } return false; } // "close" button pressed on window? if (ev->type == ev_quit) { // First click on close button = bring up quit confirm message. // Second click on close button = confirm quit if (menuactive && messageToPrint && messageRoutine == M_QuitResponse) { M_QuitResponse(key_menu_confirm); } else { S_StartSound(NULL, sfx_swtchn); M_QuitStrife(0); } return true; } // key is the key pressed, ch is the actual character typed ch = 0; key = -1; if (ev->type == ev_joystick && joywait < I_GetTime()) { if (ev->data3 < 0) { key = key_menu_up; joywait = I_GetTime() + 5; } else if (ev->data3 > 0) { key = key_menu_down; joywait = I_GetTime() + 5; } if (ev->data2 < 0) { key = key_menu_left; joywait = I_GetTime() + 2; } else if (ev->data2 > 0) { key = key_menu_right; joywait = I_GetTime() + 2; } if (ev->data1&1) { key = key_menu_forward; joywait = I_GetTime() + 5; } if (ev->data1&2) { key = key_menu_back; joywait = I_GetTime() + 5; } if (joybmenu >= 0 && (ev->data1 & (1 << joybmenu)) != 0) { key = key_menu_activate; joywait = I_GetTime() + 5; } } else { if (ev->type == ev_mouse && mousewait < I_GetTime()) { mousey += ev->data3; if (mousey < lasty-30) { key = key_menu_down; mousewait = I_GetTime() + 5; mousey = lasty -= 30; } else if (mousey > lasty+30) { key = key_menu_up; mousewait = I_GetTime() + 5; mousey = lasty += 30; } mousex += ev->data2; if (mousex < lastx-30) { key = key_menu_left; mousewait = I_GetTime() + 5; mousex = lastx -= 30; } else if (mousex > lastx+30) { key = key_menu_right; mousewait = I_GetTime() + 5; mousex = lastx += 30; } if (ev->data1&1) { key = key_menu_forward; mousewait = I_GetTime() + 15; mouse_fire_countdown = 5; // villsa [STRIFE] } if (ev->data1&2) { key = key_menu_back; mousewait = I_GetTime() + 15; } } else { if (ev->type == ev_keydown) { key = ev->data1; ch = ev->data2; } } } if (key == -1) return false; // Save Game string input if (saveStringEnter) { switch(key) { case KEY_BACKSPACE: if (saveCharIndex > 0) { saveCharIndex--; savegamestrings[quickSaveSlot][saveCharIndex] = 0; } break; case KEY_ESCAPE: saveStringEnter = 0; I_StopTextInput(); M_StringCopy(savegamestrings[quickSaveSlot], saveOldString, sizeof(savegamestrings[quickSaveSlot])); break; case KEY_ENTER: // [STRIFE] saveStringEnter = 0; I_StopTextInput(); if(gameversion == exe_strife_1_31 && !namingCharacter) { // In 1.31, we can be here as a result of normal saving again, // whereas in 1.2 this only ever happens when naming your // character to begin a new game. M_DoSave(quickSaveSlot); return true; } if (savegamestrings[quickSaveSlot][0]) M_DoNameChar(quickSaveSlot); break; default: // Savegame name entry. This is complicated. // Vanilla has a bug where the shift key is ignored when entering // a savegame name. If vanilla_keyboard_mapping is on, we want // to emulate this bug by using ev->data1. But if it's turned off, // it implies the user doesn't care about Vanilla emulation: // instead, use ev->data3 which gives the fully-translated and // modified key input. if (ev->type != ev_keydown) { break; } if (vanilla_keyboard_mapping) { ch = ev->data1; } else { ch = ev->data3; } ch = toupper(ch); if (ch != ' ' && (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE)) { break; } if (ch >= 32 && ch <= 127 && saveCharIndex < SAVESTRINGSIZE-1 && M_StringWidth(savegamestrings[quickSaveSlot]) < (SAVESTRINGSIZE-2)*8) { savegamestrings[quickSaveSlot][saveCharIndex++] = ch; savegamestrings[quickSaveSlot][saveCharIndex] = 0; } break; } return true; } // Take care of any messages that need input if (messageToPrint) { if (messageNeedsInput) { if (key != ' ' && key != KEY_ESCAPE && key != key_menu_confirm && key != key_menu_abort) { return false; } } menuactive = messageLastMenuActive; messageToPrint = 0; if (messageRoutine) messageRoutine(key); menupause = false; // [STRIFE] unpause menuactive = false; S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound return true; } // [STRIFE]: // * In v1.2 this is moved to F9 (quickload) // * In v1.31 it is moved to F12 with DM spy, and quicksave // functionality is restored separate from normal saving /* if (devparm && key == key_menu_help) { G_ScreenShot (); return true; } */ // F-Keys if (!menuactive) { if (key == key_menu_decscreen) // Screen size down { if (automapactive || chat_on) return false; M_SizeDisplay(0); S_StartSound(NULL, sfx_stnmov); return true; } else if (key == key_menu_incscreen) // Screen size up { if (automapactive || chat_on) return false; M_SizeDisplay(1); S_StartSound(NULL, sfx_stnmov); return true; } else if (key == key_menu_help) // Help key { M_StartControlPanel (); // haleyjd 08/29/10: [STRIFE] always ReadDef1 currentMenu = &ReadDef1; itemOn = 0; S_StartSound(NULL, sfx_swtchn); return true; } else if (key == key_menu_save) // Save { // [STRIFE]: Hub saves if(gameversion == exe_strife_1_31) namingCharacter = false; // just saving normally, in 1.31 if(netgame || players[consoleplayer].health <= 0 || players[consoleplayer].cheats & CF_ONFIRE) { S_StartSound(NULL, sfx_oof); } else { M_StartControlPanel(); S_StartSound(NULL, sfx_swtchn); M_SaveGame(0); } return true; } else if (key == key_menu_load) // Load { // [STRIFE]: Hub saves if(gameversion == exe_strife_1_31) { // 1.31: normal save loading namingCharacter = false; M_StartControlPanel(); M_LoadGame(0); S_StartSound(NULL, sfx_swtchn); } else { // Pre 1.31: quickload only S_StartSound(NULL, sfx_swtchn); M_QuickLoad(); } return true; } else if (key == key_menu_volume) // Sound Volume { M_StartControlPanel (); currentMenu = &SoundDef; itemOn = sfx_vol; S_StartSound(NULL, sfx_swtchn); return true; } else if (key == key_menu_detail) // Detail toggle { //M_ChangeDetail(0); M_AutoUseHealth(); // [STRIFE] S_StartSound(NULL, sfx_swtchn); return true; } else if (key == key_menu_qsave) // Quicksave { // [STRIFE]: Hub saves if(gameversion == exe_strife_1_31) namingCharacter = false; // for 1.31 save changes if(netgame || players[consoleplayer].health <= 0 || players[consoleplayer].cheats & CF_ONFIRE) { S_StartSound(NULL, sfx_oof); } else { S_StartSound(NULL, sfx_swtchn); M_QuickSave(); } return true; } else if (key == key_menu_endgame) // End game { S_StartSound(NULL, sfx_swtchn); M_EndGame(0); return true; } else if (key == key_menu_messages) // Toggle messages { //M_ChangeMessages(0); M_ChangeShowText(); // [STRIFE] S_StartSound(NULL, sfx_swtchn); return true; } else if (key == key_menu_qload) // Quickload { // [STRIFE] // * v1.2: takes a screenshot // * v1.31: does quickload again if(gameversion == exe_strife_1_31) { namingCharacter = false; S_StartSound(NULL, sfx_swtchn); M_QuickLoad(); } else G_ScreenShot(); return true; } else if (key == key_menu_quit) // Quit DOOM { S_StartSound(NULL, sfx_swtchn); M_QuitStrife(0); return true; } else if (key == key_menu_gamma) // gamma toggle { usegamma++; if (usegamma > 4+4) // [crispy] intermediate gamma levels usegamma = 0; players[consoleplayer].message = DEH_String(gammamsg[usegamma]); I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE)); return true; } else if(gameversion == exe_strife_1_31 && key == key_spy) { // haleyjd 20130301: 1.31 moved screenshots to F12. G_ScreenShot(); return true; } else if (key != 0 && key == key_menu_screenshot) { G_ScreenShot(); return true; } } // Pop-up menu? if (!menuactive) { if (key == key_menu_activate) { M_StartControlPanel (); S_StartSound(NULL, sfx_swtchn); return true; } return false; } // Keys usable within menu if (key == key_menu_down) { // Move down to next item do { if (itemOn+1 > currentMenu->numitems-1) itemOn = 0; else itemOn++; S_StartSound(NULL, sfx_pstop); } while(currentMenu->menuitems[itemOn].status==-1); return true; } else if (key == key_menu_up) { // Move back up to previous item do { if (!itemOn) itemOn = currentMenu->numitems-1; else itemOn--; S_StartSound(NULL, sfx_pstop); } while(currentMenu->menuitems[itemOn].status==-1); return true; } else if (key == key_menu_left) { // Slide slider left if (currentMenu->menuitems[itemOn].routine && currentMenu->menuitems[itemOn].status == 2) { S_StartSound(NULL, sfx_stnmov); currentMenu->menuitems[itemOn].routine(0); } return true; } else if (key == key_menu_right) { // Slide slider right if (currentMenu->menuitems[itemOn].routine && currentMenu->menuitems[itemOn].status == 2) { S_StartSound(NULL, sfx_stnmov); currentMenu->menuitems[itemOn].routine(1); } return true; } else if (key == key_menu_forward) { // Activate menu item 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_swish); [STRIFE] No sound is played here. } } return true; } else if (key == key_menu_activate) { // Deactivate menu if(gameversion == exe_strife_1_31) // [STRIFE]: 1.31 saving namingCharacter = false; if(menuindialog) // [STRIFE] - Get out of dialog engine semi-gracefully P_DialogDoChoice(-1); currentMenu->lastOn = itemOn; M_ClearMenus (0); S_StartSound(NULL, sfx_mtalht); // villsa [STRIFE]: sounds return true; } else if (key == key_menu_back) { // Go back to previous menu currentMenu->lastOn = itemOn; if (currentMenu->prevMenu) { currentMenu = currentMenu->prevMenu; itemOn = currentMenu->lastOn; S_StartSound(NULL, sfx_swtchn); } return true; } // Keyboard shortcut? // Vanilla Strife has a weird behavior where it jumps to the scroll bars // when certain keys are pressed, so emulate this. else if (ch != 0 || IsNullKey(key)) { // Keyboard shortcut? 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; } // // M_StartControlPanel // void M_StartControlPanel (void) { // intro might call this repeatedly if (menuactive) return; menuactive = 1; menupause = true; currentMenu = &MainDef; // JDC itemOn = currentMenu->lastOn; // JDC } // // M_Drawer // Called after the view has been rendered, // but before it has been blitted. // void M_Drawer (void) { static short x; static short y; unsigned int i; unsigned int max; char string[80]; const char *name; int start; inhelpscreens = false; // Horiz. & Vertically center string and print it. if (messageToPrint) { start = 0; y = 100 - M_StringHeight(messageString) / 2; while (messageString[start] != '\0') { int foundnewline = 0; for (i = 0; i < strlen(messageString + start); i++) { if (messageString[start + i] == '\n') { M_StringCopy(string, messageString + start, sizeof(string)); if (i < sizeof(string)) { string[i] = '\0'; } foundnewline = 1; start += i + 1; break; } } if (!foundnewline) { M_StringCopy(string, messageString + start, sizeof(string)); start += strlen(string); } x = 160 - M_StringWidth(string) / 2; M_WriteText(x, y, string); y += SHORT(hu_font[0]->height); } return; } if (!menuactive) return; if (currentMenu->routine) currentMenu->routine(); // call Draw routine // DRAW MENU x = currentMenu->x; y = currentMenu->y; max = currentMenu->numitems; for (i=0;imenuitems[i].name); if (name[0]) { V_DrawPatchDirect (x, y, W_CacheLumpName(name, PU_CACHE)); } y += LINEHEIGHT; } // haleyjd 08/27/10: [STRIFE] Adjust to draw spinning Sigil // DRAW SIGIL V_DrawPatchDirect(x + CURSORXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT, W_CacheLumpName(DEH_String(cursorName[whichCursor]), PU_CACHE)); } // // M_ClearMenus // // haleyjd 08/28/10: [STRIFE] Added an int param so this can be called by menus. // 09/08/10: Added menupause. // void M_ClearMenus (int choice) { choice = 0; // haleyjd: for no warning; not from decompilation. menuactive = 0; menupause = 0; } // // M_SetupNextMenu // void M_SetupNextMenu(menu_t *menudef) { currentMenu = menudef; itemOn = currentMenu->lastOn; } // // M_Ticker // // haleyjd 08/27/10: [STRIFE] Rewritten for Sigil cursor // void M_Ticker (void) { if (--cursorAnimCounter <= 0) { whichCursor = (whichCursor + 1) % 8; cursorAnimCounter = 5; } } // // M_Init // // haleyjd 08/27/10: [STRIFE] Removed DOOM gamemode stuff // void M_Init (void) { currentMenu = &MainDef; menuactive = 0; itemOn = currentMenu->lastOn; whichCursor = 0; cursorAnimCounter = 10; screenSize = screenblocks - 3; messageToPrint = 0; messageString = NULL; messageLastMenuActive = menuactive; // STRIFE-FIXME: assigns 0 here... quickSaveSlot = -1; // [STRIFE]: Initialize savegame paths and clear temporary directory G_WriteSaveName(5, "ME"); ClearTmp(); // Here we could catch other version dependencies, // like HELP1/2, and four episodes. } crispy-doom-crispy-doom-5.6.4/src/strife/m_menu.h000066400000000000000000000047461360717211000217630ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Menu widget stuff, episode selection and such. // #ifndef __M_MENU__ #define __M_MENU__ #include "d_event.h" // // MENU TYPEDEFS // // haleyjd 09/04/10: [STRIFE] Made external typedef struct { // 0 = no cursor here, 1 = ok, 2 = arrows ok short status; char name[10]; // choice = menu item #. // if status = 2, // choice=0:leftarrow,1:rightarrow void (*routine)(int choice); // hotkey in menu char alphaKey; } menuitem_t; typedef struct menu_s { short numitems; // # of menu items struct menu_s* prevMenu; // previous menu menuitem_t* menuitems; // menu items void (*routine)(); // draw routine short x; short y; // x,y of menu short lastOn; // last item user was on in menu } menu_t; extern menu_t* currentMenu; // villsa [STRIFE] made external extern short itemOn; // // MENUS // // Called by main loop, // saves config file and calls I_Quit when user exits. // Even when the menu is not displayed, // this can resize the view and change game parameters. // Does all the real work of the menu interaction. boolean M_Responder (event_t *ev); // Called by main loop, // only used for menu (skull cursor) animation. void M_Ticker (void); // Called by main loop, // draws the menus directly into the screen buffer. void M_Drawer (void); // Called by D_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); // haleyjd 09/04/10: Externalized. Draws menu text. int M_WriteText(int x, int y, const char *string); // haleyjd 09/04/10: [STRIFE] New function. void M_DialogDimMsg(int x, int y, char *str, boolean useyfont); // haleyjd [STRIFE] Externalized void M_ClearMenus (int choice); void M_LoadSelect(int choice); extern int detailLevel; extern int screenblocks; #endif crispy-doom-crispy-doom-5.6.4/src/strife/m_random.c000066400000000000000000000047571360717211000222740ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Random number LUT. // #include #include "m_random.h" // // M_Random // Returns a 0-255 number // static const unsigned char rndtable[256] = { 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , 120, 163, 236, 249 }; int rndindex = 0; int prndindex = 0; // Which one is deterministic? int P_Random (void) { prndindex = (prndindex+1)&0xff; return rndtable[prndindex]; } int M_Random (void) { rndindex = (rndindex+1)&0xff; return rndtable[rndindex]; } // // M_ClearRandom // // haleyjd 20110204 [STRIFE]: No "seeding" of M_Random index // void M_ClearRandom (void) { prndindex = 0; rndindex = 0; } crispy-doom-crispy-doom-5.6.4/src/strife/m_random.h000066400000000000000000000016151360717211000222670ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __M_RANDOM__ #define __M_RANDOM__ #include "doomtype.h" // Returns a number from 0 to 255, // from a lookup table. int M_Random (void); // As M_Random, but used only by the play simulation. int P_Random (void); // Fix randoms for demos. void M_ClearRandom (void); #endif crispy-doom-crispy-doom-5.6.4/src/strife/m_saves.c000066400000000000000000000275241360717211000221320ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2010 James Haley, Samuel Villarreal // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // // [STRIFE] New Module // // Strife Hub Saving Code // #include #include #include #include "z_zone.h" #include "i_glob.h" #include "i_system.h" #include "d_player.h" #include "deh_str.h" #include "doomstat.h" #include "m_misc.h" #include "m_saves.h" #include "p_dialog.h" // // File Paths // // Strife maintains multiple file paths related to savegames. // char *savepath; // The actual path of the selected saveslot char *savepathtemp; // The path of the temporary saveslot (strfsav6.ssg) char *loadpath; // Path used while loading the game char character_name[CHARACTER_NAME_LEN]; // Name of "character" for saveslot // // ClearTmp // // Clear the temporary save directory // void ClearTmp(void) { glob_t *glob; if(savepathtemp == NULL) I_Error("you fucked up savedir man!"); glob = I_StartGlob(savepathtemp, "*", 0); if (glob == NULL) I_Error("ClearTmp: Couldn't open dir %s", savepathtemp); for (;;) { const char *path = I_NextGlob(glob); if (path == NULL) { break; } remove(path); } I_EndGlob(glob); } // // ClearSlot // // Clear a single save slot folder // void ClearSlot(void) { glob_t *glob; if(savepath == NULL) I_Error("userdir is fucked up man!"); glob = I_StartGlob(savepath, "*", 0); if (glob == NULL) I_Error("ClearSlot: Couldn't open dir %s", savepath); for (;;) { const char *filepath = I_NextGlob(glob); if (filepath == NULL) { break; } remove(filepath); } I_EndGlob(glob); } // // FromCurr // // Copying files from savepathtemp to savepath // void FromCurr(void) { glob_t *glob; glob = I_StartGlob(savepathtemp, "*", 0); if (glob == NULL) I_Error("FromCurr: Couldn't open dir %s", savepathtemp); for (;;) { byte *filebuffer; int filelen; const char *srcfilename; char *dstfilename; srcfilename = I_NextGlob(glob); if (srcfilename == NULL) { break; } dstfilename = M_SafeFilePath(savepath, M_BaseName(srcfilename)); filelen = M_ReadFile(srcfilename, &filebuffer); M_WriteFile(dstfilename, filebuffer, filelen); Z_Free(filebuffer); Z_Free(dstfilename); } I_EndGlob(glob); } // // ToCurr // // Copying files from savepath to savepathtemp // void ToCurr(void) { glob_t *glob; ClearTmp(); // BUG: Rogue copypasta'd this error message, which is why we don't know // the real original name of this function. glob = I_StartGlob(savepath, "*", 0); if (glob == NULL) I_Error("ClearSlot: Couldn't open dir %s", savepath); for (;;) { byte *filebuffer; int filelen; const char *srcfilename; char *dstfilename; srcfilename = I_NextGlob(glob); if (srcfilename == NULL) { break; } dstfilename = M_SafeFilePath(savepathtemp, M_BaseName(srcfilename)); filelen = M_ReadFile(srcfilename, &filebuffer); M_WriteFile(dstfilename, filebuffer, filelen); Z_Free(filebuffer); Z_Free(dstfilename); } I_EndGlob(glob); } // // M_SaveMoveMapToHere // // Moves a map to the "HERE" save. // void M_SaveMoveMapToHere(void) { char *mapsave = NULL; char *heresave = NULL; char tmpnum[33]; // haleyjd: no itoa available... M_snprintf(tmpnum, sizeof(tmpnum), "%d", gamemap); // haleyjd: use M_SafeFilePath, not sprintf mapsave = M_SafeFilePath(savepath, tmpnum); heresave = M_SafeFilePath(savepath, "here"); // haleyjd: use M_FileExists, not access if(M_FileExists(mapsave)) { remove(heresave); rename(mapsave, heresave); } Z_Free(mapsave); Z_Free(heresave); } // // M_SaveMoveHereToMap // // Moves the "HERE" save to a map. // void M_SaveMoveHereToMap(void) { char *mapsave = NULL; char *heresave = NULL; char tmpnum[33]; // haleyjd: no itoa available... M_snprintf(tmpnum, sizeof(tmpnum), "%d", gamemap); mapsave = M_SafeFilePath(savepathtemp, tmpnum); heresave = M_SafeFilePath(savepathtemp, "here"); if(M_FileExists(heresave)) { remove(mapsave); rename(heresave, mapsave); } Z_Free(mapsave); Z_Free(heresave); } // // M_SaveMisObj // // Writes the mission objective into the MIS_OBJ file. // boolean M_SaveMisObj(const char *path) { boolean result; char *destpath = NULL; // haleyjd 20110210: use M_SafeFilePath, not sprintf destpath = M_SafeFilePath(path, "mis_obj"); result = M_WriteFile(destpath, mission_objective, OBJECTIVE_LEN); Z_Free(destpath); return result; } // // M_ReadMisObj // // Reads the mission objective from the MIS_OBJ file. // void M_ReadMisObj(void) { FILE *f = NULL; char *srcpath = NULL; // haleyjd: use M_SafeFilePath, not sprintf srcpath = M_SafeFilePath(savepathtemp, "mis_obj"); if((f = fopen(srcpath, "rb"))) { int retval = fread(mission_objective, 1, OBJECTIVE_LEN, f); fclose(f); if (retval != OBJECTIVE_LEN) { I_Error("M_ReadMisObj: error while reading mission objective"); } } Z_Free(srcpath); } //============================================================================= // // Original Routines // // haleyjd - None of the below code is derived from Strife itself, but has been // adapted or created in order to provide secure, portable filepath handling // for the purposes of savegame support. This is partially needed to allow for // differences in Choco due to it being multiplatform. The rest exists because // I cannot stand programming in an impoverished ANSI C environment that // calls sprintf on fixed-size buffers. :P // // // M_Calloc // // haleyjd 20110210 - original routine // Because Choco doesn't have Z_Calloc O_o // void *M_Calloc(size_t n1, size_t n2) { return (n1 *= n2) ? memset(Z_Malloc(n1, PU_STATIC, NULL), 0, n1) : NULL; } // // M_StringAlloc // // haleyjd: This routine takes any number of strings and a number of extra // characters, calculates their combined length, and calls Z_Alloca to create // a temporary buffer of that size. This is extremely useful for allocation of // file paths, and is used extensively in d_main.c. The pointer returned is // to a temporary Z_Alloca buffer, which lives until the next main loop // iteration, so don't cache it. Note that this idiom is not possible with the // normal non-standard alloca function, which allocates stack space. // // [STRIFE] - haleyjd 20110210 // This routine is taken from the Eternity Engine and adapted to do without // Z_Alloca. I need secure string concatenation for filepath handling. The // only difference from use in EE is that the pointer returned in *str must // be manually freed. // int M_StringAlloc(char **str, int numstrs, size_t extra, const char *str1, ...) { va_list args; size_t len = extra; if(numstrs < 1) I_Error("M_StringAlloc: invalid input\n"); len += strlen(str1); --numstrs; if(numstrs != 0) { va_start(args, str1); while(numstrs != 0) { const char *argstr = va_arg(args, const char *); len += strlen(argstr); --numstrs; } va_end(args); } ++len; *str = (char *)(M_Calloc(1, len)); return len; } // // M_NormalizeSlashes // // Remove trailing slashes, translate backslashes to slashes // The string to normalize is passed and returned in str // // killough 11/98: rewritten // // [STRIFE] - haleyjd 20110210: Borrowed from Eternity and adapted to respect // the DIR_SEPARATOR define used by Choco Doom. This routine originated in // BOOM. // void M_NormalizeSlashes(char *str) { char *p; // Convert all slashes/backslashes to DIR_SEPARATOR for(p = str; *p; p++) { if((*p == '/' || *p == '\\') && *p != DIR_SEPARATOR) *p = DIR_SEPARATOR; } // Remove trailing slashes while(p > str && *--p == DIR_SEPARATOR) *p = 0; // Collapse multiple slashes for(p = str; (*str++ = *p); ) if(*p++ == DIR_SEPARATOR) while(*p == DIR_SEPARATOR) p++; } // // M_SafeFilePath // // haleyjd 20110210 - original routine. // This routine performs safe, portable concatenation of a base file path // with another path component or file name. The returned string is Z_Malloc'd // and should be freed when it has exhausted its usefulness. // char *M_SafeFilePath(const char *basepath, const char *newcomponent) { int newstrlen = 0; char *newstr = NULL; if (!strcmp(basepath, "")) { basepath = "."; } // Always throw in a slash. M_NormalizeSlashes will remove it in the case // that either basepath or newcomponent includes a redundant slash at the // end or beginning respectively. newstrlen = M_StringAlloc(&newstr, 3, 1, basepath, "/", newcomponent); M_snprintf(newstr, newstrlen, "%s/%s", basepath, newcomponent); M_NormalizeSlashes(newstr); return newstr; } // // M_CreateSaveDirs // // haleyjd 20110210: Vanilla Strife went tits-up if it didn't have the full set // of save folders which were created externally by the installer. fraggle says // that's no good for Choco purposes, and I agree, so this routine will create // the full set of folders under the configured savegamedir. // void M_CreateSaveDirs(const char *savedir) { int i; for(i = 0; i < 7; i++) { char *compositedir; // compose the full path by concatenating with savedir compositedir = M_SafeFilePath(savedir, M_MakeStrifeSaveDir(i, "")); M_MakeDirectory(compositedir); Z_Free(compositedir); } } // // M_MakeStrifeSaveDir // // haleyjd 20110211: Convenience routine // char *M_MakeStrifeSaveDir(int slotnum, const char *extra) { static char tmpbuffer[32]; M_snprintf(tmpbuffer, sizeof(tmpbuffer), "strfsav%d.ssg%s", slotnum, extra); return tmpbuffer; } // // M_GetFilePath // // haleyjd: STRIFE-FIXME: Temporary? // Code borrowed from Eternity, and modified to return separator char // char M_GetFilePath(const char *fn, char *dest, size_t len) { boolean found_slash = false; char *p; char sepchar = '\0'; memset(dest, 0, len); p = dest + len - 1; M_StringCopy(dest, fn, len); while(p >= dest) { if(*p == '/' || *p == '\\') { sepchar = *p; found_slash = true; // mark that the path ended with a slash *p = '\0'; break; } *p = '\0'; p--; } // haleyjd: in the case that no slash was ever found, yet the // path string is empty, we are dealing with a file local to the // working directory. The proper path to return for such a string is // not "", but ".", since the format strings add a slash now. When // the string is empty but a slash WAS found, we really do want to // return the empty string, since the path is relative to the root. if(!found_slash && *dest == '\0') *dest = '.'; // if a separator is not found, default to forward, because Windows // supports that too. if(sepchar == '\0') sepchar = '/'; return sepchar; } // EOF crispy-doom-crispy-doom-5.6.4/src/strife/m_saves.h000066400000000000000000000030451360717211000221270ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2010 James Haley, Samuel Villareal // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // // [STRIFE] New Module // // Strife Hub Saving Code // #ifndef M_SAVES_H__ #define M_SAVES_H__ #define CHARACTER_NAME_LEN 32 extern char *savepath; extern char *savepathtemp; extern char *loadpath; extern char character_name[CHARACTER_NAME_LEN]; // Strife Savegame Functions void ClearTmp(void); void ClearSlot(void); void FromCurr(void); void ToCurr(void); void M_SaveMoveMapToHere(void); void M_SaveMoveHereToMap(void); boolean M_SaveMisObj(const char *path); void M_ReadMisObj(void); // Custom Utilities for Filepath Handling void *M_Calloc(size_t n1, size_t n2); void M_NormalizeSlashes(char *str); int M_StringAlloc(char **str, int numstrs, size_t extra, const char *str1, ...); char *M_SafeFilePath(const char *basepath, const char *newcomponent); char M_GetFilePath(const char *fn, char *dest, size_t len); char *M_MakeStrifeSaveDir(int slotnum, const char *extra); void M_CreateSaveDirs(const char *savedir); #endif // EOF crispy-doom-crispy-doom-5.6.4/src/strife/p_ceilng.c000066400000000000000000000162621360717211000222520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Ceiling aninmation (lowering, crushing, raising) // #include "z_zone.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // // CEILINGS // ceiling_t* activeceilings[MAXCEILINGS]; // // T_MoveCeiling // void T_MoveCeiling (ceiling_t* ceiling) { result_e res; switch(ceiling->direction) { case 0: // IN STASIS break; case 1: // UP res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false,1,ceiling->direction); if (!(leveltime&7)) { switch(ceiling->type) { case silentCrushAndRaise: break; default: S_StartSound(&ceiling->sector->soundorg, sfx_stnmov); // ? break; } } if (res == pastdest) { switch(ceiling->type) { case raiseToHighest: P_RemoveActiveCeiling(ceiling); break; case silentCrushAndRaise: S_StartSound(&ceiling->sector->soundorg, sfx_pstop); case fastCrushAndRaise: case crushAndRaise: ceiling->direction = -1; break; default: break; } } break; case -1: // DOWN res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush,1,ceiling->direction); if (!(leveltime&7)) { switch(ceiling->type) { case silentCrushAndRaise: break; default: S_StartSound(&ceiling->sector->soundorg, sfx_stnmov); } } if (res == pastdest) { switch(ceiling->type) { case silentCrushAndRaise: S_StartSound(&ceiling->sector->soundorg, sfx_pstop); case crushAndRaise: ceiling->speed = CEILSPEED; case fastCrushAndRaise: ceiling->direction = 1; break; case lowerAndCrush: case lowerToFloor: P_RemoveActiveCeiling(ceiling); break; default: break; } } else // ( res != pastdest ) { if (res == crushed) { switch(ceiling->type) { case silentCrushAndRaise: case crushAndRaise: case lowerAndCrush: ceiling->speed = CEILSPEED / 8; break; default: break; } } } break; } } // // EV_DoCeiling // Move a ceiling up/down and all around! // // haleyjd 10/04/10: [STRIFE] Changes: // * Fast crushers were made 2x as fast. // * lowerAndCrush was apparently "fixed" to actually crush, and was also // altered to lower all the way to the floor rather than remain 8 above. // * silentCrushAndRaise and crushAndRaise no longer crush. int EV_DoCeiling ( line_t* line, ceiling_e type ) { int secnum; int rtn; sector_t* sec; ceiling_t* ceiling; secnum = -1; rtn = 0; // Reactivate in-stasis ceilings...for certain types. switch(type) { case fastCrushAndRaise: case silentCrushAndRaise: case crushAndRaise: P_ActivateInStasisCeiling(line); default: break; } while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // new door thinker rtn = 1; ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); P_AddThinker (&ceiling->thinker); sec->specialdata = ceiling; ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; ceiling->sector = sec; ceiling->crush = false; switch(type) { case fastCrushAndRaise: // [STRIFE]: Speed of fast crushers increased by 2x! ceiling->crush = true; ceiling->topheight = sec->ceilingheight; ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); ceiling->direction = -1; ceiling->speed = CEILSPEED * 4; // [STRIFE] Was CEILSPEED * 2 break; case lowerAndCrush: // [STRIFE] lowerAndCrush doesn't seem to have crushed in DOOM, // but it was certainly made to do so in Strife! It is also // changed to lower all the way to the floor. ceiling->crush = 1; ceiling->direction = -1; ceiling->speed = CEILSPEED; ceiling->bottomheight = sec->floorheight; break; case silentCrushAndRaise: case crushAndRaise: // [STRIFE] haleyjd 20130209: Turns out these types do NOT crush // in Strife... yeah, that makes a lot of sense. Thanks to Gez for // having detected this difference. //ceiling->crush = true; ceiling->topheight = sec->ceilingheight; case lowerToFloor: ceiling->bottomheight = sec->floorheight; if (type != lowerToFloor) ceiling->bottomheight += 8*FRACUNIT; ceiling->direction = -1; ceiling->speed = CEILSPEED; break; case raiseToHighest: ceiling->topheight = P_FindHighestCeilingSurrounding(sec); ceiling->direction = 1; ceiling->speed = CEILSPEED; break; } ceiling->tag = sec->tag; ceiling->type = type; P_AddActiveCeiling(ceiling); } return rtn; } // // Add an active ceiling // void P_AddActiveCeiling(ceiling_t* c) { int i; for (i = 0; i < MAXCEILINGS;i++) { if (activeceilings[i] == NULL) { activeceilings[i] = c; return; } } } // // Remove a ceiling's thinker // void P_RemoveActiveCeiling(ceiling_t* c) { int i; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] == c) { activeceilings[i]->sector->specialdata = NULL; P_RemoveThinker (&activeceilings[i]->thinker); activeceilings[i] = NULL; break; } } } // // Restart a ceiling that's in-stasis // void P_ActivateInStasisCeiling(line_t* line) { int i; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction == 0)) { activeceilings[i]->direction = activeceilings[i]->olddirection; activeceilings[i]->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; } } } // // EV_CeilingCrushStop // Stop a ceiling from crushing! // int EV_CeilingCrushStop(line_t *line) { int i; int rtn; rtn = 0; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction != 0)) { activeceilings[i]->olddirection = activeceilings[i]->direction; activeceilings[i]->thinker.function.acv = (actionf_v)NULL; activeceilings[i]->direction = 0; // in-stasis rtn = 1; } } return rtn; } crispy-doom-crispy-doom-5.6.4/src/strife/p_dialog.c000066400000000000000000001172021360717211000222440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2010 James Haley, Samuel Villarreal // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // // [STRIFE] New Module // // Dialog Engine for Strife // #include #include "z_zone.h" #include "w_wad.h" #include "deh_str.h" #include "d_main.h" #include "d_mode.h" #include "d_player.h" #include "doomstat.h" #include "m_random.h" #include "m_menu.h" #include "m_misc.h" #include "r_main.h" #include "v_video.h" #include "p_local.h" #include "sounds.h" #include "p_dialog.h" #include "s_sound.h" #include "p_local.h" #include "p_inter.h" // // Defines and Macros // // haleyjd: size of the original Strife mapdialog_t structure. #define ORIG_MAPDIALOG_SIZE 0x5EC #define DIALOG_INT(field, ptr) \ field = ((int)ptr[0] | \ ((int)ptr[1] << 8) | \ ((int)ptr[2] << 16) | \ ((int)ptr[3] << 24)); \ ptr += 4; #define DIALOG_STR(field, ptr, len) \ memcpy(field, ptr, len); \ ptr += len; // // Globals // // This can be toggled at runtime to determine if the full dialog messages // are subtitled on screen or not. Defaults to off. int dialogshowtext = false; // The global mission objective buffer. This gets written to and read from file, // and is set by dialogs and line actions. char mission_objective[OBJECTIVE_LEN]; // // Static Globals // // True if SCRIPT00 is loaded. static boolean script0loaded; // Number of dialogs defined in the current level's script. static int numleveldialogs; // The actual level dialogs. This didn't exist in Strife, but is new to account // for structure alignment/packing concerns, given that Chocolate Doom is // multiplatform. static mapdialog_t *leveldialogs; // The actual script00 dialogs. As above. static mapdialog_t *script0dialogs; // Number of dialogs defined in the SCRIPT00 lump. static int numscript0dialogs; // The player engaged in dialog. This is always player 1, though, since Rogue // never completed the ability to use dialog outside of single-player mode. static player_t *dialogplayer; // The object to which the player is speaking. static mobj_t *dialogtalker; // The talker's current angle static angle_t dialogtalkerangle; // The currently active mapdialog object. static mapdialog_t *currentdialog; // Text at the end of the choices static char dialoglastmsgbuffer[48]; // Item to display to player when picked up or recieved static char pickupstring[46]; // Health based on gameskill given by the front's medic static const int healthamounts[] = { -100 , -75, -50, -50, -100 }; //============================================================================= // // Dialog State Sets // // These are used to animate certain actors in response to what happens in // their dialog sequences. // typedef struct dialogstateset_s { mobjtype_t type; // the type of object statenum_t greet; // greeting state, for start of dialog statenum_t yes; // "yes" state, for an affirmative response statenum_t no; // "no" state, when you don't have the right items } dialogstateset_t; static dialogstateset_t dialogstatesets[] = { { MT_PLAYER, S_NULL, S_NULL, S_NULL }, { MT_SHOPKEEPER_W, S_MRGT_00, S_MRYS_00, S_MRNO_00 }, { MT_SHOPKEEPER_B, S_MRGT_00, S_MRYS_00, S_MRNO_00 }, { MT_SHOPKEEPER_A, S_MRGT_00, S_MRYS_00, S_MRNO_00 }, { MT_SHOPKEEPER_M, S_MRGT_00, S_MRYS_00, S_MRNO_00 } }; // Rogue stored this in a static global rather than making it a define... static int numdialogstatesets = arrlen(dialogstatesets); // Current dialog talker state static dialogstateset_t *dialogtalkerstates; //============================================================================= // // Random Messages // // Rogue hard-coded these so they wouldn't have to repeat them several times // in the SCRIPT00 lump, apparently. // #define MAXRNDMESSAGES 10 typedef struct rndmessage_s { const char *type_name; int nummessages; const char *messages[MAXRNDMESSAGES]; } rndmessage_t; static rndmessage_t rndMessages[] = { // Peasants { "PEASANT", 10, { "PLEASE DON'T HURT ME.", "IF YOU'RE LOOKING TO HURT ME, I'M \n" "NOT REALLY WORTH THE EFFORT.", "I DON'T KNOW ANYTHING.", "GO AWAY OR I'LL CALL THE GUARDS!", "I WISH SOMETIMES THAT ALL THESE \n" "REBELS WOULD JUST LEARN THEIR \n" "PLACE AND STOP THIS NONSENSE.", "JUST LEAVE ME ALONE, OK?", "I'M NOT SURE, BUT SOMETIMES I THINK \n" "THAT I KNOW SOME OF THE ACOLYTES.", "THE ORDER'S GOT EVERYTHING AROUND HERE PRETTY WELL LOCKED UP TIGHT.", "THERE'S NO WAY THAT THIS IS JUST A \n" "SECURITY FORCE.", "I'VE HEARD THAT THE ORDER IS REALLY \n" "NERVOUS ABOUT THE FRONT'S \n" "ACTIONS AROUND HERE." } }, // Rebel { "REBEL", 10, { "THERE'S NO WAY THE ORDER WILL \n" "STAND AGAINST US.", "WE'RE ALMOST READY TO STRIKE. \n" "MACIL'S PLANS ARE FALLING IN PLACE.", "WE'RE ALL BEHIND YOU, DON'T WORRY.", "DON'T GET TOO CLOSE TO ANY OF THOSE BIG ROBOTS. THEY'LL MELT YOU DOWN \n" "FOR SCRAP!", "THE DAY OF OUR GLORY WILL SOON \n" "COME, AND THOSE WHO OPPOSE US WILL \n" "BE CRUSHED!", "DON'T GET TOO COMFORTABLE. WE'VE \n" "STILL GOT OUR WORK CUT OUT FOR US.", "MACIL SAYS THAT YOU'RE THE NEW \n" "HOPE. BEAR THAT IN MIND.", "ONCE WE'VE TAKEN THESE CHARLATANS DOWN, WE'LL BE ABLE TO REBUILD THIS " "WORLD AS IT SHOULD BE.", "REMEMBER THAT YOU AREN'T FIGHTING \n" "JUST FOR YOURSELF, BUT FOR \n" "EVERYONE HERE AND OUTSIDE.", "AS LONG AS ONE OF US STILL STANDS, \n" "WE WILL WIN." } }, // Acolyte { "AGUARD", 10, { "MOVE ALONG, PEASANT.", "FOLLOW THE TRUE FAITH, ONLY THEN \n" "WILL YOU BEGIN TO UNDERSTAND.", "ONLY THROUGH DEATH CAN ONE BE \n" "TRULY REBORN.", "I'M NOT INTERESTED IN YOUR USELESS \n" "DRIVEL.", "IF I HAD WANTED TO TALK TO YOU I \n" "WOULD HAVE TOLD YOU SO.", "GO AND ANNOY SOMEONE ELSE!", "KEEP MOVING!", "IF THE ALARM GOES OFF, JUST STAY OUT OF OUR WAY!", "THE ORDER WILL CLEANSE THE WORLD \n" "AND USHER IT INTO THE NEW ERA.", "PROBLEM? NO, I THOUGHT NOT.", } }, // Beggar { "BEGGAR", 10, { "ALMS FOR THE POOR?", "WHAT ARE YOU LOOKING AT, SURFACER?", "YOU WOULDN'T HAVE ANY EXTRA FOOD, WOULD YOU?", "YOU SURFACE PEOPLE WILL NEVER \n" " " " UNDERSTAND US.", "HA, THE GUARDS CAN'T FIND US. THOSE \n" "IDIOTS DON'T EVEN KNOW WE EXIST.", "ONE DAY EVERYONE BUT THOSE WHO SERVE THE ORDER WILL BE FORCED TO " " JOIN US.", "STARE NOW, BUT YOU KNOW THAT THIS WILL BE YOUR OWN FACE ONE DAY.", // Note: "NOTHING THING" is an authentic typo "THERE'S NOTHING THING MORE \n" "ANNOYING THAN A SURFACER WITH AN ATTITUDE!", "THE ORDER WILL MAKE SHORT WORK OF YOUR PATHETIC FRONT.", "WATCH YOURSELF SURFACER. WE KNOW OUR ENEMIES!" } }, // Templar { "PGUARD", 10, { "WE ARE THE HANDS OF FATE. TO EARN \n" "OUR WRATH IS TO FIND OBLIVION!", "THE ORDER WILL CLEANSE THE WORLD \n" "OF THE WEAK AND CORRUPT!", "OBEY THE WILL OF THE MASTERS!", "LONG LIFE TO THE BROTHERS OF THE \n" "ORDER!", "FREE WILL IS AN ILLUSION THAT BINDS \n" "THE WEAK MINDED.", "POWER IS THE PATH TO GLORY. TO \n" "FOLLOW THE ORDER IS TO WALK THAT \n" "PATH!", "TAKE YOUR PLACE AMONG THE \n" "RIGHTEOUS, JOIN US!", "THE ORDER PROTECTS ITS OWN.", "ACOLYTES? THEY HAVE YET TO SEE THE FULL GLORY OF THE ORDER.", "IF THERE IS ANY HONOR INSIDE THAT \n" "PATHETIC SHELL OF A BODY, \n" "YOU'LL ENTER INTO THE ARMS OF THE \n" "ORDER." } } }; // And again, this could have been a define, but was a variable. static int numrndmessages = arrlen(rndMessages); //============================================================================= // // Dialog Menu Structure // // The Strife dialog system is actually just a serious abuse of the DOOM menu // engine. Hence why it doesn't work in multiplayer games or during demo // recording. // #define NUMDIALOGMENUITEMS 6 static void P_DialogDrawer(void); static menuitem_t dialogmenuitems[] = { { 1, "", P_DialogDoChoice, '1' }, // These items are loaded dynamically { 1, "", P_DialogDoChoice, '2' }, { 1, "", P_DialogDoChoice, '3' }, { 1, "", P_DialogDoChoice, '4' }, { 1, "", P_DialogDoChoice, '5' }, { 1, "", P_DialogDoChoice, '6' } // Item 6 is always the dismissal item }; static menu_t dialogmenu = { NUMDIALOGMENUITEMS, NULL, dialogmenuitems, P_DialogDrawer, 42, 75, 0 }; // Lump number of the dialog background picture, if any. static int dialogbgpiclumpnum; // Name of current speaking character. static const char *dialogname; // Current dialog text. static const char *dialogtext; //============================================================================= // // Routines // // // P_ParseDialogLump // // haleyjd 09/02/10: This is an original function added to parse out the // dialogs from the dialog lump rather than reading them raw from the lump // pointer. This avoids problems with structure packing. // static void P_ParseDialogLump(byte *lump, mapdialog_t **dialogs, int numdialogs, int tag) { int i; byte *rover = lump; *dialogs = Z_Malloc(numdialogs * sizeof(mapdialog_t), tag, NULL); for(i = 0; i < numdialogs; i++) { int j; mapdialog_t *curdialog = &((*dialogs)[i]); DIALOG_INT(curdialog->speakerid, rover); DIALOG_INT(curdialog->dropitem, rover); DIALOG_INT(curdialog->checkitem[0], rover); DIALOG_INT(curdialog->checkitem[1], rover); DIALOG_INT(curdialog->checkitem[2], rover); DIALOG_INT(curdialog->jumptoconv, rover); DIALOG_STR(curdialog->name, rover, MDLG_NAMELEN); DIALOG_STR(curdialog->voice, rover, MDLG_LUMPLEN); DIALOG_STR(curdialog->backpic, rover, MDLG_LUMPLEN); DIALOG_STR(curdialog->text, rover, MDLG_TEXTLEN); // copy choices for(j = 0; j < 5; j++) { mapdlgchoice_t *curchoice = &(curdialog->choices[j]); DIALOG_INT(curchoice->giveitem, rover); DIALOG_INT(curchoice->needitems[0], rover); DIALOG_INT(curchoice->needitems[1], rover); DIALOG_INT(curchoice->needitems[2], rover); DIALOG_INT(curchoice->needamounts[0], rover); DIALOG_INT(curchoice->needamounts[1], rover); DIALOG_INT(curchoice->needamounts[2], rover); DIALOG_STR(curchoice->text, rover, MDLG_CHOICELEN); DIALOG_STR(curchoice->textok, rover, MDLG_MSGLEN); DIALOG_INT(curchoice->next, rover); DIALOG_INT(curchoice->objective, rover); DIALOG_STR(curchoice->textno, rover, MDLG_MSGLEN); } } } // // P_DialogLoad // // [STRIFE] New function // haleyjd 09/02/10: Loads the dialog script for the current map. Also loads // SCRIPT00 if it has not yet been loaded. // void P_DialogLoad(void) { char lumpname[9]; int lumpnum; // load the SCRIPTxy lump corresponding to MAPxy, if it exists. DEH_snprintf(lumpname, sizeof(lumpname), "script%02d", gamemap); if((lumpnum = W_CheckNumForName(lumpname)) == -1) numleveldialogs = 0; else { byte *leveldialogptr = W_CacheLumpNum(lumpnum, PU_STATIC); numleveldialogs = W_LumpLength(lumpnum) / ORIG_MAPDIALOG_SIZE; P_ParseDialogLump(leveldialogptr, &leveldialogs, numleveldialogs, PU_LEVEL); Z_Free(leveldialogptr); // haleyjd: free the original lump } // also load SCRIPT00 if it has not been loaded yet if(!script0loaded) { byte *script0ptr; script0loaded = true; // BUG: Rogue should have used W_GetNumForName here... lumpnum = W_CheckNumForName(DEH_String("script00")); script0ptr = W_CacheLumpNum(lumpnum, PU_STATIC); numscript0dialogs = W_LumpLength(lumpnum) / ORIG_MAPDIALOG_SIZE; P_ParseDialogLump(script0ptr, &script0dialogs, numscript0dialogs, PU_STATIC); Z_Free(script0ptr); // haleyjd: free the original lump } } // // P_PlayerHasItem // // [STRIFE] New function // haleyjd 09/02/10: Checks for inventory items, quest flags, etc. for dialogs. // Returns the amount possessed, or 0 if none. // int P_PlayerHasItem(player_t *player, mobjtype_t type) { int i; if(type > 0) { // check keys if(type >= MT_KEY_BASE && type < MT_INV_SHADOWARMOR) return (player->cards[type - MT_KEY_BASE]); // check sigil pieces if(type >= MT_SIGIL_A && type <= MT_SIGIL_E) return (type - MT_SIGIL_A <= player->sigiltype); // check quest tokens if(type >= MT_TOKEN_QUEST1 && type <= MT_TOKEN_QUEST31) return (player->questflags & (1 << (type - MT_TOKEN_QUEST1))); // check inventory for(i = 0; i < 32; i++) { if(type == player->inventory[i].type) return player->inventory[i].amount; } } return 0; } // // P_DialogFind // // [STRIFE] New function // haleyjd 09/03/10: Looks for a dialog definition matching the given // Script ID # for an mobj. // mapdialog_t *P_DialogFind(mobjtype_t type, int jumptoconv) { int i; // check the map-specific dialogs first for(i = 0; i < numleveldialogs; i++) { if(type == leveldialogs[i].speakerid) { if(jumptoconv <= 1) return &leveldialogs[i]; else --jumptoconv; } } // check SCRIPT00 dialogs next for(i = 0; i < numscript0dialogs; i++) { if(type == script0dialogs[i].speakerid) return &script0dialogs[i]; } // the default dialog is script 0 in the SCRIPT00 lump. return &script0dialogs[0]; } // // P_DialogGetStates // // [STRIFE] New function // haleyjd 09/03/10: Find the set of special dialog states (greetings, yes, no) // for a particular thing type. // static dialogstateset_t *P_DialogGetStates(mobjtype_t type) { int i; // look for a match by type for(i = 0; i < numdialogstatesets; i++) { if(type == dialogstatesets[i].type) return &dialogstatesets[i]; } // return the default 0 record if no match. return &dialogstatesets[0]; } // // P_DialogGetMsg // // [STRIFE] New function // haleyjd 09/03/10: Redirects dialog messages when the script indicates that // the actor should use a random message stored in the executable instead. // static const char *P_DialogGetMsg(const char *message) { // if the message starts with "RANDOM"... if(!strncasecmp(message, DEH_String("RANDOM"), 6)) { int i; const char *nameloc = message + 7; // look for a match in rndMessages for the string starting // 7 chars after "RANDOM_" for(i = 0; i < numrndmessages; i++) { if(!strncasecmp(nameloc, rndMessages[i].type_name, 4)) { // found a match, so return a random message int rnd = M_Random(); int nummessages = rndMessages[i].nummessages; return DEH_String(rndMessages[i].messages[rnd % nummessages]); } } } // otherwise, just return the message passed in. return message; } // // P_GiveInventoryItem // // [STRIFE] New function // haleyjd 09/03/10: Give an inventory item to the player, if possible. // villsa 09/09/10: Fleshed out routine // boolean P_GiveInventoryItem(player_t *player, int sprnum, mobjtype_t type) { int curinv = 0; int i; boolean ok = false; mobjtype_t item = 0; inventory_t* invtail; // repaint the status bar due to inventory changing player->st_update = true; while(1) { // inventory is full if(curinv > player->numinventory) return true; item = player->inventory[curinv].type; if(type < item) { if(curinv != MAXINVENTORYSLOTS) { // villsa - sort inventory item if needed invtail = &player->inventory[player->numinventory - 1]; if(player->numinventory >= (curinv + 1)) { for(i = player->numinventory; i >= (curinv + 1); --i) { invtail[1].sprite = invtail[0].sprite; invtail[1].type = invtail[0].type; invtail[1].amount = invtail[0].amount; invtail--; } } // villsa - add inventory item player->inventory[curinv].amount = 1; player->inventory[curinv].sprite = sprnum; player->inventory[curinv].type = type; // sort cursor if needed if(player->numinventory) { if(curinv <= player->inventorycursor) player->inventorycursor++; } player->numinventory++; return true; } return false; } if(type == item) break; curinv++; } // check amount of inventory item by using the mass from mobjinfo if(player->inventory[curinv].amount < mobjinfo[item].mass) { player->inventory[curinv].amount++; ok = true; } else ok = false; return ok; } // // P_GiveItemToPlayer // // [STRIFE] New function // haleyjd 09/03/10: Sorts out how to give something to the player. // Not strictly just for inventory items. // villsa 09/09/10: Fleshed out function // boolean P_GiveItemToPlayer(player_t *player, int sprnum, mobjtype_t type) { int i = 0; line_t junk; int sound = sfx_itemup; // haleyjd 09/21/10: different sounds for items // set quest if mf_givequest flag is set if(mobjinfo[type].flags & MF_GIVEQUEST) player->questflags |= 1 << (mobjinfo[type].speed - 1); // check for keys if(type >= MT_KEY_BASE && type <= MT_NEWKEY5) { P_GiveCard(player, type - MT_KEY_BASE); return true; } // check for quest tokens if(type >= MT_TOKEN_QUEST1 && type <= MT_TOKEN_QUEST31) { if(mobjinfo[type].name) { M_StringCopy(pickupstring, DEH_String(mobjinfo[type].name), 39); player->message = pickupstring; } player->questflags |= 1 << (type - MT_TOKEN_QUEST1); if(player == &players[consoleplayer]) S_StartSound(NULL, sound); return true; } // haleyjd 09/22/10: Refactored to give sprites higher priority than // mobjtypes and to implement missing logic. switch(sprnum) { case SPR_HELT: // This is given only by the "DONNYTRUMP" cheat (aka Midas) P_GiveInventoryItem(player, SPR_HELT, MT_TOKEN_TOUGHNESS); P_GiveInventoryItem(player, SPR_GUNT, MT_TOKEN_ACCURACY); // [STRIFE] Bizarre... for(i = 0; i < 5 * player->accuracy + 300; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; case SPR_ARM1: // Armor 1 if(!P_GiveArmor(player, -2)) P_GiveInventoryItem(player, sprnum, type); break; case SPR_ARM2: // Armor 2 if(!P_GiveArmor(player, -1)) P_GiveInventoryItem(player, sprnum, type); break; case SPR_COIN: // 1 Gold P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; case SPR_CRED: // 10 Gold for(i = 0; i < 10; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; case SPR_SACK: // 25 gold for(i = 0; i < 25; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; case SPR_CHST: // 50 gold for(i = 0; i < 50; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; // haleyjd 20141215: missing break, caused Rowan to not take ring from you. case SPR_BBOX: // Box of Bullets if(!P_GiveAmmo(player, am_bullets, 5)) return false; break; case SPR_BLIT: // Bullet Clip if(!P_GiveAmmo(player, am_bullets, 1)) return false; break; case SPR_PMAP: // Map powerup if(!P_GivePower(player, pw_allmap)) return false; sound = sfx_yeah; // bluh-doop! break; case SPR_COMM: // Communicator if(!P_GivePower(player, pw_communicator)) return false; sound = sfx_yeah; // bluh-doop! break; case SPR_MSSL: // Mini-missile if(!P_GiveAmmo(player, am_missiles, 1)) return false; break; case SPR_ROKT: // Crate of missiles if(!P_GiveAmmo(player, am_missiles, 5)) return false; break; case SPR_BRY1: // Battery cell if(!P_GiveAmmo(player, am_cell, 1)) return false; break; case SPR_CPAC: // Cell pack if(!P_GiveAmmo(player, am_cell, 5)) return false; break; case SPR_PQRL: // Poison bolts if(!P_GiveAmmo(player, am_poisonbolts, 5)) return false; break; case SPR_XQRL: // Electric bolts if(!P_GiveAmmo(player, am_elecbolts, 5)) return false; break; case SPR_GRN1: // HE Grenades if(!P_GiveAmmo(player, am_hegrenades, 1)) return false; break; case SPR_GRN2: // WP Grenades if(!P_GiveAmmo(player, am_wpgrenades, 1)) return false; break; case SPR_BKPK: // Backpack (aka Ammo Satchel) if(!player->backpack) { for(i = 0; i < NUMAMMO; i++) player->maxammo[i] *= 2; player->backpack = true; } for(i = 0; i < NUMAMMO; i++) P_GiveAmmo(player, i, 1); break; case SPR_RIFL: // Assault Rifle if(player->weaponowned[wp_rifle]) return false; if(!P_GiveWeapon(player, wp_rifle, false)) return false; sound = sfx_wpnup; // SHK-CHK! break; case SPR_FLAM: // Flamethrower if(player->weaponowned[wp_flame]) return false; if(!P_GiveWeapon(player, wp_flame, false)) return false; sound = sfx_wpnup; // SHK-CHK! break; case SPR_MMSL: // Mini-missile Launcher if(player->weaponowned[wp_missile]) return false; if(!P_GiveWeapon(player, wp_missile, false)) return false; sound = sfx_wpnup; // SHK-CHK! break; case SPR_TRPD: // Mauler if(player->weaponowned[wp_mauler]) return false; if(!P_GiveWeapon(player, wp_mauler, false)) return false; sound = sfx_wpnup; // SHK-CHK! break; case SPR_CBOW: // Here's a crossbow. Just aim straight, and *SPLAT!* if(player->weaponowned[wp_elecbow]) return false; if(!P_GiveWeapon(player, wp_elecbow, false)) return false; sound = sfx_wpnup; // SHK-CHK! break; case SPR_TOKN: // Miscellaneous items - These are determined by thingtype. switch(type) { case MT_KEY_HAND: // Severed hand P_GiveCard(player, key_SeveredHand); break; case MT_MONY_300: // 300 Gold (this is the only way to get it, in fact) for(i = 0; i < 300; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; case MT_TOKEN_AMMO: // Ammo token - you get this from the Weapons Trainer if(player->ammo[am_bullets] >= 50) return false; player->ammo[am_bullets] = 50; break; case MT_TOKEN_HEALTH: // Health token - from the Front's doctor if(!P_GiveBody(player, healthamounts[gameskill])) return false; break; case MT_TOKEN_ALARM: // Alarm token - particularly from the Oracle. P_NoiseAlert(player->mo, player->mo); A_AlertSpectreC(dialogtalker); // BUG: assumes in a dialog o_O break; case MT_TOKEN_DOOR1: // Door special 1 junk.tag = 222; EV_DoDoor(&junk, vld_open); break; case MT_TOKEN_PRISON_PASS: // Door special 1 - Prison pass junk.tag = 223; EV_DoDoor(&junk, vld_open); if(gamemap == 2) // If on Tarnhill, give Prison pass object P_GiveInventoryItem(player, sprnum, type); break; case MT_TOKEN_SHOPCLOSE: // Door special 3 - "Shop close" - unused? junk.tag = 222; EV_DoDoor(&junk, vld_close); break; case MT_TOKEN_DOOR3: // Door special 4 (or 3? :P ) junk.tag = 224; EV_DoDoor(&junk, vld_close); break; case MT_TOKEN_STAMINA: // Stamina upgrade if(player->stamina >= 100) return false; player->stamina += 10; P_GiveBody(player, 200); // full healing break; case MT_TOKEN_NEW_ACCURACY: // Accuracy upgrade if(player->accuracy >= 100) return false; player->accuracy += 10; break; case MT_SLIDESHOW: // Slideshow (start a finale) gameaction = ga_victory; if(gamemap == 10) P_GiveItemToPlayer(player, SPR_TOKN, MT_TOKEN_QUEST17); break; default: // The default is to just give it as an inventory item. P_GiveInventoryItem(player, sprnum, type); break; } break; default: // The ultimate default: Give it as an inventory item. if(!P_GiveInventoryItem(player, sprnum, type)) return false; break; } // Play sound. if(player == &players[consoleplayer]) S_StartSound(NULL, sound); return true; } // // P_TakeDialogItem // // [STRIFE] New function // haleyjd 09/03/10: Removes needed items from the player's inventory. // static void P_TakeDialogItem(player_t *player, int type, int amount) { int i; if(amount <= 0) return; for(i = 0; i < player->numinventory; i++) { // find a matching item if(type != player->inventory[i].type) continue; // if there is none left... if((player->inventory[i].amount -= amount) < 1) { // ...shift everything above it down int j; // BUG: They should have stopped at j < numinventory. This // seems to implicitly assume that numinventory is always at // least one less than the max # of slots, otherwise it // pulls in data from the following player_t fields: // st_update, numinventory, inventorycursor, accuracy, stamina for(j = i + 1; j <= player->numinventory; j++) { inventory_t *item1 = &(player->inventory[j - 1]); inventory_t *item2 = &(player->inventory[j]); *item1 = *item2; } // blank the topmost slot // BUG: This will overwrite the aforementioned fields if // numinventory is equal to the number of slots! // STRIFE-TODO: Overflow emulation? player->inventory[player->numinventory].type = NUMMOBJTYPES; player->inventory[player->numinventory].sprite = -1; player->numinventory--; // update cursor position if(player->inventorycursor >= player->numinventory) { if(player->inventorycursor) player->inventorycursor--; } } // end if return; // done! } // end for } // // P_DialogDrawer // // This function is set as the drawer callback for the dialog menu. // static void P_DialogDrawer(void) { angle_t angle; int y; int i; int height; int finaly; char choicetext[64]; char choicetext2[64]; // Run down bonuscount faster than usual so that flashes from being given // items are less obvious. if(dialogplayer->bonuscount) { dialogplayer->bonuscount -= 3; if(dialogplayer->bonuscount < 0) dialogplayer->bonuscount = 0; } angle = R_PointToAngle2(dialogplayer->mo->x, dialogplayer->mo->y, dialogtalker->x, dialogtalker->y); angle -= dialogplayer->mo->angle; // Dismiss the dialog if the player is out of alignment, or the thing he was // talking to is now engaged in battle. if ((angle > ANG45 && angle < (ANG270+ANG45)) || (dialogtalker->flags & MF_NODIALOG) != 0) { P_DialogDoChoice(dialogmenu.numitems - 1); } dialogtalker->reactiontime = 2; // draw background if(dialogbgpiclumpnum != -1) { patch_t *patch = W_CacheLumpNum(dialogbgpiclumpnum, PU_CACHE); V_DrawPatchDirect(0, 0, patch); } // if there's a valid background pic, delay drawing the rest of the menu // for a while; otherwise, it will appear immediately if(dialogbgpiclumpnum == -1 || menupausetime <= gametic) { if(menuindialog) { // time to pause the game? if(menupausetime + 3 < gametic) menupause = true; } // draw character name M_WriteText(12, 18, dialogname); y = 28; // show text (optional for dialogs with voices) if(dialogshowtext || currentdialog->voice[0] == '\0') y = M_WriteText(20, 28, dialogtext); height = 20 * dialogmenu.numitems; finaly = 175 - height; // preferred height if(y > finaly) finaly = 199 - height; // height it will bump down to if necessary. // draw divider M_WriteText(42, finaly - 6, DEH_String("______________________________")); dialogmenu.y = finaly + 6; y = 0; // draw the menu items for(i = 0; i < dialogmenu.numitems - 1; i++) { DEH_snprintf(choicetext, sizeof(choicetext), "%d) %s", i + 1, currentdialog->choices[i].text); // alternate text for items that need money if(currentdialog->choices[i].needamounts[0] > 0) { // haleyjd 20120401: necessary to avoid undefined behavior: M_StringCopy(choicetext2, choicetext, sizeof(choicetext2)); DEH_snprintf(choicetext, sizeof(choicetext), "%s for %d", choicetext2, currentdialog->choices[i].needamounts[0]); } M_WriteText(dialogmenu.x, dialogmenu.y + 3 + y, choicetext); y += 19; } // draw the final item for dismissing the dialog M_WriteText(dialogmenu.x, 19 * i + dialogmenu.y + 3, dialoglastmsgbuffer); } } // // P_DialogDoChoice // // [STRIFE] New function // haleyjd 09/05/10: Handles making a choice in a dialog. Installed as the // callback for all items in the dialogmenu structure. // void P_DialogDoChoice(int choice) { int i = 0, nextdialog = 0; boolean candochoice = true; const char *message = NULL; mapdlgchoice_t *currentchoice; if(choice == -1) choice = dialogmenu.numitems - 1; currentchoice = &(currentdialog->choices[choice]); I_StartVoice(NULL); // STRIFE-TODO: verify (should stop previous voice I believe) // villsa 09/08/10: converted into for loop for(i = 0; i < MDLG_MAXITEMS; i++) { if(P_PlayerHasItem(dialogplayer, currentchoice->needitems[i]) < currentchoice->needamounts[i]) { candochoice = false; // nope, missing something } } if(choice != dialogmenu.numitems - 1 && candochoice) { int item; message = currentchoice->textok; if(dialogtalkerstates->yes) P_SetMobjState(dialogtalker, dialogtalkerstates->yes); item = currentchoice->giveitem; if(item < 0 || P_GiveItemToPlayer(dialogplayer, states[mobjinfo[item].spawnstate].sprite, item)) { // if successful, take needed items int count = 0; // villsa 09/08/10: converted into for loop for(count = 0; count < MDLG_MAXITEMS; count++) { P_TakeDialogItem(dialogplayer, currentchoice->needitems[count], currentchoice->needamounts[count]); } } else message = DEH_String("You seem to have enough!"); // store next dialog into the talking actor nextdialog = currentchoice->next; if(nextdialog != 0) dialogtalker->miscdata = (byte)(abs(nextdialog)); } else { // not successful message = currentchoice->textno; if(dialogtalkerstates->no) P_SetMobjState(dialogtalker, dialogtalkerstates->no); } if(choice != dialogmenu.numitems - 1) { int objective; char *objlump; if((objective = currentchoice->objective)) { DEH_snprintf(mission_objective, OBJECTIVE_LEN, "log%i", objective); objlump = W_CacheLumpName(mission_objective, PU_CACHE); M_StringCopy(mission_objective, objlump, OBJECTIVE_LEN); } // haleyjd 20130301: v1.31 hack: if first char of message is a period, // clear the player's message. Is this actually used anywhere? if(gameversion == exe_strife_1_31 && message[0] == '.') message = NULL; dialogplayer->message = message; } dialogtalker->angle = dialogtalkerangle; dialogplayer->st_update = true; M_ClearMenus(0); if(nextdialog >= 0 || gameaction == ga_victory) // Macil hack menuindialog = false; else P_DialogStart(dialogplayer); } // // P_DialogStartP1 // // [STRIFE] New function // haleyjd 09/13/10: This is a hack used by the finale system. // void P_DialogStartP1(void) { P_DialogStart(&players[0]); } // // P_DialogStart // // villsa [STRIFE] New function // void P_DialogStart(player_t *player) { int i = 0; int pic; int rnd = 0; const char *byetext; int jumptoconv; if(menuactive || netgame) return; // are we facing towards our NPC? P_AimLineAttack(player->mo, player->mo->angle, (128*FRACUNIT)); if(!linetarget) { P_AimLineAttack(player->mo, player->mo->angle + (ANG90/16), (128*FRACUNIT)); if(!linetarget) P_AimLineAttack(player->mo, player->mo->angle - (ANG90/16), (128*FRACUNIT)); } if(!linetarget) return; // already in combat, can't talk to it if(linetarget->flags & MF_NODIALOG) return; // set pointer to the character talking dialogtalker = linetarget; // play a sound if(player == &players[consoleplayer]) S_StartSound(0, sfx_radio); linetarget->target = player->mo; // target the player dialogtalker->reactiontime = 2; // set reactiontime dialogtalkerangle = dialogtalker->angle; // remember original angle // face talker towards player A_FaceTarget(dialogtalker); // face towards NPC's direction player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, dialogtalker->x, dialogtalker->y); // set pointer to player talking dialogplayer = player; // haleyjd 09/08/10: get any stored dialog state from this object jumptoconv = linetarget->miscdata; // check item requirements while(1) { int i = 0; currentdialog = P_DialogFind(linetarget->type, jumptoconv); // dialog's jumptoconv equal to 0? There's nothing to jump to. if(currentdialog->jumptoconv == 0) break; // villsa 09/08/10: converted into for loop for(i = 0; i < MDLG_MAXITEMS; i++) { // if the item is non-zero, the player must have at least one in his // or her inventory if(currentdialog->checkitem[i] != 0 && P_PlayerHasItem(dialogplayer, currentdialog->checkitem[i]) < 1) break; } if(i < MDLG_MAXITEMS) // didn't find them all? this is our dialog! break; jumptoconv = currentdialog->jumptoconv; } M_DialogDimMsg(20, 28, currentdialog->text, false); dialogtext = P_DialogGetMsg(currentdialog->text); // get states dialogtalkerstates = P_DialogGetStates(linetarget->type); // have talker greet the player if(dialogtalkerstates->greet) P_SetMobjState(dialogtalker, dialogtalkerstates->greet); // get talker's name if(currentdialog->name[0]) dialogname = currentdialog->name; else { // use a fallback: if(mobjinfo[linetarget->type].name) dialogname = DEH_String(mobjinfo[linetarget->type].name); // mobjtype name else dialogname = DEH_String("Person"); // default name - like Joe in Doom 3 :P } // setup number of choices to choose from for(i = 0; i < MDLG_MAXCHOICES; i++) { if(!currentdialog->choices[i].giveitem) break; } // set number of choices to menu dialogmenu.numitems = i + 1; rnd = M_Random() % 3; // setup dialog menu M_StartControlPanel(); menupause = false; menuindialog = true; menupausetime = gametic + 17; currentMenu = &dialogmenu; if(i >= dialogmenu.lastOn) itemOn = dialogmenu.lastOn; else itemOn = 0; // get backdrop pic = W_CheckNumForName(currentdialog->backpic); dialogbgpiclumpnum = pic; if(pic != -1) V_DrawPatchDirect(0, 0, W_CacheLumpNum(pic, PU_CACHE)); // get voice I_StartVoice(currentdialog->voice); // get bye text switch(rnd) { case 2: byetext = DEH_String("BYE!"); break; case 1: byetext = DEH_String("Thanks, Bye!"); break; default: case 0: byetext = DEH_String("See you later!"); break; } DEH_snprintf(dialoglastmsgbuffer, sizeof(dialoglastmsgbuffer), "%d) %s", i + 1, byetext); } // EOF crispy-doom-crispy-doom-5.6.4/src/strife/p_dialog.h000066400000000000000000000070061360717211000222510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1996 Rogue Entertainment / Velocity, Inc. // Copyright(C) 2010 James Haley, Samuel Villareal // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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: // // [STRIFE] New Module // // Dialog Engine for Strife // #ifndef P_DIALOG_H__ #define P_DIALOG_H__ #define OBJECTIVE_LEN 300 #define MAXINVENTORYSLOTS 30 #define MDLG_CHOICELEN 32 #define MDLG_MSGLEN 80 #define MDLG_NAMELEN 16 #define MDLG_LUMPLEN 8 #define MDLG_TEXTLEN 320 #define MDLG_MAXCHOICES 5 #define MDLG_MAXITEMS 3 extern char mission_objective[OBJECTIVE_LEN]; extern int dialogshowtext; // villsa - convenient macro for giving objective logs to player #define GiveObjective(x, minlumpnum) \ do { \ int obj_ln = W_CheckNumForName(DEH_String(x)); \ if(obj_ln > minlumpnum) \ M_StringCopy(mission_objective, W_CacheLumpNum(obj_ln, PU_CACHE), \ OBJECTIVE_LEN);\ } while(0) // haleyjd - voice and objective in one #define GiveVoiceObjective(voice, log, minlumpnum) \ do { \ int obj_ln = W_CheckNumForName(DEH_String(log)); \ I_StartVoice(DEH_String(voice)); \ if(obj_ln > minlumpnum) \ M_StringCopy(mission_objective, W_CacheLumpNum(obj_ln, PU_CACHE), \ OBJECTIVE_LEN);\ } while(0) typedef struct mapdlgchoice_s { int giveitem; // item given when successful int needitems[MDLG_MAXITEMS]; // item needed for success int needamounts[MDLG_MAXITEMS]; // amount of items needed char text[MDLG_CHOICELEN]; // normal text char textok[MDLG_MSGLEN]; // message given on success int next; // next dialog? int objective; // ??? char textno[MDLG_MSGLEN]; // message given on failure } mapdlgchoice_t; typedef struct mapdialog_s { int speakerid; // script ID# for mobjtype that will use this dialog int dropitem; // item to drop if that thingtype is killed int checkitem[MDLG_MAXITEMS]; // item(s) needed to see this dialog int jumptoconv; // conversation to jump to when... ? char name[MDLG_NAMELEN]; // name of speaker char voice[MDLG_LUMPLEN]; // voice file to play char backpic[MDLG_LUMPLEN]; // backdrop pic for character, if any char text[MDLG_TEXTLEN]; // main message text // options that this dialog gives the player mapdlgchoice_t choices[MDLG_MAXCHOICES]; } mapdialog_t; void P_DialogLoad(void); void P_DialogStart(player_t *player); void P_DialogDoChoice(int choice); boolean P_GiveItemToPlayer(player_t *player, int sprnum, mobjtype_t type); boolean P_GiveInventoryItem(player_t *player, int sprnum, mobjtype_t type); boolean P_UseInventoryItem(player_t* player, int item); void P_DialogStartP1(void); mapdialog_t* P_DialogFind(mobjtype_t type, int jumptoconv); int P_PlayerHasItem(player_t *player, mobjtype_t type); #endif // EOF crispy-doom-crispy-doom-5.6.4/src/strife/p_doors.c000066400000000000000000001053411360717211000221340ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: Door animation code (opening/closing) // #include "z_zone.h" #include "doomdef.h" #include "deh_main.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "dstrings.h" #include "sounds.h" // [STRIFE] #include "p_dialog.h" #include "i_system.h" // // VERTICAL DOORS // // // T_VerticalDoor // void T_VerticalDoor(vldoor_t* door) { result_e res1; result_e res2; switch(door->direction) { case 0: // WAITING if (!--door->topcountdown) { switch(door->type) { case vld_blazeRaise: door->direction = -1; // time to go back down S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_normal: door->direction = -1; // time to go back down // villsa [STRIFE] closesound added S_StartSound(&door->sector->soundorg, door->closesound); break; // villsa [STRIFE] case vld_shopClose: door->direction = 1; door->speed = (2*FRACUNIT); S_StartSound(&door->sector->soundorg, door->opensound); break; case vld_close30ThenOpen: door->direction = 1; // villsa [STRIFE] opensound added S_StartSound(&door->sector->soundorg, door->opensound); break; default: break; } } break; case 2: // INITIAL WAIT if (!--door->topcountdown) { switch(door->type) { case vld_raiseIn5Mins: door->direction = 1; door->type = vld_normal; // villsa [STRIFE] opensound added S_StartSound(&door->sector->soundorg, door->opensound); break; default: break; } } break; // villsa [STRIFE] case -2: // SPLIT res1 = T_MovePlane(door->sector, door->speed, door->topheight, 0, 1, 1); res2 = T_MovePlane(door->sector, door->speed, door->topwait, 0, 0, -1); if(res1 == pastdest && res2 == pastdest) { door->sector->specialdata = NULL; P_RemoveThinker(&door->thinker); // unlink and free } break; case -1: // DOWN res1 = T_MovePlane(door->sector, door->speed, door->sector->floorheight, false, 1, door->direction); if(res1 == pastdest) { switch(door->type) { case vld_normal: case vld_close: case vld_blazeRaise: case vld_blazeClose: door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free // villsa [STRIFE] no sounds break; case vld_close30ThenOpen: door->direction = 0; door->topcountdown = TICRATE*30; break; // villsa [STRIFE] case vld_shopClose: door->direction = 0; door->topcountdown = TICRATE*120; break; default: break; } } else if(res1 == crushed) { switch(door->type) { case vld_blazeClose: case vld_close: // DO NOT GO BACK UP! case vld_shopClose: // villsa [STRIFE] break; default: door->direction = 1; // villsa [STRIFE] opensound added S_StartSound(&door->sector->soundorg, door->opensound); break; } } break; case 1: // UP res1 = T_MovePlane(door->sector, door->speed, door->topheight, false,1,door->direction); if(res1 == pastdest) { switch(door->type) { case vld_blazeRaise: case vld_normal: door->direction = 0; // wait at top door->topcountdown = door->topwait; break; case vld_close30ThenOpen: case vld_blazeOpen: case vld_open: case vld_shopClose: // villsa [STRIFE] door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free break; default: break; } } break; } } // // EV_DoLockedDoor // Move a locked door up/down // // [STRIFE] This game has a crap load of keys. And this function doesn't even // deal with all of them... // int EV_DoLockedDoor(line_t* line, vldoor_e type, mobj_t* thing) { player_t* p; p = thing->player; if(!p) return 0; switch(line->special) { case 99: case 133: if(!p->cards[key_IDCard]) { p->message = DEH_String("You need an id card"); S_StartSound(NULL, sfx_oof); return 0; } break; case 134: case 135: if(!p->cards[key_IDBadge]) { p->message = DEH_String("You need an id badge"); S_StartSound(NULL, sfx_oof); return 0; } break; case 136: case 137: if(!p->cards[key_Passcard]) { p->message = DEH_String("You need a pass card"); S_StartSound(NULL, sfx_oof); return 0; } break; case 151: case 164: if(!p->cards[key_GoldKey]) { p->message = DEH_String("You need a gold key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 153: case 163: if(!p->cards[key_SilverKey]) { p->message = DEH_String("You need a silver key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 152: case 162: if(!p->cards[key_BrassKey]) { p->message = DEH_String("You need a brass key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 167: case 168: if(!p->cards[key_SeveredHand]) { p->message = DEH_String("Hand print not on file"); S_StartSound(NULL, sfx_oof); return 0; } break; case 171: if(!p->cards[key_PrisonKey]) { p->message = DEH_String("You don't have the key to the prison"); S_StartSound(NULL, sfx_oof); return 0; } break; case 172: if(!p->cards[key_Power1Key]) { p->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 173: if(!p->cards[key_Power2Key]) { p->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 176: if(!p->cards[key_Power3Key]) { p->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 189: if(!p->cards[key_OracleKey]) { p->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 191: if(!p->cards[key_MilitaryID]) { p->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 192: if(!p->cards[key_WarehouseKey]) { p->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return 0; } break; case 223: if(!p->cards[key_MineKey]) { p->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return 0; } break; } return EV_DoDoor(line,type); } // // EV_DoDoor // int EV_DoDoor(line_t* line, vldoor_e type) { int secnum, rtn; sector_t* sec; vldoor_t* door; secnum = -1; rtn = 0; while((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if(sec->specialdata) continue; // new door thinker rtn = 1; door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; door->sector = sec; door->type = type; door->topwait = VDOORWAIT; door->speed = VDOORSPEED; R_SoundNumForDoor(door); // villsa [STRIFE] set door sounds switch(type) { // villsa [STRIFE] new door type case vld_splitOpen: door->direction = -2; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->speed = FRACUNIT; // yes, it using topwait to get the floor height door->topwait = P_FindLowestFloorSurrounding(sec); if(door->topheight == sec->ceilingheight) continue; S_StartSound(&sec->soundorg, door->opensound); break; // villsa [STRIFE] new door type case vld_splitRaiseNearest: door->direction = -2; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->speed = FRACUNIT; // yes, it using topwait to get the floor height door->topwait = P_FindHighestFloorSurrounding(sec); if(door->topheight == sec->ceilingheight) continue; S_StartSound(&sec->soundorg, door->opensound); break; case vld_blazeClose: case vld_shopClose: // villsa [STRIFE] door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; door->speed = VDOORSPEED * 4; S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_close: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; // villsa [STRIFE] set door sounds S_StartSound(&door->sector->soundorg, door->opensound); break; case vld_close30ThenOpen: door->topheight = sec->ceilingheight; door->direction = -1; // villsa [STRIFE] set door sounds S_StartSound(&door->sector->soundorg, door->closesound); break; case vld_blazeRaise: case vld_blazeOpen: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->speed = VDOORSPEED * 4; if (door->topheight != sec->ceilingheight) S_StartSound(&door->sector->soundorg, sfx_bdopn); break; case vld_normal: case vld_open: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; if(door->topheight != sec->ceilingheight) S_StartSound(&door->sector->soundorg, door->opensound); break; default: break; } } return rtn; } // // EV_ClearForceFields // // villsa [STRIFE] new function // boolean EV_ClearForceFields(line_t* line) { int secnum; sector_t* sec; int i; line_t* secline; boolean ret = false; secnum = -1; while((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; line->special = 0; ret = true; // haleyjd 09/18/10: fixed to continue w/linecount == 0, not return for(i = 0; i < sec->linecount; i++) { secline = sec->lines[i]; if(!(secline->flags & ML_TWOSIDED)) continue; if(secline->special != 148) continue; secline->flags &= ~ML_BLOCKING; secline->special = 0; sides[secline->sidenum[0]].midtexture = 0; sides[secline->sidenum[1]].midtexture = 0; } } return ret; } // // EV_VerticalDoor : open a door manually, no tag value // // [STRIFE] Tons of new door types were added. // void EV_VerticalDoor(line_t* line, mobj_t* thing) { player_t* player; sector_t* sec; vldoor_t* door; int side; side = 0; // only front sides can be used // Check for locks player = thing->player; // haleyjd 09/15/10: [STRIFE] myriad checks here... switch(line->special) { case 26: // DR ID Card door case 32: // D1 ID Card door if(!player->cards[key_IDCard]) { player->message = DEH_String("You need an id card to open this door"); S_StartSound(NULL, sfx_oof); return; } break; case 27: // DR Pass Card door case 34: // D1 Pass Card door if(!player->cards[key_Passcard]) { player->message = DEH_String("You need a pass card key to open this door"); S_StartSound(NULL, sfx_oof); return; } break; case 28: // DR ID Badge door case 33: // D1 ID Badge door if(!player->cards[key_IDBadge]) { player->message = DEH_String("You need an id badge to open this door"); S_StartSound(NULL, sfx_oof); return; } break; case 156: // D1 brass key door case 161: // DR brass key door if(!player->cards[key_BrassKey]) { player->message = DEH_String("You need a brass key"); S_StartSound(NULL, sfx_oof); return; } break; case 157: // D1 silver key door case 160: // DR silver key door if(!player->cards[key_SilverKey]) { player->message = DEH_String("You need a silver key"); S_StartSound(NULL, sfx_oof); return; } break; case 158: // D1 gold key door case 159: // DR gold key door if(!player->cards[key_GoldKey]) { player->message = DEH_String("You need a gold key"); S_StartSound(NULL, sfx_oof); return; } break; // villsa [STRIFE] added 09/15/10 case 165: player->message = DEH_String("That doesn't seem to work"); S_StartSound(NULL, sfx_oof); return; case 166: // DR Hand Print door if(!player->cards[key_SeveredHand]) { player->message = DEH_String("Hand print not on file"); S_StartSound(NULL, sfx_oof); return; } break; case 169: // DR Base key door if(!player->cards[key_BaseKey]) { player->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return; } break; case 170: // DR Gov's Key door if(!player->cards[key_GovsKey]) { player->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return; } break; case 190: // DR Order Key door if(!player->cards[key_OrderKey]) { player->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return; } break; case 205: // DR "Only in retail" player->message = DEH_String("THIS AREA IS ONLY AVAILABLE IN THE " "RETAIL VERSION OF STRIFE"); S_StartSound(NULL, sfx_oof); return; case 213: // DR Chalice door if(!P_PlayerHasItem(player, MT_INV_CHALICE)) { player->message = DEH_String("You need the chalice!"); S_StartSound(NULL, sfx_oof); return; } break; case 217: // DR Core Key door if(!player->cards[key_CoreKey]) { player->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return; } break; case 221: // DR Mauler Key door if(!player->cards[key_MaulerKey]) { player->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return; } break; case 224: // DR Chapel Key door if(!player->cards[key_ChapelKey]) { player->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return; } break; case 225: // DR Catacomb Key door if(!player->cards[key_CatacombKey]) { player->message = DEH_String("You don't have the key"); S_StartSound(NULL, sfx_oof); return; } break; case 232: // DR Oracle Pass door if(!(player->questflags & QF_QUEST18)) { player->message = DEH_String("You need the Oracle Pass!"); S_StartSound(NULL, sfx_oof); return; } break; default: break; } // if the sector has an active thinker, use it sec = sides[ line->sidenum[side^1]] .sector; if (sec->specialdata) { door = sec->specialdata; // [STRIFE] Adjusted to handle linetypes handled here by Strife. // BUG: Not all door types are checked here. This means that certain // door lines are allowed to fall through and start a new thinker on the // sector! This is why some doors can become jammed in Strife - stuck in // midair, or unable to be opened at all. Multiple thinkers will fight // over how to move the door. They should have added a default return if // they weren't going to handle this unconditionally... switch(line->special) { case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s case 26: case 27: case 28: case 117: case 159: // villsa case 160: // haleyjd case 161: // villsa case 166: // villsa case 169: // villsa case 170: // villsa case 190: // villsa case 213: // villsa case 232: // villsa if(door->direction == -1) door->direction = 1; // go back up else { if (!thing->player) return; // When is a door not a door? // In Vanilla, door->direction is set, even though // "specialdata" might not actually point at a door. if (door->thinker.function.acp1 == (actionf_p1) T_VerticalDoor) { door->direction = -1; // start going down immediately } else if (door->thinker.function.acp1 == (actionf_p1) T_PlatRaise) { // Erm, this is a plat, not a door. // This notably causes a problem in ep1-0500.lmp where // a plat and a door are cross-referenced; the door // doesn't open on 64-bit. // The direction field in vldoor_t corresponds to the wait // field in plat_t. Let's set that to -1 instead. plat_t *plat; plat = (plat_t *) door; plat->wait = -1; } else { // This isn't a door OR a plat. Now we're in trouble. fprintf(stderr, "EV_VerticalDoor: Tried to close " "something that wasn't a door.\n"); // Try closing it anyway. At least it will work on 32-bit // machines. door->direction = -1; } } return; default: break; } } // haleyjd 09/15/10: [STRIFE] Removed DOOM door sounds // new door thinker door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->thinker.function.acp1 = (actionf_p1) T_VerticalDoor; door->sector = sec; door->direction = 1; door->speed = VDOORSPEED; door->topwait = VDOORWAIT; R_SoundNumForDoor(door); // haleyjd 09/15/10: [STRIFE] Get door sounds // for proper sound - [STRIFE] - verified complete switch(line->special) { case 117: // BLAZING DOOR RAISE case 118: // BLAZING DOOR OPEN S_StartSound(&sec->soundorg, sfx_bdopn); break; default: // NORMAL DOOR SOUND S_StartSound(&sec->soundorg, door->opensound); break; } // haleyjd: [STRIFE] - verified all. switch(line->special) { case 1: case 26: case 27: case 28: door->type = vld_normal; break; case 31: case 32: case 33: case 34: case 156: // villsa [STRIFE] case 157: // villsa [STRIFE] case 158: // villsa [STRIFE] door->type = vld_open; line->special = 0; break; case 117: // blazing door raise door->type = vld_blazeRaise; door->speed = VDOORSPEED*4; break; case 118: // blazing door open door->type = vld_blazeOpen; line->special = 0; door->speed = VDOORSPEED*4; break; default: // haleyjd: [STRIFE] pretty important to have this here! door->type = vld_normal; break; } // find the top and bottom of the movement range door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; } // // Spawn a door that closes after 30 seconds // void P_SpawnDoorCloseIn30 (sector_t* sec) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; door->sector = sec; door->direction = 0; door->type = vld_normal; door->speed = VDOORSPEED; door->topcountdown = 30 * TICRATE; } // // Spawn a door that opens after 5 minutes // void P_SpawnDoorRaiseIn5Mins ( sector_t* sec, int secnum ) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; door->sector = sec; door->direction = 2; door->type = vld_raiseIn5Mins; door->speed = VDOORSPEED; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->topwait = VDOORWAIT; door->topcountdown = 5 * 60 * TICRATE; } // villsa [STRIFE] resurrected sliding doors // // // villsa [STRIFE] // // Sliding door name information // static slidename_t slideFrameNames[MAXSLIDEDOORS] = { // SIGLDR { "SIGLDR01", // frame1 "SIGLDR02", // frame2 "SIGLDR03", // frame3 "SIGLDR04", // frame4 "SIGLDR05", // frame5 "SIGLDR06", // frame6 "SIGLDR07", // frame7 "SIGLDR08" // frame8 }, // DORSTN { "DORSTN01", // frame1 "DORSTN02", // frame2 "DORSTN03", // frame3 "DORSTN04", // frame4 "DORSTN05", // frame5 "DORSTN06", // frame6 "DORSTN07", // frame7 "DORSTN08" // frame8 }, // DORQTR { "DORQTR01", // frame1 "DORQTR02", // frame2 "DORQTR03", // frame3 "DORQTR04", // frame4 "DORQTR05", // frame5 "DORQTR06", // frame6 "DORQTR07", // frame7 "DORQTR08" // frame8 }, // DORCRG { "DORCRG01", // frame1 "DORCRG02", // frame2 "DORCRG03", // frame3 "DORCRG04", // frame4 "DORCRG05", // frame5 "DORCRG06", // frame6 "DORCRG07", // frame7 "DORCRG08" // frame8 }, // DORCHN { "DORCHN01", // frame1 "DORCHN02", // frame2 "DORCHN03", // frame3 "DORCHN04", // frame4 "DORCHN05", // frame5 "DORCHN06", // frame6 "DORCHN07", // frame7 "DORCHN08" // frame8 }, // DORIRS { "DORIRS01", // frame1 "DORIRS02", // frame2 "DORIRS03", // frame3 "DORIRS04", // frame4 "DORIRS05", // frame5 "DORIRS06", // frame6 "DORIRS07", // frame7 "DORIRS08" // frame8 }, // DORALN { "DORALN01", // frame1 "DORALN02", // frame2 "DORALN03", // frame3 "DORALN04", // frame4 "DORALN05", // frame5 "DORALN06", // frame6 "DORALN07", // frame7 "DORALN08" // frame8 }, {"\0","\0","\0","\0","\0","\0","\0","\0"} }; // // villsa [STRIFE] // // Sliding door open sounds // static sfxenum_t slideOpenSounds[MAXSLIDEDOORS] = { sfx_drlmto, sfx_drston, sfx_airlck, sfx_drsmto, sfx_drchno, sfx_airlck, sfx_airlck, sfx_None }; // // villsa [STRIFE] // // Sliding door close sounds // static sfxenum_t slideCloseSounds[MAXSLIDEDOORS] = { sfx_drlmtc, sfx_drston, sfx_airlck, sfx_drsmtc, sfx_drchnc, sfx_airlck, sfx_airlck, sfx_None }; slideframe_t slideFrames[MAXSLIDEDOORS]; // // P_InitSlidingDoorFrames // // villsa [STRIFE] resurrected // void P_InitSlidingDoorFrames(void) { int i; int f1; int f2; int f3; int f4; memset(slideFrames, 0, sizeof(slideframe_t) * MAXSLIDEDOORS); for(i = 0; i < MAXSLIDEDOORS; i++) { if(!slideFrameNames[i].frame1[0]) break; f1 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame1)); f2 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame2)); f3 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame3)); f4 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame4)); slideFrames[i].frames[0] = f1; slideFrames[i].frames[1] = f2; slideFrames[i].frames[2] = f3; slideFrames[i].frames[3] = f4; f1 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame5)); f2 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame6)); f3 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame7)); f4 = R_TextureNumForName(DEH_String(slideFrameNames[i].frame8)); slideFrames[i].frames[4] = f1; slideFrames[i].frames[5] = f2; slideFrames[i].frames[6] = f3; slideFrames[i].frames[7] = f4; } } // // P_FindSlidingDoorType // // Return index into "slideFrames" array // for which door type to use // // villsa [STRIFE] resurrected // int P_FindSlidingDoorType(line_t* line) { int i; int val; for(i = 0; i < MAXSLIDEDOORS-1; i++) { val = sides[line->sidenum[0]].toptexture; if(val == slideFrames[i].frames[0]) return i; } return -1; } // // T_SlidingDoor // // villsa [STRIFE] resurrected // void T_SlidingDoor(slidedoor_t* door) { sector_t* sec; sec = door->frontsector; switch(door->status) { case sd_opening: if(!door->timer--) { if(++door->frame == SNUMFRAMES) { // IF DOOR IS DONE OPENING... door->line1->flags &= ~ML_BLOCKING; door->line2->flags &= ~ML_BLOCKING; if(door->type == sdt_openOnly) { door->frontsector->specialdata = NULL; P_RemoveThinker (&door->thinker); return; } door->timer = SDOORWAIT; door->status = sd_waiting; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door->timer = SWAITTICS; sides[door->line2->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; sides[door->line2->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; sides[door->line1->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; sides[door->line1->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; } } return; case sd_waiting: // IF DOOR IS DONE WAITING... if(!door->timer--) { fixed_t speed; fixed_t cheight; sec = door->frontsector; // CAN DOOR CLOSE? if(sec->thinglist != NULL) { door->timer = SDOORWAIT; return; } else { cheight = sec->ceilingheight; speed = cheight - sec->floorheight - (10*FRACUNIT); // something blocking it? if(T_MovePlane(sec, speed, sec->floorheight, 0, 1, -1) == crushed) { door->timer = SDOORWAIT; return; } else { // Instantly move plane T_MovePlane(sec, (128*FRACUNIT), cheight, 0, 1, 1); // turn line blocking back on door->line1->flags |= ML_BLOCKING; door->line2->flags |= ML_BLOCKING; // play close sound S_StartSound(&sec->soundorg, slideCloseSounds[door->whichDoorIndex]); door->status = sd_closing; door->timer = SWAITTICS; } } } return; case sd_closing: if (!door->timer--) { if(--door->frame < 0) { // IF DOOR IS DONE CLOSING... T_MovePlane(sec, (128*FRACUNIT), sec->floorheight, 0, 1, -1); door->frontsector->specialdata = NULL; P_RemoveThinker (&door->thinker); return; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door->timer = SWAITTICS; sides[door->line2->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; sides[door->line2->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; sides[door->line1->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; sides[door->line1->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex].frames[door->frame]; } } return; } } // // EV_RemoteSlidingDoor // // villsa [STRIFE] new function // int EV_RemoteSlidingDoor(line_t* line, mobj_t* thing) { int secnum; sector_t* sec; int i; int rtn; line_t* secline; secnum = -1; rtn = 0; while((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if(sec->specialdata) continue; for(i = 0; i < 4; i++) { secline = sec->lines[i]; if(P_FindSlidingDoorType(secline) < 0) continue; EV_SlidingDoor(secline, thing); rtn = 1; } } return rtn; } // // EV_SlidingDoor // // villsa [STRIFE] // void EV_SlidingDoor(line_t* line, mobj_t* thing) { sector_t* sec; slidedoor_t* door; int i; line_t* secline; // Make sure door isn't already being animated sec = sides[line->sidenum[1]].sector; door = NULL; if(sec->specialdata) { if (!thing->player) return; door = sec->specialdata; if(door->type == sdt_openAndClose) { if(door->status == sd_waiting) { door->status = sd_closing; door->timer = SWAITTICS; // villsa [STRIFE] } } else return; } // Init sliding door vars if(!door) { door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->type = sdt_openAndClose; door->status = sd_opening; door->whichDoorIndex = P_FindSlidingDoorType(line); // villsa [STRIFE] different error message if(door->whichDoorIndex < 0) I_Error(DEH_String("EV_SlidingDoor: Textures are not defined for sliding door!")); sides[line->sidenum[0]].midtexture = sides[line->sidenum[0]].toptexture; // villsa [STRIFE] door->line1 = line; door->line2 = line; // villsa [STRIFE] this loop assumes that the sliding door is made up // of only four linedefs! for(i = 0; i < 4; i++) { secline = sec->lines[i]; if(secline != line) { side_t* side1; side_t* side2; side1 = &sides[secline->sidenum[0]]; side2 = &sides[line->sidenum[0]]; if(side1->toptexture == side2->toptexture) door->line2 = secline; } } door->thinker.function.acp1 = (actionf_p1)T_SlidingDoor; door->timer = SWAITTICS; door->frontsector = sec; door->frame = 0; // villsa [STRIFE] preset flags door->line1->flags |= ML_BLOCKING; door->line2->flags |= ML_BLOCKING; // villsa [STRIFE] set the closing sector T_MovePlane( door->frontsector, (128*FRACUNIT), P_FindLowestCeilingSurrounding(door->frontsector), 0, 1, 1); // villsa [STRIFE] play open sound S_StartSound(&door->frontsector->soundorg, slideOpenSounds[door->whichDoorIndex]); } } crispy-doom-crispy-doom-5.6.4/src/strife/p_enemy.c000066400000000000000000002351141360717211000221250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Enemy thinking, AI. // Action Pointer Functions // that are associated with states/frames. // #include #include #include "m_random.h" #include "i_system.h" #include "doomdef.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" #include "g_game.h" #include "z_zone.h" // villsa [STRIFE] // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // [STRIFE] Dialog / Inventory #include "p_dialog.h" #include "deh_str.h" #include "w_wad.h" #include "f_finale.h" #include "p_inter.h" // Forward Declarations: void A_RandomWalk(mobj_t *); void A_ProgrammerAttack(mobj_t* actor); void A_FireSigilEOffshoot(mobj_t *actor); void A_SpectreCAttack(mobj_t *actor); void A_SpectreDAttack(mobj_t *actor); void A_SpectreEAttack(mobj_t *actor); void P_ThrustMobj(mobj_t *actor, angle_t angle, fixed_t force); typedef enum { DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_NODIR, NUMDIRS } dirtype_t; // // P_NewChaseDir related LUT. // dirtype_t opposite[] = { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR }; dirtype_t diags[] = { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST }; void A_Fall (mobj_t *actor); // // 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. // // haleyjd 09/05/10: [STRIFE] Verified unmodified // mobj_t* soundtarget; void P_RecursiveSound ( sector_t* sec, int soundblocks ) { int i; line_t* check; sector_t* other; // wake up all monsters in this sector if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1) { return; // already flooded } sec->validcount = validcount; sec->soundtraversed = soundblocks+1; sec->soundtarget = soundtarget; for (i=0 ;ilinecount ; i++) { check = sec->lines[i]; if (! (check->flags & ML_TWOSIDED) ) continue; P_LineOpening (check); if (openrange <= 0) continue; // closed door if ( sides[ check->sidenum[0] ].sector == sec) other = sides[ check->sidenum[1] ] .sector; else other = sides[ check->sidenum[0] ].sector; if (check->flags & ML_SOUNDBLOCK) { if (!soundblocks) P_RecursiveSound (other, 1); } else P_RecursiveSound (other, soundblocks); } } // // P_NoiseAlert // If a monster yells at a player, // it will alert other monsters to the player. // // haleyjd 09/05/10: [STRIFE] Verified unmodified // void P_NoiseAlert ( mobj_t* target, mobj_t* emmiter ) { soundtarget = target; validcount++; P_RecursiveSound (emmiter->subsector->sector, 0); } // // P_WakeUpThing // // villsa [STRIFE] New function // Wakes up an mobj.nearby when somebody has been punched. // static void P_WakeUpThing(mobj_t* puncher, mobj_t* bystander) { if(!(bystander->flags & MF_NODIALOG)) { bystander->target = puncher; if(bystander->info->seesound) S_StartSound(bystander, bystander->info->seesound); P_SetMobjState(bystander, bystander->info->seestate); } } // // P_DoPunchAlert // // villsa [STRIFE] New function (by Quasar ;) // Wake up buddies nearby when the player thinks he's gotten too clever // with the punch dagger. Walks sector links. // void P_DoPunchAlert(mobj_t *puncher, mobj_t *punchee) { mobj_t *rover; // don't bother with this crap if we're already on alert if(punchee->subsector->sector->soundtarget) return; // gotta still be alive to call for help if(punchee->health <= 0) return; // has to be something you can wake up and kill too if(!(punchee->flags & MF_COUNTKILL) || punchee->flags & MF_NODIALOG) return; // make the punchee hurt - haleyjd 09/05/10: Fixed to use painstate. punchee->target = puncher; P_SetMobjState(punchee, punchee->info->painstate); // wake up everybody nearby // scan forward on sector list for(rover = punchee->snext; rover; rover = rover->snext) { // we only wake up certain thing types (Acolytes and Templars?) if(rover->health > 0 && rover->type >= MT_GUARD1 && rover->type <= MT_PGUARD && (P_CheckSight(rover, puncher) || P_CheckSight(rover, punchee))) { P_WakeUpThing(puncher, rover); rover->flags |= MF_NODIALOG; } } // scan backward on sector list for(rover = punchee->sprev; rover; rover = rover->sprev) { // we only wake up certain thing types (Acolytes and Templars?) if(rover->health > 0 && rover->type >= MT_GUARD1 && rover->type <= MT_PGUARD && (P_CheckSight(rover, puncher) || P_CheckSight(rover, punchee))) { P_WakeUpThing(puncher, rover); rover->flags |= MF_NODIALOG; } } } // // P_CheckMeleeRange // // [STRIFE] Minor change to meleerange. // boolean P_CheckMeleeRange(mobj_t* actor) { mobj_t* pl; fixed_t dist; if(!actor->target) return false; pl = actor->target; if(actor->z + 3 * actor->height / 2 < pl->z) // villsa [STRIFE] return false; dist = P_AproxDistance(pl->x - actor->x, pl->y - actor->y); if(dist >= MELEERANGE - 20*FRACUNIT + pl->info->radius) return false; if(!P_CheckSight (actor, actor->target)) return false; return true; } // // P_CheckMissileRange // // [STRIFE] // Changes to eliminate DOOM-specific code and to allow for // varying attack ranges for Strife monsters, as well as a general tweak // to considered distance for all monsters. // boolean P_CheckMissileRange(mobj_t* actor) { fixed_t dist; if(!P_CheckSight(actor, actor->target)) return false; if(actor->flags & MF_JUSTHIT) { // the target just hit the enemy, // so fight back! actor->flags &= ~MF_JUSTHIT; return true; } if(actor->reactiontime) return false; // do not attack yet // OPTIMIZE: get this from a global checksight dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - 64*FRACUNIT; if (!actor->info->meleestate) dist -= 128*FRACUNIT; // no melee attack, so fire more dist >>= 16; // villsa [STRIFE] checks for acolytes // haleyjd 09/05/10: Repaired to match disassembly: Was including // SHADOWGUARD in the wrong case, was missing MT_SENTINEL entirely. // Structure of ASM also indicates this was probably a switch // statement turned into a cascading if/else by the compiler. switch(actor->type) { case MT_GUARD1: case MT_GUARD2: case MT_GUARD3: case MT_GUARD4: case MT_GUARD5: case MT_GUARD6: // oddly, not all Acolytes are included here... dist >>= 4; break; case MT_SHADOWGUARD: case MT_CRUSADER: case MT_SENTINEL: dist >>= 1; break; default: break; } // villsa [STRIFE] changed to 150 if (dist > 150) dist = 150; // haleyjd 20100910: Hex-Rays was leaving this out completely: if (actor->type == MT_CRUSADER && dist > 120) dist = 120; // haleyjd 20110224 [STRIFE]: reversed predicate return (dist < P_Random()); } // // P_CheckRobotRange // // villsa [STRIFE] New function // boolean P_CheckRobotRange(mobj_t *actor) { fixed_t dist; if(!P_CheckSight(actor, actor->target)) return false; if(actor->reactiontime) return false; // do not attack yet dist = (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - 64*FRACUNIT) >> FRACBITS; return (dist < 200); } // // P_Move // Move in the current direction, // returns false if the move is blocked. // // [STRIFE] // villsa/haleyjd 09/05/10: Modified for terrain types and 3D object // clipping. Below constants are verified to be unmodified: // fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; extern line_t* spechit[]; extern int numspechit; boolean P_Move (mobj_t* actor) { fixed_t tryx; fixed_t tryy; line_t* ld; // warning: 'catch', 'throw', and 'try' // are all C++ reserved words boolean try_ok; boolean good; if (actor->movedir == DI_NODIR) return false; if ((unsigned)actor->movedir >= 8) I_Error ("Weird actor->movedir!"); tryx = actor->x + actor->info->speed*xspeed[actor->movedir]; tryy = actor->y + actor->info->speed*yspeed[actor->movedir]; try_ok = P_TryMove (actor, tryx, tryy); if (!try_ok) { // open any specials if (actor->flags & MF_FLOAT && floatok) { // must adjust height if (actor->z < tmfloorz) actor->z += FLOATSPEED; // [STRIFE] Note FLOATSPEED == 5*FRACUNIT else actor->z -= FLOATSPEED; actor->flags |= MF_INFLOAT; return true; } if (!numspechit) return false; actor->movedir = DI_NODIR; good = false; while (numspechit--) { ld = spechit[numspechit]; // if the special is not a door // that can be opened, // return false if (P_UseSpecialLine (actor, ld,0)) good = true; } return good; } else { actor->flags &= ~(MF_INFLOAT|MF_FEETCLIPPED); // villsa [STRIFE] // villsa [STRIFE] if(P_GetTerrainType(actor) != FLOOR_SOLID) actor->flags |= MF_FEETCLIPPED; } // villsa [STRIFE] Removed pulling non-floating actors down to the ground. // (haleyjd 09/05/10: Verified) /*if (! (actor->flags & MF_FLOAT) ) actor->z = actor->floorz;*/ return true; } // // TryWalk // Attempts to move actor on // in its current (ob->moveangle) direction. // If blocked by either a wall or an actor // returns FALSE // If move is either clear or blocked only by a door, // returns TRUE and sets... // If a door is in the way, // an OpenDoor call is made to start it opening. // // haleyjd 09/05/10: [STRIFE] Verified unmodified. // boolean P_TryWalk (mobj_t* actor) { if (!P_Move (actor)) { return false; } actor->movecount = P_Random()&15; return true; } // // P_NewChaseDir // void P_NewChaseDir(mobj_t* actor) { fixed_t deltax; fixed_t deltay; dirtype_t d[3]; int tdir; dirtype_t olddir; dirtype_t turnaround; // villsa [STRIFE] don't bomb out and instead set spawnstate if(!actor->target) { //I_Error("P_NewChaseDir: called with no target"); P_SetMobjState(actor, actor->info->spawnstate); return; } olddir = actor->movedir; turnaround=opposite[olddir]; deltax = actor->target->x - actor->x; deltay = actor->target->y - actor->y; if (deltax>10*FRACUNIT) d[1]= DI_EAST; else if (deltax<-10*FRACUNIT) d[1]= DI_WEST; else d[1]=DI_NODIR; if (deltay<-10*FRACUNIT) d[2]= DI_SOUTH; else if (deltay>10*FRACUNIT) d[2]= DI_NORTH; else d[2]=DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { actor->movedir = diags[((deltay<0)<<1)+(deltax>0)]; if (actor->movedir != (int) turnaround && P_TryWalk(actor)) return; } // try other directions if (P_Random() > 200 || abs(deltay)>abs(deltax)) { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } if (d[1]==turnaround) d[1]=DI_NODIR; if (d[2]==turnaround) d[2]=DI_NODIR; if (d[1]!=DI_NODIR) { actor->movedir = d[1]; if (P_TryWalk(actor)) { // either moved forward or attacked return; } } if (d[2]!=DI_NODIR) { actor->movedir =d[2]; if (P_TryWalk(actor)) return; } // there is no direct path to the player, // so pick another direction. if (olddir!=DI_NODIR) { actor->movedir =olddir; if (P_TryWalk(actor)) return; } // randomly determine direction of search if (P_Random()&1) { for ( tdir=DI_EAST; tdir<=DI_SOUTHEAST; tdir++ ) { if (tdir != (int) turnaround) { actor->movedir =tdir; if ( P_TryWalk(actor) ) return; } } } else { for ( tdir=DI_SOUTHEAST; tdir != (DI_EAST-1); tdir-- ) { if (tdir != (int) turnaround) { actor->movedir = tdir; if ( P_TryWalk(actor) ) return; } } } if (turnaround != DI_NODIR) { actor->movedir =turnaround; if ( P_TryWalk(actor) ) return; } actor->movedir = DI_NODIR; // can not move } // // P_NewRandomDir // // villsa [STRIFE] new function // // haleyjd: Almost identical to the tail-end of P_NewChaseDir, this function // finds a purely random direction for an object to walk. Called from // A_RandomWalk. // // Shockingly similar to the RandomWalk pointer in Eternity :) // void P_NewRandomDir(mobj_t* actor) { int dir = 0; int omovedir = opposite[actor->movedir]; // haleyjd 20110223: nerfed this... // randomly determine direction of search if(P_Random() & 1) { // Try all non-reversal directions forward, first for(dir = 0; dir < DI_NODIR; dir++) { if(dir != omovedir) { actor->movedir = dir; if(P_Random() & 1) { if(P_TryWalk(actor)) break; } } } // haleyjd 20110223: logic missing entirely: // failed all non-reversal directions? try reversing if(dir > DI_SOUTHEAST) { if(omovedir == DI_NODIR) { actor->movedir = DI_NODIR; return; } actor->movedir = omovedir; if(P_TryWalk(actor)) return; else { actor->movedir = DI_NODIR; return; } } } else { // Try directions one at a time in backward order dir = DI_SOUTHEAST; while(1) { // haleyjd 09/05/10: missing random code. if(dir != omovedir) { actor->movedir = dir; // villsa 09/06/10: un-inlined code if(P_TryWalk(actor)) return; } // Ran out of non-reversal directions to try? Reverse. if(--dir == -1) { if(omovedir == DI_NODIR) { actor->movedir = DI_NODIR; return; } actor->movedir = omovedir; // villsa 09/06/10: un-inlined code if(P_TryWalk(actor)) return; else { actor->movedir = DI_NODIR; return; } } // end if(--dir == -1) } // end while(1) } // end else } // haleyjd 09/05/10: Needed below. extern void P_BulletSlope (mobj_t *mo); // // P_LookForPlayers // // If allaround is false, only look 180 degrees in front. // Returns true if a player is targeted. // // [STRIFE] // haleyjd 09/05/10: Modifications to support friendly units. // boolean P_LookForPlayers ( mobj_t* actor, boolean allaround ) { int c; int stop; player_t* player; angle_t an; fixed_t dist; mobj_t * master = players[actor->miscdata].mo; // haleyjd 09/05/10: handle Allies if(actor->flags & MF_ALLY) { // Deathmatch: support team behavior for Rebels. if(netgame) { // Rebels adopt the allied player's target if it is not of the same // allegiance. Other allies do it unconditionally. if(master && master->target && (master->target->type != MT_REBEL1 || master->target->miscdata != actor->miscdata)) { actor->target = master->target; } else { // haleyjd 09/06/10: Note that this sets actor->target in Strife! P_BulletSlope(actor); // Clear target if nothing is visible, or if the target is a // friendly Rebel or the allied player. if (linetarget == NULL || (actor->target->type == MT_REBEL1 && actor->target->miscdata == actor->miscdata) || actor->target == master) { actor->target = NULL; return false; } } } else { // Single-player: Adopt any non-allied player target. if(master && master->target && !(master->target->flags & MF_ALLY)) { actor->target = master->target; return true; } // haleyjd 09/06/10: Note that this sets actor->target in Strife! P_BulletSlope(actor); // Clear target if nothing is visible, or if the target is an ally. if(!linetarget || actor->target->flags & MF_ALLY) { actor->target = NULL; return false; } } return true; } c = 0; // NOTE: This behavior has been changed from the Vanilla behavior, where // an infinite loop can occur if players 0-3 all quit the game. Although // technically this is not what Vanilla does, fixing this is highly // desirable, and having the game simply lock up is not acceptable. // stop = (actor->lastlook - 1) & 3; // for (;; actor->lastlook = (actor->lastlook + 1) & 3) stop = (actor->lastlook + MAXPLAYERS - 1) % MAXPLAYERS; for ( ; ; actor->lastlook = (actor->lastlook + 1) % MAXPLAYERS) { if (!playeringame[actor->lastlook]) continue; if (c++ == 2 || actor->lastlook == stop) { // done looking return false; } player = &players[actor->lastlook]; if (player->health <= 0) continue; // dead if (!P_CheckSight (actor, player->mo)) continue; // out of sight if (!allaround) { an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; if (an > ANG90 && an < ANG270) { dist = P_AproxDistance (player->mo->x - actor->x, player->mo->y - actor->y); // if real close, react anyway if (dist > MELEERANGE) continue; // behind back } } actor->target = player->mo; return true; } return false; } // haleyjd 09/05/10: [STRIFE] Removed A_KeenDie // // ACTION ROUTINES // // // A_Look // Stay in state until a player is sighted. // // [STRIFE] // haleyjd 09/05/10: Adjusted for allies, Inquisitors, etc. // void A_Look (mobj_t* actor) { mobj_t* targ; actor->threshold = 0; // any shot will wake up targ = actor->subsector->sector->soundtarget; if (targ && (targ->flags & MF_SHOOTABLE) ) { // [STRIFE] Allies wander when they call this. if(actor->flags & MF_ALLY) A_RandomWalk(actor); else { actor->target = targ; if ( actor->flags & MF_AMBUSH ) { if (P_CheckSight (actor, actor->target)) goto seeyou; } else goto seeyou; } } // haleyjd 09/05/10: This is bizarre, as Rogue keeps using the GIVEQUEST flag // as a parameter to control allaround look behavior. Did they just run out of // flags, or what? // STRIFE-TODO: Needs serious verification. if (!P_LookForPlayers(actor, (actor->flags & MF_GIVEQUEST) != 0)) return; // go into chase state seeyou: if (actor->info->seesound) { int sound = actor->info->seesound; mobj_t * emitter = actor; // [STRIFE] Removed DOOM random sounds. // [STRIFE] Only Inquisitors roar loudly here. if (actor->type == MT_INQUISITOR) emitter = NULL; S_StartSound (emitter, sound); } // [STRIFE] Set threshold (kinda odd as it's still set to 0 above...) actor->threshold = 20; P_SetMobjState (actor, actor->info->seestate); } // // A_RandomWalk // // [STRIFE] New function. // haleyjd 09/05/10: Action routine used to meander about. // void A_RandomWalk(mobj_t* actor) { // Standing actors do not wander. if(actor->flags & MF_STAND) return; if(actor->reactiontime) actor->reactiontime--; // count down reaction time else { // turn to a new angle if(actor->movedir < DI_NODIR) { int delta; actor->angle &= (7 << 29); delta = actor->angle - (actor->movedir << 29); if(delta < 0) actor->angle += ANG90/2; else if(delta > 0) actor->angle -= ANG90/2; } // try moving if(--actor->movecount < 0 || !P_Move(actor)) { P_NewRandomDir(actor); actor->movecount += 5; } } } // // A_FriendLook // // [STRIFE] New function // haleyjd 09/05/10: Action function used mostly by mundane characters such as // peasants. // void A_FriendLook(mobj_t* actor) { mobj_t *soundtarget = actor->subsector->sector->soundtarget; actor->threshold = 0; if(soundtarget && soundtarget->flags & MF_SHOOTABLE) { // Handle allies, except on maps 3 and 34 (Front Base/Movement Base) if((actor->flags & MF_ALLY) == (soundtarget->flags & MF_ALLY) && gamemap != 3 && gamemap != 34) { // STRIFE-TODO: Needs serious verification. if(P_LookForPlayers(actor, (actor->flags & MF_GIVEQUEST) != 0)) { P_SetMobjState(actor, actor->info->seestate); actor->flags |= MF_NODIALOG; return; } } else { actor->target = soundtarget; if(!(actor->flags & MF_AMBUSH) || P_CheckSight(actor, actor->target)) { actor->threshold = 10; P_SetMobjState(actor, actor->info->seestate); return; } } } // do some idle animation if(P_Random() < 30) { int t = P_Random(); P_SetMobjState(actor, (t & 1) + actor->info->spawnstate + 1); } // wander around a bit if(!(actor->flags & MF_STAND) && P_Random() < 40) P_SetMobjState(actor, actor->info->spawnstate + 3); } // // A_Listen // // [STRIFE] New function // haleyjd 09/05/10: Action routine used to strictly listen for a target. // void A_Listen(mobj_t* actor) { mobj_t *soundtarget; actor->threshold = 0; soundtarget = actor->subsector->sector->soundtarget; if(soundtarget && (soundtarget->flags & MF_SHOOTABLE)) { if((actor->flags & MF_ALLY) != (soundtarget->flags & MF_ALLY)) { actor->target = soundtarget; if(!(actor->flags & MF_AMBUSH) || P_CheckSight(actor, actor->target)) { if(actor->info->seesound) S_StartSound(actor, actor->info->seesound); actor->threshold = 10; P_SetMobjState(actor, actor->info->seestate); } } } } // // A_Chase // Actor has a melee attack, // so it tries to close as fast as possible // // haleyjd 09/05/10: [STRIFE] Various minor changes // void A_Chase (mobj_t* actor) { int delta; if (actor->reactiontime) actor->reactiontime--; // modify target threshold if (actor->threshold) { // haleyjd 20110204 [STRIFE]: No health <= 0 check here! if (actor->target) actor->threshold--; else actor->threshold = 0; } // turn towards movement direction if not there yet if (actor->movedir < 8) { actor->angle &= (7<<29); delta = actor->angle - (actor->movedir << 29); if (delta > 0) actor->angle -= ANG90/2; else if (delta < 0) actor->angle += ANG90/2; } if (!actor->target || !(actor->target->flags&MF_SHOOTABLE)) { // look for a new target if (P_LookForPlayers(actor, true)) return; // got a new target P_SetMobjState (actor, actor->info->spawnstate); return; } // do not attack twice in a row if (actor->flags & MF_JUSTATTACKED) { actor->flags &= ~MF_JUSTATTACKED; // [STRIFE] Checks only against fastparm, not gameskill == 5 if (!fastparm) P_NewChaseDir (actor); return; } // check for melee attack if (actor->info->meleestate && P_CheckMeleeRange (actor)) { if (actor->info->attacksound) S_StartSound (actor, actor->info->attacksound); P_SetMobjState (actor, actor->info->meleestate); return; } // check for missile attack if (actor->info->missilestate) { // [STRIFE] Checks only fastparm. if (!fastparm && actor->movecount) { goto nomissile; } if (!P_CheckMissileRange (actor)) goto nomissile; P_SetMobjState (actor, actor->info->missilestate); // [STRIFE] Add NODIALOG flag to disable dialog actor->flags |= (MF_NODIALOG|MF_JUSTATTACKED); return; } // ? nomissile: // possibly choose another target if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) ) { if (P_LookForPlayers(actor, true)) return; // got a new target } // chase towards player if (--actor->movecount<0 || !P_Move (actor)) { P_NewChaseDir (actor); } // [STRIFE] Changes to active sound behavior: // * Significantly more frequent // * Acolytes have randomized wandering sounds // make active sound if (actor->info->activesound && P_Random () < 38) { if(actor->info->activesound >= sfx_agrac1 && actor->info->activesound <= sfx_agrac4) { S_StartSound(actor, sfx_agrac1 + P_Random() % 4); } else S_StartSound(actor, actor->info->activesound); } } // // A_FaceTarget // // [STRIFE] // haleyjd 09/05/10: Handling for visibility-modifying flags. // 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) { // [STRIFE] increased SHADOW inaccuracy by a power of 2 int t = P_Random(); actor->angle += (t - P_Random()) << 22; } else if(actor->target->flags & MF_MVIS) { // [STRIFE] MVIS gives even worse aiming! int t = P_Random(); actor->angle += (t - P_Random()) << 23; } } // // A_PeasantPunch // // [STRIFE] New function // haleyjd 09/05/10: Attack used by Peasants as a one-time retaliation // when the player or a monster injures them. Weak doesn't begin to // describe it :P // void A_PeasantPunch(mobj_t* actor) { if(!actor->target) return; A_FaceTarget(actor); if(P_CheckMeleeRange(actor)) P_DamageMobj(actor->target, actor, actor, 2 * (P_Random() % 5) + 2); } // // A_ReaverAttack // // [STRIFE] New function // haleyjd 09/06/10: Action routine used by Reavers to fire bullets. // Also apparently used by Inquistors, though they don't seem to use // it too often, as they're content to blow your face off with their // HE grenades instead. // void A_ReaverAttack(mobj_t* actor) { int i = 0; fixed_t slope; if(!actor->target) return; S_StartSound(actor, sfx_reavat); A_FaceTarget(actor); slope = P_AimLineAttack(actor, actor->angle, 2048*FRACUNIT); do { int t = P_Random(); angle_t shootangle = actor->angle + ((t - P_Random()) << 20); int damage = 3*((P_Random() & 7) + 1); P_LineAttack(actor, shootangle, 2048*FRACUNIT, slope, damage); ++i; } while(i < 3); } // // A_BulletAttack // // [STRIFE] New function // haleyjd 09/06/10: Action function for generic bullet attacks. Used by // a lot of different characters, including Acolytes, Rebels, and Macil. // void A_BulletAttack(mobj_t* actor) { int t, damage; fixed_t slope; angle_t shootangle; if(!actor->target) return; S_StartSound(actor, sfx_rifle); A_FaceTarget(actor); slope = P_AimLineAttack(actor, actor->angle, 2048*FRACUNIT); t = P_Random(); shootangle = ((t - P_Random()) << 19) + actor->angle; damage = 3 * (P_Random() % 5 + 1); P_LineAttack(actor, shootangle, 2048*FRACUNIT, slope, damage); } // // A_CheckTargetVisible // // [STRIFE] New function // haleyjd 09/06/10: Action routine which sets a thing back to its // seestate at random, or if it cannot see its target, or its target // is dead. Used by diverse actors. // void A_CheckTargetVisible(mobj_t* actor) { A_FaceTarget(actor); if(P_Random() >= 30) { mobj_t *target = actor->target; if(!target || target->health <= 0 || !P_CheckSight(actor, target) || P_Random() < 40) { P_SetMobjState(actor, actor->info->seestate); } } } // // A_SentinelAttack // // [STRIFE] New function // haleyjd 09/06/10: Action function implementing the Sentinel's laser attack // villsa 09/06/10 implemented // void A_SentinelAttack(mobj_t* actor) { mobj_t* mo; mobj_t* mo2; fixed_t x; fixed_t y; fixed_t z; angle_t an; int i; mo = P_SpawnFacingMissile(actor, actor->target, MT_L_LASER); an = actor->angle >> ANGLETOFINESHIFT; if(mo->momy | mo->momx) // villsa - fixed typo (yes, they actually used '|' instead of'||') { for(i = 8; i > 1; i--) { x = mo->x + FixedMul(mobjinfo[MT_L_LASER].radius * i, finecosine[an]); y = mo->y + FixedMul(mobjinfo[MT_L_LASER].radius * i, finesine[an]); z = mo->z + i * (mo->momz >> 2); mo2 = P_SpawnMobj(x, y, z, MT_R_LASER); mo2->target = actor; mo2->momx = mo->momx; mo2->momy = mo->momy; mo2->momz = mo->momz; P_CheckMissileSpawn(mo2); } } mo->z += mo->momz >> 2; } // // A_StalkerThink // // [STRIFE] New function // haleyjd 09/06/10: Action function to drive Stalker logic. // void A_StalkerThink(mobj_t* actor) { statenum_t statenum; if(actor->flags & MF_NOGRAVITY) { if(actor->ceilingz - actor->info->height <= actor->z) return; statenum = S_SPID_11; // 1020 } else statenum = S_SPID_18; // 1027 P_SetMobjState(actor, statenum); } // // A_StalkerSetLook // // [STRIFE] New function // haleyjd 09/06/10: Action function to marshall transitions to the // Stalker's spawnstate. // void A_StalkerSetLook(mobj_t* actor) { statenum_t statenum; if(!actor) // weird; totally unnecessary. return; if(actor->flags & MF_NOGRAVITY) { if(actor->state->nextstate == S_SPID_01) // 1010 return; statenum = S_SPID_01; // 1010 } else { if(actor->state->nextstate == S_SPID_02) // 1011 return; statenum = S_SPID_02; // 1011 } P_SetMobjState(actor, statenum); } // // A_StalkerDrop // // [STRIFE] New function // haleyjd 09/06/10: Dead simple: removes NOGRAVITY status. // void A_StalkerDrop(mobj_t* actor) { actor->flags &= ~MF_NOGRAVITY; } // // A_StalkerScratch // // [STRIFE] New function // haleyjd 09/06/10: Action function for Stalker's attack. // void A_StalkerScratch(mobj_t* actor) { if(actor->flags & MF_NOGRAVITY) { // Drop him down before he can attack P_SetMobjState(actor, S_SPID_11); // 1020 return; } if(!actor->target) return; A_FaceTarget(actor); if(P_CheckMeleeRange(actor)) P_DamageMobj(actor->target, actor, actor, 2 * (P_Random() % 8) + 2); } // // A_FloatWeave // // [STRIFE] New function // haleyjd 09/06/10: Action function which is responsible for floating // actors' constant upward and downward movement. Probably a really bad // idea in retrospect given how dodgy the 3D clipping implementation is. // void A_FloatWeave(mobj_t* actor) { fixed_t height; fixed_t z; if(actor->threshold) return; if(actor->flags & MF_INFLOAT) return; height = actor->info->height; // v2 z = actor->floorz + 96*FRACUNIT; // v1 if ( z > actor->ceilingz - height - 16*FRACUNIT ) z = actor->ceilingz - height - 16*FRACUNIT; if ( z >= actor->z ) actor->momz += FRACUNIT; else actor->momz -= FRACUNIT; if ( z == actor->z ) actor->threshold = 4; else actor->threshold = 8; } // // A_RobotMelee // // [STRIFE] New function // haleyjd 09/06/10: Action function for Reaver and Templar melee attacks. // void A_RobotMelee(mobj_t* actor) { if(!actor->target) return; A_FaceTarget(actor); if(P_CheckMeleeRange(actor)) { S_StartSound(actor, sfx_revbld); P_DamageMobj(actor->target, actor, actor, 3 * (P_Random() % 8 + 1)); } } // // A_TemplarMauler // // [STRIFE] New function // haleyjd 09/06/10: Exactly what it sounds like. Kicks your ass. // void A_TemplarMauler(mobj_t* actor) { int i, t; int angle; int bangle; int damage; int slope; if(!actor->target) return; S_StartSound(actor, sfx_pgrdat); A_FaceTarget(actor); bangle = actor->angle; slope = P_AimLineAttack(actor, bangle, 2048*FRACUNIT); for(i = 0; i < 10; i++) { // haleyjd 09/06/10: Very carefully preserved order of P_Random calls damage = (P_Random() & 4) * 2; t = P_Random(); angle = bangle + ((t - P_Random()) << 19); t = P_Random(); slope = ((t - P_Random()) << 5) + slope; P_LineAttack(actor, angle, 2112*FRACUNIT, slope, damage); } } // // A_CrusaderAttack // // villsa [STRIFE] new codepointer // 09/06/10: Action function for the Crusader's Flamethrower. // Very similar to the player's flamethrower, seeing how it was ripped // off a Crusader by the Rat People ;) // void A_CrusaderAttack(mobj_t* actor) { if(!actor->target) return; actor->z += (8*FRACUNIT); if(P_CheckRobotRange(actor)) { A_FaceTarget(actor); actor->angle -= (ANG90 / 8); P_SpawnFacingMissile(actor, actor->target, MT_C_FLAME); } else if(P_CheckMissileRange(actor)) { A_FaceTarget(actor); actor->z += (16*FRACUNIT); P_SpawnFacingMissile(actor, actor->target, MT_C_MISSILE); actor->angle -= (ANG45 / 32); actor->z -= (16*FRACUNIT); P_SpawnFacingMissile(actor, actor->target, MT_C_MISSILE); actor->angle += (ANG45 / 16); P_SpawnFacingMissile(actor, actor->target, MT_C_MISSILE); P_SetMobjState(actor, actor->info->seestate); actor->reactiontime += 15; } else P_SetMobjState(actor, actor->info->seestate); actor->z -= (8*FRACUNIT); } // // A_CrusaderLeft // // villsa [STRIFE] new codepointer // void A_CrusaderLeft(mobj_t* actor) { mobj_t* mo; actor->angle += (ANG90 / 16); mo = P_SpawnFacingMissile(actor, actor->target, MT_C_FLAME); mo->momz = FRACUNIT; mo->z += (16*FRACUNIT); } // // A_CrusaderRight // // villsa [STRIFE] new codepointer // void A_CrusaderRight(mobj_t* actor) { mobj_t* mo; actor->angle -= (ANG90 / 16); mo = P_SpawnFacingMissile(actor, actor->target, MT_C_FLAME); mo->momz = FRACUNIT; mo->z += (16*FRACUNIT); } // // A_CheckTargetVisible2 // // [STRIFE] New function // haleyjd 09/06/10: Mostly the same as CheckTargetVisible, except without // the randomness. // void A_CheckTargetVisible2(mobj_t* actor) { if(!actor->target || actor->target->health <= 0 || !P_CheckSight(actor, actor->target)) { P_SetMobjState(actor, actor->info->seestate); } } // // A_InqFlyCheck // // [STRIFE] New function // haleyjd 09/06/10: Action function to check if an Inquisitor wishes // to take to flight. // void A_InqFlyCheck(mobj_t* actor) { if(!actor->target) return; A_FaceTarget(actor); // if not in "robot" range, shoot grenades. if(!P_CheckRobotRange(actor)) P_SetMobjState(actor, S_ROB3_14); // 1061 if(actor->z != actor->target->z) { // Take off all zig! if(actor->z + actor->height + 54*FRACUNIT < actor->ceilingz) P_SetMobjState(actor, S_ROB3_17); // 1064 } } // // A_InqGrenade // // villsa [STRIFE] new codepointer // 09/06/10: Inquisitor grenade attack action routine. // void A_InqGrenade(mobj_t* actor) { mobj_t* mo; if(!actor->target) return; A_FaceTarget(actor); actor->z += MAXRADIUS; // grenade 1 actor->angle -= (ANG45 / 32); mo = P_SpawnFacingMissile(actor, actor->target, MT_INQGRENADE); mo->momz += (9*FRACUNIT); // grenade 2 actor->angle += (ANG45 / 16); mo = P_SpawnFacingMissile(actor, actor->target, MT_INQGRENADE); mo->momz += (16*FRACUNIT); actor->z -= MAXRADIUS; } // // A_InqTakeOff // // [STRIFE] New function // haleyjd 09/06/10: Makes an Inquisitor start flying. // void A_InqTakeOff(mobj_t* actor) { angle_t an; fixed_t speed = actor->info->speed * (2 * FRACUNIT / 3); fixed_t dist; if(!actor->target) return; S_StartSound(actor, sfx_inqjmp); actor->z += 64 * FRACUNIT; A_FaceTarget(actor); an = actor->angle >> ANGLETOFINESHIFT; actor->momx = FixedMul(finecosine[an], speed); actor->momy = FixedMul(finesine[an], speed); dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y); dist /= speed; if(dist < 1) dist = 1; actor->momz = (actor->target->z - actor->z) / dist; actor->reactiontime = 60; actor->flags |= MF_NOGRAVITY; } // // A_InqFly // // [STRIFE] New function // haleyjd 09/06/10: Handles an Inquisitor in flight. // void A_InqFly(mobj_t* actor) { if(!(leveltime & 7)) S_StartSound(actor, sfx_inqjmp); if(--actor->reactiontime < 0 || !actor->momx || !actor->momy || actor->z <= actor->floorz) { // Come in for a landing. P_SetMobjState(actor, actor->info->seestate); actor->reactiontime = 0; actor->flags &= ~MF_NOGRAVITY; } } // // A_FireSigilWeapon // // [STRIFE] New function // haleyjd 09/06/10: Action function for the Entity's attack. // void A_FireSigilWeapon(mobj_t* actor) { int choice = P_Random() % 5; // STRIFE-TODO: Needs verification. This switch is just weird. switch(choice) { case 0: A_ProgrammerAttack(actor); break; // ain't not seen no case 1, bub... case 2: A_FireSigilEOffshoot(actor); break; case 3: A_SpectreCAttack(actor); break; case 4: A_SpectreDAttack(actor); break; case 5: // BUG: never used? wtf were they thinking? A_SpectreEAttack(actor); break; default: break; } } // // A_ProgrammerAttack // // [STRIFE] New function // haleyjd 09/06/10: Action function for the Programmer's main // attack; equivalent to the player's first Sigil. // void A_ProgrammerAttack(mobj_t* actor) { mobj_t *mo; if(!actor->target) return; mo = P_SpawnMobj(actor->target->x, actor->target->y, ONFLOORZ, MT_SIGIL_A_GROUND); mo->threshold = 25; mo->target = actor; mo->health = -2; mo->tracer = actor->target; } // // A_Sigil_A_Action // // [STRIFE] New function // haleyjd 09/06/10: Called by MT_SIGIL_A_GROUND to zot anyone nearby with // corny looking lightning bolts. // void A_Sigil_A_Action(mobj_t* actor) { int t, x, y, type; mobj_t *mo; if(actor->threshold) actor->threshold--; t = P_Random(); actor->momx += ((t & 3) - (P_Random() & 3)) << FRACBITS; t = P_Random(); actor->momy += ((t & 3) - (P_Random() & 3)) << FRACBITS; t = P_Random(); x = 50*FRACUNIT * ((t & 3) - (P_Random() & 3)) + actor->x; t = P_Random(); y = 50*FRACUNIT * ((t & 3) - (P_Random() & 3)) + actor->y; if(actor->threshold <= 25) type = MT_SIGIL_A_ZAP_LEFT; else type = MT_SIGIL_A_ZAP_RIGHT; mo = P_SpawnMobj(x, y, ONCEILINGZ, type); mo->momz = -18 * FRACUNIT; mo->target = actor->target; mo->health = actor->health; mo = P_SpawnMobj(actor->x, actor->y, ONCEILINGZ, MT_SIGIL_A_ZAP_RIGHT); mo->momz = -18 * FRACUNIT; mo->target = actor->target; mo->health = actor->health; } // // A_SpectreEAttack // // [STRIFE] New function // haleyjd 09/06/10: Action function for the Loremaster's Spectre. // Equivalent to the player's final Sigil attack. // void A_SpectreEAttack(mobj_t* actor) { mobj_t *mo; if(!actor->target) return; mo = P_SpawnMissile(actor, actor->target, MT_SIGIL_SE_SHOT); mo->health = -2; } // // A_SpectreCAttack // // villsa [STRIFE] new codepointer // 09/06/10: Action routine for the Oracle's Spectre. Equivalent to the player's // third Sigil attack. // void A_SpectreCAttack(mobj_t* actor) { mobj_t* mo; int i; if(!actor->target) return; mo = P_SpawnMobj(actor->x, actor->y, actor->z + (32*FRACUNIT), MT_SIGIL_A_ZAP_RIGHT); mo->momz = -(18*FRACUNIT); mo->target = actor; mo->health = -2; mo->tracer = actor->target; actor->angle -= ANG90; for(i = 0; i < 20; i++) { actor->angle += (ANG90 / 10); mo = P_SpawnMortar(actor, MT_SIGIL_C_SHOT); mo->health = -2; mo->z = actor->z + (32*FRACUNIT); } actor->angle -= ANG90; } // // A_AlertSpectreC // // [STRIFE] New function // haleyjd 09/06/10: Action function called by the Oracle when it is // killed. Finds an MT_SPECTRE_C anywhere on the map and awakens it. // void A_AlertSpectreC(mobj_t* actor) { thinker_t *th; for(th = thinkercap.next; th != &thinkercap; th = th->next) { if(th->function.acp1 == (actionf_p1)P_MobjThinker) { mobj_t *mo = (mobj_t *)th; if(mo->type == MT_SPECTRE_C) { P_SetMobjState(mo, mo->info->seestate); mo->target = actor->target; return; } } } } // // A_Sigil_E_Action // // villsa [STRIFE] new codepointer // 09/06/10: Action routine for Sigil "E" shots. Spawns the room-filling // lightning bolts that seem to often do almost nothing. // void A_Sigil_E_Action(mobj_t* actor) { actor->angle += ANG90; P_SpawnMortar(actor, MT_SIGIL_E_OFFSHOOT); actor->angle -= ANG180; P_SpawnMortar(actor, MT_SIGIL_E_OFFSHOOT); actor->angle += ANG90; P_SpawnMortar(actor, MT_SIGIL_E_OFFSHOOT); } // // A_SigilTrail // // villsa [STRIFE] new codepointer // void A_SigilTrail(mobj_t* actor) { mobj_t* mo; mo = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, actor->z, MT_SIGIL_TRAIL); mo->angle = actor->angle; mo->health = actor->health; } // // A_SpectreDAttack // // [STRIFE] New function // haleyjd 09/06/10: Action function for Macil's Spectre. // Equivalent of the player's fourth Sigil attack. // void A_SpectreDAttack(mobj_t* actor) { mobj_t *mo; if(!actor->target) return; mo = P_SpawnMissile(actor, actor->target, MT_SIGIL_SD_SHOT); mo->health = -2; mo->tracer = actor->target; } // // A_FireSigilEOffshoot // // [STRIFE] New function // haleyjd 09/06/10: Action function to fire part of a Sigil E // attack. Used at least by the Entity. // void A_FireSigilEOffshoot(mobj_t* actor) { mobj_t *mo; if(!actor->target) return; mo = P_SpawnMissile(actor, actor->target, MT_SIGIL_E_OFFSHOOT); mo->health = -2; } // // A_ShadowOff // // villsa [STRIFE] new codepointer // 09/06/10: Disables SHADOW and MVIS flags. // void A_ShadowOff(mobj_t* actor) { actor->flags &= ~(MF_SHADOW|MF_MVIS); } // // A_ModifyVisibility // // villsa [STRIFE] new codepointer // 09/06/10: Turns on SHADOW, and turns off MVIS. // void A_ModifyVisibility(mobj_t* actor) { actor->flags |= MF_SHADOW; actor->flags &= ~MF_MVIS; } // // A_ShadowOn // // villsa [STRIFE] new codepointer // 09/06/10: Turns on SHADOW and MVIS. // void A_ShadowOn(mobj_t* actor) { actor->flags |= (MF_SHADOW|MF_MVIS); } // // A_SetTLOptions // // villsa [STRIFE] new codepointer // 09/06/10: Sets SHADOW and/or MVIS based on the thing's spawnpoint options. // void A_SetTLOptions(mobj_t* actor) { if(actor->spawnpoint.options & MTF_TRANSLUCENT) actor->flags |= MF_SHADOW; if(actor->spawnpoint.options & MTF_MVIS) actor->flags |= MF_MVIS; } // // A_BossMeleeAtk // // villsa [STRIFE] new codepointer // 09/06/10: Gratuitous melee attack used by multiple boss characters, // just for the sake of having one. It's not like anybody in their right // mind would get close to any of the maniacs that use this ;) // void A_BossMeleeAtk(mobj_t* actor) { if(!actor->target) return; P_DamageMobj(actor->target, actor, actor, 10 * (P_Random() & 9)); } // // A_BishopAttack // // villsa [STRIFE] new codepointer // 09/06/10: Bishop's homing missile attack. // void A_BishopAttack(mobj_t* actor) { mobj_t* mo; if(!actor->target) return; actor->z += MAXRADIUS; mo = P_SpawnMissile(actor, actor->target, MT_SEEKMISSILE); mo->tracer = actor->target; actor->z -= MAXRADIUS; } // // A_FireHookShot // // villsa [STRIFE] new codepointer // 09/06/10: Action function for the Loremaster's hookshot attack. // void A_FireHookShot(mobj_t* actor) { if(!actor->target) return; P_SpawnMissile(actor, actor->target, MT_HOOKSHOT); } // // A_FireChainShot // // villsa [STRIFE] new codepointer // 09/06/10: Action function for the hookshot projectile. Spawns echoes // to create a chain-like appearance. // void A_FireChainShot(mobj_t* actor) { S_StartSound(actor, sfx_tend); P_SpawnMobj(actor->x, actor->y, actor->z, MT_CHAINSHOT); // haleyjd: fixed type P_SpawnMobj(actor->x - (actor->momx >> 1), actor->y - (actor->momy >> 1), actor->z, MT_CHAINSHOT); P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, actor->z, MT_CHAINSHOT); } // // A_MissileSmoke // // villsa [STRIFE] new codepointer // void A_MissileSmoke(mobj_t* actor) { mobj_t* mo; S_StartSound(actor, sfx_rflite); P_SpawnPuff(actor->x, actor->y, actor->z); mo = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momy, actor->z, MT_MISSILESMOKE); mo->momz = FRACUNIT; } // // A_SpawnSparkPuff // // villsa [STRIFE] new codepointer // void A_SpawnSparkPuff(mobj_t* actor) { int r; mobj_t* mo; fixed_t x; fixed_t y; r = P_Random(); x = (10*FRACUNIT) * ((r & 3) - (P_Random() & 3)) + actor->x; r = P_Random(); y = (10*FRACUNIT) * ((r & 3) - (P_Random() & 3)) + actor->y; mo = P_SpawnMobj(x, y, actor->z, MT_SPARKPUFF); P_SetMobjState(mo, S_BNG4_01); // 199 mo->momz = FRACUNIT; } // haleyjd 09/05/10: [STRIFE] Removed: // A_PosAttack, A_SPosAttack, A_CPosAttack, A_CPosRefire, A_SpidRefire, // A_BspiAttack, A_TroopAttack, A_SargAttack, A_HeadAttack, A_CyberAttack, // A_BruisAttack, A_SkelMissile int TRACEANGLE = 0xE000000; // villsa [STRIFE] changed from 0xC000000 to 0xE000000 // // A_Tracer // void A_Tracer (mobj_t* actor) { angle_t exact; fixed_t dist; fixed_t slope; mobj_t* dest; //mobj_t* th; // villsa [STRIFE] removed all randomization and puff code // 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) { // villsa [STRIFE] slightly different algorithm 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; } // // A_ProgrammerMelee // // villsa [STRIFE] new codepointer // 09/08/10: Melee attack for the Programmer. // haleyjd - fixed damage formula // void A_ProgrammerMelee(mobj_t* actor) { if(!actor->target) return; A_FaceTarget(actor); if(P_CheckMeleeRange(actor)) { int damage = 6 * (P_Random() % 10 + 1); S_StartSound(actor, sfx_mtalht); P_DamageMobj(actor->target, actor, actor, damage); } } // haleyjd 09/05/10: [STRIFE] Removed: // A_SkelWhoosh, A_SkelFist, PIT_VileCheck, A_VileChase, A_VileStart, // A_StartFire, A_FireCrackle, A_Fire, A_VileTarget, A_VileAttack // A_FatRaise, A_FatAttack1, A_FatAttack2, A_FatAttack3, A_SkullAttack, // A_PainShootSkull, A_PainAttack, A_PainDie // // A_Scream // // villsa [STRIFE] // * Has no random death sounds, so play deathsound directly // * Full-volume roars for the Entity and Inquisitor. // void A_Scream(mobj_t* actor) { if(!actor->info->deathsound) return; // Check for bosses. if(actor->type == MT_ENTITY || actor->type == MT_INQUISITOR) S_StartSound(NULL, actor->info->deathsound); // full volume else S_StartSound(actor, actor->info->deathsound); } // // A_XScream // // villsa [STRIFE] // * Robots will play deathsound while non-robots play the slop sfx // void A_XScream(mobj_t* actor) { int sound; if(actor->flags & MF_NOBLOOD && actor->info->deathsound) sound = actor->info->deathsound; else sound = sfx_slop; S_StartSound(actor, sound); } // // A_Pain // // villsa [STRIFE] // * Play random peasant sounds; otherwise play painsound directly // void A_Pain(mobj_t* actor) { int sound = actor->info->painsound; if(sound) { if(sound >= sfx_pespna && sound <= sfx_pespnd) sound = sfx_pespna + (P_Random() % 4); S_StartSound(actor, sound); } } // // A_PeasantCrash // // villsa [STRIFE] new codepointer // 09/08/10: Called from Peasant's "crash" state (not to be confused with // Heretic crash states), which is invoked when the Peasant has taken // critical but sub-fatal damage. It will "bleed out" the rest of its // health by calling this function repeatedly. // void A_PeasantCrash(mobj_t* actor) { // Set NODIALOG, because you probably wouldn't feel like talking either // if somebody just stabbed you in the gut with a punch dagger... actor->flags |= MF_NODIALOG; if(!(P_Random() % 5)) { A_Pain(actor); // inlined in asm actor->health--; } if(actor->health <= 0) P_KillMobj(actor->target, actor); } // // A_Fall // // [STRIFE] // * Set NODIALOG, and clear NOGRAVITY and SHADOW // void A_Fall (mobj_t *actor) { // villsa [STRIFE] set NODIALOG flag to stop dialog actor->flags |= MF_NODIALOG; // actor is on ground, it can be walked over // villsa [STRIFE] remove nogravity/shadow flags as well actor->flags &= ~(MF_SOLID|MF_NOGRAVITY|MF_SHADOW); } // // A_HideZombie // // villsa [STRIFE] new codepointer // Used by the "Becoming" Acolytes on the Loremaster's level. // void A_HideZombie(mobj_t* actor) { line_t junk; junk.tag = 999; EV_DoDoor(&junk, vld_blazeClose); if(actor->target && actor->target->player) P_NoiseAlert(actor->target, actor); // inlined in asm } // // A_MerchantPain // // villsa [STRIFE] new codepointer // 09/08/10: Pain pointer for merchant characters. They close up shop for // a while and set off the alarm. // void A_MerchantPain(mobj_t* actor) { line_t junk; junk.tag = 999; EV_DoDoor(&junk, vld_shopClose); if(actor->target && actor->target->player) P_NoiseAlert(actor->target, actor); // inlined in asm } // haleyjd 09/05/10: Removed unused CheckBossEnd Choco routine. // haleyjd 09/05/10: [STRIFE] Removed: // A_Hoof, A_Metal, A_BabyMetal, A_OpenShotgun2, A_LoadShotgun2, // A_CloseShotgun2, A_BrainAwake, A_BrainPain, A_BrainScream, A_BrainExplode, // A_BrainDie, A_BrainSpit, A_SpawnSound, A_SpawnFly // // A_ProgrammerDie // // villsa [STRIFE] new codepointer // 09/08/10: Action routine for the Programmer's grisly death. Spawns the // separate mechanical base object and sends it flying off in some random // direction. // void A_ProgrammerDie(mobj_t* actor) { int r; angle_t an; mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 24*FRACUNIT, MT_PROGRAMMERBASE); // haleyjd 20110223: fix add w/ANG180 r = P_Random(); an = ((r - P_Random()) << 22) + actor->angle + ANG180; mo->angle = an; P_ThrustMobj(mo, an, mo->info->speed); // inlined in asm mo->momz = P_Random() << 9; } // // A_InqTossArm // // villsa [STRIFE] new codepointer // 09/08/10: Inquisitor death action. Spawns an arm and tosses it. // void A_InqTossArm(mobj_t* actor) { int r; angle_t an; mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + (24*FRACUNIT), MT_INQARM); r = P_Random(); an = ((r - P_Random()) << 22) + actor->angle - ANG90; mo->angle = an; P_ThrustMobj(mo, an, mo->info->speed); // inlined in asm mo->momz = P_Random() << 10; } // // A_SpawnSpectreA // // villsa [STRIFE] new codepointer (unused) // 09/08/10: Spawns Spectre A. Or would, if anything actually used this. // This is evidence that the Programmer's spectre, which appears in the // Catacombs in the final version, was originally meant to be spawned // after his death. // void A_SpawnSpectreA(mobj_t* actor) { mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPECTRE_A); mo->momz = P_Random() << 9; } // // A_SpawnSpectreB // // villsa [STRIFE] new codepointer // 09/08/10: Action function to spawn the Bishop's spectre. // void A_SpawnSpectreB(mobj_t* actor) { mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPECTRE_B); mo->momz = P_Random() << 9; } // // A_SpawnSpectreC // // villsa [STRIFE] new codepointer (unused) // 09/08/10: Action function to spawn the Oracle's spectre. Also // unused, because the Oracle's spectre is already present on the // map and is awakened on his death. Also left over from the // unreleased beta (and demo) versions. // void A_SpawnSpectreC(mobj_t* actor) { mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPECTRE_C); mo->momz = P_Random() << 9; } // // A_SpawnSpectreD // // villsa [STRIFE] new codepointer // 09/08/10: Action function to spawn Macil's Spectre. // void A_SpawnSpectreD(mobj_t* actor) { mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPECTRE_D); mo->momz = P_Random() << 9; } // // A_SpawnSpectreE // // villsa [STRIFE] new codepointer // 09/08/10: Action function to spawn the Loremaster's Spectre. // void A_SpawnSpectreE(mobj_t* actor) { mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPECTRE_E); mo->momz = P_Random() << 9; } // [STRIFE] New statics - Remember the Entity's spawning position. static fixed_t entity_pos_x = 0; static fixed_t entity_pos_y = 0; static fixed_t entity_pos_z = 0; // // A_SpawnEntity // // villsa [STRIFE] new codepointer // 09/08/10: You will fall on your knees before the True God, the One Light. // void A_SpawnEntity(mobj_t* actor) { mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 70*FRACUNIT, MT_ENTITY); mo->momz = 5*FRACUNIT; entity_pos_x = mo->x; entity_pos_y = mo->y; entity_pos_z = mo->z; } // // P_ThrustMobj // // villsa [STRIFE] new function // Thrusts an thing in a specified force/direction // Beware! This is inlined everywhere in the asm // void P_ThrustMobj(mobj_t *actor, angle_t angle, fixed_t force) { angle_t an = angle >> ANGLETOFINESHIFT; actor->momx += FixedMul(finecosine[an], force); actor->momy += FixedMul(finesine[an], force); } // // A_EntityDeath // // [STRIFE] // haleyjd 09/08/10: The death of the Entity's spectre brings forth // three subentities, which are significantly less dangerous on their // own but threatening together. // void A_EntityDeath(mobj_t* actor) { mobj_t *subentity; angle_t an; fixed_t dist; dist = 2 * mobjinfo[MT_SUBENTITY].radius; // Subentity One an = actor->angle >> ANGLETOFINESHIFT; subentity = P_SpawnMobj(FixedMul(finecosine[an], dist) + entity_pos_x, FixedMul(finesine[an], dist) + entity_pos_y, entity_pos_z, MT_SUBENTITY); subentity->target = actor->target; A_FaceTarget(subentity); P_ThrustMobj(subentity, subentity->angle, 625 << 13); // Subentity Two an = (actor->angle + ANG90) >> ANGLETOFINESHIFT; subentity = P_SpawnMobj(FixedMul(finecosine[an], dist) + entity_pos_x, FixedMul(finesine[an], dist) + entity_pos_y, entity_pos_z, MT_SUBENTITY); subentity->target = actor->target; P_ThrustMobj(subentity, actor->angle + ANG90, 4); A_FaceTarget(subentity); // Subentity Three an = (actor->angle - ANG90) >> ANGLETOFINESHIFT; subentity = P_SpawnMobj(FixedMul(finecosine[an], dist) + entity_pos_x, FixedMul(finesine[an], dist) + entity_pos_y, entity_pos_z, MT_SUBENTITY); subentity->target = actor->target; P_ThrustMobj(subentity, actor->angle - ANG90, 4); A_FaceTarget(subentity); } // // A_SpawnZombie // // villsa [STRIFE] new codepointer // void A_SpawnZombie(mobj_t* actor) { P_SpawnMobj(actor->x, actor->y, actor->z, MT_ZOMBIE); } // // A_ZombieInSpecialSector // // villsa [STRIFE] new codepointer // void A_ZombieInSpecialSector(mobj_t* actor) { sector_t* sector; fixed_t force; angle_t angle; int tagval; sector = actor->subsector->sector; if(actor->z != sector->floorheight) return; if(sector->special <= 15) P_DamageMobj(actor, NULL, NULL, 999); else if(sector->special == 18) { tagval = sector->tag - 100; force = (tagval % 10) << 12; angle = (tagval / 10) << 29; P_ThrustMobj(actor, angle, force); // inlined in asm } } // // A_CrystalExplode // // villsa [STRIFE] new codepointer // Throws out debris from the Power Crystal and sets its sector floorheight // to the lowest surrounding floor (this is maybe the only time a direct // level-changing action is done by an object in this fashion in any of // the DOOM engine games... they usually call a line special instead) // void A_CrystalExplode(mobj_t* actor) { sector_t* sector; mobj_t* rubble; int i; int r; sector = actor->subsector->sector; sector->lightlevel = 0; sector->floorheight = P_FindLowestFloorSurrounding(sector); // spawn rubble for(i = 0; i < 8; i++) { rubble = P_SpawnMobj(actor->x, actor->y, actor->z, MT_RUBBLE1 + i); r = P_Random(); rubble->momx = ((r & 0x0f) - (P_Random() & 7)) << FRACBITS; r = P_Random(); rubble->momy = ((r & 7) - (P_Random() & 7)) << FRACBITS; rubble->momz = ((P_Random() & 3) << FRACBITS) + (7*FRACUNIT); } } // [STRIFE] New static global - buffer used for various player messages. static char pmsgbuffer[80]; // // P_FreePrisoners // // haleyjd 09/08/10: [STRIFE] New function // * Called when the prisoners get freed, obviously. Gives a // message and awards quest token 13. // void P_FreePrisoners(void) { int i; DEH_snprintf(pmsgbuffer, sizeof(pmsgbuffer), "You've freed the prisoners!"); for(i = 0; i < MAXPLAYERS; i++) { P_GiveItemToPlayer(&players[i], SPR_TOKN, MT_TOKEN_QUEST13); players[i].message = pmsgbuffer; } } // // P_DestroyConverter // // haleyjd 09/08/10: [STRIFE] New function // * Called when the converter is shut down in the factory. // Gives several items and a message. // void P_DestroyConverter(void) { int i; DEH_snprintf(pmsgbuffer, sizeof(pmsgbuffer), "You've destroyed the Converter!"); for(i = 0; i < MAXPLAYERS; i++) { P_GiveItemToPlayer(&players[i], SPR_TOKN, MT_TOKEN_QUEST25); P_GiveItemToPlayer(&players[i], SPR_TOKN, MT_TOKEN_STAMINA); P_GiveItemToPlayer(&players[i], SPR_TOKN, MT_TOKEN_NEW_ACCURACY); players[i].message = pmsgbuffer; } } // // A_QuestMsg // // villsa [STRIFE] new codepointer // Displays text based on quest item's name // Quest item is based on actor's speed // void A_QuestMsg(mobj_t* actor) { const char *name; int quest; int i; // get name name = DEH_String(mobjinfo[(MT_TOKEN_QUEST1 - 1) + actor->info->speed].name); M_StringCopy(pmsgbuffer, name, sizeof(pmsgbuffer)); // inlined in asm // give quest and display message to players for(i = 0; i < MAXPLAYERS; i++) { quest = 1 << (actor->info->speed - 1); players[i].message = pmsgbuffer; players[i].questflags |= quest; } } // // A_ExtraLightOff // // villsa [STRIFE] new codepointer // 09/08/10: Called by the Power Crystal to turn off the extended // flash of light caused by its explosion. // void A_ExtraLightOff(mobj_t* actor) { if(!actor->target) return; if(!actor->target->player) return; actor->target->player->extralight = 0; } // // A_CrystalRadiusAtk // // villsa [STRIFE] new codepointer // 09/08/10: Called by the power crystal when it dies. // void A_CrystalRadiusAtk(mobj_t* actor) { P_RadiusAttack(actor, actor->target, 512); if(!(actor->target && actor->target->player)) return; // set extralight to 5 for near full-bright actor->target->player->extralight = 5; } // // A_DeathExplode5 // // villsa [STRIFE] new codepointer // void A_DeathExplode5(mobj_t* actor) { P_RadiusAttack(actor, actor->target, 192); if(actor->target && actor->target->player) P_NoiseAlert(actor->target, actor); // inlined in asm } // // A_DeathExplode1 // // villsa [STRIFE] new codepointer // void A_DeathExplode1(mobj_t* actor) { P_RadiusAttack(actor, actor->target, 128); if(actor->target && actor->target->player) P_NoiseAlert(actor->target, actor); // inlined in asm } // // A_DeathExplode2 // // villsa [STRIFE] new codepointer // void A_DeathExplode2(mobj_t* actor) { P_RadiusAttack(actor, actor->target, 64); if(actor->target && actor->target->player) P_NoiseAlert(actor->target, actor); // inlined in asm } // // A_DeathExplode3 // // villsa [STRIFE] new codepointer // void A_DeathExplode3(mobj_t* actor) { P_RadiusAttack(actor, actor->target, 32); if(actor->target && actor->target->player) P_NoiseAlert(actor->target, actor); // inlined in asm } // // A_RaiseAlarm // // villsa [STRIFE] new codepointer // 09/08/10: Set off the infamous alarm. This is just a noise alert. // void A_RaiseAlarm(mobj_t* actor) { if(actor->target && actor->target->player) P_NoiseAlert(actor->target, actor); // inlined in asm } // // A_MissileTick // villsa [STRIFE] - new codepointer // void A_MissileTick(mobj_t* actor) { if(--actor->reactiontime <= 0) { P_ExplodeMissile(actor); actor->flags &= ~MF_MISSILE; } } // // A_SpawnGrenadeFire // villsa [STRIFE] - new codepointer // void A_SpawnGrenadeFire(mobj_t* actor) { P_SpawnMobj(actor->x, actor->y, actor->z, MT_PFLAME); } // // A_NodeChunk // // villsa [STRIFE] - new codepointer // Throw out "nodes" from a spectral entity // void A_NodeChunk(mobj_t* actor) { int r; mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 10*FRACUNIT, MT_NODE); r = P_Random(); mo->momx = ((r & 0x0f) - (P_Random() & 7)) << FRACBITS; r = P_Random(); mo->momy = ((r & 7) - (P_Random() & 0x0f)) << FRACBITS; mo->momz = (P_Random() & 0x0f) << FRACBITS; } // // A_HeadChunk // // villsa [STRIFE] - new codepointer // Throw out the little "eye"-like object from a spectral entity when it dies. // void A_HeadChunk(mobj_t* actor) { int r; mobj_t* mo; mo = P_SpawnMobj(actor->x, actor->y, actor->z + 10*FRACUNIT, MT_SPECTREHEAD); r = P_Random(); mo->momx = ((r & 7) - (P_Random() & 0x0f)) << FRACBITS; r = P_Random(); mo->momy = ((r & 0x0f) - (P_Random() & 7)) << FRACBITS; mo->momz = (P_Random() & 7) << FRACBITS; } // // A_BurnSpread // villsa [STRIFE] - new codepointer // void A_BurnSpread(mobj_t* actor) { int t; mobj_t* mo; fixed_t x; fixed_t y; actor->momz -= (8*FRACUNIT); t = P_Random(); actor->momx += ((t & 3) - (P_Random() & 3)) << FRACBITS; t = P_Random(); actor->momy += ((t & 3) - (P_Random() & 3)) << FRACBITS; S_StartSound(actor, sfx_lgfire); if(actor->flags & MF_DROPPED) return; // not the parent // haleyjd 20110223: match order of calls in binary y = actor->y + (((P_Random() + 12) & 31) << FRACBITS); x = actor->x + (((P_Random() + 12) & 31) << FRACBITS); // spawn child mo = P_SpawnMobj(x, y, actor->z + (4*FRACUNIT), MT_PFLAME); t = P_Random(); mo->momx += ((t & 7) - (P_Random() & 7)) << FRACBITS; t = P_Random(); mo->momy += ((t & 7) - (P_Random() & 7)) << FRACBITS; mo->momz -= FRACUNIT; mo->flags |= MF_DROPPED; mo->reactiontime = (P_Random() & 3) + 2; } // // A_BossDeath // // Possibly trigger special effects // if on first boss level // // haleyjd 09/17/10: [STRIFE] // * Modified to handle all Strife bosses. // void A_BossDeath (mobj_t* actor) { int i; thinker_t *th; line_t junk; // only the following types can be a boss: switch(actor->type) { case MT_CRUSADER: case MT_SPECTRE_A: case MT_SPECTRE_B: case MT_SPECTRE_C: case MT_SPECTRE_D: case MT_SPECTRE_E: case MT_SUBENTITY: case MT_PROGRAMMER: break; default: return; } // check for a living player for(i = 0; i < MAXPLAYERS; i++) { if(playeringame[i] && players[i].health > 0) break; } if(i == MAXPLAYERS) return; // everybody's dead. // check for a still living boss for(th = thinkercap.next; th != &thinkercap; th = th->next) { if(th->function.acp1 == (actionf_p1) P_MobjThinker) { mobj_t *mo = (mobj_t *)th; if(mo != actor && mo->type == actor->type && mo->health > 0) return; // one is still alive. } } // Victory! switch(actor->type) { case MT_CRUSADER: junk.tag = 667; EV_DoFloor(&junk, lowerFloorToLowest); break; case MT_SPECTRE_A: GiveVoiceObjective("VOC95", "LOG95", 0); junk.tag = 999; EV_DoFloor(&junk, lowerFloorToLowest); break; case MT_SPECTRE_B: P_GiveItemToPlayer(&players[0], SPR_TOKN, MT_TOKEN_BISHOP); GiveVoiceObjective("VOC74", "LOG74", 0); break; case MT_SPECTRE_C: // Look for an MT_ORACLE - this is for in case the player awakened the // Oracle's spectre without killing the Oracle, which is possible by // looking up to max and firing the Sigil at it. If this were not done, // a serious sequence break possibility would arise where one could // kill both the Oracle AND Macil, possibly throwing the game out of // sorts entirely. Too bad they thought of it ;) However this also // causes a bug sometimes! The Oracle, in its death state, sets the // Spectre C back to its seestate. If the Spectre C is already dead, // it becomes an undead ghost monster. Then it's a REAL spectre ;) for(th = thinkercap.next; th != &thinkercap; th = th->next) { if(th->function.acp1 == (actionf_p1) P_MobjThinker) { mobj_t *mo = (mobj_t *)th; // KILL ALL ORACLES! RAWWR! if(mo != actor && mo->type == MT_ORACLE && mo->health > 0) P_KillMobj(actor, mo); } } P_GiveItemToPlayer(&players[0], SPR_TOKN, MT_TOKEN_ORACLE); // Bishop is dead? - verify. if(players[0].questflags & QF_QUEST21) P_GiveItemToPlayer(&players[0], SPR_TOKN, MT_TOKEN_QUEST22); // Macil is dead? if(players[0].questflags & QF_QUEST24) { // Loremaster is dead? if(players[0].questflags & QF_QUEST26) { // We wield the complete sigil, blahblah GiveVoiceObjective("VOC85", "LOG85", 0); } } else { // So much for prognostication! GiveVoiceObjective("VOC87", "LOG87", 0); } junk.tag = 222; // Open the exit door again; EV_DoDoor(&junk, vld_open); // Note this is NOT the Loremaster door... break; case MT_SPECTRE_D: P_GiveItemToPlayer(&players[0], SPR_TOKN, MT_TOKEN_MACIL); if(players[0].questflags & QF_QUEST25) // Destroyed converter? GiveVoiceObjective("VOC106", "LOG106", 0); else GiveVoiceObjective("VOC79", "LOG79", 0); break; case MT_SPECTRE_E: P_GiveItemToPlayer(&players[0], SPR_TOKN, MT_TOKEN_LOREMASTER); if(!netgame) { P_GiveItemToPlayer(&players[0], SPR_TOKN, MT_TOKEN_STAMINA); P_GiveItemToPlayer(&players[0], SPR_TOKN, MT_TOKEN_NEW_ACCURACY); } if(players[0].sigiltype == 4) GiveVoiceObjective("VOC85", "LOG85", 0); else GiveVoiceObjective("VOC83", "LOG83", 0); junk.tag = 666; EV_DoFloor(&junk, lowerFloorToLowest); break; case MT_SUBENTITY: F_StartFinale(); break; case MT_PROGRAMMER: F_StartFinale(); G_ExitLevel(0); break; default: // Real classy, Rogue. if(actor->type) I_Error("Error: Unconnected BossDeath id %d", actor->type); break; } } // // A_AcolyteSpecial // // villsa [STRIFE] - new codepointer // Awards quest #7 when all the Blue Acolytes are killed in Tarnhill // void A_AcolyteSpecial(mobj_t* actor) { int i; thinker_t* th; if(actor->type != MT_GUARD8) return; // must be MT_GUARD8 for(i = 0; i < MAXPLAYERS; i++) { if(playeringame[i] && players[i].health > 0) break; } if(i == 8) return; for(th = thinkercap.next; th != &thinkercap; th = th->next) { if(th->function.acp1 == (actionf_p1) P_MobjThinker) { mobj_t *mo = (mobj_t *)th; // Found a living MT_GUARD8? if(mo != actor && mo->type == actor->type && mo->health > 0) return; } } // All MT_GUARD8 are dead, give quest token #7 to all players for(i = 0; i < MAXPLAYERS; i++) P_GiveItemToPlayer(&players[i], SPR_TOKN, MT_TOKEN_QUEST7); // play voice, give objective GiveVoiceObjective("VOC14", "LOG14", 0); } // // A_InqChase // villsa [STRIFE] - new codepointer // void A_InqChase(mobj_t* actor) { S_StartSound(actor, sfx_inqact); A_Chase(actor); } // // A_StalkerChase // villsa [STRIFE] - new codepointer // void A_StalkerChase(mobj_t* actor) { S_StartSound(actor, sfx_spdwlk); A_Chase(actor); } // // A_PlayerScream // // [STRIFE] // * Modified to eliminate gamemode check and to use Strife sound. // void A_PlayerScream (mobj_t* mo) { // Default death sound. int sound = sfx_pldeth; // villsa [STRIFE] don't check for gamemode if(mo->health < -50) { // IF THE PLAYER DIES // LESS THAN -50% WITHOUT GIBBING sound = sfx_plxdth; // villsa [STRIFE] different sound } S_StartSound (mo, sound); } // // A_TeleportBeacon // // villsa [STRIFE] - new codepointer // void A_TeleportBeacon(mobj_t* actor) { mobj_t* mobj; mobj_t* fog; fixed_t fog_x; fixed_t fog_y; if(actor->target != players[actor->miscdata].mo) actor->target = players[actor->miscdata].mo; mobj = P_SpawnMobj(actor->x, actor->y, ONFLOORZ, MT_REBEL1); // haleyjd 20141024: missing code from disassembly; transfer allegiance // originally from master player to the rebel. mobj->miscdata = actor->miscdata; if(!P_TryMove(mobj, mobj->x, mobj->y)) { // Rebel is probably stuck in something.. too bad P_RemoveMobj(mobj); return; } // beacon no longer special actor->flags &= ~MF_SPECIAL; // 20160306: set rebel threshold mobj->threshold = 100; // set rebel color and flags mobj->flags |= ((actor->miscdata << MF_TRANSSHIFT) | MF_NODIALOG); mobj->target = NULL; // double Rebel's health in deathmatch mode if(deathmatch) mobj->health <<= 1; if(actor->target) { mobj_t* targ = actor->target->target; if(targ) { if(targ->type != MT_REBEL1 || targ->miscdata != mobj->miscdata) mobj->target = targ; } } P_SetMobjState(mobj, mobj->info->seestate); mobj->angle = actor->angle; fog_x = mobj->x + FixedMul(20*FRACUNIT, finecosine[actor->angle>>ANGLETOFINESHIFT]); fog_y = mobj->y + FixedMul(20*FRACUNIT, finesine[actor->angle>>ANGLETOFINESHIFT]); fog = P_SpawnMobj(fog_x, fog_y, mobj->z, MT_TFOG); S_StartSound(fog, sfx_telept); if(--actor->health < 0) P_RemoveMobj(actor); } // // A_BodyParts // // villsa [STRIFE] new codepointer // 09/06/10: Spawns gibs when organic actors get splattered, or junk // when robots explode. // void A_BodyParts(mobj_t* actor) { mobjtype_t type; mobj_t* mo; angle_t an; if(actor->flags & MF_NOBLOOD) // Robots are flagged NOBLOOD type = MT_JUNK; else type = MT_MEAT; mo = P_SpawnMobj(actor->x, actor->y, actor->z + (24*FRACUNIT), type); P_SetMobjState(mo, mo->info->spawnstate + (P_Random() % 19)); an = (P_Random() << 13) / 255; mo->angle = an << ANGLETOFINESHIFT; mo->momx = FixedMul(finecosine[an], (P_Random() & 0x0f) << FRACBITS); mo->momy = FixedMul(finesine[an], (P_Random() & 0x0f) << FRACBITS); mo->momz = (P_Random() & 0x0f) << FRACBITS; } // // A_ClaxonBlare // // [STRIFE] New function // haleyjd 09/08/10: The ever-dreadful Strife alarm! // void A_ClaxonBlare(mobj_t* actor) { // Timer ran down? if(--actor->reactiontime < 0) { // reset to initial state actor->target = NULL; actor->reactiontime = actor->info->reactiontime; // listen for more noise A_Listen(actor); // If we heard something, stay on for a while, // otherwise return to spawnstate. if(actor->target) actor->reactiontime = 50; else P_SetMobjState(actor, actor->info->spawnstate); } // When almost ran down, clear the soundtarget so it doesn't // retrigger the alarm. // Also, play the harsh, grating claxon. if(actor->reactiontime == 2) actor->subsector->sector->soundtarget = NULL; else if(actor->reactiontime > 50) S_StartSound(actor, sfx_alarm); } // // A_ActiveSound // // villsa [STRIFE] new codepointer // 09/06/10: Plays an object's active sound periodically. // void A_ActiveSound(mobj_t* actor) { if(actor->info->activesound) { if(!(leveltime & 7)) // haleyjd: added parens S_StartSound(actor, actor->info->activesound); } } // // A_ClearSoundTarget // // villsa [STRIFE] new codepointer // 09/06/10: Clears the actor's sector soundtarget, so that the actor // will not be continually alerted/awakened ad infinitum. Used by // shopkeepers. // void A_ClearSoundTarget(mobj_t* actor) { actor->subsector->sector->soundtarget = NULL; } // // A_DropBurnFlesh // // villsa [STRIFE] new codepointer // void A_DropBurnFlesh(mobj_t* actor) { mobj_t* mo; mobjtype_t type; type = actor->type; mo = P_SpawnMobj(actor->x, actor->y, actor->z + (24*FRACUNIT), MT_BURNDROP); mo->momz = -FRACUNIT; actor->type = MT_SFIREBALL; P_RadiusAttack(actor, actor, 64); actor->type = type; } // // A_FlameDeath // // villsa [STRIFE] new codepointer // 09/06/10: Death animation for flamethrower fireballs. // void A_FlameDeath(mobj_t* actor) { actor->flags |= MF_NOGRAVITY; actor->momz = (P_Random() & 3) << FRACBITS; } // // A_ClearForceField // // villsa [STRIFE] new codepointer // check for all matching lines in the sector // and disable blocking/midtextures // void A_ClearForceField(mobj_t* actor) { int i; sector_t *sec; line_t *secline; actor->flags &= ~(MF_SOLID|MF_SPECIAL); sec = actor->subsector->sector; if(!sec->linecount) return; for(i = 0; i < sec->linecount; i++) { secline = sec->lines[i]; // BUG: will crash if 1S line has TWOSIDED flag! if(!(secline->flags & ML_TWOSIDED)) continue; if(secline->special != 148) continue; secline->flags &= ~ML_BLOCKING; secline->special = 0; sides[secline->sidenum[0]].midtexture = 0; sides[secline->sidenum[1]].midtexture = 0; } } crispy-doom-crispy-doom-5.6.4/src/strife/p_floor.c000066400000000000000000000415211360717211000221260ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Floor animation: raising stairs. // #include "z_zone.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // // FLOORS // // // Move a plane (floor or ceiling) and check for crushing // // [STRIFE] Various changes were made to remove calls to P_ChangeSector when // P_ChangeSector returns true. // result_e T_MovePlane ( sector_t* sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction ) { boolean flag; fixed_t lastpos; switch(floorOrCeiling) { case 0: // FLOOR switch(direction) { case -1: // DOWN if (sector->floorheight - speed < dest) { // villsa [STRIFE] unused //lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector,crush); // villsa [STRIFE] unused /*if (flag == true) { sector->floorheight =lastpos; P_ChangeSector(sector,crush); //return crushed; }*/ return pastdest; } else { // villsa [STRIFE] unused //lastpos = sector->floorheight; sector->floorheight -= speed; flag = P_ChangeSector(sector,crush); // villsa [STRIFE] unused /*if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector,crush); return crushed; }*/ return ok; } break; case 1: // UP if (sector->floorheight + speed > dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { // COULD GET CRUSHED lastpos = sector->floorheight; sector->floorheight += speed; flag = P_ChangeSector(sector,crush); if (flag == true) { // haleyjd 20130210: Bug fix - Strife DOES do this. if (crush == true) return crushed; sector->floorheight = lastpos; P_ChangeSector(sector,crush); return crushed; } else return ok; } break; } break; case 1: // CEILING switch(direction) { case -1: // DOWN if (sector->ceilingheight - speed < dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { // COULD GET CRUSHED lastpos = sector->ceilingheight; sector->ceilingheight -= speed; flag = P_ChangeSector(sector,crush); if (flag == true) { if (crush == true) return crushed; sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); return crushed; } } break; case 1: // UP if (sector->ceilingheight + speed > dest) { // villsa [STRIFE] unused //lastpos = sector->ceilingheight; sector->ceilingheight = dest; // villsa [STRIFE] unused //flag = P_ChangeSector(sector,crush); /*if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); //return crushed; }*/ return pastdest; } else { // villsa [STRIFE] unused //lastpos = sector->ceilingheight; sector->ceilingheight += speed; // villsa [STRIFE] unused //flag = P_ChangeSector(sector,crush); return ok; } break; } break; } return ok; } // // MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) // void T_MoveFloor(floormove_t* floor) { result_e res; res = T_MovePlane(floor->sector, floor->speed, floor->floordestheight, floor->crush,0,floor->direction); if (!(leveltime&7)) S_StartSound(&floor->sector->soundorg, sfx_stnmov); if (res == pastdest) { floor->sector->specialdata = NULL; if (floor->direction == 1) { switch(floor->type) { case donutRaise: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } } else if (floor->direction == -1) { switch(floor->type) { case lowerAndChange: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } } P_RemoveThinker(&floor->thinker); S_StartSound(&floor->sector->soundorg, sfx_pstop); } } // // HANDLE FLOOR TYPES // // haleyjd 09/16/2010: [STRIFE] Modifications to floortypes: // * raiseFloor24 was changed into raiseFloor64 // * turboLower does not appear to adjust the floor height (STRIFE-TODO: verify) // * raiseFloor512AndChange type was added. // int EV_DoFloor ( line_t* line, floor_e floortype ) { int secnum; int rtn; int i; sector_t* sec; floormove_t* floor; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // new floor thinker rtn = 1; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = floortype; floor->crush = false; switch(floortype) { case lowerFloor: // [STRIFE] verified unmodified floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindHighestFloorSurrounding(sec); break; case lowerFloorToLowest: // [STRIFE] verified unmodified floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); break; case turboLower: // [STRIFE] Modified: does not += 8 floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED * 4; floor->floordestheight = P_FindHighestFloorSurrounding(sec); //if (floor->floordestheight != sec->floorheight) // floor->floordestheight += 8*FRACUNIT; break; case raiseFloorCrush: // [STRIFE] verified unmodified floor->crush = true; case raiseFloor: floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestCeilingSurrounding(sec); if (floor->floordestheight > sec->ceilingheight) floor->floordestheight = sec->ceilingheight; floor->floordestheight -= (8*FRACUNIT)* (floortype == raiseFloorCrush); break; case raiseFloorTurbo: // [STRIFE] verified unmodified floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED*4; floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); break; case raiseFloorToNearest: // [STRIFE] verified unmodified floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); break; case raiseFloor64: // [STRIFE] modified from raiseFloor24! floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 64 * FRACUNIT; // [STRIFE] break; case raiseFloor512: // [STRIFE] verified unmodified floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 512 * FRACUNIT; break; case raiseFloor24AndChange: // [STRIFE] verified unmodified floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; sec->floorpic = line->frontsector->floorpic; sec->special = line->frontsector->special; break; case raiseFloor512AndChange: // [STRIFE] New floor type floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = floor->sector->floorheight + 512 * FRACUNIT; sec->floorpic = line->frontsector->floorpic; sec->special = line->frontsector->special; break; case raiseToTexture: // [STRIFE] verified unmodified { int minsize = INT_MAX; side_t* side; floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; for (i = 0; i < sec->linecount; i++) { if (twoSided (secnum, i) ) { side = getSide(secnum,i,0); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; side = getSide(secnum,i,1); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; } } floor->floordestheight = floor->sector->floorheight + minsize; } break; case lowerAndChange: // [STRIFE] verified unmodified floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); floor->texture = sec->floorpic; for (i = 0; i < sec->linecount; i++) { if ( twoSided(secnum, i) ) { if (getSide(secnum,i,0)->sector-sectors == secnum) { sec = getSector(secnum,i,1); if (sec->floorheight == floor->floordestheight) { floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } } else { sec = getSector(secnum,i,0); if (sec->floorheight == floor->floordestheight) { floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } } } } default: break; } } return rtn; } // // BUILD A STAIRCASE! // int EV_BuildStairs ( line_t* line, stair_e type ) { int secnum; int height; int i; int newsecnum; int texture; int ok; int rtn; sector_t* sec; sector_t* tsec; floormove_t* floor; // Either Watcom or Rogue moved the switch out of the loop below, probably // because it was a loop invariant, and put the default values in the // initializers here. I cannot be bothered to figure it out without doing // this myself :P fixed_t stairsize = 8*FRACUNIT; fixed_t speed = FLOORSPEED; int direction = 1; switch(type) { case build8: // [STRIFE] Verified unmodified. speed = FLOORSPEED/4; break; case turbo16: // [STRIFE] Verified unmodified. speed = FLOORSPEED*4; stairsize = 16*FRACUNIT; break; case buildDown16: // [STRIFE] New stair type stairsize = -16*FRACUNIT; direction = -1; break; } secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // new floor thinker rtn = 1; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->tag = 0; // haleyjd 20140919: [STRIFE] clears tag of first stair sector sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->direction = direction; // haleyjd 20140919: bug fix: direction, not "1" floor->sector = sec; floor->speed = speed; height = sec->floorheight + stairsize; floor->floordestheight = height; texture = sec->floorpic; // Find next sector to raise // 1. Find 2-sided line with same sector side[0] // 2. Other side is the next sector to raise do { ok = 0; for (i = 0;i < sec->linecount;i++) { if ( !((sec->lines[i])->flags & ML_TWOSIDED) ) continue; tsec = (sec->lines[i])->frontsector; newsecnum = tsec-sectors; if (secnum != newsecnum) continue; tsec = (sec->lines[i])->backsector; newsecnum = tsec - sectors; if (tsec->floorpic != texture) continue; height += stairsize; if (tsec->specialdata) continue; sec = tsec; secnum = newsecnum; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->direction = direction; // [STRIFE]: for buildDown16 floor->sector = sec; floor->speed = speed; floor->floordestheight = height; ok = 1; break; } } while(ok); } return rtn; } crispy-doom-crispy-doom-5.6.4/src/strife/p_inter.c000066400000000000000000001137001360717211000221250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handling interactions (i.e., collisions). // // Data. #include "doomdef.h" #include "dstrings.h" #include "sounds.h" #include "deh_main.h" #include "deh_misc.h" #include "doomstat.h" #include "m_misc.h" #include "m_random.h" #include "i_system.h" #include "am_map.h" #include "p_local.h" #include "p_dialog.h" // villsa [STRIFE] #include "s_sound.h" #include "p_inter.h" #include "hu_stuff.h" // villsa [STRIFE] #include "z_zone.h" // villsa [STRIFE] // haleyjd [STRIFE] #include "w_wad.h" #include "p_pspr.h" #include "p_dialog.h" #include "f_finale.h" #define BONUSADD 6 // a weapon is found with two clip loads, // a big item has five clip loads // villsa [STRIFE] updated arrays int maxammo[NUMAMMO] = { 250, 50, 25, 400, 100, 30, 16 }; int clipammo[NUMAMMO] = { 10, 4, 2, 20, 4, 6, 4 }; // // 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 // // [STRIFE] Modified for Strife ammo types // boolean P_GiveAmmo(player_t* player, ammotype_t ammo, int num) { int oldammo; if(ammo == am_noammo) return false; if(ammo > NUMAMMO) I_Error ("P_GiveAmmo: bad type %i", ammo); if(player->ammo[ammo] == player->maxammo[ammo]) return false; if(num) num *= clipammo[ammo]; else num = clipammo[ammo]/2; if(gameskill == sk_baby || gameskill == sk_nightmare) { // give double ammo in trainer mode, // you'll need in nightmare num <<= 1; } oldammo = player->ammo[ammo]; player->ammo[ammo] += num; if(player->ammo[ammo] > player->maxammo[ammo]) player->ammo[ammo] = player->maxammo[ammo]; // If non zero ammo, // don't change up weapons, // player was lower on purpose. if(oldammo) return true; // We were down to zero, // so select a new weapon. // Preferences are not user selectable. // villsa [STRIFE] ammo update // where's the check for grenades? - haleyjd: verified no switch to grenades // haleyjd 10/03/10: don't change to electric bow when picking up poison // arrows. if(!player->readyweapon) { switch(ammo) { case am_bullets: if(player->weaponowned[wp_rifle]) player->pendingweapon = wp_rifle; break; case am_elecbolts: if(player->weaponowned[wp_elecbow]) player->pendingweapon = wp_elecbow; break; case am_cell: if(player->weaponowned[wp_mauler]) player->pendingweapon = wp_mauler; break; case am_missiles: if(player->weaponowned[wp_missile]) player->pendingweapon = wp_missile; break; default: break; } } return true; } // // P_GiveWeapon // The weapon name may have a MF_DROPPED flag ored in. // // villsa [STRIFE] some stuff has been changed/moved around // boolean P_GiveWeapon(player_t* player, weapontype_t weapon, boolean dropped) { boolean gaveammo; boolean gaveweapon; // villsa [STRIFE] new code for giving alternate version // of the weapon to player if(player->weaponowned[weapon]) gaveweapon = false; else { gaveweapon = true; player->weaponowned[weapon] = true; // Alternate "sister" weapons that you also get as a bonus: switch(weapon) { case wp_elecbow: player->weaponowned[wp_poisonbow] = true; break; case wp_hegrenade: player->weaponowned[wp_wpgrenade] = true; break; case wp_mauler: player->weaponowned[wp_torpedo] = true; break; default: break; } // check for the standard weapons only if(weapon > player->readyweapon && weapon <= wp_sigil) player->pendingweapon = weapon; } if(netgame && (deathmatch != 2) && !dropped) { // leave placed weapons forever on net games if(!gaveweapon) return false; player->bonuscount += BONUSADD; player->weaponowned[weapon] = true; if(deathmatch) P_GiveAmmo(player, weaponinfo[weapon].ammo, 5); else P_GiveAmmo(player, weaponinfo[weapon].ammo, 2); if(player == &players[consoleplayer]) S_StartSound (NULL, sfx_wpnup); return false; } if(weaponinfo[weapon].ammo != am_noammo) { // give one clip with a dropped weapon, // two clips with a found weapon if(dropped) gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1); else gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); } else gaveammo = false; return(gaveweapon || gaveammo); } // // P_GiveBody // Returns false if the body isn't needed at all // // villsa [STRIFE] a lot of changes have been added for stamina // boolean P_GiveBody(player_t* player, int num) { int maxhealth; int healing; maxhealth = MAXHEALTH + player->stamina; if(num >= 0) // haleyjd 20100923: fixed to give proper amount of health { mobj_t *mo; // haleyjd 20110225: needed below... // any healing to do? if(player->health >= maxhealth) return false; // give, and cap to maxhealth player->health += num; if(player->health >= maxhealth) player->health = maxhealth; // Set mo->health for consistency. // haleyjd 20110225: Seems Strife can call this on a NULL player->mo // when giving items to players that are not in the game... mo = P_SubstNullMobj(player->mo); mo->health = player->health; } else { // [STRIFE] handle healing from the Front's medic // The amount the player's health will be set to scales up with stamina // increases. // Ex 1: On the wimpiest skill level, -100 is sent in. This restores // full health no matter what your stamina. // (100*100)/100 = 100 // (200*100)/100 = 200 // Ex 2: On the most stringent skill levels, -50 is sent in. This will // restore at most half of your health. // (100*50)/100 = 50 // (200*50)/100 = 100 healing = (-num * maxhealth) / MAXHEALTH; // This is also the "threshold" of healing. You need less health than // the amount that will be restored in order to get any benefit. // So on the easiest skill you will always be fully healed. // On the hardest skill you must have less than 50 health, and will // only recover to 50 (assuming base stamina stat) if(player->health >= healing) return false; // Set health. BUG: Oddly, mo->health is NOT set here... player->health = healing; } return true; } // // P_GiveArmor // Returns false if the armor is worse // than the current armor. // // [STRIFE] Modified for Strife armor items // boolean P_GiveArmor(player_t* player, int armortype) { int hits; // villsa [STRIFE] if(armortype < 0) { if(player->armorpoints) return false; armortype = -armortype; } hits = armortype * 100; if(player->armorpoints >= hits) return false; // don't pick up player->armortype = armortype; player->armorpoints = hits; return true; } // // P_GiveCard // // [STRIFE] Modified to use larger bonuscount // boolean P_GiveCard(player_t* player, card_t card) { if (player->cards[card]) return false; // villsa [STRIFE] multiply by 2 player->bonuscount = BONUSADD * 2; player->cards[card] = true; return true; } // // P_GivePower // // [STRIFE] Modifications for new powerups // boolean P_GivePower(player_t* player, powertype_t power) { // haleyjd 09/14/10: [STRIFE] moved to top, exception for Shadow Armor if(player->powers[power] && power != pw_invisibility) return false; // already got it // if giving pw_invisibility and player already has MVIS, no can do. if(power == pw_invisibility && (player->mo->flags & MF_MVIS)) return false; // villsa [STRIFE] if(power == pw_targeter) { player->powers[power] = TARGTICS; P_SetPsprite(player, ps_targcenter, S_TRGT_00); // 10 P_SetPsprite(player, ps_targleft, S_TRGT_01); // 11 P_SetPsprite(player, ps_targright, S_TRGT_02); // 12 player->psprites[ps_targcenter].sx = (160*FRACUNIT); player->psprites[ps_targleft ].sy = (100*FRACUNIT); player->psprites[ps_targcenter].sy = (100*FRACUNIT); player->psprites[ps_targright ].sy = (100*FRACUNIT); return true; } if(power == pw_invisibility) { // if player already had this power... if(player->powers[power]) { // remove SHADOW, give MVIS. player->mo->flags &= ~MF_SHADOW; player->mo->flags |= MF_MVIS; } else // give SHADOW player->mo->flags |= MF_SHADOW; // set tics if giving shadow, or renew them if MVIS. player->powers[power] = INVISTICS; return true; } if(power == pw_ironfeet) { player->powers[power] = IRONTICS; return true; } if(power == pw_strength) { P_GiveBody(player, 100); player->powers[power] = 1; return true; } // villsa [STRIFE] if(power == pw_allmap) { // remember in mapstate if(gamemap < 40) player->mapstate[gamemap] = true; player->powers[power] = 1; return true; } // villsa [STRIFE] if(power == pw_communicator) { player->powers[power] = 1; return true; } // default behavior: player->powers[power] = 1; return true; } // villsa [STRIFE] static char pickupmsg[80]; // // P_TouchSpecialThing // // [STRIFE] Rewritten for Strife collectables. // void P_TouchSpecialThing(mobj_t* special, mobj_t* toucher) { player_t* player; int i; fixed_t delta; int sound; delta = special->z - toucher->z; if(delta > toucher->height || delta < -8*FRACUNIT) 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; // villsa [STRIFE] damage toucher if special is spectral // haleyjd 09/21/10: corrected to test for SPECTRE thingtypes specifically switch(special->type) { case MT_SPECTRE_A: case MT_SPECTRE_B: case MT_SPECTRE_C: case MT_SPECTRE_D: case MT_SPECTRE_E: case MT_ENTITY: case MT_SUBENTITY: P_DamageMobj(toucher, NULL, NULL, 5); return; default: break; } // villsa [STRIFE] pickupmsg[0] = 0; // Identify by sprite. // villsa [STRIFE] new items switch(special->sprite) { // bullets case SPR_BLIT: // haleyjd: fixed missing MF_DROPPED check if(!P_GiveAmmo(player, am_bullets, !(special->flags & MF_DROPPED))) return; break; // box of bullets case SPR_BBOX: if(!P_GiveAmmo(player, am_bullets, 5)) return; break; // missile case SPR_MSSL: if(!P_GiveAmmo(player, am_missiles, 1)) return; break; // box of missiles case SPR_ROKT: if(!P_GiveAmmo(player, am_missiles, 5)) return; break; // battery case SPR_BRY1: if(!P_GiveAmmo(player, am_cell, 1)) return; break; // cell pack case SPR_CPAC: if(!P_GiveAmmo(player, am_cell, 5)) return; break; // poison bolts case SPR_PQRL: if(!P_GiveAmmo(player, am_poisonbolts, 5)) return; break; // electric bolts case SPR_XQRL: if(!P_GiveAmmo(player, am_elecbolts, 5)) return; break; // he grenades case SPR_GRN1: if(!P_GiveAmmo(player, am_hegrenades, 1)) return; break; // wp grenades case SPR_GRN2: if(!P_GiveAmmo(player, am_wpgrenades, 1)) return; break; // rifle case SPR_RIFL: if(!P_GiveWeapon(player, wp_rifle, (special->flags & MF_DROPPED) != 0)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // flame thrower case SPR_FLAM: if(!P_GiveWeapon(player, wp_flame, false)) return; // haleyjd: gives extra ammo. P_GiveAmmo(player, am_cell, 3); sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // missile launcher case SPR_MMSL: if(!P_GiveWeapon(player, wp_missile, false)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // grenade launcher case SPR_GRND: if(!P_GiveWeapon(player, wp_hegrenade, (special->flags & MF_DROPPED) != 0)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // mauler case SPR_TRPD: if(!P_GiveWeapon(player, wp_mauler, false)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // electric bolt crossbow case SPR_CBOW: if(!P_GiveWeapon(player, wp_elecbow, (special->flags & MF_DROPPED) != 0)) return; sound = sfx_wpnup; // haleyjd: SHK-CHK! break; // haleyjd 09/21/10: missed case: THE SIGIL! case SPR_SIGL: if(!P_GiveWeapon(player, wp_sigil, (special->flags & MF_DROPPED) != 0)) { player->sigiltype = special->frame; return; } if(netgame) player->sigiltype = 4; player->pendingweapon = wp_sigil; player->st_update = true; if(deathmatch) return; sound = sfx_wpnup; break; // backpack case SPR_BKPK: if(!player->backpack) { for(i = 0; i < NUMAMMO; i++) player->maxammo[i] *= 2; player->backpack = true; } for(i = 0; i < NUMAMMO; i++) P_GiveAmmo(player, i, 1); break; // 1 Gold case SPR_COIN: P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; // 10 Gold case SPR_CRED: for(i = 0; i < 10; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; // 25 Gold case SPR_SACK: // haleyjd 09/21/10: missed code: if a SPR_SACK object has negative // health, it will give that much gold - STRIFE-TODO: verify if(special->health < 0) { for(i = special->health; i != 0; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); } else { for(i = 0; i < 25; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); } break; // 50 Gold case SPR_CHST: for(i = 0; i < 50; i++) P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1); break; // Leather Armor case SPR_ARM1: if(!P_GiveArmor(player, -2)) if(!P_GiveInventoryItem(player, special->sprite, special->type)) pickupmsg[0] = '!'; break; // Metal Armor case SPR_ARM2: if(!P_GiveArmor(player, -1)) if(!P_GiveInventoryItem(player, special->sprite, special->type)) pickupmsg[0] = '!'; break; // All-map powerup case SPR_PMAP: if(!P_GivePower(player, pw_allmap)) return; sound = sfx_yeah; break; // The Comm Unit - because you need Blackbird whining in your ear the // whole time and telling you how lost she is :P case SPR_COMM: if(!P_GivePower(player, pw_communicator)) return; sound = sfx_yeah; break; // haleyjd 09/21/10: missed case - Shadow Armor; though, I do not know why // this has a case distinct from generic inventory items... Maybe it started // out as an auto-use-if-possible item much like regular armor... case SPR_SHD1: if(!P_GiveInventoryItem(player, SPR_SHD1, special->type)) pickupmsg[0] = '!'; break; // villsa [STRIFE] check default items case SPR_TOKN: default: if(special->type >= MT_KEY_BASE && special->type <= MT_NEWKEY5) { // haleyjd 09/21/10: Strife player still picks up keys that // he has already found. (break, not return) if(!P_GiveCard(player, special->type - MT_KEY_BASE)) break; } else { if(!P_GiveInventoryItem(player, special->sprite, special->type)) pickupmsg[0] = '!'; } break; } // villsa [STRIFE] set message if(!pickupmsg[0]) { if(special->info->name) { DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You picked up the %s.", DEH_String(special->info->name)); } else DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You picked up the item."); } // use the first character to indicate that the player is full on items else if(pickupmsg[0] == '!') { DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You cannot hold any more."); player->message = pickupmsg; return; } if(special->flags & MF_GIVEQUEST) { // [STRIFE]: Award quest flag based on the thing's speed. Quest 8 was // apparently at some point given by the Broken Power Coupling, which is // why they don't want to award it if you have Quest 6 (which is // acquired by destroying the Front's working power coupling). BUT, the // broken coupling object's speed is NOT 8... it is 512*FRACUNIT. For // strict portability beyond the x86, we need to AND the operand by 31. if(special->info->speed != 8 || !(player->questflags & QF_QUEST6)) player->questflags |= 1 << ((special->info->speed - 1) & 31); } // haleyjd 08/30/10: [STRIFE] No itemcount //if (special->flags & MF_COUNTITEM) // player->itemcount++; P_RemoveMobj(special); player->message = pickupmsg; player->bonuscount += BONUSADD; if(player == &players[consoleplayer]) S_StartSound(NULL, sound); } // villsa [STRIFE] static char plrkilledmsg[80]; // // KillMobj // // [STRIFE] Major modifications for drop types, no tic randomization, etc. // void P_KillMobj(mobj_t* source, mobj_t* target) { mobjtype_t item; mobj_t* mo; line_t junk; int i; // villsa [STRIFE] corpse and dropoff are removed, but why when these two flags // are set a few lines later? watcom nonsense perhaps? target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_BOUNCE|MF_CORPSE|MF_DROPOFF); // villsa [STRIFE] unused /* if (target->type != MT_SKULL) target->flags &= ~MF_NOGRAVITY; */ target->flags |= MF_CORPSE|MF_DROPOFF; target->height = FRACUNIT; // villsa [STRIFE] set to fracunit instead of >>= 2 if(source && source->player) { // count for intermission if(target->flags & MF_COUNTKILL) source->player->killcount++; if(target->player) { source->player->frags[target->player-players]++; // villsa [STRIFE] new messages when fragging players // haleyjd 20141024: corrected; uses player->allegiance, not mo->miscdata DEH_snprintf(plrkilledmsg, sizeof(plrkilledmsg), "%s killed %s", player_names[source->player->allegiance], player_names[target->player->allegiance]); if(netgame) players[consoleplayer].message = plrkilledmsg; } } else if(!netgame && (target->flags & MF_COUNTKILL)) { // count all monster deaths, // even those caused by other monsters players[0].killcount++; } if(target->player) { // count environment kills against you if(!source) target->player->frags[target->player-players]++; if(gamemap == 29 && !netgame) { // haleyjd 09/13/10: [STRIFE] Give player the bad ending. F_StartFinale(); return; } // villsa [STRIFE] spit out inventory items when killed if(netgame) { int amount = 0; mobj_t* loot; int r = 0; while(1) { if(target->player->inventory[0].amount <= 0) break; item = target->player->inventory[0].type; if(item == MT_MONY_1) { loot = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), MT_MONY_25); // [STRIFE] TODO - what the hell is it doing here? loot->health = target->player->inventory[0].amount; loot->health = -target->player->inventory[0].amount; amount = target->player->inventory[0].amount; } else { loot = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), item); amount = 1; } P_RemoveInventoryItem(target->player, 0, amount); r = P_Random(); loot->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS; loot->momy += ((P_Random() & 7) + 1) << FRACBITS; loot->flags |= MF_DROPPED; } } //target->flags &= ~MF_SOLID; target->player->playerstate = PST_DEAD; target->player->mo->momz += 5*FRACUNIT; // [STRIFE]: small hop! P_DropWeapon(target->player); if(target->player == &players[consoleplayer] && automapactive) { // don't die in auto map, // switch view prior to dying AM_Stop (); } } // villsa [STRIFE] some modifications to setting states if(target->state != &states[S_BURN_23]) { if(target->health == -6666) P_SetMobjState(target, S_DISR_00); // 373 else { // haleyjd [STRIFE] 20160111: Rogue changed check from < to <= if(target->health <= -target->info->spawnhealth && target->info->xdeathstate) P_SetMobjState(target, target->info->xdeathstate); else P_SetMobjState(target, target->info->deathstate); } } // villsa [STRIFE] no death tics randomization // Drop stuff. // villsa [STRIFE] get item from dialog target item = P_DialogFind(target->type, target->miscdata)->dropitem; if(!item) { // villsa [STRIFE] drop default items switch(target->type) { case MT_ORACLE: item = MT_MEAT; break; case MT_PROGRAMMER: item = MT_SIGIL_A; break; case MT_PRIEST: item = MT_JUNK; break; case MT_BISHOP: item = MT_AMINIBOX; break; case MT_PGUARD: case MT_CRUSADER: item = MT_ACELL; break; case MT_RLEADER: item = MT_AAMMOBOX; break; case MT_GUARD1: case MT_REBEL1: case MT_SHADOWGUARD: item = MT_ACLIP; break; case MT_SPECTRE_B: item = MT_SIGIL_B; break; case MT_SPECTRE_C: item = MT_SIGIL_C; break; case MT_SPECTRE_D: item = MT_SIGIL_D; break; case MT_SPECTRE_E: item = MT_SIGIL_E; break; case MT_COUPLING: junk.tag = 225; EV_DoDoor(&junk, vld_close); junk.tag = 44; EV_DoFloor(&junk, lowerFloor); GiveVoiceObjective("VOC13", "LOG13", 0); item = MT_COUPLING_BROKEN; players[0].questflags |= (1 << (mobjinfo[MT_COUPLING].speed - 1)); break; default: return; } } // handle special case for scripted target's dropped item switch(item) { case MT_TOKEN_SHOPCLOSE: junk.tag = 222; EV_DoDoor(&junk, vld_close); P_NoiseAlert(players[0].mo, players[0].mo); M_snprintf(plrkilledmsg, sizeof(plrkilledmsg), "%s", DEH_String("You're dead! You set off the alarm.")); if(!deathmatch) players[consoleplayer].message = plrkilledmsg; return; case MT_TOKEN_PRISON_PASS: junk.tag = 223; EV_DoDoor(&junk, vld_open); return; case MT_TOKEN_DOOR3: junk.tag = 224; EV_DoDoor(&junk, vld_open); return; case MT_SIGIL_A: case MT_SIGIL_B: case MT_SIGIL_C: case MT_SIGIL_D: case MT_SIGIL_E: for(i = 0; i < MAXPLAYERS; i++) { if(!P_GiveWeapon(&players[i], wp_sigil, false)) { if(players[i].sigiltype++ > 4) players[i].sigiltype = 4; } // haleyjd 20110225: fixed these two assignments which Watcom munged // up in the assembly by pre-incrementing the pointer into players[] players[i].st_update = true; players[i].pendingweapon = wp_sigil; } return; case MT_TOKEN_ALARM: P_NoiseAlert(players[0].mo, players[0].mo); M_snprintf(plrkilledmsg, sizeof(plrkilledmsg), "%s", DEH_String("You Fool! You've set off the alarm")); if(!deathmatch) players[consoleplayer].message = plrkilledmsg; return; default: break; } // villsa [STRIFE] toss out item if(!deathmatch || !(mobjinfo[item].flags & MF_NOTDMATCH)) { int r; mo = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), item); r = P_Random(); mo->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS; r = P_Random(); mo->momy += ((r & 7) - (P_Random() & 7)) << FRACBITS; mo->flags |= (MF_SPECIAL|MF_DROPPED); // special versions of items } } // // P_IsMobjBoss // // villsa [STRIFE] new function // static boolean P_IsMobjBoss(mobjtype_t type) { switch(type) { case MT_PROGRAMMER: case MT_BISHOP: case MT_RLEADER: case MT_ORACLE: case MT_PRIEST: return true; default: return false; } } // // 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. // // [STRIFE] Extensive changes for spectrals, fire damage, disintegration, and // a plethora of mobjtype-specific hacks. // void P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage) { angle_t ang; int saved; player_t* player; fixed_t thrust; int temp; if(!(target->flags & MF_SHOOTABLE) ) return; // shouldn't happen... if(target->health <= 0) return; player = target->player; // villsa [STRIFE] unused - skullfly check (removed) // villsa [STRIFE] handle spectral stuff // notes on projectile health: // -2 == enemy spectral projectile // -1 == player spectral projectile // haleyjd 20110203: refactored completely if(inflictor && (inflictor->flags & MF_SPECTRAL)) { // players aren't damaged by their own (or others???) sigils // STRIFE-TODO: verify in deathmatch if(target->type == MT_PLAYER && inflictor->health == -1) return; // enemies aren't damaged by enemy sigil attacks if((target->flags & MF_SPECTRAL) && inflictor->health == -2) return; // Macil2, Oracle, and Spectre C cannot be damaged by Sigil A switch(target->type) { case MT_RLEADER2: case MT_ORACLE: case MT_SPECTRE_C: // haleyjd: added source->player validity check for safety... if(source->player && source->player->sigiltype < 1) return; default: break; } } // villsa [STRIFE] new checks for various actors if(inflictor) { // Fire damage inflictors if(inflictor->type == MT_SFIREBALL || inflictor->type == MT_C_FLAME || inflictor->type == MT_PFLAME) { temp = damage / 2; if(P_IsMobjBoss(target->type)) damage /= 2; else if(inflictor->type == MT_PFLAME) { damage /= 2; // robots take very little damage if(target->flags & MF_NOBLOOD) damage = temp / 2; } } else { switch(inflictor->type) { case MT_HOOKSHOT: // haleyjd 20110203: should use source, not inflictor ang = R_PointToAngle2( target->x, target->y, source->x, source->y) >> ANGLETOFINESHIFT; target->momx += FixedMul(finecosine[ang], (12750*FRACUNIT) / target->info->mass); target->momy += FixedMul(finesine[ang], (12750*FRACUNIT) / target->info->mass); target->reactiontime += 10; temp = P_AproxDistance(target->x - source->x, target->y - source->y); temp /= target->info->mass; if(temp < 1) temp = 1; target->momz = (source->z - target->z) / temp; break; case MT_POISARROW: // don't affect robots if(target->flags & MF_NOBLOOD) return; // instant kill damage = target->health + 10; break; default: // Spectral retaliation, though this may in fact be unreachable // since non-spectral inflictors are mostly filtered out. if(target->flags & MF_SPECTRAL && !(inflictor->flags & MF_SPECTRAL)) { P_SetMobjState(target, target->info->missilestate); return; // take no damage } break; } } } // villsa [STRIFE] special cases for shopkeepers and macil if((target->type >= MT_SHOPKEEPER_W && target->type <= MT_SHOPKEEPER_M) || target->type == MT_RLEADER) { if(source) target->target = source; P_SetMobjState(target, target->info->painstate); return; } // villsa [STRIFE] handle fieldguard damage if(target->type == MT_FIELDGUARD) { // degnin ores are only allowed to damage the fieldguard if(!inflictor || inflictor->type != MT_DEGNINORE) return; damage = target->health; } 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_flame)) { ang = R_PointToAngle2(inflictor->x, inflictor->y, target->x, target->y); thrust = damage * (FRACUNIT>>3) * 100 / target->info->mass; // make fall forwards sometimes if(damage < 40 && damage > target->health && target->z - inflictor->z > 64*FRACUNIT && (P_Random() & 1)) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; target->momx += FixedMul (thrust, finecosine[ang]); target->momy += FixedMul (thrust, finesine[ang]); } // player specific if(player) { // end of game hell hack if (target->subsector->sector->special == 11 && damage >= target->health) { damage = target->health - 1; } // Below certain threshold, // ignore damage in GOD mode. // villsa [STRIFE] removed pw_invulnerability check if(damage < 1000 && (player->cheats & CF_GODMODE)) return; // villsa [STRIFE] flame attacks don't damage player if wearing envirosuit if(player->powers[pw_ironfeet] && inflictor) { if(inflictor->type == MT_SFIREBALL || inflictor->type == MT_C_FLAME || inflictor->type == MT_PFLAME) { damage = 0; } } if(player->armortype) { if (player->armortype == 1) saved = damage/3; else saved = damage/2; if(player->armorpoints <= saved) { // armor is used up saved = player->armorpoints; player->armortype = 0; // villsa [STRIFE] P_UseInventoryItem(player, SPR_ARM1); P_UseInventoryItem(player, SPR_ARM2); } player->armorpoints -= saved; damage -= saved; } player->health -= damage; // mirror mobj health here for Dave // [STRIFE] haleyjd 20130302: bug fix - this is *not* capped here. //if(player->health < 0) // player->health = 0; player->attacker = source; player->damagecount += damage; // add damage after armor / invuln // haleyjd 20110203 [STRIFE]: target->target set here if(target != source) target->target = source; if(player->damagecount > 100) player->damagecount = 100; // teleport stomp does 10k points... temp = damage < 100 ? damage : 100; if(player == &players[consoleplayer]) I_Tactile (40,10,40+temp*2); } // do the damage target->health -= damage; // villsa [STRIFE] auto use medkits if(player && player->health < 50) { if(deathmatch || player->cheats & CF_AUTOHEALTH) { while(player->health < 50 && P_UseInventoryItem(player, SPR_MDKT)); while(player->health < 50 && P_UseInventoryItem(player, SPR_STMP)); } } if(target->health <= 0) { // villsa [STRIFE] grenades hurt... OUCH if(inflictor && inflictor->type == MT_HEGRENADE) target->health = -target->info->spawnhealth; else if(!(target->flags & MF_NOBLOOD)) { // villsa [STRIFE] disintegration death if(inflictor && (inflictor->type == MT_STRIFEPUFF3 || inflictor->type == MT_L_LASER || inflictor->type == MT_TORPEDO || inflictor->type == MT_TORPEDOSPREAD)) { S_StartSound(target, sfx_dsrptr); target->health = -6666; } } // villsa [STRIFE] flame death stuff if(!(target->flags & MF_NOBLOOD) && inflictor && (inflictor->type == MT_SFIREBALL || inflictor->type == MT_C_FLAME || inflictor->type == MT_PFLAME)) { target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SHADOW|MF_MVIS); if(target->player) { target->player->cheats |= CF_ONFIRE; target->player->powers[pw_invisibility] = false; target->player->readyweapon = 0; P_SetPsprite(target->player, ps_weapon, S_WAVE_00); // 02 P_SetPsprite(target->player, ps_flash, S_NULL); } P_SetMobjState(target, S_BURN_00); // 349 S_StartSound(target, sfx_burnme); return; } P_KillMobj(source, target); return; } // villsa [STRIFE] set crash state if(target->health <= 6 && target->info->crashstate) { P_SetMobjState(target, target->info->crashstate); return; } if(damage) { // villsa [STRIFE] removed unused skullfly flag if(P_Random() < target->info->painchance) { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState (target, target->info->painstate); } } target->reactiontime = 0; // we're awake now... // villsa [STRIFE] new checks for thing types if (target->type != MT_PROGRAMMER && (!target->threshold || target->type == MT_ENTITY) && source && source != target && source->type != MT_ENTITY && ((source->flags & MF_ALLY) != (target->flags & MF_ALLY))) { // if not intent on another player, // chase after this one target->target = source; target->threshold = BASETHRESHOLD; if(target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL) P_SetMobjState (target, target->info->seestate); } } crispy-doom-crispy-doom-5.6.4/src/strife/p_inter.h000066400000000000000000000021341360717211000221300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // // #ifndef __P_INTER__ #define __P_INTER__ // haleyjd [STRIFE]: Multiple externals added boolean P_GiveCard(player_t* player, card_t card); boolean P_GiveBody(player_t* player, int num); boolean P_GiveArmor(player_t* player, int armortype); boolean P_GivePower(player_t* player, powertype_t power); boolean P_GiveAmmo(player_t* player, ammotype_t ammo, int num); boolean P_GiveWeapon(player_t* player, weapontype_t weapon, boolean dropped); void P_KillMobj(mobj_t* source, mobj_t* target); #endif crispy-doom-crispy-doom-5.6.4/src/strife/p_lights.c000066400000000000000000000172671360717211000223110ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handle Sector base lighting effects. // Muzzle flash? // #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" // State. #include "r_state.h" // // FIRELIGHT FLICKER // // // T_FireFlicker // // [STRIFE] // haleyjd 2011023: Changes to amount and duration of flicker // void T_FireFlicker (fireflicker_t* flick) { int amount; if (--flick->count) return; amount = (P_Random() & 3) * 8; // [STRIFE] 16 -> 8 if (flick->sector->lightlevel - amount < flick->minlight) flick->sector->lightlevel = flick->minlight; else flick->sector->lightlevel = flick->maxlight - amount; // [STRIFE] flicker count made random! flick->count = (P_Random() & 3) + 1; } // // P_SpawnFireFlicker // // [STRIFE] // haleyjd 2011023: Changes to minimum light level and initial duration // void P_SpawnFireFlicker (sector_t* sector) { fireflicker_t* flick; // Note that we are resetting sector attributes. // Nothing special about it during gameplay. sector->special = 0; flick = Z_Malloc ( sizeof(*flick), PU_LEVSPEC, 0); P_AddThinker (&flick->thinker); flick->thinker.function.acp1 = (actionf_p1) T_FireFlicker; flick->sector = sector; flick->maxlight = sector->lightlevel; flick->minlight = sector->lightlevel - 32; // [STRIFE] changed from min surrounding+16 flick->count = 2; // [STRIFE]: Initial count 4 -> 2 } // // BROKEN LIGHT FLASHING // // // T_LightFlash // Do flashing lights. // // [STRIFE] Verified unmodified // void T_LightFlash (lightflash_t* flash) { if (--flash->count) return; if (flash->sector->lightlevel == flash->maxlight) { flash->sector->lightlevel = flash->minlight; flash->count = (P_Random()&flash->mintime)+1; } else { flash->sector->lightlevel = flash->maxlight; flash->count = (P_Random()&flash->maxtime)+1; } } // // P_SpawnLightFlash // After the map has been loaded, scan each sector // for specials that spawn thinkers // // [STRIFE] Verified unmodified // void P_SpawnLightFlash (sector_t* sector) { lightflash_t* flash; // nothing special about it during gameplay sector->special = 0; flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); P_AddThinker (&flash->thinker); flash->thinker.function.acp1 = (actionf_p1) T_LightFlash; flash->sector = sector; flash->maxlight = sector->lightlevel; flash->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); flash->maxtime = 64; flash->mintime = 7; flash->count = (P_Random()&flash->maxtime)+1; } // // STROBE LIGHT FLASHING // // // T_StrobeFlash // // [STRIFE] Verified unmodified // void T_StrobeFlash (strobe_t* flash) { if (--flash->count) return; if (flash->sector->lightlevel == flash->minlight) { flash-> sector->lightlevel = flash->maxlight; flash->count = flash->brighttime; } else { flash-> sector->lightlevel = flash->minlight; flash->count =flash->darktime; } } // // P_SpawnStrobeFlash // After the map has been loaded, scan each sector // for specials that spawn thinkers // // [STRIFE] Verified unmodified // void P_SpawnStrobeFlash ( sector_t* sector, int fastOrSlow, int inSync ) { strobe_t* flash; flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); P_AddThinker (&flash->thinker); flash->sector = sector; flash->darktime = fastOrSlow; flash->brighttime = STROBEBRIGHT; flash->thinker.function.acp1 = (actionf_p1) T_StrobeFlash; flash->maxlight = sector->lightlevel; flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); if (flash->minlight == flash->maxlight) flash->minlight = 0; // nothing special about it during gameplay sector->special = 0; if (!inSync) flash->count = (P_Random()&7)+1; else flash->count = 1; } // // Start strobing lights (usually from a trigger) // // [STRIFE] Verified unmodified // void EV_StartLightStrobing(line_t* line) { int secnum; sector_t* sec; secnum = -1; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; P_SpawnStrobeFlash (sec, SLOWDARK, 0); } } // // TURN LINE'S TAG LIGHTS OFF // // [STRIFE] Verified unmodified // void EV_TurnTagLightsOff(line_t* line) { int i; int j; int min; sector_t* sector; sector_t* tsec; line_t* templine; sector = sectors; for (j = 0;j < numsectors; j++, sector++) { if (sector->tag == line->tag) { min = sector->lightlevel; for (i = 0;i < sector->linecount; i++) { templine = sector->lines[i]; tsec = getNextSector(templine,sector); if (!tsec) continue; if (tsec->lightlevel < min) min = tsec->lightlevel; } sector->lightlevel = min; } } } // // TURN LINE'S TAG LIGHTS ON // // [STRIFE] Verified unmodified // void EV_LightTurnOn ( line_t* line, int bright ) { int i; int j; sector_t* sector; sector_t* temp; line_t* templine; sector = sectors; for (i=0;itag == line->tag) { // bright = 0 means to search // for highest light level // surrounding sector if (!bright) { for (j = 0;j < sector->linecount; j++) { templine = sector->lines[j]; temp = getNextSector(templine,sector); if (!temp) continue; if (temp->lightlevel > bright) bright = temp->lightlevel; } } sector-> lightlevel = bright; } } } // // Spawn glowing light // // [STRIFE] Verified unmodified // void T_Glow(glow_t* g) { switch(g->direction) { case -1: // DOWN g->sector->lightlevel -= GLOWSPEED; if (g->sector->lightlevel <= g->minlight) { g->sector->lightlevel += GLOWSPEED; g->direction = 1; } break; case 1: // UP g->sector->lightlevel += GLOWSPEED; if (g->sector->lightlevel >= g->maxlight) { g->sector->lightlevel -= GLOWSPEED; g->direction = -1; } break; } } // // [STRIFE] Verified unmodified // void P_SpawnGlowingLight(sector_t* sector) { glow_t* g; g = Z_Malloc( sizeof(*g), PU_LEVSPEC, 0); P_AddThinker(&g->thinker); g->sector = sector; g->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); g->maxlight = sector->lightlevel; g->thinker.function.acp1 = (actionf_p1) T_Glow; g->direction = -1; sector->special = 0; } crispy-doom-crispy-doom-5.6.4/src/strife/p_local.h000066400000000000000000000155501360717211000221070ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Play functions, animation, global header. // #ifndef __P_LOCAL__ #define __P_LOCAL__ #ifndef __R_LOCAL__ #include "r_local.h" #endif #define FLOATSPEED (FRACUNIT*5) // villsa [STRIFE] change to 5 (was 4) #define MAXHEALTH 100 #define VIEWHEIGHT (41*FRACUNIT) // mapblocks are used to check movement // against lines and things #define MAPBLOCKUNITS 128 #define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) #define MAPBLOCKSHIFT (FRACBITS+7) #define MAPBMASK (MAPBLOCKSIZE-1) #define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) // player radius for movement checking #define PLAYERRADIUS 16*FRACUNIT // MAXRADIUS is for precalculated sector block boxes // the spider demon is larger, // but we do not have any moving sectors nearby #define MAXRADIUS 32*FRACUNIT #define GRAVITY FRACUNIT #define MAXMOVE (30*FRACUNIT) #define USERANGE (64*FRACUNIT) #define MELEERANGE (64*FRACUNIT) #define PLAYERMELEERANGE (80*FRACUNIT) // haleyjd [STRIFE] New constant #define MISSILERANGE (32*64*FRACUNIT) // follow a player exlusively for 3 seconds #define BASETHRESHOLD 100 // // P_TICK // // both the head and tail of the thinker list extern thinker_t thinkercap; void P_InitThinkers (void); void P_AddThinker (thinker_t* thinker); void P_RemoveThinker (thinker_t* thinker); // // P_PSPR // void P_SetupPsprites (player_t* curplayer); void P_MovePsprites (player_t* curplayer); void P_DropWeapon (player_t* player); // // P_USER // // haleyjd 09/15/10: externalized #define INVERSECOLORMAP 32 void P_PlayerThink (player_t* player); // haleyjd 08/30/10: [STRIFE] Needed externally void P_Thrust (player_t* player, angle_t angle, fixed_t move); // villsa [STRIFE] const char *P_RemoveInventoryItem(player_t *player, int slot, int amount); // // P_MOBJ // #define ONFLOORZ INT_MIN #define ONCEILINGZ INT_MAX // Time interval for item respawning. #define ITEMQUESIZE 128 extern mapthing_t itemrespawnque[ITEMQUESIZE]; extern int itemrespawntime[ITEMQUESIZE]; extern int iquehead; extern int iquetail; void P_RespawnSpecials (void); mobj_t* P_SpawnMobj ( fixed_t x, fixed_t y, fixed_t z, mobjtype_t type ); void P_RemoveMobj (mobj_t* th); mobj_t* P_SubstNullMobj (mobj_t* th); boolean P_SetMobjState (mobj_t* mobj, statenum_t state); void P_MobjThinker (mobj_t* mobj); void P_SpawnPuff (fixed_t x, fixed_t y, fixed_t z); mobj_t* P_SpawnSparkPuff(fixed_t x, fixed_t y, fixed_t z); // villsa [STRIFE] void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage); mobj_t* P_SpawnMissile (mobj_t* source, mobj_t* dest, mobjtype_t type); mobj_t* P_SpawnFacingMissile(mobj_t* source, mobj_t* target, mobjtype_t type); // villsa [STRIFE] mobj_t* P_SpawnPlayerMissile(mobj_t* source, mobjtype_t type); mobj_t* P_SpawnMortar(mobj_t *source, mobjtype_t type); // villsa [STRIFE] void P_ExplodeMissile (mobj_t* mo); // villsa [STRIFE] // // P_ENEMY // void P_NoiseAlert (mobj_t* target, mobj_t* emmiter); void P_DoPunchAlert(mobj_t *puncher, mobj_t *punchee); // villsa [STRIFE] void A_BodyParts(mobj_t *actor); // haleyjd: [STRIFE] void A_AlertSpectreC(mobj_t* actor); void A_FaceTarget (mobj_t* actor); void P_FreePrisoners(void); void P_DestroyConverter(void); // // P_MAPUTL // typedef struct { fixed_t x; fixed_t y; fixed_t dx; fixed_t dy; } divline_t; typedef struct { fixed_t frac; // along trace line boolean isaline; union { mobj_t* thing; line_t* line; } d; } intercept_t; // Extended MAXINTERCEPTS, to allow for intercepts overrun emulation. #define MAXINTERCEPTS_ORIGINAL 128 #define MAXINTERCEPTS (MAXINTERCEPTS_ORIGINAL + 61) extern intercept_t intercepts[MAXINTERCEPTS]; extern intercept_t* intercept_p; typedef boolean (*traverser_t) (intercept_t *in); fixed_t P_AproxDistance (fixed_t dx, fixed_t dy); int P_PointOnLineSide (fixed_t x, fixed_t y, line_t* line); int P_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t* line); void P_MakeDivline (line_t* li, divline_t* dl); fixed_t P_InterceptVector (divline_t* v2, divline_t* v1); int P_BoxOnLineSide (fixed_t* tmbox, line_t* ld); extern fixed_t opentop; extern fixed_t openbottom; extern fixed_t openrange; extern fixed_t lowfloor; void P_LineOpening (line_t* linedef); boolean P_BlockLinesIterator (int x, int y, boolean(*func)(line_t*) ); boolean P_BlockThingsIterator (int x, int y, boolean(*func)(mobj_t*) ); #define PT_ADDLINES 1 #define PT_ADDTHINGS 2 #define PT_EARLYOUT 4 extern divline_t trace; boolean P_PathTraverse ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean (*trav) (intercept_t *)); void P_UnsetThingPosition (mobj_t* thing); void P_SetThingPosition (mobj_t* thing); // // P_MAP // // If "floatok" true, move would be ok // if within "tmfloorz - tmceilingz". extern boolean floatok; extern fixed_t tmfloorz; extern fixed_t tmceilingz; extern line_t *ceilingline; extern line_t *blockingline; // [STRIFE] New global boolean P_CheckPosition (mobj_t *thing, fixed_t x, fixed_t y); boolean P_TryMove (mobj_t* thing, fixed_t x, fixed_t y); boolean P_CheckPositionZ(mobj_t* thing, fixed_t z); // villsa [STRIFE] boolean P_TeleportMove (mobj_t* thing, fixed_t x, fixed_t y); void P_SlideMove (mobj_t* mo); boolean P_CheckSight (mobj_t* t1, mobj_t* t2); void P_UseLines (player_t* player); boolean P_ChangeSector (sector_t* sector, boolean crunch); extern mobj_t* linetarget; // who got hit (or NULL) fixed_t P_AimLineAttack ( mobj_t* t1, angle_t angle, fixed_t distance ); void P_LineAttack ( mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage ); void P_RadiusAttack ( mobj_t* spot, mobj_t* source, int damage ); // // P_SETUP // extern byte* rejectmatrix; // for fast sight rejection extern short* blockmaplump; // offsets in blockmap are from here extern short* blockmap; extern int bmapwidth; extern int bmapheight; // in mapblocks extern fixed_t bmaporgx; extern fixed_t bmaporgy; // origin of block map extern mobj_t** blocklinks; // for thing chains // // P_INTER // extern int maxammo[NUMAMMO]; extern int clipammo[NUMAMMO]; void P_TouchSpecialThing ( mobj_t* special, mobj_t* toucher ); void P_DamageMobj ( mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage ); // // P_SPEC // #include "p_spec.h" #endif // __P_LOCAL__ crispy-doom-crispy-doom-5.6.4/src/strife/p_map.c000066400000000000000000001204601360717211000215620ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard, Andrey Budko // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Movement, collision handling. // Shooting and aiming. // #include #include #include "deh_misc.h" #include "m_bbox.h" #include "m_random.h" #include "i_system.h" #include "doomdef.h" #include "m_argv.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // Spechit overrun magic value. // // This is the value used by PrBoom-plus. I think the value below is // actually better and works with more demos. However, I think // it's better for the spechits emulation to be compatible with // PrBoom-plus, at least so that the big spechits emulation list // on Doomworld can also be used with Chocolate Doom. #define DEFAULT_SPECHIT_MAGIC 0x01C09C98 // This is from a post by myk on the Doomworld forums, // outputted from entryway's spechit_magic generator for // s205n546.lmp. The _exact_ value of this isn't too // important; as long as it is in the right general // range, it will usually work. Otherwise, we can use // the generator (hacked doom2.exe) and provide it // with -spechit. //#define DEFAULT_SPECHIT_MAGIC 0x84f968e8 fixed_t tmbbox[4]; mobj_t* tmthing; int tmflags; fixed_t tmx; fixed_t tmy; // If "floatok" true, move would be ok // if within "tmfloorz - tmceilingz". boolean floatok; fixed_t tmfloorz; fixed_t tmceilingz; fixed_t tmdropoffz; // keep track of the line that lowers the ceiling, // so missiles don't explode against sky hack walls line_t* ceilingline; // haleyjd 20110203: [STRIFE] New global // "blockingline" tracks the linedef responsible for blocking motion of an mobj // for purposes of doing impact special activation by missiles. Suspiciously // similar to the solution used by Raven in Heretic and Hexen. line_t *blockingline; // keep track of special lines as they are hit, // but don't process them until the move is proven valid // fraggle: I have increased the size of this buffer. In the original Doom, // overrunning past this limit caused other bits of memory to be overwritten, // affecting demo playback. However, in doing so, the limit was still // exceeded. So we have to support more than 8 specials. // // We keep the original limit, to detect what variables in memory were // overwritten (see SpechitOverrun()) #define MAXSPECIALCROSS 20 #define MAXSPECIALCROSS_ORIGINAL 8 line_t* spechit[MAXSPECIALCROSS]; int numspechit; // // TELEPORT MOVE // // // PIT_StompThing // // [STRIFE] haleyjd 09/15/10: Modified so monsters can telestomp. // boolean PIT_StompThing (mobj_t* thing) { fixed_t blockdist; if (!(thing->flags & MF_SHOOTABLE) ) return true; blockdist = thing->radius + tmthing->radius; if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; // [STRIFE] monsters DO stomp things, on all levels // Basically, one thing involved must be a player. // Monsters can telefrag players, and players can telefrag monsters, but // monsters cannot telefrag other monsters. if (!(tmthing->player || thing->player)) return false; P_DamageMobj (thing, tmthing, tmthing, 10000); return true; } // // P_TeleportMove // // [STRIFE] // haleyjd 09/15/10: Modified to set thing z position. // boolean P_TeleportMove(mobj_t* thing, fixed_t x, fixed_t y) { int xl; int xh; int yl; int yh; int bx; int by; subsector_t* newsubsec; // kill anything occupying the position tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector (x,y); ceilingline = NULL; // The base floor/ceiling is from the subsector // that contains the point. // Any contacted lines the step closer together // will adjust them. tmfloorz = tmdropoffz = newsubsec->sector->floorheight; tmceilingz = newsubsec->sector->ceilingheight; validcount++; numspechit = 0; // stomp on any things contacted xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!P_BlockThingsIterator(bx,by,PIT_StompThing)) return false; // the move is ok, // so link the thing into its new position P_UnsetThingPosition (thing); thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; thing->z = tmfloorz; // haleyjd 09/15/10: [STRIFE] Rogue added a z-set here P_SetThingPosition (thing); return true; } // // MOVEMENT ITERATOR FUNCTIONS // static void SpechitOverrun(line_t *ld); // // PIT_CheckLine // Adjusts tmfloorz and tmceilingz as lines are contacted // boolean PIT_CheckLine (line_t* ld) { if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] ) return true; if (P_BoxOnLineSide (tmbbox, ld) != -1) return true; // A line has been hit // The moving thing's destination position will cross // the given line. // If this should not be allowed, return false. // If the line is special, keep track of it // to process later if the move is proven ok. // NOTE: specials are NOT sorted by order, // so two special lines that are only 8 pixels apart // could be crossed in either order. if (!ld->backsector) return false; // one sided line if (!(tmthing->flags & MF_MISSILE) ) { // villsa [STRIFE] include jumpover flag if ( ld->flags & ML_BLOCKING && (!(ld->flags & ML_JUMPOVER) || tmfloorz + (32*FRACUNIT) > tmthing->z) ) return false; // explicitly blocking everything // villsa [STRIFE] exclude floaters from blockmonster lines if ( !tmthing->player && (ld->flags & ML_BLOCKMONSTERS) && !(tmthing->flags & MF_FLOAT)) return false; // block monsters only // villsa [STRIFE] if ( ld->flags & ML_BLOCKFLOATERS && tmthing->flags & MF_FLOAT ) return false; // block floaters only } // set openrange, opentop, openbottom P_LineOpening (ld); // adjust floor / ceiling heights if (opentop < tmceilingz) { tmceilingz = opentop; ceilingline = ld; } if (openbottom > tmfloorz) tmfloorz = openbottom; if (lowfloor < tmdropoffz) tmdropoffz = lowfloor; // if contacted a special line, add it to the list if (ld->special) { spechit[numspechit] = ld; numspechit++; // fraggle: spechits overrun emulation code from prboom-plus if (numspechit > MAXSPECIALCROSS_ORIGINAL) { SpechitOverrun(ld); } } return true; } // // PIT_CheckThing // boolean PIT_CheckThing (mobj_t* thing) { fixed_t blockdist; boolean solid; int damage; if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) )) return true; // don't clip against self if (thing == tmthing) return true; blockdist = thing->radius + tmthing->radius; if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) { // didn't hit it return true; } // villsa [STRIFE] see if it went over / under if(thing->height + thing->z < tmthing->z) return true; // overhead // villsa [STRIFE] see if it went over / under if (tmthing->z + tmthing->height < thing->z) return true; // underneath // villsa [STRIFE] unused // check for skulls slamming into things (removed) // missiles can hit other things if (tmthing->flags & MF_MISSILE) { // villsa [STRIFE] code to check over/under clipping moved to the beginning of the // function, so that it applies to everything. // villsa [STRIFE] updated to strife version if (tmthing->target && (tmthing->target->type == thing->type)) { // Don't hit same species as originator. if (thing == tmthing->target) return true; // sdh: Add deh_species_infighting here. We can override the // "monsters of the same species cant hurt each other" behavior // through dehacked patches if (thing->type != MT_PLAYER && !deh_species_infighting) { // Explode, but do no damage. // Let players missile other players. return false; } } if (!(thing->flags & MF_SHOOTABLE)) { // didn't do any damage return !(thing->flags & MF_SOLID); } // haleyjd 09/01/10: [STRIFE] Spectral check: // Missiles cannot hit SPECTRAL entities unless the missiles are also // flagged as SPECTRAL. if (thing->flags & MF_SPECTRAL && !(tmthing->flags & MF_SPECTRAL)) return true; // keep going // damage / explode // haleyjd 09/01/10: [STRIFE] Modified missile damage formula damage = ((P_Random()&3)+1)*tmthing->info->damage; P_DamageMobj (thing, tmthing, tmthing->target, damage); // don't traverse any more return false; } // check for special pickup if (thing->flags & MF_SPECIAL) { solid = (thing->flags & MF_SOLID) != 0; if (tmthing->player) // villsa [STRIFE] no longer checks MF_PICKUP flag { // can remove thing P_TouchSpecialThing (thing, tmthing); } return !solid; } return !(thing->flags & MF_SOLID); } // // MOVEMENT CLIPPING // // // P_CheckPosition // This is purely informative, nothing is modified // (except things picked up). // // in: // a mobj_t (can be valid or invalid) // a position to be checked // (doesn't need to be related to the mobj_t->x,y) // // during: // special things are touched if MF_PICKUP // early out on solid lines? // // out: // newsubsec // floorz // ceilingz // tmdropoffz // the lowest point contacted // (monsters won't move to a dropoff) // speciallines[] // numspeciallines // // haleyjd 20110203: // [STRIFE] Modified to clear blockingline in advance of P_BlockLinesIterator // boolean P_CheckPosition ( mobj_t* thing, fixed_t x, fixed_t y ) { int xl; int xh; int yl; int yh; int bx; int by; subsector_t* newsubsec; tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; newsubsec = R_PointInSubsector (x,y); // [STRIFE] clear blockingline (see P_XYMovement, P_BlockLinesIterator) blockingline = NULL; ceilingline = NULL; // The base floor / ceiling is from the subsector // that contains the point. // Any contacted lines the step closer together // will adjust them. tmfloorz = tmdropoffz = newsubsec->sector->floorheight; tmceilingz = newsubsec->sector->ceilingheight; validcount++; numspechit = 0; if ( tmflags & MF_NOCLIP ) return true; // Check things first, possibly picking things up. // The bounding box is extended by MAXRADIUS // because mobj_ts are grouped into mapblocks // based on their origin point, and can overlap // into adjacent blocks by up to MAXRADIUS units. xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!P_BlockThingsIterator(bx,by,PIT_CheckThing)) return false; // check lines xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!P_BlockLinesIterator (bx,by,PIT_CheckLine)) return false; return true; } // // P_TryMove // Attempt to move to a new position, // crossing special lines unless MF_TELEPORT is set. // boolean P_TryMove ( mobj_t* thing, fixed_t x, fixed_t y ) { fixed_t oldx; fixed_t oldy; int side; int oldside; line_t* ld; floatok = false; if (!P_CheckPosition (thing, x, y)) return false; // solid wall or thing if ( !(thing->flags & MF_NOCLIP) ) { if (tmceilingz - tmfloorz < thing->height) return false; // doesn't fit floatok = true; // villsa [STRIFE] Removed MF_TELEPORT if (tmceilingz - thing->z < thing->height) return false; // mobj must lower itself to fit // villsa [STRIFE] non-robots are limited to 16 unit step height if ((thing->flags & MF_NOBLOOD) == 0 && tmfloorz - thing->z > (16*FRACUNIT)) return false; if (tmfloorz - thing->z > 24*FRACUNIT) return false; // too big a step up // villsa [STRIFE] special case for missiles if((thing->flags & MF_MISSILE) && tmfloorz - thing->z > 4*FRACUNIT) return false; // haleyjd 20110204 [STRIFE]: dropoff height changed 24 -> 32 if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT)) && tmfloorz - tmdropoffz > 32*FRACUNIT ) return false; // don't stand over a dropoff } // the move is ok, // so link the thing into its new position P_UnsetThingPosition (thing); oldx = thing->x; oldy = thing->y; thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; P_SetThingPosition (thing); // if any special lines were hit, do the effect if (! (thing->flags&MF_NOCLIP) ) // villsa [STRIFE] MF_TELEPORT not used { while (numspechit--) { // see if the line was crossed ld = spechit[numspechit]; side = P_PointOnLineSide (thing->x, thing->y, ld); oldside = P_PointOnLineSide (oldx, oldy, ld); if (side != oldside) { if (ld->special) P_CrossSpecialLine (ld-lines, oldside, thing); } } } return true; } // // P_CheckPositionZ // // villsa [STRIFE] new function // Check colliding things on top of one another; ie., 3D Object Clipping // boolean P_CheckPositionZ(mobj_t* thing, fixed_t height) { fixed_t x; fixed_t y; fixed_t z; int xl; int xh; int yl; int yh; int bx; int by; subsector_t* newsubsec; x = thing->x; y = thing->y; z = thing->z; thing->z = height; tmthing = thing; tmflags = thing->flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing->radius; tmbbox[BOXBOTTOM] = y - tmthing->radius; tmbbox[BOXRIGHT] = x + tmthing->radius; tmbbox[BOXLEFT] = x - tmthing->radius; ceilingline = 0; newsubsec = thing->subsector; // 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; if(tmflags & MF_NOCLIP) return true; // Check things first, possibly picking things up. // The bounding box is extended by MAXRADIUS // because mobj_ts are grouped into mapblocks // based on their origin point, and can overlap // into adjacent blocks by up to MAXRADIUS units. xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; for(bx = xl; bx <= xh; bx++) { for(by = yl; by <= yh; by++) { if(!P_BlockThingsIterator(bx, by, PIT_CheckThing)) { tmthing->z = z; return false; } } } return true; } // // P_ThingHeightClip // Takes a valid thing and adjusts the thing->floorz, // thing->ceilingz, and possibly thing->z. // This is called for all nearby monsters // whenever a sector changes height. // If the thing doesn't fit, // the z will be set to the lowest value // and false will be returned. // // [STRIFE] Verified unmodified // boolean P_ThingHeightClip (mobj_t* thing) { boolean onfloor; onfloor = (thing->z == thing->floorz); P_CheckPosition (thing, thing->x, thing->y); // what about stranding a monster partially off an edge? thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; if (onfloor) { // walking monsters rise and fall with the floor thing->z = thing->floorz; } else { // don't adjust a floating monster unless forced to if (thing->z+thing->height > thing->ceilingz) thing->z = thing->ceilingz - thing->height; } if (thing->ceilingz - thing->floorz < thing->height) return false; return true; } // // SLIDE MOVE // Allows the player to slide along any angled walls. // fixed_t bestslidefrac; fixed_t secondslidefrac; line_t* bestslideline; line_t* secondslideline; mobj_t* slidemo; fixed_t tmxmove; fixed_t tmymove; // // P_HitSlideLine // Adjusts the xmove / ymove // so that the next move will slide along the wall. // // [STRIFE] Verified unmodified // void P_HitSlideLine (line_t* ld) { int side; angle_t lineangle; angle_t moveangle; angle_t deltaangle; fixed_t movelen; fixed_t newlen; if (ld->slopetype == ST_HORIZONTAL) { tmymove = 0; return; } if (ld->slopetype == ST_VERTICAL) { tmxmove = 0; return; } side = P_PointOnLineSide (slidemo->x, slidemo->y, ld); lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy); if (side == 1) lineangle += ANG180; moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove); deltaangle = moveangle-lineangle; if (deltaangle > ANG180) deltaangle += ANG180; // I_Error ("SlideLine: ang>ANG180"); lineangle >>= ANGLETOFINESHIFT; deltaangle >>= ANGLETOFINESHIFT; movelen = P_AproxDistance (tmxmove, tmymove); newlen = FixedMul (movelen, finecosine[deltaangle]); tmxmove = FixedMul (newlen, finecosine[lineangle]); tmymove = FixedMul (newlen, finesine[lineangle]); } // // PTR_SlideTraverse // // [STRIFE] Modified for smaller step-up height // boolean 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)) { // don't hit the back side return true; } goto isblocking; } // set openrange, opentop, openbottom P_LineOpening (li); if (openrange < slidemo->height) goto isblocking; // doesn't fit if (opentop - slidemo->z < slidemo->height) goto isblocking; // mobj is too high // villsa [STRIFE] change from 24 to 16 if (openbottom - slidemo->z > 16*FRACUNIT ) goto isblocking; // too big a step up // this line doesn't block movement return true; // the line does block movement, // see if it is closer than best so far isblocking: if (in->frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in->frac; bestslideline = li; } return false; // stop } // // P_SlideMove // The momx / momy move is bad, so try to slide // along a wall. // Find the first line hit, move flush to it, // and slide along it // // This is a kludgy mess. // // [STRIFE] Verified unmodified // void P_SlideMove (mobj_t* mo) { fixed_t leadx; fixed_t leady; fixed_t trailx; fixed_t traily; fixed_t newx; fixed_t newy; int hitcount; slidemo = mo; hitcount = 0; retry: if (++hitcount == 3) goto stairstep; // don't loop forever // trace along the three leading corners if (mo->momx > 0) { leadx = mo->x + mo->radius; trailx = mo->x - mo->radius; } else { leadx = mo->x - mo->radius; trailx = mo->x + mo->radius; } if (mo->momy > 0) { leady = mo->y + mo->radius; traily = mo->y - mo->radius; } else { leady = mo->y - mo->radius; traily = mo->y + mo->radius; } bestslidefrac = FRACUNIT+1; P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy, PT_ADDLINES, PTR_SlideTraverse ); P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy, PT_ADDLINES, PTR_SlideTraverse ); P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy, PT_ADDLINES, PTR_SlideTraverse ); // move up to the wall if (bestslidefrac == FRACUNIT+1) { // the move most have hit the middle, so stairstep stairstep: if (!P_TryMove (mo, mo->x, mo->y + mo->momy)) P_TryMove (mo, mo->x + mo->momx, mo->y); return; } // fudge a bit to make sure it doesn't hit bestslidefrac -= 0x800; if (bestslidefrac > 0) { newx = FixedMul (mo->momx, bestslidefrac); newy = FixedMul (mo->momy, bestslidefrac); if (!P_TryMove (mo, mo->x+newx, mo->y+newy)) goto stairstep; } // Now continue along the wall. // First calculate remainder. bestslidefrac = FRACUNIT-(bestslidefrac+0x800); if (bestslidefrac > FRACUNIT) bestslidefrac = FRACUNIT; if (bestslidefrac <= 0) return; tmxmove = FixedMul (mo->momx, bestslidefrac); tmymove = FixedMul (mo->momy, bestslidefrac); P_HitSlideLine (bestslideline); // clip the moves mo->momx = tmxmove; mo->momy = tmymove; if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove)) { goto retry; } } // // P_LineAttack // mobj_t* linetarget; // who got hit (or NULL) mobj_t* shootthing; // Height if not aiming up or down // ???: use slope for monsters? fixed_t shootz; int la_damage; fixed_t attackrange; fixed_t aimslope; // slopes to top and bottom of target extern fixed_t topslope; extern fixed_t bottomslope; // // PTR_AimTraverse // Sets linetaget and aimslope when a target is aimed at. // // [STRIFE] Verified unmodified // boolean PTR_AimTraverse (intercept_t* in) { line_t* li; mobj_t* th; fixed_t slope; fixed_t thingtopslope; fixed_t thingbottomslope; fixed_t dist; if (in->isaline) { li = in->d.line; if ( !(li->flags & ML_TWOSIDED) ) return false; // stop // Crosses a two sided line. // A two sided line will restrict // the possible target ranges. P_LineOpening (li); if (openbottom >= opentop) return false; // stop dist = FixedMul (attackrange, in->frac); // Return false if there is no back sector. This should never // be the case if the line is two-sided; however, some WADs // (eg. ottawau.wad) use this as an "impassible glass" trick // and rely on Vanilla Doom's (unintentional) support for this. if (li->backsector == NULL) { return false; } if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > bottomslope) bottomslope = slope; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv (opentop - shootz , dist); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop return true; // shot continues } // shoot a thing th = in->d.thing; if (th == shootthing) return true; // can't shoot self if (!(th->flags&MF_SHOOTABLE)) return true; // corpse or something // check angles to see if the thing can be aimed at dist = FixedMul (attackrange, in->frac); thingtopslope = FixedDiv (th->z+th->height - shootz , dist); if (thingtopslope < bottomslope) return true; // shot over the thing thingbottomslope = FixedDiv (th->z - shootz, dist); if (thingbottomslope > topslope) return true; // shot under the thing // this thing can be hit! if (thingtopslope > topslope) thingtopslope = topslope; if (thingbottomslope < bottomslope) thingbottomslope = bottomslope; aimslope = (thingtopslope+thingbottomslope)/2; linetarget = th; return false; // don't go any farther } // // PTR_ShootTraverse // // [STRIFE] Changes for Spectres and Mauler puff/damage inflictor // boolean PTR_ShootTraverse (intercept_t* in) { fixed_t x; fixed_t y; fixed_t z; fixed_t frac; line_t* li; mobj_t* th; mobj_t* th2; // villsa [STRIFE] fixed_t slope; fixed_t dist; fixed_t thingtopslope; fixed_t thingbottomslope; if (in->isaline) { li = in->d.line; if (li->special) P_ShootSpecialLine (shootthing, li); if ( !(li->flags & ML_TWOSIDED) ) goto hitline; // crosses a two sided line P_LineOpening (li); dist = FixedMul (attackrange, in->frac); // Check if backsector is NULL. See comment in PTR_AimTraverse. if (li->backsector == NULL) { goto hitline; } if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > aimslope) goto hitline; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv (opentop - shootz , dist); if (slope < aimslope) goto hitline; } // shot continues return true; // hit line hitline: // position a bit closer frac = in->frac - FixedDiv (4*FRACUNIT,attackrange); x = trace.x + FixedMul (trace.dx, frac); y = trace.y + FixedMul (trace.dy, frac); z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); if (li->frontsector->ceilingpic == skyflatnum) { // don't shoot the sky! if (z > li->frontsector->ceilingheight) return false; // it's a sky hack wall if (li->backsector && li->backsector->ceilingpic == skyflatnum) return false; } // villsa [STRIFE] if(la_damage > 0) { // villsa [STRIFE] Test against Mauler attack range if(attackrange != 2112*FRACUNIT) P_SpawnPuff(x, y, z); // Spawn bullet puffs. else P_SpawnMobj(x, y, z, MT_STRIFEPUFF3); } // 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 // haleyjd 09/18/10: [STRIFE] Corrected - not MVIS, but SPECTRAL. if(th->flags & MF_SPECTRAL) return true; // is a spectral entity // 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)); // villsa [STRIFE] Check for Mauler attack range if(attackrange == 2112*FRACUNIT) { th2 = P_SpawnMobj(x, y, z, MT_STRIFEPUFF3); th2->momz = -FRACUNIT; P_DamageMobj(th, th2, shootthing, la_damage); return false; } // villsa [STRIFE] disabled check for damage //if (la_damage) P_DamageMobj (th, shootthing, shootthing, la_damage); // Spawn bullet puffs or blod spots, // depending on target type. if (in->d.thing->flags & MF_NOBLOOD) P_SpawnSparkPuff(x, y, z); // villsa [STRIFE] call spark puff function instead else P_SpawnBlood (x,y,z, la_damage); // don't go any farther return false; } // // P_AimLineAttack // // [STRIFE] Modified to support player->pitch // fixed_t P_AimLineAttack ( mobj_t* t1, angle_t angle, fixed_t distance ) { 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; P_PathTraverse ( t1->x, t1->y, x2, y2, PT_ADDLINES|PT_ADDTHINGS, PTR_AimTraverse ); if (linetarget) return aimslope; else // villsa [STRIFE] checks for player pitch { if(t1->player) return (t1->player->pitch << FRACBITS) / 160; } return 0; } // // P_LineAttack // If damage == 0, it is just a test trace // that will leave linetarget set. // // [STRIFE] Modified to check lines only if damage <= 0 (see P_RadiusAttack) // void P_LineAttack ( mobj_t* t1, angle_t angle, fixed_t distance, fixed_t slope, int damage ) { fixed_t x2; fixed_t y2; int traverseflags; 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; // villsa [STRIFE] test lines only if damage is <= 0 if(damage >= 1) traverseflags = (PT_ADDLINES|PT_ADDTHINGS); else traverseflags = PT_ADDLINES; P_PathTraverse(t1->x, t1->y, x2, y2, traverseflags, PTR_ShootTraverse); } // // USE LINES // // [STRIFE] Verified unmodified // mobj_t* usething; boolean PTR_UseTraverse (intercept_t* in) { int side; if (!in->d.line->special) { P_LineOpening (in->d.line); if (openrange <= 0) { S_StartSound (usething, sfx_noway); // can't use through a wall return false; } // not a special line, but keep checking return true ; } side = 0; if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) side = 1; // return false; // don't use back side P_UseSpecialLine (usething, in->d.line, side); // can't use for than one special line in a row return false; } // // P_UseLines // Looks for special lines in front of the player to activate. // // [STRIFE] Verified unmodified // void P_UseLines (player_t* player) { int angle; fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; usething = player->mo; angle = player->mo->angle >> ANGLETOFINESHIFT; x1 = player->mo->x; y1 = player->mo->y; x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); } // // RADIUS ATTACK // mobj_t* bombsource; mobj_t* bombspot; int bombdamage; // // PIT_RadiusAttack // "bombsource" is the creature // that caused the explosion at "bombspot". // // [STRIFE] Modified for Spectral and Inquisitor exclusions // boolean PIT_RadiusAttack (mobj_t* thing) { fixed_t dx; fixed_t dy; fixed_t dist; if (!(thing->flags & MF_SHOOTABLE)) return true; // haleyjd 10/04/10: Spectrals are not damaged by blast radii if(thing->flags & MF_SPECTRAL) return true; // Boss spider and cyborg // take no damage from concussion. // villsa [STRIFE] unused // - haleyjd: INQUISITOR if(thing->type == MT_INQUISITOR) return true; dx = abs(thing->x - bombspot->x); dy = abs(thing->y - bombspot->y); dist = dx>dy ? dx : dy; dist = (dist - thing->radius) >> FRACBITS; if (dist < 0) dist = 0; if (dist >= bombdamage) return true; // out of range if ( P_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. // // [STRIFE] Modified to emit "test" tracers which can shatter glass screens // and windows. // 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)>>MAPBLOCKSHIFT; yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT; xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT; xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT; bombspot = spot; bombsource = source; bombdamage = damage; for (y=yl ; y<=yh ; y++) for (x=xl ; x<=xh ; x++) P_BlockThingsIterator (x, y, PIT_RadiusAttack ); // villsa [STRIFE] Send out 0 damage tracers to shatter nearby glass. spot->z += 32*FRACUNIT; P_LineAttack(spot, 0, dist, 1, 0); P_LineAttack(spot, ANG90, dist, 1, 0); P_LineAttack(spot, ANG180, dist, 1, 0); P_LineAttack(spot, ANG270, dist, 1, 0); spot->z -= 32*FRACUNIT; } // // 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. // boolean crushchange; boolean nofit; // // PIT_ChangeSector // // [STRIFE] Changes to crushing behavior // boolean PIT_ChangeSector (mobj_t* thing) { mobj_t* mo; if (P_ThingHeightClip (thing)) { // keep checking return true; } // crunch bodies to giblets if (thing->health <= 0) { // villsa [STRIFE] do something with the player if(thing->player && thing->subsector->sector->specialdata) { nofit = true; return false; } //P_SetMobjState (thing, S_GIBS); // villsa [STRIFE] unused A_BodyParts(thing); // villsa [STRIFE] spit out meat/junk stuff thing->flags &= ~MF_SOLID; thing->height = 0; thing->radius = 0; // keep checking return true; } // crunch dropped items if (thing->flags & MF_DROPPED) { P_RemoveMobj (thing); // keep checking return true; } if (! (thing->flags & MF_SHOOTABLE) ) { // assume it is bloody gibs or something return true; } nofit = true; if (crushchange && !(leveltime&3) ) { int t; S_StartSound(thing, sfx_pcrush); // villsa [STRIFE] 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_DEATH); t = P_Random(); mo->momx = (t - P_Random ()) << 12; t = P_Random(); mo->momy = (t - P_Random ()) << 12; } // keep checking (crush other things) return true; } // // P_ChangeSector // // [STRIFE] Verified unmodified // boolean P_ChangeSector ( sector_t* sector, boolean crunch ) { int x; int y; nofit = false; crushchange = crunch; // re-check heights for all things near the moving sector for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++) for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++) P_BlockThingsIterator (x, y, PIT_ChangeSector); return nofit; } // Code to emulate the behavior of Vanilla Doom when encountering an overrun // of the spechit array. This is by Andrey Budko (e6y) and comes from his // PrBoom plus port. A big thanks to Andrey for this. static void SpechitOverrun(line_t *ld) { static unsigned int baseaddr = 0; unsigned int addr; if (baseaddr == 0) { int p; // This is the first time we have had an overrun. Work out // what base address we are going to use. // Allow a spechit value to be specified on the command line. //! // @category compat // @arg // // Use the specified magic value when emulating spechit overruns. // p = M_CheckParmWithArgs("-spechit", 1); if (p > 0) { M_StrToInt(myargv[p+1], (int *) &baseaddr); } else { baseaddr = DEFAULT_SPECHIT_MAGIC; } } // Calculate address used in doom2.exe addr = baseaddr + (ld - lines) * 0x3E; switch(numspechit) { case 9: case 10: case 11: case 12: tmbbox[numspechit-9] = addr; break; case 13: nofit = addr; // haleyjd 20110204: nofit/crushchange are in opposite break; // order in the Strife binary. case 14: crushchange = addr; break; default: fprintf(stderr, "SpechitOverrun: Warning: unable to emulate" "an overrun where numspechit=%i\n", numspechit); break; } } crispy-doom-crispy-doom-5.6.4/src/strife/p_maputl.c000066400000000000000000000554401360717211000223140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2005, 2006 Andrey Budko // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Movement/collision utility functions, // as used by function in p_map.c. // BLOCKMAP Iterator functions, // and some PIT_* functions to use for iteration. // #include #include "m_bbox.h" #include "doomdef.h" #include "doomstat.h" #include "p_local.h" // State. #include "r_state.h" // // P_AproxDistance // Gives an estimation of distance (not exact) // // [STRIFE] Verified unmodified // fixed_t P_AproxDistance ( fixed_t dx, fixed_t dy ) { dx = abs(dx); dy = abs(dy); if (dx < dy) return dx+dy-(dx>>1); return dx+dy-(dy>>1); } // // P_PointOnLineSide // Returns 0 or 1 // // [STRIFE] Verified unmodified // int P_PointOnLineSide ( fixed_t x, fixed_t y, line_t* line ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!line->dx) { if (x <= line->v1->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->v1->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->v1->x); dy = (y - line->v1->y); left = FixedMul ( line->dy>>FRACBITS , dx ); right = FixedMul ( dy , line->dx>>FRACBITS ); if (right < left) return 0; // front side return 1; // back side } // // P_BoxOnLineSide // Considers the line to be infinite // Returns side 0 or 1, -1 if box crosses the line. // // [STRIFE] Verified unmodified // int P_BoxOnLineSide ( fixed_t* tmbox, line_t* ld ) { int p1 = 0; int p2 = 0; switch (ld->slopetype) { case ST_HORIZONTAL: p1 = tmbox[BOXTOP] > ld->v1->y; p2 = tmbox[BOXBOTTOM] > ld->v1->y; if (ld->dx < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_VERTICAL: p1 = tmbox[BOXRIGHT] < ld->v1->x; p2 = tmbox[BOXLEFT] < ld->v1->x; if (ld->dy < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_POSITIVE: p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); break; case ST_NEGATIVE: p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); break; } if (p1 == p2) return p1; return -1; } // // P_PointOnDivlineSide // Returns 0 or 1. // // [STRIFE] Verified unmodified // int P_PointOnDivlineSide ( fixed_t x, fixed_t y, divline_t* line ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!line->dx) { if (x <= line->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->x); dy = (y - line->y); // try to quickly decide by looking at sign bits if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 ) { if ( (line->dy ^ dx) & 0x80000000 ) return 1; // (left is negative) return 0; } left = FixedMul ( line->dy>>8, dx>>8 ); right = FixedMul ( dy>>8 , line->dx>>8 ); if (right < left) return 0; // front side return 1; // back side } // // P_MakeDivline // void P_MakeDivline ( line_t* li, divline_t* dl ) { dl->x = li->v1->x; dl->y = li->v1->y; dl->dx = li->dx; dl->dy = li->dy; } // // P_InterceptVector // Returns the fractional intercept point // along the first divline. // This is only called by the addthings // and addlines traversers. // // [STRIFE] Verified unmodified // fixed_t P_InterceptVector ( divline_t* v2, divline_t* v1 ) { #if 1 fixed_t frac; fixed_t num; fixed_t den; den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy ) +FixedMul ( (v2->y - v1->y)>>8, v1->dx ); frac = FixedDiv (num , den); return frac; #else // UNUSED, float debug. float frac; float num; float den; float v1x; float v1y; float v1dx; float v1dy; float v2x; float v2y; float v2dx; float v2dy; v1x = (float)v1->x/FRACUNIT; v1y = (float)v1->y/FRACUNIT; v1dx = (float)v1->dx/FRACUNIT; v1dy = (float)v1->dy/FRACUNIT; v2x = (float)v2->x/FRACUNIT; v2y = (float)v2->y/FRACUNIT; v2dx = (float)v2->dx/FRACUNIT; v2dy = (float)v2->dy/FRACUNIT; den = v1dy*v2dx - v1dx*v2dy; if (den == 0) return 0; // parallel num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; frac = num / den; return frac*FRACUNIT; #endif } // // P_LineOpening // Sets opentop and openbottom to the window // through a two sided line. // OPTIMIZE: keep this precalculated // // [STRIFE] Verified unmodified // fixed_t opentop; fixed_t openbottom; fixed_t openrange; fixed_t lowfloor; void P_LineOpening (line_t* linedef) { sector_t* front; sector_t* back; if (linedef->sidenum[1] == -1) { // single sided line openrange = 0; return; } front = linedef->frontsector; back = linedef->backsector; if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; if (front->floorheight > back->floorheight) { openbottom = front->floorheight; lowfloor = back->floorheight; } else { openbottom = back->floorheight; lowfloor = front->floorheight; } openrange = opentop - openbottom; } // // THING POSITION SETTING // // // P_UnsetThingPosition // Unlinks a thing from block map and sectors. // On each position change, BLOCKMAP and other // lookups maintaining lists ot things inside // these structures need to be updated. // // [STRIFE] Verified unmodified // void P_UnsetThingPosition (mobj_t* thing) { int blockx; int blocky; if ( ! (thing->flags & MF_NOSECTOR) ) { // inert things don't need to be in blockmap? // unlink from subsector if (thing->snext) thing->snext->sprev = thing->sprev; if (thing->sprev) thing->sprev->snext = thing->snext; else thing->subsector->sector->thinglist = thing->snext; } if ( ! (thing->flags & MF_NOBLOCKMAP) ) { // inert things don't need to be in blockmap // unlink from block map if (thing->bnext) thing->bnext->bprev = thing->bprev; if (thing->bprev) thing->bprev->bnext = thing->bnext; else { blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky bnext; } } } } // // P_SetThingPosition // Links a thing into both a block and a subsector // based on it's x y. // Sets thing->subsector properly // // [STRIFE] Verified unmodified // void P_SetThingPosition (mobj_t* thing) { subsector_t* ss; sector_t* sec; int blockx; int blocky; mobj_t** link; // link into subsector ss = R_PointInSubsector (thing->x,thing->y); thing->subsector = ss; if ( ! (thing->flags & MF_NOSECTOR) ) { // invisible things don't go into the sector links sec = ss->sector; thing->sprev = NULL; thing->snext = sec->thinglist; if (sec->thinglist) sec->thinglist->sprev = thing; sec->thinglist = thing; } // link into blockmap if ( ! (thing->flags & MF_NOBLOCKMAP) ) { // inert things don't need to be in blockmap blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky < bmapheight) { link = &blocklinks[blocky*bmapwidth+blockx]; thing->bprev = NULL; thing->bnext = *link; if (*link) (*link)->bprev = thing; *link = thing; } else { // thing is off the map thing->bnext = thing->bprev = NULL; } } } // // BLOCK MAP ITERATORS // For each line/thing in the given mapblock, // call the passed PIT_* function. // If the function returns false, // exit with false without checking anything else. // // // P_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. // // haleyjd 20110203: // [STRIFE] Modified to track blockingline // boolean P_BlockLinesIterator ( int x, int y, boolean(*func)(line_t*) ) { int offset; short* list; line_t* ld; if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) { return true; } offset = y*bmapwidth+x; offset = *(blockmap+offset); for ( list = blockmaplump+offset ; *list != -1 ; list++) { ld = &lines[*list]; // [STRIFE]: set blockingline (see P_XYMovement @ p_mobj.c) blockingline = ld; 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 // // [STRIFE] Verified unmodified // boolean P_BlockThingsIterator ( int x, int y, boolean(*func)(mobj_t*) ) { mobj_t* mobj; if ( x<0 || y<0 || x>=bmapwidth || y>=bmapheight) { return true; } for (mobj = blocklinks[y*bmapwidth+x] ; mobj ; mobj = mobj->bnext) { if (!func( mobj ) ) return false; } return true; } // // INTERCEPT ROUTINES // intercept_t intercepts[MAXINTERCEPTS]; intercept_t* intercept_p; divline_t trace; boolean earlyout; int ptflags; //static void InterceptsOverrun(int num_intercepts, intercept_t *intercept); // // PIT_AddLineIntercepts. // Looks for lines in the given block // that intercept the given trace // to add to the intercepts list. // // A line is crossed if its endpoints // are on opposite sides of the trace. // Returns true if earlyout and a solid line hit. // // haleyjd 20110204 [STRIFE]: Added Rogue's fix for intercepts overflows // boolean PIT_AddLineIntercepts (line_t* ld) { int s1; int s2; fixed_t frac; divline_t dl; // avoid precision problems with two routines if ( trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16 || trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16) { s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); } else { s1 = P_PointOnLineSide (trace.x, trace.y, ld); s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld); } if (s1 == s2) return true; // line isn't crossed // hit the line P_MakeDivline (ld, &dl); frac = P_InterceptVector (&trace, &dl); if (frac < 0) return true; // behind source // try to early out the check if (earlyout && frac < FRACUNIT && !ld->backsector) { return false; // stop checking } intercept_p->frac = frac; intercept_p->isaline = true; intercept_p->d.line = ld; intercept_p++; // haleyjd 20110204 [STRIFE] // Evidently Rogue had trouble with intercepts overflows during // development, as they added this check here which will stop adding // intercepts if the array would be overflown. if(intercept_p <= &intercepts[MAXINTERCEPTS_ORIGINAL-2]) return true; // continue else return false; // [STRIFE] Not needed? //InterceptsOverrun(intercept_p - intercepts, intercept_p); } // // PIT_AddThingIntercepts // boolean PIT_AddThingIntercepts (mobj_t* thing) { fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; int s1; int s2; boolean tracepositive; divline_t dl; fixed_t frac; tracepositive = (trace.dx ^ trace.dy)>0; // check a corner to corner crossection for hit if (tracepositive) { x1 = thing->x - thing->radius; y1 = thing->y + thing->radius; x2 = thing->x + thing->radius; y2 = thing->y - thing->radius; } else { x1 = thing->x - thing->radius; y1 = thing->y - thing->radius; x2 = thing->x + thing->radius; y2 = thing->y + thing->radius; } s1 = P_PointOnDivlineSide (x1, y1, &trace); s2 = P_PointOnDivlineSide (x2, y2, &trace); if (s1 == s2) return true; // line isn't crossed dl.x = x1; dl.y = y1; dl.dx = x2-x1; dl.dy = y2-y1; frac = P_InterceptVector (&trace, &dl); if (frac < 0) return true; // behind source intercept_p->frac = frac; intercept_p->isaline = false; intercept_p->d.thing = thing; intercept_p++; // haleyjd 20110204 [STRIFE]: As above, protection against intercepts // overflows, courtesy of Rogue Software. if(intercept_p <= &intercepts[MAXINTERCEPTS_ORIGINAL-2]) return true; // keep going else return false; // haleyjd [STRIFE]: Not needed? //InterceptsOverrun(intercept_p - intercepts, intercept_p); } // // P_TraverseIntercepts // Returns true if the traverser function returns true // for all lines. // // [STRIFE] Verified unmodified. // boolean P_TraverseIntercepts ( traverser_t func, fixed_t maxfrac ) { int count; fixed_t dist; intercept_t* scan; intercept_t* in; count = intercept_p - intercepts; in = 0; // shut up compiler warning while (count--) { dist = INT_MAX; for (scan = intercepts ; scanfrac < dist) { dist = scan->frac; in = scan; } } if (dist > maxfrac) return true; // checked everything in range #if 0 // UNUSED { // don't check these yet, there may be others inserted in = scan = intercepts; for ( scan = intercepts ; scanfrac > maxfrac) *in++ = *scan; intercept_p = in; return false; } #endif if ( !func (in) ) return false; // don't bother going farther in->frac = INT_MAX; } return true; // everything was traversed } extern fixed_t bulletslope; #if 0 // Intercepts Overrun emulation, from PrBoom-plus. // Thanks to Andrey Budko (entryway) for researching this and his // implementation of Intercepts Overrun emulation in PrBoom-plus // which this is based on. typedef struct { int len; void *addr; boolean int16_array; } intercepts_overrun_t; // 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. // haleyjd 20110204: NB: This array has *not* been updated for Strife, // because Strife has protection against intercepts overflows. The memory // layout of the 1.2 and 1.31 EXEs is radically different with respect // to this area of the BSS segment, so it would have to be redone entirely // if it were needed. static 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}, }; // 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; } } // Emulate overruns of the intercepts[] array. static void InterceptsOverrun(int num_intercepts, intercept_t *intercept) { int location; if (num_intercepts <= MAXINTERCEPTS_ORIGINAL) { // No overrun return; } 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) intercept->d.thing); } #endif // // 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. // // [STRIFE] Verified unmodified // boolean P_PathTraverse ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean (*trav) (intercept_t *)) { fixed_t xt1; fixed_t yt1; fixed_t xt2; fixed_t yt2; fixed_t xstep; fixed_t ystep; fixed_t partial; fixed_t xintercept; fixed_t yintercept; int mapx; int mapy; int mapxstep; int mapystep; int count; earlyout = (flags & PT_EARLYOUT) != 0; validcount++; intercept_p = intercepts; if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) y1 += FRACUNIT; // don't side exactly on a line trace.x = x1; trace.y = y1; trace.dx = x2 - x1; trace.dy = y2 - y1; x1 -= bmaporgx; y1 -= bmaporgy; xt1 = x1>>MAPBLOCKSHIFT; yt1 = y1>>MAPBLOCKSHIFT; x2 -= bmaporgx; y2 -= bmaporgy; xt2 = x2>>MAPBLOCKSHIFT; yt2 = y2>>MAPBLOCKSHIFT; if (xt2 > xt1) { mapxstep = 1; partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); ystep = 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 (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 ); } crispy-doom-crispy-doom-5.6.4/src/strife/p_mobj.c000066400000000000000000001024111360717211000217300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Moving object handling. Spawn functions. // #include #include "i_system.h" #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" #include "sounds.h" #include "st_stuff.h" #include "hu_stuff.h" #include "s_sound.h" #include "doomstat.h" #include "d_main.h" // villsa [STRIFE] extern line_t *spechit[]; // haleyjd: extern int numspechit; // [STRIFE] - needed in P_XYMovement void G_PlayerReborn (int player); void P_SpawnMapThing (mapthing_t* mthing); // // P_SetMobjState // Returns true if the mobj is still present. // // [STRIFE] Verified unmodified // int test; boolean P_SetMobjState ( mobj_t* mobj, statenum_t state ) { state_t* st; do { if (state == S_NULL) { mobj->state = (state_t *) S_NULL; P_RemoveMobj (mobj); return false; } st = &states[state]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // Modified handling. // Call action functions when the state is set if (st->action.acp1) st->action.acp1(mobj); state = st->nextstate; } while (!mobj->tics); return true; } // // P_ExplodeMissile // // [STRIFE] Removed randomization of deathstate tics // void P_ExplodeMissile (mobj_t* mo) { mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, mobjinfo[mo->type].deathstate); // villsa [STRIFE] removed tics randomization mo->flags &= ~MF_MISSILE; if (mo->info->deathsound) S_StartSound (mo, mo->info->deathsound); } // // P_XYMovement // // [STRIFE] Modifications for: // * No SKULLFLY logic (replaced by BOUNCE flag) // * Missiles can activate G1/GR line types // * Player walking logic // * Air friction for players // #define STOPSPEED 0x1000 #define FRICTION 0xe800 #define AIRFRICTION 0xfff0 // [STRIFE] void P_XYMovement (mobj_t* mo) { fixed_t ptryx; fixed_t ptryy; player_t* player; fixed_t xmove; fixed_t ymove; // villsa [STRIFE] unused /* if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, mo->info->spawnstate); } return; } */ player = mo->player; if (mo->momx > MAXMOVE) mo->momx = MAXMOVE; else if (mo->momx < -MAXMOVE) mo->momx = -MAXMOVE; if (mo->momy > MAXMOVE) mo->momy = MAXMOVE; else if (mo->momy < -MAXMOVE) mo->momy = -MAXMOVE; xmove = mo->momx; ymove = mo->momy; do { if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2) { ptryx = mo->x + xmove/2; ptryy = mo->y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } if (!P_TryMove (mo, ptryx, ptryy)) { // blocked move if (mo->player) { // try to slide along it P_SlideMove (mo); } // villsa [STRIFE] check for bouncy missiles else if(mo->flags & MF_BOUNCE) { mo->momx >>= 3; mo->momy >>= 3; if (P_TryMove(mo, mo->x - xmove, ymove + mo->y)) mo->momy = -mo->momy; else mo->momx = -mo->momx; xmove = 0; ymove = 0; } else if (mo->flags & MF_MISSILE) { // haley 20110203: [STRIFE] // This modification allows missiles to activate shoot specials. // *** BUG: In vanilla Strife the second condition is simply // if(numspechit). However, numspechit can be negative, and // when it is, this accesses spechit[-2]. This always causes the // DOS exe to read from NULL, and the 'special' value there (in // DOS 6.22 at least) is 0x70, which does nothing. if(blockingline && blockingline->special) P_ShootSpecialLine(mo, blockingline); if(numspechit > 0) P_ShootSpecialLine(mo, spechit[numspechit-1]); // explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. P_RemoveMobj (mo); return; } P_ExplodeMissile (mo); } else mo->momx = mo->momy = 0; } } while (xmove || ymove); // slow down if (player && player->cheats & CF_NOMOMENTUM) { // debug option for no sliding at all mo->momx = mo->momy = 0; return; } // villsa [STRIFE] replace skullfly flag with MF_BOUNCE if (mo->flags & (MF_MISSILE | MF_BOUNCE) ) return; // no friction for missiles ever // haleyjd 20110224: [STRIFE] players experience friction even in the air, // although less than when on the ground. With this fix, the 1.2-and-up // IWAD demo is now in sync! if (mo->z > mo->floorz) { if(player) { mo->momx = FixedMul (mo->momx, AIRFRICTION); mo->momy = FixedMul (mo->momy, AIRFRICTION); } return; // no friction when airborne } if (mo->flags & MF_CORPSE) { // do not stop sliding // if halfway off a step with some momentum if (mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 || mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) { if (mo->floorz != mo->subsector->sector->floorheight) return; } } if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && mo->momy > -STOPSPEED && mo->momy < STOPSPEED && (!player || (player->cmd.forwardmove == 0 && player->cmd.sidemove == 0 ) ) ) { // if in a walking frame, stop moving // villsa [STRIFE]: different player state (haleyjd - verified 20110202) if ( player&&(unsigned)((player->mo->state - states) - S_PLAY_01) < 4) P_SetMobjState (player->mo, S_PLAY_00); mo->momx = 0; mo->momy = 0; } else { mo->momx = FixedMul (mo->momx, FRICTION); mo->momy = FixedMul (mo->momy, FRICTION); } } // // P_ZMovement // // [STRIFE] Modifications for: // * 3D Object Clipping // * Different momz handling // * No SKULLFLY logic (replaced with BOUNCE) // * Missiles don't hit sky flats // void P_ZMovement (mobj_t* mo) { fixed_t dist; fixed_t delta; // check for smooth step up if (mo->player && mo->z < mo->floorz) { mo->player->viewheight -= mo->floorz-mo->z; mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; } // adjust height // villsa [STRIFE] check for things standing on top of other things if(!P_CheckPositionZ(mo, mo->z + mo->momz)) { if(mo->momz >= 0) mo->ceilingz = mo->height + mo->z; else mo->floorz = mo->z; } //mo->z += mo->momz; // villsa [STRIFE] unused if ( mo->flags & MF_FLOAT && mo->target) { // float down towards target if too close if ( /*!(mo->flags & MF_SKULLFLY) // villsa [STRIFE] unused &&*/ !(mo->flags & MF_INFLOAT) ) { dist = P_AproxDistance (mo->x - mo->target->x, mo->y - mo->target->y); delta =(mo->target->z + (mo->height>>1)) - mo->z; if (delta<0 && dist < -(delta*3) ) mo->z -= FLOATSPEED; else if (delta>0 && dist < (delta*3) ) mo->z += FLOATSPEED; } } // clip movement if (mo->z <= mo->floorz) { // hit the floor if (mo->flags & MF_BOUNCE) { // the skull slammed into something // villsa [STRIFE] affect reactiontime // momz is also shifted by 1 mo->momz = -mo->momz >> 1; mo->reactiontime >>= 1; // villsa [STRIFE] get terrain type if(P_GetTerrainType(mo) != FLOOR_SOLID) mo->flags &= ~MF_BOUNCE; } if (mo->momz < 0) { if (mo->player && mo->momz < -GRAVITY*8) { // Squat down. // Decrease viewheight for a moment // after hitting the ground (hard), // and utter appropriate sound. mo->player->deltaviewheight = mo->momz>>3; // villsa [STRIFE] fall damage // haleyjd 09/18/10: Repaired calculation if(mo->momz < -20*FRACUNIT) P_DamageMobj(mo, NULL, mo, mo->momz / -25000); // haleyjd 20110224: *Any* fall centers your view, not just // damaging falls (moved outside the above if). mo->player->centerview = 1; S_StartSound (mo, sfx_oof); } mo->momz = 0; } mo->z = mo->floorz; // cph 2001/05/26 - // See lost soul bouncing comment above. We need this here for bug // compatibility with original Doom2 v1.9 - if a soul is charging and // hit by a raising floor this incorrectly reverses its Y momentum. // // villsa [STRIFE] unused /* if (!correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) mo->momz = -mo->momz; */ // villsa [STRIFE] also check for MF_BOUNCE if ( (mo->flags & MF_MISSILE) && !(mo->flags & (MF_NOCLIP|MF_BOUNCE)) ) { P_ExplodeMissile (mo); } } else // haleyjd 20110224: else here, not else if - Strife change or what? { if (! (mo->flags & MF_NOGRAVITY) ) { if (mo->momz == 0) mo->momz = -GRAVITY*2; else mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // villsa [STRIFE] replace skullfly flag with MF_BOUNCE if (mo->flags & MF_BOUNCE) { // villsa [STRIFE] affect reactiontime // momz is also shifted by 1 mo->momz = -mo->momz >> 1; mo->reactiontime >>= 1; } // hit the ceiling if (mo->momz > 0) mo->momz = 0; mo->z = mo->ceilingz - mo->height; // villsa [STRIFE] also check for MF_BOUNCE if ( (mo->flags & MF_MISSILE) && !(mo->flags & (MF_NOCLIP|MF_BOUNCE)) ) { // villsa [STRIFE] check against skies if(mo->subsector->sector->ceilingpic == skyflatnum) P_RemoveMobj(mo); else P_ExplodeMissile (mo); } } } } // // P_NightmareRespawn // // [STRIFE] Modifications for: // * Destination fog z coordinate // * Restoration of all Strife mapthing flags // void P_NightmareRespawn (mobj_t* mobj) { fixed_t x; fixed_t y; fixed_t z; mobj_t* mo; mapthing_t* mthing; x = mobj->spawnpoint.x << FRACBITS; y = mobj->spawnpoint.y << FRACBITS; // somthing is occupying it's position? if (!P_CheckPosition (mobj, x, y) ) return; // no respwan // spawn a teleport fog at old spot // because of removal of the body? mo = P_SpawnMobj (mobj->x, mobj->y, mobj->subsector->sector->floorheight , MT_TFOG); // initiate teleport sound S_StartSound (mo, sfx_telept); // spawn a teleport fog at the new spot //ss = R_PointInSubsector (x,y); // haleyjd [STRIFE]: Uses ONFLOORZ instead of ss->sector->floorheight mo = P_SpawnMobj (x, y, ONFLOORZ , MT_TFOG); S_StartSound (mo, sfx_telept); // spawn the new monster mthing = &mobj->spawnpoint; // spawn it if (mobj->info->flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; // inherit attributes from deceased one mo = P_SpawnMobj (x,y,z, mobj->type); mo->spawnpoint = mobj->spawnpoint; mo->angle = ANG45 * (mthing->angle/45); if (mthing->options & MTF_AMBUSH) mo->flags |= MF_AMBUSH; if (mthing->options & MTF_STAND) // [STRIFE] Standing mode, for NPCs mobj->flags |= MF_STAND; if (mthing->options & MTF_FRIEND) // [STRIFE] Allies mobj->flags |= MF_ALLY; if (mthing->options & MTF_TRANSLUCENT) // [STRIFE] Translucent object mobj->flags |= MF_SHADOW; if (mthing->options & MTF_MVIS) // [STRIFE] Alt. Translucency mobj->flags |= MF_MVIS; mo->reactiontime = 18; // remove the old monster, P_RemoveMobj (mobj); } // // P_MobjThinker // // [STRIFE] Modified for: // * Terrain effects // * Stonecold cheat // * Altered skill 5 respawn behavior // void P_MobjThinker (mobj_t* mobj) { // momentum movement if (mobj->momx || mobj->momy /*|| (mobj->flags&MF_SKULLFLY)*/ ) // villsa [STRIFE] unused { P_XYMovement (mobj); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj->thinker.function.acv == (actionf_v) (-1)) return; // mobj was removed // villsa [STRIFE] terrain clipping if(P_GetTerrainType(mobj) == FLOOR_SOLID) mobj->flags &= ~MF_FEETCLIPPED; else mobj->flags |= MF_FEETCLIPPED; } if ( (mobj->z != mobj->floorz && !(mobj->flags & MF_NOGRAVITY)) // villsa [STRIFE] || mobj->momz ) { P_ZMovement (mobj); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj->thinker.function.acv == (actionf_v) (-1)) return; // mobj was removed // villsa [STRIFE] terrain clipping and sounds if(P_GetTerrainType(mobj) == FLOOR_SOLID) mobj->flags &= ~MF_FEETCLIPPED; else { S_StartSound(mobj, sfx_wsplsh); mobj->flags |= MF_FEETCLIPPED; } } // cycle through states, // calling action functions at transitions if (mobj->tics != -1) { mobj->tics--; // villsa [STRIFE] stonecold cheat if(stonecold) { if(mobj->flags & MF_COUNTKILL) P_DamageMobj(mobj, mobj, mobj, 10); } // 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++; // haleyjd [STRIFE]: respawn time increased from 12 to 16 if (mobj->movecount < 16*TICRATE) return; if ( leveltime&31 ) return; if (P_Random () > 4) return; // haleyjd [STRIFE]: NOTDMATCH things don't respawn if(mobj->flags & MF_NOTDMATCH) return; P_NightmareRespawn (mobj); } } // // P_SpawnMobj // // [STRIFE] Modifications to reactiontime and for terrain types. // 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; mobj->flags = info->flags; mobj->health = info->spawnhealth; // haleyjd 09/25/10: [STRIFE] Doesn't do this; messes up flamethrower // and a lot of other stuff using reactiontime as a counter. //if (gameskill != sk_nightmare) mobj->reactiontime = info->reactiontime; mobj->lastlook = P_Random () % MAXPLAYERS; // do not set the state with P_SetMobjState, // because action routines can not be called yet st = &states[info->spawnstate]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // set subsector and/or block links P_SetThingPosition (mobj); mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; if (z == ONFLOORZ) { mobj->z = mobj->floorz; // villsa [STRIFE] if(P_GetTerrainType(mobj) != FLOOR_SOLID) mobj->flags |= MF_FEETCLIPPED; } else if (z == ONCEILINGZ) mobj->z = mobj->ceilingz - mobj->info->height; else mobj->z = z; mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; P_AddThinker (&mobj->thinker); return mobj; } // // P_RemoveMobj // // [STRIFE] Modifications for item respawn timing // mapthing_t itemrespawnque[ITEMQUESIZE]; int itemrespawntime[ITEMQUESIZE]; int iquehead; int iquetail; void P_RemoveMobj (mobj_t* mobj) { // villsa [STRIFE] removed invuln/invis. sphere exceptions if ((mobj->flags & MF_SPECIAL) && !(mobj->flags & MF_DROPPED)) { itemrespawnque[iquehead] = mobj->spawnpoint; itemrespawntime[iquehead] = leveltime + 30*TICRATE; // [STRIFE] // [STRIFE] haleyjd 20130915 // -random parameter affects the behavior of respawning items here. if(randomparm && iquehead != iquetail) { short type = itemrespawnque[iquehead].type; short options = itemrespawnque[iquehead].options; // swap the type and options of iquehead and iquetail itemrespawnque[iquehead].type = itemrespawnque[iquetail].type; itemrespawnque[iquehead].options = itemrespawnque[iquetail].options; itemrespawnque[iquetail].type = type; itemrespawnque[iquetail].options = options; } iquehead = (iquehead+1)&(ITEMQUESIZE-1); // lose one off the end? if (iquehead == iquetail) iquetail = (iquetail+1)&(ITEMQUESIZE-1); } // unlink from sector and block lists P_UnsetThingPosition (mobj); // stop any playing sound S_StopSound (mobj); // free block P_RemoveThinker ((thinker_t*)mobj); } // // P_RespawnSpecials // // [STRIFE] modification to item respawn time handling // void P_RespawnSpecials (void) { fixed_t x; fixed_t y; fixed_t z; subsector_t* ss; mobj_t* mo; mapthing_t* mthing; int i; // only respawn items in deathmatch if (deathmatch != 2) return; // nothing left to respawn? if (iquehead == iquetail) return; // haleyjd [STRIFE]: 30 second wait is not accounted for here, see above. if (leveltime < itemrespawntime[iquetail]) return; mthing = &itemrespawnque[iquetail]; x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; // spawn a teleport fog at the new spot ss = R_PointInSubsector (x,y); mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); S_StartSound (mo, sfx_itmbk); // find which type to spawn for (i=0 ; i< NUMMOBJTYPES ; i++) { if (mthing->type == mobjinfo[i].doomednum) break; } if (i >= NUMMOBJTYPES) { I_Error("P_RespawnSpecials: Failed to find mobj type with doomednum " "%d when respawning thing. This would cause a buffer overrun " "in vanilla Strife.", 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 que iquetail = (iquetail+1)&(ITEMQUESIZE-1); } // // P_SpawnPlayer // Called when a player is spawned on the level. // Most of the player structure stays unchanged // between levels. // // [STRIFE] Modifications for: // * stonecold cheat, -workparm // * default inventory/questflags // void P_SpawnPlayer(mapthing_t* mthing) { player_t* p; fixed_t x; fixed_t y; fixed_t z; mobj_t* mobj; if(mthing->type == 0) return; // not playing? if(!playeringame[mthing->type-1]) return; p = &players[mthing->type-1]; if (p->playerstate == PST_REBORN) G_PlayerReborn (mthing->type-1); x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; z = ONFLOORZ; mobj = P_SpawnMobj (x,y,z, MT_PLAYER); // set color translations for player sprites if(mthing->type > 1) mobj->flags |= (mthing->type-1)<angle = ANG45 * (mthing->angle/45); mobj->player = p; mobj->health = p->health; p->mo = mobj; p->playerstate = PST_LIVE; p->refire = 0; p->message = NULL; p->damagecount = 0; p->bonuscount = 0; p->extralight = 0; p->fixedcolormap = 0; p->viewheight = VIEWHEIGHT; // setup gun psprite P_SetupPsprites(p); // villsa [STRIFE] stonecold = false; // villsa [STRIFE] what a nasty hack... if(gamemap == 10) p->weaponowned[wp_sigil] = true; // villsa [STRIFE] instead of just giving cards in deathmatch mode, also // set accuracy to 50 and give all quest flags if(deathmatch) { int i; p->accuracy = 50; p->questflags = QF_ALLQUESTS; // 0x7fffffff for(i = 0; i < NUMCARDS; i++) p->cards[i] = true; } // villsa [STRIFE] set godmode? if(workparm) p->cheats |= CF_GODMODE; if(mthing->type - 1 == consoleplayer) { // wake up the status bar ST_Start (); // wake up the heads up text HU_Start (); } } // // P_SpawnMapThing // The fields of the mapthing should // already be in host byte order. // // [STRIFE] Modifications for: // * No Lost Souls, item count // * New mapthing_t flag bits // * 8-player support // void P_SpawnMapThing (mapthing_t* mthing) { int i; int bit; mobj_t* mobj; fixed_t x; fixed_t y; fixed_t z; // count deathmatch start positions if (mthing->type == 11) { if (deathmatch_p < &deathmatchstarts[10]) { memcpy (deathmatch_p, mthing, sizeof(*mthing)); deathmatch_p++; } return; } if (mthing->type <= 0) { // Thing type 0 is actually "player -1 start". // For some reason, Vanilla Doom accepts/ignores this. return; } // check for players specially // haleyjd 20120209: [STRIFE] 8 player starts if (mthing->type <= 8) { // save spots for respawning in network games playerstarts[mthing->type-1] = *mthing; if (!deathmatch) P_SpawnPlayer (mthing); return; } // check for apropriate skill level if (!netgame && (mthing->options & 16) ) return; if (gameskill == sk_baby) bit = 1; else if (gameskill == sk_nightmare) bit = 4; else bit = 1<<(gameskill-1); if (!(mthing->options & bit) ) return; // find which type to spawn for (i=0 ; i< NUMMOBJTYPES ; i++) if (mthing->type == mobjinfo[i].doomednum) break; if (i==NUMMOBJTYPES) I_Error ("P_SpawnMapThing: Unknown type %i at (%i, %i)", mthing->type, mthing->x, mthing->y); // don't spawn keycards and players in deathmatch if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) return; // don't spawn any monsters if -nomonsters // villsa [STRIFE] Removed MT_SKULL if (nomonsters && (mobjinfo[i].flags & MF_COUNTKILL)) return; // spawn it x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; if (mobjinfo[i].flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; mobj = P_SpawnMobj (x,y,z, i); mobj->spawnpoint = *mthing; if (mobj->tics > 0) mobj->tics = 1 + (P_Random () % mobj->tics); if (mobj->flags & MF_COUNTKILL) totalkills++; // villsa [STRIFE] unused /* if (mobj->flags & MF_COUNTITEM) totalitems++; */ mobj->angle = ANG45 * (mthing->angle/45); if (mthing->options & MTF_AMBUSH) mobj->flags |= MF_AMBUSH; if (mthing->options & MTF_STAND) // [STRIFE] Standing mode, for NPCs mobj->flags |= MF_STAND; if (mthing->options & MTF_FRIEND) // [STRIFE] Allies mobj->flags |= MF_ALLY; if (mthing->options & MTF_TRANSLUCENT) // [STRIFE] Translucent object mobj->flags |= MF_SHADOW; if (mthing->options & MTF_MVIS) // [STRIFE] Alt. Translucency mobj->flags |= MF_MVIS; } // // GAME SPAWN FUNCTIONS // // // P_SpawnPuff // // [STRIFE] Modifications for: // * No spawn tics randomization // * Player melee behavior // extern fixed_t attackrange; void P_SpawnPuff ( fixed_t x, fixed_t y, fixed_t z ) { mobj_t* th; int t; t = P_Random(); z += ((t - P_Random()) << 10); // [STRIFE] removed momz and tics randomization th = P_SpawnMobj(x, y, z, MT_STRIFEPUFF); // [STRIFE]: new type // don't make punches spark on the wall // [STRIFE] Use a separate melee attack range for the player if(attackrange == PLAYERMELEERANGE) P_SetMobjState(th, S_POW2_00); // 141 // villsa [STRIFE] unused /* if (th->tics < 1) th->tics = 1; */ } // // P_SpawnSparkPuff // // villsa [STRIFE] new function // mobj_t* P_SpawnSparkPuff(fixed_t x, fixed_t y, fixed_t z) { int t = P_Random(); return P_SpawnMobj(x, y, ((t - P_Random()) << 10) + z, MT_SPARKPUFF); } // // P_SpawnBlood // // [STRIFE] Modifications for: // * No spawn tics randomization // * Different damage ranges for state setting // void P_SpawnBlood ( fixed_t x, fixed_t y, fixed_t z, int damage ) { mobj_t* th; int temp; temp = P_Random(); z += (temp - P_Random()) << 10; th = P_SpawnMobj(x, y, z, MT_BLOOD_DEATH); th->momz = FRACUNIT*2; // villsa [STRIFE]: removed tics randomization // villsa [STRIFE] different checks for damage range if(damage >= 10 && damage <= 13) P_SetMobjState(th, S_BLOD_00); else if(damage >= 7 && damage < 10) P_SetMobjState(th, S_BLOD_01); else if(damage < 7) P_SetMobjState(th, S_BLOD_02); } // // P_CheckMissileSpawn // Moves the missile forward a bit // and possibly explodes it right there. // // [STRIFE] Modifications for: // * No spawn tics randomization // void P_CheckMissileSpawn (mobj_t* th) { // villsa [STRIFE] removed tics randomization // move a little forward so an angle can // be computed if it immediately explodes th->x += (th->momx>>1); th->y += (th->momy>>1); th->z += (th->momz>>1); if (!P_TryMove (th, th->x, th->y)) P_ExplodeMissile (th); } // 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_SpawnMissile // // [STRIFE] Added MVIS inaccuracy // mobj_t* P_SpawnMissile ( mobj_t* source, mobj_t* dest, mobjtype_t type ) { mobj_t* th; angle_t an; int dist; th = P_SpawnMobj (source->x, source->y, source->z + 4*8*FRACUNIT, type); if (th->info->seesound) S_StartSound (th, th->info->seesound); th->target = source; // where it came from an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); // fuzzy player if (dest->flags & MF_SHADOW) { int t = P_Random(); // haleyjd 20110223: remove order-of-evaluation dependencies an += (t - P_Random()) << 21; } // villsa [STRIFE] check for heavily transparent things else if(dest->flags & MF_MVIS) { int t = P_Random(); an += (t - P_Random()) << 22; } 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_SpawnFacingMissile // // villsa [STRIFE] new function // Spawn a missile based on source's angle // mobj_t* P_SpawnFacingMissile(mobj_t* source, mobj_t* target, mobjtype_t type) { mobj_t* th; angle_t an; fixed_t dist; th = P_SpawnMobj(source->x, source->y, source->z + (32*FRACUNIT), type); if(th->info->seesound) S_StartSound(th, th->info->seesound); th->target = source; // where it came from th->angle = source->angle; // haleyjd 09/06/10: fix0red an = th->angle; // fuzzy player if (target->flags & MF_SHADOW) { int t = P_Random(); an += (t - P_Random()) << 21; } // villsa [STRIFE] check for heavily transparent things else if(target->flags & MF_MVIS) { int t = P_Random(); an += (t - P_Random()) << 22; } an >>= ANGLETOFINESHIFT; th->momx = FixedMul (th->info->speed, finecosine[an]); th->momy = FixedMul (th->info->speed, finesine[an]); dist = P_AproxDistance (target->x - source->x, target->y - source->y); dist = dist / th->info->speed; if(dist < 1) dist = 1; th->momz = (target->z - source->z) / dist; P_CheckMissileSpawn (th); return th; } // // P_SpawnPlayerMissile // // Tries to aim at a nearby monster // villsa [STRIFE] now returns a mobj // * Also modified to allow up/down look, and to account for foot-clipping // by liquid terrain. // mobj_t* P_SpawnPlayerMissile(mobj_t* source, mobjtype_t type) { mobj_t* th; angle_t an; fixed_t x; fixed_t y; fixed_t z; fixed_t slope; // see which target is to be aimed at an = source->angle; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); } if (!linetarget) { an = source->angle; // haleyjd 09/21/10: [STRIFE] Removed, for look up/down support. //slope = 0; } } // villsa [STRIFE] if(linetarget) source->target = linetarget; x = source->x; y = source->y; // villsa [STRIFE] if(!(source->flags & MF_FEETCLIPPED)) z = source->z + 32*FRACUNIT; else z = source->z + 22*FRACUNIT; th = P_SpawnMobj (x,y,z, type); if (th->info->seesound) S_StartSound (th, th->info->seesound); th->target = source; th->angle = an; th->momx = FixedMul( th->info->speed, finecosine[an>>ANGLETOFINESHIFT]); th->momy = FixedMul( th->info->speed, finesine[an>>ANGLETOFINESHIFT]); th->momz = FixedMul( th->info->speed, slope); P_CheckMissileSpawn (th); return th; } // // P_SpawnMortar // // villsa [STRIFE] new function // Spawn a high-arcing ballistic projectile // mobj_t* P_SpawnMortar(mobj_t *source, mobjtype_t type) { mobj_t* th; angle_t an; fixed_t slope; an = source->angle; th = P_SpawnMobj(source->x, source->y, source->z, type); th->target = source; th->angle = an; an >>= ANGLETOFINESHIFT; // haleyjd 20110203: corrected order of function calls th->momx = FixedMul(th->info->speed, finecosine[an]); th->momy = FixedMul(th->info->speed, finesine[an]); P_CheckMissileSpawn(th); slope = P_AimLineAttack(source, source->angle, 1024*FRACUNIT); th->momz = FixedMul(th->info->speed, slope); return th; } crispy-doom-crispy-doom-5.6.4/src/strife/p_mobj.h000066400000000000000000000241141360717211000217400ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Map Objects, MObj, definition and handling. // #ifndef __P_MOBJ__ #define __P_MOBJ__ // Basics. #include "tables.h" #include "m_fixed.h" // We need the thinker_t stuff. #include "d_think.h" // We need the WAD data structure for Map things, // from the THINGS lump. #include "doomdata.h" // States are tied to finite states are // tied to animation frames. // Needs precompiled tables/data structures. #include "info.h" // // NOTES: mobj_t // // mobj_ts are used to tell the refresh where to draw an image, // tell the world simulation when objects are contacted, // and tell the sound driver how to position a sound. // // The refresh uses the next and prev links to follow // lists of things in sectors as they are being drawn. // The sprite, frame, and angle elements determine which patch_t // is used to draw the sprite if it is visible. // The sprite and frame values are allmost allways set // from state_t structures. // The statescr.exe utility generates the states.h and states.c // files that contain the sprite/frame numbers from the // statescr.txt source file. // The xyz origin point represents a point at the bottom middle // of the sprite (between the feet of a biped). // This is the default origin position for patch_ts grabbed // with lumpy.exe. // A walking creature will have its z equal to the floor // it is standing on. // // The sound code uses the x,y, and subsector fields // to do stereo positioning of any sound effited by the mobj_t. // // The play simulation uses the blocklinks, x,y,z, radius, height // to determine when mobj_ts are touching each other, // touching lines in the map, or hit by trace lines (gunshots, // lines of sight, etc). // The mobj_t->flags element has various bit flags // used by the simulation. // // Every mobj_t is linked into a single sector // based on its origin coordinates. // The subsector_t is found with R_PointInSubsector(x,y), // and the sector_t can be found with subsector->sector. // The sector links are only used by the rendering code, // the play simulation does not care about them at all. // // Any mobj_t that needs to be acted upon by something else // in the play world (block movement, be shot, etc) will also // need to be linked into the blockmap. // If the thing has the MF_NOBLOCK flag set, it will not use // the block links. It can still interact with other things, // but only as the instigator (missiles will run into other // things, but nothing can run into a missile). // Each block in the grid is 128*128 units, and knows about // every line_t that it contains a piece of, and every // interactable mobj_t that has its origin contained. // // A valid mobj_t is a mobj_t that has the proper subsector_t // filled in for its xy coordinates and is linked into the // sector from which the subsector was made, or has the // MF_NOSECTOR flag set (the subsector_t needs to be valid // even if MF_NOSECTOR is set), and is linked into a blockmap // block or has the MF_NOBLOCKMAP flag set. // Links should only be modified by the P_[Un]SetThingPosition() // functions. // Do not change the MF_NO? flags while a thing is valid. // // Any questions? // // // Misc. mobj flags // typedef enum { // Call P_SpecialThing when touched. MF_SPECIAL = 1, // Blocks. MF_SOLID = 2, // Can be hit. MF_SHOOTABLE = 4, // Don't use the sector links (invisible but touchable). MF_NOSECTOR = 8, // Don't use the blocklinks (inert but displayable) MF_NOBLOCKMAP = 16, // villsa [STRIFE] Stand around until alerted MF_STAND = 32, // Will try to attack right back. MF_JUSTHIT = 64, // Will take at least one step before attacking. MF_JUSTATTACKED = 128, // On level spawning (initial position), // hang from ceiling instead of stand on floor. MF_SPAWNCEILING = 256, // Don't apply gravity (every tic), // that is, object will float, keeping current height // or changing it actively. MF_NOGRAVITY = 512, // Movement flags. // This allows jumps from high places. MF_DROPOFF = 0x400, // villsa [STRIFE] For players, count as quest item MF_GIVEQUEST = 0x800, // Player cheat. ??? MF_NOCLIP = 0x1000, // villsa [STRIFE] are feet clipped into water/slude floor? MF_FEETCLIPPED = 0x2000, // Allow moves to any height, no gravity. // For active floaters, e.g. cacodemons, pain elementals. MF_FLOAT = 0x4000, // villsa [STRIFE] can NPC talk? MF_NODIALOG = 0x8000, // Don't hit same species, explode on block. // Player missiles as well as fireballs of various kinds. MF_MISSILE = 0x10000, // Dropped by a demon, not level spawned. // E.g. ammo clips dropped by dying former humans. MF_DROPPED = 0x20000, // Use fuzzy draw (shadow demons or spectres), // temporary player invisibility powerup. MF_SHADOW = 0x40000, // Flag: don't bleed when shot (use puff), // barrels and shootable furniture shall not bleed. MF_NOBLOOD = 0x80000, // Don't stop moving halfway off a step, // that is, have dead bodies slide down all the way. MF_CORPSE = 0x100000, // Floating to a height for a move, ??? // don't auto float to target's height. MF_INFLOAT = 0x200000, // On kill, count this enemy object // towards intermission kill total. // Happy gathering. MF_COUNTKILL = 0x400000, // Not to be activated by sound, deaf monster. MF_AMBUSH = 0x800000, // villsa [STRIFE] flag used for bouncing projectiles MF_BOUNCE = 0x1000000, // Don't spawn this object // in death match mode (e.g. key cards). MF_NOTDMATCH = 0x2000000, // villsa [STRIFE] friendly towards player with matching flag MF_ALLY = 0x4000000, // villsa [STRIFE] 75% or 25% transparency? -- NEEDS VERIFICATION MF_MVIS = 0x8000000, // villsa [STRIFE] color translation MF_COLORSWAP1 = 0x10000000, // villsa [STRIFE] color translation MF_COLORSWAP2 = 0x20000000, // villsa [STRIFE] color translation MF_COLORSWAP3 = 0x40000000, // villsa [STRIFE] spectral entity, only damaged by spectral missiles MF_SPECTRAL = 0x80000000, // Player sprites in multiplayer modes are modified // using an internal color lookup table for re-indexing. // haleyjd 09/06/10: redid for Strife translations MF_TRANSLATION = (MF_COLORSWAP1|MF_COLORSWAP2|MF_COLORSWAP3), // Turns 0x10000000 into 0x01 to get a translation index. // villsa [STRIFE] change from 26 to 28 MF_TRANSSHIFT = 28 } mobjflag_t; // Map Object definition. // // [STRIFE]: Amazingly, only one modification was made to mobj_t over DOOM // 1.666, and that was the addition of the single-byte allegiance field for // tracking with which player friendly monsters are allied. // typedef struct mobj_s { // List: thinker links. thinker_t thinker; // Info for drawing: position. fixed_t x; fixed_t y; fixed_t z; // More list: links in sector (if needed) struct mobj_s* snext; struct mobj_s* sprev; //More drawing info: to determine current sprite. angle_t angle; // orientation spritenum_t sprite; // used to find patch_t and flip value int frame; // might be ORed with FF_FULLBRIGHT // Interaction info, by BLOCKMAP. // Links in blocks (if needed). struct mobj_s* bnext; struct mobj_s* bprev; struct subsector_s* subsector; // The closest interval over all contacted Sectors. fixed_t floorz; fixed_t ceilingz; // For movement checking. fixed_t radius; fixed_t height; // Momentums, used to update position. fixed_t momx; fixed_t momy; fixed_t momz; // If == validcount, already checked. int validcount; mobjtype_t type; mobjinfo_t* info; // &mobjinfo[mobj->type] int tics; // state tic counter state_t* state; int flags; int health; // Movement direction, movement generation (zig-zagging). int movedir; // 0-7 int movecount; // when 0, select a new dir // Thing being chased/attacked (or NULL), // also the originator for missiles. struct mobj_s* target; // Reaction time: if non 0, don't attack yet. // Used by player to freeze a bit after teleporting. int reactiontime; // If >0, the target will be chased // no matter what (even if shot) int threshold; // Additional info record for player avatars only. // Only valid if type == MT_PLAYER struct player_s* player; // Player number last looked for. int lastlook; // For nightmare respawn. mapthing_t spawnpoint; // Thing being chased/attacked for tracers. struct mobj_s* tracer; // [STRIFE] haleyjd 09/05/10: // * In multiplayer this stores allegiance, for friends and teleport beacons // * In single-player this tracks dialog state. byte miscdata; } mobj_t; // haleyjd [STRIFE] Exported void P_CheckMissileSpawn (mobj_t* th); #endif crispy-doom-crispy-doom-5.6.4/src/strife/p_plats.c000066400000000000000000000213021360717211000221230ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Plats (i.e. elevator platforms) code, raising/lowering. // #include #include "i_system.h" #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" plat_t* activeplats[MAXPLATS]; // // Move a plat up and down // void T_PlatRaise(plat_t* plat) { result_e res; switch(plat->status) { case up: res = T_MovePlane(plat->sector, plat->speed, plat->high, plat->crush, 0, 1); if(plat->type == raiseAndChange || plat->type == raiseToNearestAndChange) { if(!(leveltime&7)) S_StartSound(&plat->sector->soundorg, sfx_stnmov); } // villsa [STRIFE] if(plat->type == slowDWUS) { if(!(leveltime&7)) S_StartSound(&plat->sector->soundorg, sfx_stnmov); } if(res == crushed && (!plat->crush)) { plat->count = plat->wait; plat->status = down; S_StartSound(&plat->sector->soundorg, sfx_pstart); } else { if(res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg, sfx_pstop); switch(plat->type) { case blazeDWUS: case downWaitUpStay: case raiseAndChange: case slowDWUS: // villsa [STRIFE] case raiseToNearestAndChange: P_RemoveActivePlat(plat); break; default: break; } } } break; case down: res = T_MovePlane(plat->sector, plat->speed, plat->low, false, 0, -1); // villsa [STRIFE] if(plat->type == slowDWUS) { if(!(leveltime&7)) S_StartSound(&plat->sector->soundorg, sfx_stnmov); } if(res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg,sfx_pstop); // villsa [STRIFE] if(plat->type == upWaitDownStay) P_RemoveActivePlat(plat); } break; case waiting: if(!--plat->count) { if(plat->sector->floorheight == plat->low) plat->status = up; else plat->status = down; S_StartSound(&plat->sector->soundorg,sfx_pstart); } case in_stasis: break; } } // // EV_DoPlat // // Do Platforms "amount" is only used for SOME platforms. // int EV_DoPlat(line_t* line, plattype_e type, int amount) { plat_t* plat; int secnum; int rtn; sector_t* sec; secnum = -1; rtn = 0; // Activate all plats that are in_stasis switch(type) { case perpetualRaise: P_ActivateInStasis(line->tag); break; default: break; } while((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // Find lowest & highest floors around sector rtn = 1; plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); P_AddThinker(&plat->thinker); plat->type = type; plat->sector = sec; plat->sector->specialdata = plat; plat->thinker.function.acp1 = (actionf_p1) T_PlatRaise; plat->crush = false; plat->tag = line->tag; switch(type) { case raiseToNearestAndChange: plat->speed = PLATSPEED/2; sec->floorpic = sides[line->sidenum[0]].sector->floorpic; plat->high = P_FindNextHighestFloor(sec,sec->floorheight); plat->wait = 0; plat->status = up; // NO MORE DAMAGE, IF APPLICABLE sec->special = 0; S_StartSound(&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(&sec->soundorg, sfx_stnmov); break; // villsa [STRIFE] case upWaitDownStay: plat->speed = PLATSPEED * 4; plat->high = P_FindNextHighestFloor(sec, sec->floorheight); plat->low = sec->floorheight; plat->wait = TICRATE * PLATWAIT; plat->status = up; S_StartSound(&sec->soundorg, sfx_pstart); 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 = TICRATE * PLATWAIT; plat->status = down; S_StartSound(&sec->soundorg, sfx_pstart); break; // villsa [STRIFE] case slowDWUS: plat->speed = PLATSPEED; plat->low = P_FindLowestFloorSurrounding(sec); if(plat->low > sec->floorheight) plat->low = sec->floorheight; plat->high = sec->floorheight; plat->wait = TICRATE * (PLATWAIT * 10); plat->status = down; S_StartSound(&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 = TICRATE * PLATWAIT; plat->status = down; S_StartSound(&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 = TICRATE * PLATWAIT; plat->status = P_Random() & 1; S_StartSound(&sec->soundorg, sfx_pstart); break; } P_AddActivePlat(plat); } return rtn; } // // P_ActivateInStasis // void P_ActivateInStasis(int tag) { int i; for(i = 0; i < MAXPLATS; i++) if(activeplats[i] && (activeplats[i])->tag == tag && (activeplats[i])->status == in_stasis) { (activeplats[i])->status = (activeplats[i])->oldstatus; (activeplats[i])->thinker.function.acp1 = (actionf_p1)T_PlatRaise; } } // // EV_StopPlat // void EV_StopPlat(line_t* line) { int j; for(j = 0; j < MAXPLATS; j++) if (activeplats[j] && ((activeplats[j])->status != in_stasis) && ((activeplats[j])->tag == line->tag)) { (activeplats[j])->oldstatus = (activeplats[j])->status; (activeplats[j])->status = in_stasis; (activeplats[j])->thinker.function.acv = (actionf_v)NULL; } } // // P_AddActivePlat // void P_AddActivePlat(plat_t* plat) { int i; for(i = 0; i < MAXPLATS; i++) { if (activeplats[i] == NULL) { activeplats[i] = plat; return; } } I_Error("P_AddActivePlat: no more plats!"); } // // P_RemoveActivePlat // void P_RemoveActivePlat(plat_t* plat) { int i; for(i = 0; i < MAXPLATS; i++) { if(plat == activeplats[i]) { (activeplats[i])->sector->specialdata = NULL; P_RemoveThinker(&(activeplats[i])->thinker); activeplats[i] = NULL; return; } } I_Error("P_RemoveActivePlat: can't find plat!"); } crispy-doom-crispy-doom-5.6.4/src/strife/p_pspr.c000066400000000000000000000577271360717211000220100ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Weapon sprite animation, weapon objects. // Action functions for weapons. // #include "doomdef.h" #include "d_event.h" #include "deh_misc.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" // Data. #include "sounds.h" #include "p_pspr.h" #define LOWERSPEED FRACUNIT*6 #define RAISESPEED FRACUNIT*6 #define WEAPONBOTTOM 128*FRACUNIT #define WEAPONTOP 32*FRACUNIT // // P_SetPsprite // // [STRIFE] // villsa: Removed psprite sx, sy modification via misc1/2 // void P_SetPsprite ( player_t* player, int position, statenum_t stnum ) { pspdef_t* psp; state_t* state; psp = &player->psprites[position]; do { if (!stnum) { // object removed itself psp->state = NULL; break; } state = &states[stnum]; psp->state = state; psp->tics = state->tics; // could be 0 // villsa [STRIFE] unused /*if (state->misc1) { // coordinate set psp->sx = state->misc1 << FRACBITS; psp->sy = state->misc2 << FRACBITS; }*/ // Call action routine. // Modified handling. if (state->action.acp2) { state->action.acp2(player, psp); if (!psp->state) break; } stnum = psp->state->nextstate; } while (!psp->tics); // an initial state of 0 could cycle through } // haleyjd 09/06/10: [STRIFE] Removed P_CalcSwing // // P_BringUpWeapon // Starts bringing the pending weapon up // from the bottom of the screen. // Uses player // // villsa [STRIFE] Modifications for Strife weapons // void P_BringUpWeapon (player_t* player) { statenum_t newstate; if (player->pendingweapon == wp_nochange) player->pendingweapon = player->readyweapon; if (player->pendingweapon == wp_flame) S_StartSound (player->mo, sfx_flidl); // villsa [STRIFE] flame sounds newstate = weaponinfo[player->pendingweapon].upstate; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite (player, ps_weapon, newstate); // villsa [STRIFE] set various flash states if(player->pendingweapon == wp_elecbow) P_SetPsprite(player, ps_flash, S_XBOW_10); // 31 else if(player->pendingweapon == wp_sigil && player->sigiltype) P_SetPsprite(player, ps_flash, S_SIGH_00 + player->sigiltype); // 117 else P_SetPsprite(player, ps_flash, S_NULL); player->pendingweapon = wp_nochange; } // // P_CheckAmmo // Returns true if there is enough ammo to shoot. // If not, selects the next weapon to use. // // villsa [STRIFE] Changes to handle Strife weapons // boolean P_CheckAmmo (player_t* player) { ammotype_t ammo; int count; ammo = weaponinfo[player->readyweapon].ammo; // Minimal amount for one shot varies. if (player->readyweapon == wp_torpedo) count = 30; else if (player->readyweapon == wp_mauler) count = 20; else count = 1; // Regular. // Some do not need ammunition anyway. // Return if current ammunition sufficient. if (ammo == am_noammo || player->ammo[ammo] >= count) return true; // Out of ammo, pick a weapon to change to. // Preferences are set here. // villsa [STRIFE] new weapon preferences if (player->weaponowned[wp_mauler] && player->ammo[am_cell] >= 20) player->pendingweapon = wp_mauler; else if(player->weaponowned[wp_rifle] && player->ammo[am_bullets]) player->pendingweapon = wp_rifle; else if (player->weaponowned[wp_elecbow] && player->ammo[am_elecbolts]) player->pendingweapon = wp_elecbow; else if (player->weaponowned[wp_missile] && player->ammo[am_missiles]) player->pendingweapon = wp_missile; else if (player->weaponowned[wp_flame] && player->ammo[am_cell]) player->pendingweapon = wp_flame; else if (player->weaponowned[wp_hegrenade] && player->ammo[am_hegrenades]) player->pendingweapon = wp_hegrenade; else if (player->weaponowned[wp_poisonbow] && player->ammo[am_poisonbolts]) player->pendingweapon = wp_poisonbow; else if (player->weaponowned[wp_wpgrenade] && player->ammo[am_wpgrenades]) player->pendingweapon = wp_wpgrenade; // BUG: This will *never* be selected for an automatic switch because the // normal Mauler is higher priority and uses less ammo. else if (player->weaponowned[wp_torpedo] && player->ammo[am_cell] >= 30) player->pendingweapon = wp_torpedo; else player->pendingweapon = wp_fist; // Now set appropriate weapon overlay. P_SetPsprite(player, ps_weapon, weaponinfo[player->readyweapon].downstate); return false; } // // P_FireWeapon. // // villsa [STRIFE] Changes for player state and weapons // void P_FireWeapon (player_t* player) { statenum_t newstate; if (!P_CheckAmmo (player)) return; P_SetMobjState (player->mo, S_PLAY_05); // 292 newstate = weaponinfo[player->readyweapon].atkstate; P_SetPsprite (player, ps_weapon, newstate); // villsa [STRIFE] exclude these weapons from causing noise if(player->readyweapon > wp_elecbow && player->readyweapon != wp_poisonbow) 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) { statenum_t newstate; int angle; // get out of attack state if (player->mo->state == &states[S_PLAY_05] || // 292 player->mo->state == &states[S_PLAY_06]) // 293 { P_SetMobjState (player->mo, S_PLAY_00); // 287 } // villsa [STRIFE] check for wp_flame instead of chainsaw // haleyjd 09/06/10: fixed state (00 rather than 01) if (player->readyweapon == wp_flame && psp->state == &states[S_FLMT_00]) // 62 { S_StartSound (player->mo, sfx_flidl); } // check for change // if player is dead, put the weapon away if (player->pendingweapon != wp_nochange || !player->health) { // change weapon // (pending weapon should allready be validated) newstate = weaponinfo[player->readyweapon].downstate; P_SetPsprite (player, ps_weapon, newstate); return; } // check for fire // the missile launcher and torpedo do not auto fire if (player->cmd.buttons & BT_ATTACK) { if ( !player->attackdown || (player->readyweapon != wp_missile && player->readyweapon != wp_torpedo)) // villsa [STRIFE] replace bfg with torpedo { player->attackdown = true; P_FireWeapon (player); return; } } else player->attackdown = false; // bob the weapon based on movement speed angle = (128*leveltime)&FINEMASK; psp->sx = FRACUNIT + FixedMul (player->bob, finecosine[angle]); angle &= FINEANGLES/2-1; psp->sy = WEAPONTOP + FixedMul (player->bob, finesine[angle]); } // // A_ReFire // The player can re-fire the weapon // without lowering it entirely. // void A_ReFire ( player_t* player, pspdef_t* psp ) { // check for fire // (if a weaponchange is pending, let it go through instead) if ( (player->cmd.buttons & BT_ATTACK) && player->pendingweapon == wp_nochange && player->health) { player->refire++; P_FireWeapon (player); } else { player->refire = 0; P_CheckAmmo (player); } } // // A_CheckReload // void A_CheckReload(player_t* player, pspdef_t* psp) { P_CheckAmmo(player); // villsa [STRIFE] set animating sprite for crossbow if(player->readyweapon == wp_elecbow) P_SetPsprite(player, player->readyweapon, S_XBOW_10); } // // A_Lower // Lowers current weapon, // and changes weapon at bottom. // void A_Lower ( player_t* player, pspdef_t* psp ) { psp->sy += LOWERSPEED; // Is already down. if (psp->sy < WEAPONBOTTOM ) return; // Player is dead. if (player->playerstate == PST_DEAD) { psp->sy = WEAPONBOTTOM; // don't bring weapon back up return; } // The old weapon has been lowered off the screen, // so change the weapon and start raising it if (!player->health) { // Player is dead, so keep the weapon off screen. P_SetPsprite (player, ps_weapon, S_NULL); return; } player->readyweapon = player->pendingweapon; P_BringUpWeapon (player); } // // A_Raise // void A_Raise ( player_t* player, pspdef_t* psp ) { statenum_t newstate; psp->sy -= RAISESPEED; if (psp->sy > WEAPONTOP ) return; psp->sy = WEAPONTOP; // The weapon has been raised all the way, // so change to the ready state. newstate = weaponinfo[player->readyweapon].readystate; P_SetPsprite (player, ps_weapon, newstate); } // // A_GunFlash // void A_GunFlash ( player_t* player, pspdef_t* psp ) { P_SetMobjState (player->mo, S_PLAY_06); P_SetPsprite (player,ps_flash,weaponinfo[player->readyweapon].flashstate); } // // WEAPON ATTACKS // // // A_Punch // void A_Punch(player_t* player, pspdef_t* psp) { angle_t angle; int damage; int slope; int sound; int stamina; int t; // villsa [STRIFE] new damage formula // haleyjd 09/19/10: seriously corrected... stamina = player->stamina; damage = (P_Random() & ((stamina/10) + 7)) * ((stamina/10) + 2); if(player->powers[pw_strength]) damage *= 10; angle = player->mo->angle; t = P_Random(); angle += (t - P_Random()) << 18; slope = P_AimLineAttack (player->mo, angle, PLAYERMELEERANGE); P_LineAttack (player->mo, angle, PLAYERMELEERANGE, slope, damage); // turn to face target if(linetarget) { // villsa [STRIFE] check for non-flesh types if(linetarget->flags & MF_NOBLOOD) sound = sfx_mtalht; else sound = sfx_meatht; S_StartSound (player->mo, sound); player->mo->angle = R_PointToAngle2 (player->mo->x, player->mo->y, linetarget->x, linetarget->y); // villsa [STRIFE] apply flag player->mo->flags |= MF_JUSTATTACKED; // villsa [STRIFE] do punch alert routine P_DoPunchAlert(player->mo, linetarget); } else S_StartSound (player->mo, sfx_swish); } // // A_FireFlameThrower // // villsa [STRIFE] new codepointer // void A_FireFlameThrower(player_t* player, pspdef_t* psp) { mobj_t* mo; int t; P_SetMobjState(player->mo, S_PLAY_06); player->ammo[weaponinfo[player->readyweapon].ammo]--; t = P_Random(); player->mo->angle += (t - P_Random()) << 18; mo = P_SpawnPlayerMissile(player->mo, MT_SFIREBALL); mo->momz += (5*FRACUNIT); } // // A_FireMissile // // villsa [STRIFE] completly new compared to the original // void A_FireMissile(player_t* player, pspdef_t* psp) { angle_t an; int t; // haleyjd 09/19/10: I previously missed an add op that meant it should be // accuracy * 5, not 4. Checks out with other sources. an = player->mo->angle; t = P_Random(); player->mo->angle += (t - P_Random()) << (19 - (player->accuracy * 5 / 100)); P_SetMobjState(player->mo, S_PLAY_06); player->ammo[weaponinfo[player->readyweapon].ammo]--; P_SpawnPlayerMissile(player->mo, MT_MINIMISSLE); player->mo->angle = an; } // // A_FireMauler2 // // villsa [STRIFE] - new codepointer // void A_FireMauler2(player_t* player, pspdef_t* pspr) { P_SetMobjState(player->mo, S_PLAY_06); P_DamageMobj(player->mo, player->mo, NULL, 20); player->ammo[weaponinfo[player->readyweapon].ammo] -= 30; P_SpawnPlayerMissile(player->mo, MT_TORPEDO); P_Thrust(player, player->mo->angle + ANG180, 512000); } // // A_FireGrenade // // villsa [STRIFE] - new codepointer // void A_FireGrenade(player_t* player, pspdef_t* pspr) { mobjtype_t type; mobj_t* mo; state_t* st1; state_t* st2; angle_t an; fixed_t radius; // decide on what type of grenade to spawn if(player->readyweapon == wp_hegrenade) { type = MT_HEGRENADE; } else if(player->readyweapon == wp_wpgrenade) { type = MT_PGRENADE; } else { type = MT_HEGRENADE; fprintf(stderr, "Warning: A_FireGrenade used on wrong weapon!\n"); } player->ammo[weaponinfo[player->readyweapon].ammo]--; // set flash frame st1 = &states[(pspr->state - states) + weaponinfo[player->readyweapon].flashstate]; st2 = &states[weaponinfo[player->readyweapon].atkstate]; P_SetPsprite(player, ps_flash, st1 - st2); player->mo->z += 32*FRACUNIT; // ugh mo = P_SpawnMortar(player->mo, type); player->mo->z -= 32*FRACUNIT; // ugh // change momz based on player's pitch mo->momz = FixedMul((player->pitch<info->speed) + (8*FRACUNIT); S_StartSound(mo, mo->info->seesound); radius = mobjinfo[type].radius + player->mo->info->radius; an = (player->mo->angle >> ANGLETOFINESHIFT); mo->x += FixedMul(finecosine[an], radius + (4*FRACUNIT)); mo->y += FixedMul(finesine[an], radius + (4*FRACUNIT)); // shoot grenade from left or right side? if(&states[weaponinfo[player->readyweapon].atkstate] == pspr->state) an = (player->mo->angle - ANG90) >> ANGLETOFINESHIFT; else an = (player->mo->angle + ANG90) >> ANGLETOFINESHIFT; mo->x += FixedMul((15*FRACUNIT), finecosine[an]); mo->y += FixedMul((15*FRACUNIT), finesine[an]); // set bounce flag mo->flags |= MF_BOUNCE; } // // A_FireElectricBolt // villsa [STRIFE] - new codepointer // void A_FireElectricBolt(player_t* player, pspdef_t* pspr) { angle_t an = player->mo->angle; int t; // haleyjd 09/19/10: Use 5 mul on accuracy here as well t = P_Random(); player->mo->angle += (t - P_Random()) << (18 - (player->accuracy * 5 / 100)); player->ammo[weaponinfo[player->readyweapon].ammo]--; P_SpawnPlayerMissile(player->mo, MT_ELECARROW); player->mo->angle = an; S_StartSound(player->mo, sfx_xbow); } // // A_FirePoisonBolt // villsa [STRIFE] - new codepointer // void A_FirePoisonBolt(player_t* player, pspdef_t* pspr) { angle_t an = player->mo->angle; int t; // haleyjd 09/19/10: Use 5 mul on accuracy here as well t = P_Random(); player->mo->angle += (t - P_Random()) << (18 - (player->accuracy * 5 / 100)); player->ammo[weaponinfo[player->readyweapon].ammo]--; P_SpawnPlayerMissile(player->mo, MT_POISARROW); player->mo->angle = an; S_StartSound(player->mo, sfx_xbow); } // // P_BulletSlope // Sets a slope so a near miss is at aproximately // the height of the intended target // // haleyjd 09/06/10 [STRIFE] Modified with a little target hack... // fixed_t bulletslope; void P_BulletSlope (mobj_t *mo) { angle_t an; // see which target is to be aimed at an = mo->angle; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); } } // haleyjd 09/06/10: [STRIFE] Somebody added this here, and without it, you // will get spurious crashing in routines such as P_LookForPlayers! if(linetarget) mo->target = linetarget; } // // P_GunShot // // [STRIFE] Modifications to support accuracy. // void P_GunShot ( mobj_t* mo, boolean accurate ) { angle_t angle; int damage; angle = mo->angle; // villsa [STRIFE] apply player accuracy // haleyjd 09/18/10: made some corrections: use 5x accuracy; // eliminated order-of-evaluation dependency if (!accurate) { int t = P_Random(); angle += (t - P_Random()) << (20 - ((mo->player->accuracy * 5) / 100)); } // haleyjd 09/18/10 [STRIFE] corrected damage formula and moved down to // preserve proper P_Random call order. damage = 4 * (P_Random() % 3 + 1); P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage); } // // A_FireRifle // // villsa [STRIFE] - new codepointer // void A_FireRifle(player_t* player, pspdef_t* pspr) { S_StartSound(player->mo, sfx_rifle); if(player->ammo[weaponinfo[player->readyweapon].ammo]) { P_SetMobjState(player->mo, S_PLAY_06); // 293 player->ammo[weaponinfo[player->readyweapon].ammo]--; P_BulletSlope(player->mo); P_GunShot(player->mo, !player->refire); } } // // A_FireMauler1 // // villsa [STRIFE] - new codepointer // void A_FireMauler1(player_t* player, pspdef_t* pspr) { int i; angle_t angle; int damage; // haleyjd 09/18/10: Corrected ammo check to use >= if(player->ammo[weaponinfo[player->readyweapon].ammo] >= 20) { player->ammo[weaponinfo[player->readyweapon].ammo] -= 20; P_BulletSlope(player->mo); S_StartSound(player->mo, sfx_pgrdat); for(i = 0; i < 20; i++) { int t; damage = 5*(P_Random ()%3+1); angle = player->mo->angle; t = P_Random(); angle += (t - P_Random()) << 19; t = P_Random(); P_LineAttack(player->mo, angle, 2112*FRACUNIT, bulletslope + ((t - P_Random())<<5), damage); } } } // // A_SigilSound // // villsa [STRIFE] - new codepointer // void A_SigilSound(player_t* player, pspdef_t* pspr) { S_StartSound(player->mo, sfx_siglup); player->extralight = 2; } // // A_FireSigil // // villsa [STRIFE] - new codepointer // void A_FireSigil(player_t* player, pspdef_t* pspr) { mobj_t* mo; angle_t an; int i; // keep info on armor because sigil does piercing damage i = player->armortype; player->armortype = 0; // BUG: setting inflictor causes firing the Sigil to always push the player // toward the east, no matter what direction he is facing. P_DamageMobj(player->mo, player->mo, NULL, 4 * (player->sigiltype + 1)); // restore armor player->armortype = i; S_StartSound(player->mo, sfx_siglup); switch(player->sigiltype) { // falling lightning bolts from the sky case 0: P_BulletSlope(player->mo); if(linetarget) { // haleyjd 09/18/10: corrected z coordinate mo = P_SpawnMobj(linetarget->x, linetarget->y, ONFLOORZ, MT_SIGIL_A_GROUND); mo->tracer = linetarget; } else { an = player->mo->angle>>ANGLETOFINESHIFT; mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SIGIL_A_GROUND); mo->momx += FixedMul((28*FRACUNIT), finecosine[an]); mo->momy += FixedMul((28*FRACUNIT), finesine[an]); } mo->health = -1; mo->target = player->mo; break; // simple projectile case 1: P_SpawnPlayerMissile(player->mo, MT_SIGIL_B_SHOT)->health = -1; break; // spread shot case 2: player->mo->angle -= ANG90; // starting at 270... for(i = 0; i < 20; i++) // increment by 1/10 of 90, 20 times. { player->mo->angle += (ANG90 / 10); mo = P_SpawnMortar(player->mo, MT_SIGIL_C_SHOT); mo->health = -1; mo->z = player->mo->z + (32*FRACUNIT); } player->mo->angle -= ANG90; // subtract off the extra 90 break; // tracer attack case 3: P_BulletSlope(player->mo); if(linetarget) { mo = P_SpawnPlayerMissile(player->mo, MT_SIGIL_D_SHOT); mo->tracer = linetarget; } else { an = player->mo->angle >> ANGLETOFINESHIFT; mo = P_SpawnPlayerMissile(player->mo, MT_SIGIL_D_SHOT); mo->momx += FixedMul(mo->info->speed, finecosine[an]); mo->momy += FixedMul(mo->info->speed, finesine[an]); } mo->health = -1; break; // mega blast case 4: mo = P_SpawnPlayerMissile(player->mo, MT_SIGIL_E_SHOT); mo->health = -1; if(!linetarget) { an = (unsigned int)player->pitch >> ANGLETOFINESHIFT; mo->momz += FixedMul(finesine[an], mo->info->speed); } break; default: break; } } // // A_GunFlashThinker // // villsa [STRIFE] - new codepointer // void A_GunFlashThinker(player_t* player, pspdef_t* pspr) { if(player->readyweapon == wp_sigil && player->sigiltype) P_SetPsprite(player, ps_flash, S_SIGH_00 + player->sigiltype); else P_SetPsprite(player, ps_flash, S_NULL); } // // ? // void A_Light0 (player_t *player, pspdef_t *psp) { player->extralight = 0; } void A_Light1 (player_t *player, pspdef_t *psp) { player->extralight = 1; } void A_Light2 (player_t *player, pspdef_t *psp) { player->extralight = 2; } // // A_SigilShock // // villsa [STRIFE] - new codepointer // void A_SigilShock (player_t *player, pspdef_t *psp) { player->extralight = -3; } // // A_TorpedoExplode // // villsa [STRIFE] - new codepointer // void A_TorpedoExplode(mobj_t* actor) { int i; actor->angle -= ANG180; for(i = 0; i < 80; i++) { actor->angle += (ANG90 / 20); P_SpawnMortar(actor, MT_TORPEDOSPREAD)->target = actor->target; } } // // A_MaulerSound // // villsa [STRIFE] - new codepointer // void A_MaulerSound(player_t *player, pspdef_t *psp) { int t; S_StartSound(player->mo, sfx_proton); t = P_Random(); psp->sx += (t - P_Random()) << 10; t = P_Random(); psp->sy += (t - P_Random()) << 10; } // // P_SetupPsprites // Called at start of level for each player. // void P_SetupPsprites(player_t* player) { int i; // remove all psprites for(i = 0; i < NUMPSPRITES; i++) player->psprites[i].state = 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) { int i; pspdef_t* psp; state_t* state; psp = &player->psprites[0]; for(i = 0; i < NUMPSPRITES; i++, psp++) { // a null state means not active if((state = psp->state)) { // drop tic count and possibly change state // a -1 tic count never changes if(psp->tics != -1) { psp->tics--; if(!psp->tics) P_SetPsprite (player, i, psp->state->nextstate); } } } player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; // villsa [STRIFE] extra stuff for targeter player->psprites[ps_targleft].sx = (160*FRACUNIT) - ((100 - player->accuracy) << FRACBITS); player->psprites[ps_targright].sx = ((100 - player->accuracy) << FRACBITS) + (160*FRACUNIT); } crispy-doom-crispy-doom-5.6.4/src/strife/p_pspr.h000066400000000000000000000034111360717211000217720ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Sprite animation. // #ifndef __P_PSPR__ #define __P_PSPR__ // Basic data types. // Needs fixed point, and BAM angles. #include "m_fixed.h" #include "tables.h" // // Needs to include the precompiled // sprite animation tables. // Header generated by multigen utility. // This includes all the data for thing animation, // i.e. the Thing Atrributes table // and the Frame Sequence table. #include "info.h" // // Frame flags: // handles maximum brightness (torches, muzzle flare, light sources) // #define FF_FULLBRIGHT 0x8000 // flag in thing->frame #define FF_FRAMEMASK 0x7fff // // Overlay psprites are scaled shapes // drawn directly on the view screen, // coordinates are given for a 320*200 view screen. // typedef enum { ps_weapon, ps_flash, ps_targcenter, // villsa [STRIFE] ps_targleft, // villsa [STRIFE] ps_targright, // villsa [STRIFE] NUMPSPRITES } psprnum_t; typedef struct { state_t* state; // a NULL state means not active int tics; fixed_t sx; fixed_t sy; } pspdef_t; typedef struct player_s *playerptr; // haleyjd [STRIFE] Exported void P_SetPsprite(playerptr player, int position, statenum_t stnum); #endif crispy-doom-crispy-doom-5.6.4/src/strife/p_saveg.c000066400000000000000000001346211360717211000221160ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Archiving: SaveGame I/O. // #include #include #include "dstrings.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "m_misc.h" #include "p_local.h" #include "p_saveg.h" // State. #include "doomstat.h" #include "r_state.h" #define SAVEGAME_EOF 0x1d // haleyjd 09/28/10: [STRIFE] VERSIONSIZE == 8 #define VERSIONSIZE 8 FILE *save_stream; int savegamelength; boolean savegame_error; // Get the filename of a temporary file to write the savegame to. After // the file has been successfully saved, it will be renamed to the // real file. char *P_TempSaveGameFile(void) { static char *filename = NULL; if (filename == NULL) { filename = M_StringJoin(savegamedir, "temp.dsg", NULL); } return filename; } // Get the filename of the save game file to use for the specified slot. char *P_SaveGameFile(int slot) { static char *filename = NULL; static size_t filename_size; char basename[32]; if (filename == NULL) { filename_size = strlen(savegamedir) + 32; filename = malloc(filename_size); } DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot); M_snprintf(filename, filename_size, "%s%s", savegamedir, basename); return filename; } // Endian-safe integer read/write functions static byte saveg_read8(void) { byte result; if (fread(&result, 1, 1, save_stream) < 1) { if (!savegame_error) { fprintf(stderr, "saveg_read8: Unexpected end of file while " "reading save game\n"); savegame_error = true; } } return result; } static void saveg_write8(byte value) { if (fwrite(&value, 1, 1, save_stream) < 1) { if (!savegame_error) { fprintf(stderr, "saveg_write8: Error while writing save game\n"); savegame_error = true; } } } static short saveg_read16(void) { int result; result = saveg_read8(); result |= saveg_read8() << 8; return result; } static void saveg_write16(short value) { saveg_write8(value & 0xff); saveg_write8((value >> 8) & 0xff); } static int saveg_read32(void) { int result; result = saveg_read8(); result |= saveg_read8() << 8; result |= saveg_read8() << 16; result |= saveg_read8() << 24; return result; } static void saveg_write32(int value) { saveg_write8(value & 0xff); saveg_write8((value >> 8) & 0xff); saveg_write8((value >> 16) & 0xff); saveg_write8((value >> 24) & 0xff); } // Pad to 4-byte boundaries static void saveg_read_pad(void) { unsigned long pos; int padding; int i; pos = ftell(save_stream); padding = (4 - (pos & 3)) & 3; for (i=0; ix = saveg_read16(); // short y; str->y = saveg_read16(); // short angle; str->angle = saveg_read16(); // short type; str->type = saveg_read16(); // short options; str->options = saveg_read16(); } static void saveg_write_mapthing_t(mapthing_t *str) { // short x; saveg_write16(str->x); // short y; saveg_write16(str->y); // short angle; saveg_write16(str->angle); // short type; saveg_write16(str->type); // short options; saveg_write16(str->options); } // // actionf_t // static void saveg_read_actionf_t(actionf_t *str) { // actionf_p1 acp1; str->acp1 = saveg_readp(); } static void saveg_write_actionf_t(actionf_t *str) { // actionf_p1 acp1; saveg_writep(str->acp1); } // // think_t // // This is just an actionf_t. // #define saveg_read_think_t saveg_read_actionf_t #define saveg_write_think_t saveg_write_actionf_t // // thinker_t // static void saveg_read_thinker_t(thinker_t *str) { // struct thinker_s* prev; str->prev = saveg_readp(); // struct thinker_s* next; str->next = saveg_readp(); // think_t function; saveg_read_think_t(&str->function); } static void saveg_write_thinker_t(thinker_t *str) { // struct thinker_s* prev; saveg_writep(str->prev); // struct thinker_s* next; saveg_writep(str->next); // think_t function; saveg_write_think_t(&str->function); } // // mobj_t // // haleyjd 09/28/10: [STRIFE] Changed to match Strife binary mobj_t structure. // static void saveg_read_mobj_t(mobj_t *str) { int pl; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // fixed_t x; str->x = saveg_read32(); // fixed_t y; str->y = saveg_read32(); // fixed_t z; str->z = saveg_read32(); // struct mobj_s* snext; str->snext = saveg_readp(); // struct mobj_s* sprev; str->sprev = saveg_readp(); // angle_t angle; str->angle = saveg_read32(); // spritenum_t sprite; str->sprite = saveg_read_enum(); // int frame; str->frame = saveg_read32(); // struct mobj_s* bnext; str->bnext = saveg_readp(); // struct mobj_s* bprev; str->bprev = saveg_readp(); // struct subsector_s* subsector; str->subsector = saveg_readp(); // fixed_t floorz; str->floorz = saveg_read32(); // fixed_t ceilingz; str->ceilingz = saveg_read32(); // fixed_t radius; str->radius = saveg_read32(); // fixed_t height; str->height = saveg_read32(); // fixed_t momx; str->momx = saveg_read32(); // fixed_t momy; str->momy = saveg_read32(); // fixed_t momz; str->momz = saveg_read32(); // int validcount; str->validcount = saveg_read32(); // mobjtype_t type; str->type = saveg_read_enum(); // mobjinfo_t* info; str->info = saveg_readp(); // int tics; str->tics = saveg_read32(); // state_t* state; str->state = &states[saveg_read32()]; // int flags; str->flags = saveg_read32(); // int health; str->health = saveg_read32(); // int movedir; str->movedir = saveg_read32(); // int movecount; str->movecount = saveg_read32(); // struct mobj_s* target; str->target = saveg_readp(); // int reactiontime; str->reactiontime = saveg_read32(); // int threshold; str->threshold = saveg_read32(); // struct player_s* player; pl = saveg_read32(); if (pl > 0) { str->player = &players[pl - 1]; str->player->mo = str; } else { str->player = NULL; } // int lastlook; str->lastlook = saveg_read32(); // mapthing_t spawnpoint; saveg_read_mapthing_t(&str->spawnpoint); // struct mobj_s* tracer; str->tracer = saveg_readp(); // byte miscdata; str->miscdata = saveg_read8(); // [STRIFE] Only change to mobj_t. } static void saveg_write_mobj_t(mobj_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // fixed_t x; saveg_write32(str->x); // fixed_t y; saveg_write32(str->y); // fixed_t z; saveg_write32(str->z); // struct mobj_s* snext; saveg_writep(str->snext); // struct mobj_s* sprev; saveg_writep(str->sprev); // angle_t angle; saveg_write32(str->angle); // spritenum_t sprite; saveg_write_enum(str->sprite); // int frame; saveg_write32(str->frame); // struct mobj_s* bnext; saveg_writep(str->bnext); // struct mobj_s* bprev; saveg_writep(str->bprev); // struct subsector_s* subsector; saveg_writep(str->subsector); // fixed_t floorz; saveg_write32(str->floorz); // fixed_t ceilingz; saveg_write32(str->ceilingz); // fixed_t radius; saveg_write32(str->radius); // fixed_t height; saveg_write32(str->height); // fixed_t momx; saveg_write32(str->momx); // fixed_t momy; saveg_write32(str->momy); // fixed_t momz; saveg_write32(str->momz); // int validcount; saveg_write32(str->validcount); // mobjtype_t type; saveg_write_enum(str->type); // mobjinfo_t* info; saveg_writep(str->info); // int tics; saveg_write32(str->tics); // state_t* state; saveg_write32(str->state - states); // int flags; saveg_write32(str->flags); // int health; saveg_write32(str->health); // int movedir; saveg_write32(str->movedir); // int movecount; saveg_write32(str->movecount); // struct mobj_s* target; saveg_writep(str->target); // int reactiontime; saveg_write32(str->reactiontime); // int threshold; saveg_write32(str->threshold); // struct player_s* player; if (str->player) { saveg_write32(str->player - players + 1); } else { saveg_write32(0); } // int lastlook; saveg_write32(str->lastlook); // mapthing_t spawnpoint; saveg_write_mapthing_t(&str->spawnpoint); // struct mobj_s* tracer; saveg_writep(str->tracer); // byte miscdata; saveg_write8(str->miscdata); // [STRIFE] Only change to mobj_t. } // // ticcmd_t // // haleyjd 09/28/10: [STRIFE] Modified for Strife binary ticcmd_t structure. // static void saveg_read_ticcmd_t(ticcmd_t *str) { // signed char forwardmove; str->forwardmove = saveg_read8(); // signed char sidemove; str->sidemove = saveg_read8(); // short angleturn; str->angleturn = saveg_read16(); // short consistancy; // STRIFE-FIXME: throwing away top byte of consistancy until // the true Strife ticcmd_t structure is available. str->consistancy = (byte)saveg_read16(); // byte chatchar; str->chatchar = saveg_read8(); // byte buttons; str->buttons = saveg_read8(); // byte buttons2; str->buttons2 = saveg_read8(); // [STRIFE] // int inventory; str->inventory = saveg_read32(); // [STRIFE] } static void saveg_write_ticcmd_t(ticcmd_t *str) { // signed char forwardmove; saveg_write8(str->forwardmove); // signed char sidemove; saveg_write8(str->sidemove); // short angleturn; saveg_write16(str->angleturn); // short consistancy; saveg_write16(str->consistancy); // byte chatchar; saveg_write8(str->chatchar); // byte buttons; saveg_write8(str->buttons); // byte buttons2; saveg_write8(str->buttons2); // [STRIFE] // int inventory; saveg_write32(str->inventory); // [STRIFE] } // // pspdef_t // static void saveg_read_pspdef_t(pspdef_t *str) { int state; // state_t* state; state = saveg_read32(); if (state > 0) { str->state = &states[state]; } else { str->state = NULL; } // int tics; str->tics = saveg_read32(); // fixed_t sx; str->sx = saveg_read32(); // fixed_t sy; str->sy = saveg_read32(); } static void saveg_write_pspdef_t(pspdef_t *str) { // state_t* state; if (str->state) { saveg_write32(str->state - states); } else { saveg_write32(0); } // int tics; saveg_write32(str->tics); // fixed_t sx; saveg_write32(str->sx); // fixed_t sy; saveg_write32(str->sy); } // // inventory_t // // haleyjd 09/28/10: [STRIFE] handle inventory input/output // static void saveg_read_inventory_t(inventory_t *str) { //int sprite; str->sprite = saveg_read32(); //int type; str->type = saveg_read32(); //int amount; str->amount = saveg_read32(); } static void saveg_write_inventory_t(inventory_t *str) { saveg_write32(str->sprite); saveg_write32(str->type); saveg_write32(str->amount); } // // player_t // // haleyjd 09/28/10: [STRIFE] Modified for Strife binary player_t structure. // static void saveg_read_player_t(player_t *str) { int i; // mobj_t* mo; str->mo = saveg_readp(); // playerstate_t playerstate; str->playerstate = saveg_read_enum(); // ticcmd_t cmd; saveg_read_ticcmd_t(&str->cmd); // fixed_t viewz; str->viewz = saveg_read32(); // fixed_t viewheight; str->viewheight = saveg_read32(); // fixed_t deltaviewheight; str->deltaviewheight = saveg_read32(); // fixed_t bob; str->bob = saveg_read32(); // int health; str->health = saveg_read32(); // int armorpoints; str->armorpoints = saveg_read16(); // [STRIFE] 32 -> 16 // int armortype; str->armortype = saveg_read16(); // [STRIFE] 32 -> 16 // int powers[NUMPOWERS]; for (i=0; ipowers[i] = saveg_read32(); } // int sigiltype; str->sigiltype = saveg_read32(); // [STRIFE] // int nukagecount; str->nukagecount = saveg_read32(); // [STRIFE] // int questflags; str->questflags = saveg_read32(); // [STRIFE] // int pitch; str->pitch = saveg_read32(); // [STRIFE] // int centerview; str->centerview = saveg_read32(); // [STRIFE] // inventory_t inventory[NUMINVENTORY]; for(i = 0; i < NUMINVENTORY; i++) { saveg_read_inventory_t(&(str->inventory[i])); // [STRIFE] } // int st_update; str->st_update = saveg_read32(); // [STRIFE] // short numinventory; str->numinventory = saveg_read16(); // [STRIFE] // short inventorycursor; str->inventorycursor = saveg_read16(); // [STRIFE] // short accuracy; str->accuracy = saveg_read16(); // [STRIFE] // short stamina; str->stamina = saveg_read16(); // [STRIFE] // boolean cards[NUMCARDS]; for (i=0; icards[i] = saveg_read32(); } // boolean backpack; str->backpack = saveg_read32(); // int attackdown; str->attackdown = saveg_read32(); // int usedown; str->usedown = saveg_read32(); // int inventorydown; str->inventorydown = saveg_read32(); // [STRIFE] // int frags[MAXPLAYERS]; for (i=0; ifrags[i] = saveg_read32(); } // weapontype_t readyweapon; str->readyweapon = saveg_read_enum(); // weapontype_t pendingweapon; str->pendingweapon = saveg_read_enum(); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i] = saveg_read32(); } // int ammo[NUMAMMO]; for (i=0; iammo[i] = saveg_read32(); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i] = saveg_read32(); } // int cheats; str->cheats = saveg_read32(); // int refire; str->refire = saveg_read32(); // short killcount; str->killcount = saveg_read16(); // [STRIFE] 32 -> 16 // haleyjd 08/30/10 [STRIFE] No itemcount. // int itemcount; //str->itemcount = saveg_read32(); // haleyjd 08/30/10 [STRIFE] No secretcount. // int secretcount; //str->secretcount = saveg_read32(); // char* message; str->message = saveg_readp(); // int damagecount; str->damagecount = saveg_read32(); // int bonuscount; str->bonuscount = saveg_read32(); // mobj_t* attacker; str->attacker = saveg_readp(); // int extralight; str->extralight = saveg_read32(); // int fixedcolormap; str->fixedcolormap = saveg_read32(); // int colormap; - [STRIFE] no such field //str->colormap = saveg_read32(); // short allegiance; str->allegiance = saveg_read16(); // [STRIFE] // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // int mapstate[40]; for(i = 0; i < 40; ++i) // [STRIFE] { str->mapstate[i] = saveg_read32(); } // haleyjd 08/30/10: [STRIFE] No intermission, no didsecret. // boolean didsecret; //str->didsecret = saveg_read32(); } static void saveg_write_player_t(player_t *str) { int i; // mobj_t* mo; saveg_writep(str->mo); // playerstate_t playerstate; saveg_write_enum(str->playerstate); // ticcmd_t cmd; saveg_write_ticcmd_t(&str->cmd); // fixed_t viewz; saveg_write32(str->viewz); // fixed_t viewheight; saveg_write32(str->viewheight); // fixed_t deltaviewheight; saveg_write32(str->deltaviewheight); // fixed_t bob; saveg_write32(str->bob); // int health; saveg_write32(str->health); // int armorpoints; saveg_write16(str->armorpoints); // [STRIFE] 32 -> 16 // int armortype; saveg_write16(str->armortype); // [STRIFE] 32 -> 16 // int powers[NUMPOWERS]; for (i=0; ipowers[i]); } // int sigiltype; saveg_write32(str->sigiltype); // [STRIFE] // int nukagecount; saveg_write32(str->nukagecount); // [STRIFE] // int questflags; saveg_write32(str->questflags); // [STRIFE] // int pitch; saveg_write32(str->pitch); // [STRIFE] // int centerview; saveg_write32(str->centerview); // [STRIFE] // inventory_t inventory[NUMINVENTORY]; for(i = 0; i < NUMINVENTORY; ++i) // [STRIFE] { saveg_write_inventory_t(&str->inventory[i]); } // int st_update; saveg_write32(str->st_update); // [STRIFE] // short numinventory; saveg_write16(str->numinventory); // [STRIFE] // short inventorycursor; saveg_write16(str->inventorycursor); // [STRIFE] // short accuracy; saveg_write16(str->accuracy); // [STRIFE] // short stamina; saveg_write16(str->stamina); // [STRIFE] // boolean cards[NUMCARDS]; for (i=0; icards[i]); } // boolean backpack; saveg_write32(str->backpack); // int attackdown; saveg_write32(str->attackdown); // int usedown; saveg_write32(str->usedown); // int inventorydown; saveg_write32(str->inventorydown); // [STRIFE] // int frags[MAXPLAYERS]; for (i=0; ifrags[i]); } // weapontype_t readyweapon; saveg_write_enum(str->readyweapon); // weapontype_t pendingweapon; saveg_write_enum(str->pendingweapon); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i]); } // int ammo[NUMAMMO]; for (i=0; iammo[i]); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i]); } // int cheats; saveg_write32(str->cheats); // int refire; saveg_write32(str->refire); // short killcount; saveg_write16(str->killcount); // [STRIFE] 32 -> 16 // haleyjd 08/30/10 [STRIFE] No itemcount // int itemcount; //saveg_write32(str->itemcount); // haleyjd 08/30/10 [STRIFE] No secretcount // int secretcount; //saveg_write32(str->secretcount); // char* message; saveg_writep(str->message); // int damagecount; saveg_write32(str->damagecount); // int bonuscount; saveg_write32(str->bonuscount); // mobj_t* attacker; saveg_writep(str->attacker); // int extralight; saveg_write32(str->extralight); // int fixedcolormap; saveg_write32(str->fixedcolormap); // int colormap; [STRIFE] no such field //saveg_write32(str->colormap); // short allegiance; saveg_write16(str->allegiance); // [STRIFE] // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // int mapstate[40]; for(i = 0; i < 40; ++i) // [STRIFE] { saveg_write32(str->mapstate[i]); } // haleyjd 08/30/10: [STRIFE] No intermission, no secret. // boolean didsecret; //saveg_write32(str->didsecret); } // // ceiling_t // static void saveg_read_ceiling_t(ceiling_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // ceiling_e type; str->type = saveg_read_enum(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t bottomheight; str->bottomheight = saveg_read32(); // fixed_t topheight; str->topheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); // boolean crush; str->crush = saveg_read32(); // int direction; str->direction = saveg_read32(); // int tag; str->tag = saveg_read32(); // int olddirection; str->olddirection = saveg_read32(); } static void saveg_write_ceiling_t(ceiling_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // ceiling_e type; saveg_write_enum(str->type); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t bottomheight; saveg_write32(str->bottomheight); // fixed_t topheight; saveg_write32(str->topheight); // fixed_t speed; saveg_write32(str->speed); // boolean crush; saveg_write32(str->crush); // int direction; saveg_write32(str->direction); // int tag; saveg_write32(str->tag); // int olddirection; saveg_write32(str->olddirection); } // // vldoor_t // // haleyjd 09/28/10: [STRIFE] Modified for Strife binary vldoor_t structure. // static void saveg_read_vldoor_t(vldoor_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // vldoor_e type; str->type = saveg_read_enum(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t topheight; str->topheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); // int direction; str->direction = saveg_read32(); // int topwait; str->topwait = saveg_read32(); // int topcountdown; str->topcountdown = saveg_read32(); // villsa [STRIFE] new field - sound to play when opening //int opensound; str->opensound = saveg_read32(); // villsa [STRIFE] new field - sound to play when closing //int closesound; str->closesound = saveg_read32(); } static void saveg_write_vldoor_t(vldoor_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // vldoor_e type; saveg_write_enum(str->type); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t topheight; saveg_write32(str->topheight); // fixed_t speed; saveg_write32(str->speed); // int direction; saveg_write32(str->direction); // int topwait; saveg_write32(str->topwait); // int topcountdown; saveg_write32(str->topcountdown); // villsa [STRIFE] new field - sound to play when opening //int opensound; saveg_write32(str->opensound); // villsa [STRIFE] new field - sound to play when closing //int closesound; saveg_write32(str->closesound); } // // slidedoor_t [STRIFE]: new thinker type // static void saveg_read_slidedoor_t(slidedoor_t *str) { int sector; int line; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sdt_e type; str->type = saveg_read_enum(); // line_t *line1; line = saveg_read32(); str->line1 = &lines[line]; // line_t *line2; line = saveg_read32(); str->line2 = &lines[line]; // int frame; str->frame = saveg_read32(); // int whichDoorIndex; str->whichDoorIndex = saveg_read32(); // int timer; str->timer = saveg_read32(); // sector_t *frontsector; sector = saveg_read32(); str->frontsector = §ors[sector]; // sd_e status; str->status = saveg_read_enum(); } static void saveg_write_slidedoor_t(slidedoor_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sdt_e type; saveg_write_enum(str->type); // line_t *line1; saveg_write32(str->line1 - lines); // line_t *line2; saveg_write32(str->line2 - lines); // int frame; saveg_write32(str->frame); // int whichDoorIndex; saveg_write32(str->whichDoorIndex); // int timer; saveg_write32(str->timer); // sector_t *frontsector; saveg_write32(str->frontsector - sectors); // sd_e status; saveg_write_enum(str->status); } // // floormove_t // static void saveg_read_floormove_t(floormove_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // floor_e type; str->type = saveg_read_enum(); // boolean crush; str->crush = saveg_read32(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int direction; str->direction = saveg_read32(); // int newspecial; str->newspecial = saveg_read32(); // short texture; str->texture = saveg_read16(); // fixed_t floordestheight; str->floordestheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); } static void saveg_write_floormove_t(floormove_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // floor_e type; saveg_write_enum(str->type); // boolean crush; saveg_write32(str->crush); // sector_t* sector; saveg_write32(str->sector - sectors); // int direction; saveg_write32(str->direction); // int newspecial; saveg_write32(str->newspecial); // short texture; saveg_write16(str->texture); // fixed_t floordestheight; saveg_write32(str->floordestheight); // fixed_t speed; saveg_write32(str->speed); } // // plat_t // static void saveg_read_plat_t(plat_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t speed; str->speed = saveg_read32(); // fixed_t low; str->low = saveg_read32(); // fixed_t high; str->high = saveg_read32(); // int wait; str->wait = saveg_read32(); // int count; str->count = saveg_read32(); // plat_e status; str->status = saveg_read_enum(); // plat_e oldstatus; str->oldstatus = saveg_read_enum(); // boolean crush; str->crush = saveg_read32(); // int tag; str->tag = saveg_read32(); // plattype_e type; str->type = saveg_read_enum(); } static void saveg_write_plat_t(plat_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t speed; saveg_write32(str->speed); // fixed_t low; saveg_write32(str->low); // fixed_t high; saveg_write32(str->high); // int wait; saveg_write32(str->wait); // int count; saveg_write32(str->count); // plat_e status; saveg_write_enum(str->status); // plat_e oldstatus; saveg_write_enum(str->oldstatus); // boolean crush; saveg_write32(str->crush); // int tag; saveg_write32(str->tag); // plattype_e type; saveg_write_enum(str->type); } // // lightflash_t // static void saveg_read_lightflash_t(lightflash_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int count; str->count = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int minlight; str->minlight = saveg_read32(); // int maxtime; str->maxtime = saveg_read32(); // int mintime; str->mintime = saveg_read32(); } static void saveg_write_lightflash_t(lightflash_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int count; saveg_write32(str->count); // int maxlight; saveg_write32(str->maxlight); // int minlight; saveg_write32(str->minlight); // int maxtime; saveg_write32(str->maxtime); // int mintime; saveg_write32(str->mintime); } // // strobe_t // static void saveg_read_strobe_t(strobe_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int count; str->count = saveg_read32(); // int minlight; str->minlight = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int darktime; str->darktime = saveg_read32(); // int brighttime; str->brighttime = saveg_read32(); } static void saveg_write_strobe_t(strobe_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int count; saveg_write32(str->count); // int minlight; saveg_write32(str->minlight); // int maxlight; saveg_write32(str->maxlight); // int darktime; saveg_write32(str->darktime); // int brighttime; saveg_write32(str->brighttime); } // // glow_t // static void saveg_read_glow_t(glow_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int minlight; str->minlight = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int direction; str->direction = saveg_read32(); } static void saveg_write_glow_t(glow_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int minlight; saveg_write32(str->minlight); // int maxlight; saveg_write32(str->maxlight); // int direction; saveg_write32(str->direction); } // // Write the header for a savegame // // haleyjd 09/28/10: [STRIFE] numerous modifications. // void P_WriteSaveGameHeader(char *description) { char name[VERSIONSIZE]; int i; /* [STRIFE] This is in the "NAME" file in a Strife save directory. for (i=0; description[i] != '\0'; ++i) saveg_write8(description[i]); for (; i> 16) & 0xff); saveg_write8((leveltime >> 8) & 0xff); saveg_write8(leveltime & 0xff); } // // Read the header for a savegame // boolean P_ReadSaveGameHeader(void) { int i; byte a, b, c; char vcheck[VERSIONSIZE]; char read_vcheck[VERSIONSIZE]; // skip the description field /* for (i=0; ifloorheight >> FRACBITS); saveg_write16(sec->ceilingheight >> FRACBITS); saveg_write16(sec->floorpic); //saveg_write16(sec->ceilingpic); [STRIFE] not saved. saveg_write16(sec->lightlevel); saveg_write16(sec->special); // needed? //saveg_write16(sec->tag); // needed? [STRIFE] not saved. } // do lines for (i=0, li = lines ; iflags); saveg_write16(li->special); //saveg_write16(li->tag); [STRIFE] not saved. for (j=0 ; j<2 ; j++) { if (li->sidenum[j] == -1) continue; si = &sides[li->sidenum[j]]; // [STRIFE] offsets not saved. //saveg_write16(si->textureoffset >> FRACBITS); //saveg_write16(si->rowoffset >> FRACBITS); saveg_write16(si->toptexture); saveg_write16(si->bottomtexture); saveg_write16(si->midtexture); } } } // // P_UnArchiveWorld // void P_UnArchiveWorld (void) { int i; int j; sector_t* sec; line_t* li; side_t* si; // do sectors for (i=0, sec = sectors ; ifloorheight = saveg_read16() << FRACBITS; sec->ceilingheight = saveg_read16() << FRACBITS; sec->floorpic = saveg_read16(); //sec->ceilingpic = saveg_read16(); [STRIFE] not saved sec->lightlevel = saveg_read16(); sec->special = saveg_read16(); // needed? //sec->tag = saveg_read16(); // needed? [STRIFE] not saved sec->specialdata = 0; sec->soundtarget = 0; } // do lines for (i=0, li = lines ; iflags = saveg_read16(); li->special = saveg_read16(); //li->tag = saveg_read16(); [STRIFE] not saved for (j=0 ; j<2 ; j++) { if (li->sidenum[j] == -1) continue; si = &sides[li->sidenum[j]]; // [STRIFE] offsets not saved. //si->textureoffset = saveg_read16() << FRACBITS; //si->rowoffset = saveg_read16() << FRACBITS; si->toptexture = saveg_read16(); si->bottomtexture = saveg_read16(); si->midtexture = saveg_read16(); } } } // // Thinkers // typedef enum { tc_end, tc_mobj } thinkerclass_t; // // P_ArchiveThinkers // // [STRIFE] Verified unmodified. // void P_ArchiveThinkers (void) { thinker_t* th; // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) { saveg_write8(tc_mobj); saveg_write_pad(); saveg_write_mobj_t((mobj_t *) th); continue; } // haleyjd: This may seem mysterious but in the DOOM prebeta, // different types of things used different thinker functions. // Those would have all been handled here and this message is // probably a relic of that old system, not to mention the odd // name of this function, and use of an enumeration with only // two values in it. // I_Error ("P_ArchiveThinkers: Unknown thinker function"); } // add a terminating marker saveg_write8(tc_end); } // // P_UnArchiveThinkers // void P_UnArchiveThinkers (void) { byte tclass; thinker_t* currentthinker; thinker_t* next; mobj_t* mobj; // remove all the current thinkers currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { next = currentthinker->next; if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) P_RemoveMobj ((mobj_t *)currentthinker); else Z_Free (currentthinker); currentthinker = next; } P_InitThinkers (); // read in saved thinkers while (1) { tclass = saveg_read8(); switch (tclass) { case tc_end: return; // end of list case tc_mobj: saveg_read_pad(); mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL); saveg_read_mobj_t(mobj); // haleyjd 09/29/10: Strife sets the targets of non-allied creatures // who had a non-NULL target at save time to players[0].mo so that // they won't fall back asleep. // // BUG: As the player may not have been spawned yet, we could be // setting monsters' targets to the mobj which was spawned by // P_SetupLevel and then removed just above. Due to a subtle glitch // in the DOOM engine whereby all things removed in this function // are leaked until the next time P_SetupLevel is called, this is a // safe operation - the call to P_InitThinkers above stops any of // the objects removed, including the player's previous body, from // being passed to Z_Free. One glitch relying on another! if(mobj->target != NULL && (mobj->flags & MF_ALLY) != MF_ALLY) mobj->target = players[0].mo; else mobj->target = NULL; // WARNING! Strife does not seem to set tracer! I am leaving it be // for now because so far no crashes have been observed, and failing // to set this here will almost certainly crash Choco. mobj->tracer = NULL; P_SetThingPosition (mobj); mobj->info = &mobjinfo[mobj->type]; // [STRIFE]: doesn't set these //mobj->floorz = mobj->subsector->sector->floorheight; //mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; P_AddThinker (&mobj->thinker); break; default: I_Error ("Unknown tclass %i in savegame",tclass); } } } // // P_ArchiveSpecials // enum { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, tc_slidingdoor, // [STRIFE] tc_endspecials } specials_e; // // Things to handle: // // T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list // T_VerticalDoor, (vldoor_t: sector_t * swizzle), // T_SlidingDoor, (slidedoor_t: sector_t *, line_t * x 2 swizzle) [STRIFE] // T_MoveFloor, (floormove_t: sector_t * swizzle), // T_LightFlash, (lightflash_t: sector_t * swizzle), // T_StrobeFlash, (strobe_t: sector_t *), // T_Glow, (glow_t: sector_t *), // T_PlatRaise, (plat_t: sector_t *), - active list // void P_ArchiveSpecials (void) { thinker_t* th; int i; // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acv == (actionf_v)NULL) { for (i = 0; i < MAXCEILINGS;i++) if (activeceilings[i] == (ceiling_t *)th) break; if (ifunction.acp1 == (actionf_p1)T_MoveCeiling) { saveg_write8(tc_ceiling); saveg_write_pad(); saveg_write_ceiling_t((ceiling_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_VerticalDoor) { saveg_write8(tc_door); saveg_write_pad(); saveg_write_vldoor_t((vldoor_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_SlidingDoor) { saveg_write8(tc_slidingdoor); saveg_write_pad(); saveg_write_slidedoor_t((slidedoor_t *)th); continue; } if (th->function.acp1 == (actionf_p1)T_MoveFloor) { saveg_write8(tc_floor); saveg_write_pad(); saveg_write_floormove_t((floormove_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_PlatRaise) { saveg_write8(tc_plat); saveg_write_pad(); saveg_write_plat_t((plat_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_LightFlash) { saveg_write8(tc_flash); saveg_write_pad(); saveg_write_lightflash_t((lightflash_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_StrobeFlash) { saveg_write8(tc_strobe); saveg_write_pad(); saveg_write_strobe_t((strobe_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_Glow) { saveg_write8(tc_glow); saveg_write_pad(); saveg_write_glow_t((glow_t *) th); continue; } } // add a terminating marker saveg_write8(tc_endspecials); } // // P_UnArchiveSpecials // void P_UnArchiveSpecials (void) { byte tclass; ceiling_t* ceiling; vldoor_t* door; slidedoor_t* slidedoor; // haleyjd [STRIFE] floormove_t* floor; plat_t* plat; lightflash_t* flash; strobe_t* strobe; glow_t* glow; // read in saved thinkers while (1) { tclass = saveg_read8(); switch (tclass) { case tc_endspecials: return; // end of list case tc_ceiling: saveg_read_pad(); ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); saveg_read_ceiling_t(ceiling); ceiling->sector->specialdata = ceiling; if (ceiling->thinker.function.acp1) ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; P_AddThinker (&ceiling->thinker); P_AddActiveCeiling(ceiling); break; case tc_door: saveg_read_pad(); door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); saveg_read_vldoor_t(door); door->sector->specialdata = door; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; P_AddThinker (&door->thinker); break; case tc_slidingdoor: // haleyjd 09/29/10: [STRIFE] New thinker type for sliding doors saveg_read_pad(); slidedoor = Z_Malloc(sizeof(*slidedoor), PU_LEVEL, NULL); saveg_read_slidedoor_t(slidedoor); slidedoor->frontsector->specialdata = slidedoor; slidedoor->thinker.function.acp1 = (actionf_p1)T_SlidingDoor; P_AddThinker(&slidedoor->thinker); break; case tc_floor: saveg_read_pad(); floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); saveg_read_floormove_t(floor); floor->sector->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; P_AddThinker (&floor->thinker); break; case tc_plat: saveg_read_pad(); plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); saveg_read_plat_t(plat); plat->sector->specialdata = plat; if (plat->thinker.function.acp1) plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; P_AddThinker (&plat->thinker); P_AddActivePlat(plat); break; case tc_flash: saveg_read_pad(); flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); saveg_read_lightflash_t(flash); flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; P_AddThinker (&flash->thinker); break; case tc_strobe: saveg_read_pad(); strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); saveg_read_strobe_t(strobe); strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; P_AddThinker (&strobe->thinker); break; case tc_glow: saveg_read_pad(); glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); saveg_read_glow_t(glow); glow->thinker.function.acp1 = (actionf_p1)T_Glow; P_AddThinker (&glow->thinker); break; default: I_Error ("P_UnarchiveSpecials:Unknown tclass %i " "in savegame",tclass); } } } crispy-doom-crispy-doom-5.6.4/src/strife/p_saveg.h000066400000000000000000000030551360717211000221170ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Savegame I/O, archiving, persistence. // #ifndef __P_SAVEG__ #define __P_SAVEG__ #include // maximum size of a savegame description #define SAVESTRINGSIZE 24 // temporary filename to use while saving. char *P_TempSaveGameFile(void); // filename to use for a savegame slot char *P_SaveGameFile(int slot); // Savegame file header read/write functions boolean P_ReadSaveGameHeader(void); void P_WriteSaveGameHeader(char *description); // Savegame end-of-file read/write functions boolean P_ReadSaveGameEOF(void); void P_WriteSaveGameEOF(void); // Persistent storage/archiving. // These are the load / save game routines. void P_ArchivePlayers (void); void P_UnArchivePlayers (boolean userload); void P_ArchiveWorld (void); void P_UnArchiveWorld (void); void P_ArchiveThinkers (void); void P_UnArchiveThinkers (void); void P_ArchiveSpecials (void); void P_UnArchiveSpecials (void); extern FILE *save_stream; extern boolean savegame_error; #endif crispy-doom-crispy-doom-5.6.4/src/strife/p_setup.c000066400000000000000000000464711360717211000221560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Do all the WAD I/O, get map description, // set up initial state and misc. LUTs. // #include #include "z_zone.h" #include "deh_main.h" #include "i_swap.h" #include "m_argv.h" #include "m_bbox.h" #include "g_game.h" #include "i_system.h" #include "w_wad.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "doomstat.h" void P_SpawnMapThing (mapthing_t* mthing); // // 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; static int totallines; // BLOCKMAP // Created from axis aligned bounding box // of the map, a rectangular array of // blocks of size ... // Used to speed up collision detection // by spatial subdivision in 2D. // // Blockmap size. int bmapwidth; int bmapheight; // size in mapblocks short* blockmap; // int for larger maps // offsets in blockmap are from here short* blockmaplump; // origin of block map fixed_t bmaporgx; fixed_t bmaporgy; // for thing chains mobj_t** blocklinks; // REJECT // For fast sight rejection. // Speeds up enemy AI by skipping detailed // LineOf Sight calculation. // Without special effect, this could be // used as a PVS lookup as well. // byte* rejectmatrix; // Maintain single and multi player starting spots. #define MAX_DEATHMATCH_STARTS 10 mapthing_t deathmatchstarts[MAX_DEATHMATCH_STARTS]; mapthing_t* deathmatch_p; mapthing_t playerstarts[MAXPLAYERS]; // haleyjd 08/24/10: [STRIFE] rift spots for player spawning mapthing_t riftSpots[MAXRIFTSPOTS]; // // P_LoadVertexes // void P_LoadVertexes (int lump) { byte* data; int i; mapvertex_t* ml; vertex_t* li; // Determine number of lumps: // total lump length / vertex record length. numvertexes = W_LumpLength (lump) / sizeof(mapvertex_t); // Allocate zone memory for buffer. vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0); // Load data into cache. data = W_CacheLumpNum (lump, PU_STATIC); ml = (mapvertex_t *)data; li = vertexes; // Copy and convert vertex coordinates, // internal representation as fixed. for (i=0 ; ix = SHORT(ml->x)<y = SHORT(ml->y)<v1 = &vertexes[SHORT(ml->v1)]; li->v2 = &vertexes[SHORT(ml->v2)]; li->angle = (SHORT(ml->angle))<<16; li->offset = (SHORT(ml->offset))<<16; linedef = SHORT(ml->linedef); ldef = &lines[linedef]; li->linedef = ldef; side = SHORT(ml->side); li->sidedef = &sides[ldef->sidenum[side]]; li->frontsector = sides[ldef->sidenum[side]].sector; if (ldef-> flags & ML_TWOSIDED) { sidenum = ldef->sidenum[side ^ 1]; // If the sidenum is out of range, this may be a "glass hack" // impassible window. Point at side #0 (this may not be // the correct Vanilla behavior; however, it seems to work for // OTTAWAU.WAD, which is the one place I've seen this trick // used). if (sidenum < 0 || sidenum >= numsides) { sidenum = 0; } li->backsector = sides[sidenum].sector; } else { li->backsector = 0; } } W_ReleaseLumpNum(lump); } // // P_LoadSubsectors // void P_LoadSubsectors (int lump) { byte* data; int i; mapsubsector_t* ms; subsector_t* ss; numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t); subsectors = Z_Malloc (numsubsectors*sizeof(subsector_t),PU_LEVEL,0); data = W_CacheLumpNum (lump,PU_STATIC); ms = (mapsubsector_t *)data; memset (subsectors,0, numsubsectors*sizeof(subsector_t)); ss = subsectors; for (i=0 ; inumlines = SHORT(ms->numsegs); ss->firstline = SHORT(ms->firstseg); } W_ReleaseLumpNum(lump); } // // P_LoadSectors // void P_LoadSectors (int lump) { byte* data; int i; mapsector_t* ms; sector_t* ss; numsectors = W_LumpLength (lump) / sizeof(mapsector_t); sectors = Z_Malloc (numsectors*sizeof(sector_t),PU_LEVEL,0); memset (sectors, 0, numsectors*sizeof(sector_t)); data = W_CacheLumpNum (lump,PU_STATIC); ms = (mapsector_t *)data; ss = sectors; for (i=0 ; ifloorheight = SHORT(ms->floorheight)<ceilingheight = SHORT(ms->ceilingheight)<floorpic = R_FlatNumForName(ms->floorpic); ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); ss->lightlevel = SHORT(ms->lightlevel); ss->special = SHORT(ms->special); ss->tag = SHORT(ms->tag); ss->thinglist = NULL; } W_ReleaseLumpNum(lump); } // // P_LoadNodes // void P_LoadNodes (int lump) { byte* data; int i; int j; int k; mapnode_t* mn; node_t* no; numnodes = W_LumpLength (lump) / sizeof(mapnode_t); nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0); data = W_CacheLumpNum (lump,PU_STATIC); mn = (mapnode_t *)data; no = nodes; for (i=0 ; ix = SHORT(mn->x)<y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = SHORT(mn->children[j]); for (k=0 ; k<4 ; k++) no->bbox[j][k] = SHORT(mn->bbox[j][k])<type)) { case 68: // Arachnotron case 64: // Archvile case 88: // Boss Brain case 89: // Boss Shooter case 69: // Hell Knight case 67: // Mancubus case 71: // Pain Elemental case 65: // Former Human Commando case 66: // Revenant case 84: // Wolf SS spawn = false; break; } } if (spawn == false) break; */ // Do spawn all other stuff. spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); spawnthing.options = SHORT(mt->options); // haleyjd 08/24/2010: Special Strife checks if(spawnthing.type >= 118 && spawnthing.type < 128) { // initialize riftSpots int riftSpotNum = spawnthing.type - 118; riftSpots[riftSpotNum] = spawnthing; riftSpots[riftSpotNum].type = 1; } else if(spawnthing.type >= 9001 && spawnthing.type < 9011) { // STRIFE-TODO: mystery array of 90xx objects } else P_SpawnMapThing(&spawnthing); } W_ReleaseLumpNum(lump); } // // P_LoadLineDefs // Also counts secret lines for intermissions. // void P_LoadLineDefs (int lump) { byte* data; int i; maplinedef_t* mld; line_t* ld; vertex_t* v1; vertex_t* v2; numlines = W_LumpLength (lump) / sizeof(maplinedef_t); lines = Z_Malloc (numlines*sizeof(line_t),PU_LEVEL,0); memset (lines, 0, numlines*sizeof(line_t)); data = W_CacheLumpNum (lump,PU_STATIC); mld = (maplinedef_t *)data; ld = lines; for (i=0 ; iflags = SHORT(mld->flags); ld->special = SHORT(mld->special); ld->tag = SHORT(mld->tag); v1 = ld->v1 = &vertexes[SHORT(mld->v1)]; v2 = ld->v2 = &vertexes[SHORT(mld->v2)]; ld->dx = v2->x - v1->x; ld->dy = v2->y - v1->y; if (!ld->dx) ld->slopetype = ST_VERTICAL; else if (!ld->dy) ld->slopetype = ST_HORIZONTAL; else { if (FixedDiv (ld->dy , ld->dx) > 0) ld->slopetype = ST_POSITIVE; else ld->slopetype = ST_NEGATIVE; } if (v1->x < v2->x) { ld->bbox[BOXLEFT] = v1->x; ld->bbox[BOXRIGHT] = v2->x; } else { ld->bbox[BOXLEFT] = v2->x; ld->bbox[BOXRIGHT] = v1->x; } if (v1->y < v2->y) { ld->bbox[BOXBOTTOM] = v1->y; ld->bbox[BOXTOP] = v2->y; } else { ld->bbox[BOXBOTTOM] = v2->y; ld->bbox[BOXTOP] = v1->y; } ld->sidenum[0] = SHORT(mld->sidenum[0]); ld->sidenum[1] = SHORT(mld->sidenum[1]); if (ld->sidenum[0] != -1) ld->frontsector = sides[ld->sidenum[0]].sector; else ld->frontsector = 0; if (ld->sidenum[1] != -1) ld->backsector = sides[ld->sidenum[1]].sector; else ld->backsector = 0; } W_ReleaseLumpNum(lump); } // // P_LoadSideDefs // void P_LoadSideDefs (int lump) { byte* data; int i; mapsidedef_t* msd; side_t* sd; numsides = W_LumpLength (lump) / sizeof(mapsidedef_t); sides = Z_Malloc (numsides*sizeof(side_t),PU_LEVEL,0); memset (sides, 0, numsides*sizeof(side_t)); data = W_CacheLumpNum (lump,PU_STATIC); msd = (mapsidedef_t *)data; sd = sides; for (i=0 ; itextureoffset = SHORT(msd->textureoffset)<rowoffset = SHORT(msd->rowoffset)<toptexture = R_TextureNumForName(msd->toptexture); sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); sd->midtexture = R_TextureNumForName(msd->midtexture); sd->sector = §ors[SHORT(msd->sector)]; } W_ReleaseLumpNum(lump); } // // P_LoadBlockMap // void P_LoadBlockMap (int lump) { int i; int count; int lumplen; lumplen = W_LumpLength(lump); count = lumplen / 2; blockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL); W_ReadLump(lump, blockmaplump); blockmap = blockmaplump + 4; // Swap all short integers to native byte ordering. for (i=0; ifirstline]; ss->sector = seg->sidedef->sector; } // count number of lines in each sector li = lines; totallines = 0; for (i=0 ; ifrontsector->linecount++; if (li->backsector && li->backsector != li->frontsector) { li->backsector->linecount++; totallines++; } } // build line tables for each sector linebuffer = Z_Malloc (totallines*sizeof(line_t *), PU_LEVEL, 0); for (i=0; ifrontsector != NULL) { sector = li->frontsector; sector->lines[sector->linecount] = li; ++sector->linecount; } if (li->backsector != NULL && li->frontsector != li->backsector) { sector = li->backsector; sector->lines[sector->linecount] = li; ++sector->linecount; } } // Generate bounding boxes for sectors sector = sectors; for (i=0 ; ilinecount; j++) { li = sector->lines[j]; M_AddToBox (bbox, li->v1->x, li->v1->y); M_AddToBox (bbox, li->v2->x, li->v2->y); } // set the degenmobj_t to the middle of the bounding box sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2; sector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2; // adjust bounding box to map blocks block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT; block = block >= bmapheight ? bmapheight-1 : block; sector->blockbox[BOXTOP]=block; block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXBOTTOM]=block; block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT; block = block >= bmapwidth ? bmapwidth-1 : block; sector->blockbox[BOXRIGHT]=block; block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector->blockbox[BOXLEFT]=block; } } // Pad the REJECT lump with extra data when the lump is too small, // to simulate a REJECT buffer overflow in Vanilla Doom. static void PadRejectArray(byte *array, unsigned int len) { unsigned int i; unsigned int byte_num; byte *dest; unsigned int padvalue; // Values to pad the REJECT array with: unsigned int rejectpad[4] = { 0, // Size 0, // Part of z_zone block header 50, // PU_LEVEL 0x1d4a11 // DOOM_CONST_ZONEID }; rejectpad[0] = ((totallines * 4 + 3) & ~3) + 24; // Copy values from rejectpad into the destination array. dest = array; for (i=0; i> (byte_num * 8)) & 0xff; ++dest; } // We only have a limited pad size. Print a warning if the // REJECT lump is too small. if (len > sizeof(rejectpad)) { fprintf(stderr, "PadRejectArray: REJECT lump too short to pad! (%u > %i)\n", len, (int) sizeof(rejectpad)); // Pad remaining space with 0 (or 0xff, if specified on command line). if (M_CheckParm("-reject_pad_with_ff")) { padvalue = 0xff; } else { padvalue = 0xf00; } memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad)); } } static void P_LoadReject(int lumpnum) { int minlength; int lumplen; // Calculate the size that the REJECT lump *should* be. minlength = (numsectors * numsectors + 7) / 8; // If the lump meets the minimum length, it can be loaded directly. // Otherwise, we need to allocate a buffer of the correct size // and pad it with appropriate data. lumplen = W_LumpLength(lumpnum); if (lumplen >= minlength) { rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL); } else { rejectmatrix = Z_Malloc(minlength, PU_LEVEL, &rejectmatrix); W_ReadLump(lumpnum, rejectmatrix); PadRejectArray(rejectmatrix + lumplen, minlength - lumplen); } } // // P_SetupLevel // void P_SetupLevel ( int map, int playermask, skill_t skill) { int i; char lumpname[9]; int lumpnum; // haleyjd 20110205 [STRIFE]: removed totalitems and wminfo totalkills = totalsecret = 0; for (i=0 ; idx) { if (x==node->x) return 2; if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (x==node->y) return 2; if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); left = (node->dy>>FRACBITS) * (dx>>FRACBITS); right = (dy>>FRACBITS) * (node->dx>>FRACBITS); if (right < left) return 0; // front side if (left == right) return 2; return 1; // back side } // // P_InterceptVector2 // Returns the fractional intercept point // along the first divline. // This is only called by the addthings and addlines traversers. // // [STRIFE] Verified unmodified // fixed_t P_InterceptVector2 ( divline_t* v2, divline_t* v1 ) { fixed_t frac; fixed_t num; fixed_t den; den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy) + FixedMul ( (v2->y - v1->y)>>8 , v1->dx); frac = FixedDiv (num , den); return frac; } // // P_CrossSubsector // Returns true // if strace crosses the given subsector successfully. // // [STRIFE] Verified unmodified // boolean P_CrossSubsector (int num) { seg_t* seg; line_t* line; int s1; int s2; int count; subsector_t* sub; sector_t* front; sector_t* back; fixed_t opentop; fixed_t openbottom; divline_t divl; vertex_t* v1; vertex_t* v2; fixed_t frac; fixed_t slope; #ifdef RANGECHECK if (num>=numsubsectors) I_Error ("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors); #endif sub = &subsectors[num]; // check lines count = sub->numlines; seg = &segs[sub->firstline]; for ( ; count ; seg++, count--) { line = seg->linedef; // allready checked other side? if (line->validcount == validcount) continue; line->validcount = validcount; v1 = line->v1; v2 = line->v2; s1 = P_DivlineSide (v1->x, v1->y, &strace); s2 = P_DivlineSide (v2->x, v2->y, &strace); // line isn't crossed? if (s1 == s2) continue; divl.x = v1->x; divl.y = v1->y; divl.dx = v2->x - v1->x; divl.dy = v2->y - v1->y; s1 = P_DivlineSide (strace.x, strace.y, &divl); s2 = P_DivlineSide (t2x, t2y, &divl); // line isn't crossed? if (s1 == s2) continue; // Backsector may be NULL if this is an "impassible // glass" hack line. if (line->backsector == NULL) { return false; } // stop because it is not two sided anyway // might do this after updating validcount? if ( !(line->flags & ML_TWOSIDED) ) return false; // crosses a two sided line front = seg->frontsector; back = seg->backsector; // no wall to block sight with? if (front->floorheight == back->floorheight && front->ceilingheight == back->ceilingheight) continue; // possible occluder // because of ceiling height differences if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; // because of ceiling height differences if (front->floorheight > back->floorheight) openbottom = front->floorheight; else openbottom = back->floorheight; // quick test for totally closed doors if (openbottom >= opentop) return false; // stop frac = P_InterceptVector2 (&strace, &divl); if (front->floorheight != back->floorheight) { slope = FixedDiv (openbottom - sightzstart , frac); if (slope > bottomslope) bottomslope = slope; } if (front->ceilingheight != back->ceilingheight) { slope = FixedDiv (opentop - sightzstart , frac); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop } // passed the subsector ok return true; } // // P_CrossBSPNode // Returns true // if strace crosses the given node successfully. // // [STRIFE] Verified unmodified // boolean P_CrossBSPNode (int bspnum) { node_t* bsp; int side; if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) return P_CrossSubsector (0); else return P_CrossSubsector (bspnum&(~NF_SUBSECTOR)); } bsp = &nodes[bspnum]; // decide which side the start point is on side = P_DivlineSide (strace.x, strace.y, (divline_t *)bsp); if (side == 2) side = 0; // an "on" should cross both sides // cross the starting side if (!P_CrossBSPNode (bsp->children[side]) ) return false; // the partition plane is crossed here if (side == P_DivlineSide (t2x, t2y,(divline_t *)bsp)) { // the line doesn't touch the other side return true; } // cross the ending side return P_CrossBSPNode (bsp->children[side^1]); } // // P_CheckSight // Returns true // if a straight line between t1 and t2 is unobstructed. // Uses REJECT. // // [STRIFE] Verified unmodified // boolean P_CheckSight ( mobj_t* t1, mobj_t* t2 ) { int s1; int s2; int pnum; int bytenum; int bitnum; // First check for trivial rejection. // Determine subsector entries in REJECT table. s1 = (t1->subsector->sector - sectors); s2 = (t2->subsector->sector - sectors); pnum = s1*numsectors + s2; bytenum = pnum>>3; bitnum = 1 << (pnum&7); // Check in REJECT table. if (rejectmatrix[bytenum]&bitnum) { sightcounts[0]++; // can't possibly be connected return false; } // An unobstructed LOS is possible. // Now look from eyes of t1 to any part of t2. sightcounts[1]++; validcount++; sightzstart = t1->z + t1->height - (t1->height>>2); topslope = (t2->z+t2->height) - sightzstart; bottomslope = (t2->z) - sightzstart; strace.x = t1->x; strace.y = t1->y; t2x = t2->x; t2y = t2->y; strace.dx = t2->x - t1->x; strace.dy = t2->y - t1->y; // the head node is the last node output return P_CrossBSPNode (numnodes-1); } crispy-doom-crispy-doom-5.6.4/src/strife/p_spec.c000066400000000000000000001441511360717211000217420ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Implements special effects: // Texture animation, height or lighting changes // according to adjacent sectors, respective // utility functions, etc. // Line Tag handling. Line and Sector triggers. // #include #include "doomdef.h" #include "doomstat.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "m_argv.h" #include "m_misc.h" #include "m_random.h" #include "w_wad.h" #include "r_local.h" #include "p_local.h" #include "g_game.h" #include "s_sound.h" // State. #include "r_state.h" // Data. #include "sounds.h" // [STRIFE] #include "hu_stuff.h" #include "p_dialog.h" // // Animating textures and planes // There is another anim_t used in wi_stuff, unrelated. // typedef struct { boolean istexture; int picnum; int basepic; int numpics; int speed; } anim_t; // // source animation definition // typedef struct { int istexture; // if false, it is a flat char endname[9]; char startname[9]; int speed; } animdef_t; // haleyjd 08/30/10: [STRIFE] MAXANIMS raised from 32 to 40 #define MAXANIMS 40 // // P_InitPicAnims // // Floor/ceiling animation sequences, // defined by first and last frame, // i.e. the flat (64x64 tile) name to // be used. // The full animation sequence is given // using all the flats between the start // and end entry, in the order found in // the WAD file. // // haleyjd 08/29/10: [STRIFE] Changed animdefs. // animdef_t animdefs[] = { { false, "F_SCANR8", "F_SCANR5", 4}, { false, "F_WATR03", "F_WATR01", 8}, { false, "F_PWATR3", "F_PWATR1", 11}, { false, "F_SCANR4", "F_SCANR1", 4}, { true, "SCAN08", "SCAN05", 4}, { true, "SWTRMG03", "SWTRMG01", 4}, { true, "SCAN04", "SCAN01", 4}, { true, "COMP04", "COMP01", 4}, { true, "COMP08", "COMP05", 6}, { true, "COMP12", "COMP09", 11}, { true, "COMP16", "COMP13", 12}, { true, "COMP20", "COMP17", 12}, { true, "COMP24", "COMP21", 12}, { true, "COMP28", "COMP25", 12}, { true, "COMP32", "COMP29", 12}, { true, "COMP37", "COMP33", 12}, { true, "COMP41", "COMP38", 12}, { true, "COMP49", "COMP42", 10}, { true, "BRKGRY16", "BRKGRY13", 10}, { true, "BRNSCN04", "BRNSCN01", 10}, { true, "CONCRT12", "CONCRT09", 11}, { true, "CONCRT25", "CONCRT22", 11}, { true, "WALPMP02", "WALPMP01", 16}, { true, "WALTEK17", "WALTEK16", 8}, { true, "FORCE04", "FORCE01", 4}, { true, "FORCE08", "FORCE05", 4}, { true, "FAN02", "FAN01", 4}, { false, "F_VWATR3", "P_VWATR1", 4}, { false, "F_HWATR3", "F_HWATR1", 4}, { false, "F_TELE2", "F_TELE1", 4}, { false, "F_FAN2", "F_FAN1", 4}, { false, "F_CONVY2", "F_CONVY1", 4}, { false, "F_RDALN4", "F_RDALN1", 4}, { -1, "", "", 0}, }; anim_t anims[MAXANIMS]; anim_t* lastanim; // // Animating line specials // // haleyjd 08/29/10: [STRIFE] MAXLINEANIMS raised from 64 to 96 #define MAXLINEANIMS 96*256 extern short numlinespecials; extern line_t* linespeciallist[MAXLINEANIMS]; void P_InitPicAnims (void) { int i; // Init animation lastanim = anims; for (i=0 ; animdefs[i].istexture != -1 ; i++) { const char *startname, *endname; startname = DEH_String(animdefs[i].startname); endname = DEH_String(animdefs[i].endname); if (animdefs[i].istexture) { // different episode ? if (R_CheckTextureNumForName(startname) == -1) continue; lastanim->picnum = R_TextureNumForName(endname); lastanim->basepic = R_TextureNumForName(startname); } else { if (W_CheckNumForName(startname) == -1) continue; lastanim->picnum = R_FlatNumForName(endname); lastanim->basepic = R_FlatNumForName(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", startname, endname); lastanim->speed = animdefs[i].speed; lastanim++; } } // villsa [STRIFE] terrain type definitions typedef struct { const char *flat; int type; int num; } terraintype_t; terraintype_t terraintypes[] = { { "F_WATR03", FLOOR_WATER, -1 }, { "F_WATR02", FLOOR_WATER, -1 }, { "F_WATR01", FLOOR_WATER, -1 }, { "F_VWATR3", FLOOR_WATER, -1 }, { "F_VWATR2", FLOOR_WATER, -1 }, { "P_VWATR1", FLOOR_WATER, -1 }, { "F_HWATR3", FLOOR_WATER, -1 }, { "F_HWATR2", FLOOR_WATER, -1 }, { "F_HWATR1", FLOOR_WATER, -1 }, { "F_PWATR3", FLOOR_SLIME, -1 }, { "F_PWATR2", FLOOR_SLIME, -1 }, { "F_PWATR1", FLOOR_SLIME, -1 }, { "END", FLOOR_END, -1 }, }; // // P_GetTerrainType // villsa [STRIFE] new function // terraintype_e P_GetTerrainType(mobj_t* mobj) { int i = 0; subsector_t* ss = mobj->subsector; if(mobj->z <= ss->sector->floorheight && terraintypes[0].type != FLOOR_END) { while(ss->sector->floorpic != terraintypes[i].num) { if(terraintypes[i+1].type == FLOOR_END) return FLOOR_SOLID; i++; } return terraintypes[i].type; } return FLOOR_SOLID; } // // P_InitTerrainTypes // villsa [STRIFE] new function // Initialize terrain types // void P_InitTerrainTypes(void) { int i = 0; if(terraintypes[0].type != FLOOR_END) { while(terraintypes[i].type != FLOOR_END) { terraintypes[i].num = R_FlatNumForName(terraintypes[i].flat); i++; } } } // // UTILITIES // // // getSide() // Will return a side_t* // given the number of the current sector, // the line number, and the side (0/1) that you want. // side_t* getSide ( int currentSector, int line, int side ) { return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ]; } // // getSector() // Will return a sector_t* // given the number of the current sector, // the line number and the side (0/1) that you want. // sector_t* getSector ( int currentSector, int line, int side ) { return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector; } // // twoSided() // Given the sector number and the line number, // it will tell you whether the line is two-sided or not. // int twoSided ( int sector, int line ) { return (sectors[sector].lines[line])->flags & ML_TWOSIDED; } // // getNextSector() // Return sector_t * of sector next to current. // NULL if not two-sided line // sector_t* getNextSector ( line_t* line, sector_t* sec ) { if (!(line->flags & ML_TWOSIDED)) return NULL; if (line->frontsector == sec) return line->backsector; return line->frontsector; } // // P_FindLowestFloorSurrounding() // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS // fixed_t P_FindLowestFloorSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t floor = sec->floorheight; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight < floor) floor = other->floorheight; } return floor; } // // P_FindHighestFloorSurrounding() // FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS // fixed_t P_FindHighestFloorSurrounding(sector_t *sec) { int i; line_t* check; sector_t* other; fixed_t floor = -500*FRACUNIT; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight > floor) floor = other->floorheight; } return floor; } // // P_FindNextHighestFloor // FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS // Note: this should be doable w/o a fixed array. // Thanks to entryway for the Vanilla overflow emulation. // 20 adjoining sectors max! #define MAX_ADJOINING_SECTORS 20 fixed_t P_FindNextHighestFloor ( sector_t* sec, int currentheight ) { int i; int h; int min; line_t* check; sector_t* other; fixed_t height = currentheight; fixed_t heightlist[MAX_ADJOINING_SECTORS + 2]; for (i=0, h=0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight > height) { // Emulation of memory (stack) overflow if (h == MAX_ADJOINING_SECTORS + 1) { height = other->floorheight; } else if (h == MAX_ADJOINING_SECTORS + 2) { // Fatal overflow: game crashes at 22 sectors I_Error("Sector with more than 22 adjoining sectors. " "Vanilla will crash here"); } heightlist[h++] = other->floorheight; } } // Find lowest height in list if (!h) { return currentheight; } min = heightlist[0]; // Range checking? for (i = 1; i < h; i++) { if (heightlist[i] < min) { min = heightlist[i]; } } return min; } // // FIND LOWEST CEILING IN THE SURROUNDING SECTORS // fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t height = INT_MAX; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->ceilingheight < height) height = other->ceilingheight; } return height; } // // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS // fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t height = 0; for (i=0 ;i < sec->linecount ; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->ceilingheight > height) height = other->ceilingheight; } return height; } // // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO // int P_FindSectorFromLineTag ( line_t* line, int start ) { int i; for (i=start+1;itag) return i; return -1; } // // Find minimum light from an adjacent sector // int P_FindMinSurroundingLight ( sector_t* sector, int max ) { int i; int min; line_t* line; sector_t* check; min = max; for (i=0 ; i < sector->linecount ; i++) { line = sector->lines[i]; check = getNextSector(line,sector); if (!check) continue; if (check->lightlevel < min) min = check->lightlevel; } return min; } // // EVENTS // Events are operations triggered by using, crossing, // or shooting special lines, or by timed thinkers. // // [STRIFE] static char crosslinestr[90]; // // P_CrossSpecialLine - TRIGGER // Called every time a thing origin is about // to cross a line with a non 0 special. // void P_CrossSpecialLine ( int linenum, int side, mobj_t* thing ) { line_t* line; side_t* sidedef; // [STRIFE] int flag; // [STRIFE] int ok; line = &lines[linenum]; // haleyjd 09/21/10: corpses and missiles cannot activate any cross-over // line types, *except* 182 (which is for the sake of missiles). if((thing->flags & (MF_MISSILE|MF_CORPSE)) && line->special != 182) return; // Triggers that other things can activate if (!thing->player) { // Things that should NOT trigger specials... // villsa [STRIFE] unused // haleyjd: removed dead switch. Strife only excludes missiles and // corpses, which is handled above. ok = 0; // [STRIFE] Added several line types. Removed none. switch(line->special) { case 97: // TELEPORT RETRIGGER case 185: // haleyjd: [STRIFE] Silent Teleport (used for Converter) case 195: // haleyjd: [STRIFE] Silent Teleport and Change Zombie case 231: // haleyjd: [STRIFE] WR Teleport (Silent at Source) case 125: // TELEPORT MONSTERONLY TRIGGER case 126: // TELEPORT MONSTERONLY RETRIGGER case 182: // haleyjd: [STRIFE] Break glass - it's a W1 type too! case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER case 39: // TELEPORT TRIGGER case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER case 4: // RAISE DOOR ok = 1; break; } if (!ok) return; } // Note: could use some const's here. switch (line->special) { // // TRIGGERS. // All from here to RETRIGGERS. // case 230: // haleyjd 09/21/10: [STRIFE] W1 Open Door if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 2: // Open Door - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_open); line->special = 0; break; case 227: // haleyjd 09/21/10: [STRIFE] W1 Close Door if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 3: // Close Door - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_close); line->special = 0; break; case 4: // Raise Door - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_normal); line->special = 0; break; case 5: // Raise Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor); line->special = 0; break; case 6: // Fast Ceiling Crush & Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,fastCrushAndRaise); line->special = 0; break; case 8: // Build Stairs - [STRIFE] Verified unmodified. EV_BuildStairs(line,build8); line->special = 0; break; case 10: // PlatDownWaitUp - [STRIFE] Verified unmodified. EV_DoPlat(line,downWaitUpStay,0); line->special = 0; break; case 12: // Light Turn On - brightest near - [STRIFE] Verified unmodified. EV_LightTurnOn(line,0); line->special = 0; break; case 13: // Light Turn On 255 - [STRIFE] Verified unmodified. EV_LightTurnOn(line,255); line->special = 0; break; case 16: // Close Door 30 - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_close30ThenOpen); line->special = 0; break; case 17: // Start Light Strobing - [STRIFE] Verified unmodified. EV_StartLightStrobing(line); line->special = 0; break; case 19: // Lower Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerFloor); line->special = 0; break; case 22: // villsa [STRIFE] Verified unmodified. // Raise floor to nearest height and change texture EV_DoPlat(line,raiseToNearestAndChange,0); line->special = 0; break; case 25: // Ceiling Crush and Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,crushAndRaise); line->special = 0; break; case 30: // Raise floor to shortest texture height - [STRIFE] Verified unmodified. // on either side of lines. EV_DoFloor(line,raiseToTexture); line->special = 0; break; case 35: // Lights Very Dark - [STRIFE] Verified unmodified. EV_LightTurnOn(line,35); line->special = 0; break; case 36: // Lower Floor (TURBO) - [STRIFE] Verified unmodified. EV_DoFloor(line,turboLower); line->special = 0; break; case 37: // LowerAndChange - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerAndChange); line->special = 0; break; case 193: // haleyjd 09/21/10: [STRIFE] W1 Floor Lower to Lowest if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t // must have the questflag indicated in the line's y offset if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 38: // Lower Floor To Lowest - [STRIFE] Verified unmodified. EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break; case 39: // TELEPORT! - [STRIFE] Verified unmodified (except for 0 flags param) EV_Teleport( line, side, thing, TF_NORMAL ); line->special = 0; break; /*case 40: // RaiseCeilingLowerFloor EV_DoCeiling( line, raiseToHighest ); EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break;*/ case 44: // Ceiling Crush - [STRIFE] Verified unmodified. EV_DoCeiling( line, lowerAndCrush ); line->special = 0; break; case 52: // EXIT! - haleyjd 09/21/10: [STRIFE] Exit to level tag/100 G_ExitLevel (line->tag / 100); break; case 53: // Perpetual Platform Raise - [STRIFE] Verified unmodified. EV_DoPlat(line,perpetualRaise,0); line->special = 0; break; case 54: // Platform Stop - [STRIFE] Verified unmodified. EV_StopPlat(line); line->special = 0; break; case 56: // Raise Floor Crush - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorCrush); line->special = 0; break; case 57: // Ceiling Crush Stop - [STRIFE] Verified unmodified. EV_CeilingCrushStop(line); line->special = 0; break; case 58: // [STRIFE] raiseFloor24 was modified into raiseFloor64 // Raise Floor 64 EV_DoFloor(line,raiseFloor64); line->special = 0; break; case 59: // Raise Floor 24 And Change - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor24AndChange); line->special = 0; break; case 104: // Turn lights off in sector(tag) - [STRIFE] Verified unmodified. EV_TurnTagLightsOff(line); line->special = 0; break; case 108: // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,vld_blazeRaise); line->special = 0; break; case 109: // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,vld_blazeOpen); line->special = 0; break; case 100: // Build Stairs Turbo 16 - [STRIFE] Verified unmodified. EV_BuildStairs(line,turbo16); line->special = 0; break; case 197: // haleyjd 09/21/10: [STRIFE] Blazing Door Close if Has Sigil B if(thing->player->sigiltype <= 0) break; // fall-through: case 110: // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,vld_blazeClose); line->special = 0; break; case 119: // Raise floor to nearest surr. floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorToNearest); line->special = 0; break; case 121: // villsa [STRIFE] Verified unmodified. // Blazing PlatDownWaitUpStay EV_DoPlat(line,blazeDWUS,0); line->special = 0; break; case 124: // haleyjd 09/21/10: [STRIFE] W1 Start Finale // Altered from G_SecretExitLevel. G_StartFinale(); break; case 125: // TELEPORT MonsterONLY - [STRIFE] Verified unmodified // (except for 0 flags parameter) if (!thing->player) { EV_Teleport( line, side, thing, TF_NORMAL ); line->special = 0; } break; case 130: // Raise Floor Turbo - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorTurbo); line->special = 0; break; case 141: // Silent Ceiling Crush & Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,silentCrushAndRaise); line->special = 0; break; case 174: // villsa [STRIFE] Split Open EV_DoDoor(line, vld_splitOpen); line->special = 0; break; case 183: // villsa [STRIFE] Split Raise Nearest EV_DoDoor(line, vld_splitRaiseNearest); line->special = 0; break; case 178: // haleyjd 09/24/10: [STRIFE] W1 Build Stairs Down 16 EV_BuildStairs(line, buildDown16); line->special = 0; break; case 179: // haleyjd 09/25/10: [STRIFE] W1 Ceiling Lower to Floor EV_DoCeiling(line, lowerToFloor); line->special = 0; break; case 182: // haleyjd 09/21/10: [STRIFE] Break Glass // 182 is a unique linetype in that it is both a G1 and a W1 linetype, // but only missiles may activate it as a W1 type. if(thing->flags & MF_MISSILE) P_ChangeSwitchTexture(line, 1); // why 1? it will be cleared anyway. break; case 187: // haleyjd 09/21/10: [STRIFE] W1 Clear Force Fields if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t // must have the questflag indicated in the line's y offset if(!(thing->player->questflags & (1 << flag))) break; // Do it! EV_ClearForceFields(line); line->special = 0; break; case 188: // haleyjd 09/21/10: [STRIFE] W1 Open Door if Quest 16 (Gate Mechanism // Destroyed) if(!(thing->player->questflags & QF_QUEST16)) break; EV_DoDoor(line, vld_open); line->special = 0; break; case 196: // haleyjd 09/26/10: [STRIFE] W1 Floor Lower to Lowest if Sigil Type > 0 if(thing->player->sigiltype > 0) { EV_DoFloor(line, lowerFloorToLowest); line->special = 0; } break; case 200: // haleyjd 09/21/10: [STRIFE] W1 Open Door if Sigil Owned if(!(thing->player->weaponowned[wp_sigil])) break; EV_DoDoor(line, vld_open); line->special = 0; break; case 201: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective (First Side Only) if(side == 1) break; // fall-through: case 202: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective (Tag = VOC/LOG #) // must be consoleplayer if(thing->player != &players[consoleplayer]) break; // must have comm unit if(!(thing->player->powers[pw_communicator])) break; // load voice DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag); I_StartVoice(crosslinestr); // load objective DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag); GiveObjective(crosslinestr, 0); // Put up a message thing->player->message = DEH_String("Incoming Message..."); line->special = 0; break; case 210: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective if Flamethrower???? // I don't think this is actually used anywhere o_O // must be player 1... if(thing->player != &players[0]) break; // must have comm unit if(!(thing->player->powers[pw_communicator])) break; // must have... the flamethrower?! if(!(thing->player->weaponowned[wp_flame])) break; // load voice DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag); I_StartVoice(crosslinestr); // load objective DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag); GiveObjective(crosslinestr, 0); // Put up a message thing->player->message = DEH_String("Incoming Message from BlackBird..."); line->special = 0; break; case 212: // haleyjd 09/25/10: [STRIFE] W1 Floor Lower to Lowest if Have Flamethrower if(thing->player->weaponowned[wp_flame]) { EV_DoFloor(line, lowerFloorToLowest); line->special = 0; } break; case 215: // haleyjd 09/21/10: [STRIFE] W1 Voiced Objective if Quest (Tag/100, Tag%100) // must be player 1... if(thing->player != &players[0]) break; // must have comm unit if(!(thing->player->powers[pw_communicator])) break; if(line->tag != 0) { // test for questflag if(!(thing->player->questflags & (1 << (line->tag % 100 - 1)))) break; } // start voice DEH_snprintf(crosslinestr, sizeof(crosslinestr), "voc%i", line->tag/100); I_StartVoice(crosslinestr); // give objective DEH_snprintf(crosslinestr, sizeof(crosslinestr), "log%i", line->tag/100); GiveObjective(crosslinestr, 0); // Put up a message thing->player->message = DEH_String("Incoming Message from BlackBird..."); line->special = 0; break; case 204: // haleyjd 09/21/10: [STRIFE] W1 Change Music (unused!) if(thing->player != &players[0]) break; S_ChangeMusic(line->tag, 1); line->special = 0; break; case 228: // haleyjd 09/21/10: [STRIFE] W1 Entity Voice? if(!(thing->player->questflags & QF_QUEST24)) // Not killed Macil??? break; // STRIFE-TODO: verify... if(!(thing->player->questflags & QF_QUEST28)) // ????? STRIFE-TODO I_StartVoice(DEH_String("voc128")); else I_StartVoice(DEH_String("voc130")); line->special = 0; break; // // RETRIGGERS. All from here till end. // case 72: // Ceiling Crush - [STRIFE] Verified unmodified. EV_DoCeiling( line, lowerAndCrush ); break; case 73: // Ceiling Crush and Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,crushAndRaise); break; case 74: // Ceiling Crush Stop - [STRIFE] Verified unmodified. EV_CeilingCrushStop(line); break; case 75: // Close Door - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_close); break; case 76: // Close Door 30 - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_close30ThenOpen); break; case 77: // Fast Ceiling Crush & Raise - [STRIFE] Verified unmodified. EV_DoCeiling(line,fastCrushAndRaise); break; case 79: // Lights Very Dark - [STRIFE] Verified unmodified. EV_LightTurnOn(line,35); break; case 80: // Light Turn On - brightest near - [STRIFE] Verified unmodified. EV_LightTurnOn(line,0); break; case 81: // Light Turn On 255 - [STRIFE] Verified unmodified. EV_LightTurnOn(line,255); break; case 82: // Lower Floor To Lowest - [STRIFE] Verified unmodified. EV_DoFloor( line, lowerFloorToLowest ); break; case 83: // Lower Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerFloor); break; case 84: // LowerAndChange - [STRIFE] Verified unmodified. EV_DoFloor(line,lowerAndChange); break; case 86: // Open Door - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_open); break; case 87: // Perpetual Platform Raise - [STRIFE] Verified unmodified. EV_DoPlat(line,perpetualRaise,0); break; case 88: // PlatDownWaitUp - [STRIFE] Verified unmodified. EV_DoPlat(line,downWaitUpStay,0); break; case 89: // Platform Stop - [STRIFE] Verified unmodified. EV_StopPlat(line); break; case 216: // haleyjd 09/21/10: [STRIFE] WR Raise Door if Quest sidedef = &sides[line->sidenum[0]]; flag = (sidedef->rowoffset >> FRACBITS) - 1; // note is fixed_t. if(!(thing->player->questflags & (1 << flag))) break; // fall-through: case 90: // Raise Door - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_normal); break; case 91: // Raise Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor); break; case 92: // [STRIFE] raiseFloor24 changed to raiseFloor64 // Raise Floor 64 EV_DoFloor(line,raiseFloor64); break; case 93: // Raise Floor 24 And Change - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloor24AndChange); break; case 94: // Raise Floor Crush - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorCrush); break; case 95: // villsa [STRIFE] Verified unmodified. // Raise floor to nearest height // and change texture. EV_DoPlat(line,raiseToNearestAndChange,0); break; case 96: // Raise floor to shortest texture height - [STRIFE] Verified unmodified. // on either side of lines. EV_DoFloor(line,raiseToTexture); break; case 97: // TELEPORT! - [STRIFE] Verified unmodified (except for 0 flags param) EV_Teleport( line, side, thing, TF_NORMAL ); break; case 98: // Lower Floor (TURBO) - [STRIFE] Verified unmodified. EV_DoFloor(line,turboLower); break; case 105: // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,vld_blazeRaise); break; case 106: // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,vld_blazeOpen); break; case 107: // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified. EV_DoDoor (line,vld_blazeClose); break; case 120: // villsa [STRIFE] Verified unmodified. // Blazing PlatDownWaitUpStay. EV_DoPlat(line,blazeDWUS,0); break; case 126: // TELEPORT MonsterONLY. - [STRIFE] Verified unmodified (except for 0 flags param) if (!thing->player) EV_Teleport( line, side, thing, TF_NORMAL ); break; case 128: // Raise To Nearest Floor - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorToNearest); break; case 129: // Raise Floor Turbo - [STRIFE] Verified unmodified. EV_DoFloor(line,raiseFloorTurbo); break; case 186: // haleyjd [STRIFE] Exit Level to Spot, First Side Only if(side == 1) break; // fall-through: case 145: // haleyjd [STRIFE] Exit Level to Spot thing->momx = thing->momy = thing->momz = 0; { int map = line->tag / 100; int spot = line->tag % 100; if(thing->player->weaponowned[wp_sigil]) { if(map == 3) map = 30; else if(map == 7) map = 10; } DEH_snprintf(crosslinestr, sizeof(crosslinestr), "Entering%s", DEH_String(mapnames[map - 1]) + 8); thing->player->message = crosslinestr; if(netgame && deathmatch) { if(levelTimer && levelTimeCount != 0) { DEH_snprintf(crosslinestr, sizeof(crosslinestr), "%d min left", (levelTimeCount/TICRATE)/60); break; } // raise switch from floor EV_DoFloor(line, raiseFloor64); } else { // normal single-player exit // BUG: Here is the opening for a flaming player to cross past // the exit line and hit a deathmatch switch ;) It's not so much // that this is incorrect, as that they forgot to add such a // check to the other kind of exit lines too ;) if(thing->player->health <= 0) break; G_RiftExitLevel(map, spot, thing->angle); } } break; case 175: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if < 16 Above Floor if(thing->z < thing->floorz + 16 * FRACUNIT) P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 198: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if No Guard Uniform if(P_PlayerHasItem(thing->player, MT_QUEST_GUARD_UNIFORM)) break; // fall-through: case 150: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 208: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if Have Flamethrower // O_o - this is definitely unused. Was an entire flamethrower quest // cut out of the game before release? if(thing->player->weaponowned[wp_flame]) P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 206: // haleyjd 09/21/10: [STRIFE] WR Raise Alarm if Have Chalice // This *is* used, inside the Tavern in Tarnhill. Oddly there is also // one just randomly placed outside the entrance to the Power Station. if(P_PlayerHasItem(thing->player, MT_INV_CHALICE)) P_NoiseAlert(thing->player->mo, thing->player->mo); break; case 184: // villsa [STRIFE] plat up wait down stay if(EV_DoPlat(line, upWaitDownStay, 0)) P_ChangeSwitchTexture(line, 1); // In P_CrossSpecialLine? Copypasta error? break; case 185: // haleyjd 09/21/10: [STRIFE] Silent Teleport (used for Converter) EV_Teleport(line, side, thing, TF_FULLSILENCE); break; case 195: // haleyjd 09/21/10: [STRIFE] Silent Teleport and Change Zombie EV_Teleport(line, side, thing, TF_FULLSILENCE); P_SetMobjState(thing, S_AGRD_00); // 419 break; case 203: // haleyjd 09/21/10: [STRIFE] WR Change Music if(thing->player != &players[0]) break; S_ChangeMusic(line->tag, 1); break; case 231: // haleyjd 09/21/10: [STRIFE] WR Teleport (Silent at Source) EV_Teleport(line, side, thing, TF_SRCSILENCE); break; // haleyjd 09/21/10: Moved one-time-use lines up above with the others. } } // // P_ShootSpecialLine - IMPACT SPECIALS // Called when a thing shoots a special line. // void P_ShootSpecialLine ( mobj_t* thing, line_t* line ) { int ok; // Impacts that other things can activate. if (!thing->player) { ok = 0; switch(line->special) { case 46: // OPEN DOOR IMPACT case 182: // villsa [STRIFE] for windows ok = 1; break; } if (!ok) return; } switch(line->special) { case 24: // RAISE FLOOR - [STRIFE] Verified unmodified EV_DoFloor(line,raiseFloor); P_ChangeSwitchTexture(line,0); break; case 46: // OPEN DOOR - [STRIFE] Verified unmodified. EV_DoDoor(line,vld_open); P_ChangeSwitchTexture(line,1); break; case 47: // villsa [STRIFE] Verified unmodified. // RAISE FLOOR NEAR AND CHANGE EV_DoPlat(line,raiseToNearestAndChange,0); P_ChangeSwitchTexture(line,0); break; case 180: // haleyjd 09/22/10: [STRIFE] G1 Raise Floor 512 & Change EV_DoFloor(line, raiseFloor512AndChange); P_ChangeSwitchTexture(line, 0); break; case 182: // villsa [STRIFE] G1 Break Glass // haleyjd: note that 182 is also a W1 type in P_CrossSpecialLine, but // can only be activated in that manner by an MF_MISSILE object. P_ChangeSwitchTexture(line, 0); break; } } // // P_PlayerInSpecialSector // Called every tic frame // that the player origin is in a special sector // // [STRIFE] Modified for new sector types and changes to old ones. // void P_PlayerInSpecialSector (player_t* player) { sector_t* sector; sector = player->mo->subsector->sector; // Falling, not all the way down yet? if (player->mo->z != sector->floorheight) return; // Has hitten ground. switch (sector->special) { case 5: // HELLSLIME DAMAGE // [STRIFE] +2 to nukagecount if(!player->powers[pw_ironfeet]) player->nukagecount += 2; break; case 16: // [STRIFE] +4 to nukagecount if(!player->powers[pw_ironfeet]) player->nukagecount += 4; break; case 4: case 7: // [STRIFE] Immediate 5 damage every 31 tics if(!player->powers[pw_ironfeet]) if(!(leveltime & 0x1f)) P_DamageMobj(player->mo, NULL, NULL, 5); break; case 9: // SECRET SECTOR //player->secretcount++; [STRIFE] Don't have a secret count. sector->special = 0; if(player - players == consoleplayer) S_StartSound(NULL, sfx_yeah); break; case 11: // EXIT SUPER DAMAGE! (for E1M8 finale) player->cheats &= ~CF_GODMODE; if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 20); if (player->health <= 10) G_ExitLevel(0); break; case 15: // haleyjd 08/30/10: [STRIFE] "Instant" Death sector P_DamageMobj(player->mo, NULL, NULL, 999); break; case 18: // haleyjd 08/30/10: [STRIFE] Water current { int tagval = sector->tag - 100; fixed_t force; angle_t angle; if(player->cheats & CF_NOCLIP) return; force = (tagval % 10) << 12; angle = (tagval / 10) << 29; P_Thrust(player, angle, force); } break; default: I_Error ("P_PlayerInSpecialSector: " "unknown special %i", sector->special); break; }; } // // P_UpdateSpecials // Animate planes, scroll walls, etc. // // [STRIFE] Modifications to support multiple scrolling line types. // boolean levelTimer; int levelTimeCount; void P_UpdateSpecials (void) { anim_t* anim; int pic; int i; line_t* line; // LEVEL TIMER if (levelTimer == true) { if(levelTimeCount) // [STRIFE] Does not allow to go negative levelTimeCount--; /* // [STRIFE] Not done here. Exit lines check this manually instead. if (!levelTimeCount) G_ExitLevel(0); */ } // 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; } } // ANIMATE LINE SPECIALS for (i = 0; i < numlinespecials; i++) { line = linespeciallist[i]; switch(line->special) { case 48: // EFFECT FIRSTCOL SCROLL + sides[line->sidenum[0]].textureoffset += FRACUNIT; break; case 142: // haleyjd 09/25/10 [STRIFE] Scroll Up Slow sides[line->sidenum[0]].rowoffset += FRACUNIT; break; case 143: // haleyjd 09/25/10 [STRIFE] Scroll Down Fast (3 Units/Tic) sides[line->sidenum[0]].rowoffset -= 3*FRACUNIT; break; case 149: // haleyjd 09/25/10 [STRIFE] Scroll Down Slow sides[line->sidenum[0]].rowoffset -= FRACUNIT; break; } } // DO BUTTONS for (i = 0; i < MAXBUTTONS; i++) if (buttonlist[i].btimer) { buttonlist[i].btimer--; if (!buttonlist[i].btimer) { switch(buttonlist[i].where) { case top: sides[buttonlist[i].line->sidenum[0]].toptexture = buttonlist[i].btexture; break; case middle: sides[buttonlist[i].line->sidenum[0]].midtexture = buttonlist[i].btexture; break; case bottom: sides[buttonlist[i].line->sidenum[0]].bottomtexture = buttonlist[i].btexture; break; } S_StartSound(&buttonlist[i].soundorg,sfx_swtchn); memset(&buttonlist[i],0,sizeof(button_t)); } } } // // Donut overrun emulation // // Derived from the code from PrBoom+. Thanks go to Andrey Budko (entryway) // as usual :-) // #define DONUT_FLOORHEIGHT_DEFAULT 0x00000000 #define DONUT_FLOORPIC_DEFAULT 0x16 static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic, line_t *line, sector_t *pillar_sector) { static int first = 1; static int tmp_s3_floorheight; static int tmp_s3_floorpic; extern int numflats; if (first) { int p; // This is the first time we have had an overrun. first = 0; // Default values tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT; tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; //! // @category compat // @arg // // Use the specified magic values when emulating behavior caused // by memory overruns from improperly constructed donuts. // In Vanilla Strife this can differ depending on the operating // system. The default (if this option is not specified) is to // emulate the behavior when running under Windows 98. p = M_CheckParmWithArgs("-donut", 2); if (p > 0) { // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008 // // C:\>debug // -d 0:0 // // DOS 6.22: // 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) // DOS 7.1: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // Win98: // 0000:0000 (00 00 00 00) 65 04 70 00-(16 00) // DOSBox under XP: // 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) M_StrToInt(myargv[p + 1], &tmp_s3_floorheight); M_StrToInt(myargv[p + 2], &tmp_s3_floorpic); if (tmp_s3_floorpic >= numflats) { fprintf(stderr, "DonutOverrun: The second parameter for \"-donut\" " "switch should be greater than 0 and less than number " "of flats (%d). Using default value (%d) instead. \n", numflats, DONUT_FLOORPIC_DEFAULT); tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; } } } /* fprintf(stderr, "Linedef: %d; Sector: %d; " "New floor height: %d; New floor pic: %d\n", line->iLineID, pillar_sector->iSectorID, tmp_s3_floorheight >> 16, tmp_s3_floorpic); */ *s3_floorheight = (fixed_t) tmp_s3_floorheight; *s3_floorpic = (short) tmp_s3_floorpic; } // // Special Stuff that can not be categorized // int EV_DoDonut(line_t* line) { sector_t* s1; sector_t* s2; sector_t* s3; int secnum; int rtn; int i; floormove_t* floor; fixed_t s3_floorheight; short s3_floorpic; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { s1 = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (s1->specialdata) continue; rtn = 1; s2 = getNextSector(s1->lines[0],s1); // Vanilla Doom does not check if the linedef is one sided. The // game does not crash, but reads invalid memory and causes the // sector floor to move "down" to some unknown height. // DOSbox prints a warning about an invalid memory access. // // I'm not sure exactly what invalid memory is being read. This // isn't something that should be done, anyway. // Just print a warning and return. if (s2 == NULL) { fprintf(stderr, "EV_DoDonut: linedef had no second sidedef! " "Unexpected behavior may occur in Vanilla Doom. \n"); break; } for (i = 0; i < s2->linecount; i++) { s3 = s2->lines[i]->backsector; if (s3 == s1) continue; if (s3 == NULL) { // e6y // s3 is NULL, so // s3->floorheight is an int at 0000:0000 // s3->floorpic is a short at 0000:0008 // Trying to emulate fprintf(stderr, "EV_DoDonut: WARNING: emulating buffer overrun due to " "NULL back sector. " "Unexpected behavior may occur in Vanilla Doom.\n"); DonutOverrun(&s3_floorheight, &s3_floorpic, line, s1); } else { s3_floorheight = s3->floorheight; s3_floorpic = s3->floorpic; } // Spawn rising slime floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s2->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = donutRaise; floor->crush = false; floor->direction = 1; floor->sector = s2; floor->speed = FLOORSPEED / 2; floor->texture = s3_floorpic; floor->newspecial = 0; floor->floordestheight = s3_floorheight; // Spawn lowering donut-hole floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s1->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = lowerFloor; floor->crush = false; floor->direction = -1; floor->sector = s1; floor->speed = FLOORSPEED / 2; floor->floordestheight = s3_floorheight; break; } } return rtn; } // // SPECIAL SPAWNING // // // P_SpawnSpecials // After the map has been loaded, scan for specials // that spawn thinkers // short numlinespecials; line_t* linespeciallist[MAXLINEANIMS]; // Parses command line parameters. // // haleyjd 09/25/10: [STRIFE] Modifications for more scrolling line types and // for initialization of sliding door resources. // void P_SpawnSpecials (void) { sector_t* sector; int i; // See if -TIMER was specified. if (timelimit > 0 && deathmatch) { levelTimer = true; levelTimeCount = timelimit * 60 * TICRATE; } else { levelTimer = false; } // Init special SECTORs - [STRIFE] Verified unmodified. sector = sectors; for (i=0 ; ispecial) continue; switch (sector->special) { case 1: // FLICKERING LIGHTS P_SpawnLightFlash (sector); break; case 2: // STROBE FAST P_SpawnStrobeFlash(sector,FASTDARK,0); break; case 3: // STROBE SLOW P_SpawnStrobeFlash(sector,SLOWDARK,0); break; case 4: // STROBE FAST/DEATH SLIME P_SpawnStrobeFlash(sector,FASTDARK,0); sector->special = 4; break; case 8: // GLOWING LIGHT P_SpawnGlowingLight(sector); break; case 9: // SECRET SECTOR totalsecret++; break; case 10: // DOOR CLOSE IN 30 SECONDS P_SpawnDoorCloseIn30 (sector); break; case 12: // SYNC STROBE SLOW P_SpawnStrobeFlash (sector, SLOWDARK, 1); break; case 13: // SYNC STROBE FAST P_SpawnStrobeFlash (sector, FASTDARK, 1); break; case 14: // DOOR RAISE IN 5 MINUTES P_SpawnDoorRaiseIn5Mins (sector, i); break; case 17: P_SpawnFireFlicker(sector); break; } } // Init line EFFECTs numlinespecials = 0; for (i = 0;i < numlines; i++) { switch(lines[i].special) { case 48: // EFFECT FIRSTCOL SCROLL+ case 142: case 143: case 149: linespeciallist[numlinespecials] = &lines[i]; numlinespecials++; break; } } // Init other misc stuff for (i = 0;i < MAXCEILINGS;i++) activeceilings[i] = NULL; for (i = 0;i < MAXPLATS;i++) activeplats[i] = NULL; for (i = 0;i < MAXBUTTONS;i++) memset(&buttonlist[i],0,sizeof(button_t)); // villsa [STRIFE] P_InitSlidingDoorFrames(); } crispy-doom-crispy-doom-5.6.4/src/strife/p_spec.h000066400000000000000000000261121360717211000217430ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // Implements special effects: // Texture animation, height or lighting changes // according to adjacent sectors, respective // utility functions, etc. // #ifndef __P_SPEC__ #define __P_SPEC__ // // End-level timer (-TIMER option) // extern boolean levelTimer; extern int levelTimeCount; // Define values for map objects #define MO_TELEPORTMAN 14 // at game start void P_InitPicAnims (void); // villsa [STRIFE] typedef enum { FLOOR_WATER = 0, FLOOR_SLIME = 1, FLOOR_SOLID = 2, FLOOR_END = -1 } terraintype_e; void P_InitTerrainTypes(void); // villsa [STRIFE] terraintype_e P_GetTerrainType(mobj_t* mobj); // villsa [STRIFE] // at map load void P_SpawnSpecials (void); // every tic void P_UpdateSpecials (void); // when needed boolean P_UseSpecialLine ( mobj_t* thing, line_t* line, int side ); void P_ShootSpecialLine ( mobj_t* thing, line_t* line ); void P_CrossSpecialLine ( int linenum, int side, mobj_t* thing ); void P_PlayerInSpecialSector (player_t* player); int twoSided ( int sector, int line ); sector_t* getSector ( int currentSector, int line, int side ); side_t* getSide ( int currentSector, int line, int side ); fixed_t P_FindLowestFloorSurrounding(sector_t* sec); fixed_t P_FindHighestFloorSurrounding(sector_t* sec); fixed_t P_FindNextHighestFloor ( sector_t* sec, int currentheight ); fixed_t P_FindLowestCeilingSurrounding(sector_t* sec); fixed_t P_FindHighestCeilingSurrounding(sector_t* sec); int P_FindSectorFromLineTag ( line_t* line, int start ); int P_FindMinSurroundingLight ( sector_t* sector, int max ); sector_t* getNextSector ( line_t* line, sector_t* sec ); // // SPECIAL // int EV_DoDonut(line_t* line); boolean EV_ClearForceFields(line_t* line); // villsa [STRIFE] // // P_LIGHTS // typedef struct { thinker_t thinker; sector_t* sector; int count; int maxlight; int minlight; } fireflicker_t; typedef struct { thinker_t thinker; sector_t* sector; int count; int maxlight; int minlight; int maxtime; int mintime; } lightflash_t; typedef struct { thinker_t thinker; sector_t* sector; int count; int minlight; int maxlight; int darktime; int brighttime; } strobe_t; typedef struct { thinker_t thinker; sector_t* sector; int minlight; int maxlight; int direction; } glow_t; #define GLOWSPEED 8 #define STROBEBRIGHT 5 #define FASTDARK 15 #define SLOWDARK 35 void P_SpawnFireFlicker (sector_t* sector); void T_LightFlash (lightflash_t* flash); void P_SpawnLightFlash (sector_t* sector); void T_StrobeFlash (strobe_t* flash); void P_SpawnStrobeFlash ( sector_t* sector, int fastOrSlow, int inSync ); void EV_StartLightStrobing(line_t* line); void EV_TurnTagLightsOff(line_t* line); void EV_LightTurnOn ( line_t* line, int bright ); void T_Glow(glow_t* g); void P_SpawnGlowingLight(sector_t* sector); // // P_SWITCH // typedef struct { char name1[9]; char name2[9]; short episode; int sound; // villsa [STRIFE] } switchlist_t; typedef enum { top, middle, bottom } bwhere_e; typedef struct { line_t* line; bwhere_e where; int btexture; int btimer; degenmobj_t *soundorg; } button_t; // max # of wall switches in a level #define MAXSWITCHES 80 // villsa [STRIFE] changed from 50 to 80 // 4 players, 4 buttons each at once, max. #define MAXBUTTONS 16 // 1 second, in ticks. #define BUTTONTIME 35 extern button_t buttonlist[MAXBUTTONS]; void P_ChangeSwitchTexture ( line_t* line, int useAgain ); void P_InitSwitchList(void); // // P_PLATS // typedef enum { up, down, waiting, in_stasis } plat_e; typedef enum { perpetualRaise, downWaitUpStay, slowDWUS, // villsa [STRIFE] raiseAndChange, raiseToNearestAndChange, blazeDWUS, upWaitDownStay // villsa [STRIFE] } plattype_e; typedef struct { thinker_t thinker; sector_t* sector; fixed_t speed; fixed_t low; fixed_t high; int wait; int count; plat_e status; plat_e oldstatus; boolean crush; int tag; plattype_e type; } plat_t; #define PLATWAIT 3 #define PLATSPEED FRACUNIT #define MAXPLATS 30*256 extern plat_t* activeplats[MAXPLATS]; void T_PlatRaise(plat_t* plat); int EV_DoPlat ( line_t* line, plattype_e type, int amount ); void P_AddActivePlat(plat_t* plat); void P_RemoveActivePlat(plat_t* plat); void EV_StopPlat(line_t* line); void P_ActivateInStasis(int tag); // // P_DOORS // typedef enum { vld_normal, vld_close30ThenOpen, vld_close, vld_open, vld_raiseIn5Mins, vld_blazeRaise, vld_blazeOpen, vld_blazeClose, vld_shopClose, // villsa [STRIFE] vld_splitRaiseNearest, // villsa [STRIFE] vld_splitOpen // villsa [STRIFE] } vldoor_e; typedef struct { thinker_t thinker; vldoor_e type; sector_t* sector; fixed_t topheight; fixed_t speed; // 1 = up, 0 = waiting at top, -1 = down int direction; // tics to wait at the top int topwait; // (keep in case a door going down is reset) // when it reaches 0, start going down int topcountdown; // villsa [STRIFE] new field - sound to play when opening int opensound; // villsa [STRIFE] new field - sound to play when closing int closesound; } vldoor_t; #define VDOORSPEED FRACUNIT*2 #define VDOORWAIT 150 void EV_VerticalDoor ( line_t* line, mobj_t* thing ); int EV_DoDoor ( line_t* line, vldoor_e type ); int EV_DoLockedDoor ( line_t* line, vldoor_e type, mobj_t* thing ); void T_VerticalDoor (vldoor_t* door); void P_SpawnDoorCloseIn30 (sector_t* sec); void P_SpawnDoorRaiseIn5Mins ( sector_t* sec, int secnum ); // villsa [STRIFE] resurrected sliding doors // // Sliding doors... // typedef enum { sd_opening, sd_waiting, sd_closing } sd_e; typedef enum { sdt_openOnly, sdt_closeOnly, sdt_openAndClose } sdt_e; // villsa [STRIFE] Rogue added a second line_t in the struct // backsector is removed typedef struct { thinker_t thinker; sdt_e type; line_t* line1; line_t* line2; int frame; int whichDoorIndex; int timer; sector_t* frontsector; sd_e status; } slidedoor_t; // villsa [STRIFE] no front/back frames typedef struct { char frame1[9]; char frame2[9]; char frame3[9]; char frame4[9]; char frame5[9]; char frame6[9]; char frame7[9]; char frame8[9]; } slidename_t; // villsa [STRIFE] no front/back frames typedef struct { int frames[8]; } slideframe_t; // haleyjd 09/29/10: [STRIFE] Externalized for savegames void T_SlidingDoor(slidedoor_t* door); // how many frames of animation #define SNUMFRAMES 8 // villsa [STRIFE] changed from 4 to 8 #define SDOORWAIT TICRATE*3 #define SWAITTICS 4 // how many diff. types of anims #define MAXSLIDEDOORS 8 // villsa [STRIFE] changed from 5 to 8 void P_InitSlidingDoorFrames(void); void EV_SlidingDoor(line_t* line, mobj_t* thing); int EV_RemoteSlidingDoor(line_t* line, mobj_t* thing); // // P_CEILNG // typedef enum { lowerToFloor, raiseToHighest, lowerAndCrush, crushAndRaise, fastCrushAndRaise, silentCrushAndRaise } ceiling_e; typedef struct { thinker_t thinker; ceiling_e type; sector_t* sector; fixed_t bottomheight; fixed_t topheight; fixed_t speed; boolean crush; // 1 = up, 0 = waiting, -1 = down int direction; // ID int tag; int olddirection; } ceiling_t; #define CEILSPEED FRACUNIT #define CEILWAIT 150 #define MAXCEILINGS 30 extern ceiling_t* activeceilings[MAXCEILINGS]; int EV_DoCeiling ( line_t* line, ceiling_e type ); void T_MoveCeiling (ceiling_t* ceiling); void P_AddActiveCeiling(ceiling_t* c); void P_RemoveActiveCeiling(ceiling_t* c); int EV_CeilingCrushStop(line_t* line); void P_ActivateInStasisCeiling(line_t* line); // // P_FLOOR // typedef enum { // lower floor to highest surrounding floor lowerFloor, // lower floor to lowest surrounding floor lowerFloorToLowest, // lower floor to highest surrounding floor VERY FAST turboLower, // raise floor to lowest surrounding CEILING raiseFloor, // raise floor to next highest surrounding floor raiseFloorToNearest, // raise floor to shortest height texture around it raiseToTexture, // lower floor to lowest surrounding floor // and change floorpic lowerAndChange, raiseFloor64, // [STRIFE] changed from 24 to 64 raiseFloor24AndChange, raiseFloorCrush, // raise to next highest floor, turbo-speed raiseFloorTurbo, donutRaise, raiseFloor512, // [STRIFE] New floor type - used for the coolant reactor pit raiseFloor512AndChange } floor_e; typedef enum { build8, // slowly build by 8 turbo16, // quickly build by 16 buildDown16 // haleyjd 09/24/10: [STRIFE] new stair type } stair_e; typedef struct { thinker_t thinker; floor_e type; boolean crush; sector_t* sector; int direction; int newspecial; short texture; fixed_t floordestheight; fixed_t speed; } floormove_t; #define FLOORSPEED FRACUNIT typedef enum { ok, crushed, pastdest } result_e; result_e T_MovePlane ( sector_t* sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction ); int EV_BuildStairs ( line_t* line, stair_e type ); int EV_DoFloor ( line_t* line, floor_e floortype ); void T_MoveFloor( floormove_t* floor); // // P_TELEPT // // [STRIFE] Teleportation flags - teleflags // Not to be conflated with telefrags, though they be tangentially related ;) typedef enum teleflags { TF_NOSRCSND = 0x01, TF_NODSTSND = 0x02, TF_NODSTFOG = 0x10, TF_NOSRCFOG = 0x20, TF_NORMAL = 0, TF_DSTSILENCE = (TF_NODSTSND|TF_NODSTFOG), // 0x12 (18) (Not used) TF_SRCSILENCE = (TF_NOSRCSND|TF_NOSRCFOG), // 0x21 (33) TF_FULLSILENCE = (TF_SRCSILENCE|TF_DSTSILENCE) // 0x33 (51) } teleflags_e; int EV_Teleport ( line_t* line, int side, mobj_t* thing, teleflags_e flags); #endif crispy-doom-crispy-doom-5.6.4/src/strife/p_switch.c000066400000000000000000001005211360717211000223020ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // DESCRIPTION: // Switches, buttons. Two-state animation. Exits. // #include #include "i_system.h" #include "deh_main.h" #include "doomdef.h" #include "p_local.h" #include "g_game.h" #include "d_main.h" // villsa [STRIFE] #include "z_zone.h" // villsa [STRIFE] #include "w_wad.h" // villsa [STRIFE] #include "s_sound.h" #include "m_random.h" // haleyjd [STRIFE] #include "p_dialog.h" #include "p_local.h" // haleyjd [STRIFE] #include "m_bbox.h" // villsa [STRIFE] #include "m_misc.h" // Data. #include "sounds.h" // State. #include "doomstat.h" #include "r_state.h" // // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE // // villsa [STRIFE] new switch list switchlist_t alphSwitchList[] = { { "GLASS01", "GLASS02", 1, sfx_bglass }, { "GLASS03", "GLASS04", 1, sfx_bglass }, { "GLASS05", "GLASS06", 1, sfx_bglass }, { "GLASS07", "GLASS08", 1, sfx_bglass }, { "GLASS17", "GLASS18", 1, sfx_bglass }, { "GLASS19", "GLASS20", 1, sfx_bglass }, { "SWKNOB01", "SWKNOB02", 1, sfx_swknob }, { "SWLITE01", "SWLITE02", 1, sfx_None }, { "SWCHN01", "SWCHN02", 1, sfx_pulchn }, { "COMP01", "COMP04B", 1, sfx_bglass }, { "COMP05", "COMP12B", 1, sfx_bglass }, { "COMP09", "COMP12B", 1, sfx_bglass }, { "COMP12", "COMP04B", 1, sfx_bglass }, { "COMP13", "COMP12B", 1, sfx_bglass }, { "COMP17", "COMP20B", 1, sfx_bglass }, { "COMP21", "COMP28B", 1, sfx_bglass }, { "WALTEK09", "WALTEKB1", 1, sfx_None }, { "WALTEK10", "WALTEKB1", 1, sfx_None }, { "WALTEK15", "WALTEKB1", 1, sfx_None }, { "SWFORC01", "SWFORC02", 1, sfx_None }, { "SWEXIT01", "SWEXIT02", 1, sfx_None }, { "DORSBK01", "DORSBK02", 1, sfx_swston }, { "SWSLD01", "SWSLD02", 1, sfx_None }, { "DORWS04", "DORWS05", 1, sfx_swbolt }, { "SWIRON01", "SWIRON02", 1, sfx_None }, { "GLASS09", "GLASS10", 2, sfx_bglass }, { "GLASS11", "GLASS12", 2, sfx_bglass }, { "GLASS13", "GLASS14", 2, sfx_bglass }, { "GLASS15", "GLASS16", 2, sfx_bglass }, { "SWFORC03", "SWFORC04", 2, sfx_None }, { "SWCIT01", "SWCIT02", 2, sfx_None }, { "SWTRMG01", "SWTRMG04", 2, sfx_None }, { "SWMETL01", "SWMETL02", 2, sfx_None }, { "SWWOOD01", "SWWOOD02", 2, sfx_None }, { "SWTKBL01", "SWTKBL02", 2, sfx_None }, { "AZWAL21", "AZWAL22", 2, sfx_None }, { "SWINDT01", "SWINDT02", 2, sfx_None }, { "SWRUST01", "SWRUST02", 2, sfx_None }, { "SWCHAP01", "SWCHAP02", 2, sfx_None }, { "SWALIN01", "SWALIN02", 2, sfx_None }, { "SWWALG01", "SWWALG02", 2, sfx_None }, { "SWWALG03", "SWWALG04", 2, sfx_None }, { "SWTRAM01", "SWTRAM02", 2, sfx_None }, { "SWTRAM03", "SWTRAM04", 2, sfx_None }, { "SWORC01", "SWORC02", 2, sfx_None }, { "SWBRIK01", "SWBRIK02", 2, sfx_None }, { "SWIRON03", "SWIRON04", 2, sfx_None }, { "SWIRON05", "SWIRON06", 2, sfx_None }, { "SWIRON07", "SWIRON08", 2, sfx_None }, { "SWCARD01", "SWCARD02", 2, sfx_keycrd }, { "SWSIGN01", "SWSIGN02", 2, sfx_None }, { "SWLEV01", "SWLEV02", 2, sfx_None }, { "SWLEV03", "SWLEV04", 2, sfx_None }, { "SWLEV05", "SWLEV06", 2, sfx_None }, { "SWBRN01", "SWBRN02", 2, sfx_keycrd }, { "SWPIP01", "SWPIP02", 2, sfx_valve }, { "SWPALM01", "SWPALM02", 2, sfx_swscan }, { "SWKNOB03", "SWKNOB04", 2, sfx_swknob }, { "ALTSW01", "ALTSW02", 2, sfx_None }, { "COMP25", "COMP28B", 2, sfx_bglass }, { "COMP29", "COMP20B", 2, sfx_bglass }, { "COMP33", "COMP50", 2, sfx_bglass }, { "COMP42", "COMP51", 2, sfx_bglass }, { "GODSCRN1", "GODSCRN2", 2, sfx_difool }, { "ALIEN04", "ALIEN05", 2, sfx_None }, { "CITADL04", "CITADL05", 2, sfx_None }, { "SWITE03", "SWITE04", 2, sfx_None }, { "SWTELP01", "SWTELP02", 2, sfx_None }, { "BRNSCN01", "BRNSCN05", 2, sfx_firxpl }, }; int switchlist[MAXSWITCHES * 2]; int numswitches; button_t buttonlist[MAXBUTTONS]; // // P_InitSwitchList // Only called at game initialization. // void P_InitSwitchList(void) { int i, slindex, episode; // Note that this is called "episode" here but it's actually something // quite different. As we progress from Shareware->Registered->Doom II // we support more switch textures. if (isregistered) { episode = 2; } else { episode = 1; } slindex = 0; for (i = 0; i < arrlen(alphSwitchList); i++) { if (alphSwitchList[i].episode <= episode) { switchlist[slindex++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name1)); switchlist[slindex++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name2)); } } numswitches = slindex / 2; switchlist[slindex] = -1; } // // P_StartButton // Start a button counting down till it turns off. // void P_StartButton(line_t* line, bwhere_e w, int texture, int time) { int i; // See if button is already pressed for(i = 0; i < MAXBUTTONS; i++) { if(buttonlist[i].btimer && buttonlist[i].line == line) return; } for(i = 0; i < MAXBUTTONS; i++) { if(!buttonlist[i].btimer) { buttonlist[i].line = line; buttonlist[i].where = w; buttonlist[i].btexture = texture; buttonlist[i].btimer = time; buttonlist[i].soundorg = &line->frontsector->soundorg; return; } } I_Error("P_StartButton: no button slots left!"); } // // P_SpawnBrokenGlass // villsa [STRIFE] new function // static void P_SpawnBrokenGlass(line_t* line) { fixed_t x1; fixed_t x2; fixed_t y1; fixed_t y2; int i; mobj_t* glass; angle_t an; x1 = (line->v2->x + line->v1->x) / 2; y1 = (line->v2->y + line->v1->y) / 2; x2 = ((line->frontsector->soundorg.x - x1) / 5) + x1; y2 = ((line->frontsector->soundorg.y - y1) / 5) + y1; for(i = 0; i < 7; i++) { glass = P_SpawnMobj(x2, y2, ONFLOORZ, MT_JUNK); glass->z += (24*FRACUNIT); glass->flags |= (MF_SHADOW|MF_MVIS); P_SetMobjState(glass, P_Random() % 3 + S_SHRD_03); // 284 an = ((P_Random() << 13) / 255); glass->angle = (an << ANGLETOFINESHIFT); glass->momx = FixedMul(finecosine[an], (P_Random() & 3) << FRACBITS); glass->momy = FixedMul(finesine[an], (P_Random() & 3) << FRACBITS); glass->momz = (P_Random() & 7) << FRACBITS; glass->tics += (P_Random() + 7) & 7; } } // // Function that changes wall texture. // Tell it if switch is ok to use again (1=yes, it's a button). // void P_ChangeSwitchTexture(line_t* line, int useAgain) { int texTop; int texMid; int texBot; int i; int sound; boolean breakglass; // villsa [STRIFE] switchlist_t* sl; // villsa [STRIFE] breakglass = false; // villsa [STRIFE] texTop = sides[line->sidenum[0]].toptexture; texMid = sides[line->sidenum[0]].midtexture; texBot = sides[line->sidenum[0]].bottomtexture; sound = sfx_swtchn; // villsa [STRIFE] check for linetype 182 (break glass) if(line->special == 182) { line->flags &= ~ML_BLOCKING; breakglass = true; if(useAgain) { // haleyjd 09/21/10: Corrected (>> 16 == next field) texTop = 0; texBot = 0; } if(texMid) // haleyjd 09/21/10: Corrected (>> 16 == next field) useAgain = 0; sound = sfx_bglass; } if(!useAgain) line->special = 0; for(i = 0; i < numswitches*2; i++) { sl = &alphSwitchList[i / 2]; // villsa [STRIFE] if(switchlist[i] == texTop) { // villsa [STRIFE] set sound if(sl->sound) sound = sl->sound; // haleyjd 20141026: [STRIFE]: Rogue fixed wrong sound origin S_StartSound(&line->frontsector->soundorg, sound); sides[line->sidenum[0]].toptexture = switchlist[i^1]; if(useAgain) P_StartButton(line,top,switchlist[i],BUTTONTIME); if(breakglass) P_SpawnBrokenGlass(line); return; } else { if(switchlist[i] == texMid) { // villsa [STRIFE] set sound if(sl->sound) sound = sl->sound; // haleyjd 20141026: [STRIFE]: Rogue fixed wrong sound origin S_StartSound(&line->frontsector->soundorg, sound); sides[line->sidenum[0]].midtexture = switchlist[i^1]; // villsa [STRIFE] affect second side of line // BUG: will crash if 1S line is marked with TWOSIDED flag! if(line->flags & ML_TWOSIDED) sides[line->sidenum[1]].midtexture = switchlist[i^1]; if(useAgain) P_StartButton(line, middle,switchlist[i],BUTTONTIME); // villsa [STRIFE]: Mines Transmitter hack if(sound == sfx_firxpl) { breakglass = true; // give quest flag 29 to player players[0].questflags |= QF_QUEST29; // give stamina/accuracy items if(!netgame) { P_GiveItemToPlayer(players, SPR_TOKN, MT_TOKEN_STAMINA); P_GiveItemToPlayer(players, SPR_TOKN, MT_TOKEN_NEW_ACCURACY); } } // villsa [STRIFE] if(breakglass || sound == sfx_bglass) P_SpawnBrokenGlass(line); return; } else { if(switchlist[i] == texBot) { // villsa [STRIFE] set sound if(sl->sound) sound = sl->sound; // haleyjd 20141026: [STRIFE]: Rogue fixed wrong sound origin S_StartSound(&line->frontsector->soundorg, sound); sides[line->sidenum[0]].bottomtexture = switchlist[i^1]; if(useAgain) P_StartButton(line, bottom,switchlist[i],BUTTONTIME); if(breakglass) P_SpawnBrokenGlass(line); return; } } } } } // // P_MoveWall // // villsa [STRIFE] New function. // Dynamically move a solid line. Unused in Strife // static void P_MoveWall(line_t *line, mobj_t *thing) { vertex_t *v2; vertex_t *v1; fixed_t x; fixed_t y; v1 = line->v1; v2 = line->v2; S_StartSound(thing, sfx_stnmov); if (line->dx) { if (thing->x >= v1->x) { v1->y -= (8 * FRACUNIT); v2->y -= (8 * FRACUNIT); } else { v1->y += (8 * FRACUNIT); v2->y += (8 * FRACUNIT); } } else { if (thing->y >= v1->y) { v1->x -= (8 * FRACUNIT); v2->x -= (8 * FRACUNIT); } else { v1->x += (8 * FRACUNIT); v2->x += (8 * FRACUNIT); } } if (v1->x >= v2->x) { line->bbox[BOXLEFT] = v2->x; x = v1->x; } else { line->bbox[BOXLEFT] = v1->x; x = v2->x; } line->bbox[BOXRIGHT] = x; if (v1->y >= v2->y) { line->bbox[BOXBOTTOM] = v2->y; y = v1->y; } else { line->bbox[BOXBOTTOM] = v1->y; y = v2->y; } line->bbox[BOXTOP] = y; } // villsa [STRIFE] static char usemessage[92]; // // P_UseSpecialLine // Called when a thing uses a special line. // Only the front sides of lines are usable. // boolean P_UseSpecialLine(mobj_t* thing, line_t* line, int side) { // Err... // Use the back sides of VERY SPECIAL lines... if (side) { switch(line->special) { case 148: // haleyjd [STRIFE] break; default: return false; } } // Switches that other things can activate. if (!thing->player) { // never open secret doors if (line->flags & ML_SECRET) return false; switch(line->special) { case 1: // MANUAL DOOR RAISE case 31: // haleyjd [STRIFE] case 144: // haleyjd [STRIFE] Manual sliding door break; default: return false; break; } } // do something switch(line->special) { // MANUALS case 1: // Vertical Door case 26: // DR ID Card case 27: // DR Pass Card case 28: // DR ID Badge case 31: // Manual door open case 32: // D1 ID Card case 33: // D1 ID Badge case 34: // D1 Pass Card case 117: // Blazing door raise case 118: // Blazing door open case 156: // haleyjd [STRIFE] D1 Brass Key case 157: // haleyjd [STRIFE] D1 Silver Key case 158: // haleyjd [STRIFE] D1 Gold Key case 159: // haleyjd [STRIFE] DR Gold Key case 160: // haleyjd [STRIFE] DR Silver Key case 161: // haleyjd [STRIFE] DR Brass Key case 165: // villsa [STRIFE] That doesn't seem to work case 166: // haleyjd [STRIFE] DR Hand Print case 169: // haleyjd [STRIFE] DR Base Key case 170: // haleyjd [STRIFE] DR Gov's Key case 190: // haleyjd [STRIFE] DR Order Key case 205: // villsa [STRIFE] Available in retail only case 213: // haleyjd [STRIFE] DR Chalice case 217: // haleyjd [STRIFE] DR Core Key case 221: // haleyjd [STRIFE] DR Mauler Key case 224: // haleyjd [STRIFE] DR Chapel Key case 225: // haleyjd [STRIFE] DR Catacomb Key case 232: // villsa [STRIFE] DR Oracle Pass EV_VerticalDoor (line, thing); break; // haleyjd: For the sake of our sanity, I have reordered all the line // specials from this point down so that they are strictly in numeric // order, and not divided up in a semi-arbitrary fashion. case 7: // Build Stairs - [STRIFE] Verified unmodified if (EV_BuildStairs(line,build8)) P_ChangeSwitchTexture(line,0); break; case 9: // Change Donut - [STRIFE] Verified unmodified if (EV_DoDonut(line)) P_ChangeSwitchTexture(line,0); break; case 11: // Exit level - [STRIFE] Modified to take tag, etc. P_ChangeSwitchTexture(line, 1); if(levelTimer && levelTimeCount) break; G_ExitLevel(line->tag); break; case 14: // Raise Floor 32 and change texture - [STRIFE] Verified unmodified 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 - [STRIFE] Verified unmodified if (EV_DoFloor(line, raiseFloorToNearest)) P_ChangeSwitchTexture(line,0); break; case 20: // Raise Plat next highest floor and change texture - [STRIFE] Verified unmodified if(EV_DoPlat(line, raiseToNearestAndChange, 0)) P_ChangeSwitchTexture(line,0); break; case 21: // PlatDownWaitUpStay - [STRIFE] Verified unmodified if (EV_DoPlat(line, downWaitUpStay,0)) P_ChangeSwitchTexture(line,0); break; case 23: // Lower Floor to Lowest - [STRIFE] Verified unmodified if (EV_DoFloor(line,lowerFloorToLowest)) P_ChangeSwitchTexture(line,0); break; case 29: // Raise Door - [STRIFE] Verified unmodified if (EV_DoDoor(line,vld_normal)) P_ChangeSwitchTexture(line,0); break; case 40: // villsa [STRIFE] Split Open Door if(EV_DoDoor(line, vld_splitOpen)) P_ChangeSwitchTexture(line, 0); break; // haleyjd case 41: // Lower Ceiling to Floor - [STRIFE] Verified unmodified if (EV_DoCeiling(line,lowerToFloor)) P_ChangeSwitchTexture(line,0); break; case 42: // Close Door - [STRIFE] Verified unmodified if (EV_DoDoor(line,vld_close)) P_ChangeSwitchTexture(line,1); break; case 43: // Lower Ceiling to Floor - [STRIFE] Verified unmodified if (EV_DoCeiling(line,lowerToFloor)) P_ChangeSwitchTexture(line,1); break; case 45: // Lower Floor to Surrounding floor height - [STRIFE] Verified unmodified if (EV_DoFloor(line,lowerFloor)) P_ChangeSwitchTexture(line,1); break; case 49: // Ceiling Crush And Raise - [STRIFE] Verified unmodified if (EV_DoCeiling(line,crushAndRaise)) P_ChangeSwitchTexture(line,0); break; case 50: // Close Door - [STRIFE] Verified unmodified if (EV_DoDoor(line,vld_close)) P_ChangeSwitchTexture(line,0); break; case 51: // [STRIFE] Modifed into S1 Start Finale (was Secret Exit) P_ChangeSwitchTexture(line,0); G_StartFinale(); break; case 55: // Raise Floor Crush - [STRIFE] Verified unmodified if (EV_DoFloor(line,raiseFloorCrush)) P_ChangeSwitchTexture(line,0); break; case 60: // Lower Floor to Lowest - [STRIFE] Verified unmodified if (EV_DoFloor(line,lowerFloorToLowest)) P_ChangeSwitchTexture(line,1); break; case 61: // Open Door - [STRIFE] Verified unmodified if (EV_DoDoor(line,vld_open)) P_ChangeSwitchTexture(line,1); break; case 62: // PlatDownWaitUpStay - [STRIFE] Verified unmodified if (EV_DoPlat(line, downWaitUpStay,1)) P_ChangeSwitchTexture(line,1); break; case 63: // Raise Door - [STRIFE] Verified unmodified if (EV_DoDoor(line,vld_normal)) P_ChangeSwitchTexture(line,1); break; case 64: // Raise Floor to ceiling - [STRIFE] Verified unmodified if (EV_DoFloor(line,raiseFloor)) P_ChangeSwitchTexture(line,1); break; case 65: // Raise Floor Crush - [STRIFE] Verified unmodified if (EV_DoFloor(line,raiseFloorCrush)) P_ChangeSwitchTexture(line,1); break; case 66: // Raise Floor 24 and change texture - [STRIFE] Verified unmodified if (EV_DoPlat(line, raiseAndChange, 24)) P_ChangeSwitchTexture(line,1); break; case 67: // Raise Floor 32 and change texture - [STRIFE] Verified unmodified if (EV_DoPlat(line, raiseAndChange, 32)) P_ChangeSwitchTexture(line,1); break; case 68: // Raise Plat to next highest floor and change texture - [STRIFE] Verified unmodified if (EV_DoPlat(line, raiseToNearestAndChange, 0)) P_ChangeSwitchTexture(line,1); break; case 69: // Raise Floor to next highest floor - [STRIFE] Verified unmodified if (EV_DoFloor(line, raiseFloorToNearest)) P_ChangeSwitchTexture(line,1); break; case 70: // Turbo Lower Floor - [STRIFE] Verified unmodified if (EV_DoFloor(line,turboLower)) P_ChangeSwitchTexture(line,1); break; case 71: // Turbo Lower Floor - [STRIFE] Verified unmodified if (EV_DoFloor(line,turboLower)) P_ChangeSwitchTexture(line,0); break; case 101: // Raise Floor - [STRIFE] Verified unmodified if (EV_DoFloor(line,raiseFloor)) P_ChangeSwitchTexture(line,0); break; case 102: // Lower Floor to Surrounding floor height - [STRIFE] Verified unmodified if (EV_DoFloor(line,lowerFloor)) P_ChangeSwitchTexture(line,0); break; case 103: // Open Door - [STRIFE] Verified unmodified if (EV_DoDoor(line,vld_open)) P_ChangeSwitchTexture(line,0); break; case 111: // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified if (EV_DoDoor (line,vld_blazeRaise)) P_ChangeSwitchTexture(line,0); break; case 112: // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified if (EV_DoDoor (line,vld_blazeOpen)) P_ChangeSwitchTexture(line,0); break; case 113: // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified if (EV_DoDoor (line,vld_blazeClose)) P_ChangeSwitchTexture(line,0); break; case 114: // Blazing Door Raise (faster than TURBO!) - [STRIFE] Verified unmodified if (EV_DoDoor (line,vld_blazeRaise)) P_ChangeSwitchTexture(line,1); break; case 115: // Blazing Door Open (faster than TURBO!) - [STRIFE] Verified unmodified if (EV_DoDoor (line,vld_blazeOpen)) P_ChangeSwitchTexture(line,1); break; case 116: // Blazing Door Close (faster than TURBO!) - [STRIFE] Verified unmodified if (EV_DoDoor (line,vld_blazeClose)) P_ChangeSwitchTexture(line,1); break; case 122: // Blazing PlatDownWaitUpStay - [STRIFE] Verified unmodified if(EV_DoPlat(line, blazeDWUS, 0)) P_ChangeSwitchTexture(line,0); break; case 123: // Blazing PlatDownWaitUpStay - [STRIFE] Verified unmodified if(EV_DoPlat(line, blazeDWUS, 0)) P_ChangeSwitchTexture(line,1); break; case 127: // Build Stairs Turbo 16 - [STRIFE] Verified unmodified if (EV_BuildStairs(line,turbo16)) P_ChangeSwitchTexture(line,0); break; case 131: // Raise Floor Turbo - [STRIFE] Verified unmodified if (EV_DoFloor(line,raiseFloorTurbo)) P_ChangeSwitchTexture(line,0); break; case 132: // Raise Floor Turbo - [STRIFE] Verified unmodified if (EV_DoFloor(line,raiseFloorTurbo)) P_ChangeSwitchTexture(line,1); break; case 133: // [STRIFE] TODO - which key is it? case 135: // [STRIFE] TODO - which key is it? case 137: // [STRIFE] TODO - which key is it? if (EV_DoLockedDoor (line,vld_blazeOpen,thing)) P_ChangeSwitchTexture(line,0); break; case 99: // [STRIFE] TODO: which key is it? case 134: // [STRIFE] TODO: which key is it? case 136: // [STRIFE] TODO: which key is it? if (EV_DoLockedDoor (line,vld_blazeOpen,thing)) P_ChangeSwitchTexture(line,1); break; case 138: // Light Turn On - [STRIFE] Verified unmodified EV_LightTurnOn(line,255); P_ChangeSwitchTexture(line,1); break; case 139: // Light Turn Off - [STRIFE] Verified unmodified EV_LightTurnOn(line,35); P_ChangeSwitchTexture(line,1); break; case 140: // Raise Floor 512 - [STRIFE] Verified unmodified if (EV_DoFloor(line,raiseFloor512)) P_ChangeSwitchTexture(line,0); break; case 144: // villsa [STRIFE] manual sliding door EV_SlidingDoor(line, thing); break; case 146: // haleyjd 09/24/10: [STRIFE] S1 Build Stairs Down 16 (new type) if(EV_BuildStairs(line, buildDown16)) P_ChangeSwitchTexture(line, 0); break; case 147: // haleyjd 09/24/10: [STRIFE] S1 Clear Force Fields if(EV_ClearForceFields(line)) P_ChangeSwitchTexture(line, 0); break; case 148: // haleyjd 09/16/10: [STRIFE] using forcefields hurts P_DamageMobj(thing, NULL, NULL, 16); P_Thrust(thing->player, thing->angle + ANG180, 125*FRACUNIT/16); break; case 151: // villsa [STRIFE] BlzOpenDoor Gold key case 152: // [STRIFE] TODO: which key is it? case 153: // [STRIFE] TODO: which key is it? if(EV_DoLockedDoor(line, vld_blazeOpen, thing)) P_ChangeSwitchTexture(line, 1); break; case 154: // villsa [STRIFE] plat lower wait rise if have gold key if(thing->player->cards[key_GoldKey]) { if(EV_DoPlat(line, downWaitUpStay, 0)) P_ChangeSwitchTexture(line, 1); } else { thing->player->message = DEH_String("You need a gold key"); S_StartSound(thing, sfx_oof); } break; case 155: // villsa [STRIFE] raise plat wait lower if(EV_DoPlat(line, upWaitDownStay, 0)) P_ChangeSwitchTexture(line, 1); break; case 162: // [STRIFE] TODO: which key is it? case 163: // [STRIFE] TODO: which key is it? case 164: // villsa [STRIFE] BlzOpenDoor Gold key case 167: // [STRIFE] TODO: which key is it? if(EV_DoLockedDoor(line, vld_blazeOpen, thing)) P_ChangeSwitchTexture(line, 0); break; case 168: // [STRIFE] TODO: which key is it? // haleyjd 09/25/10: [STRIFE] SR Blaze Open Door ???? Key if(EV_DoLockedDoor(line, vld_blazeOpen, thing)) P_ChangeSwitchTexture(line, 1); break; case 171: // [STRIFE] TODO: which key is it? // haleyjd 09/25/10: [STRIFE] S1 Open Door ???? Key if(EV_DoLockedDoor(line, vld_open, thing)) P_ChangeSwitchTexture(line, 0); break; case 172: // [STRIFE] TODO: which key is it? case 173: // [STRIFE] TODO: which key is it? case 176: // [STRIFE] TODO: which key is it? case 191: // [STRIFE] TODO: which key is it? case 192: // [STRIFE] TODO: which key is it? case 223: // [STRIFE] TODO: which key is it? if(EV_DoLockedDoor(line, vld_normal, thing)) P_ChangeSwitchTexture(line, 1); break; case 177: // villsa [STRIFE] plat lower wait rise if have power3 key if(thing->player->cards[key_Power3Key]) { if(EV_DoPlat(line, downWaitUpStay, 0)) P_ChangeSwitchTexture(line, 1); } else { thing->player->message = DEH_String("You don't have the key"); S_StartSound(thing, sfx_oof); } break; case 181: // haleyjd 09/25/10: [STRIFE] S1 Floor Raise 512 & Change if(EV_DoFloor(line, raiseFloor512AndChange)) P_ChangeSwitchTexture(line, 0); break; case 189: // [STRIFE] TODO: which key is it??? // haleyjd 09/25/10: [STRIFE] S1 Split Open Door ???? Key if(EV_DoLockedDoor(line, vld_splitOpen, thing)) P_ChangeSwitchTexture(line, 0); break; case 194: // villsa [STRIFE] S1 Free Prisoners if(EV_DoDoor(line, vld_open)) { P_ChangeSwitchTexture(line, 0); P_FreePrisoners(); } break; case 199: // haleyjd 09/25/10: [STRIFE] S1 Destroy Converter if(EV_DoCeiling(line, lowerAndCrush)) { P_ChangeSwitchTexture(line, 0); P_DestroyConverter(); } break; case 207: // villsa [STRIFE] SR Remote Sliding Door if(EV_RemoteSlidingDoor(line, thing)) P_ChangeSwitchTexture(line, 1); break; // haleyjd case 209: // haleyjd 09/24/10: [STRIFE] S1 Build Stairs Down 16 if Have Chalice if(!P_PlayerHasItem(thing->player, MT_INV_CHALICE)) { DEH_snprintf(usemessage, sizeof(usemessage), "You need the chalice!"); thing->player->message = usemessage; S_StartSound(thing, sfx_oof); break; } else if(EV_BuildStairs(line, buildDown16)) P_ChangeSwitchTexture(line, 0); break; case 211: // villsa [STRIFE] S1 Play VOC## sound if(&players[consoleplayer] == thing->player && thing->player->powers[pw_communicator]) { DEH_snprintf(usemessage, sizeof(usemessage), "voc%i", line->tag); I_StartVoice(usemessage); line->special = 0; } break; case 214: // villsa [STRIFE] S1 slow lift lower wait up stay if(EV_DoPlat(line, slowDWUS, 1)) P_ChangeSwitchTexture(line, 1); break; case 219: // haleyjd 09/25/10: S1 Lower Floor Blue Crystal if(!thing->player->cards[key_BlueCrystalKey]) { thing->player->message = DEH_String("You need the Blue Crystal"); S_StartSound(thing, sfx_oof); } else if(EV_DoFloor(line, lowerFloor)) P_ChangeSwitchTexture(line, 0); break; case 220: // haleyjd 09/25/10: S1 Lower Floor Red Crystal if(!thing->player->cards[key_RedCrystalKey]) { thing->player->message = DEH_String("You need the Red Crystal"); S_StartSound(thing, sfx_oof); } else if(EV_DoFloor(line, lowerFloor)) P_ChangeSwitchTexture(line, 0); break; case 226: // villsa [STRIFE] S1 Complete Training Area if(EV_DoFloor(line, lowerFloor)) { P_GiveItemToPlayer(thing->player, SPR_TOKN, MT_TOKEN_STAMINA); P_GiveItemToPlayer(thing->player, SPR_TOKN, MT_TOKEN_NEW_ACCURACY); P_ChangeSwitchTexture(line, 0); DEH_snprintf(usemessage, sizeof(usemessage), "Congratulations! You have completed the training area."); thing->player->message = usemessage; } break; case 229: // villsa [STRIFE] SR Sigil Sliding Door if(thing->player->sigiltype == 4) { if(EV_RemoteSlidingDoor(line, thing)) P_ChangeSwitchTexture(line, 1); } break; // haleyjd case 233: // villsa [STRIFE] objective given after revealing the computer if(!EV_DoDoor(line, vld_splitOpen)) return true; P_ChangeSwitchTexture(line, 1); GiveVoiceObjective("voc70", "log70", 0); // haleyjd: Strife used sprintf here, not a direct set. DEH_snprintf(usemessage, sizeof(usemessage), "Incoming Message from BlackBird..."); thing->player->message = usemessage; break; case 234: // haleyjd 09/24/10: [STRIFE] SR Raise Door if Quest 3 if(!(thing->player->questflags & QF_QUEST3)) // QUEST3 == Irale { // BUG: doesn't make sfx_oof sound like all other message- // giving door types. I highly doubt this was intentional. DEH_snprintf(usemessage, sizeof(usemessage), "That doesn't seem to work!"); thing->player->message = usemessage; } else if(EV_DoDoor(line, vld_normal)) P_ChangeSwitchTexture(line, 1); break; case 235: // haleyjd 09/25/10: [STRIFE] S1 Split Open Door if Have Sigil 4 if(thing->player->sigiltype == 4) { if(EV_DoDoor(line, vld_splitOpen)) P_ChangeSwitchTexture(line, 0); } break; case 666: // villsa [STRIFE] SR Move Wall P_MoveWall(line, thing); break; } return true; } crispy-doom-crispy-doom-5.6.4/src/strife/p_telept.c000066400000000000000000000106651360717211000223070ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Teleportation. // #include "doomdef.h" #include "doomstat.h" #include "s_sound.h" #include "p_local.h" // Data. #include "sounds.h" // State. #include "r_state.h" // // TELEPORTATION // // haleyjd 09/22/10: [STRIFE] Modified to take a flags parameter to control // silent teleportation. Rogue also removed the check for missiles, and the // z-set was replaced with one in P_TeleportMove. // int EV_Teleport ( line_t* line, int side, mobj_t* thing, teleflags_e flags) { int i; int tag; mobj_t* m; mobj_t* fog = NULL; unsigned an; thinker_t* thinker; sector_t* sector; fixed_t oldx; fixed_t oldy; fixed_t oldz; // haleyjd 20110205 [STRIFE]: this is not checked here // don't teleport missiles //if (thing->flags & MF_MISSILE) // return 0; // Don't teleport if hit back of line, // so you can get out of teleporter. if (side == 1) return 0; tag = line->tag; for (i = 0; i < numsectors; i++) { if (sectors[ i ].tag == tag ) { thinker = thinkercap.next; for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { // not a mobj if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) continue; m = (mobj_t *)thinker; // not a teleportman if (m->type != MT_TELEPORTMAN ) continue; sector = m->subsector->sector; // wrong sector if (sector-sectors != i ) continue; oldx = thing->x; oldy = thing->y; oldz = thing->z; if (!P_TeleportMove (thing, m->x, m->y)) return 0; // fraggle: this was changed in final doom, // problem between normal doom2 1.9 and final doom // // Note that although chex.exe is based on Final Doom, // it does not have this quirk. // // haleyjd 20110205 [STRIFE] This code is *not* present, // because of a z-set which Rogue added to P_TeleportMove. /* if (gameversion < exe_final || gameversion == exe_chex) thing->z = thing->floorz; */ if (thing->player) thing->player->viewz = thing->z+thing->player->viewheight; // spawn teleport fog at source and destination // haleyjd 09/22/10: [STRIFE] controlled by teleport flags // BUG: Behavior would be undefined if this function were passed // any combination of teleflags that has the NO*FOG but not the // corresponding NO*SND flag - fortunately this is never done // anywhere in the code. if(!(flags & TF_NOSRCFOG)) fog = P_SpawnMobj (oldx, oldy, oldz, MT_TFOG); if(!(flags & TF_NOSRCSND)) S_StartSound (fog, sfx_telept); an = m->angle >> ANGLETOFINESHIFT; if(!(flags & TF_NODSTFOG)) fog = P_SpawnMobj (m->x+20*finecosine[an], m->y+20*finesine[an], thing->z, MT_TFOG); if(!(flags & TF_NODSTSND)) S_StartSound (fog, sfx_telept); // don't move for a bit if (thing->player) thing->reactiontime = 18; thing->angle = m->angle; thing->momx = thing->momy = thing->momz = 0; return 1; } } } return 0; } crispy-doom-crispy-doom-5.6.4/src/strife/p_tick.c000066400000000000000000000063201360717211000217350ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Archiving: SaveGame I/O. // Thinker, Ticker. // #include "z_zone.h" #include "p_local.h" #include "doomstat.h" int leveltime; // // THINKERS // All thinkers should be allocated by Z_Malloc // so they can be operated on uniformly. // The actual structures will vary in size, // but the first element must be thinker_t. // // Both the head and tail of the thinker list. thinker_t thinkercap; // // P_InitThinkers // // [STRIFE] Verified unmodified // void P_InitThinkers (void) { thinkercap.prev = thinkercap.next = &thinkercap; } // // P_AddThinker // Adds a new thinker at the end of the list. // // [STRIFE] Verified unmodified // void P_AddThinker (thinker_t* thinker) { thinkercap.prev->next = thinker; thinker->next = &thinkercap; thinker->prev = thinkercap.prev; thinkercap.prev = thinker; } // // P_RemoveThinker // Deallocation is lazy -- it will not actually be freed // until its thinking turn comes up. // // [STRIFE] Verified unmodified // void P_RemoveThinker (thinker_t* thinker) { // FIXME: NOP. thinker->function.acv = (actionf_v)(-1); } // // P_AllocateThinker // Allocates memory and adds a new thinker at the end of the list. // void P_AllocateThinker (thinker_t* thinker) { } // // P_RunThinkers // // [STRIFE] Verified unmodified // void P_RunThinkers (void) { thinker_t *currentthinker, *nextthinker; currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { if ( currentthinker->function.acv == (actionf_v)(-1) ) { // time to remove it nextthinker = currentthinker->next; currentthinker->next->prev = currentthinker->prev; currentthinker->prev->next = currentthinker->next; Z_Free (currentthinker); } else { if (currentthinker->function.acp1) currentthinker->function.acp1 (currentthinker); nextthinker = currentthinker->next; } currentthinker = nextthinker; } } // // P_Ticker // // [STRIFE] Menu pause behavior modified // void P_Ticker (void) { int i; // run the tic if (paused) return; // pause if in menu and at least one tic has been run // haleyjd 09/08/10 [STRIFE]: menuactive -> menupause if (!netgame && menupause && !demoplayback && players[consoleplayer].viewz != 1) { return; } for (i=0 ; i #include "doomdef.h" #include "d_event.h" #include "p_local.h" #include "sounds.h" // villsa [STRIFE] #include "p_dialog.h" // villsa [STRIFE] #include "d_main.h" // villsa [STRIFE] #include "doomstat.h" #include "deh_str.h" // haleyjd [STRIFE] #include "z_zone.h" #include "w_wad.h" #include "p_pspr.h" #include "m_misc.h" #include "m_random.h" #include "s_sound.h" #include "p_inter.h" // Index of the special effects (INVUL inverse) map. #define LOOKPITCHAMOUNT 6 // villsa [STRIFE] #define CENTERVIEWAMOUNT (LOOKPITCHAMOUNT + 2) // villsa [STRIFE] #define LOOKUPMAX 90 // villsa [STRIFE] #define LOOKDOWNMAX -110 // villsa [STRIFE] void P_DropInventoryItem(player_t* player, int sprite); // villsa [STRIFE] boolean P_ItemBehavior(player_t* player, int item); // villsa [STRIFE] static char useinventorymsg[44]; // villsa [STRIFE] // // Movement. // // 16 pixels of bob #define MAXBOB 0x100000 boolean onground; // // P_Thrust // Moves the given origin along a given angle. // // [STRIFE] Verified unmodified // void P_Thrust ( player_t* player, angle_t angle, fixed_t move ) { angle >>= ANGLETOFINESHIFT; player->mo->momx += FixedMul(move,finecosine[angle]); player->mo->momy += FixedMul(move,finesine[angle]); } // // P_CalcHeight // Calculate the walking / running height adjustment // // [STRIFE] Some odd adjustments, and terrain view height adjustment // void P_CalcHeight (player_t* player) { int angle; fixed_t bob; // Regular movement bobbing // (needs to be calculated for gun swing // even if not on ground) // OPTIMIZE: tablify angle // Note: a LUT allows for effects // like a ramp with low health. player->bob = FixedMul (player->mo->momx, player->mo->momx) + FixedMul (player->mo->momy,player->mo->momy); player->bob >>= 2; if (player->bob>MAXBOB) player->bob = MAXBOB; // haleyjd 20110205 [STRIFE]: No CF_NOMOMENTUM check, and Rogue also removed // the dead code inside. if (!onground) { /* player->viewz = player->mo->z + VIEWHEIGHT; if (player->viewz > player->mo->ceilingz-4*FRACUNIT) player->viewz = player->mo->ceilingz-4*FRACUNIT; */ player->viewz = player->mo->z + player->viewheight; return; } angle = (FINEANGLES/20*leveltime)&FINEMASK; bob = FixedMul ( player->bob/2, finesine[angle]); // move viewheight if (player->playerstate == PST_LIVE) { player->viewheight += player->deltaviewheight; if (player->viewheight > VIEWHEIGHT) { player->viewheight = VIEWHEIGHT; player->deltaviewheight = 0; } if (player->viewheight < VIEWHEIGHT/2) { player->viewheight = VIEWHEIGHT/2; if (player->deltaviewheight <= 0) player->deltaviewheight = 1; } if (player->deltaviewheight) { player->deltaviewheight += FRACUNIT/4; if (!player->deltaviewheight) player->deltaviewheight = 1; } } player->viewz = player->mo->z + player->viewheight + bob; // villsa [STRIFE] account for terrain lowering the view if(player->mo->flags & MF_FEETCLIPPED) player->viewz -= 13*FRACUNIT; if (player->viewz > player->mo->ceilingz-4*FRACUNIT) player->viewz = player->mo->ceilingz-4*FRACUNIT; // haleyjd [STRIFE]: added a floorz clip here if (player->viewz < player->mo->floorz) player->viewz = player->mo->floorz; } // // P_MovePlayer // // [STRIFE] Adjustments to allow air control, jumping, and up/down look. // void P_MovePlayer (player_t* player) { ticcmd_t* cmd; cmd = &player->cmd; player->mo->angle += (cmd->angleturn<<16); // Do not let the player control movement // if not onground. onground = (player->mo->z <= player->mo->floorz); // villsa [STRIFE] allows player to climb over things by jumping // haleyjd 20110205: air control thrust should be 256, not cmd->forwardmove if(!onground) { if(cmd->forwardmove) P_Thrust (player, player->mo->angle, 256); } else { // villsa [STRIFE] jump button if (cmd->buttons2 & BT2_JUMP) { if(!player->deltaviewheight) player->mo->momz += 8*FRACUNIT; } // haleyjd 20110205 [STRIFE] Either Rogue or Watcom removed the // redundant "onground" checks from these if's. if (cmd->forwardmove) P_Thrust (player, player->mo->angle, cmd->forwardmove*2048); if (cmd->sidemove) P_Thrust (player, player->mo->angle-ANG90, cmd->sidemove*2048); } // villsa [STRIFE] player walking state set if ( (cmd->forwardmove || cmd->sidemove) && player->mo->state == &states[S_PLAY_00] ) { P_SetMobjState (player->mo, S_PLAY_01); } // villsa [STRIFE] centerview button if (cmd->buttons2 & BT2_CENTERVIEW) player->centerview = 1; // villsa [STRIFE] adjust player's pitch when centerviewing if (player->centerview) { if (player->pitch <= 0) { if (player->pitch < 0) player->pitch = player->pitch + CENTERVIEWAMOUNT; } else { player->pitch = player->pitch - CENTERVIEWAMOUNT; } if (abs(player->pitch) < CENTERVIEWAMOUNT) { player->pitch = 0; player->centerview = 0; } } // villsa [STRIFE] look up action if (cmd->buttons2 & BT2_LOOKUP) { player->pitch += LOOKPITCHAMOUNT; if (player->pitch > LOOKUPMAX || player->pitch < LOOKDOWNMAX) player->pitch -= LOOKPITCHAMOUNT; } else { // villsa [STRIFE] look down action if (cmd->buttons2 & BT2_LOOKDOWN) { player->pitch -= LOOKPITCHAMOUNT; if (player->pitch > LOOKUPMAX || player->pitch < LOOKDOWNMAX) player->pitch += LOOKPITCHAMOUNT; } } } // // P_DeathThink // Fall on your face when dying. // Decrease POV height to floor height. // // [STRIFE] Modifications for up/down look. // #define ANG5 (ANG90/18) void P_DeathThink(player_t* player) { angle_t angle; angle_t delta; P_MovePsprites(player); // fall to the ground if (player->viewheight > 6*FRACUNIT) player->viewheight -= FRACUNIT; if (player->viewheight < 6*FRACUNIT) player->viewheight = 6*FRACUNIT; player->deltaviewheight = 0; onground = (player->mo->z <= player->mo->floorz); P_CalcHeight(player); if(player->attacker && player->attacker != player->mo) { angle = R_PointToAngle2 (player->mo->x, player->mo->y, player->attacker->x, player->attacker->y); delta = angle - player->mo->angle; if (delta < ANG5 || delta > (unsigned)-ANG5) { // Looking at killer, // so fade damage flash down. player->mo->angle = angle; if (player->damagecount) player->damagecount--; } else if (delta < ANG180) player->mo->angle += ANG5; else player->mo->angle -= ANG5; } else if (player->damagecount) player->damagecount--; // villsa [STRIFE] if(player->pitch <= 90) player->pitch = player->pitch + 3; if(player->cmd.buttons & BT_USE) player->playerstate = PST_REBORN; } // // P_PlayerThink // // [STRIFE] Massive changes/additions: // * NOCLIP hack removed // * P_DeathThink moved up // * Inventory use/dropping // * Strife weapons logic // * Dialog // * Strife powerups and nukage behavior // * Fire Death/Sigil Shock // void P_PlayerThink (player_t* player) { ticcmd_t* cmd; weapontype_t newweapon; // villsa [STRIFE] unused code (see ST_Responder) /* // fixme: do this in the cheat code if (player->cheats & CF_NOCLIP) player->mo->flags |= MF_NOCLIP; else player->mo->flags &= ~MF_NOCLIP; */ // haleyjd 20110205 [STRIFE]: P_DeathThink moved up if (player->playerstate == PST_DEAD) { P_DeathThink (player); return; } // 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; } // Move around. // Reactiontime is used to prevent movement // for a bit after a teleport. if (player->mo->reactiontime) player->mo->reactiontime--; else P_MovePlayer (player); P_CalcHeight (player); if (player->mo->subsector->sector->special) P_PlayerInSpecialSector (player); // villsa [STRIFE] handle inventory input if(cmd->buttons2 & (BT2_HEALTH|BT2_INVUSE|BT2_INVDROP)) { if(!player->inventorydown) { if(cmd->buttons2 & BT2_HEALTH) P_UseInventoryItem(player, SPR_FULL); else if(cmd->buttons2 & BT2_INVUSE) P_UseInventoryItem(player, cmd->inventory); else if(cmd->buttons2 & BT2_INVDROP) { P_DropInventoryItem(player, cmd->inventory); // haleyjd 20110205: removed incorrect "else" here // villsa [STRIFE] if(workparm) { int cheat = player->cheats ^ 1; player->cheats ^= CF_NOCLIP; if(cheat & CF_NOCLIP) { player->message = DEH_String("No Clipping Mode ON"); player->mo->flags |= MF_NOCLIP; } else { player->mo->flags &= ~MF_NOCLIP; player->message = DEH_String("No Clipping Mode OFF"); } } } } player->inventorydown = true; } else player->inventorydown = false; // Check for weapon change. // A special event has no other buttons. if(cmd->buttons & BT_SPECIAL) cmd->buttons = 0; if(cmd->buttons & BT_CHANGE) { // The actual changing of the weapon is done // when the weapon psprite can do it // (read: not in the middle of an attack). newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT; // villsa [STRIFE] select poison bow if(newweapon == wp_elecbow) { if(player->weaponowned[wp_poisonbow] && player->readyweapon == wp_elecbow) { if(player->ammo[weaponinfo[wp_poisonbow].ammo]) newweapon = wp_poisonbow; } } // villsa [STRIFE] select wp grenade launcher if(newweapon == wp_hegrenade) { if(player->weaponowned[wp_wpgrenade] && player->readyweapon == wp_hegrenade) { if(player->ammo[weaponinfo[wp_wpgrenade].ammo]) newweapon = wp_wpgrenade; } } // villsa [STRIFE] select torpedo if(newweapon == wp_mauler) { if(player->weaponowned[wp_torpedo] && player->readyweapon == wp_mauler) { // haleyjd 20140924: bug fix - using wrong enum value am_cell // caused this to check the missile launcher for rocket ammo if(player->ammo[weaponinfo[wp_torpedo].ammo] >= 30) newweapon = wp_torpedo; } } if(player->weaponowned[newweapon] && newweapon != player->readyweapon) { // villsa [STRIFE] check weapon if in demo mode or not if(weaponinfo[newweapon].availabledemo || !isdemoversion) { if(player->ammo[weaponinfo[newweapon].ammo]) player->pendingweapon = newweapon; else { // decide between electric bow or poison arrow if(newweapon == wp_elecbow && player->ammo[am_poisonbolts] && player->readyweapon != wp_poisonbow) { player->pendingweapon = wp_poisonbow; } // decide between hp grenade launcher or wp grenade launcher else if(newweapon == wp_hegrenade && player->ammo[am_wpgrenades] && player->readyweapon != wp_wpgrenade) { player->pendingweapon = wp_wpgrenade; } // villsa [STRIFE] - no check for mauler/torpedo?? } } } } // check for use if(cmd->buttons & BT_USE) { if(!player->usedown) { P_DialogStart(player); // villsa [STRIFE] P_UseLines (player); player->usedown = true; } } else player->usedown = false; // cycle psprites P_MovePsprites (player); // Counters, time dependend power ups. // Strength counts up to diminish fade. if (player->powers[pw_strength]) player->powers[pw_strength]++; // villsa [STRIFE] targeter powerup if(player->powers[pw_targeter]) { player->powers[pw_targeter]--; if(player->powers[pw_targeter] == 1) { P_SetPsprite(player, ps_targcenter, S_NULL); P_SetPsprite(player, ps_targleft, S_NULL); P_SetPsprite(player, ps_targright, S_NULL); } else if(player->powers[pw_targeter] - 1 < 5*TICRATE) { if(player->powers[pw_targeter] & 32) { P_SetPsprite(player, ps_targright, S_NULL); P_SetPsprite(player, ps_targleft, S_TRGT_01); // 11 } else if(player->powers[pw_targeter] & 16) // haleyjd 20110205: missing else { P_SetPsprite(player, ps_targright, S_TRGT_02); // 12 P_SetPsprite(player, ps_targleft, S_NULL); } } } if(player->powers[pw_invisibility]) { // villsa [STRIFE] remove mvis flag as well if(!--player->powers[pw_invisibility]) player->mo->flags &= ~(MF_SHADOW|MF_MVIS); } if(player->powers[pw_ironfeet]) { player->powers[pw_ironfeet]--; // villsa [STRIFE] gasmask sound if(!(leveltime & 0x3f)) S_StartSound(player->mo, sfx_mask); } if(player->powers[pw_allmap] > 1) player->powers[pw_allmap]--; // haleyjd 08/30/10: [STRIFE] // Nukage count keeps track of exposure to hazardous conditions over time. // After accumulating 16 total seconds or more of exposure, you will take // 5 damage roughly once per second until the count drops back under 560 // tics. if(player->nukagecount) { player->nukagecount--; if(!(leveltime & 0x1f) && player->nukagecount > 16*TICRATE) P_DamageMobj(player->mo, NULL, NULL, 5); } if(player->damagecount) player->damagecount--; if(player->bonuscount) player->bonuscount--; // villsa [STRIFE] checks for extralight if(player->extralight >= 0) { if(player->cheats & CF_ONFIRE) player->fixedcolormap = 1; else player->fixedcolormap = 0; } else // Sigil shock: player->fixedcolormap = INVERSECOLORMAP; } // // P_RemoveInventoryItem // villsa [STRIFE] new function // const char* P_RemoveInventoryItem(player_t *player, int slot, int amount) { mobjtype_t type; player->inventory[slot].amount -= amount; player->st_update = true; type = player->inventory[slot].type; if(!player->inventory[slot].amount) { // shift everything above it down // see P_TakeDialogItem for notes on possible bugs int j; for(j = slot + 1; j <= player->numinventory; j++) { inventory_t *item1 = &(player->inventory[j - 1]); inventory_t *item2 = &(player->inventory[j]); *item1 = *item2; } player->inventory[player->numinventory].type = NUMMOBJTYPES; player->inventory[player->numinventory].sprite = -1; player->numinventory--; // update cursor position if(player->inventorycursor >= player->numinventory) { if(player->inventorycursor) player->inventorycursor--; } } return mobjinfo[type].name; } // // P_DropInventoryItem // villsa [STRIFE] new function // void P_DropInventoryItem(player_t* player, int sprite) { int invslot; inventory_t *item; mobjtype_t type; int amount; invslot = 0; amount = 1; while(invslot < player->numinventory && sprite != player->inventory[invslot].sprite) invslot++; item = &(player->inventory[invslot]); type = item->type; if(item->amount) { angle_t angle; fixed_t dist; mobj_t* mo; mobj_t* mobjitem; fixed_t x; fixed_t y; fixed_t z; int r; if(item->type == MT_MONY_1) { if(item->amount >= 50) { type = MT_MONY_50; amount = 50; } else if(item->amount >= 25) { type = MT_MONY_25; amount = 25; } else if(item->amount >= 10) { type = MT_MONY_10; amount = 10; } } if(type >= NUMMOBJTYPES) return; angle = player->mo->angle; r = P_Random(); angle = (angle + ((r - P_Random()) << 18)) >> ANGLETOFINESHIFT; if(angle < 7618 && angle >= 6718) angle = 7618; else if(angle < 5570 && angle >= 4670) angle = 5570; else if(angle < 3522 && angle >= 2622) angle = 3522; else if(angle < 1474 && angle >= 574) angle = 1474; mo = player->mo; dist = mobjinfo[type].radius + mo->info->radius + (4*FRACUNIT); x = mo->x + FixedMul(finecosine[angle], dist); y = mo->y + FixedMul(finesine[angle], dist); z = mo->z + (10*FRACUNIT); mobjitem = P_SpawnMobj(x, y, z, type); mobjitem->flags |= (MF_SPECIAL|MF_DROPPED); if(P_CheckPosition(mobjitem, x, y)) { mobjitem->angle = (angle << ANGLETOFINESHIFT); mobjitem->momx = FixedMul(finecosine[angle], (5*FRACUNIT)) + mo->momx; mobjitem->momy = FixedMul(finesine[angle], (5*FRACUNIT)) + mo->momy; mobjitem->momz = FRACUNIT; P_RemoveInventoryItem(player, invslot, amount); } else P_RemoveMobj(mobjitem); } } // // P_TossDegninOre // villsa [STRIFE] new function // boolean P_TossDegninOre(player_t* player) { angle_t angle; mobj_t* mo; mobj_t* ore; fixed_t x; fixed_t y; fixed_t z; fixed_t dist; angle = player->mo->angle >> ANGLETOFINESHIFT; if(angle < 7618 && angle >= 6718) angle = 7618; else if(angle < 5570 && angle >= 4670) angle = 5570; else if(angle < 3522 && angle >= 2622) angle = 3522; else if(angle < 1474 && angle >= 574) angle = 1474; mo = player->mo; dist = mobjinfo[MT_DEGNINORE].radius + mo->info->radius + (4*FRACUNIT); x = mo->x + FixedMul(finecosine[angle], dist); y = mo->y + FixedMul(finesine[angle], dist); z = mo->z + (10*FRACUNIT); ore = P_SpawnMobj(x, y, z, MT_DEGNINORE); if(P_CheckPosition(ore, x, y)) { ore->target = mo; ore->angle = (angle << ANGLETOFINESHIFT); ore->momx = FixedMul(finecosine[angle], (5*FRACUNIT)); ore->momy = FixedMul(finesine[angle], (5*FRACUNIT)); ore->momz = FRACUNIT; return true; } else P_RemoveMobj(ore); return false; } // // P_SpawnTeleportBeacon // // villsa [STRIFE] new function // haleyjd 20140918: bug fixed to propagate allegiance properly. // boolean P_SpawnTeleportBeacon(player_t* player) { angle_t angle; int r; mobj_t* mo; mobj_t* beacon; fixed_t x; fixed_t y; fixed_t z; fixed_t dist; angle = player->mo->angle; r = P_Random(); angle = (angle + ((r - P_Random()) << 18)) >> ANGLETOFINESHIFT; if(angle < 7618 && angle >= 6718) angle = 7618; else if(angle < 5570 && angle >= 4670) angle = 5570; else if(angle < 3522 && angle >= 2622) angle = 3522; else if(angle < 1474 && angle >= 574) angle = 1474; mo = player->mo; dist = mobjinfo[MT_BEACON].radius + mo->info->radius + (4*FRACUNIT); x = mo->x + FixedMul(finecosine[angle], dist); y = mo->y + FixedMul(finesine[angle], dist); z = mo->z + (10*FRACUNIT); beacon = P_SpawnMobj(x, y, z, MT_BEACON); if(P_CheckPosition(beacon, x, y)) { beacon->target = mo; beacon->miscdata = (byte)(player->allegiance); beacon->angle = (angle << ANGLETOFINESHIFT); beacon->momx = FixedMul(finecosine[angle], (5*FRACUNIT)); beacon->momy = FixedMul(finesine[angle], (5*FRACUNIT)); beacon->momz = FRACUNIT; P_SetMobjState(beacon, beacon->info->seestate); return true; } else P_RemoveMobj(beacon); return false; } // // P_UseInventoryItem // villsa [STRIFE] new function // boolean P_UseInventoryItem(player_t* player, int item) { int i; const char *name; if(player->cheats & CF_ONFIRE) return false; for(i = 0; i < player->numinventory; i++) { if(item != player->inventory[i].sprite) continue; if(!P_ItemBehavior(player, item)) return false; name = P_RemoveInventoryItem(player, i, 1); if(name == NULL) name = "Item"; M_snprintf(useinventorymsg, sizeof(useinventorymsg), "You used the %s.", name); player->message = useinventorymsg; if(player == &players[consoleplayer]) S_StartSound(NULL, sfx_itemup); return true; } return false; } // // P_ItemBehavior // villsa [STRIFE] new function // boolean P_ItemBehavior(player_t* player, int item) { switch(item) { case SPR_ARM1: // 136 return P_GiveArmor(player, 2); case SPR_ARM2: // 137 return P_GiveArmor(player, 1); case SPR_SHD1: // 186 return P_GivePower(player, pw_invisibility); case SPR_MASK: // 187 return P_GivePower(player, pw_ironfeet); case SPR_PMUP: // 191 if(!player->powers[pw_allmap]) { player->message = "The scanner won't work without a map!"; return false; } player->powers[pw_allmap] = PMUPTICS; return true; // haleyjd 20110228: repaired case SPR_STMP: // 180 return P_GiveBody(player, 10); case SPR_MDKT: // 181 return P_GiveBody(player, 25); case SPR_FULL: // 130 return P_GiveBody(player, 200); case SPR_BEAC: // 135 return P_SpawnTeleportBeacon(player); case SPR_TARG: // 108 return P_GivePower(player, pw_targeter); } return false; } crispy-doom-crispy-doom-5.6.4/src/strife/r_bsp.c000066400000000000000000000266101360717211000215750ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // BSP traversal, handling of LineSegs for rendering. // #include "doomdef.h" #include "m_bbox.h" #include "i_system.h" #include "r_main.h" #include "r_plane.h" #include "r_things.h" // State. #include "doomstat.h" #include "r_state.h" //#include "r_local.h" seg_t* curline; side_t* sidedef; line_t* linedef; sector_t* frontsector; sector_t* backsector; drawseg_t drawsegs[MAXDRAWSEGS]; drawseg_t* ds_p; void R_StoreWallRange ( int start, int stop ); // // R_ClearDrawSegs // void R_ClearDrawSegs (void) { ds_p = drawsegs; } // // ClipWallSegment // Clips the given range of columns // and includes it in the new clip list. // typedef struct { int first; int last; } cliprange_t; // We must expand MAXSEGS to the theoretical limit of the number of solidsegs // that can be generated in a scene by the DOOM engine. This was determined by // Lee Killough during BOOM development to be a function of the screensize. // The simplest thing we can do, other than fix this bug, is to let the game // render overage and then bomb out by detecting the overflow after the // fact. -haleyjd //#define MAXSEGS 32 #define MAXSEGS (MAXWIDTH / 2 + 1) // newend is one past the last valid seg cliprange_t* newend; cliprange_t solidsegs[MAXSEGS]; // // R_ClipSolidWallSegment // Does handle solid walls, // e.g. single sided LineDefs (middle texture) // that entirely block the view. // void R_ClipSolidWallSegment ( int first, int last ) { cliprange_t* next; cliprange_t* start; // Find the first range that touches the range // (adjacent pixels are touching). start = solidsegs; while (start->last < first-1) start++; if (first < start->first) { if (last < start->first-1) { // Post is entirely visible (above start), // so insert a new clippost. R_StoreWallRange (first, last); next = newend; newend++; while (next != start) { *next = *(next-1); next--; } next->first = first; next->last = last; return; } // There is a fragment above *start. R_StoreWallRange (first, start->first - 1); // Now adjust the clip size. start->first = first; } // Bottom contained in start? if (last <= start->last) return; next = start; while (last >= (next+1)->first-1) { // There is a fragment between two posts. R_StoreWallRange (next->last + 1, (next+1)->first - 1); next++; if (last <= next->last) { // Bottom is contained in next. // Adjust the clip size. start->last = next->last; goto crunch; } } // There is a fragment after *next. R_StoreWallRange (next->last + 1, last); // Adjust the clip size. start->last = last; // Remove start+1 to next from the clip list, // because start now covers their area. crunch: if (next == start) { // Post just extended past the bottom of one post. return; } while (next++ != newend) { // Remove a post. *++start = *next; } newend = start+1; } // // R_ClipPassWallSegment // Clips the given range of columns, // but does not includes it in the clip list. // Does handle windows, // e.g. LineDefs with upper and lower texture. // void R_ClipPassWallSegment ( int first, int last ) { cliprange_t* start; // Find the first range that touches the range // (adjacent pixels are touching). start = solidsegs; while (start->last < first-1) start++; if (first < start->first) { if (last < start->first-1) { // Post is entirely visible (above start). R_StoreWallRange (first, last); return; } // There is a fragment above *start. R_StoreWallRange (first, start->first - 1); } // Bottom contained in start? if (last <= start->last) return; while (last >= (start+1)->first-1) { // There is a fragment between two posts. R_StoreWallRange (start->last + 1, (start+1)->first - 1); start++; if (last <= start->last) return; } // There is a fragment after *next. R_StoreWallRange (start->last + 1, last); } // // R_ClearClipSegs // void R_ClearClipSegs (void) { solidsegs[0].first = -0x7fffffff; solidsegs[0].last = -1; solidsegs[1].first = viewwidth; solidsegs[1].last = 0x7fffffff; newend = solidsegs+2; } // // R_AddLine // Clips the given segment // and adds any visible pieces to the line list. // void R_AddLine (seg_t* line) { int x1; int x2; angle_t angle1; angle_t angle2; angle_t span; angle_t tspan; curline = line; // OPTIMIZE: quickly reject orthogonal back sides. angle1 = R_PointToAngle (line->v1->x, line->v1->y); angle2 = R_PointToAngle (line->v2->x, line->v2->y); // Clip to view edges. // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). span = angle1 - angle2; // Back side? I.e. backface culling? if (span >= ANG180) return; // Global angle needed by segcalc. rw_angle1 = angle1; angle1 -= viewangle; angle2 -= viewangle; tspan = angle1 + clipangle; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return; angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return; angle2 = 0 - clipangle; } // The seg is in the view range, // but not necessarily visible. angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; x1 = viewangletox[angle1]; x2 = viewangletox[angle2]; // Does not cross a pixel? if (x1 == x2) return; backsector = line->backsector; // Single sided line? if (!backsector) goto clipsolid; // Closed door. if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) goto clipsolid; // Window. if (backsector->ceilingheight != frontsector->ceilingheight || backsector->floorheight != frontsector->floorheight) goto clippass; // Reject empty lines used for triggers // and special events. // Identical floor and ceiling on both sides, // identical light levels on both sides, // and no middle texture. if (backsector->ceilingpic == frontsector->ceilingpic && backsector->floorpic == frontsector->floorpic && backsector->lightlevel == frontsector->lightlevel && curline->sidedef->midtexture == 0) { return; } clippass: R_ClipPassWallSegment (x1, x2-1); return; clipsolid: R_ClipSolidWallSegment (x1, x2-1); } // // R_CheckBBox // Checks BSP node/subtree bounding box. // Returns true // if some part of the bbox might be visible. // int checkcoord[12][4] = { {3,0,2,1}, {3,0,2,0}, {3,1,2,0}, {0}, {2,0,2,1}, {0,0,0,0}, {3,1,3,0}, {0}, {2,0,3,1}, {2,1,3,1}, {2,1,3,0} }; boolean R_CheckBBox (fixed_t* bspcoord) { int boxx; int boxy; int boxpos; fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; angle_t angle1; angle_t angle2; angle_t span; angle_t tspan; cliprange_t* start; int sx1; int sx2; // Find the corners of the box // that define the edges from current viewpoint. if (viewx <= bspcoord[BOXLEFT]) boxx = 0; else if (viewx < bspcoord[BOXRIGHT]) boxx = 1; else boxx = 2; if (viewy >= bspcoord[BOXTOP]) boxy = 0; else if (viewy > bspcoord[BOXBOTTOM]) boxy = 1; else boxy = 2; boxpos = (boxy<<2)+boxx; if (boxpos == 5) return true; x1 = bspcoord[checkcoord[boxpos][0]]; y1 = bspcoord[checkcoord[boxpos][1]]; x2 = bspcoord[checkcoord[boxpos][2]]; y2 = bspcoord[checkcoord[boxpos][3]]; // check clip list for an open space angle1 = R_PointToAngle (x1, y1) - viewangle; angle2 = R_PointToAngle (x2, y2) - viewangle; span = angle1 - angle2; // Sitting on a line? if (span >= ANG180) return true; tspan = angle1 + clipangle; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return false; angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return false; angle2 = 0 - clipangle; } // Find the first clippost // that touches the source post // (adjacent pixels are touching). angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; sx1 = viewangletox[angle1]; sx2 = viewangletox[angle2]; // Does not cross a pixel. if (sx1 == sx2) return false; sx2--; start = solidsegs; while (start->last < sx2) start++; if (sx1 >= start->first && sx2 <= start->last) { // The clippost contains the new span. return false; } return true; } // // R_Subsector // Determine floor/ceiling planes. // Add sprites of things in sector. // Draw one or more line segments. // void R_Subsector (int num) { int count; seg_t* line; subsector_t* sub; #ifdef RANGECHECK if (num>=numsubsectors) I_Error ("R_Subsector: ss %i with numss = %i", num, numsubsectors); #endif sscount++; sub = &subsectors[num]; frontsector = sub->sector; count = sub->numlines; line = &segs[sub->firstline]; if (frontsector->floorheight < viewz) { floorplane = R_FindPlane (frontsector->floorheight, frontsector->floorpic, frontsector->lightlevel); } else floorplane = NULL; if (frontsector->ceilingheight > viewz || frontsector->ceilingpic == skyflatnum) { ceilingplane = R_FindPlane (frontsector->ceilingheight, frontsector->ceilingpic, frontsector->lightlevel); } else ceilingplane = NULL; R_AddSprites (frontsector); while (count--) { R_AddLine (line); line++; } // check for solidsegs overflow - extremely unsatisfactory! if(newend > &solidsegs[32] && false) I_Error("R_Subsector: solidsegs overflow (vanilla may crash here)\n"); } // // RenderBSPNode // Renders all subsectors below a given node, // traversing subtree recursively. // Just call with BSP root. void R_RenderBSPNode (int bspnum) { node_t* bsp; int side; // Found a subsector? if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) R_Subsector (0); else R_Subsector (bspnum&(~NF_SUBSECTOR)); return; } bsp = &nodes[bspnum]; // Decide which side the view point is on. side = R_PointOnSide (viewx, viewy, bsp); // Recursively divide front space. R_RenderBSPNode (bsp->children[side]); // Possibly divide back space. if (R_CheckBBox (bsp->bbox[side^1])) R_RenderBSPNode (bsp->children[side^1]); } crispy-doom-crispy-doom-5.6.4/src/strife/r_bsp.h000066400000000000000000000025311360717211000215760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh module, BSP traversal and handling. // #ifndef __R_BSP__ #define __R_BSP__ extern seg_t* curline; extern side_t* sidedef; extern line_t* linedef; extern sector_t* frontsector; extern sector_t* backsector; extern int rw_x; extern int rw_stopx; extern boolean segtextured; // false if the back side is the same plane extern boolean markfloor; extern boolean markceiling; extern boolean skymap; extern drawseg_t drawsegs[MAXDRAWSEGS]; extern drawseg_t* ds_p; extern lighttable_t** hscalelight; extern lighttable_t** vscalelight; extern lighttable_t** dscalelight; typedef void (*drawfunc_t) (int start, int stop); // BSP? void R_ClearClipSegs (void); void R_ClearDrawSegs (void); void R_RenderBSPNode (int bspnum); #endif crispy-doom-crispy-doom-5.6.4/src/strife/r_data.c000066400000000000000000000551261360717211000217260ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Preparation of data for rendering, // generation of lookups, caching, retrieval by name. // #include #include "d_main.h" #include "deh_main.h" #include "i_swap.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "doomdef.h" #include "r_local.h" #include "p_local.h" #include "doomstat.h" #include "m_misc.h" #include "r_sky.h" #include "r_data.h" #include "sounds.h" // villsa [STRIFE] // // 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 PACKED_STRUCT ( { short originx; short originy; short patch; //short stepdir; // villsa [STRIFE] removed //short colormap; // villsa [STRIFE] removed }) mappatch_t; // // Texture definition. // A DOOM wall texture is a list of patches // which are to be combined in a predefined order. // typedef PACKED_STRUCT ( { char name[8]; int masked; short width; short height; //int obsolete; // villsa [STRIFE] removed short patchcount; mappatch_t patches[1]; }) maptexture_t; // A single patch from a texture definition, // basically a rectangular area within // the texture rectangle. typedef struct { // Block origin (allways UL), // which has allready accounted // for the internal origin of the patch. short originx; short originy; int patch; } texpatch_t; // A maptexturedef_t describes a rectangular texture, // which is composed of one or more mappatch_t structures // that arrange graphic patches. typedef struct texture_s texture_t; struct texture_s { // Keep name for switch changing, etc. char name[8]; short width; short height; // Index in textures list int index; // Next in hash table chain texture_t *next; // All the patches[patchcount] // are drawn back to front into the cached texture. short patchcount; texpatch_t patches[1]; }; int firstflat; int lastflat; int numflats; int firstpatch; int lastpatch; int numpatches; int firstspritelump; int lastspritelump; int numspritelumps; int numtextures; texture_t** textures; texture_t** textures_hashtable; int* texturewidthmask; // needed for texture pegging fixed_t* textureheight; int* texturecompositesize; short** texturecolumnlump; unsigned short** texturecolumnofs; byte** texturecomposite; // for global animation int* flattranslation; int* texturetranslation; // needed for pre rendering fixed_t* spritewidth; fixed_t* spriteoffset; fixed_t* spritetopoffset; lighttable_t *colormaps; // // MAPTEXTURE_T CACHING // When a texture is first needed, // it counts the number of composite columns // required in the texture and allocates space // for a column directory and any new columns. // The directory will simply point inside other patches // if there is only one patch in a given column, // but any columns with multiple patches // will have new column_ts generated. // // // R_DrawColumnInCache // Clip and draw a column // from a patch into a cached post. // void R_DrawColumnInCache ( column_t* patch, byte* cache, int originy, int cacheheight ) { int count; int position; byte* source; while (patch->topdelta != 0xff) { source = (byte *)patch + 3; count = patch->length; position = originy + patch->topdelta; if (position < 0) { count += position; position = 0; } if (position + count > cacheheight) count = cacheheight - position; if (count > 0) memcpy (cache + position, source, count); patch = (column_t *)( (byte *)patch + patch->length + 4); } } // // R_GenerateComposite // Using the texture definition, // the composite texture is created from the patches, // and each column is cached. // void R_GenerateComposite (int texnum) { byte* block; texture_t* texture; texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; column_t* patchcol; short* collump; unsigned short* colofs; texture = textures[texnum]; block = Z_Malloc (texturecompositesize[texnum], PU_STATIC, &texturecomposite[texnum]); collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // Composite the columns together. patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1<0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; x= 0) continue; patchcol = (column_t *)((byte *)realpatch + LONG(realpatch->columnofs[x-x1])); R_DrawColumnInCache (patchcol, block + colofs[x], patch->originy, texture->height); } } // Now that the texture has been built in column cache, // it is purgable from zone memory. Z_ChangeTag (block, PU_CACHE); } // // R_GenerateLookup // void R_GenerateLookup (int texnum) { texture_t* texture; byte* patchcount; // patchcount[texture->width] texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; short* collump; unsigned short* colofs; texture = textures[texnum]; // Composited texture not created yet. texturecomposite[texnum] = 0; texturecompositesize[texnum] = 0; collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // Now count the number of columns // that are covered by more than one patch. // Fill in the lump / offset, so columns // with only a single patch are all done. patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount); memset (patchcount, 0, texture->width); patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; xpatch; colofs[x] = LONG(realpatch->columnofs[x-x1])+3; } } for (x=0 ; xwidth ; x++) { if (!patchcount[x]) { printf ("R_GenerateLookup: column without a patch (%s)\n", texture->name); return; } // I_Error ("R_GenerateLookup: column without a patch"); if (patchcount[x] > 1) { // Use the cached block. collump[x] = -1; colofs[x] = texturecompositesize[texnum]; if (texturecompositesize[texnum] > 0x10000-texture->height) { I_Error ("R_GenerateLookup: texture %i is >64k", texnum); } texturecompositesize[texnum] += texture->height; } } Z_Free(patchcount); } // // R_GetColumn // byte* R_GetColumn ( int tex, int col ) { int lump; int ofs; col &= texturewidthmask[tex]; lump = texturecolumnlump[tex][col]; ofs = texturecolumnofs[tex][col]; if (lump > 0) return (byte *)W_CacheLumpNum(lump,PU_CACHE)+ofs; if (!texturecomposite[tex]) R_GenerateComposite (tex); return texturecomposite[tex] + ofs; } static void GenerateTextureHashTable(void) { texture_t **rover; int i; int key; textures_hashtable = Z_Malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0); memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures); // Add all textures to hash table for (i=0; iindex = i; // Vanilla Doom does a linear search of the texures array // and stops at the first entry it finds. If there are two // entries with the same name, the first one in the array // wins. The new entry must therefore be added at the end // of the hash chain, so that earlier entries win. key = W_LumpNameHash(textures[i]->name) % numtextures; rover = &textures_hashtable[key]; while (*rover != NULL) { rover = &(*rover)->next; } // Hook into hash table textures[i]->next = NULL; *rover = textures[i]; } } // // R_InitTextures // Initializes the texture list // with the textures from the world map. // void R_InitTextures (void) { maptexture_t* mtexture; texture_t* texture; mappatch_t* mpatch; texpatch_t* patch; int i; int j; int* maptex; int* maptex2; int* maptex1; char name[9]; char* names; char* name_p; int* patchlookup; int totalwidth; int nummappatches; int offset; int maxoff; int maxoff2; int numtextures1; int numtextures2; int* directory; int temp1; int temp2; int temp3; // Load the patch names from pnames.lmp. names = W_CacheLumpName (DEH_String("PNAMES"), PU_STATIC); nummappatches = LONG ( *((int *)names) ); name_p = names+4; patchlookup = Z_Malloc(nummappatches*sizeof(*patchlookup), PU_STATIC, NULL); for (i = 0; i < nummappatches; i++) { M_StringCopy(name, name_p + i * 8, sizeof(name)); patchlookup[i] = W_CheckNumForName (name); } W_ReleaseLumpName(DEH_String("PNAMES")); // Load the map texture definitions from textures.lmp. // The data is contained in one or two lumps, // TEXTURE1 for shareware, plus TEXTURE2 for commercial. maptex = maptex1 = W_CacheLumpName (DEH_String("TEXTURE1"), PU_STATIC); numtextures1 = LONG(*maptex); maxoff = W_LumpLength (W_GetNumForName (DEH_String("TEXTURE1"))); directory = maptex+1; if (W_CheckNumForName (DEH_String("TEXTURE2")) != -1) { maptex2 = W_CacheLumpName (DEH_String("TEXTURE2"), PU_STATIC); numtextures2 = LONG(*maptex2); maxoff2 = W_LumpLength (W_GetNumForName (DEH_String("TEXTURE2"))); } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; } numtextures = numtextures1 + numtextures2; textures = Z_Malloc (numtextures * sizeof(*textures), PU_STATIC, 0); texturecolumnlump = Z_Malloc (numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0); texturecolumnofs = Z_Malloc (numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0); texturecomposite = Z_Malloc (numtextures * sizeof(*texturecomposite), PU_STATIC, 0); texturecompositesize = Z_Malloc (numtextures * sizeof(*texturecompositesize), PU_STATIC, 0); texturewidthmask = Z_Malloc (numtextures * sizeof(*texturewidthmask), PU_STATIC, 0); textureheight = Z_Malloc (numtextures * sizeof(*textureheight), PU_STATIC, 0); totalwidth = 0; // Really complex printing shit... temp1 = W_GetNumForName (DEH_String("S_START")); // P_??????? temp2 = W_GetNumForName (DEH_String("S_END")) - 1; temp3 = ((temp2-temp1+63)/64) + ((numtextures+63)/64); // If stdout is a real console, use the classic vanilla "filling // up the box" effect, which uses backspace to "step back" inside // the box. If stdout is a file, don't draw the box. // haleyjd 20110206 [STRIFE]: box is in devparm only if (devparm && I_ConsoleStdout()) { printf("["); for (i = 0; i < temp3 + 9; i++) printf(" "); printf("]"); for (i = 0; i < temp3 + 10; i++) printf("\b"); } for (i=0 ; i maxoff) I_Error ("R_InitTextures: bad texture directory"); mtexture = (maptexture_t *) ( (byte *)maptex + offset); texture = textures[i] = Z_Malloc (sizeof(texture_t) + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1), PU_STATIC, 0); texture->width = SHORT(mtexture->width); texture->height = SHORT(mtexture->height); texture->patchcount = SHORT(mtexture->patchcount); memcpy (texture->name, mtexture->name, sizeof(texture->name)); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; for (j=0 ; jpatchcount ; j++, mpatch++, patch++) { patch->originx = SHORT(mpatch->originx); patch->originy = SHORT(mpatch->originy); patch->patch = patchlookup[SHORT(mpatch->patch)]; if (patch->patch == -1) { I_Error ("R_InitTextures: Missing patch in texture %s", texture->name); } } texturecolumnlump[i] = Z_Malloc (texture->width*sizeof(**texturecolumnlump), PU_STATIC,0); texturecolumnofs[i] = Z_Malloc (texture->width*sizeof(**texturecolumnofs), PU_STATIC,0); j = 1; while (j*2 <= texture->width) j<<=1; texturewidthmask[i] = j-1; textureheight[i] = texture->height<width; } Z_Free(patchlookup); W_ReleaseLumpName(DEH_String("TEXTURE1")); if (maptex2) W_ReleaseLumpName(DEH_String("TEXTURE2")); // Precalculate whatever possible. for (i=0 ; iwidth)<leftoffset)<topoffset)<name, name, 8) ) return texture->index; texture = texture->next; } return -1; } // // R_TextureNumForName // Calls R_CheckTextureNumForName, // aborts with error message. // int R_TextureNumForName (const char* name) { int i; i = R_CheckTextureNumForName (name); if (i==-1) { I_Error ("R_TextureNumForName: %s not found", name); } return i; } // // R_SoundNumForDoor // // villsa [STRIFE] - new function // Set sounds associated with door though why // on earth is this function placed here? // void R_SoundNumForDoor(vldoor_t* door) { int i; sector_t *sector; line_t *line; texture_t *texture; char name[8]; char c1; char c2; // set default sounds door->opensound = sfx_drsmto; door->closesound = sfx_drsmtc; for(sector = door->sector, i = 0; i < sector->linecount; i++) { line = sector->lines[i]; if(!(line->flags & ML_TWOSIDED)) continue; texture = textures[sides[line->sidenum[0]].toptexture]; memcpy(name, texture->name, 8); if(strncmp(name, "DOR", 3)) continue; c1 = name[3]; c2 = name[4]; // S type if(c1 == 'S') { door->opensound = sfx_drston; door->closesound = sfx_drston; return; } // M type if(c1 == 'M') { // L subtype if(c2 == 'L') { door->opensound = sfx_drlmto; door->closesound = sfx_drlmtc; } // S subtype else if(c2 == 'S') { door->opensound = sfx_drsmto; door->closesound = sfx_drsmtc; } return; } // W type else if(c1 == 'W') { // L subtype if(c2 == 'L') { door->opensound = sfx_drlwud; door->closesound = sfx_drlwud; } // S subtype else if(c2 == 'S') { door->opensound = sfx_drswud; door->closesound = sfx_drswud; } return; } } } // // R_PrecacheLevel // Preloads all relevant graphics for the level. // int flatmemory; int texturememory; int spritememory; void R_PrecacheLevel (void) { char* flatpresent; char* texturepresent; char* spritepresent; int i; int j; int k; int lump; texture_t* texture; thinker_t* th; spriteframe_t* sf; if (demoplayback) return; // Precache flats. flatpresent = Z_Malloc(numflats, PU_STATIC, NULL); memset (flatpresent,0,numflats); for (i=0 ; isize; W_CacheLumpNum(lump, PU_CACHE); } } Z_Free(flatpresent); // Precache textures. texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL); memset (texturepresent,0, numtextures); for (i=0 ; ipatchcount ; j++) { lump = texture->patches[j].patch; texturememory += lumpinfo[lump]->size; W_CacheLumpNum(lump , PU_CACHE); } } Z_Free(texturepresent); // Precache sprites. spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL); memset (spritepresent,0, numsprites); for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) spritepresent[((mobj_t *)th)->sprite] = 1; } spritememory = 0; for (i=0 ; ilump[k]; spritememory += lumpinfo[lump]->size; W_CacheLumpNum(lump , PU_CACHE); } } } Z_Free(spritepresent); } crispy-doom-crispy-doom-5.6.4/src/strife/r_data.h000066400000000000000000000025421360717211000217250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh module, data I/O, caching, retrieval of graphics // by name. // #ifndef __R_DATA__ #define __R_DATA__ #include "r_defs.h" #include "r_state.h" #include "p_spec.h" // villsa [STRIFE] // Retrieve column data for span blitting. byte* R_GetColumn ( int tex, int col ); // I/O, setting up the stuff. void R_InitData (void); void R_PrecacheLevel (void); // Retrieval. // Floor/ceiling opaque texture tiles, // lookup by name. For animation? int R_FlatNumForName(const char *name); // Called by P_Ticker for switches and animations, // returns the texture number for the texture name. int R_TextureNumForName (const char *name); int R_CheckTextureNumForName (const char *name); void R_SoundNumForDoor(vldoor_t* door); // villsa [STRIFE] #endif crispy-doom-crispy-doom-5.6.4/src/strife/r_defs.h000066400000000000000000000200031360717211000217250ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh/rendering module, shared data struct definitions. // #ifndef __R_DEFS__ #define __R_DEFS__ // Screenwidth. #include "doomdef.h" // Some more or less basic data types // we depend on. #include "m_fixed.h" // We rely on the thinker data struct // to handle sound origins in sectors. #include "d_think.h" // SECTORS do store MObjs anyway. #include "p_mobj.h" #include "i_video.h" #include "v_patch.h" // 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*8 // // INTERNAL MAP TYPES // used by play and refresh // // // Your plain vanilla vertex. // Note: transformed values not buffered locally, // like some DOOM-alikes ("wt", "WebView") did. // typedef struct { fixed_t x; fixed_t y; } vertex_t; // Forward of LineDefs, for Sectors. struct line_s; // Each sector has a degenmobj_t in its center // for sound origin purposes. // I suppose this does not handle sound from // moving objects (doppler), because // position is prolly just buffered, not // updated. typedef struct { thinker_t thinker; // not used for anything fixed_t x; fixed_t y; fixed_t z; } degenmobj_t; // // The SECTORS record, at runtime. // Stores things/mobjs. // typedef struct { fixed_t floorheight; fixed_t ceilingheight; short floorpic; short ceilingpic; short lightlevel; short special; short tag; // 0 = untraversed, 1,2 = sndlines -1 int soundtraversed; // thing that made a sound (or null) mobj_t* soundtarget; // mapblock bounding box for height changes int blockbox[4]; // origin for any sounds played by the sector degenmobj_t soundorg; // if == validcount, already checked int validcount; // list of mobjs in sector mobj_t* thinglist; // thinker_t for reversable actions void* specialdata; int linecount; struct line_s** lines; // [linecount] size } sector_t; // // The SideDef. // typedef struct { // add this to the calculated texture column fixed_t textureoffset; // add this to the calculated texture top fixed_t rowoffset; // Texture indices. // We do not maintain names here. short toptexture; short bottomtexture; short midtexture; // Sector the SideDef is facing. sector_t* sector; } side_t; // // Move clipping aid for LineDefs. // typedef enum { ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE } slopetype_t; typedef struct line_s { // Vertices, from v1 to v2. vertex_t* v1; vertex_t* v2; // Precalculated v2 - v1 for side checking. fixed_t dx; fixed_t dy; // Animation related. short flags; short special; short tag; // Visual appearance: SideDefs. // sidenum[1] will be -1 if one sided short sidenum[2]; // Neat. Another bounding box, for the extent // of the LineDef. fixed_t bbox[4]; // To aid move clipping. slopetype_t slopetype; // Front and back sector. // Note: redundant? Can be retrieved from SideDefs. sector_t* frontsector; sector_t* backsector; // if == validcount, already checked int validcount; // thinker_t for reversable actions void* specialdata; } line_t; // // A SubSector. // References a Sector. // Basically, this is a list of LineSegs, // indicating the visible walls that define // (all or some) sides of a convex BSP leaf. // typedef struct subsector_s { sector_t* sector; short numlines; short firstline; } subsector_t; // // The LineSeg. // typedef struct { vertex_t* v1; vertex_t* v2; fixed_t offset; angle_t angle; side_t* sidedef; line_t* linedef; // Sector references. // Could be retrieved from linedef, too. // backsector is NULL for one sided lines sector_t* frontsector; sector_t* backsector; } seg_t; // // BSP node. // typedef struct { // Partition line. fixed_t x; fixed_t y; fixed_t dx; fixed_t dy; // Bounding box for each child. fixed_t bbox[2][4]; // If NF_SUBSECTOR its a subsector. unsigned short children[2]; } node_t; // PC direct to screen pointers //B UNUSED - keep till detailshift in r_draw.c resolved //extern byte* destview; //extern byte* destscreen; // // OTHER TYPES // // This could be wider for >8 bit display. // Indeed, true color support is posibble // precalculating 24bpp lightmap/colormap LUT. // from darkening PLAYPAL to all black. // Could even us emore than 32 levels. typedef byte lighttable_t; // // ? // typedef struct drawseg_s { seg_t* curline; int x1; int x2; fixed_t scale1; fixed_t scale2; fixed_t scalestep; // 0=none, 1=bottom, 2=top, 3=both int silhouette; // do not clip sprites above this fixed_t bsilheight; // do not clip sprites below this fixed_t tsilheight; // Pointers to lists for sprite clipping, // all three adjusted so [x1] is first value. short* sprtopclip; short* sprbottomclip; short* maskedtexturecol; } drawseg_t; // A vissprite_t is a thing // that will be drawn during a refresh. // I.e. a sprite object that is partly visible. typedef struct vissprite_s { // Doubly linked list. struct vissprite_s* prev; struct vissprite_s* next; int x1; int x2; // for line side calculation fixed_t gx; fixed_t gy; // global bottom / top for silhouette clipping fixed_t gz; fixed_t gzt; // horizontal position of x1 fixed_t startfrac; fixed_t scale; // negative if flipped fixed_t xiscale; fixed_t texturemid; int patch; // for color translation and shadow draw, // maxbright frames as well lighttable_t* colormap; int mobjflags; } vissprite_t; // // Sprites are patches with a special naming convention // so they can be recognized by R_InitSprites. // The base name is NNNNFx or NNNNFxFx, with // x indicating the rotation, x = 0, 1-7. // The sprite and frame specified by a thing_t // is range checked at run time. // A sprite is a patch_t that is assumed to represent // a three dimensional object and may have multiple // rotations pre drawn. // Horizontal flipping is used to save space, // thus NNNNF2F5 defines a mirrored patch. // Some sprites will only have one picture used // for all views: NNNNF0 // typedef struct { // If false use 0 for any position. // Note: as eight entries are available, // we might as well insert the same name eight times. boolean rotate; // Lump to use for view angles 0-7. short lump[8]; // Flip bit (1 = flip) to use for view angles 0-7. byte flip[8]; } spriteframe_t; // // A sprite definition: // a number of animation frames. // typedef struct { int numframes; spriteframe_t* spriteframes; } spritedef_t; // // Now what is a visplane, anyway? // typedef struct { fixed_t height; int picnum; int lightlevel; int minx; int maxx; // leave pads for [minx-1]/[maxx+1] unsigned short pad1; // Here lies the rub for all // dynamic resize/change of resolution. unsigned short top[MAXWIDTH]; unsigned short pad2; unsigned short pad3; // See above. unsigned short bottom[MAXWIDTH]; unsigned short pad4; } visplane_t; #endif crispy-doom-crispy-doom-5.6.4/src/strife/r_draw.c000066400000000000000000000576001360717211000217510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The actual span/column drawing functions. // Here find the main potential for optimization, // e.g. inline assembly, different algorithms. // #include "doomdef.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "r_local.h" // Needs access to LFB (guess what). #include "v_video.h" // State. #include "doomstat.h" // ? //#define MAXWIDTH 1120 //#define MAXHEIGHT 832 // status bar height at bottom of screen // haleyjd 08/31/10: Verified unmodified. #define SBARHEIGHT (32 << crispy->hires) // // All drawing to the view buffer is accomplished in this file. // The other refresh files only know about ccordinates, // not the architecture of the frame buffer. // Conveniently, the frame buffer is a linear one, // and we need only the base address, // and the total size == width*height*depth/8., // byte* viewimage; int viewwidth; int scaledviewwidth; int viewheight; int viewwindowx; int viewwindowy; byte* ylookup[MAXHEIGHT]; int columnofs[MAXWIDTH]; // Color tables for different players, // translate a limited part to another // (color ramps used for suit colors). // // [STRIFE] Unused. //byte translations[3][256]; // Backing buffer containing the bezel drawn around the screen and // surrounding background. static byte *background_buffer = NULL; // haleyjd 08/29/10: [STRIFE] Rogue added the ability to customize the view // border flat by storing it in the configuration file. char *back_flat = "F_PAVE01"; // // R_DrawColumn // Source is the top of the column to scale. // lighttable_t* dc_colormap; int dc_x; int dc_yl; int dc_yh; fixed_t dc_iscale; fixed_t dc_texturemid; // first pixel in a column (possibly virtual) byte* dc_source; // just for profiling int dccount; // // A column is a vertical slice/span from a wall texture that, // given the DOOM style restrictions on the view orientation, // will always have constant z depth. // Thus a special case loop for very fast rendering can // be used. It has also been used with Wolfenstein 3D. // void R_DrawColumn (void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; count = dc_yh - dc_yl; // Zero length, column does not exceed a pixel. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = ylookup[dc_yl] + columnofs[dc_x]; // Determine scaling, // which is the only mapping to be done. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } // UNUSED. // Loop unrolled. #if 0 void R_DrawColumn (void) { int count; byte* source; byte* dest; byte* colormap; unsigned frac; unsigned fracstep; unsigned fracstep2; unsigned fracstep3; unsigned fracstep4; count = dc_yh - dc_yl + 1; source = dc_source; colormap = dc_colormap; dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale<<9; frac = (dc_texturemid + (dc_yl-centery)*dc_iscale)<<9; fracstep2 = fracstep+fracstep; fracstep3 = fracstep2+fracstep; fracstep4 = fracstep3+fracstep; while (count >= 8) { dest[0] = colormap[source[frac>>25]]; dest[SCREENWIDTH] = colormap[source[(frac+fracstep)>>25]]; dest[SCREENWIDTH*2] = colormap[source[(frac+fracstep2)>>25]]; dest[SCREENWIDTH*3] = colormap[source[(frac+fracstep3)>>25]]; frac += fracstep4; dest[SCREENWIDTH*4] = colormap[source[frac>>25]]; dest[SCREENWIDTH*5] = colormap[source[(frac+fracstep)>>25]]; dest[SCREENWIDTH*6] = colormap[source[(frac+fracstep2)>>25]]; dest[SCREENWIDTH*7] = colormap[source[(frac+fracstep3)>>25]]; frac += fracstep4; dest += SCREENWIDTH*8; count -= 8; } while (count > 0) { *dest = colormap[source[frac>>25]]; dest += SCREENWIDTH; frac += fracstep; count--; } } #endif // haleyjd 09/06/10 [STRIFE] Removed low detail // // Spectre/Invisibility. // // haleyjd 09/06/10: ]STRIFE] replaced fuzzdraw with translucency. // // R_DrawMVisTLColumn // // villsa [STRIFE] new function // Replacement for R_DrawFuzzColumn // void R_DrawMVisTLColumn(void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; // Adjust borders. Low... if (!dc_yl) dc_yl = 1; // .. and high. if (dc_yh == viewheight-1) dc_yh = viewheight - 2; count = dc_yh - dc_yl; // Zero length. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawFuzzColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[dc_x]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; do { byte src = dc_colormap[dc_source[(frac>>FRACBITS)&127]]; byte col = xlatab[*dest + (src << 8)]; *dest = col; dest += SCREENWIDTH; frac += fracstep; } while(count--); } // // R_DrawTLColumn // // villsa [STRIFE] new function // Achieves a second translucency level using the same lookup table, // via inversion of the colors in the index computation. // void R_DrawTLColumn(void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; // Adjust borders. Low... if (!dc_yl) dc_yl = 1; // .. and high. if (dc_yh == viewheight-1) dc_yh = viewheight - 2; count = dc_yh - dc_yl; // Zero length. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawFuzzColumn2: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[dc_x]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; do { byte src = dc_colormap[dc_source[(frac>>FRACBITS)&127]]; byte col = xlatab[(*dest << 8) + src]; *dest = col; dest += SCREENWIDTH; frac += fracstep; } while(count--); } // // R_DrawTranslatedColumn // Used to draw player sprites // with the green colorramp mapped to others. // Could be used with different translation // tables, e.g. the lighter colored version // of the BaronOfHell, the HellKnight, uses // identical sprites, kinda brightened up. // byte* dc_translation; byte* translationtables; void R_DrawTranslatedColumn (void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[dc_x]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Here we do an additional index re-mapping. do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. *dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } // haleyjd 09/06/10 [STRIFE] Removed low detail // // R_DrawTRTLColumn // // villsa [STRIFE] new function // Combines translucency and color translation. // void R_DrawTRTLColumn(void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[dc_x]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Here we do an additional index re-mapping. do { byte src = dc_colormap[dc_translation[dc_source[frac>>FRACBITS&127]]]; byte col = xlatab[(*dest << 8) + src]; *dest = col; dest += SCREENWIDTH; frac += fracstep; } while (count--); } // // R_InitTranslationTables // Creates the translation tables to map // the green color ramp to gray, brown, red. // Assumes a given structure of the PLAYPAL. // Could be read from a lump instead. // // haleyjd 08/26/10: [STRIFE] // * Added loading of XLATAB // void R_InitTranslationTables (void) { int i; byte col1, col2; // [STRIFE] Load xlatab. Here's how Rogue did it: // v7 = W_CacheLumpName("XLATAB", PU_CACHE); // note potential cache bug... // HIWORD(v8) = (Z_Malloc(131072, PU_STATIC, NULL) + 65535) >> 16; // LOWORD(v8) = 0; // aligning to a 64K boundary, as if this is Wolf3D. // xlatab = v8; // memcpy(v8, v7, 65536); // As you can see, they copypasta'd id's unnecessary 64K boundary alignment // from the colormap code. Since this doesn't accomplish anything, and isn't // strictly portable, all we need to do is this: // villsa [STRIFE] 09/26/10: load table through this function instead V_LoadXlaTable(); // villsa [STRIFE] allocate a larger size for translation tables translationtables = Z_Malloc (256*8, PU_STATIC, 0); col1 = 0xFA; col2 = 0xE0; // villsa [STRIFE] setup all translation tables for(i = 0; i < 256; i++) { if(i >= 0x80 && i <= 0x8f) { translationtables [i ] = (i & 0x0f) + 64; translationtables [i+ 256] = (i & 0x0f) - 80; translationtables [i+2*256] = (i & 0x0f) + 16; translationtables [i+3*256] = (i & 0x0f) + 48; translationtables [i+4*256] = (i & 0x0f) + 80; translationtables [i+5*256] = (i & 0x0f) + 96; translationtables [i+6*256] = (i & 0x0f) - 112; } else if(i >= 0x50 && i<= 0x5f) { translationtables [i ] = i; translationtables [i+ 256] = i; translationtables [i+2*256] = i; translationtables [i+3*256] = i; translationtables [i+4*256] = (i & 0x0f) + 0x80; translationtables [i+5*256] = (i & 0x0f) + 16; translationtables [i+6*256] = (i & 0x0f) + 64; } else if(i >= 0xd0 && i<= 0xdf) { translationtables [i ] = i; translationtables [i+ 256] = i; translationtables [i+2*256] = i; translationtables [i+3*256] = i; translationtables [i+4*256] = (i & 0x0f) - 80; translationtables [i+5*256] = (i & 0x0f) + 48; translationtables [i+6*256] = (i & 0x0f) + 16; } else if(i >= 0xc0 && i<= 0xcf) { translationtables [i ] = i; translationtables [i+ 256] = i; translationtables [i+2*256] = i; translationtables [i+3*256] = i; translationtables [i+4*256] = (i & 0x0f) - 96; translationtables [i+5*256] = (i & 0x0f) + 32; translationtables [i+6*256] = (i & 0x0f); // haleyjd 20110213: missing code: if(!(i & 0x0f)) translationtables[i+6*256] = 1; } else if(i >= 0xf7 && i<= 0xfb) { translationtables [i ] = col1; translationtables [i+ 256] = i; translationtables [i+2*256] = i; translationtables [i+3*256] = i; translationtables [i+4*256] = i; translationtables [i+5*256] = i; translationtables [i+6*256] = i; } else if(i >= 0xf1 && i<= 0xf6) { translationtables [i ] = (i & 0x0f) - 33; translationtables [i+ 256] = i; translationtables [i+2*256] = i; translationtables [i+3*256] = i; translationtables [i+4*256] = i; translationtables [i+5*256] = i; translationtables [i+6*256] = i; } else if(i >= 0x20 && i <= 0x3f) // haleyjd 20110213: fixed upper end { translationtables [i ] = col2; translationtables [i+ 256] = col2; translationtables [i+2*256] = (i & 0x0f) - 48; translationtables [i+3*256] = (i & 0x0f) - 48; translationtables [i+4*256] = col2; translationtables [i+5*256] = col2; translationtables [i+6*256] = col2; } else // Keep all other colors as is. { translationtables[i]= translationtables[i+256]= translationtables[i+2*256]= translationtables[i+3*256]= translationtables[i+4*256]= translationtables[i+5*256]= translationtables[i+6*256]=i; } ++col1; ++col2; } } // // R_DrawSpan // With DOOM style restrictions on view orientation, // the floors and ceilings consist of horizontal slices // or spans with constant z depth. // However, rotation around the world z axis is possible, // thus this mapping, while simpler and faster than // perspective correct texture mapping, has to traverse // the texture at an angle in all but a few cases. // In consequence, flats are not stored by column (like walls), // and the inner loop has to step in texture space u and v. // int ds_y; int ds_x1; int ds_x2; lighttable_t* ds_colormap; fixed_t ds_xfrac; fixed_t ds_yfrac; fixed_t ds_xstep; fixed_t ds_ystep; // start of a 64*64 tile image byte* ds_source; // just for profiling int dscount; // // Draws the actual span. void R_DrawSpan (void) { unsigned int position, step; byte *dest; int count; int spot; unsigned int xtemp, ytemp; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH || (unsigned)ds_y>SCREENHEIGHT) { I_Error( "R_DrawSpan: %i to %i at %i", ds_x1,ds_x2,ds_y); } // dscount++; #endif // Pack position and step variables into a single 32-bit integer, // with x in the top 16 bits and y in the bottom 16 bits. For // each 16-bit part, the top 6 bits are the integer part and the // bottom 10 bits are the fractional part of the pixel position. position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0x0000ffff); step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); dest = ylookup[ds_y] + columnofs[ds_x1]; // We do not check for zero spans here? count = ds_x2 - ds_x1; do { // Calculate current texture index in u,v. ytemp = (position >> 4) & 0x0fc0; xtemp = (position >> 26); spot = xtemp | ytemp; // Lookup pixel from flat texture tile, // re-index using light/colormap. *dest++ = ds_colormap[ds_source[spot]]; position += step; } while (count--); } // UNUSED. // Loop unrolled by 4. #if 0 void R_DrawSpan (void) { unsigned position, step; byte* source; byte* colormap; byte* dest; unsigned count; usingned spot; unsigned value; unsigned temp; unsigned xtemp; unsigned ytemp; position = ((ds_xfrac<<10)&0xffff0000) | ((ds_yfrac>>6)&0xffff); step = ((ds_xstep<<10)&0xffff0000) | ((ds_ystep>>6)&0xffff); source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; count = ds_x2 - ds_x1 + 1; while (count >= 4) { ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[0] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[1] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[2] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[3] = colormap[source[spot]]; count -= 4; dest += 4; } while (count > 0) { ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; *dest++ = colormap[source[spot]]; count--; } } #endif // // Again.. // void R_DrawSpanLow (void) { unsigned int position, step; unsigned int xtemp, ytemp; byte *dest; int count; int spot; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH || (unsigned)ds_y>SCREENHEIGHT) { I_Error( "R_DrawSpan: %i to %i at %i", ds_x1,ds_x2,ds_y); } // dscount++; #endif position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0x0000ffff); step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); count = (ds_x2 - ds_x1); // Blocky mode, need to multiply by 2. ds_x1 <<= 1; ds_x2 <<= 1; dest = ylookup[ds_y] + columnofs[ds_x1]; do { // Calculate current texture index in u,v. ytemp = (position >> 4) & 0x0fc0; xtemp = (position >> 26); spot = xtemp | ytemp; // Lowres/blocky mode does it twice, // while scale is adjusted appropriately. *dest++ = ds_colormap[ds_source[spot]]; *dest++ = ds_colormap[ds_source[spot]]; position += step; } while (count--); } // // R_InitBuffer // Creats lookup tables that avoid // multiplies and other hazzles // for getting the framebuffer address // of a pixel to draw. // void R_InitBuffer ( int width, int height ) { int i; // Handle resize, // e.g. smaller view windows // with border and/or status bar. viewwindowx = (SCREENWIDTH-width) >> 1; // Column offset. For windows. for (i=0 ; i> 1; // Preclaculate all row offsets. for (i=0 ; i> crispy->hires) ; x+=8) V_DrawPatch((viewwindowx >> crispy->hires)+x, (viewwindowy >> crispy->hires)-8, patch); patch = W_CacheLumpName(DEH_String("brdr_b"),PU_CACHE); for (x=0 ; x<(scaledviewwidth >> crispy->hires) ; x+=8) V_DrawPatch((viewwindowx >> crispy->hires)+x, (viewwindowy >> crispy->hires)+(viewheight >> crispy->hires), patch); patch = W_CacheLumpName(DEH_String("brdr_l"),PU_CACHE); for (y=0 ; y<(viewheight >> crispy->hires) ; y+=8) V_DrawPatch((viewwindowx >> crispy->hires)-8, (viewwindowy >> crispy->hires)+y, patch); patch = W_CacheLumpName(DEH_String("brdr_r"),PU_CACHE); for (y=0 ; y<(viewheight >> crispy->hires) ; y+=8) V_DrawPatch((viewwindowx >> crispy->hires)+(scaledviewwidth >> crispy->hires), (viewwindowy >> crispy->hires)+y, patch); // Draw beveled edge. V_DrawPatch((viewwindowx >> crispy->hires)-8, (viewwindowy >> crispy->hires)-8, W_CacheLumpName(DEH_String("brdr_tl"),PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires)+(scaledviewwidth >> crispy->hires), (viewwindowy >> crispy->hires)-8, W_CacheLumpName(DEH_String("brdr_tr"),PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires)-8, (viewwindowy >> crispy->hires)+(viewheight >> crispy->hires), W_CacheLumpName(DEH_String("brdr_bl"),PU_CACHE)); V_DrawPatch((viewwindowx >> crispy->hires)+(scaledviewwidth >> crispy->hires), (viewwindowy >> crispy->hires)+(viewheight >> crispy->hires), W_CacheLumpName(DEH_String("brdr_br"),PU_CACHE)); V_RestoreBuffer(); } // // Copy a screen buffer. // void R_VideoErase ( unsigned ofs, int count ) { // LFB copy. // This might not be a good idea if memcpy // is not optiomal, e.g. byte by byte on // a 32bit CPU, as GNU GCC/Linux libc did // at one point. if (background_buffer != NULL) { memcpy(I_VideoBuffer + ofs, background_buffer + ofs, count); } } // // R_DrawViewBorder // Draws the border around the view // for different size windows? // void R_DrawViewBorder (void) { int top; int side; int ofs; int i; if (scaledviewwidth == SCREENWIDTH) return; top = ((SCREENHEIGHT-SBARHEIGHT)-viewheight)/2; side = (SCREENWIDTH-scaledviewwidth)/2; // copy top and one line of left side R_VideoErase (0, top*SCREENWIDTH+side); // copy one line of right side and bottom ofs = (viewheight+top)*SCREENWIDTH-side; R_VideoErase (ofs, top*SCREENWIDTH+side); // copy sides using wraparound ofs = top*SCREENWIDTH + SCREENWIDTH-side; side <<= 1; for (i=1 ; i #include #include "doomdef.h" #include "doomstat.h" // villsa [STRIFE] #include "d_main.h" #include "m_bbox.h" #include "m_menu.h" #include "r_local.h" #include "r_sky.h" // Fineangles in the SCREENWIDTH wide window. #define FIELDOFVIEW 2048 int viewangleoffset; // increment every time a check is made int validcount = 1; lighttable_t* fixedcolormap; extern lighttable_t** walllights; int centerx; int centery; fixed_t centerxfrac; fixed_t centeryfrac; fixed_t projection; // just for profiling purposes int framecount; int sscount; int linecount; int loopcount; fixed_t viewx; fixed_t viewy; fixed_t viewz; int viewpitch; // villsa [STRIFE] angle_t viewangle; fixed_t viewcos; fixed_t viewsin; player_t* viewplayer; // 0 = high, 1 = low int detailshift; // // precalculated math tables // angle_t clipangle; // The viewangletox[viewangle + FINEANGLES/4] lookup // maps the visible view angles to screen X coordinates, // flattening the arc to a flat projection plane. // There will be many angles mapped to the same X. int viewangletox[FINEANGLES/2]; // The xtoviewangleangle[] table maps a screen pixel // to the lowest viewangle that maps back to x ranges // from clipangle to -clipangle. angle_t xtoviewangle[MAXWIDTH+1]; lighttable_t* scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; lighttable_t* scalelightfixed[MAXLIGHTSCALE]; lighttable_t* zlight[LIGHTLEVELS][MAXLIGHTZ]; // bumped light from gun blasts int extralight; void (*colfunc) (void); void (*basecolfunc) (void); void (*fuzzcolfunc) (void); void (*transcolfunc) (void); void (*spanfunc) (void); // // R_AddPointToBox // Expand a given bbox // so that it encloses a given point. // void R_AddPointToBox ( int x, int y, fixed_t* box ) { if (x< box[BOXLEFT]) box[BOXLEFT] = x; if (x> box[BOXRIGHT]) box[BOXRIGHT] = x; if (y< box[BOXBOTTOM]) box[BOXBOTTOM] = y; if (y> box[BOXTOP]) box[BOXTOP] = y; } // // R_PointOnSide // Traverse BSP (sub) tree, // check point against partition plane. // Returns side 0 (front) or 1 (back). // int R_PointOnSide ( fixed_t x, fixed_t y, node_t* node ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!node->dx) { if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); // Try to quickly decide by looking at sign bits. if ( (node->dy ^ node->dx ^ dx ^ dy)&0x80000000 ) { if ( (node->dy ^ dx) & 0x80000000 ) { // (left is negative) return 1; } return 0; } left = FixedMul ( node->dy>>FRACBITS , dx ); right = FixedMul ( dy , node->dx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } int R_PointOnSegSide ( fixed_t x, fixed_t y, seg_t* line ) { fixed_t lx; fixed_t ly; fixed_t ldx; fixed_t ldy; fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; lx = line->v1->x; ly = line->v1->y; ldx = line->v2->x - lx; ldy = line->v2->y - ly; if (!ldx) { if (x <= lx) return ldy > 0; return ldy < 0; } if (!ldy) { if (y <= ly) return ldx < 0; return ldx > 0; } dx = (x - lx); dy = (y - ly); // Try to quickly decide by looking at sign bits. if ( (ldy ^ ldx ^ dx ^ dy)&0x80000000 ) { if ( (ldy ^ dx) & 0x80000000 ) { // (left is negative) return 1; } return 0; } left = FixedMul ( ldy>>FRACBITS , dx ); right = FixedMul ( dy , ldx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } // // R_PointToAngle // To get a global angle from cartesian coordinates, // the coordinates are flipped until they are in // the first octant of the coordinate system, then // the y (<=x) is scaled and divided by x to get a // tangent (slope) value which is looked up in the // tantoangle[] table. // angle_t R_PointToAngle ( fixed_t x, fixed_t y ) { x -= viewx; y -= viewy; if ( (!x) && (!y) ) return 0; if (x>= 0) { // x >=0 if (y>= 0) { // y>= 0 if (x>y) { // octant 0 return tantoangle[ SlopeDiv(y,x)]; } else { // octant 1 return ANG90-1-tantoangle[ SlopeDiv(x,y)]; } } else { // y<0 y = -y; if (x>y) { // octant 8 return 0 - tantoangle[SlopeDiv(y,x)]; } else { // octant 7 return ANG270+tantoangle[ SlopeDiv(x,y)]; } } } else { // x<0 x = -x; if (y>= 0) { // y>= 0 if (x>y) { // octant 3 return ANG180-1-tantoangle[ SlopeDiv(y,x)]; } else { // octant 2 return ANG90+ tantoangle[ SlopeDiv(x,y)]; } } else { // y<0 y = -y; if (x>y) { // octant 4 return ANG180+tantoangle[ SlopeDiv(y,x)]; } else { // octant 5 return ANG270-1-tantoangle[ SlopeDiv(x,y)]; } } } return 0; } angle_t R_PointToAngle2 ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2 ) { viewx = x1; viewy = y1; return R_PointToAngle (x2, y2); } fixed_t R_PointToDist ( fixed_t x, fixed_t y ) { int angle; fixed_t dx; fixed_t dy; fixed_t temp; fixed_t dist; fixed_t frac; dx = abs(x - viewx); dy = abs(y - viewy); if (dy>dx) { temp = dx; dx = dy; dy = temp; } // Fix crashes in udm1.wad if (dx != 0) { frac = FixedDiv(dy, dx); } else { frac = 0; } angle = (tantoangle[frac>>DBITS]+ANG90) >> ANGLETOFINESHIFT; // use as cosine dist = FixedDiv (dx, finesine[angle] ); return dist; } // // R_InitPointToAngle // void R_InitPointToAngle (void) { // UNUSED - now getting from tables.c #if 0 int i; long t; float f; // // slope (tangent) to angle lookup // for (i=0 ; i<=SLOPERANGE ; i++) { f = atan( (float)i/SLOPERANGE )/(3.141592657*2); t = 0xffffffff*f; tantoangle[i] = t; } #endif } // // R_ScaleFromGlobalAngle // Returns the texture mapping scale // for the current line (horizontal span) // at the given angle. // rw_distance must be calculated first. // fixed_t R_ScaleFromGlobalAngle (angle_t visangle) { fixed_t scale; angle_t anglea; angle_t angleb; int sinea; int sineb; fixed_t num; int den; // UNUSED #if 0 { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv = finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist = FixedDiv (rw_distance, sinv); cosv = finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z = abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z); return scale; } #endif anglea = ANG90 + (visangle-viewangle); angleb = ANG90 + (visangle-rw_normalangle); // both sines are allways positive sinea = finesine[anglea>>ANGLETOFINESHIFT]; sineb = finesine[angleb>>ANGLETOFINESHIFT]; num = FixedMul(projection,sineb)< num>>16) { scale = FixedDiv (num, den); if (scale > 64*FRACUNIT) scale = 64*FRACUNIT; else if (scale < 256) scale = 256; } else scale = 64*FRACUNIT; return scale; } // // R_InitTables // void R_InitTables (void) { // UNUSED: now getting from tables.c #if 0 int i; float a; float fv; int t; // viewangle tangent table for (i=0 ; i FRACUNIT*2) t = -1; else if (finetangent[i] < -FRACUNIT*2) t = viewwidth+1; else { t = FixedMul (finetangent[i], focallength); t = (centerxfrac - t+FRACUNIT-1)>>FRACBITS; if (t < -1) t = -1; else if (t>viewwidth+1) t = viewwidth+1; } viewangletox[i] = t; } // Scan viewangletox[] to generate xtoviewangle[]: // xtoviewangle will give the smallest view angle // that maps to x. for (x=0;x<=viewwidth;x++) { i = 0; while (viewangletox[i]>x) i++; xtoviewangle[x] = (i<>= LIGHTSCALESHIFT; level = startmap - scale/DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS-1; zlight[i][j] = colormaps + level*256; } } } // // R_SetViewSize // Do not really change anything here, // because it might be in the middle of a refresh. // The change will take effect next refresh. // boolean setsizeneeded; int setblocks; int setdetail; void R_SetViewSize ( int blocks, int detail ) { setsizeneeded = true; setblocks = blocks; setdetail = detail; } // // R_ExecuteSetViewSize // void R_ExecuteSetViewSize (void) { fixed_t cosadj; fixed_t dy; int i; int j; int level; int startmap; setsizeneeded = false; if (setblocks == 11) { scaledviewwidth = SCREENWIDTH; viewheight = SCREENHEIGHT; } else { scaledviewwidth = (setblocks*32) << crispy->hires; viewheight = ((setblocks*168/10)&~7) << crispy->hires; } detailshift = setdetail; viewwidth = scaledviewwidth>>detailshift; // villsa [STRIFE] calculate centery from player's pitch centery = (setblocks*players[consoleplayer].pitch); centery = (unsigned int)(centery/10)+viewheight/2; centerx = viewwidth/2; centerxfrac = centerx< centery, accounts for up/down look dy = ((i - centery)<>ANGLETOFINESHIFT]); distscale[i] = FixedDiv (FRACUNIT,cosadj); } // Calculate the light levels to use // for each level / scale combination. for (i=0 ; i< LIGHTLEVELS ; i++) { startmap = ((LIGHTLEVELS-1-i)*2)*NUMCOLORMAPS/LIGHTLEVELS; for (j=0 ; j= NUMCOLORMAPS) level = NUMCOLORMAPS-1; scalelight[i][j] = colormaps + level*256; } } } // // R_Init // void R_Init (void) { R_InitData (); if(devparm) printf ("."); else D_IntroTick(); // [STRIFE] tick intro R_InitPointToAngle (); if(devparm) printf ("."); R_InitTables (); // viewwidth / viewheight / detailLevel are set by the defaults if(devparm) printf ("."); R_SetViewSize (screenblocks, detailLevel); R_InitPlanes (); if(devparm) printf ("."); R_InitLightTables (); if(devparm) printf ("."); else D_IntroTick(); R_InitSkyMap (); if(!devparm) D_IntroTick(); R_InitTranslationTables (); if(devparm) printf ("."); else D_IntroTick(); framecount = 0; } // // R_PointInSubsector // subsector_t* R_PointInSubsector ( fixed_t x, fixed_t y ) { node_t* node; int side; int nodenum; // single subsector is a special case if (!numnodes) return subsectors; nodenum = numnodes-1; while (! (nodenum & NF_SUBSECTOR) ) { node = &nodes[nodenum]; side = R_PointOnSide (x, y, node); nodenum = node->children[side]; } return &subsectors[nodenum & ~NF_SUBSECTOR]; } // // R_SetupPitch // villsa [STRIFE] new function // Calculate centery/centeryfrac for player viewpitch // void R_SetupPitch(player_t* player) { int pitchfrac; int i = 0; if(viewpitch != player->pitch) { viewpitch = player->pitch; pitchfrac = (setblocks * (player->pitch << crispy->hires)) / 10; centery = pitchfrac + viewheight / 2; centeryfrac = centery << FRACBITS; for(i = 0; i < viewheight; i++) { yslope[i] = FixedDiv(viewwidth / 2 * FRACUNIT, abs(((i - centery) << FRACBITS) + (FRACUNIT/2))); } } } // // R_SetupFrame // void R_SetupFrame (player_t* player) { int i; R_SetupPitch(player); // villsa [STRIFE] viewplayer = player; viewx = player->mo->x; viewy = player->mo->y; viewangle = player->mo->angle + viewangleoffset; extralight = player->extralight; viewz = player->viewz; viewsin = finesine[viewangle>>ANGLETOFINESHIFT]; viewcos = finecosine[viewangle>>ANGLETOFINESHIFT]; sscount = 0; if (player->fixedcolormap) { fixedcolormap = colormaps + player->fixedcolormap*256*sizeof(lighttable_t); walllights = scalelightfixed; for (i=0 ; i #include #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "doomdef.h" #include "doomstat.h" #include "r_local.h" #include "r_sky.h" planefunction_t floorfunc; planefunction_t ceilingfunc; // // opening // // Here comes the obnoxious "visplane". // haleyjd 08/29/10: [STRIFE] MAXVISPLANES increased to 200 #define MAXVISPLANES 200*8 visplane_t visplanes[MAXVISPLANES]; visplane_t* lastvisplane; visplane_t* floorplane; visplane_t* ceilingplane; // ? #define MAXOPENINGS MAXWIDTH*64*4 short openings[MAXOPENINGS]; short* lastopening; // // Clip values are the solid pixel bounding the range. // floorclip starts out SCREENHEIGHT // ceilingclip starts out -1 // short floorclip[MAXWIDTH]; short ceilingclip[MAXWIDTH]; // // spanstart holds the start of a plane span // initialized to 0 at start // int spanstart[MAXHEIGHT]; int spanstop[MAXHEIGHT]; // // texture mapping // lighttable_t** planezlight; fixed_t planeheight; fixed_t yslope[MAXHEIGHT]; fixed_t distscale[MAXWIDTH]; fixed_t basexscale; fixed_t baseyscale; fixed_t cachedheight[MAXHEIGHT]; fixed_t cacheddistance[MAXHEIGHT]; fixed_t cachedxstep[MAXHEIGHT]; fixed_t cachedystep[MAXHEIGHT]; // // R_InitPlanes // Only at game startup. // void R_InitPlanes (void) { // Doh! } // // R_MapPlane // // Uses global vars: // planeheight // ds_source // basexscale // baseyscale // viewx // viewy // // BASIC PRIMITIVE // void R_MapPlane ( int y, int x1, int x2 ) { angle_t angle; fixed_t distance; fixed_t length; unsigned index; #ifdef RANGECHECK if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) { I_Error ("R_MapPlane: %i, %i at %i",x1,x2,y); } #endif if (planeheight != cachedheight[y]) { cachedheight[y] = planeheight; distance = cacheddistance[y] = FixedMul (planeheight, yslope[y]); ds_xstep = cachedxstep[y] = FixedMul (distance,basexscale); ds_ystep = cachedystep[y] = FixedMul (distance,baseyscale); } else { distance = cacheddistance[y]; ds_xstep = cachedxstep[y]; ds_ystep = cachedystep[y]; } length = FixedMul (distance,distscale[x1]); angle = (viewangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; ds_xfrac = viewx + FixedMul(finecosine[angle], length); ds_yfrac = -viewy - FixedMul(finesine[angle], length); if (fixedcolormap) ds_colormap = fixedcolormap; else { index = distance >> LIGHTZSHIFT; if (index >= MAXLIGHTZ ) index = MAXLIGHTZ-1; ds_colormap = planezlight[index]; } ds_y = y; ds_x1 = x1; ds_x2 = x2; // high or low detail spanfunc (); } // // R_ClearPlanes // At begining of frame. // void R_ClearPlanes (void) { int i; angle_t angle; // opening / clipping determination for (i=0 ; i>ANGLETOFINESHIFT; // scale will be unit scale at SCREENWIDTH/2 distance basexscale = FixedDiv (finecosine[angle],centerxfrac); baseyscale = -FixedDiv (finesine[angle],centerxfrac); } // // R_FindPlane // visplane_t* R_FindPlane ( fixed_t height, int picnum, int lightlevel ) { visplane_t* check; if (picnum == skyflatnum) { height = 0; // all skys map together lightlevel = 0; } for (check=visplanes; checkheight && picnum == check->picnum && lightlevel == check->lightlevel) { break; } } if (check < lastvisplane) return check; if (lastvisplane - visplanes == MAXVISPLANES) I_Error ("R_FindPlane: no more visplanes"); lastvisplane++; check->height = height; check->picnum = picnum; check->lightlevel = lightlevel; check->minx = SCREENWIDTH; check->maxx = -1; memset (check->top,0xff,sizeof(check->top)); return check; } // // R_CheckPlane // visplane_t* R_CheckPlane ( visplane_t* pl, int start, int stop ) { int intrl; int intrh; int unionl; int unionh; int x; if (start < pl->minx) { intrl = pl->minx; unionl = start; } else { unionl = pl->minx; intrl = start; } if (stop > pl->maxx) { intrh = pl->maxx; unionh = stop; } else { unionh = pl->maxx; intrh = stop; } for (x=intrl ; x<= intrh ; x++) if (pl->top[x] != 0xffff) break; if (x > intrh) { pl->minx = unionl; pl->maxx = unionh; // use the same one return pl; } // make a new visplane lastvisplane->height = pl->height; lastvisplane->picnum = pl->picnum; lastvisplane->lightlevel = pl->lightlevel; pl = lastvisplane++; pl->minx = start; pl->maxx = stop; memset (pl->top,0xff,sizeof(pl->top)); return pl; } // // R_MakeSpans // void R_MakeSpans ( int x, int t1, int b1, int t2, int b2 ) { while (t1 < t2 && t1<=b1) { R_MapPlane (t1,spanstart[t1],x-1); t1++; } while (b1 > b2 && b1>=t1) { R_MapPlane (b1,spanstart[b1],x-1); b1--; } while (t2 < t1 && t2<=b2) { spanstart[t2] = x; t2++; } while (b2 > b1 && b2>=t2) { spanstart[b2] = x; b2--; } } // // R_DrawPlanes // At the end of each frame. // void R_DrawPlanes (void) { visplane_t* pl; int light; int x; int stop; int angle; int lumpnum; #ifdef RANGECHECK if (ds_p - drawsegs > MAXDRAWSEGS) I_Error ("R_DrawPlanes: drawsegs overflow (%" PRIiPTR ")", ds_p - drawsegs); if (lastvisplane - visplanes > MAXVISPLANES) I_Error ("R_DrawPlanes: visplane overflow (%" PRIiPTR ")", lastvisplane - visplanes); if (lastopening - openings > MAXOPENINGS) I_Error ("R_DrawPlanes: opening overflow (%" PRIiPTR ")", lastopening - openings); #endif for (pl = visplanes ; pl < lastvisplane ; pl++) { if (pl->minx > pl->maxx) continue; // sky flat if (pl->picnum == skyflatnum) { dc_iscale = pspriteiscale>>detailshift; // Sky is allways drawn full bright, // i.e. colormaps[0] is used. // Because of this hack, sky is not affected // by INVUL inverse mapping. dc_colormap = colormaps; dc_texturemid = skytexturemid; for (x=pl->minx ; x <= pl->maxx ; x++) { dc_yl = pl->top[x]; dc_yh = pl->bottom[x]; if (dc_yl <= dc_yh) { angle = (viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT; dc_x = x; dc_source = R_GetColumn(skytexture, angle); colfunc (); } } continue; } // regular flat lumpnum = firstflat + flattranslation[pl->picnum]; ds_source = W_CacheLumpNum(lumpnum, PU_STATIC); planeheight = abs(pl->height-viewz); light = (pl->lightlevel >> LIGHTSEGSHIFT)+extralight; if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; if (light < 0) light = 0; planezlight = zlight[light]; pl->top[pl->maxx+1] = 0xffff; pl->top[pl->minx-1] = 0xffff; stop = pl->maxx + 1; for (x=pl->minx ; x<= stop ; x++) { R_MakeSpans(x,pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } W_ReleaseLumpNum(lumpnum); } } crispy-doom-crispy-doom-5.6.4/src/strife/r_plane.h000066400000000000000000000026131360717211000221120ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh, visplane stuff (floor, ceilings). // #ifndef __R_PLANE__ #define __R_PLANE__ #include "r_data.h" // Visplane related. extern short* lastopening; typedef void (*planefunction_t) (int top, int bottom); extern planefunction_t floorfunc; extern planefunction_t ceilingfunc_t; extern short floorclip[MAXWIDTH]; extern short ceilingclip[MAXWIDTH]; extern fixed_t yslope[MAXHEIGHT]; extern fixed_t distscale[MAXWIDTH]; void R_InitPlanes (void); void R_ClearPlanes (void); void R_MapPlane ( int y, int x1, int x2 ); void R_MakeSpans ( int x, int t1, int b1, int t2, int b2 ); void R_DrawPlanes (void); visplane_t* R_FindPlane ( fixed_t height, int picnum, int lightlevel ); visplane_t* R_CheckPlane ( visplane_t* pl, int start, int stop ); #endif crispy-doom-crispy-doom-5.6.4/src/strife/r_segs.c000066400000000000000000000416711360717211000217560ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // All the clipping: columns, horizontal spans, sky columns. // #include #include #include "i_system.h" #include "doomdef.h" #include "doomstat.h" #include "r_local.h" #include "r_sky.h" // OPTIMIZE: closed two sided lines as single sided // True if any of the segs textures might be visible. boolean segtextured; // False if the back side is the same plane. boolean markfloor; boolean markceiling; boolean maskedtexture; int toptexture; int bottomtexture; int midtexture; angle_t rw_normalangle; // angle to line origin int rw_angle1; // // regular wall // int rw_x; int rw_stopx; angle_t rw_centerangle; fixed_t rw_offset; fixed_t rw_distance; fixed_t rw_scale; fixed_t rw_scalestep; fixed_t rw_midtexturemid; fixed_t rw_toptexturemid; fixed_t rw_bottomtexturemid; int worldtop; int worldbottom; int worldhigh; int worldlow; fixed_t pixhigh; fixed_t pixlow; fixed_t pixhighstep; fixed_t pixlowstep; fixed_t topfrac; fixed_t topstep; fixed_t bottomfrac; fixed_t bottomstep; lighttable_t** walllights; short* maskedtexturecol; // // R_RenderMaskedSegRange // void R_RenderMaskedSegRange ( drawseg_t* ds, int x1, int x2 ) { unsigned index; column_t* col; int lightnum; int texnum; // Calculate light table. // Use different light tables // for horizontal / vertical / diagonal. Diagonal? // OPTIMIZE: get rid of LIGHTSEGSHIFT globally curline = ds->curline; frontsector = curline->frontsector; backsector = curline->backsector; texnum = texturetranslation[curline->sidedef->midtexture]; lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight; if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS-1]; else walllights = scalelight[lightnum]; maskedtexturecol = ds->maskedtexturecol; rw_scalestep = ds->scalestep; spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; mfloorclip = ds->sprbottomclip; mceilingclip = ds->sprtopclip; // find positioning if (curline->linedef->flags & ML_DONTPEGBOTTOM) { dc_texturemid = frontsector->floorheight > backsector->floorheight ? frontsector->floorheight : backsector->floorheight; dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; } else { dc_texturemid =frontsector->ceilingheightceilingheight ? frontsector->ceilingheight : backsector->ceilingheight; dc_texturemid = dc_texturemid - viewz; } dc_texturemid += curline->sidedef->rowoffset; if (fixedcolormap) dc_colormap = fixedcolormap; // villsa [STRIFE] render as transparent (25% or 75%?) if(curline->linedef->flags & ML_TRANSPARENT1) colfunc = fuzzcolfunc; // villsa [STRIFE] render as transparent (25% or 75%?) if(curline->linedef->flags & ML_TRANSPARENT2) colfunc = R_DrawMVisTLColumn; // draw the columns for (dc_x = x1 ; dc_x <= x2 ; dc_x++) { // calculate lighting if (maskedtexturecol[dc_x] != SHRT_MAX) { if (!fixedcolormap) { index = spryscale>>(LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE ) index = MAXLIGHTSCALE-1; dc_colormap = walllights[index]; } sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); dc_iscale = 0xffffffffu / (unsigned)spryscale; // draw the texture col = (column_t *)( (byte *)R_GetColumn(texnum,maskedtexturecol[dc_x]) -3); // villsa [STRIFE] added 0 argument R_DrawMaskedColumn (col, 0); maskedtexturecol[dc_x] = SHRT_MAX; } spryscale += rw_scalestep; } colfunc = basecolfunc; // villsa [STRIFE] reset draw routines } // // 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. // #define HEIGHTBITS 12 #define HEIGHTUNIT (1<>HEIGHTBITS; // no space above wall? if (yl < ceilingclip[rw_x]+1) yl = ceilingclip[rw_x]+1; if (markceiling) { top = ceilingclip[rw_x]+1; bottom = yl-1; if (bottom >= floorclip[rw_x]) bottom = floorclip[rw_x]-1; if (top <= bottom) { ceilingplane->top[rw_x] = top; ceilingplane->bottom[rw_x] = bottom; } } yh = bottomfrac>>HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x]-1; if (markfloor) { top = yh+1; bottom = floorclip[rw_x]-1; if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x]+1; if (top <= bottom) { floorplane->top[rw_x] = top; floorplane->bottom[rw_x] = bottom; } } // texturecolumn and lighting are independent of wall tiers if (segtextured) { // calculate texture offset angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT; texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale>>(LIGHTSCALESHIFT + crispy->hires); if (index >= MAXLIGHTSCALE ) index = MAXLIGHTSCALE-1; dc_colormap = walllights[index]; dc_x = rw_x; dc_iscale = 0xffffffffu / (unsigned)rw_scale; } else { // purely to shut up the compiler texturecolumn = 0; } // draw the wall tiers if (midtexture) { // single sided line dc_yl = yl; dc_yh = yh; dc_texturemid = rw_midtexturemid; dc_source = R_GetColumn(midtexture,texturecolumn); colfunc (); ceilingclip[rw_x] = viewheight; floorclip[rw_x] = -1; } else { // two sided line if (toptexture) { // top wall mid = pixhigh>>HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x]-1; if (mid >= yl) { dc_yl = yl; dc_yh = mid; dc_texturemid = rw_toptexturemid; dc_source = R_GetColumn(toptexture,texturecolumn); colfunc (); ceilingclip[rw_x] = mid; } else ceilingclip[rw_x] = yl-1; } else { // no top wall if (markceiling) ceilingclip[rw_x] = yl-1; } if (bottomtexture) { // bottom wall mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS; pixlow += pixlowstep; // no space above wall? if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x]+1; if (mid <= yh) { dc_yl = mid; dc_yh = yh; dc_texturemid = rw_bottomtexturemid; dc_source = R_GetColumn(bottomtexture, texturecolumn); colfunc (); floorclip[rw_x] = mid; } else floorclip[rw_x] = yh+1; } else { // no bottom wall if (markfloor) floorclip[rw_x] = yh+1; } if (maskedtexture) { // save texturecol // for backdrawing of masked mid texture maskedtexturecol[rw_x] = texturecolumn; } } rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } } // // R_StoreWallRange // A wall segment will be drawn // between start and stop pixels (inclusive). // void R_StoreWallRange ( int start, int stop ) { fixed_t hyp; fixed_t sineval; angle_t distangle, offsetangle; fixed_t vtop; int lightnum; // don't overflow and crash if (ds_p == &drawsegs[MAXDRAWSEGS]) return; #ifdef RANGECHECK if (start >=viewwidth || start > stop) I_Error ("Bad R_RenderWallRange: %i to %i", start , stop); #endif sidedef = curline->sidedef; linedef = curline->linedef; // mark the segment as visible for auto map linedef->flags |= ML_MAPPED; // calculate rw_distance for scale calculation rw_normalangle = curline->angle + ANG90; offsetangle = abs(rw_normalangle-rw_angle1); if (offsetangle > ANG90) offsetangle = ANG90; distangle = ANG90 - offsetangle; hyp = R_PointToDist (curline->v1->x, curline->v1->y); sineval = finesine[distangle>>ANGLETOFINESHIFT]; rw_distance = FixedMul (hyp, sineval); ds_p->x1 = rw_x = start; ds_p->x2 = stop; ds_p->curline = curline; rw_stopx = stop+1; // calculate scale at both ends and step ds_p->scale1 = rw_scale = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[start]); if (stop > start ) { ds_p->scale2 = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[stop]); ds_p->scalestep = rw_scalestep = (ds_p->scale2 - rw_scale) / (stop-start); } else { // UNUSED: try to fix the stretched line bug #if 0 if (rw_distance < FRACUNIT/2) { fixed_t trx,try; fixed_t gxt,gyt; trx = curline->v1->x - viewx; try = curline->v1->y - viewy; gxt = FixedMul(trx,viewcos); gyt = -FixedMul(try,viewsin); ds_p->scale1 = FixedDiv(projection, gxt-gyt)<scale2 = ds_p->scale1; } // calculate texture boundaries // and decide if floor / ceiling marks are needed worldtop = frontsector->ceilingheight - viewz; worldbottom = frontsector->floorheight - viewz; midtexture = toptexture = bottomtexture = maskedtexture = 0; ds_p->maskedtexturecol = NULL; if (!backsector) { // single sided line midtexture = texturetranslation[sidedef->midtexture]; // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; if (linedef->flags & ML_DONTPEGBOTTOM) { vtop = frontsector->floorheight + textureheight[sidedef->midtexture]; // bottom of texture at bottom rw_midtexturemid = vtop - viewz; } else { // top of texture at top rw_midtexturemid = worldtop; } rw_midtexturemid += sidedef->rowoffset; ds_p->silhouette = SIL_BOTH; ds_p->sprtopclip = screenheightarray; ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->tsilheight = INT_MIN; } else { // two sided line ds_p->sprtopclip = ds_p->sprbottomclip = NULL; ds_p->silhouette = 0; if (frontsector->floorheight > backsector->floorheight) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = frontsector->floorheight; } else if (backsector->floorheight > viewz) { ds_p->silhouette = SIL_BOTTOM; ds_p->bsilheight = INT_MAX; // ds_p->sprbottomclip = negonearray; } if (frontsector->ceilingheight < backsector->ceilingheight) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = frontsector->ceilingheight; } else if (backsector->ceilingheight < viewz) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT_MIN; // ds_p->sprtopclip = screenheightarray; } if (backsector->ceilingheight <= frontsector->floorheight) { ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->silhouette |= SIL_BOTTOM; } if (backsector->floorheight >= frontsector->ceilingheight) { ds_p->sprtopclip = screenheightarray; ds_p->tsilheight = INT_MIN; ds_p->silhouette |= SIL_TOP; } worldhigh = backsector->ceilingheight - viewz; worldlow = backsector->floorheight - viewz; // hack to allow height changes in outdoor areas if (frontsector->ceilingpic == skyflatnum && backsector->ceilingpic == skyflatnum) { worldtop = worldhigh; } if (worldlow != worldbottom || backsector->floorpic != frontsector->floorpic || backsector->lightlevel != frontsector->lightlevel) { markfloor = true; } else { // same plane on both sides markfloor = false; } if (worldhigh != worldtop || backsector->ceilingpic != frontsector->ceilingpic || backsector->lightlevel != frontsector->lightlevel) { markceiling = true; } else { // same plane on both sides markceiling = false; } if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) { // closed door markceiling = markfloor = true; } if (worldhigh < worldtop) { // top texture toptexture = texturetranslation[sidedef->toptexture]; if (linedef->flags & ML_DONTPEGTOP) { // top of texture at top rw_toptexturemid = worldtop; } else { vtop = backsector->ceilingheight + textureheight[sidedef->toptexture]; // bottom of texture rw_toptexturemid = vtop - viewz; } } if (worldlow > worldbottom) { // bottom texture bottomtexture = texturetranslation[sidedef->bottomtexture]; if (linedef->flags & ML_DONTPEGBOTTOM ) { // bottom of texture at bottom // top of texture at top rw_bottomtexturemid = worldtop; } else // top of texture at top rw_bottomtexturemid = worldlow; } rw_toptexturemid += sidedef->rowoffset; rw_bottomtexturemid += sidedef->rowoffset; // allocate space for masked texture tables if (sidedef->midtexture) { // masked midtexture maskedtexture = true; ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; lastopening += rw_stopx - rw_x; } } // calculate rw_offset (only needed for textured lines) segtextured = midtexture | toptexture | bottomtexture | maskedtexture; if (segtextured) { offsetangle = rw_normalangle-rw_angle1; if (offsetangle > ANG180) offsetangle = 0 - offsetangle; if (offsetangle > ANG90) offsetangle = ANG90; sineval = finesine[offsetangle >>ANGLETOFINESHIFT]; rw_offset = FixedMul (hyp, sineval); if (rw_normalangle-rw_angle1 < ANG180) rw_offset = -rw_offset; rw_offset += sidedef->textureoffset + curline->offset; rw_centerangle = ANG90 + viewangle - rw_normalangle; // calculate light table // use different light tables // for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally if (!fixedcolormap) { lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight; if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS-1]; else walllights = scalelight[lightnum]; } } // if a floor / ceiling plane is on the wrong side // of the view plane, it is definitely invisible // and doesn't need to be marked. if (frontsector->floorheight >= viewz) { // above view plane markfloor = false; } if (frontsector->ceilingheight <= viewz && frontsector->ceilingpic != skyflatnum) { // below view plane markceiling = false; } // calculate incremental stepping values for texture edges worldtop >>= 4; worldbottom >>= 4; topstep = -FixedMul (rw_scalestep, worldtop); topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale); bottomstep = -FixedMul (rw_scalestep,worldbottom); bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale); if (backsector) { worldhigh >>= 4; worldlow >>= 4; if (worldhigh < worldtop) { pixhigh = (centeryfrac>>4) - FixedMul (worldhigh, rw_scale); pixhighstep = -FixedMul (rw_scalestep,worldhigh); } if (worldlow > worldbottom) { pixlow = (centeryfrac>>4) - FixedMul (worldlow, rw_scale); pixlowstep = -FixedMul (rw_scalestep,worldlow); } } // render it if (markceiling) ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1); if (markfloor) floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1); R_RenderSegLoop (); // save sprite clipping info if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) { memcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start)); ds_p->sprtopclip = lastopening - start; lastopening += rw_stopx - start; } if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && !ds_p->sprbottomclip) { memcpy (lastopening, floorclip+start, 2*(rw_stopx-start)); ds_p->sprbottomclip = lastopening - start; lastopening += rw_stopx - start; } if (maskedtexture && !(ds_p->silhouette&SIL_TOP)) { ds_p->silhouette |= SIL_TOP; ds_p->tsilheight = INT_MIN; } if (maskedtexture && !(ds_p->silhouette&SIL_BOTTOM)) { ds_p->silhouette |= SIL_BOTTOM; ds_p->bsilheight = INT_MAX; } ds_p++; } crispy-doom-crispy-doom-5.6.4/src/strife/r_segs.h000066400000000000000000000014141360717211000217520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh module, drawing LineSegs from BSP. // #ifndef __R_SEGS__ #define __R_SEGS__ void R_RenderMaskedSegRange ( drawseg_t* ds, int x1, int x2 ); #endif crispy-doom-crispy-doom-5.6.4/src/strife/r_sky.c000066400000000000000000000024511360717211000216140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Sky rendering. The DOOM sky is a texture map like any // wall, wrapping around. A 1024 columns equal 360 degrees. // The default sky map is 256 columns and repeats 4 times // on a 320 screen? // // // Needed for FRACUNIT. #include "m_fixed.h" // Needed for Flat retrieval. #include "r_data.h" #include "r_sky.h" // // sky mapping // int skyflatnum; int skytexture; int skytexturemid; // // R_InitSkyMap // Called whenever the view size changes. // void R_InitSkyMap (void) { // haleyjd 10/03/10: [STRIFE] Sky is set here, not in G_DoLoadLevel. // Also skytexturemid changed from 100 to 199. skyflatnum = R_FlatNumForName ( SKYFLATNAME ); skytexturemid = 199*FRACUNIT; } crispy-doom-crispy-doom-5.6.4/src/strife/r_sky.h000066400000000000000000000016701360717211000216230ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Sky rendering. // #ifndef __R_SKY__ #define __R_SKY__ // SKY, store the number for name. #define SKYFLATNAME "F_SKY001" // villsa [STRIFE] // The sky map is 256*128*4 maps. #define ANGLETOSKYSHIFT 22 extern int skytexture; extern int skytexturemid; // Called whenever the view size changes. void R_InitSkyMap (void); #endif crispy-doom-crispy-doom-5.6.4/src/strife/r_state.h000066400000000000000000000044511360717211000221350ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh/render internal state variables (global). // #ifndef __R_STATE__ #define __R_STATE__ // Need data structure definitions. #include "d_player.h" #include "r_data.h" // // Refresh internal data structures, // for rendering. // // needed for texture pegging extern fixed_t* textureheight; // needed for pre rendering (fracs) extern fixed_t* spritewidth; extern fixed_t* spriteoffset; extern fixed_t* spritetopoffset; extern lighttable_t* colormaps; extern int viewwidth; extern int scaledviewwidth; extern int viewheight; extern int firstflat; // for global animation extern int* flattranslation; extern int* texturetranslation; // Sprite.... extern int firstspritelump; extern int lastspritelump; extern int numspritelumps; // // Lookup tables for map data. // extern int numsprites; extern spritedef_t* sprites; extern int numvertexes; extern vertex_t* vertexes; extern int numsegs; extern seg_t* segs; extern int numsectors; extern sector_t* sectors; extern int numsubsectors; extern subsector_t* subsectors; extern int numnodes; extern node_t* nodes; extern int numlines; extern line_t* lines; extern int numsides; extern side_t* sides; // // POV data. // extern fixed_t viewx; extern fixed_t viewy; extern fixed_t viewz; extern angle_t viewangle; extern player_t* viewplayer; // ? extern angle_t clipangle; extern int viewangletox[FINEANGLES/2]; extern angle_t xtoviewangle[MAXWIDTH+1]; //extern fixed_t finetangent[FINEANGLES/2]; extern fixed_t rw_distance; extern angle_t rw_normalangle; // angle to line origin extern int rw_angle1; // Segs count? extern int sscount; extern visplane_t* floorplane; extern visplane_t* ceilingplane; #endif crispy-doom-crispy-doom-5.6.4/src/strife/r_things.c000066400000000000000000000565111360717211000223100ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh of things, i.e. objects represented by sprites. // #include #include #include "deh_main.h" #include "doomdef.h" #include "i_swap.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "r_local.h" #include "doomstat.h" // haleyjd #include "p_local.h" #define MINZ (FRACUNIT*4) #define BASEYCENTER 100 //void R_DrawColumn (void); //void R_DrawFuzzColumn (void); typedef struct { int x1; int x2; int column; int topclip; int bottomclip; } maskdraw_t; // // Sprite rotation 0 is facing the viewer, // rotation 1 is one angle turn CLOCKWISE around the axis. // This is not the same as the angle, // which increases counter clockwise (protractor). // There was a lot of stuff grabbed wrong, so I changed it... // fixed_t pspritescale; fixed_t pspriteiscale; lighttable_t** spritelights; // constant arrays // used for psprite clipping and initializing clipping short negonearray[MAXWIDTH]; short screenheightarray[MAXWIDTH]; // // INITIALIZATION FUNCTIONS // // variables used to look up // and range check thing_t sprites patches spritedef_t* sprites; int numsprites; spriteframe_t sprtemp[29]; int maxframe; const char *spritename; // // R_InstallSpriteLump // Local function for R_InitSprites. // void R_InstallSpriteLump ( int lump, unsigned frame, unsigned rotation, boolean flipped ) { int r; if (frame >= 29 || rotation > 8) I_Error("R_InstallSpriteLump: " "Bad frame characters in lump %i", lump); if ((int)frame > maxframe) maxframe = frame; if (rotation == 0) { // the lump should be used for all rotations if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has " "multip rot=0 lump", spritename, 'A'+frame); if (sprtemp[frame].rotate == true) I_Error ("R_InitSprites: Sprite %s frame %c has rotations " "and a rot=0 lump", spritename, 'A'+frame); sprtemp[frame].rotate = false; for (r=0 ; r<8 ; r++) { sprtemp[frame].lump[r] = lump - firstspritelump; sprtemp[frame].flip[r] = (byte)flipped; } return; } // the lump is only used for one rotation if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has rotations " "and a rot=0 lump", spritename, 'A'+frame); sprtemp[frame].rotate = true; // make 0 based rotation--; if (sprtemp[frame].lump[rotation] != -1) I_Error ("R_InitSprites: Sprite %s : %c : %c " "has two lumps mapped to it", spritename, 'A'+frame, '1'+rotation); sprtemp[frame].lump[rotation] = lump - firstspritelump; sprtemp[frame].flip[rotation] = (byte)flipped; } // // R_InitSpriteDefs // Pass a null terminated list of sprite names // (4 chars exactly) to be used. // Builds the sprite rotation matrixes to account // for horizontally flipped sprites. // Will report an error if the lumps are inconsistant. // Only called at startup. // // Sprite lump names are 4 characters for the actor, // a letter for the frame, and a number for the rotation. // A sprite that is flippable will have an additional // letter/number appended. // The rotation character can be 0 to signify no rotations. // void R_InitSpriteDefs (const char** namelist) { const char **check; int i; int l; int frame; int rotation; int start; int end; int patched; // count the number of sprite names check = namelist; while (*check != NULL) check++; numsprites = check-namelist; if (!numsprites) return; sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL); start = firstspritelump-1; end = lastspritelump+1; // scan all the lump names for each of the names, // noting the highest frame letter. // Just compare 4 characters as ints for (i=0 ; iname, spritename, 4)) { frame = lumpinfo[l]->name[4] - 'A'; rotation = lumpinfo[l]->name[5] - '0'; if (modifiedgame) patched = W_GetNumForName (lumpinfo[l]->name); else patched = l; R_InstallSpriteLump (patched, frame, rotation, false); if (lumpinfo[l]->name[6]) { frame = lumpinfo[l]->name[6] - 'A'; rotation = lumpinfo[l]->name[7] - '0'; R_InstallSpriteLump (l, frame, rotation, true); } } } // check the frames that were found for completeness if (maxframe == -1) { sprites[i].numframes = 0; continue; } maxframe++; for (frame = 0 ; frame < maxframe ; frame++) { switch ((int)sprtemp[frame].rotate) { case -1: // no rotations were found for that frame at all I_Error ("R_InitSprites: No patches found " "for %s frame %c", spritename, frame+'A'); break; case 0: // only the first rotation is needed break; case 1: // must have all 8 frames for (rotation=0 ; rotation<8 ; rotation++) if (sprtemp[frame].lump[rotation] == -1) I_Error ("R_InitSprites: Sprite %s frame %c " "is missing rotations", spritename, frame+'A'); break; } } // allocate space for the frames present and copy sprtemp to it sprites[i].numframes = maxframe; sprites[i].spriteframes = Z_Malloc (maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); memcpy (sprites[i].spriteframes, sprtemp, maxframe*sizeof(spriteframe_t)); } } // // GAME FUNCTIONS // vissprite_t vissprites[MAXVISSPRITES]; vissprite_t* vissprite_p; int newvissprite; int sprbotscreen; // villsa [STRIFE] // // R_InitSprites // Called at program start. // void R_InitSprites (const char** namelist) { int i; for (i=0 ; itopdelta != 0xff ; ) { // calculate unclipped screen coordinates // for post topscreen = sprtopscreen + spryscale*column->topdelta; bottomscreen = topscreen + spryscale*column->length; dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS; dc_yh = (bottomscreen-1)>>FRACBITS; if (dc_yh >= mfloorclip[dc_x]) dc_yh = mfloorclip[dc_x]-1; if (dc_yl <= mceilingclip[dc_x]) dc_yl = mceilingclip[dc_x]+1; // villsa [STRIFE] checks for clipping if(baseclip) { if(dc_yh > baseclip) dc_yh = baseclip; } if (dc_yl <= dc_yh) { dc_source = (byte *)column + 3; dc_texturemid = basetexturemid - (column->topdelta<topdelta; // Drawn by either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. colfunc (); } column = (column_t *)( (byte *)column + column->length + 4); } dc_texturemid = basetexturemid; } // // R_DrawVisSprite // mfloorclip and mceilingclip should also be set. // void R_DrawVisSprite ( vissprite_t* vis, int x1, int x2 ) { column_t* column; int texturecolumn; fixed_t frac; patch_t* patch; int clip; // villsa [STRIFE] int translation; // villsa [STRIFE] patch = W_CacheLumpNum (vis->patch+firstspritelump, PU_CACHE); dc_colormap = vis->colormap; // villsa [STRIFE] // haleyjd 09/06/10: updated MF_TRANSLATION for Strife translation = vis->mobjflags & MF_TRANSLATION; // villsa [STRIFE] unused /*if (!dc_colormap) { // NULL colormap = shadow draw colfunc = fuzzcolfunc; }*/ // villsa [STRIFE] Handle the two types of translucency if(vis->mobjflags & MF_SHADOW) { if(!translation) { if(vis->mobjflags & MF_MVIS) colfunc = R_DrawMVisTLColumn; else colfunc = fuzzcolfunc; } else { colfunc = R_DrawTRTLColumn; dc_translation = translationtables - 256 + (translation >> (MF_TRANSSHIFT - 8)); } } else if(vis->mobjflags & MF_MVIS) { // haleyjd 20141215: [STRIFE] Objects which are *only* MF_MVIS (players // using double Shadow Armors, in particular) are totally invisible. // Upstreamed after discovered in SVE. Note this causes a // vanilla-accurate glitch with Shadow Acolytes - if they die while // MF_MVIS is set, A_Fall fails to remove it and their corpse will // completely disappear (that's also fixed in SVE, but not here). return; } else if(translation) // villsa [STRIFE] new translation tables { colfunc = transcolfunc; dc_translation = translationtables - 256 + (translation >> (MF_TRANSSHIFT - 8)); } dc_iscale = abs(vis->xiscale)>>detailshift; dc_texturemid = vis->texturemid; frac = vis->startfrac; spryscale = vis->scale; sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale); // villsa [STRIFE] clip sprite's feet if needed if(vis->mobjflags & MF_FEETCLIPPED) { sprbotscreen = sprtopscreen + FixedMul(spryscale, SHORT(patch->height) << FRACBITS); clip = (sprbotscreen - FixedMul(10*FRACUNIT, spryscale)) >> FRACBITS; } else clip = 0; for (dc_x=vis->x1 ; dc_x<=vis->x2 ; dc_x++, frac += vis->xiscale) { texturecolumn = frac>>FRACBITS; #ifdef RANGECHECK if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) I_Error ("R_DrawSpriteRange: bad texturecolumn"); #endif column = (column_t *) ((byte *)patch + LONG(patch->columnofs[texturecolumn])); R_DrawMaskedColumn (column, clip); // villsa [STRIFE] clip argument } colfunc = basecolfunc; } // // R_ProjectSprite // Generates a vissprite for a thing // if it might be visible. // void R_ProjectSprite (mobj_t* thing) { fixed_t tr_x; fixed_t tr_y; fixed_t gxt; fixed_t gyt; fixed_t tx; fixed_t tz; fixed_t xscale; int x1; int x2; spritedef_t* sprdef; spriteframe_t* sprframe; int lump; unsigned rot; boolean flip; int index; vissprite_t* vis; angle_t ang; fixed_t iscale; // transform the origin point tr_x = thing->x - viewx; tr_y = thing->y - viewy; gxt = FixedMul(tr_x,viewcos); gyt = -FixedMul(tr_y,viewsin); tz = gxt-gyt; // thing is behind view plane? if (tz < MINZ) return; xscale = FixedDiv(projection, tz); gxt = -FixedMul(tr_x,viewsin); gyt = FixedMul(tr_y,viewcos); tx = -(gyt+gxt); // too far off the side? if (abs(tx)>(tz<<2)) return; // decide which patch to use for sprite relative to player #ifdef RANGECHECK if ((unsigned int) thing->sprite >= (unsigned int) numsprites) I_Error ("R_ProjectSprite: invalid sprite number %i ", thing->sprite); #endif sprdef = &sprites[thing->sprite]; #ifdef RANGECHECK if ( (thing->frame&FF_FRAMEMASK) >= sprdef->numframes ) I_Error ("R_ProjectSprite: invalid sprite frame %i : %i ", thing->sprite, thing->frame); #endif sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK]; if (sprframe->rotate) { // choose a different rotation based on player view ang = R_PointToAngle (thing->x, thing->y); rot = (ang-thing->angle+(unsigned)(ANG45/2)*9)>>29; lump = sprframe->lump[rot]; flip = (boolean)sprframe->flip[rot]; } else { // use single rotation for all views lump = sprframe->lump[0]; flip = (boolean)sprframe->flip[0]; } // calculate edges of the shape tx -= spriteoffset[lump]; x1 = (centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS; // off the right side? if (x1 > viewwidth) return; tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS) - 1; // off the left side if (x2 < 0) return; // store information in a vissprite vis = R_NewVisSprite (); vis->mobjflags = thing->flags; vis->scale = xscale<gx = thing->x; vis->gy = thing->y; vis->gz = thing->z; // villsa [STRIFE] if(thing->flags & MF_FEETCLIPPED) vis->gz -= (10*FRACUNIT); // villsa [STRIFE] vis->gzt = vis->gz + spritetopoffset[lump]; vis->texturemid = vis->gzt - viewz; vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; iscale = FixedDiv (FRACUNIT, xscale); if (flip) { vis->startfrac = spritewidth[lump]-1; vis->xiscale = -iscale; } else { vis->startfrac = 0; vis->xiscale = iscale; } if (vis->x1 > x1) vis->startfrac += vis->xiscale*(vis->x1-x1); vis->patch = lump; // get light level // villsa [STRIFE] unused /*if (thing->flags & MF_SHADOW) { // shadow draw vis->colormap = NULL; } else */if (fixedcolormap) { // fixed map vis->colormap = fixedcolormap; } else if (thing->frame & FF_FULLBRIGHT) { // full bright vis->colormap = colormaps; } else { // diminished light index = xscale>>(LIGHTSCALESHIFT-detailshift+crispy->hires); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE-1; vis->colormap = spritelights[index]; } } // // R_AddSprites // During BSP traversal, this adds sprites by sector. // void R_AddSprites (sector_t* sec) { mobj_t* thing; int lightnum; // BSP is traversed by subsector. // A sector might have been split into several // subsectors during BSP building. // Thus we check whether its already added. if (sec->validcount == validcount) return; // Well, now it will be done. sec->validcount = validcount; lightnum = (sec->lightlevel >> LIGHTSEGSHIFT)+extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; // Handle all things in sector. for (thing = sec->thinglist ; thing ; thing = thing->snext) R_ProjectSprite (thing); } // // R_DrawPSprite // void R_DrawPSprite (pspdef_t* psp) { fixed_t tx; int x1; int x2; spritedef_t* sprdef; spriteframe_t* sprframe; int lump; boolean flip; vissprite_t* vis; vissprite_t avis; // decide which patch to use #ifdef RANGECHECK if ( (unsigned)psp->state->sprite >= (unsigned int) numsprites) I_Error ("R_ProjectSprite: invalid sprite number %i ", psp->state->sprite); #endif sprdef = &sprites[psp->state->sprite]; #ifdef RANGECHECK if ( (psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) I_Error ("R_ProjectSprite: invalid sprite frame %i : %i ", psp->state->sprite, psp->state->frame); #endif sprframe = &sprdef->spriteframes[ psp->state->frame & FF_FRAMEMASK ]; lump = sprframe->lump[0]; // [STRIFE] haleyjd 20110629: -flip replaces this. //flip = (boolean)sprframe->flip[0]; flip = flipparm; // calculate edges of the shape tx = psp->sx-160*FRACUNIT; tx -= spriteoffset[lump]; x1 = (centerxfrac + FixedMul (tx,pspritescale) ) >>FRACBITS; // off the right side if (x1 > viewwidth) return; tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx, pspritescale) ) >>FRACBITS) - 1; // off the left side if (x2 < 0) return; // store information in a vissprite vis = &avis; vis->mobjflags = 0; vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; vis->scale = pspritescale<xiscale = -pspriteiscale; vis->startfrac = spritewidth[lump]-1; } else { vis->xiscale = pspriteiscale; vis->startfrac = 0; } // villsa [STRIFE] calculate y offset with view pitch vis->texturemid = ((BASEYCENTER<sy-spritetopoffset[lump]) + FixedMul(vis->xiscale, (centery-viewheight/2)<x1 > x1) vis->startfrac += vis->xiscale*(vis->x1-x1); vis->patch = lump; if (viewplayer->powers[pw_invisibility] > 4*32 || (viewplayer->powers[pw_invisibility] & 8)) { // shadow draw vis->colormap = spritelights[MAXLIGHTSCALE-1]; vis->mobjflags |= MF_SHADOW; } else if(viewplayer->powers[pw_invisibility] & 4) { vis->mobjflags |= (MF_SHADOW|MF_MVIS); } // When not MVIS, or if SHADOW, behave normally: if(!(viewplayer->mo->flags & MF_MVIS) || (viewplayer->mo->flags & MF_SHADOW)) { if (fixedcolormap) { // fixed color vis->colormap = fixedcolormap; } else if (psp->state->frame & FF_FULLBRIGHT) { // full bright vis->colormap = colormaps; } else { // local light vis->colormap = spritelights[MAXLIGHTSCALE-1]; } } else { // When MVIS, use invulnerability colormap vis->colormap = colormaps + INVERSECOLORMAP * 256 * sizeof(lighttable_t); } R_DrawVisSprite (vis, vis->x1, vis->x2); } // // R_DrawPlayerSprites // void R_DrawPlayerSprites (void) { int i; int lightnum; pspdef_t* psp; // get light level lightnum = (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) +extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; // clip to screen bounds mfloorclip = screenheightarray; mceilingclip = negonearray; // add all active psprites for (i=0, psp=viewplayer->psprites; istate) R_DrawPSprite (psp); } } // // R_SortVisSprites // vissprite_t vsprsortedhead; void R_SortVisSprites (void) { int i; int count; vissprite_t* ds; vissprite_t* best; vissprite_t unsorted; fixed_t bestscale; count = vissprite_p - vissprites; unsorted.next = unsorted.prev = &unsorted; if (!count) return; for (ds=vissprites ; dsnext = ds+1; ds->prev = ds-1; } vissprites[0].prev = &unsorted; unsorted.next = &vissprites[0]; (vissprite_p-1)->next = &unsorted; unsorted.prev = vissprite_p-1; // pull the vissprites out by scale vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; for (i=0 ; inext) { if (ds->scale < bestscale) { bestscale = ds->scale; best = ds; } } best->next->prev = best->prev; best->prev->next = best->next; best->next = &vsprsortedhead; best->prev = vsprsortedhead.prev; vsprsortedhead.prev->next = best; vsprsortedhead.prev = best; } } // // R_DrawSprite // void R_DrawSprite (vissprite_t* spr) { drawseg_t* ds; short clipbot[MAXWIDTH]; short cliptop[MAXWIDTH]; int x; int r1; int r2; fixed_t scale; fixed_t lowscale; int silhouette; for (x = spr->x1 ; x<=spr->x2 ; x++) clipbot[x] = cliptop[x] = -2; // Scan drawsegs from end to start for obscuring segs. // The first drawseg that has a greater scale // is the clip seg. for (ds=ds_p-1 ; ds >= drawsegs ; ds--) { // determine if the drawseg obscures the sprite if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || (!ds->silhouette && !ds->maskedtexturecol) ) { // does not cover sprite continue; } r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; if (ds->scale1 > ds->scale2) { lowscale = ds->scale2; scale = ds->scale1; } else { lowscale = ds->scale1; scale = ds->scale2; } if (scale < spr->scale || ( lowscale < spr->scale && !R_PointOnSegSide (spr->gx, spr->gy, ds->curline) ) ) { // masked mid texture? if (ds->maskedtexturecol) R_RenderMaskedSegRange (ds, r1, r2); // seg is behind sprite continue; } // clip this piece of the sprite silhouette = ds->silhouette; if (spr->gz >= ds->bsilheight) silhouette &= ~SIL_BOTTOM; if (spr->gzt <= ds->tsilheight) silhouette &= ~SIL_TOP; if (silhouette == 1) { // bottom sil for (x=r1 ; x<=r2 ; x++) if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; } else if (silhouette == 2) { // top sil for (x=r1 ; x<=r2 ; x++) if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } else if (silhouette == 3) { // both for (x=r1 ; x<=r2 ; x++) { if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } } } // all clipping has been performed, so draw the sprite // check for unclipped columns for (x = spr->x1 ; x<=spr->x2 ; x++) { if (clipbot[x] == -2) clipbot[x] = viewheight; if (cliptop[x] == -2) cliptop[x] = -1; } mfloorclip = clipbot; mceilingclip = cliptop; R_DrawVisSprite (spr, spr->x1, spr->x2); } // // R_DrawMasked // void R_DrawMasked (void) { vissprite_t* spr; drawseg_t* ds; R_SortVisSprites (); if (vissprite_p > vissprites) { // draw all vissprites back to front for (spr = vsprsortedhead.next ; spr != &vsprsortedhead ; spr=spr->next) { R_DrawSprite (spr); } } // render any remaining masked mid textures for (ds=ds_p-1 ; ds >= drawsegs ; ds--) if (ds->maskedtexturecol) R_RenderMaskedSegRange (ds, ds->x1, ds->x2); // draw the psprites on top of everything // but does not draw on side views if (!viewangleoffset) R_DrawPlayerSprites (); } crispy-doom-crispy-doom-5.6.4/src/strife/r_things.h000066400000000000000000000030761360717211000223130ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Rendering of moving objects, sprites. // #ifndef __R_THINGS__ #define __R_THINGS__ #define MAXVISSPRITES 128*8 extern vissprite_t vissprites[MAXVISSPRITES]; extern vissprite_t* vissprite_p; extern vissprite_t vsprsortedhead; // Constant arrays used for psprite clipping // and initializing clipping. extern short negonearray[MAXWIDTH]; extern short screenheightarray[MAXWIDTH]; // vars for R_DrawMaskedColumn extern short* mfloorclip; extern short* mceilingclip; extern fixed_t spryscale; extern fixed_t sprtopscreen; extern fixed_t pspritescale; extern fixed_t pspriteiscale; // villsa [STIFE] new argument void R_DrawMaskedColumn (column_t *column, int baseclip); void R_SortVisSprites (void); void R_AddSprites (sector_t* sec); void R_AddPSprites (void); void R_DrawSprites (void); void R_InitSprites (const char** namelist); void R_ClearSprites (void); void R_DrawMasked (void); void R_ClipVisSprite ( vissprite_t* vis, int xl, int xh ); #endif crispy-doom-crispy-doom-5.6.4/src/strife/s_sound.c000066400000000000000000000454601360717211000221460ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: none // #include #include #include #include "i_sound.h" #include "i_system.h" #include "deh_str.h" #include "doomstat.h" #include "doomtype.h" #include "sounds.h" #include "s_sound.h" #include "m_misc.h" #include "m_random.h" #include "m_argv.h" #include "p_local.h" #include "w_wad.h" #include "z_zone.h" // when to clip out sounds // Does not fit the large outdoor areas. #define S_CLIPPING_DIST (1200 * FRACUNIT) // Distance tp origin when sounds should be maxed out. // This should relate to movement clipping resolution // (see BLOCKMAP handling). // In the source code release: (160*FRACUNIT). Changed back to the // Vanilla value of 200 (why was this changed?) #define S_CLOSE_DIST (200 * FRACUNIT) // The range over which sound attenuates #define S_ATTENUATOR ((S_CLIPPING_DIST - S_CLOSE_DIST) >> FRACBITS) // Stereo separation #define S_STEREO_SWING (96 * FRACUNIT) #define NORM_PRIORITY 64 #define NORM_SEP 128 typedef struct { // sound information (if null, channel avail.) sfxinfo_t *sfxinfo; // origin of sound mobj_t *origin; // handle of the sound being played int handle; int pitch; } channel_t; // The set of channels available static channel_t *channels; // Maximum volume of a sound effect. // Internal default is max out of 0-15. int sfxVolume = 8; // Maximum volume of music. int musicVolume = 13; // haleyjd 08/29/10: [STRIFE] New global variable // Volume of voice channel. int voiceVolume = 15; // Internal volume level, ranging from 0-127 static int snd_SfxVolume; // haleyjd 09/11/10: [STRIFE] Internal voice volume level static int snd_VoiceVolume; // Whether songs are mus_paused static boolean mus_paused; // Music currently being played static musicinfo_t *mus_playing = NULL; // Number of channels to use int snd_channels = 8; // haleyjd 09/11/10: [STRIFE] Handle of current voice channel. // This has been implemented at a higher level than it was implemented // in strife1.exe, as there it relied on a priority system which was // implicit in the SFX_PlayPatch API of DMX. Here we'll just ignore // the current voice channel when doing normal sound playing. static int i_voicehandle = -1; // haleyjd 09/11/10: [STRIFE] whether to play voices or not int disable_voices = 0; // // Initializes sound stuff, including volume // Sets channels, SFX and music volume, // allocates channel buffer, sets S_sfx lookup. // // haleyjd 09/11/10: [STRIFE] Added voice volume // void S_Init(int sfxVolume, int musicVolume, int voiceVolume) { int i; I_SetOPLDriverVer(opl_doom_1_9); I_PrecacheSounds(S_sfx, NUMSFX); S_SetSfxVolume(sfxVolume); S_SetMusicVolume(musicVolume); S_SetVoiceVolume(voiceVolume); // Allocating the internal channels for mixing // (the maximum numer of sounds rendered // simultaneously) within zone memory. channels = Z_Malloc(snd_channels*sizeof(channel_t), PU_STATIC, 0); // Free all channels for use for (i=0 ; isfxinfo) { // 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; } } // degrade usefulness of sound data c->sfxinfo->usefulness--; c->sfxinfo = NULL; } } // // Per level startup code. // Kills playing sounds at start of level, // determines music if any, changes music. // // haleyjd 08/31/10: [STRIFE] // * Removed DOOM music handling and replaced with Strife code. // void S_Start(void) { int cnum; int mnum; // kill all playing sounds at start of level // (trust me - a good idea) for (cnum=0 ; cnumpriority > channels[cnum].sfxinfo->priority) return -1; S_StopChannel(cnum); break; } } // None available if (cnum == snd_channels) { // Look for lower priority for (cnum=0 ; cnumpriority >= sfxinfo->priority) { // haleyjd 09/11/10: [STRIFE] voice has absolute priority if (isvoice || cnum != i_voicehandle) break; } } if (cnum == snd_channels) { // FUCK! No lower priority. Sorry, Charlie. return -1; } else { // Otherwise, kick out lower priority. S_StopChannel(cnum); } } c = &channels[cnum]; // channel is decided to be cnum. c->sfxinfo = sfxinfo; c->origin = origin; return cnum; } // // Changes volume and stereo-separation variables // from the norm of a sound effect to be played. // If the sound is not audible, returns a 0. // Otherwise, modifies parameters and returns 1. // // [STRIFE] // haleyjd 20110220: changed to eliminate the gamemap == 8 hack that was // left over from Doom 1's original boss levels. Kind of amazing that Rogue // was able to catch the smallest things like that. // static int S_AdjustSoundParams(mobj_t *listener, mobj_t *source, int *vol, int *sep) { fixed_t approx_dist; fixed_t adx; fixed_t ady; angle_t angle; // calculate the distance to sound origin // and clip it if necessary adx = abs(listener->x - source->x); ady = abs(listener->y - source->y); // From _GG1_ p.428. Appox. eucledian distance fast. approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); // [STRIFE] removed gamemap == 8 hack 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 = angle - listener->angle; } else { angle = angle + (0xffffffff - listener->angle); } angle >>= ANGLETOFINESHIFT; // stereo separation *sep = 128 - (FixedMul(S_STEREO_SWING, finesine[angle]) >> FRACBITS); // volume calculation // [STRIFE] Removed gamemap == 8 hack if (approx_dist < S_CLOSE_DIST) { *vol = snd_SfxVolume; } else { // distance effect *vol = (snd_SfxVolume * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } return (*vol > 0); } void S_StartSound(void *origin_p, int sfx_id) { sfxinfo_t *sfx; mobj_t *origin; int rc; int sep; int pitch; int cnum; int volume; origin = (mobj_t *) origin_p; volume = snd_SfxVolume; // check for bogus sound # if (sfx_id < 1 || sfx_id > NUMSFX) { // [STRIFE]: BUG - Note: vanilla had some extremely buggy and dangerous // code here that tried to print the sprite name of the object playing // the bad sound. Because it invokes multiple undefined behaviors and // is of basically no consequence, it has deliberately not been ported. I_Error("Bad sfx #: %d", sfx_id); } sfx = &S_sfx[sfx_id]; // Initialize sound parameters if (sfx->link) { volume += sfx->volume; if (volume < 1) { return; } if (volume > snd_SfxVolume) { volume = snd_SfxVolume; } } // Check to see if it is audible, // and if not, modify the params if (origin && origin != players[consoleplayer].mo) { rc = S_AdjustSoundParams(players[consoleplayer].mo, origin, &volume, &sep); if (origin->x == players[consoleplayer].mo->x && origin->y == players[consoleplayer].mo->y) { sep = NORM_SEP; } if (!rc) { return; } } else { sep = NORM_SEP; } pitch = NORM_PITCH; // kill old sound [STRIFE] - nope! //S_StopSound(origin); // try to find a channel cnum = S_GetChannel(origin, sfx, false); // haleyjd: not a voice. if (cnum < 0) { return; } // increase the usefulness if (sfx->usefulness++ < 0) { sfx->usefulness = 1; } if (sfx->lumpnum < 0) { sfx->lumpnum = I_GetSfxLumpNum(sfx); } channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep, pitch); } // haleyjd 09/11/10: [STRIFE] // None of this was necessary in the vanilla EXE but Choco's low-level code // won't play nice with a temporary sfxinfo because it insists that the // "driver_data" member remain valid from the last time the sound was used, // even if it has already stopped playing. Thanks to this cuteness I get // to maintain a dynamic cache of sfxinfo objects! typedef struct voiceinfo_s { sfxinfo_t sfx; struct voiceinfo_s *next; // next on hash chain } voiceinfo_t; #define NUMVOICECHAINS 257 // // Ripped from Eternity. // static unsigned int S_voiceHash(const char *str) { const char *c = str; unsigned int h = 0; if(!str) I_Error("S_voiceHash: cannot hash NULL string!\n"); // note: this needs to be case insensitive for lump names while(*c) { h = 5 * h + toupper(*c); ++c; } return h; } static voiceinfo_t *voices[NUMVOICECHAINS]; // // S_getVoice // // Gets an entry from the voice table, if it exists. If it does not, one will be // created. // static voiceinfo_t *S_getVoice(const char *name, int lumpnum) { voiceinfo_t *voice; unsigned int hashkey = S_voiceHash(name) % NUMVOICECHAINS; voice = voices[hashkey]; while(voice && strcasecmp(voice->sfx.name, name)) voice = voice->next; if(!voice) { voice = calloc(1, sizeof(voiceinfo_t)); M_StringCopy(voice->sfx.name, name, sizeof(voice->sfx.name)); voice->sfx.priority = INT_MIN; // make highest possible priority voice->sfx.pitch = -1; voice->sfx.volume = -1; voice->sfx.numchannels = -1; voice->sfx.usefulness = -1; voice->sfx.lumpnum = lumpnum; // throw it onto the table. voice->next = voices[hashkey]; voices[hashkey] = voice; } return voice; } // // I_StartVoice // // haleyjd 09/11/10: [STRIFE] New function // Note this was in i_sound.c in Strife itself, but relied on DMX-specific // features to ensure voice channels had absolute priority. Here we must // populate a fake sfxinfo_t and send the sound through some of the normal // routines. But in the end, it still works the same. // void I_StartVoice(const char *lumpname) { int lumpnum; voiceinfo_t *voice; // choco-specific char lumpnamedup[9]; // no voices in deathmatch mode. if(netgame) return; // STRIFE-TODO: checks if snd_SfxDevice == 83 // This is probably turning off voice if using PC speaker... // user has disabled voices? if(disable_voices) return; // have a voice playing already? stop it. if(i_voicehandle >= 0) S_StopChannel(i_voicehandle); // Vanilla STRIFE appears to have stopped any current voice without // starting a new one if NULL was passed in here, though I cannot // find an explicit check for NULL in the assembly. Either way, it // didn't crash, so do a check now: if(lumpname == NULL) return; // Because of constness problems... M_StringCopy(lumpnamedup, lumpname, sizeof(lumpnamedup)); if((lumpnum = W_CheckNumForName(lumpnamedup)) != -1) { // haleyjd: Choco-specific: get a voice structure voice = S_getVoice(lumpnamedup, lumpnum); // get a channel for the voice i_voicehandle = S_GetChannel(NULL, &voice->sfx, true); channels[i_voicehandle].handle = I_StartSound(&voice->sfx, i_voicehandle, snd_VoiceVolume, NORM_SEP, NORM_PITCH); } } // // Stop and resume music, during game PAUSE. // void S_PauseSound(void) { if (mus_playing && !mus_paused) { I_PauseSong(); mus_paused = true; } } void S_ResumeSound(void) { if (mus_playing && mus_paused) { I_ResumeSong(); mus_paused = false; } } // // Updates music & sounds // void S_UpdateSounds(mobj_t *listener) { int audible; int cnum; int volume; int sep; sfxinfo_t* sfx; channel_t* c; I_UpdateSound(); for (cnum=0; cnumsfxinfo; if (c->sfxinfo) { if (I_SoundIsPlaying(c->handle)) { // initialize parameters volume = snd_SfxVolume; sep = NORM_SEP; if (sfx->link) { 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 != c->origin) { audible = S_AdjustSoundParams(listener, c->origin, &volume, &sep); if (!audible) { S_StopChannel(cnum); } else { I_UpdateSoundParams(c->handle, volume, sep); } } } else { // if channel is allocated but sound has stopped, // free it S_StopChannel(cnum); } } } } void S_SetMusicVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("Attempt to set music volume at %d", volume); } I_SetMusicVolume(volume); } void S_SetSfxVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("Attempt to set sfx volume at %d", volume); } snd_SfxVolume = volume; } // // S_SetVoiceVolume // // haleyjd 09/11/10: [STRIFE] // Set the internal voice volume level. // void S_SetVoiceVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("Attempt to set voice volume at %d", volume); } snd_VoiceVolume = 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 = NULL; char namebuf[9]; void *handle; if (musicnum <= mus_None || musicnum >= NUMMUSIC) { I_Error("Bad music number %d", musicnum); } else { music = &S_music[musicnum]; } if (mus_playing == music) { return; } // shutdown old music S_StopMusic(); // get lumpnum if neccessary if (!music->lumpnum) { M_snprintf(namebuf, sizeof(namebuf), "d_%s", DEH_String(music->name)); music->lumpnum = W_GetNumForName(namebuf); } music->data = W_CacheLumpNum(music->lumpnum, PU_STATIC); handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); music->handle = handle; I_PlaySong(handle, looping); mus_playing = music; } boolean S_MusicPlaying(void) { return I_MusicIsPlaying(); } void S_StopMusic(void) { if (mus_playing) { if (mus_paused) { I_ResumeSong(); } I_StopSong(); I_UnRegisterSong(mus_playing->handle); W_ReleaseLumpNum(mus_playing->lumpnum); mus_playing->data = NULL; mus_playing = NULL; } } crispy-doom-crispy-doom-5.6.4/src/strife/s_sound.h000066400000000000000000000040721360717211000221450ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The not so system specific sound interface. // #ifndef __S_SOUND__ #define __S_SOUND__ #include "p_mobj.h" #include "sounds.h" // // Initializes sound stuff, including volume // Sets channels, SFX and music volume, // allocates channel buffer, sets S_sfx lookup. // void S_Init(int sfxVolume, int musicVolume, int voiceVolume); // Shut down sound void S_Shutdown(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); // haleyjd 09/11/10: [STRIFE] Start a voice. void I_StartVoice(const char *lumpname); // Stop sound for thing at void S_StopSound(mobj_t *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); // query if music is playing boolean S_MusicPlaying(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(mobj_t *listener); void S_SetMusicVolume(int volume); void S_SetSfxVolume(int volume); void S_SetVoiceVolume(int volume); // haleyjd 09/11/10: [STRIFE] extern int snd_channels; extern int disable_voices; #endif crispy-doom-crispy-doom-5.6.4/src/strife/sounds.c000066400000000000000000000126101360717211000217760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Created by a sound utility. // Kept as a sample, DOOM2 sounds. // #include #include "doomtype.h" #include "sounds.h" // // Information about all the music // #define MUSIC(name) \ { name, 0, NULL, NULL } // villsa [STRIFE] musicinfo_t S_music[] = { MUSIC(NULL), MUSIC("logo"), MUSIC("action"), MUSIC("tavern"), MUSIC("danger"), MUSIC("fast"), MUSIC("intro"), MUSIC("darker"), MUSIC("strike"), MUSIC("slide"), MUSIC("tribal"), MUSIC("march"), MUSIC("danger"), MUSIC("mood"), MUSIC("castle"), MUSIC("darker"), MUSIC("action"), MUSIC("fight"), MUSIC("spense"), MUSIC("slide"), MUSIC("strike"), MUSIC("dark"), MUSIC("tech"), MUSIC("slide"), MUSIC("drone"), MUSIC("panthr"), MUSIC("sad"), MUSIC("instry"), MUSIC("tech"), MUSIC("action"), MUSIC("instry"), MUSIC("drone"), MUSIC("fight"), MUSIC("happy"), MUSIC("end") }; // // Information about all the sfx // #define SOUND(name, priority) \ { NULL, name, priority, NULL, -1, -1, 0, 0, -1, NULL } #define SOUND_LINK(name, priority, link_id, pitch, volume) \ { NULL, name, priority, &S_sfx[link_id], pitch, volume, 0, 0, -1, NULL } // villsa [STRIFE] sfxinfo_t S_sfx[] = { // S_sfx[0] needs to be a dummy for odd reasons. SOUND("none", 0), SOUND("swish", 64), SOUND("meatht", 64), SOUND("mtalht", 64), SOUND("wpnup", 78), SOUND("rifle", 64), SOUND("mislht", 64), SOUND("barexp", 32), SOUND("flburn", 64), SOUND("flidl", 118), SOUND("agrsee", 98), SOUND("plpain", 96), SOUND("pcrush", 96), SOUND("pespna", 98), SOUND("pespnb", 98), SOUND("pespnc", 98), SOUND("pespnd", 98), SOUND("agrdpn", 98), SOUND("pldeth", 32), SOUND("plxdth", 32), SOUND("slop", 78), SOUND("rebdth", 98), SOUND("agrdth", 98), SOUND("lgfire", 211), SOUND("smfire", 211), SOUND("alarm", 210), SOUND("drlmto", 98), SOUND("drlmtc", 98), SOUND("drsmto", 98), SOUND("drsmtc", 98), SOUND("drlwud", 98), SOUND("drswud", 98), SOUND("drston", 98), SOUND("bdopn", 98), SOUND("bdcls", 98), SOUND("swtchn", 78), SOUND("swbolt", 98), SOUND("swscan", 98), SOUND("yeah", 10), SOUND("mask", 210), SOUND("pstart", 100), SOUND("pstop", 100), SOUND("itemup", 78), SOUND("bglass", 200), SOUND("wriver", 201), SOUND("wfall", 201), SOUND("wdrip", 201), SOUND("wsplsh", 95), SOUND("rebact", 200), SOUND("agrac1", 98), SOUND("agrac2", 98), SOUND("agrac3", 98), SOUND("agrac4", 98), SOUND("ambppl", 218), SOUND("ambbar", 218), SOUND("telept", 32), SOUND("ratact", 99), SOUND("itmbk", 100), SOUND("xbow", 99), SOUND("burnme", 95), SOUND("oof", 96), SOUND("wbrldt", 98), SOUND("psdtha", 109), SOUND("psdthb", 109), SOUND("psdthc", 109), SOUND("rb2pn", 96), SOUND("rb2dth", 32), SOUND("rb2see", 98), SOUND("rb2act", 98), SOUND("firxpl", 70), SOUND("stnmov", 100), SOUND("noway", 78), SOUND("rlaunc", 64), SOUND("rflite", 65), SOUND("radio", 60), SOUND("pulchn", 98), SOUND("swknob", 98), SOUND("keycrd", 98), SOUND("swston", 98), SOUND("sntsee", 98), SOUND("sntdth", 98), SOUND("sntact", 98), SOUND("pgrdat", 64), SOUND("pgrsee", 90), SOUND("pgrdpn", 96), SOUND("pgrdth", 32), SOUND("pgract", 120), SOUND("proton", 64), SOUND("protfl", 64), SOUND("plasma", 64), SOUND("dsrptr", 30), SOUND("reavat", 64), SOUND("revbld", 64), SOUND("revsee", 90), SOUND("reavpn", 96), SOUND("revdth", 32), SOUND("revact", 120), SOUND("spisit", 90), SOUND("spdwlk", 65), SOUND("spidth", 32), SOUND("spdatk", 32), SOUND("chant", 218), SOUND("static", 32), SOUND("chain", 70), SOUND("tend", 100), SOUND("phoot", 32), SOUND("explod", 32), SOUND("sigil", 32), SOUND("sglhit", 32), SOUND("siglup", 32), SOUND("prgpn", 96), SOUND("progac", 120), SOUND("lorpn", 96), SOUND("lorsee", 90), SOUND("difool", 32), SOUND("inqdth", 32), SOUND("inqact", 98), SOUND("inqsee", 90), SOUND("inqjmp", 65), SOUND("amaln1", 99), SOUND("amaln2", 99), SOUND("amaln3", 99), SOUND("amaln4", 99), SOUND("amaln5", 99), SOUND("amaln6", 99), SOUND("mnalse", 64), SOUND("alnsee", 64), SOUND("alnpn", 96), SOUND("alnact", 120), SOUND("alndth", 32), SOUND("mnaldt", 32), SOUND("reactr", 31), SOUND("airlck", 98), SOUND("drchno", 98), SOUND("drchnc", 98), SOUND("valve", 98) }; crispy-doom-crispy-doom-5.6.4/src/strife/sounds.h000066400000000000000000000073011360717211000220040ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Created by the sound utility written by Dave Taylor. // Kept as a sample, DOOM2 sounds. Frozen. // #ifndef __SOUNDS__ #define __SOUNDS__ #include "i_sound.h" // 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. // // villsa [STRIFE] typedef enum { mus_None, mus_logo, mus_action, mus_tavern, mus_danger, mus_fast, mus_intro, mus_darker, mus_strike, mus_slide, mus_tribal, mus_march, mus_danger2, mus_mood, mus_castle, mus_darker2, mus_action2, mus_fight, mus_spense, mus_slide2, mus_strike2, mus_dark, mus_tech, mus_slide3, mus_drone, mus_panthr, mus_sad, mus_instry, mus_tech2, mus_action3, mus_instry2, mus_drone2, mus_fight2, mus_happy, mus_end, NUMMUSIC } musicenum_t; // // Identifiers for all sfx in game. // typedef enum { sfx_None, sfx_swish, sfx_meatht, sfx_mtalht, sfx_wpnup, sfx_rifle, sfx_mislht, sfx_barexp, sfx_flburn, sfx_flidl, sfx_agrsee, sfx_plpain, sfx_pcrush, sfx_pespna, sfx_pespnb, sfx_pespnc, sfx_pespnd, sfx_agrdpn, sfx_pldeth, sfx_plxdth, sfx_slop, sfx_rebdth, sfx_agrdth, sfx_lgfire, sfx_smfire, sfx_alarm, sfx_drlmto, sfx_drlmtc, sfx_drsmto, sfx_drsmtc, sfx_drlwud, sfx_drswud, sfx_drston, sfx_bdopn, sfx_bdcls, sfx_swtchn, sfx_swbolt, sfx_swscan, sfx_yeah, sfx_mask, sfx_pstart, sfx_pstop, sfx_itemup, sfx_bglass, sfx_wriver, sfx_wfall, sfx_wdrip, sfx_wsplsh, sfx_rebact, sfx_agrac1, sfx_agrac2, sfx_agrac3, sfx_agrac4, sfx_ambppl, sfx_ambbar, sfx_telept, sfx_ratact, sfx_itmbk, sfx_xbow, sfx_burnme, sfx_oof, sfx_wbrldt, sfx_psdtha, sfx_psdthb, sfx_psdthc, sfx_rb2pn, sfx_rb2dth, sfx_rb2see, sfx_rb2act, sfx_firxpl, sfx_stnmov, sfx_noway, sfx_rlaunc, sfx_rflite, sfx_radio, sfx_pulchn, sfx_swknob, sfx_keycrd, sfx_swston, sfx_sntsee, sfx_sntdth, sfx_sntact, sfx_pgrdat, sfx_pgrsee, sfx_pgrdpn, sfx_pgrdth, sfx_pgract, sfx_proton, sfx_protfl, sfx_plasma, sfx_dsrptr, sfx_reavat, sfx_revbld, sfx_revsee, sfx_reavpn, sfx_revdth, sfx_revact, sfx_spisit, sfx_spdwlk, sfx_spidth, sfx_spdatk, sfx_chant, sfx_static, sfx_chain, sfx_tend, sfx_phoot, sfx_explod, sfx_sigil, sfx_sglhit, sfx_siglup, sfx_prgpn, sfx_progac, sfx_lorpn, sfx_lorsee, sfx_difool, sfx_inqdth, sfx_inqact, sfx_inqsee, sfx_inqjmp, sfx_amaln1, sfx_amaln2, sfx_amaln3, sfx_amaln4, sfx_amaln5, sfx_amaln6, sfx_mnalse, sfx_alnsee, sfx_alnpn, sfx_alnact, sfx_alndth, sfx_mnaldt, sfx_reactr, sfx_airlck, sfx_drchno, sfx_drchnc, sfx_valve, NUMSFX } sfxenum_t; #endif crispy-doom-crispy-doom-5.6.4/src/strife/st_lib.c000066400000000000000000000133661360717211000217500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The status bar widget code. // #include #include #include "deh_main.h" #include "doomdef.h" #include "z_zone.h" #include "v_video.h" #include "i_swap.h" #include "i_system.h" #include "w_wad.h" #include "st_stuff.h" #include "st_lib.h" #include "r_local.h" // in AM_map.c extern boolean automapactive; // // Hack display negative frags. // Loads and store the stminus lump. // patch_t* sttminus; void STlib_init(void) { // haleyjd 08/28/10: [STRIFE] STTMINUS -> STCFN045 sttminus = (patch_t *) W_CacheLumpName(DEH_String("STCFN045"), PU_STATIC); } // // STlib_initNum // // haleyjd 09/01/10: [STRIFE] // * Rogue removed the "on" member of st_number_t. void STlib_initNum ( st_number_t* n, int x, int y, patch_t** pl, int* num, int width ) { n->x = x; n->y = y; n->width = width; n->num = num; n->p = pl; } // // STlib_drawNum // // A fairly efficient way to draw a number // based on differences from the old number. // Note: worth the trouble? // // haleyjd 09/01/10: [STRIFE] // * Rogue removed the "refresh" parameter and caching code // void STlib_drawNum ( st_number_t* n) { int numdigits = n->width; int num = *n->num; int w = SHORT(n->p[0]->width) + 1; // [STRIFE] +1 int x = n->x; int neg; neg = num < 0; if (neg) { if (numdigits == 2 && num < -9) num = -9; else if (numdigits == 3 && num < -99) num = -99; num = -num; } /* haleyjd 09/01/10: [STRIFE] Widget caching system removed by Rogue // clear the area x = n->x - numdigits*w; if (n->y - ST_Y < 0) I_Error("drawNum: n->y - ST_Y < 0"); V_CopyRect(x, n->y - ST_Y, st_backing_screen, w*numdigits, h, x, n->y); */ // if non-number, do not draw it if (num == 1994) return; x = n->x; // in the special case of 0, you draw 0 if (!num) V_DrawPatch(x - w, n->y, n->p[ 0 ]); // draw the new number while (num && numdigits--) { x -= w; V_DrawPatch(x, n->y, n->p[ num % 10 ]); num /= 10; } // draw a minus sign if necessary if (neg) V_DrawPatch(x - 8, n->y, sttminus); } // // STlib_drawNumPositive // // haleyjd 09/01/10: [STRIFE] New function. // * Mostly the same as STlib_drawNum, except doesn't draw negatives. // void STlib_drawNumPositive ( st_number_t* n) { int numdigits = n->width; int num = *n->num; int w = SHORT(n->p[0]->width) + 1; // [STRIFE] +1 int x = n->x; // Don't draw negative values. if (num < 0) num = 0; // if non-number, do not draw it if (num == 1994) return; x = n->x; // in the special case of 0, you draw 0 if (!num) V_DrawPatch(x - w, n->y, n->p[ 0 ]); // draw the new number while (num && numdigits--) { x -= w; V_DrawPatch(x, n->y, n->p[ num % 10 ]); num /= 10; } } // haleyjd 09/01/10: [STRIFE] All other functions were removed. /* void STlib_updateNum ( st_number_t* n, boolean refresh ) { if (*n->on) STlib_drawNum(n, refresh); } // void STlib_initPercent ( st_percent_t* p, int x, int y, patch_t** pl, int* num, boolean* on, patch_t* percent ) { STlib_initNum(&p->n, x, y, pl, num, on, 3); p->p = percent; } void STlib_updatePercent ( st_percent_t* per, int refresh ) { if (refresh && *per->n.on) V_DrawPatch(per->n.x, per->n.y, per->p); STlib_updateNum(&per->n, refresh); } void STlib_initMultIcon ( st_multicon_t* i, int x, int y, patch_t** il, int* inum, boolean* on ) { i->x = x; i->y = y; i->oldinum = -1; i->inum = inum; i->on = on; i->p = il; } void STlib_updateMultIcon ( st_multicon_t* mi, boolean refresh ) { int w; int h; int x; int y; if (*mi->on && (mi->oldinum != *mi->inum || refresh) && (*mi->inum!=-1)) { if (mi->oldinum != -1) { x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); w = SHORT(mi->p[mi->oldinum]->width); h = SHORT(mi->p[mi->oldinum]->height); if (y - ST_Y < 0) I_Error("updateMultIcon: y - ST_Y < 0"); V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y); } V_DrawPatch(mi->x, mi->y, mi->p[*mi->inum]); mi->oldinum = *mi->inum; } } void STlib_initBinIcon ( st_binicon_t* b, int x, int y, patch_t* i, boolean* val, boolean* on ) { b->x = x; b->y = y; b->oldval = false; b->val = val; b->on = on; b->p = i; } void STlib_updateBinIcon ( st_binicon_t* bi, boolean refresh ) { int x; int y; int w; int h; if (*bi->on && (bi->oldval != *bi->val || refresh)) { x = bi->x - SHORT(bi->p->leftoffset); y = bi->y - SHORT(bi->p->topoffset); w = SHORT(bi->p->width); h = SHORT(bi->p->height); if (y - ST_Y < 0) I_Error("updateBinIcon: y - ST_Y < 0"); if (*bi->val) V_DrawPatch(bi->x, bi->y, bi->p); else V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y); bi->oldval = *bi->val; } } */ crispy-doom-crispy-doom-5.6.4/src/strife/st_lib.h000066400000000000000000000072331360717211000217510ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // The status bar widget code. // #ifndef __STLIB__ #define __STLIB__ // We are referring to patches. #include "r_defs.h" // // 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; // haleyjd 09/01/10: [STRIFE] Removed "oldnum" member //int oldnum; // pointer to current value int* num; // haleyjd 09/01/10: [STRIFE] Removed "on" member // boolean* on; // list of patches for 0-9 patch_t** p; // user data int data; } st_number_t; // Percent widget ("child" of number widget, // or, more precisely, contains a number widget.) typedef struct { // number information st_number_t n; // percent sign graphic patch_t* p; } st_percent_t; // Multiple Icon widget typedef struct { // center-justified location of icons int x; int y; // last icon number int oldinum; // pointer to current icon int* inum; // pointer to boolean stating // whether to update icon boolean* on; // list of icons patch_t** p; // user data int data; } st_multicon_t; // Binary Icon widget typedef struct { // center-justified location of icon int x; int y; // last icon value boolean oldval; // pointer to current icon status boolean* val; // pointer to boolean // stating whether to update icon boolean* on; patch_t* p; // icon int data; // user data } st_binicon_t; // // Widget creation, access, and update routines // // Initializes widget library. // More precisely, initialize STMINUS, // everything else is done somewhere else. // void STlib_init(void); // Number widget routines // haleyjd 09/01/10: [STRIFE] Removed "on" parameter. void STlib_initNum ( st_number_t* n, int x, int y, patch_t** pl, int* num, int width ); // haleyjd 09/01/10: [STRIFE] Made globally visible. void STlib_drawNum ( st_number_t* n); // haleyjd 09/01/10: [STRIFE] New function void STlib_drawNumPositive ( st_number_t* n); /* haleyjd 09/01/10: [STRIFE] All the below were removed void STlib_updateNum ( st_number_t* n, boolean refresh ); // Percent widget routines void STlib_initPercent ( st_percent_t* p, int x, int y, patch_t** pl, int* num, boolean* on, patch_t* percent ); void STlib_updatePercent ( st_percent_t* per, int refresh ); // Multiple Icon widget routines void STlib_initMultIcon ( st_multicon_t* mi, int x, int y, patch_t** il, int* inum, boolean* on ); void STlib_updateMultIcon ( st_multicon_t* mi, boolean refresh ); // Binary Icon widget routines void STlib_initBinIcon ( st_binicon_t* b, int x, int y, patch_t* i, boolean* val, boolean* on ); void STlib_updateBinIcon ( st_binicon_t* bi, boolean refresh ); */ #endif crispy-doom-crispy-doom-5.6.4/src/strife/st_stuff.c000066400000000000000000001255621360717211000223330ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Status bar code. // Does the face/direction indicator animatin. // Does palette indicators as well (red pain/berserk, bright pickup) // #include #include "i_system.h" #include "i_video.h" #include "z_zone.h" #include "m_random.h" #include "w_wad.h" #include "deh_main.h" #include "deh_misc.h" #include "doomdef.h" #include "doomkeys.h" #include "g_game.h" #include "st_stuff.h" #include "st_lib.h" #include "r_local.h" #include "p_local.h" #include "p_inter.h" #include "p_dialog.h" // villsa [STRIFE] #include "am_map.h" #include "m_cheat.h" #include "m_menu.h" // villsa [STRIFE] #include "m_misc.h" #include "s_sound.h" // Needs access to LFB. #include "v_video.h" #include "i_swap.h" // State. #include "doomstat.h" #include "d_main.h" // [STRIFE] // Data. #include "dstrings.h" #include "sounds.h" #include "m_controls.h" #include "hu_lib.h" // [STRIFE] #include "hu_stuff.h" // // STATUS BAR DATA // // Palette indices. // For damage/bonus red-/gold-shifts #define STARTREDPALS 1 #define STARTBONUSPALS 9 #define NUMREDPALS 8 #define NUMBONUSPALS 4 // Radiation suit, green shift. #define RADIATIONPAL 13 // Location of status bar #define ST_X 0 // Location and size of statistics, // justified according to widget type. // Problem is, within which space? STbar? Screen? // Note: this could be read in by a lump. // Problem is, is the stuff rendered // into a buffer, // or into the frame buffer? // AMMO number pos. // haleyjd 20100901: [STRIFE] Adjusted. #define ST_AMMOWIDTH 3 #define ST_AMMOX 311 #define ST_AMMOY 162 // HEALTH number pos. // haleyjd 20100901: [STRIFE] Adjusted. #define ST_HEALTHWIDTH 3 #define ST_HEALTHX 79 #define ST_HEALTHY 162 // [STRIFE] // Removed: // * Weapon pos. // * Frags pos. // * ARMOR number pos. // * Key icon positions. // Ammunition counter. // haleyjd 20110213 [STRIFE]: ammo counters for the popup widget #define ST_POPUPAMMOX 206 static const int st_yforammo[NUMAMMO] = { 75, 99, 91, 139, 131, 115, 123 }; static const int st_wforammo[NUMAMMO] = { 3, 3, 2, 3, 3, 2, 3 }; // Indicate maximum ammunition. // Only needed because backpack exists. // haleyjd 20110213 [STRIFE]: maxammo counters for the popup widget #define ST_POPUPMAXAMMOX 239 // [STRIFE] // Removed: // * Doom weapon stuff // * DETH title (???) // Dimensions given in characters. #define ST_MSGWIDTH 52 // haleyjd 20100831: [STRIFE] // * Removed faces. // haleyjd 20100901: // * Removed DOOM pre-beta cruft. // * Removed deathmatch frags/arms-related stuff. // * Removed arms panel stuff. // * Removed unused widgets. // * Removed more faces, keyboxes, st_randomnumber // graphics are drawn to a backing screen and blitted to the real screen //byte *st_backing_screen; - [STRIFE]: Unused. // main player in game static player_t* plyr; // ST_Start() has just been called static boolean st_firsttime; // lump number for PLAYPAL static int lu_palette; // whether in automap or first-person static st_stateenum_t st_gamestate; // whether left-side main status bar is active static boolean st_statusbaron; // villsa [STRIFE] static boolean st_dosizedisplay = false; // haleyjd 09/01/10: [STRIFE] // Whether or not a popup is currently displayed static boolean st_displaypopup = false; // villsa [STRIFE] static int st_popupdisplaytics = 0; // villsa [STRIFE] // Whether or not show popup objective screen static boolean st_showobjective = false; // villsa [STRIFE] static boolean st_showinvpop = false; // villsa [STRIFE] static boolean st_showkeys = false; // villsa [STRIFE] TODO - identify variables static int st_keypage = -1; // [unused] static int dword_88490 = 0; // haleyjd 09/19/10: [STRIFE] Cached player data static int st_lastcursorpos; static int st_lastammo; static int st_lastarmortype; static int st_lasthealth; // haleyjd 20100901: [STRIFE] sbar -> invback // main inventory background and other bits static patch_t* invback; // main bar static patch_t* stback; // multiplayer background static patch_t* invtop; // top bit static patch_t* invpop; // popup frame with text static patch_t* invpop2; // plain popup frame static patch_t* invpbak; // popup background w/details static patch_t* invpbak2; // plain popup background static patch_t* invcursor; // cursor // ammo/weapon/armor patches static patch_t* invammo[NUMAMMO]; // ammo/weapons static patch_t* invsigil[5]; // sigil pieces static patch_t* invarmor[2]; // armor icons // names for ammo patches static const char *invammonames[NUMAMMO] = { "I_BLIT", "I_XQRL", "I_PQRL", "I_BRY1", "I_ROKT", "I_GRN1", "I_GRN2" }; // haleyjd 20100901: [STRIFE] Replaced tallnum, shortnum w/inv fonts // 0-9, green numbers static patch_t* invfontg[10]; // 0-9, yellow numbers static patch_t* invfonty[10]; // [unused] 3 key-cards, 3 skulls -- [STRIFE] has a lot more keys than 3 :P //static patch_t* keys[NUMCARDS]; // ready-weapon widget static st_number_t w_ready; // haleyjd [STRIFE]: This is still used. // haleyjd: [STRIFE] This is still used but was changed to a st_number_t. // health widget static st_number_t w_health; // ammo widgets static st_number_t w_ammo[NUMAMMO]; // haleyjd [STRIFE]: Still used. // max ammo widgets static st_number_t w_maxammo[NUMAMMO]; // haleyjd [STRIFE]: Still used. // [unused] number of frags so far in deathmatch //static int st_fragscount; cheatseq_t cheat_mus = CHEAT("spin", 2); // [STRIFE]: idmus -> spin cheatseq_t cheat_god = CHEAT("omnipotent", 0); // [STRIFE]: iddqd -> omnipotent cheatseq_t cheat_ammo = CHEAT("boomstix", 0); // [STRIFE]: idfa -> boomstix cheatseq_t cheat_noclip = CHEAT("elvis", 0); // [STRIFE]: idclip -> elvis cheatseq_t cheat_clev = CHEAT("rift", 2); // [STRIFE]: idclev -> rift cheatseq_t cheat_mypos = CHEAT("gps", 0); // [STRIFE]: idmypos -> gps cheatseq_t cheat_scoot = CHEAT("scoot", 1); // [STRIFE]: new cheat scoot cheatseq_t cheat_nuke = CHEAT("stonecold", 0); // [STRIFE]: new cheat stonecold cheatseq_t cheat_keys = CHEAT("jimmy", 0); // [STRIFE]: new cheat jimmy (all keys) cheatseq_t cheat_stealth = CHEAT("gripper", 0); // [STRIFE]: new cheat gripper cheatseq_t cheat_midas = CHEAT("donnytrump", 0); // [STRIFE]: new cheat cheatseq_t cheat_lego = CHEAT("lego", 0); // [STRIFE]: new cheat cheatseq_t cheat_dev = CHEAT("dots", 0); // [STRIFE]: new cheat // haleyjd 20110224: enumeration for access to powerup cheats enum { ST_PUMPUP_B, ST_PUMPUP_I, ST_PUMPUP_M, ST_PUMPUP_H, ST_PUMPUP_P, ST_PUMPUP_S, ST_PUMPUP_T, ST_PUMPUP, NUM_ST_PUMPUP }; cheatseq_t cheat_powerup[NUM_ST_PUMPUP] = // [STRIFE] { CHEAT("pumpupb", 0), CHEAT("pumpupi", 0), CHEAT("pumpupm", 0), CHEAT("pumpuph", 0), CHEAT("pumpupp", 0), CHEAT("pumpups", 0), CHEAT("pumpupt", 0), CHEAT("pumpup", 0), }; //cheatseq_t cheat_choppers = CHEAT("idchoppers", 0); [STRIFE] no such thing void M_SizeDisplay(int choice); // villsa [STRIFE] // // STATUS BAR CODE // void ST_Stop(void); // [STRIFE] static char st_msgbuf[ST_MSGWIDTH]; // Respond to keyboard input events, // intercept cheats. boolean ST_Responder(event_t* ev) { // haleyjd 09/27/10: made static to ST_Responder static boolean st_keystate = false; int i; // Filter automap on/off. if(ev->type == ev_keyup) { if((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; } return false; } // villsa [STRIFE] if(ev->data1 != key_invpop && ev->data1 != key_mission && ev->data1 != key_invkey) return false; // villsa [STRIFE] if(ev->data1 == key_invpop) st_showinvpop = false; else { if(ev->data1 == key_mission) st_showobjective = false; else { if(ev->data1 == key_invkey) { st_showkeys = false; st_keystate = false; } } } if(!st_showkeys && !st_showobjective && !st_showinvpop) { if(!st_popupdisplaytics) { st_displaypopup = false; if(st_dosizedisplay) M_SizeDisplay(true); st_dosizedisplay = false; } } return true; } // if a user keypress... if(ev->type != ev_keydown) return false; // haleyjd 20100927: No input allowed when the player is dead if(plyr->mo->health <= 0) return false; // keydown events if(ev->data1 == key_invquery) // inventory query { inventory_t *inv = &(plyr->inventory[plyr->inventorycursor]); if(inv->amount) { DEH_snprintf(st_msgbuf, sizeof(st_msgbuf), "%d %s", inv->amount, DEH_String(mobjinfo[inv->type].name)); plyr->message = st_msgbuf; } } // villsa [STRIFE] if(ev->data1 == key_invpop || ev->data1 == key_invkey || ev->data1 == key_mission) { if(ev->data1 == key_invkey) { st_showobjective = false; st_showinvpop = false; if(!st_keystate) { st_keystate = true; if(++st_keypage > 2) { st_popupdisplaytics = 0; st_showkeys = false; st_displaypopup = false; st_keypage = -1; return true; } } if(netgame) st_popupdisplaytics = 20; else st_popupdisplaytics = 50; st_showkeys = true; } else { if(ev->data1 != key_mission || netgame) { if(ev->data1 == key_invpop) { st_keypage = -1; st_popupdisplaytics = false; st_showkeys = false; st_showobjective = false; st_showinvpop = true; } } else { st_showkeys = netgame; st_showinvpop = netgame; st_keypage = -1; st_popupdisplaytics = ev->data2 ^ key_mission; st_showobjective = true; } } if(st_showkeys || st_showobjective || st_showinvpop) { st_displaypopup = true; if(viewheight == SCREENHEIGHT) { M_SizeDisplay(false); st_dosizedisplay = true; } } } if(ev->data1 == key_invleft) // inventory move left { if(plyr->inventorycursor > 0) plyr->inventorycursor--; return true; } else if(ev->data1 == key_invright) { if(plyr->inventorycursor < plyr->numinventory - 1) plyr->inventorycursor++; return true; } else if(ev->data1 == key_invhome) { plyr->inventorycursor = 0; return true; } else if(ev->data1 == key_invend) { if(plyr->numinventory) plyr->inventorycursor = plyr->numinventory - 1; else plyr->inventorycursor = 0; return true; } // // [STRIFE] Cheats which are allowed in netgames/demos: // // 'spin' cheat for changing music if (cht_CheckCheat(&cheat_mus, ev->data2)) { char buf[3]; int musnum; plyr->message = DEH_String(STSTR_MUS); cht_GetParam(&cheat_mus, buf); musnum = (buf[0] - '0') * 10 + buf[1] - '0'; if (((buf[0]-'0')*10 + buf[1]-'0') > 35) plyr->message = DEH_String(STSTR_NOMUS); else S_ChangeMusic(musnum, 1); } // [STRIFE]: "dev" cheat - "DOTS" else if (cht_CheckCheat(&cheat_dev, ev->data2)) { devparm = !devparm; if (devparm) plyr->message = DEH_String("devparm ON"); else plyr->message = DEH_String("devparm OFF"); } // [STRIFE] Cheats below are not allowed in netgames or demos if(netgame || !usergame) return false; if (cht_CheckCheat(&cheat_god, ev->data2)) { // 'omnipotent' cheat for toggleable god mode plyr->cheats ^= CF_GODMODE; if (plyr->cheats & CF_GODMODE) { if (plyr->mo) plyr->mo->health = 100; plyr->health = deh_god_mode_health; plyr->st_update = true; // [STRIFE] plyr->message = DEH_String(STSTR_DQDON); } else { plyr->st_update = true; plyr->message = DEH_String(STSTR_DQDOFF); } } else if (cht_CheckCheat(&cheat_ammo, ev->data2)) { // [STRIFE]: "BOOMSTIX" cheat for all normal weapons plyr->armorpoints = deh_idkfa_armor; plyr->armortype = deh_idkfa_armor_class; for (i = 0; i < NUMWEAPONS; i++) if(!isdemoversion || weaponinfo[i].availabledemo) plyr->weaponowned[i] = true; // Takes away the Sigil, even if you already had it... plyr->weaponowned[wp_sigil] = false; for (i=0;iammo[i] = plyr->maxammo[i]; plyr->message = DEH_String(STSTR_FAADDED); } else if(cht_CheckCheat(&cheat_keys, ev->data2)) { // villsa [STRIFE]: "JIMMY" cheat for all keys #define FIRSTKEYSETAMOUNT 16 if(plyr->cards[FIRSTKEYSETAMOUNT - 1]) { if(plyr->cards[NUMCARDS - 1] || isdemoversion) { for(i = 0; i < NUMCARDS; i++) plyr->cards[i] = false; plyr->message = DEH_String("Keys removed"); } else { for(i = 0; i < NUMCARDS; i++) plyr->cards[i] = true; plyr->message = DEH_String("Cheater Keys Added"); } } else { for(i = 0; i < FIRSTKEYSETAMOUNT; i++) plyr->cards[i] = true; plyr->message = DEH_String("Cheater Keys Added"); } } else if (cht_CheckCheat(&cheat_noclip, ev->data2)) { // [STRIFE] Removed idspispopd, added NOCLIP flag setting/removal // Noclip cheat - "ELVIS" (hah-hah :P ) plyr->cheats ^= CF_NOCLIP; if (plyr->cheats & CF_NOCLIP) { plyr->message = DEH_String(STSTR_NCON); plyr->mo->flags |= MF_NOCLIP; } else { plyr->message = DEH_String(STSTR_NCOFF); plyr->mo->flags &= ~MF_NOCLIP; } } else if(cht_CheckCheat(&cheat_stealth, ev->data2)) { // villsa [STRIFE]: "GRIPPER" cheat; nothing to do with stealth... plyr->cheats ^= CF_NOMOMENTUM; if(plyr->cheats & CF_NOMOMENTUM) plyr->message = DEH_String("STEALTH BOOTS ON"); else plyr->message = DEH_String("STEALTH BOOTS OFF"); } for(i = 0; i < ST_PUMPUP_B + 3; ++i) { // [STRIFE]: Handle berserk, invisibility, and envirosuit if(cht_CheckCheat(&cheat_powerup[i], ev->data2)) { if(plyr->powers[i]) plyr->powers[i] = (i != 1); else P_GivePower(plyr, i); plyr->message = DEH_String(STSTR_BEHOLDX); } } if(cht_CheckCheat(&cheat_powerup[ST_PUMPUP_H], ev->data2)) { // [STRIFE]: PUMPUPH gives medical inventory items P_GiveItemToPlayer(plyr, SPR_STMP, MT_INV_MED1); P_GiveItemToPlayer(plyr, SPR_MDKT, MT_INV_MED2); P_GiveItemToPlayer(plyr, SPR_FULL, MT_INV_MED3); plyr->message = DEH_String("you got the stuff!"); } if(cht_CheckCheat(&cheat_powerup[ST_PUMPUP_P], ev->data2)) { // [STRIFE]: PUMPUPP gives backpack if(!plyr->backpack) { for(i = 0; i < NUMAMMO; ++i) plyr->maxammo[i] = 2 * plyr->maxammo[i]; } plyr->backpack = true; for(i = 0; i < NUMAMMO; ++i) P_GiveAmmo(plyr, i, 1); plyr->message = DEH_String("you got the stuff!"); } if(cht_CheckCheat(&cheat_powerup[ST_PUMPUP_S], ev->data2)) { // [STRIFE]: PUMPUPS gives stamina and accuracy upgrades P_GiveItemToPlayer(plyr, SPR_TOKN, MT_TOKEN_STAMINA); P_GiveItemToPlayer(plyr, SPR_TOKN, MT_TOKEN_NEW_ACCURACY); plyr->message = DEH_String("you got the stuff!"); } if(cht_CheckCheat(&cheat_powerup[ST_PUMPUP_T], ev->data2)) { // [STRIFE] PUMPUPT gives targeter P_GivePower(plyr, pw_targeter); plyr->message = DEH_String("you got the stuff!"); } // [STRIFE]: PUMPUP if (cht_CheckCheat(&cheat_powerup[ST_PUMPUP], ev->data2)) { // 'behold' power-up menu plyr->message = DEH_String(STSTR_BEHOLD); return false; } if (cht_CheckCheat(&cheat_mypos, ev->data2)) { // [STRIFE] 'GPS' for player position static char buf[ST_MSGWIDTH]; M_snprintf(buf, sizeof(buf), "ang=0x%x;x,y=(0x%x,0x%x)", players[consoleplayer].mo->angle, players[consoleplayer].mo->x, players[consoleplayer].mo->y); plyr->message = buf; } // 'rift' change-level cheat if (cht_CheckCheat(&cheat_clev, ev->data2)) { char buf[3]; int map; cht_GetParam(&cheat_clev, buf); map = (buf[0] - '0') * 10 + buf[1] - '0'; // haleyjd 20100901: Removed Chex Quest stuff. // haleyjd 20100915: Removed retail/registered/shareware stuff // haleyjd 20130301: different bounds in v1.31 // Ohmygod - this is not going to work. if(gameversion == exe_strife_1_31) { if ((isdemoversion && (map < 32 || map > 34)) || (isregistered && (map <= 0 || map > 34))) return false; } else { if (map <= 0 || map > 40) return false; } // So be it. plyr->message = DEH_String(STSTR_CLEV); G_RiftExitLevel(map, 0, plyr->mo->angle); } else if(cht_CheckCheat(&cheat_scoot, ev->data2)) { char buf[3]; int spot; cht_GetParam(&cheat_scoot, buf); spot = buf[0] - '0'; // BUG: should be <= 9. Shouldn't do anything bad though... if(spot <= 10) { plyr->message = DEH_String("Spawning to spot"); G_RiftCheat(spot); return false; } } // villsa [STRIFE] if(cht_CheckCheat(&cheat_nuke, ev->data2)) { stonecold ^= 1; plyr->message = DEH_String("Kill 'em. Kill 'em All"); return false; } // villsa [STRIFE] if(cht_CheckCheat(&cheat_midas, ev->data2)) { plyr->message = DEH_String("YOU GOT THE MIDAS TOUCH, BABY"); P_GiveItemToPlayer(plyr, SPR_HELT, MT_TOKEN_TOUGHNESS); } // villsa [STRIFE] // haleyjd 20110224: No sigil in demo version if(!isdemoversion && cht_CheckCheat(&cheat_lego, ev->data2)) { plyr->st_update = true; if(plyr->weaponowned[wp_sigil]) { if(++plyr->sigiltype > 4) { plyr->sigiltype = -1; plyr->pendingweapon = wp_fist; plyr->weaponowned[wp_sigil] = false; } } else { plyr->weaponowned[wp_sigil] = true; plyr->sigiltype = 0; } // BUG: This brings up a bad version of the Sigil (sigiltype -1) which // causes some VERY interesting behavior, when you type LEGO for the // sixth time. This shouldn't be done when taking it away, and yet it // is here... verified with vanilla. plyr->pendingweapon = wp_sigil; } return false; } /* int ST_calcPainOffset(void) { // haleyjd 08/31/10: [STRIFE] Removed. } */ // // This is a not-very-pretty routine which handles // the face states and their timing. // the precedence of expressions is: // dead > evil grin > turned head > straight ahead // /* void ST_updateFaceWidget(void) { // haleyjd 08/31/10: [STRIFE] Removed. } */ /* void ST_updateWidgets(void) { // haleyjd 09/01/10: [STRIFE] Rogue merged this into ST_Ticker below. } */ // // ST_Ticker // // haleyjd 09/01/10: [STRIFE] // * Removed st_clock and st_randomnumber. // * Merged ST_updateWidgets here. Wasn't inlined, as doesn't exist separately // in the binary as inlined functions normally do. // void ST_Ticker (void) { static int largeammo = 1994; // means "n/a" // must redirect the pointer if the ready weapon has changed. if (weaponinfo[plyr->readyweapon].ammo == am_noammo) w_ready.num = &largeammo; else w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; w_ready.data = plyr->readyweapon; // STRIFE-TODO: Gobbledeegunk. /* v2 = dword_88490-- == 1; // no clue yet... if(v2) dword_DC7F4 = dword_DC7F0;*/ if(st_popupdisplaytics) { int tics = st_popupdisplaytics; --st_popupdisplaytics; if(tics == 1) { st_displaypopup = false; st_showkeys = false; st_keypage = -1; if(st_dosizedisplay) M_SizeDisplay(true); // mondo hack? st_dosizedisplay = false; } } // haleyjd 20100901: [STRIFE] Keys are handled on a popup // haleyjd 20100831: [STRIFE] No face widget // haleyjd 20100901: [STRIFE] Armor, weapons, frags, etc. handled elsewhere // haleyjd: This is from the PRE-BETA! Left here because it amuses me ;) // get rid of chat window if up because of message //if (!--st_msgcounter) // st_chat = st_oldchat; } static int st_palette = 0; // // ST_doPaletteStuff // // haleyjd 20100831: [STRIFE] // * Changed radsuit palette handling for Strife nukagecount. // * All other logic verified to be unmodified. // void ST_doPaletteStuff(void) { int palette; byte* pal; int cnt; int bzc; cnt = plyr->damagecount; if (plyr->powers[pw_strength]) { // slowly fade the berzerk out bzc = 12 - (plyr->powers[pw_strength]>>6); if (bzc > cnt) cnt = bzc; } if (cnt) { palette = (cnt+7)>>3; if (palette >= NUMREDPALS) palette = NUMREDPALS-1; palette += STARTREDPALS; } else if (plyr->bonuscount) { palette = (plyr->bonuscount+7)>>3; if (palette >= NUMBONUSPALS) palette = NUMBONUSPALS-1; palette += STARTBONUSPALS; } // haleyjd 20100831: [STRIFE] Flash green when in nukage, not when has // an environment suit (a breathing sound is played to indicate that // instead). else if ( plyr->nukagecount > 16*TICRATE || (plyr->nukagecount & 8)) palette = RADIATIONPAL; else palette = 0; // haleyjd 08/31/10: Removed Chex Quest if (palette != st_palette) { st_palette = palette; pal = (byte *) W_CacheLumpNum (lu_palette, PU_CACHE)+palette*768; I_SetPalette (pal); } } /* void ST_drawWidgets(boolean refresh) { haleyjd 09/01/10: [STRIFE] Removed } */ // // ST_drawNumFontY // // haleyjd 20100919: [STRIFE] New function // Draws a small yellow number for inventory etc. // void ST_drawNumFontY(int x, int y, int num) { if(!num) V_DrawPatch(x, y, invfonty[0]); while(num) { V_DrawPatch(x, y, invfonty[num % 10]); x -= SHORT(invfonty[0]->width) + 1; num /= 10; } } // // ST_drawNumFontY2 // // haleyjd 20100919: [STRIFE] New function // As above, but turns negative numbers into zero. // void ST_drawNumFontY2(int x, int y, int num) { if(!num) V_DrawPatch(x, y, invfonty[0]); if(num < 0) num = 0; while(num) { V_DrawPatchDirect(x, y, invfonty[num % 10]); x -= SHORT(invfonty[0]->width) + 1; num /= 10; } } // // ST_drawLine // // haleyjd 20100920: [STRIFE] New function // Basic horizontal line drawing routine used for the health bars. // void ST_drawLine(int x, int y, int len, int color) { byte putcolor = (byte)(color); byte *drawpos = I_VideoBuffer + (y << crispy->hires) * SCREENWIDTH + (x << crispy->hires); int i = 0; while(i < (len << crispy->hires)) { if (crispy->hires) *(drawpos + SCREENWIDTH) = putcolor; *drawpos++ = putcolor; ++i; } } // // ST_doRefresh // // haleyjd 20100920: Evidence more than suggests that Rogue moved all status bar // drawing down to this function. // void ST_doRefresh(void) { // draw status bar background to off-screen buff if (st_statusbaron) { int firstinventory, icon_x, num_x, i, numdrawn; // haleyjd 20100919: No backscreen caching in Strife. //V_UseBuffer(st_backing_screen); // TODO: only sometimes drawing? plyr->st_update = false; // cache data st_lastcursorpos = plyr->inventorycursor; st_lastammo = weaponinfo[plyr->readyweapon].ammo; st_lastarmortype = plyr->armortype; st_lasthealth = plyr->health; st_firsttime = false; // draw main status bar V_DrawPatch(ST_X, ST_Y, invback); // draw multiplayer armor backdrop if netgame // haleyjd 20131031: BUG - vanilla is accessing a NULL pointer here when // playing back netdemos! It doesn't appear to draw anything, and there // is no apparent ill effect on gameplay, so the best we can do is check. if(netgame && stback) V_DrawPatch(ST_X, 173, stback); if(plyr->inventorycursor >= 6) firstinventory = plyr->inventorycursor - 5; else firstinventory = 0; // Draw cursor. if(plyr->numinventory) { V_DrawPatch(35 * (plyr->inventorycursor - firstinventory) + 42, 180, invcursor); } // Draw inventory bar for(num_x = 68, icon_x = 48, i = firstinventory, numdrawn = 0; num_x < 278; num_x += 35, icon_x += 35, i++, numdrawn++) { int lumpnum; patch_t *patch; char iconname[8]; if(plyr->numinventory <= numdrawn) break; DEH_snprintf(iconname, sizeof(iconname), "I_%s", DEH_String(sprnames[plyr->inventory[i].sprite])); lumpnum = W_CheckNumForName(iconname); if(lumpnum == -1) patch = W_CacheLumpName(DEH_String("STCFN063"), PU_CACHE); else patch = W_CacheLumpNum(lumpnum, PU_STATIC); V_DrawPatch(icon_x, 182, patch); ST_drawNumFontY(num_x, 191, plyr->inventory[i].amount); } // haleyjd 20100919: Draw sigil icon if(plyr->weaponowned[wp_sigil]) V_DrawPatch(253, 175, invsigil[plyr->sigiltype]); // haleyjd 20100919: Draw ammo if(st_lastammo < NUMAMMO) V_DrawPatch(290, 180, invammo[st_lastammo]); // haleyjd 20100919: Draw armor if(plyr->armortype) { V_DrawPatch(2, 177, invarmor[plyr->armortype - 1]); ST_drawNumFontY(20, 191, plyr->armorpoints); } // haleyjd 20100920: Draw life bars. { int barlength; int lifecolor1; int lifecolor2; barlength = plyr->health; if(barlength > 100) barlength = 200 - plyr->health; barlength *= 2; if(plyr->health < 11) // Danger, Will Robinson! lifecolor1 = 64; else if(plyr->health < 21) // Caution lifecolor1 = 80; else // All is well. lifecolor1 = 96; if(plyr->cheats & CF_GODMODE) // Gold, probably a throwback to DOOM. lifecolor1 = 226; lifecolor2 = lifecolor1 + 3; // Draw the normal health bars ST_drawLine(49, 172, barlength, lifecolor1); ST_drawLine(49, 173, barlength, lifecolor2); ST_drawLine(49, 175, barlength, lifecolor1); ST_drawLine(49, 176, barlength, lifecolor2); // Draw the > 100 health lines if(plyr->health > 100) { int oldbarlength = barlength; lifecolor1 = 112; // Shades of blue lifecolor2 = lifecolor1 + 3; // take up the difference not drawn by the first (<= 100) bar barlength = 200 - barlength; ST_drawLine(49 + oldbarlength, 172, barlength, lifecolor1); ST_drawLine(49 + oldbarlength, 173, barlength, lifecolor2); ST_drawLine(49 + oldbarlength, 175, barlength, lifecolor1); ST_drawLine(49 + oldbarlength, 176, barlength, lifecolor2); } } // end local-scope block // haleyjd 20100919: nope, not in Strife. //V_RestoreBuffer(); //V_CopyRect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y); } } // haleyjd [STRIFE]: Removed ST_diffDraw void ST_Drawer (boolean fullscreen, boolean refresh) { st_statusbaron = (!fullscreen) || automapactive; st_firsttime = st_firsttime || refresh; // Do red-/gold-shifts from damage/items ST_doPaletteStuff(); // If just after ST_Start(), refresh all ST_doRefresh(); // Otherwise, update as little as possible //ST_diffDraw(); [STRIFE]: nope } // // ST_calcFrags // // haleyjd [STRIFE] New function. // Calculate frags for display on the frags popup. // static int ST_calcFrags(int pnum) { int i; int result = 0; for(i = 0; i < MAXPLAYERS; i++) { if(i == pnum) // self-frags result -= players[pnum].frags[i]; else result += players[pnum].frags[i]; } return result; } // // ST_drawTime // // villsa [STRIFE] New function. // Draws game time on pop up screen // static void ST_drawTime(int x, int y, int time) { int hours; int minutes; int seconds; char string[16]; // haleyjd 20110213: fixed minutes hours = time / 3600; minutes = (time / 60) % 60; seconds = time % 60; DEH_snprintf(string, 16, "%02d:%02d:%02d", hours, minutes, seconds); HUlib_drawYellowText(x, y, string); } #define ST_KEYSPERPAGE 10 #define ST_KEYS_X 20 #define ST_KEYS_Y 63 #define ST_KEYNAME_X 17 #define ST_KEYNAME_Y 4 #define ST_KEYS_YSTEP 17 #define ST_KEYS_NUMROWS 4 #define ST_KEYS_COL2X 160 // // ST_drawKeysPopup // // haleyjd 20110213: [STRIFE] New function // This has taken the longest out of almost everything to get working properly. // static boolean ST_drawKeysPopup(void) { int x, y, yt, key, keycount; mobjinfo_t *info; V_DrawXlaPatch(0, 56, invpbak2); V_DrawPatchDirect(0, 56, invpop2); if(deathmatch) { int pnum; patch_t *colpatch; char buffer[128]; int frags; // In deathmatch, the keys popup is replaced by a chart of frag counts // first column y = 64; yt = 66; for(pnum = 0; pnum < MAXPLAYERS/2; pnum++) { DEH_snprintf(buffer, sizeof(buffer), "stcolor%d", pnum+1); colpatch = W_CacheLumpName(buffer, PU_CACHE); V_DrawPatchDirect(28, y, colpatch); frags = ST_calcFrags(pnum); DEH_snprintf(buffer, sizeof(buffer), "%s%d", player_names[pnum], frags); HUlib_drawYellowText(38, yt, buffer); if(!playeringame[pnum]) HUlib_drawYellowText(28, pnum*17 + 65, "X"); y += 17; yt += 17; } // second column y = 64; yt = 66; for(pnum = MAXPLAYERS/2; pnum < MAXPLAYERS; pnum++) { DEH_snprintf(buffer, sizeof(buffer), "stcolor%d", pnum+1); colpatch = W_CacheLumpName(buffer, PU_CACHE); V_DrawPatchDirect(158, y, colpatch); frags = ST_calcFrags(pnum); DEH_snprintf(buffer, sizeof(buffer), "%s%d", player_names[pnum], frags); HUlib_drawYellowText(168, yt, buffer); if(!playeringame[pnum]) HUlib_drawYellowText(158, pnum*17 - 3, "X"); y += 17; yt += 17; } } else { // Bounds-check page number if(st_keypage < 0 || st_keypage > 2) { st_keypage = -1; st_popupdisplaytics = 0; st_displaypopup = false; return false; } // Are there any keys to display on this page? if(st_keypage > 0) { boolean haskeyinrange = false; for(key = ST_KEYSPERPAGE * st_keypage, keycount = 0; keycount < ST_KEYSPERPAGE && key < NUMCARDS; ++key, ++keycount) { if(plyr->cards[key]) haskeyinrange = true; } if(!haskeyinrange) { st_displaypopup = false; st_showkeys = false; st_keypage = -1; return false; } } // Draw the keys for the current page key = ST_KEYSPERPAGE * st_keypage; keycount = 0; x = ST_KEYS_X; y = ST_KEYS_Y; info = &mobjinfo[MT_KEY_BASE + key]; for(; keycount < ST_KEYSPERPAGE && key < NUMCARDS; ++key, ++keycount, ++info) { char sprname[8]; patch_t *patch; memset(sprname, 0, sizeof(sprname)); if(plyr->cards[key]) { // Get spawnstate sprite name and load corresponding icon DEH_snprintf(sprname, sizeof(sprname), "I_%s", sprnames[states[info->spawnstate].sprite]); patch = W_CacheLumpName(sprname, PU_CACHE); V_DrawPatchDirect(x, y, patch); HUlib_drawYellowText(x + ST_KEYNAME_X, y + ST_KEYNAME_Y, info->name); } if(keycount != ST_KEYS_NUMROWS) y += ST_KEYS_YSTEP; else { x = ST_KEYS_COL2X; y = ST_KEYS_Y; } } } return true; } // // ST_DrawExternal // // haleyjd 20100901: [STRIFE] New function. // * Draws external portions of the status bar such the top bar and popups. // boolean ST_DrawExternal(void) { int i; if(st_statusbaron) { V_DrawPatchDirect(0, 160, invtop); STlib_drawNumPositive(&w_health); STlib_drawNumPositive(&w_ready); } else { ammotype_t ammo; ST_drawNumFontY2(15, 194, plyr->health); ammo = weaponinfo[plyr->readyweapon].ammo; if (ammo != am_noammo) ST_drawNumFontY2(310, 194, plyr->ammo[ammo]); } if(!st_displaypopup) return false; // villsa [STRIFE] added 20100926 if(st_showobjective) { V_DrawXlaPatch(0, 56, invpbak2); V_DrawPatchDirect(0, 56, invpop2); M_DialogDimMsg(24, 74, mission_objective, true); HUlib_drawYellowText(24, 74, mission_objective); ST_drawTime(210, 64, leveltime / TICRATE); } else { int keys = 0; // villsa [STRIFE] keys popup if(st_showkeys || st_popupdisplaytics) return ST_drawKeysPopup(); V_DrawXlaPatch(0, 56, invpbak); V_DrawPatchDirect(0, 56, invpop); for(i = 0; i < NUMCARDS; i++) { if(plyr->cards[i]) keys++; } ST_drawNumFontY2(261, 132, keys); if(plyr->weaponowned[wp_elecbow]) { V_DrawPatchDirect(38, 86, W_CacheLumpName(DEH_String("CBOWA0"), PU_CACHE)); } if(plyr->weaponowned[wp_rifle]) { V_DrawPatchDirect(40, 107, W_CacheLumpName(DEH_String("RIFLA0"), PU_CACHE)); } if(plyr->weaponowned[wp_missile]) { V_DrawPatchDirect(39, 131, W_CacheLumpName(DEH_String("MMSLA0"), PU_CACHE)); } if(plyr->weaponowned[wp_hegrenade]) { V_DrawPatchDirect(78, 87, W_CacheLumpName(DEH_String("GRNDA0"), PU_CACHE)); } if(plyr->weaponowned[wp_flame]) { V_DrawPatchDirect(80, 117, W_CacheLumpName(DEH_String("FLAMA0"), PU_CACHE)); } if(plyr->weaponowned[wp_mauler]) { V_DrawPatchDirect(75, 142, W_CacheLumpName(DEH_String("TRPDA0"), PU_CACHE)); } // haleyjd 20110213: draw ammo for(i = 0; i < NUMAMMO; i++) { STlib_drawNumPositive(&w_ammo[i]); STlib_drawNumPositive(&w_maxammo[i]); } ST_drawNumFontY2(261, 84, plyr->accuracy); ST_drawNumFontY2(261, 108, plyr->stamina); if(plyr->powers[pw_communicator]) { V_DrawPatchDirect(280, 130, W_CacheLumpName(DEH_String("I_COMM"), PU_CACHE)); } } return true; } typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); // // ST_loadUnloadGraphics // // Iterates through all graphics to be loaded or unloaded, along with // the variable they use, invoking the specified callback function. // // [STRIFE] Altered to load all Strife status bar resources. // static void ST_loadUnloadGraphics(load_callback_t callback) { int i; char namebuf[9]; // haleyjd 20100901: [STRIFE] // Load the numbers, green and yellow for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "INVFONG%d", i); callback(namebuf, &invfontg[i]); DEH_snprintf(namebuf, 9, "INVFONY%d", i); callback(namebuf, &invfonty[i]); } // haleyjd 20100919: load Sigil patches if(!isdemoversion) { for(i = 0; i < 5; i++) { DEH_snprintf(namebuf, 9, "I_SGL%d", i+1); callback(namebuf, &invsigil[i]); } } // load ammo patches for(i = 0; i < NUMAMMO; i++) callback(DEH_String(invammonames[i]), &invammo[i]); // load armor patches callback(DEH_String("I_ARM2"), &invarmor[0]); callback(DEH_String("I_ARM1"), &invarmor[1]); // haleyjd 20100919: [STRIFE] // * No face, but there is this patch, which appears behind the armor DEH_snprintf(namebuf, 9, "STBACK0%d", consoleplayer + 1); if(netgame) callback(namebuf, &stback); // 20100901: // * Removed all unused DOOM stuff (arms, numbers, %, etc). // haleyjd 20100901: [STRIFE]: stbar -> invback, added new patches // status bar background bits callback(DEH_String("INVBACK"), &invback); callback(DEH_String("INVTOP"), &invtop); callback(DEH_String("INVPOP"), &invpop); callback(DEH_String("INVPOP2"), &invpop2); callback(DEH_String("INVPBAK"), &invpbak); callback(DEH_String("INVPBAK2"), &invpbak2); callback(DEH_String("INVCURS"), &invcursor); } static void ST_loadCallback(const char *lumpname, patch_t **variable) { *variable = W_CacheLumpName(lumpname, PU_STATIC); } void ST_loadGraphics(void) { ST_loadUnloadGraphics(ST_loadCallback); } void ST_loadData(void) { // static int dword_8848C = 1; // STRIFE-TODO: what is the purpose of this? // dword_8848C = 0; lu_palette = W_GetNumForName (DEH_String("PLAYPAL")); ST_loadGraphics(); } static void ST_unloadCallback(const char *lumpname, patch_t **variable) { W_ReleaseLumpName(lumpname); *variable = NULL; } void ST_unloadGraphics(void) { ST_loadUnloadGraphics(ST_unloadCallback); } void ST_unloadData(void) { ST_unloadGraphics(); } // // ST_initData // // haleyjd 20100901: [STRIFE] // * Removed prebeta cruft, face stuff, keyboxes, and oldwe // void ST_initData(void) { st_firsttime = true; plyr = &players[consoleplayer]; st_gamestate = FirstPersonState; st_statusbaron = true; st_palette = -1; STlib_init(); } void ST_createWidgets(void) { int i; // ready weapon ammo STlib_initNum(&w_ready, ST_AMMOX, ST_AMMOY, invfontg, &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], ST_AMMOWIDTH); // the last weapon type w_ready.data = plyr->readyweapon; // health percentage STlib_initNum(&w_health, ST_HEALTHX, ST_HEALTHY, invfontg, &plyr->health, ST_HEALTHWIDTH); // haleyjd 20100831: [STRIFE] // * No face. // 20100901: // * No arms, weaponsowned, frags, armor, keyboxes // haleyjd 20110213: Ammo Widgets!!! for(i = 0; i < NUMAMMO; i++) { STlib_initNum(&w_ammo[i], ST_POPUPAMMOX, st_yforammo[i], invfonty, &plyr->ammo[i], st_wforammo[i]); STlib_initNum(&w_maxammo[i], ST_POPUPMAXAMMOX, st_yforammo[i], invfonty, &plyr->maxammo[i], st_wforammo[i]); } } static boolean st_stopped = true; void ST_Start (void) { if (!st_stopped) ST_Stop(); ST_initData(); ST_createWidgets(); st_stopped = false; } void ST_Stop (void) { if (st_stopped) return; I_SetPalette (W_CacheLumpNum (lu_palette, PU_CACHE)); st_stopped = true; } void ST_Init (void) { ST_loadData(); // haleyjd 20100919: This is not used by Strife. More memory for voices! //st_backing_screen = (byte *) Z_Malloc(ST_WIDTH * ST_HEIGHT, PU_STATIC, 0); } crispy-doom-crispy-doom-5.6.4/src/strife/st_stuff.h000066400000000000000000000051171360717211000223310ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Status bar code. // Does the face/direction indicator 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 "m_cheat.h" // Size of statusbar. // Now sensitive for scaling. #define ST_HEIGHT 32 #define ST_WIDTH ORIGWIDTH #define ST_Y (ORIGHEIGHT - ST_HEIGHT) // // STATUS BAR // // Called by main loop. boolean ST_Responder (event_t* ev); // Called by main loop. void ST_Ticker (void); // Called by main loop. void ST_Drawer (boolean fullscreen, boolean refresh); // haleyjd 09/01/10: [STRIFE] New function. // Called by main loop to draw external status bar bits. // Returns true if a popup is drawing. boolean ST_DrawExternal(void); // Called when the console player is spawned on each level. void ST_Start (void); // Called by startup code. void ST_Init (void); // States for status bar code. typedef enum { AutomapState, FirstPersonState } st_stateenum_t; // States for the chat code. typedef enum { StartChatState, WaitDestState, GetChatState } st_chatstateenum_t; extern byte *st_backing_screen; extern cheatseq_t cheat_mus; // [STRIFE]: idmus -> spin extern cheatseq_t cheat_god; // [STRIFE]: iddqd -> omnipotent extern cheatseq_t cheat_ammo; // [STRIFE]: idfa -> boomstix extern cheatseq_t cheat_noclip; // [STRIFE]: idclip -> elvis extern cheatseq_t cheat_clev; // [STRIFE]: idclev -> rift extern cheatseq_t cheat_mypos; // [STRIFE]: idmypos -> gps extern cheatseq_t cheat_scoot; // [STRIFE]: new cheat scoot extern cheatseq_t cheat_nuke; // [STRIFE]: new cheat stonecold extern cheatseq_t cheat_keys; // [STRIFE]: new cheat jimmy (all keys) extern cheatseq_t cheat_stealth; // [STRIFE]: new cheat gripper extern cheatseq_t cheat_midas; // [STRIFE]: new cheat extern cheatseq_t cheat_lego; // [STRIFE]: new cheat extern cheatseq_t cheat_dev; // [STRIFE]: new cheat extern cheatseq_t cheat_powerup[]; #endif crispy-doom-crispy-doom-5.6.4/src/strife/wi_stuff.c000066400000000000000000001022471360717211000223170ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Intermission screens. // // haleyjd 08/23/2010: There is no intermission in Strife #if 0 #include #include "z_zone.h" #include "m_random.h" #include "deh_main.h" #include "i_swap.h" #include "i_system.h" #include "w_wad.h" #include "g_game.h" #include "r_local.h" #include "s_sound.h" #include "doomstat.h" // Data. #include "sounds.h" // Needs access to LFB. #include "v_video.h" #include "wi_stuff.h" // // Data needed to add patches to full screen intermission pics. // Patches are statistics messages, and animations. // Loads of by-pixel layout and placement, offsets etc. // // // Different vetween registered DOOM (1994) and // Ultimate DOOM - Final edition (retail, 1995?). // This is supposedly ignored for commercial // release (aka DOOM II), which had 34 maps // in one episode. So there. #define NUMEPISODES 4 #define NUMMAPS 9 // 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 // SINGPLE-PLAYER STUFF #define SP_STATSX 50 #define SP_STATSY 50 #define SP_TIMEX 16 #define SP_TIMEY (SCREENHEIGHT-32) // NET GAME STUFF #define NG_STATSY 50 #define NG_STATSX (32 + SHORT(star->width)/2 + 32*!dofrags) #define NG_SPACINGX 64 // DEATHMATCH STUFF #define DM_MATRIXX 42 #define DM_MATRIXY 68 #define DM_SPACINGX 40 #define DM_TOTALSX 269 #define DM_KILLERSX 10 #define DM_KILLERSY 100 #define DM_VICTIMSX 5 #define DM_VICTIMSY 50 typedef enum { ANIM_ALWAYS, ANIM_RANDOM, ANIM_LEVEL } animenum_t; typedef struct { int x; int y; } point_t; // // Animation. // There is another anim_t used in p_spec. // typedef struct { animenum_t type; // period in tics between animations int period; // number of animation frames int nanims; // location of animation point_t loc; // ALWAYS: n/a, // RANDOM: period deviation (<256), // LEVEL: level int data1; // ALWAYS: n/a, // RANDOM: random base period, // LEVEL: n/a int data2; // actual graphics for frames of animations patch_t* p[3]; // following must be initialized to zero before use! // next value of bcnt (used in conjunction with period) int nexttic; // last drawn animation frame int lastdrawn; // next frame number to animate int ctr; // used by RANDOM and LEVEL when animating int state; } anim_t; 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. // #define ANIM(type, period, nanims, x, y, nexttic) \ { (type), (period), (nanims), { (x), (y) }, (nexttic), \ 0, { NULL, NULL, NULL }, 0, 0, 0, 0 } static anim_t epsd0animinfo[] = { ANIM(ANIM_ALWAYS, TICRATE/3, 3, 224, 104, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 184, 160, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 112, 136, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 72, 112, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 88, 96, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 48, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 192, 40, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 136, 16, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 80, 16, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 24, 0), }; static anim_t epsd1animinfo[] = { ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 1), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 2), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 3), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 4), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 5), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 6), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 7), ANIM(ANIM_LEVEL, TICRATE/3, 3, 192, 144, 8), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 8), }; static anim_t epsd2animinfo[] = { ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 168, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 40, 136, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 160, 96, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 80, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 120, 32, 0), ANIM(ANIM_ALWAYS, TICRATE/4, 3, 40, 0, 0), }; static int NUMANIMS[NUMEPISODES] = { arrlen(epsd0animinfo), arrlen(epsd1animinfo), arrlen(epsd2animinfo), }; static anim_t *anims[NUMEPISODES] = { epsd0animinfo, epsd1animinfo, epsd2animinfo }; // // GENERAL DATA // // // Locally used stuff. // // States for single-player #define SP_KILLS 0 #define SP_ITEMS 2 #define SP_SECRET 4 #define SP_FRAGS 6 #define SP_TIME 8 #define SP_PAR ST_TIME #define SP_PAUSE 1 // in seconds #define SHOWNEXTLOCDELAY 4 //#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY // used to accelerate or skip a stage static int acceleratestage; // wbs->pnum static int me; // specifies current state static stateenum_t state; // contains information passed into intermission static wbstartstruct_t* wbs; static wbplayerstruct_t* plrs; // wbs->plyr[] // used for general timing static int cnt; // used for timing of background animation static int bcnt; // signals to refresh everything for one frame static int firstrefresh; static int cnt_kills[MAXPLAYERS]; static int cnt_items[MAXPLAYERS]; static int cnt_secret[MAXPLAYERS]; static int cnt_time; static int cnt_par; static int cnt_pause; // # of commercial levels static int NUMCMAPS; // // GRAPHICS // // You Are Here graphic static patch_t* yah[3] = { NULL, NULL, NULL }; // splat static patch_t* splat[2] = { NULL, NULL }; // %, : graphics static patch_t* percent; static patch_t* colon; // 0-9 graphic static patch_t* num[10]; // minus sign static patch_t* wiminus; // "Finished!" graphics static patch_t* finished; // "Entering" graphic static patch_t* entering; // "secret" static patch_t* sp_secret; // "Kills", "Scrt", "Items", "Frags" static patch_t* kills; static patch_t* secret; static patch_t* items; static patch_t* frags; // Time sucks. static patch_t* timepatch; static patch_t* par; static patch_t* sucks; // "killers", "victims" static patch_t* killers; static patch_t* victims; // "Total", your face, your dead face static patch_t* total; static patch_t* star; static patch_t* bstar; // "red P[1..MAXPLAYERS]" static patch_t* p[MAXPLAYERS]; // "gray P[1..MAXPLAYERS]" static patch_t* bp[MAXPLAYERS]; // Name graphics of each level (centered) static patch_t** lnames; // Buffer storing the backdrop static patch_t *background; // // CODE // // slam background void WI_slamBackground(void) { V_DrawPatch(0, 0, background); } // The ticker is used to detect keys // because of timing issues in netgames. boolean WI_Responder(event_t* ev) { return false; } // Draws " Finished!" void WI_drawLF(void) { int y = WI_TITLEY; if (gamemode != commercial || wbs->last < NUMCMAPS) { // draw V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->last]->width))/2, y, lnames[wbs->last]); // draw "Finished!" y += (5*SHORT(lnames[wbs->last]->height))/4; V_DrawPatch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished); } else if (wbs->last == NUMCMAPS) { // MAP33 - nothing is displayed! } else if (wbs->last > NUMCMAPS) { // > MAP33. Doom bombs out here with a Bad V_DrawPatch error. // I'm pretty sure that doom2.exe is just reading into random // bits of memory at this point, but let's try to be accurate // anyway. This deliberately triggers a V_DrawPatch error. patch_t tmp = { SCREENWIDTH, SCREENHEIGHT, 1, 1, { 0, 0, 0, 0, 0, 0, 0, 0 } }; V_DrawPatch(0, y, &tmp); } } // Draws "Entering " void WI_drawEL(void) { int y = WI_TITLEY; // draw "Entering" V_DrawPatch((SCREENWIDTH - SHORT(entering->width))/2, y, entering); // draw level y += (5*SHORT(lnames[wbs->next]->height))/4; V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->next]->width))/2, y, lnames[wbs->next]); } void WI_drawOnLnode ( int n, patch_t* c[] ) { int i; int left; int top; int right; int bottom; boolean fits = false; i = 0; do { left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset); top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset); right = left + SHORT(c[i]->width); bottom = top + SHORT(c[i]->height); if (left >= 0 && right < SCREENWIDTH && top >= 0 && bottom < SCREENHEIGHT) { fits = true; } else { i++; } } while (!fits && i!=2 && c[i] != NULL); if (fits && i<2) { V_DrawPatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, c[i]); } else { // DEBUG printf("Could not place patch on level %d", n+1); } } void WI_initAnimatedBack(void) { int i; anim_t* a; if (gamemode == commercial) return; if (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; } } void WI_updateAnimatedBack(void) { int i; anim_t* a; if (gamemode == commercial) return; if (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; } } } } void WI_drawAnimatedBack(void) { int i; anim_t* a; if (gamemode == commercial) return; if (wbs->epsd > 2) return; for (i=0 ; iepsd] ; i++) { a = &anims[wbs->epsd][i]; if (a->ctr >= 0) V_DrawPatch(a->loc.x, a->loc.y, a->p[a->ctr]); } } // // Draws a number. // If digits > 0, then use that many digits minimum, // otherwise only use as many as necessary. // Returns new x position. // int WI_drawNum ( int x, int y, int n, int digits ) { int fontwidth = SHORT(num[0]->width); int neg; int temp; if (digits < 0) { if (!n) { // make variable-length zeros 1 digit long digits = 1; } else { // figure out # of digits in # digits = 0; temp = n; while (temp) { temp /= 10; digits++; } } } neg = n < 0; if (neg) n = -n; // if non-number, do not draw it if (n == 1994) return 0; // draw the new number while (digits--) { x -= fontwidth; V_DrawPatch(x, y, num[ n % 10 ]); n /= 10; } // draw a minus sign if necessary if (neg) V_DrawPatch(x-=8, y, wiminus); return x; } void WI_drawPercent ( int x, int y, int p ) { if (p < 0) return; V_DrawPatch(x, y, percent); WI_drawNum(x, y, p, -1); } // // Display level completion time and par, // or "sucks" message if overflow. // void WI_drawTime ( int x, int y, int t ) { int div; int n; if (t<0) return; if (t <= 61*59) { div = 1; do { n = (t / div) % 60; x = WI_drawNum(x, y, n, 2) - SHORT(colon->width); div *= 60; // draw if (div==60 || t / div) V_DrawPatch(x, y, colon); } while (t / div); } else { // "sucks" V_DrawPatch(x - SHORT(sucks->width), y, sucks); } } void WI_End(void) { void WI_unloadData(void); WI_unloadData(); } void WI_initNoState(void) { state = NoState; acceleratestage = 0; cnt = 10; } void WI_updateNoState(void) { WI_updateAnimatedBack(); if (!--cnt) { // Don't call WI_End yet. G_WorldDone doesnt immediately // change gamestate, so WI_Drawer is still going to get // run until that happens. If we do that after WI_End // (which unloads all the graphics), we're in trouble. //WI_End(); G_WorldDone(); } } static boolean snl_pointeron = false; void WI_initShowNextLoc(void) { state = ShowNextLoc; acceleratestage = 0; cnt = SHOWNEXTLOCDELAY * TICRATE; WI_initAnimatedBack(); } void WI_updateShowNextLoc(void) { WI_updateAnimatedBack(); if (!--cnt || acceleratestage) WI_initNoState(); else snl_pointeron = (cnt & 31) < 20; } void WI_drawShowNextLoc(void) { int i; int last; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); if ( gamemode != commercial) { if (wbs->epsd > 2) { WI_drawEL(); return; } last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; // draw a splat on taken cities. for (i=0 ; i<=last ; i++) WI_drawOnLnode(i, splat); // splat the secret level? if (wbs->didsecret) WI_drawOnLnode(8, splat); // draw flashing ptr if (snl_pointeron) WI_drawOnLnode(wbs->next, yah); } // draws which level you are entering.. if ( (gamemode != commercial) || wbs->next != 30) WI_drawEL(); } void WI_drawNoState(void) { snl_pointeron = true; WI_drawShowNextLoc(); } int WI_fragSum(int playernum) { int i; int frags = 0; for (i=0 ; i 99) dm_frags[i][j] = 99; if (dm_frags[i][j] < -99) dm_frags[i][j] = -99; stillticking = true; } } dm_totals[i] = WI_fragSum(i); if (dm_totals[i] > 99) dm_totals[i] = 99; if (dm_totals[i] < -99) dm_totals[i] = -99; } } if (!stillticking) { S_StartSound(0, sfx_barexp); dm_state++; } } else if (dm_state == 4) { if (acceleratestage) { S_StartSound(0, sfx_slop); if ( gamemode == commercial) WI_initNoState(); else WI_initShowNextLoc(); } } else if (dm_state & 1) { if (!--cnt_pause) { dm_state++; cnt_pause = TICRATE; } } } void WI_drawDeathmatchStats(void) { int i; int j; int x; int y; int w; int lh; // line height lh = WI_SPACINGY; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); // draw stat titles (top line) V_DrawPatch(DM_TOTALSX-SHORT(total->width)/2, DM_MATRIXY-WI_SPACINGY+10, total); V_DrawPatch(DM_KILLERSX, DM_KILLERSY, killers); V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, victims); // draw P? x = DM_MATRIXX + DM_SPACINGX; y = DM_MATRIXY; for (i=0 ; iwidth)/2, DM_MATRIXY - WI_SPACINGY, p[i]); V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2, y, p[i]); if (i == me) { V_DrawPatch(x-SHORT(p[i]->width)/2, DM_MATRIXY - WI_SPACINGY, bstar); V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2, y, star); } } else { // V_DrawPatch(x-SHORT(bp[i]->width)/2, // DM_MATRIXY - WI_SPACINGY, bp[i]); // V_DrawPatch(DM_MATRIXX-SHORT(bp[i]->width)/2, // y, bp[i]); } x += DM_SPACINGX; y += WI_SPACINGY; } // draw stats y = DM_MATRIXY+10; w = SHORT(num[0]->width); for (i=0 ; imaxkills; cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; if (dofrags) cnt_frags[i] = WI_fragSum(i); } S_StartSound(0, sfx_barexp); ng_state = 10; } if (ng_state == 2) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); stillticking = false; for (i=0 ; i= (plrs[i].skills * 100) / wbs->maxkills) cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; else stillticking = true; } if (!stillticking) { S_StartSound(0, sfx_barexp); ng_state++; } } else if (ng_state == 4) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); stillticking = false; for (i=0 ; i= (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= (plrs[i].ssecret * 100) / wbs->maxsecret) cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; else stillticking = true; } if (!stillticking) { S_StartSound(0, sfx_barexp); ng_state += 1 + 2*!dofrags; } } else if (ng_state == 8) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); stillticking = false; for (i=0 ; i= (fsum = WI_fragSum(i))) cnt_frags[i] = fsum; else stillticking = true; } if (!stillticking) { S_StartSound(0, sfx_pldeth); ng_state++; } } else if (ng_state == 10) { if (acceleratestage) { S_StartSound(0, sfx_sgcock); if ( gamemode == commercial ) WI_initNoState(); else WI_initShowNextLoc(); } } else if (ng_state & 1) { if (!--cnt_pause) { ng_state++; cnt_pause = TICRATE; } } } void WI_drawNetgameStats(void) { int i; int x; int y; int pwidth = SHORT(percent->width); WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); // draw stat titles (top line) V_DrawPatch(NG_STATSX+NG_SPACINGX-SHORT(kills->width), NG_STATSY, kills); V_DrawPatch(NG_STATSX+2*NG_SPACINGX-SHORT(items->width), NG_STATSY, items); V_DrawPatch(NG_STATSX+3*NG_SPACINGX-SHORT(secret->width), NG_STATSY, secret); if (dofrags) V_DrawPatch(NG_STATSX+4*NG_SPACINGX-SHORT(frags->width), NG_STATSY, frags); // draw stats y = NG_STATSY + SHORT(kills->height); for (i=0 ; iwidth), y, p[i]); if (i == me) V_DrawPatch(x-SHORT(p[i]->width), y, star); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_kills[i]); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_items[i]); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_secret[i]); x += NG_SPACINGX; if (dofrags) WI_drawNum(x, y+10, cnt_frags[i], -1); y += WI_SPACINGY; } } static int sp_state; void WI_initStats(void) { state = StatCount; acceleratestage = 0; sp_state = 1; cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; cnt_time = cnt_par = -1; cnt_pause = TICRATE; WI_initAnimatedBack(); } void WI_updateStats(void) { WI_updateAnimatedBack(); if (acceleratestage && sp_state != 10) { acceleratestage = 0; cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; cnt_time = plrs[me].stime / TICRATE; cnt_par = wbs->partime / TICRATE; S_StartSound(0, sfx_barexp); sp_state = 10; } if (sp_state == 2) { cnt_kills[0] += 2; if (!(bcnt&3)) S_StartSound(0, sfx_pistol); if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) { cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; S_StartSound(0, sfx_barexp); sp_state++; } } else if (sp_state == 4) { cnt_items[0] += 2; if (!(bcnt&3)) S_StartSound(0, sfx_pistol); if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) { cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; S_StartSound(0, sfx_barexp); sp_state++; } } else if (sp_state == 6) { cnt_secret[0] += 2; if (!(bcnt&3)) S_StartSound(0, sfx_pistol); if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret) { cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; S_StartSound(0, sfx_barexp); sp_state++; } } else if (sp_state == 8) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); cnt_time += 3; if (cnt_time >= plrs[me].stime / TICRATE) cnt_time = plrs[me].stime / TICRATE; cnt_par += 3; if (cnt_par >= wbs->partime / TICRATE) { cnt_par = wbs->partime / TICRATE; if (cnt_time >= plrs[me].stime / TICRATE) { S_StartSound(0, sfx_barexp); sp_state++; } } } else if (sp_state == 10) { if (acceleratestage) { S_StartSound(0, sfx_sgcock); if (gamemode == commercial) WI_initNoState(); else WI_initShowNextLoc(); } } else if (sp_state & 1) { if (!--cnt_pause) { sp_state++; cnt_pause = TICRATE; } } } void WI_drawStats(void) { // line height int lh; lh = (3*SHORT(num[0]->height))/2; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); V_DrawPatch(SP_STATSX, SP_STATSY, kills); WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); V_DrawPatch(SP_STATSX, SP_STATSY+lh, items); WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]); V_DrawPatch(SP_STATSX, SP_STATSY+2*lh, sp_secret); WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]); V_DrawPatch(SP_TIMEX, SP_TIMEY, timepatch); WI_drawTime(SCREENWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time); if (wbs->epsd < 3) { V_DrawPatch(SCREENWIDTH/2 + SP_TIMEX, SP_TIMEY, par); WI_drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par); } } void WI_checkForAccelerate(void) { int i; player_t *player; // check for button presses to skip delays for (i=0, player = players ; 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; } } } // Updates stuff each tick void WI_Ticker(void) { // counter for general background animation bcnt++; if (bcnt == 1) { // intermission music if ( gamemode == commercial ) S_ChangeMusic(mus_dm2int, true); else S_ChangeMusic(mus_inter, true); } WI_checkForAccelerate(); switch (state) { case StatCount: if (deathmatch) WI_updateDeathmatchStats(); else if (netgame) WI_updateNetgameStats(); else WI_updateStats(); break; case ShowNextLoc: WI_updateShowNextLoc(); break; case NoState: WI_updateNoState(); break; } } typedef void (*load_callback_t)(char *lumpname, patch_t **variable); // Common load/unload function. Iterates over all the graphics // lumps to be loaded/unloaded into memory. static void WI_loadUnloadData(load_callback_t callback) { int i, j; char name[9]; anim_t *a; if (gamemode == commercial) { for (i=0 ; iepsd, i); callback(name, &lnames[i]); } // you are here callback(DEH_String("WIURH0"), &yah[0]); // you are here (alt.) callback(DEH_String("WIURH1"), &yah[1]); // splat callback(DEH_String("WISPLAT"), &splat[0]); 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 DEH_snprintf(name, 9, "WIA%d%.2d%.2d", wbs->epsd, j, i); callback(name, &a->p[i]); } else { // HACK ALERT! a->p[i] = anims[1][4].p[i]; } } } } } // More hacks on minus sign. callback(DEH_String("WIMINUS"), &wiminus); for (i=0;i<10;i++) { // numbers 0-9 DEH_snprintf(name, 9, "WINUM%d", i); callback(name, &num[i]); } // percent sign callback(DEH_String("WIPCNT"), &percent); // "finished" callback(DEH_String("WIF"), &finished); // "entering" callback(DEH_String("WIENTER"), &entering); // "kills" callback(DEH_String("WIOSTK"), &kills); // "scrt" callback(DEH_String("WIOSTS"), &secret); // "secret" callback(DEH_String("WISCRT2"), &sp_secret); // french wad uses WIOBJ (?) if (W_CheckNumForName(DEH_String("WIOBJ")) >= 0) { // "items" if (netgame && !deathmatch) callback(DEH_String("WIOBJ"), &items); else callback(DEH_String("WIOSTI"), &items); } else { callback(DEH_String("WIOSTI"), &items); } // "frgs" callback(DEH_String("WIFRGS"), &frags); // ":" callback(DEH_String("WICOLON"), &colon); // "time" callback(DEH_String("WITIME"), &timepatch); // "sucks" callback(DEH_String("WISUCKS"), &sucks); // "par" callback(DEH_String("WIPAR"), &par); // "killers" (vertical) callback(DEH_String("WIKILRS"), &killers); // "victims" (horiz) callback(DEH_String("WIVCTMS"), &victims); // "total" callback(DEH_String("WIMSTT"), &total); for (i=0 ; iepsd == 3) { M_StringCopy(name, DEH_String("INTERPIC"), sizeof(name)); name[8] = '\0'; } else { DEH_snprintf(name, 9, "WIMAP%d", wbs->epsd); } // Draw backdrop and save to a temporary buffer callback(name, &background); } static void WI_loadCallback(char *name, patch_t **variable) { *variable = W_CacheLumpName(name, PU_STATIC); } void WI_loadData(void) { if (gamemode == commercial) { NUMCMAPS = 32; lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS, PU_STATIC, NULL); } else { lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMMAPS, PU_STATIC, NULL); } WI_loadUnloadData(WI_loadCallback); // These two graphics are special cased because we're sharing // them with the status bar code // your face star = W_CacheLumpName(DEH_String("STFST01"), PU_STATIC); // dead face bstar = W_CacheLumpName(DEH_String("STFDEAD0"), PU_STATIC); } static void WI_unloadCallback(char *name, patch_t **variable) { W_ReleaseLumpName(name); *variable = NULL; } void WI_unloadData(void) { WI_loadUnloadData(WI_unloadCallback); // We do not free these lumps as they are shared with the status // bar code. // W_ReleaseLumpName("STFST01"); // W_ReleaseLumpName("STFDEAD0"); } void WI_Drawer (void) { switch (state) { case StatCount: if (deathmatch) WI_drawDeathmatchStats(); else if (netgame) WI_drawNetgameStats(); else WI_drawStats(); break; case ShowNextLoc: WI_drawShowNextLoc(); break; case NoState: WI_drawNoState(); break; } } void WI_initVariables(wbstartstruct_t* wbstartstruct) { wbs = wbstartstruct; #ifdef RANGECHECKING if (gamemode != commercial) { if ( gamemode == retail ) RNGCHECK(wbs->epsd, 0, 3); else RNGCHECK(wbs->epsd, 0, 2); } else { RNGCHECK(wbs->last, 0, 8); RNGCHECK(wbs->next, 0, 8); } RNGCHECK(wbs->pnum, 0, MAXPLAYERS); RNGCHECK(wbs->pnum, 0, MAXPLAYERS); #endif acceleratestage = 0; cnt = bcnt = 0; firstrefresh = 1; me = wbs->pnum; plrs = wbs->plyr; if (!wbs->maxkills) wbs->maxkills = 1; if (!wbs->maxitems) wbs->maxitems = 1; if (!wbs->maxsecret) wbs->maxsecret = 1; if ( gamemode != retail ) if (wbs->epsd > 2) wbs->epsd -= 3; } void WI_Start(wbstartstruct_t* wbstartstruct) { WI_initVariables(wbstartstruct); WI_loadData(); if (deathmatch) WI_initDeathmatchStats(); else if (netgame) WI_initNetgameStats(); else WI_initStats(); } #endif crispy-doom-crispy-doom-5.6.4/src/strife/wi_stuff.h000066400000000000000000000023251360717211000223200ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Intermission. // #ifndef __WI_STUFF__ #define __WI_STUFF__ // haleyjd 08/23/2010: Strife does not have an intermission #if 0 //#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); // Shut down the intermission screen void WI_End(void); #endif #endif crispy-doom-crispy-doom-5.6.4/src/tables.c000066400000000000000000003770311360717211000204540ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Lookup tables. // Do not try to look them up :-). // In the order of appearance: // // int finetangent[4096] - Tangens LUT. // Should work with BAM fairly well (12 of 16bit, // effectively, by shifting). // // int finesine[10240] - Sine lookup. // Guess what, serves as cosine, too. // Remarkable thing is, how to use BAMs with this? // // int tantoangle[2049] - ArcTan LUT, // maps tan(angle) to angle fast. Gotta search. // // #include "tables.h" // to get a global angle from cartesian coordinates, the coordinates are // flipped until they are in the first octant of the coordinate system, then // the y (<=x) is scaled and divided by x to get a tangent (slope) value // which is looked up in the tantoangle[] table. The +1 size is to handle // the case when x==y without additional checking. int SlopeDiv(unsigned int num, unsigned int den) { unsigned ans; if (den < 512) { return SLOPERANGE; } else { ans = (num << 3) / (den >> 8); if (ans <= SLOPERANGE) { return ans; } else { return SLOPERANGE; } } } // [crispy] catch SlopeDiv overflows, only used in rendering int SlopeDivCrispy(unsigned int num, unsigned int den) { if (den < 512) { return SLOPERANGE; } else { uint64_t ans = ((uint64_t) num << 3) / (den >> 8); if (ans <= SLOPERANGE) { return (int) ans; } else { return SLOPERANGE; } } } const fixed_t finetangent[4096] = { -170910304,-56965752,-34178904,-24413316,-18988036,-15535599,-13145455,-11392683, -10052327,-8994149,-8137527,-7429880,-6835455,-6329090,-5892567,-5512368, -5178251,-4882318,-4618375,-4381502,-4167737,-3973855,-3797206,-3635590, -3487165,-3350381,-3223918,-3106651,-2997613,-2895966,-2800983,-2712030, -2628549,-2550052,-2476104,-2406322,-2340362,-2277919,-2218719,-2162516, -2109087,-2058233,-2009771,-1963536,-1919378,-1877161,-1836758,-1798063, -1760956,-1725348,-1691149,-1658278,-1626658,-1596220,-1566898,-1538632, -1511367,-1485049,-1459630,-1435065,-1411312,-1388330,-1366084,-1344537, -1323658,-1303416,-1283783,-1264730,-1246234,-1228269,-1210813,-1193846, -1177345,-1161294,-1145673,-1130465,-1115654,-1101225,-1087164,-1073455, -1060087,-1047046,-1034322,-1021901,-1009774,-997931,-986361,-975054, -964003,-953199,-942633,-932298,-922186,-912289,-902602,-893117, -883829,-874730,-865817,-857081,-848520,-840127,-831898,-823827, -815910,-808143,-800521,-793041,-785699,-778490,-771411,-764460, -757631,-750922,-744331,-737853,-731486,-725227,-719074,-713023, -707072,-701219,-695462,-689797,-684223,-678737,-673338,-668024, -662792,-657640,-652568,-647572,-642651,-637803,-633028,-628323, -623686,-619117,-614613,-610174,-605798,-601483,-597229,-593033, -588896,-584815,-580789,-576818,-572901,-569035,-565221,-561456, -557741,-554074,-550455,-546881,-543354,-539870,-536431,-533034, -529680,-526366,-523094,-519861,-516667,-513512,-510394,-507313, -504269,-501261,-498287,-495348,-492443,-489571,-486732,-483925, -481150,-478406,-475692,-473009,-470355,-467730,-465133,-462565, -460024,-457511,-455024,-452564,-450129,-447720,-445337,-442978, -440643,-438332,-436045,-433781,-431540,-429321,-427125,-424951, -422798,-420666,-418555,-416465,-414395,-412344,-410314,-408303, -406311,-404338,-402384,-400448,-398530,-396630,-394747,-392882, -391034,-389202,-387387,-385589,-383807,-382040,-380290,-378555, -376835,-375130,-373440,-371765,-370105,-368459,-366826,-365208, -363604,-362013,-360436,-358872,-357321,-355783,-354257,-352744, -351244,-349756,-348280,-346816,-345364,-343924,-342495,-341078, -339671,-338276,-336892,-335519,-334157,-332805,-331464,-330133, -328812,-327502,-326201,-324910,-323629,-322358,-321097,-319844, -318601,-317368,-316143,-314928,-313721,-312524,-311335,-310154, -308983,-307819,-306664,-305517,-304379,-303248,-302126,-301011, -299904,-298805,-297714,-296630,-295554,-294485,-293423,-292369, -291322,-290282,-289249,-288223,-287204,-286192,-285186,-284188, -283195,-282210,-281231,-280258,-279292,-278332,-277378,-276430, -275489,-274553,-273624,-272700,-271782,-270871,-269965,-269064, -268169,-267280,-266397,-265519,-264646,-263779,-262917,-262060, -261209,-260363,-259522,-258686,-257855,-257029,-256208,-255392, -254581,-253774,-252973,-252176,-251384,-250596,-249813,-249035, -248261,-247492,-246727,-245966,-245210,-244458,-243711,-242967, -242228,-241493,-240763,-240036,-239314,-238595,-237881,-237170, -236463,-235761,-235062,-234367,-233676,-232988,-232304,-231624, -230948,-230275,-229606,-228941,-228279,-227621,-226966,-226314, -225666,-225022,-224381,-223743,-223108,-222477,-221849,-221225, -220603,-219985,-219370,-218758,-218149,-217544,-216941,-216341, -215745,-215151,-214561,-213973,-213389,-212807,-212228,-211652, -211079,-210509,-209941,-209376,-208815,-208255,-207699,-207145, -206594,-206045,-205500,-204956,-204416,-203878,-203342,-202809, -202279,-201751,-201226,-200703,-200182,-199664,-199149,-198636, -198125,-197616,-197110,-196606,-196105,-195606,-195109,-194614, -194122,-193631,-193143,-192658,-192174,-191693,-191213,-190736, -190261,-189789,-189318,-188849,-188382,-187918,-187455,-186995, -186536,-186080,-185625,-185173,-184722,-184274,-183827,-183382, -182939,-182498,-182059,-181622,-181186,-180753,-180321,-179891, -179463,-179037,-178612,-178190,-177769,-177349,-176932,-176516, -176102,-175690,-175279,-174870,-174463,-174057,-173653,-173251, -172850,-172451,-172053,-171657,-171263,-170870,-170479,-170089, -169701,-169315,-168930,-168546,-168164,-167784,-167405,-167027, -166651,-166277,-165904,-165532,-165162,-164793,-164426,-164060, -163695,-163332,-162970,-162610,-162251,-161893,-161537,-161182, -160828,-160476,-160125,-159775,-159427,-159079,-158734,-158389, -158046,-157704,-157363,-157024,-156686,-156349,-156013,-155678, -155345,-155013,-154682,-154352,-154024,-153697,-153370,-153045, -152722,-152399,-152077,-151757,-151438,-151120,-150803,-150487, -150172,-149859,-149546,-149235,-148924,-148615,-148307,-148000, -147693,-147388,-147084,-146782,-146480,-146179,-145879,-145580, -145282,-144986,-144690,-144395,-144101,-143808,-143517,-143226, -142936,-142647,-142359,-142072,-141786,-141501,-141217,-140934, -140651,-140370,-140090,-139810,-139532,-139254,-138977,-138701, -138426,-138152,-137879,-137607,-137335,-137065,-136795,-136526, -136258,-135991,-135725,-135459,-135195,-134931,-134668,-134406, -134145,-133884,-133625,-133366,-133108,-132851,-132594,-132339, -132084,-131830,-131576,-131324,-131072,-130821,-130571,-130322, -130073,-129825,-129578,-129332,-129086,-128841,-128597,-128353, -128111,-127869,-127627,-127387,-127147,-126908,-126669,-126432, -126195,-125959,-125723,-125488,-125254,-125020,-124787,-124555, -124324,-124093,-123863,-123633,-123404,-123176,-122949,-122722, -122496,-122270,-122045,-121821,-121597,-121374,-121152,-120930, -120709,-120489,-120269,-120050,-119831,-119613,-119396,-119179, -118963,-118747,-118532,-118318,-118104,-117891,-117678,-117466, -117254,-117044,-116833,-116623,-116414,-116206,-115998,-115790, -115583,-115377,-115171,-114966,-114761,-114557,-114354,-114151, -113948,-113746,-113545,-113344,-113143,-112944,-112744,-112546, -112347,-112150,-111952,-111756,-111560,-111364,-111169,-110974, -110780,-110586,-110393,-110200,-110008,-109817,-109626,-109435, -109245,-109055,-108866,-108677,-108489,-108301,-108114,-107927, -107741,-107555,-107369,-107184,-107000,-106816,-106632,-106449, -106266,-106084,-105902,-105721,-105540,-105360,-105180,-105000, -104821,-104643,-104465,-104287,-104109,-103933,-103756,-103580, -103404,-103229,-103054,-102880,-102706,-102533,-102360,-102187, -102015,-101843,-101671,-101500,-101330,-101159,-100990,-100820, -100651,-100482,-100314,-100146,-99979,-99812,-99645,-99479, -99313,-99148,-98982,-98818,-98653,-98489,-98326,-98163, -98000,-97837,-97675,-97513,-97352,-97191,-97030,-96870, -96710,-96551,-96391,-96233,-96074,-95916,-95758,-95601, -95444,-95287,-95131,-94975,-94819,-94664,-94509,-94354, -94200,-94046,-93892,-93739,-93586,-93434,-93281,-93129, -92978,-92826,-92675,-92525,-92375,-92225,-92075,-91926, -91777,-91628,-91480,-91332,-91184,-91036,-90889,-90742, -90596,-90450,-90304,-90158,-90013,-89868,-89724,-89579, -89435,-89292,-89148,-89005,-88862,-88720,-88577,-88435, -88294,-88152,-88011,-87871,-87730,-87590,-87450,-87310, -87171,-87032,-86893,-86755,-86616,-86479,-86341,-86204, -86066,-85930,-85793,-85657,-85521,-85385,-85250,-85114, -84980,-84845,-84710,-84576,-84443,-84309,-84176,-84043, -83910,-83777,-83645,-83513,-83381,-83250,-83118,-82987, -82857,-82726,-82596,-82466,-82336,-82207,-82078,-81949, -81820,-81691,-81563,-81435,-81307,-81180,-81053,-80925, -80799,-80672,-80546,-80420,-80294,-80168,-80043,-79918, -79793,-79668,-79544,-79420,-79296,-79172,-79048,-78925, -78802,-78679,-78557,-78434,-78312,-78190,-78068,-77947, -77826,-77705,-77584,-77463,-77343,-77223,-77103,-76983, -76864,-76744,-76625,-76506,-76388,-76269,-76151,-76033, -75915,-75797,-75680,-75563,-75446,-75329,-75213,-75096, -74980,-74864,-74748,-74633,-74517,-74402,-74287,-74172, -74058,-73944,-73829,-73715,-73602,-73488,-73375,-73262, -73149,-73036,-72923,-72811,-72699,-72587,-72475,-72363, -72252,-72140,-72029,-71918,-71808,-71697,-71587,-71477, -71367,-71257,-71147,-71038,-70929,-70820,-70711,-70602, -70494,-70385,-70277,-70169,-70061,-69954,-69846,-69739, -69632,-69525,-69418,-69312,-69205,-69099,-68993,-68887, -68781,-68676,-68570,-68465,-68360,-68255,-68151,-68046, -67942,-67837,-67733,-67629,-67526,-67422,-67319,-67216, -67113,-67010,-66907,-66804,-66702,-66600,-66498,-66396, -66294,-66192,-66091,-65989,-65888,-65787,-65686,-65586, -65485,-65385,-65285,-65185,-65085,-64985,-64885,-64786, -64687,-64587,-64488,-64389,-64291,-64192,-64094,-63996, -63897,-63799,-63702,-63604,-63506,-63409,-63312,-63215, -63118,-63021,-62924,-62828,-62731,-62635,-62539,-62443, -62347,-62251,-62156,-62060,-61965,-61870,-61775,-61680, -61585,-61491,-61396,-61302,-61208,-61114,-61020,-60926, -60833,-60739,-60646,-60552,-60459,-60366,-60273,-60181, -60088,-59996,-59903,-59811,-59719,-59627,-59535,-59444, -59352,-59261,-59169,-59078,-58987,-58896,-58805,-58715, -58624,-58534,-58443,-58353,-58263,-58173,-58083,-57994, -57904,-57815,-57725,-57636,-57547,-57458,-57369,-57281, -57192,-57104,-57015,-56927,-56839,-56751,-56663,-56575, -56487,-56400,-56312,-56225,-56138,-56051,-55964,-55877, -55790,-55704,-55617,-55531,-55444,-55358,-55272,-55186, -55100,-55015,-54929,-54843,-54758,-54673,-54587,-54502, -54417,-54333,-54248,-54163,-54079,-53994,-53910,-53826, -53741,-53657,-53574,-53490,-53406,-53322,-53239,-53156, -53072,-52989,-52906,-52823,-52740,-52657,-52575,-52492, -52410,-52327,-52245,-52163,-52081,-51999,-51917,-51835, -51754,-51672,-51591,-51509,-51428,-51347,-51266,-51185, -51104,-51023,-50942,-50862,-50781,-50701,-50621,-50540, -50460,-50380,-50300,-50221,-50141,-50061,-49982,-49902, -49823,-49744,-49664,-49585,-49506,-49427,-49349,-49270, -49191,-49113,-49034,-48956,-48878,-48799,-48721,-48643, -48565,-48488,-48410,-48332,-48255,-48177,-48100,-48022, -47945,-47868,-47791,-47714,-47637,-47560,-47484,-47407, -47331,-47254,-47178,-47102,-47025,-46949,-46873,-46797, -46721,-46646,-46570,-46494,-46419,-46343,-46268,-46193, -46118,-46042,-45967,-45892,-45818,-45743,-45668,-45593, -45519,-45444,-45370,-45296,-45221,-45147,-45073,-44999, -44925,-44851,-44778,-44704,-44630,-44557,-44483,-44410, -44337,-44263,-44190,-44117,-44044,-43971,-43898,-43826, -43753,-43680,-43608,-43535,-43463,-43390,-43318,-43246, -43174,-43102,-43030,-42958,-42886,-42814,-42743,-42671, -42600,-42528,-42457,-42385,-42314,-42243,-42172,-42101, -42030,-41959,-41888,-41817,-41747,-41676,-41605,-41535, -41465,-41394,-41324,-41254,-41184,-41113,-41043,-40973, -40904,-40834,-40764,-40694,-40625,-40555,-40486,-40416, -40347,-40278,-40208,-40139,-40070,-40001,-39932,-39863, -39794,-39726,-39657,-39588,-39520,-39451,-39383,-39314, -39246,-39178,-39110,-39042,-38973,-38905,-38837,-38770, -38702,-38634,-38566,-38499,-38431,-38364,-38296,-38229, -38161,-38094,-38027,-37960,-37893,-37826,-37759,-37692, -37625,-37558,-37491,-37425,-37358,-37291,-37225,-37158, -37092,-37026,-36959,-36893,-36827,-36761,-36695,-36629, -36563,-36497,-36431,-36365,-36300,-36234,-36168,-36103, -36037,-35972,-35907,-35841,-35776,-35711,-35646,-35580, -35515,-35450,-35385,-35321,-35256,-35191,-35126,-35062, -34997,-34932,-34868,-34803,-34739,-34675,-34610,-34546, -34482,-34418,-34354,-34289,-34225,-34162,-34098,-34034, -33970,-33906,-33843,-33779,-33715,-33652,-33588,-33525, -33461,-33398,-33335,-33272,-33208,-33145,-33082,-33019, -32956,-32893,-32830,-32767,-32705,-32642,-32579,-32516, -32454,-32391,-32329,-32266,-32204,-32141,-32079,-32017, -31955,-31892,-31830,-31768,-31706,-31644,-31582,-31520, -31458,-31396,-31335,-31273,-31211,-31150,-31088,-31026, -30965,-30904,-30842,-30781,-30719,-30658,-30597,-30536, -30474,-30413,-30352,-30291,-30230,-30169,-30108,-30048, -29987,-29926,-29865,-29805,-29744,-29683,-29623,-29562, -29502,-29441,-29381,-29321,-29260,-29200,-29140,-29080, -29020,-28959,-28899,-28839,-28779,-28719,-28660,-28600, -28540,-28480,-28420,-28361,-28301,-28241,-28182,-28122, -28063,-28003,-27944,-27884,-27825,-27766,-27707,-27647, -27588,-27529,-27470,-27411,-27352,-27293,-27234,-27175, -27116,-27057,-26998,-26940,-26881,-26822,-26763,-26705, -26646,-26588,-26529,-26471,-26412,-26354,-26295,-26237, -26179,-26120,-26062,-26004,-25946,-25888,-25830,-25772, -25714,-25656,-25598,-25540,-25482,-25424,-25366,-25308, -25251,-25193,-25135,-25078,-25020,-24962,-24905,-24847, -24790,-24732,-24675,-24618,-24560,-24503,-24446,-24389, -24331,-24274,-24217,-24160,-24103,-24046,-23989,-23932, -23875,-23818,-23761,-23704,-23647,-23591,-23534,-23477, -23420,-23364,-23307,-23250,-23194,-23137,-23081,-23024, -22968,-22911,-22855,-22799,-22742,-22686,-22630,-22573, -22517,-22461,-22405,-22349,-22293,-22237,-22181,-22125, -22069,-22013,-21957,-21901,-21845,-21789,-21733,-21678, -21622,-21566,-21510,-21455,-21399,-21343,-21288,-21232, -21177,-21121,-21066,-21010,-20955,-20900,-20844,-20789, -20734,-20678,-20623,-20568,-20513,-20457,-20402,-20347, -20292,-20237,-20182,-20127,-20072,-20017,-19962,-19907, -19852,-19797,-19742,-19688,-19633,-19578,-19523,-19469, -19414,-19359,-19305,-19250,-19195,-19141,-19086,-19032, -18977,-18923,-18868,-18814,-18760,-18705,-18651,-18597, -18542,-18488,-18434,-18380,-18325,-18271,-18217,-18163, -18109,-18055,-18001,-17946,-17892,-17838,-17784,-17731, -17677,-17623,-17569,-17515,-17461,-17407,-17353,-17300, -17246,-17192,-17138,-17085,-17031,-16977,-16924,-16870, -16817,-16763,-16710,-16656,-16603,-16549,-16496,-16442, -16389,-16335,-16282,-16229,-16175,-16122,-16069,-16015, -15962,-15909,-15856,-15802,-15749,-15696,-15643,-15590, -15537,-15484,-15431,-15378,-15325,-15272,-15219,-15166, -15113,-15060,-15007,-14954,-14901,-14848,-14795,-14743, -14690,-14637,-14584,-14531,-14479,-14426,-14373,-14321, -14268,-14215,-14163,-14110,-14057,-14005,-13952,-13900, -13847,-13795,-13742,-13690,-13637,-13585,-13533,-13480, -13428,-13375,-13323,-13271,-13218,-13166,-13114,-13062, -13009,-12957,-12905,-12853,-12800,-12748,-12696,-12644, -12592,-12540,-12488,-12436,-12383,-12331,-12279,-12227, -12175,-12123,-12071,-12019,-11967,-11916,-11864,-11812, -11760,-11708,-11656,-11604,-11552,-11501,-11449,-11397, -11345,-11293,-11242,-11190,-11138,-11086,-11035,-10983, -10931,-10880,-10828,-10777,-10725,-10673,-10622,-10570, -10519,-10467,-10415,-10364,-10312,-10261,-10209,-10158, -10106,-10055,-10004,-9952,-9901,-9849,-9798,-9747, -9695,-9644,-9592,-9541,-9490,-9438,-9387,-9336, -9285,-9233,-9182,-9131,-9080,-9028,-8977,-8926, -8875,-8824,-8772,-8721,-8670,-8619,-8568,-8517, -8466,-8414,-8363,-8312,-8261,-8210,-8159,-8108, -8057,-8006,-7955,-7904,-7853,-7802,-7751,-7700, -7649,-7598,-7547,-7496,-7445,-7395,-7344,-7293, -7242,-7191,-7140,-7089,-7038,-6988,-6937,-6886, -6835,-6784,-6733,-6683,-6632,-6581,-6530,-6480, -6429,-6378,-6327,-6277,-6226,-6175,-6124,-6074, -6023,-5972,-5922,-5871,-5820,-5770,-5719,-5668, -5618,-5567,-5517,-5466,-5415,-5365,-5314,-5264, -5213,-5162,-5112,-5061,-5011,-4960,-4910,-4859, -4808,-4758,-4707,-4657,-4606,-4556,-4505,-4455, -4404,-4354,-4303,-4253,-4202,-4152,-4101,-4051, -4001,-3950,-3900,-3849,-3799,-3748,-3698,-3648, -3597,-3547,-3496,-3446,-3395,-3345,-3295,-3244, -3194,-3144,-3093,-3043,-2992,-2942,-2892,-2841, -2791,-2741,-2690,-2640,-2590,-2539,-2489,-2439, -2388,-2338,-2288,-2237,-2187,-2137,-2086,-2036, -1986,-1935,-1885,-1835,-1784,-1734,-1684,-1633, -1583,-1533,-1483,-1432,-1382,-1332,-1281,-1231, -1181,-1131,-1080,-1030,-980,-929,-879,-829, -779,-728,-678,-628,-578,-527,-477,-427, -376,-326,-276,-226,-175,-125,-75,-25, 25,75,125,175,226,276,326,376, 427,477,527,578,628,678,728,779, 829,879,929,980,1030,1080,1131,1181, 1231,1281,1332,1382,1432,1483,1533,1583, 1633,1684,1734,1784,1835,1885,1935,1986, 2036,2086,2137,2187,2237,2288,2338,2388, 2439,2489,2539,2590,2640,2690,2741,2791, 2841,2892,2942,2992,3043,3093,3144,3194, 3244,3295,3345,3395,3446,3496,3547,3597, 3648,3698,3748,3799,3849,3900,3950,4001, 4051,4101,4152,4202,4253,4303,4354,4404, 4455,4505,4556,4606,4657,4707,4758,4808, 4859,4910,4960,5011,5061,5112,5162,5213, 5264,5314,5365,5415,5466,5517,5567,5618, 5668,5719,5770,5820,5871,5922,5972,6023, 6074,6124,6175,6226,6277,6327,6378,6429, 6480,6530,6581,6632,6683,6733,6784,6835, 6886,6937,6988,7038,7089,7140,7191,7242, 7293,7344,7395,7445,7496,7547,7598,7649, 7700,7751,7802,7853,7904,7955,8006,8057, 8108,8159,8210,8261,8312,8363,8414,8466, 8517,8568,8619,8670,8721,8772,8824,8875, 8926,8977,9028,9080,9131,9182,9233,9285, 9336,9387,9438,9490,9541,9592,9644,9695, 9747,9798,9849,9901,9952,10004,10055,10106, 10158,10209,10261,10312,10364,10415,10467,10519, 10570,10622,10673,10725,10777,10828,10880,10931, 10983,11035,11086,11138,11190,11242,11293,11345, 11397,11449,11501,11552,11604,11656,11708,11760, 11812,11864,11916,11967,12019,12071,12123,12175, 12227,12279,12331,12383,12436,12488,12540,12592, 12644,12696,12748,12800,12853,12905,12957,13009, 13062,13114,13166,13218,13271,13323,13375,13428, 13480,13533,13585,13637,13690,13742,13795,13847, 13900,13952,14005,14057,14110,14163,14215,14268, 14321,14373,14426,14479,14531,14584,14637,14690, 14743,14795,14848,14901,14954,15007,15060,15113, 15166,15219,15272,15325,15378,15431,15484,15537, 15590,15643,15696,15749,15802,15856,15909,15962, 16015,16069,16122,16175,16229,16282,16335,16389, 16442,16496,16549,16603,16656,16710,16763,16817, 16870,16924,16977,17031,17085,17138,17192,17246, 17300,17353,17407,17461,17515,17569,17623,17677, 17731,17784,17838,17892,17946,18001,18055,18109, 18163,18217,18271,18325,18380,18434,18488,18542, 18597,18651,18705,18760,18814,18868,18923,18977, 19032,19086,19141,19195,19250,19305,19359,19414, 19469,19523,19578,19633,19688,19742,19797,19852, 19907,19962,20017,20072,20127,20182,20237,20292, 20347,20402,20457,20513,20568,20623,20678,20734, 20789,20844,20900,20955,21010,21066,21121,21177, 21232,21288,21343,21399,21455,21510,21566,21622, 21678,21733,21789,21845,21901,21957,22013,22069, 22125,22181,22237,22293,22349,22405,22461,22517, 22573,22630,22686,22742,22799,22855,22911,22968, 23024,23081,23137,23194,23250,23307,23364,23420, 23477,23534,23591,23647,23704,23761,23818,23875, 23932,23989,24046,24103,24160,24217,24274,24331, 24389,24446,24503,24560,24618,24675,24732,24790, 24847,24905,24962,25020,25078,25135,25193,25251, 25308,25366,25424,25482,25540,25598,25656,25714, 25772,25830,25888,25946,26004,26062,26120,26179, 26237,26295,26354,26412,26471,26529,26588,26646, 26705,26763,26822,26881,26940,26998,27057,27116, 27175,27234,27293,27352,27411,27470,27529,27588, 27647,27707,27766,27825,27884,27944,28003,28063, 28122,28182,28241,28301,28361,28420,28480,28540, 28600,28660,28719,28779,28839,28899,28959,29020, 29080,29140,29200,29260,29321,29381,29441,29502, 29562,29623,29683,29744,29805,29865,29926,29987, 30048,30108,30169,30230,30291,30352,30413,30474, 30536,30597,30658,30719,30781,30842,30904,30965, 31026,31088,31150,31211,31273,31335,31396,31458, 31520,31582,31644,31706,31768,31830,31892,31955, 32017,32079,32141,32204,32266,32329,32391,32454, 32516,32579,32642,32705,32767,32830,32893,32956, 33019,33082,33145,33208,33272,33335,33398,33461, 33525,33588,33652,33715,33779,33843,33906,33970, 34034,34098,34162,34225,34289,34354,34418,34482, 34546,34610,34675,34739,34803,34868,34932,34997, 35062,35126,35191,35256,35321,35385,35450,35515, 35580,35646,35711,35776,35841,35907,35972,36037, 36103,36168,36234,36300,36365,36431,36497,36563, 36629,36695,36761,36827,36893,36959,37026,37092, 37158,37225,37291,37358,37425,37491,37558,37625, 37692,37759,37826,37893,37960,38027,38094,38161, 38229,38296,38364,38431,38499,38566,38634,38702, 38770,38837,38905,38973,39042,39110,39178,39246, 39314,39383,39451,39520,39588,39657,39726,39794, 39863,39932,40001,40070,40139,40208,40278,40347, 40416,40486,40555,40625,40694,40764,40834,40904, 40973,41043,41113,41184,41254,41324,41394,41465, 41535,41605,41676,41747,41817,41888,41959,42030, 42101,42172,42243,42314,42385,42457,42528,42600, 42671,42743,42814,42886,42958,43030,43102,43174, 43246,43318,43390,43463,43535,43608,43680,43753, 43826,43898,43971,44044,44117,44190,44263,44337, 44410,44483,44557,44630,44704,44778,44851,44925, 44999,45073,45147,45221,45296,45370,45444,45519, 45593,45668,45743,45818,45892,45967,46042,46118, 46193,46268,46343,46419,46494,46570,46646,46721, 46797,46873,46949,47025,47102,47178,47254,47331, 47407,47484,47560,47637,47714,47791,47868,47945, 48022,48100,48177,48255,48332,48410,48488,48565, 48643,48721,48799,48878,48956,49034,49113,49191, 49270,49349,49427,49506,49585,49664,49744,49823, 49902,49982,50061,50141,50221,50300,50380,50460, 50540,50621,50701,50781,50862,50942,51023,51104, 51185,51266,51347,51428,51509,51591,51672,51754, 51835,51917,51999,52081,52163,52245,52327,52410, 52492,52575,52657,52740,52823,52906,52989,53072, 53156,53239,53322,53406,53490,53574,53657,53741, 53826,53910,53994,54079,54163,54248,54333,54417, 54502,54587,54673,54758,54843,54929,55015,55100, 55186,55272,55358,55444,55531,55617,55704,55790, 55877,55964,56051,56138,56225,56312,56400,56487, 56575,56663,56751,56839,56927,57015,57104,57192, 57281,57369,57458,57547,57636,57725,57815,57904, 57994,58083,58173,58263,58353,58443,58534,58624, 58715,58805,58896,58987,59078,59169,59261,59352, 59444,59535,59627,59719,59811,59903,59996,60088, 60181,60273,60366,60459,60552,60646,60739,60833, 60926,61020,61114,61208,61302,61396,61491,61585, 61680,61775,61870,61965,62060,62156,62251,62347, 62443,62539,62635,62731,62828,62924,63021,63118, 63215,63312,63409,63506,63604,63702,63799,63897, 63996,64094,64192,64291,64389,64488,64587,64687, 64786,64885,64985,65085,65185,65285,65385,65485, 65586,65686,65787,65888,65989,66091,66192,66294, 66396,66498,66600,66702,66804,66907,67010,67113, 67216,67319,67422,67526,67629,67733,67837,67942, 68046,68151,68255,68360,68465,68570,68676,68781, 68887,68993,69099,69205,69312,69418,69525,69632, 69739,69846,69954,70061,70169,70277,70385,70494, 70602,70711,70820,70929,71038,71147,71257,71367, 71477,71587,71697,71808,71918,72029,72140,72252, 72363,72475,72587,72699,72811,72923,73036,73149, 73262,73375,73488,73602,73715,73829,73944,74058, 74172,74287,74402,74517,74633,74748,74864,74980, 75096,75213,75329,75446,75563,75680,75797,75915, 76033,76151,76269,76388,76506,76625,76744,76864, 76983,77103,77223,77343,77463,77584,77705,77826, 77947,78068,78190,78312,78434,78557,78679,78802, 78925,79048,79172,79296,79420,79544,79668,79793, 79918,80043,80168,80294,80420,80546,80672,80799, 80925,81053,81180,81307,81435,81563,81691,81820, 81949,82078,82207,82336,82466,82596,82726,82857, 82987,83118,83250,83381,83513,83645,83777,83910, 84043,84176,84309,84443,84576,84710,84845,84980, 85114,85250,85385,85521,85657,85793,85930,86066, 86204,86341,86479,86616,86755,86893,87032,87171, 87310,87450,87590,87730,87871,88011,88152,88294, 88435,88577,88720,88862,89005,89148,89292,89435, 89579,89724,89868,90013,90158,90304,90450,90596, 90742,90889,91036,91184,91332,91480,91628,91777, 91926,92075,92225,92375,92525,92675,92826,92978, 93129,93281,93434,93586,93739,93892,94046,94200, 94354,94509,94664,94819,94975,95131,95287,95444, 95601,95758,95916,96074,96233,96391,96551,96710, 96870,97030,97191,97352,97513,97675,97837,98000, 98163,98326,98489,98653,98818,98982,99148,99313, 99479,99645,99812,99979,100146,100314,100482,100651, 100820,100990,101159,101330,101500,101671,101843,102015, 102187,102360,102533,102706,102880,103054,103229,103404, 103580,103756,103933,104109,104287,104465,104643,104821, 105000,105180,105360,105540,105721,105902,106084,106266, 106449,106632,106816,107000,107184,107369,107555,107741, 107927,108114,108301,108489,108677,108866,109055,109245, 109435,109626,109817,110008,110200,110393,110586,110780, 110974,111169,111364,111560,111756,111952,112150,112347, 112546,112744,112944,113143,113344,113545,113746,113948, 114151,114354,114557,114761,114966,115171,115377,115583, 115790,115998,116206,116414,116623,116833,117044,117254, 117466,117678,117891,118104,118318,118532,118747,118963, 119179,119396,119613,119831,120050,120269,120489,120709, 120930,121152,121374,121597,121821,122045,122270,122496, 122722,122949,123176,123404,123633,123863,124093,124324, 124555,124787,125020,125254,125488,125723,125959,126195, 126432,126669,126908,127147,127387,127627,127869,128111, 128353,128597,128841,129086,129332,129578,129825,130073, 130322,130571,130821,131072,131324,131576,131830,132084, 132339,132594,132851,133108,133366,133625,133884,134145, 134406,134668,134931,135195,135459,135725,135991,136258, 136526,136795,137065,137335,137607,137879,138152,138426, 138701,138977,139254,139532,139810,140090,140370,140651, 140934,141217,141501,141786,142072,142359,142647,142936, 143226,143517,143808,144101,144395,144690,144986,145282, 145580,145879,146179,146480,146782,147084,147388,147693, 148000,148307,148615,148924,149235,149546,149859,150172, 150487,150803,151120,151438,151757,152077,152399,152722, 153045,153370,153697,154024,154352,154682,155013,155345, 155678,156013,156349,156686,157024,157363,157704,158046, 158389,158734,159079,159427,159775,160125,160476,160828, 161182,161537,161893,162251,162610,162970,163332,163695, 164060,164426,164793,165162,165532,165904,166277,166651, 167027,167405,167784,168164,168546,168930,169315,169701, 170089,170479,170870,171263,171657,172053,172451,172850, 173251,173653,174057,174463,174870,175279,175690,176102, 176516,176932,177349,177769,178190,178612,179037,179463, 179891,180321,180753,181186,181622,182059,182498,182939, 183382,183827,184274,184722,185173,185625,186080,186536, 186995,187455,187918,188382,188849,189318,189789,190261, 190736,191213,191693,192174,192658,193143,193631,194122, 194614,195109,195606,196105,196606,197110,197616,198125, 198636,199149,199664,200182,200703,201226,201751,202279, 202809,203342,203878,204416,204956,205500,206045,206594, 207145,207699,208255,208815,209376,209941,210509,211079, 211652,212228,212807,213389,213973,214561,215151,215745, 216341,216941,217544,218149,218758,219370,219985,220603, 221225,221849,222477,223108,223743,224381,225022,225666, 226314,226966,227621,228279,228941,229606,230275,230948, 231624,232304,232988,233676,234367,235062,235761,236463, 237170,237881,238595,239314,240036,240763,241493,242228, 242967,243711,244458,245210,245966,246727,247492,248261, 249035,249813,250596,251384,252176,252973,253774,254581, 255392,256208,257029,257855,258686,259522,260363,261209, 262060,262917,263779,264646,265519,266397,267280,268169, 269064,269965,270871,271782,272700,273624,274553,275489, 276430,277378,278332,279292,280258,281231,282210,283195, 284188,285186,286192,287204,288223,289249,290282,291322, 292369,293423,294485,295554,296630,297714,298805,299904, 301011,302126,303248,304379,305517,306664,307819,308983, 310154,311335,312524,313721,314928,316143,317368,318601, 319844,321097,322358,323629,324910,326201,327502,328812, 330133,331464,332805,334157,335519,336892,338276,339671, 341078,342495,343924,345364,346816,348280,349756,351244, 352744,354257,355783,357321,358872,360436,362013,363604, 365208,366826,368459,370105,371765,373440,375130,376835, 378555,380290,382040,383807,385589,387387,389202,391034, 392882,394747,396630,398530,400448,402384,404338,406311, 408303,410314,412344,414395,416465,418555,420666,422798, 424951,427125,429321,431540,433781,436045,438332,440643, 442978,445337,447720,450129,452564,455024,457511,460024, 462565,465133,467730,470355,473009,475692,478406,481150, 483925,486732,489571,492443,495348,498287,501261,504269, 507313,510394,513512,516667,519861,523094,526366,529680, 533034,536431,539870,543354,546881,550455,554074,557741, 561456,565221,569035,572901,576818,580789,584815,588896, 593033,597229,601483,605798,610174,614613,619117,623686, 628323,633028,637803,642651,647572,652568,657640,662792, 668024,673338,678737,684223,689797,695462,701219,707072, 713023,719074,725227,731486,737853,744331,750922,757631, 764460,771411,778490,785699,793041,800521,808143,815910, 823827,831898,840127,848520,857081,865817,874730,883829, 893117,902602,912289,922186,932298,942633,953199,964003, 975054,986361,997931,1009774,1021901,1034322,1047046,1060087, 1073455,1087164,1101225,1115654,1130465,1145673,1161294,1177345, 1193846,1210813,1228269,1246234,1264730,1283783,1303416,1323658, 1344537,1366084,1388330,1411312,1435065,1459630,1485049,1511367, 1538632,1566898,1596220,1626658,1658278,1691149,1725348,1760956, 1798063,1836758,1877161,1919378,1963536,2009771,2058233,2109087, 2162516,2218719,2277919,2340362,2406322,2476104,2550052,2628549, 2712030,2800983,2895966,2997613,3106651,3223918,3350381,3487165, 3635590,3797206,3973855,4167737,4381502,4618375,4882318,5178251, 5512368,5892567,6329090,6835455,7429880,8137527,8994149,10052327, 11392683,13145455,15535599,18988036,24413316,34178904,56965752,170910304 }; const fixed_t finesine[10240] = { 25,75,125,175,226,276,326,376, 427,477,527,578,628,678,728,779, 829,879,929,980,1030,1080,1130,1181, 1231,1281,1331,1382,1432,1482,1532,1583, 1633,1683,1733,1784,1834,1884,1934,1985, 2035,2085,2135,2186,2236,2286,2336,2387, 2437,2487,2537,2587,2638,2688,2738,2788, 2839,2889,2939,2989,3039,3090,3140,3190, 3240,3291,3341,3391,3441,3491,3541,3592, 3642,3692,3742,3792,3843,3893,3943,3993, 4043,4093,4144,4194,4244,4294,4344,4394, 4445,4495,4545,4595,4645,4695,4745,4796, 4846,4896,4946,4996,5046,5096,5146,5197, 5247,5297,5347,5397,5447,5497,5547,5597, 5647,5697,5748,5798,5848,5898,5948,5998, 6048,6098,6148,6198,6248,6298,6348,6398, 6448,6498,6548,6598,6648,6698,6748,6798, 6848,6898,6948,6998,7048,7098,7148,7198, 7248,7298,7348,7398,7448,7498,7548,7598, 7648,7697,7747,7797,7847,7897,7947,7997, 8047,8097,8147,8196,8246,8296,8346,8396, 8446,8496,8545,8595,8645,8695,8745,8794, 8844,8894,8944,8994,9043,9093,9143,9193, 9243,9292,9342,9392,9442,9491,9541,9591, 9640,9690,9740,9790,9839,9889,9939,9988, 10038,10088,10137,10187,10237,10286,10336,10386, 10435,10485,10534,10584,10634,10683,10733,10782, 10832,10882,10931,10981,11030,11080,11129,11179, 11228,11278,11327,11377,11426,11476,11525,11575, 11624,11674,11723,11773,11822,11872,11921,11970, 12020,12069,12119,12168,12218,12267,12316,12366, 12415,12464,12514,12563,12612,12662,12711,12760, 12810,12859,12908,12957,13007,13056,13105,13154, 13204,13253,13302,13351,13401,13450,13499,13548, 13597,13647,13696,13745,13794,13843,13892,13941, 13990,14040,14089,14138,14187,14236,14285,14334, 14383,14432,14481,14530,14579,14628,14677,14726, 14775,14824,14873,14922,14971,15020,15069,15118, 15167,15215,15264,15313,15362,15411,15460,15509, 15557,15606,15655,15704,15753,15802,15850,15899, 15948,15997,16045,16094,16143,16191,16240,16289, 16338,16386,16435,16484,16532,16581,16629,16678, 16727,16775,16824,16872,16921,16970,17018,17067, 17115,17164,17212,17261,17309,17358,17406,17455, 17503,17551,17600,17648,17697,17745,17793,17842, 17890,17939,17987,18035,18084,18132,18180,18228, 18277,18325,18373,18421,18470,18518,18566,18614, 18663,18711,18759,18807,18855,18903,18951,19000, 19048,19096,19144,19192,19240,19288,19336,19384, 19432,19480,19528,19576,19624,19672,19720,19768, 19816,19864,19912,19959,20007,20055,20103,20151, 20199,20246,20294,20342,20390,20438,20485,20533, 20581,20629,20676,20724,20772,20819,20867,20915, 20962,21010,21057,21105,21153,21200,21248,21295, 21343,21390,21438,21485,21533,21580,21628,21675, 21723,21770,21817,21865,21912,21960,22007,22054, 22102,22149,22196,22243,22291,22338,22385,22433, 22480,22527,22574,22621,22668,22716,22763,22810, 22857,22904,22951,22998,23045,23092,23139,23186, 23233,23280,23327,23374,23421,23468,23515,23562, 23609,23656,23703,23750,23796,23843,23890,23937, 23984,24030,24077,24124,24171,24217,24264,24311, 24357,24404,24451,24497,24544,24591,24637,24684, 24730,24777,24823,24870,24916,24963,25009,25056, 25102,25149,25195,25241,25288,25334,25381,25427, 25473,25520,25566,25612,25658,25705,25751,25797, 25843,25889,25936,25982,26028,26074,26120,26166, 26212,26258,26304,26350,26396,26442,26488,26534, 26580,26626,26672,26718,26764,26810,26856,26902, 26947,26993,27039,27085,27131,27176,27222,27268, 27313,27359,27405,27450,27496,27542,27587,27633, 27678,27724,27770,27815,27861,27906,27952,27997, 28042,28088,28133,28179,28224,28269,28315,28360, 28405,28451,28496,28541,28586,28632,28677,28722, 28767,28812,28858,28903,28948,28993,29038,29083, 29128,29173,29218,29263,29308,29353,29398,29443, 29488,29533,29577,29622,29667,29712,29757,29801, 29846,29891,29936,29980,30025,30070,30114,30159, 30204,30248,30293,30337,30382,30426,30471,30515, 30560,30604,30649,30693,30738,30782,30826,30871, 30915,30959,31004,31048,31092,31136,31181,31225, 31269,31313,31357,31402,31446,31490,31534,31578, 31622,31666,31710,31754,31798,31842,31886,31930, 31974,32017,32061,32105,32149,32193,32236,32280, 32324,32368,32411,32455,32499,32542,32586,32630, 32673,32717,32760,32804,32847,32891,32934,32978, 33021,33065,33108,33151,33195,33238,33281,33325, 33368,33411,33454,33498,33541,33584,33627,33670, 33713,33756,33799,33843,33886,33929,33972,34015, 34057,34100,34143,34186,34229,34272,34315,34358, 34400,34443,34486,34529,34571,34614,34657,34699, 34742,34785,34827,34870,34912,34955,34997,35040, 35082,35125,35167,35210,35252,35294,35337,35379, 35421,35464,35506,35548,35590,35633,35675,35717, 35759,35801,35843,35885,35927,35969,36011,36053, 36095,36137,36179,36221,36263,36305,36347,36388, 36430,36472,36514,36555,36597,36639,36681,36722, 36764,36805,36847,36889,36930,36972,37013,37055, 37096,37137,37179,37220,37262,37303,37344,37386, 37427,37468,37509,37551,37592,37633,37674,37715, 37756,37797,37838,37879,37920,37961,38002,38043, 38084,38125,38166,38207,38248,38288,38329,38370, 38411,38451,38492,38533,38573,38614,38655,38695, 38736,38776,38817,38857,38898,38938,38979,39019, 39059,39100,39140,39180,39221,39261,39301,39341, 39382,39422,39462,39502,39542,39582,39622,39662, 39702,39742,39782,39822,39862,39902,39942,39982, 40021,40061,40101,40141,40180,40220,40260,40300, 40339,40379,40418,40458,40497,40537,40576,40616, 40655,40695,40734,40773,40813,40852,40891,40931, 40970,41009,41048,41087,41127,41166,41205,41244, 41283,41322,41361,41400,41439,41478,41517,41556, 41595,41633,41672,41711,41750,41788,41827,41866, 41904,41943,41982,42020,42059,42097,42136,42174, 42213,42251,42290,42328,42366,42405,42443,42481, 42520,42558,42596,42634,42672,42711,42749,42787, 42825,42863,42901,42939,42977,43015,43053,43091, 43128,43166,43204,43242,43280,43317,43355,43393, 43430,43468,43506,43543,43581,43618,43656,43693, 43731,43768,43806,43843,43880,43918,43955,43992, 44029,44067,44104,44141,44178,44215,44252,44289, 44326,44363,44400,44437,44474,44511,44548,44585, 44622,44659,44695,44732,44769,44806,44842,44879, 44915,44952,44989,45025,45062,45098,45135,45171, 45207,45244,45280,45316,45353,45389,45425,45462, 45498,45534,45570,45606,45642,45678,45714,45750, 45786,45822,45858,45894,45930,45966,46002,46037, 46073,46109,46145,46180,46216,46252,46287,46323, 46358,46394,46429,46465,46500,46536,46571,46606, 46642,46677,46712,46747,46783,46818,46853,46888, 46923,46958,46993,47028,47063,47098,47133,47168, 47203,47238,47273,47308,47342,47377,47412,47446, 47481,47516,47550,47585,47619,47654,47688,47723, 47757,47792,47826,47860,47895,47929,47963,47998, 48032,48066,48100,48134,48168,48202,48237,48271, 48305,48338,48372,48406,48440,48474,48508,48542, 48575,48609,48643,48676,48710,48744,48777,48811, 48844,48878,48911,48945,48978,49012,49045,49078, 49112,49145,49178,49211,49244,49278,49311,49344, 49377,49410,49443,49476,49509,49542,49575,49608, 49640,49673,49706,49739,49771,49804,49837,49869, 49902,49935,49967,50000,50032,50065,50097,50129, 50162,50194,50226,50259,50291,50323,50355,50387, 50420,50452,50484,50516,50548,50580,50612,50644, 50675,50707,50739,50771,50803,50834,50866,50898, 50929,50961,50993,51024,51056,51087,51119,51150, 51182,51213,51244,51276,51307,51338,51369,51401, 51432,51463,51494,51525,51556,51587,51618,51649, 51680,51711,51742,51773,51803,51834,51865,51896, 51926,51957,51988,52018,52049,52079,52110,52140, 52171,52201,52231,52262,52292,52322,52353,52383, 52413,52443,52473,52503,52534,52564,52594,52624, 52653,52683,52713,52743,52773,52803,52832,52862, 52892,52922,52951,52981,53010,53040,53069,53099, 53128,53158,53187,53216,53246,53275,53304,53334, 53363,53392,53421,53450,53479,53508,53537,53566, 53595,53624,53653,53682,53711,53739,53768,53797, 53826,53854,53883,53911,53940,53969,53997,54026, 54054,54082,54111,54139,54167,54196,54224,54252, 54280,54308,54337,54365,54393,54421,54449,54477, 54505,54533,54560,54588,54616,54644,54672,54699, 54727,54755,54782,54810,54837,54865,54892,54920, 54947,54974,55002,55029,55056,55084,55111,55138, 55165,55192,55219,55246,55274,55300,55327,55354, 55381,55408,55435,55462,55489,55515,55542,55569, 55595,55622,55648,55675,55701,55728,55754,55781, 55807,55833,55860,55886,55912,55938,55965,55991, 56017,56043,56069,56095,56121,56147,56173,56199, 56225,56250,56276,56302,56328,56353,56379,56404, 56430,56456,56481,56507,56532,56557,56583,56608, 56633,56659,56684,56709,56734,56760,56785,56810, 56835,56860,56885,56910,56935,56959,56984,57009, 57034,57059,57083,57108,57133,57157,57182,57206, 57231,57255,57280,57304,57329,57353,57377,57402, 57426,57450,57474,57498,57522,57546,57570,57594, 57618,57642,57666,57690,57714,57738,57762,57785, 57809,57833,57856,57880,57903,57927,57950,57974, 57997,58021,58044,58067,58091,58114,58137,58160, 58183,58207,58230,58253,58276,58299,58322,58345, 58367,58390,58413,58436,58459,58481,58504,58527, 58549,58572,58594,58617,58639,58662,58684,58706, 58729,58751,58773,58795,58818,58840,58862,58884, 58906,58928,58950,58972,58994,59016,59038,59059, 59081,59103,59125,59146,59168,59190,59211,59233, 59254,59276,59297,59318,59340,59361,59382,59404, 59425,59446,59467,59488,59509,59530,59551,59572, 59593,59614,59635,59656,59677,59697,59718,59739, 59759,59780,59801,59821,59842,59862,59883,59903, 59923,59944,59964,59984,60004,60025,60045,60065, 60085,60105,60125,60145,60165,60185,60205,60225, 60244,60264,60284,60304,60323,60343,60363,60382, 60402,60421,60441,60460,60479,60499,60518,60537, 60556,60576,60595,60614,60633,60652,60671,60690, 60709,60728,60747,60766,60785,60803,60822,60841, 60859,60878,60897,60915,60934,60952,60971,60989, 61007,61026,61044,61062,61081,61099,61117,61135, 61153,61171,61189,61207,61225,61243,61261,61279, 61297,61314,61332,61350,61367,61385,61403,61420, 61438,61455,61473,61490,61507,61525,61542,61559, 61577,61594,61611,61628,61645,61662,61679,61696, 61713,61730,61747,61764,61780,61797,61814,61831, 61847,61864,61880,61897,61913,61930,61946,61963, 61979,61995,62012,62028,62044,62060,62076,62092, 62108,62125,62141,62156,62172,62188,62204,62220, 62236,62251,62267,62283,62298,62314,62329,62345, 62360,62376,62391,62407,62422,62437,62453,62468, 62483,62498,62513,62528,62543,62558,62573,62588, 62603,62618,62633,62648,62662,62677,62692,62706, 62721,62735,62750,62764,62779,62793,62808,62822, 62836,62850,62865,62879,62893,62907,62921,62935, 62949,62963,62977,62991,63005,63019,63032,63046, 63060,63074,63087,63101,63114,63128,63141,63155, 63168,63182,63195,63208,63221,63235,63248,63261, 63274,63287,63300,63313,63326,63339,63352,63365, 63378,63390,63403,63416,63429,63441,63454,63466, 63479,63491,63504,63516,63528,63541,63553,63565, 63578,63590,63602,63614,63626,63638,63650,63662, 63674,63686,63698,63709,63721,63733,63745,63756, 63768,63779,63791,63803,63814,63825,63837,63848, 63859,63871,63882,63893,63904,63915,63927,63938, 63949,63960,63971,63981,63992,64003,64014,64025, 64035,64046,64057,64067,64078,64088,64099,64109, 64120,64130,64140,64151,64161,64171,64181,64192, 64202,64212,64222,64232,64242,64252,64261,64271, 64281,64291,64301,64310,64320,64330,64339,64349, 64358,64368,64377,64387,64396,64405,64414,64424, 64433,64442,64451,64460,64469,64478,64487,64496, 64505,64514,64523,64532,64540,64549,64558,64566, 64575,64584,64592,64601,64609,64617,64626,64634, 64642,64651,64659,64667,64675,64683,64691,64699, 64707,64715,64723,64731,64739,64747,64754,64762, 64770,64777,64785,64793,64800,64808,64815,64822, 64830,64837,64844,64852,64859,64866,64873,64880, 64887,64895,64902,64908,64915,64922,64929,64936, 64943,64949,64956,64963,64969,64976,64982,64989, 64995,65002,65008,65015,65021,65027,65033,65040, 65046,65052,65058,65064,65070,65076,65082,65088, 65094,65099,65105,65111,65117,65122,65128,65133, 65139,65144,65150,65155,65161,65166,65171,65177, 65182,65187,65192,65197,65202,65207,65212,65217, 65222,65227,65232,65237,65242,65246,65251,65256, 65260,65265,65270,65274,65279,65283,65287,65292, 65296,65300,65305,65309,65313,65317,65321,65325, 65329,65333,65337,65341,65345,65349,65352,65356, 65360,65363,65367,65371,65374,65378,65381,65385, 65388,65391,65395,65398,65401,65404,65408,65411, 65414,65417,65420,65423,65426,65429,65431,65434, 65437,65440,65442,65445,65448,65450,65453,65455, 65458,65460,65463,65465,65467,65470,65472,65474, 65476,65478,65480,65482,65484,65486,65488,65490, 65492,65494,65496,65497,65499,65501,65502,65504, 65505,65507,65508,65510,65511,65513,65514,65515, 65516,65518,65519,65520,65521,65522,65523,65524, 65525,65526,65527,65527,65528,65529,65530,65530, 65531,65531,65532,65532,65533,65533,65534,65534, 65534,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65534, 65534,65534,65533,65533,65532,65532,65531,65531, 65530,65530,65529,65528,65527,65527,65526,65525, 65524,65523,65522,65521,65520,65519,65518,65516, 65515,65514,65513,65511,65510,65508,65507,65505, 65504,65502,65501,65499,65497,65496,65494,65492, 65490,65488,65486,65484,65482,65480,65478,65476, 65474,65472,65470,65467,65465,65463,65460,65458, 65455,65453,65450,65448,65445,65442,65440,65437, 65434,65431,65429,65426,65423,65420,65417,65414, 65411,65408,65404,65401,65398,65395,65391,65388, 65385,65381,65378,65374,65371,65367,65363,65360, 65356,65352,65349,65345,65341,65337,65333,65329, 65325,65321,65317,65313,65309,65305,65300,65296, 65292,65287,65283,65279,65274,65270,65265,65260, 65256,65251,65246,65242,65237,65232,65227,65222, 65217,65212,65207,65202,65197,65192,65187,65182, 65177,65171,65166,65161,65155,65150,65144,65139, 65133,65128,65122,65117,65111,65105,65099,65094, 65088,65082,65076,65070,65064,65058,65052,65046, 65040,65033,65027,65021,65015,65008,65002,64995, 64989,64982,64976,64969,64963,64956,64949,64943, 64936,64929,64922,64915,64908,64902,64895,64887, 64880,64873,64866,64859,64852,64844,64837,64830, 64822,64815,64808,64800,64793,64785,64777,64770, 64762,64754,64747,64739,64731,64723,64715,64707, 64699,64691,64683,64675,64667,64659,64651,64642, 64634,64626,64617,64609,64600,64592,64584,64575, 64566,64558,64549,64540,64532,64523,64514,64505, 64496,64487,64478,64469,64460,64451,64442,64433, 64424,64414,64405,64396,64387,64377,64368,64358, 64349,64339,64330,64320,64310,64301,64291,64281, 64271,64261,64252,64242,64232,64222,64212,64202, 64192,64181,64171,64161,64151,64140,64130,64120, 64109,64099,64088,64078,64067,64057,64046,64035, 64025,64014,64003,63992,63981,63971,63960,63949, 63938,63927,63915,63904,63893,63882,63871,63859, 63848,63837,63825,63814,63803,63791,63779,63768, 63756,63745,63733,63721,63709,63698,63686,63674, 63662,63650,63638,63626,63614,63602,63590,63578, 63565,63553,63541,63528,63516,63504,63491,63479, 63466,63454,63441,63429,63416,63403,63390,63378, 63365,63352,63339,63326,63313,63300,63287,63274, 63261,63248,63235,63221,63208,63195,63182,63168, 63155,63141,63128,63114,63101,63087,63074,63060, 63046,63032,63019,63005,62991,62977,62963,62949, 62935,62921,62907,62893,62879,62865,62850,62836, 62822,62808,62793,62779,62764,62750,62735,62721, 62706,62692,62677,62662,62648,62633,62618,62603, 62588,62573,62558,62543,62528,62513,62498,62483, 62468,62453,62437,62422,62407,62391,62376,62360, 62345,62329,62314,62298,62283,62267,62251,62236, 62220,62204,62188,62172,62156,62141,62125,62108, 62092,62076,62060,62044,62028,62012,61995,61979, 61963,61946,61930,61913,61897,61880,61864,61847, 61831,61814,61797,61780,61764,61747,61730,61713, 61696,61679,61662,61645,61628,61611,61594,61577, 61559,61542,61525,61507,61490,61473,61455,61438, 61420,61403,61385,61367,61350,61332,61314,61297, 61279,61261,61243,61225,61207,61189,61171,61153, 61135,61117,61099,61081,61062,61044,61026,61007, 60989,60971,60952,60934,60915,60897,60878,60859, 60841,60822,60803,60785,60766,60747,60728,60709, 60690,60671,60652,60633,60614,60595,60576,60556, 60537,60518,60499,60479,60460,60441,60421,60402, 60382,60363,60343,60323,60304,60284,60264,60244, 60225,60205,60185,60165,60145,60125,60105,60085, 60065,60045,60025,60004,59984,59964,59944,59923, 59903,59883,59862,59842,59821,59801,59780,59759, 59739,59718,59697,59677,59656,59635,59614,59593, 59572,59551,59530,59509,59488,59467,59446,59425, 59404,59382,59361,59340,59318,59297,59276,59254, 59233,59211,59190,59168,59146,59125,59103,59081, 59059,59038,59016,58994,58972,58950,58928,58906, 58884,58862,58840,58818,58795,58773,58751,58729, 58706,58684,58662,58639,58617,58594,58572,58549, 58527,58504,58481,58459,58436,58413,58390,58367, 58345,58322,58299,58276,58253,58230,58207,58183, 58160,58137,58114,58091,58067,58044,58021,57997, 57974,57950,57927,57903,57880,57856,57833,57809, 57785,57762,57738,57714,57690,57666,57642,57618, 57594,57570,57546,57522,57498,57474,57450,57426, 57402,57377,57353,57329,57304,57280,57255,57231, 57206,57182,57157,57133,57108,57083,57059,57034, 57009,56984,56959,56935,56910,56885,56860,56835, 56810,56785,56760,56734,56709,56684,56659,56633, 56608,56583,56557,56532,56507,56481,56456,56430, 56404,56379,56353,56328,56302,56276,56250,56225, 56199,56173,56147,56121,56095,56069,56043,56017, 55991,55965,55938,55912,55886,55860,55833,55807, 55781,55754,55728,55701,55675,55648,55622,55595, 55569,55542,55515,55489,55462,55435,55408,55381, 55354,55327,55300,55274,55246,55219,55192,55165, 55138,55111,55084,55056,55029,55002,54974,54947, 54920,54892,54865,54837,54810,54782,54755,54727, 54699,54672,54644,54616,54588,54560,54533,54505, 54477,54449,54421,54393,54365,54337,54308,54280, 54252,54224,54196,54167,54139,54111,54082,54054, 54026,53997,53969,53940,53911,53883,53854,53826, 53797,53768,53739,53711,53682,53653,53624,53595, 53566,53537,53508,53479,53450,53421,53392,53363, 53334,53304,53275,53246,53216,53187,53158,53128, 53099,53069,53040,53010,52981,52951,52922,52892, 52862,52832,52803,52773,52743,52713,52683,52653, 52624,52594,52564,52534,52503,52473,52443,52413, 52383,52353,52322,52292,52262,52231,52201,52171, 52140,52110,52079,52049,52018,51988,51957,51926, 51896,51865,51834,51803,51773,51742,51711,51680, 51649,51618,51587,51556,51525,51494,51463,51432, 51401,51369,51338,51307,51276,51244,51213,51182, 51150,51119,51087,51056,51024,50993,50961,50929, 50898,50866,50834,50803,50771,50739,50707,50675, 50644,50612,50580,50548,50516,50484,50452,50420, 50387,50355,50323,50291,50259,50226,50194,50162, 50129,50097,50065,50032,50000,49967,49935,49902, 49869,49837,49804,49771,49739,49706,49673,49640, 49608,49575,49542,49509,49476,49443,49410,49377, 49344,49311,49278,49244,49211,49178,49145,49112, 49078,49045,49012,48978,48945,48911,48878,48844, 48811,48777,48744,48710,48676,48643,48609,48575, 48542,48508,48474,48440,48406,48372,48338,48304, 48271,48237,48202,48168,48134,48100,48066,48032, 47998,47963,47929,47895,47860,47826,47792,47757, 47723,47688,47654,47619,47585,47550,47516,47481, 47446,47412,47377,47342,47308,47273,47238,47203, 47168,47133,47098,47063,47028,46993,46958,46923, 46888,46853,46818,46783,46747,46712,46677,46642, 46606,46571,46536,46500,46465,46429,46394,46358, 46323,46287,46252,46216,46180,46145,46109,46073, 46037,46002,45966,45930,45894,45858,45822,45786, 45750,45714,45678,45642,45606,45570,45534,45498, 45462,45425,45389,45353,45316,45280,45244,45207, 45171,45135,45098,45062,45025,44989,44952,44915, 44879,44842,44806,44769,44732,44695,44659,44622, 44585,44548,44511,44474,44437,44400,44363,44326, 44289,44252,44215,44178,44141,44104,44067,44029, 43992,43955,43918,43880,43843,43806,43768,43731, 43693,43656,43618,43581,43543,43506,43468,43430, 43393,43355,43317,43280,43242,43204,43166,43128, 43091,43053,43015,42977,42939,42901,42863,42825, 42787,42749,42711,42672,42634,42596,42558,42520, 42481,42443,42405,42366,42328,42290,42251,42213, 42174,42136,42097,42059,42020,41982,41943,41904, 41866,41827,41788,41750,41711,41672,41633,41595, 41556,41517,41478,41439,41400,41361,41322,41283, 41244,41205,41166,41127,41088,41048,41009,40970, 40931,40891,40852,40813,40773,40734,40695,40655, 40616,40576,40537,40497,40458,40418,40379,40339, 40300,40260,40220,40180,40141,40101,40061,40021, 39982,39942,39902,39862,39822,39782,39742,39702, 39662,39622,39582,39542,39502,39462,39422,39382, 39341,39301,39261,39221,39180,39140,39100,39059, 39019,38979,38938,38898,38857,38817,38776,38736, 38695,38655,38614,38573,38533,38492,38451,38411, 38370,38329,38288,38248,38207,38166,38125,38084, 38043,38002,37961,37920,37879,37838,37797,37756, 37715,37674,37633,37592,37551,37509,37468,37427, 37386,37344,37303,37262,37220,37179,37137,37096, 37055,37013,36972,36930,36889,36847,36805,36764, 36722,36681,36639,36597,36556,36514,36472,36430, 36388,36347,36305,36263,36221,36179,36137,36095, 36053,36011,35969,35927,35885,35843,35801,35759, 35717,35675,35633,35590,35548,35506,35464,35421, 35379,35337,35294,35252,35210,35167,35125,35082, 35040,34997,34955,34912,34870,34827,34785,34742, 34699,34657,34614,34571,34529,34486,34443,34400, 34358,34315,34272,34229,34186,34143,34100,34057, 34015,33972,33929,33886,33843,33799,33756,33713, 33670,33627,33584,33541,33498,33454,33411,33368, 33325,33281,33238,33195,33151,33108,33065,33021, 32978,32934,32891,32847,32804,32760,32717,32673, 32630,32586,32542,32499,32455,32411,32368,32324, 32280,32236,32193,32149,32105,32061,32017,31974, 31930,31886,31842,31798,31754,31710,31666,31622, 31578,31534,31490,31446,31402,31357,31313,31269, 31225,31181,31136,31092,31048,31004,30959,30915, 30871,30826,30782,30738,30693,30649,30604,30560, 30515,30471,30426,30382,30337,30293,30248,30204, 30159,30114,30070,30025,29980,29936,29891,29846, 29801,29757,29712,29667,29622,29577,29533,29488, 29443,29398,29353,29308,29263,29218,29173,29128, 29083,29038,28993,28948,28903,28858,28812,28767, 28722,28677,28632,28586,28541,28496,28451,28405, 28360,28315,28269,28224,28179,28133,28088,28042, 27997,27952,27906,27861,27815,27770,27724,27678, 27633,27587,27542,27496,27450,27405,27359,27313, 27268,27222,27176,27131,27085,27039,26993,26947, 26902,26856,26810,26764,26718,26672,26626,26580, 26534,26488,26442,26396,26350,26304,26258,26212, 26166,26120,26074,26028,25982,25936,25889,25843, 25797,25751,25705,25658,25612,25566,25520,25473, 25427,25381,25334,25288,25241,25195,25149,25102, 25056,25009,24963,24916,24870,24823,24777,24730, 24684,24637,24591,24544,24497,24451,24404,24357, 24311,24264,24217,24171,24124,24077,24030,23984, 23937,23890,23843,23796,23750,23703,23656,23609, 23562,23515,23468,23421,23374,23327,23280,23233, 23186,23139,23092,23045,22998,22951,22904,22857, 22810,22763,22716,22668,22621,22574,22527,22480, 22433,22385,22338,22291,22243,22196,22149,22102, 22054,22007,21960,21912,21865,21817,21770,21723, 21675,21628,21580,21533,21485,21438,21390,21343, 21295,21248,21200,21153,21105,21057,21010,20962, 20915,20867,20819,20772,20724,20676,20629,20581, 20533,20485,20438,20390,20342,20294,20246,20199, 20151,20103,20055,20007,19959,19912,19864,19816, 19768,19720,19672,19624,19576,19528,19480,19432, 19384,19336,19288,19240,19192,19144,19096,19048, 19000,18951,18903,18855,18807,18759,18711,18663, 18614,18566,18518,18470,18421,18373,18325,18277, 18228,18180,18132,18084,18035,17987,17939,17890, 17842,17793,17745,17697,17648,17600,17551,17503, 17455,17406,17358,17309,17261,17212,17164,17115, 17067,17018,16970,16921,16872,16824,16775,16727, 16678,16629,16581,16532,16484,16435,16386,16338, 16289,16240,16191,16143,16094,16045,15997,15948, 15899,15850,15802,15753,15704,15655,15606,15557, 15509,15460,15411,15362,15313,15264,15215,15167, 15118,15069,15020,14971,14922,14873,14824,14775, 14726,14677,14628,14579,14530,14481,14432,14383, 14334,14285,14236,14187,14138,14089,14040,13990, 13941,13892,13843,13794,13745,13696,13646,13597, 13548,13499,13450,13401,13351,13302,13253,13204, 13154,13105,13056,13007,12957,12908,12859,12810, 12760,12711,12662,12612,12563,12514,12464,12415, 12366,12316,12267,12218,12168,12119,12069,12020, 11970,11921,11872,11822,11773,11723,11674,11624, 11575,11525,11476,11426,11377,11327,11278,11228, 11179,11129,11080,11030,10981,10931,10882,10832, 10782,10733,10683,10634,10584,10534,10485,10435, 10386,10336,10286,10237,10187,10137,10088,10038, 9988,9939,9889,9839,9790,9740,9690,9640, 9591,9541,9491,9442,9392,9342,9292,9243, 9193,9143,9093,9043,8994,8944,8894,8844, 8794,8745,8695,8645,8595,8545,8496,8446, 8396,8346,8296,8246,8196,8147,8097,8047, 7997,7947,7897,7847,7797,7747,7697,7648, 7598,7548,7498,7448,7398,7348,7298,7248, 7198,7148,7098,7048,6998,6948,6898,6848, 6798,6748,6698,6648,6598,6548,6498,6448, 6398,6348,6298,6248,6198,6148,6098,6048, 5998,5948,5898,5848,5798,5748,5697,5647, 5597,5547,5497,5447,5397,5347,5297,5247, 5197,5146,5096,5046,4996,4946,4896,4846, 4796,4745,4695,4645,4595,4545,4495,4445, 4394,4344,4294,4244,4194,4144,4093,4043, 3993,3943,3893,3843,3792,3742,3692,3642, 3592,3541,3491,3441,3391,3341,3291,3240, 3190,3140,3090,3039,2989,2939,2889,2839, 2788,2738,2688,2638,2587,2537,2487,2437, 2387,2336,2286,2236,2186,2135,2085,2035, 1985,1934,1884,1834,1784,1733,1683,1633, 1583,1532,1482,1432,1382,1331,1281,1231, 1181,1130,1080,1030,980,929,879,829, 779,728,678,628,578,527,477,427, 376,326,276,226,175,125,75,25, -25,-75,-125,-175,-226,-276,-326,-376, -427,-477,-527,-578,-628,-678,-728,-779, -829,-879,-929,-980,-1030,-1080,-1130,-1181, -1231,-1281,-1331,-1382,-1432,-1482,-1532,-1583, -1633,-1683,-1733,-1784,-1834,-1884,-1934,-1985, -2035,-2085,-2135,-2186,-2236,-2286,-2336,-2387, -2437,-2487,-2537,-2588,-2638,-2688,-2738,-2788, -2839,-2889,-2939,-2989,-3039,-3090,-3140,-3190, -3240,-3291,-3341,-3391,-3441,-3491,-3541,-3592, -3642,-3692,-3742,-3792,-3843,-3893,-3943,-3993, -4043,-4093,-4144,-4194,-4244,-4294,-4344,-4394, -4445,-4495,-4545,-4595,-4645,-4695,-4745,-4796, -4846,-4896,-4946,-4996,-5046,-5096,-5146,-5197, -5247,-5297,-5347,-5397,-5447,-5497,-5547,-5597, -5647,-5697,-5748,-5798,-5848,-5898,-5948,-5998, -6048,-6098,-6148,-6198,-6248,-6298,-6348,-6398, -6448,-6498,-6548,-6598,-6648,-6698,-6748,-6798, -6848,-6898,-6948,-6998,-7048,-7098,-7148,-7198, -7248,-7298,-7348,-7398,-7448,-7498,-7548,-7598, -7648,-7697,-7747,-7797,-7847,-7897,-7947,-7997, -8047,-8097,-8147,-8196,-8246,-8296,-8346,-8396, -8446,-8496,-8545,-8595,-8645,-8695,-8745,-8794, -8844,-8894,-8944,-8994,-9043,-9093,-9143,-9193, -9243,-9292,-9342,-9392,-9442,-9491,-9541,-9591, -9640,-9690,-9740,-9790,-9839,-9889,-9939,-9988, -10038,-10088,-10137,-10187,-10237,-10286,-10336,-10386, -10435,-10485,-10534,-10584,-10634,-10683,-10733,-10782, -10832,-10882,-10931,-10981,-11030,-11080,-11129,-11179, -11228,-11278,-11327,-11377,-11426,-11476,-11525,-11575, -11624,-11674,-11723,-11773,-11822,-11872,-11921,-11970, -12020,-12069,-12119,-12168,-12218,-12267,-12316,-12366, -12415,-12464,-12514,-12563,-12612,-12662,-12711,-12760, -12810,-12859,-12908,-12957,-13007,-13056,-13105,-13154, -13204,-13253,-13302,-13351,-13401,-13450,-13499,-13548, -13597,-13647,-13696,-13745,-13794,-13843,-13892,-13941, -13990,-14040,-14089,-14138,-14187,-14236,-14285,-14334, -14383,-14432,-14481,-14530,-14579,-14628,-14677,-14726, -14775,-14824,-14873,-14922,-14971,-15020,-15069,-15118, -15167,-15215,-15264,-15313,-15362,-15411,-15460,-15509, -15557,-15606,-15655,-15704,-15753,-15802,-15850,-15899, -15948,-15997,-16045,-16094,-16143,-16191,-16240,-16289, -16338,-16386,-16435,-16484,-16532,-16581,-16629,-16678, -16727,-16775,-16824,-16872,-16921,-16970,-17018,-17067, -17115,-17164,-17212,-17261,-17309,-17358,-17406,-17455, -17503,-17551,-17600,-17648,-17697,-17745,-17793,-17842, -17890,-17939,-17987,-18035,-18084,-18132,-18180,-18228, -18277,-18325,-18373,-18421,-18470,-18518,-18566,-18614, -18663,-18711,-18759,-18807,-18855,-18903,-18951,-19000, -19048,-19096,-19144,-19192,-19240,-19288,-19336,-19384, -19432,-19480,-19528,-19576,-19624,-19672,-19720,-19768, -19816,-19864,-19912,-19959,-20007,-20055,-20103,-20151, -20199,-20246,-20294,-20342,-20390,-20438,-20485,-20533, -20581,-20629,-20676,-20724,-20772,-20819,-20867,-20915, -20962,-21010,-21057,-21105,-21153,-21200,-21248,-21295, -21343,-21390,-21438,-21485,-21533,-21580,-21628,-21675, -21723,-21770,-21817,-21865,-21912,-21960,-22007,-22054, -22102,-22149,-22196,-22243,-22291,-22338,-22385,-22433, -22480,-22527,-22574,-22621,-22668,-22716,-22763,-22810, -22857,-22904,-22951,-22998,-23045,-23092,-23139,-23186, -23233,-23280,-23327,-23374,-23421,-23468,-23515,-23562, -23609,-23656,-23703,-23750,-23796,-23843,-23890,-23937, -23984,-24030,-24077,-24124,-24171,-24217,-24264,-24311, -24357,-24404,-24451,-24497,-24544,-24591,-24637,-24684, -24730,-24777,-24823,-24870,-24916,-24963,-25009,-25056, -25102,-25149,-25195,-25241,-25288,-25334,-25381,-25427, -25473,-25520,-25566,-25612,-25658,-25705,-25751,-25797, -25843,-25889,-25936,-25982,-26028,-26074,-26120,-26166, -26212,-26258,-26304,-26350,-26396,-26442,-26488,-26534, -26580,-26626,-26672,-26718,-26764,-26810,-26856,-26902, -26947,-26993,-27039,-27085,-27131,-27176,-27222,-27268, -27313,-27359,-27405,-27450,-27496,-27542,-27587,-27633, -27678,-27724,-27770,-27815,-27861,-27906,-27952,-27997, -28042,-28088,-28133,-28179,-28224,-28269,-28315,-28360, -28405,-28451,-28496,-28541,-28586,-28632,-28677,-28722, -28767,-28812,-28858,-28903,-28948,-28993,-29038,-29083, -29128,-29173,-29218,-29263,-29308,-29353,-29398,-29443, -29488,-29533,-29577,-29622,-29667,-29712,-29757,-29801, -29846,-29891,-29936,-29980,-30025,-30070,-30114,-30159, -30204,-30248,-30293,-30337,-30382,-30426,-30471,-30515, -30560,-30604,-30649,-30693,-30738,-30782,-30826,-30871, -30915,-30959,-31004,-31048,-31092,-31136,-31181,-31225, -31269,-31313,-31357,-31402,-31446,-31490,-31534,-31578, -31622,-31666,-31710,-31754,-31798,-31842,-31886,-31930, -31974,-32017,-32061,-32105,-32149,-32193,-32236,-32280, -32324,-32368,-32411,-32455,-32499,-32542,-32586,-32630, -32673,-32717,-32760,-32804,-32847,-32891,-32934,-32978, -33021,-33065,-33108,-33151,-33195,-33238,-33281,-33325, -33368,-33411,-33454,-33498,-33541,-33584,-33627,-33670, -33713,-33756,-33799,-33843,-33886,-33929,-33972,-34015, -34057,-34100,-34143,-34186,-34229,-34272,-34315,-34358, -34400,-34443,-34486,-34529,-34571,-34614,-34657,-34699, -34742,-34785,-34827,-34870,-34912,-34955,-34997,-35040, -35082,-35125,-35167,-35210,-35252,-35294,-35337,-35379, -35421,-35464,-35506,-35548,-35590,-35633,-35675,-35717, -35759,-35801,-35843,-35885,-35927,-35969,-36011,-36053, -36095,-36137,-36179,-36221,-36263,-36305,-36347,-36388, -36430,-36472,-36514,-36555,-36597,-36639,-36681,-36722, -36764,-36805,-36847,-36889,-36930,-36972,-37013,-37055, -37096,-37137,-37179,-37220,-37262,-37303,-37344,-37386, -37427,-37468,-37509,-37551,-37592,-37633,-37674,-37715, -37756,-37797,-37838,-37879,-37920,-37961,-38002,-38043, -38084,-38125,-38166,-38207,-38248,-38288,-38329,-38370, -38411,-38451,-38492,-38533,-38573,-38614,-38655,-38695, -38736,-38776,-38817,-38857,-38898,-38938,-38979,-39019, -39059,-39100,-39140,-39180,-39221,-39261,-39301,-39341, -39382,-39422,-39462,-39502,-39542,-39582,-39622,-39662, -39702,-39742,-39782,-39822,-39862,-39902,-39942,-39982, -40021,-40061,-40101,-40141,-40180,-40220,-40260,-40299, -40339,-40379,-40418,-40458,-40497,-40537,-40576,-40616, -40655,-40695,-40734,-40773,-40813,-40852,-40891,-40931, -40970,-41009,-41048,-41087,-41127,-41166,-41205,-41244, -41283,-41322,-41361,-41400,-41439,-41478,-41517,-41556, -41595,-41633,-41672,-41711,-41750,-41788,-41827,-41866, -41904,-41943,-41982,-42020,-42059,-42097,-42136,-42174, -42213,-42251,-42290,-42328,-42366,-42405,-42443,-42481, -42520,-42558,-42596,-42634,-42672,-42711,-42749,-42787, -42825,-42863,-42901,-42939,-42977,-43015,-43053,-43091, -43128,-43166,-43204,-43242,-43280,-43317,-43355,-43393, -43430,-43468,-43506,-43543,-43581,-43618,-43656,-43693, -43731,-43768,-43806,-43843,-43880,-43918,-43955,-43992, -44029,-44067,-44104,-44141,-44178,-44215,-44252,-44289, -44326,-44363,-44400,-44437,-44474,-44511,-44548,-44585, -44622,-44659,-44695,-44732,-44769,-44806,-44842,-44879, -44915,-44952,-44989,-45025,-45062,-45098,-45135,-45171, -45207,-45244,-45280,-45316,-45353,-45389,-45425,-45462, -45498,-45534,-45570,-45606,-45642,-45678,-45714,-45750, -45786,-45822,-45858,-45894,-45930,-45966,-46002,-46037, -46073,-46109,-46145,-46180,-46216,-46252,-46287,-46323, -46358,-46394,-46429,-46465,-46500,-46536,-46571,-46606, -46642,-46677,-46712,-46747,-46783,-46818,-46853,-46888, -46923,-46958,-46993,-47028,-47063,-47098,-47133,-47168, -47203,-47238,-47273,-47308,-47342,-47377,-47412,-47446, -47481,-47516,-47550,-47585,-47619,-47654,-47688,-47723, -47757,-47792,-47826,-47860,-47895,-47929,-47963,-47998, -48032,-48066,-48100,-48134,-48168,-48202,-48236,-48271, -48304,-48338,-48372,-48406,-48440,-48474,-48508,-48542, -48575,-48609,-48643,-48676,-48710,-48744,-48777,-48811, -48844,-48878,-48911,-48945,-48978,-49012,-49045,-49078, -49112,-49145,-49178,-49211,-49244,-49278,-49311,-49344, -49377,-49410,-49443,-49476,-49509,-49542,-49575,-49608, -49640,-49673,-49706,-49739,-49771,-49804,-49837,-49869, -49902,-49935,-49967,-50000,-50032,-50065,-50097,-50129, -50162,-50194,-50226,-50259,-50291,-50323,-50355,-50387, -50420,-50452,-50484,-50516,-50548,-50580,-50612,-50644, -50675,-50707,-50739,-50771,-50803,-50834,-50866,-50898, -50929,-50961,-50993,-51024,-51056,-51087,-51119,-51150, -51182,-51213,-51244,-51276,-51307,-51338,-51369,-51401, -51432,-51463,-51494,-51525,-51556,-51587,-51618,-51649, -51680,-51711,-51742,-51773,-51803,-51834,-51865,-51896, -51926,-51957,-51988,-52018,-52049,-52079,-52110,-52140, -52171,-52201,-52231,-52262,-52292,-52322,-52353,-52383, -52413,-52443,-52473,-52503,-52534,-52564,-52594,-52624, -52653,-52683,-52713,-52743,-52773,-52803,-52832,-52862, -52892,-52922,-52951,-52981,-53010,-53040,-53069,-53099, -53128,-53158,-53187,-53216,-53246,-53275,-53304,-53334, -53363,-53392,-53421,-53450,-53479,-53508,-53537,-53566, -53595,-53624,-53653,-53682,-53711,-53739,-53768,-53797, -53826,-53854,-53883,-53911,-53940,-53969,-53997,-54026, -54054,-54082,-54111,-54139,-54167,-54196,-54224,-54252, -54280,-54308,-54337,-54365,-54393,-54421,-54449,-54477, -54505,-54533,-54560,-54588,-54616,-54644,-54672,-54699, -54727,-54755,-54782,-54810,-54837,-54865,-54892,-54920, -54947,-54974,-55002,-55029,-55056,-55084,-55111,-55138, -55165,-55192,-55219,-55246,-55274,-55300,-55327,-55354, -55381,-55408,-55435,-55462,-55489,-55515,-55542,-55569, -55595,-55622,-55648,-55675,-55701,-55728,-55754,-55781, -55807,-55833,-55860,-55886,-55912,-55938,-55965,-55991, -56017,-56043,-56069,-56095,-56121,-56147,-56173,-56199, -56225,-56250,-56276,-56302,-56328,-56353,-56379,-56404, -56430,-56456,-56481,-56507,-56532,-56557,-56583,-56608, -56633,-56659,-56684,-56709,-56734,-56760,-56785,-56810, -56835,-56860,-56885,-56910,-56935,-56959,-56984,-57009, -57034,-57059,-57083,-57108,-57133,-57157,-57182,-57206, -57231,-57255,-57280,-57304,-57329,-57353,-57377,-57402, -57426,-57450,-57474,-57498,-57522,-57546,-57570,-57594, -57618,-57642,-57666,-57690,-57714,-57738,-57762,-57785, -57809,-57833,-57856,-57880,-57903,-57927,-57950,-57974, -57997,-58021,-58044,-58067,-58091,-58114,-58137,-58160, -58183,-58207,-58230,-58253,-58276,-58299,-58322,-58345, -58367,-58390,-58413,-58436,-58459,-58481,-58504,-58527, -58549,-58572,-58594,-58617,-58639,-58662,-58684,-58706, -58729,-58751,-58773,-58795,-58818,-58840,-58862,-58884, -58906,-58928,-58950,-58972,-58994,-59016,-59038,-59059, -59081,-59103,-59125,-59146,-59168,-59190,-59211,-59233, -59254,-59276,-59297,-59318,-59340,-59361,-59382,-59404, -59425,-59446,-59467,-59488,-59509,-59530,-59551,-59572, -59593,-59614,-59635,-59656,-59677,-59697,-59718,-59739, -59759,-59780,-59801,-59821,-59842,-59862,-59883,-59903, -59923,-59944,-59964,-59984,-60004,-60025,-60045,-60065, -60085,-60105,-60125,-60145,-60165,-60185,-60205,-60225, -60244,-60264,-60284,-60304,-60323,-60343,-60363,-60382, -60402,-60421,-60441,-60460,-60479,-60499,-60518,-60537, -60556,-60576,-60595,-60614,-60633,-60652,-60671,-60690, -60709,-60728,-60747,-60766,-60785,-60803,-60822,-60841, -60859,-60878,-60897,-60915,-60934,-60952,-60971,-60989, -61007,-61026,-61044,-61062,-61081,-61099,-61117,-61135, -61153,-61171,-61189,-61207,-61225,-61243,-61261,-61279, -61297,-61314,-61332,-61350,-61367,-61385,-61403,-61420, -61438,-61455,-61473,-61490,-61507,-61525,-61542,-61559, -61577,-61594,-61611,-61628,-61645,-61662,-61679,-61696, -61713,-61730,-61747,-61764,-61780,-61797,-61814,-61831, -61847,-61864,-61880,-61897,-61913,-61930,-61946,-61963, -61979,-61995,-62012,-62028,-62044,-62060,-62076,-62092, -62108,-62125,-62141,-62156,-62172,-62188,-62204,-62220, -62236,-62251,-62267,-62283,-62298,-62314,-62329,-62345, -62360,-62376,-62391,-62407,-62422,-62437,-62453,-62468, -62483,-62498,-62513,-62528,-62543,-62558,-62573,-62588, -62603,-62618,-62633,-62648,-62662,-62677,-62692,-62706, -62721,-62735,-62750,-62764,-62779,-62793,-62808,-62822, -62836,-62850,-62865,-62879,-62893,-62907,-62921,-62935, -62949,-62963,-62977,-62991,-63005,-63019,-63032,-63046, -63060,-63074,-63087,-63101,-63114,-63128,-63141,-63155, -63168,-63182,-63195,-63208,-63221,-63235,-63248,-63261, -63274,-63287,-63300,-63313,-63326,-63339,-63352,-63365, -63378,-63390,-63403,-63416,-63429,-63441,-63454,-63466, -63479,-63491,-63504,-63516,-63528,-63541,-63553,-63565, -63578,-63590,-63602,-63614,-63626,-63638,-63650,-63662, -63674,-63686,-63698,-63709,-63721,-63733,-63745,-63756, -63768,-63779,-63791,-63803,-63814,-63825,-63837,-63848, -63859,-63871,-63882,-63893,-63904,-63915,-63927,-63938, -63949,-63960,-63971,-63981,-63992,-64003,-64014,-64025, -64035,-64046,-64057,-64067,-64078,-64088,-64099,-64109, -64120,-64130,-64140,-64151,-64161,-64171,-64181,-64192, -64202,-64212,-64222,-64232,-64242,-64252,-64261,-64271, -64281,-64291,-64301,-64310,-64320,-64330,-64339,-64349, -64358,-64368,-64377,-64387,-64396,-64405,-64414,-64424, -64433,-64442,-64451,-64460,-64469,-64478,-64487,-64496, -64505,-64514,-64523,-64532,-64540,-64549,-64558,-64566, -64575,-64584,-64592,-64601,-64609,-64617,-64626,-64634, -64642,-64651,-64659,-64667,-64675,-64683,-64691,-64699, -64707,-64715,-64723,-64731,-64739,-64747,-64754,-64762, -64770,-64777,-64785,-64793,-64800,-64808,-64815,-64822, -64830,-64837,-64844,-64852,-64859,-64866,-64873,-64880, -64887,-64895,-64902,-64908,-64915,-64922,-64929,-64936, -64943,-64949,-64956,-64963,-64969,-64976,-64982,-64989, -64995,-65002,-65008,-65015,-65021,-65027,-65033,-65040, -65046,-65052,-65058,-65064,-65070,-65076,-65082,-65088, -65094,-65099,-65105,-65111,-65117,-65122,-65128,-65133, -65139,-65144,-65150,-65155,-65161,-65166,-65171,-65177, -65182,-65187,-65192,-65197,-65202,-65207,-65212,-65217, -65222,-65227,-65232,-65237,-65242,-65246,-65251,-65256, -65260,-65265,-65270,-65274,-65279,-65283,-65287,-65292, -65296,-65300,-65305,-65309,-65313,-65317,-65321,-65325, -65329,-65333,-65337,-65341,-65345,-65349,-65352,-65356, -65360,-65363,-65367,-65371,-65374,-65378,-65381,-65385, -65388,-65391,-65395,-65398,-65401,-65404,-65408,-65411, -65414,-65417,-65420,-65423,-65426,-65429,-65431,-65434, -65437,-65440,-65442,-65445,-65448,-65450,-65453,-65455, -65458,-65460,-65463,-65465,-65467,-65470,-65472,-65474, -65476,-65478,-65480,-65482,-65484,-65486,-65488,-65490, -65492,-65494,-65496,-65497,-65499,-65501,-65502,-65504, -65505,-65507,-65508,-65510,-65511,-65513,-65514,-65515, -65516,-65518,-65519,-65520,-65521,-65522,-65523,-65524, -65525,-65526,-65527,-65527,-65528,-65529,-65530,-65530, -65531,-65531,-65532,-65532,-65533,-65533,-65534,-65534, -65534,-65535,-65535,-65535,-65535,-65535,-65535,-65535, -65535,-65535,-65535,-65535,-65535,-65535,-65535,-65534, -65534,-65534,-65533,-65533,-65532,-65532,-65531,-65531, -65530,-65530,-65529,-65528,-65527,-65527,-65526,-65525, -65524,-65523,-65522,-65521,-65520,-65519,-65518,-65516, -65515,-65514,-65513,-65511,-65510,-65508,-65507,-65505, -65504,-65502,-65501,-65499,-65497,-65496,-65494,-65492, -65490,-65488,-65486,-65484,-65482,-65480,-65478,-65476, -65474,-65472,-65470,-65467,-65465,-65463,-65460,-65458, -65455,-65453,-65450,-65448,-65445,-65442,-65440,-65437, -65434,-65431,-65429,-65426,-65423,-65420,-65417,-65414, -65411,-65408,-65404,-65401,-65398,-65395,-65391,-65388, -65385,-65381,-65378,-65374,-65371,-65367,-65363,-65360, -65356,-65352,-65349,-65345,-65341,-65337,-65333,-65329, -65325,-65321,-65317,-65313,-65309,-65305,-65300,-65296, -65292,-65287,-65283,-65279,-65274,-65270,-65265,-65260, -65256,-65251,-65246,-65242,-65237,-65232,-65227,-65222, -65217,-65212,-65207,-65202,-65197,-65192,-65187,-65182, -65177,-65171,-65166,-65161,-65155,-65150,-65144,-65139, -65133,-65128,-65122,-65117,-65111,-65105,-65099,-65094, -65088,-65082,-65076,-65070,-65064,-65058,-65052,-65046, -65040,-65033,-65027,-65021,-65015,-65008,-65002,-64995, -64989,-64982,-64976,-64969,-64963,-64956,-64949,-64943, -64936,-64929,-64922,-64915,-64908,-64902,-64895,-64887, -64880,-64873,-64866,-64859,-64852,-64844,-64837,-64830, -64822,-64815,-64808,-64800,-64793,-64785,-64777,-64770, -64762,-64754,-64747,-64739,-64731,-64723,-64715,-64707, -64699,-64691,-64683,-64675,-64667,-64659,-64651,-64642, -64634,-64626,-64617,-64609,-64601,-64592,-64584,-64575, -64566,-64558,-64549,-64540,-64532,-64523,-64514,-64505, -64496,-64487,-64478,-64469,-64460,-64451,-64442,-64433, -64424,-64414,-64405,-64396,-64387,-64377,-64368,-64358, -64349,-64339,-64330,-64320,-64310,-64301,-64291,-64281, -64271,-64261,-64252,-64242,-64232,-64222,-64212,-64202, -64192,-64181,-64171,-64161,-64151,-64140,-64130,-64120, -64109,-64099,-64088,-64078,-64067,-64057,-64046,-64035, -64025,-64014,-64003,-63992,-63981,-63971,-63960,-63949, -63938,-63927,-63915,-63904,-63893,-63882,-63871,-63859, -63848,-63837,-63825,-63814,-63803,-63791,-63779,-63768, -63756,-63745,-63733,-63721,-63709,-63698,-63686,-63674, -63662,-63650,-63638,-63626,-63614,-63602,-63590,-63578, -63565,-63553,-63541,-63528,-63516,-63504,-63491,-63479, -63466,-63454,-63441,-63429,-63416,-63403,-63390,-63378, -63365,-63352,-63339,-63326,-63313,-63300,-63287,-63274, -63261,-63248,-63235,-63221,-63208,-63195,-63182,-63168, -63155,-63141,-63128,-63114,-63101,-63087,-63074,-63060, -63046,-63032,-63019,-63005,-62991,-62977,-62963,-62949, -62935,-62921,-62907,-62893,-62879,-62865,-62850,-62836, -62822,-62808,-62793,-62779,-62764,-62750,-62735,-62721, -62706,-62692,-62677,-62662,-62648,-62633,-62618,-62603, -62588,-62573,-62558,-62543,-62528,-62513,-62498,-62483, -62468,-62453,-62437,-62422,-62407,-62391,-62376,-62360, -62345,-62329,-62314,-62298,-62283,-62267,-62251,-62236, -62220,-62204,-62188,-62172,-62156,-62141,-62125,-62108, -62092,-62076,-62060,-62044,-62028,-62012,-61995,-61979, -61963,-61946,-61930,-61913,-61897,-61880,-61864,-61847, -61831,-61814,-61797,-61780,-61764,-61747,-61730,-61713, -61696,-61679,-61662,-61645,-61628,-61611,-61594,-61577, -61559,-61542,-61525,-61507,-61490,-61473,-61455,-61438, -61420,-61403,-61385,-61367,-61350,-61332,-61314,-61297, -61279,-61261,-61243,-61225,-61207,-61189,-61171,-61153, -61135,-61117,-61099,-61081,-61062,-61044,-61026,-61007, -60989,-60971,-60952,-60934,-60915,-60897,-60878,-60859, -60841,-60822,-60803,-60785,-60766,-60747,-60728,-60709, -60690,-60671,-60652,-60633,-60614,-60595,-60576,-60556, -60537,-60518,-60499,-60479,-60460,-60441,-60421,-60402, -60382,-60363,-60343,-60323,-60304,-60284,-60264,-60244, -60225,-60205,-60185,-60165,-60145,-60125,-60105,-60085, -60065,-60045,-60025,-60004,-59984,-59964,-59944,-59923, -59903,-59883,-59862,-59842,-59821,-59801,-59780,-59759, -59739,-59718,-59697,-59677,-59656,-59635,-59614,-59593, -59572,-59551,-59530,-59509,-59488,-59467,-59446,-59425, -59404,-59382,-59361,-59340,-59318,-59297,-59276,-59254, -59233,-59211,-59189,-59168,-59146,-59125,-59103,-59081, -59059,-59038,-59016,-58994,-58972,-58950,-58928,-58906, -58884,-58862,-58840,-58818,-58795,-58773,-58751,-58729, -58706,-58684,-58662,-58639,-58617,-58594,-58572,-58549, -58527,-58504,-58481,-58459,-58436,-58413,-58390,-58367, -58345,-58322,-58299,-58276,-58253,-58230,-58207,-58183, -58160,-58137,-58114,-58091,-58067,-58044,-58021,-57997, -57974,-57950,-57927,-57903,-57880,-57856,-57833,-57809, -57785,-57762,-57738,-57714,-57690,-57666,-57642,-57618, -57594,-57570,-57546,-57522,-57498,-57474,-57450,-57426, -57402,-57377,-57353,-57329,-57304,-57280,-57255,-57231, -57206,-57182,-57157,-57133,-57108,-57083,-57059,-57034, -57009,-56984,-56959,-56935,-56910,-56885,-56860,-56835, -56810,-56785,-56760,-56734,-56709,-56684,-56659,-56633, -56608,-56583,-56557,-56532,-56507,-56481,-56456,-56430, -56404,-56379,-56353,-56328,-56302,-56276,-56250,-56225, -56199,-56173,-56147,-56121,-56095,-56069,-56043,-56017, -55991,-55965,-55938,-55912,-55886,-55860,-55833,-55807, -55781,-55754,-55728,-55701,-55675,-55648,-55622,-55595, -55569,-55542,-55515,-55489,-55462,-55435,-55408,-55381, -55354,-55327,-55300,-55274,-55246,-55219,-55192,-55165, -55138,-55111,-55084,-55056,-55029,-55002,-54974,-54947, -54920,-54892,-54865,-54837,-54810,-54782,-54755,-54727, -54699,-54672,-54644,-54616,-54588,-54560,-54533,-54505, -54477,-54449,-54421,-54393,-54365,-54337,-54308,-54280, -54252,-54224,-54196,-54167,-54139,-54111,-54082,-54054, -54026,-53997,-53969,-53940,-53911,-53883,-53854,-53826, -53797,-53768,-53739,-53711,-53682,-53653,-53624,-53595, -53566,-53537,-53508,-53479,-53450,-53421,-53392,-53363, -53334,-53304,-53275,-53246,-53216,-53187,-53158,-53128, -53099,-53069,-53040,-53010,-52981,-52951,-52922,-52892, -52862,-52832,-52803,-52773,-52743,-52713,-52683,-52653, -52624,-52594,-52564,-52534,-52503,-52473,-52443,-52413, -52383,-52353,-52322,-52292,-52262,-52231,-52201,-52171, -52140,-52110,-52079,-52049,-52018,-51988,-51957,-51926, -51896,-51865,-51834,-51803,-51773,-51742,-51711,-51680, -51649,-51618,-51587,-51556,-51525,-51494,-51463,-51432, -51401,-51369,-51338,-51307,-51276,-51244,-51213,-51182, -51150,-51119,-51087,-51056,-51024,-50993,-50961,-50929, -50898,-50866,-50834,-50803,-50771,-50739,-50707,-50675, -50644,-50612,-50580,-50548,-50516,-50484,-50452,-50420, -50387,-50355,-50323,-50291,-50259,-50226,-50194,-50162, -50129,-50097,-50065,-50032,-50000,-49967,-49935,-49902, -49869,-49837,-49804,-49771,-49739,-49706,-49673,-49640, -49608,-49575,-49542,-49509,-49476,-49443,-49410,-49377, -49344,-49311,-49278,-49244,-49211,-49178,-49145,-49112, -49078,-49045,-49012,-48978,-48945,-48911,-48878,-48844, -48811,-48777,-48744,-48710,-48676,-48643,-48609,-48575, -48542,-48508,-48474,-48440,-48406,-48372,-48338,-48305, -48271,-48237,-48202,-48168,-48134,-48100,-48066,-48032, -47998,-47963,-47929,-47895,-47860,-47826,-47792,-47757, -47723,-47688,-47654,-47619,-47585,-47550,-47516,-47481, -47446,-47412,-47377,-47342,-47307,-47273,-47238,-47203, -47168,-47133,-47098,-47063,-47028,-46993,-46958,-46923, -46888,-46853,-46818,-46783,-46747,-46712,-46677,-46642, -46606,-46571,-46536,-46500,-46465,-46429,-46394,-46358, -46323,-46287,-46251,-46216,-46180,-46145,-46109,-46073, -46037,-46002,-45966,-45930,-45894,-45858,-45822,-45786, -45750,-45714,-45678,-45642,-45606,-45570,-45534,-45498, -45462,-45425,-45389,-45353,-45316,-45280,-45244,-45207, -45171,-45135,-45098,-45062,-45025,-44989,-44952,-44915, -44879,-44842,-44806,-44769,-44732,-44695,-44659,-44622, -44585,-44548,-44511,-44474,-44437,-44400,-44363,-44326, -44289,-44252,-44215,-44178,-44141,-44104,-44067,-44029, -43992,-43955,-43918,-43880,-43843,-43806,-43768,-43731, -43693,-43656,-43618,-43581,-43543,-43506,-43468,-43430, -43393,-43355,-43317,-43280,-43242,-43204,-43166,-43128, -43091,-43053,-43015,-42977,-42939,-42901,-42863,-42825, -42787,-42749,-42711,-42672,-42634,-42596,-42558,-42520, -42481,-42443,-42405,-42366,-42328,-42290,-42251,-42213, -42174,-42136,-42097,-42059,-42020,-41982,-41943,-41904, -41866,-41827,-41788,-41750,-41711,-41672,-41633,-41595, -41556,-41517,-41478,-41439,-41400,-41361,-41322,-41283, -41244,-41205,-41166,-41127,-41087,-41048,-41009,-40970, -40931,-40891,-40852,-40813,-40773,-40734,-40695,-40655, -40616,-40576,-40537,-40497,-40458,-40418,-40379,-40339, -40299,-40260,-40220,-40180,-40141,-40101,-40061,-40021, -39982,-39942,-39902,-39862,-39822,-39782,-39742,-39702, -39662,-39622,-39582,-39542,-39502,-39462,-39422,-39382, -39341,-39301,-39261,-39221,-39180,-39140,-39100,-39059, -39019,-38979,-38938,-38898,-38857,-38817,-38776,-38736, -38695,-38655,-38614,-38573,-38533,-38492,-38451,-38411, -38370,-38329,-38288,-38248,-38207,-38166,-38125,-38084, -38043,-38002,-37961,-37920,-37879,-37838,-37797,-37756, -37715,-37674,-37633,-37592,-37550,-37509,-37468,-37427, -37386,-37344,-37303,-37262,-37220,-37179,-37137,-37096, -37055,-37013,-36972,-36930,-36889,-36847,-36805,-36764, -36722,-36681,-36639,-36597,-36556,-36514,-36472,-36430, -36388,-36347,-36305,-36263,-36221,-36179,-36137,-36095, -36053,-36011,-35969,-35927,-35885,-35843,-35801,-35759, -35717,-35675,-35633,-35590,-35548,-35506,-35464,-35421, -35379,-35337,-35294,-35252,-35210,-35167,-35125,-35082, -35040,-34997,-34955,-34912,-34870,-34827,-34785,-34742, -34699,-34657,-34614,-34571,-34529,-34486,-34443,-34400, -34358,-34315,-34272,-34229,-34186,-34143,-34100,-34057, -34015,-33972,-33929,-33886,-33843,-33799,-33756,-33713, -33670,-33627,-33584,-33541,-33498,-33454,-33411,-33368, -33325,-33281,-33238,-33195,-33151,-33108,-33065,-33021, -32978,-32934,-32891,-32847,-32804,-32760,-32717,-32673, -32630,-32586,-32542,-32499,-32455,-32411,-32368,-32324, -32280,-32236,-32193,-32149,-32105,-32061,-32017,-31974, -31930,-31886,-31842,-31798,-31754,-31710,-31666,-31622, -31578,-31534,-31490,-31446,-31402,-31357,-31313,-31269, -31225,-31181,-31136,-31092,-31048,-31004,-30959,-30915, -30871,-30826,-30782,-30738,-30693,-30649,-30604,-30560, -30515,-30471,-30426,-30382,-30337,-30293,-30248,-30204, -30159,-30114,-30070,-30025,-29980,-29936,-29891,-29846, -29801,-29757,-29712,-29667,-29622,-29577,-29533,-29488, -29443,-29398,-29353,-29308,-29263,-29218,-29173,-29128, -29083,-29038,-28993,-28948,-28903,-28858,-28812,-28767, -28722,-28677,-28632,-28586,-28541,-28496,-28451,-28405, -28360,-28315,-28269,-28224,-28179,-28133,-28088,-28042, -27997,-27952,-27906,-27861,-27815,-27770,-27724,-27678, -27633,-27587,-27542,-27496,-27450,-27405,-27359,-27313, -27268,-27222,-27176,-27131,-27085,-27039,-26993,-26947, -26902,-26856,-26810,-26764,-26718,-26672,-26626,-26580, -26534,-26488,-26442,-26396,-26350,-26304,-26258,-26212, -26166,-26120,-26074,-26028,-25982,-25936,-25889,-25843, -25797,-25751,-25705,-25658,-25612,-25566,-25520,-25473, -25427,-25381,-25334,-25288,-25241,-25195,-25149,-25102, -25056,-25009,-24963,-24916,-24870,-24823,-24777,-24730, -24684,-24637,-24591,-24544,-24497,-24451,-24404,-24357, -24311,-24264,-24217,-24171,-24124,-24077,-24030,-23984, -23937,-23890,-23843,-23796,-23750,-23703,-23656,-23609, -23562,-23515,-23468,-23421,-23374,-23327,-23280,-23233, -23186,-23139,-23092,-23045,-22998,-22951,-22904,-22857, -22810,-22763,-22716,-22668,-22621,-22574,-22527,-22480, -22432,-22385,-22338,-22291,-22243,-22196,-22149,-22102, -22054,-22007,-21960,-21912,-21865,-21817,-21770,-21723, -21675,-21628,-21580,-21533,-21485,-21438,-21390,-21343, -21295,-21248,-21200,-21153,-21105,-21057,-21010,-20962, -20915,-20867,-20819,-20772,-20724,-20676,-20629,-20581, -20533,-20485,-20438,-20390,-20342,-20294,-20246,-20199, -20151,-20103,-20055,-20007,-19959,-19912,-19864,-19816, -19768,-19720,-19672,-19624,-19576,-19528,-19480,-19432, -19384,-19336,-19288,-19240,-19192,-19144,-19096,-19048, -19000,-18951,-18903,-18855,-18807,-18759,-18711,-18663, -18614,-18566,-18518,-18470,-18421,-18373,-18325,-18277, -18228,-18180,-18132,-18084,-18035,-17987,-17939,-17890, -17842,-17793,-17745,-17697,-17648,-17600,-17551,-17503, -17455,-17406,-17358,-17309,-17261,-17212,-17164,-17115, -17067,-17018,-16970,-16921,-16872,-16824,-16775,-16727, -16678,-16629,-16581,-16532,-16484,-16435,-16386,-16338, -16289,-16240,-16191,-16143,-16094,-16045,-15997,-15948, -15899,-15850,-15802,-15753,-15704,-15655,-15606,-15557, -15509,-15460,-15411,-15362,-15313,-15264,-15215,-15167, -15118,-15069,-15020,-14971,-14922,-14873,-14824,-14775, -14726,-14677,-14628,-14579,-14530,-14481,-14432,-14383, -14334,-14285,-14236,-14187,-14138,-14089,-14040,-13990, -13941,-13892,-13843,-13794,-13745,-13696,-13647,-13597, -13548,-13499,-13450,-13401,-13351,-13302,-13253,-13204, -13154,-13105,-13056,-13007,-12957,-12908,-12859,-12810, -12760,-12711,-12662,-12612,-12563,-12514,-12464,-12415, -12366,-12316,-12267,-12217,-12168,-12119,-12069,-12020, -11970,-11921,-11872,-11822,-11773,-11723,-11674,-11624, -11575,-11525,-11476,-11426,-11377,-11327,-11278,-11228, -11179,-11129,-11080,-11030,-10981,-10931,-10882,-10832, -10782,-10733,-10683,-10634,-10584,-10534,-10485,-10435, -10386,-10336,-10286,-10237,-10187,-10137,-10088,-10038, -9988,-9939,-9889,-9839,-9790,-9740,-9690,-9640, -9591,-9541,-9491,-9442,-9392,-9342,-9292,-9243, -9193,-9143,-9093,-9043,-8994,-8944,-8894,-8844, -8794,-8745,-8695,-8645,-8595,-8545,-8496,-8446, -8396,-8346,-8296,-8246,-8196,-8147,-8097,-8047, -7997,-7947,-7897,-7847,-7797,-7747,-7697,-7648, -7598,-7548,-7498,-7448,-7398,-7348,-7298,-7248, -7198,-7148,-7098,-7048,-6998,-6948,-6898,-6848, -6798,-6748,-6698,-6648,-6598,-6548,-6498,-6448, -6398,-6348,-6298,-6248,-6198,-6148,-6098,-6048, -5998,-5948,-5898,-5848,-5798,-5747,-5697,-5647, -5597,-5547,-5497,-5447,-5397,-5347,-5297,-5247, -5197,-5146,-5096,-5046,-4996,-4946,-4896,-4846, -4796,-4745,-4695,-4645,-4595,-4545,-4495,-4445, -4394,-4344,-4294,-4244,-4194,-4144,-4093,-4043, -3993,-3943,-3893,-3843,-3792,-3742,-3692,-3642, -3592,-3541,-3491,-3441,-3391,-3341,-3291,-3240, -3190,-3140,-3090,-3039,-2989,-2939,-2889,-2839, -2788,-2738,-2688,-2638,-2588,-2537,-2487,-2437, -2387,-2336,-2286,-2236,-2186,-2135,-2085,-2035, -1985,-1934,-1884,-1834,-1784,-1733,-1683,-1633, -1583,-1532,-1482,-1432,-1382,-1331,-1281,-1231, -1181,-1130,-1080,-1030,-980,-929,-879,-829, -779,-728,-678,-628,-578,-527,-477,-427, -376,-326,-276,-226,-175,-125,-75,-25, 25,75,125,175,226,276,326,376, 427,477,527,578,628,678,728,779, 829,879,929,980,1030,1080,1130,1181, 1231,1281,1331,1382,1432,1482,1532,1583, 1633,1683,1733,1784,1834,1884,1934,1985, 2035,2085,2135,2186,2236,2286,2336,2387, 2437,2487,2537,2587,2638,2688,2738,2788, 2839,2889,2939,2989,3039,3090,3140,3190, 3240,3291,3341,3391,3441,3491,3542,3592, 3642,3692,3742,3792,3843,3893,3943,3993, 4043,4093,4144,4194,4244,4294,4344,4394, 4445,4495,4545,4595,4645,4695,4745,4796, 4846,4896,4946,4996,5046,5096,5146,5197, 5247,5297,5347,5397,5447,5497,5547,5597, 5647,5697,5747,5798,5848,5898,5948,5998, 6048,6098,6148,6198,6248,6298,6348,6398, 6448,6498,6548,6598,6648,6698,6748,6798, 6848,6898,6948,6998,7048,7098,7148,7198, 7248,7298,7348,7398,7448,7498,7548,7598, 7648,7697,7747,7797,7847,7897,7947,7997, 8047,8097,8147,8196,8246,8296,8346,8396, 8446,8496,8545,8595,8645,8695,8745,8794, 8844,8894,8944,8994,9043,9093,9143,9193, 9243,9292,9342,9392,9442,9491,9541,9591, 9640,9690,9740,9790,9839,9889,9939,9988, 10038,10088,10137,10187,10237,10286,10336,10386, 10435,10485,10534,10584,10634,10683,10733,10782, 10832,10882,10931,10981,11030,11080,11129,11179, 11228,11278,11327,11377,11426,11476,11525,11575, 11624,11674,11723,11773,11822,11872,11921,11970, 12020,12069,12119,12168,12218,12267,12316,12366, 12415,12464,12514,12563,12612,12662,12711,12760, 12810,12859,12908,12957,13007,13056,13105,13154, 13204,13253,13302,13351,13401,13450,13499,13548, 13597,13647,13696,13745,13794,13843,13892,13941, 13990,14040,14089,14138,14187,14236,14285,14334, 14383,14432,14481,14530,14579,14628,14677,14726, 14775,14824,14873,14922,14971,15020,15069,15118, 15167,15215,15264,15313,15362,15411,15460,15509, 15557,15606,15655,15704,15753,15802,15850,15899, 15948,15997,16045,16094,16143,16191,16240,16289, 16338,16386,16435,16484,16532,16581,16629,16678, 16727,16775,16824,16872,16921,16970,17018,17067, 17115,17164,17212,17261,17309,17358,17406,17455, 17503,17551,17600,17648,17697,17745,17793,17842, 17890,17939,17987,18035,18084,18132,18180,18228, 18277,18325,18373,18421,18470,18518,18566,18614, 18663,18711,18759,18807,18855,18903,18951,19000, 19048,19096,19144,19192,19240,19288,19336,19384, 19432,19480,19528,19576,19624,19672,19720,19768, 19816,19864,19912,19959,20007,20055,20103,20151, 20199,20246,20294,20342,20390,20438,20485,20533, 20581,20629,20676,20724,20772,20819,20867,20915, 20962,21010,21057,21105,21153,21200,21248,21295, 21343,21390,21438,21485,21533,21580,21628,21675, 21723,21770,21817,21865,21912,21960,22007,22054, 22102,22149,22196,22243,22291,22338,22385,22432, 22480,22527,22574,22621,22668,22716,22763,22810, 22857,22904,22951,22998,23045,23092,23139,23186, 23233,23280,23327,23374,23421,23468,23515,23562, 23609,23656,23703,23750,23796,23843,23890,23937, 23984,24030,24077,24124,24171,24217,24264,24311, 24357,24404,24451,24497,24544,24591,24637,24684, 24730,24777,24823,24870,24916,24963,25009,25056, 25102,25149,25195,25241,25288,25334,25381,25427, 25473,25520,25566,25612,25658,25705,25751,25797, 25843,25889,25936,25982,26028,26074,26120,26166, 26212,26258,26304,26350,26396,26442,26488,26534, 26580,26626,26672,26718,26764,26810,26856,26902, 26947,26993,27039,27085,27131,27176,27222,27268, 27313,27359,27405,27450,27496,27542,27587,27633, 27678,27724,27770,27815,27861,27906,27952,27997, 28042,28088,28133,28179,28224,28269,28315,28360, 28405,28451,28496,28541,28586,28632,28677,28722, 28767,28812,28858,28903,28948,28993,29038,29083, 29128,29173,29218,29263,29308,29353,29398,29443, 29488,29533,29577,29622,29667,29712,29757,29801, 29846,29891,29936,29980,30025,30070,30114,30159, 30204,30248,30293,30337,30382,30427,30471,30516, 30560,30604,30649,30693,30738,30782,30826,30871, 30915,30959,31004,31048,31092,31136,31181,31225, 31269,31313,31357,31402,31446,31490,31534,31578, 31622,31666,31710,31754,31798,31842,31886,31930, 31974,32017,32061,32105,32149,32193,32236,32280, 32324,32368,32411,32455,32499,32542,32586,32630, 32673,32717,32760,32804,32847,32891,32934,32978, 33021,33065,33108,33151,33195,33238,33281,33325, 33368,33411,33454,33498,33541,33584,33627,33670, 33713,33756,33799,33843,33886,33929,33972,34015, 34057,34100,34143,34186,34229,34272,34315,34358, 34400,34443,34486,34529,34571,34614,34657,34699, 34742,34785,34827,34870,34912,34955,34997,35040, 35082,35125,35167,35210,35252,35294,35337,35379, 35421,35464,35506,35548,35590,35633,35675,35717, 35759,35801,35843,35885,35927,35969,36011,36053, 36095,36137,36179,36221,36263,36305,36347,36388, 36430,36472,36514,36556,36597,36639,36681,36722, 36764,36805,36847,36889,36930,36972,37013,37055, 37096,37137,37179,37220,37262,37303,37344,37386, 37427,37468,37509,37551,37592,37633,37674,37715, 37756,37797,37838,37879,37920,37961,38002,38043, 38084,38125,38166,38207,38248,38288,38329,38370, 38411,38451,38492,38533,38573,38614,38655,38695, 38736,38776,38817,38857,38898,38938,38979,39019, 39059,39100,39140,39180,39221,39261,39301,39341, 39382,39422,39462,39502,39542,39582,39622,39662, 39702,39742,39782,39822,39862,39902,39942,39982, 40021,40061,40101,40141,40180,40220,40260,40299, 40339,40379,40418,40458,40497,40537,40576,40616, 40655,40695,40734,40773,40813,40852,40891,40931, 40970,41009,41048,41087,41127,41166,41205,41244, 41283,41322,41361,41400,41439,41478,41517,41556, 41595,41633,41672,41711,41750,41788,41827,41866, 41904,41943,41982,42020,42059,42097,42136,42174, 42213,42251,42290,42328,42366,42405,42443,42481, 42520,42558,42596,42634,42672,42711,42749,42787, 42825,42863,42901,42939,42977,43015,43053,43091, 43128,43166,43204,43242,43280,43317,43355,43393, 43430,43468,43506,43543,43581,43618,43656,43693, 43731,43768,43806,43843,43880,43918,43955,43992, 44029,44067,44104,44141,44178,44215,44252,44289, 44326,44363,44400,44437,44474,44511,44548,44585, 44622,44659,44695,44732,44769,44806,44842,44879, 44915,44952,44989,45025,45062,45098,45135,45171, 45207,45244,45280,45316,45353,45389,45425,45462, 45498,45534,45570,45606,45642,45678,45714,45750, 45786,45822,45858,45894,45930,45966,46002,46037, 46073,46109,46145,46180,46216,46252,46287,46323, 46358,46394,46429,46465,46500,46536,46571,46606, 46642,46677,46712,46747,46783,46818,46853,46888, 46923,46958,46993,47028,47063,47098,47133,47168, 47203,47238,47273,47308,47342,47377,47412,47446, 47481,47516,47550,47585,47619,47654,47688,47723, 47757,47792,47826,47861,47895,47929,47963,47998, 48032,48066,48100,48134,48168,48202,48237,48271, 48305,48338,48372,48406,48440,48474,48508,48542, 48575,48609,48643,48676,48710,48744,48777,48811, 48844,48878,48911,48945,48978,49012,49045,49078, 49112,49145,49178,49211,49244,49278,49311,49344, 49377,49410,49443,49476,49509,49542,49575,49608, 49640,49673,49706,49739,49771,49804,49837,49869, 49902,49935,49967,50000,50032,50064,50097,50129, 50162,50194,50226,50259,50291,50323,50355,50387, 50420,50452,50484,50516,50548,50580,50612,50644, 50675,50707,50739,50771,50803,50834,50866,50898, 50929,50961,50993,51024,51056,51087,51119,51150, 51182,51213,51244,51276,51307,51338,51369,51401, 51432,51463,51494,51525,51556,51587,51618,51649, 51680,51711,51742,51773,51803,51834,51865,51896, 51926,51957,51988,52018,52049,52079,52110,52140, 52171,52201,52231,52262,52292,52322,52353,52383, 52413,52443,52473,52503,52534,52564,52594,52624, 52653,52683,52713,52743,52773,52803,52832,52862, 52892,52922,52951,52981,53010,53040,53069,53099, 53128,53158,53187,53216,53246,53275,53304,53334, 53363,53392,53421,53450,53479,53508,53537,53566, 53595,53624,53653,53682,53711,53739,53768,53797, 53826,53854,53883,53912,53940,53969,53997,54026, 54054,54082,54111,54139,54167,54196,54224,54252, 54280,54309,54337,54365,54393,54421,54449,54477, 54505,54533,54560,54588,54616,54644,54672,54699, 54727,54755,54782,54810,54837,54865,54892,54920, 54947,54974,55002,55029,55056,55084,55111,55138, 55165,55192,55219,55246,55274,55300,55327,55354, 55381,55408,55435,55462,55489,55515,55542,55569, 55595,55622,55648,55675,55701,55728,55754,55781, 55807,55833,55860,55886,55912,55938,55965,55991, 56017,56043,56069,56095,56121,56147,56173,56199, 56225,56250,56276,56302,56328,56353,56379,56404, 56430,56456,56481,56507,56532,56557,56583,56608, 56633,56659,56684,56709,56734,56760,56785,56810, 56835,56860,56885,56910,56935,56959,56984,57009, 57034,57059,57083,57108,57133,57157,57182,57206, 57231,57255,57280,57304,57329,57353,57377,57402, 57426,57450,57474,57498,57522,57546,57570,57594, 57618,57642,57666,57690,57714,57738,57762,57785, 57809,57833,57856,57880,57903,57927,57950,57974, 57997,58021,58044,58067,58091,58114,58137,58160, 58183,58207,58230,58253,58276,58299,58322,58345, 58367,58390,58413,58436,58459,58481,58504,58527, 58549,58572,58594,58617,58639,58662,58684,58706, 58729,58751,58773,58795,58818,58840,58862,58884, 58906,58928,58950,58972,58994,59016,59038,59059, 59081,59103,59125,59146,59168,59190,59211,59233, 59254,59276,59297,59318,59340,59361,59382,59404, 59425,59446,59467,59488,59509,59530,59551,59572, 59593,59614,59635,59656,59677,59697,59718,59739, 59759,59780,59801,59821,59842,59862,59883,59903, 59923,59944,59964,59984,60004,60025,60045,60065, 60085,60105,60125,60145,60165,60185,60205,60225, 60244,60264,60284,60304,60323,60343,60363,60382, 60402,60421,60441,60460,60479,60499,60518,60537, 60556,60576,60595,60614,60633,60652,60671,60690, 60709,60728,60747,60766,60785,60803,60822,60841, 60859,60878,60897,60915,60934,60952,60971,60989, 61007,61026,61044,61062,61081,61099,61117,61135, 61153,61171,61189,61207,61225,61243,61261,61279, 61297,61314,61332,61350,61367,61385,61403,61420, 61438,61455,61473,61490,61507,61525,61542,61559, 61577,61594,61611,61628,61645,61662,61679,61696, 61713,61730,61747,61764,61780,61797,61814,61831, 61847,61864,61880,61897,61913,61930,61946,61963, 61979,61995,62012,62028,62044,62060,62076,62092, 62108,62125,62141,62156,62172,62188,62204,62220, 62236,62251,62267,62283,62298,62314,62329,62345, 62360,62376,62391,62407,62422,62437,62453,62468, 62483,62498,62513,62528,62543,62558,62573,62588, 62603,62618,62633,62648,62662,62677,62692,62706, 62721,62735,62750,62764,62779,62793,62808,62822, 62836,62850,62865,62879,62893,62907,62921,62935, 62949,62963,62977,62991,63005,63019,63032,63046, 63060,63074,63087,63101,63114,63128,63141,63155, 63168,63182,63195,63208,63221,63235,63248,63261, 63274,63287,63300,63313,63326,63339,63352,63365, 63378,63390,63403,63416,63429,63441,63454,63466, 63479,63491,63504,63516,63528,63541,63553,63565, 63578,63590,63602,63614,63626,63638,63650,63662, 63674,63686,63698,63709,63721,63733,63745,63756, 63768,63779,63791,63803,63814,63825,63837,63848, 63859,63871,63882,63893,63904,63915,63927,63938, 63949,63960,63971,63981,63992,64003,64014,64025, 64035,64046,64057,64067,64078,64088,64099,64109, 64120,64130,64140,64151,64161,64171,64181,64192, 64202,64212,64222,64232,64242,64252,64261,64271, 64281,64291,64301,64310,64320,64330,64339,64349, 64358,64368,64377,64387,64396,64405,64414,64424, 64433,64442,64451,64460,64469,64478,64487,64496, 64505,64514,64523,64532,64540,64549,64558,64566, 64575,64584,64592,64600,64609,64617,64626,64634, 64642,64651,64659,64667,64675,64683,64691,64699, 64707,64715,64723,64731,64739,64747,64754,64762, 64770,64777,64785,64793,64800,64808,64815,64822, 64830,64837,64844,64852,64859,64866,64873,64880, 64887,64895,64902,64908,64915,64922,64929,64936, 64943,64949,64956,64963,64969,64976,64982,64989, 64995,65002,65008,65015,65021,65027,65033,65040, 65046,65052,65058,65064,65070,65076,65082,65088, 65094,65099,65105,65111,65117,65122,65128,65133, 65139,65144,65150,65155,65161,65166,65171,65177, 65182,65187,65192,65197,65202,65207,65212,65217, 65222,65227,65232,65237,65242,65246,65251,65256, 65260,65265,65270,65274,65279,65283,65287,65292, 65296,65300,65305,65309,65313,65317,65321,65325, 65329,65333,65337,65341,65345,65349,65352,65356, 65360,65363,65367,65371,65374,65378,65381,65385, 65388,65391,65395,65398,65401,65404,65408,65411, 65414,65417,65420,65423,65426,65429,65431,65434, 65437,65440,65442,65445,65448,65450,65453,65455, 65458,65460,65463,65465,65467,65470,65472,65474, 65476,65478,65480,65482,65484,65486,65488,65490, 65492,65494,65496,65497,65499,65501,65502,65504, 65505,65507,65508,65510,65511,65513,65514,65515, 65516,65518,65519,65520,65521,65522,65523,65524, 65525,65526,65527,65527,65528,65529,65530,65530, 65531,65531,65532,65532,65533,65533,65534,65534, 65534,65535,65535,65535,65535,65535,65535,65535 }; const fixed_t *finecosine = &finesine[FINEANGLES/4]; const angle_t tantoangle[2049] = { 0,333772,667544,1001315,1335086,1668857,2002626,2336395, 2670163,3003929,3337694,3671457,4005219,4338979,4672736,5006492, 5340245,5673995,6007743,6341488,6675230,7008968,7342704,7676435, 8010164,8343888,8677609,9011325,9345037,9678744,10012447,10346145, 10679838,11013526,11347209,11680887,12014558,12348225,12681885,13015539, 13349187,13682829,14016464,14350092,14683714,15017328,15350936,15684536, 16018129,16351714,16685291,17018860,17352422,17685974,18019518,18353054, 18686582,19020100,19353610,19687110,20020600,20354080,20687552,21021014, 21354466,21687906,22021338,22354758,22688168,23021568,23354956,23688332, 24021698,24355052,24688396,25021726,25355046,25688352,26021648,26354930, 26688200,27021456,27354702,27687932,28021150,28354356,28687548,29020724, 29353888,29687038,30020174,30353296,30686404,31019496,31352574,31685636, 32018684,32351718,32684734,33017736,33350722,33683692,34016648,34349584, 34682508,35015412,35348300,35681172,36014028,36346868,36679688,37012492, 37345276,37678044,38010792,38343524,38676240,39008936,39341612,39674272, 40006912,40339532,40672132,41004716,41337276,41669820,42002344,42334848, 42667332,42999796,43332236,43664660,43997060,44329444,44661800,44994140, 45326456,45658752,45991028,46323280,46655512,46987720,47319908,47652072, 47984212,48316332,48648428,48980500,49312548,49644576,49976580,50308556, 50640512,50972444,51304352,51636236,51968096,52299928,52631740,52963524, 53295284,53627020,53958728,54290412,54622068,54953704,55285308,55616888, 55948444,56279972,56611472,56942948,57274396,57605816,57937212,58268576, 58599916,58931228,59262512,59593768,59924992,60256192,60587364,60918508, 61249620,61580704,61911760,62242788,62573788,62904756,63235692,63566604, 63897480,64228332,64559148,64889940,65220696,65551424,65882120,66212788, 66543420,66874024,67204600,67535136,67865648,68196120,68526568,68856984, 69187360,69517712,69848024,70178304,70508560,70838776,71168960,71499112, 71829224,72159312,72489360,72819376,73149360,73479304,73809216,74139096, 74468936,74798744,75128520,75458264,75787968,76117632,76447264,76776864, 77106424,77435952,77765440,78094888,78424304,78753688,79083032,79412336, 79741608,80070840,80400032,80729192,81058312,81387392,81716432,82045440, 82374408,82703336,83032224,83361080,83689896,84018664,84347400,84676096, 85004760,85333376,85661952,85990488,86318984,86647448,86975864,87304240, 87632576,87960872,88289128,88617344,88945520,89273648,89601736,89929792, 90257792,90585760,90913688,91241568,91569408,91897200,92224960,92552672, 92880336,93207968,93535552,93863088,94190584,94518040,94845448,95172816, 95500136,95827416,96154648,96481832,96808976,97136080,97463136,97790144, 98117112,98444032,98770904,99097736,99424520,99751256,100077944,100404592, 100731192,101057744,101384248,101710712,102037128,102363488,102689808,103016080, 103342312,103668488,103994616,104320696,104646736,104972720,105298656,105624552, 105950392,106276184,106601928,106927624,107253272,107578872,107904416,108229920, 108555368,108880768,109206120,109531416,109856664,110181872,110507016,110832120, 111157168,111482168,111807112,112132008,112456856,112781648,113106392,113431080, 113755720,114080312,114404848,114729328,115053760,115378136,115702464,116026744, 116350960,116675128,116999248,117323312,117647320,117971272,118295176,118619024, 118942816,119266560,119590248,119913880,120237456,120560984,120884456,121207864, 121531224,121854528,122177784,122500976,122824112,123147200,123470224,123793200, 124116120,124438976,124761784,125084528,125407224,125729856,126052432,126374960, 126697424,127019832,127342184,127664472,127986712,128308888,128631008,128953072, 129275080,129597024,129918912,130240744,130562520,130884232,131205888,131527480, 131849016,132170496,132491912,132813272,133134576,133455816,133776992,134098120, 134419184,134740176,135061120,135382000,135702816,136023584,136344272,136664912, 136985488,137306016,137626464,137946864,138267184,138587456,138907664,139227808, 139547904,139867920,140187888,140507776,140827616,141147392,141467104,141786752, 142106336,142425856,142745312,143064720,143384048,143703312,144022512,144341664, 144660736,144979744,145298704,145617584,145936400,146255168,146573856,146892480, 147211040,147529536,147847968,148166336,148484640,148802880,149121056,149439152, 149757200,150075168,150393072,150710912,151028688,151346400,151664048,151981616, 152299136,152616576,152933952,153251264,153568496,153885680,154202784,154519824, 154836784,155153696,155470528,155787296,156104000,156420624,156737200,157053696, 157370112,157686480,158002768,158318976,158635136,158951216,159267232,159583168, 159899040,160214848,160530592,160846256,161161840,161477376,161792832,162108208, 162423520,162738768,163053952,163369040,163684080,163999040,164313936,164628752, 164943504,165258176,165572784,165887312,166201776,166516160,166830480,167144736, 167458912,167773008,168087040,168400992,168714880,169028688,169342432,169656096, 169969696,170283216,170596672,170910032,171223344,171536576,171849728,172162800, 172475808,172788736,173101600,173414384,173727104,174039728,174352288,174664784, 174977200,175289536,175601792,175913984,176226096,176538144,176850096,177161984, 177473792,177785536,178097200,178408784,178720288,179031728,179343088,179654368, 179965568,180276704,180587744,180898720,181209616,181520448,181831184,182141856, 182452448,182762960,183073408,183383760,183694048,184004240,184314368,184624416, 184934400,185244288,185554096,185863840,186173504,186483072,186792576,187102000, 187411344,187720608,188029808,188338912,188647936,188956896,189265760,189574560, 189883264,190191904,190500448,190808928,191117312,191425632,191733872,192042016, 192350096,192658096,192966000,193273840,193581584,193889264,194196848,194504352, 194811792,195119136,195426400,195733584,196040688,196347712,196654656,196961520, 197268304,197574992,197881616,198188144,198494592,198800960,199107248,199413456, 199719584,200025616,200331584,200637456,200943248,201248960,201554576,201860128, 202165584,202470960,202776256,203081456,203386592,203691632,203996592,204301472, 204606256,204910976,205215600,205520144,205824592,206128960,206433248,206737456, 207041584,207345616,207649568,207953424,208257216,208560912,208864512,209168048, 209471488,209774832,210078112,210381296,210684384,210987408,211290336,211593184, 211895936,212198608,212501184,212803680,213106096,213408432,213710672,214012816, 214314880,214616864,214918768,215220576,215522288,215823920,216125472,216426928, 216728304,217029584,217330784,217631904,217932928,218233856,218534704,218835472, 219136144,219436720,219737216,220037632,220337952,220638192,220938336,221238384, 221538352,221838240,222138032,222437728,222737344,223036880,223336304,223635664, 223934912,224234096,224533168,224832160,225131072,225429872,225728608,226027232, 226325776,226624240,226922608,227220880,227519056,227817152,228115168,228413088, 228710912,229008640,229306288,229603840,229901312,230198688,230495968,230793152, 231090256,231387280,231684192,231981024,232277760,232574416,232870960,233167440, 233463808,233760096,234056288,234352384,234648384,234944304,235240128,235535872, 235831504,236127056,236422512,236717888,237013152,237308336,237603424,237898416, 238193328,238488144,238782864,239077488,239372016,239666464,239960816,240255072, 240549232,240843312,241137280,241431168,241724960,242018656,242312256,242605776, 242899200,243192512,243485744,243778896,244071936,244364880,244657744,244950496, 245243168,245535744,245828224,246120608,246412912,246705104,246997216,247289216, 247581136,247872960,248164688,248456320,248747856,249039296,249330640,249621904, 249913056,250204128,250495088,250785968,251076736,251367424,251658016,251948512, 252238912,252529200,252819408,253109520,253399536,253689456,253979280,254269008, 254558640,254848176,255137632,255426976,255716224,256005376,256294432,256583392, 256872256,257161024,257449696,257738272,258026752,258315136,258603424,258891600, 259179696,259467696,259755600,260043392,260331104,260618704,260906224,261193632, 261480960,261768176,262055296,262342320,262629248,262916080,263202816,263489456, 263776000,264062432,264348784,264635024,264921168,265207216,265493168,265779024, 266064784,266350448,266636000,266921472,267206832,267492096,267777264,268062336, 268347312,268632192,268916960,269201632,269486208,269770688,270055072,270339360, 270623552,270907616,271191616,271475488,271759296,272042976,272326560,272610048, 272893440,273176736,273459936,273743040,274026048,274308928,274591744,274874432, 275157024,275439520,275721920,276004224,276286432,276568512,276850528,277132416, 277414240,277695936,277977536,278259040,278540448,278821728,279102944,279384032, 279665056,279945952,280226752,280507456,280788064,281068544,281348960,281629248, 281909472,282189568,282469568,282749440,283029248,283308960,283588544,283868032, 284147424,284426720,284705920,284985024,285264000,285542912,285821696,286100384, 286378976,286657440,286935840,287214112,287492320,287770400,288048384,288326240, 288604032,288881696,289159264,289436768,289714112,289991392,290268576,290545632, 290822592,291099456,291376224,291652896,291929440,292205888,292482272,292758528, 293034656,293310720,293586656,293862496,294138240,294413888,294689440,294964864, 295240192,295515424,295790560,296065600,296340512,296615360,296890080,297164704, 297439200,297713632,297987936,298262144,298536256,298810240,299084160,299357952, 299631648,299905248,300178720,300452128,300725408,300998592,301271680,301544640, 301817536,302090304,302362976,302635520,302908000,303180352,303452608,303724768, 303996800,304268768,304540608,304812320,305083968,305355520,305626944,305898272, 306169472,306440608,306711616,306982528,307253344,307524064,307794656,308065152, 308335552,308605856,308876032,309146112,309416096,309685984,309955744,310225408, 310494976,310764448,311033824,311303072,311572224,311841280,312110208,312379040, 312647776,312916416,313184960,313453376,313721696,313989920,314258016,314526016, 314793920,315061728,315329408,315597024,315864512,316131872,316399168,316666336, 316933408,317200384,317467232,317733984,318000640,318267200,318533632,318799968, 319066208,319332352,319598368,319864288,320130112,320395808,320661408,320926912, 321192320,321457632,321722816,321987904,322252864,322517760,322782528,323047200, 323311744,323576192,323840544,324104800,324368928,324632992,324896928,325160736, 325424448,325688096,325951584,326215008,326478304,326741504,327004608,327267584, 327530464,327793248,328055904,328318496,328580960,328843296,329105568,329367712, 329629760,329891680,330153536,330415264,330676864,330938400,331199808,331461120, 331722304,331983392,332244384,332505280,332766048,333026752,333287296,333547776, 333808128,334068384,334328544,334588576,334848512,335108352,335368064,335627712, 335887200,336146624,336405920,336665120,336924224,337183200,337442112,337700864, 337959552,338218112,338476576,338734944,338993184,339251328,339509376,339767296, 340025120,340282848,340540480,340797984,341055392,341312704,341569888,341826976, 342083968,342340832,342597600,342854272,343110848,343367296,343623648,343879904, 344136032,344392064,344648000,344903808,345159520,345415136,345670656,345926048, 346181344,346436512,346691616,346946592,347201440,347456224,347710880,347965440, 348219872,348474208,348728448,348982592,349236608,349490528,349744320,349998048, 350251648,350505152,350758528,351011808,351264992,351518048,351771040,352023872, 352276640,352529280,352781824,353034272,353286592,353538816,353790944,354042944, 354294880,354546656,354798368,355049952,355301440,355552800,355804096,356055264, 356306304,356557280,356808128,357058848,357309504,357560032,357810464,358060768, 358311008,358561088,358811104,359060992,359310784,359560480,359810048,360059520, 360308896,360558144,360807296,361056352,361305312,361554144,361802880,362051488, 362300032,362548448,362796736,363044960,363293056,363541024,363788928,364036704, 364284384,364531936,364779392,365026752,365274016,365521152,365768192,366015136, 366261952,366508672,366755296,367001792,367248192,367494496,367740704,367986784, 368232768,368478656,368724416,368970080,369215648,369461088,369706432,369951680, 370196800,370441824,370686752,370931584,371176288,371420896,371665408,371909792, 372154080,372398272,372642336,372886304,373130176,373373952,373617600,373861152, 374104608,374347936,374591168,374834304,375077312,375320224,375563040,375805760, 376048352,376290848,376533248,376775520,377017696,377259776,377501728,377743584, 377985344,378227008,378468544,378709984,378951328,379192544,379433664,379674688, 379915584,380156416,380397088,380637696,380878176,381118560,381358848,381599040, 381839104,382079072,382318912,382558656,382798304,383037856,383277280,383516640, 383755840,383994976,384233984,384472896,384711712,384950400,385188992,385427488, 385665888,385904160,386142336,386380384,386618368,386856224,387093984,387331616, 387569152,387806592,388043936,388281152,388518272,388755296,388992224,389229024, 389465728,389702336,389938816,390175200,390411488,390647680,390883744,391119712, 391355584,391591328,391826976,392062528,392297984,392533312,392768544,393003680, 393238720,393473632,393708448,393943168,394177760,394412256,394646656,394880960, 395115136,395349216,395583200,395817088,396050848,396284512,396518080,396751520, 396984864,397218112,397451264,397684288,397917248,398150080,398382784,398615424, 398847936,399080320,399312640,399544832,399776928,400008928,400240832,400472608, 400704288,400935872,401167328,401398720,401629984,401861120,402092192,402323136, 402553984,402784736,403015360,403245888,403476320,403706656,403936896,404167008, 404397024,404626944,404856736,405086432,405316032,405545536,405774912,406004224, 406233408,406462464,406691456,406920320,407149088,407377760,407606336,407834784, 408063136,408291392,408519520,408747584,408975520,409203360,409431072,409658720, 409886240,410113664,410340992,410568192,410795296,411022304,411249216,411476032, 411702720,411929312,412155808,412382176,412608480,412834656,413060736,413286720, 413512576,413738336,413964000,414189568,414415040,414640384,414865632,415090784, 415315840,415540800,415765632,415990368,416215008,416439552,416663968,416888288, 417112512,417336640,417560672,417784576,418008384,418232096,418455712,418679200, 418902624,419125920,419349120,419572192,419795200,420018080,420240864,420463552, 420686144,420908608,421130976,421353280,421575424,421797504,422019488,422241344, 422463104,422684768,422906336,423127776,423349120,423570400,423791520,424012576, 424233536,424454368,424675104,424895744,425116288,425336736,425557056,425777280, 425997408,426217440,426437376,426657184,426876928,427096544,427316064,427535488, 427754784,427974016,428193120,428412128,428631040,428849856,429068544,429287168, 429505664,429724064,429942368,430160576,430378656,430596672,430814560,431032352, 431250048,431467616,431685120,431902496,432119808,432336992,432554080,432771040, 432987936,433204736,433421408,433637984,433854464,434070848,434287104,434503296, 434719360,434935360,435151232,435367008,435582656,435798240,436013696,436229088, 436444352,436659520,436874592,437089568,437304416,437519200,437733856,437948416, 438162880,438377248,438591520,438805696,439019744,439233728,439447584,439661344, 439875008,440088576,440302048,440515392,440728672,440941824,441154880,441367872, 441580736,441793472,442006144,442218720,442431168,442643552,442855808,443067968, 443280032,443492000,443703872,443915648,444127296,444338880,444550336,444761696, 444972992,445184160,445395232,445606176,445817056,446027840,446238496,446449088, 446659552,446869920,447080192,447290400,447500448,447710432,447920320,448130112, 448339776,448549376,448758848,448968224,449177536,449386720,449595808,449804800, 450013664,450222464,450431168,450639776,450848256,451056640,451264960,451473152, 451681248,451889248,452097152,452304960,452512672,452720288,452927808,453135232, 453342528,453549760,453756864,453963904,454170816,454377632,454584384,454791008, 454997536,455203968,455410304,455616544,455822688,456028704,456234656,456440512, 456646240,456851904,457057472,457262912,457468256,457673536,457878688,458083744, 458288736,458493600,458698368,458903040,459107616,459312096,459516480,459720768, 459924960,460129056,460333056,460536960,460740736,460944448,461148064,461351584, 461554976,461758304,461961536,462164640,462367680,462570592,462773440,462976160, 463178816,463381344,463583776,463786144,463988384,464190560,464392608,464594560, 464796448,464998208,465199872,465401472,465602944,465804320,466005600,466206816, 466407904,466608896,466809824,467010624,467211328,467411936,467612480,467812896, 468013216,468213440,468413600,468613632,468813568,469013440,469213184,469412832, 469612416,469811872,470011232,470210528,470409696,470608800,470807776,471006688, 471205472,471404192,471602784,471801312,471999712,472198048,472396288,472594400, 472792448,472990400,473188256,473385984,473583648,473781216,473978688,474176064, 474373344,474570528,474767616,474964608,475161504,475358336,475555040,475751648, 475948192,476144608,476340928,476537184,476733312,476929376,477125344,477321184, 477516960,477712640,477908224,478103712,478299104,478494400,478689600,478884704, 479079744,479274656,479469504,479664224,479858880,480053408,480247872,480442240, 480636512,480830656,481024736,481218752,481412640,481606432,481800128,481993760, 482187264,482380704,482574016,482767264,482960416,483153472,483346432,483539296, 483732064,483924768,484117344,484309856,484502240,484694560,484886784,485078912, 485270944,485462880,485654720,485846464,486038144,486229696,486421184,486612576, 486803840,486995040,487186176,487377184,487568096,487758912,487949664,488140320, 488330880,488521312,488711712,488901984,489092160,489282240,489472256,489662176, 489851968,490041696,490231328,490420896,490610336,490799712,490988960,491178144, 491367232,491556224,491745120,491933920,492122656,492311264,492499808,492688256, 492876608,493064864,493253056,493441120,493629120,493817024,494004832,494192544, 494380160,494567712,494755136,494942496,495129760,495316928,495504000,495691008, 495877888,496064704,496251424,496438048,496624608,496811040,496997408,497183680, 497369856,497555936,497741920,497927840,498113632,498299360,498484992,498670560, 498856000,499041376,499226656,499411840,499596928,499781920,499966848,500151680, 500336416,500521056,500705600,500890080,501074464,501258752,501442944,501627040, 501811072,501995008,502178848,502362592,502546240,502729824,502913312,503096704, 503280000,503463232,503646368,503829408,504012352,504195200,504377984,504560672, 504743264,504925760,505108192,505290496,505472736,505654912,505836960,506018944, 506200832,506382624,506564320,506745952,506927488,507108928,507290272,507471552, 507652736,507833824,508014816,508195744,508376576,508557312,508737952,508918528, 509099008,509279392,509459680,509639904,509820032,510000064,510180000,510359872, 510539648,510719328,510898944,511078432,511257856,511437216,511616448,511795616, 511974688,512153664,512332576,512511392,512690112,512868768,513047296,513225792, 513404160,513582432,513760640,513938784,514116800,514294752,514472608,514650368, 514828064,515005664,515183168,515360608,515537952,515715200,515892352,516069440, 516246432,516423328,516600160,516776896,516953536,517130112,517306592,517482976, 517659264,517835488,518011616,518187680,518363648,518539520,518715296,518891008, 519066624,519242144,519417600,519592960,519768256,519943424,520118528,520293568, 520468480,520643328,520818112,520992800,521167392,521341888,521516320,521690656, 521864896,522039072,522213152,522387168,522561056,522734912,522908640,523082304, 523255872,523429376,523602784,523776096,523949312,524122464,524295552,524468512, 524641440,524814240,524986976,525159616,525332192,525504640,525677056,525849344, 526021568,526193728,526365792,526537760,526709632,526881440,527053152,527224800, 527396352,527567840,527739200,527910528,528081728,528252864,528423936,528594880, 528765760,528936576,529107296,529277920,529448480,529618944,529789344,529959648, 530129856,530300000,530470048,530640000,530809888,530979712,531149440,531319072, 531488608,531658080,531827488,531996800,532166016,532335168,532504224,532673184, 532842080,533010912,533179616,533348288,533516832,533685312,533853728,534022048, 534190272,534358432,534526496,534694496,534862400,535030240,535197984,535365632, 535533216,535700704,535868128,536035456,536202720,536369888,536536992,536704000, 536870912 }; // Now where did these came from? const byte gammatable[5][256] = { { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96, 97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112, 113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 }, { 2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23, 24,25,26,27,29,30,31,32,33,34,36,37,38,39,40,41, 42,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59, 60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76, 77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92, 93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108, 109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124, 125,126,127,128,129,129,130,131,132,133,134,135,136,137,138,139, 140,141,142,143,144,145,146,147,148,148,149,150,151,152,153,154, 155,156,157,158,159,160,161,162,163,163,164,165,166,167,168,169, 170,171,172,173,174,175,175,176,177,178,179,180,181,182,183,184, 185,186,186,187,188,189,190,191,192,193,194,195,196,196,197,198, 199,200,201,202,203,204,205,205,206,207,208,209,210,211,212,213, 214,214,215,216,217,218,219,220,221,222,222,223,224,225,226,227, 228,229,230,230,231,232,233,234,235,236,237,237,238,239,240,241, 242,243,244,245,245,246,247,248,249,250,251,252,252,253,254,255 }, { 4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32, 33,35,36,38,39,40,42,43,45,46,47,48,50,51,52,54, 55,56,57,59,60,61,62,63,65,66,67,68,69,70,72,73, 74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90, 91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,114,115,116,117,118,119,120,121,122, 123,124,125,126,127,128,129,130,131,132,133,133,134,135,136,137, 138,139,140,141,142,143,144,144,145,146,147,148,149,150,151,152, 153,153,154,155,156,157,158,159,160,160,161,162,163,164,165,166, 166,167,168,169,170,171,172,172,173,174,175,176,177,178,178,179, 180,181,182,183,183,184,185,186,187,188,188,189,190,191,192,193, 193,194,195,196,197,197,198,199,200,201,201,202,203,204,205,206, 206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218, 219,220,221,221,222,223,224,224,225,226,227,228,228,229,230,231, 231,232,233,234,235,235,236,237,238,238,239,240,241,241,242,243, 244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,255 }, { 8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45, 47,49,50,52,53,55,57,58,60,61,63,64,65,67,68,70, 71,72,74,75,76,77,79,80,81,82,84,85,86,87,88,90, 91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123, 124,125,126,127,128,129,130,131,132,133,134,135,135,136,137,138, 139,140,141,142,143,143,144,145,146,147,148,149,150,150,151,152, 153,154,155,155,156,157,158,159,160,160,161,162,163,164,165,165, 166,167,168,169,169,170,171,172,173,173,174,175,176,176,177,178, 179,180,180,181,182,183,183,184,185,186,186,187,188,189,189,190, 191,192,192,193,194,195,195,196,197,197,198,199,200,200,201,202, 202,203,204,205,205,206,207,207,208,209,210,210,211,212,212,213, 214,214,215,216,216,217,218,219,219,220,221,221,222,223,223,224, 225,225,226,227,227,228,229,229,230,231,231,232,233,233,234,235, 235,236,237,237,238,238,239,240,240,241,242,242,243,244,244,245, 246,246,247,247,248,249,249,250,251,251,252,253,253,254,254,255 }, { 16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64, 66,68,69,71,73,75,76,78,80,81,83,84,86,87,89,90, 92,93,94,96,97,98,100,101,102,103,105,106,107,108,109,110, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,128, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156, 157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169, 169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180, 181,182,182,183,184,184,185,186,187,187,188,189,189,190,191,191, 192,193,193,194,195,195,196,196,197,198,198,199,200,200,201,202, 202,203,203,204,205,205,206,207,207,208,208,209,210,210,211,211, 212,213,213,214,214,215,216,216,217,217,218,219,219,220,220,221, 221,222,223,223,224,224,225,225,226,227,227,228,228,229,229,230, 230,231,232,232,233,233,234,234,235,235,236,236,237,237,238,239, 239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247, 247,248,248,249,249,250,250,251,251,252,252,253,254,254,255,255 } }; crispy-doom-crispy-doom-5.6.4/src/tables.h000066400000000000000000000046631360717211000204570ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Lookup tables. // Do not try to look them up :-). // In the order of appearance: // // int finetangent[4096] - Tangens LUT. // Should work with BAM fairly well (12 of 16bit, // effectively, by shifting). // // int finesine[10240] - Sine lookup. // Guess what, serves as cosine, too. // Remarkable thing is, how to use BAMs with this? // // int tantoangle[2049] - ArcTan LUT, // maps tan(angle) to angle fast. Gotta search. // #ifndef __TABLES__ #define __TABLES__ #include "doomtype.h" #include "m_fixed.h" #define FINEANGLES 8192 #define FINEMASK (FINEANGLES-1) // 0x100000000 to 0x2000 #define ANGLETOFINESHIFT 19 // Effective size is 10240. extern const fixed_t finesine[5*FINEANGLES/4]; // Re-use data, is just PI/2 pahse shift. extern const fixed_t *finecosine; // Effective size is 4096. extern const fixed_t finetangent[FINEANGLES/2]; // Gamma correction tables. extern const byte gammatable[5][256]; // Binary Angle Measument, BAM. #define ANG45 0x20000000 #define ANG90 0x40000000 #define ANG180 0x80000000 #define ANG270 0xc0000000 #define ANG_MAX 0xffffffff #define ANG1 (ANG45 / 45) #define ANG60 (ANG180 / 3) // Heretic code uses this definition as though it represents one // degree, but it is not! This is actually ~1.40 degrees. #define ANG1_X 0x01000000 #define SLOPERANGE 2048 #define SLOPEBITS 11 #define DBITS (FRACBITS-SLOPEBITS) typedef unsigned int angle_t; // Effective size is 2049; // The +1 size is to handle the case when x==y // without additional checking. extern const angle_t tantoangle[SLOPERANGE+1]; // Utility function, // called by R_PointToAngle. int SlopeDiv(unsigned int num, unsigned int den); int SlopeDivCrispy(unsigned int num, unsigned int den); #endif crispy-doom-crispy-doom-5.6.4/src/v_diskicon.c000066400000000000000000000105761360717211000213300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Disk load indicator. // #include "doomtype.h" #include "deh_str.h" #include "i_swap.h" #include "i_video.h" #include "m_argv.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "v_diskicon.h" // Only display the disk icon if more then this much bytes have been read // during the previous tic. static const int diskicon_threshold = 20*1024; // Two buffers: disk_data contains the data representing the disk icon // (raw, not a patch_t) while saved_background is an equivalently-sized // buffer where we save the background data while the disk is on screen. static pixel_t *disk_data; static pixel_t *saved_background; static int loading_disk_xoffs = 0; static int loading_disk_yoffs = 0; // Number of bytes read since the last call to V_DrawDiskIcon(). static size_t recent_bytes_read = 0; static boolean disk_drawn; static void CopyRegion(pixel_t *dest, int dest_pitch, pixel_t *src, int src_pitch, int w, int h) { pixel_t *s, *d; int y; s = src; d = dest; for (y = 0; y < h; ++y) { memcpy(d, s, w * sizeof(*d)); s += src_pitch; d += dest_pitch; } } static void SaveDiskData(const char *disk_lump, int xoffs, int yoffs) { pixel_t *tmpscreen; patch_t *disk; // Allocate a complete temporary screen where we'll draw the patch. tmpscreen = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen), PU_STATIC, NULL); memset(tmpscreen, 0, SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen)); V_UseBuffer(tmpscreen); // Buffer where we'll save the disk data. if (disk_data != NULL) { Z_Free(disk_data); disk_data = NULL; } disk_data = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H * sizeof(*disk_data), PU_STATIC, NULL); // Draw the patch and save the result to disk_data. disk = W_CacheLumpName(disk_lump, PU_STATIC); V_DrawPatch(loading_disk_xoffs >> crispy->hires, loading_disk_yoffs >> crispy->hires, disk); CopyRegion(disk_data, LOADING_DISK_W, tmpscreen + yoffs * SCREENWIDTH + xoffs, SCREENWIDTH, LOADING_DISK_W, LOADING_DISK_H); W_ReleaseLumpName(disk_lump); V_RestoreBuffer(); Z_Free(tmpscreen); } void V_EnableLoadingDisk(const char *lump_name, int xoffs, int yoffs) { loading_disk_xoffs = xoffs; loading_disk_yoffs = yoffs; if (saved_background != NULL) { Z_Free(saved_background); saved_background = NULL; } saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H * sizeof(*saved_background), PU_STATIC, NULL); SaveDiskData(lump_name, xoffs, yoffs); } void V_BeginRead(size_t nbytes) { recent_bytes_read += nbytes; } static pixel_t *DiskRegionPointer(void) { return I_VideoBuffer + loading_disk_yoffs * SCREENWIDTH + loading_disk_xoffs; } void V_DrawDiskIcon(void) { if (disk_data != NULL && recent_bytes_read > diskicon_threshold) { // Save the background behind the disk before we draw it. CopyRegion(saved_background, LOADING_DISK_W, DiskRegionPointer(), SCREENWIDTH, LOADING_DISK_W, LOADING_DISK_H); // Write the disk to the screen buffer. CopyRegion(DiskRegionPointer(), SCREENWIDTH, disk_data, LOADING_DISK_W, LOADING_DISK_W, LOADING_DISK_H); disk_drawn = true; } recent_bytes_read = 0; } void V_RestoreDiskBackground(void) { if (disk_drawn) { // Restore the background. CopyRegion(DiskRegionPointer(), SCREENWIDTH, saved_background, LOADING_DISK_W, LOADING_DISK_W, LOADING_DISK_H); disk_drawn = false; } } crispy-doom-crispy-doom-5.6.4/src/v_diskicon.h000066400000000000000000000020321360717211000213210ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Disk load indicator. // #ifndef __V_DISKICON__ #define __V_DISKICON__ #include "crispy.h" // Dimensions of the flashing "loading" disk icon #define LOADING_DISK_W (16 << crispy->hires) #define LOADING_DISK_H (16 << crispy->hires) extern void V_EnableLoadingDisk(const char *lump_name, int xoffs, int yoffs); extern void V_BeginRead(size_t nbytes); extern void V_DrawDiskIcon(void); extern void V_RestoreDiskBackground(void); #endif crispy-doom-crispy-doom-5.6.4/src/v_patch.h000066400000000000000000000026651360717211000206310ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Refresh/rendering module, shared data struct definitions. // #ifndef V_PATCH_H #define V_PATCH_H // 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 PACKED_STRUCT ( { short width; // bounding box size short height; short leftoffset; // pixels to the left of origin short topoffset; // pixels below the origin int columnofs[8]; // only [width] used // the [0] is &columnofs[width] }) patch_t; // posts are runs of non masked source pixels typedef PACKED_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; #endif crispy-doom-crispy-doom-5.6.4/src/v_trans.c000066400000000000000000000206111360717211000206430ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2014 Fabian Greffrath, Paul Haeberli // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // // Color translation tables // #include #include "doomtype.h" #include "v_trans.h" // [crispy] here used to be static color translation tables based on // the ones found in Boom and MBF. Nowadays these are recalculated // by means of actual color space conversions in r_data:R_InitColormaps(). // this one will be the identity matrix static byte cr_none[256]; // this one will be the ~50% darker matrix static byte cr_dark[256]; static byte cr_gray[256]; static byte cr_green[256]; static byte cr_gold[256]; static byte cr_red[256]; static byte cr_blue[256]; static const byte cr_red2blue[256] = {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, 32,33,34,35,36,37,38,39,40,41,42,43,207,207,46,207, 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 64,65,66,207,68,69,70,71,72,73,74,75,76,77,78,79, 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 200,200,201,201,202,202,203,203,204,204,205,205,206,206,207,207, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}; static const byte cr_red2green[256] = {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, 32,33,34,35,36,37,38,39,40,41,42,43,127,127,46,127, 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 64,65,66,127,68,69,70,71,72,73,74,75,76,77,78,79, 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 114,115,116,117,118,119,120,121,122,123,124,125,126,126,127,127, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}; byte *cr[] = { (byte *) &cr_none, (byte *) &cr_dark, (byte *) &cr_gray, (byte *) &cr_green, (byte *) &cr_gold, (byte *) &cr_red, (byte *) &cr_blue, (byte *) &cr_red2blue, (byte *) &cr_red2green }; char **crstr = 0; /* Date: Sun, 26 Oct 2014 10:36:12 -0700 From: paul haeberli Subject: Re: colors and color conversions To: Fabian Greffrath Yes, this seems exactly like the solution I was looking for. I just couldn't find code to do the HSV->RGB conversion. Speaking of the code, would you allow me to use this code in my software? The Doom source code is licensed under the GNU GPL, so this code yould have to be under a compatible license. Yes. I'm happy to contribute this code to your project. GNU GPL or anything compatible sounds fine. Regarding the conversions, the procedure you sent me will leave grays (r=g=b) untouched, no matter what I set as HUE, right? Is it possible, then, to also use this routine to convert colors *to* gray? You can convert any color to an equivalent grey by setting the saturation to 0.0 - Paul Haeberli */ #define CTOLERANCE (0.0001) typedef struct vect { float x; float y; float z; } vect; static void hsv_to_rgb(vect *hsv, vect *rgb) { float h, s, v; h = hsv->x; s = hsv->y; v = hsv->z; h *= 360.0; if (sx = v; rgb->y = v; rgb->z = v; } else { int i; float f, p, q, t; if (h>=360.0) h -= 360.0; h /= 60.0; i = floor(h); f = h - i; p = v*(1.0-s); q = v*(1.0-(s*f)); t = v*(1.0-(s*(1.0-f))); switch (i) { case 0 : rgb->x = v; rgb->y = t; rgb->z = p; break; case 1 : rgb->x = q; rgb->y = v; rgb->z = p; break; case 2 : rgb->x = p; rgb->y = v; rgb->z = t; break; case 3 : rgb->x = p; rgb->y = q; rgb->z = v; break; case 4 : rgb->x = t; rgb->y = p; rgb->z = v; break; case 5 : rgb->x = v; rgb->y = p; rgb->z = q; break; } } } static void rgb_to_hsv(vect *rgb, vect *hsv) { float h, s, v; float cmax, cmin; float r, g, b; r = rgb->x; g = rgb->y; b = rgb->z; /* find the cmax and cmin of r g b */ cmax = r; cmin = r; cmax = (g>cmax ? g:cmax); cmin = (gcmax ? b:cmax); cmin = (bCTOLERANCE) s = (cmax - cmin)/cmax; else { s = 0.0; h = 0.0; } if (sx = h/360.0; hsv->y = s; hsv->z = v; } // [crispy] copied over from i_video.c static int I_GetPaletteIndex2(byte *palette, int r, int g, int b) { int best, best_diff, diff; int i; best = 0; best_diff = INT_MAX; for (i = 0; i < 256; ++i) { diff = (r - palette[3 * i + 0]) * (r - palette[3 * i + 0]) + (g - palette[3 * i + 1]) * (g - palette[3 * i + 1]) + (b - palette[3 * i + 2]) * (b - palette[3 * i + 2]); if (diff < best_diff) { best = i; best_diff = diff; } if (diff == 0) { break; } } return best; } byte V_Colorize (byte *playpal, int cr, byte source, boolean keepgray109) { vect rgb, hsv; // [crispy] preserve gray drop shadow in IWAD status bar numbers if (cr == CR_NONE || (keepgray109 && source == 109)) return source; rgb.x = playpal[3 * source + 0] / 255.; rgb.y = playpal[3 * source + 1] / 255.; rgb.z = playpal[3 * source + 2] / 255.; rgb_to_hsv(&rgb, &hsv); if (cr == CR_DARK) hsv.z *= 0.5; else if (cr == CR_GRAY) hsv.y = 0; else { // [crispy] hack colors to full saturation hsv.y = 1.0; if (cr == CR_GREEN) { // hsv.x = 135./360.; hsv.x = (150. * hsv.z + 120. * (1. - hsv.z))/360.; } else if (cr == CR_GOLD) { // hsv.x = 45./360.; // hsv.x = (50. * hsv.z + 30. * (1. - hsv.z))/360.; hsv.x = (7.0 + 53. * hsv.z)/360.; hsv.y = 1.0 - 0.4 * hsv.z; hsv.z = 0.2 + 0.8 * hsv.z; } else if (cr == CR_RED) { hsv.x = 0.; } else if (cr == CR_BLUE) { hsv.x = 240./360.; } } hsv_to_rgb(&hsv, &rgb); rgb.x *= 255.; rgb.y *= 255.; rgb.z *= 255.; return I_GetPaletteIndex2(playpal, (int) rgb.x, (int) rgb.y, (int) rgb.z); } crispy-doom-crispy-doom-5.6.4/src/v_trans.h000066400000000000000000000037251360717211000206570ustar00rootroot00000000000000// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: v_video.h,v 1.9 1998/05/06 11:12:54 jim Exp $ // // BOOM, a modified and improved DOOM engine // Copyright (C) 1999 by // id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT 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_TRANS__ #define __V_TRANS__ #include "doomtype.h" enum { CR_NONE, CR_DARK, CR_GRAY, CR_GREEN, CR_GOLD, CR_RED, CR_BLUE, CR_RED2BLUE, CR_RED2GREEN, CRMAX } cr_t; #define CR_GREY CR_GRAY extern byte *cr[CRMAX]; extern char **crstr; #define cr_esc '~' #ifndef CRISPY_TRUECOLOR extern byte *tranmap; #else extern const pixel_t (*blendfunc) (const pixel_t fg, const pixel_t bg); extern const pixel_t I_BlendAdd (const pixel_t bg, const pixel_t fg); extern const pixel_t I_BlendDark (const pixel_t bg, const int d); extern const pixel_t I_BlendOver (const pixel_t bg, const pixel_t fg); #endif #endif // __V_TRANS__ crispy-doom-crispy-doom-5.6.4/src/v_video.c000066400000000000000000000764221360717211000206350ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Gamma correction LUT stuff. // Functions to draw patches (by post) directly to screen. // Functions to blit a block to the screen. // #include "SDL_version.h" // [crispy] #include #include #include #include #include "i_system.h" #include "doomtype.h" #include "deh_str.h" #include "i_input.h" #include "i_swap.h" #include "i_video.h" #include "m_bbox.h" #include "m_misc.h" #ifdef CRISPY_TRUECOLOR #include "v_trans.h" #endif #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "crispy.h" #include "config.h" #ifdef HAVE_LIBPNG #include #endif // TODO: There are separate RANGECHECK defines for different games, but this // is common code. Fix this. #define RANGECHECK // Blending table used for fuzzpatch, etc. // Only used in Heretic/Hexen byte *tinttable = NULL; byte *tranmap = NULL; byte *dp_translation = NULL; boolean dp_translucent = false; #ifdef CRISPY_TRUECOLOR extern pixel_t *colormaps; #endif // villsa [STRIFE] Blending table used for Strife byte *xlatab = NULL; // The screen buffer that the v_video.c code draws to. static pixel_t *dest_screen = NULL; int dirtybox[4]; // haleyjd 08/28/10: clipping callback function for patches. // This is needed for Chocolate Strife, which clips patches to the screen. static vpatchclipfunc_t patchclip_callback = NULL; // // V_MarkRect // void V_MarkRect(int x, int y, int width, int height) { // If we are temporarily using an alternate screen, do not // affect the update box. if (dest_screen == I_VideoBuffer) { M_AddToBox (dirtybox, x, y); M_AddToBox (dirtybox, x + width-1, y + height-1); } } // // V_CopyRect // void V_CopyRect(int srcx, int srcy, pixel_t *source, int width, int height, int destx, int desty) { pixel_t *src; pixel_t *dest; srcx <<= crispy->hires; srcy <<= crispy->hires; width <<= crispy->hires; height <<= crispy->hires; destx <<= crispy->hires; desty <<= crispy->hires; #ifdef RANGECHECK if (srcx < 0 || srcx + width > SCREENWIDTH || srcy < 0 || srcy + height > SCREENHEIGHT || destx < 0 || destx /* + width */ > SCREENWIDTH || desty < 0 || desty /* + height */ > SCREENHEIGHT) { I_Error ("Bad V_CopyRect"); } #endif // [crispy] prevent framebuffer overflow if (destx + width > SCREENWIDTH) width = SCREENWIDTH - destx; if (desty + height > SCREENHEIGHT) height = SCREENHEIGHT - desty; V_MarkRect(destx, desty, width, height); src = source + SCREENWIDTH * srcy + srcx; dest = dest_screen + SCREENWIDTH * desty + destx; for ( ; height>0 ; height--) { memcpy(dest, src, width * sizeof(*dest)); src += SCREENWIDTH; dest += SCREENWIDTH; } } // // V_SetPatchClipCallback // // haleyjd 08/28/10: Added for Strife support. // By calling this function, you can setup runtime error checking for patch // clipping. Strife never caused errors by drawing patches partway off-screen. // Some versions of vanilla DOOM also behaved differently than the default // implementation, so this could possibly be extended to those as well for // accurate emulation. // void V_SetPatchClipCallback(vpatchclipfunc_t func) { patchclip_callback = func; } // // V_DrawPatch // Masks a column based masked pic to the screen. // // [crispy] four different rendering functions // for each possible combination of dp_translation and dp_translucent: // (1) normal, opaque patch static const inline pixel_t drawpatchpx00 (const pixel_t dest, const pixel_t source) #ifndef CRISPY_TRUECOLOR {return source;} #else {return colormaps[source];} #endif // (2) color-translated, opaque patch static const inline pixel_t drawpatchpx01 (const pixel_t dest, const pixel_t source) #ifndef CRISPY_TRUECOLOR {return dp_translation[source];} #else {return colormaps[dp_translation[source]];} #endif // (3) normal, translucent patch static const inline pixel_t drawpatchpx10 (const pixel_t dest, const pixel_t source) #ifndef CRISPY_TRUECOLOR {return tranmap[(dest<<8)+source];} #else {return I_BlendOver(dest, colormaps[source]);} #endif // (4) color-translated, translucent patch static const inline pixel_t drawpatchpx11 (const pixel_t dest, const pixel_t source) #ifndef CRISPY_TRUECOLOR {return tranmap[(dest<<8)+dp_translation[source]];} #else {return I_BlendOver(dest, colormaps[dp_translation[source]]);} #endif // [crispy] array of function pointers holding the different rendering functions typedef const pixel_t drawpatchpx_t (const pixel_t dest, const pixel_t source); static drawpatchpx_t *const drawpatchpx_a[2][2] = {{drawpatchpx11, drawpatchpx10}, {drawpatchpx01, drawpatchpx00}}; static fixed_t dx, dxi, dy, dyi; void V_DrawPatch(int x, int y, patch_t *patch) { int count; int col; column_t *column; pixel_t *desttop; pixel_t *dest; byte *source; int w; // [crispy] four different rendering functions drawpatchpx_t *const drawpatchpx = drawpatchpx_a[!dp_translucent][!dp_translation]; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); // haleyjd 08/28/10: Strife needs silent error checking here. if(patchclip_callback) { if(!patchclip_callback(patch, x, y)) return; } #ifdef RANGECHECK_NOTHANKS if (x < 0 || x + SHORT(patch->width) > ORIGWIDTH || y < 0 || y + SHORT(patch->height) > ORIGHEIGHT) { I_Error("Bad V_DrawPatch"); } #endif V_MarkRect(x, y, SHORT(patch->width), SHORT(patch->height)); col = 0; desttop = dest_screen + ((y * dy) >> FRACBITS) * SCREENWIDTH + ((x * dx) >> FRACBITS); w = SHORT(patch->width); for ( ; col= SCREENWIDTH) { break; } column = (column_t *)((byte *)patch + LONG(patch->columnofs[col >> FRACBITS])); // step through the posts in a column while (column->topdelta != 0xff) { int top, srccol = 0; // [crispy] support for DeePsea tall patches if (column->topdelta <= topdelta) { topdelta += column->topdelta; } else { topdelta = column->topdelta; } top = ((y + topdelta) * dy) >> FRACBITS; source = (byte *)column + 3; dest = desttop + ((topdelta * dy) >> FRACBITS)*SCREENWIDTH; count = (column->length * dy) >> FRACBITS; // [crispy] too low / height if (top + count > SCREENHEIGHT) { count = SCREENHEIGHT - top; } // [crispy] nothing left to draw? if (count < 1) { break; } while (count--) { // [crispy] too high if (top++ >= 0) { *dest = drawpatchpx(*dest, source[srccol >> FRACBITS]); } srccol += dyi; dest += SCREENWIDTH; } column = (column_t *)((byte *)column + column->length + 4); } } } void V_DrawPatchFullScreen(patch_t *patch, boolean flipped) { const short width = SHORT(patch->width); const short height = SHORT(patch->height); dx = (SCREENWIDTH << FRACBITS) / width; dxi = (width << FRACBITS) / SCREENWIDTH; dy = (SCREENHEIGHT << FRACBITS) / height; dyi = (height << FRACBITS) / SCREENHEIGHT; patch->leftoffset = 0; patch->topoffset = 0; if (flipped) { V_DrawPatchFlipped(0, 0, patch); } else { V_DrawPatch(0, 0, patch); } dx = (SCREENWIDTH << FRACBITS) / ORIGWIDTH; dxi = (ORIGWIDTH << FRACBITS) / SCREENWIDTH; dy = (SCREENHEIGHT << FRACBITS) / ORIGHEIGHT; dyi = (ORIGHEIGHT << FRACBITS) / SCREENHEIGHT; } // // V_DrawPatchFlipped // Masks a column based masked pic to the screen. // Flips horizontally, e.g. to mirror face. // void V_DrawPatchFlipped(int x, int y, patch_t *patch) { int count; int col; column_t *column; pixel_t *desttop; pixel_t *dest; byte *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); // haleyjd 08/28/10: Strife needs silent error checking here. if(patchclip_callback) { if(!patchclip_callback(patch, x, y)) return; } #ifdef RANGECHECK_NOTHANKS if (x < 0 || x + SHORT(patch->width) > ORIGWIDTH || y < 0 || y + SHORT(patch->height) > ORIGHEIGHT) { I_Error("Bad V_DrawPatchFlipped"); } #endif V_MarkRect (x, y, SHORT(patch->width), SHORT(patch->height)); col = 0; desttop = dest_screen + ((y * dy) >> FRACBITS) * SCREENWIDTH + ((x * dx) >> FRACBITS); w = SHORT(patch->width); for ( ; col= SCREENWIDTH) { break; } column = (column_t *)((byte *)patch + LONG(patch->columnofs[w-1-(col >> FRACBITS)])); // step through the posts in a column while (column->topdelta != 0xff ) { int top, srccol = 0; // [crispy] support for DeePsea tall patches if (column->topdelta <= topdelta) { topdelta += column->topdelta; } else { topdelta = column->topdelta; } top = ((y + topdelta) * dy) >> FRACBITS; source = (byte *)column + 3; dest = desttop + ((topdelta * dy) >> FRACBITS)*SCREENWIDTH; count = (column->length * dy) >> FRACBITS; // [crispy] too low / height if (top + count > SCREENHEIGHT) { count = SCREENHEIGHT - top; } // [crispy] nothing left to draw? if (count < 1) { break; } while (count--) { // [crispy] too high if (top++ >= 0) { #ifndef CRISPY_TRUECOLOR *dest = source[srccol >> FRACBITS]; #else *dest = colormaps[source[srccol >> FRACBITS]]; #endif } srccol += dyi; dest += SCREENWIDTH; } column = (column_t *)((byte *)column + column->length + 4); } } } // // V_DrawPatchDirect // Draws directly to the screen on the pc. // void V_DrawPatchDirect(int x, int y, patch_t *patch) { V_DrawPatch(x, y, patch); } // // V_DrawTLPatch // // Masks a column based translucent masked pic to the screen. // void V_DrawTLPatch(int x, int y, patch_t * patch) { int count, col; column_t *column; pixel_t *desttop, *dest; byte *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if (x < 0 || x + SHORT(patch->width) > ORIGWIDTH || y < 0 || y + SHORT(patch->height) > ORIGHEIGHT) { I_Error("Bad V_DrawTLPatch"); } col = 0; desttop = dest_screen + ((y * dy) >> FRACBITS) * SCREENWIDTH + ((x * dx) >> FRACBITS); w = SHORT(patch->width); for (; col < w << FRACBITS; x++, col+=dxi, desttop++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col >> FRACBITS])); // step through the posts in a column while (column->topdelta != 0xff) { int srccol = 0; source = (byte *) column + 3; dest = desttop + ((column->topdelta * dy) >> FRACBITS) * SCREENWIDTH; count = (column->length * dy) >> FRACBITS; while (count--) { *dest = tinttable[((*dest) << 8) + source[srccol >> FRACBITS]]; srccol += dyi; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // V_DrawXlaPatch // // villsa [STRIFE] Masks a column based translucent masked pic to the screen. // void V_DrawXlaPatch(int x, int y, patch_t * patch) { int count, col; column_t *column; pixel_t *desttop, *dest; byte *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if(patchclip_callback) { if(!patchclip_callback(patch, x, y)) return; } col = 0; desttop = dest_screen + ((y * dy) >> FRACBITS) * SCREENWIDTH + ((x * dx) >> FRACBITS); w = SHORT(patch->width); for(; col < w << FRACBITS; x++, col+=dxi, desttop++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col >> FRACBITS])); // step through the posts in a column while(column->topdelta != 0xff) { int srccol = 0; source = (byte *) column + 3; dest = desttop + ((column->topdelta * dy) >> FRACBITS) * SCREENWIDTH; count = (column->length * dy) >> FRACBITS; while(count--) { *dest = xlatab[*dest + (source[srccol >> FRACBITS] << 8)]; srccol += dyi; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // V_DrawAltTLPatch // // Masks a column based translucent masked pic to the screen. // void V_DrawAltTLPatch(int x, int y, patch_t * patch) { int count, col; column_t *column; pixel_t *desttop, *dest; byte *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if (x < 0 || x + SHORT(patch->width) > ORIGWIDTH || y < 0 || y + SHORT(patch->height) > ORIGHEIGHT) { I_Error("Bad V_DrawAltTLPatch"); } col = 0; desttop = dest_screen + ((y * dy) >> FRACBITS) * SCREENWIDTH + ((x * dx) >> FRACBITS); w = SHORT(patch->width); for (; col < w << FRACBITS; x++, col+=dxi, desttop++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col >> FRACBITS])); // step through the posts in a column while (column->topdelta != 0xff) { int srccol = 0; source = (byte *) column + 3; dest = desttop + ((column->topdelta * dy) >> FRACBITS) * SCREENWIDTH; count = (column->length * dy) >> FRACBITS; while (count--) { *dest = tinttable[((*dest) << 8) + source[srccol >> FRACBITS]]; srccol += dyi; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // V_DrawShadowedPatch // // Masks a column based masked pic to the screen. // void V_DrawShadowedPatch(int x, int y, patch_t *patch) { int count, col; column_t *column; pixel_t *desttop, *dest; byte *source; pixel_t *desttop2, *dest2; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if (x < 0 || x + SHORT(patch->width) > ORIGWIDTH || y < 0 || y + SHORT(patch->height) > ORIGHEIGHT) { I_Error("Bad V_DrawShadowedPatch"); } col = 0; desttop = dest_screen + ((y * dy) >> FRACBITS) * SCREENWIDTH + ((x * dx) >> FRACBITS); desttop2 = dest_screen + (((y + 2) * dy) >> FRACBITS) * SCREENWIDTH + (((x + 2) * dx) >> FRACBITS); w = SHORT(patch->width); for (; col < w << FRACBITS; x++, col+=dxi, desttop++, desttop2++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col >> FRACBITS])); // step through the posts in a column while (column->topdelta != 0xff) { int srccol = 0; source = (byte *) column + 3; dest = desttop + ((column->topdelta * dy) >> FRACBITS) * SCREENWIDTH; dest2 = desttop2 + ((column->topdelta * dy) >> FRACBITS) * SCREENWIDTH; count = (column->length * dy) >> FRACBITS; while (count--) { *dest2 = tinttable[((*dest2) << 8)]; dest2 += SCREENWIDTH; *dest = source[srccol >> FRACBITS]; srccol += dyi; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // Load tint table from TINTTAB lump. // void V_LoadTintTable(void) { tinttable = W_CacheLumpName("TINTTAB", PU_STATIC); } // // V_LoadXlaTable // // villsa [STRIFE] Load xla table from XLATAB lump. // void V_LoadXlaTable(void) { xlatab = W_CacheLumpName("XLATAB", PU_STATIC); } // // V_DrawBlock // Draw a linear block of pixels into the view buffer. // void V_DrawBlock(int x, int y, int width, int height, pixel_t *src) { pixel_t *dest; #ifdef RANGECHECK if (x < 0 || x + width >SCREENWIDTH || y < 0 || y + height > SCREENHEIGHT) { I_Error ("Bad V_DrawBlock"); } #endif V_MarkRect (x, y, width, height); dest = dest_screen + (y << crispy->hires) * SCREENWIDTH + x; while (height--) { memcpy (dest, src, width * sizeof(*dest)); src += width; dest += SCREENWIDTH; } } void V_DrawScaledBlock(int x, int y, int width, int height, pixel_t *src) { pixel_t *dest; int i, j; #ifdef RANGECHECK if (x < 0 || x + width > ORIGWIDTH || y < 0 || y + height > ORIGHEIGHT) { I_Error ("Bad V_DrawScaledBlock"); } #endif V_MarkRect (x, y, width, height); dest = dest_screen + (y << crispy->hires) * SCREENWIDTH + (x << crispy->hires); for (i = 0; i < (height << crispy->hires); i++) { for (j = 0; j < (width << crispy->hires); j++) { *(dest + i * SCREENWIDTH + j) = *(src + (i >> crispy->hires) * width + (j >> crispy->hires)); } } } void V_DrawFilledBox(int x, int y, int w, int h, int c) { pixel_t *buf, *buf1; int x1, y1; buf = I_VideoBuffer + SCREENWIDTH * y + x; for (y1 = 0; y1 < h; ++y1) { buf1 = buf; for (x1 = 0; x1 < w; ++x1) { *buf1++ = c; } buf += SCREENWIDTH; } } void V_DrawHorizLine(int x, int y, int w, int c) { pixel_t *buf; int x1; // [crispy] prevent framebuffer overflows if (x + w > (unsigned)SCREENWIDTH) w = SCREENWIDTH - x; buf = I_VideoBuffer + SCREENWIDTH * y + x; for (x1 = 0; x1 < w; ++x1) { *buf++ = c; } } void V_DrawVertLine(int x, int y, int h, int c) { pixel_t *buf; int y1; buf = I_VideoBuffer + SCREENWIDTH * y + x; for (y1 = 0; y1 < h; ++y1) { *buf = c; buf += SCREENWIDTH; } } void V_DrawBox(int x, int y, int w, int h, int c) { V_DrawHorizLine(x, y, w, c); V_DrawHorizLine(x, y+h-1, w, c); V_DrawVertLine(x, y, h, c); V_DrawVertLine(x+w-1, y, h, c); } // // Draw a "raw" screen (lump containing raw data to blit directly // to the screen) // void V_CopyScaledBuffer(pixel_t *dest, pixel_t *src, size_t size) { int i, j; #ifdef RANGECHECK if (size > ORIGWIDTH * ORIGHEIGHT) { I_Error("Bad V_CopyScaledBuffer"); } #endif while (size--) { for (i = 0; i <= crispy->hires; i++) { for (j = 0; j <= crispy->hires; j++) { *(dest + (size << crispy->hires) + (crispy->hires * (int) (size / ORIGWIDTH) + i) * SCREENWIDTH + j) = *(src + size); } } } } void V_DrawRawScreen(pixel_t *raw) { V_CopyScaledBuffer(dest_screen, raw, ORIGWIDTH * ORIGHEIGHT); } // // V_Init // void V_Init (void) { // [crispy] initialize resolution-agnostic patch drawing if (SCREENWIDTH && SCREENHEIGHT) { dx = (SCREENWIDTH << FRACBITS) / ORIGWIDTH; dxi = (ORIGWIDTH << FRACBITS) / SCREENWIDTH; dy = (SCREENHEIGHT << FRACBITS) / ORIGHEIGHT; dyi = (ORIGHEIGHT << FRACBITS) / SCREENHEIGHT; } // no-op! // There used to be separate screens that could be drawn to; these are // now handled in the upper layers. } // Set the buffer that the code draws to. void V_UseBuffer(pixel_t *buffer) { dest_screen = buffer; } // Restore screen buffer to the i_video screen buffer. void V_RestoreBuffer(void) { dest_screen = I_VideoBuffer; } // // SCREEN SHOTS // typedef PACKED_STRUCT ( { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin; unsigned short ymin; unsigned short xmax; unsigned short ymax; unsigned short hres; unsigned short vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned char data; // unbounded }) pcx_t; // // WritePCXfile // void WritePCXfile(char *filename, pixel_t *data, int width, int height, byte *palette) { int i; int length; pcx_t* pcx; byte* pack; pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL); pcx->manufacturer = 0x0a; // PCX id pcx->version = 5; // 256 color pcx->encoding = 1; // uncompressed pcx->bits_per_pixel = 8; // 256 color pcx->xmin = 0; pcx->ymin = 0; pcx->xmax = SHORT(width-1); pcx->ymax = SHORT(height-1); pcx->hres = SHORT(1); pcx->vres = SHORT(1); memset (pcx->palette,0,sizeof(pcx->palette)); pcx->reserved = 0; // PCX spec: reserved byte must be zero pcx->color_planes = 1; // chunky image pcx->bytes_per_line = SHORT(width); pcx->palette_type = SHORT(2); // not a grey scale memset (pcx->filler,0,sizeof(pcx->filler)); // pack the image pack = &pcx->data; for (i=0 ; i= mouse_threshold) { // Undo acceleration and get back the original mouse speed original_speed = speed - mouse_threshold; original_speed = (int) (original_speed / mouse_acceleration); original_speed += mouse_threshold; linelen = (original_speed * redline_x) / mouse_threshold; } else { linelen = (speed * redline_x) / mouse_threshold; } // Horizontal "thermometer" if (linelen > MOUSE_SPEED_BOX_WIDTH - 1) { linelen = MOUSE_SPEED_BOX_WIDTH - 1; } if (linelen < redline_x) { V_DrawHorizLine(MOUSE_SPEED_BOX_X + 1, MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, linelen, white); } else { V_DrawHorizLine(MOUSE_SPEED_BOX_X + 1, MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, redline_x, white); V_DrawHorizLine(MOUSE_SPEED_BOX_X + redline_x, MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, linelen - redline_x, yellow); } // Draw acceleration threshold line V_DrawVertLine(MOUSE_SPEED_BOX_X + redline_x, MOUSE_SPEED_BOX_Y + 1, MOUSE_SPEED_BOX_HEIGHT - 2, red); } // Highest seen mouse turn speed. We scale the range of the thermometer // according to this value, so that it never exceeds the range. Initially // this is set to a 1:1 setting where 1 pixel = 1 unit of speed. static int max_seen_speed = MOUSE_SPEED_BOX_WIDTH - 1; static void DrawNonAcceleratingBox(int speed) { int white; int linelen; #ifndef CRISPY_TRUECOLOR white = I_GetPaletteIndex(0xff, 0xff, 0xff); #else white = I_MapRGB(0xff, 0xff, 0xff); #endif if (speed > max_seen_speed) { max_seen_speed = speed; } // Draw horizontal "thermometer": linelen = speed * (MOUSE_SPEED_BOX_WIDTH - 1) / max_seen_speed; V_DrawHorizLine(MOUSE_SPEED_BOX_X + 1, MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, linelen, white); } void V_DrawMouseSpeedBox(int speed) { extern int usemouse; int bgcolor, bordercolor, black; // If the mouse is turned off, don't draw the box at all. if (!usemouse) { return; } // Get palette indices for colors for widget. These depend on the // palette of the game being played. #ifndef CRISPY_TRUECOLOR bgcolor = I_GetPaletteIndex(0x77, 0x77, 0x77); bordercolor = I_GetPaletteIndex(0x55, 0x55, 0x55); black = I_GetPaletteIndex(0x00, 0x00, 0x00); #else bgcolor = I_MapRGB(0x77, 0x77, 0x77); bordercolor = I_MapRGB(0x55, 0x55, 0x55); black = I_MapRGB(0x00, 0x00, 0x00); #endif // Calculate box position V_DrawFilledBox(MOUSE_SPEED_BOX_X, MOUSE_SPEED_BOX_Y, MOUSE_SPEED_BOX_WIDTH, MOUSE_SPEED_BOX_HEIGHT, bgcolor); V_DrawBox(MOUSE_SPEED_BOX_X, MOUSE_SPEED_BOX_Y, MOUSE_SPEED_BOX_WIDTH, MOUSE_SPEED_BOX_HEIGHT, bordercolor); V_DrawHorizLine(MOUSE_SPEED_BOX_X + 1, MOUSE_SPEED_BOX_Y + 4, MOUSE_SPEED_BOX_WIDTH - 2, black); // If acceleration is used, draw a box that helps to calibrate the // threshold point. if (mouse_threshold > 0 && fabs(mouse_acceleration - 1) > 0.01) { DrawAcceleratingBox(speed); } else { DrawNonAcceleratingBox(speed); } } crispy-doom-crispy-doom-5.6.4/src/v_video.h000066400000000000000000000063171360717211000206360ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Gamma correction LUT. // Functions to draw patches (by post) directly to screen. // Functions to blit a block to the screen. // #ifndef __V_VIDEO__ #define __V_VIDEO__ #include "doomtype.h" // Needed because we are refering to patches. #include "v_patch.h" // // VIDEO // #define CENTERY (SCREENHEIGHT/2) extern int dirtybox[4]; extern byte *tinttable; extern byte *dp_translation; extern boolean dp_translucent; // haleyjd 08/28/10: implemented for Strife support // haleyjd 08/28/10: Patch clipping callback, implemented to support Choco // Strife. typedef boolean (*vpatchclipfunc_t)(patch_t *, int, int); void V_SetPatchClipCallback(vpatchclipfunc_t func); // Allocates buffer screens, call before R_Init. void V_Init (void); // Draw a block from the specified source screen to the screen. void V_CopyRect(int srcx, int srcy, pixel_t *source, int width, int height, int destx, int desty); void V_DrawPatch(int x, int y, patch_t *patch); void V_DrawPatchFlipped(int x, int y, patch_t *patch); void V_DrawTLPatch(int x, int y, patch_t *patch); void V_DrawAltTLPatch(int x, int y, patch_t * patch); void V_DrawShadowedPatch(int x, int y, patch_t *patch); void V_DrawXlaPatch(int x, int y, patch_t * patch); // villsa [STRIFE] void V_DrawPatchDirect(int x, int y, patch_t *patch); void V_DrawPatchFullScreen(patch_t *patch, boolean flipped); // Draw a linear block of pixels into the view buffer. void V_DrawBlock(int x, int y, int width, int height, pixel_t *src); void V_DrawScaledBlock(int x, int y, int width, int height, pixel_t *src); void V_MarkRect(int x, int y, int width, int height); void V_DrawFilledBox(int x, int y, int w, int h, int c); void V_DrawHorizLine(int x, int y, int w, int c); void V_DrawVertLine(int x, int y, int h, int c); void V_DrawBox(int x, int y, int w, int h, int c); void V_CopyScaledBuffer(pixel_t *dest, pixel_t *src, size_t size); // Draw a raw screen lump void V_DrawRawScreen(pixel_t *raw); // Temporarily switch to using a different buffer to draw graphics, etc. void V_UseBuffer(pixel_t *buffer); // Return to using the normal screen buffer to draw graphics. void V_RestoreBuffer(void); // Save a screenshot of the current screen to a file, named in the // format described in the string passed to the function, eg. // "DOOM%02i.pcx" void V_ScreenShot(const char *format); // Load the lookup table for translucency calculations from the TINTTAB // lump. void V_LoadTintTable(void); // villsa [STRIFE] // Load the lookup table for translucency calculations from the XLATAB // lump. void V_LoadXlaTable(void); void V_DrawMouseSpeedBox(int speed); #endif crispy-doom-crispy-doom-5.6.4/src/w_checksum.c000066400000000000000000000043061360717211000213220ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Generate a checksum of the WAD directory. // #include #include #include #include "i_system.h" #include "m_misc.h" #include "sha1.h" #include "w_checksum.h" #include "w_wad.h" static wad_file_t **open_wadfiles = NULL; static int num_open_wadfiles = 0; static int GetFileNumber(wad_file_t *handle) { int i; int result; for (i = 0; i < num_open_wadfiles; ++i) { if (open_wadfiles[i] == handle) { return i; } } // Not found in list. This is a new file we haven't seen yet. // Allocate another slot for this file. open_wadfiles = I_Realloc(open_wadfiles, sizeof(wad_file_t *) * (num_open_wadfiles + 1)); open_wadfiles[num_open_wadfiles] = handle; result = num_open_wadfiles; ++num_open_wadfiles; return result; } static void ChecksumAddLump(sha1_context_t *sha1_context, lumpinfo_t *lump) { char buf[9]; M_StringCopy(buf, lump->name, sizeof(buf)); SHA1_UpdateString(sha1_context, buf); SHA1_UpdateInt32(sha1_context, GetFileNumber(lump->wad_file)); SHA1_UpdateInt32(sha1_context, lump->position); SHA1_UpdateInt32(sha1_context, lump->size); } void W_Checksum(sha1_digest_t digest) { sha1_context_t sha1_context; unsigned int i; SHA1_Init(&sha1_context); num_open_wadfiles = 0; // Go through each entry in the WAD directory, adding information // about each entry to the SHA1 hash. for (i = 0; i < numlumps; ++i) { ChecksumAddLump(&sha1_context, lumpinfo[i]); } SHA1_Final(digest, &sha1_context); } crispy-doom-crispy-doom-5.6.4/src/w_checksum.h000066400000000000000000000014541360717211000213300ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Generate a checksum of the WAD directory. // #ifndef W_CHECKSUM_H #define W_CHECKSUM_H #include "doomtype.h" extern void W_Checksum(sha1_digest_t digest); #endif /* #ifndef W_CHECKSUM_H */ crispy-doom-crispy-doom-5.6.4/src/w_file.c000066400000000000000000000035641360717211000204440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // WAD I/O functions. // #include #include "config.h" #include "doomtype.h" #include "m_argv.h" #include "w_file.h" extern wad_file_class_t stdc_wad_file; #ifdef _WIN32 extern wad_file_class_t win32_wad_file; #endif #ifdef HAVE_MMAP extern wad_file_class_t posix_wad_file; #endif static wad_file_class_t *wad_file_classes[] = { #ifdef _WIN32 &win32_wad_file, #endif #ifdef HAVE_MMAP &posix_wad_file, #endif &stdc_wad_file, }; wad_file_t *W_OpenFile(const char *path) { wad_file_t *result; int i; //! // @category obscure // // Use the OS's virtual memory subsystem to map WAD files // directly into memory. // if (!M_CheckParm("-mmap")) { return stdc_wad_file.OpenFile(path); } // Try all classes in order until we find one that works result = NULL; for (i=0; iOpenFile(path); if (result != NULL) { break; } } return result; } void W_CloseFile(wad_file_t *wad) { wad->file_class->CloseFile(wad); } size_t W_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len) { return wad->file_class->Read(wad, offset, buffer, buffer_len); } crispy-doom-crispy-doom-5.6.4/src/w_file.h000066400000000000000000000040721360717211000204440ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // WAD I/O functions. // #ifndef __W_FILE__ #define __W_FILE__ #include #include "doomtype.h" typedef struct _wad_file_s wad_file_t; typedef struct { // Open a file for reading. wad_file_t *(*OpenFile)(const char *path); // Close the specified file. void (*CloseFile)(wad_file_t *file); // Read data from the specified position in the file into the // provided buffer. Returns the number of bytes read. size_t (*Read)(wad_file_t *file, unsigned int offset, void *buffer, size_t buffer_len); } wad_file_class_t; struct _wad_file_s { // Class of this file. wad_file_class_t *file_class; // If this is NULL, the file cannot be mapped into memory. If this // is non-NULL, it is a pointer to the mapped file. byte *mapped; // Length of the file, in bytes. unsigned int length; // File's location on disk. char *path; // [crispy] un-const }; // Open the specified file. Returns a pointer to a new wad_file_t // handle for the WAD file, or NULL if it could not be opened. wad_file_t *W_OpenFile(const char *path); // Close the specified WAD file. void W_CloseFile(wad_file_t *wad); // Read data from the specified file into the provided buffer. The // data is read from the specified offset from the start of the file. // Returns the number of bytes read. size_t W_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len); #endif /* #ifndef __W_FILE__ */ crispy-doom-crispy-doom-5.6.4/src/w_file_posix.c000066400000000000000000000073141360717211000216630ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // WAD I/O functions. // #include "config.h" #ifdef HAVE_MMAP #include #include #include #include #include #include "m_misc.h" #include "w_file.h" #include "z_zone.h" typedef struct { wad_file_t wad; int handle; } posix_wad_file_t; extern wad_file_class_t posix_wad_file; static void MapFile(posix_wad_file_t *wad, const char *filename) { void *result; int protection; int flags; // Mapped area can be read and written to. Ideally // this should be read-only, as none of the Doom code should // change the WAD files after being read. However, there may // be code lurking in the source that does. protection = PROT_READ|PROT_WRITE; // Writes to the mapped area result in private changes that are // *not* written to disk. flags = MAP_PRIVATE; result = mmap(NULL, wad->wad.length, protection, flags, wad->handle, 0); wad->wad.mapped = result; if (result == NULL) { fprintf(stderr, "W_POSIX_OpenFile: Unable to mmap() %s - %s\n", filename, strerror(errno)); } } unsigned int GetFileLength(int handle) { return lseek(handle, 0, SEEK_END); } static wad_file_t *W_POSIX_OpenFile(const char *path) { posix_wad_file_t *result; int handle; handle = open(path, 0); if (handle < 0) { return NULL; } // Create a new posix_wad_file_t to hold the file handle. result = Z_Malloc(sizeof(posix_wad_file_t), PU_STATIC, 0); result->wad.file_class = &posix_wad_file; result->wad.length = GetFileLength(handle); result->wad.path = M_StringDuplicate(path); result->handle = handle; // Try to map the file into memory with mmap: MapFile(result, path); return &result->wad; } static void W_POSIX_CloseFile(wad_file_t *wad) { posix_wad_file_t *posix_wad; posix_wad = (posix_wad_file_t *) wad; // If mapped, unmap it. // Close the file close(posix_wad->handle); Z_Free(posix_wad); } // Read data from the specified position in the file into the // provided buffer. Returns the number of bytes read. size_t W_POSIX_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len) { posix_wad_file_t *posix_wad; byte *byte_buffer; size_t bytes_read; int result; posix_wad = (posix_wad_file_t *) wad; // Jump to the specified position in the file. lseek(posix_wad->handle, offset, SEEK_SET); // Read into the buffer. bytes_read = 0; byte_buffer = buffer; while (buffer_len > 0) { result = read(posix_wad->handle, byte_buffer, buffer_len); if (result < 0) { perror("W_POSIX_Read"); break; } else if (result == 0) { break; } // Successfully read some bytes byte_buffer += result; buffer_len -= result; bytes_read += result; } return bytes_read; } wad_file_class_t posix_wad_file = { W_POSIX_OpenFile, W_POSIX_CloseFile, W_POSIX_Read, }; #endif /* #ifdef HAVE_MMAP */ crispy-doom-crispy-doom-5.6.4/src/w_file_stdc.c000066400000000000000000000042321360717211000214520ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // WAD I/O functions. // #include #include "m_misc.h" #include "w_file.h" #include "z_zone.h" typedef struct { wad_file_t wad; FILE *fstream; } stdc_wad_file_t; extern wad_file_class_t stdc_wad_file; static wad_file_t *W_StdC_OpenFile(const char *path) { stdc_wad_file_t *result; FILE *fstream; fstream = fopen(path, "rb"); if (fstream == NULL) { return NULL; } // Create a new stdc_wad_file_t to hold the file handle. result = Z_Malloc(sizeof(stdc_wad_file_t), PU_STATIC, 0); result->wad.file_class = &stdc_wad_file; result->wad.mapped = NULL; result->wad.length = M_FileLength(fstream); result->wad.path = M_StringDuplicate(path); result->fstream = fstream; return &result->wad; } static void W_StdC_CloseFile(wad_file_t *wad) { stdc_wad_file_t *stdc_wad; stdc_wad = (stdc_wad_file_t *) wad; fclose(stdc_wad->fstream); Z_Free(stdc_wad); } // Read data from the specified position in the file into the // provided buffer. Returns the number of bytes read. size_t W_StdC_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len) { stdc_wad_file_t *stdc_wad; size_t result; stdc_wad = (stdc_wad_file_t *) wad; // Jump to the specified position in the file. fseek(stdc_wad->fstream, offset, SEEK_SET); // Read into the buffer. result = fread(buffer, 1, buffer_len, stdc_wad->fstream); return result; } wad_file_class_t stdc_wad_file = { W_StdC_OpenFile, W_StdC_CloseFile, W_StdC_Read, }; crispy-doom-crispy-doom-5.6.4/src/w_file_win32.c000066400000000000000000000111351360717211000214570ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // WAD I/O functions. // #include "config.h" #ifdef _WIN32 #include #define WIN32_LEAN_AND_MEAN #include #include "i_system.h" #include "m_misc.h" #include "w_file.h" #include "z_zone.h" // This constant doesn't exist in VC6: #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER 0xffffffff #endif typedef struct { wad_file_t wad; HANDLE handle; HANDLE handle_map; } win32_wad_file_t; extern wad_file_class_t win32_wad_file; static void MapFile(win32_wad_file_t *wad, const char *filename) { wad->handle_map = CreateFileMapping(wad->handle, NULL, PAGE_WRITECOPY, 0, 0, NULL); if (wad->handle_map == NULL) { fprintf(stderr, "W_Win32_OpenFile: Unable to CreateFileMapping() " "for %s\n", filename); return; } wad->wad.mapped = MapViewOfFile(wad->handle_map, FILE_MAP_COPY, 0, 0, 0); if (wad->wad.mapped == NULL) { fprintf(stderr, "W_Win32_OpenFile: Unable to MapViewOfFile() for %s\n", filename); } } unsigned int GetFileLength(HANDLE handle) { DWORD result; result = SetFilePointer(handle, 0, NULL, FILE_END); if (result == INVALID_SET_FILE_POINTER) { I_Error("W_Win32_OpenFile: Failed to read file length"); } return result; } static wad_file_t *W_Win32_OpenFile(const char *path) { win32_wad_file_t *result; wchar_t wpath[MAX_PATH + 1]; HANDLE handle; // Open the file: MultiByteToWideChar(CP_OEMCP, 0, path, strlen(path) + 1, wpath, sizeof(wpath)); handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { return NULL; } // Create a new win32_wad_file_t to hold the file handle. result = Z_Malloc(sizeof(win32_wad_file_t), PU_STATIC, 0); result->wad.file_class = &win32_wad_file; result->wad.length = GetFileLength(handle); result->wad.path = M_StringDuplicate(path); result->handle = handle; // Try to map the file into memory with mmap: MapFile(result, path); return &result->wad; } static void W_Win32_CloseFile(wad_file_t *wad) { win32_wad_file_t *win32_wad; win32_wad = (win32_wad_file_t *) wad; // If mapped, unmap it. if (win32_wad->wad.mapped != NULL) { UnmapViewOfFile(win32_wad->wad.mapped); } if (win32_wad->handle_map != NULL) { CloseHandle(win32_wad->handle_map); } // Close the file if (win32_wad->handle != NULL) { CloseHandle(win32_wad->handle); } Z_Free(win32_wad); } // Read data from the specified position in the file into the // provided buffer. Returns the number of bytes read. size_t W_Win32_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len) { win32_wad_file_t *win32_wad; DWORD bytes_read; DWORD result; win32_wad = (win32_wad_file_t *) wad; // Jump to the specified position in the file. result = SetFilePointer(win32_wad->handle, offset, NULL, FILE_BEGIN); if (result == INVALID_SET_FILE_POINTER) { I_Error("W_Win32_Read: Failed to set file pointer to %i", offset); } // Read into the buffer. if (!ReadFile(win32_wad->handle, buffer, buffer_len, &bytes_read, NULL)) { I_Error("W_Win32_Read: Error reading from file"); } return bytes_read; } wad_file_class_t win32_wad_file = { W_Win32_OpenFile, W_Win32_CloseFile, W_Win32_Read, }; #endif /* #ifdef _WIN32 */ crispy-doom-crispy-doom-5.6.4/src/w_main.c000066400000000000000000000147501360717211000204500ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Common code to parse command line, identifying WAD files to load. // #include #include "config.h" #include "d_iwad.h" #include "i_glob.h" #include "i_system.h" #include "m_argv.h" #include "w_main.h" #include "w_merge.h" #include "w_wad.h" #include "z_zone.h" // Parse the command line, merging WAD files that are sppecified. // Returns true if at least one file was added. boolean W_ParseCommandLine(void) { boolean modifiedgame = false; int p; // Merged PWADs are loaded first, because they are supposed to be // modified IWADs. //! // @arg // @category mod // // Simulates the behavior of deutex's -merge option, merging a PWAD // into the main IWAD. Multiple files may be specified. // p = M_CheckParmWithArgs("-merge", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Simulates the behavior of NWT's -merge option. Multiple files // may be specified. p = M_CheckParmWithArgs("-nwtmerge", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Simulates the behavior of NWT's -af option, merging flats into // the main IWAD directory. Multiple files may be specified. // p = M_CheckParmWithArgs("-af", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Simulates the behavior of NWT's -as option, merging sprites // into the main IWAD directory. Multiple files may be specified. // p = M_CheckParmWithArgs("-as", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Equivalent to "-af -as ". // p = M_CheckParmWithArgs("-aa", 1); if (p > 0) { for (p = p + 1; p // @vanilla // // Load the specified PWAD files. // p = M_CheckParmWithArgs ("-file", 1); if (p) { // the parms after p are wadfile/lump names, // until end of parms or another - preceded parm modifiedgame = true; // homebrew levels while (++p != myargc && myargv[p][0] != '-') { char *filename; filename = D_TryFindWADByName(myargv[p]); // [crispy] always merge arguments of "-file" parameter printf(" merging %s !\n", filename); W_MergeFile(filename); free(filename); } } // W_PrintDirectory(); return modifiedgame; } // Load all WAD files from the given directory. void W_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; } printf(" [autoload] merging %s\n", filename); W_MergeFile(filename); } I_EndGlob(glob); } // Lump names that are unique to particular game types. This lets us check // the user is not trying to play with the wrong executable, eg. // chocolate-doom -iwad hexen.wad. static const struct { GameMission_t mission; const char *lumpname; } unique_lumps[] = { { doom, "POSSA1" }, { heretic, "IMPXA1" }, { hexen, "ETTNA1" }, { strife, "AGRDA1" }, }; void W_CheckCorrectIWAD(GameMission_t mission) { int i; lumpindex_t lumpnum; for (i = 0; i < arrlen(unique_lumps); ++i) { if (mission != unique_lumps[i].mission) { lumpnum = W_CheckNumForName(unique_lumps[i].lumpname); if (lumpnum >= 0) { I_Error("\nYou are trying to use a %s IWAD file with " "the %s%s binary.\nThis isn't going to work.\n" "You probably want to use the %s%s binary.", D_SuggestGameName(unique_lumps[i].mission, indetermined), PROGRAM_PREFIX, D_GameMissionString(mission), PROGRAM_PREFIX, D_GameMissionString(unique_lumps[i].mission)); } } } } crispy-doom-crispy-doom-5.6.4/src/w_main.h000066400000000000000000000016561360717211000204560ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Common code to parse command line, identifying WAD files to load. // #ifndef W_MAIN_H #define W_MAIN_H #include "d_mode.h" boolean W_ParseCommandLine(void); void W_CheckCorrectIWAD(GameMission_t mission); int W_MergeDump (const char *file); // Autoload all .wad files from the given directory: void W_AutoLoadWADs(const char *path); #endif /* #ifndef W_MAIN_H */ crispy-doom-crispy-doom-5.6.4/src/w_merge.c000066400000000000000000000436161360717211000206260ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handles merging of PWADs, similar to deutex's -merge option // // Ideally this should work exactly the same as in deutex, but trying to // read the deutex source code made my brain hurt. // #include #include #include #include #include "doomtype.h" #include "i_swap.h" // [crispy] LONG() #include "i_system.h" #include "m_misc.h" #include "w_merge.h" #include "w_wad.h" #include "z_zone.h" typedef enum { SECTION_NORMAL, SECTION_FLATS, SECTION_SPRITES, } section_t; typedef struct { lumpinfo_t **lumps; int numlumps; } searchlist_t; typedef struct { char sprname[4]; char frame; lumpinfo_t *angle_lumps[8]; } sprite_frame_t; static searchlist_t iwad; static searchlist_t iwad_sprites; static searchlist_t pwad; static searchlist_t iwad_flats; static searchlist_t pwad_sprites; static searchlist_t pwad_flats; // lumps with these sprites must be replaced in the IWAD static sprite_frame_t *sprite_frames; static int num_sprite_frames; static int sprite_frames_alloced; // Search in a list to find a lump with a particular name // Linear search (slow!) // // Returns -1 if not found static int FindInList(searchlist_t *list, const char *name) { int i; for (i=0; inumlumps; ++i) { if (!strncasecmp(list->lumps[i]->name, name, 8)) return i; } return -1; } static boolean SetupList(searchlist_t *list, searchlist_t *src_list, const char *startname, const char *endname, const char *startname2, const char *endname2) { int startlump, endlump; list->numlumps = 0; startlump = FindInList(src_list, startname); if (startname2 != NULL && startlump < 0) { startlump = FindInList(src_list, startname2); } if (startlump >= 0) { endlump = FindInList(src_list, endname); if (endname2 != NULL && endlump < 0) { endlump = FindInList(src_list, endname2); } if (endlump > startlump) { list->lumps = src_list->lumps + startlump + 1; list->numlumps = endlump - startlump - 1; return true; } } return false; } // Sets up the sprite/flat search lists static void SetupLists(void) { // IWAD if (!SetupList(&iwad_flats, &iwad, "F_START", "F_END", NULL, NULL)) { I_Error("Flats section not found in IWAD"); } if (!SetupList(&iwad_sprites, &iwad, "S_START", "S_END", NULL, NULL)) { I_Error("Sprites section not found in IWAD"); } // PWAD SetupList(&pwad_flats, &pwad, "F_START", "F_END", "FF_START", "FF_END"); SetupList(&pwad_sprites, &pwad, "S_START", "S_END", "SS_START", "SS_END"); } // Initialize the replace list static void InitSpriteList(void) { if (sprite_frames == NULL) { sprite_frames_alloced = 128; sprite_frames = Z_Malloc(sizeof(*sprite_frames) * sprite_frames_alloced, PU_STATIC, NULL); } num_sprite_frames = 0; } static boolean ValidSpriteLumpName(char *name) { if (name[0] == '\0' || name[1] == '\0' || name[2] == '\0' || name[3] == '\0') { return false; } // First frame: if (name[4] == '\0' || !isdigit(name[5])) { return false; } // Second frame (optional): if (name[6] != '\0' && !isdigit(name[7])) { return false; } return true; } // Find a sprite frame static sprite_frame_t *FindSpriteFrame(char *name, int frame) { sprite_frame_t *result; int i; // Search the list and try to find the frame for (i=0; isprname, name, 4) && cur->frame == frame) { return cur; } } // Not found in list; Need to add to the list // Grow list? if (num_sprite_frames >= sprite_frames_alloced) { sprite_frame_t *newframes; newframes = Z_Malloc(sprite_frames_alloced * 2 * sizeof(*sprite_frames), PU_STATIC, NULL); memcpy(newframes, sprite_frames, sprite_frames_alloced * sizeof(*sprite_frames)); Z_Free(sprite_frames); sprite_frames_alloced *= 2; sprite_frames = newframes; } // Add to end of list result = &sprite_frames[num_sprite_frames]; memcpy(result->sprname, name, 4); result->frame = frame; for (i=0; i<8; ++i) result->angle_lumps[i] = NULL; ++num_sprite_frames; return result; } // Check if sprite lump is needed in the new wad static boolean SpriteLumpNeeded(lumpinfo_t *lump) { sprite_frame_t *sprite; int angle_num; int i; if (!ValidSpriteLumpName(lump->name)) { return true; } // check the first frame sprite = FindSpriteFrame(lump->name, lump->name[4]); angle_num = lump->name[5] - '0'; if (angle_num == 0) { // must check all frames for (i=0; i<8; ++i) { if (sprite->angle_lumps[i] == lump) return true; } } else { // check if this lump is being used for this frame if (sprite->angle_lumps[angle_num - 1] == lump) return true; } // second frame if any // no second frame? if (lump->name[6] == '\0') return false; sprite = FindSpriteFrame(lump->name, lump->name[6]); angle_num = lump->name[7] - '0'; if (angle_num == 0) { // must check all frames for (i=0; i<8; ++i) { if (sprite->angle_lumps[i] == lump) return true; } } else { // check if this lump is being used for this frame if (sprite->angle_lumps[angle_num - 1] == lump) return true; } return false; } static void AddSpriteLump(lumpinfo_t *lump) { sprite_frame_t *sprite; int angle_num; int i; if (!ValidSpriteLumpName(lump->name)) { return; } // first angle sprite = FindSpriteFrame(lump->name, lump->name[4]); angle_num = lump->name[5] - '0'; if (angle_num == 0) { for (i=0; i<8; ++i) sprite->angle_lumps[i] = lump; } else { sprite->angle_lumps[angle_num - 1] = lump; } // second angle // no second angle? if (lump->name[6] == '\0') return; sprite = FindSpriteFrame(lump->name, lump->name[6]); angle_num = lump->name[7] - '0'; if (angle_num == 0) { for (i=0; i<8; ++i) sprite->angle_lumps[i] = lump; } else { sprite->angle_lumps[angle_num - 1] = lump; } } // Generate the list. Run at the start, before merging static void GenerateSpriteList(void) { int i; InitSpriteList(); // Add all sprites from the IWAD for (i=0; iname, "F_START", 8)) { current_section = SECTION_FLATS; } else if (!strncasecmp(lump->name, "S_START", 8)) { current_section = SECTION_SPRITES; } newlumps[num_newlumps++] = lump; break; case SECTION_FLATS: // Have we reached the end of the section? if (!strncasecmp(lump->name, "F_END", 8)) { // Add all new flats from the PWAD to the end // of the section for (n=0; nname); if (lumpindex < 0) { newlumps[num_newlumps++] = lump; } } break; case SECTION_SPRITES: // Have we reached the end of the section? if (!strncasecmp(lump->name, "S_END", 8)) { // add all the PWAD sprites for (n=0; nname, "F_START", 8) || !strncasecmp(lump->name, "FF_START", 8)) { current_section = SECTION_FLATS; } else if (!strncasecmp(lump->name, "S_START", 8) || !strncasecmp(lump->name, "SS_START", 8)) { current_section = SECTION_SPRITES; } else { // Don't include the headers of sections newlumps[num_newlumps++] = lump; } break; case SECTION_FLATS: // PWAD flats are ignored (already merged) if (!strncasecmp(lump->name, "FF_END", 8) || !strncasecmp(lump->name, "F_END", 8)) { // end of section current_section = SECTION_NORMAL; } break; case SECTION_SPRITES: // PWAD sprites are ignored (already merged) if (!strncasecmp(lump->name, "SS_END", 8) || !strncasecmp(lump->name, "S_END", 8)) { // end of section current_section = SECTION_NORMAL; } break; } } // Switch to the new lumpinfo, and free the old one free(lumpinfo); lumpinfo = newlumps; numlumps = num_newlumps; } void W_PrintDirectory(void) { unsigned int i, n; // debug for (i=0; iname[n] != '\0'; ++n) putchar(lumpinfo[i]->name[n]); putchar('\n'); } } // Merge in a file by name void W_MergeFile(const char *filename) { int old_numlumps; old_numlumps = numlumps; // Load PWAD if (W_AddFile(filename) == NULL) return; // IWAD is at the start, PWAD was appended to the end iwad.lumps = lumpinfo; iwad.numlumps = old_numlumps; pwad.lumps = lumpinfo + old_numlumps; pwad.numlumps = numlumps - old_numlumps; // Setup sprite/flat lists SetupLists(); // Generate list of sprites to be replaced by the PWAD GenerateSpriteList(); // Perform the merge DoMerge(); } // Replace lumps in the given list with lumps from the PWAD static void W_NWTAddLumps(searchlist_t *list) { int i; // Go through the IWAD list given, replacing lumps with lumps of // the same name from the PWAD for (i=0; inumlumps; ++i) { int index; index = FindInList(&pwad, list->lumps[i]->name); if (index > 0) { memcpy(list->lumps[i], pwad.lumps[index], sizeof(lumpinfo_t)); } } } // Merge sprites and flats in the way NWT does with its -af and -as // command-line options. void W_NWTMergeFile(const char *filename, int flags) { int old_numlumps; old_numlumps = numlumps; // Load PWAD if (W_AddFile(filename) == NULL) return; // IWAD is at the start, PWAD was appended to the end iwad.lumps = lumpinfo; iwad.numlumps = old_numlumps; pwad.lumps = lumpinfo + old_numlumps; pwad.numlumps = numlumps - old_numlumps; // Setup sprite/flat lists SetupLists(); // Merge in flats? if (flags & W_NWT_MERGE_FLATS) { W_NWTAddLumps(&iwad_flats); } // Sprites? if (flags & W_NWT_MERGE_SPRITES) { W_NWTAddLumps(&iwad_sprites); } // Discard the PWAD numlumps = old_numlumps; } // Simulates the NWT -merge command line parameter. What this does is load // a PWAD, then search the IWAD sprites, removing any sprite lumps that also // exist in the PWAD. void W_NWTDashMerge(const char *filename) { wad_file_t *wad_file; int old_numlumps; int i; old_numlumps = numlumps; // Load PWAD wad_file = W_AddFile(filename); if (wad_file == NULL) { return; } // IWAD is at the start, PWAD was appended to the end iwad.lumps = lumpinfo; iwad.numlumps = old_numlumps; pwad.lumps = lumpinfo + old_numlumps; pwad.numlumps = numlumps - old_numlumps; // Setup sprite/flat lists SetupLists(); // Search through the IWAD sprites list. for (i=0; iname) >= 0) { // Replace this entry with an empty string. This is what // nwt -merge does. M_StringCopy(iwad_sprites.lumps[i]->name, "", 8); } } // Discard PWAD // The PWAD must now be added in again with -file. numlumps = old_numlumps; W_CloseFile(wad_file); } // [crispy] dump merged WAD data into a new IWAD file int W_MergeDump (const char *file) { FILE *fp = NULL; char *lump_p = NULL; uint32_t i, dir_p; // [crispy] WAD directory structure typedef struct { uint32_t pos; uint32_t size; char name[8]; } directory_t; directory_t *dir = NULL; // [crispy] open file for writing fp = fopen(file, "wb"); if (!fp) { I_Error("W_MergeDump: Failed writing to file '%s'!", file); } // [crispy] prepare directory dir = calloc(numlumps, sizeof(*dir)); if (!dir) { I_Error("W_MergeDump: Error allocating memory!"); } // [crispy] write lumps to file, starting at offset 12 fseek(fp, 12, SEEK_SET); for (i = 0; i < numlumps; i++) { dir[i].pos = LONG(ftell(fp)); dir[i].size = LONG(lumpinfo[i]->size); // [crispy] lump names are zero-byte padded memset(dir[i].name, 0, 8); strncpy(dir[i].name, lumpinfo[i]->name, 8); // [crispy] avoid flooding Doom's Zone Memory lump_p = I_Realloc(lump_p, lumpinfo[i]->size); W_ReadLump(i, lump_p); fwrite(lump_p, 1, lumpinfo[i]->size, fp); } free(lump_p); // [crispy] write directory dir_p = LONG(ftell(fp)); fwrite(dir, sizeof(*dir), i, fp); free(dir); // [crispy] write WAD header fseek(fp, 0, SEEK_SET); fwrite("IWAD", 1, 4, fp); i = LONG(i); fwrite(&i, 4, 1, fp); fwrite(&dir_p, 4, 1, fp); fclose(fp); return (i); } crispy-doom-crispy-doom-5.6.4/src/w_merge.h000066400000000000000000000023261360717211000206240ustar00rootroot00000000000000// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handles merging of PWADs, similar to deutex's -merge option // // Ideally this should work exactly the same as in deutex, but trying to // read the deutex source code made my brain hurt. // #ifndef W_MERGE_H #define W_MERGE_H #define W_NWT_MERGE_SPRITES 0x1 #define W_NWT_MERGE_FLATS 0x2 // Add a new WAD and merge it into the main directory void W_MergeFile(const char *filename); // NWT-style merging void W_NWTMergeFile(const char *filename, int flags); // Acts the same as NWT's "-merge" option. void W_NWTDashMerge(const char *filename); // Debug function that prints the WAD directory. void W_PrintDirectory(void); #endif /* #ifndef W_MERGE_H */ crispy-doom-crispy-doom-5.6.4/src/w_wad.c000066400000000000000000000332711360717211000202760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Handles WAD file header, directory, lump I/O. // #include #include #include #include #include "doomtype.h" #include "i_swap.h" #include "i_system.h" #include "i_video.h" #include "m_misc.h" #include "v_diskicon.h" #include "z_zone.h" #include "w_wad.h" typedef PACKED_STRUCT ( { // Should be "IWAD" or "PWAD". char identification[4]; int numlumps; int infotableofs; }) wadinfo_t; typedef PACKED_STRUCT ( { int filepos; int size; char name[8]; }) filelump_t; // // GLOBALS // // Location of each lump on disk. lumpinfo_t **lumpinfo; unsigned int numlumps = 0; // Hash table for fast lookups static lumpindex_t *lumphash; // Variables for the reload hack: filename of the PWAD to reload, and the // lumps from WADs before the reload file, so we can resent numlumps and // load the file again. static wad_file_t *reloadhandle = NULL; static lumpinfo_t *reloadlumps = NULL; static char *reloadname = NULL; static int reloadlump = -1; // Hash function used for lump names. unsigned int W_LumpNameHash(const char *s) { // This is the djb2 string hash function, modded to work on strings // that have a maximum length of 8. unsigned int result = 5381; unsigned int i; for (i=0; i < 8 && s[i] != '\0'; ++i) { result = ((result << 5) ^ result ) ^ toupper(s[i]); } return result; } // // LUMP BASED ROUTINES. // // // W_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. wad_file_t *W_AddFile (const char *filename) { wadinfo_t header; lumpindex_t i; wad_file_t *wad_file; int length; int startlump; filelump_t *fileinfo; filelump_t *filerover; lumpinfo_t *filelumps; int numfilelumps; // If the filename begins with a ~, it indicates that we should use the // reload hack. if (filename[0] == '~') { if (reloadname != NULL) { I_Error("Prefixing a WAD filename with '~' indicates that the " "WAD should be reloaded\n" "on each level restart, for use by level authors for " "rapid development. You\n" "can only reload one WAD file, and it must be the last " "file in the -file list."); } reloadname = strdup(filename); reloadlump = numlumps; ++filename; } // Open the file and add to directory wad_file = W_OpenFile(filename); if (wad_file == NULL) { printf (" couldn't open %s\n", filename); return NULL; } if (strcasecmp(filename+strlen(filename)-3 , "wad" ) ) { // single lump file // fraggle: Swap the filepos and size here. The WAD directory // parsing code expects a little-endian directory, so will swap // them back. Effectively we're constructing a "fake WAD directory" // here, as it would appear on disk. fileinfo = Z_Malloc(sizeof(filelump_t), PU_STATIC, 0); fileinfo->filepos = LONG(0); fileinfo->size = LONG(wad_file->length); // Name the lump after the base of the filename (without the // extension). M_ExtractFileBase (filename, fileinfo->name); numfilelumps = 1; } else { // WAD file W_Read(wad_file, 0, &header, sizeof(header)); if (strncmp(header.identification,"IWAD",4)) { // Homebrew levels? if (strncmp(header.identification,"PWAD",4)) { W_CloseFile(wad_file); I_Error ("Wad file %s doesn't have IWAD " "or PWAD id\n", filename); } // ???modifiedgame = true; } header.numlumps = LONG(header.numlumps); // Vanilla Doom doesn't like WADs with more than 4046 lumps // https://www.doomworld.com/vb/post/1010985 // [crispy] disable PWAD lump number limit if (!strncmp(header.identification,"PWAD",4) && header.numlumps > 4046 && false) { W_CloseFile(wad_file); I_Error ("Error: Vanilla limit for lumps in a WAD is 4046, " "PWAD %s has %d", filename, header.numlumps); } header.infotableofs = LONG(header.infotableofs); length = header.numlumps*sizeof(filelump_t); fileinfo = Z_Malloc(length, PU_STATIC, 0); W_Read(wad_file, header.infotableofs, fileinfo, length); numfilelumps = header.numlumps; } // Increase size of numlumps array to accomodate the new file. filelumps = calloc(numfilelumps, sizeof(lumpinfo_t)); if (filelumps == NULL) { W_CloseFile(wad_file); I_Error("Failed to allocate array for lumps from new file."); } startlump = numlumps; numlumps += numfilelumps; lumpinfo = I_Realloc(lumpinfo, numlumps * sizeof(lumpinfo_t *)); filerover = fileinfo; for (i = startlump; i < numlumps; ++i) { lumpinfo_t *lump_p = &filelumps[i - startlump]; lump_p->wad_file = wad_file; lump_p->position = LONG(filerover->filepos); lump_p->size = LONG(filerover->size); lump_p->cache = NULL; strncpy(lump_p->name, filerover->name, 8); lumpinfo[i] = lump_p; ++filerover; } Z_Free(fileinfo); if (lumphash != NULL) { Z_Free(lumphash); lumphash = NULL; } // If this is the reload file, we need to save some details about the // file so that we can close it later on when we do a reload. if (reloadname) { reloadhandle = wad_file; reloadlumps = filelumps; } return wad_file; } // // W_NumLumps // int W_NumLumps (void) { return numlumps; } // // W_CheckNumForName // Returns -1 if name not found. // lumpindex_t W_CheckNumForName(const char *name) { lumpindex_t i; // Do we have a hash table yet? if (lumphash != NULL) { int hash; // We do! Excellent. hash = W_LumpNameHash(name) % numlumps; for (i = lumphash[hash]; i != -1; i = lumpinfo[i]->next) { if (!strncasecmp(lumpinfo[i]->name, name, 8)) { return i; } } } else { // We don't have a hash table generate yet. Linear search :-( // // scan backwards so patch lump files take precedence for (i = numlumps - 1; i >= 0; --i) { if (!strncasecmp(lumpinfo[i]->name, name, 8)) { return i; } } } // TFB. Not found. return -1; } // // W_GetNumForName // Calls W_CheckNumForName, but bombs out if not found. // lumpindex_t W_GetNumForName(const char *name) { lumpindex_t i; i = W_CheckNumForName (name); if (i < 0) { I_Error ("W_GetNumForName: %s not found!", name); } return i; } lumpindex_t W_CheckNumForNameFromTo(const char *name, int from, int to) { lumpindex_t i; for (i = from; i >= to; i--) { if (!strncasecmp(lumpinfo[i]->name, name, 8)) { return i; } } return -1; } // // W_LumpLength // Returns the buffer size needed to load the given lump. // int W_LumpLength(lumpindex_t 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(lumpindex_t lump, void *dest) { int c; lumpinfo_t *l; if (lump >= numlumps) { I_Error ("W_ReadLump: %i >= numlumps", lump); } l = lumpinfo[lump]; V_BeginRead(l->size); c = W_Read(l->wad_file, l->position, dest, l->size); if (c < l->size) { I_Error("W_ReadLump: only read %i of %i on lump %i", c, l->size, lump); } } // // W_CacheLumpNum // // Load a lump into memory and return a pointer to a buffer containing // the lump data. // // 'tag' is the type of zone memory buffer to allocate for the lump // (usually PU_STATIC or PU_CACHE). If the lump is loaded as // PU_STATIC, it should be released back using W_ReleaseLumpNum // when no longer needed (do not use Z_ChangeTag). // void *W_CacheLumpNum(lumpindex_t lumpnum, int tag) { byte *result; lumpinfo_t *lump; if ((unsigned)lumpnum >= numlumps) { I_Error ("W_CacheLumpNum: %i >= numlumps", lumpnum); } lump = lumpinfo[lumpnum]; // Get the pointer to return. If the lump is in a memory-mapped // file, we can just return a pointer to within the memory-mapped // region. If the lump is in an ordinary file, we may already // have it cached; otherwise, load it into memory. if (lump->wad_file->mapped != NULL) { // Memory mapped file, return from the mmapped region. result = lump->wad_file->mapped + lump->position; } else if (lump->cache != NULL) { // Already cached, so just switch the zone tag. result = lump->cache; Z_ChangeTag(lump->cache, tag); } else { // Not yet loaded, so load it now lump->cache = Z_Malloc(W_LumpLength(lumpnum), tag, &lump->cache); W_ReadLump (lumpnum, lump->cache); result = lump->cache; } return result; } // // W_CacheLumpName // void *W_CacheLumpName(const char *name, int tag) { return W_CacheLumpNum(W_GetNumForName(name), tag); } // // Release a lump back to the cache, so that it can be reused later // without having to read from disk again, or alternatively, discarded // if we run out of memory. // // Back in Vanilla Doom, this was just done using Z_ChangeTag // directly, but now that we have WAD mmap, things are a bit more // complicated ... // void W_ReleaseLumpNum(lumpindex_t lumpnum) { lumpinfo_t *lump; if ((unsigned)lumpnum >= numlumps) { I_Error ("W_ReleaseLumpNum: %i >= numlumps", lumpnum); } lump = lumpinfo[lumpnum]; if (lump->wad_file->mapped != NULL) { // Memory-mapped file, so nothing needs to be done here. } else { Z_ChangeTag(lump->cache, PU_CACHE); } } void W_ReleaseLumpName(const char *name) { W_ReleaseLumpNum(W_GetNumForName(name)); } #if 0 // // W_Profile // int info[2500][10]; int profilecount; void W_Profile (void) { int i; memblock_t* block; void* ptr; char ch; FILE* f; int j; char name[9]; for (i=0 ; itag < PU_PURGELEVEL) ch = 'S'; else ch = 'P'; } info[i][profilecount] = ch; } profilecount++; f = fopen ("waddump.txt","w"); name[8] = 0; for (i=0 ; i 0) { lumphash = Z_Malloc(sizeof(lumpindex_t) * numlumps, PU_STATIC, NULL); for (i = 0; i < numlumps; ++i) { lumphash[i] = -1; } for (i = 0; i < numlumps; ++i) { unsigned int hash; hash = W_LumpNameHash(lumpinfo[i]->name) % numlumps; // Hook into the hash table lumpinfo[i]->next = lumphash[hash]; lumphash[hash] = i; } } // All done! } // The Doom reload hack. The idea here is that if you give a WAD file to -file // prefixed with the ~ hack, that WAD file will be reloaded each time a new // level is loaded. This lets you use a level editor in parallel and make // incremental changes to the level you're working on without having to restart // the game after every change. // But: the reload feature is a fragile hack... void W_Reload(void) { char *filename; lumpindex_t i; if (reloadname == NULL) { return; } // We must free any lumps being cached from the PWAD we're about to reload: for (i = reloadlump; i < numlumps; ++i) { if (lumpinfo[i]->cache != NULL) { Z_Free(lumpinfo[i]->cache); } } // Reset numlumps to remove the reload WAD file: numlumps = reloadlump; // Now reload the WAD file. filename = reloadname; W_CloseFile(reloadhandle); free(reloadlumps); reloadname = NULL; reloadlump = -1; reloadhandle = NULL; W_AddFile(filename); free(filename); // The WAD directory has changed, so we have to regenerate the // fast lookup hashtable: W_GenerateHashTable(); } const char *W_WadNameForLump(const lumpinfo_t *lump) { return M_BaseName(lump->wad_file->path); } boolean W_IsIWADLump(const lumpinfo_t *lump) { return lump->wad_file == lumpinfo[0]->wad_file; } crispy-doom-crispy-doom-5.6.4/src/w_wad.h000066400000000000000000000034051360717211000202770ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // WAD I/O functions. // #ifndef __W_WAD__ #define __W_WAD__ #include #include "doomtype.h" #include "w_file.h" // // TYPES // // // WADFILE I/O related stuff. // typedef struct lumpinfo_s lumpinfo_t; typedef int lumpindex_t; struct lumpinfo_s { char name[8]; wad_file_t *wad_file; int position; int size; void *cache; // Used for hash table lookups lumpindex_t next; }; extern lumpinfo_t **lumpinfo; extern unsigned int numlumps; wad_file_t *W_AddFile(const char *filename); void W_Reload(void); lumpindex_t W_CheckNumForName(const char *name); lumpindex_t W_GetNumForName(const char *name); lumpindex_t W_CheckNumForNameFromTo(const char *name, int from, int to); int W_LumpLength(lumpindex_t lump); void W_ReadLump(lumpindex_t lump, void *dest); void *W_CacheLumpNum(lumpindex_t lump, int tag); void *W_CacheLumpName(const char *name, int tag); void W_GenerateHashTable(void); extern unsigned int W_LumpNameHash(const char *s); void W_ReleaseLumpNum(lumpindex_t lump); void W_ReleaseLumpName(const char *name); const char *W_WadNameForLump(const lumpinfo_t *lump); boolean W_IsIWADLump(const lumpinfo_t *lump); #endif crispy-doom-crispy-doom-5.6.4/src/z_native.c000066400000000000000000000225161360717211000210140ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Zone Memory Allocation. Neat. // // This is an implementation of the zone memory API which // uses native calls to malloc() and free(). // #include #include #include "z_zone.h" #include "i_system.h" #include "doomtype.h" #define ZONEID 0x1d4a11 typedef struct memblock_s memblock_t; struct memblock_s { int id; // = ZONEID int tag; int size; void **user; memblock_t *prev; memblock_t *next; }; // Linked list of allocated blocks for each tag type static memblock_t *allocated_blocks[PU_NUM_TAGS]; #ifdef TESTING static int test_malloced = 0; void *test_malloc(size_t size) { int *result; if (test_malloced + size > 2 * 1024 * 1024) { return NULL; } test_malloced += size; result = malloc(size + sizeof(int)); *result = size; return result + 1; } void test_free(void *data) { int *i; i = ((int *) data) - 1; test_malloced -= *i; free(i); } #define malloc test_malloc #define free test_free #endif /* #ifdef TESTING */ // Add a block into the linked list for its type. static void Z_InsertBlock(memblock_t *block) { block->prev = NULL; block->next = allocated_blocks[block->tag]; allocated_blocks[block->tag] = block; if (block->next != NULL) { block->next->prev = block; } } // Remove a block from its linked list. static void Z_RemoveBlock(memblock_t *block) { // Unlink from list if (block->prev == NULL) { // Start of list allocated_blocks[block->tag] = block->next; } else { block->prev->next = block->next; } if (block->next != NULL) { block->next->prev = block->prev; } } // // Z_Init // void Z_Init (void) { memset(allocated_blocks, 0, sizeof(allocated_blocks)); printf("zone memory: Using native C allocator.\n"); } // // Z_Free // void Z_Free (void* ptr) { memblock_t* block; block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) { I_Error ("Z_Free: freed a pointer without ZONEID"); } if (block->tag != PU_FREE && block->user != NULL) { // clear the user's mark *block->user = NULL; } Z_RemoveBlock(block); // Free back to system free(block); } // Empty data from the cache list to allocate enough data of the size // required. // // Returns true if any blocks were freed. static boolean ClearCache(int size) { memblock_t *block; memblock_t *next_block; int remaining; block = allocated_blocks[PU_CACHE]; if (block == NULL) { // Cache is already empty. return false; } // Search to the end of the PU_CACHE list. The blocks at the end // of the list are the ones that have been free for longer and // are more likely to be unneeded now. while (block->next != NULL) { block = block->next; } //printf("out of memory; cleaning out the cache: %i\n", test_malloced); // Search backwards through the list freeing blocks until we have // freed the amount of memory required. remaining = size; while (remaining > 0) { if (block == NULL) { // No blocks left to free; we've done our best. break; } next_block = block->prev; Z_RemoveBlock(block); remaining -= block->size; if (block->user) { *block->user = NULL; } free(block); block = next_block; } return true; } // // Z_Malloc // You can pass a NULL user if the tag is < PU_PURGELEVEL. // void *Z_Malloc(int size, int tag, void *user) { memblock_t *newblock; unsigned char *data; void *result; if (tag < 0 || tag >= PU_NUM_TAGS || tag == PU_FREE) { I_Error("Z_Malloc: attempted to allocate a block with an invalid " "tag: %i", tag); } if (user == NULL && tag >= PU_PURGELEVEL) { I_Error ("Z_Malloc: an owner is required for purgable blocks"); } // Malloc a block of the required size newblock = NULL; while (newblock == NULL) { newblock = (memblock_t *) malloc(sizeof(memblock_t) + size); if (newblock == NULL) { if (!ClearCache(sizeof(memblock_t) + size)) { I_Error("Z_Malloc: failed on allocation of %i bytes", size); } } } newblock->tag = tag; // Hook into the linked list for this tag type newblock->id = ZONEID; newblock->user = user; newblock->size = size; Z_InsertBlock(newblock); data = (unsigned char *) newblock; result = data + sizeof(memblock_t); if (user != NULL) { *newblock->user = result; } return result; } // // Z_FreeTags // void Z_FreeTags(int lowtag, int hightag) { int i; for (i=lowtag; i<= hightag; ++i) { memblock_t *block; memblock_t *next; // Free all in this chain for (block=allocated_blocks[i]; block != NULL; ) { next = block->next; // Free this block if (block->user != NULL) { *block->user = NULL; } free(block); // Jump to the next in the chain block = next; } // This chain is empty now allocated_blocks[i] = NULL; } } // // Z_DumpHeap // void Z_DumpHeap(int lowtag, int hightag) { // broken #if 0 memblock_t* block; printf ("zone size: %i location: %p\n", mainzone->size,mainzone); printf ("tag range: %i to %i\n", lowtag, hightag); for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->tag >= lowtag && block->tag <= hightag) printf ("block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) printf ("ERROR: block size does not touch the next block\n"); if ( block->next->prev != block) printf ("ERROR: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) printf ("ERROR: two consecutive free blocks\n"); } #endif } // // Z_FileDumpHeap // void Z_FileDumpHeap(FILE *f) { // broken #if 0 memblock_t* block; fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone); for (block = mainzone->blocklist.next ; ; block = block->next) { fprintf (f,"block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) fprintf (f,"ERROR: block size does not touch the next block\n"); if ( block->next->prev != block) fprintf (f,"ERROR: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) fprintf (f,"ERROR: two consecutive free blocks\n"); } #endif } // // Z_CheckHeap // void Z_CheckHeap (void) { memblock_t *block; memblock_t *prev; int i; // Check all chains for (i=0; inext) { if (block->id != ZONEID) { I_Error("Z_CheckHeap: Block without a ZONEID!"); } if (block->prev != prev) { I_Error("Z_CheckHeap: Doubly-linked list corrupted!"); } prev = block; } } } // // Z_ChangeTag // void Z_ChangeTag2(void *ptr, int tag, const char *file, int line) { memblock_t* block; block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) I_Error("%s:%i: Z_ChangeTag: block without a ZONEID!", file, line); if (tag >= PU_PURGELEVEL && block->user == NULL) I_Error("%s:%i: Z_ChangeTag: an owner is required " "for purgable blocks", file, line); // Remove the block from its current list, and rehook it into // its new list. Z_RemoveBlock(block); block->tag = tag; Z_InsertBlock(block); } void Z_ChangeUser(void *ptr, void **user) { memblock_t* block; block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) { I_Error("Z_ChangeUser: Tried to change user for invalid block!"); } block->user = user; *user = ptr; } // // Z_FreeMemory // int Z_FreeMemory(void) { // Limited by the system?? return -1; } unsigned int Z_ZoneSize(void) { return 0; } crispy-doom-crispy-doom-5.6.4/src/z_zone.c000066400000000000000000000305321360717211000204760ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Zone Memory Allocation. Neat. // #include #include "doomtype.h" #include "i_system.h" #include "m_argv.h" #include "z_zone.h" // // ZONE MEMORY ALLOCATION // // There is never any space between memblocks, // and there will never be two contiguous free memblocks. // The rover can be left pointing at a non-empty block. // // It is of no value to free a cachable block, // because it will get overwritten automatically if needed. // #define MEM_ALIGN sizeof(void *) #define ZONEID 0x1d4a11 typedef struct memblock_s { int size; // including the header and possibly tiny fragments void** user; int tag; // PU_FREE if this is free int id; // should be ZONEID struct memblock_s* next; struct memblock_s* prev; } memblock_t; typedef struct { // total bytes malloced, including header int size; // start / end cap for linked list memblock_t blocklist; memblock_t* rover; } memzone_t; static memzone_t *mainzone; static boolean zero_on_free; static boolean scan_on_free; // // Z_ClearZone // void Z_ClearZone (memzone_t* zone) { memblock_t* block; // set the entire zone to one free block zone->blocklist.next = zone->blocklist.prev = block = (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); zone->blocklist.user = (void *)zone; zone->blocklist.tag = PU_STATIC; zone->rover = block; block->prev = block->next = &zone->blocklist; // a free block. block->tag = PU_FREE; block->size = zone->size - sizeof(memzone_t); } // // Z_Init // void Z_Init (void) { memblock_t* block; int size; mainzone = (memzone_t *)I_ZoneBase (&size); mainzone->size = size; // set the entire zone to one free block mainzone->blocklist.next = mainzone->blocklist.prev = block = (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) ); mainzone->blocklist.user = (void *)mainzone; mainzone->blocklist.tag = PU_STATIC; mainzone->rover = block; block->prev = block->next = &mainzone->blocklist; // free block block->tag = PU_FREE; block->size = mainzone->size - sizeof(memzone_t); // [Deliberately undocumented] // Zone memory debugging flag. If set, memory is zeroed after it is freed // to deliberately break any code that attempts to use it after free. // zero_on_free = M_ParmExists("-zonezero"); // [Deliberately undocumented] // Zone memory debugging flag. If set, each time memory is freed, the zone // heap is scanned to look for remaining pointers to the freed block. // scan_on_free = M_ParmExists("-zonescan"); } // Scan the zone heap for pointers within the specified range, and warn about // any remaining pointers. static void ScanForBlock(void *start, void *end) { memblock_t *block; void **mem; int i, len, tag; block = mainzone->blocklist.next; while (block->next != &mainzone->blocklist) { tag = block->tag; if (tag == PU_STATIC || tag == PU_LEVEL || tag == PU_LEVSPEC) { // Scan for pointers on the assumption that pointers are aligned // on word boundaries (word size depending on pointer size): mem = (void **) ((byte *) block + sizeof(memblock_t)); len = (block->size - sizeof(memblock_t)) / sizeof(void *); for (i = 0; i < len; ++i) { if (start <= mem[i] && mem[i] <= end) { fprintf(stderr, "%p has dangling pointer into freed block " "%p (%p -> %p)\n", mem, start, &mem[i], mem[i]); } } } block = block->next; } } // // Z_Free // void Z_Free (void* ptr) { memblock_t* block; memblock_t* other; block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) I_Error ("Z_Free: freed a pointer without ZONEID"); if (block->tag != PU_FREE && block->user != NULL) { // clear the user's mark *block->user = 0; } // mark as free block->tag = PU_FREE; block->user = NULL; block->id = 0; // If the -zonezero flag is provided, we zero out the block on free // to break code that depends on reading freed memory. if (zero_on_free) { memset(ptr, 0, block->size - sizeof(memblock_t)); } if (scan_on_free) { ScanForBlock(ptr, (byte *) ptr + block->size - sizeof(memblock_t)); } other = block->prev; if (other->tag == PU_FREE) { // merge with previous free block other->size += block->size; other->next = block->next; other->next->prev = other; if (block == mainzone->rover) mainzone->rover = other; block = other; } other = block->next; if (other->tag == PU_FREE) { // merge the next free block onto the end block->size += other->size; block->next = other->next; block->next->prev = block; if (other == mainzone->rover) mainzone->rover = block; } } // // Z_Malloc // You can pass a NULL user if the tag is < PU_PURGELEVEL. // #define MINFRAGMENT 64 void* Z_Malloc ( int size, int tag, void* user ) { int extra; memblock_t* start; memblock_t* rover; memblock_t* newblock; memblock_t* base; void *result; size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1); // scan through the block list, // looking for the first free block // of sufficient size, // throwing out any purgable blocks along the way. // account for size of block header size += sizeof(memblock_t); // if there is a free block behind the rover, // back up over them base = mainzone->rover; if (base->prev->tag == PU_FREE) base = base->prev; rover = base; start = base->prev; do { if (rover == start) { // scanned all the way around the list // I_Error ("Z_Malloc: failed on allocation of %i bytes", size); // [crispy] allocate another zone twice as big Z_Init(); base = mainzone->rover; rover = base; start = base->prev; } if (rover->tag != PU_FREE) { if (rover->tag < PU_PURGELEVEL) { // hit a block that can't be purged, // so move base past it base = rover = rover->next; } else { // free the rover block (adding the size to base) // the rover can be the base block base = base->prev; Z_Free ((byte *)rover+sizeof(memblock_t)); base = base->next; rover = base->next; } } else { rover = rover->next; } } while (base->tag != PU_FREE || base->size < size); // found a block big enough extra = base->size - size; if (extra > MINFRAGMENT) { // there will be a free fragment after the allocated block newblock = (memblock_t *) ((byte *)base + size ); newblock->size = extra; newblock->tag = PU_FREE; newblock->user = NULL; newblock->prev = base; newblock->next = base->next; newblock->next->prev = newblock; base->next = newblock; base->size = size; } if (user == NULL && tag >= PU_PURGELEVEL) I_Error ("Z_Malloc: an owner is required for purgable blocks"); base->user = user; base->tag = tag; result = (void *) ((byte *)base + sizeof(memblock_t)); if (base->user) { *base->user = result; } // next allocation will start looking here mainzone->rover = base->next; base->id = ZONEID; return result; } // // Z_FreeTags // void Z_FreeTags ( int lowtag, int hightag ) { memblock_t* block; memblock_t* next; for (block = mainzone->blocklist.next ; block != &mainzone->blocklist ; block = next) { // get link before freeing next = block->next; // free block? if (block->tag == PU_FREE) continue; if (block->tag >= lowtag && block->tag <= hightag) Z_Free ( (byte *)block+sizeof(memblock_t)); } } // // Z_DumpHeap // Note: TFileDumpHeap( stdout ) ? // void Z_DumpHeap ( int lowtag, int hightag ) { memblock_t* block; printf ("zone size: %i location: %p\n", mainzone->size,mainzone); printf ("tag range: %i to %i\n", lowtag, hightag); for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->tag >= lowtag && block->tag <= hightag) printf ("block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) printf ("ERROR: block size does not touch the next block\n"); if ( block->next->prev != block) printf ("ERROR: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) printf ("ERROR: two consecutive free blocks\n"); } } // // Z_FileDumpHeap // void Z_FileDumpHeap (FILE* f) { memblock_t* block; fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone); for (block = mainzone->blocklist.next ; ; block = block->next) { fprintf (f,"block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) fprintf (f,"ERROR: block size does not touch the next block\n"); if ( block->next->prev != block) fprintf (f,"ERROR: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) fprintf (f,"ERROR: two consecutive free blocks\n"); } } // // Z_CheckHeap // void Z_CheckHeap (void) { memblock_t* block; for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) I_Error ("Z_CheckHeap: block size does not touch the next block\n"); if ( block->next->prev != block) I_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) I_Error ("Z_CheckHeap: two consecutive free blocks\n"); } } // // Z_ChangeTag // void Z_ChangeTag2(void *ptr, int tag, const char *file, int line) { memblock_t* block; block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) I_Error("%s:%i: Z_ChangeTag: block without a ZONEID!", file, line); if (tag >= PU_PURGELEVEL && block->user == NULL) I_Error("%s:%i: Z_ChangeTag: an owner is required " "for purgable blocks", file, line); block->tag = tag; } void Z_ChangeUser(void *ptr, void **user) { memblock_t* block; block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) { I_Error("Z_ChangeUser: Tried to change user for invalid block!"); } block->user = user; *user = ptr; } // // Z_FreeMemory // int Z_FreeMemory (void) { memblock_t* block; int free; free = 0; for (block = mainzone->blocklist.next ; block != &mainzone->blocklist; block = block->next) { if (block->tag == PU_FREE || block->tag >= PU_PURGELEVEL) free += block->size; } return free; } unsigned int Z_ZoneSize(void) { return mainzone->size; } crispy-doom-crispy-doom-5.6.4/src/z_zone.h000066400000000000000000000040661360717211000205060ustar00rootroot00000000000000// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // Zone Memory Allocation, perhaps NeXT ObjectiveC inspired. // Remark: this was the only stuff that, according // to John Carmack, might have been useful for // Quake. // #ifndef __Z_ZONE__ #define __Z_ZONE__ #include // // ZONE MEMORY // PU - purge tags. enum { PU_STATIC = 1, // static entire execution time PU_SOUND, // static while playing PU_MUSIC, // static while playing PU_FREE, // a free block PU_LEVEL, // static until level exited PU_LEVSPEC, // a special thinker in a level // Tags >= PU_PURGELEVEL are purgable whenever needed. PU_PURGELEVEL, PU_CACHE, // Total number of different tag types PU_NUM_TAGS }; void Z_Init (void); void* Z_Malloc (int size, int tag, void *ptr); void Z_Free (void *ptr); void Z_FreeTags (int lowtag, int hightag); void Z_DumpHeap (int lowtag, int hightag); void Z_FileDumpHeap (FILE *f); void Z_CheckHeap (void); void Z_ChangeTag2 (void *ptr, int tag, const char *file, int line); void Z_ChangeUser(void *ptr, void **user); int Z_FreeMemory (void); unsigned int Z_ZoneSize(void); // // This is used to get the local FILE:LINE info from CPP // prior to really call the function in question. // #define Z_ChangeTag(p,t) \ Z_ChangeTag2((p), (t), __FILE__, __LINE__) #endif crispy-doom-crispy-doom-5.6.4/textscreen/000077500000000000000000000000001360717211000204205ustar00rootroot00000000000000crispy-doom-crispy-doom-5.6.4/textscreen/.gitignore000066400000000000000000000000511360717211000224040ustar00rootroot00000000000000Makefile Makefile.in .deps *.a tags TAGS crispy-doom-crispy-doom-5.6.4/textscreen/CMakeLists.txt000066400000000000000000000023261360717211000231630ustar00rootroot00000000000000add_library(textscreen STATIC textscreen.h txt_conditional.c txt_conditional.h txt_checkbox.c txt_checkbox.h txt_desktop.c txt_desktop.h txt_dropdown.c txt_dropdown.h txt_fileselect.c txt_fileselect.h txt_gui.c txt_gui.h txt_inputbox.c txt_inputbox.h txt_io.c txt_io.h txt_main.h txt_button.c txt_button.h txt_label.c txt_label.h txt_radiobutton.c txt_radiobutton.h txt_scrollpane.c txt_scrollpane.h txt_separator.c txt_separator.h txt_spinctrl.c txt_spinctrl.h txt_sdl.c txt_sdl.h txt_strut.c txt_strut.h txt_table.c txt_table.h txt_utf8.c txt_utf8.h txt_widget.c txt_widget.h txt_window.c txt_window.h txt_window_action.c txt_window_action.h) target_include_directories(textscreen INTERFACE "." PRIVATE "../src/") target_link_libraries(textscreen m SDL2::SDL2) crispy-doom-crispy-doom-5.6.4/textscreen/Doxyfile000066400000000000000000003211621360717211000221330ustar00rootroot00000000000000# Doxyfile 1.8.13 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "libtextscreen" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = . # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 0. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. FILE_PATTERNS = *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # cost of reduced performance. This can be particularly helpful with template # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse-libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /